View Javadoc

1   ////////////////////////////////////////////////////////////////////////////////
2   // MillScript: an Open Spice interpreter and batch website creation tool
3   // Copyright (C) 2005 Kevin Rogers
4   //
5   // This file is part of MillScript.
6   //
7   // MillScript is free software; you can redistribute it and/or modify it under
8   // the terms of the GNU General Public License as published by the Free
9   // Software Foundation; either version 2 of the License, or (at your option)
10  // any later version.
11  //
12  // MillScript is distributed in the hope that it will be useful, but WITHOUT
13  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  // more details.
16  //
17  // You should have received a copy of the GNU General Public License along with
18  // MillScript; if not, write to the Free Software Foundation, Inc., 59 Temple
19  // Place, Suite 330, Boston, MA  02111-1307  USA
20  ////////////////////////////////////////////////////////////////////////////////
21  package org.millscript.millscript.functions;
22  
23  import org.millscript.commons.alert.Alert;
24  import org.millscript.commons.alert.alerts.Fault;
25  import org.millscript.commons.util.list.ELinkedList;
26  import org.millscript.commons.xml.alerts.XmlWellFormednessAlert;
27  import org.millscript.commons.xml.api.token.AttListDeclToken;
28  import org.millscript.commons.xml.api.token.CharDataToken;
29  import org.millscript.commons.xml.api.token.CommentToken;
30  import org.millscript.commons.xml.api.token.DTDToken;
31  import org.millscript.commons.xml.api.token.ElementDeclToken;
32  import org.millscript.commons.xml.api.token.EmptyElementToken;
33  import org.millscript.commons.xml.api.token.EndTagToken;
34  import org.millscript.commons.xml.api.token.EntityDeclToken;
35  import org.millscript.commons.xml.api.token.NotationDeclToken;
36  import org.millscript.commons.xml.api.token.PIToken;
37  import org.millscript.commons.xml.api.token.StartTagToken;
38  import org.millscript.commons.xml.api.token.TokenVisitor;
39  import org.millscript.commons.xml.api.token.XmlDeclToken;
40  import org.millscript.millscript.alert.Alerts;
41  import org.millscript.millscript.datatypes.XmlComment;
42  import org.millscript.millscript.datatypes.XmlElement;
43  import org.millscript.millscript.vm.Machine;
44  
45  /**
46   * This class provides validation and handling logic for parsing a String which
47   * contains some XML fragments. The XML is considered to match the
48   * <code>content</code> production in the XML specification, e.g. no XML
49   * declaration or DTD.
50   */
51  public class StringToXMLTokenVisitor implements TokenVisitor {
52  
53      /**
54       * This body expression for the element.
55       */
56      ELinkedList< Object > body = new ELinkedList< Object >();
57  
58      /**
59       * The machine we push our root entities onto.
60       */
61      private final Machine machine;
62  
63      /**
64       * The parent token visitor we should return to once this one has finished
65       * what it is supposed to do.
66       */
67      final StringToXMLTokenVisitor parentTokenVisitor;
68  
69      /**
70       * The start tag that began the section this token visitor represents.
71       */
72      final StartTagToken startTagToken;
73  
74      /**
75       * The function that is controlling the XML parse.
76       */
77      final StringToXMLFunction stringToXMLFunction;
78  
79      /**
80       * This class is used when visiting an XML element and it handles the
81       * tokens visited in the elements content.
82       */
83      private class ElementTokenVisitor extends StringToXMLTokenVisitor {
84  
85          /**
86           * Constructs a new element token visitor, with the specified
87           * controlling function, parent token visitor and start tag.
88           *
89           * @param stf   the controlling function
90           * @param stv   the parent token visitor
91           * @param stt   the start tag token
92           */
93          public ElementTokenVisitor( final StringToXMLFunction stf, final StringToXMLTokenVisitor stv, final StartTagToken stt ) {
94              super( stf, stv, stt, null );
95          }
96  
97          /**
98           * @see org.millscript.millscript.functions.StringToXMLTokenVisitor#pushChildElement(java.lang.Object)
99           */
100         @Override
101         void pushChildElement( final Object o ) {
102             this.body.addLast( o );
103         }
104 
105         /**
106          * @see org.millscript.millscript.functions.StringToXMLTokenVisitor#reportAnyErrors()
107          */
108         @Override
109         public void reportAnyErrors() {
110             throw new Alert(
111                 "The matching end tag for this start tag appears to be missing"
112             ).decorate( this.startTagToken ).mishap();
113         }
114 
115         /**
116          * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.EndTagToken)
117          */
118         @Override
119         public void visit( final EndTagToken token ) {
120             // To be valid, this token must match the start tags
121             if ( this.startTagToken.getName().equals( token.getName() ) ) {
122                 // Make the Expr for this XML element and push it into the parent
123                 // elements body.
124                 this.parentTokenVisitor.pushChildElement(
125                     new XmlElement(
126                         this.startTagToken.getName(),
127                         this.startTagToken.getAttributes(),
128                         body.toArray()
129                     )
130                 );
131                 // End this element
132                 this.stringToXMLFunction.tokenVisitor = this.parentTokenVisitor;
133             } else {
134                 throw new XmlWellFormednessAlert(
135                     "Mismatched start/end tags"
136                 ).decorate( this.startTagToken ).decorate( token ).mishap();
137             }
138         }
139 
140     }
141 
142     /**
143      * Constructs a new XML string token visitor, with the specified
144      * controlling function, parent token visitor, start tag and machine.
145      *
146      * @param stf   the controlling function
147      * @param stv   the parent token visitor
148      * @param stt   the start tag token
149      * @param mc    the machine to push results to
150      */
151     public StringToXMLTokenVisitor( final StringToXMLFunction stf, final StringToXMLTokenVisitor stv, final StartTagToken stt, final Machine mc ) {
152         this.machine = mc;
153         this.parentTokenVisitor = stv;
154         this.startTagToken = stt;
155         this.stringToXMLFunction = stf;
156     }
157 
158     /**
159      * Pushes the expression as part of this elements body.
160      *
161      * @param o the next body element for this element
162      */
163     void pushChildElement( final Object o ) {
164         // At the top level, we simply want to return the resulting object
165         this.machine.pushObject( o );
166     }
167 
168     /**
169      * Reports any errors left over after processing the string.
170      */
171     public void reportAnyErrors() {
172         // Nothing to report
173     }
174 
175     /**
176      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.AttListDeclToken)
177      */
178     public void visit( final AttListDeclToken token ) {
179         // We don't handle processing instructions
180         throw(
181             Alerts.unimplemented(
182                 "Attribute list declarations cannot be compiled at the moment, sorry!"
183             ).decorate( token ).mishap()
184         );
185     }
186 
187     /**
188      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.CharDataToken)
189      */
190     public void visit( final CharDataToken token ) {
191         if ( token.getCharData().length() == 0 ) {
192             // TODO - We might be able to remove this
193             throw new Fault(
194                 "Appending empty character data"
195             ).mishap();
196         }
197         // TODO - This needs to respect whitespace, CDATA and plain text
198         this.pushChildElement(
199             token.getCharData()
200         );
201     }
202 
203     /**
204      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.CommentToken)
205      */
206     public void visit( final CommentToken token ) {
207         if ( token.getCommentText().length() == 0 ) {
208             // TODO - We might be able to remove this. I'm more certain about
209             // this than the one for character data
210             throw new Fault(
211                 "Appending empty comment"
212             ).mishap();
213         }
214         // NOTE - We allow XML comments to behave like format strings, so we
215         // must run each one through the format function.
216         this.pushChildElement(
217             new XmlComment( token.getCommentText() )
218         );
219     }
220 
221     /**
222      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.DTDToken)
223      */
224     public void visit( final DTDToken token ) {
225         throw(
226             Alerts.unimplemented(
227                 "Document type declarations cannot be compiled at the moment, sorry!"
228             ).decorate( token ).mishap()
229         );
230     }
231 
232     /**
233      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.ElementDeclToken)
234      */
235     public void visit( final ElementDeclToken token ) {
236         throw(
237             Alerts.unimplemented(
238                 "Element declarations cannot be compiled at the moment, sorry!"
239             ).decorate( token ).mishap()
240         );
241     }
242 
243     /**
244      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.EmptyElementToken)
245      */
246     public void visit( final EmptyElementToken token ) {
247         // TODO - Keep it simple for now, we can make it a tiny bit faster soon
248         this.visit( (StartTagToken) token );
249         // The above visit will change the token visitor we should use for the
250         // end tag, so the following line is different from the previous one
251         this.stringToXMLFunction.tokenVisitor.visit( (EndTagToken)token );
252     }
253 
254     /**
255      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.EndTagToken)
256      */
257     public void visit( final EndTagToken token ) {
258         throw new Alert(
259             "The start tag for this element appears to be missing"
260         ).decorate( token ).mishap();
261     }
262 
263     /**
264      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.EntityDeclToken)
265      */
266     public void visit( final EntityDeclToken token ) {
267         throw(
268             Alerts.unimplemented(
269                 "Entity declarations cannot be compiled at the moment, sorry!"
270             ).decorate( token ).mishap()
271         );
272     }
273 
274     /**
275      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.NotationDeclToken)
276      */
277     public void visit( final NotationDeclToken token ) {
278         throw(
279             Alerts.unimplemented(
280                 "Notation declarations cannot be compiled at the moment, sorry!"
281             ).decorate( token ).mishap()
282         );
283     }
284 
285     /**
286      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.PIToken)
287      */
288     public void visit( final PIToken token ) {
289         throw(
290             Alerts.unimplemented(
291                 "Processing instructions cannot be compiled at the moment, sorry!"
292             ).decorate( token ).mishap()
293         );
294     }
295 
296     /**
297      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.StartTagToken)
298      */
299     public void visit( final StartTagToken token ) {
300         // Start a new element
301         this.stringToXMLFunction.tokenVisitor = new ElementTokenVisitor( this.stringToXMLFunction, this.stringToXMLFunction.tokenVisitor, token );
302     }
303 
304     /**
305      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.XmlDeclToken)
306      */
307     public void visit( final XmlDeclToken token ) {
308         // Ignore the XML declaration, we don't really care about it
309     }
310 
311 }