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

Quick Search    Search Deep

Source code: com/trapezium/vrml/visitor/PROTOcollector.java


1   /*
2    * @(#)PROTOcollector.java
3    *
4    * Copyright (c) 1998-1999 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.visitor;
12  
13  import com.trapezium.pattern.Visitor;
14  import com.trapezium.vrml.node.PROTObase;
15  import com.trapezium.vrml.node.PROTOInstance;
16  import com.trapezium.vrml.Scene;
17  import com.trapezium.vrml.ROUTE;
18  import com.trapezium.vrml.VrmlElement;
19  import com.trapezium.vrml.node.Node;
20  import com.trapezium.vrml.node.DEFUSENode;
21  import com.trapezium.vrml.grammar.DEFNameFactory;
22  import com.trapezium.parse.TokenEnumerator;
23  
24  import java.util.Vector;
25  import java.util.BitSet;
26  import java.util.Hashtable;
27  
28  /** The PROTOcollector resolves conflicts introduced when one scene graph 
29   *  is merged into another.  It collects infomation about all conflicts,
30   *  then provides information about those conflicts.
31   *
32   *  The method "resolveConflicts" resolves those which can be handled in-place.
33   *  Otherwise, it has methods for extracting the information token by token.
34   *  In-place conflict resolution handles DEF/USE/PROTO naming conflicts, but
35   *  does not handle moving PROTOs to new locations in the file.
36   *
37   *  Conflict resolution is one of two ways:  the DEFNameFactory in the destination
38   *  scene is used if it exists, otherwise an algorithm of appending "_1", "_2", etc.
39   *  is used until a name without a conflict is found.
40   */
41  public class PROTOcollector extends Visitor {
42      // the list of PROTOs in the Scene
43      Vector protoList;
44      
45      // the visited Scene is merged with a destination Scene
46      // the visited Scene is the container for all objects visited
47      Scene visitedScene;
48      
49      // the destination Scene is not visited, it is necessary only to
50      // resolve name space conflicts
51      Scene destinationScene;
52      
53      // bitmap of PROTOInstance locations
54      BitSet protoInstanceLocations;
55      // bitmap of PROTO locations
56      BitSet protoLocations;
57      // bitmap of USE locations
58      BitSet useLocations;
59      // bitmap of ROUTE locations
60      BitSet routeLocations;
61      int numberTokens;
62      
63      // PROTO name conflict resolution, key is old name, value is new name
64      Hashtable protoMapTable;
65      
66      // DEF name conflict resolution, key is old name, value is new name
67      Hashtable defMapTable;
68      
69      // PROTO lookup, key is PROTO token offset, value is PROTO
70      Hashtable protoTable;
71      
72      // ROUTE lookup, key is ROUTE token offset, value is ROUTE
73      Hashtable routeTable;
74      
75      // list of ROUTEs
76      Vector routes;
77      
78      /** class constructor
79       *
80       *  @param s the Scene to merge into the destination Scene
81       *  @param destinationScene the Scene that is growing
82       */
83      public PROTOcollector( Scene s, Scene destinationScene ) {
84          super( s.getTokenEnumerator() );
85          this.visitedScene = s;
86          protoList = null;
87          this.destinationScene = destinationScene;
88          numberTokens = dataSource.getNumberTokens();
89          protoInstanceLocations = new BitSet( numberTokens );
90          protoLocations = new BitSet( numberTokens );
91          useLocations = new BitSet( numberTokens );
92          routeLocations = new BitSet( numberTokens );
93          protoMapTable = new Hashtable();
94          defMapTable = new Hashtable();
95          protoTable = new Hashtable();
96          routeTable = new Hashtable();
97      }
98  
99      /** Get the scene that is going to be modified as a result of merging into
100      *  the destinationScene.
101      */
102     public Scene getScene() {
103         return( visitedScene );
104     }
105     
106     /** were any PROTOs found? */
107     public boolean hasPROTOs() {
108         return( protoList != null );
109     }
110     
111     /** How many PROTOs were found */
112     public int getNumberPROTOs() {
113         if ( protoList != null ) {
114             return( protoList.size() );
115         } else {
116             return( 0 );
117         }
118     }
119     
120     /** Get a particular PROTO */
121     public PROTObase getPROTO( int offset ) {
122         return( (PROTObase)protoList.elementAt( offset ));
123     }
124 
125     /** Visit an object in the Scene, save PROTO, DEF/USE, and ROUTE info
126      *  for possible conflict resolution.
127      */
128     public boolean visitObject( Object a ) {
129         if ( a instanceof PROTObase ) {
130             savePROTO( (PROTObase)a );
131         } else if ( a instanceof PROTOInstance ) {
132             savePROTOinstance( (PROTOInstance)a );
133         } else if ( a instanceof DEFUSENode ) {
134             saveDEFUSENode( (DEFUSENode)a );
135         } else if ( a instanceof ROUTE ) {
136             saveROUTE( (ROUTE)a );
137         }
138         return( true );
139     }
140     
141     /** Save a ROUTE, if either its source or dest object name is remapped
142      *  according to the defMapTable, redo the token.
143      */
144     void saveROUTE( ROUTE route ) {
145         int offset = route.getFirstTokenOffset();
146         routeLocations.set( offset );
147         routeTable.put( new Integer( offset ), route );
148         String sourceObject = route.getSourceDEFname();
149         String destObject = route.getDestDEFname();
150         String remapSource = (String)defMapTable.get( sourceObject );
151         String remapDest = (String)defMapTable.get( destObject );
152         if ( remapSource != null ) {
153             VrmlElement root = route.getRoot();
154             if ( root instanceof Scene ) {
155                 Scene sroot = (Scene)root;
156                 TokenEnumerator te = sroot.getTokenEnumerator();
157                 VrmlElement v = route.getChildAt( 0 );
158                 te.replace( v.getFirstTokenOffset(), remapSource + "." + route.getSourceFieldName() );
159             }
160         }
161         if ( remapDest != null ) {
162             VrmlElement root = route.getRoot();
163             if ( root instanceof Scene ) {
164                 Scene sroot = (Scene)root;
165                 TokenEnumerator te = sroot.getTokenEnumerator();
166                 VrmlElement v = route.getChildAt( 2 );
167                 te.replace( v.getFirstTokenOffset(), remapDest + "." + route.getDestFieldName() );
168             }
169         }
170         if ( routes == null ) {
171             routes = new Vector();
172         }
173         routes.addElement( route );
174     }
175     
176     /** Check if any ROUTEs were saved */
177     public boolean hasROUTEs() {
178         return( routes != null );
179     }
180     
181     /** Get the number of ROUTEs saved */
182     public int getNumberROUTEs() {
183         return( routes.size() );
184     }
185     
186     /** Get a specific ROUTE that was saved */
187     public ROUTE getROUTE( int offset ) {
188         return( (ROUTE)routes.elementAt( offset ));
189     }
190  
191     /** Save a DEFUSENode.  If it is a DEF, it is renamed if its name
192      *  conflicts with one in the destination scene.  The DEF is also
193      *  registered with the destination scene to allow detection of
194      *  other conflicts.  If it is a USE, it is renamed if the corresponding
195      *  DEF was renamed.
196      */
197     void saveDEFUSENode( DEFUSENode dun ) {
198         if ( dun.isDEF() ) {
199             String defName = dun.getDEFName();
200             if ( destinationScene.getDEF( defName ) == null ) {
201                 destinationScene.registerDEF( dun );
202             } else {
203                 DEFNameFactory dnf = destinationScene.getDEFNameFactory();
204                 if ( dnf == null ) {
205                     for ( int i = 1; i < 100; i++ ) {
206                         String s = defName + "_" + i;
207                         if ( destinationScene.getDEF( s ) == null ) {
208                             defMapTable.put( defName, s );
209                             dun.setDEFName( s );
210                             destinationScene.registerDEF( dun );
211                             return;
212                         }
213                     }
214                 } else {
215                     String nodeName = "unknown";
216                     Node n = dun.getNode();
217                     if ( n != null ) {
218                         nodeName = n.getNodeName();
219                     }
220                     String s = dnf.createDEFName( nodeName );
221                     // NOTE: we assume user supplied DEF name factory generates
222                     // a name without a conflict
223                     defMapTable.put( defName, s );
224                     dun.setDEFName( s );
225                     destinationScene.registerDEF( dun );
226                     return;
227                 }
228             }
229         } else {
230             int offset = dun.getFirstTokenOffset();
231             offset = dataSource.getNextToken( offset );
232             if ( offset != -1 ) {
233                 useLocations.set( offset );
234             }
235         }
236     }
237     
238     /** Save a PROTO, renaming it if its name conflicts with one
239      *  in the destination scene.  The PROTO is also registered in
240      *  the destination scene, since this is its ultimate destination,
241      *  so that conflicts between this and others merging into the
242      *  same destination are detected.
243      */
244     void savePROTO( PROTObase proto ) {
245         if ( protoList == null ) {
246             protoList = new Vector();
247         }
248         protoList.addElement( proto );
249         int offset = proto.getFirstTokenOffset();
250         protoTable.put( new Integer( offset ), proto );
251         if ( offset != -1 ) {
252             protoLocations.set( offset );
253         }
254         String protoId = proto.getId();
255         if ( destinationScene.getPROTO( protoId ) == null ) {
256             destinationScene.registerPROTO( proto );
257         } else {
258             for ( int i = 1; i < 100; i++ ) {
259                 String s = protoId + "_" + i;
260                 if ( destinationScene.getPROTO( s ) == null ) {
261                     protoMapTable.put( protoId, s );
262                     proto.setId( s );
263                     destinationScene.registerPROTO( proto );
264                     return;
265                 }
266             }
267         }
268     }
269     
270     /** Save a PROTOInstance, if the PROTO declaration has a naming conflict
271      *  all PROTOInstances must be renamed.  This method saves the location of
272      *  a PROTOInstance in case this is necessary.
273      */
274     void savePROTOinstance( PROTOInstance a ) {
275         int offset = a.getFirstTokenOffset();
276         if ( offset != -1 ) {
277             protoInstanceLocations.set( offset );
278         }
279     }
280     
281     int currentToken = -1;
282     
283     /** Token scanning methods */
284     
285     /** Start scanning tokens in the scene to merge */
286     public void scanTokens() {
287         currentToken = 0;
288     }
289     
290     /** Check if there are more tokens to access in the scene to merge */
291     public boolean hasMoreTokens() {
292         return( currentToken < numberTokens );
293     }
294 
295     /** Is there a PROTO instance at a particular token offset that is renamed due
296      *  to a PROTO declaration conflict.
297      *
298      *  @param tokenOffset the offset to check
299      *
300      *  @return true if the token offset is the first token in a PROTO instance,
301      *    and that PROTO instance has a corresponding PROTO declaration which
302      *    has been renamed due to a conflict with another PROTO declartion in the
303      *    destination Scene.
304      */
305     public boolean protoIsRemapped( int tokenOffset ) {
306         return( protoInstanceLocations.get( tokenOffset ));
307     }
308     
309     
310     /** Is there a USE node at a particular token offset that is renamed due to
311     *   a DEF declaration conflict.
312     *
313     *  @param tokenOFfset the offset to check
314     *
315     *  @return true if the token offset is the first token in a USE node, and that
316     *    USE node has a corresponding DEF that has been renamed due to a conflict
317     *    with another DEF in the destination Scene.
318     */
319     public boolean useIsRemapped( int tokenOffset ) {
320         return( useLocations.get( tokenOffset ));
321     }
322  
323     
324     /** Get the name for a remapped PROTO.
325      *
326      *  @param tokenOffset the offset of the PROTO
327      *
328      *  @return the new name for the PROTO, or original name if the tokenOffset parameter
329      *     does not indicate a remapped PROTO (this latter case should not occur
330      *     normally)
331      */
332     public String remapProto( int tokenOffset ) {
333         return( remap( tokenOffset, protoMapTable ));
334     }
335     
336     
337     /** Get the name for a remapped USE node.
338      *
339      *  @param tokenOffset the offset for the USE node
340      *
341      *  @return the new name for the USE, or original name if the tokenOffset parameter
342      *     does not indicate a remapped USE (this latter case should not occur
343      *     normally)
344      */
345     public String remapUse( int tokenOffset ) {
346         return( remap( tokenOffset, defMapTable ));
347     }
348     
349     
350     /** Generic remapping of Strings based on token offset.
351      *
352      *  @param tokenOffset the offset used as a key into the remapping table
353      *  @param table the remapping table
354      *
355      *  @return the remapped name, based on the contents of the remapping table.
356      */
357     String remap( int tokenOffset, Hashtable table ) {
358         String original = dataSource.toString( tokenOffset );
359         String remap = (String)table.get( original );
360         if ( remap == null ) {
361             return( original );
362         } else {
363             return( remap );
364         }
365     }
366     
367     /** Get the next token offset */
368     public int getNextToken() {
369         currentToken++;
370         while ( protoLocations.get( currentToken ) || routeLocations.get( currentToken )) {
371             if ( protoLocations.get( currentToken )) {
372                 PROTObase p = (PROTObase)protoTable.get( new Integer( currentToken ));
373                 if ( p != null ) {
374                     currentToken = p.getLastTokenOffset();
375                     currentToken++;
376                     if ( currentToken >= numberTokens ) {
377                         return( -1 );
378                     }
379                 }
380             } else if ( routeLocations.get( currentToken )) {
381                 ROUTE r = (ROUTE)routeTable.get( new Integer( currentToken ));
382                 if ( r != null ) {
383                     currentToken = r.getLastTokenOffset();
384                     currentToken++;
385                     if ( currentToken >= numberTokens ) {
386                         return( -1 );
387                     }
388                 }
389             }
390         }
391         return( currentToken );
392     }
393 }