]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling/src/org/simantics/modeling/services/CaseInsensitiveComponentFunctionNamingStrategy.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / services / CaseInsensitiveComponentFunctionNamingStrategy.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.modeling.services;\r
13 \r
14 import java.util.ArrayList;\r
15 import java.util.Comparator;\r
16 import java.util.Formatter;\r
17 import java.util.List;\r
18 import java.util.Set;\r
19 import java.util.TreeSet;\r
20 \r
21 import org.simantics.databoard.Bindings;\r
22 import org.simantics.db.ReadGraph;\r
23 import org.simantics.db.Resource;\r
24 import org.simantics.db.common.Indexing;\r
25 import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;\r
26 import org.simantics.db.common.request.PossibleIndexRoot;\r
27 import org.simantics.db.common.request.UnaryRead;\r
28 import org.simantics.db.common.utils.NameUtils;\r
29 import org.simantics.db.exception.DatabaseException;\r
30 import org.simantics.db.layer0.genericrelation.IndexQueries;\r
31 import org.simantics.db.service.GraphChangeListenerSupport;\r
32 import org.simantics.layer0.Layer0;\r
33 import org.simantics.scl.runtime.function.Function;\r
34 import org.simantics.scl.runtime.function.Function1;\r
35 import org.simantics.scl.runtime.tuple.Tuple3;\r
36 import org.simantics.scl.runtime.tuple.Tuple4;\r
37 import org.simantics.structural.stubs.StructuralResource2;\r
38 \r
39 import gnu.trove.set.hash.THashSet;\r
40 \r
41 /**\r
42  * A component naming strategy implementation for structural models based on an\r
43  * SCL function that lists used names in a model.\r
44  * \r
45  * <p>\r
46  * The type of the function is expected to be:\r
47  * <code>ReadGraph => Resource -> String -> Integer -> List&lt;Map&lt;String,Object&gt;&gt;</code>\r
48  * \r
49  * @author Tuukka Lehtonen\r
50  * \r
51  * @see ComponentNamingStrategy\r
52  */\r
53 public class CaseInsensitiveComponentFunctionNamingStrategy extends ComponentNamingStrategyBase {\r
54 \r
55     protected static final boolean                                  DEBUG_INDEX_SEARCH = false | DEBUG_ALL;\r
56 \r
57     @SuppressWarnings("rawtypes")\r
58     private final Function                                          index;\r
59 \r
60     /**\r
61      * A filter that is applied to all user-provided name propositions before\r
62      * processing them.\r
63      */\r
64     private Function1<String, String>                               propositionPreFilter;\r
65 \r
66     /**\r
67      * Construct an index-based naming strategy with "%s %d" as the generated\r
68      * name format. See {@link Formatter} for the format specification.\r
69      * \r
70      * @param index the index function for looking up used names. The function\r
71      *        must be of type\r
72      *        <code>ReadGraph => Resource -> String -> Integer -> List&lt;Map&lt;String,Object&gt;&gt;</code>\r
73      */\r
74     @SuppressWarnings("rawtypes")\r
75     public CaseInsensitiveComponentFunctionNamingStrategy(Function index) {\r
76         this("%s %d", index);\r
77     }\r
78 \r
79     /**\r
80      * Construct an index-based naming strategy with the specified format as the\r
81      * generated name format. See {@link Formatter} for the format\r
82      * specification.\r
83      * \r
84      * @param generatedNameFormat the format to use for generated names, see\r
85      *        {@link Formatter}\r
86      * @param index the index function for looking up used names. The function\r
87      *        must be of type\r
88      *        <code>ReadGraph => Resource -> String -> Integer -> List&lt;Map&lt;String,Object&gt;&gt;</code>\r
89      */\r
90     @SuppressWarnings("rawtypes")\r
91     public CaseInsensitiveComponentFunctionNamingStrategy(String generatedNameFormat, Function index) {\r
92         super(generatedNameFormat);\r
93         this.index = index;\r
94     }\r
95 \r
96     /**\r
97      * Construct an index-based naming strategy with the specified format as the\r
98      * generated name format. See {@link Formatter} for the format\r
99      * specification.\r
100      * \r
101      * @param generatedNameFormat the format to use for generated names, see\r
102      *        {@link Formatter}\r
103      * @param index the index function for looking up used names. The function\r
104      *        must be of type\r
105      *        <code>ReadGraph => Resource -> String -> Integer -> List&lt;Map&lt;String,Object&gt;&gt;</code>\r
106      * @param an optional function to \r
107      */\r
108     @SuppressWarnings("rawtypes")\r
109     public CaseInsensitiveComponentFunctionNamingStrategy(String generatedNameFormat, Function index, Function1<String, String> propositionPreFilter) {\r
110         super(generatedNameFormat);\r
111         this.index = index;\r
112         this.propositionPreFilter = propositionPreFilter;\r
113     }\r
114 \r
115     CaseInsensitiveComponentNamingStrategy2 fallbackStrategy = null;\r
116 \r
117     @Override\r
118     public String validateInstanceName(ReadGraph graph,\r
119                 Resource configurationRoot, Resource component, String proposition, boolean acceptProposition)\r
120                 throws NamingException, DatabaseException {\r
121 \r
122         String lowercaseProposition = proposition.toLowerCase();\r
123         \r
124         Layer0 L0 = Layer0.getInstance(graph);\r
125         String name = graph.getPossibleRelatedValue(component, L0.HasName, Bindings.STRING);\r
126         if (name != null) {\r
127         \r
128                 String lowercaseName = name.toLowerCase();\r
129                 if(lowercaseName.startsWith(lowercaseProposition)) {\r
130                         \r
131                 Resource indexRoot = graph.syncRequest(new PossibleIndexRoot(configurationRoot));\r
132                 if (indexRoot != null) {\r
133                         \r
134                     synchronized (this) {\r
135                         \r
136                         String search = "Name:" + lowercaseName + "*";\r
137                         @SuppressWarnings("unchecked")\r
138                         List<Resource> components = (List<Resource>) index.apply(graph, indexRoot, search, Integer.MAX_VALUE);\r
139 \r
140                         Set<Resource> rs = new THashSet<Resource>();\r
141                         for (Resource componentResult : components) {\r
142                             if (DEBUG_INDEX_SEARCH)\r
143                                 System.out.println(getClass().getSimpleName() + ": found " + componentResult);\r
144                             String n = graph.getPossibleRelatedValue(componentResult, L0.HasName, Bindings.STRING);\r
145                             if (n != null && n.toLowerCase().equals(lowercaseName))\r
146                                 rs.add(componentResult);\r
147                         }\r
148                         \r
149                         Cache c = getCache(graph, indexRoot);\r
150                         \r
151                         if(rs.size() == 0) { \r
152                                 if(!c.getRequested().contains(name)) {\r
153                                         c.addRequested(name);\r
154                                         return name;\r
155                                 }\r
156                         } else if(rs.size() == 1) {\r
157                                 if(component.equals(rs.iterator().next())) {\r
158                                 return name;\r
159                                 }\r
160                         }\r
161 \r
162                     }\r
163                         \r
164                 }\r
165                         \r
166                 }\r
167                 \r
168         }\r
169 \r
170         StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
171         Resource container = graph.getSingleObject(component, L0.PartOf);\r
172         Resource componentType = graph.getSingleType(component, STR.Component);\r
173         return validateInstanceName(graph, configurationRoot, container, componentType, proposition, acceptProposition);\r
174         \r
175     }\r
176     \r
177     static class ComponentsRequest extends UnaryRead<Tuple4, Set<String>>{\r
178 \r
179                 public ComponentsRequest(Tuple4 parameter) {\r
180                         super(parameter);\r
181                 }\r
182 \r
183                 @Override\r
184                 public Set<String> perform(ReadGraph graph) throws DatabaseException {\r
185                         \r
186                         Resource indexRoot = (Resource)parameter.get(0);\r
187                         Function index = (Function)parameter.get(1);\r
188                         String search = (String)parameter.get(2);\r
189                         Comparator<Object> comparator = (Comparator<Object>)parameter.get(3);\r
190                         \r
191             List<Resource> components = (List<Resource>) index.apply(graph, indexRoot, search, Integer.MAX_VALUE);\r
192             Set<String> reserved = new TreeSet<String>(comparator);\r
193 \r
194             Layer0 L0 = Layer0.getInstance(graph);\r
195             for (Resource componentResult : components) {\r
196                 if (DEBUG_INDEX_SEARCH)\r
197                     System.out.println(getClass().getSimpleName() + ": found " + componentResult);\r
198                 String name = graph.getPossibleRelatedValue(componentResult, L0.HasName, Bindings.STRING);\r
199                 if (name != null)\r
200                     reserved.add(name);\r
201             }\r
202             \r
203             System.err.println("found " + reserved.size() + " components");\r
204             \r
205             return reserved;\r
206 \r
207                 }\r
208         \r
209     }\r
210     \r
211     @Override\r
212     public String validateInstanceName(ReadGraph graph, Resource configurationRoot, Resource container,\r
213             Resource componentType, String proposition, boolean acceptProposition) throws NamingException, DatabaseException {\r
214         Resource indexRoot = graph.syncRequest(new PossibleIndexRoot(configurationRoot));\r
215         if (indexRoot == null) {\r
216             System.err.println("Could not find index root from configuration root '" + NameUtils.getSafeName(graph, configurationRoot, true) + "'");\r
217             if(fallbackStrategy == null)\r
218                 fallbackStrategy = \r
219                     new CaseInsensitiveComponentNamingStrategy2(graph.getService(GraphChangeListenerSupport.class), generatedNameFormat);\r
220             return fallbackStrategy.validateInstanceName(graph, configurationRoot, container, componentType, proposition, acceptProposition);\r
221         }\r
222 \r
223         if (propositionPreFilter != null)\r
224             proposition = propositionPreFilter.apply(proposition);\r
225 \r
226         synchronized (this) {\r
227                 \r
228             String search = "Name:" + proposition + "*";\r
229             \r
230             Set<String> reserved = graph.syncRequest(new ComponentsRequest(new Tuple4(indexRoot, index, search, getComparator())), TransientCacheAsyncListener.instance());\r
231 \r
232             Cache cache = getCache(graph, indexRoot);\r
233 \r
234             findStartsWithMatches(cache.getRequested(), proposition, reserved);\r
235 \r
236             String result = findFreshName(reserved, proposition, acceptProposition);\r
237             cache.addRequested(result);\r
238 \r
239             if (DEBUG_INDEX_SEARCH)\r
240                 System.out.println(getClass().getSimpleName() + ": validated instance name " + result);\r
241 \r
242             return result;\r
243         }\r
244     }\r
245 \r
246     @Override\r
247     public List<String> validateInstanceNames(\r
248             ReadGraph graph,\r
249             Resource configurationRoot,\r
250             List<String> propositions,\r
251             boolean acceptProposition,\r
252             Set<String> externallyReserved) throws NamingException, DatabaseException\r
253     {\r
254         Resource indexRoot = graph.syncRequest(new PossibleIndexRoot(configurationRoot));\r
255         if (indexRoot == null)\r
256             throw new NamingException("Could not find index root from configuration root '" + NameUtils.getSafeName(graph, configurationRoot, true) + "'");\r
257 \r
258         Layer0 L0 = Layer0.getInstance(graph);\r
259 \r
260         synchronized (this) {\r
261             List<String> result = new ArrayList<String>(propositions.size());\r
262             Set<String> reserved = new TreeSet<String>(getComparator());\r
263 \r
264             for (String proposition : propositions) {\r
265                 if (propositionPreFilter != null)\r
266                     proposition = propositionPreFilter.apply(proposition);\r
267 \r
268                 String search = "Name:" + IndexQueries.escape( proposition ) + "*";\r
269                 @SuppressWarnings("unchecked")\r
270                 List<Resource> components = (List<Resource>) index.apply(graph, indexRoot, search, Integer.MAX_VALUE);\r
271 \r
272                 if (DEBUG_INDEX_SEARCH)\r
273                     System.out.println(getClass().getSimpleName() + ": found " + components.size()\r
274                             + " index results for index root " + indexRoot + " & configurationRoot " + configurationRoot\r
275                             + " & proposition '" + proposition + "':");\r
276 \r
277                 reserved.clear();\r
278                 for (Resource componentResult : components) {\r
279                     if (DEBUG_INDEX_SEARCH)\r
280                         System.out.println(getClass().getSimpleName() + ": found " + componentResult);\r
281                     String name = graph.getPossibleRelatedValue(componentResult, L0.HasName, Bindings.STRING);\r
282                     if (name != null)\r
283                         reserved.add(name);\r
284                 }\r
285 \r
286                 if (externallyReserved != null)\r
287                     reserved.addAll(externallyReserved);\r
288                 reserved.addAll(result);\r
289                 String name = findFreshName(reserved, proposition, acceptProposition);\r
290 \r
291                 result.add(name);\r
292 \r
293                 if (DEBUG_INDEX_SEARCH)\r
294                     System.out.println(getClass().getSimpleName() + ": validated instance name " + proposition + " -> " + name);\r
295             }\r
296 \r
297             if (DEBUG_INDEX_SEARCH)\r
298                 System.out.println(getClass().getSimpleName() + ": validated instance names " + propositions + " -> " + result);\r
299 \r
300             return result;\r
301         }\r
302     }\r
303 \r
304     private Cache getCache(ReadGraph graph, Resource root) throws DatabaseException {\r
305         Cache cache = Indexing.getCache(root, Cache.class);\r
306         if(cache != null) return cache;\r
307         synchronized (this) {\r
308             cache = Indexing.getCache(root, Cache.class);\r
309             if(cache != null) return cache;\r
310             return Indexing.createCache(root, new Cache(caseInsensitive)); \r
311         }\r
312     }\r
313 \r
314     static class Cache {\r
315         \r
316         private final Set<String> requested;\r
317 \r
318         Cache(boolean caseInsensitive) {\r
319             this.requested = new TreeSet<String>(getComparator(caseInsensitive));\r
320         }\r
321 \r
322         public Set<String> getRequested() {\r
323             return requested;\r
324         }\r
325 \r
326         public void addRequested(String name) {\r
327             requested.add(name);\r
328         }\r
329 \r
330         @Override\r
331         public String toString() {\r
332             return getClass().getSimpleName() + ": " + requested;\r
333         }\r
334 \r
335     }\r
336 \r
337 }\r