001: /*
002: * $RCSfile: SymbolTable.java,v $
003: *
004: * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * - Redistribution of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * - Redistribution in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * Neither the name of Sun Microsystems, Inc. or the names of
019: * contributors may be used to endorse or promote products derived
020: * from this software without specific prior written permission.
021: *
022: * This software is provided "AS IS," without a warranty of any
023: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
024: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
025: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
026: * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
027: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
028: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
029: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
030: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
031: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
032: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
033: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
034: * POSSIBILITY OF SUCH DAMAGES.
035: *
036: * You acknowledge that this software is not designed, licensed or
037: * intended for use in the design, construction, operation or
038: * maintenance of any nuclear facility.
039: *
040: * $Revision: 1.5 $
041: * $Date: 2007/02/09 17:20:29 $
042: * $State: Exp $
043: */
044:
045: package com.sun.j3d.utils.scenegraph.io.retained;
046:
047: import java.io.IOException;
048: import java.io.DataOutput;
049: import java.io.DataInput;
050: import java.util.HashMap;
051: import java.util.ArrayList;
052: import java.util.ListIterator;
053: import java.util.LinkedList;
054: import java.util.Iterator;
055: import java.util.HashSet;
056: import java.util.Collection;
057: import java.util.Stack;
058:
059: import javax.media.j3d.SceneGraphObject;
060:
061: import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.SceneGraphObjectState;
062: import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.NullSceneGraphObjectState;
063: import com.sun.j3d.utils.scenegraph.io.state.javax.media.j3d.NodeComponentState;
064: import com.sun.j3d.utils.scenegraph.io.NamedObjectException;
065: import com.sun.j3d.utils.scenegraph.io.ObjectNotLoadedException;
066: import com.sun.j3d.utils.scenegraph.io.SceneGraphObjectReferenceControl;
067:
068: /**
069: * SymbolTable class for SceneGraph I/O.
070: */
071: public class SymbolTable extends java.lang.Object implements
072: SceneGraphObjectReferenceControl {
073:
074: private int nodeID = 1; // ID of zero represents null
075: private HashMap j3dNodeIndex; // Index by SceneGraphObject
076: private ArrayList nodeIDIndex; // Index by NodeID of Nodes
077: private HashMap danglingReferences; // Java3D objects without a current State object
078: private Stack unsavedNodeComponentsStack;
079: private LinkedList sharedNodes; // Nodes and NodeComponents referenced more than once
080: private HashMap namedObjects;
081: private ArrayList branchGraphs; // Root of each branch graph
082: private ArrayList branchGraphDependencies; // Dependencies between the branchgraphs
083: // For a graph branchGraphDep[graph] will contain a set of all nodes (in other graphs) on which the graph is dependent
084:
085: private Controller control;
086: private int currentBranchGraphID = -1; // ID's start at 0, -1 is null, -2 is during read, -3 is dangling
087: private int nextBranchGraphID = 0;
088:
089: /** Creates new SymbolTable */
090: public SymbolTable(Controller control) {
091: this .control = control;
092: j3dNodeIndex = new HashMap();
093: danglingReferences = new HashMap();
094: nodeIDIndex = new ArrayList();
095: nodeIDIndex.add(null); // Element zero is null
096: sharedNodes = new LinkedList();
097: namedObjects = new HashMap();
098: branchGraphs = new ArrayList();
099: branchGraphDependencies = new ArrayList();
100: unsavedNodeComponentsStack = new Stack();
101: }
102:
103: /**
104: * At this stage their should be no dangling references
105: *
106: */
107: private void checkforDanglingReferences() {
108: ListIterator list = sharedNodes.listIterator();
109:
110: while (list.hasNext()) {
111: SymbolTableData data = (SymbolTableData) list.next();
112: if (data.branchGraphID == -3) {
113: System.err
114: .println("Warning : node "
115: + data.j3dNode
116: + " is referenced but is not attached to a BranchGraph");
117: System.err
118: .println("Setting reference to null. This scene may not look correct when loaded");
119: }
120: }
121: }
122:
123: /**
124: * Remove dependencies on objects which are not attached to a
125: * branchgraph
126: */
127: private void removeNullDependencies(HashSet set) {
128: Iterator it = set.iterator();
129: while (it.hasNext()) {
130: SymbolTableData symbol = (SymbolTableData) it.next();
131: if (symbol.branchGraphID == -3)
132: it.remove();
133: }
134: }
135:
136: public void writeTable(DataOutput out) throws IOException {
137:
138: // At this stage their should be no dangling references
139: checkforDanglingReferences();
140:
141: ListIterator list = sharedNodes.listIterator();
142: out.writeInt(sharedNodes.size());
143: out.writeInt(nodeID);
144: while (list.hasNext()) {
145: SymbolTableData data = (SymbolTableData) list.next();
146: data.writeObject(out);
147: }
148:
149: // Write Named objects
150: String[] names = getNames();
151: out.writeInt(names.length);
152: for (int i = 0; i < names.length; i++) {
153: out.writeUTF(names[i]);
154: SceneGraphObject node = (SceneGraphObject) namedObjects
155: .get(names[i]);
156: SymbolTableData symbol = getSymbol(node);
157: if (symbol != null)
158: out.writeInt(symbol.nodeID);
159: else
160: out.writeInt(0); // Null
161: }
162:
163: // Write BranchGraph roots
164: out.writeInt(branchGraphs.size());
165: for (int i = 0; i < branchGraphs.size(); i++)
166: ((SymbolTableData) branchGraphs.get(i)).writeObject(out);
167:
168: for (int i = 0; i < branchGraphDependencies.size(); i++) {
169: HashSet set = (HashSet) branchGraphDependencies.get(i);
170: if (set == null) {
171: out.writeInt(0);
172: } else {
173: removeNullDependencies(set);
174: out.writeInt(set.size());
175: Iterator it = set.iterator();
176: while (it.hasNext()) {
177: SymbolTableData symbol = (SymbolTableData) it
178: .next();
179: out.writeInt(symbol.nodeID);
180: }
181: }
182: }
183: }
184:
185: /**
186: * Read and store the entire symbol table
187: *
188: * @param streamRead - true if reading from a Stream in which case only the
189: * branchGraphs and named objects are read.
190: */
191: public void readTable(java.io.DataInput in, boolean streamRead)
192: throws IOException {
193: int size = in.readInt();
194: nodeID = in.readInt();
195: nodeIDIndexEnsureCapacity(nodeID);
196: for (int i = 0; i < size; i++) {
197: SymbolTableData symbol = new SymbolTableData(0, null, null,
198: -1);
199: symbol.readObject(in);
200:
201: // If we are loading from a stream then the NodeComponents have
202: // already been loaded and their symbols created. Therefore
203: // the symbols loaded here are discarded.
204: if (!streamRead) {
205: sharedNodes.add(symbol);
206: nodeIDIndex.set(symbol.nodeID, symbol);
207: }
208: }
209:
210: // Read Named objects
211: size = in.readInt();
212: for (int j = 0; j < size; j++) {
213: String name = in.readUTF();
214: int id = in.readInt();
215: namedObjects.put(name, new Integer(id));
216: }
217:
218: size = in.readInt();
219: //System.out.println("Symbol table BranchGraph size "+size );
220: for (int i = 0; i < size; i++)
221: branchGraphs.add(null);
222:
223: // Read each branchgraph symbol and check that the symbol is not
224: // already in the symbol table.
225: for (int j = 0; j < size; j++) {
226: SymbolTableData tmp = new SymbolTableData(0, null, null, -1);
227: tmp.readObject(in);
228:
229: SymbolTableData symbol = getSymbol(tmp.nodeID);
230:
231: if (symbol == null) {
232: symbol = tmp;
233: if (symbol.referenceCount > 1)
234: sharedNodes.add(symbol);
235: nodeIDIndex.set(symbol.nodeID, symbol);
236: }
237:
238: branchGraphs.set(j, symbol);
239: }
240:
241: for (int i = 0; i < size; i++) {
242: int setSize = in.readInt();
243:
244: if (setSize == 0)
245: branchGraphDependencies.add(null);
246: else {
247: HashSet set = new HashSet();
248: branchGraphDependencies.add(set);
249: for (int j = 0; j < setSize; j++) {
250: set.add(getSymbol(in.readInt()));
251: }
252: }
253: }
254: }
255:
256: /**
257: * Mark the node referenced by this Symbol as a branch graph root
258: *
259: *The filePointer is the position of the BranchGraph in the file, this
260: *is not the same as the BranchGroups position due to the extra data stored
261: *for a graph.
262: */
263: public void setBranchGraphRoot(SymbolTableData symbol,
264: long filePointer) {
265: if (symbol.branchGraphID < 0) {
266: symbol.branchGraphID = nextBranchGraphID++;
267: }
268:
269: currentBranchGraphID = symbol.branchGraphID;
270: for (int i = branchGraphs.size(); i < currentBranchGraphID + 1; i++) {
271: branchGraphs.add(null);
272: branchGraphDependencies.add(null);
273: }
274:
275: branchGraphs.set(currentBranchGraphID, symbol);
276: symbol.branchGraphFilePointer = filePointer;
277: }
278:
279: public SymbolTableData getBranchGraphRoot(int graphID) {
280: //System.out.println("BranchGraph root "+graphID+" "+(SymbolTableData)branchGraphs.get(graphID) );
281: return (SymbolTableData) branchGraphs.get(graphID);
282: }
283:
284: /**
285: * Set the branchGraphID in the symbol to the current branch graph ID
286: */
287: public void setBranchGraphID(SymbolTableData symbol) {
288: symbol.branchGraphID = currentBranchGraphID;
289: }
290:
291: /**
292: * Return an array of each BranchGraph on which graphID is dependent for
293: * closure of the graph
294: *
295: * Only Nodes (not node components) cause dependencies
296: *
297: * If there are no dependencies int[0] is returned
298: */
299: public int[] getBranchGraphDependencies(int graphID) {
300: HashSet set = (HashSet) branchGraphDependencies.get(graphID);
301: if (set == null)
302: return new int[0];
303:
304: int[] ret = new int[set.size()];
305: Iterator it = set.iterator();
306: int i = 0;
307: while (it.hasNext())
308: ret[i++] = ((SymbolTableData) it.next()).branchGraphID;
309:
310: return ret;
311: }
312:
313: /**
314: * Return true if the graph is dependent on nodes in
315: * other graphs
316: *
317: * Only Nodes (not node components) cause dependencies
318: *
319: */
320: public boolean branchGraphHasDependencies(int graphID) {
321: HashSet set = (HashSet) branchGraphDependencies.get(graphID);
322:
323: if (set == null || set.size() == 0)
324: return false;
325: else
326: return true;
327: }
328:
329: public int getBranchGraphCount() {
330: return branchGraphs.size();
331: }
332:
333: public long getBranchGraphFilePosition(int graphID) {
334: SymbolTableData symbol = (SymbolTableData) branchGraphs
335: .get(graphID);
336: return symbol.branchGraphFilePointer;
337: }
338:
339: /**
340: * Create a new symbol and provide a new nodeID
341: * This is used during the save process
342: */
343: public SymbolTableData createSymbol(SceneGraphObject node) {
344:
345: // TODO : Remove this get, it's here to provide debug consistancy check
346: SymbolTableData data = (SymbolTableData) j3dNodeIndex.get(node);
347:
348: SymbolTableData dangling = (SymbolTableData) danglingReferences
349: .get(node);
350:
351: //System.out.println("Checking for dangling "+dangling+" "+node);
352:
353: if (dangling != null) {
354: data = dangling;
355: data.branchGraphID = currentBranchGraphID;
356: danglingReferences.remove(dangling);
357: //System.out.println("Updating dangling ref count"); // TODO - remove
358: } else if (data == null) {
359: data = new SymbolTableData(nodeID++, node, null,
360: currentBranchGraphID);
361: j3dNodeIndex.put(node, data);
362: nodeIDIndex.add(data);
363: } else if (data.j3dNode instanceof javax.media.j3d.Node) {
364: throw new RuntimeException(
365: "Object already in Symbol table " + node);
366: }
367:
368: return data;
369: }
370:
371: /**
372: * Create a new symbol using the specified nodeID
373: * This is used during the load process.
374: */
375: public SymbolTableData createSymbol(SceneGraphObjectState state,
376: SceneGraphObject node, int nodeID) {
377:
378: // TODO : Remove this get, it's here to provide debug consistancy check
379: SymbolTableData data = (SymbolTableData) j3dNodeIndex.get(node);
380:
381: if (data == null) {
382: nodeIDIndexEnsureCapacity(nodeID);
383: data = (SymbolTableData) nodeIDIndex.get(nodeID);
384: if (data == null) {
385: data = new SymbolTableData(nodeID, node, state, -2);
386: j3dNodeIndex.put(node, data);
387: nodeIDIndex.set(data.getNodeID(), data);
388: } else if (data.getJ3dNode() == null) { // Only use state and node if
389: data.j3dNode = node; // this is the first instantiation
390: data.nodeState = state; // of the node
391: j3dNodeIndex.put(node, data);
392: }
393: } else
394: throw new SGIORuntimeException(
395: "Object already in Symbol table ");
396:
397: return data;
398: }
399:
400: private void nodeIDIndexEnsureCapacity(int size) {
401: nodeIDIndex.ensureCapacity(size);
402: int adjust = size - nodeIDIndex.size();
403: for (int i = 0; i <= adjust; i++)
404: nodeIDIndex.add(null);
405: }
406:
407: /**
408: * Create or return the SymbolTableData for a node which does not
409: * necessary have a State object yet
410: *
411: */
412: private SymbolTableData createDanglingSymbol(SceneGraphObject node) {
413: SymbolTableData data = (SymbolTableData) j3dNodeIndex.get(node);
414:
415: if (data == null) {
416: data = new SymbolTableData(nodeID++, node, null, -3);
417: j3dNodeIndex.put(node, data);
418: nodeIDIndex.add(data);
419: danglingReferences.put(node, data);
420: } else if (data.nodeState == null) {
421: if (data.referenceCount == 1)
422: sharedNodes.add(data);
423: data.referenceCount++;
424: } else
425: throw new SGIORuntimeException(
426: "Object already in Symbol table ");
427:
428: return data;
429: }
430:
431: private SymbolTableData createNodeComponentSymbol(
432: SceneGraphObject node) {
433: SymbolTableData symbol = new SymbolTableData(nodeID++, node,
434: null, currentBranchGraphID);
435: symbol.isNodeComponent = true;
436: j3dNodeIndex.put(node, symbol);
437: nodeIDIndex.add(symbol);
438:
439: ((LinkedList) unsavedNodeComponentsStack.peek()).add(symbol);
440:
441: control.createState(symbol);
442:
443: return symbol;
444: }
445:
446: public int getUnsavedNodeComponentsSize() {
447: return ((LinkedList) unsavedNodeComponentsStack.peek()).size();
448: }
449:
450: public ListIterator getUnsavedNodeComponents() {
451: return ((LinkedList) unsavedNodeComponentsStack.peek())
452: .listIterator(0);
453: }
454:
455: public void startUnsavedNodeComponentFrame() {
456: unsavedNodeComponentsStack.push(new LinkedList());
457: }
458:
459: public void endUnsavedNodeComponentFrame() {
460: unsavedNodeComponentsStack.pop();
461: confirmInterGraphDependency();
462: }
463:
464: /**
465: * Check for and remove any inter graph dependency
466: * labels that have been resolved to the current graph
467: */
468: private void confirmInterGraphDependency() {
469: HashSet set = (HashSet) branchGraphDependencies
470: .get(currentBranchGraphID);
471: if (set == null)
472: return;
473:
474: Iterator it = set.iterator();
475: while (it.hasNext()) {
476: SymbolTableData symbol = (SymbolTableData) it.next();
477: if (symbol.branchGraphID == currentBranchGraphID)
478: it.remove();
479: }
480:
481: }
482:
483: /**
484: * Add a dependency to the current branchgraph on <code>symbol</code>
485: *
486: * Only nodes (not nodeComponents) affect intergraph dependencies
487: */
488: private void addInterGraphDependency(SymbolTableData symbol) {
489: HashSet set = (HashSet) branchGraphDependencies
490: .get(currentBranchGraphID);
491: if (set == null) {
492: set = new HashSet();
493: branchGraphDependencies.set(currentBranchGraphID, set);
494: }
495:
496: set.add(symbol);
497: }
498:
499: /**
500: * Update the reference count for the node component.
501: *
502: * Called during NodeComponentState.addSubReference()
503: */
504: public void incNodeComponentRefCount(int nodeID) {
505: if (nodeID == 0)
506: return;
507:
508: SymbolTableData symbol = getSymbol(nodeID);
509:
510: ((NodeComponentState) symbol.nodeState).addSubReference();
511:
512: if (symbol.referenceCount == 1)
513: sharedNodes.add(symbol);
514: symbol.referenceCount++;
515: }
516:
517: /**
518: * Add a refernce to the specified node
519: * Also returns the nodes id
520: */
521: public int addReference(SceneGraphObject node) {
522: if (node == null)
523: return 0;
524:
525: SymbolTableData symbol = getSymbol(node);
526:
527: if (symbol == null) {
528: if (node instanceof javax.media.j3d.Node) {
529: symbol = createDanglingSymbol(node);
530: if (symbol.branchGraphID != currentBranchGraphID) {
531: //System.out.println("------------- Adding Reference "+symbol.nodeID+" "+node ); // TODO - remove
532: addInterGraphDependency(symbol);
533: sharedNodes.add(symbol);
534: }
535: } else {
536: symbol = createNodeComponentSymbol(node);
537: }
538: return symbol.nodeID;
539: } else {
540: return addReference(symbol);
541: }
542: }
543:
544: /**
545: * Add a refernce to the specified node
546: * Also returns the nodes id
547: */
548: public int addReference(SymbolTableData symbol) {
549:
550: if (symbol != null) {
551: if (symbol.referenceCount == 1)
552: sharedNodes.add(symbol);
553: symbol.referenceCount++;
554:
555: if (symbol.j3dNode instanceof javax.media.j3d.NodeComponent
556: && symbol.referenceCount > 1) {
557: ((NodeComponentState) symbol.nodeState)
558: .addSubReference();
559: }
560:
561: if (symbol.branchGraphID != currentBranchGraphID
562: && symbol.j3dNode instanceof javax.media.j3d.Node) {
563: // System.out.println("------------- Adding Reference "+symbol.nodeID+" "+symbol.j3dNode ); // TODO - remove
564: addInterGraphDependency(symbol);
565: }
566: } else {
567: throw new SGIORuntimeException("Null Symbol");
568: }
569:
570: return symbol.nodeID;
571: }
572:
573: /**
574: * Add a refernce to the BranchGraph root
575: * Also returns the nodes id
576: *
577: * Used to associate graphs with a locale without storing the graph at the
578: * current time.
579: */
580: public int addBranchGraphReference(SceneGraphObject node,
581: int branchGraphID) {
582: if (node == null)
583: return 0;
584:
585: SymbolTableData symbol = getSymbol(node);
586:
587: if (symbol != null) {
588: if (symbol.referenceCount == 1)
589: sharedNodes.add(symbol);
590: symbol.referenceCount++;
591: } else {
592: symbol = new SymbolTableData(nodeID++, node, null, -3);
593: j3dNodeIndex.put(node, symbol);
594: nodeIDIndex.add(symbol);
595: danglingReferences.put(node, symbol);
596: }
597:
598: symbol.branchGraphID = branchGraphID;
599: for (int i = branchGraphs.size(); i < branchGraphID + 1; i++) {
600: branchGraphs.add(null);
601: branchGraphDependencies.add(null);
602: }
603:
604: branchGraphs.set(symbol.branchGraphID, symbol);
605:
606: return symbol.nodeID;
607: }
608:
609: /**
610: * Return true if this node has already been loaded
611: */
612: public boolean isLoaded(int nodeID) {
613: SymbolTableData symbol = getSymbol(nodeID);
614:
615: if (symbol == null)
616: return false;
617:
618: if (symbol.j3dNode == null)
619: return false;
620:
621: return true;
622: }
623:
624: /**
625: * Return the Java3D node associated with the nodeID.
626: *
627: * The method will call buildGraph() on the node if necessary
628: */
629: public SceneGraphObject getJ3dNode(int nodeID) {
630: if (nodeID == 0)
631: return null;
632:
633: SymbolTableData symbol = getSymbol(nodeID);
634:
635: // Although referenced this node was not attached to the
636: // scenegraph, so return null
637: if (symbol.branchGraphID == -3)
638: return null;
639:
640: if (symbol != null && symbol.j3dNode == null) {
641: if (symbol.isNodeComponent
642: && (control instanceof RandomAccessFileControl)) {
643: try {
644: ((RandomAccessFileControl) control)
645: .loadNodeComponent(symbol);
646: } catch (IOException e) {
647: System.out
648: .println("FAILED to seek and load NodeComponent");
649: return null;
650: }
651: } else {
652: System.out
653: .println("WARNING - Object has not been loaded "
654: + nodeID);
655: System.out.println("Need to load branchgraph "
656: + symbol.branchGraphID);
657: return null;
658: }
659: } else if (symbol == null) {
660: throw new SGIORuntimeException("Missing Symbol " + nodeID);
661: }
662:
663: if (!symbol.graphBuilt) {
664: symbol.graphBuilt = true;
665: symbol.nodeState.buildGraph();
666: }
667:
668: return symbol.j3dNode;
669: }
670:
671: /**
672: * Get the table entry for node
673: */
674: public SymbolTableData getSymbol(SceneGraphObject node) {
675: //System.out.println("getSymbol "+node+" "+j3dNodeIndex.get( node ));
676: return (SymbolTableData) j3dNodeIndex.get(node);
677: }
678:
679: /**
680: * Return the node with the give ID
681: */
682: public SymbolTableData getSymbol(int nodeID) {
683: // nodeID's start at 1
684:
685: if (nodeID == 0 || nodeID > nodeIDIndex.size())
686: return null;
687: else
688: return (SymbolTableData) nodeIDIndex.get(nodeID);
689: }
690:
691: /** Get the symbol for the shared group
692: * If the sharedgroup has not been loaded then load it before
693: * returning (if we are using RandomAccessFileControl
694: */
695: public SymbolTableData getSharedGroup(int nodeID) {
696: SymbolTableData symbol = getSymbol(nodeID);
697:
698: if (symbol.nodeState == null
699: && control instanceof RandomAccessFileControl) {
700: try {
701: ((RandomAccessFileControl) control)
702: .loadSharedGroup(symbol);
703: } catch (java.io.IOException e) {
704: e.printStackTrace();
705: throw new SGIORuntimeException(
706: "Internal error in getSharedGroup");
707: }
708: }
709:
710: return symbol;
711: }
712:
713: /**
714: * Set the position of the object referenced by state
715: */
716: public void setFilePosition(long ptr, SceneGraphObjectState state) {
717: if (state instanceof NullSceneGraphObjectState)
718: return;
719:
720: SymbolTableData symbol = getSymbol(state.getNodeID());
721:
722: symbol.filePosition = ptr;
723: }
724:
725: /**
726: * Associate the name with the scene graph object
727: */
728: public void addNamedObject(String name, SceneGraphObject object) {
729: namedObjects.put(name, object);
730: }
731:
732: /**
733: * Add all the named objects in <code>map</code>
734: */
735: public void addNamedObjects(HashMap map) {
736: if (map != null)
737: namedObjects.putAll(map);
738: }
739:
740: /**
741: * Return the SceneGraphObject associated with the name
742: */
743: public SceneGraphObject getNamedObject(String name)
744: throws NamedObjectException, ObjectNotLoadedException {
745: Object obj = namedObjects.get(name);
746: if (obj == null)
747: throw new NamedObjectException("Unknown name :" + name);
748:
749: if (obj instanceof SceneGraphObject)
750: return (SceneGraphObject) obj;
751: else {
752: SymbolTableData symbol = getSymbol(((Integer) obj)
753: .intValue());
754: if (symbol == null || symbol.j3dNode == null)
755: throw new ObjectNotLoadedException(((Integer) obj)
756: .toString());
757: return symbol.j3dNode;
758: }
759: }
760:
761: /**
762: * Get all the names of the named objects
763: */
764: public String[] getNames() {
765: return (String[]) namedObjects.keySet()
766: .toArray(new String[] {});
767: }
768:
769: /**
770: * Add the namedObject mappings to <code>map</code>
771: */
772: public void getNamedObjectMap(HashMap map) {
773: map.putAll(namedObjects);
774: }
775:
776: public String toString() {
777: StringBuffer buf = new StringBuffer();
778:
779: for (int i = 0; i < nodeIDIndex.size(); i++) {
780: SymbolTableData data = (SymbolTableData) nodeIDIndex.get(i);
781: if (data != null)
782: buf.append(data.nodeID + " " + data.referenceCount
783: + " " + data.filePosition + " "
784: + data.branchGraphID + " " + data.nodeState
785: + "\n");
786: }
787:
788: buf.append("\nShared Objects\n");
789:
790: ListIterator l = sharedNodes.listIterator();
791: while (l.hasNext()) {
792: SymbolTableData data = (SymbolTableData) l.next();
793: buf.append(data.nodeID + " " + data.referenceCount + " "
794: + data.filePosition + " " + data.branchGraphID
795: + " " + data.j3dNode + "\n");
796: }
797:
798: buf.append("\nNamed Objects\n");
799:
800: String[] names = getNames();
801: for (int i = 0; i < names.length; i++)
802: buf.append(names[i] + " " + namedObjects.get(names[i]));
803:
804: buf.append("\nBranch Graphs\n");
805: for (int i = 0; i < branchGraphs.size(); i++) {
806: SymbolTableData data = (SymbolTableData) branchGraphs
807: .get(i);
808: if (data == null)
809: System.out.println("Data is null " + i + " "
810: + branchGraphs.size());
811: buf
812: .append(data.nodeID + " " + data.referenceCount
813: + " " + data.filePosition + " "
814: + data.branchGraphID + " " + data.j3dNode
815: + " " + data.nodeState + "\n");
816: }
817:
818: buf.append("\nBranch Graph Dependencies\n");
819: for (int i = 0; i < branchGraphDependencies.size(); i++) {
820: buf.append("Graph " + i + " - ");
821: HashSet set = (HashSet) branchGraphDependencies.get(i);
822: if (set != null) {
823: Iterator it = set.iterator();
824: while (it.hasNext())
825: buf.append(((SymbolTableData) it.next()).nodeID
826: + " ");
827: }
828: buf.append("\n");
829: }
830:
831: buf.append("------------------");
832:
833: return buf.toString();
834: }
835:
836: /**
837: * Clear all elements from the symbol table
838: */
839: public void clear() {
840: j3dNodeIndex.clear();
841: nodeIDIndex.clear();
842: while (!unsavedNodeComponentsStack.empty())
843: unsavedNodeComponentsStack.pop();
844: danglingReferences.clear();
845: sharedNodes.clear();
846: namedObjects.clear();
847: nodeID = 1;
848: }
849:
850: /**
851: * Clear all the Symbols that are not shared with other Graphs in the file
852: *
853: * Remove all Symbols from all structures with referenceCounts=1
854: */
855: public void clearUnshared() {
856: // Convert as many named objects as possible to reference to j3dNode
857: String[] names = getNames();
858: for (int i = 0; i < names.length; i++) {
859: try {
860: Object obj = namedObjects.get(names[i]);
861: if (obj instanceof Integer) {
862: SymbolTableData symbol = getSymbol(((Integer) obj)
863: .intValue());
864: if (symbol != null && symbol.j3dNode != null)
865: namedObjects.put(names[i], symbol.j3dNode);
866: }
867: } catch (Exception e) {
868: e.printStackTrace();
869: }
870: }
871:
872: j3dNodeIndex.clear();
873: nodeIDIndex.clear();
874: while (!unsavedNodeComponentsStack.empty())
875: unsavedNodeComponentsStack.pop();
876:
877: nodeIDIndexEnsureCapacity(nodeID);
878:
879: // Add the shared and dangling Symbols back into the other structures
880: ListIterator list = sharedNodes.listIterator();
881: while (list.hasNext()) {
882: SymbolTableData symbol = (SymbolTableData) list.next();
883: nodeIDIndex.set(symbol.nodeID, symbol);
884: j3dNodeIndex.put(symbol.j3dNode, symbol);
885: }
886:
887: Iterator it = danglingReferences.values().iterator();
888: while (it.hasNext()) {
889: SymbolTableData symbol = (SymbolTableData) it.next();
890: nodeIDIndex.set(symbol.nodeID, symbol);
891: j3dNodeIndex.put(symbol.j3dNode, symbol);
892: }
893:
894: }
895:
896: /**
897: * Given a nodeID return the corresponding scene graph object.
898: *
899: * Use only during the load cycle
900: */
901: public javax.media.j3d.SceneGraphObject resolveReference(int nodeID) {
902: return getJ3dNode(nodeID);
903: }
904:
905: }
|