]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.modeling/src/org/simantics/modeling/services/ComponentNamingStrategyBase.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / services / ComponentNamingStrategyBase.java
diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/services/ComponentNamingStrategyBase.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/services/ComponentNamingStrategyBase.java
new file mode 100644 (file)
index 0000000..0d9f1fe
--- /dev/null
@@ -0,0 +1,276 @@
+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
+}
\ No newline at end of file