1 package org.simantics.fileimport;
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.OutputStream;
6 import java.nio.file.Files;
7 import java.nio.file.Path;
8 import java.nio.file.Paths;
9 import java.util.ArrayList;
10 import java.util.Collections;
11 import java.util.HashMap;
12 import java.util.List;
14 import java.util.Optional;
15 import java.util.Properties;
17 import java.util.function.Consumer;
18 import java.util.stream.Collectors;
20 import org.osgi.framework.InvalidSyntaxException;
21 import org.osgi.framework.ServiceReference;
22 import org.simantics.databoard.util.Base64;
23 import org.simantics.db.Resource;
24 import org.simantics.fileimport.dropins.FileImportDropins;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
29 * Utility class for Simantics File import functions
31 * @author Jani Simomaa
34 public class FileImportService {
36 private static final Logger LOGGER = LoggerFactory.getLogger(FileImportService.class);
38 private FileImportService() {}
40 public static final String DB_FILE = ".simanticsdb";
42 private static List<IGenericFileImport> getFileImportServices() {
43 ServiceReference<?>[] serviceReferences = new ServiceReference<?>[0];
45 serviceReferences = Activator.getContext().getAllServiceReferences(IGenericFileImport.class.getName(),
47 } catch (InvalidSyntaxException e) {
48 LOGGER.error("Could not get service references for IGenericFileImport!", e);
50 if (serviceReferences.length == 0)
51 return Collections.emptyList();
53 List<IGenericFileImport> services = new ArrayList<>(serviceReferences.length);
54 for (ServiceReference<?> reference : serviceReferences) {
55 IGenericFileImport service = (IGenericFileImport) Activator.getContext().getService(reference);
56 services.add(service);
62 * Lists all supported file extensions which have a registered service for handling the import
64 * @return Map containing the extension and the description of the extension in that order
66 public static Map<String, String> supportedExtensionsWithFilters() {
67 List<IGenericFileImport> services = getFileImportServices();
68 Map<String, String> extensionsWithFilters = new HashMap<>();
69 for (IGenericFileImport service : services)
70 extensionsWithFilters.putAll(service.allowedExtensionsWithFilters());
72 return extensionsWithFilters;
75 private static class ConsumerHolder implements Consumer<Throwable> {
77 private Throwable throwable;
80 public void accept(Throwable t) {
84 public Throwable getThrowable() {
90 public static String performFileImport(String base64, String name) throws Throwable {
91 byte[] bytes = Base64.decode(base64);
92 Path file = Activator.getModelsFolder().resolve(name);
93 Files.write(file, bytes);
95 ConsumerHolder holder = new ConsumerHolder();
96 String result = performFileImport(file, Optional.of(holder));
97 if (holder.getThrowable() != null)
98 throw holder.getThrowable();
103 * 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
105 * @param file Path file to be imported
106 * @param callback Optional callback which can be used to catch Throwables thrown in the import process
108 public static String performFileImport(Path file, Optional<Consumer<Throwable>> callback) {
109 if (file.getFileName().toString().equals(DB_FILE)) {
112 String result = "Import failed";
113 IGenericFileImport service = findServiceForFileExtension(file);
114 if (service != null) {
116 Optional<String> resource = service.perform(file);
117 saveResourceForPath(file, resource);
118 result = resource.get();
119 } catch (Throwable t) {
120 if (callback.isPresent()) {
121 callback.get().accept(t);
123 LOGGER.error("Could not import file " + file, t);
127 LOGGER.warn("Could not find service for importing file " + file);
128 if (callback.isPresent())
129 callback.get().accept(new Exception("Could not find IGenericFileImport service for file " + file));
136 * 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
138 * @param file Path file that was deleted
139 * @param callback Optional callback to catch Throwables thrown during the deletion process
141 public static void removeResourceForFile(Path file, Optional<Consumer<Throwable>> callback) {
143 Optional<String> resource = getResourceForPath(file);
144 if (!resource.isPresent())
146 IGenericFileImport service = findServiceForFileExtension(file);
147 if (service == null) {
148 LOGGER.warn("Could not find service for importing file " + file);
149 if (callback.isPresent())
150 callback.get().accept(new Exception("Could not find IGenericFileImport service for file " + file));
152 service.remove(resource.get());
153 removeResourceForPath(file);
154 } catch (Throwable t) {
155 if (callback.isPresent()) {
156 callback.get().accept(t);
158 LOGGER.error("Could not remove resource for file " + file.toAbsolutePath(), t);
163 public static void removeFileForResource(long id, Optional<Consumer<Throwable>> callback) {
164 Optional<Path> fileOp;
166 fileOp = findPathForId(id);
167 } catch (IOException e) {
168 LOGGER.error("Could not remove file for resource id " + id, e);
171 if (!fileOp.isPresent())
173 Path file = fileOp.get();
176 Optional<String> resource = getResourceForPath(file);
177 if (!resource.isPresent())
179 IGenericFileImport service = findServiceForFileExtension(file);
180 if (service == null) {
181 LOGGER.warn("Could not find service for importing file " + file);
182 if (callback.isPresent())
183 callback.get().accept(new Exception("Could not find IGenericFileImport service for file " + file));
185 service.remove(resource.get());
186 removeResourceForPath(file);
189 } catch (IOException e) {
192 } catch (Throwable t) {
193 if (callback.isPresent()) {
194 callback.get().accept(t);
196 LOGGER.error("Could not remove file for resource " + id, t);
201 private static Optional<Path> findPathForId(long id) throws IOException {
202 Path db = Activator.getDropinsFolder().resolve(DB_FILE);
203 if (!Files.exists(db))
204 Files.createFile(db);
205 Properties props = new Properties();
206 try (InputStream stream = Files.newInputStream(db)) {
209 for (Map.Entry<Object, Object> entry : props.entrySet()) {
210 Long value = Long.valueOf(entry.getValue().toString());
211 if (value.longValue() == id) {
212 String key = (String) entry.getKey();
213 return Optional.of(Paths.get(key));
216 return Optional.empty();
219 static final String FOLDER = "_folder_";
222 * Method for finding a File Import service for the given file based on the file extension
224 * @param file Path file for which the import service is looked for
225 * @return Optional IGenerigFileImport service which is able to handle the import of this type of file
227 public static IGenericFileImport findServiceForFileExtension(Path file) {
228 String extension = "";
230 int i = file.getFileName().toString().lastIndexOf('.');
232 extension = file.getFileName().toString().substring(i);
234 // Handle case that file is actually a directory
235 if (Files.isDirectory(file) || !Files.isRegularFile(file)) {
239 return findServiceForExtension(extension);
242 public static List<String> filterSupportedExtensions(String filter) {
243 return getFileImportServices().stream().filter(s -> s.allowedExtensionsWithFilters().keySet().contains(filter)).map(s -> s.allowedExtensionsWithFilters().keySet()).flatMap(Set::stream).collect(Collectors.toList());
246 public static IGenericFileImport findServiceForExtension(String extension) {
247 List<IGenericFileImport> services = findServicesForExtension(extension);
248 IGenericFileImport service = null;
249 if (services.size() == 1) {
250 service = services.get(0);
252 for (IGenericFileImport servicee : services) {
254 if (isPerfectMatch(servicee.allowedExtensionsWithFilters().keySet(), extension))
261 public static List<IGenericFileImport> findServicesForExtension(String extension) {
262 List<IGenericFileImport> result = new ArrayList<>();
263 List<IGenericFileImport> services = getFileImportServices();
264 for (IGenericFileImport service : services) {
265 for (Map.Entry<String, String> entry : service.allowedExtensionsWithFilters().entrySet()) {
266 String possibleExtensions = entry.getKey();
267 if (possibleExtensions.startsWith("*"))
268 possibleExtensions = possibleExtensions.substring(1);
269 if (possibleExtensions.equals(extension) || possibleExtensions.isEmpty()) {
270 if (extension.equals(FOLDER) && possibleExtensions.equals(FOLDER)) {
272 } else if (!extension.isEmpty() && !extension.equals(FOLDER)){
282 * Method for listing all current paths and their corresponding identifiers in Simantics database
284 * @return Map containing
286 public static Map<String, String> getPathsAndResources() {
288 Path db = Activator.getDropinsFolder().resolve(DB_FILE);
289 if (!Files.exists(db))
290 Files.createFile(db);
291 Properties props = new Properties();
292 try (InputStream stream = Files.newInputStream(db)) {
295 Map<String, String> map = new HashMap<>();
296 for (Map.Entry<Object, Object> entry : props.entrySet()) {
297 String value = (String) entry.getValue();
298 String key = (String) entry.getKey();
302 } catch (IOException e) {
303 LOGGER.error("Could not get current paths and resources!", e);
304 return Collections.emptyMap();
308 private static void saveResourceForPath(Path file, Optional<String> resource) {
309 resource.ifPresent(res -> {
311 Path db = Activator.getDropinsFolder().resolve(DB_FILE);
312 if (!Files.exists(db))
313 Files.createFile(db);
314 Properties props = new Properties();
315 try (InputStream stream = Files.newInputStream(db)) {
318 props.put(file.getFileName().toString(), resource.get());
319 try (OutputStream stream = Files.newOutputStream(db)) {
320 props.store(stream, null);
322 } catch (IOException e) {
323 LOGGER.error("Could not save resource for path " + file.toAbsolutePath() + " and resource " + resource.get(), e);
328 private static void removeResourceForPath(Path file) throws IOException {
329 Path db = Activator.getDropinsFolder().resolve(DB_FILE);
330 if (!Files.exists(db))
331 Files.createFile(db);
332 Properties props = new Properties();
333 try (InputStream stream = Files.newInputStream(db)) {
336 props.remove(file.getFileName().toString());
337 try (OutputStream stream = Files.newOutputStream(db)) {
338 props.store(stream, null);
342 private static Optional<String> getResourceForPath(Path file) throws IOException {
343 Path db = Activator.getDropinsFolder().resolve(DB_FILE);
344 if (!Files.exists(db))
345 Files.createFile(db);
346 Properties props = new Properties();
347 try (InputStream stream = Files.newInputStream(db)) {
350 String value = props.getProperty(file.getFileName().toString());
352 return Optional.empty();
353 return Optional.of(value);
356 public static String importGenericFileWithExtension(String path, String extension) throws Exception {
357 IGenericFileImport service = findServiceForExtension(extension);
358 Optional<String> result = service.perform(Paths.get(path));
362 public static Resource importGenericFileWithExtensionAndParent(Resource parent, String path, String extension) throws Exception {
363 IGenericFileImport service = findServiceForExtension(extension);
364 Optional<Resource> result = service.perform(parent, Paths.get(path));
368 private static boolean isPerfectMatch(Set<String> candidates, String extension) {
369 for (String ext : candidates) {
370 if (ext.startsWith("*"))
371 ext = ext.substring(1);
372 if (ext.equals(extension))