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