1 package org.simantics.modeling.services;
\r
3 import java.nio.CharBuffer;
\r
4 import java.util.Comparator;
\r
5 import java.util.Formatter;
\r
6 import java.util.List;
\r
7 import java.util.Locale;
\r
8 import java.util.Set;
\r
9 import java.util.TreeSet;
\r
11 import org.simantics.db.ReadGraph;
\r
12 import org.simantics.db.Resource;
\r
13 import org.simantics.db.exception.DatabaseException;
\r
16 * An abstract base class that contains some helpers for implementing your own
\r
17 * {@link ComponentNamingStrategy}. A default implementation is also provided
\r
18 * for {@link #findFreshInstanceName(ReadGraph, Resource, Resource, Resource)}
\r
19 * based on the Layer0 ontology <code>HasGeneratedNamePrefix</code> property.
\r
21 * @author Tuukka Lehtonen
\r
23 public abstract class ComponentNamingStrategyBase implements ComponentNamingStrategy {
\r
25 protected static final boolean DEBUG_ALL = false;
\r
26 protected static final boolean DEBUG_NAME_MATCHING = false | DEBUG_ALL;
\r
28 protected final String generatedNameFormat;
\r
29 protected final boolean caseInsensitive;
\r
32 * Base constructor for a naming strategy with the specified format as the
\r
33 * generated name format. The format will receive two arguments:
\r
35 * <li>proposed name as {@link String}</li>
\r
36 * <li>an {@link Integer} that attempts to make the name unique within a
\r
39 * The simplest format specification utilizing both values is
\r
40 * <code>"%s %d"</code>, producing "FOO 1". Another example of a useful name
\r
41 * format is <code>"%s%04d"</code>, producing "FOO0001". Reordering the
\r
42 * arguments is also possible, e.g. <code>"%2$03d %1$s"</code>, producing
\r
46 * See {@link Formatter} for the format specification and how to customize
\r
50 * This constructor will create a case-insensitive naming strategy.
\r
52 * @param generatedNameFormat the format to use for generated names
\r
54 public ComponentNamingStrategyBase(String generatedNameFormat) {
\r
55 this(generatedNameFormat, true);
\r
59 * Base constructor for a naming strategy with the specified format as the
\r
60 * generated name format. The format will receive two arguments:
\r
62 * <li>proposed name as {@link String}</li>
\r
63 * <li>an {@link Integer} that attempts to make the name unique within a
\r
66 * The simplest format specification utilizing both values is
\r
67 * <code>"%s %d"</code>, producing "FOO 1". Another example of a useful name
\r
68 * format is <code>"%s%04d"</code>, producing "FOO0001". Reordering the
\r
69 * arguments is also possible, e.g. <code>"%2$03d %1$s"</code>, producing
\r
73 * See {@link Formatter} for the format specification and how to customize
\r
76 * @param generatedNameFormat the format to use for generated names
\r
77 * @param caseInsensitive controls whether the strategy shall be case-insensitive or not
\r
79 public ComponentNamingStrategyBase(String generatedNameFormat, boolean caseInsensitive) {
\r
80 this.generatedNameFormat = generatedNameFormat;
\r
81 this.caseInsensitive = caseInsensitive;
\r
85 public String findFreshInstanceName(ReadGraph graph, Resource configurationRoot, Resource container,
\r
86 Resource componentType) throws NamingException, DatabaseException {
\r
87 String proposition = ComponentNamingUtil.generateProposition(graph, container, componentType);
\r
88 return validateInstanceName(graph, configurationRoot, container, componentType, proposition);
\r
92 public String validateInstanceName(ReadGraph graph, Resource configurationRoot, Resource container,
\r
93 Resource componentType, String proposition) throws NamingException, DatabaseException {
\r
94 return validateInstanceName(graph, configurationRoot, container, componentType, proposition, false);
\r
98 public List<String> validateInstanceNames(
\r
100 Resource configurationRoot,
\r
101 List<String> propositions,
\r
102 boolean acceptProposition,
\r
103 Set<String> externallyReserved)
\r
104 throws NamingException, DatabaseException
\r
106 throw new UnsupportedOperationException();
\r
109 protected Comparator<Object> getComparator() {
\r
110 return getComparator(caseInsensitive);
\r
113 protected static Comparator<Object> getComparator(boolean caseInsensitive) {
\r
114 return caseInsensitive ? CASE_INSENSITIVE_STRING_CHARBUFFER_COMPARATOR : CASE_SENSITIVE_STRING_CHARBUFFER_COMPARATOR;
\r
117 protected static final Comparator<Object> CASE_SENSITIVE_STRING_CHARBUFFER_COMPARATOR = new Comparator<Object>() {
\r
119 public int compare(Object o1, Object o2) {
\r
122 if (o1 instanceof String)
\r
124 if (o2 instanceof String)
\r
126 if (s1 != null && s2 != null)
\r
127 return s1.compareTo((String) o2);
\r
129 if (s1 == null && s2 == null)
\r
132 if (s1 == null && (o1 instanceof CharBuffer))
\r
133 return -compare(s2, (CharBuffer) o1);
\r
134 if (s2 == null && (o2 instanceof CharBuffer))
\r
135 return compare(s1, (CharBuffer) o2);
\r
139 int compare(String s, CharBuffer buf) {
\r
140 int len1 = s.length();
\r
141 int len2 = buf.position();
\r
142 int n = Math.min(len1, len2);
\r
145 char c1 = s.charAt(k);
\r
146 char c2 = buf.get(k);
\r
152 return len1 - len2;
\r
156 protected static final Comparator<Object> CASE_INSENSITIVE_STRING_CHARBUFFER_COMPARATOR = new Comparator<Object>() {
\r
158 public int compare(Object o1, Object o2) {
\r
161 if (o1 instanceof String)
\r
163 if (o2 instanceof String)
\r
165 if (s1 != null && s2 != null)
\r
166 return s1.compareToIgnoreCase((String) o2);
\r
168 if (s1 == null && s2 == null)
\r
171 if (s1 == null && (o1 instanceof CharBuffer))
\r
172 return -compare(s2, (CharBuffer) o1);
\r
173 if (s2 == null && (o2 instanceof CharBuffer))
\r
174 return compare(s1, (CharBuffer) o2);
\r
178 int compare(String s, CharBuffer buf) {
\r
179 int len1 = s.length();
\r
180 int len2 = buf.position();
\r
181 int n = Math.min(len1, len2);
\r
184 char c1 = s.charAt(k);
\r
185 char c2 = buf.get(k);
\r
187 c1 = Character.toUpperCase(c1);
\r
188 c2 = Character.toUpperCase(c2);
\r
190 c1 = Character.toLowerCase(c1);
\r
191 c2 = Character.toLowerCase(c2);
\r
199 return len1 - len2;
\r
203 protected Set<String> findStartsWithMatches(Set<String> matchUniverse, String proposition) {
\r
204 Set<String> result = new TreeSet<String>(getComparator());
\r
205 if (matchUniverse.isEmpty())
\r
208 for (String name : matchUniverse)
\r
209 if (name.startsWith(proposition))
\r
214 protected Set<String> findStartsWithMatches(Set<String> matchUniverse, String proposition, Set<String> result) {
\r
215 if (matchUniverse.isEmpty())
\r
218 for (String name : matchUniverse)
\r
219 if (name.startsWith(proposition))
\r
224 protected String findFreshName(Set<String> allReservedNames, Set<String> allRequestedNames, String proposition, boolean acceptProposition) {
\r
225 if (allReservedNames == null)
\r
226 throw new NullPointerException("null reserved name set");
\r
227 if (proposition == null)
\r
228 throw new NullPointerException("null proposition");
\r
230 Set<String> startsWithMatches = findStartsWithMatches(allReservedNames, proposition);
\r
231 if (allRequestedNames != null)
\r
232 findStartsWithMatches(allRequestedNames, proposition, startsWithMatches);
\r
234 return findFreshName(startsWithMatches, proposition, acceptProposition);
\r
237 protected String findFreshName(Set<String> reserved, String proposition, boolean acceptProposition) {
\r
238 return findFreshName(reserved, proposition, acceptProposition, generatedNameFormat);
\r
241 protected String findFreshName(Set<String> reserved, String proposition, boolean acceptProposition, String nameFormat) {
\r
242 if (proposition == null)
\r
243 throw new NullPointerException("null proposition");
\r
245 if (DEBUG_NAME_MATCHING)
\r
246 System.out.println("Finding fresh proposed name '" + proposition + "' among matching reservations: " + reserved);
\r
248 if (acceptProposition && !reserved.contains(proposition))
\r
249 return proposition;
\r
251 // Trying to optimize away unnecessary allocation of new String instances
\r
252 // when looking for fresh names for objects.
\r
253 CharBuffer cb = CharBuffer.allocate(proposition.length() + 10);
\r
256 @SuppressWarnings("resource")
\r
257 Formatter formatter = new Formatter(cb, Locale.US);
\r
259 //int i = reserved.size() + 1;
\r
263 formatter.format(nameFormat, proposition, i);
\r
264 if (!reserved.contains(cb)) {
\r
265 // Construct a String from the CharBuffer
\r
266 cb.limit(cb.position());
\r
268 String result = cb.toString();
\r
269 if (DEBUG_NAME_MATCHING)
\r
270 System.out.println("\tfound fresh name: " + result);
\r