]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.fileimport/src/org/simantics/fileimport/FileImportService.java
Some fileimport enhancements - Add support for Excel import
[simantics/platform.git] / bundles / org.simantics.fileimport / src / org / simantics / fileimport / FileImportService.java
index e07dfaadd5d5999af4b6ddbfe2256bb623ee062a..15612d5fbd02905b8c2672d9621ad7d69dec719b 100644 (file)
-package org.simantics.fileimport;\r
-\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
-import java.io.OutputStream;\r
-import java.nio.file.Files;\r
-import java.nio.file.Path;\r
-import java.nio.file.Paths;\r
-import java.util.ArrayList;\r
-import java.util.Collections;\r
-import java.util.HashMap;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Optional;\r
-import java.util.Properties;\r
-import java.util.function.Consumer;\r
-\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
-\r
-public class FileImportService {\r
-\r
-    public static final String DB_FILE = ".simanticsdb";\r
-\r
-    public static List<IGenericFileImport> getFileImportServices() {\r
-        ServiceReference<?>[] serviceReferences = new ServiceReference<?>[0];\r
-        try {\r
-            serviceReferences = Activator.getContext().getAllServiceReferences(IGenericFileImport.class.getName(),\r
-                    null);\r
-        } catch (InvalidSyntaxException e) {\r
-            e.printStackTrace();\r
-        }\r
-        if (serviceReferences.length == 0)\r
-            return Collections.emptyList();\r
-\r
-        List<IGenericFileImport> services = new ArrayList<>(serviceReferences.length);\r
-        for (ServiceReference<?> reference : serviceReferences) {\r
-            IGenericFileImport service = (IGenericFileImport) Activator.getContext().getService(reference);\r
-            services.add(service);\r
-        }\r
-        return services;\r
-    }\r
-\r
-    public static Map<String, String> supportedExtensionsWithFilters() {\r
-        List<IGenericFileImport> services = getFileImportServices();\r
-        Map<String, String> extensionsWithFilters = new HashMap<>();\r
-        for (IGenericFileImport service : services)\r
-            extensionsWithFilters.putAll(service.allowedExtensionsWithFilters());\r
-\r
-        return extensionsWithFilters;\r
-    }\r
-\r
-    public static void performFileImport(Path file, Optional<Consumer<Throwable>> callback) {\r
-        if (file.getFileName().toString().equals(DB_FILE))\r
-            return;\r
-        Optional<IGenericFileImport> serviceOp = findServiceForFileExtension(file);\r
-        serviceOp.ifPresent(service -> {\r
-            try {\r
-                Optional<String> resource = service.perform(file);\r
-                saveResourceForPath(file, resource);\r
-            } catch (Throwable t) {\r
-                if (callback.isPresent()) {\r
-                    callback.get().accept(t);\r
-                } else {\r
-                    t.printStackTrace();\r
-                }\r
-            }\r
-        });\r
-    }\r
-\r
-    public static void removeResourceForFile(Path file, Optional<Consumer<Throwable>> callback) {\r
-        Optional<IGenericFileImport> serviceOp = findServiceForFileExtension(file);\r
-        serviceOp.ifPresent(service -> {\r
-            try {\r
-                Optional<String> resource = getResourceForPath(file);\r
-                if (!resource.isPresent())\r
-                    return;\r
-                service.remove(resource.get());\r
-                removeResourceForPath(file);\r
-            } catch (Throwable t) {\r
-                if (callback.isPresent()) {\r
-                    callback.get().accept(t);\r
-                } else {\r
-                    t.printStackTrace();\r
-                }\r
-            }\r
-        });\r
-    }\r
-    \r
-    public static void removeFileForResource(long id, Optional<Consumer<Throwable>> callback) {\r
-        Optional<Path> fileOp;\r
-        try {\r
-            fileOp = findPathForId(id);\r
-        } catch (IOException e) {\r
-            e.printStackTrace();\r
-            return;\r
-        }\r
-        if (!fileOp.isPresent())\r
-            return;\r
-        Path file = fileOp.get();\r
-        Optional<IGenericFileImport> serviceOp = findServiceForFileExtension(file);\r
-        serviceOp.ifPresent(service -> {\r
-            try {\r
-                Optional<String> resource = getResourceForPath(file);\r
-                if (!resource.isPresent())\r
-                    return;\r
-                service.remove(resource.get());\r
-                removeResourceForPath(file);\r
-            } catch (Throwable t) {\r
-                if (callback.isPresent()) {\r
-                    callback.get().accept(t);\r
-                } else {\r
-                    t.printStackTrace();\r
-                }\r
-            }\r
-        });\r
-    }\r
-\r
-    private static Optional<Path> findPathForId(long id) throws IOException {\r
-        Path db = Activator.getDropinsFolder().resolve(DB_FILE);\r
-        if (!Files.exists(db))\r
-            Files.createFile(db);\r
-        Properties props = new Properties();\r
-        try (InputStream stream = Files.newInputStream(db)) {\r
-            props.load(stream);\r
-        }\r
-        for (Map.Entry<Object, Object> entry : props.entrySet()) {\r
-            Long value = Long.valueOf(entry.getValue().toString());\r
-            if (value.longValue() == id) {\r
-                String key = (String) entry.getKey();\r
-                return Optional.of(Paths.get(key));\r
-            }\r
-        }\r
-        return Optional.empty();\r
-    }\r
-\r
-    static final String FOLDER = "_folder_";\r
-    \r
-    public static Optional<IGenericFileImport> findServiceForFileExtension(Path file) {\r
-        String extension = "";\r
-\r
-        int i = file.getFileName().toString().lastIndexOf('.');\r
-        if (i > 0) {\r
-            extension = file.getFileName().toString().substring(i);\r
-        } else {\r
-            // Handle case that file is actually a directory\r
-            if (Files.isDirectory(file) || !Files.isRegularFile(file)) {\r
-                extension = FOLDER;\r
-            }\r
-        }\r
-\r
-        List<IGenericFileImport> services = getFileImportServices();\r
-        for (IGenericFileImport service : services) {\r
-            for (Map.Entry<String, String> entry : service.allowedExtensionsWithFilters().entrySet()) {\r
-                String possibleExtensions = entry.getKey();\r
-                if (possibleExtensions.startsWith("*"))\r
-                    possibleExtensions = possibleExtensions.substring(1);\r
-                if (possibleExtensions.equals(extension) || possibleExtensions.isEmpty()) {\r
-                    if (extension.equals(FOLDER) && possibleExtensions.equals(FOLDER)) {\r
-                        return Optional.of(service);\r
-                    } else if (!extension.isEmpty() && !extension.equals(FOLDER)){\r
-                        return Optional.of(service);\r
-                    }\r
-                }\r
-            }\r
-        }\r
-        return Optional.empty();\r
-    }\r
-    \r
-    public static Map<String, Long> getPathsAndResources() {\r
-        try {\r
-            Path db = Activator.getDropinsFolder().resolve(DB_FILE);\r
-            if (!Files.exists(db))\r
-                Files.createFile(db);\r
-            Properties props = new Properties();\r
-            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
-            e.printStackTrace();\r
-            return Collections.emptyMap();\r
-        }\r
-    }\r
-\r
-    private static void saveResourceForPath(Path file, Optional<String> resource) {\r
-        resource.ifPresent(res -> {\r
-            try {\r
-                Path db = Activator.getDropinsFolder().resolve(DB_FILE);\r
-                if (!Files.exists(db))\r
-                    Files.createFile(db);\r
-                Properties props = new Properties();\r
-                try (InputStream stream = Files.newInputStream(db)) {\r
-                    props.load(stream);\r
-                }\r
-                props.put(file.getFileName().toString(), resource.get());\r
-                try (OutputStream stream = Files.newOutputStream(db)) {\r
-                    props.store(stream, null);\r
-                }\r
-            } catch (IOException e) {\r
-                e.printStackTrace();\r
-            }\r
-        });\r
-    }\r
-\r
-    private static void removeResourceForPath(Path file) throws IOException {\r
-        Path db = Activator.getDropinsFolder().resolve(DB_FILE);\r
-        if (!Files.exists(db))\r
-            Files.createFile(db);\r
-        Properties props = new Properties();\r
-        try (InputStream stream = Files.newInputStream(db)) {\r
-            props.load(stream);\r
-        }\r
-        props.remove(file.getFileName().toString());\r
-        try (OutputStream stream = Files.newOutputStream(db)) {\r
-            props.store(stream, null);\r
-        }\r
-    }\r
-    \r
-    private static Optional<String> getResourceForPath(Path file) throws IOException {\r
-        Path db = Activator.getDropinsFolder().resolve(DB_FILE);\r
-        if (!Files.exists(db))\r
-            Files.createFile(db);\r
-        Properties props = new Properties();\r
-        try (InputStream stream = Files.newInputStream(db)) {\r
-            props.load(stream);\r
-        }\r
-        String value = props.getProperty(file.getFileName().toString());\r
-        if (value == null)\r
-            return Optional.empty();\r
-        return Optional.of(value);\r
-    }\r
-}\r
+package org.simantics.fileimport;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.simantics.databoard.util.Base64;
+import org.simantics.db.Resource;
+import org.simantics.fileimport.dropins.FileImportDropins;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class for Simantics File import functions
+ * 
+ * @author Jani Simomaa
+ *
+ */
+public class FileImportService {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(FileImportService.class);
+    
+    private FileImportService() {}
+    
+    public static final String DB_FILE = ".simanticsdb";
+
+    private static List<IGenericFileImport> getFileImportServices() {
+        ServiceReference<?>[] serviceReferences = new ServiceReference<?>[0];
+        try {
+            serviceReferences = Activator.getContext().getAllServiceReferences(IGenericFileImport.class.getName(),
+                    null);
+        } catch (InvalidSyntaxException e) {
+            LOGGER.error("Could not get service references for IGenericFileImport!", e);
+        }
+        if (serviceReferences.length == 0)
+            return Collections.emptyList();
+
+        List<IGenericFileImport> services = new ArrayList<>(serviceReferences.length);
+        for (ServiceReference<?> reference : serviceReferences) {
+            IGenericFileImport service = (IGenericFileImport) Activator.getContext().getService(reference);
+            services.add(service);
+        }
+        return services;
+    }
+
+    /**
+     * Lists all supported file extensions which have a registered service for handling the import
+     * 
+     * @return Map containing the extension and the description of the extension in that order
+     */
+    public static Map<String, String> supportedExtensionsWithFilters() {
+        List<IGenericFileImport> services = getFileImportServices();
+        Map<String, String> extensionsWithFilters = new HashMap<>();
+        for (IGenericFileImport service : services)
+            extensionsWithFilters.putAll(service.allowedExtensionsWithFilters());
+
+        return extensionsWithFilters;
+    }
+    
+    private static class ConsumerHolder implements Consumer<Throwable> {
+
+        private Throwable throwable;
+        
+        @Override
+        public void accept(Throwable t) {
+            throwable = t;
+        }
+        
+        public Throwable getThrowable() {
+            return throwable;
+        }
+        
+    }
+    
+    public static String performFileImport(String base64, String name) throws Throwable {
+        byte[] bytes = Base64.decode(base64);
+        Path file = Activator.getModelsFolder().resolve(name);
+        Files.write(file, bytes);
+        
+        ConsumerHolder holder = new ConsumerHolder();
+        String result = performFileImport(file, Optional.of(holder));
+        if (holder.getThrowable() != null)
+            throw holder.getThrowable();
+        return result;
+    }
+
+    /**
+     * Method that performs the import of the given file. This method is called when e.g. {@link FileImportDropins} watcher detects {@link java.nio.file.StandardWatchEventKinds.ENTRY_CREATE} operation
+     * 
+     * @param file Path file to be imported
+     * @param callback Optional callback which can be used to catch Throwables thrown in the import process
+     */
+    public static String performFileImport(Path file, Optional<Consumer<Throwable>> callback) {
+        if (file.getFileName().toString().equals(DB_FILE)) {
+            return null;
+        }
+        String result = "Import failed";
+        IGenericFileImport service = findServiceForFileExtension(file);
+        if (service != null) {
+            try {
+                Optional<String> resource = service.perform(file);
+                saveResourceForPath(file, resource);
+                result = resource.get();
+            } catch (Throwable t) {
+                if (callback.isPresent()) {
+                    callback.get().accept(t);
+                } else {
+                    LOGGER.error("Could not import file " + file, t);
+                }
+            }
+        } else {
+            LOGGER.warn("Could not find service for importing file " + file);
+            if (callback.isPresent())
+                callback.get().accept(new Exception("Could not find IGenericFileImport service for file " + file));
+        }
+        return result;
+    }
+
+    
+    /**
+     * Remove the entity that matches the file. This method is called when e.g. the {@link FileImportDropins} watcher detects {@link java.nio.file.StandardWatchEventKinds.ENTRY_DELETE} operation
+     * 
+     * @param file Path file that was deleted
+     * @param callback Optional callback to catch Throwables thrown during the deletion process
+     */
+    public static void removeResourceForFile(Path file, Optional<Consumer<Throwable>> callback) {
+        try {
+            Optional<String> resource = getResourceForPath(file);
+            if (!resource.isPresent())
+                return;
+            IGenericFileImport service = findServiceForFileExtension(file);
+            if (service == null) {
+                LOGGER.warn("Could not find service for importing file " + file);
+                if (callback.isPresent())
+                    callback.get().accept(new Exception("Could not find IGenericFileImport service for file " + file));
+            }
+            service.remove(resource.get());
+            removeResourceForPath(file);
+        } catch (Throwable t) {
+            if (callback.isPresent()) {
+                callback.get().accept(t);
+            } else {
+                LOGGER.error("Could not remove resource for file " + file.toAbsolutePath(), t);
+            }
+        }
+    }
+    
+    public static void removeFileForResource(long id, Optional<Consumer<Throwable>> callback) {
+        Optional<Path> fileOp;
+        try {
+            fileOp = findPathForId(id);
+        } catch (IOException e) {
+            LOGGER.error("Could not remove file for resource id " + id, e);
+            return;
+        }
+        if (!fileOp.isPresent())
+            return;
+        Path file = fileOp.get();
+
+        try {
+            Optional<String> resource = getResourceForPath(file);
+            if (!resource.isPresent())
+                return;
+            IGenericFileImport service = findServiceForFileExtension(file);
+            if (service == null) {
+                LOGGER.warn("Could not find service for importing file " + file);
+                if (callback.isPresent())
+                    callback.get().accept(new Exception("Could not find IGenericFileImport service for file " + file));
+            }
+            service.remove(resource.get());
+            removeResourceForPath(file);
+            try {
+                Files.delete(file);
+            } catch (IOException e) {
+                Files.delete(file);
+            }
+        } catch (Throwable t) {
+            if (callback.isPresent()) {
+                callback.get().accept(t);
+            } else {
+                LOGGER.error("Could not remove file for resource " + id, t);
+            }
+        }
+    }
+
+    private static Optional<Path> findPathForId(long id) throws IOException {
+        Path db = Activator.getDropinsFolder().resolve(DB_FILE);
+        if (!Files.exists(db))
+            Files.createFile(db);
+        Properties props = new Properties();
+        try (InputStream stream = Files.newInputStream(db)) {
+            props.load(stream);
+        }
+        for (Map.Entry<Object, Object> entry : props.entrySet()) {
+            Long value = Long.valueOf(entry.getValue().toString());
+            if (value.longValue() == id) {
+                String key = (String) entry.getKey();
+                return Optional.of(Paths.get(key));
+            }
+        }
+        return Optional.empty();
+    }
+
+    static final String FOLDER = "_folder_";
+    
+    /**
+     * Method for finding a File Import service for the given file based on the file extension
+     * 
+     * @param file Path file for which the import service is looked for
+     * @return Optional IGenerigFileImport service which is able to handle the import of this type of file
+     */
+    public static IGenericFileImport findServiceForFileExtension(Path file) {
+        String extension = "";
+
+        int i = file.getFileName().toString().lastIndexOf('.');
+        if (i > 0) {
+            extension = file.getFileName().toString().substring(i);
+        } else {
+            // Handle case that file is actually a directory
+            if (Files.isDirectory(file) || !Files.isRegularFile(file)) {
+                extension = FOLDER;
+            }
+        }
+        return findServiceForExtension(extension);
+    }
+
+    public static List<String> filterSupportedExtensions(String filter) {
+        return getFileImportServices().stream().filter(s -> s.allowedExtensionsWithFilters().keySet().contains(filter)).map(s -> s.allowedExtensionsWithFilters().keySet()).flatMap(Set::stream).collect(Collectors.toList());
+    }
+
+    public static IGenericFileImport findServiceForExtension(String extension) {
+        List<IGenericFileImport> services = findServicesForExtension(extension);
+        IGenericFileImport service = null;
+        if (services.size() == 1) {
+            service = services.get(0);
+        } else {
+            for (IGenericFileImport servicee : services) {
+                service = servicee;
+                if (isPerfectMatch(servicee.allowedExtensionsWithFilters().keySet(), extension))
+                    break;
+            }
+        }
+        return service;
+    }
+    
+    public static List<IGenericFileImport> findServicesForExtension(String extension) {
+        List<IGenericFileImport> result = new ArrayList<>();
+        List<IGenericFileImport> services = getFileImportServices();
+        for (IGenericFileImport service : services) {
+            for (Map.Entry<String, String> entry : service.allowedExtensionsWithFilters().entrySet()) {
+                String possibleExtensions = entry.getKey();
+                if (possibleExtensions.startsWith("*"))
+                    possibleExtensions = possibleExtensions.substring(1);
+                if (possibleExtensions.equals(extension) || possibleExtensions.isEmpty()) {
+                    if (extension.equals(FOLDER) && possibleExtensions.equals(FOLDER)) {
+                        result.add(service);
+                    } else if (!extension.isEmpty() && !extension.equals(FOLDER)){
+                        result.add(service);
+                    }
+                }
+            }
+        }
+        return result;
+    }
+    
+    /**
+     * Method for listing all current paths and their corresponding identifiers in Simantics database
+     * 
+     * @return Map containing 
+     */
+    public static Map<String, String> getPathsAndResources() {
+        try {
+            Path db = Activator.getDropinsFolder().resolve(DB_FILE);
+            if (!Files.exists(db))
+                Files.createFile(db);
+            Properties props = new Properties();
+            try (InputStream stream = Files.newInputStream(db)) {
+                props.load(stream);
+            }
+            Map<String, String> map = new HashMap<>();
+            for (Map.Entry<Object, Object> entry : props.entrySet()) {
+                String value = (String) entry.getValue();
+                String key = (String) entry.getKey();
+                map.put(key, value);
+            }
+            return map;
+        } catch (IOException e) {
+            LOGGER.error("Could not get current paths and resources!", e);
+            return Collections.emptyMap();
+        }
+    }
+
+    private static void saveResourceForPath(Path file, Optional<String> resource) {
+        resource.ifPresent(res -> {
+            try {
+                Path db = Activator.getDropinsFolder().resolve(DB_FILE);
+                if (!Files.exists(db))
+                    Files.createFile(db);
+                Properties props = new Properties();
+                try (InputStream stream = Files.newInputStream(db)) {
+                    props.load(stream);
+                }
+                props.put(file.getFileName().toString(), resource.get());
+                try (OutputStream stream = Files.newOutputStream(db)) {
+                    props.store(stream, null);
+                }
+            } catch (IOException e) {
+                LOGGER.error("Could not save resource for path " + file.toAbsolutePath() + " and resource " + resource.get(), e);
+            }
+        });
+    }
+
+    private static void removeResourceForPath(Path file) throws IOException {
+        Path db = Activator.getDropinsFolder().resolve(DB_FILE);
+        if (!Files.exists(db))
+            Files.createFile(db);
+        Properties props = new Properties();
+        try (InputStream stream = Files.newInputStream(db)) {
+            props.load(stream);
+        }
+        props.remove(file.getFileName().toString());
+        try (OutputStream stream = Files.newOutputStream(db)) {
+            props.store(stream, null);
+        }
+    }
+    
+    private static Optional<String> getResourceForPath(Path file) throws IOException {
+        Path db = Activator.getDropinsFolder().resolve(DB_FILE);
+        if (!Files.exists(db))
+            Files.createFile(db);
+        Properties props = new Properties();
+        try (InputStream stream = Files.newInputStream(db)) {
+            props.load(stream);
+        }
+        String value = props.getProperty(file.getFileName().toString());
+        if (value == null)
+            return Optional.empty();
+        return Optional.of(value);
+    }
+    
+    public static String importGenericFileWithExtension(String path, String extension) throws Exception {
+        IGenericFileImport service = findServiceForExtension(extension);
+        Optional<String> result = service.perform(Paths.get(path));
+        return result.get();
+    }
+    
+    public static Resource importGenericFileWithExtensionAndParent(Resource parent, String path, String extension) throws Exception {
+        IGenericFileImport service = findServiceForExtension(extension);
+        Optional<Resource> result = service.perform(parent, Paths.get(path));
+        return result.get();
+    }
+
+    private static boolean isPerfectMatch(Set<String> candidates, String extension) {
+        for (String ext : candidates) {
+            if (ext.startsWith("."))
+                ext = ext.substring(1);
+            if (ext.equals(extension))
+                return true;
+        }
+        return false;
+    }
+}