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 * */ 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() { @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); } }, e -> { if (e != null) ExceptionUtils.logAndShowError("Cannot import file " + fileName, e); }); } /** * 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 names = new HashSet(); 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 names = new HashSet(); Collection 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 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 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); } }