]> gerrit.simantics Code Review - simantics/platform.git/commitdiff
Fixed multiple issues causing dangling references to discarded queries 19/4419/4 master
authorJussi Koskela <jussi.koskela@semantum.fi>
Wed, 2 Sep 2020 09:17:31 +0000 (12:17 +0300)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Thu, 8 Oct 2020 07:56:28 +0000 (07:56 +0000)
gitlab #594

Change-Id: Iaa6b12f60d7dfa2bbcbc9614ef837973885586cc

19 files changed:
bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/CacheEntry.java
bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/CacheEntryBase.java
bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/ExternalReadEntry.java
bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryCollectorImpl.java
bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryIdentityHash.java
bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryIdentityHashSet.java
bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryListening.java
bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java
bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/NodeStructureRequest.java
bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/variable/NodeValueRequest.java
bundles/org.simantics.diagram/src/org/simantics/diagram/symbollibrary/ui/SymbolLibraryComposite.java
bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/CopyAdvisorUtil.java
bundles/org.simantics.document.server/src/org/simantics/document/server/request/DocumentRequest.java
bundles/org.simantics.document.server/src/org/simantics/document/server/request/NodesRequest.java
bundles/org.simantics.g2d/src/org/simantics/g2d/gallery/GalleryViewer.java
bundles/org.simantics.graph/src/org/simantics/graph/refactoring/FixExportedOntology.java
bundles/org.simantics.modeling/src/org/simantics/modeling/mapping/ComponentCopyAdvisor.java
bundles/org.simantics.modeling/src/org/simantics/modeling/scl/GraphModuleSourceRepository.java
bundles/org.simantics.scenegraph.profile/src/org/simantics/scenegraph/profile/common/ObserverGroupListener.java

index edb0870254f327ebba56d297c2a166a40003efd2..a650f32ce8dc6ee1f70166cbb3e21c31b6604f17 100644 (file)
@@ -42,6 +42,7 @@ public abstract class CacheEntry<Procedure> {
     abstract Query getQuery();
 
     abstract CacheEntry pruneFirstParents();
+    abstract void pruneParentSet();
     abstract void removeParent(CacheEntry entry);
     abstract void addParent(CacheEntry entry);
     abstract boolean hasParents();
index 56da11ace36b718d75ce3fa48ac9012c6f3eedf0..98e7f0d00554f1c3f351339d8aa4b891028966ae 100644 (file)
@@ -278,7 +278,31 @@ public abstract class CacheEntryBase<Procedure> extends CacheEntry<Procedure> {
        }
         
     }
