]> gerrit.simantics Code Review - simantics/3d.git/blob - org.simantics.proconf.g3d/src/org/simantics/proconf/g3d/base/ScenegraphAdapterImpl.java
git-svn-id: https://www.simantics.org/svn/simantics/3d/branches/dev@9354 ac1ea38d...
[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 G3DNode " + 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().getResourceId()+") 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         private IQueryListener listener = null;\r
477         \r
478         public NodeQuery(Resource r) {\r
479                 this.nodeResource = r;\r
480         }\r
481         \r
482         protected abstract Object compute2(Graph graph);\r
483         \r
484         @Override\r
485         public Object performQuery(Graph graph) {\r
486                 if (disposed) return null;\r
487                 return compute2(graph);\r
488         }\r
489         \r
490         /**\r
491          *  \r
492          * @param oldResult result of the query before the change.\r
493          * @param newResult result of the query after the change.\r
494          */\r
495         public abstract boolean updated(Graph graph, Object oldResult, Object newResult);\r
496         \r
497         @Override\r
498         public int getType() {\r
499                 return IQuery.SCHEDULED_UPDATE;\r
500         }\r
501         \r
502         @Override\r
503         public void resultChangedRaw(final Object oldResult, final Object newResult) {\r
504                 if (disposed)\r
505                         throw new RuntimeException("Updating disposed query"); //return;\r
506                 transactionRunner.run(new GraphRequestAdapter() {\r
507                         @Override\r
508                         public GraphRequestStatus perform(Graph g) throws Exception {\r
509                                 if (!disposed) {\r
510                                         if (oldResult == IQueryListener.NO_VALUE)\r
511                                                 updated(g, null, newResult);\r
512                                         else\r
513                                                 updated(g, oldResult, newResult);\r
514                                 } \r
515                                 return GraphRequestStatus.transactionComplete();\r
516                         }\r
517                 });\r
518 \r
519         }\r
520         \r
521         \r
522         @Override\r
523         public boolean equalsQuery(IGraphQuery<Object> other) {\r
524                 return nodeResource.equals(((NodeQuery)other).nodeResource);\r
525         }\r
526         \r
527         @Override\r
528         final public int hash() {\r
529             return nodeResource.hashCode();\r
530         }\r
531         \r
532         \r
533         /**\r
534          * Disposes the query\r
535          */\r
536         public void dispose() {\r
537                 disposed = true;\r
538                 if (DEBUG) System.out.println("NodeQuery " +  nodeResource +  " disposed()" + " " + this.getClass());\r
539         }\r
540         \r
541         @Override\r
542         public boolean isDisposed() {\r
543             return disposed;\r
544         }\r
545         \r
546         // without separate listener, this query would work only once\r
547         @Override\r
548         public IQueryListener getListener() {\r
549                 if (listener == null) {\r
550                         listener = new IQueryListener() {\r
551                                 @Override\r
552                                 public boolean isDisposed() {\r
553                                         return NodeQuery.this.disposed;\r
554                                 }\r
555                                 \r
556                                 @Override\r
557                                 public void resultChangedRaw(Object oldResult, Object newResult) {\r
558                                         NodeQuery.this.resultChangedRaw(oldResult, newResult);\r
559                                 }\r
560                         };\r
561                 }\r
562                 return listener;\r
563         }\r
564     }\r
565     \r
566     /**\r
567      * \r
568      * Query that tracks changes in scene-graph structure (parent/child relationships).\r
569      * \r
570      * @author Marko Luukkainen\r
571      *\r
572      */\r
573     public abstract class ScenegraphQuery extends NodeQuery {\r
574         \r
575         List<Resource> added = new ArrayList<Resource>();\r
576         List<Resource> removed = new ArrayList<Resource>();\r
577         \r
578         private boolean initialized;\r
579         \r
580         public ScenegraphQuery(Resource nodeResource) {\r
581                         super(nodeResource);\r
582                         initialized = false;\r
583                         if(DEBUG)System.out.println("ScenegraphQuery created for " + nodeResource);\r
584                 }\r
585         \r
586         @Override\r
587         public List<Resource> compute2(Graph g) {\r
588                 IEntity node = EntityFactory.create(g,nodeResource);\r
589                 Collection<IEntity> children = node.getRelatedObjects(Resources.g3dResource.HasChild);\r
590                 List<Resource> list = new ArrayList<Resource>();\r
591                 for (IEntity n: children)\r
592                         list.add(n.getResource());\r
593                 if (DEBUG) System.out.println("ScenegraphQuery " + nodeResource +   " has " + list.size() + " children");\r
594                 return list;\r
595         }\r
596         \r
597         @SuppressWarnings("unchecked")\r
598                 @Override\r
599         public boolean updated(Graph graph, Object oldResult, Object newResult) {\r
600                 List<Resource> oldChildren;\r
601                 if (oldResult != null)\r
602                         oldChildren = (List<Resource>)oldResult;\r
603                 else\r
604                         oldChildren = new ArrayList<Resource>();\r
605                 List<Resource> newChildren      = (List<Resource>)newResult;\r
606                 \r
607                 if (DEBUG) System.out.println("ScenegraphQuery " + nodeResource +  " updated: had " + oldChildren.size() + " children, but now has " + newChildren.size() + " children");\r
608                 added.clear();\r
609                 removed.clear();\r
610                 if (initialized) {\r
611                                 for (Resource r : oldChildren)\r
612                                         if (!newChildren.contains(r))\r
613                                                 removed.add(r);\r
614                                 for (Resource r : newChildren)\r
615                                         if (!oldChildren.contains(r))\r
616                                                 added.add(r);\r
617                                 for (Resource r : removed) {\r
618                                         if (DEBUG)\r
619                                                 System.out.println("ScenegraphQuery " + nodeResource\r
620                                                                 + " removed " + r);\r
621                                         removeNode(nodeResource, r);\r
622                                 }\r
623                                 if (added.size() > 0) {\r
624                                         G3DNode parent = new G3DNode(graph, nodeResource);\r
625                                         /*\r
626                                          * try {\r
627                                          * \r
628                                          * G3DTools.propagateTransformChange(parent); } catch\r
629                                          * (Exception e) { ErrorLogger.defaultLogError(e); }\r
630                                          */\r
631                                         for (Resource r : added) {\r
632                                                 IEntity e = EntityFactory.create(graph, r);\r
633                                                 G3DTools.propagateLocalTransformChange(parent, e);\r
634                                                 IGraphicsNode n = addNode(parent, e);\r
635                                                 shapeAdded(graph, n);\r
636                                         }\r
637                                 }\r
638                                 return (added.size() > 0 || removed.size() > 0);\r
639                 } else {\r
640                         // when query is run for the first time, we can assume that transformations are correct.\r
641                         initialized = true;\r
642                         for (Resource r : newChildren)\r
643                                 added.add(r);\r
644                         if (added.size() > 0) {\r
645                                         G3DNode parent = new G3DNode(graph, nodeResource);\r
646                                         for (Resource r : added) {\r
647                                                 IEntity e = EntityFactory.create(graph, r);\r
648                                                 IGraphicsNode n = addNode(parent, e);\r
649                                                 shapeAdded(graph, n);\r
650                                         }\r
651                                         return true;\r
652                                 }\r
653                         return false;\r
654                 }\r
655                 \r
656         }\r
657         \r
658         \r
659         /**\r
660          * This method is run after a node is added to scene-graph.\r
661          * \r
662          * @param graph Graph of the current transaction.\r
663          * @param node the newly added scene-graph node\r
664          */\r
665         public abstract void shapeAdded(Graph graph,IGraphicsNode node);\r
666         \r
667 //      @Override\r
668 //      public void attach() {\r
669 //              scenegraphQueries.put(nodeResource, this);\r
670 //      }\r
671         \r
672         \r
673     }\r
674     \r
675     /**\r
676      * Tracks changes in scene-graph nodes' properties\r
677      * \r
678      * @author Marko Luukkainen\r
679      *\r
680      */\r
681     public abstract class NodePropertyQuery extends NodeQuery {\r
682  \r
683         private boolean initialized;\r
684         \r
685         public NodePropertyQuery(Resource nodeResource) {\r
686                 super(nodeResource);\r
687                 initialized = false;\r
688                 if(DEBUG)System.out.println("NodePropertyQuery created for " + nodeResource);\r
689         }\r
690         \r
691         @Override\r
692         public List<Object> compute2(Graph g) {\r
693                 IEntity t = EntityFactory.create(g,nodeResource);\r
694                 \r
695                 Collection<Property> properties = t.getRelatedProperties(Resources.g3dResource.HasNonTransformation);\r
696                 List<Object> propertyValues = new ArrayList<Object>();\r
697                 p(properties,propertyValues);\r
698 \r
699                 return propertyValues;\r
700         }\r
701         \r
702         private void p(Collection<Property> properties, List<Object> propertyValues) {\r
703                 for (Property p : properties) {\r
704                         Collection<Property> subProperties = p.getRelatedProperties(p.getGraph().getBuiltins().HasProperty);\r
705                         if (subProperties.size() != 0) {\r
706                                 p(subProperties,propertyValues);\r
707                         } \r
708                         if (p.hasValue()){\r
709                                 propertyValues.add(p.getValue());\r
710                         }\r
711                 }\r
712         }\r
713         \r
714         @Override\r
715         public boolean updated(Graph graph, Object oldResult, Object newResult) {\r
716                 if (initialized) {\r
717                         if (DEBUG) System.out.println("NodePropertyQuery changed " + nodeResource + " " + abstractGraphicsNodes.size());\r
718                 IGraphicsNode mo = abstractGraphicsNodes.get(nodeResource);\r
719                 if (mo == null) {\r
720                         if (DEBUG) System.out.println("NodePropertyQuery invalid change " + nodeResource + " " + abstractGraphicsNodes.size());\r
721                         ErrorLogger.defaultLogError("Got update from resource " + nodeResource + " but its not part of the scenegraph", null);\r
722                         dispose();\r
723                         return false;\r
724                 }\r
725                 shapeUpdated(graph,mo);\r
726                 return true;\r
727                 } else {\r
728                         initialized = true;\r
729                         return false;\r
730                 }\r
731         }\r
732 \r
733         \r
734         /**\r
735          * This method is run when a scene-graph node is changed.\r
736          * \r
737          * @param shape the changed node\r
738          */\r
739         public abstract void shapeUpdated(Graph graph,IGraphicsNode shape);\r
740         \r
741 //      @Override\r
742 //      public void attach() {\r
743 //              propertyQueries.put(nodeResource, this);\r
744 //      }\r
745         \r
746     }\r
747     \r
748     public abstract class NodeTransformationQuery extends NodeQuery {\r
749         \r
750         private boolean initialized;\r
751         \r
752         public NodeTransformationQuery(Resource nodeResource) {\r
753                 super(nodeResource);\r
754                 initialized = false;\r
755                 if(DEBUG)System.out.println("NodeTransformationQuery created for " + nodeResource);\r
756         }\r
757         \r
758         @Override\r
759         public List<Object> compute2(Graph g) {\r
760                 IEntity t = EntityFactory.create(g,nodeResource);\r
761                 \r
762                 Collection<Property> properties = t.getRelatedProperties(Resources.g3dResource.HasTransformation);\r
763                 \r
764                 List<Object> propertyValues = new ArrayList<Object>();\r
765                 p(properties,propertyValues);\r
766                 return propertyValues;\r
767 \r
768         }\r
769         \r
770         private void p(Collection<Property> properties, List<Object> propertyValues) {\r
771                 for (Property p : properties) {\r
772                         Collection<Property> subProperties = p.getRelatedProperties(p.getGraph().getBuiltins().HasProperty);\r
773                         if (subProperties.size() != 0) {\r
774                                 p(subProperties,propertyValues);\r
775                         } \r
776                         if (p.hasValue()){\r
777                                 propertyValues.add(p.getValue());\r
778                         }\r
779                 }\r
780         }\r
781         \r
782         @Override\r
783         public boolean updated(Graph graph,Object oldResult, Object newResult) {\r
784                 if (initialized) {\r
785                         if (DEBUG) System.out.println("NodeTransformationQuery changed " + nodeResource + " " + abstractGraphicsNodes.size());\r
786 \r
787                         G3DTools.transformationUpdate(graph, nodeResource);\r
788 \r
789                         IGraphicsNode mo = abstractGraphicsNodes.get(nodeResource);\r
790                 if (mo == null) {\r
791                         if (DEBUG) System.out.println("NodeTransformationQuery invalid change " + nodeResource + " " + abstractGraphicsNodes.size());\r
792                         ErrorLogger.defaultLogError("Got update from resource " + nodeResource + " but its not part of the scenegraph", null);\r
793                         dispose();\r
794                         return false;\r
795                 }\r
796                 shapeUpdated(graph,mo);\r
797                 return true;\r
798                 } else {\r
799                         initialized = true;\r
800                         return false;\r
801                 }\r
802         }\r
803 \r
804         \r
805         /**\r
806          * This method is run when a scene-graph node is changed.\r
807          * \r
808          * @param shape the changed node\r
809          */\r
810         public abstract void shapeUpdated(Graph graph,IGraphicsNode shape);\r
811         \r
812 //      @Override\r
813 //      public void attach() {\r
814 //              transformationQueries.put(nodeResource, this);\r
815 //      }\r
816         \r
817     }\r
818     \r
819 \r
820 }\r