]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling/src/org/simantics/modeling/services/ComponentNamingStrategyBase.java
Index query fixes after commit 5e340942
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / services / ComponentNamingStrategyBase.java
1 package org.simantics.modeling.services;
2
3 import java.nio.CharBuffer;
4 import java.util.Comparator;
5 import java.util.Formatter;
6 import java.util.List;
7 import java.util.Locale;
8 import java.util.Set;
9 import java.util.TreeSet;
10
11 import org.simantics.db.ReadGraph;
12 import org.simantics.db.Resource;
13 import org.simantics.db.exception.DatabaseException;
14
15 /**
16  * An abstract base class that contains some helpers for implementing your own
17  * {@link ComponentNamingStrategy}. A default implementation is also provided
18  * for {@link #findFreshInstanceName(ReadGraph, Resource, Resource, Resource)}
19  * based on the Layer0 ontology <code>HasGeneratedNamePrefix</code> property.
20  * 
21  * @author Tuukka Lehtonen
22  */
23 public abstract class ComponentNamingStrategyBase implements ComponentNamingStrategy {
24
25     protected static final boolean DEBUG_ALL           = false;
26     protected static final boolean DEBUG_NAME_MATCHING = false | DEBUG_ALL;
27
28     protected final String         generatedNameFormat;
29     protected final boolean        caseInsensitive;
30
31     /**
32      * Base constructor for a naming strategy with the specified format as the
33      * generated name format. The format will receive two arguments:
34      * <ol>
35      * <li>proposed name as {@link String}</li>
36      * <li>an {@link Integer} that attempts to make the name unique within a
37      * context</li>
38      * </ol>
39      * The simplest format specification utilizing both values is
40      * <code>"%s %d"</code>, producing "FOO 1". Another example of a useful name
41      * format is <code>"%s%04d"</code>, producing "FOO0001". Reordering the
42      * arguments is also possible, e.g. <code>"%2$03d %1$s"</code>, producing
43      * "001 FOO".
44      * 
45      * <p>
46      * See {@link Formatter} for the format specification and how to customize
47      * it.
48      * 
49      * <p>
50      * This constructor will create a case-insensitive naming strategy.
51      * 
52      * @param generatedNameFormat the format to use for generated names
53      */
54     public ComponentNamingStrategyBase(String generatedNameFormat) {
55         this(generatedNameFormat, true);
56     }
57
58     /**
59      * Base constructor for a naming strategy with the specified format as the
60      * generated name format. The format will receive two arguments:
61      * <ol>
62      * <li>proposed name as {@link String}</li>
63      * <li>an {@link Integer} that attempts to make the name unique within a
64      * context</li>
65      * </ol>
66      * The simplest format specification utilizing both values is
67      * <code>"%s %d"</code>, producing "FOO 1". Another example of a useful name
68      * format is <code>"%s%04d"</code>, producing "FOO0001". Reordering the
69      * arguments is also possible, e.g. <code>"%2$03d %1$s"</code>, producing
70      * "001 FOO".
71      * 
72      * <p>
73      * See {@link Formatter} for the format specification and how to customize
74      * it.
75      * 
76      * @param generatedNameFormat the format to use for generated names
77      * @param caseInsensitive controls whether the strategy shall be case-insensitive or not
78      */
79     public ComponentNamingStrategyBase(String generatedNameFormat, boolean caseInsensitive) {
80         this.generatedNameFormat = generatedNameFormat;
81         this.caseInsensitive = caseInsensitive;
82     }
83
84     @Override
85     public String findFreshInstanceName(ReadGraph graph, Resource configurationRoot, Resource container,
86             Resource componentType) throws NamingException, DatabaseException {
87         String proposition = ComponentNamingUtil.generateProposition(graph, container, componentType);
88         return validateInstanceName(graph, configurationRoot, container, componentType, proposition);
89     }
90
91     @Override
92     public String validateInstanceName(ReadGraph graph, Resource configurationRoot, Resource container,
93             Resource componentType, String proposition) throws NamingException, DatabaseException {
94         return validateInstanceName(graph, configurationRoot, container, componentType, proposition, false);
95     }
96
97     @Override
98     public List<String> validateInstanceNames(
99             ReadGraph graph,
100             Resource configurationRoot,
101             List<String> propositions,
102             boolean acceptProposition,
103             Set<String> externallyReserved)
104                     throws NamingException, DatabaseException
105     {
106         throw new UnsupportedOperationException();
107     }
108
109     protected Comparator<Object> getComparator() {
110         return getComparator(caseInsensitive);
111     }
112
113     protected static Comparator<Object> getComparator(boolean caseInsensitive) {
114         return caseInsensitive ? CASE_INSENSITIVE_STRING_CHARBUFFER_COMPARATOR : CASE_SENSITIVE_STRING_CHARBUFFER_COMPARATOR;
115     }
116
117     protected static final Comparator<Object> CASE_SENSITIVE_STRING_CHARBUFFER_COMPARATOR = new Comparator<Object>() {
118         @Override
119         public int compare(Object o1, Object o2) {
120             String s1 = null;
121             String s2 = null;
122             if (o1 instanceof String)
123                 s1 = (String) o1;
124             if (o2 instanceof String)
125                 s2 = (String) o2;
126             if (s1 != null && s2 != null)
127                 return s1.compareTo((String) o2);
128
129             if (s1 == null && s2 == null)
130                 return 0;
131
132             if (s1 == null && (o1 instanceof CharBuffer))
133                 return -compare(s2, (CharBuffer) o1);
134             if (s2 == null && (o2 instanceof CharBuffer))
135                 return compare(s1, (CharBuffer) o2);
136
137             return 0;
138         }
139         int compare(String s, CharBuffer buf) {
140             int len1 = s.length();
141             int len2 = buf.position();
142             int n = Math.min(len1, len2);
143             int k = 0;
144             while (k < n) {
145                 char c1 = s.charAt(k);
146                 char c2 = buf.get(k);
147                 if (c1 != c2) {
148                     return c1 - c2;
149                 }
150                 k++;
151             }
152             return len1 - len2;
153         }
154     };
155
156     protected static final Comparator<Object> CASE_INSENSITIVE_STRING_CHARBUFFER_COMPARATOR = new Comparator<Object>() {
157         @Override
158         public int compare(Object o1, Object o2) {
159             String s1 = null;
160             String s2 = null;
161             if (o1 instanceof String)
162                 s1 = (String) o1;
163             if (o2 instanceof String)
164                 s2 = (String) o2;
165             if (s1 != null && s2 != null)
166                 return s1.compareToIgnoreCase((String) o2);
167
168             if (s1 == null && s2 == null)
169                 return 0;
170
171             if (s1 == null && (o1 instanceof CharBuffer))
172                 return -compare(s2, (CharBuffer) o1);
173             if (s2 == null && (o2 instanceof CharBuffer))
174                 return compare(s1, (CharBuffer) o2);
175
176             return 0;
177         }
178         int compare(String s, CharBuffer buf) {
179             int len1 = s.length();
180             int len2 = buf.position();
181             int n = Math.min(len1, len2);
182             int k = 0;
183             while (k < n) {
184                 char c1 = s.charAt(k);
185                 char c2 = buf.get(k);
186                 if (c1 != c2) {
187                     c1 = Character.toUpperCase(c1);
188                     c2 = Character.toUpperCase(c2);
189                     if (c1 != c2) {
190                         c1 = Character.toLowerCase(c1);
191                         c2 = Character.toLowerCase(c2);
192                         if (c1 != c2) {
193                             return c1 - c2;
194                         }
195                     }
196                 }
197                 k++;
198             }
199             return len1 - len2;
200         }
201     };
202
203     protected Set<String> findStartsWithMatches(Set<String> matchUniverse, String proposition) {
204         Set<String> result = new TreeSet<String>(getComparator());
205         if (matchUniverse.isEmpty())
206             return result;
207
208         for (String name : matchUniverse)
209             if (name.startsWith(proposition))
210                 result.add(name);
211         return result;
212     }
213
214     protected Set<String> findStartsWithMatches(Set<String> matchUniverse, String proposition, Set<String> result) {
215         if (matchUniverse.isEmpty())
216             return result;
217
218         for (String name : matchUniverse)
219             if (name.startsWith(proposition))
220                 result.add(name);
221         return result;
222     }
223
224     protected String findFreshName(Set<String> allReservedNames, Set<String> allRequestedNames, String proposition, boolean acceptProposition) {
225         if (allReservedNames == null)
226             throw new NullPointerException("null reserved name set");
227         if (proposition == null)
228             throw new NullPointerException("null proposition");
229
230         Set<String> startsWithMatches = findStartsWithMatches(allReservedNames, proposition);
231         if (allRequestedNames != null)
232             findStartsWithMatches(allRequestedNames, proposition, startsWithMatches);
233
234         return findFreshName(startsWithMatches, proposition, acceptProposition);
235     }
236
237     protected String findFreshName(Set<String> reserved, String proposition, boolean acceptProposition) {
238         return findFreshName(reserved, proposition, acceptProposition, generatedNameFormat);
239     }
240
241     protected String findFreshName(Set<String> reserved, String proposition, boolean acceptProposition, String nameFormat) {
242         if (proposition == null)
243             throw new NullPointerException("null proposition");
244
245         if (DEBUG_NAME_MATCHING)
246             System.out.println("Finding fresh proposed name '" + proposition + "' among matching reservations: " + reserved);
247
248         if (acceptProposition && !reserved.contains(proposition))
249             return proposition;
250
251         // Trying to optimize away unnecessary allocation of new String instances
252         // when looking for fresh names for objects.
253         CharBuffer cb = CharBuffer.allocate(proposition.length() + 10);
254         cb.mark();
255
256         @SuppressWarnings("resource")
257         Formatter formatter = new Formatter(cb, Locale.US);
258
259         //int i = reserved.size() + 1;
260         int i = 1;
261         for (;; ++i) {
262             cb.reset();
263             formatter.format(nameFormat, proposition, i);
264             if (!reserved.contains(cb)) {
265                 // Construct a String from the CharBuffer
266                 cb.limit(cb.position());
267                 cb.rewind();
268                 String result = cb.toString();
269                 if (DEBUG_NAME_MATCHING)
270                     System.out.println("\tfound fresh name: " + result);
271                 return result;
272             }
273         }
274     }
275
276 }