1 /*******************************************************************************
2 * Copyright (c) 2013 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.graphfile.util;
14 import java.io.ByteArrayInputStream;
15 import java.io.DataInputStream;
17 import java.io.FileInputStream;
18 import java.io.FileOutputStream;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.nio.ByteBuffer;
22 import java.nio.channels.FileChannel;
23 import java.nio.file.Path;
24 import java.util.Collection;
25 import java.util.HashMap;
28 import org.simantics.Simantics;
29 import org.simantics.databoard.Bindings;
30 import org.simantics.databoard.util.binary.RandomAccessBinary;
31 import org.simantics.db.ReadGraph;
32 import org.simantics.db.Resource;
33 import org.simantics.db.WriteGraph;
34 import org.simantics.db.common.request.ReadRequest;
35 import org.simantics.db.common.request.WriteRequest;
36 import org.simantics.db.common.request.WriteResultRequest;
37 import org.simantics.db.common.utils.LiteralFileUtil;
38 import org.simantics.db.common.utils.Logger;
39 import org.simantics.db.exception.DatabaseException;
40 import org.simantics.db.exception.DoesNotContainValueException;
41 import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;
42 import org.simantics.db.exception.NoSingleResultException;
43 import org.simantics.db.exception.ServiceException;
44 import org.simantics.db.request.Read;
45 import org.simantics.db.service.ClusteringSupport;
46 import org.simantics.db.service.TransferableGraphSupport;
47 import org.simantics.graphfile.ontology.GraphFileResource;
48 import org.simantics.layer0.Layer0;
51 * @author Marko Luukkainen
53 public class GraphFileUtil {
56 public static boolean USE_RANDOM_ACCESS_BINARY = true;
58 * Creates a temp file of a graphFile.
61 * @throws DatabaseException
63 public static File toTempFile(final Resource res)throws DatabaseException {
64 return Simantics.getSession().syncRequest(new Read<File>() {
66 public File perform(ReadGraph graph) throws DatabaseException {
67 return toTempFile(graph, res);
73 * Creates a temp file of a graphFile.
77 * @throws DatabaseException
79 public static File toTempFile(ReadGraph graph, Resource res)throws DatabaseException {
81 GraphFileResource gf = GraphFileResource.getInstance(graph);
82 String filename = graph.getPossibleRelatedValue(res, gf.HasResourceName);
84 filename = graph.getRelatedValue(res, Layer0.getInstance(graph).HasName);
86 int index = filename.lastIndexOf(".");
90 name = filename.substring(0,index);
91 ext = filename.substring(index+1);
95 if (name.length() < 3) {
96 for (int i = name.length(); i < 3; i++)
100 File file = File.createTempFile(name, "."+ ext);
101 writeDataToFile(graph, res, file);
103 } catch (Exception e) {
104 throw new DatabaseException(e);
108 public static void writeDataToFile(final Resource res, final File file) throws DatabaseException{
109 Simantics.getSession().syncRequest(new ReadRequest() {
112 public void run(ReadGraph graph) throws DatabaseException {
114 writeDataToFile(graph, res, file);
115 } catch (IOException e) {
116 throw new DatabaseException(e);
123 * Writes contents of a graphFile to file.
127 * @throws DatabaseException
128 * @throws IOException
130 public static void writeDataToFile(ReadGraph graph, Resource res, File file) throws DatabaseException, IOException {
132 GraphFileResource gf = GraphFileResource.getInstance(graph);
133 if (USE_RANDOM_ACCESS_BINARY) {
134 Resource filedata = graph.getSingleObject(res, gf.HasFiledata);
135 LiteralFileUtil.copyRandomAccessBinaryToFile(graph, filedata, file);
138 byte[] data = graph.getRelatedValue(res, gf.HasFiledata);
139 FileOutputStream fos = new FileOutputStream(file);
145 Long lastModified = graph.getPossibleRelatedValue(res, gf.LastModified);
146 if (lastModified != null)
147 file.setLastModified(lastModified);
151 * Updates contents of a graphFile, including the name.
154 * @throws DatabaseException
156 public static void toGraph(final String filename, final Resource graphFile)throws DatabaseException {
157 Simantics.getSession().syncRequest(new WriteRequest() {
159 public void perform(WriteGraph graph) throws DatabaseException {
161 toGraph(graph, filename,graphFile);
162 } catch (IOException e) {
163 throw new DatabaseException(e);
170 * Updates contents of a graphFile, including the name.
174 * @throws DatabaseException
175 * @throws IOException
177 public static void toGraph(WriteGraph graph, String filename, Resource graphFile) throws DatabaseException, IOException {
178 File file = new File(filename);
180 throw new IOException("File " + filename + " not found.");
182 toGraph(graph, file, graphFile);
186 * Updates contents of a graphFile, including the name.
190 * @throws DatabaseException
191 * @throws IOException
193 public static void toGraph(WriteGraph graph, File file, Resource graphFile) throws DatabaseException, IOException {
195 writeDataToGraph(graph, file, graphFile);
196 String name = file.getName();
197 GraphFileResource gf = GraphFileResource.getInstance(graph);
198 graph.claimLiteral(graphFile, gf.HasResourceName, name);
203 * Writes contents of a file to a graphFile (data and time stamp).
207 * @throws IOException
208 * @throws ManyObjectsForFunctionalRelationException
209 * @throws ServiceException
211 public static void writeDataToGraph(WriteGraph graph, File file, Resource graphFile) throws IOException, DatabaseException{
212 GraphFileResource gf = GraphFileResource.getInstance(graph);
213 if (USE_RANDOM_ACCESS_BINARY) {
215 Resource fileData = graph.getPossibleObject(graphFile, gf.HasFiledata);
216 RandomAccessBinary rab = null;
217 if (fileData == null) {
218 Layer0 l0 = Layer0.getInstance(graph);
219 ClusteringSupport cs = graph.getService(ClusteringSupport.class);
220 fileData = graph.newResource(cs.createCluster());
221 graph.claim(fileData, l0.InstanceOf, l0.ByteArray);
222 graph.claim(graphFile, gf.HasFiledata, fileData);
223 rab = graph.createRandomAccessBinary(fileData, Bindings.BYTE_ARRAY.type(), null);
225 rab = graph.getRandomAccessBinary(fileData);
227 LiteralFileUtil.copyRandomAccessBinaryFromFile(file, rab);
229 FileInputStream stream = new FileInputStream(file);
230 FileChannel chan = stream.getChannel();
231 long lsize = chan.size();
232 if (lsize > Integer.MAX_VALUE)
233 throw new IOException("File is too big");
234 int size = (int)lsize;
235 final byte[] array = new byte[size];
236 ByteBuffer buf = ByteBuffer.wrap(array);
238 size -= chan.read(buf);
240 graph.claimLiteral(graphFile, gf.HasFiledata, array);
245 graph.claimLiteral(graphFile, gf.LastModified, file.lastModified());
249 public static void writeDataToGraph(WriteGraph graph, byte data[], Resource graphFile) throws IOException, DatabaseException {
250 GraphFileResource gf = GraphFileResource.getInstance(graph);
251 if (USE_RANDOM_ACCESS_BINARY) {
252 Resource fileData = graph.getPossibleObject(graphFile, gf.HasFiledata);
253 if (fileData == null) {
254 Layer0 l0 = Layer0.getInstance(graph);
255 ClusteringSupport cs = graph.getService(ClusteringSupport.class);
256 fileData = graph.newResource(cs.createCluster());
257 graph.claim(fileData, l0.InstanceOf, l0.ByteArray);
258 graph.claim(graphFile, gf.HasFiledata, fileData);
259 graph.createRandomAccessBinary(fileData, Bindings.BYTE_ARRAY.type(), data);
261 InputStream input = new ByteArrayInputStream(data);
262 LiteralFileUtil.copyStreamToRandomAccessBinary(graph, input, fileData);
265 graph.claimLiteral(graphFile, gf.HasFiledata, data);
267 graph.claimLiteral(graphFile, gf.LastModified, System.currentTimeMillis());
271 * Writes contents of a file to a graphFile (data and time stamp).
274 * @throws DatabaseException
276 public static void writeDataToGraph(final File file, final Resource graphFile) throws DatabaseException {
277 Simantics.getSession().syncRequest(new WriteRequest() {
280 public void perform(WriteGraph graph) throws DatabaseException {
282 writeDataToGraph(graph, file, graphFile);
283 } catch (IOException e) {
284 throw new DatabaseException(e);
291 public static void syncFolderToGraph(WriteGraph g, File folder, Resource folderRes) throws Exception {
292 File subFiles[] = folder.listFiles();
293 Layer0 l0 = Layer0.getInstance(g);
294 GraphFileResource gf = GraphFileResource.getInstance(g);
295 Collection<Resource> subFileResources = g.getObjects(folderRes, gf.HasFile);
296 Collection<Resource> subFolderResources = g.getObjects(folderRes, gf.HasFolder);
298 Map<Resource,File> matching = new HashMap<Resource, File>();
300 for (File f : subFiles) {
301 String name = f.getName();
302 if (f.isDirectory()) {
303 Resource matchingFolder = findWithName(g, subFolderResources, name);
305 if (matchingFolder != null) {
306 if (matching.containsKey(matchingFolder))
307 throw new Exception("Matching folder already in use" + f.getAbsolutePath() + " " + matchingFolder);
309 matching.put(matchingFolder, f);
310 syncFolderToGraph(g, f, matchingFolder);
312 matchingFolder = g.newResource();
313 g.claim(matchingFolder, l0.InstanceOf, gf.Folder);
314 g.claimLiteral(matchingFolder, gf.HasResourceName, name);
315 g.claimLiteral(matchingFolder, l0.HasName, name);
316 g.claim(folderRes, gf.HasFolder, matchingFolder);
317 matching.put(matchingFolder, f);
318 syncFolderToGraph(g, f, matchingFolder);
321 Resource fileRes = findWithName(g, subFileResources, name);
322 if (fileRes != null) {
323 if (matching.containsKey(fileRes))
324 throw new Exception("Matching file already in use" + f.getAbsolutePath() + " " + fileRes);
325 matching.put(fileRes, f);
326 toGraph(g, f, fileRes);
328 fileRes = g.newResource();
329 g.claim(fileRes, l0.InstanceOf, gf.File);
330 g.claimLiteral(fileRes, gf.HasResourceName, name);
331 g.claimLiteral(fileRes, l0.HasName, name);
332 g.claim(folderRes, gf.HasFile, fileRes);
333 matching.put(fileRes, f);
334 toGraph(g, f, fileRes);
338 // delete resources, which have no matching file (or folder)
339 for (Resource subFolder : subFolderResources) {
340 if (!matching.containsKey(subFolder))
344 for (Resource subFolder : subFileResources) {
345 if (!matching.containsKey(subFolder))
350 public static void writeFolderToDisk(ReadGraph g, Resource folderRes, File folder) throws DatabaseException, IOException{
352 GraphFileResource gf = GraphFileResource.getInstance(g);
353 for (Resource subFolder : g.getObjects(folderRes, gf.HasFolder)) {
354 String name = g.getRelatedValue(subFolder, gf.HasResourceName);
356 if (name.length() == 0)
357 throw new DatabaseException("Empty folder name for " + subFolder);
359 File newFolder = new File(folder.getAbsolutePath() + "/" + name);
360 if (!newFolder.mkdir()) {
361 throw new DatabaseException("Could not create folder " + name + " for resource " + subFolder);
363 writeFolderToDisk(g, subFolder, newFolder);
365 for (Resource fileRes : g.getObjects(folderRes, gf.HasFile)) {
366 String name = g.getRelatedValue(fileRes, gf.HasResourceName);
367 File file = new File(folder.getAbsolutePath() + "/" + name);
368 writeDataToFile(g, fileRes, file);
372 public static void syncFolderToGraph(WriteGraph g, File folder, Resource folderRes, ToGraphHelper helper) throws Exception {
373 File subFiles[] = folder.listFiles();
374 GraphFileResource gf = GraphFileResource.getInstance(g);
375 Collection<Resource> subFileResources = g.getObjects(folderRes, gf.HasFile);
376 Collection<Resource> subFolderResources = g.getObjects(folderRes, gf.HasFolder);
378 Map<Resource,File> matching = new HashMap<Resource, File>();
380 for (File f : subFiles) {
381 String name = f.getName();
382 if (f.isDirectory()) {
383 Resource matchingFolder = helper.findFolder(g, subFolderResources, name);
385 if (matchingFolder != null) {
386 if (matching.containsKey(matchingFolder))
387 throw new Exception("Matching folder already in use" + f.getAbsolutePath() + " " + matchingFolder);
389 matching.put(matchingFolder, f);
390 syncFolderToGraph(g, f, matchingFolder,helper);
392 matchingFolder = helper.createFolder(g, name);
393 g.claim(folderRes, gf.HasFolder, matchingFolder);
394 matching.put(matchingFolder, f);
395 syncFolderToGraph(g, f, matchingFolder,helper);
398 Resource fileRes = helper.findFile(g, subFileResources, name);
399 if (fileRes != null) {
400 if (matching.containsKey(fileRes))
401 throw new Exception("Matching file already in use" + f.getAbsolutePath() + " " + fileRes);
402 matching.put(fileRes, f);
403 toGraph(g, f, fileRes);
405 fileRes = helper.createFile(g, name);
406 g.claim(folderRes, gf.HasFile, fileRes);
407 matching.put(fileRes, f);
408 toGraph(g, f, fileRes);
412 // delete resources, which have no matching file (or folder)
413 for (Resource subFolder : subFolderResources) {
414 if (!matching.containsKey(subFolder))
418 for (Resource subFolder : subFileResources) {
419 if (!matching.containsKey(subFolder))
424 public static interface ToGraphHelper {
425 public Resource findFolder(ReadGraph g, Collection<Resource> subFolderResources, String name) throws DatabaseException;
426 public Resource createFolder(WriteGraph g, String name) throws DatabaseException;
427 public Resource findFile(ReadGraph g, Collection<Resource> subFileResources, String name) throws DatabaseException;
428 public Resource createFile(WriteGraph g, String name) throws DatabaseException;
431 public static interface ToDiskHelper {
432 public String getName(ReadGraph g, Resource systemResource) throws DatabaseException;
435 public static void writeFolderToDisk(ReadGraph g, Resource folderRes, File folder, ToDiskHelper helper) throws DatabaseException, IOException{
436 GraphFileResource gf = GraphFileResource.getInstance(g);
437 for (Resource subFolder : g.getObjects(folderRes, gf.HasFolder)) {
438 String name = helper.getName(g, subFolder);
440 if (name.length() == 0)
441 throw new DatabaseException("Empty folder name for " + subFolder);
443 File newFolder = new File(folder.getAbsolutePath() + "/" + name);
444 if (!newFolder.mkdir()) {
445 throw new DatabaseException("Could not create folder " + name + " for resource " + subFolder);
447 writeFolderToDisk(g, subFolder, newFolder, helper);
449 for (Resource fileRes : g.getObjects(folderRes, gf.HasFile)) {
450 String name = helper.getName(g, fileRes);
451 File file = new File(folder.getAbsolutePath() + "/" + name);
452 writeDataToFile(g, fileRes, file);
456 public static interface ToDiskHelper2 extends ToDiskHelper {
457 public Resource findFolder(ReadGraph g, Collection<Resource> subFolderResources, String name) throws DatabaseException;
458 public Resource findFile(ReadGraph g, Collection<Resource> subFileResources, String name) throws DatabaseException;
461 public static void syncFolderToDisk(ReadGraph g, Resource folderRes, File folder, ToDiskHelper2 helper) throws Exception {
462 File subFiles[] = folder.listFiles();
463 GraphFileResource gf = GraphFileResource.getInstance(g);
464 Collection<Resource> subFileResources = g.getObjects(folderRes, gf.HasFile);
465 Collection<Resource> subFolderResources = g.getObjects(folderRes, gf.HasFolder);
467 Map<Resource,File> matching = new HashMap<Resource, File>();
469 for (File f : subFiles) {
470 String name = f.getName();
471 if (f.isDirectory()) {
472 Resource matchingFolder = helper.findFolder(g, subFolderResources, name);
474 if (matchingFolder != null) {
475 if (matching.containsKey(matchingFolder))
476 throw new Exception("Matching folder already in use" + f.getAbsolutePath() + " " + matchingFolder);
478 matching.put(matchingFolder, f);
479 syncFolderToDisk(g, matchingFolder, f, helper);
481 deleteDirectoryStructure(f);
484 Resource fileRes = helper.findFile(g, subFileResources, name);
485 if (fileRes != null) {
486 if (matching.containsKey(fileRes))
487 throw new Exception("Matching file already in use" + f.getAbsolutePath() + " " + fileRes);
488 matching.put(fileRes, f);
489 writeDataToFile(g, fileRes, f);
492 throw new Exception("Cannot delete file " + f.getAbsolutePath());
496 // create files and folders, which have no matching graphFile (or folder)
497 for (Resource subFolder : subFolderResources) {
498 if (!matching.containsKey(subFolder)) {
499 String name = helper.getName(g, subFolder);
501 if (name.length() == 0)
502 throw new DatabaseException("Empty folder name for " + subFolder);
504 File newFolder = new File(folder.getAbsolutePath() + "/" + name);
505 if (!newFolder.mkdir()) {
506 throw new DatabaseException("Could not create folder " + name + " for resource " + subFolder);
508 writeFolderToDisk(g, subFolder, newFolder, helper);
512 for (Resource fileRes : subFileResources) {
513 if (!matching.containsKey(fileRes)) {
514 String name = helper.getName(g, fileRes);
515 File file = new File(folder.getAbsolutePath() + "/" + name);
516 writeDataToFile(g, fileRes, file);
524 public static Resource findWithName(ReadGraph g, Collection<Resource> resources, String name) throws ServiceException, NoSingleResultException, DoesNotContainValueException {
525 GraphFileResource gf = GraphFileResource.getInstance(g);
526 for (Resource r : resources)
527 if (name.equals(g.getRelatedValue(r, gf.HasResourceName)))
533 * Deletes the directory and all it contents.
537 public static void deleteDirectoryStructure(File dir) throws Exception{
538 deleteDirectoryStructure(dir, true);
542 * Deletes the directory's contents, but does not delete the directory.
546 public static void clearDirectoryStructure(File dir) throws Exception{
547 deleteDirectoryStructure(dir, false);
550 private static void deleteDirectoryStructure(File dir, boolean deleteDir) throws Exception{
551 File subFiles[] = dir.listFiles();
552 for (File f : subFiles) {
554 deleteDirectoryStructure(f,true);
557 throw new Exception("Cannot delete file " + f.getAbsolutePath());
562 throw new Exception("Cannot delete folder " + dir.getAbsolutePath());
567 public static Resource createFileReference(final Resource parent, final Path path) throws DatabaseException {
568 return Simantics.getSession().syncRequest(new WriteResultRequest<Resource>() {
571 public Resource perform(WriteGraph graph) throws DatabaseException {
572 Layer0 L0 = Layer0.getInstance(graph);
573 GraphFileResource GF = GraphFileResource.getInstance(graph);
574 Resource file = graph.newResource();
575 graph.claim(file, L0.PartOf, parent);
576 graph.claim(file, L0.InstanceOf, GF.File);
577 String name = path.getFileName().toString();
578 graph.claimLiteral(file, L0.HasName, name, Bindings.STRING);
579 graph.claimLiteral(file, GF.SystemPath, path.toAbsolutePath().toString(), Bindings.STRING);
586 * SCL java binding cannot make difference between methods that have two versions, one with Read/WriteGraph, and one without.
587 * Hence we have to introduce a set of methods which have no alternate versions.
591 public static void writeDataToGraphSCLhack(WriteGraph graph, File file, Resource graphFile) throws IOException, DatabaseException{
592 writeDataToGraph(graph, file, graphFile);
596 public static File toTempFileSCLhack(ReadGraph graph, Resource res)throws DatabaseException {
597 return toTempFile(graph, res);
600 public static void writeDataToFileSCLhack(ReadGraph graph, Resource res, File file) throws DatabaseException, IOException {
601 writeDataToFile(graph, res, file);
604 public static void toGraphSCLhack(WriteGraph graph, String filename, Resource graphFile) throws DatabaseException, IOException {
605 toGraph(graph, filename, graphFile);
609 public static byte[] getData(ReadGraph graph, Resource graphFile) throws DatabaseException{
610 GraphFileResource GF = GraphFileResource.getInstance(graph);
611 Resource fileData = graph.getSingleObject(graphFile, GF.HasFiledata);
612 TransferableGraphSupport tgs = graph.getService(TransferableGraphSupport.class);
613 InputStream input = tgs.getValueStream(graph, fileData);
614 DataInputStream di = new DataInputStream(input);
616 int length = di.readInt();
617 byte[] content = new byte[length];
620 } catch (IOException e) {
621 Logger.defaultLogError(e);
626 public static String getDataAsString(ReadGraph graph, Resource graphFile) throws DatabaseException{
627 byte[] content = getData(graph, graphFile);
628 String s = new String(content);