Source code: com/trapezium/vrml/grammar/VRML97parser.java
1 /*
2 * @(#)VRML97parser.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.grammar;
12
13 import com.trapezium.vrml.VrmlElement;
14 import com.trapezium.vrml.Scene;
15 import com.trapezium.vrml.ErrorElement;
16 import com.trapezium.vrml.visitor.ParentSetter;
17 import com.trapezium.parse.TokenEnumerator;
18
19 /**
20 * Main class for parsing a VRML 2.0 file.
21 *
22 * A Scene is the root of a VRML element hierarchy. This is a simple hierarchy where each
23 * VrmlElement may or may not have children. Much of the additional functionality required
24 * is implemented by using the "Visitor Pattern" on this hierarchy. This pattern encapsulates
25 * features in the visitor objects.
26 *
27 * Within the SceneRule factory, the above parameters eventually turn into a Token enumerator,
28 * and the Scene is constructed as follows:
29 *
30 * TokenEnumerator tokenEnumerator = ...
31 * Scene scene = new Scene( fileUrl, tokenEnumerator );
32 * VRML97parser parser = new VRML97parser();
33 * parser.Build( tokenEnumerator, scene );
34 *
35 * The convention followed by this and all other grammar rules is that a static public
36 * "Build" method is used to add children to an object.
37 *
38 * The SceneRule handles the following portion of the grammar from the VRML 2.0 spec:
39 *
40 * vrmlScene ::=
41 * statements ;
42 * statements ::=
43 * statement |
44 * statement statements |
45 * empty ;
46 *
47 * @author Johannes N. Johannsen
48 * @version 1.12, 2 April 1998, allow inclusion of first token in parsing
49 * @version 1.1, 21 Jan 1998
50 *
51 * @since 1.0
52 */
53 public class VRML97parser {
54 NodeRule nodeRule;
55 StatementRule statementRule;
56
57 // PROTOs use same parser as base
58 static public VRML97parser singleton = null;
59
60 /** Parser with no DEFNameFactory and no error reporting limits */
61 public VRML97parser() {
62 this( null );
63 }
64
65 /** Parser with an optional DEFNameFactory for generating DEF names.
66 *
67 * If the "Build" method gets called with a Scene that also has
68 * a DEFNameFactory, that DEFNameFactory will replace the VRML97parser
69 * DEFNameFactory.
70 */
71 public VRML97parser( DEFNameFactory defNameFactory ) {
72 nodeRule = new NodeRule( defNameFactory );
73 statementRule = new StatementRule( nodeRule );
74 singleton = this;
75 }
76
77 /**
78 * Apply the SceneRule to update the Scene.
79 */
80 public void Build( TokenEnumerator v, Scene scene ) {
81 String line = v.getLineAt( 0 );
82 if ( line.indexOf( "#VRML V2.0 utf8" ) != 0 ) {
83 scene.addChild( new ErrorElement( 0, "Bad header, expected 'VRML V2.0 utf8'" ));
84 } else {
85 Build( v, scene, 0 );
86 }
87 }
88
89 /**
90 * Build Scene, startToken needed for dynamic generation. Normally
91 * this is 0, which is the header, which is not ignored by the parsing.
92 * For dynamically generated Nodes, with no header, this is set to
93 * -1 so that first token is not ignored in parsing.
94 */
95 public void Build( TokenEnumerator v, Scene scene, int startToken ) {
96 v.setState( startToken );
97
98 // use the scene's DEFNameFactory, if any
99 if ( scene.getDEFNameFactory() != null ) {
100 nodeRule.setDEFNameFactory( scene.getDEFNameFactory() );
101 }
102 Build( v, scene, scene, "" );
103
104 // necessary to prevent infinite loop on visitors if
105 // scene graph has Script field self references
106 ParentSetter ps = new ParentSetter( v );
107 scene.traverse( ps );
108 }
109
110 /**
111 * Build either an embedded Scene (for PROTOs), or a Scene from a file. If the
112 * "terminator" is a "}", we are building an embedded Scene.
113 */
114 void Build( TokenEnumerator v, Scene scene, VrmlElement parent, String terminator ) {
115 GrammarRule.Enter( "SceneRule.Build" );
116 scene.setFirstTokenOffset( v.getCurrentTokenOffset() );
117 int tokenOffset = -1;
118 while ( true ) {
119 tokenOffset = v.getNextToken();
120 if ( tokenOffset == -1 ) {
121 break;
122 }
123 if ( v.sameAs( tokenOffset, terminator )) {
124 break;
125 }
126 statementRule.Build( tokenOffset, v, scene, parent );
127 }
128 if ( tokenOffset == -1 ) {
129 scene.setLastTokenOffset( v.getCurrentTokenOffset() - 1 );
130 } else {
131 scene.setLastTokenOffset( v.getCurrentTokenOffset() );
132 }
133 GrammarRule.Exit( "SceneRule.Build" );
134 }
135 }
136