-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.fileimport.dropins.FileImportDropins;\r
-\r
-/**\r
- * Utility class for Simantics File import functions\r
- * \r
- * @author Jani Simomaa\r
- *\r
- */\r
-public class FileImportService {\r
-\r
- private FileImportService() {}\r
- \r
- public static final String DB_FILE = ".simanticsdb";\r
-\r
- private 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
- /**\r
- * Lists all supported file extensions which have a registered service for handling the import\r
- * \r
- * @return Map containing the extension and the description of the extension in that order\r
- */\r
- public static Map<String, String> supportedExtensionsWithFilters() {\r
- List<IGenericFileImport> services = getFileImportServices();\r
- Map<String, String> extensionsWithFilters = new HashMap<>();\r
- for (IGenericFileImport service : services)\r
- extensionsWithFilters.putAll(service.allowedExtensionsWithFilters());\r
-\r
- return extensionsWithFilters;\r
- }\r
-\r
- /**\r
- * Method that performs the import of the given file. This method is called when e.g. {@link FileImportDropins} watcher detects {@link java.nio.file.StandardWatchEventKinds.ENTRY_CREATE} operation\r
- * \r
- * @param file Path file to be imported\r
- * @param callback Optional callback which can be used to catch Throwables thrown in the import process\r
- */\r
- public static void performFileImport(Path file, Optional<Consumer<Throwable>> callback) {\r
- if (file.getFileName().toString().equals(DB_FILE))\r
- return;\r
- 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
- \r
- /**\r
- * Remove the entity that matches the file. This method is called when e.g. the {@link FileImportDropins} watcher detects {@link java.nio.file.StandardWatchEventKinds.ENTRY_DELETE} operation\r
- * \r
- * @param file Path file that was deleted\r
- * @param callback Optional callback to catch Throwables thrown during the deletion process\r
- */\r
- public static void removeResourceForFile(Path file, Optional<Consumer<Throwable>> callback) {\r
- Optional<IGenericFileImport> serviceOp = findServiceForFileExtension(file);\r
- serviceOp.ifPresent(service -> {\r
- 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
- /**\r
- * Method for finding a File Import service for the given file based on the file extension\r
- * \r
- * @param file Path file for which the import service is looked for\r
- * @return Optiona IGenerigFileImport service which is able to handle the import of this type of file\r
- */\r
- public static Optional<IGenericFileImport> findServiceForFileExtension(Path file) {\r
- String extension = "";\r
-\r
- 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
- /**\r
- * Method for listing all current paths and their corresponding identifiers in Simantics database\r
- * \r
- * @return Map containing \r
- */\r
- public static Map<String, String> getPathsAndResources() {\r
- try {\r
- Path db = Activator.getDropinsFolder().resolve(DB_FILE);\r
- if (!Files.exists(db))\r
- Files.createFile(db);\r
- Properties props = new Properties();\r
- try (InputStream stream = Files.newInputStream(db)) {\r
- props.load(stream);\r
- }\r
- Map<String, String> map = new HashMap<>();\r
- for (Map.Entry<Object, Object> entry : props.entrySet()) {\r
- String value = (String) entry.getValue();\r
- String key = (String) entry.getKey();\r
- map.put(key, value);\r
- }\r
- return map;\r
- } catch (IOException e) {\r
- e.printStackTrace();\r
- return Collections.emptyMap();\r
- }\r
- }\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;
+ }
+}