Move graph file document codes around a bit 90/390/1
authorAntti Villberg <antti.villberg@semantum.fi>
Mon, 3 Apr 2017 07:19:37 +0000 (10:19 +0300)
committerAntti Villberg <antti.villberg@semantum.fi>
Mon, 3 Apr 2017 07:19:37 +0000 (10:19 +0300)
refs #7118

Change-Id: I8a1036a8717b74f3e57b5ea2519de27fb9dc2eab

16 files changed:
bundles/org.simantics.document.ontology/META-INF/MANIFEST.MF
bundles/org.simantics.document.ontology/graph.tg
bundles/org.simantics.document.ontology/graph/Document.pgraph
bundles/org.simantics.document.ontology/src/org/simantics/document/DocumentResource.java
bundles/org.simantics.document.ui/src/org/simantics/document/ui/actions/AddUrlDocument.java
bundles/org.simantics.document.ui/src/org/simantics/document/ui/actions/AddUrlDocumentWithDetail.java
bundles/org.simantics.document.ui/src/org/simantics/document/ui/actions/ExportDocumentFolder.java
bundles/org.simantics.document.ui/src/org/simantics/document/ui/actions/ImportDocumentFolder.java
bundles/org.simantics.document.ui/src/org/simantics/document/ui/actions/ImportDocumentWithDetail.java
bundles/org.simantics.document.ui/src/org/simantics/document/ui/wizard/FileDocumentImportWizard.java
bundles/org.simantics.document/META-INF/MANIFEST.MF
bundles/org.simantics.document/src/org/simantics/document/Activator.java
bundles/org.simantics.document/src/org/simantics/document/AddDocumentAction.java [new file with mode: 0644]
bundles/org.simantics.document/src/org/simantics/document/DocumentVersionUtils.java [new file with mode: 0644]
bundles/org.simantics.document/src/org/simantics/document/FileDocumentUtil.java [new file with mode: 0644]
bundles/org.simantics.document/src/org/simantics/document/ImportDocument.java [new file with mode: 0644]

index 5397d24db2e0fd080cac9cb5315252a58a9ec03a..517ce7f3b4a44b80eaa8288315e283edfd80c369 100644 (file)
@@ -9,6 +9,7 @@ Require-Bundle: org.simantics.layer0,
  org.simantics.scenegraph.ontology;bundle-version="1.0.0",
  org.simantics.viewpoint.ontology;bundle-version="1.0.0",
  org.simantics.selectionview.ontology;bundle-version="1.1.0",
- org.simantics.graphfile.ontology;bundle-version="0.1.0"
+ org.simantics.graphfile.ontology;bundle-version="0.1.0",
+ org.simantics.action.ontology;bundle-version="1.1.0"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Export-Package: org.simantics.document
index aadabaa9a92d5782509edce10315771b1e9dbcf2..94f3d05181d30bad5872617c811080b9a5861860 100644 (file)
Binary files a/bundles/org.simantics.document.ontology/graph.tg and b/bundles/org.simantics.document.ontology/graph.tg differ
index e006a5fd8a141157a7f5e7687a1b79a030f617c6..b35002d40c3aa9fbad91e1c94b30dbc97c4a11d8 100644 (file)
@@ -3,6 +3,7 @@ L0X = <http://www.simantics.org/Layer0X-1.1>
 SG = <http://www.simantics.org/Scenegraph-1.1>
 SEL = <http://www.simantics.org/SelectionView-1.2>
 GF = <http://www.simantics.org/GraphFile-0.1>
+ACT = <http://www.simantics.org/Action-1.1>
 
 DOC = <http://www.simantics.org/Document-1.2> : L0.Ontology
     @L0.new
@@ -158,4 +159,8 @@ DOC.PlainTextDocument <T DOC.Document
 
 
 DOC.DocumentLibrary <T L0.Library
+
+DOC.Actions : L0.Library
+
+DOC.Actions.ImportFileDocument : ACT.Action
    
