Merge "InputStream returns -1 on EOF instead of throwing IOException"
[simantics/platform.git] / bundles / org.simantics.document.ui / src / org / simantics / document / ui / graphfile / FileDocumentUtil.java
1 package org.simantics.document.ui.graphfile;\r
2 \r
3 import java.io.BufferedReader;\r
4 import java.io.File;\r
5 import java.io.FileInputStream;\r
6 import java.io.IOException;\r
7 import java.io.InputStreamReader;\r
8 import java.io.PrintStream;\r
9 import java.util.Collection;\r
10 import java.util.HashSet;\r
11 import java.util.Set;\r
12 \r
13 import org.eclipse.core.runtime.IProgressMonitor;\r
14 import org.simantics.Simantics;\r
15 import org.simantics.db.ReadGraph;\r
16 import org.simantics.db.Resource;\r
17 import org.simantics.db.WriteGraph;\r
18 import org.simantics.db.common.request.ReadRequest;\r
19 import org.simantics.db.common.request.WriteRequest;\r
20 import org.simantics.db.common.request.WriteResultRequest;\r
21 import org.simantics.db.exception.DatabaseException;\r
22 import org.simantics.document.DocumentResource;\r
23 import org.simantics.graphfile.ontology.GraphFileResource;\r
24 import org.simantics.graphfile.util.GraphFileUtil;\r
25 import org.simantics.layer0.Layer0;\r
26 import org.simantics.utils.ui.ExceptionUtils;\r
27 \r
28 /**\r
29  * \r
30  * @author Marko Luukkainen <marko.luukkainen@vtt.fi>\r
31  *\r
32  */\r
33 public class FileDocumentUtil {\r
34         \r
35         /**\r
36          * Imports file, sets its L0.hasName, and and adds it to a library\r
37          * \r
38          * Note: if library relation is L0.ConsistsOf, L0.HasName is set to next available unique name.\r
39          * \r
40          * @param fileName\r
41          * @param lib\r
42          * @param rel\r
43          * @throws DatabaseException \r
44          */\r
45         public static Resource importFile(final String fileName, final Resource lib, final Resource rel) throws DatabaseException {\r
46                 return Simantics.getSession().syncRequest(new WriteResultRequest<Resource>() {\r
47                         @Override\r
48                         public Resource perform(WriteGraph graph) throws DatabaseException {\r
49                                 return importFile(graph, fileName,lib,rel);\r
50                         }\r
51                 });\r
52                         \r
53                 \r
54         }\r
55         \r
56         public static void importFileAsync(final String fileName, final Resource lib, final Resource rel)  {\r
57                 Simantics.getSession().asyncRequest(new WriteRequest() {\r
58                         \r
59                         @Override\r
60                         public void perform(WriteGraph graph) throws DatabaseException {\r
61                                  importFile(graph, fileName,lib,rel);\r
62                                 \r
63                         }\r
64                 },new org.simantics.utils.datastructures.Callback<DatabaseException>() {\r
65                         \r
66                         @Override\r
67                         public void run(DatabaseException parameter) {\r
68                                 if (parameter != null)\r
69                                         ExceptionUtils.logAndShowError("Cannot import file " + fileName, parameter);\r
70                                 \r
71                         }\r
72                 });\r
73                         \r
74                 \r
75         }\r
76         \r
77         /**\r
78          * Imports file, sets its L0.HasName, and and adds it to a library\r
79          * \r
80          * Note: if library relation is L0.ConsistsOf, L0.HasName is set to next available unique name.\r
81          * \r
82          * @param graph\r
83          * @param fileName\r
84          * @param lib\r
85          * @param rel\r
86          * @throws DatabaseException\r
87          */\r
88         public static Resource importFile(WriteGraph graph, String fileName, Resource lib, Resource rel) throws DatabaseException{\r
89                 Layer0 l0 = Layer0.getInstance(graph);\r
90                 Resource fileResource = importFile(graph, fileName);\r
91                 graph.claim(lib, rel, fileResource);\r
92                 File file = new File(fileName);\r
93                 String name = file.getName();\r
94                 graph.claimLiteral(fileResource, l0.HasName, name);\r
95                 setUniqueName(graph, fileResource, lib, rel);\r
96                 return fileResource;\r
97         }\r
98         \r
99         public static Resource importFileWithName(WriteGraph graph, String fileName) throws DatabaseException{\r
100                 Layer0 l0 = Layer0.getInstance(graph);\r
101                 Resource fileResource = importFile(graph, fileName);\r
102                 File file = new File(fileName);\r
103                 String name = file.getName();\r
104                 graph.claimLiteral(fileResource, l0.HasName, name);\r
105                 return fileResource;\r
106         }\r
107         \r
108         /**\r
109          * Imports folder of documents recursively (including all sub folders). \r
110          * @param graph\r
111          * @param folderName  Name of imported folder\r
112          * @param lib         Library, where imported folder is attached.\r
113          * @param folderType  Type of folders\r
114          * @param relation    Relation used to create file/folder hierarchy\r
115          * @return            the imported folder\r
116          * @throws DatabaseException\r
117          */\r
118         public static Resource importFolderWithName(WriteGraph graph, String folderName, Resource lib, Resource folderType, Resource relation, IProgressMonitor monitor) throws Exception{\r
119                 Resource folderRes = importFolderWithName(graph, folderName, folderType, relation,monitor);\r
120                 graph.claim(lib, relation, folderRes);\r
121                 FileDocumentUtil.createUniqueName(graph, folderRes);\r
122                 return folderRes;\r
123         }\r
124         \r
125         /**\r
126          * Imports folder of documents recursively (including all sub folders). \r
127          * @param graph\r
128          * @param folderName  Name of imported folder\r
129          * @param folderType  Type of folders\r
130          * @param relation    Relation used to create file/folder hierarchy\r
131          * @param monitor     ProgessMonitor or null\r
132          * @return            the imported folder\r
133          * @throws DatabaseException\r
134          */\r
135         public static Resource importFolderWithName(WriteGraph graph, String folderName, Resource folderType, Resource relation, IProgressMonitor monitor) throws Exception{\r
136                 Layer0 l0 = Layer0.getInstance(graph);\r
137                 File folder = new File(folderName);\r
138                 Resource rootFolderRes = graph.newResource();\r
139                 graph.claim(rootFolderRes, l0.InstanceOf, folderType);\r
140                 graph.claimLiteral(rootFolderRes, l0.HasName, folder.getName());\r
141                 importFolder(graph, folder, rootFolderRes, relation, monitor);\r
142                 return rootFolderRes;\r
143         }\r
144         \r
145         /**\r
146          * Imports folder of documents recursively (including all sub folders).\r
147          * @param graph\r
148          * @param folder            Imported folder\r
149          * @param folderResource    Resource folder matching file system folder\r
150          * @param relation          Relation used to create file/folder hierarchy\r
151          * @throws DatabaseException\r
152          */\r
153         public static void importFolder(WriteGraph graph, File folder, Resource folderResource, Resource relation, IProgressMonitor monitor) throws Exception{\r
154                 if (monitor != null) {\r
155                         int count = _countFiles(folder);\r
156                         monitor.beginTask("Import files", count);\r
157                 }\r
158                 _importFolder(graph, folder, folderResource, relation, monitor);\r
159                 if (monitor != null)\r
160                         monitor.done();\r
161         }\r
162         \r
163         private static void _importFolder(WriteGraph graph, File folder, Resource folderResource, Resource relation, IProgressMonitor monitor) throws Exception{\r
164                 Layer0 l0 = Layer0.getInstance(graph);\r
165                 File files[] = folder.listFiles();\r
166                 for (File f : files) {\r
167                         if (f.isDirectory()) {\r
168                                 Resource newFolderRes = graph.newResource();\r
169                                 graph.claim(newFolderRes, l0.InstanceOf, graph.getSingleType(folderResource));\r
170                                 graph.claim(folderResource, relation, newFolderRes);\r
171                                 graph.claimLiteral(newFolderRes, l0.HasName, f.getName());\r
172                                 _importFolder(graph, f, newFolderRes, relation,monitor);\r
173                         } else {\r
174                                 Resource fileRes = null;\r
175                                 if (isUrl(f)) {\r
176                                 } else {\r
177                                     fileRes = importURL(graph, f);\r
178                                         fileRes = importFileWithName(graph, f.getAbsolutePath());\r
179                                 }\r
180                                 graph.claim(folderResource, relation, fileRes);\r
181                                 if (monitor != null)\r
182                                         monitor.worked(1);\r
183                         }\r
184                 }\r
185         }\r
186         \r
187         private static int _countFiles(File folder) {\r
188                 \r
189                 int count = 0;\r
190                 File files[] = folder.listFiles();\r
191                 for (File f : files) {\r
192                         if (f.isDirectory()) {\r
193                                 count += _countFiles(f);\r
194                         } else {\r
195                                 count++;\r
196                         }\r
197                 }\r
198                 return count;\r
199         }\r
200         \r
201         \r
202         public static void createUniqueName(WriteGraph graph, Resource document) throws DatabaseException {\r
203                 Layer0 l0 = Layer0.getInstance(graph);\r
204                 Resource lib = graph.getPossibleObject(document, l0.PartOf);\r
205                 if (lib == null)\r
206                         return;\r
207                 setUniqueName(graph, document, lib, l0.ConsistsOf);\r
208         }\r
209         \r
210         public static void setUniqueName(WriteGraph graph, Resource res, Resource lib, Resource rel) throws DatabaseException{\r
211                 Layer0 l0 = Layer0.getInstance(graph);\r
212                 Set<String> names = new HashSet<String>();\r
213                 for (Resource r : graph.getObjects(lib, rel)) {\r
214                         if (r.equals(res))\r
215                                 continue;\r
216                         names.add((String)graph.getRelatedValue(r, l0.HasName));\r
217                 }\r
218                 String name = graph.getRelatedValue(res, l0.HasName);\r
219                 if (!names.contains(name))\r
220                         return;\r
221                 int i = 1;\r
222                 while (true) {\r
223                         String proposal = name +" (" + i +")";\r
224                         if (!names.contains(proposal)) {\r
225                                 graph.claimLiteral(res, l0.HasName, proposal);\r
226                                 return;\r
227                         }\r
228                         i++;\r
229                 }\r
230                 \r
231         }\r
232         \r
233         /**\r
234          * Imports a file\r
235          * \r
236          * @param graph\r
237          * @param fileName\r
238          * @return\r
239          * @throws DatabaseException\r
240          */\r
241         public static Resource importFile(WriteGraph graph, String fileName) throws DatabaseException{\r
242                 Layer0 l0 = Layer0.getInstance(graph);\r
243                 DocumentResource doc = DocumentResource.getInstance(graph);\r
244                 \r
245                 Resource fileResource = graph.newResource();\r
246                 graph.claim(fileResource, l0.InstanceOf, doc.FileDocument);\r
247                 try {\r
248                         GraphFileUtil.toGraph(graph,fileName, fileResource);\r
249                         \r
250                 } catch (IOException e) {\r
251                         throw new DatabaseException(e);\r
252                 }\r
253                 return fileResource;\r
254                 \r
255         }\r
256         \r
257         /**\r
258          * Exports graph folder recursively to file system. \r
259          * @param graph\r
260          * @param folderResource\r
261          * @param folder\r
262          * @param relation\r
263          * @throws DatabaseException\r
264          */\r
265         public static void exportDocumentFolder(final Resource folderResource, final File folder, final Resource relation, final IProgressMonitor monitor) throws Exception{\r
266                 Simantics.getSession().syncRequest(new ReadRequest() {\r
267                         \r
268                         @Override\r
269                         public void run(ReadGraph graph) throws DatabaseException {\r
270                                 try {\r
271                                         exportDocumentFolder(graph, folderResource, folder, relation, monitor);\r
272                                 } catch (Exception e) {\r
273                                         throw new DatabaseException(e);\r
274                                 }\r
275                                 \r
276                         }\r
277                 });\r
278         }\r
279         \r
280         \r
281         /**\r
282          * Exports graph folder recursively to file system. \r
283          * @param graph\r
284          * @param folderResource\r
285          * @param folder\r
286          * @param relation\r
287          * @throws DatabaseException\r
288          */\r
289         public static void exportDocumentFolder(ReadGraph graph, Resource folderResource, File folder, Resource relation, IProgressMonitor monitor) throws Exception{\r
290                 Layer0 l0 = Layer0.getInstance(graph);\r
291                 DocumentResource doc = DocumentResource.getInstance(graph);\r
292                 GraphFileResource gf = GraphFileResource.getInstance(graph);\r
293                 Set<String> names = new HashSet<String>();\r
294                 Collection<Resource> folderType = graph.getPrincipalTypes(folderResource);\r
295                 for (Resource r : graph.getObjects(folderResource, relation)) {\r
296                         if (graph.isInstanceOf(r, doc.Document)) {\r
297                                 String name = null;\r
298                                 boolean canExport = false;\r
299                                 if (graph.isInstanceOf(r, doc.FileDocument)) {\r
300                                         name = graph.getRelatedValue(r, gf.HasResourceName);\r
301                                         canExport = true;\r
302                                 } else if (graph.isInstanceOf(r, doc.UrlDocument)) {\r
303                                         name = graph.getRelatedValue(r, l0.HasName) +".url";\r
304                                         canExport = true;\r
305                                 }\r
306                                 if (canExport) {\r
307                                         name = resolveName(folder, name, names, true);\r
308                                         File file = new File(folder.getAbsolutePath()+"/"+name);\r
309                                         if (graph.isInstanceOf(r, doc.FileDocument)) {\r
310                                                 GraphFileUtil.writeDataToFile(graph,r, file);\r
311                                         } else if (graph.isInstanceOf(r, doc.UrlDocument)) {\r
312                                                 String url = graph.getRelatedValue(r, doc.HasUrl);\r
313                                                 String n = graph.getRelatedValue(r, l0.HasName);\r
314                                                 exportUrl(file, n, url);\r
315                                         }\r
316                                         if (monitor != null)\r
317                                                 monitor.worked(1);\r
318                                 }\r
319                                 \r
320                         } else {\r
321                                 Collection<Resource> type = graph.getPrincipalTypes(r);\r
322                                 if (type.size() == folderType.size() && folderType.containsAll(type)) {\r
323                                         String name = graph.getRelatedValue(r, l0.HasName);\r
324                                         name = resolveName(folder, name, names, false);\r
325                                         File subFolder = new File(folder.getAbsolutePath()+"/"+name);\r
326                                         if (!subFolder.exists()) {\r
327                                                 if (!subFolder.mkdir()) {\r
328                                                         // TODO : error.\r
329                                                         continue;\r
330                                                 }\r
331                                         }\r
332                                         exportDocumentFolder(graph, r, subFolder, relation,monitor);\r
333                                 }\r
334                         }\r
335                 }\r
336         }\r
337         \r
338         /**\r
339          * Print URL to a file (Windows specific format?)\r
340          * @param toFile\r
341          * @param url\r
342          * @throws DatabaseException\r
343          */\r
344         private static void exportUrl(File toFile, String name, String url) throws Exception{\r
345                 PrintStream os = new PrintStream(toFile,"UTF-8");\r
346                 os.println("[InternetShortcut]");\r
347                 os.println("URL="+url);\r
348                 os.println("name="+name);\r
349                 os.flush();\r
350                 os.close();\r
351         }\r
352         \r
353         public static Resource importURL(WriteGraph graph, File file) throws Exception{\r
354                 String s = null;\r
355                 String url = null;\r
356                 String name = null;\r
357                 BufferedReader is = null;\r
358                 try {\r
359                 is = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));\r
360                 while ((s = is.readLine()) != null) {\r
361                         if (s.startsWith("URL=")) {\r
362                                 url = s.substring(4);\r
363                         } else if (s.startsWith("name=")) {\r
364                                 name = s.substring(5);\r
365                         }\r
366                 }\r
367                 } finally {\r
368                     if (is != null)\r
369                         is.close();\r
370                 }\r
371                 \r
372                 if (url == null)\r
373                         return null;\r
374                 \r
375                 Layer0 l0 = Layer0.getInstance(graph);\r
376                 DocumentResource doc = DocumentResource.getInstance(graph);\r
377                 \r
378                 Resource fileResource = graph.newResource();\r
379                 graph.claim(fileResource, l0.InstanceOf, doc.UrlDocument);\r
380                 if (name == null) {\r
381                         name = file.getName();\r
382                         name = unescape(name);\r
383                         name = name.substring(0,name.length()-4);\r
384                 }\r
385                 graph.claimLiteral(fileResource, l0.HasName, name);\r
386                 graph.claimLiteral(fileResource, doc.HasUrl, url);\r
387                 return fileResource;\r
388         }\r
389         \r
390         private static boolean isUrl(File file) throws Exception{\r
391                 return (file.getAbsolutePath().endsWith(".url"));\r
392         }\r
393         \r
394         private static final char ESCAPE = '%';\r
395         \r
396         private static String escape(String s) {\r
397                 \r
398                 int len = s.length();\r
399                 StringBuilder sb = new StringBuilder(len);\r
400                 for (int i = 0; i < len; i++) {\r
401                     char ch = s.charAt(i);\r
402                     if (ch < ' ' || ch >= 0x7F || ch == '/'  || ch == '\\' || ch == ':' || ch == ESCAPE) {\r
403                         sb.append(ESCAPE);\r
404                         if (ch < 0x10) {\r
405                             sb.append('0');\r
406                         }\r
407                         sb.append(Integer.toHexString(ch));\r
408                     } else {\r
409                         sb.append(ch);\r
410                     }\r
411                 }\r
412                 return sb.toString();\r
413         }\r
414         \r
415         private static String unescape(String s) {\r
416                 int len = s.length();\r
417                 StringBuilder sb = new StringBuilder(len);\r
418                 for (int i = 0; i < len; i++) {\r
419                     char ch = s.charAt(i);\r
420                     if (ch == ESCAPE) {\r
421                         String num = "0x";\r
422                         num += s.charAt(++i);\r
423                         num += s.charAt(++i);\r
424                         ch = (char)Integer.decode(num).intValue();\r
425                     }\r
426                     sb.append(ch);\r
427                 }\r
428                 return sb.toString();\r
429                 \r
430         }\r
431         \r
432         private static String resolveName(File parentFolder, String proposal, Set<String> used, boolean file) {\r
433                 String current = escape(proposal);\r
434                 int i = 0;\r
435                 if (file) {\r
436                         while (true) {\r
437                                 i++;\r
438                                 if (used.contains(current)) {\r
439                                         current = createFileName(proposal, i);\r
440                                 } else {\r
441                                         File subFile = new File(parentFolder.getAbsolutePath()+"/"+current);\r
442                                         if (!subFile.exists())\r
443                                                 break;\r
444                                         if (subFile.exists() && subFile.isFile() && subFile.canWrite()) {\r
445                                                 break;\r
446                                         }\r
447                                 }\r
448                         }\r
449                 } else {\r
450                         while (true) {\r
451                                 i++;\r
452                                 if (used.contains(current)) {\r
453                                         current = proposal+i;\r
454                                 } else {\r
455                                         File subFolder = new File(parentFolder.getAbsolutePath()+"/"+current);\r
456                                         if (!subFolder.exists())\r
457                                                 break;\r
458                                         if (subFolder.exists() && subFolder.isDirectory()) {\r
459                                                 break;\r
460                                         }\r
461                                 }\r
462                         }\r
463                 }\r
464                 used.add(current);\r
465                 return current;\r
466         }\r
467         \r
468         private static String createFileName(String original, int i) {\r
469                 int extIndex = original.lastIndexOf(".");\r
470                 if (extIndex == -1)\r
471                         return original+i;\r
472                 return original.substring(0,extIndex) + i + original.substring(extIndex);\r
473         }\r
474 \r
475 }\r