]> gerrit.simantics Code Review - simantics/3d.git/blob - org.simantics.proconf.g3d/src/org/simantics/proconf/g3d/base/ScenegraphAdapterImpl.java
adfcc6527cf717428885a62e490d0ba95a26c2f9
[simantics/3d.git] / org.simantics.proconf.g3d / src / org / simantics / proconf / g3d / base / ScenegraphAdapterImpl.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007 VTT Technical Research Centre of Finland and others.\r
3  * All rights reserved. This program and the accompanying materials\r
4  * are made available under the terms of the Eclipse Public License v1.0\r
5  * which accompanies this distribution, and is available at\r
6  * http://www.eclipse.org/legal/epl-v10.html\r
7  *\r
8  * Contributors:\r
9  *     VTT Technical Research Centre of Finland - initial API and implementation\r
10  *******************************************************************************/\r
11 package org.simantics.proconf.g3d.base;\r
12 \r
13 import java.util.ArrayList;\r
14 import java.util.Collection;\r
15 import java.util.HashMap;\r
16 import java.util.HashSet;\r
17 import java.util.List;\r
18 import java.util.Queue;\r
19 import java.util.Set;\r
20 import java.util.Stack;\r
21 import java.util.concurrent.ConcurrentLinkedQueue;\r
22 \r
23 import org.simantics.db.AbstractQuery;\r
24 import org.simantics.db.Graph;\r
25 import org.simantics.db.GraphRequestAdapter;\r
26 import org.simantics.db.GraphRequestStatus;\r
27 import org.simantics.db.Resource;\r
28 import org.simantics.db.Session;\r
29 import org.simantics.db.queries.IGraphQuery;\r
30 import org.simantics.db.queries.IQuery;\r
31 import org.simantics.db.queries.IQueryListener;\r
32 import org.simantics.db.utils.transaction.MergingTransactionRunner;\r
33 import org.simantics.layer0.utils.EntityFactory;\r
34 import org.simantics.layer0.utils.IEntity;\r
35 import org.simantics.layer0.utils.Property;\r
36 import org.simantics.proconf.g3d.Resources;\r
37 import org.simantics.proconf.g3d.scenegraph.IGeometryNode;\r
38 import org.simantics.proconf.g3d.scenegraph.IGraphicsNode;\r
39 import org.simantics.proconf.g3d.scenegraph.RootGraphicsNode;\r
40 import org.simantics.proconf.g3d.stubs.G3DNode;\r
41 import org.simantics.utils.ErrorLogger;\r
42 import org.simantics.utils.datastructures.BijectionMap;\r
43 \r
44 import com.jme.scene.Node;\r
45 \r
46 \r
47 /**\r
48  * Scene-graph adapter :\r
49  * 1. Adapts graph change events into changes in actual scene-graph nodes.\r
50  * 2. Handles instantiating and disposing of Scene-graph nodes.\r
51  * 3.  \r
52  * \r
53  * @author Marko Luukkainen\r
54  *\r
55  */\r
56 public abstract class ScenegraphAdapterImpl implements ScenegraphAdapter {\r
57         \r
58         protected static boolean DEBUG = false;\r
59         \r
60         private RootGraphicsNode root;\r
61         private HashMap<Resource,ScenegraphQuery> scenegraphQueries = new HashMap<Resource, ScenegraphQuery>();\r
62     private HashMap<Resource,NodePropertyQuery> propertyQueries = new HashMap<Resource, NodePropertyQuery>();\r
63     private HashMap<Resource,NodeTransformationQuery> transformationQueries = new HashMap<Resource, NodeTransformationQuery>();\r
64         private HashMap<Resource, IGraphicsNode> abstractGraphicsNodes = new HashMap<Resource, IGraphicsNode>();\r
65     protected Queue<IGeometryNode> geometryUpdates = new ConcurrentLinkedQueue<IGeometryNode>();\r
66         \r
67     private BijectionMap<Resource, String> nameMap = new BijectionMap<Resource, String>();\r
68     \r
69     protected JmeRenderingComponent component;\r
70     \r
71     protected boolean viewChanged = false;\r
72     \r
73     protected Session session;\r
74     \r
75     private MergingTransactionRunner transactionRunner;\r
76     \r
77     public ScenegraphAdapterImpl(Session session, JmeRenderingComponent component) {\r
78         this.component = component;\r
79         this.session = session;\r
80         transactionRunner = new MergingTransactionRunner(session,true);\r
81     }\r
82     \r
83     \r
84     @Override\r
85     public JmeRenderingComponent getRenderingComponent() {\r
86         return component;\r
87     }\r
88     \r
89     /* (non-Javadoc)\r
90          * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#setRootNode(fi.vtt.simantics.g3d.stubs.G3DNode)\r
91          */\r
92     public void setRootNode(G3DNode rootNode) {\r
93         addSubnodeListener(rootNode);\r
94         root = new RootGraphicsNode(component,rootNode.getResource());\r
95         abstractGraphicsNodes.put(rootNode.getResource(),root);\r
96         G3DTools.reloadCache(rootNode.getGraph(),root.getResource());\r
97         addRootPropertyListener(rootNode.getGraph());\r
98     }\r
99     \r
100     /* (non-Javadoc)\r
101          * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#needsUpdateGeometry()\r
102          */\r
103     public boolean needsUpdateGeometry() {\r
104         return geometryUpdates.size() > 0;\r
105     }\r
106     \r
107     /* (non-Javadoc)\r
108          * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#updateGeometry(fi.vtt.simantics.g3d.scenegraph.IGeometryNode)\r
109          */\r
110     public void updateGeometry(IGeometryNode node) {\r
111         geometryUpdates.add(node);\r
112     }\r
113     \r
114     /* (non-Javadoc)\r
115          * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#updateGeometry(fi.vtt.simantics.db.connection.Resource)\r
116          */\r
117     public void updateGeometry(Resource nodeResource) {\r
118         geometryUpdates.add((IGeometryNode)abstractGraphicsNodes.get(nodeResource));\r
119     }\r
120     \r
121     \r
122     /* (non-Javadoc)\r
123          * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#updateGeometry(fi.vtt.simantics.db.connection.Graph)\r
124          */\r
125     public synchronized void updateGeometry(Graph graph) {\r
126         if (geometryUpdates.size() > 0) {\r
127                         for (IGeometryNode n : geometryUpdates) {\r
128                                 try {\r
129                                         n.updateGeometry(graph);\r
130                                         if (DEBUG) System.out.println("ScenegraphAdapterImpl: geometryUpdated " + n.getResource());\r
131                                 } catch (Exception e) {\r
132                                         ErrorLogger.defaultLogError("Failed to update geometry of node" + n.getResource(), e);\r
133                                 }\r
134                         }\r
135                         geometryUpdates.clear();\r
136                         viewChanged = true;\r
137                         //if (DEBUG) System.out.println("ScenegraphAdapterImpl: geometryUpdated");\r
138                 }\r
139     }\r
140     \r
141     /* (non-Javadoc)\r
142          * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#getNodes()\r
143          */\r
144     public Collection<IGraphicsNode> getNodes() {\r
145         return abstractGraphicsNodes.values();\r
146     }\r
147     \r
148     /* (non-Javadoc)\r
149          * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#getNode(fi.vtt.simantics.db.connection.Resource)\r
150          */\r
151     public IGraphicsNode getNode(Resource resource) {\r
152         return abstractGraphicsNodes.get(resource);\r
153     }\r
154     \r
155     /* (non-Javadoc)\r
156          * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#getRootNode()\r
157          */\r
158     public IGraphicsNode getRootNode() {\r
159         return getNode(getRootResource());\r
160     }\r
161     \r
162     /* (non-Javadoc)\r
163          * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#getNode(fi.vtt.simantics.layer0.utils.IEntity)\r
164          */\r
165     public IGraphicsNode getNode(IEntity IEntity) {\r
166         return abstractGraphicsNodes.get(IEntity.getResource());\r
167     }\r
168     \r
169     /* (non-Javadoc)\r
170          * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#getNodeResource(java.lang.String)\r
171          */\r
172     public Resource getNodeResource(String uid) {\r
173         return nameMap.getLeft(uid);\r
174     }\r
175     \r
176     /* (non-Javadoc)\r
177          * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#getNodeUID(fi.vtt.simantics.db.connection.Resource)\r
178          */\r
179     public String getNodeUID(Resource nodeResource) {\r
180         String name = nameMap.getRight(nodeResource);\r
181         if (name == null) {\r
182                 //name = UUID.randomUUID().toString();\r
183                 name = Long.toString(nodeResource.getResourceId());\r
184                 nameMap.map(nodeResource, name);\r
185         }\r
186         return name;\r
187     }\r
188     \r
189     /* (non-Javadoc)\r
190          * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#hasNode(fi.vtt.simantics.db.connection.Resource)\r
191          */\r
192     public boolean hasNode(Resource resource) {\r
193         return abstractGraphicsNodes.containsKey(resource);\r
194     }\r
195     \r
196     /* (non-Javadoc)\r
197          * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#getRootResource()\r
198          */\r
199     public Resource getRootResource() {\r
200         if (root == null)\r
201                 return null;\r
202         return root.getResource();\r
203     }\r
204     \r
205     /* (non-Javadoc)\r
206          * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#getRoot()\r
207          */\r
208     public Node getRoot() {\r
209         return root.getGroup();\r
210     }\r
211     \r
212     /* (non-Javadoc)\r
213          * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#dispose()\r
214          */\r
215     public void dispose() {\r
216         Set<Resource> shapes = new HashSet<Resource>(abstractGraphicsNodes.keySet());\r
217         for (Resource r : shapes) {\r
218             removeNode(r);\r
219         }\r
220     }\r
221     \r
222     /* (non-Javadoc)\r
223          * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#isChanged()\r
224          */\r
225     public boolean isChanged() {\r
226         return viewChanged;\r
227     }\r
228     \r
229     /* (non-Javadoc)\r
230          * @see fi.vtt.simantics.g3d.scenegraph.IScenegraphAdapter#setChanged(boolean)\r
231          */\r
232     public void setChanged(boolean changed) {\r
233         viewChanged = changed;\r
234     }\r
235     \r
236     \r
237     /**\r
238      * Instantiates Listener that listens hierarchy changes in the node (subnode\r
239      * added and/or removed)\r
240      * \r
241      * @param node\r
242      * @return\r
243      */\r
244     protected abstract ScenegraphQuery newSubnodeListener(G3DNode node);\r
245     \r
246     protected void addSubnodeListener(G3DNode node) {\r
247         if (DEBUG) System.out.println("ScenegraphAdapter.addSubnodeListener( " + node.getResource() + " )");\r
248         ScenegraphQuery q = newSubnodeListener(node);\r
249         node.getGraph().performQuery(q);\r
250         scenegraphQueries.put(node.getResource(), q);\r
251     }\r
252     \r
253     /**\r
254      * Returns propertyQuery for a single scene-graph node.\r
255      * @param node\r
256      * @return\r
257      */\r
258     protected abstract NodePropertyQuery newPropertyListener(G3DNode node);\r
259     \r
260     /**\r
261      * Returns transformationQuery for a single scene-graph node\r
262      * @param root\r
263      * @return\r
264      */\r
265     protected abstract NodeTransformationQuery newTransformationListener(G3DNode node);\r
266     \r
267     /**\r
268      * Returns propertyQuery for the root node.\r
269      * May return null if root node has no interesting properties.\r
270      * \r
271      * Potentially root node could contain lighting settings, and so on...\r
272      * \r
273      * @param root\r
274      * @return\r
275      */\r
276     protected abstract NodePropertyQuery newRootPropertyListener(G3DNode root);\r
277     \r
278     protected void addPropertyListener(G3DNode node) {\r
279         if (DEBUG) System.out.println("ScenegraphAdapter.addPropertyListener( " + node.getResource() + " )");\r
280         NodePropertyQuery q = newPropertyListener(node);\r
281         node.getGraph().performQuery(q);\r
282         propertyQueries.put(node.getResource(),q);\r
283     }\r
284     \r
285     \r
286     protected void addTransformationListener(G3DNode node) {\r
287         if (DEBUG) System.out.println("ScenegraphAdapter.addPropertyListener( " + node.getResource() + " )");\r
288         NodeTransformationQuery q = newTransformationListener(node);\r
289         node.getGraph().performQuery(q);\r
290         transformationQueries.put(node.getResource(),q);\r
291     }\r
292     \r
293     protected void addRootPropertyListener(Graph g) {\r
294         G3DNode node = root.getG3DNode(g);\r
295         if (DEBUG) System.out.println("ScenegraphAdapter.addRootPropertyListener( " + node.getResource() + " )");       \r
296         NodePropertyQuery q = newRootPropertyListener(node);\r
297         if (q == null)\r
298                 return;\r
299         node.getGraph().performQuery(q);\r
300         propertyQueries.put(node.getResource(),q);\r
301     }\r
302     \r
303     /**\r
304      * Instantiates a new scene-graph node\r
305      * \r
306      * @param parent the parent of the new node.\r
307      * @param node the new node.\r
308      * @return\r
309      */\r
310     protected abstract IGraphicsNode instantiateNode(IGraphicsNode parent, G3DNode node);\r
311     \r
312 \r
313     /**\r
314      * Adds node into scene-graph\r
315      * @param parent the parent of the node\r
316      * @param r resource of the node\r
317      * @return created scene-graph node\r
318      */\r
319     protected IGraphicsNode addNode(IEntity parent, IEntity r) {\r
320         if (!r.isInstanceOf(Resources.g3dResource.G3DNode)) {\r
321             ErrorLogger.defaultLogError("Trying to add node into scenegraph that is not instance of graphicsnode " + r,new Exception("ASSERT!"));\r
322             return null;\r
323         }\r
324         if (parent.equals(r)) {\r
325                 if (DEBUG) System.out.println("ThreeDimensionalEditorBase.addNodeP(" + r.getResource().getResourceId() + ") adding node to itself?!");\r
326             ErrorLogger.defaultLogError("Adding scnegraphnode " + r.getResource().getResourceId() + " to itself!", new Exception("ASSERT!"));\r
327             return abstractGraphicsNodes.get(r);\r
328         }\r
329         \r
330         if (abstractGraphicsNodes.containsKey(r)) {\r
331                 \r
332             IGraphicsNode inView = abstractGraphicsNodes.get(r);\r
333             if (inView.getParent() == null) {\r
334                 //if graphicsNode has no parent it must be the root node\r
335                 ErrorLogger.defaultLogError("Trying to add rootnode into scenegraph " + r, null);\r
336                 return null;\r
337             }\r
338             if (parent.equals(inView.getParent().getResource())) {\r
339                 if (DEBUG) System.out.println("ThreeDimensionalEditorBase.addNodeP(" + r.getResource().getResourceId() + ") already in view");\r
340                 return inView;\r
341             } else {\r
342                 if (DEBUG) System.out.println("ThreeDimensionalEditorBase.addNodeP(" + r.getResource().getResourceId() + ") already in view, but has different parent, current parent is ("+inView.getParent().getResource()+") and node is added to  ("+parent+") -> removing from old parent and inserting to new");\r
343                 removeNode(inView.getParent().getResource(),r.getResource());\r
344             }\r
345         }\r
346         \r
347         G3DNode node = new G3DNode(r);\r
348 \r
349         IGraphicsNode mo;\r
350         IGraphicsNode parentNode = abstractGraphicsNodes.get(parent);\r
351         if (parentNode == null) {\r
352                 if (DEBUG) System.out.println("No graphicsnode for (" + parent.getResource().getResourceId() + ")");\r
353             return null;\r
354         } else {\r
355             mo = instantiateNode(parentNode, node);\r
356             if (mo == null) {\r
357                 ErrorLogger.defaultLogError("Could not instantiate scenegraph node for " + r.getResource().getResourceId(), null);\r
358                 return null;\r
359             }\r
360             if (DEBUG) System.out.println("ThreeDimensionalEditorBase.addNodeP(" + r.getResource().getResourceId() + ") added to parent (" + parent.getResource().getResourceId() + ") " + mo.getClass());\r
361         }\r
362         addSubnodeListener(node);\r
363         addPropertyListener(node);\r
364         addTransformationListener(node);\r
365 \r
366         abstractGraphicsNodes.put(r.getResource(), mo);\r
367 \r
368         \r
369         \r
370         // FIXME : this is a hack to fix transformations of instantiated nodes\r
371 //              if (graph.getCurrentTransaction() != null) {\r
372 //                      try {\r
373 //                              G3DTools.propagateWorldTransformChange(parentNode\r
374 //                                              .getG3DNode());\r
375 //                              graph.commitChanges(CommitMessage.CHANGE_MESSAGE);\r
376 //                              // G3DNodeTools.transformationUpdate(graph, r.getId());\r
377 //                      } catch (Exception e) {\r
378 //                              ErrorLogger.defaultLogError(e);\r
379 //                      }\r
380 //              }\r
381 \r
382         \r
383         return mo;\r
384     }\r
385 \r
386     /**\r
387          * This is used only when view is disposed!\r
388          * \r
389          * @param r\r
390          */\r
391     private void removeNode(Resource r) {\r
392         NodeTransformationQuery tq = transformationQueries.get(r);\r
393         //StructuralChangeMonitor monitor = monitors.getLeft(r);\r
394         if (tq == null) {\r
395                 if (abstractGraphicsNodes.containsKey(r)) {\r
396                 // root node has no monitor (no transformation to monitor)\r
397                 //System.out.println("ThreeDimensionalEditorBase.removeNode(" + r + ") node has no monitor, but has node in scenegraph");  \r
398                 abstractGraphicsNodes.remove(r);\r
399                 if(scenegraphQueries.get(r) != null) {\r
400                         scenegraphQueries.get(r).dispose();\r
401                         scenegraphQueries.remove(r);\r
402                 }\r
403             } else {\r
404                 //System.out.println("ThreeDimensionalEditorBase.removeNode(" + r + ") not in view");\r
405             }\r
406                 return;\r
407         }\r
408         // remove listeners\r
409         propertyQueries.remove(r).dispose();     \r
410         transformationQueries.remove(r).dispose();\r
411         scenegraphQueries.get(r);\r
412         scenegraphQueries.remove(r).dispose();\r
413         // remove children\r
414         IGraphicsNode node = abstractGraphicsNodes.get(r);\r
415         ArrayList<IGraphicsNode> children = new ArrayList<IGraphicsNode>(node.getChildren());\r
416         for (IGraphicsNode n : children) {\r
417             removeNode(n.getResource());\r
418         }\r
419         // remove the node\r
420         if (DEBUG) System.out.println("ThreeDimensionalEditorBase.removeNode(" + r + ") removed");\r
421         \r
422         node.dispose();\r
423         abstractGraphicsNodes.remove(r);\r
424 \r
425     }\r
426     \r
427     /**\r
428      * Removes a scene-graph node.\r
429      * @param parent the parent of the node\r
430      * @param r the node.\r
431      */\r
432     protected void removeNode(Resource parent,Resource r) {\r
433         NodePropertyQuery q = propertyQueries.get(r);\r
434         if (q == null) {\r
435             assert(!abstractGraphicsNodes.containsKey(r));\r
436             if (DEBUG) System.out.println("ThreeDimensionalEditorBase.removeNodeP(" + r + ") not in view");\r
437             return;\r
438         }\r
439         \r
440         IGraphicsNode node = abstractGraphicsNodes.get(r);\r
441         Resource rParent = node.getParent().getResource();\r
442         if (!rParent.equals(parent)) {\r
443             // this event may happen, depending of the order of events in transaction\r
444                 if (DEBUG) System.out.println("ThreeDimensionalEditorBase.removeNodeP(" + r + ") trying to remove from wrong parent current ("+rParent+") remove parentnode null("+parent+")");    \r
445             return;\r
446         }\r
447         // removing listeners\r
448         propertyQueries.remove(r).dispose();\r
449         transformationQueries.remove(r).dispose();\r
450         scenegraphQueries.remove(r).dispose();\r
451         // remove node's children\r
452         ArrayList<IGraphicsNode> children = new ArrayList<IGraphicsNode>(node.getChildren());\r
453         for (IGraphicsNode n : children) {\r
454             removeNode(r,n.getResource());\r
455         }       \r
456         if (DEBUG) System.out.println("ThreeDimensionalEditorBase.removeNodeP(" + r + ") from ("+parent+")");    \r
457         // remove the node\r
458         \r
459         abstractGraphicsNodes.remove(r);\r
460         if (geometryUpdates.contains(node)) {\r
461                 geometryUpdates.remove(node);\r
462         }\r
463         node.dispose();\r
464     }\r
465 \r
466     \r
467     /**\r
468      * Query that tracks changes in resources.\r
469      * \r
470      * @author Marko Luukkainen\r
471      *\r
472      */\r
473     public abstract class NodeQuery extends AbstractQuery<Object>{\r
474         protected Resource nodeResource;\r
475         private boolean disposed = false;\r
476         private IQueryListener listener;\r
477         //private OverridingTransactionRunner runner;\r
478         \r
479         \r
480         public NodeQuery(Resource r) {\r
481                 this.nodeResource = r;\r
482                 //runner = new OverridingTransactionRunner(session,true);\r
483                 \r
484         }\r
485         \r
486         protected abstract Object compute2(Graph graph);\r
487         \r
488         @Override\r
489         public Object performQuery(Graph graph) {\r
490                 if (disposed) return null;\r
491                 return compute2(graph);\r
492         }\r
493         \r
494         /**\r
495          *  \r
496          * @param oldResult result of the query before the change.\r
497          * @param newResult result of the query after the change.\r
498          */\r
499         public abstract boolean updated(Graph graph, Object oldResult, Object newResult);\r
500         \r
501         @Override\r
502         public int getType() {\r
503                 return IQuery.SCHEDULED_UPDATE;\r
504         }\r
505         \r
506         @Override\r
507         public void resultChangedRaw(final Object oldResult, final Object newResult) {\r
508                 if (disposed)\r
509                         throw new RuntimeException("Updating disposed query"); //return;\r
510                 transactionRunner.run(new GraphRequestAdapter() {\r
511                 //session.asyncWrite(new GraphRequestAdapter() {\r
512                         @Override\r
513                         public GraphRequestStatus perform(Graph g) throws Exception {\r
514                                 if (!disposed) {\r
515                                         if (oldResult == IQueryListener.NO_VALUE)\r
516                                                 updated(g, null, newResult);\r
517                                         else\r
518                                                 updated(g, oldResult, newResult);\r
519                                 } \r
520                                 return GraphRequestStatus.transactionComplete();\r
521                         }\r
522                 });\r
523 \r
524         }\r
525         \r
526         \r
527         @Override\r
528         public boolean equalsQuery(IGraphQuery<Object> other) {\r
529                 return nodeResource.equals(((NodeQuery)other).nodeResource);\r
530         }\r
531         \r
532         @Override\r
533         final public int hash() {\r
534             return nodeResource.hashCode();\r
535         }\r
536         \r
537         \r
538         /**\r
539          * Disposes the query\r
540          */\r
541         public void dispose() {\r
542                 disposed = true;\r
543                 if (DEBUG) System.out.println("NodeQuery " +  nodeResource +  " disposed()" + " " + this.getClass());\r
544         }\r
545         \r
546         //public abstract NodeQuery instantiateQuery(Resource node);\r
547         //public abstract void attach();\r
548         \r
549         @Override\r
550         public boolean isDisposed() {\r
551             return disposed;\r
552         }\r
553         \r
554         @Override\r
555         public IQueryListener getListener() {\r
556                 if (listener == null) {\r
557                         listener = new IQueryListener() {\r
558                                 @Override\r
559                                 public boolean isDisposed() {\r
560                                         return NodeQuery.this.disposed;\r
561                                 }\r
562                                 \r
563                                 @Override\r
564                                 public void resultChangedRaw(Object oldResult, Object newResult) {\r
565                                         NodeQuery.this.resultChangedRaw(oldResult, newResult);\r
566                                 }\r
567                         };\r
568                 }\r
569                 return listener;\r
570         }\r
571         \r
572     }\r
573     \r
574     /**\r
575      * \r
576      * Query that tracks changes in scene-graph structure (parent/child relationships).\r
577      * \r
578      * @author Marko Luukkainen\r
579      *\r
580      */\r
581     public abstract class ScenegraphQuery extends NodeQuery {\r
582         \r
583         List<Resource> added = new ArrayList<Resource>();\r
584         List<Resource> removed = new ArrayList<Resource>();\r
585         \r
586         private boolean initialized;\r
587         \r
588         public ScenegraphQuery(Resource nodeResource) {\r
589                         super(nodeResource);\r
590                         initialized = false;\r
591                         if(DEBUG)System.out.println("ScenegraphQuery created for " + nodeResource);\r
592                 }\r
593         \r
594         @Override\r
595         public List<Resource> compute2(Graph g) {\r
596                 IEntity node = EntityFactory.create(g,nodeResource);\r
597                 Collection<IEntity> children = node.getRelatedObjects(Resources.g3dResource.HasChild);\r
598                 List<Resource> list = new ArrayList<Resource>();\r
599                 for (IEntity n: children)\r
600                         list.add(n.getResource());\r
601                 if (DEBUG) System.out.println("ScenegraphQuery " + nodeResource +   " has " + list.size() + " children");\r
602                 return list;\r
603         }\r
604         \r
605         @SuppressWarnings("unchecked")\r
606                 @Override\r
607         public boolean updated(Graph graph, Object oldResult, Object newResult) {\r
608                 List<Resource> oldChildren;\r
609                 if (oldResult != null)\r
610                         oldChildren = (List<Resource>)oldResult;\r
611                 else\r
612                         oldChildren = new ArrayList<Resource>();\r
613                 List<Resource> newChildren      = (List<Resource>)newResult;\r
614                 \r
615                 if (DEBUG) System.out.println("ScenegraphQuery " + nodeResource +  " updated: had " + oldChildren.size() + " children, but now has " + newChildren.size() + " children");\r
616                 added.clear();\r
617                 removed.clear();\r
618                 if (initialized) {\r
619                                 for (Resource r : oldChildren)\r
620                                         if (!newChildren.contains(r))\r
621                                                 removed.add(r);\r
622                                 for (Resource r : newChildren)\r
623                                         if (!oldChildren.contains(r))\r
624                                                 added.add(r);\r
625                                 for (Resource r : removed) {\r
626                                         if (DEBUG)\r
627                                                 System.out.println("ScenegraphQuery " + nodeResource\r
628                                                                 + " removed " + r);\r
629                                         removeNode(nodeResource, r);\r
630                                 }\r
631                                 if (added.size() > 0) {\r
632                                         G3DNode parent = new G3DNode(graph, nodeResource);\r
633                                         /*\r
634                                          * try {\r
635                                          * \r
636                                          * G3DTools.propagateTransformChange(parent); } catch\r
637                                          * (Exception e) { ErrorLogger.defaultLogError(e); }\r
638                                          */\r
639                                         for (Resource r : added) {\r
640                                                 IEntity e = EntityFactory.create(graph, r);\r
641                                                 G3DTools.propagateLocalTransformChange(parent, e);\r
642                                                 IGraphicsNode n = addNode(parent, e);\r
643                                                 shapeAdded(graph, n);\r
644                                         }\r
645                                 }\r
646                                 return (added.size() > 0 || removed.size() > 0);\r
647                 } else {\r
648                         // when query is run for the first time, we can assume that transformations are correct.\r
649                         initialized = true;\r
650                         for (Resource r : newChildren)\r
651                                 added.add(r);\r
652                         if (added.size() > 0) {\r
653                                         G3DNode parent = new G3DNode(graph, nodeResource);\r
654                                         for (Resource r : added) {\r
655                                                 IEntity e = EntityFactory.create(graph, r);\r
656                                                 IGraphicsNode n = addNode(parent, e);\r
657                                                 shapeAdded(graph, n);\r
658                                         }\r
659                                         return true;\r
660                                 }\r
661                         return false;\r
662                 }\r
663                 \r
664         }\r
665         \r
666         \r
667         /**\r
668          * This method is run after a node is added to scene-graph.\r
669          * \r
670          * @param graph Graph of the current transaction.\r
671          * @param node the newly added scene-graph node\r
672          */\r
673         public abstract void shapeAdded(Graph graph,IGraphicsNode node);\r
674         \r
675 //      @Override\r
676 //      public void attach() {\r
677 //              scenegraphQueries.put(nodeResource, this);\r
678 //      }\r
679         \r
680         \r
681     }\r
682     \r
683     /**\r
684      * Tracks changes in scene-graph nodes' properties\r
685      * \r
686      * @author Marko Luukkainen\r
687      *\r
688      */\r
689     public abstract class NodePropertyQuery extends NodeQuery {\r
690  \r
691         private boolean initialized;\r
692         \r
693         public NodePropertyQuery(Resource nodeResource) {\r
694                 super(nodeResource);\r
695                 initialized = false;\r
696                 if(DEBUG)System.out.println("NodePropertyQuery created for " + nodeResource);\r
697         }\r
698         \r
699         @Override\r
700         public List<Object> compute2(Graph g) {\r
701                 IEntity t = EntityFactory.create(g,nodeResource);\r
702                 \r
703                 Collection<Property> properties = t.getRelatedProperties(Resources.g3dResource.HasNonTransformation);\r
704                 List<Object> propertyValues = new ArrayList<Object>();\r
705                 p(properties,propertyValues);\r
706 \r
707                 return propertyValues;\r
708         }\r
709         \r
710         private void p(Collection<Property> properties, List<Object> propertyValues) {\r
711                 for (Property p : properties) {\r
712                         Collection<Property> subProperties = p.getRelatedProperties(p.getGraph().getBuiltins().HasProperty);\r
713                         if (subProperties.size() != 0) {\r
714                                 p(subProperties,propertyValues);\r
715                         } \r
716                         if (p.hasValue()){\r
717                                 propertyValues.add(p.getValue());\r
718                         }\r
719                 }\r
720         }\r
721         \r
722         @Override\r
723         public boolean updated(Graph graph, Object oldResult, Object newResult) {\r
724                 if (initialized) {\r
725                         if (DEBUG) System.out.println("NodePropertyQuery changed " + nodeResource + " " + abstractGraphicsNodes.size());\r
726                 IGraphicsNode mo = abstractGraphicsNodes.get(nodeResource);\r
727                 if (mo == null) {\r
728                         if (DEBUG) System.out.println("NodePropertyQuery invalid change " + nodeResource + " " + abstractGraphicsNodes.size());\r
729                         ErrorLogger.defaultLogError("Got update from resource " + nodeResource + " but its not part of the scenegraph", null);\r
730                         dispose();\r
731                         return false;\r
732                 }\r
733                 shapeUpdated(graph,mo);\r
734                 return true;\r
735                 } else {\r
736                         initialized = true;\r
737                         return false;\r
738                 }\r
739         }\r
740 \r
741         \r
742         /**\r
743          * This method is run when a scene-graph node is changed.\r
744          * \r
745          * @param shape the changed node\r
746          */\r
747         public abstract void shapeUpdated(Graph graph,IGraphicsNode shape);\r
748         \r
749 //      @Override\r
750 //      public void attach() {\r
751 //              propertyQueries.put(nodeResource, this);\r
752 //      }\r
753         \r
754     }\r
755     \r
756     public abstract class NodeTransformationQuery extends NodeQuery {\r
757         \r
758         private boolean initialized;\r
759         \r
760         public NodeTransformationQuery(Resource nodeResource) {\r
761                 super(nodeResource);\r
762                 initialized = false;\r
763                 if(DEBUG)System.out.println("NodeTransformationQuery created for " + nodeResource);\r
764         }\r
765         \r
766         @Override\r
767         public List<Object> compute2(Graph g) {\r
768                 IEntity t = EntityFactory.create(g,nodeResource);\r
769                 \r
770                 Collection<Property> properties = t.getRelatedProperties(Resources.g3dResource.HasTransformation);\r
771                 \r
772                 List<Object> propertyValues = new ArrayList<Object>();\r
773                 p(properties,propertyValues);\r
774                 return propertyValues;\r
775 \r
776         }\r
777         \r
778         private void p(Collection<Property> properties, List<Object> propertyValues) {\r
779                 for (Property p : properties) {\r
780                         Collection<Property> subProperties = p.getRelatedProperties(p.getGraph().getBuiltins().HasProperty);\r
781                         if (subProperties.size() != 0) {\r
782                                 p(subProperties,propertyValues);\r
783                         } \r
784                         if (p.hasValue()){\r
785                                 propertyValues.add(p.getValue());\r
786                         }\r
787                 }\r
788         }\r
789         \r
790         @Override\r
791         public boolean updated(Graph graph,Object oldResult, Object newResult) {\r
792                 if (initialized) {\r
793                         if (DEBUG) System.out.println("NodeTransformationQuery changed " + nodeResource + " " + abstractGraphicsNodes.size());\r
794 \r
795                         G3DTools.transformationUpdate(graph, nodeResource);\r
796 \r
797                         IGraphicsNode mo = abstractGraphicsNodes.get(nodeResource);\r
798                 if (mo == null) {\r
799                         if (DEBUG) System.out.println("NodeTransformationQuery invalid change " + nodeResource + " " + abstractGraphicsNodes.size());\r
800                         ErrorLogger.defaultLogError("Got update from resource " + nodeResource + " but its not part of the scenegraph", null);\r
801                         dispose();\r
802                         return false;\r
803                 }\r
804                 shapeUpdated(graph,mo);\r
805                 return true;\r
806                 } else {\r
807                         initialized = true;\r
808                         return false;\r
809                 }\r
810         }\r
811 \r
812         \r
813         /**\r
814          * This method is run when a scene-graph node is changed.\r
815          * \r
816          * @param shape the changed node\r
817          */\r
818         public abstract void shapeUpdated(Graph graph,IGraphicsNode shape);\r
819         \r
820 //      @Override\r
821 //      public void attach() {\r
822 //              transformationQueries.put(nodeResource, this);\r
823 //      }\r
824         \r
825     }\r
826     \r
827 \r
828 }\r