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

Quick Search    Search Deep

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


1   /*
2    * @(#)VrmlElement.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.pattern.Visitor;
15  import com.trapezium.pattern.VisitorPattern;
16  import com.trapezium.vrml.visitor.DumpVisitor;
17  import com.trapezium.vrml.visitor.ComplexityVisitor;
18  import com.trapezium.vrml.visitor.AdjustmentVisitor;
19  import com.trapezium.util.GlobalProgressIndicator;
20  import java.util.Vector;
21  
22  /**
23   *  A VrmlElement is the base class for any object in the VRML 2.0 object hierarchy.
24   *  Specific subclasses get created as a ".wrl" file is parsed.  The grammar determines
25   *  which type of element to create during parsing.
26   *
27   *  Each VrmlElement contains zero or more children, which are also VrmlElement.
28   *  The object hierarchy for this is very simple.  The root level object is a Scene, with
29   *  children that are Nodes or ROUTEs.  Node children are fields.  This object hierarchy
30   *  parallels the VRML syntax.
31   *
32   *  The VrmlElement implements the visitor pattern, which allows the object structure
33   *  to be traversed after it is created.  See LintVisitor for how parsing errors get
34   *  displayed.
35   *
36   *  @author          Johannes N. Johannsen
37   *  @version         1.21, field bug fix, self reference PROTO USE
38   *                   1.12, 29 May 1998, base profile nonconformance category
39   *                   1.12, 7 May 1998, make Serializable
40   *                   1.12, 29 March 1998, added "adjust" method
41   *  @version         1.1, 14 Jan 1998
42   *
43   *  @since           1.0
44   */
45  abstract public class VrmlElement implements VisitorPattern, java.io.Serializable {
46      /** Option, disable warnings entirely */
47    static public boolean nowarning = false;
48    /** Option, disable unused DEF warnings */
49    static public boolean noUnusedDEFwarning = false;
50    /** Option, only record nonconformance messages */
51    static public boolean baseProfile = false;
52    /** Option, disable nonconformance messages */
53    static public boolean disableBaseProfile = false;
54    /** Used to track number of VrmlElements created during parsing, not thread safe */
55    static public int createCount = 0;
56  
57    /** any error creating a specific element is noted here, see setError, getError */
58    public transient String errorString = null;
59  
60    /** parent, necessary for scoping searches */
61    VrmlElement parent = null;
62  
63    /** children are seen only through the Visitor pattern */
64    Object children = null;
65  
66    /** default constructor */
67    public VrmlElement() {
68        createCount++;
69    }
70  
71      /** Write out the object for serialization, mark progress if GlobalProgressIndicator
72       *  is set up.
73       */
74      private void writeObject(java.io.ObjectOutputStream oos) throws java.io.IOException {
75          GlobalProgressIndicator.markProgress();
76          oos.defaultWriteObject();
77      }
78  
79      /** Read in serialized object, mark progress if GlobalProgressIndicator is set up.
80       */
81      private void readObject(java.io.ObjectInputStream oos) throws java.io.IOException {
82          try {
83              GlobalProgressIndicator.markProgress();
84              oos.defaultReadObject();
85          } catch ( ClassNotFoundException e ) {
86              e.printStackTrace();
87              throw new java.io.IOException();
88          }
89      }
90  
91  
92    /** get the class name without package */
93    public String getBaseName() {
94      String className = getClass().getName();
95      int firstIndex = className.lastIndexOf( '.' ) + 1;
96      int lastIndex = className.length();
97      return( className.substring( firstIndex, lastIndex ));
98    }
99  
100   /* Set first token */
101   abstract public void setFirstTokenOffset( int tokenOffset );
102 
103   /** Get first token */
104   abstract public int getFirstTokenOffset();
105 
106   /** Set last token */
107   abstract public void setLastTokenOffset( int tokenOffset ) throws FunctionCallException;
108 
109   /** Get last token */
110   abstract public int getLastTokenOffset();
111 
112   /** Adjust token values */
113   abstract public void adjust( int boundary, int amount );
114 
115     public VrmlElement vrmlClone( VrmlElement protoInstance ) {
116         return( null );
117     }
118 
119   /** Add a child VrmlElement */
120   public void addChild( Object child ) {
121 
122     if ( child == null ) {
123       return;
124     }
125     // optimization, one child added directly, if more than one
126     // create Vector
127     if ( children == null ) {
128       children = child;
129     } else if ( children instanceof Vector ) {
130         ((Vector)children).addElement( child );
131     } else {
132         Object original = children;
133         children = new Vector();
134         ((Vector)children).addElement( original );
135         ((Vector)children).addElement( child );
136     }
137     if ( child instanceof VrmlElement ) {
138       VrmlElement pChild = (VrmlElement)child;
139       pChild.setParent( this );
140     }
141   }
142 
143   /** Remove a child.
144    *
145    *  @param child child to remove
146    *  @throws VrmlElementNotFoundException if the child is not part
147    *     of this VrmlElement
148    */
149   public void removeChild( Object child ) throws VrmlElementNotFoundException {
150       if ( !contains( child )) {
151           throw new VrmlElementNotFoundException();
152       }
153         if ( children instanceof Vector ) {
154       ((Vector)children).removeElement( child );
155       } else {
156           children = null;
157       }
158   }
159 
160   /** Check if a specific child exists.
161    *
162    *  @param child child to check for
163    *  @return true if child found, otherwise false
164    */
165   public boolean contains( Object child ) {
166       if ( children != null ) {
167           if ( children instanceof Vector ) {
168               return( ((Vector)children).contains( child ));
169           } else {
170               return( children == child );
171           }
172       } else {
173           return( false );
174       }
175   }
176 
177     /** does nothing unless DEFUSENode */
178     public void deregisterSelf() {
179     }
180 
181 
182     /** Remove a child, and update the text as well.
183      *
184      *  @param f the VrmlElement to remove
185      *  @throws VrmlElementNotFoundException if the VrmlElement parameter does not have this
186      *     VrmlElement as one of its ancestors.
187      */
188     public void removeVrmlElement( VrmlElement f ) throws VrmlElementNotFoundException {
189         f.deregisterSelf();
190         VrmlElement scanner = f.getParent();
191         VrmlElement container = null;
192         if ( contains( f )) {
193             container = this;
194         }
195         while (( scanner != null ) && ( scanner != this )) {
196             if ( scanner.contains( f )) {
197                 container = scanner;
198             }
199             scanner = scanner.getParent();
200         }
201         if (( scanner == null ) || ( container == null )) {
202             throw new VrmlElementNotFoundException();
203         }
204         container.removeChild( f );
205         int numberTokens = f.getLastTokenOffset() - f.getFirstTokenOffset() + 1;
206         Scene s = (Scene)f.getScene();
207         TokenEnumerator tokenEnumerator = s.getTokenEnumerator();
208         tokenEnumerator.startLineWith( f.getFirstTokenOffset() );
209         tokenEnumerator.startLineWith( f.getLastTokenOffset() + 1 );
210         AdjustmentVisitor av = new AdjustmentVisitor( tokenEnumerator, f.getFirstTokenOffset() + numberTokens - 1, -numberTokens );
211         s.traverse( av );
212         int firstLineNumber = tokenEnumerator.getLineNumber( f.getFirstTokenOffset() );
213         int numberLines = tokenEnumerator.getLineNumber( f.getLastTokenOffset() ) - firstLineNumber + 1;
214         int nTokens = f.getLastTokenOffset() - f.getFirstTokenOffset() + 1;
215         tokenEnumerator.removeTokens( f.getFirstTokenOffset(), nTokens );
216         tokenEnumerator.removeLines( firstLineNumber, numberLines );
217     }
218 
219   /** Get the specific child.
220    *
221    * @param offset the offset of the child, from 0 to (numberChlidren-1)
222    * @return the child VrmlElement, null if none or the offset value out
223    *     of range.
224    */
225   public VrmlElement getChildAt( int offset ) {
226     if ( children == null ) {
227       return( null );
228     } else if ( children instanceof Vector ) {
229         Vector vchildren = (Vector)children;
230             if ( vchildren.size() <= offset ) {
231           return( null );
232         } else {
233           return( (VrmlElement)vchildren.elementAt( offset ));
234         }
235     } else if ( offset == 0 ) {
236         return( (VrmlElement)children );
237     } else {
238         return( null );
239     }
240   }
241 
242   /** Get the last child of this VrmlElement */
243   public VrmlElement getLastChild() {
244       if ( children == null ) {
245           return( null );
246       } else if ( children instanceof Vector ) {
247           Vector vchildren = (Vector)children;
248           return( getChildAt( vchildren.size() - 1 ));
249       } else {
250           return( (VrmlElement)children );
251       }
252   }
253 
254   /** Get the number of children */
255   public int numberChildren() {
256     if ( children == null ) {
257       return( 0 );
258     } else if ( children instanceof Vector ) {
259         Vector vchildren = (Vector)children;
260       return( vchildren.size() );
261     } else {
262         return( 1 );
263     }
264   }
265 
266   /** set a child's parent */
267   public void setParent( VrmlElement p ) {
268     parent = p;
269   }
270 
271   /** get this element's parent */
272   public VrmlElement getParent() {
273     return( parent );
274   }
275 
276   /** set the error string */
277   public void setError( String s ) {
278       if ( s == null ) {
279           errorString = null;
280           return;
281       }
282     if ( nowarning ) {
283       if ( s.indexOf( "Warning" ) == 0 ) {
284         return;
285       }
286     }
287     if ( noUnusedDEFwarning ) {
288         if ( s.indexOf( "DEF is not used" ) > 0 ) {
289             return;
290         }
291     }
292     if ( baseProfile ) {
293         if ( s.indexOf( "Nonconformance" ) == -1 ) {
294             return;
295         }
296     }
297     if ( disableBaseProfile ) {
298         if ( s.indexOf( "Nonconformance" ) == 0 ) {
299             return;
300         }
301     }
302     if ( errorString == null ) {
303       errorString = s;
304     } else {
305       if ( errorString.indexOf( s ) >= 0 ) {
306         return;
307       }
308       StringBuffer b = null;
309       if (( errorString.indexOf( "Warning" ) == 0 ) || ( errorString.indexOf( "Nonconformance" ) == 0 )) {
310         b = new StringBuffer( s );
311         b.append( ", " + errorString );
312       } else {
313         b = new StringBuffer( errorString );
314         b.append( ", " + s );
315       }
316       errorString = new String( b );
317     }
318   }
319 
320   /** get the error string */
321   public String getError() {
322     return( errorString );
323   }
324 
325     /** is this VrmlElement traversable */
326   abstract public boolean isTraversable();
327 
328     /** template method, Field objects override this to traverse their
329      *  field value.
330      */
331     public void fieldValueTraverse( Visitor v ) {
332     }
333 
334   /** Visitor pattern, traverse structure with a particular visitor */
335   public void twoPassTraverse( Visitor v ) {
336     // the visitor controls whether children get visited or not
337     if ( v.visit( this )) {
338         fieldValueTraverse( v );
339       if ( children != null ) {
340           int nChildren = numberChildren();
341         for ( int i = 0; i < nChildren; i++ ) {
342           VrmlElement child = getChildAt( i );
343           if ( v.acceptsPassOne( child )) {
344               child.twoPassTraverse( v );
345           }
346         }
347         for ( int i = 0; i < nChildren; i++ ) {
348           VrmlElement child = getChildAt( i );
349           if ( v.acceptsPassTwo( child )) {
350               child.twoPassTraverse( v );
351           }
352         }
353       }
354     }
355     // used to keep track of how many levels down we are visiting
356     v.done();
357   }
358   
359   /** Visitor pattern, traverse structure with a particular visitor */
360   public void traverse( Visitor v ) {
361     // the visitor controls whether children get visited or not
362     if ( v.visit( this )) {
363         fieldValueTraverse( v );
364       if ( children != null ) {
365           int nChildren = numberChildren();
366         for ( int i = 0; i < nChildren; i++ ) {
367           VrmlElement child = getChildAt( i );
368             if ( !( v instanceof ComplexityVisitor ) && !child.isTraversable()) {
369               v.visit( child );
370               v.done();
371             continue;
372           }
373           if ( v.accepts( child )) {
374             child.traverse( v );
375           }
376         }
377       }
378     }
379     // used to keep track of how many levels down we are visiting
380     v.done();
381   }
382 
383     /** Get the root of the scene graph */
384   public VrmlElement getRoot() {
385     if ( parent == null ) {
386       return( this );
387     } else {
388       return( parent.getRoot() );
389     }
390   }
391 
392   /** Get the root scene containing this element.
393    *  This is necessary in some cases because the Scene contains the
394    *  name space for DEFs and PROTOs.
395    *  This is done within VrmlElement, because we need this name space
396    *  for a particular element.
397    *  The "isScene" method is a simple template method, returns false
398    *  for all VrmlElement instances except for Scene.
399    *
400    *  @return  the Scene object containing this element, or null if
401    *           none found.
402    */
403   public VrmlElement getScene() {
404     VrmlElement scanner = this;
405     while ( !( scanner.isScene() )) {
406       scanner = scanner.parent;
407       if ( scanner == null ) {
408         break;
409       } else if ( scanner == this ) {
410           // cycle
411           scanner = null;
412           break;
413       }
414     }
415 
416     if ( scanner == null ) {
417       return( null );
418     } else {
419       return( scanner );
420     }
421   }
422 
423     /** Does this element or any of its children have any errors */
424     public boolean containsErrors() {
425         if ( errorString != null ) {
426             if (( errorString.indexOf( "Warning" ) != 0 ) && ( errorString.indexOf( "Nonconformance" ) != 0 )) {
427                 return( true );
428             }
429         }
430         int nChildren = numberChildren();
431         for ( int i = 0; i < nChildren; i++ ) {
432             VrmlElement v = getChildAt( i );
433             if ( v.containsErrors() ) {
434                 return( true );
435             }
436         }
437         return( false );
438     }
439 
440     /** template method, Scene overrides this to return true */
441   public boolean isScene() {
442     return( false );
443   }
444   
445   /** Get the TokenEnumerator for this element */
446   public TokenEnumerator getTokenEnumerator() {
447       Scene s = (Scene)getScene();
448       if ( s != null ) {
449           return( s.getTokenEnumerator() );
450       } else {
451           return( null );
452       }
453   }
454 
455     /** Add a warning Value child.  This exists for those cases where
456      *  there is no VrmlElement for the tokenOffset associated with the
457      *  warning.  NOTE:  this may be restricted (i.e. not called) by
458      *  the ErrorSummary object contained in the Scene.  The ErrorSummary
459      *  restriction prevents OutOfMemory conditions when there are too
460      *  many warnings in a file.
461      *
462      *  @param tokenOffset token offset where warning is to be attached
463      *  @param warning String message containing warning
464      */
465     public void addWarning( int tokenOffset, String warning ) {
466         if ( !nowarning ) {
467             Value v = new Value( tokenOffset );
468             v.setError( warning );
469             addChild( v );
470         }
471     }
472 
473     /** debugging dump method */
474   public void dump( String header ) {
475     dump( header, false );
476   }
477 
478     /** another debugging dump method */
479   public void dumpUserDefined( String header ) {
480     System.out.println( "BEFORE: " + header );
481     Scene s = (Scene)getScene();
482     DumpVisitor dv = new DumpVisitor( System.out, s.getTokenEnumerator() );
483     dv.dumpUserDefinedFields();
484     traverse( dv );
485     System.out.println( "AFTER: " + header );
486   }
487 
488     /** and another debugging dump method */
489   public void dump( String header, boolean tokensToo ) {
490     System.out.println( "BEFORE: " + header );
491     Scene s = (Scene)getScene();
492     DumpVisitor dv = new DumpVisitor( System.out, s.getTokenEnumerator() );
493     traverse( dv );
494     System.out.println( "AFTER: " + header );
495   }
496 
497 }