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;
16 import java.util.function.Consumer;
18 import org.osgi.framework.InvalidSyntaxException;
19 import org.osgi.framework.ServiceReference;
20 import org.simantics.databoard.util.Base64;
21 import org.simantics.fileimport.dropins.FileImportDropins;
22 import org.simantics.utils.FileUtils;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
27 * Utility class for Simantics File import functions
29 * @author Jani Simomaa
32 public class FileImportService {
34 private static final Logger LOGGER = LoggerFactory.getLogger(FileImportService.class);
36 private FileImportService() {}
38 public static final String DB_FILE = ".simanticsdb";
40 private static List<IGenericFileImport> getFileImportServices() {
41 ServiceReference<?>[] serviceReferences = new ServiceReference<?>[0];
43 serviceReferences = Activator.getContext().getAllServiceReferences(IGenericFileImport.class.getName(),
45 } catch (InvalidSyntaxException e) {
46 LOGGER.error("Could not get service references for IGenericFileImport!", e);
48 if (serviceReferences.length == 0)
49 return Collections.emptyList();
51 List<IGenericFileImport> services = new ArrayList<>(serviceReferences.length);
52 for (ServiceReference<?> reference : serviceReferences) {
53 IGenericFileImport service = (IGenericFileImport) Activator.getContext().getService(reference);
54 services.add(service);
60 * Lists all supported file extensions which have a registered service for handling the import
62 * @return Map containing the extension and the description of the extension in that order
64 public static Map<String, String> supportedExtensionsWithFilters() {
65 List<IGenericFileImport> services = getFileImportServices();
66 Map<String, String> extensionsWithFilters = new HashMap<>();
67 for (IGenericFileImport service : services)
68 extensionsWithFilters.putAll(service.allowedExtensionsWithFilters());
70 return extensionsWithFilters;
73 private static class ConsumerHolder implements Consumer<Throwable> {
75 private Throwable throwable;
78 public void accept(Throwable t) {
82 public Throwable getThrowable() {
88 public static String performFileImport(String base64, String name) throws Throwable {
89 byte[] bytes = Base64.decode(base64);
90 Path file = Activator.getModelsFolder().resolve(name);
91 Files.write(file, bytes);
93 ConsumerHolder holder = new ConsumerHolder();
94 String result = performFileImport(file, Optional.of(holder));
95 if (holder.getThrowable() != null)
96 throw holder.getThrowable();
101 * 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
103 * @param file Path file to be imported
104 * @param callback Optional callback which can be used to catch Throwables thrown in the import process
106 public static String performFileImport(Path file, Optional<Consumer<Throwable>> callback) {
107 if (file.getFileName().toString().equals(DB_FILE)) {
110 String result = "Import failed";
111 Optional<IGenericFileImport> serviceOp = findServiceForFileExtension(file);
112 if (serviceOp.isPresent()) {
113 IGenericFileImport service = serviceOp.get();
115 Optional<String> resource = service.perform(file);
116 saveResourceForPath(file, resource);
117 result = resource.get();
118 } catch (Throwable t) {
119 if (callback.isPresent()) {
120 callback.get().accept(t);
122 LOGGER.error("Could not import file " + file, t);
126 LOGGER.warn("Could not find service for importing file " + file);
127 if (callback.isPresent())
128 callback.get().accept(new Exception("Could not find IGenericFileImport service for file " + file));
135 * 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
137 * @param file Path file that was deleted
138 * @param callback Optional callback to catch Throwables thrown during the deletion process
140 public static void removeResourceForFile(Path file, Optional<Consumer<Throwable>> callback) {
141 Optional<IGenericFileImport> serviceOp = findServiceForFileExtension(file);
142 serviceOp.ifPresent(service -> {
144 Optional<String> resource = getResourceForPath(file);
145 if (!resource.isPresent())
147 service.remove(resource.get());
148 removeResourceForPath(file);
149 } catch (Throwable t) {
150 if (callback.isPresent()) {
151 callback.get().accept(t);
159 public static void removeFileForResource(long id, Optional<Consumer<Throwable>> callback) {
160 Optional<Path> fileOp;
162 fileOp = findPathForId(id);
163 } catch (IOException e) {
167 if (!fileOp.isPresent())
169 Path file = fileOp.get();
170 Optional<IGenericFileImport> serviceOp = findServiceForFileExtension(file);
171 serviceOp.ifPresent(service -> {
173 Optional<String> resource = getResourceForPath(file);
174 if (!resource.isPresent())
176 service.remove(resource.get());
177 removeResourceForPath(file);
180 } catch (IOException e) {
183 } catch (Throwable t) {
184 if (callback.isPresent()) {
185 callback.get().accept(t);
193 private static Optional<Path> findPathForId(long id) throws IOException {
194 Path db = Activator.getDropinsFolder().resolve(DB_FILE);
195 if (!Files.exists(db))
196 Files.createFile(db);
197 Properties props = new Properties();
198 try (InputStream stream = Files.newInputStream(db)) {
201 for (Map.Entry<Object, Object> entry : props.entrySet()) {
202 Long value = Long.valueOf(entry.getValue().toString());
203 if (value.longValue() == id) {
204 String key = (String) entry.getKey();
205 return Optional.of(Paths.get(key));
208 return Optional.empty();
211 static final String FOLDER = "_folder_";
214 * Method for finding a File Import service for the given file based on the file extension
216 * @param file Path file for which the import service is looked for
217 * @return Optiona IGenerigFileImport service which is able to handle the import of this type of file
219 public static Optional<IGenericFileImport> findServiceForFileExtension(Path file) {
220 String extension = "";
222 int i = file.getFileName().toString().lastIndexOf('.');
224 extension = file.getFileName().toString().substring(i);
226 // Handle case that file is actually a directory
227 if (Files.isDirectory(file) || !Files.isRegularFile(file)) {
232 List<IGenericFileImport> services = getFileImportServices();
233 for (IGenericFileImport service : services) {
234 for (Map.Entry<String, String> entry : service.allowedExtensionsWithFilters().entrySet()) {
235 String possibleExtensions = entry.getKey();
236 if (possibleExtensions.startsWith("*"))
237 possibleExtensions = possibleExtensions.substring(1);
238 if (possibleExtensions.equals(extension) || possibleExtensions.isEmpty()) {
239 if (extension.equals(FOLDER) && possibleExtensions.equals(FOLDER)) {
240 return Optional.of(service);
241 } else if (!extension.isEmpty() && !extension.equals(FOLDER)){
242 return Optional.of(service);
247 return Optional.empty();
251 * Method for listing all current paths and their corresponding identifiers in Simantics database
253 * @return Map containing
255 public static Map<String, String> getPathsAndResources() {
257 Path db = Activator.getDropinsFolder().resolve(DB_FILE);
258 if (!Files.exists(db))
259 Files.createFile(db);
260 Properties props = new Properties();
261 try (InputStream stream = Files.newInputStream(db)) {
264 Map<String, String> map = new HashMap<>();
265 for (Map.Entry<Object, Object> entry : props.entrySet()) {
266 String value = (String) entry.getValue();
267 String key = (String) entry.getKey();
271 } catch (IOException e) {
273 return Collections.emptyMap();
277 private static void saveResourceForPath(Path file, Optional<String> resource) {
278 resource.ifPresent(res -> {
280 Path db = Activator.getDropinsFolder().resolve(DB_FILE);
281 if (!Files.exists(db))
282 Files.createFile(db);
283 Properties props = new Properties();
284 try (InputStream stream = Files.newInputStream(db)) {
287 props.put(file.getFileName().toString(), resource.get());
288 try (OutputStream stream = Files.newOutputStream(db)) {
289 props.store(stream, null);
291 } catch (IOException e) {
297 private static void removeResourceForPath(Path file) throws IOException {
298 Path db = Activator.getDropinsFolder().resolve(DB_FILE);
299 if (!Files.exists(db))
300 Files.createFile(db);
301 Properties props = new Properties();
302 try (InputStream stream = Files.newInputStream(db)) {
305 props.remove(file.getFileName().toString());
306 try (OutputStream stream = Files.newOutputStream(db)) {
307 props.store(stream, null);
311 private static Optional<String> getResourceForPath(Path file) throws IOException {
312 Path db = Activator.getDropinsFolder().resolve(DB_FILE);
313 if (!Files.exists(db))
314 Files.createFile(db);
315 Properties props = new Properties();
316 try (InputStream stream = Files.newInputStream(db)) {
319 String value = props.getProperty(file.getFileName().toString());
321 return Optional.empty();
322 return Optional.of(value);