Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/trapezium/vrml/Scene.java


1   /*
2    * @(#)Scene.java
3    *
4    * Copyright (c) 1998 by Trapezium Development LLC.  All Rights Reserved.
5    *
6    * The information in this file is the property of Trapezium Development LLC
7    * and may be used only in accordance with the terms of the license granted
8    * by Trapezium.
9    *
10   */
11  package com.trapezium.vrml;
12  
13  import com.trapezium.parse.TokenEnumerator;
14  import com.trapezium.parse.TokenFactory;
15  import com.trapezium.vrml.node.PROTO;
16  import com.trapezium.vrml.node.PROTObase;
17  import com.trapezium.vrml.node.PROTOInstance;
18  import com.trapezium.vrml.node.DEFUSENode;
19  import com.trapezium.vrml.node.Node;
20  import com.trapezium.vrml.node.NodeType;
21  import com.trapezium.vrml.node.TokenData;
22  import com.trapezium.util.ReturnInteger;
23  import com.trapezium.vrml.grammar.VRML97;
24  import com.trapezium.vrml.grammar.DEFNameFactory;
25  import com.trapezium.vrml.visitor.DEFVisitor;
26  import com.trapezium.vrml.visitor.NodeSelectionVisitor;
27  import com.trapezium.vrml.grammar.VRML97parser;
28  import com.trapezium.vorlon.ErrorSummary;
29  import com.trapezium.parse.InputStreamFactory;
30  
31  import java.util.Hashtable;
32  import java.util.Vector;
33  import java.util.StringTokenizer;
34  import java.io.InputStream;
35  
36  /**
37   *  Scene graph component representing an entire VRML file, or the
38   *  body of a PROTO.
39   *
40   *  The only difference between the PROTO body and a Scene is that
41   *  the PROTO body is required to have at least one node.  This
42   *  restriction is not part of the Scene, and is a check performed
43   *  by the grammar package during parsing.
44   *
45   *  The Scene has a single Declaration for each child element.
46   *
47   *  The Scene gives the name scope for PROTO and DEF nodes.  As a file is processed, any
48   *  PROTO and DEF nodes are registered with the Scene.  Name uniqueness is forced during
49   *  this registration by appending "_1", "_2", etc.
50   *
51   *  @author          Johannes N. Johannsen
52   *  @version         1.1, 14 Jan 1998
53   *
54   *  @since           1.0
55   */
56  
57  public class Scene extends MultipleTokenElement implements SelectNode {
58      /** The Scene tokens exist only in relation to a specific TokenEnumerator kept by all Scenes */
59      TokenEnumerator dataSource = null;
60      
61      /** Set the data source for this Scene's text */
62      public void setTokenEnumerator( TokenEnumerator t ) {
63          dataSource = t;
64      }
65      
66      /** Get the data source for this Scene's text */
67      public TokenEnumerator getTokenEnumerator() {
68          return( dataSource );
69      }
70      
71      /** Can get count from ComplexityVisitor and set here */
72      int vrmlElementCount = 0;
73      public void setVrmlElementCount( int n ) {
74          vrmlElementCount = n;
75      }
76      public int getVrmlElementCount() {
77          return( vrmlElementCount );
78      }
79      
80      /** ErrorSummary info, possibly null */
81      ErrorSummary errorSummary;
82      
83      public ErrorSummary getErrorSummary() {
84          return( errorSummary );
85      }
86      
87      public void setErrorSummary( ErrorSummary errorSummary ) {
88          this.errorSummary = errorSummary;
89      }
90      
91      /** List of nodes verified, created if necessary */
92      Hashtable verifyList;
93      public Hashtable getVerifyList() {
94          if ( verifyList == null ) {
95              verifyList = new Hashtable();
96          }
97          return( verifyList );
98      }
99      
100   /** PROTO nodes by type */
101   public Hashtable protoTable = null;
102 
103   /** PROTO node declarations available to Scene, identified by PROTO name */
104   public Hashtable PROTONodes = null;
105 
106   /** DEF/USE nodes scope is limited to file */
107   public Hashtable DEFNodes = null;
108   
109   /** list of all ROUTEs, used to check for duplicates */
110   public Hashtable routeTable = null;
111   
112   /** add a ROUTE to the list kept by this Scene */
113   public void addRoute( String sourceDEF, String sourceField, String destDEF, String destField ) {
114       if (( sourceDEF != null ) && ( sourceField != null ) && ( destDEF != null ) && ( destField != null )) {
115           if ( routeTable == null ) {
116               routeTable = new Hashtable();
117           }
118             String routeText = sourceDEF + '.' + sourceField + '_' + destDEF + '.' + destField;
119             routeTable.put( routeText, routeText );
120         }
121     }
122     
123     /** Check if a ROUTE is already known to this Scene */
124     public boolean hasRoute( String sourceDEF, String sourceField, String destDEF, String destField ) {
125         if ( routeTable == null ) {
126             return( false );
127         }
128         if (( sourceDEF != null ) && ( sourceField != null ) && ( destDEF != null ) && ( destField != null )) {
129             String routeText = sourceDEF + '.' + sourceField + '_' + destDEF + '.' + destField;
130             return( routeTable.get( routeText ) != null );
131         }
132         return( false );
133     }
134           
135     
136   //
137   //  IFS specific tables that have a scene context, since we can only check usage in a 
138   //  global context.  The key to the hash table is the "coord", "texCoord", "color",
139   //  and "normal" node.  The value associated with the key is a UsageInfo object
140   //  which contains the following:
141   //
142   //    - a BitSet indicating the usage of those values.
143   //    - a count of the actual number of values
144   //    - the node owning those values
145   //
146   Hashtable usageTable = null;
147   public Hashtable getUsageTable() {
148     if ( usageTable == null ) {
149       usageTable = new Hashtable();
150     }
151     return( usageTable );
152   }
153   //
154   //  The key is an actual Coordinate, TextureCoordinate, Color, or Normal node,
155   //  the associated value is an IndexInfo (see node/NodeType.java for IndexInfo class)
156   //
157   Hashtable coordTable = null;
158   public Hashtable getCoordTable() {
159     if ( coordTable == null ) {
160       coordTable = new Hashtable();
161     }
162     return( coordTable );
163   }
164   Hashtable texCoordTable = null;
165   public Hashtable getTexCoordTable() {
166     if ( texCoordTable == null ) {
167       texCoordTable = new Hashtable();
168     }
169     return( texCoordTable );
170   }
171   Hashtable colorTable = null;
172   public Hashtable getColorTable() {
173     if ( colorTable == null ) {
174       colorTable = new Hashtable();
175     }
176     return( colorTable );
177   }
178   Hashtable normalTable = null;
179   public Hashtable getNormalTable() {
180     if ( normalTable == null ) {
181       normalTable = new Hashtable();
182     }
183     return( normalTable );
184   }
185 
186   /** A Scene has a PROTO parent if it is embedded within a PROTO node */
187   PROTO protoParent = null;
188   public PROTO getPROTOparent() {
189     return( protoParent );
190   }
191   public void setPROTOparent( PROTO rent ) {
192     protoParent = rent;
193   }
194 
195     /** for the autoDEF feature */
196     DEFNameFactory defNameFactory;
197     
198   /** main url */
199   String url;
200   
201   /** Construct a scene entirely from a url.
202    *
203    *  @param urlName url to use in constructing scene
204    */
205   public Scene( String urlName ) {
206       super( -1 );
207        try {
208            InputStream is = InputStreamFactory.getInputStream( urlName );
209 
210            // null return is possible
211            if ( is != null ) {
212                TokenEnumerator vrmlTokens = new TokenEnumerator( is, urlName );
213                init( urlName, vrmlTokens, null );
214                VRML97parser parser = new VRML97parser();
215                parser.Build( vrmlTokens, this );
216            }
217         } catch ( Exception e ) {
218            // could not create InputStream or TokenEnumerator
219         }
220   }
221   
222   public Scene() {
223       this( null, null, null );
224   }
225 
226   public Scene( String urlName, TokenEnumerator te ) {
227       this( urlName, te, null );
228   }
229   
230   /** Main constructor, all other constructors go through this one
231    *
232    *  @param urlName url used to identify the scene
233    *  @param te TokenEnumerator containing text used to create the Scene
234    *  @param defNameFactory optional DEFNameFactory for autoDEF feature
235    */
236   static int staticSceneId = 1;
237   int sceneId;
238   public Scene( String urlName, TokenEnumerator te, DEFNameFactory defNameFactory ) {
239       super( -1 );
240       init( urlName, te, defNameFactory );
241   }
242   
243   void init( String urlName, TokenEnumerator te, DEFNameFactory defNameFactory ) {
244       sceneId = staticSceneId++;
245       this.url = urlName;
246       this.dataSource = te;
247       this.defNameFactory = defNameFactory;
248     }
249     
250     /** Each scene assigned an id number as it is created */
251     public int getSceneId() {
252         return( sceneId );
253     }
254     
255     /** Get the url used to identify the scene */
256     public String getUrl() {
257         return( url );
258     }
259     
260     /** Get the DEFNameFactory associated with the Scene */
261     public DEFNameFactory getDEFNameFactory() {
262         return( defNameFactory );
263     }
264     
265     /** Set the DEFNameFactory for the Scene */
266     public void setDEFNameFactory( DEFNameFactory defNameFactory ) {
267         this.defNameFactory = defNameFactory;
268     }
269 
270   /** untitled scenes */
271   static public int untitledCount = 0;
272   static public String getUntitle() {
273     untitledCount++;
274     return( "Untitled" + untitledCount + ".wrl" );
275   }
276 
277   /** optional text header used by display */
278   String text;
279   public void setText( String t ) {
280     text = t;
281   }
282   public String getText() {
283     return( text );
284   }
285 
286   /** How many node classes, including PROTOs, exist for a particular actual type */
287   public int getClassCount( String typeString ) {
288     String[] typeList = (String[])NodeType.typeTable.get( typeString );
289     if ( typeList != null ) {
290       System.out.println( "Count for nodeType '" + typeString + "' is " + typeList.length );
291       return( typeList.length );
292     } else {
293       System.out.println( "Count for actualType '" + typeString + "' is 0" );
294       return( 0 );
295     }
296   }
297 
298   /** Get a string listing current classes, including protos */
299   public String getClassList( String typeString ) {
300     StringBuffer result = new StringBuffer( "NULL" );
301     String[] typeList = (String[])NodeType.typeTable.get( typeString );
302     if ( typeList != null ) {
303       for ( int i = 0; i < typeList.length; i++ ) {
304         result.append( "," );
305         result.append( typeList[i] );
306       }
307     }
308     return( new String( result ));
309   }
310 
311     /** create DEF node hashtable if it doesn't already exist */
312   void createDEFtable() {
313     if ( DEFNodes == null ) {
314       DEFNodes = new Hashtable();
315     }
316   }
317 
318   /** register DEF node in Scene.  This is done as file is processed so at this
319    *  point names are changed to enforce uniqueness.
320    */
321   public void registerDEF( DEFUSENode def ) {
322       deregisterDEF( def );
323     DEFNodes.put( def.getId(), def );
324   }
325   
326   /** deregister DEF node in Scene, used before a rename */
327   public void deregisterDEF( DEFUSENode def ) {
328       createDEFtable();
329       if ( DEFNodes.get( def.getId() ) != null ) {
330           DEFNodes.remove( def.getId() );
331       }
332   }
333   
334   /** just reserve the String of a DEF */
335   public void registerDEF( String defName ) {
336     createDEFtable();
337     DEFNodes.put( defName, "" );
338   }
339 
340   /** get a def node given a "USE" */
341   public DEFUSENode getDEF( DEFUSENode use ) {
342     createDEFtable();
343     DEFUSENode d = (DEFUSENode)DEFNodes.get( use.getId() );
344     return( d );
345   }
346 
347   /** lookup a DEF node given an id */
348   public DEFUSENode getDEF( String id ) {
349     if ( DEFNodes != null ) {
350       return( (DEFUSENode)DEFNodes.get( id ));
351     } else {
352       return( null );
353     }
354   }
355 
356     /** is there a DEF node with a given name */
357   public boolean DEFexists( String id ) {
358     if ( DEFNodes != null ) {
359       return( DEFNodes.get( id ) != null );
360     } else {
361       return( false );
362     }
363   }
364   
365   
366     public Hashtable getDEFtable() {
367         return( DEFNodes );
368     }
369     
370   /**
371    *  Register PROTO node in Scene.
372    */
373   public void registerPROTO( PROTObase proto ) {
374     // register the PROTO with the actual type vector if necessary
375     if ( proto.getBuiltInNodeType() == null ) {
376       // PROTOs without a type are allowed, but reported with errors
377     } else if ( NodeType.typeTable.get( proto.getBuiltInNodeType() ) != null ) {
378       // create the Scene's protoTable if necessary.
379       // The "protoTable" keeps track of PROTOs that can be substituted
380       // for nodes of a particular type. 
381       if ( protoTable == null ) {
382         protoTable = new Hashtable();
383       }
384 
385       // see if there is already a vector for this particular type, if not create
386       // the vector and add it to the proto table
387       Vector vec = (Vector)protoTable.get( proto.getBuiltInNodeType() );
388       if ( vec == null ) {
389         vec = new Vector();
390         protoTable.put( proto.getBuiltInNodeType(), vec );
391       }
392 
393       // add the PROTO to its built in type vector
394       vec.addElement( proto );
395     }
396 
397     // force a unique name in the proto
398     if ( PROTONodes == null ) {
399       PROTONodes = new Hashtable();
400     } else if ( PROTONodes.get( proto.getId() ) != null ) {
401       PROTONodes.remove( proto.getId() );
402     }
403     PROTONodes.put( proto.getId(), proto );
404   }
405 
406   /**
407    *  Get the built in node name.  First check for the name in the PROTO nodes, and if
408    *  found, use the proto to determine the built in node name.  Otherwise, assume it is
409    *  a built in node, and use that name.
410    */
411   public String getBuiltInNodeName( String name ) {
412     if ( PROTONodes != null ) {
413       PROTObase p = (PROTObase)PROTONodes.get( name );
414       if ( p != null ) {
415         return( p.getBuiltInNodeType() );
416       }
417     }
418     if ( parent != null ) {
419       Scene s = (Scene)parent.getScene();
420       if ( s != null ) {
421         return( s.getBuiltInNodeName( name ));
422       }
423     }
424 
425     return( name );
426   }
427 
428     /** get the PROTO declaration associated with a PROTO name.
429      * If not found in this scene, and this scene is contained within another
430      * scene (i.e. this scene itself is contained in a PROTO), check its parent
431      * scene for the PROTO also.
432      */
433   public PROTObase getPROTO( String name ) {
434     PROTObase result = null;
435     if ( PROTONodes != null ) {
436       result = (PROTObase)PROTONodes.get( name );
437     } 
438     if ( result == null ) {
439       if ( parent != null ) {
440         Scene s = (Scene)parent.getScene();
441         if ( s != null ) {
442           return( s.getPROTO( name ));
443         }
444       }
445     }
446     return( result );
447   }
448   
449   
450   /** Create an instance of a particular PROTO */
451   public PROTOInstance PROTOFactory( String PROTOName ) {
452       PROTObase pb = getPROTO( PROTOName );
453       if ( pb == null ) {
454           return( null );
455       } else {
456           return( new PROTOInstance( pb ));
457       }
458   }
459   
460   /** Is there a PROTO with the given name */
461   public boolean isPROTO( String name ) {
462       return( getPROTO( name ) != null );
463   }
464 
465     /** template method, overrides VrmlElement.isScene(), used for finding
466      *  Scene that contains a particular VrmlElement.
467      */
468   public boolean isScene() {
469     return( true );
470   }
471 
472 
473   /**
474    *  Get the first node type in the Scene.  When Scene is contained in a PROTO,
475    *  this first type is the the actual type for PROTO nodes.
476    */
477   public String getFirstNodeType() {
478     if ( children != null ) {
479         int nChildren = numberChildren();
480       for ( int i = 0; i < nChildren; i++ ) {
481         VrmlElement e = getChildAt( i );
482         if ( e instanceof Node ) {
483           Node n = (Node)e;
484           if ( n instanceof DEFUSENode ) {
485             DEFUSENode dun = (DEFUSENode)n;
486             n = n.getNode();
487             // handle case where USE has bad id
488             if ( n == null ) {
489                 return( null );
490             }
491           }
492           return( n.getBaseName() );
493         }
494       }
495     }
496     return( null );
497   }
498   
499   /** Get the name of an existing PROTO that is the closest match with
500    *  the unknown PROTO name provided.
501    */
502   public String getClosestMatch( String protoString, ReturnInteger result ) {
503       return( VRML97.getClosestMatch( protoString, PROTONodes, result ));
504   }
505   
506     /** Add a ROUTE to the scene.  If the ROUTE has DEFs that conflict with
507      *  previously existing DEFs in this scene, then the Scene may have
508      *  re DEFfed those Nodes to eliminate the conflict (if the Scene was
509      *  constructed with a DEFNameFactory).  In this case, the ROUTE DEF 
510      *  names are renamed in exactly the same way.  Otherwise, the ROUTE
511      *  names are preserved, which may result in a parsing error if the
512      *  DEF names they refer to do not exist.
513      *
514      *  @param route  ROUTE from another scene graph
515      *  @return newly added ROUTE
516      */
517   public ROUTE addROUTE( ROUTE route ) {
518       StringBuffer routeString = new StringBuffer();
519       Scene s = (Scene)route.getScene();
520       TokenEnumerator dataSource = s.getTokenEnumerator();
521       Hashtable mapper = getNameMapper( s );
522       for ( int i = route.getFirstTokenOffset(); i <= route.getLastTokenOffset(); i++ ) {
523           String sourceString = dataSource.toString( i );
524           if ( mapper != null ) {
525               // if String has the "." in it, break with StringTokenizer,
526               // and if first token is in mapper, rebuild string
527               if ( sourceString.indexOf( "." ) > 0 ) {
528                   StringTokenizer st = new StringTokenizer( sourceString, "." );
529                   String oldName = st.nextToken();
530                   String newName = (String)mapper.get( oldName );
531                   if ( newName != null ) {
532                       sourceString = new String( newName + "." + st.nextToken() );
533                   }
534               }
535           }
536           routeString.append( sourceString );
537           if ( i < route.getLastTokenOffset() ) {
538               routeString.append( " " );
539           }
540       }
541       return( addROUTE( new String( routeString )));
542   }
543   
544     /** Add a ROUTE from a String
545      *
546      *  @param route  String form of ROUTE
547      *  @return newly added ROUTE
548      */
549   public ROUTE addROUTE( String routeString ) {
550       TokenEnumerator dataDestination = getTokenEnumerator();
551       int newTokenOffset = dataDestination.getNumberTokens();
552       dataDestination.addLine( routeString, new TokenFactory() );
553       dataDestination.setState( newTokenOffset );
554       ROUTE newROUTE = new ROUTE( newTokenOffset, dataDestination );
555     RouteSource rs = new RouteSource( dataDestination.getNextToken(), dataDestination, this );
556     newROUTE.addChild( rs );
557     newROUTE.addChild( new TO( dataDestination.getNextToken(), dataDestination ));
558     RouteDestination rd = new RouteDestination( dataDestination.getNextToken(), dataDestination, this );
559     newROUTE.addChild( rd );
560     newROUTE.setLastTokenOffset( rd.getLastTokenOffset() );
561     addChild( newROUTE );
562     setLastTokenOffset( newROUTE.getLastTokenOffset() );
563     return( newROUTE );
564     }
565     
566     /** Add a Node to the scene.
567      *
568      *  @param node Node, possibly from another scene graph, to add to this scene
569      *  @return newly added Node
570      */
571     public Node addNode( Node node ) {
572         DEFResolver defResolver = createDEFNames( node );
573         TokenData sceneTokenData = new TokenData( this );
574         TokenData nodeTokenData = new TokenData( node, TokenData.ReCreate );
575         sceneTokenData.insert( nodeTokenData );
576         Node newNode = nodeTokenData.getNode();
577         addChild( newNode );
578         setLastTokenOffset( newNode.getLastTokenOffset() );
579         if ( defResolver != null ) {
580             defResolver.resolve( newNode );
581         }
582         return( newNode );
583     }
584     
585     /** Add a Node to the Scene.
586      *
587      *  @param sourceNode node in String form
588      *  @return newly added Node
589      */
590     public Node addNode( String sourceNode ) {
591         TokenData newTokenData = new TokenData( sourceNode, defNameFactory );
592         return( addNode( newTokenData.getNode() ));
593     }
594 
595     /** table of source Scenes for node copies */
596     Hashtable sourceScenes = null;
597     
598     /** Rename conflicting DEFs with new names using DEFNameFactory.
599      *
600      *  @param node  Node with possibly conflicting DEFs being added to Scene
601      *
602      *  @return DEFResolver object that has all info necessary to resolve
603      *     conflicting DEFs after new node gets created
604      */
605     public DEFResolver createDEFNames( Node node ) {
606         if ( defNameFactory != null ) {
607             DEFVisitor defVisitor = new DEFVisitor();
608             node.traverse( defVisitor );
609             int defCount = defVisitor.getNumberDEFs();
610             Scene s = (Scene)node.getScene();
611             if ( s != null ) {
612                 for ( int i = 0; i < defCount; i++ ) {
613                     if ( DEFexists( defVisitor.getDEF( i ))) {
614                         DEFUSENode dun = getDEF( defVisitor.getDEF( i ));
615                         if (( dun != null ) && ( dun.getNode() != null )) {
616                             resolveDEF( s, defVisitor.getDEF( i ), defNameFactory.createDEFName( dun.getNode().getBaseName() ));
617                         }
618                     }
619                 }
620                 if ( defCount > 0 ) {
621                     return( new DEFResolver( this, s ));
622                 }
623             }
624         }
625         return( null );
626     }
627     
628     /** Resolve a DEF name conflict, generate a new name for the DEF.
629      *
630      *  @param scene source Scene for original Node
631      *  @param originalName original DEF name
632      *  @param newName new name as generated by DEFNameFactory
633      */
634     void resolveDEF( Scene scene, String originalName, String newName ) {
635         if ( sourceScenes == null ) {
636             sourceScenes = new Hashtable();
637         }
638         Hashtable mapper = (Hashtable)sourceScenes.get( scene );
639         if ( mapper == null ) {
640             mapper = new Hashtable();
641             sourceScenes.put( scene, mapper );
642         }
643         mapper.put( originalName, newName );
644     }
645     
646     /** Get the name mapper for a particular source Scene. */
647     public Hashtable getNameMapper( Scene scene ) {
648         if ( sourceScenes == null ) {
649             return( null );
650         } else {
651             return( (Hashtable)sourceScenes.get( scene ));
652         }
653     }
654     
655     /** Select a node, SelectNode interface */
656     public boolean selectNode( NodeSelection nodeSelection ) {
657         if (( nodeSelection.startLine == -1 ) || ( nodeSelection.endLine == -1 )) {
658             return( false );
659         }
660         if (( nodeSelection.startColumn == -1 ) || ( nodeSelection.endColumn == -1 )) {
661             return( false );
662         }
663         NodeSelectionVisitor nsv = new NodeSelectionVisitor( dataSource, nodeSelection );
664         traverse( nsv );
665         return( nsv.updateNodeSelection() );
666     }
667 }