\ No newline at end of file
index 2bca437a26934bbbd1be4c85eb835e34b1dd2cc1..d98c2e5b065eea2a940d8ae7132b190f3c22d76e 100644 (file)
@@ -10,6 +10,8 @@ import org.simantics.db.service.QueryControl;
 
 public class DocumentResource {
     
+    public final Resource Actions;
+    public final Resource Actions_ImportFileDocument;
     public final Resource Document;
     public final Resource DocumentLibrary;
     public final Resource DocumentSettings;
@@ -91,6 +93,8 @@ public class DocumentResource {
     public final Resource documentSettings_Inverse;
         
     public static class URIs {
+        public static final String Actions = "http://www.simantics.org/Document-1.2/Actions";
+        public static final String Actions_ImportFileDocument = "http://www.simantics.org/Document-1.2/Actions/ImportFileDocument";
         public static final String Document = "http://www.simantics.org/Document-1.2/Document";
         public static final String DocumentLibrary = "http://www.simantics.org/Document-1.2/DocumentLibrary";
         public static final String DocumentSettings = "http://www.simantics.org/Document-1.2/DocumentSettings";
@@ -182,6 +186,8 @@ public class DocumentResource {
     }
     
     public DocumentResource(ReadGraph graph) {
+        Actions = getResourceOrNull(graph, URIs.Actions);
+        Actions_ImportFileDocument = getResourceOrNull(graph, URIs.Actions_ImportFileDocument);
         Document = getResourceOrNull(graph, URIs.Document);
         DocumentLibrary = getResourceOrNull(graph, URIs.DocumentLibrary);
         DocumentSettings = getResourceOrNull(graph, URIs.DocumentSettings);
index ffb874f6fb00957a775ccecc455c6050910a70da..b5fbe0adddad16de16acc3f5694caf847aa1a9a5 100644 (file)
@@ -19,6 +19,7 @@ import org.simantics.db.Resource;
 import org.simantics.db.WriteGraph;
 import org.simantics.db.common.request.WriteRequest;
 import org.simantics.db.exception.DatabaseException;
+import org.simantics.document.AddDocumentAction;
 import org.simantics.document.DocumentResource;
 import org.simantics.layer0.Layer0;
 import org.simantics.utils.datastructures.Callback;
index 824a0b5fbb8de26ebe6d6a138049809053dfa96a..5ed7a1a2bcf0926fb20a5fc417bafc86e877da49 100644 (file)
@@ -21,6 +21,7 @@ import org.simantics.db.common.request.WriteRequest;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;
 import org.simantics.db.exception.ServiceException;
+import org.simantics.document.AddDocumentAction;
 import org.simantics.document.DocumentResource;
 import org.simantics.document.ui.dialogs.UrlDetailDialog;
 import org.simantics.layer0.Layer0;
index e118e9988012a32f442c14c2949a89c6142cb0e2..3956a5923bdd7b377c71ac5d14562ee2c4758d93 100644 (file)
@@ -26,8 +26,8 @@ import org.simantics.db.ReadGraph;
 import org.simantics.db.Resource;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.layer0.adapter.ActionFactory;
+import org.simantics.document.FileDocumentUtil;
 import org.simantics.document.ui.Activator;
-import org.simantics.document.ui.graphfile.FileDocumentUtil;
 import org.simantics.graphfile.util.GraphFileUtil;
 
 /**
index 3c8e0af303151f21ebacc7cdf20fcb6c469c57c5..88eb503f5a56e28b0f65c2381a1dae3618138a43 100644 (file)
@@ -25,8 +25,8 @@ import org.simantics.db.WriteGraph;
 import org.simantics.db.common.request.WriteRequest;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.layer0.adapter.ActionFactory;
+import org.simantics.document.FileDocumentUtil;
 import org.simantics.document.ui.Activator;
-import org.simantics.document.ui.graphfile.FileDocumentUtil;
 
 /**
  * Action for importing files as documents.
index 7235f55f09382b854d3dada7ca025df36610f763..42a0a7c94fa8bd9476b069a73bf00698796fd8e9 100644 (file)
@@ -21,8 +21,9 @@ import org.simantics.db.Resource;
 import org.simantics.db.WriteGraph;
 import org.simantics.db.common.request.WriteRequest;
 import org.simantics.db.exception.DatabaseException;
+import org.simantics.document.AddDocumentAction;
+import org.simantics.document.FileDocumentUtil;
 import org.simantics.document.ui.dialogs.FileDetailDialog;
-import org.simantics.document.ui.graphfile.FileDocumentUtil;
 import org.simantics.layer0.Layer0;
 import org.simantics.utils.datastructures.Callback;
 import org.simantics.utils.ui.ExceptionUtils;
index abd95948d2c032f6d05968de16709f8f0ccd6f68..0959ae9f854b9ba3aa900acc84143f8c26b1466a 100644 (file)
@@ -5,7 +5,7 @@ import java.io.File;
 import org.eclipse.jface.wizard.IWizardPage;
 import org.eclipse.jface.wizard.Wizard;
 import org.simantics.db.Resource;
-import org.simantics.document.ui.graphfile.FileDocumentUtil;
+import org.simantics.document.FileDocumentUtil;
 
 public abstract  class FileDocumentImportWizard extends Wizard{
        
index f20e8863957c8e6b45508a73a79541069f36a9d4..44e4db9dc3ea33a543570ce017539a416bea8078 100644 (file)
@@ -13,7 +13,9 @@ Require-Bundle: org.simantics;bundle-version="1.0.0",
  org.simantics.wiki.ui;bundle-version="1.1.0",
  org.simantics.export.core;bundle-version="1.0.0",
  org.simantics.utils.ui;bundle-version="1.1.0",
- org.simantics.image2.ontology;bundle-version="1.2.0"
+ org.simantics.image2.ontology;bundle-version="1.2.0",
+ org.simantics.graphfile.ontology;bundle-version="0.1.0",
+ org.simantics.graphfile;bundle-version="0.1.0"
 Export-Package: org.simantics.document,
  org.simantics.document.export,
  org.simantics.document.function,
index b5065445af809312f81b4ecdd19f11322d9c0a93..20b594d6f1f9a89e9bcb5bfbc7d7abb8e7d5463e 100644 (file)
@@ -17,6 +17,9 @@ import org.osgi.framework.BundleContext;
 
 public class Activator implements BundleActivator {
 
+       // The plug-in ID
+       public static final String PLUGIN_ID = "org.simantics.document"; //$NON-NLS-1$
+
        private static BundleContext context;
 
        static BundleContext getContext() {
diff --git a/bundles/org.simantics.document/src/org/simantics/document/AddDocumentAction.java b/bundles/org.simantics.document/src/org/simantics/document/AddDocumentAction.java
new file mode 100644 (file)
index 0000000..8012c4d
--- /dev/null
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.document;
+
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.adapter.ActionFactory;
+
+/**
+ * Abstract base class for adding documents.
+ * 
+ * If import target is an document, a new version of a document is created.
+ * 
+ * Document version creation has two modes:
+ * FLAT: new version is added to the same library as the old document.
+ * TREE: new version replaces the old document in the library, and old document is moved under the new document.
+ * 
+ * 
+ * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
+ *
+ */
+public abstract class AddDocumentAction implements ActionFactory {
+       
+       private Resource relation;
+       
+       public AddDocumentAction(ReadGraph graph, String relationUri) throws DatabaseException {
+               relation = graph.getResource(relationUri);
+       }
+       
+
+       protected Resource getRelation() {
+               return relation;
+       }
+       
+       protected void linkDocument(WriteGraph graph, Resource target, Resource newDocument) throws DatabaseException{
+               DocumentResource doc = DocumentResource.getInstance(graph);
+       
+               if (graph.isInstanceOf(target, doc.Document)) {
+                       // adding a new revision
+                       DocumentVersionUtils.createNewVersion(graph, target, newDocument, relation);
+                       
+                       
+               } else {
+                       graph.claim(target, relation, newDocument);
+                       FileDocumentUtil.createUniqueName(graph, newDocument);
+               }
+       }
+
+}
diff --git a/bundles/org.simantics.document/src/org/simantics/document/DocumentVersionUtils.java b/bundles/org.simantics.document/src/org/simantics/document/DocumentVersionUtils.java
new file mode 100644 (file)
index 0000000..819a06a
--- /dev/null
@@ -0,0 +1,178 @@
+package org.simantics.document;
+
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.utils.NameUtils;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.document.DocumentResource;
+import org.simantics.layer0.Layer0;
+/**
+ * Util class for managing document versions
+ * 
+ * Document version history mode  is searched with relations:
+ * 1. l0.PartOf
+ * 2. l0.IsOwnedBy
+ * 3. l0.IsDependencyOf 
+ * 
+ * If library is not found, history mode is assumed to be flat.
+ * 
+ * In order to have working tree history, the library must contain proper configuration for relations:
+ *  doc.HasLibraryRelation
+ *  doc.HasVersionType
+ *  
+ * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
+ *
+ */
+public class DocumentVersionUtils {
+
+       public enum VersionMode{FLAT,TREE};
+       
+       
+       /**
+        * Adds a new document version.
+        * 
+        * Expects that newDocument is not part of document version chain, and oldDocument does not have newer version.
+        * 
+        * @param graph
+        * @param oldDocument
+        * @param newDocument
+        * @param relation
+        * @throws DatabaseException
+        */
+       public static void createNewVersion(WriteGraph graph, Resource oldDocument, Resource newDocument, Resource relation) throws DatabaseException{
+               DocumentResource doc = DocumentResource.getInstance(graph);
+               if (graph.hasStatement(oldDocument, doc.HasNewerVersion))
+                       throw new DatabaseException("Document " + NameUtils.getSafeName(graph, oldDocument) +" has already new version");
+               
+               Resource inverse = graph.getInverse(relation);
+               Resource lib = graph.getSingleObject(oldDocument, inverse);
+               String modeString = graph.getPossibleRelatedValue(lib, doc.HasVersionType);
+               VersionMode mode = VersionMode.FLAT;
+               if (modeString != null)
+                       mode = VersionMode.valueOf(modeString);
+               
+               graph.claim(oldDocument, doc.HasNewerVersion, newDocument);
+               graph.claim(lib, relation, newDocument);
+               
+               if (mode == VersionMode.TREE) {
+                       graph.deny(lib,relation,oldDocument);
+                       graph.claim(newDocument,relation,oldDocument);
+               }
+               FileDocumentUtil.createUniqueName(graph, newDocument);
+       }
+       
+       /**
+        * Sets document version relationship between two documents. 
+        * 
+        * If the documents are already part of the same version chain this method does nothing.
+        * 
+        * If documents are already set to some version chain with given relation, this method replaces the exiting links.
+        * 
+        * @param graph
+        * @param document1
+        * @param document2
+        * @param versionRel
+        * @throws DatabaseException
+        */
+       public static void setVersion(WriteGraph graph, Resource document1, Resource document2, Resource versionRel) throws DatabaseException {
+               DocumentResource doc = DocumentResource.getInstance(graph);
+               // document type must match
+               if (!graph.getSingleType(document2, doc.Document).equals(graph.getSingleType(document1, doc.Document)))
+                       return;
+               if (!versionRel.equals(doc.HasNewerVersion) && !(versionRel.equals(doc.HasOlderVersion)))
+                       throw new IllegalArgumentException("Unknow version relation + " + graph.getPossibleURI(versionRel));
+               
+               Resource versionRelInv = graph.getInverse(versionRel);
+               // toSet must not be part of the document's version history
+               Resource r = document1;
+               while (r != null) {
+                       if (r.equals(document2))
+                               return;
+                       r = graph.getPossibleObject(r, versionRel);
+               }
+               
+               r = document2;
+               while (r != null) {
+                       if (r.equals(document1))
+                               return;
+                       r = graph.getPossibleObject(r, versionRel);
+               }
+               // At the moment document revision history is linear (no branching).
+               Resource document1Ver = graph.getPossibleObject(document1, versionRel);
+               if (document1Ver != null)
+                       unsetVersion(graph, document1, document1Ver, versionRel);
+               Resource document2Ver = graph.getPossibleObject(document2, versionRelInv);
+               if (document2Ver != null)
+                       unsetVersion(graph, document2, document2Ver, versionRelInv);
+               
+               graph.claim(document1, versionRel, document2);
+               
+               Resource lib = getLib(graph, document1);
+               if (lib != null) {
+                       Resource relation = graph.getPossibleObject(lib, doc.HasLibraryRelation);
+                       String type= graph.getPossibleRelatedValue(lib, doc.HasVersionType);
+                       if ("TREE".equals(type) && relation != null) {
+                               if (versionRel.equals(doc.HasOlderVersion)) {
+                                       graph.deny(document2, graph.getInverse(relation));
+                                       graph.claim(document1,relation,document2);
+                               } else {
+                                       graph.deny(document1, graph.getInverse(relation));
+                                       graph.claim(document2,relation,document1);
+                               }
+                       }
+                       
+               }
+       }
+       
+       /**
+        * Unlinks document version relationship between two documents. 
+        * @param graph
+        * @param document1
+        * @param document2
+        * @param versionRel
+        * @throws DatabaseException
+        */
+       public static void unsetVersion(WriteGraph graph, Resource document1, Resource document2, Resource versionRel) throws DatabaseException {
+               DocumentResource doc = DocumentResource.getInstance(graph);
+               if (!versionRel.equals(doc.HasNewerVersion) && !(versionRel.equals(doc.HasOlderVersion)))
+                       throw new IllegalArgumentException("Unknow version relation + " + graph.getPossibleURI(versionRel));
+               
+               graph.deny(document1, versionRel,document2);
+               Resource lib = getLib(graph, document1);
+               if (lib != null) {
+                       Resource relation = graph.getPossibleObject(lib, doc.HasLibraryRelation);
+                       String type= graph.getPossibleRelatedValue(lib, doc.HasVersionType);
+                       if ("TREE".equals(type) && relation != null) {
+                               if (versionRel.equals(doc.HasOlderVersion)) {
+                                       graph.deny(document1, relation);
+                                       graph.claim(lib,relation,document2);
+                                       FileDocumentUtil.createUniqueName(graph, document2);
+                               } else {
+                                       graph.deny(document1, graph.getInverse(relation));
+                                       graph.claim(lib,relation,document1);
+                                       FileDocumentUtil.createUniqueName(graph, document1);
+                               }
+                       }
+                       
+               }
+       }
+       
+       private static Resource getLib(ReadGraph graph, Resource document) throws DatabaseException {
+               Layer0 l0 = Layer0.getInstance(graph);
+               DocumentResource doc = DocumentResource.getInstance(graph);
+               Resource r = document;
+               while (true) {
+                       Resource lib = graph.getPossibleObject(r, l0.PartOf);
+                       if (lib == null)
+                               lib = graph.getPossibleObject(r, l0.IsOwnedBy);
+                       if (lib == null)
+                               lib =  graph.getPossibleObject(r, l0.IsDependencyOf);
+                       if (lib == null)
+                               return null;
+                       if (!graph.isInstanceOf(lib, doc.Document))
+                               return lib;
+                       r = lib;
+               }
+       }
+}
diff --git a/bundles/org.simantics.document/src/org/simantics/document/FileDocumentUtil.java b/bundles/org.simantics.document/src/org/simantics/document/FileDocumentUtil.java
new file mode 100644 (file)
index 0000000..cab7e9a
--- /dev/null
@@ -0,0 +1,474 @@
+package org.simantics.document;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.simantics.Simantics;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.ReadRequest;
+import org.simantics.db.common.request.WriteRequest;
+import org.simantics.db.common.request.WriteResultRequest;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.graphfile.ontology.GraphFileResource;
+import org.simantics.graphfile.util.GraphFileUtil;
+import org.simantics.layer0.Layer0;
+import org.simantics.utils.ui.ExceptionUtils;
+
+/**
+ * 
+ * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
+ *
+ */
+public class FileDocumentUtil {
+       
+       /**
+        * Imports file, sets its L0.hasName, and and adds it to a library
+        * 
+        * Note: if library relation is L0.ConsistsOf, L0.HasName is set to next available unique name.
+        * 
+        * @param fileName
+        * @param lib
+        * @param rel
+        * @throws DatabaseException 
+        */
+       public static Resource importFile(final String fileName, final Resource lib, final Resource rel) throws DatabaseException {
+               return Simantics.getSession().syncRequest(new WriteResultRequest<Resource>() {
+                       @Override
+                       public Resource perform(WriteGraph graph) throws DatabaseException {
+                               return importFile(graph, fileName,lib,rel);
+                       }
+               });
+                       
+               
+       }
+       
+       public static void importFileAsync(final String fileName, final Resource lib, final Resource rel)  {
+               Simantics.getSession().asyncRequest(new WriteRequest() {
+                       
+                       @Override
+                       public void perform(WriteGraph graph) throws DatabaseException {
+                                importFile(graph, fileName,lib,rel);
+                               
+                       }
+               },new org.simantics.utils.datastructures.Callback<DatabaseException>() {
+                       
+                       @Override
+                       public void run(DatabaseException parameter) {
+                               if (parameter != null)
+                                       ExceptionUtils.logAndShowError("Cannot import file " + fileName, parameter);
+                               
+                       }
+               });
+                       
+               
+       }
+       
+       /**
+        * Imports file, sets its L0.HasName, and and adds it to a library
+        * 
+        * Note: if library relation is L0.ConsistsOf, L0.HasName is set to next available unique name.
+        * 
+        * @param graph
+        * @param fileName
+        * @param lib
+        * @param rel
+        * @throws DatabaseException
+        */
+       public static Resource importFile(WriteGraph graph, String fileName, Resource lib, Resource rel) throws DatabaseException{
+               Layer0 l0 = Layer0.getInstance(graph);
+               Resource fileResource = importFile(graph, fileName);
+               graph.claim(lib, rel, fileResource);
+               File file = new File(fileName);
+               String name = file.getName();
+               graph.claimLiteral(fileResource, l0.HasName, name);
+               setUniqueName(graph, fileResource, lib, rel);
+               return fileResource;
+       }
+       
+       public static Resource importFileWithName(WriteGraph graph, String fileName) throws DatabaseException{
+               Layer0 l0 = Layer0.getInstance(graph);
+               Resource fileResource = importFile(graph, fileName);
+               File file = new File(fileName);
+               String name = file.getName();
+               graph.claimLiteral(fileResource, l0.HasName, name);
+               return fileResource;
+       }
+       
+       /**
+        * Imports folder of documents recursively (including all sub folders). 
+        * @param graph
+        * @param folderName  Name of imported folder
+        * @param lib         Library, where imported folder is attached.
+        * @param folderType  Type of folders
+        * @param relation    Relation used to create file/folder hierarchy
+        * @return            the imported folder
+        * @throws DatabaseException
+        */
+       public static Resource importFolderWithName(WriteGraph graph, String folderName, Resource lib, Resource folderType, Resource relation, IProgressMonitor monitor) throws Exception{
+               Resource folderRes = importFolderWithName(graph, folderName, folderType, relation,monitor);
+               graph.claim(lib, relation, folderRes);
+               FileDocumentUtil.createUniqueName(graph, folderRes);
+               return folderRes;
+       }
+       
+       /**
+        * Imports folder of documents recursively (including all sub folders). 
+        * @param graph
+        * @param folderName  Name of imported folder
+        * @param folderType  Type of folders
+        * @param relation    Relation used to create file/folder hierarchy
+        * @param monitor     ProgessMonitor or null
+        * @return            the imported folder
+        * @throws DatabaseException
+        */
+       public static Resource importFolderWithName(WriteGraph graph, String folderName, Resource folderType, Resource relation, IProgressMonitor monitor) throws Exception{
+               Layer0 l0 = Layer0.getInstance(graph);
+               File folder = new File(folderName);
+               Resource rootFolderRes = graph.newResource();
+               graph.claim(rootFolderRes, l0.InstanceOf, folderType);
+               graph.claimLiteral(rootFolderRes, l0.HasName, folder.getName());
+               importFolder(graph, folder, rootFolderRes, relation, monitor);
+               return rootFolderRes;
+       }
+       
+       /**
+        * Imports folder of documents recursively (including all sub folders).
+        * @param graph
+        * @param folder            Imported folder
+        * @param folderResource    Resource folder matching file system folder
+        * @param relation          Relation used to create file/folder hierarchy
+        * @throws DatabaseException
+        */
+       public static void importFolder(WriteGraph graph, File folder, Resource folderResource, Resource relation, IProgressMonitor monitor) throws Exception{
+               if (monitor != null) {
+                       int count = _countFiles(folder);
+                       monitor.beginTask("Import files", count);
+               }
+               _importFolder(graph, folder, folderResource, relation, monitor);
+               if (monitor != null)
+                       monitor.done();
+       }
+       
+       private static void _importFolder(WriteGraph graph, File folder, Resource folderResource, Resource relation, IProgressMonitor monitor) throws Exception{
+               Layer0 l0 = Layer0.getInstance(graph);
+               File files[] = folder.listFiles();
+               for (File f : files) {
+                       if (f.isDirectory()) {
+                               Resource newFolderRes = graph.newResource();
+                               graph.claim(newFolderRes, l0.InstanceOf, graph.getSingleType(folderResource));
+                               graph.claim(folderResource, relation, newFolderRes);
+                               graph.claimLiteral(newFolderRes, l0.HasName, f.getName());
+                               _importFolder(graph, f, newFolderRes, relation,monitor);
+                       } else {
+                               Resource fileRes = null;
+                               if (isUrl(f)) {
+                               } else {
+                                   fileRes = importURL(graph, f);
+                                       fileRes = importFileWithName(graph, f.getAbsolutePath());
+                               }
+                               graph.claim(folderResource, relation, fileRes);
+                               if (monitor != null)
+                                       monitor.worked(1);
+                       }
+               }
+       }
+       
+       private static int _countFiles(File folder) {
+               
+               int count = 0;
+               File files[] = folder.listFiles();
+               for (File f : files) {
+                       if (f.isDirectory()) {
+                               count += _countFiles(f);
+                       } else {
+                               count++;
+                       }
+               }
+               return count;
+       }
+       
+       
+       public static void createUniqueName(WriteGraph graph, Resource document) throws DatabaseException {
+               Layer0 l0 = Layer0.getInstance(graph);
+               Resource lib = graph.getPossibleObject(document, l0.PartOf);
+               if (lib == null)
+                       return;
+               setUniqueName(graph, document, lib, l0.ConsistsOf);
+       }
+       
+       public static void setUniqueName(WriteGraph graph, Resource res, Resource lib, Resource rel) throws DatabaseException{
+               Layer0 l0 = Layer0.getInstance(graph);
+               Set<String> names = new HashSet<String>();
+               for (Resource r : graph.getObjects(lib, rel)) {
+                       if (r.equals(res))
+                               continue;
+                       names.add((String)graph.getRelatedValue(r, l0.HasName));
+               }
+               String name = graph.getRelatedValue(res, l0.HasName);
+               if (!names.contains(name))
+                       return;
+               int i = 1;
+               while (true) {
+                       String proposal = name +" (" + i +")";
+                       if (!names.contains(proposal)) {
+                               graph.claimLiteral(res, l0.HasName, proposal);
+                               return;
+                       }
+                       i++;
+               }
+               
+       }
+       
+       /**
+        * Imports a file
+        * 
+        * @param graph
+        * @param fileName
+        * @return
+        * @throws DatabaseException
+        */
+       public static Resource importFile(WriteGraph graph, String fileName) throws DatabaseException{
+               Layer0 l0 = Layer0.getInstance(graph);
+               DocumentResource doc = DocumentResource.getInstance(graph);
+               
+               Resource fileResource = graph.newResource();
+               graph.claim(fileResource, l0.InstanceOf, doc.FileDocument);
+               try {
+                       GraphFileUtil.toGraph(graph,fileName, fileResource);
+                       
+               } catch (IOException e) {
+                       throw new DatabaseException(e);
+               }
+               return fileResource;
+               
+       }
+       
+       /**
+        * Exports graph folder recursively to file system. 
+        * @param graph
+        * @param folderResource
+        * @param folder
+        * @param relation
+        * @throws DatabaseException
+        */
+       public static void exportDocumentFolder(final Resource folderResource, final File folder, final Resource relation, boolean useResourceNames, final IProgressMonitor monitor) throws Exception{
+               Simantics.getSession().syncRequest(new ReadRequest() {
+                       
+                       @Override
+                       public void run(ReadGraph graph) throws DatabaseException {
+                               try {
+                                       exportDocumentFolder(graph, folderResource, folder, relation, useResourceNames, monitor);
+                               } catch (Exception e) {
+                                       throw new DatabaseException(e);
+                               }
+                               
+                       }
+               });
+       }
+       
+       
+       /**
+        * Exports graph folder recursively to file system. 
+        * @param graph
+        * @param folderResource
+        * @param folder
+        * @param relation
+        * @throws DatabaseException
+        */
+       public static void exportDocumentFolder(ReadGraph graph, Resource folderResource, File folder, Resource relation, boolean useResourceNames, IProgressMonitor monitor) throws Exception{
+               Layer0 l0 = Layer0.getInstance(graph);
+               DocumentResource doc = DocumentResource.getInstance(graph);
+               GraphFileResource gf = GraphFileResource.getInstance(graph);
+               Set<String> names = new HashSet<String>();
+               Collection<Resource> folderType = graph.getPrincipalTypes(folderResource);
+               for (Resource r : graph.getObjects(folderResource, relation)) {
+                       if (graph.isInstanceOf(r, doc.Document)) {
+                               String name = null;
+                               boolean canExport = false;
+                               if (graph.isInstanceOf(r, doc.FileDocument)) {
+                                       name = graph.getRelatedValue(r, useResourceNames ? gf.HasResourceName : l0.HasName);
+                                       canExport = true;
+                               } else if (graph.isInstanceOf(r, doc.UrlDocument)) {
+                                       name = graph.getRelatedValue(r, l0.HasName) +".url";
+                                       canExport = true;
+                               }
+                               if (canExport) {
+                                       name = resolveName(folder, name, names, true);
+                                       File file = new File(folder.getAbsolutePath()+"/"+name);
+                                       if (graph.isInstanceOf(r, doc.FileDocument)) {
+                                               GraphFileUtil.writeDataToFile(graph,r, file);
+                                       } else if (graph.isInstanceOf(r, doc.UrlDocument)) {
+                                               String url = graph.getRelatedValue(r, doc.HasUrl);
+                                               String n = graph.getRelatedValue(r, l0.HasName);
+                                               exportUrl(file, n, url);
+                                       }
+                                       if (monitor != null)
+                                               monitor.worked(1);
+                               }
+                               
+                       } else {
+                               Collection<Resource> type = graph.getPrincipalTypes(r);
+                               if (type.size() == folderType.size() && folderType.containsAll(type)) {
+                                       String name = graph.getRelatedValue(r, l0.HasName);
+                                       name = resolveName(folder, name, names, false);
+                                       File subFolder = new File(folder.getAbsolutePath()+"/"+name);
+                                       if (!subFolder.exists()) {
+                                               if (!subFolder.mkdir()) {
+                                                       // TODO : error.
+                                                       continue;
+                                               }
+                                       }
+                                       exportDocumentFolder(graph, r, subFolder, relation, useResourceNames, monitor);
+                               }
+                       }
+               }
+       }
+       
+       /**
+        * Print URL to a file (Windows specific format?)
+        * @param toFile
+        * @param url
+        * @throws DatabaseException
+        */
+       private static void exportUrl(File toFile, String name, String url) throws Exception{
+               PrintStream os = new PrintStream(toFile,"UTF-8");
+               os.println("[InternetShortcut]");
+               os.println("URL="+url);
+               os.println("name="+name);
+               os.flush();
+               os.close();
+       }
+       
+       public static Resource importURL(WriteGraph graph, File file) throws Exception{
+               String s = null;
+               String url = null;
+               String name = null;
+               BufferedReader is = null;
+               try {
+               is = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
+               while ((s = is.readLine()) != null) {
+                       if (s.startsWith("URL=")) {
+                               url = s.substring(4);
+                       } else if (s.startsWith("name=")) {
+                               name = s.substring(5);
+                       }
+               }
+               } finally {
+                   if (is != null)
+                       is.close();
+               }
+               
+               if (url == null)
+                       return null;
+               
+               Layer0 l0 = Layer0.getInstance(graph);
+               DocumentResource doc = DocumentResource.getInstance(graph);
+               
+               Resource fileResource = graph.newResource();
+               graph.claim(fileResource, l0.InstanceOf, doc.UrlDocument);
+               if (name == null) {
+                       name = file.getName();
+                       name = unescape(name);
+                       name = name.substring(0,name.length()-4);
+               }
+               graph.claimLiteral(fileResource, l0.HasName, name);
+               graph.claimLiteral(fileResource, doc.HasUrl, url);
+               return fileResource;
+       }
+       
+       private static boolean isUrl(File file) throws Exception{
+               return (file.getAbsolutePath().endsWith(".url"));
+       }
+       
+       private static final char ESCAPE = '%';
+       
+       private static String escape(String s) {
+               
+               int len = s.length();
+               StringBuilder sb = new StringBuilder(len);
+               for (int i = 0; i < len; i++) {
+                   char ch = s.charAt(i);
+                   if (ch < ' ' || ch >= 0x7F || ch == '/'  || ch == '\\' || ch == ':' || ch == ESCAPE) {
+                       sb.append(ESCAPE);
+                       if (ch < 0x10) {
+                           sb.append('0');
+                       }
+                       sb.append(Integer.toHexString(ch));
+                   } else {
+                       sb.append(ch);
+                   }
+               }
+               return sb.toString();
+       }
+       
+       private static String unescape(String s) {
+               int len = s.length();
+               StringBuilder sb = new StringBuilder(len);
+               for (int i = 0; i < len; i++) {
+                   char ch = s.charAt(i);
+                   if (ch == ESCAPE) {
+                       String num = "0x";
+                       num += s.charAt(++i);
+                       num += s.charAt(++i);
+                       ch = (char)Integer.decode(num).intValue();
+                   }
+                   sb.append(ch);
+               }
+               return sb.toString();
+               
+       }
+       
+       private static String resolveName(File parentFolder, String proposal, Set<String> used, boolean file) {
+               String current = escape(proposal);
+               int i = 0;
+               if (file) {
+                       while (true) {
+                               i++;
+                               if (used.contains(current)) {
+                                       current = createFileName(proposal, i);
+                               } else {
+                                       File subFile = new File(parentFolder.getAbsolutePath()+"/"+current);
+                                       if (!subFile.exists())
+                                               break;
+                                       if (subFile.exists() && subFile.isFile() && subFile.canWrite()) {
+                                               break;
+                                       }
+                               }
+                       }
+               } else {
+                       while (true) {
+                               i++;
+                               if (used.contains(current)) {
+                                       current = proposal+i;
+                               } else {
+                                       File subFolder = new File(parentFolder.getAbsolutePath()+"/"+current);
+                                       if (!subFolder.exists())
+                                               break;
+                                       if (subFolder.exists() && subFolder.isDirectory()) {
+                                               break;
+                                       }
+                               }
+                       }
+               }
+               used.add(current);
+               return current;
+       }
+       
+       private static String createFileName(String original, int i) {
+               int extIndex = original.lastIndexOf(".");
+               if (extIndex == -1)
+                       return original+i;
+               return original.substring(0,extIndex) + i + original.substring(extIndex);
+       }
+
+}
diff --git a/bundles/org.simantics.document/src/org/simantics/document/ImportDocument.java b/bundles/org.simantics.document/src/org/simantics/document/ImportDocument.java
new file mode 100644 (file)
index 0000000..c8d3ee3
--- /dev/null
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.document;
+
+import java.io.File;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.FileDialog;
+import org.simantics.DatabaseJob;
+import org.simantics.Simantics;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.WriteRequest;
+import org.simantics.db.exception.DatabaseException;
+
+/**
+ * Action for importing files as documents.
+ * 
+ * @author Marko Luukkainen <marko.luukkainen@vtt.fi>
+ *
+ */
+public class ImportDocument extends AddDocumentAction {
+       
+
+       public ImportDocument(ReadGraph graph, String relationUri) throws DatabaseException {
+               super(graph, relationUri);
+       }
+
+       @Override
+       public Runnable create(Object target) {
+               if(!(target instanceof Resource))
+                       return null;
+               final Resource resource = (Resource)target;
+               return new Runnable() {
+                       
+                       @Override
+                       public void run() {
+                               FileDialog dialog = new FileDialog(Display.getCurrent().getActiveShell(),SWT.OPEN | SWT.MULTI);
+                               // TODO : is there any way to read file/executable bindings from OS?
+                               //        if is, use those extensions to filter this list.
+                               //        note: in windows using "reg query ..." to read bindings form registry would work.
+                               // Note : If the above mentioned filtering is implemented it should be made optional / configurable.
+                               dialog.setFilterExtensions(new String[]{"*.*"});
+                               if (dialog.open() == null) return;
+
+                               String filterPath = dialog.getFilterPath();
+                               String[] filenames = dialog.getFileNames();
+                               
+                               ImportJob job = new ImportJob(filenames.length > 1 ? "Import files" : "Import file", resource, filterPath, filenames);
+                               job.setUser(true);
+                               job.schedule();
+                       }
+               };
+       }
+       
+       private class ImportJob extends DatabaseJob {
+
+               public ImportJob(String name, Resource resource, String path, String[] filenames) {
+                       super(name);
+                       this.resource = resource;
+                       this.path = path;
+                       this.filenames = filenames;
+               }
+
+               Resource resource;
+               String path;
+               String[] filenames;
+
+               @Override
+               protected IStatus run(final IProgressMonitor monitor) {
+                       monitor.beginTask("Importing...", filenames.length);
+                       try {
+                               Simantics.getSession().syncRequest(new WriteRequest() {
+                                       @Override
+                                       public void perform(WriteGraph graph) throws DatabaseException {
+                                               try {
+                                                       graph.markUndoPoint();
+                                                   for (String filename : filenames) {
+                                                       File f = new File(path, filename);
+                                                       Resource newDoc = FileDocumentUtil.importFileWithName(graph, f.getAbsolutePath());
+                                                       linkDocument(graph, resource, newDoc);
+                                                           monitor.worked(1);
+                                                   }
+                                               } catch (Exception e) {
+                                                       throw new DatabaseException(e);
+                                               }
+                                       }
+                               });
+                               return new Status(IStatus.OK, Activator.PLUGIN_ID, "Import succesful.");
+                       } catch (DatabaseException e) {
+                               return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Import failed.", e);
+                       } finally {
+                               monitor.done();
+                       }
+               }
+       }
+       
+}