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