]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/util/Layer0Utils.java
StructuralUtils.isImmutable now takes L0.readOnly of input into account
[simantics/platform.git] / bundles / org.simantics.db.layer0 / src / org / simantics / db / layer0 / util / Layer0Utils.java
index 5ccfcf325f49df3f919f6ee54f34b3d0f2033960..ad8676f813bdb83d4da1adde8aed547c31541ddb 100644 (file)
@@ -54,6 +54,7 @@ import org.simantics.databoard.type.StringType;
 import org.simantics.databoard.type.UnionType;
 import org.simantics.databoard.type.VariantType;
 import org.simantics.databoard.util.ObjectUtils;
+import org.simantics.datatypes.literal.GUID;
 import org.simantics.db.ChangeSetIdentifier;
 import org.simantics.db.Operation;
 import org.simantics.db.ReadGraph;
@@ -70,9 +71,11 @@ import org.simantics.db.common.primitiverequest.PossibleRelatedValue;
 import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
 import org.simantics.db.common.request.DelayedWriteRequest;
 import org.simantics.db.common.request.ObjectsWithType;
-import org.simantics.db.common.request.PossibleChild;
 import org.simantics.db.common.request.PossibleIndexRoot;
+import org.simantics.db.common.request.WriteRequest;
+import org.simantics.db.common.utils.CommonDBUtils;
 import org.simantics.db.common.utils.NameUtils;
+import org.simantics.db.event.ChangeListener;
 import org.simantics.db.exception.CancelTransactionException;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.exception.ServiceException;
@@ -81,6 +84,7 @@ import org.simantics.db.layer0.adapter.CopyHandler2;
 import org.simantics.db.layer0.adapter.GenericRelationIndex;
 import org.simantics.db.layer0.adapter.PasteHandler;
 import org.simantics.db.layer0.adapter.impl.DefaultPasteHandler;
+import org.simantics.db.layer0.adapter.impl.EntityRemover;
 import org.simantics.db.layer0.adapter.impl.TGRemover;
 import org.simantics.db.layer0.genericrelation.IndexedRelations;
 import org.simantics.db.layer0.internal.SimanticsInternal;
@@ -98,6 +102,7 @@ import org.simantics.db.service.ClusterControl;
 import org.simantics.db.service.ClusteringSupport;
 import org.simantics.db.service.CollectionSupport;
 import org.simantics.db.service.DebugSupport;
+import org.simantics.db.service.GraphChangeListenerSupport;
 import org.simantics.db.service.ManagementSupport;
 import org.simantics.db.service.UndoRedoSupport;
 import org.simantics.db.service.XSupport;
@@ -110,9 +115,7 @@ import org.simantics.graph.representation.PrettyPrintTG;
 import org.simantics.graph.representation.TransferableGraph1;
 import org.simantics.layer0.Layer0;
 import org.simantics.operation.Layer0X;
-import org.simantics.scl.compiler.environment.Environments;
 import org.simantics.scl.compiler.runtime.RuntimeEnvironment;
-import org.simantics.scl.compiler.top.SCLExpressionCompilationException;
 import org.simantics.scl.compiler.types.Type;
 import org.simantics.scl.osgi.SCLOsgi;
 import org.simantics.scl.runtime.function.Function;
