Export did not manage case differences (mode vs. Mode)
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / utils / DumpOntologyStructure.java
1 package org.simantics.modeling.utils;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.nio.charset.StandardCharsets;
6 import java.util.Collection;
7 import java.util.HashMap;
8 import java.util.HashSet;
9 import java.util.Map;
10 import java.util.Set;
11 import java.util.TreeMap;
12
13 import org.simantics.Simantics;
14 import org.simantics.db.ReadGraph;
15 import org.simantics.db.Resource;
16 import org.simantics.db.common.utils.CommonDBUtils;
17 import org.simantics.db.common.utils.NameUtils;
18 import org.simantics.db.exception.DatabaseException;
19 import org.simantics.db.layer0.variable.Variable;
20 import org.simantics.db.layer0.variable.Variables;
21 import org.simantics.layer0.Layer0;
22 import org.simantics.modeling.ModelingResources;
23 import org.simantics.scl.runtime.function.Function;
24 import org.simantics.utils.FileUtils;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 import gnu.trove.list.array.TByteArrayList;
29
30 public class DumpOntologyStructure {
31
32     private static final Logger LOGGER = LoggerFactory.getLogger(DumpOntologyStructure.class);
33
34     private Resource ontology;
35
36     private Map<Resource, String> names = new HashMap<>();
37     private Map<Resource,Resource> parents = new TreeMap<>();
38     private Map<Resource, File> libraryFolders = new HashMap<>();
39     private Map<Resource, byte[]> contentDumps = new HashMap<>();
40
41     private void readNameAndParent(ReadGraph graph, Resource container, Resource r) throws DatabaseException {
42         String name = NameUtils.getSafeName(graph, r);
43         parents.put(r, container);
44         names.put(r, FileUtils.escapeFileName(name));
45     }
46
47     /*
48      * This shall return containers sorted by full path.
49      * This makes processing order stable and ensures that
50      * directories are processed before their contents. 
51      */
52     private Collection<Resource> sortedContainers(File rootFolder) {
53         Set<Resource> parentSet = new HashSet<Resource>(parents.values());
54         TreeMap<String,Resource> result = new TreeMap<>();
55         for(Resource r : parentSet) {
56             File f = getFolder(rootFolder, r);
57             result.put(f.getAbsolutePath(), r);
58         }
59         return result.values();
60     }
61
62     private Collection<Resource> sortedResources(File rootFolder) {
63         TreeMap<String,Resource> result = new TreeMap<>();
64         for(Resource r : parents.keySet()) {
65             byte[] dump = contentDumps.get(r);
66             if(dump == null)
67                 dump = "".getBytes(StandardCharsets.UTF_8);
68             if(isParent(r)) {
69                 if(dump.length > 0) {
70                     File f = new File(getFolder(rootFolder, r), "__contents__");
71                     result.put(f.getAbsolutePath(), r);
72                 }
73             } else {
74                 File f = getFile(rootFolder, r);
75                 result.put(f.getAbsolutePath(), r);
76             }
77         }
78         return result.values();
79     }
80
81     private void readHierarchy(ReadGraph graph, Resource container) throws DatabaseException {
82         Layer0 L0 = Layer0.getInstance(graph);
83         for(Resource r : CommonDBUtils.objectsWithType(graph, container, L0.ConsistsOf, L0.Entity)) {
84             try {
85                 readNameAndParent(graph, container, r);
86                 readHierarchy(graph, r);
87             } catch (DatabaseException e) {
88                 LOGGER.error("Error while reading content dump hierarchy for " + r, e);
89             }
90         }
91     }
92
93     private void readGeneric(ReadGraph graph) throws DatabaseException {
94         ModelingResources MOD = ModelingResources.getInstance(graph);
95         for(Resource r : parents.keySet()) {
96             if(contentDumps.containsKey(r))
97                 continue;
98             TByteArrayList result = new TByteArrayList();
99             try {
100                 TreeMap<String,Resource> sortedTypes = new TreeMap<>();
101                 for(Resource type : graph.getTypes(r)) {
102                     String uri = graph.getPossibleURI(type);
103                     if(uri != null)
104                         sortedTypes.put(uri, type);
105                 }
106                 for(Resource type : sortedTypes.values()) {
107                     try {
108                         Variable typeVariable = Variables.getVariable(graph, type);
109                         @SuppressWarnings("rawtypes")
110                         Function f = typeVariable.getPossiblePropertyValue(graph, MOD.contentDumpFunction);
111                         if(f != null) {
112                             @SuppressWarnings("unchecked")
113                             byte[] dump = (byte[])Simantics.applySCLRead(graph, f, r);
114                             if(dump != null) {
115                                 result.add(dump);
116                             }
117                         }
118                     } catch (DatabaseException e) {
119                         LOGGER.error("Error while computing content dump for " + r, e);
120                     }
121                 }
122                 if(result.size() > 0)
123                     contentDumps.put(r, result.toArray());
124             } catch (DatabaseException e) {
125                 LOGGER.error("Error while computing content dump for " + r, e);
126             }
127         }
128     }
129
130     public DumpOntologyStructure read(ReadGraph graph, Resource ontology) throws DatabaseException {
131         this.ontology = ontology;
132         readHierarchy(graph, ontology);
133         readGeneric(graph);
134         return this;
135     }
136
137     private File escapeFile(File file) {
138         if(file.exists())
139             return file;
140         return new File(escapeFile(file.getParentFile()), FileUtils.escapeFileName(file.getName()));
141     }
142
143     public void write(File unsafeFolder) throws IOException {
144         File folder = escapeFile(unsafeFolder);
145         FileUtils.delete(folder.toPath());
146         folder.getParentFile().mkdirs();
147         writeDirectories(folder);
148         writeResources(folder);
149     }
150
151     Resource getParent(Resource r) {
152         return parents.get(r);
153     }
154     
155     private File getFolder(File root, Resource library) {
156         if(ontology.equals(library))
157             return root;
158         Resource parent = getParent(library);
159         if(parent == null)
160             throw new IllegalStateException("null parent for " + library);
161         File parentFolder = getFolder(root, parent);
162         return new File(parentFolder, names.get(library)); 
163     }
164
165     private File getFile(File rootFolder, Resource r) {
166         Resource parent = getParent(r);
167         File folder = getFolder(rootFolder, parent);
168         return new File(folder, names.get(r));
169     }
170
171     private File makeUnique(File original, Resource r) {
172         int counter = 2;
173         File file = new File(original.getParent(), original.getName());
174         File test = file;
175         while(test.exists()) {
176             // Here we have a name clash with small and big letters! (windows)
177             test = new File(file.getParent(), file.getName() + "____" + (counter++));
178         }
179         // Enforce this renaming in future operations also
180         names.put(r, test.getName());
181         return test;
182     }
183     
184     private void writeDirectories(File rootFolder) {
185         // Here stuff shall be returned in alphabetical order
186         for(Resource library : sortedContainers(rootFolder)) {
187             File folder = makeUnique(getFolder(rootFolder, library), library);
188             folder.mkdirs();
189             libraryFolders.put(library, folder);
190         }
191     }
192
193     private void writeResources(File rootFolder) throws IOException {
194         // Here stuff shall be returned in alphabetical order
195         for(Resource r : sortedResources(rootFolder)) {
196             writeResource(rootFolder, r);
197         }
198     }
199
200     private boolean isParent(Resource r) {
201         return parents.values().contains(r);
202     }
203
204     private void writeResource(File rootFolder, Resource resource) throws IOException {
205         byte[] dump = contentDumps.get(resource);
206         if(dump == null)
207             dump = "".getBytes(StandardCharsets.UTF_8);
208         if(isParent(resource)) {
209             if(dump.length > 0)
210                 FileUtils.writeFile(new File(getFolder(rootFolder, resource), "__contents__"), dump);
211         } else {
212             write(rootFolder, resource, dump);
213         }
214     }
215
216     private void write(File rootFolder, Resource resource, byte[] bytes) throws IOException {
217         FileUtils.writeFile(makeUnique(getFile(rootFolder, resource), resource), bytes);
218     }
219
220 }