Merge commit 'b809a171b6dfb81ed9ef9e84870dcbcbc5912f0e'
authorTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Tue, 13 Sep 2016 15:09:15 +0000 (18:09 +0300)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Tue, 13 Sep 2016 15:09:15 +0000 (18:09 +0300)
bundles/org.simantics.diagram/src/org/simantics/diagram/elements/TextGridNode.java
bundles/org.simantics.diagram/src/org/simantics/diagram/elements/TextNode.java
bundles/org.simantics.diagram/src/org/simantics/diagram/profile/TextGridStyle.java
bundles/org.simantics.fileimport/META-INF/MANIFEST.MF
bundles/org.simantics.fileimport/scl/Dropins/Core.scl
bundles/org.simantics.fileimport/src/org/simantics/fileimport/FileImportService.java
bundles/org.simantics.fileimport/src/org/simantics/fileimport/IGenericFileImport.java
bundles/org.simantics.fileimport/src/org/simantics/fileimport/SimanticsResourceFileImport.java
bundles/org.simantics.fileimport/src/org/simantics/fileimport/dropins/FileImportDropins.java
bundles/org.simantics.fileimport/src/org/simantics/fileimport/scl/DropinsSCL.java

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