-    
+
+    @Override
+    void pruneParentSet() {
+        // First parent is discarded => look for more parents
+        if(p2OrParents instanceof QueryIdentityHashSet) {
+
+            QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;
+            set.removeDiscardedReally();
+            if(set.isEmpty()) p2OrParents = null;
+
+        } else if(p2OrParents instanceof CacheEntry) {
+
+            CacheEntry entry = (CacheEntry)p2OrParents;
+            if(entry.isDiscarded()) {
+                // Second entry is also discarded => all empty
+                p2OrParents = null;
+            }
+
+        } else {
+
+            // Nothing left
+
+        }
+    }
+
     @Override
     final public void removeParent(CacheEntry entry) {
        
index 6253744bb05e26d012ff3b86906e465fc9911e95..2980b9b15f72e9a54b00a96fe06a397ef60b59b6 100644 (file)
@@ -28,7 +28,7 @@ final public class ExternalReadEntry<T> extends CacheEntryBase<AsyncProcedure<T>
     final LinkedList<T> items = new LinkedList<T>();
 
     protected ExternalRead<T> id;
-    protected ReadGraphImpl graph;
+    protected QueryProcessor processor;
     protected boolean registered = false;
 
     @Override
@@ -49,7 +49,7 @@ final public class ExternalReadEntry<T> extends CacheEntryBase<AsyncProcedure<T>
     public void discard() {
         id.unregistered();
         id = null;
-        graph = null;
+        processor = null;
         super.discard();
     }
 
@@ -65,7 +65,7 @@ final public class ExternalReadEntry<T> extends CacheEntryBase<AsyncProcedure<T>
     public ExternalReadEntry(ExternalRead<T> request, ReadGraphImpl graph) {
         assert request != null;
         this.id = request;
-        this.graph = graph;
+        this.processor = graph.processor;
     }
     
     @Override
@@ -213,7 +213,7 @@ final public class ExternalReadEntry<T> extends CacheEntryBase<AsyncProcedure<T>
 
                synchronized(items) {
                        items.addLast(result);
-                               graph.processor.updatePrimitive(id);
+                               processor.updatePrimitive(id);
                        // TODO: implement flags/logic in ExternalRead to state that all but the latest request result can be evaporated
                        // In some cases where data is produced really fast this might be necessary but currently this queueing will do.
                }
@@ -229,7 +229,7 @@ final public class ExternalReadEntry<T> extends CacheEntryBase<AsyncProcedure<T>
 
        @Override
        public boolean isDisposed() {
-               return registered && (isDiscarded() || !graph.processor.isBound(this));
+               return registered && (isDiscarded() || !processor.isBound(this));
        }
 
     @Override
index 0b6f6ef834411316255ac12e80f56faa3d3bdd93..e6dc252c41f210d00586895aeabe6d7c619001f3 100644 (file)
@@ -148,6 +148,7 @@ class QueryCollectorImpl implements QueryProcessor.QueryCollector {
                                                        
                                                } else {
                                                        
+                                                       entry.pruneParentSet();
                                                        support.setLevel(entry, parent.getLevel() + 1);
                                                        
                                                }
index ffbf4b2a9a34f2f20814b2e17df54a6ee66f349b..2db67c67970b165f836317cbbaaabea40fa6994d 100644 (file)
@@ -229,7 +229,11 @@ abstract public class QueryIdentityHash extends THash {
                        // TODO Auto-generated method stub
                        return null;
                }
-        
+
+        @Override
+            void pruneParentSet() {
+        }
+
     };
 
     /**
index 3a7eb61820203ffc5e079d0a57b12f7fe8c86842..ac2602fcb647b4ad54e8de9edf1a8c4412313d95 100644 (file)
@@ -147,7 +147,25 @@ final public class QueryIdentityHashSet extends QueryIdentityHash implements Ite
         }
         
     }
-    
+
+    final public void removeDiscardedReally() {
+
+        tempDisableAutoCompaction();
+        try {
+
+            for(int i=0;i<_set.length;i++) {
+                CacheEntry entry = _set[i];
+                if(entry != null && REMOVED != entry) {
+                    if(entry.isDiscarded()) removeAt(i);
+                }
+            }
+
+        } finally {
+            reenableAutoCompaction(false);
+        }
+
+    }
+
     /**
      * Creates an iterator over the values of the set.  The iterator
      * supports element deletion.
index 91200f1fc3ad037393bc5e3794d6acc4c29a4e7b..e0b39d6516055656995689f90c1506c19bb26265 100644 (file)
@@ -415,7 +415,7 @@ public class QueryListening {
 
         @Override
         public void run() {
-            ListenerEntry entry = addedEntries.get(base);
+            ListenerEntry entry = addedEntries.remove(base);
             if(entry != null) entry.setLastKnown(result);
         }
 
index 1bcd918771f3db8520507e6893b2de2376d61164..16f88a340a63e7f9dfeb9c0d7ef751a7ff8e7048 100644 (file)
@@ -179,7 +179,6 @@ implements Read<Function1<EvaluationContext,Object>> {
                 b2.append('\n');
             }
             SCLDatabaseException exception = new SCLDatabaseException(b.toString()+b2.toString(), b2.toString(), e.getErrors()); 
-            LOGGER.info(exception.getMessage(), exception);
             throw exception; 
         } catch(Throwable e) {
             // Should not happen!
index 5d21ca56bdf5ab3a0d0ba9aa7cd7a11542379920..707fdbd0ee46463a586eda41827beb5b9118085f 100644 (file)
@@ -7,174 +7,177 @@ import java.util.Map;
 import org.simantics.databoard.util.ObjectUtils;
 import org.simantics.db.ReadGraph;
 import org.simantics.db.common.request.ParametrizedPrimitiveRead;
-import org.simantics.db.common.utils.Logger;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.layer0.variable.Variables.NodeStructure;
 import org.simantics.db.procedure.Listener;
 import org.simantics.simulator.variable.exceptions.NodeManagerException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import gnu.trove.map.hash.THashMap;
 
 @SuppressWarnings("rawtypes")
 class NodeStructureRequest extends ParametrizedPrimitiveRead<VariableNode, NodeStructure> implements VariableNodeReadRunnable {
 
-       private Listener<NodeStructure> listener = null;
-       private NodeStructure value = Variables.PENDING_NODE_STRUCTURE;
-       private boolean wasRun = false;
-
-       static class Probe implements Runnable {
-
-               private VariableNode node;
-               public NodeStructure result;
-
-               public Probe(VariableNode node) {
-                       this.node = node;
-               }
-
-               @SuppressWarnings("unchecked")
-               @Override
-               public void run() {
-                       try {
-                               result = NodeStructureRequest.get(node);
-                               node.support.structureCache.put(node.node, result, 1000000000L);
-                       } catch (NodeManagerException e) {
-                               e.printStackTrace();
-                       }
-               }
-
-       }
-
-       public NodeStructureRequest(VariableNode node) {
-               super(node);
-       }
-
-       @SuppressWarnings("unchecked")
-       @Override
-       public void register(ReadGraph graph, final Listener<NodeStructure> procedure) {
-
-               if(procedure.isDisposed()) {
-
-                       // We are not listening
-                       NodeStructure result = (NodeStructure)parameter.support.structureCache.get(parameter.node);
-
-                       if(result != null) {
-                               // Return cached value immediately
-                               procedure.execute(result);
-                       } else {
-                               NodeStructureRequest.Probe probe = new Probe(parameter);
-                               parameter.support.manager.getRealm().asyncExec(probe);
-                               if(probe.result != null) {
-                                       procedure.execute(probe.result);
-                               } else {
-                                       procedure.execute(Variables.PENDING_NODE_STRUCTURE);
-                               }
-                       }
-
-                       return;
-
-               }
-
-               // We need to listen
-               listener = procedure;
-               // Register listening
-               parameter.support.manager.addNodeListener(parameter.node, this);
-               synchronized(this) {
-                       if(wasRun) {
-                               procedure.execute(value);
-                       } else {
-                               NodeStructure result = (NodeStructure)parameter.support.structureCache.get(parameter.node);
-                               if(result != null) {
-                                       procedure.execute(result);
-                               } else {
-                                       procedure.execute(Variables.PENDING_NODE_STRUCTURE);
-                               }
-                       }
-               }
-
-       }
-
-       static class NodeListener implements VariableNodeReadRunnable {
-
-               private VariableNode node;
-               private NodeStructureRequest request;
-
-               public NodeListener(VariableNode node, NodeStructureRequest request) {
-                       this.node = node;
-                       this.request = request;
-               }
-
-               @SuppressWarnings("unchecked")
-               @Override
-               public void run() {
-                       node.support.manager.addNodeListener(node.node, request);
-               }
-
-       }
-
-       @SuppressWarnings("unchecked")
-       @Override
-       public void unregistered() {
-               parameter.support.manager.removeNodeListener(parameter.node, this);
-               parameter.support.structureCache.removeListening(parameter.node);
-               listener = null;
-       }
-
-       @SuppressWarnings("unchecked")
-       public static NodeStructure get(VariableNode parameter) throws NodeManagerException {
-               List<?> children = parameter.support.manager.getChildren(parameter.node);
-               List<?> properties = parameter.support.manager.getProperties(parameter.node);
-               Map<String, Object> childMap = Collections.emptyMap();
-               Map<String, Object> propertyMap = childMap;
-               if(!children.isEmpty()) {
-                       childMap = new THashMap<>(children.size());
-                       for(Object o : children) {
-                               String name = parameter.support.manager.getName(o);
-                               childMap.put(name, o);
-                       }
-               }
-               if(!properties.isEmpty()) {
-                       propertyMap = new THashMap<>(properties.size());
-                       for(Object o : properties) {
-                               String name = parameter.support.manager.getName(o);
-                               propertyMap.put(name, o);
-                       }
-               }
-               return new NodeStructure(childMap, propertyMap);
-       }
-
-       @SuppressWarnings("unchecked")
-       @Override
-       public synchronized void run() {
-               try {
-                       // Cache this value with infinite cache time since we are listening
-                       NodeStructure newValue = get(parameter);
-                       if (wasRun && ObjectUtils.objectEquals(value, newValue)) {
-                               //System.out.println("CACHE VALUE MATCH (" + newValue + ") for " + node.node);
-                               return;
-                       }
-                       value = newValue;
-                       parameter.support.structureCache.put(parameter.node, value);
-               } catch (Throwable e) {
-                       // Must catch everything to prevent DB client from getting stuck.
-                       Logger.defaultLogError(e);
-                       // Invoke the exception method of the listener
-                       Listener<NodeStructure> listener = this.listener;
-                       if (listener != null) listener.exception(new DatabaseException("External data access error", e));
-                       wasRun = true;
-                       return;
-               }
-
-               // Must always invoke an existing listener, regardless of earlier errors.
-               Listener<NodeStructure> listener = this.listener;
-               if (listener != null) {
-                       listener.execute(value);
-               }
-               wasRun = true;
-       }
-
-       @Override
-       public String toString() {
-               return "NodeStructureRequest.run @ " + System.identityHashCode(this);
-       }
+    private static final Logger LOGGER = LoggerFactory.getLogger(NodeStructureRequest.class);
+
+    private Listener<NodeStructure> listener = null;
+    private NodeStructure value = Variables.PENDING_NODE_STRUCTURE;
+    private boolean wasRun = false;
+
+    static class Probe implements Runnable {
+
+        private VariableNode node;
+        public NodeStructure result;
+
+        public Probe(VariableNode node) {
+            this.node = node;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public void run() {
+            try {
+                result = NodeStructureRequest.get(node);
+                node.support.structureCache.put(node.node, result, 1000000000L);
+            } catch (NodeManagerException e) {
+                e.printStackTrace();
+            }
+        }
+
+    }
+
+    public NodeStructureRequest(VariableNode node) {
+        super(node);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public void register(ReadGraph graph, final Listener<NodeStructure> procedure) {
+
+        if(procedure.isDisposed()) {
+
+            // We are not listening
+            NodeStructure result = (NodeStructure)parameter.support.structureCache.get(parameter.node);
+
+            if(result != null) {
+                // Return cached value immediately
+                procedure.execute(result);
+            } else {
+                NodeStructureRequest.Probe probe = new Probe(parameter);
+                parameter.support.manager.getRealm().asyncExec(probe);
+                if(probe.result != null) {
+                    procedure.execute(probe.result);
+                } else {
+                    procedure.execute(Variables.PENDING_NODE_STRUCTURE);
+                }
+            }
+
+            return;
+
+        }
+
+        // We need to listen
+        listener = procedure;
+        // Register listening
+        parameter.support.manager.addNodeListener(parameter.node, this);
+        synchronized(this) {
+            if(!wasRun) {
+                NodeStructure result = (NodeStructure)parameter.support.structureCache.get(parameter.node);
+                if(result != null) {
+                    procedure.execute(result);
+                } else {
+                    procedure.execute(Variables.PENDING_NODE_STRUCTURE);
+                }
+            }
+        }
+
+    }
+
+    static class NodeListener implements VariableNodeReadRunnable {
+
+        private VariableNode node;
+        private NodeStructureRequest request;
+
+        public NodeListener(VariableNode node, NodeStructureRequest request) {
+            this.node = node;
+            this.request = request;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public void run() {
+            node.support.manager.addNodeListener(node.node, request);
+        }
+
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public void unregistered() {
+        parameter.support.manager.removeNodeListener(parameter.node, this);
+        parameter.support.structureCache.removeListening(parameter.node);
+        listener = null;
+    }
+
+    @SuppressWarnings("unchecked")
+    public static NodeStructure get(VariableNode parameter) throws NodeManagerException {
+        List<?> children = parameter.support.manager.getChildren(parameter.node);
+        List<?> properties = parameter.support.manager.getProperties(parameter.node);
+        Map<String, Object> childMap = Collections.emptyMap();
+        Map<String, Object> propertyMap = childMap;
+        if(!children.isEmpty()) {
+            childMap = new THashMap<>(children.size());
+            for(Object o : children) {
+                String name = parameter.support.manager.getName(o);
+                childMap.put(name, o);
+            }
+        }
+        if(!properties.isEmpty()) {
+            propertyMap = new THashMap<>(properties.size());
+            for(Object o : properties) {
+                String name = parameter.support.manager.getName(o);
+                propertyMap.put(name, o);
+            }
+        }
+        return new NodeStructure(childMap, propertyMap);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public synchronized void run() {
+        try {
+            // Cache this value with infinite cache time since we are listening
+            NodeStructure newValue = get(parameter);
+            if (wasRun && ObjectUtils.objectEquals(value, newValue)) {
+                //System.out.println("CACHE VALUE MATCH (" + newValue + ") for " + node.node);
+                return;
+            }
+            value = newValue;
+            parameter.support.structureCache.put(parameter.node, value);
+        } catch (Throwable e) {
+            // Must catch everything to prevent DB client from getting stuck.
+            LOGGER.error("Error while computing node structure", e);
+            // Invoke the exception method of the listener
+            Listener<NodeStructure> listener = this.listener;
+            if (listener != null) {
+                listener.exception(new DatabaseException("External data access error", e));
+                wasRun = true;
+            }
+            return;
+        }
+
+        // Must always invoke an existing listener, regardless of earlier errors.
+        Listener<NodeStructure> listener = this.listener;
+        if (listener != null) {
+            listener.execute(value);
+            wasRun = true;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "NodeStructureRequest.run @ " + System.identityHashCode(this);
+    }
 
 }
\ No newline at end of file
index 140ee8fa0841648805803c027686ac2b6efb2441..b44f16b30eeedc3ca7ee15fd9d023e0505703a9b 100644 (file)
@@ -110,9 +110,7 @@ class NodeValueRequest extends ParametrizedPrimitiveRead<Pair<VariableNode,Bindi
         // Register listening
         node.support.manager.addNodeListener(node.node, this);
         synchronized(this) {
-            if(wasRun) {
-                procedure.execute(value);
-            } else {
+            if(!wasRun) {
                 Variant result = (Variant)node.support.valueCache.get(node.node);
                 if(result != null) {
                     procedure.execute(result);
@@ -219,8 +217,10 @@ class NodeValueRequest extends ParametrizedPrimitiveRead<Pair<VariableNode,Bindi
                 LOGGER.error("Error while computing node value", e);
             // Invoke the exception method of the listener
             Listener<Variant> listener = this.listener;
-            if (listener != null) listener.exception(new DatabaseException("External data access error", e));
-            wasRun = true;
+            if (listener != null) {
+                listener.exception(new DatabaseException("External data access error", e));
+                wasRun = true;
+            }
             return;
         }
         // Must always invoke an existing listener, regardless of earlier errors.
@@ -228,8 +228,8 @@ class NodeValueRequest extends ParametrizedPrimitiveRead<Pair<VariableNode,Bindi
         if (listener != null) {
             //System.out.println("LISTENER " + listener + " invoked with value " + value);
             listener.execute(value);
+            wasRun = true;
         }
-        wasRun = true;
     }
 
     @Override
index af58be479ec1c7bf7ac4687d11ed478e26995155..31c091fd4eed1041e9b869639463f0ec5e4adfbb 100644 (file)
@@ -88,7 +88,6 @@ import org.simantics.diagram.internal.Activator;
 import org.simantics.diagram.symbolcontribution.CompositeSymbolGroup;
 import org.simantics.diagram.symbolcontribution.IIdentifiedObject;
 import org.simantics.diagram.symbolcontribution.ISymbolProvider;
-import org.simantics.diagram.symbolcontribution.IdentifiedObject;
 import org.simantics.diagram.symbolcontribution.SymbolProviderFactory;
 import org.simantics.diagram.symbollibrary.IModifiableSymbolGroup;
 import org.simantics.diagram.symbollibrary.ISymbolGroup;
@@ -1118,24 +1117,22 @@ public class SymbolLibraryComposite extends Composite {
             json.append(" \"res\" : [");
             int pos = 0;
             for(int i=0;i<res.length;i++) {
-               if(pos > 0) json.append(",");
-               Object r = res[i];
-               if(r instanceof IdentifiedObject) {
-                       Object id = ((IdentifiedObject) r).getId();
-                       if(id instanceof IAdaptable) {
-                               Object resource = ((IAdaptable) id).getAdapter(Resource.class);
-                               if(resource != null) {
-                                       long rid = ((Resource)resource).getResourceId();
-                               json.append(Long.toString(rid));
-                               pos++;
-                               }
-                       }
-               }
+                if(pos > 0) json.append(",");
+                Object r = res[i];
+                if(r instanceof IAdaptable) {
+                    Resource resource = ((IAdaptable) r).getAdapter(Resource.class);
+                    if(resource != null) {
+                        long rid = resource.getResourceId();
+                        json.append(Long.toString(rid));
+                        pos++;
+                    }
+                }
             }
             json.append("] }");
             
-            StringSelection text = new StringSelection(json.toString());
-            PlaintextTransfer plainText = new PlaintextTransfer(json.toString()); 
+            String jsonText = json.toString();
+            StringSelection text = new StringSelection(jsonText);
+            PlaintextTransfer plainText = new PlaintextTransfer(jsonText); 
             
             return new MultiTransferable(local, text, plainText);
             
index ab28463e84d5a8e3d3ae81af98b52f990393f167..bbf9a8e5b23d2134f536bacb21cae26d058e815e 100644 (file)
@@ -11,8 +11,6 @@
  *******************************************************************************/
 package org.simantics.diagram.synchronization.graph;
 
