View Javadoc

1   ////////////////////////////////////////////////////////////////////////////////
2   // MillScript: an Open Spice interpreter and batch website creation tool
3   // Copyright (C) 2001-2005 Open World Ltd
4   // Copyright (C) 2005 Kevin Rogers
5   //
6   // This file is part of MillScript.
7   //
8   // MillScript is free software; you can redistribute it and/or modify it under
9   // the terms of the GNU General Public License as published by the Free
10  // Software Foundation; either version 2 of the License, or (at your option)
11  // any later version.
12  //
13  // MillScript is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
16  // more details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // MillScript; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA  02111-1307  USA
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          // Compile the elements attributes
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          // Make the Expr for this XML element and push it into the parent
100         // elements body.
101         this.tokenVisitor.parentTokenVisitor.pushChildElement(
102             new XMLExpr(
103                 new ConstantExpr( this.tokenVisitor.startTagToken.getName() ),
104                 elementAttributes,
105                 this.tokenVisitor.getBody()
106             )
107         );
108         // End this element
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         // Start a new element
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         // TODO - It would be really great if we could pass in the line number
132         // as well, then error messages would have the right line number in the
133         // template as well as the file name
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             // We need a well-formed XmlTokenizer
154             final XmlTokenizer xmlTokenizer = new WellFormedXmlTokenizer(
155                 this.factory.getTokenizer(
156                     ((VFile) this.entry).getInputStream()
157                 )
158             );
159             // Report the start of the document
160             this.tokenVisitor = new BasicTemplateLoaderTokenVisitor( this, null, null );
161             while ( xmlTokenizer.hasNextToken() ) {
162                 xmlTokenizer.nextToken().visit( this.tokenVisitor );
163             }
164             // Report the end of the document
165             final Expr bodyExpr = this.tokenVisitor.getBody();
166             if ( bodyExpr instanceof CommaExpr ) {
167                 // Oh dear! The body has multiple elements
168                 throw Alerts.fault(
169                     "Skeletons can only contains a single root entity"
170                 ).mishap();
171             } else {
172                 // Make the function for this template
173                 return makeLambda( symbol, bodyExpr );
174             }
175         } else {
176             // Huh? Trying to load a folder as XML? Fat chance!
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 }