From: Tuukka Lehtonen Date: Tue, 13 Sep 2016 15:08:40 +0000 (+0300) Subject: Sync git svn branch with SVN repository r33189. X-Git-Tag: v1.25.0~121^2 X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=commitdiff_plain;h=b809a171b6dfb81ed9ef9e84870dcbcbc5912f0e;hp=0e8cf1e1708870e179330c2f9af2d03589d553ab Sync git svn branch with SVN repository r33189. refs #6653 refs #6684 --- diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/TextGridNode.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/TextGridNode.java index 1b817a119..886ade120 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/TextGridNode.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/TextGridNode.java @@ -192,6 +192,10 @@ public class TextGridNode extends G2DParentNode implements Decoration { get(x,y).setVerticalAlignment(verticalAlignment); } + public void setForceEventListening(int x, int y, boolean force) { + get(x,y).setForceEventListening(force); + } + public void setEditable(int x, int y, boolean editable) { get(x,y).setEditable(editable); } diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/TextNode.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/TextNode.java index 6fb0611f1..f948bc053 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/TextNode.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/TextNode.java @@ -129,11 +129,6 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L */ protected String text = null; - /** - * Tells if this node is still pending for real results or not. - */ - protected boolean pending = false; - /** * The font used to render the {@link #text}. */ @@ -180,12 +175,25 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L */ protected byte verticalAlignment = 3; - protected boolean hover = false; - boolean editable = false; - boolean showSelection = true; - - - boolean wrapText = true; + /** + * Tells if this node is still pending for real results or not. + */ + protected static final int STATE_PENDING = (1 << 0); + protected static final int STATE_HOVER = (1 << 1); + protected static final int STATE_EDITABLE = (1 << 2); + protected static final int STATE_SHOW_SELECTION = (1 << 3); + protected static final int STATE_WRAP_TEXT = (1 << 4); + protected transient static final int STATE_EDITING = (1 << 5); + protected transient static final int STATE_VALID = (1 << 6); + protected transient static final int STATE_X_OFFSET_IS_DIRTY = (1 << 7); + protected static final int STATE_ALWAYS_ADD_LISTENERS = (1 << 8); + protected static final int STATE_LISTENERS_ADDED = (1 << 9); + + /** + * A combination of all the STATE_ constants defined in this class, + * e.g. {@link #STATE_PENDING}. + */ + protected int state = STATE_SHOW_SELECTION | STATE_WRAP_TEXT | STATE_VALID | STATE_X_OFFSET_IS_DIRTY; protected RVI dataRVI = null; @@ -201,11 +209,6 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L ITextListener textListener; ITextContentFilter editContentFilter; - transient boolean editing = false; - transient boolean valid = true; - - private transient boolean xOffsetIsDirty = true; - /** * The renderable line structures parsed from {@link #text} by * {@link #parseLines(String)}, laid out by @@ -248,12 +251,55 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L super.cleanup(); } + protected boolean hasState(int flags) { + return (state & flags) == flags; + } + + protected void setState(int flags) { + this.state |= flags; + } + + protected void setState(int flags, boolean set) { + if (set) + this.state |= flags; + else + this.state &= ~flags; + } + + protected void clearState(int flags) { + this.state &= ~flags; + } + + protected void setListeners(boolean add) { + if (add) + addListeners(); + else + removeListeners(); + } + protected void addListeners() { - addEventHandler(this); + if (!hasState(STATE_LISTENERS_ADDED)) { + addEventHandler(this); + setState(STATE_LISTENERS_ADDED); + } } protected void removeListeners() { - removeEventHandler(this); + if (hasState(STATE_LISTENERS_ADDED)) { + removeEventHandler(this); + clearState(STATE_LISTENERS_ADDED); + } + } + + /** + * Set to true to always enable event listening in this TextNode to allow the text node to keep track of hovering, etc. and to allow DnD even when + * @param force + */ + public void setForceEventListening(boolean force) { + setState(STATE_ALWAYS_ADD_LISTENERS, force); + if (force && !hasState(STATE_EDITABLE)) { + setListeners(force); + } } /** @@ -277,11 +323,11 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L * @return null if no change to edit state was made */ protected Boolean setEditMode(boolean edit, boolean notify) { - if (edit && !editable) + if (edit && !hasState(STATE_EDITABLE)) return null; - if (editing == edit) + if (hasState(STATE_EDITING) == edit) return null; - this.editing = edit; + setState(STATE_EDITING); if (edit) { caret = text != null ? text.length() : 0; selectionTail = 0; @@ -298,29 +344,26 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L @SyncField({"editable"}) public void setEditable(boolean editable) { - boolean changed = this.editable != editable; - this.editable = editable; - if (editing && !editable) + boolean changed = hasState(STATE_EDITABLE) != editable; + setState(STATE_EDITABLE, editable); + if (hasState(STATE_EDITING) && !editable) setEditMode(false); - if (changed) { - if (editable) - addListeners(); - else - removeListeners(); + if (changed && !hasState(STATE_ALWAYS_ADD_LISTENERS)) { + setListeners(editable); } } public boolean isEditable() { - return editable; + return hasState(STATE_EDITABLE); } public boolean isEditMode() { - return editing; + return hasState(STATE_EDITING); } @SyncField({"wrapText"}) public void setWrapText(boolean wrapText) { - this.wrapText = wrapText; + setState(STATE_WRAP_TEXT, wrapText); } /** @@ -328,16 +371,16 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L * the width of the box is fixed */ public boolean isWrapText() { - return this.wrapText; + return hasState(STATE_WRAP_TEXT); } @SyncField({"showSelection"}) public void setShowSelection(boolean showSelection) { - this.showSelection = showSelection; + setState(STATE_SHOW_SELECTION, showSelection); } public boolean showsSelection() { - return showSelection; + return hasState(STATE_SHOW_SELECTION); } /** @@ -353,7 +396,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L // no value => value if(this.text == null && text != null) NodeUtil.decreasePending(this); - if (editing) + if (hasState(STATE_EDITING)) return; this.text = new String(text != null ? text : ""); @@ -399,7 +442,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L @SyncField({"text","caret","selectionTail"}) public void setText(String text) { //System.out.println("TextNode.setText('" + text + "', " + editing + ")"); - if (editing) + if (hasState(STATE_EDITING)) return; // value => no value @@ -416,9 +459,11 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L @SyncField({"pending"}) public void setPending(boolean pending) { - if(!this.pending && pending) NodeUtil.increasePending(this); - if(this.pending && !pending) NodeUtil.decreasePending(this); - this.pending = pending; + boolean p = hasState(STATE_PENDING); + if(!p && pending) NodeUtil.increasePending(this); + if(p && !pending) NodeUtil.decreasePending(this); + if(p != pending) + setState(STATE_PENDING, pending); } @SyncField({"fixedWidth"}) @@ -448,16 +493,16 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L } public final void synchronizeWrapText(boolean wrap) { - wrapText = wrap; + setState(STATE_WRAP_TEXT, wrap); } public boolean isHovering() { - return hover; + return hasState(STATE_HOVER); } @SyncField({"hover"}) public void setHover(boolean hover) { - this.hover = hover; + setState(STATE_HOVER, hover); repaint(); } @@ -564,6 +609,8 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L Color color = this.color; boolean isSelected = NodeUtil.isSelected(this, 1); + boolean hover = hasState(STATE_HOVER); + boolean editing = hasState(STATE_EDITING); if (!isSelected && hover) { color = add(color, 120, 120, 120); @@ -634,7 +681,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L fieldName.equals("created_by") ); } - Color backgroundColor = valid ? this.backgroundColor : Color.red; + Color backgroundColor = hasState(STATE_VALID) ? this.backgroundColor : Color.red; // RENDER if ( !isPdfField ) { @@ -887,7 +934,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L if(validator != null) { String error = validator.apply(text); - valid = (error == null); + setState(STATE_VALID, (error == null)); } resetCaches(); @@ -908,7 +955,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L @ServerSide protected void fireTextEditingCancelled() { - valid = true; + setState(STATE_VALID); if (deactivateEdit()) { if (textListener != null) @@ -925,9 +972,9 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L @ServerSide public void fireTextEditingEnded() { - if (!valid) { + if (!hasState(STATE_VALID)) { fireTextEditingCancelled(); - valid = true; + setState(STATE_VALID); return; } @@ -957,13 +1004,13 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L } private void invalidateXOffset() { - xOffsetIsDirty = true; + setState(STATE_X_OFFSET_IS_DIRTY); } private void computeEditingXOffset() { if(lines == null) return; - if(!xOffsetIsDirty) return; + if(!hasState(STATE_X_OFFSET_IS_DIRTY)) return; if(fixedWidth > 0f) { // TODO: implement @@ -979,7 +1026,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L } - xOffsetIsDirty = false; + clearState(STATE_X_OFFSET_IS_DIRTY); } @@ -1259,7 +1306,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L // Parse & layout (unaligned) Line[] lines = null; - if(wrapText) { + if(hasState(STATE_WRAP_TEXT)) { float width = fixedWidth; if(width <= 0 && targetBounds != null) width = (float) (((targetBounds.getWidth() - 2*paddingX)) * scaleRecip); @@ -1545,7 +1592,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L @Override protected boolean handleCommand(CommandEvent e) { - if (!editing) + if (!hasState(STATE_EDITING)) return false; if (Commands.SELECT_ALL.equals(e.command)) { @@ -1557,7 +1604,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L @Override protected boolean keyPressed(KeyPressedEvent event) { - if (!editing) + if (!hasState(STATE_EDITING)) return false; char c = event.character; @@ -1658,7 +1705,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L case KeyEvent.VK_ESCAPE: text = textBeforeEdit; resetCaches(); - editing = false; + clearState(STATE_EDITING); fireTextEditingCancelled(); return true; @@ -1725,7 +1772,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L if (event.button != MouseClickEvent.LEFT_BUTTON) return false; - if (hover) { + if (hasState(STATE_HOVER)) { hoverClick++; if (hoverClick < 2) return false; @@ -1734,7 +1781,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L if (ctx == null) return false; IElement e = DiagramNodeUtil.getElement(ctx, this); - if (!editing) { + if (!hasState(STATE_EDITING)) { if (Boolean.TRUE.equals(setEditMode(true))) { editActivation = activateEdit(0, e, ctx); repaint(); @@ -1742,7 +1789,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L } } else { hoverClick = 0; - if (editing) { + if (hasState(STATE_EDITING)) { fireTextEditingEnded(); } } @@ -1772,13 +1819,13 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L @Override protected boolean mouseButtonPressed(MouseButtonPressedEvent event) { - if (!editing) + if (!hasState(STATE_EDITING)) return false; Point2D local = controlToLocal( event.controlPosition ); // FIXME: once the event coordinate systems are cleared up, remove this workaround local = parentToLocal(local); - if (hover && this.containsLocal(local)) { + if (hasState(STATE_HOVER) && this.containsLocal(local)) { setCaret(local, event.isShiftDown()); } return false; @@ -1787,8 +1834,8 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L @Override protected boolean mouseMoved(MouseMovedEvent event) { boolean hit = hitTest(event, 3.0); - if (hit != hover) { - hover = hit; + if (hit != hasState(STATE_HOVER)) { + setState(STATE_HOVER, hit); repaint(); } return false; diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/profile/TextGridStyle.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/profile/TextGridStyle.java index 03069a3df..6657d5a60 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/profile/TextGridStyle.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/profile/TextGridStyle.java @@ -273,6 +273,7 @@ public abstract class TextGridStyle extends StyleBase { setTextNodeData(node, 3, row, value3, FONT, awtColor, BACKGROUND_COLOR); node.setEditable(1, row, false); + node.setForceEventListening(2, row, true); node.setEditable(2, row, modifier != null); node.setEditable(3, row, false); diff --git a/bundles/org.simantics.fileimport/META-INF/MANIFEST.MF b/bundles/org.simantics.fileimport/META-INF/MANIFEST.MF index 2c23ea6d0..d1a2f8065 100644 --- a/bundles/org.simantics.fileimport/META-INF/MANIFEST.MF +++ b/bundles/org.simantics.fileimport/META-INF/MANIFEST.MF @@ -1,6 +1,6 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 -Bundle-Name: Simantics Fileimport Interface +Bundle-Name: Simantics Fileimport Interface and base classes Bundle-SymbolicName: org.simantics.fileimport Bundle-Version: 1.0.0.qualifier Bundle-Activator: org.simantics.fileimport.Activator @@ -16,3 +16,4 @@ Bundle-ActivationPolicy: lazy Export-Package: org.simantics.fileimport Service-Component: OSGI-INF/FileReferenceFileImport.xml, OSGI-INF/LibraryFolderFileImport.xml +Bundle-Vendor: Semantum Oy diff --git a/bundles/org.simantics.fileimport/scl/Dropins/Core.scl b/bundles/org.simantics.fileimport/scl/Dropins/Core.scl index 410bda137..7fdcefebc 100644 --- a/bundles/org.simantics.fileimport/scl/Dropins/Core.scl +++ b/bundles/org.simantics.fileimport/scl/Dropins/Core.scl @@ -1,6 +1,8 @@ import "MMap" as MMap importJava "org.simantics.fileimport.scl.DropinsSCL" where + watchDropinsFolder :: () -> () + unwatchDropinsFolder :: () -> () uploadToDropinsBase64 :: String -> String -> () getUploadedFiles :: () -> MMap.T String Long removeFileForId :: Long -> () diff --git a/bundles/org.simantics.fileimport/src/org/simantics/fileimport/FileImportService.java b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/FileImportService.java index e07dfaadd..a3a6a8c41 100644 --- a/bundles/org.simantics.fileimport/src/org/simantics/fileimport/FileImportService.java +++ b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/FileImportService.java @@ -17,19 +17,19 @@ import java.util.function.Consumer; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; -import org.simantics.Simantics; -import org.simantics.db.ReadGraph; -import org.simantics.db.Resource; -import org.simantics.db.common.request.UniqueRead; -import org.simantics.db.exception.DatabaseException; -import org.simantics.db.service.SerialisationSupport; -import org.simantics.layer0.Layer0; +import org.simantics.fileimport.dropins.FileImportDropins; +/** + * Utility class for Simantics File import functions + * + * @author Jani Simomaa + * + */ public class FileImportService { public static final String DB_FILE = ".simanticsdb"; - public static List getFileImportServices() { + private static List getFileImportServices() { ServiceReference[] serviceReferences = new ServiceReference[0]; try { serviceReferences = Activator.getContext().getAllServiceReferences(IGenericFileImport.class.getName(), @@ -48,6 +48,11 @@ public class FileImportService { return services; } + /** + * Lists all supported file extensions which have a registered service for handling the import + * + * @return Map containing the extension and the description of the extension in that order + */ public static Map supportedExtensionsWithFilters() { List services = getFileImportServices(); Map extensionsWithFilters = new HashMap<>(); @@ -57,6 +62,12 @@ public class FileImportService { return extensionsWithFilters; } + /** + * Method that performs the import of the given file. This method is called when e.g. {@link FileImportDropins} watcher detects {@link java.nio.file.StandardWatchEventKinds.ENTRY_CREATE} operation + * + * @param file Path file to be imported + * @param callback Optional callback which can be used to catch Throwables thrown in the import process + */ public static void performFileImport(Path file, Optional> callback) { if (file.getFileName().toString().equals(DB_FILE)) return; @@ -75,6 +86,13 @@ public class FileImportService { }); } + + /** + * Remove the entity that matches the file. This method is called when e.g. the {@link FileImportDropins} watcher detects {@link java.nio.file.StandardWatchEventKinds.ENTRY_DELETE} operation + * + * @param file Path file that was deleted + * @param callback Optional callback to catch Throwables thrown during the deletion process + */ public static void removeResourceForFile(Path file, Optional> callback) { Optional serviceOp = findServiceForFileExtension(file); serviceOp.ifPresent(service -> { @@ -143,6 +161,12 @@ public class FileImportService { static final String FOLDER = "_folder_"; + /** + * Method for finding a File Import service for the given file based on the file extension + * + * @param file Path file for which the import service is looked for + * @return Optiona IGenerigFileImport service which is able to handle the import of this type of file + */ public static Optional findServiceForFileExtension(Path file) { String extension = ""; @@ -174,7 +198,12 @@ public class FileImportService { return Optional.empty(); } - public static Map getPathsAndResources() { + /** + * Method for listing all current paths and their corresponding identifiers in Simantics database + * + * @return Map containing + */ + public static Map getPathsAndResources() { try { Path db = Activator.getDropinsFolder().resolve(DB_FILE); if (!Files.exists(db)) @@ -183,29 +212,14 @@ public class FileImportService { try (InputStream stream = Files.newInputStream(db)) { props.load(stream); } - Map result = Simantics.getSession().syncRequest(new UniqueRead>() { - - @Override - public Map perform(ReadGraph graph) throws DatabaseException { - Map map = new HashMap<>(); - for (Map.Entry entry : props.entrySet()) { - String value = (String) entry.getValue(); - Long id = Long.valueOf(value); - SerialisationSupport ss = graph.getService(SerialisationSupport.class); - try { - Resource r = ss.getResource(id); - String name = graph.getRelatedValue(r, Layer0.getInstance(graph).HasName); - map.put(name, id); - } catch (DatabaseException e) { - e.printStackTrace(); - } - } - return map; - } - }); - - return result; - } catch (IOException | DatabaseException e) { + Map map = new HashMap<>(); + for (Map.Entry entry : props.entrySet()) { + String value = (String) entry.getValue(); + String key = (String) entry.getKey(); + map.put(key, value); + } + return map; + } catch (IOException e) { e.printStackTrace(); return Collections.emptyMap(); } diff --git a/bundles/org.simantics.fileimport/src/org/simantics/fileimport/IGenericFileImport.java b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/IGenericFileImport.java index 2de1cc742..07c0edc46 100644 --- a/bundles/org.simantics.fileimport/src/org/simantics/fileimport/IGenericFileImport.java +++ b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/IGenericFileImport.java @@ -5,28 +5,36 @@ import java.util.Map; import java.util.Optional; /** + * Base interface for performing file imports. + * + * Usually with Simantics products every file that will be imported will become + * a resource in the Simantics database. For these cases see {@link SimanticsResourceFileImport} + * * @author Jani Simomaa * */ public interface IGenericFileImport { /** - * Performs the import procedure for the given file + * Performs the import for the given file * - * @param file - * file to import + * @param file Path to file to import + * @return Optional string which will be the identifier of the imported file which can later be used for removing the imported entity + * @throws Exception */ Optional perform(Path file) throws Exception; /** - * @param resource + * Remove the entity + * + * @param identifier String identifier which can be used to remove the imported entity */ - void remove(String resource) throws Exception; + void remove(String identifier) throws Exception; /** * Returns a key-value map for file extensions this importer can handle * - * @return + * @return Map allowed extensions this service can handle. Key is the extension e.g. .sharedLibrary and the value is the description of the extension */ Map allowedExtensionsWithFilters(); diff --git a/bundles/org.simantics.fileimport/src/org/simantics/fileimport/SimanticsResourceFileImport.java b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/SimanticsResourceFileImport.java index 2836b670e..70439ef1d 100644 --- a/bundles/org.simantics.fileimport/src/org/simantics/fileimport/SimanticsResourceFileImport.java +++ b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/SimanticsResourceFileImport.java @@ -19,12 +19,19 @@ import org.simantics.db.request.Read; import org.simantics.db.service.SerialisationSupport; import org.simantics.layer0.Layer0; +/** + * Most of the implementations should extend this class which handles the storing of + * the identifier of the imported entity and the removing of the entity + * + * @author Jani Simomaa + * + */ public abstract class SimanticsResourceFileImport implements IGenericFileImport { @Override final public Optional perform(Path file) throws Exception { - Path dropins = Activator.getDropinsFolder(); + Path dropins = Activator.getDropinsFolder(); Path parts = dropins.relativize(file); Resource parent = resolveParent(null, parts); if (parent == null) @@ -37,7 +44,15 @@ public abstract class SimanticsResourceFileImport implements IGenericFileImport } } - public abstract Optional perform(Resource parent, Path file); + /** + * Performs the import for the given file + * + * @param parent Resource parent of the imported entity in Simantics database + * @param file Path file location of file + * @return Optional Resource of the imported entity in Simantics database + * @throws Exception + */ + public abstract Optional perform(Resource parent, Path file) throws Exception; @Override public void remove(String resourceId) throws Exception { diff --git a/bundles/org.simantics.fileimport/src/org/simantics/fileimport/dropins/FileImportDropins.java b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/dropins/FileImportDropins.java index a32dd9877..21a9f9ab0 100644 --- a/bundles/org.simantics.fileimport/src/org/simantics/fileimport/dropins/FileImportDropins.java +++ b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/dropins/FileImportDropins.java @@ -1,34 +1,47 @@ package org.simantics.fileimport.dropins; -import static java.nio.file.StandardWatchEventKinds.OVERFLOW; import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; +import static java.nio.file.StandardWatchEventKinds.OVERFLOW; import java.io.IOException; +import java.io.RandomAccessFile; import java.nio.file.FileSystem; +import java.nio.file.FileSystemException; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.WatchEvent; import java.nio.file.WatchEvent.Kind; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; import java.nio.file.attribute.BasicFileAttributes; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; -import java.nio.file.WatchKey; -import java.nio.file.WatchService; import org.simantics.fileimport.Activator; import org.simantics.fileimport.FileImportService; +/** + * Directory watcher based on {@link java.nio.file.WatchService} which will listen to file changes inside the dropins directory + * ~/workspace/.metadata/plugins/org.simantics.fileimport/dropins + * + * @author Jani Simomaa + * + */ public class FileImportDropins { private static Thread watcherThread = null; private static DropinsFolderWatcher watcher = null; + /** + * Start watching the dropins folder which are located in + * ~/workspace/.metadata/plugins/org.simantics.fileimport/dropins + */ public static void watchDropinsFolder() { if (watcher == null && watcherThread == null) { try { @@ -42,6 +55,9 @@ public class FileImportDropins { } } + /** + * Stop watching the dropins folder + */ public static void unwatchDropinsFolder() { watcher.stop(); try { @@ -70,6 +86,29 @@ public class FileImportDropins { registerAll(this.dropinsFolder); } + private static void syncPath(Path f) throws IOException { + // Does not seem to need 's' according to unit test in Windows + boolean synced = false; + int count = 0; + while (!synced) { + try (RandomAccessFile raf = new RandomAccessFile(f.toFile(), "rw")) { + raf.getFD().sync(); + synced = true; + } catch (IOException e) { + if (count == 3) { + throw e; + } else { + try { + Thread.sleep(50); + } catch (InterruptedException e1) { + e1.printStackTrace(); + } + count++; + } + } + } + } + @Override public void run() { stopped.set(false); @@ -91,8 +130,28 @@ public class FileImportDropins { continue; if (ENTRY_CREATE == kind) { System.out.println("New path created: " + newPath); - FileImportService.performFileImport(newPath, Optional.empty()); + int current = 0; + + while (!Files.isWritable(newPath) && current <= 10) { + System.out.println("Sleeping for file import (current=" + current +")"); + Thread.sleep(200); + current++; + } + + FileImportService.performFileImport(newPath, Optional.of(t -> { + if (t instanceof FileSystemException) { + try { + syncPath(newPath); + } catch (IOException e) { + e.printStackTrace(); + } + FileImportService.performFileImport(newPath, Optional.empty()); + } else { + t.printStackTrace(); + } + })); register(newPath); + } else if (ENTRY_MODIFY == kind) { System.out.println("New path modified: " + newPath); } else if (ENTRY_DELETE == kind) { @@ -104,8 +163,6 @@ public class FileImportDropins { keys.remove(key); // break; // loop } - } catch (IOException e) { - e.printStackTrace(); } catch (InterruptedException e) { if (!stopped.get()) e.printStackTrace(); diff --git a/bundles/org.simantics.fileimport/src/org/simantics/fileimport/scl/DropinsSCL.java b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/scl/DropinsSCL.java index 0758d3e78..cad451877 100644 --- a/bundles/org.simantics.fileimport/src/org/simantics/fileimport/scl/DropinsSCL.java +++ b/bundles/org.simantics.fileimport/src/org/simantics/fileimport/scl/DropinsSCL.java @@ -2,17 +2,40 @@ package org.simantics.fileimport.scl; import java.io.IOException; import java.nio.file.Path; +import java.util.HashMap; import java.util.Map; import java.util.Optional; +import org.simantics.Simantics; import org.simantics.databoard.util.Base64; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.request.UniqueRead; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.service.SerialisationSupport; import org.simantics.fileimport.Activator; import org.simantics.fileimport.FileImportService; import org.simantics.fileimport.dropins.FileImportDropins; +import org.simantics.layer0.Layer0; import org.simantics.utils.FileUtils; +/** + * SCL interface for Simantics File Import Functionality + * See "Dropins/Core" SCL module for more + * + * @author Jani Simomaa + * + */ public class DropinsSCL { + public static void watchDropinsFolder() { + FileImportDropins.watchDropinsFolder(); + } + + public static void unwatchDropinsFolder() { + FileImportDropins.unwatchDropinsFolder(); + } + public static void uploadToDropinsBase64(String base64, String fileName) { // ensure that watcher is awake FileImportDropins.watchDropinsFolder(); @@ -26,8 +49,29 @@ public class DropinsSCL { } } - public static Map getUploadedFiles() { - return FileImportService.getPathsAndResources(); + public static Map getUploadedFiles() throws DatabaseException { + Map results = FileImportService.getPathsAndResources(); + Map result = Simantics.getSession().syncRequest(new UniqueRead>() { + + @Override + public Map perform(ReadGraph graph) throws DatabaseException { + Map map = new HashMap<>(); + for (Map.Entry entry : results.entrySet()) { + String value = (String) entry.getValue(); + Long id = Long.valueOf(value); + SerialisationSupport ss = graph.getService(SerialisationSupport.class); + try { + Resource r = ss.getResource(id); + String name = graph.getRelatedValue(r, Layer0.getInstance(graph).HasName); + map.put(name, id); + } catch (DatabaseException e) { + e.printStackTrace(); + } + } + return map; + } + }); + return result; } public static void removeFileForId(long id) {