@@ -124,8 +127,6 @@ public class Layer0Utils {
        @SuppressWarnings("rawtypes")
        public static final ThreadLocal SCL_GRAPH = new ThreadLocal();
 
-       final public static Binding datatype_binging = Bindings.getBindingUnchecked(Datatype.class);
-
        public static Resource literal(WriteGraph g, String value) throws DatabaseException {
                Layer0 L0 = Layer0.getInstance(g);
                Resource r = g.newResource();
@@ -267,7 +268,7 @@ public class Layer0Utils {
                Resource range = graph.getPossibleObject(property, L0.HasRange);
                if(range != null && !L0.Value.equals(range)) return range;
 
-               Datatype requiredDataType = graph.getPossibleRelatedValue(property, L0X.RequiresDataType, datatype_binging);
+               Datatype requiredDataType = graph.getPossibleRelatedValue(property, L0X.RequiresDataType, Bindings.DATATYPE);
                if(requiredDataType != null) return getLiteralType(graph, requiredDataType);
 
                String requiredValueType = graph.getPossibleRelatedValue(property, L0.RequiresValueType, Bindings.STRING);
@@ -350,13 +351,9 @@ public class Layer0Utils {
                throw new IllegalArgumentException("Unable to convert datatype into SCL type: " + type);
        }
 
-
+       @Deprecated
        public static Type getSCLType(ReadGraph graph, RuntimeEnvironment runtimeEnvironment, String typeText) throws DatabaseException {
-        try {
-                       return Environments.getType(runtimeEnvironment.getEnvironment(), typeText);
-               } catch (SCLExpressionCompilationException e) {
-                       throw new DatabaseException(e);
-               }
+           return CommonDBUtils.getSCLType(graph, runtimeEnvironment, typeText);
        }
 
        public static Type getSCLType(ReadGraph graph, Variable property) throws DatabaseException {
@@ -383,7 +380,7 @@ public class Layer0Utils {
                Layer0 L0 = Layer0.getInstance(graph);
                Layer0X L0X = Layer0X.getInstance(graph);
 
-               Datatype literalDatatype = graph.getPossibleRelatedValue(value, L0.HasDataType, datatype_binging);
+               Datatype literalDatatype = graph.getPossibleRelatedValue(value, L0.HasDataType, Bindings.DATATYPE);
                if(literalDatatype != null) return getSCLType(literalDatatype);
 
                String literalValueType = graph.getPossibleRelatedValue(value, L0.HasValueType, Bindings.STRING);
@@ -391,7 +388,7 @@ public class Layer0Utils {
 
                if(predicate != null) {
 
-                       Datatype requiredDataType = graph.getPossibleRelatedValue(predicate, L0X.RequiresDataType, datatype_binging);
+                       Datatype requiredDataType = graph.getPossibleRelatedValue(predicate, L0X.RequiresDataType, Bindings.DATATYPE);
                        if(requiredDataType != null) return getSCLType(requiredDataType);
 
                        throw new DatabaseException("Unidentified literal data type for property " + NameUtils.getURIOrSafeNameInternal(graph, predicate));
@@ -409,26 +406,30 @@ public class Layer0Utils {
        }
 
        public static Datatype getPossibleDatatype(ReadGraph graph, Variable variable) throws DatabaseException {
+               return getPossibleDatatype(graph, variable, null, true);
+       }
 
+       private static Datatype getPossibleDatatype(ReadGraph graph, Variable variable, PropertyInfo info, boolean checkPropertyDatatype) throws DatabaseException {
                Layer0 L0 = Layer0.getInstance(graph);
                Layer0X L0X = Layer0X.getInstance(graph);
 
                Resource property = variable.getPossiblePredicateResource(graph);
-               if(property != null) {
-//                     Datatype requiredDataType = graph.getPossibleRelatedValue(property, L0X.RequiresDataType, datatype_binging);
-                       Datatype requiredDataType = graph.syncRequest(new PossibleRelatedValue<Datatype>(property, L0X.RequiresDataType, datatype_binging));
+               if(checkPropertyDatatype && property != null) {
+                       Datatype requiredDataType = graph.syncRequest(new PossibleRelatedValue<Datatype>(property, L0X.RequiresDataType, Bindings.DATATYPE));
                        if(requiredDataType != null) return requiredDataType;
                }
 
                Resource literal = variable.getPossibleRepresents(graph);
                if(literal != null) {
-                   Datatype literalDatatype = graph.getPossibleRelatedValue2(literal, L0.HasDataType, new StandardGraphPropertyVariable(graph, variable, null, literal, L0.HasDataType), datatype_binging);
-                   if(literalDatatype != null) return literalDatatype;
+                       Datatype literalDatatype = graph.getPossibleRelatedValue2(literal, L0.HasDataType, new StandardGraphPropertyVariable(graph, variable, null, literal, L0.HasDataType), Bindings.DATATYPE);
+                       if(literalDatatype != null) return literalDatatype;
                }
 
                if(property != null) {
 
-                       String requiredValueType = graph.getPossibleRelatedValue(property, L0.RequiresValueType, Bindings.STRING);
+                       String requiredValueType = info != null
+                                       ? info.requiredValueType
+                                       : graph.getPossibleRelatedValue(property, L0.RequiresValueType, Bindings.STRING);
                        if(requiredValueType != null) {
                                Datatype datatype = getPossibleDatatypeForValueType(requiredValueType);
                                if(datatype != null) return datatype;
@@ -450,7 +451,6 @@ public class Layer0Utils {
                }
 
                return null;
-
        }
 
        private static Datatype getPossibleDatatypeForValueType(String requiredValueType) throws DatabaseException {
@@ -463,11 +463,9 @@ public class Layer0Utils {
                        arrayType = requiredValueType.substring(1, requiredValueType.length()-1);
                }
 
-               if(arrayType != null) {
-                       Datatype arrayDataType = getArrayDataTypeForType(arrayType);
-                       if(arrayDataType != null)
-                               return arrayDataType;
-               }
+               Datatype arrayDataType = getDatatypeForType(arrayType != null ? arrayType : requiredValueType, arrayType != null);
+               if(arrayDataType != null)
+                       return arrayDataType;
 
                Datatype dt = Datatypes.getDatatype(requiredValueType);
                if(dt != null) return dt;
@@ -478,18 +476,19 @@ public class Layer0Utils {
                        return null;
                }
 
-
        }
 
-       private static Datatype getArrayDataTypeForType(String type) {
-               if("Double".equals(type)) return Datatypes.DOUBLE_ARRAY;
-               else if("String".equals(type)) return Datatypes.STRING_ARRAY;
-               else if("Integer".equals(type)) return Datatypes.INTEGER_ARRAY;
-               else if("Long".equals(type)) return Datatypes.LONG_ARRAY;
-               else if("Float".equals(type)) return Datatypes.FLOAT_ARRAY;
-               else if("Byte".equals(type)) return Datatypes.BYTE_ARRAY;
-               else if("Boolean".equals(type)) return Datatypes.BOOLEAN_ARRAY;
-               else if("Variant".equals(type)) return Datatypes.VARIANT_ARRAY;
+       private static Datatype getDatatypeForType(String type, boolean isArray) {
+               switch (type) {
+                       case "Double":  return isArray ? Datatypes.DOUBLE_ARRAY  : Datatypes.DOUBLE;
+                       case "String":  return isArray ? Datatypes.STRING_ARRAY  : Datatypes.STRING;
+                       case "Integer": return isArray ? Datatypes.INTEGER_ARRAY : Datatypes.INTEGER;
+                       case "Long":    return isArray ? Datatypes.LONG_ARRAY    : Datatypes.LONG;
+                       case "Float":   return isArray ? Datatypes.FLOAT_ARRAY   : Datatypes.FLOAT;
+                       case "Byte":    return isArray ? Datatypes.BYTE_ARRAY    : Datatypes.BYTE;
+                       case "Boolean": return isArray ? Datatypes.BOOLEAN_ARRAY : Datatypes.BOOLEAN;
+                       case "Variant": return isArray ? Datatypes.VARIANT_ARRAY : Datatypes.VARIANT;
+               }
                return null;
        }
 
@@ -502,8 +501,6 @@ public class Layer0Utils {
                }
 
                Datatype type = getDatatype(graph, variable);
-               if (type == null)
-                       throw new DatabaseException("No datatype available for variable " + variable.getURI(graph));
                return Bindings.getBinding(type);
 
        }
@@ -511,12 +508,13 @@ public class Layer0Utils {
        public static Binding getPossibleDefaultBinding(ReadGraph graph, Variable variable) throws DatabaseException {
 
                Resource property = variable.getPossiblePredicateResource(graph);
+               PropertyInfo info = null;
                if(property != null) {
-                       PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(property), TransientCacheAsyncListener.<PropertyInfo>instance());
+                       info = graph.syncRequest(new PropertyInfoRequest(property), TransientCacheAsyncListener.<PropertyInfo>instance());
                        if(info.defaultBinding != null) return info.defaultBinding;
                }
 
-               Datatype type = getPossibleDatatype(graph, variable);
+               Datatype type = getPossibleDatatype(graph, variable, info, false);
                if (type == null) return null;
 
                return Bindings.getBinding(type);
@@ -548,7 +546,7 @@ public class Layer0Utils {
                if(literal == null)
                    return "";
 
-               Datatype literalDatatype = graph.getPossibleRelatedValue2(literal, L0.HasDataType, new StandardGraphPropertyVariable(graph, variable, null, literal, L0.HasDataType), datatype_binging);
+               Datatype literalDatatype = graph.getPossibleRelatedValue2(literal, L0.HasDataType, new StandardGraphPropertyVariable(graph, variable, null, literal, L0.HasDataType), Bindings.DATATYPE);
                if(literalDatatype != null) {
                        String unit = getPossibleUnit(literalDatatype);
                        if(unit != null) return unit;
@@ -557,7 +555,7 @@ public class Layer0Utils {
                Resource property = variable.getPossiblePredicateResource(graph);
                if(property != null) {
 
-                       Datatype requiredDataType = graph.getPossibleRelatedValue(property, L0X.RequiresDataType, datatype_binging);
+                       Datatype requiredDataType = graph.getPossibleRelatedValue(property, L0X.RequiresDataType, Bindings.DATATYPE);
                        if(requiredDataType != null) {
                                String unit = getPossibleUnit(requiredDataType);
                                if(unit != null) return unit;
@@ -569,16 +567,16 @@ public class Layer0Utils {
 
        }
 
-       public static void claimAdaptedValue(WriteGraph graph, Resource objectResource, Object value, Binding binding, Datatype datatype) throws DatabaseException {
+       public static void claimAdaptedValue(WriteGraph graph, Resource objectResource, Object value, Binding binding, Datatype targetDatatype) throws DatabaseException {
 
                try {
 
-                       Datatype source = binding.type();
-                       if(source.equals(datatype)) {
+                       Datatype sourceDatatype = binding.type();
+                       if(sourceDatatype.equals(targetDatatype)) {
                                graph.claimValue(objectResource, value, binding);
                        } else {
-                               Binding target = Bindings.getBinding(datatype);
-                               Adapter adapter = Bindings.getAdapter(binding, target);
+                               Binding target = Bindings.getBinding(targetDatatype);
+                               Adapter adapter = Bindings.getTypeAdapter(binding, target);
                                graph.claimValue(objectResource, adapter.adapt(value), target);
                        }
 
@@ -642,15 +640,14 @@ public class Layer0Utils {
                return graph.getPossibleResource(graph.getURI(root) + suffix);
        }
 
+       @Deprecated
        public static Resource getPossibleChild(ReadGraph graph, Resource resource, String name) throws DatabaseException {
-               return graph.sync(new PossibleChild(resource, name));
+               return CommonDBUtils.getPossibleChild(graph, resource, name);
        }
 
+       @Deprecated
        public static Resource getPossibleChild(ReadGraph graph, Resource resource, Resource type, String name) throws DatabaseException {
-               Resource child = graph.sync(new PossibleChild(resource, name));
-               if(child == null) return null;
-               if(!graph.isInstanceOf(child, type)) return null;
-               return child;
+               return CommonDBUtils.getPossibleChild(graph, resource, type, name);
        }
 
        public static RelationContext relationContext(ReadGraph graph, Resource subject, Resource predicate) throws DatabaseException {
@@ -877,7 +874,7 @@ public class Layer0Utils {
 
        public static Collection<Resource> copyTo(WriteGraph graph, Resource targetContainer, PasteEventHandler handler, CopyHandler copyHandler, PasteHandler pasteHandler) throws DatabaseException {
                SimanticsClipboardImpl clipboard = new SimanticsClipboardImpl();
-               copyHandler.copyToClipboard(graph, clipboard);
+               copyHandler.copyToClipboard(graph, clipboard, new NullProgressMonitor());
                if(targetContainer != null) {
                        if(pasteHandler == null) pasteHandler = graph.adapt(targetContainer, PasteHandler.class);
                        return pasteHandler.pasteFromClipboard(graph, clipboard, handler);
@@ -922,6 +919,27 @@ public class Layer0Utils {
                return stm != null && stm.isAsserted(subject);
        }
 
+       /*
+        * Works around problems in WriteGraph methods with similar signature. 
+        * Especially handles better some cases with existing literals.
+        * 
+        */
+       public static void claimLiteral(WriteGraph graph, Resource r, Resource p, Resource i, Resource t, Object value, Binding binding) throws DatabaseException {
+           Statement stm = graph.getPossibleStatement(r, p);
+           if(stm != null && !stm.isAsserted(r)) {
+               if(graph.isInstanceOf(stm.getObject(), t)) {
+                   // Existing statement is compatible, reuse the literal
+                   graph.claimValue(stm.getObject(), value, binding);
+                   return;
+               } else {
+                   // Existing statement is incompatible - remove it
+                   graph.deny(stm);
+               }
+           }
+           // Create new statement
+           graph.claimLiteral(r, p, i, t, value, binding);
+       }
+       
        public static void setExpression(WriteGraph graph, Variable context, String text, Resource expressionValueType) throws DatabaseException {
                
                Resource value = context.getRepresents(graph);
@@ -1152,7 +1170,8 @@ public class Layer0Utils {
 
         Resource indexRoot = graph.syncRequest(new PossibleVariableIndexRoot(variable));
         if(indexRoot == null) return false;
-        if(variable.equals(indexRoot)) return false;
+        Resource represents = variable.getPossibleRepresents(graph);
+        if(represents != null && represents.equals(indexRoot)) return false;
         return isPublished(graph, indexRoot);
 
     }
@@ -1183,11 +1202,15 @@ public class Layer0Utils {
 
     }
 
+    public static boolean isMarkedReadOnly(ReadGraph graph, Resource r) throws DatabaseException {
+        return Boolean.TRUE.equals( graph.getPossibleRelatedValue(r, graph.<Layer0>l0().readOnly, Bindings.BOOLEAN) );
+    }
+
     private static TransferableGraph1 makeTG(ReadGraph graph, Resource r) throws DatabaseException {
 
        SimanticsClipboardImpl cp = new SimanticsClipboardImpl();
        CopyHandler c1 = graph.adapt(r, CopyHandler.class);
-       c1.copyToClipboard(graph, cp);
+       c1.copyToClipboard(graph, cp, null);
        Collection<Set<Representation>> reps = cp.getContents();
        if(reps.size() != 1) return null;
        return ClipboardUtils.accept(graph, reps.iterator().next(), SimanticsKeys.KEY_TRANSFERABLE_GRAPH);
@@ -1198,7 +1221,7 @@ public class Layer0Utils {
 
        SimanticsClipboardImpl cp = new SimanticsClipboardImpl();
        CopyHandler c1 = graph.adapt(r, CopyHandler.class);
-       c1.copyToClipboard(graph, cp);
+       c1.copyToClipboard(graph, cp, null);
        Collection<Set<Representation>> reps = cp.getContents();
        if(reps.size() != 1) return null;
        return ClipboardUtils.accept(graph, reps.iterator().next(), SimanticsKeys.KEY_TRANSFERABLE_GRAPH_SOURCE);
@@ -1244,9 +1267,11 @@ public class Layer0Utils {
         emptyTrashBin(monitor, SimanticsInternal.getSession(), SimanticsInternal.getProject());
     }
 
-    public static void emptyTrashBin(final IProgressMonitor monitor, Session session, final Resource project) throws ServiceException {
+    public static void emptyTrashBin(final IProgressMonitor monitor, Session session, final Resource project)
+            throws ServiceException {
         final SubMonitor mon = SubMonitor.convert(monitor, "Emptying Trash Bin...", 10000);
         try {
+            ArrayList<Resource> unhandled = new ArrayList<Resource>();
             session.syncRequest(new DelayedWriteRequest() {
                 @Override
                 public void perform(WriteGraph graph) throws DatabaseException {
@@ -1255,26 +1280,34 @@ public class Layer0Utils {
                     Layer0X L0X = Layer0X.getInstance(graph);
                     Resource parent = graph.getSingleObject(project, L0.PartOf);
                     Resource trashBin = Layer0Utils.getPossibleChild(graph, parent, "TrashBin");
-                    Collection<Resource> trashes = trashBin != null
-                            ? graph.getObjects(trashBin, L0.ConsistsOf)
-                            : Collections.<Resource>emptyList();
+                    Collection<Resource> trashes = trashBin != null ? graph.getObjects(trashBin, L0.ConsistsOf)
+                            : Collections.<Resource> emptyList();
                     if (trashes.isEmpty())
                         throw new CancelTransactionException();
                     mon.setWorkRemaining((2 + trashes.size()) * 1000);
-                    for(Resource trash : trashes) {
+                    for (Resource trash : trashes) {
                         if (mon.isCanceled())
                             throw new CancelTransactionException();
                         mon.subTask(NameUtils.getSafeName(graph, trash));
+                        boolean isIndexRoot = graph.isInstanceOf(trash, L0.IndexRoot);
                         TGRemover remo = new TGRemover(mon.newChild(1000, SubMonitor.SUPPRESS_ALL_LABELS), trash);
-                        remo.remove(graph);
-                        if(graph.isInstanceOf(trash, L0.IndexRoot)) {
-                               // TODO: this should be an utility 
-                                       GenericRelationIndex index = graph.adapt(L0X.DependenciesRelation, GenericRelationIndex.class);
-                                       IndexedRelations ir = graph.getService(IndexedRelations.class);
-                                       // Deletes index files
-                                       ir.reset(null, graph, L0X.DependenciesRelation, trash);
-                                       // Notifies DB listeners
-                                       index.reset(graph, trash);
+                        try {
+                            remo.remove(graph);
+                            unhandled.addAll(remo.getRoots());
+                        } catch (DatabaseException e) {
+                            // Something went wrong - try to remove this later
+                            // with EntityRemover
+                            unhandled.add(trash);
+                        }
+                        if (isIndexRoot) {
+                            // TODO: this should be an utility
+                            GenericRelationIndex index = graph.adapt(L0X.DependenciesRelation,
+                                    GenericRelationIndex.class);
+                            IndexedRelations ir = graph.getService(IndexedRelations.class);
+                            // Deletes index files
+                            ir.reset(null, graph, L0X.DependenciesRelation, trash);
+                            // Notifies DB listeners
+                            index.reset(graph, trash);
                         }
                     }
                     if (mon.isCanceled())
@@ -1283,6 +1316,15 @@ public class Layer0Utils {
                     mon.newChild(1000);
                 }
             });
+
+            session.syncRequest(new WriteRequest() {
+                @Override
+                public void perform(WriteGraph graph) throws DatabaseException {
+                    for (Resource r : unhandled)
+                        EntityRemover.remove(graph, r);
+                }
+            });
+
             if (mon.isCanceled())
                 return;
             mon.subTask("Purging Database");
@@ -1340,6 +1382,46 @@ public class Layer0Utils {
        }
        return result;
     }
+
+    public static Resource possibleObjectForType(ReadGraph graph, Resource type, Resource relation) throws DatabaseException {
+        PropertyInfo pi = graph.syncRequest(new PropertyInfoRequest(relation));
+        return possibleObjectForType(graph, type, relation, pi.isFunctional);
+    }
+
+    public static Resource possibleObjectForType(ReadGraph graph, Resource type, Resource relation, boolean functional) throws DatabaseException {
+        if(functional) {
+            Layer0 L0 = Layer0.getInstance(graph);
+            Resource result = graph.getPossibleObject(type, relation);
+            if(result != null)
+                return result;
+            for(Resource su : graph.getObjects(L0.Inherits, type)) {
+                Resource r = possibleObjectForType(graph, su, relation, functional);
+                if(r != null) {
+                    if(result != null)
+                        return null;
+                    result = r;
+                }
+            }
+            return result;
+        } else {
+            Set<Resource> found = objectsForTypeNonFunctional(graph, type, relation, new HashSet<>());
+            return found.size() == 1 ? found.iterator().next() : null;
+        }
+    }
+
+    private static Set<Resource> objectsForTypeNonFunctional(ReadGraph graph, Resource type, Resource relation, Set<Resource> found) throws DatabaseException {
+        Layer0 L0 = Layer0.getInstance(graph);
+        found.addAll(graph.getObjects(type, relation));
+        for(Resource su : graph.getObjects(L0.Inherits, type)) {
+            objectsForTypeNonFunctional(graph, su, relation, found);
+        }
+        return found;
+    }
+
+    public static Resource getPossiblePredicateByNameFromType(ReadGraph graph, Resource type, String name) throws DatabaseException {
+       Map<String,Resource> domain = getDomainOf(graph, type);
+       return domain.get(name); 
+    }
     
     public static Resource getPossiblePredicateByName(ReadGraph graph, Resource instance, String predicateName) throws DatabaseException {
        for(Resource type : graph.getPrincipalTypes(instance)) {
@@ -1387,18 +1469,55 @@ public class Layer0Utils {
         return PrettyPrintTG.print(tg, ignoreIdentifiers);
     }
 
-    public static Resource getPossibleAssertedObject(ReadGraph graph, Resource resource, Resource predicate) throws DatabaseException {
-       Resource result = null;
-       for(Resource type : graph.getPrincipalTypes(resource)) {
-               Collection<Resource> rs = graph.getAssertedObjects(type, predicate);
-               if(rs.size() > 1) return null;
-               if(rs.size() == 1) {
-                       Resource ass = rs.iterator().next();
-                       if (result != null && !result.equals(ass)) return null;
-                       result = ass;
-               }
-       }
-       return result;
+    /**
+     * Adds a random {@link GUID} as a value for <code>L0.identifier</code>
+     * 
+     * @param graph
+     * @param component
+     *            for which the identifier is added
+     * @param add
+     *            <code>true</code> to invoke addLiteral, <code>false</code> to
+     *            invoke claimLiteral
+     * @throws DatabaseException
+     */
+    public static void claimNewIdentifier(WriteGraph graph, Resource component, boolean add) throws DatabaseException {
+        Layer0 L0 = Layer0.getInstance(graph);
+        GUID guid = GUID.random();
+        if (add)
+            graph.addLiteral(component, L0.identifier, L0.identifier_Inverse, L0.GUID, guid, GUID.BINDING);
+        else
+            graph.claimLiteral(component, L0.identifier, L0.identifier_Inverse, L0.GUID, guid, GUID.BINDING);
     }
-    
+
+    /**
+     * Sets a new random unique identifier for the specified entity if it already
+     * has an identifier. If the entity does not have a previous identifier, nothing
+     * is done.
+     * 
+     * @param graph
+     * @param entity
+     *            for which the identifier is added
+     * @return <code>true</code> if the identifier was renewed, <code>false</code>
+     *         otherwise
+     * @throws DatabaseException
+     * @see {@link #claimNewIdentifier(WriteGraph, Resource, boolean)}
+     */
+    public static boolean renewIdentifier(WriteGraph graph, Resource entity) throws DatabaseException {
+        Layer0 L0 = Layer0.getInstance(graph);
+        Statement stm = graph.getPossibleStatement(entity, L0.identifier);
+        if (stm != null) {
+            graph.claimValue(stm.getObject(), GUID.random(), GUID.BINDING);
+            return true;
+        }
+        return false;
+    }
+
+    public static void addMetadataListener(ChangeListener listener) {
+        SimanticsInternal.getSession().getService(GraphChangeListenerSupport.class).addMetadataListener(listener);
+    }
+
+    public static void removeMetadataListener(ChangeListener listener) {
+        SimanticsInternal.getSession().getService(GraphChangeListenerSupport.class).removeMetadataListener(listener);
+    }
+
 }