]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - 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
diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/services/CaseInsensitiveComponentFunctionNamingStrategy.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/services/CaseInsensitiveComponentFunctionNamingStrategy.java
new file mode 100644 (file)
index 0000000..75f1108
--- /dev/null
@@ -0,0 +1,337 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.modeling.services;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Comparator;\r
+import java.util.Formatter;\r
+import java.util.List;\r
+import java.util.Set;\r
+import java.util.TreeSet;\r
+\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.common.Indexing;\r
+import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;\r
+import org.simantics.db.common.request.PossibleIndexRoot;\r
+import org.simantics.db.common.request.UnaryRead;\r
+import org.simantics.db.common.utils.NameUtils;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.genericrelation.IndexQueries;\r
+import org.simantics.db.service.GraphChangeListenerSupport;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.scl.runtime.function.Function;\r
+import org.simantics.scl.runtime.function.Function1;\r
+import org.simantics.scl.runtime.tuple.Tuple3;\r
+import org.simantics.scl.runtime.tuple.Tuple4;\r
+import org.simantics.structural.stubs.StructuralResource2;\r
+\r
+import gnu.trove.set.hash.THashSet;\r
+\r
+/**\r
+ * A component naming strategy implementation for structural models based on an\r
+ * SCL function that lists used names in a model.\r
+ * \r
+ * <p>\r
+ * The type of the function is expected to be:\r
+ * <code>ReadGraph => Resource -> String -> Integer -> List&lt;Map&lt;String,Object&gt;&gt;</code>\r
+ * \r
+ * @author Tuukka Lehtonen\r
+ * \r
+ * @see ComponentNamingStrategy\r
+ */\r
+public class CaseInsensitiveComponentFunctionNamingStrategy extends ComponentNamingStrategyBase {\r
+\r
+    protected static final boolean                                  DEBUG_INDEX_SEARCH = false | DEBUG_ALL;\r
+\r
+    @SuppressWarnings("rawtypes")\r
+    private final Function                                          index;\r
+\r
+    /**\r
+     * A filter that is applied to all user-provided name propositions before\r
+     * processing them.\r
+     */\r
+    private Function1<String, String>                               propositionPreFilter;\r
+\r
+    /**\r
+     * Construct an index-based naming strategy with "%s %d" as the generated\r
+     * name format. See {@link Formatter} for the format specification.\r
+     * \r
+     * @param index the index function for looking up used names. The function\r
+     *        must be of type\r
+     *        <code>ReadGraph => Resource -> String -> Integer -> List&lt;Map&lt;String,Object&gt;&gt;</code>\r
+     */\r
+    @SuppressWarnings("rawtypes")\r
+    public CaseInsensitiveComponentFunctionNamingStrategy(Function index) {\r
+        this("%s %d", index);\r
+    }\r
+\r
+    /**\r
+     * Construct an index-based naming strategy with the specified format as the\r
+     * generated name format. See {@link Formatter} for the format\r
+     * specification.\r
+     * \r
+     * @param generatedNameFormat the format to use for generated names, see\r
+     *        {@link Formatter}\r
+     * @param index the index function for looking up used names. The function\r
+     *        must be of type\r
+     *        <code>ReadGraph => Resource -> String -> Integer -> List&lt;Map&lt;String,Object&gt;&gt;</code>\r
+     */\r
+    @SuppressWarnings("rawtypes")\r
+    public CaseInsensitiveComponentFunctionNamingStrategy(String generatedNameFormat, Function index) {\r
+        super(generatedNameFormat);\r
+        this.index = index;\r
+    }\r
+\r
+    /**\r
+     * Construct an index-based naming strategy with the specified format as the\r
+     * generated name format. See {@link Formatter} for the format\r
+     * specification.\r
+     * \r
+     * @param generatedNameFormat the format to use for generated names, see\r
+     *        {@link Formatter}\r
+     * @param index the index function for looking up used names. The function\r
+     *        must be of type\r
+     *        <code>ReadGraph => Resource -> String -> Integer -> List&lt;Map&lt;String,Object&gt;&gt;</code>\r
+     * @param an optional function to \r
+     */\r
+    @SuppressWarnings("rawtypes")\r
+    public CaseInsensitiveComponentFunctionNamingStrategy(String generatedNameFormat, Function index, Function1<String, String> propositionPreFilter) {\r
+        super(generatedNameFormat);\r
+        this.index = index;\r
+        this.propositionPreFilter = propositionPreFilter;\r
+    }\r
+\r
+    CaseInsensitiveComponentNamingStrategy2 fallbackStrategy = null;\r
+\r
+    @Override\r
+    public String validateInstanceName(ReadGraph graph,\r
+               Resource configurationRoot, Resource component, String proposition, boolean acceptProposition)\r
+               throws NamingException, DatabaseException {\r
+\r
+       String lowercaseProposition = proposition.toLowerCase();\r
+       \r
+        Layer0 L0 = Layer0.getInstance(graph);\r
+        String name = graph.getPossibleRelatedValue(component, L0.HasName, Bindings.STRING);\r
+        if (name != null) {\r
+        \r
+               String lowercaseName = name.toLowerCase();\r
+               if(lowercaseName.startsWith(lowercaseProposition)) {\r
+                       \r
+                Resource indexRoot = graph.syncRequest(new PossibleIndexRoot(configurationRoot));\r
+                if (indexRoot != null) {\r
+                       \r
+                    synchronized (this) {\r
+                       \r
+                        String search = "Name:" + lowercaseName + "*";\r
+                        @SuppressWarnings("unchecked")\r
+                        List<Resource> components = (List<Resource>) index.apply(graph, indexRoot, search, Integer.MAX_VALUE);\r
+\r
+                        Set<Resource> rs = new THashSet<Resource>();\r
+                        for (Resource componentResult : components) {\r
+                            if (DEBUG_INDEX_SEARCH)\r
+                                System.out.println(getClass().getSimpleName() + ": found " + componentResult);\r
+                            String n = graph.getPossibleRelatedValue(componentResult, L0.HasName, Bindings.STRING);\r
+                            if (n != null && n.toLowerCase().equals(lowercaseName))\r
+                               rs.add(componentResult);\r
+                        }\r
+                        \r
+                        Cache c = getCache(graph, indexRoot);\r
+                        \r
+                        if(rs.size() == 0) { \r
+                               if(!c.getRequested().contains(name)) {\r
+                                       c.addRequested(name);\r
+                                       return name;\r
+                               }\r
+                        } else if(rs.size() == 1) {\r
+                               if(component.equals(rs.iterator().next())) {\r
+                               return name;\r
+                               }\r
+                        }\r
+\r
+                    }\r
+                       \r
+                }\r
+                       \r
+               }\r
+               \r
+        }\r
+\r
+        StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
+        Resource container = graph.getSingleObject(component, L0.PartOf);\r
+        Resource componentType = graph.getSingleType(component, STR.Component);\r
+        return validateInstanceName(graph, configurationRoot, container, componentType, proposition, acceptProposition);\r
+       \r
+    }\r
+    \r
+    static class ComponentsRequest extends UnaryRead<Tuple4, Set<String>>{\r
+\r
+               public ComponentsRequest(Tuple4 parameter) {\r
+                       super(parameter);\r
+               }\r
+\r
+               @Override\r
+               public Set<String> perform(ReadGraph graph) throws DatabaseException {\r
+                       \r
+                       Resource indexRoot = (Resource)parameter.get(0);\r
+                       Function index = (Function)parameter.get(1);\r
+                       String search = (String)parameter.get(2);\r
+                       Comparator<Object> comparator = (Comparator<Object>)parameter.get(3);\r
+                       \r
+            List<Resource> components = (List<Resource>) index.apply(graph, indexRoot, search, Integer.MAX_VALUE);\r
+            Set<String> reserved = new TreeSet<String>(comparator);\r
+\r
+            Layer0 L0 = Layer0.getInstance(graph);\r
+            for (Resource componentResult : components) {\r
+                if (DEBUG_INDEX_SEARCH)\r
+                    System.out.println(getClass().getSimpleName() + ": found " + componentResult);\r
+                String name = graph.getPossibleRelatedValue(componentResult, L0.HasName, Bindings.STRING);\r
+                if (name != null)\r
+                    reserved.add(name);\r
+            }\r
+            \r
+            System.err.println("found " + reserved.size() + " components");\r
+            \r
+            return reserved;\r
+\r
+               }\r
+       \r
+    }\r
+    \r
+    @Override\r
+    public String validateInstanceName(ReadGraph graph, Resource configurationRoot, Resource container,\r
+            Resource componentType, String proposition, boolean acceptProposition) throws NamingException, DatabaseException {\r
+        Resource indexRoot = graph.syncRequest(new PossibleIndexRoot(configurationRoot));\r
+        if (indexRoot == null) {\r
+            System.err.println("Could not find index root from configuration root '" + NameUtils.getSafeName(graph, configurationRoot, true) + "'");\r
+            if(fallbackStrategy == null)\r
+                fallbackStrategy = \r
+                    new CaseInsensitiveComponentNamingStrategy2(graph.getService(GraphChangeListenerSupport.class), generatedNameFormat);\r
+            return fallbackStrategy.validateInstanceName(graph, configurationRoot, container, componentType, proposition, acceptProposition);\r
+        }\r
+\r
+        if (propositionPreFilter != null)\r
+            proposition = propositionPreFilter.apply(proposition);\r
+\r
+        synchronized (this) {\r
+               \r
+            String search = "Name:" + proposition + "*";\r
+            \r
+            Set<String> reserved = graph.syncRequest(new ComponentsRequest(new Tuple4(indexRoot, index, search, getComparator())), TransientCacheAsyncListener.instance());\r
+\r
+            Cache cache = getCache(graph, indexRoot);\r
+\r
+            findStartsWithMatches(cache.getRequested(), proposition, reserved);\r
+\r
+            String result = findFreshName(reserved, proposition, acceptProposition);\r
+            cache.addRequested(result);\r
+\r
+            if (DEBUG_INDEX_SEARCH)\r
+                System.out.println(getClass().getSimpleName() + ": validated instance name " + result);\r
+\r
+            return result;\r
+        }\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) throws NamingException, DatabaseException\r
+    {\r
+        Resource indexRoot = graph.syncRequest(new PossibleIndexRoot(configurationRoot));\r
+        if (indexRoot == null)\r
+            throw new NamingException("Could not find index root from configuration root '" + NameUtils.getSafeName(graph, configurationRoot, true) + "'");\r
+\r
+        Layer0 L0 = Layer0.getInstance(graph);\r
+\r
+        synchronized (this) {\r
+            List<String> result = new ArrayList<String>(propositions.size());\r
+            Set<String> reserved = new TreeSet<String>(getComparator());\r
+\r
+            for (String proposition : propositions) {\r
+                if (propositionPreFilter != null)\r
+                    proposition = propositionPreFilter.apply(proposition);\r
+\r
+                String search = "Name:" + IndexQueries.escape( proposition ) + "*";\r
+                @SuppressWarnings("unchecked")\r
+                List<Resource> components = (List<Resource>) index.apply(graph, indexRoot, search, Integer.MAX_VALUE);\r
+\r
+                if (DEBUG_INDEX_SEARCH)\r
+                    System.out.println(getClass().getSimpleName() + ": found " + components.size()\r
+                            + " index results for index root " + indexRoot + " & configurationRoot " + configurationRoot\r
+                            + " & proposition '" + proposition + "':");\r
+\r
+                reserved.clear();\r
+                for (Resource componentResult : components) {\r
+                    if (DEBUG_INDEX_SEARCH)\r
+                        System.out.println(getClass().getSimpleName() + ": found " + componentResult);\r
+                    String name = graph.getPossibleRelatedValue(componentResult, L0.HasName, Bindings.STRING);\r
+                    if (name != null)\r
+                        reserved.add(name);\r
+                }\r
+\r
+                if (externallyReserved != null)\r
+                    reserved.addAll(externallyReserved);\r
+                reserved.addAll(result);\r
+                String name = findFreshName(reserved, proposition, acceptProposition);\r
+\r
+                result.add(name);\r
+\r
+                if (DEBUG_INDEX_SEARCH)\r
+                    System.out.println(getClass().getSimpleName() + ": validated instance name " + proposition + " -> " + name);\r
+            }\r
+\r
+            if (DEBUG_INDEX_SEARCH)\r
+                System.out.println(getClass().getSimpleName() + ": validated instance names " + propositions + " -> " + result);\r
+\r
+            return result;\r
+        }\r
+    }\r
+\r
+    private Cache getCache(ReadGraph graph, Resource root) throws DatabaseException {\r
+        Cache cache = Indexing.getCache(root, Cache.class);\r
+        if(cache != null) return cache;\r
+        synchronized (this) {\r
+            cache = Indexing.getCache(root, Cache.class);\r
+            if(cache != null) return cache;\r
+            return Indexing.createCache(root, new Cache(caseInsensitive)); \r
+        }\r
+    }\r
+\r
+    static class Cache {\r
+        \r
+        private final Set<String> requested;\r
+\r
+        Cache(boolean caseInsensitive) {\r
+            this.requested = new TreeSet<String>(getComparator(caseInsensitive));\r
+        }\r
+\r
+        public Set<String> getRequested() {\r
+            return requested;\r
+        }\r
+\r
+        public void addRequested(String name) {\r
+            requested.add(name);\r
+        }\r
+\r
+        @Override\r
+        public String toString() {\r
+            return getClass().getSimpleName() + ": " + requested;\r
+        }\r
+\r
+    }\r
+\r
+}\r