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.function.Consumer; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.simantics.fileimport.dropins.FileImportDropins; /** * Utility class for Simantics File import functions * * @author Jani Simomaa * */ public class FileImportService { public static final String DB_FILE = ".simanticsdb"; private static List getFileImportServices() { ServiceReference[] serviceReferences = new ServiceReference[0]; try { serviceReferences = Activator.getContext().getAllServiceReferences(IGenericFileImport.class.getName(), null); } catch (InvalidSyntaxException e) { e.printStackTrace(); } if (serviceReferences.length == 0) return Collections.emptyList(); List 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 supportedExtensionsWithFilters() { List services = getFileImportServices(); Map extensionsWithFilters = new HashMap<>(); for (IGenericFileImport service : services) extensionsWithFilters.putAll(service.allowedExtensionsWithFilters()); return extensionsWithFilters; } /** * 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 void performFileImport(Path file, Optional> callback) { if (file.getFileName().toString().equals(DB_FILE)) return; Optional serviceOp = findServiceForFileExtension(file); serviceOp.ifPresent(service -> { try { Optional resource = service.perform(file); saveResourceForPath(file, resource); } catch (Throwable t) { if (callback.isPresent()) { callback.get().accept(t); } else { t.printStackTrace(); } } }); } /** * 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> callback) { Optional serviceOp = findServiceForFileExtension(file); serviceOp.ifPresent(service -> { try { Optional resource = getResourceForPath(file); if (!resource.isPresent()) return; service.remove(resource.get()); removeResourceForPath(file); } catch (Throwable t) { if (callback.isPresent()) { callback.get().accept(t); } else { t.printStackTrace(); } } }); } public static void removeFileForResource(long id, Optional> callback) { Optional fileOp; try { fileOp = findPathForId(id); } catch (IOException e) { e.printStackTrace(); return; } if (!fileOp.isPresent()) return; Path file = fileOp.get(); Optional serviceOp = findServiceForFileExtension(file); serviceOp.ifPresent(service -> { try { Optional resource = getResourceForPath(file); if (!resource.isPresent()) return; service.remove(resource.get()); removeResourceForPath(file); } catch (Throwable t) { if (callback.isPresent()) { callback.get().accept(t); } else { t.printStackTrace(); } } }); } private static Optional 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 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 Optiona IGenerigFileImport service which is able to handle the import of this type of file */ public static Optional 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; } } List services = getFileImportServices(); for (IGenericFileImport service : services) { for (Map.Entry 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)) { return Optional.of(service); } else if (!extension.isEmpty() && !extension.equals(FOLDER)){ return Optional.of(service); } } } } return Optional.empty(); } /** * Method for listing all current paths and their corresponding identifiers in Simantics database * * @return Map containing */ public static Map 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 map = new HashMap<>(); for (Map.Entry entry : props.entrySet()) { String value = (String) entry.getValue(); String key = (String) entry.getKey(); map.put(key, value); } return map; } catch (IOException e) { e.printStackTrace(); return Collections.emptyMap(); } } private static void saveResourceForPath(Path file, Optional 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) { e.printStackTrace(); } }); } 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 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); } }