-import gnu.trove.map.hash.THashMap;
-
 import java.util.Map;
 import java.util.Set;
 import java.util.function.BiFunction;
@@ -46,6 +44,10 @@ import org.simantics.diagram.synchronization.SynchronizationHints;
 import org.simantics.graph.db.TransferableGraphs;
 import org.simantics.graph.representation.TransferableGraph1;
 import org.simantics.layer0.Layer0;
+import org.simantics.utils.datastructures.BinaryFunction;
+
+import gnu.trove.map.hash.THashMap;
+import gnu.trove.set.hash.THashSet;
 
 /**
  * This class contains utility methods for the basic cut/copy operations
@@ -82,6 +84,15 @@ public class CopyAdvisorUtil {
 
     public static final boolean DEBUG_COPY = DebugPolicy.DEBUG_COPY_PASTE;
 
+    private static class Statement4 {
+        public final Statement stm;
+        public final Resource inverse;
+        public Statement4(Statement stm, Resource inverse) {
+            this.stm = stm;
+            this.inverse = inverse;
+        }
+    }
+
     /**
      * @param context a synchronization context instance, such as
      *        {@link GraphToDiagramSynchronizer}
@@ -364,8 +375,10 @@ public class CopyAdvisorUtil {
      * @throws DatabaseException
      */
     public static Resource copy2(WriteGraph graph, Resource source,
-            BiFunction<ReadGraph, Statement, StatementEvaluation> advisor) throws DatabaseException {
-        return copy2(graph, source, 0, advisor, new THashMap<Object, Object>());
+            BiFunction<ReadGraph, Statement, StatementEvaluation> advisor)
+                    throws DatabaseException
+    {
+        return copy2(graph, source, advisor, new THashMap<>());
     }
 
     /**
@@ -380,13 +393,52 @@ public class CopyAdvisorUtil {
      * @throws DatabaseException 
      */
     public static Resource copy2(WriteGraph graph, Resource source,
-            BiFunction<ReadGraph, Statement, StatementEvaluation> advisor, Map<Object, Object> copyMap)
-            throws DatabaseException {
-        return copy2(graph, source, 0, advisor, copyMap);
+            BiFunction<ReadGraph, Statement, StatementEvaluation> advisor,
+            Map<Object, Object> copyMap)
+                    throws DatabaseException
+    {
+        Set<Statement4> pendingStatements = new THashSet<>();
+        Resource result = copy2(graph, source, 0, advisor, copyMap, pendingStatements);
+        postProcessStatements(graph, copyMap, pendingStatements);
+        return result;
+    }
+
+    /**
+     * Post-process pending statement
+     *  
+     * Rule: If both the subject and object of a pending source statement have
+     *       been copied, then the pending statement should also be copied.
+     */
+    private static void postProcessStatements(
+            WriteGraph graph,
+            Map<Object, Object> copyMap,
+            Set<Statement4> pendingStatements)
+                    throws DatabaseException
+    {
+        if (pendingStatements.isEmpty())
+            return;
+
+        if (DEBUG_COPY)
+            System.out.println("post processing " + pendingStatements.size() + " pending statements");
+        for (Statement4 srcStm : pendingStatements) {
+            // At this point, it is certain that srcStm subject has been copied
+            // but test it anyway.
+            Resource subjectCopy = (Resource) copyMap.get(srcStm.stm.getSubject());
+            Resource objectCopy = (Resource) copyMap.get(srcStm.stm.getObject());
+            if (subjectCopy == null || objectCopy == null) {
+                if (DEBUG_COPY)
+                    System.out.println("skipping pending statement: " + NameUtils.toString(graph, srcStm.stm));
+                continue;
+            }
+            if (DEBUG_COPY)
+                System.out.println("copying pending statement: " + NameUtils.toString(graph, srcStm.stm));
+            graph.claim(subjectCopy, srcStm.stm.getPredicate(), srcStm.inverse, objectCopy);
+        }
     }
 
     private static Resource copy2(final WriteGraph graph, final Resource source, final int level,
-            BiFunction<ReadGraph, Statement, StatementEvaluation> advisor, Map<Object, Object> copyMap)
+            BiFunction<ReadGraph, Statement, StatementEvaluation> advisor, Map<Object, Object> copyMap,
+            Set<Statement4> pendingSourceStatements)
     throws DatabaseException {
         if (DEBUG_COPY)
             System.out.println("[" + level + "] CopyAdvisorUtil.copy(" + NameUtils.getSafeName(graph, source) + ", advisor=" + advisor + ")");
@@ -497,12 +549,18 @@ public class CopyAdvisorUtil {
                             if (DEBUG_COPY)
                                 System.out.println("[" + level + "]\t\tcopy whole object");
 
-                            Resource clone = copy2(graph, obj, level + 1, advisor, copyMap);
+                            Resource clone = copy2(graph, obj, level + 1, advisor, copyMap, pendingSourceStatements);
                             graph.claim(copy, relation, inverse, clone);
                         }
                     } else {
-                        if (DEBUG_COPY)
-                            System.out.println("[" + level + "]\t\tskipping statement");
+                        if (graph.isSubrelationOf(relation, L0.IsRelatedTo)) {
+                            if (DEBUG_COPY)
+                                System.out.println("[" + level + "]\t\tmarking statement as pending for post-processing");
+                            pendingSourceStatements.add(new Statement4(stm, inverse));
+                        } else {
+                            if (DEBUG_COPY)
+                                System.out.println("[" + level + "]\t\tskipping weak statement");
+                        }
                     }
                 }
             }
