-package org.simantics.modeling.services;\r
-\r
-import java.nio.CharBuffer;\r
-import java.util.Comparator;\r
-import java.util.Formatter;\r
-import java.util.List;\r
-import java.util.Locale;\r
-import java.util.Set;\r
-import java.util.TreeSet;\r
-\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.exception.DatabaseException;\r
-\r
-/**\r
- * An abstract base class that contains some helpers for implementing your own\r
- * {@link ComponentNamingStrategy}. A default implementation is also provided\r
- * for {@link #findFreshInstanceName(ReadGraph, Resource, Resource, Resource)}\r
- * based on the Layer0 ontology <code>HasGeneratedNamePrefix</code> property.\r
- * \r
- * @author Tuukka Lehtonen\r
- */\r
-public abstract class ComponentNamingStrategyBase implements ComponentNamingStrategy {\r
-\r
- protected static final boolean DEBUG_ALL = false;\r
- protected static final boolean DEBUG_NAME_MATCHING = false | DEBUG_ALL;\r
-\r
- protected final String generatedNameFormat;\r
- protected final boolean caseInsensitive;\r
-\r
- /**\r
- * Base constructor for a naming strategy with the specified format as the\r
- * generated name format. The format will receive two arguments:\r
- * <ol>\r
- * <li>proposed name as {@link String}</li>\r
- * <li>an {@link Integer} that attempts to make the name unique within a\r
- * context</li>\r
- * </ol>\r
- * The simplest format specification utilizing both values is\r
- * <code>"%s %d"</code>, producing "FOO 1". Another example of a useful name\r
- * format is <code>"%s%04d"</code>, producing "FOO0001". Reordering the\r
- * arguments is also possible, e.g. <code>"%2$03d %1$s"</code>, producing\r
- * "001 FOO".\r
- * \r
- * <p>\r
- * See {@link Formatter} for the format specification and how to customize\r
- * it.\r
- * \r
- * <p>\r
- * This constructor will create a case-insensitive naming strategy.\r
- * \r
- * @param generatedNameFormat the format to use for generated names\r
- */\r
- public ComponentNamingStrategyBase(String generatedNameFormat) {\r
- this(generatedNameFormat, true);\r
- }\r
-\r
- /**\r
- * Base constructor for a naming strategy with the specified format as the\r
- * generated name format. The format will receive two arguments:\r
- * <ol>\r
- * <li>proposed name as {@link String}</li>\r
- * <li>an {@link Integer} that attempts to make the name unique within a\r
- * context</li>\r
- * </ol>\r
- * The simplest format specification utilizing both values is\r
- * <code>"%s %d"</code>, producing "FOO 1". Another example of a useful name\r
- * format is <code>"%s%04d"</code>, producing "FOO0001". Reordering the\r
- * arguments is also possible, e.g. <code>"%2$03d %1$s"</code>, producing\r
- * "001 FOO".\r
- * \r
- * <p>\r
- * See {@link Formatter} for the format specification and how to customize\r
- * it.\r
- * \r
- * @param generatedNameFormat the format to use for generated names\r
- * @param caseInsensitive controls whether the strategy shall be case-insensitive or not\r
- */\r
- public ComponentNamingStrategyBase(String generatedNameFormat, boolean caseInsensitive) {\r
- this.generatedNameFormat = generatedNameFormat;\r
- this.caseInsensitive = caseInsensitive;\r
- }\r
-\r
- @Override\r
- public String findFreshInstanceName(ReadGraph graph, Resource configurationRoot, Resource container,\r
- Resource componentType) throws NamingException, DatabaseException {\r
- String proposition = ComponentNamingUtil.generateProposition(graph, container, componentType);\r
- return validateInstanceName(graph, configurationRoot, container, componentType, proposition);\r
- }\r
-\r
- @Override\r
- public String validateInstanceName(ReadGraph graph, Resource configurationRoot, Resource container,\r
- Resource componentType, String proposition) throws NamingException, DatabaseException {\r
- return validateInstanceName(graph, configurationRoot, container, componentType, proposition, false);\r
- }\r
-\r
- @Override\r
- public List<String> validateInstanceNames(\r
- ReadGraph graph,\r
- Resource configurationRoot,\r
- List<String> propositions,\r
- boolean acceptProposition,\r
- Set<String> externallyReserved)\r
- throws NamingException, DatabaseException\r
- {\r
- throw new UnsupportedOperationException();\r
- }\r
-\r
- protected Comparator<Object> getComparator() {\r
- return getComparator(caseInsensitive);\r
- }\r
-\r
- protected static Comparator<Object> getComparator(boolean caseInsensitive) {\r
- return caseInsensitive ? CASE_INSENSITIVE_STRING_CHARBUFFER_COMPARATOR : CASE_SENSITIVE_STRING_CHARBUFFER_COMPARATOR;\r
- }\r
-\r
- protected static final Comparator<Object> CASE_SENSITIVE_STRING_CHARBUFFER_COMPARATOR = new Comparator<Object>() {\r
- @Override\r
- public int compare(Object o1, Object o2) {\r
- String s1 = null;\r
- String s2 = null;\r
- if (o1 instanceof String)\r
- s1 = (String) o1;\r
- if (o2 instanceof String)\r
- s2 = (String) o2;\r
- if (s1 != null && s2 != null)\r
- return s1.compareTo((String) o2);\r
-\r
- if (s1 == null && s2 == null)\r
- return 0;\r
-\r
- if (s1 == null && (o1 instanceof CharBuffer))\r
- return -compare(s2, (CharBuffer) o1);\r
- if (s2 == null && (o2 instanceof CharBuffer))\r
- return compare(s1, (CharBuffer) o2);\r
-\r
- return 0;\r
- }\r
- int compare(String s, CharBuffer buf) {\r
- int len1 = s.length();\r
- int len2 = buf.position();\r
- int n = Math.min(len1, len2);\r
- int k = 0;\r
- while (k < n) {\r
- char c1 = s.charAt(k);\r
- char c2 = buf.get(k);\r
- if (c1 != c2) {\r
- return c1 - c2;\r
- }\r
- k++;\r
- }\r
- return len1 - len2;\r
- }\r
- };\r
-\r
- protected static final Comparator<Object> CASE_INSENSITIVE_STRING_CHARBUFFER_COMPARATOR = new Comparator<Object>() {\r
- @Override\r
- public int compare(Object o1, Object o2) {\r
- String s1 = null;\r
- String s2 = null;\r
- if (o1 instanceof String)\r
- s1 = (String) o1;\r
- if (o2 instanceof String)\r
- s2 = (String) o2;\r
- if (s1 != null && s2 != null)\r
- return s1.compareToIgnoreCase((String) o2);\r
-\r
- if (s1 == null && s2 == null)\r
- return 0;\r
-\r
- if (s1 == null && (o1 instanceof CharBuffer))\r
- return -compare(s2, (CharBuffer) o1);\r
- if (s2 == null && (o2 instanceof CharBuffer))\r
- return compare(s1, (CharBuffer) o2);\r
-\r
- return 0;\r
- }\r
- int compare(String s, CharBuffer buf) {\r
- int len1 = s.length();\r
- int len2 = buf.position();\r
- int n = Math.min(len1, len2);\r
- int k = 0;\r
- while (k < n) {\r
- char c1 = s.charAt(k);\r
- char c2 = buf.get(k);\r
- if (c1 != c2) {\r
- c1 = Character.toUpperCase(c1);\r
- c2 = Character.toUpperCase(c2);\r
- if (c1 != c2) {\r
- c1 = Character.toLowerCase(c1);\r
- c2 = Character.toLowerCase(c2);\r
- if (c1 != c2) {\r
- return c1 - c2;\r
- }\r
- }\r
- }\r
- k++;\r
- }\r
- return len1 - len2;\r
- }\r
- };\r
-\r
- protected Set<String> findStartsWithMatches(Set<String> matchUniverse, String proposition) {\r
- Set<String> result = new TreeSet<String>(getComparator());\r
- if (matchUniverse.isEmpty())\r
- return result;\r
-\r
- for (String name : matchUniverse)\r
- if (name.startsWith(proposition))\r
- result.add(name);\r
- return result;\r
- }\r
-\r
- protected Set<String> findStartsWithMatches(Set<String> matchUniverse, String proposition, Set<String> result) {\r
- if (matchUniverse.isEmpty())\r
- return result;\r
-\r
- for (String name : matchUniverse)\r
- if (name.startsWith(proposition))\r
- result.add(name);\r
- return result;\r
- }\r
-\r
- protected String findFreshName(Set<String> allReservedNames, Set<String> allRequestedNames, String proposition, boolean acceptProposition) {\r
- if (allReservedNames == null)\r
- throw new NullPointerException("null reserved name set");\r
- if (proposition == null)\r
- throw new NullPointerException("null proposition");\r
-\r
- Set<String> startsWithMatches = findStartsWithMatches(allReservedNames, proposition);\r
- if (allRequestedNames != null)\r
- findStartsWithMatches(allRequestedNames, proposition, startsWithMatches);\r
-\r
- return findFreshName(startsWithMatches, proposition, acceptProposition);\r
- }\r
-\r
- protected String findFreshName(Set<String> reserved, String proposition, boolean acceptProposition) {\r
- return findFreshName(reserved, proposition, acceptProposition, generatedNameFormat);\r
- }\r
-\r
- protected String findFreshName(Set<String> reserved, String proposition, boolean acceptProposition, String nameFormat) {\r
- if (proposition == null)\r
- throw new NullPointerException("null proposition");\r
-\r
- if (DEBUG_NAME_MATCHING)\r
- System.out.println("Finding fresh proposed name '" + proposition + "' among matching reservations: " + reserved);\r
-\r
- if (acceptProposition && !reserved.contains(proposition))\r
- return proposition;\r
-\r
- // Trying to optimize away unnecessary allocation of new String instances\r
- // when looking for fresh names for objects.\r
- CharBuffer cb = CharBuffer.allocate(proposition.length() + 10);\r
- cb.mark();\r
-\r
- @SuppressWarnings("resource")\r
- Formatter formatter = new Formatter(cb, Locale.US);\r
-\r
- //int i = reserved.size() + 1;\r
- int i = 1;\r
- for (;; ++i) {\r
- cb.reset();\r
- formatter.format(nameFormat, proposition, i);\r
- if (!reserved.contains(cb)) {\r
- // Construct a String from the CharBuffer\r
- cb.limit(cb.position());\r
- cb.rewind();\r
- String result = cb.toString();\r
- if (DEBUG_NAME_MATCHING)\r
- System.out.println("\tfound fresh name: " + result);\r
- return result;\r
- }\r
- }\r
- }\r
-\r
+package org.simantics.modeling.services;
+
+import java.nio.CharBuffer;
+import java.util.Comparator;
+import java.util.Formatter;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.exception.DatabaseException;
+
+/**
+ * An abstract base class that contains some helpers for implementing your own
+ * {@link ComponentNamingStrategy}. A default implementation is also provided
+ * for {@link #findFreshInstanceName(ReadGraph, Resource, Resource, Resource)}
+ * based on the Layer0 ontology <code>HasGeneratedNamePrefix</code> property.
+ *
+ * @author Tuukka Lehtonen
+ */
+public abstract class ComponentNamingStrategyBase implements ComponentNamingStrategy {
+
+ protected static final boolean DEBUG_ALL = false;
+ protected static final boolean DEBUG_NAME_MATCHING = false | DEBUG_ALL;
+
+ protected final String generatedNameFormat;
+ protected final boolean caseInsensitive;
+
+ /**
+ * Base constructor for a naming strategy with the specified format as the
+ * generated name format. The format will receive two arguments:
+ * <ol>
+ * <li>proposed name as {@link String}</li>
+ * <li>an {@link Integer} that attempts to make the name unique within a
+ * context</li>
+ * </ol>
+ * The simplest format specification utilizing both values is
+ * <code>"%s %d"</code>, producing "FOO 1". Another example of a useful name
+ * format is <code>"%s%04d"</code>, producing "FOO0001". Reordering the
+ * arguments is also possible, e.g. <code>"%2$03d %1$s"</code>, producing
+ * "001 FOO".
+ *
+ * <p>
+ * See {@link Formatter} for the format specification and how to customize
+ * it.
+ *
+ * <p>
+ * This constructor will create a case-insensitive naming strategy.
+ *
+ * @param generatedNameFormat the format to use for generated names
+ */
+ public ComponentNamingStrategyBase(String generatedNameFormat) {
+ this(generatedNameFormat, true);
+ }
+
+ /**
+ * Base constructor for a naming strategy with the specified format as the
+ * generated name format. The format will receive two arguments:
+ * <ol>
+ * <li>proposed name as {@link String}</li>
+ * <li>an {@link Integer} that attempts to make the name unique within a
+ * context</li>
+ * </ol>
+ * The simplest format specification utilizing both values is
+ * <code>"%s %d"</code>, producing "FOO 1". Another example of a useful name
+ * format is <code>"%s%04d"</code>, producing "FOO0001". Reordering the
+ * arguments is also possible, e.g. <code>"%2$03d %1$s"</code>, producing
+ * "001 FOO".
+ *
+ * <p>
+ * See {@link Formatter} for the format specification and how to customize
+ * it.
+ *
+ * @param generatedNameFormat the format to use for generated names
+ * @param caseInsensitive controls whether the strategy shall be case-insensitive or not
+ */
+ public ComponentNamingStrategyBase(String generatedNameFormat, boolean caseInsensitive) {
+ this.generatedNameFormat = generatedNameFormat;
+ this.caseInsensitive = caseInsensitive;
+ }
+
+ @Override
+ public String findFreshInstanceName(ReadGraph graph, Resource configurationRoot, Resource container,
+ Resource componentType) throws NamingException, DatabaseException {
+ String proposition = ComponentNamingUtil.generateProposition(graph, container, componentType);
+ return validateInstanceName(graph, configurationRoot, container, componentType, proposition);
+ }
+
+ @Override
+ public String validateInstanceName(ReadGraph graph, Resource configurationRoot, Resource container,
+ Resource componentType, String proposition) throws NamingException, DatabaseException {
+ return validateInstanceName(graph, configurationRoot, container, componentType, proposition, false);
+ }
+
+ @Override
+ public List<String> validateInstanceNames(
+ ReadGraph graph,
+ Resource configurationRoot,
+ List<String> propositions,
+ boolean acceptProposition,
+ Set<String> externallyReserved)
+ throws NamingException, DatabaseException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ protected Comparator<Object> getComparator() {
+ return getComparator(caseInsensitive);
+ }
+
+ protected static Comparator<Object> getComparator(boolean caseInsensitive) {
+ return caseInsensitive ? CASE_INSENSITIVE_STRING_CHARBUFFER_COMPARATOR : CASE_SENSITIVE_STRING_CHARBUFFER_COMPARATOR;
+ }
+
+ protected static final Comparator<Object> CASE_SENSITIVE_STRING_CHARBUFFER_COMPARATOR = new Comparator<Object>() {
+ @Override
+ public int compare(Object o1, Object o2) {
+ String s1 = null;
+ String s2 = null;
+ if (o1 instanceof String)
+ s1 = (String) o1;
+ if (o2 instanceof String)
+ s2 = (String) o2;
+ if (s1 != null && s2 != null)
+ return s1.compareTo((String) o2);
+
+ if (s1 == null && s2 == null)
+ return 0;
+
+ if (s1 == null && (o1 instanceof CharBuffer))
+ return -compare(s2, (CharBuffer) o1);
+ if (s2 == null && (o2 instanceof CharBuffer))
+ return compare(s1, (CharBuffer) o2);
+
+ return 0;
+ }
+ int compare(String s, CharBuffer buf) {
+ int len1 = s.length();
+ int len2 = buf.position();
+ int n = Math.min(len1, len2);
+ int k = 0;
+ while (k < n) {
+ char c1 = s.charAt(k);
+ char c2 = buf.get(k);
+ if (c1 != c2) {
+ return c1 - c2;
+ }
+ k++;
+ }
+ return len1 - len2;
+ }
+ };
+
+ protected static final Comparator<Object> CASE_INSENSITIVE_STRING_CHARBUFFER_COMPARATOR = new Comparator<Object>() {
+ @Override
+ public int compare(Object o1, Object o2) {
+ String s1 = null;
+ String s2 = null;
+ if (o1 instanceof String)
+ s1 = (String) o1;
+ if (o2 instanceof String)
+ s2 = (String) o2;
+ if (s1 != null && s2 != null)
+ return s1.compareToIgnoreCase((String) o2);
+
+ if (s1 == null && s2 == null)
+ return 0;
+
+ if (s1 == null && (o1 instanceof CharBuffer))
+ return -compare(s2, (CharBuffer) o1);
+ if (s2 == null && (o2 instanceof CharBuffer))
+ return compare(s1, (CharBuffer) o2);
+
+ return 0;
+ }
+ int compare(String s, CharBuffer buf) {
+ int len1 = s.length();
+ int len2 = buf.position();
+ int n = Math.min(len1, len2);
+ int k = 0;
+ while (k < n) {
+ char c1 = s.charAt(k);
+ char c2 = buf.get(k);
+ if (c1 != c2) {
+ c1 = Character.toUpperCase(c1);
+ c2 = Character.toUpperCase(c2);
+ if (c1 != c2) {
+ c1 = Character.toLowerCase(c1);
+ c2 = Character.toLowerCase(c2);
+ if (c1 != c2) {
+ return c1 - c2;
+ }
+ }
+ }
+ k++;
+ }
+ return len1 - len2;
+ }
+ };
+
+ protected Set<String> findStartsWithMatches(Set<String> matchUniverse, String proposition) {
+ Set<String> result = new TreeSet<String>(getComparator());
+ if (matchUniverse.isEmpty())
+ return result;
+
+ for (String name : matchUniverse)
+ if (name.startsWith(proposition))
+ result.add(name);
+ return result;
+ }
+
+ protected Set<String> findStartsWithMatches(Set<String> matchUniverse, String proposition, Set<String> result) {
+ if (matchUniverse.isEmpty())
+ return result;
+
+ for (String name : matchUniverse)
+ if (name.startsWith(proposition))
+ result.add(name);
+ return result;
+ }
+
+ protected String findFreshName(Set<String> allReservedNames, Set<String> allRequestedNames, String proposition, boolean acceptProposition) {
+ if (allReservedNames == null)
+ throw new NullPointerException("null reserved name set");
+ if (proposition == null)
+ throw new NullPointerException("null proposition");
+
+ Set<String> startsWithMatches = findStartsWithMatches(allReservedNames, proposition);
+ if (allRequestedNames != null)
+ findStartsWithMatches(allRequestedNames, proposition, startsWithMatches);
+
+ return findFreshName(startsWithMatches, proposition, acceptProposition);
+ }
+
+ protected String findFreshName(Set<String> reserved, String proposition, boolean acceptProposition) {
+ return findFreshName(reserved, proposition, acceptProposition, generatedNameFormat);
+ }
+
+ protected String findFreshName(Set<String> reserved, String proposition, boolean acceptProposition, String nameFormat) {
+ if (proposition == null)
+ throw new NullPointerException("null proposition");
+
+ if (DEBUG_NAME_MATCHING)
+ System.out.println("Finding fresh proposed name '" + proposition + "' among matching reservations: " + reserved);
+
+ if (acceptProposition && !reserved.contains(proposition))
+ return proposition;
+
+ // Trying to optimize away unnecessary allocation of new String instances
+ // when looking for fresh names for objects.
+ CharBuffer cb = CharBuffer.allocate(proposition.length() + 10);
+ cb.mark();
+
+ @SuppressWarnings("resource")
+ Formatter formatter = new Formatter(cb, Locale.US);
+
+ //int i = reserved.size() + 1;
+ int i = 1;
+ for (;; ++i) {
+ cb.reset();
+ formatter.format(nameFormat, proposition, i);
+ if (!reserved.contains(cb)) {
+ // Construct a String from the CharBuffer
+ cb.limit(cb.position());
+ cb.rewind();
+ String result = cb.toString();
+ if (DEBUG_NAME_MATCHING)
+ System.out.println("\tfound fresh name: " + result);
+ return result;
+ }
+ }
+ }
+
}
\ No newline at end of file