1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.millscript.millscript.loaders;
23
24 import org.millscript.commons.util.EList;
25 import org.millscript.commons.util.MapIterator;
26 import org.millscript.commons.util.list.EArrayList;
27 import org.millscript.commons.vfs.VFile;
28 import org.millscript.commons.xml.api.Name;
29 import org.millscript.commons.xml.api.token.EndTagToken;
30 import org.millscript.commons.xml.api.token.StartTagToken;
31 import org.millscript.commons.xml.api.tokenizer.XmlTokenizer;
32 import org.millscript.commons.xml.tokenizer.XmlTokenizerFactory;
33 import org.millscript.commons.xml.tokenizer.wellformed.WellFormedXmlTokenizer;
34 import org.millscript.millscript.alert.Alerts;
35 import org.millscript.millscript.expr.CheckExpr;
36 import org.millscript.millscript.expr.CommaExpr;
37 import org.millscript.millscript.expr.ConstantExpr;
38 import org.millscript.millscript.expr.Expr;
39 import org.millscript.millscript.expr.XMLExpr;
40 import org.millscript.millscript.syntax.Parser;
41 import org.millscript.millscript.syntax.ParserImpl;
42 import org.millscript.millscript.syntax.TokenType;
43
44 import java.io.StringReader;
45
46 /**
47 * This class is a starting point for implementing a general XML template
48 * loader. It allows specific sub-classes to customize the loading process for
49 * custom tags and attributes.
50 */
51 public abstract class BasicTemplateLoader extends ExprLoader {
52
53 /**
54 * The XML tokenizer factory we use to get an XML tokenizer for a given
55 * file.
56 */
57 final XmlTokenizerFactory factory = new XmlTokenizerFactory();
58
59 /**
60 * The token visitor we are using to validate the current token.
61 */
62 BasicTemplateLoaderTokenVisitor tokenVisitor;
63
64 /**
65 * Constructs a new basic template loader with optional XML namespace
66 * support.
67 *
68 * @param ns indicates if the XML tokenizer should support namespaces
69 */
70 BasicTemplateLoader( final boolean ns ) {
71 this.factory.setNamespaceAware( ns );
72 }
73
74 /**
75 * Returns the compiled attribute value.
76 *
77 * @param value attribute value
78 * @return an Expr compiled from the specified attribute value
79 */
80 public abstract Expr compAttributeValue( final String value );
81
82 /**
83 * Performs any required steps to compile the specified end tag.
84 *
85 * @param token the end tag token to compile
86 */
87 public void compileEndTag( final EndTagToken token ) {
88
89 final EList< XMLExpr.XMLAttr > elementAttributes = new EArrayList< XMLExpr.XMLAttr >( this.tokenVisitor.startTagToken.getAttributes().size() );
90 final MapIterator< Name, String > it = this.tokenVisitor.startTagToken.getAttributes().iterator( true );
91 while ( it.hasNext() ) {
92 elementAttributes.addLast(
93 new XMLExpr.XMLAttr(
94 new ConstantExpr( it.nextKey() ),
95 this.compAttributeValue( it.currentValue().toString() )
96 )
97 );
98 }
99
100
101 this.tokenVisitor.parentTokenVisitor.pushChildElement(
102 new XMLExpr(
103 new ConstantExpr( this.tokenVisitor.startTagToken.getName() ),
104 elementAttributes,
105 this.tokenVisitor.getBody()
106 )
107 );
108
109 this.tokenVisitor = this.tokenVisitor.parentTokenVisitor;
110 }
111
112 /**
113 * Performs any required steps to compile the specified start tag.
114 *
115 * @param token the start tag token to compile
116 */
117 public void compileStartTag( final StartTagToken token ) {
118
119 this.tokenVisitor = new BasicTemplateLoaderElementTokenVisitor( this, this.tokenVisitor, token );
120 }
121
122 /**
123 * Returns the compiled special string. A special string, is considered to
124 * be normal MillScript, except that hash marks replace normal string
125 * quotes.
126 *
127 * @param s the string to compile
128 * @return an Expr compiled from the specified string
129 */
130 public final Expr compSpecialString( final String s ) {
131
132
133
134 Parser parser = new ParserImpl( this.getOrigin(), new StringReader( s ), false, pack.getConfig() );
135 Expr e = parser.readExpr();
136 if ( parser.peekToken() != TokenType.EOF ) {
137 throw(
138 Alerts.template(
139 "Unexpected extra input in template attribute",
140 "Special attribute must be single expressions"
141 ).culprit( "expression", s ).mishap()
142 );
143 }
144 return CheckExpr.make( e );
145 }
146
147 /**
148 * @see org.millscript.millscript.loaders.ExprLoader#loadExpr()
149 */
150 @Override
151 public Expr loadExpr() {
152 if ( this.entry instanceof VFile && this.entry.exists() ) {
153
154 final XmlTokenizer xmlTokenizer = new WellFormedXmlTokenizer(
155 this.factory.getTokenizer(
156 ((VFile) this.entry).getInputStream()
157 )
158 );
159
160 this.tokenVisitor = new BasicTemplateLoaderTokenVisitor( this, null, null );
161 while ( xmlTokenizer.hasNextToken() ) {
162 xmlTokenizer.nextToken().visit( this.tokenVisitor );
163 }
164
165 final Expr bodyExpr = this.tokenVisitor.getBody();
166 if ( bodyExpr instanceof CommaExpr ) {
167
168 throw Alerts.fault(
169 "Skeletons can only contains a single root entity"
170 ).mishap();
171 } else {
172
173 return makeLambda( symbol, bodyExpr );
174 }
175 } else {
176
177 throw(
178 Alerts.compile(
179 "Inventory entry is not a file",
180 "Template loader can only be used to load files"
181 ).mishap()
182 );
183 }
184 }
185
186 /**
187 * Returns a lambda expression for the specified name and body.
188 *
189 * @param theName the name for lambda expression
190 * @param body the body expression for the lambda expression
191 * @return an Expr
192 */
193 public abstract Expr makeLambda( final String theName, final Expr body );
194
195 }