index 8acc2f0d192899316c870cfba8ce27366b93d699..5b76cd344043f10dd05428642bee688c418ed12f 100644 (file)
@@ -1,6 +1,7 @@
 package org.simantics.document.server.request;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
@@ -10,7 +11,7 @@ import java.util.Set;
 import org.simantics.db.AsyncReadGraph;
 import org.simantics.db.ReadGraph;
 import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
-import org.simantics.db.common.request.AsyncReadRequest;
+import org.simantics.db.common.request.UnaryAsyncRead;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.layer0.request.VariableRead;
 import org.simantics.db.layer0.variable.Variable;
@@ -28,49 +29,55 @@ public class DocumentRequest extends VariableRead<List<JSONObject>> {
         super(var);
        }
 
-       @Override
-       public List<JSONObject> perform(ReadGraph graph) throws DatabaseException {
-               
-               long s = System.nanoTime();
-               
-        Set<Variable> nodes = graph.syncRequest(new NodesRequest(variable), TransientCacheAsyncListener.<Set<Variable>>instance());
-        HashSet<JSONObject> rs = new HashSet<JSONObject>(); // result
-        if(nodes.isEmpty()) {
-            return Collections.emptyList();
-        }
+    static class CollectNodesRequest extends UnaryAsyncRead<Collection<Variable>, Collection<JSONObject>> {
 
-        if(PROFILE) {
-            long dura = System.nanoTime()-s;
-            System.err.println("DocumentRequest1 " + System.identityHashCode(this) + " in " + 1e-6*dura + "ms. " + variable.getURI(graph));
+        public CollectNodesRequest(Collection<Variable> nodes) {
+            super(nodes);
         }
 
-        graph.syncRequest(new AsyncReadRequest() {
+        @Override
+        public void perform(AsyncReadGraph graph, AsyncProcedure<Collection<JSONObject>> procedure) {
+            HashSet<JSONObject> rs = new HashSet<JSONObject>(); // result
 
-            @Override
-            public void run(AsyncReadGraph graph) throws DatabaseException {
+            for(Variable node : parameter) {
+                graph.asyncRequest(new NodeRequest(node), new AsyncProcedure<JSONObject> () {
 
-                for(Variable node : nodes) {
-                    graph.asyncRequest(new NodeRequest(node), new AsyncProcedure<JSONObject> () {
-
-                        @Override
-                        public void execute(AsyncReadGraph graph, JSONObject result) {
-                            synchronized (rs) {
-                                rs.add(result);
-                            }
+                    @Override
+                    public void execute(AsyncReadGraph graph, JSONObject result) {
+                        synchronized(rs) {
+                            rs.add(result);
                         }
+                    }
 
-                        @Override
-                        public void exception(AsyncReadGraph graph, Throwable throwable) {
-                        }
+                    @Override
+                    public void exception(AsyncReadGraph graph, Throwable throwable) {
+                    }
+
+                });
 
-                    });
-                    
-                }
-                
             }
-            
-        });
+            procedure.execute(graph, rs);
+
+        }
+
+    }
+
+    @Override
+    public List<JSONObject> perform(ReadGraph graph) throws DatabaseException {
+
+        long s = System.nanoTime();
+
+        Set<Variable> nodes = graph.syncRequest(new NodesRequest(variable), TransientCacheAsyncListener.<Set<Variable>>instance());
+        if(nodes.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        if(PROFILE) {
+            long dura = System.nanoTime()-s;
+            System.err.println("DocumentRequest1 " + System.identityHashCode(this) + " in " + 1e-6*dura + "ms. " + variable.getURI(graph));
+        }
 
+        Collection<JSONObject> rs = graph.syncRequest(new CollectNodesRequest(nodes));
 
         if(PROFILE) {
             long dura = System.nanoTime()-s;
index 99526368e8cc3e21e1b92451f4dd915db45d40ba..133264e29b09321e399831600106ce0227ba0a04 100644 (file)
@@ -2,11 +2,12 @@ package org.simantics.document.server.request;
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Set;
 
 import org.simantics.db.AsyncReadGraph;
 import org.simantics.db.ReadGraph;
-import org.simantics.db.common.request.AsyncReadRequest;
+import org.simantics.db.common.request.UnaryAsyncRead;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.layer0.request.VariableChildren;
 import org.simantics.db.layer0.request.VariableRead;
@@ -16,52 +17,57 @@ import org.simantics.structural.stubs.StructuralResource2;
 import org.simantics.utils.threads.logger.ITask;
 import org.simantics.utils.threads.logger.ThreadLogger;
 
-import gnu.trove.set.hash.THashSet;
-
 public class NodesRequest extends VariableRead<Set<Variable>> {
 
     public NodesRequest(Variable var) {
         super(var);
     }
 
-    @Override
-    public Set<Variable> perform(ReadGraph graph) throws DatabaseException {
+    static class CollectNodesRequest2 extends UnaryAsyncRead<Collection<Variable>, Set<Variable>> {
 
-        ITask task = DocumentRequest.PROFILE ? ThreadLogger.task(this) : null;
+        public CollectNodesRequest2(Collection<Variable> nodes) {
+            super(nodes);
+        }
 
-        StructuralResource2.getInstance(graph);
-        if(variable == null)
-            return Collections.emptySet();
+        @Override
+        public void perform(AsyncReadGraph graph, AsyncProcedure<Set<Variable>> procedure) {
+            HashSet<Variable> rs = new HashSet<Variable>(); // result
 
-        Set<Variable> nodes = new THashSet<Variable>();
+            for(Variable node : parameter) {
+                graph.asyncRequest(new NodesRequest2(node), new AsyncProcedure<Set<Variable>> () {
 
-        Collection<Variable> children = graph.syncRequest(new VariableChildren(variable));
+                    @Override
+                    public void execute(AsyncReadGraph graph, Set<Variable> result) {
+                        synchronized(rs) {
+                            rs.addAll(result);
+                        }
+                    }
 
-        graph.syncRequest(new AsyncReadRequest() {
+                    @Override
+                    public void exception(AsyncReadGraph graph, Throwable throwable) {
+                    }
 
-            @Override
-            public void run(AsyncReadGraph graph) throws DatabaseException {
+                });
 
-                for(Variable child : children) {
-                    graph.asyncRequest(new NodesRequest2(child), new AsyncProcedure<Set<Variable>>() {
+            }
+            procedure.execute(graph, rs);
 
-                        @Override
-                        public void execute(AsyncReadGraph graph, Set<Variable> result) {
-                            synchronized(nodes) {
-                                nodes.addAll(result);
-                            }
-                        }
+        }
 
-                        @Override
-                        public void exception(AsyncReadGraph graph, Throwable throwable) {
-                        }
-                        
-                    });
-                }
+    }
 
-            }
+    @Override
+    public Set<Variable> perform(ReadGraph graph) throws DatabaseException {
+
+        ITask task = DocumentRequest.PROFILE ? ThreadLogger.task(this) : null;
+
+        StructuralResource2.getInstance(graph);
+        if(variable == null)
+            return Collections.emptySet();
+
+        Collection<Variable> children = graph.syncRequest(new VariableChildren(variable));
 
-        });
+        Set<Variable> nodes = graph.syncRequest(new CollectNodesRequest2(children));
 
         if(DocumentRequest.PROFILE) task.finish();
 
index 5bbe9f26e7de24047b90c875f8e66809d542b897..d09ecf4a6b6f753f7f0adbf57060b3a36ac987c1 100644 (file)
@@ -631,8 +631,10 @@ public class GalleryViewer extends ContentViewer {
                 ctx.getThreadAccess().asyncExec(() -> {
                     //System.out.println(Thread.currentThread() + ": update scene graph(" + el + ")");
                     // Update scene graph and repaint.
-                    el.getElementClass().getSingleItem(GalleryItemSGNode.class).update(el);
-                    ctx.getContentContext().setDirty();
+                    if (!ctx.isDisposed()) {
+                        el.getElementClass().getSingleItem(GalleryItemSGNode.class).update(el);
+                        ctx.getContentContext().setDirty();
+                    }
                 });
                 break;
             }
index 17aa3ed26af30ab5143a2cacf7fb16b01d2a27d3..33291cdf108e75cef32b35e64e69b19d8583c589 100644 (file)
@@ -41,8 +41,9 @@ public class FixExportedOntology {
 
        private static Path replaceExtension(Path p, String newExtension) {
                String newName = p.getFileName().toString();
-               if (newName.contains("."))
-                       newName = newName.split("\\.")[0];
+               int lastDot = newName.lastIndexOf('.');
+               if (lastDot > -1)
+                       newName = newName.substring(0, lastDot);
                return p.resolveSibling(newName + newExtension);
        }
 
index 3e6e9c1b9527096cfa1835a50e43f6b2dfb97c7f..021ecc436b86c5d82af28d062cd45d4a88b38148 100644 (file)
@@ -32,7 +32,6 @@ import org.simantics.modeling.ModelingUtils;
 import org.simantics.modeling.services.ComponentNamingUtil;
 import org.simantics.modeling.services.NamingException;
 import org.simantics.project.IProject;
-import org.simantics.structural.stubs.StructuralResource2;
 
 import gnu.trove.map.hash.THashMap;
 
@@ -58,7 +57,6 @@ public class ComponentCopyAdvisor extends GraphCopyAdvisor {
     @Override
     public Object copy(ISynchronizationContext context, WriteGraph graph, Resource source, Resource sourceContainer,
             Resource targetContainer, Map<Object, Object> map) throws DatabaseException {
-        StructuralResource2 STR = StructuralResource2.getInstance(graph);
         Resource copy = CopyAdvisorUtil.copy2(graph, source, null, map);
 
         Layer0 L0 = Layer0.getInstance(graph);
index 8eed51aca7ed70401b7ddafb7da5a13bab85b2ff..a70844cd375c8956adb593cb71406e92d782e963 100644 (file)
@@ -12,6 +12,8 @@ import org.simantics.db.common.request.WriteRequest;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.procedure.SyncListener;
 import org.simantics.db.request.Read;
+import org.simantics.db.request.ReadExt;
+import org.simantics.db.request.RequestFlags;
 import org.simantics.layer0.Layer0;
 import org.simantics.modeling.ModelingUtils;
 import org.simantics.modeling.internal.Activator;
@@ -137,6 +139,29 @@ public enum GraphModuleSourceRepository implements ModuleSourceRepository {
         }
     }
 
+    static class PossibleResourceIU extends UnaryRead<String,Resource> implements ReadExt {
+
+        public PossibleResourceIU(String parameter) {
+            super(parameter);
+        }
+
+        @Override
+        public Resource perform(ReadGraph graph) throws DatabaseException {
+            return graph.getPossibleResource(parameter);
+        }
+
+        @Override
+        public boolean isImmutable(ReadGraph graph) throws DatabaseException {
+            return false;
+        }
+
+        @Override
+        public int getType() {
+            return RequestFlags.IMMEDIATE_UPDATE;
+        }
+        
+    }
+    
     static class ReadModuleSource extends UnaryRead<String, ModuleSource> {
         public ReadModuleSource(String moduleName) {
             super(moduleName);
@@ -144,7 +169,7 @@ public enum GraphModuleSourceRepository implements ModuleSourceRepository {
 
         @Override
         public ModuleSource perform(ReadGraph graph) throws DatabaseException {
-            Resource moduleResource = graph.getPossibleResource(parameter);
+            Resource moduleResource = graph.syncRequest(new PossibleResourceIU(parameter));
             if(moduleResource == null)
                 return null;
             Layer0 L0 = Layer0.getInstance(graph);
index 31295b9b692f60178c422ad7dda719089d87b76c..10e2abe67fb1f347ce8cc91dcf9553d9abc7c529 100644 (file)
@@ -44,7 +44,7 @@ public class ObserverGroupListener implements SetListener<Resource> {
     public void add(Resource item) {
         //System.out.println("Add to group(" + this + "): " + item);
         items.put(item, item);
-        observer.update();
+        observer.update(style, item);
     }
 
     @Override
@@ -52,7 +52,7 @@ public class ObserverGroupListener implements SetListener<Resource> {
 //        new Exception().printStackTrace();
         //System.out.println("Remove from group(" + this + "): " + item);
         items.remove(item);
-        observer.update();
+        observer.update(style, item);
     }
 
     @Override