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.loaders;
22  
23  import org.millscript.commons.alert.alerts.Fault;
24  import org.millscript.commons.util.IList;
25  import org.millscript.commons.util.list.ELinkedList;
26  import org.millscript.commons.xml.api.token.AttListDeclToken;
27  import org.millscript.commons.xml.api.token.CharDataToken;
28  import org.millscript.commons.xml.api.token.CommentToken;
29  import org.millscript.commons.xml.api.token.DTDToken;
30  import org.millscript.commons.xml.api.token.ElementDeclToken;
31  import org.millscript.commons.xml.api.token.EmptyElementToken;
32  import org.millscript.commons.xml.api.token.EndTagToken;
33  import org.millscript.commons.xml.api.token.EntityDeclToken;
34  import org.millscript.commons.xml.api.token.NotationDeclToken;
35  import org.millscript.commons.xml.api.token.PIToken;
36  import org.millscript.commons.xml.api.token.StartTagToken;
37  import org.millscript.commons.xml.api.token.TokenVisitor;
38  import org.millscript.commons.xml.api.token.XmlDeclToken;
39  import org.millscript.millscript.alert.Alerts;
40  import org.millscript.millscript.datatypes.XmlComment;
41  import org.millscript.millscript.datatypes.XmlElement;
42  
43  /**
44   * This class provides validation and handling logic for loading an XML
45   * document into a static XML object.
46   */
47  public class XMLLoaderTokenVisitor implements TokenVisitor {
48  
49      /**
50       * This body expression for the element.
51       */
52      ELinkedList< Object > body = new ELinkedList< Object >();
53  
54      /**
55       * The parent token visitor we should return to once this one has finished
56       * what it is supposed to do.
57       */
58      final XMLLoaderTokenVisitor parentTokenVisitor;
59  
60      /**
61       * The start tag that began the section this token visitor represents.
62       */
63      final StartTagToken startTagToken;
64  
65      /**
66       * The loader that is controlling the XML parse.
67       */
68      final XMLLoader xmlLoader;
69  
70      /**
71       * This class is used when visiting an XML element and it handles the
72       * tokens visited in the elements content.
73       */
74      private class ElementTokenVisitor extends XMLLoaderTokenVisitor {
75  
76          /**
77           * Constructs a new element token visitor, with the specified
78           * controlling loader, parent token visitor and start tag.
79           *
80           * @param xml   the controlling loader
81           * @param ptv   the parent token visitor
82           * @param stt   the start tag token
83           */
84          public ElementTokenVisitor( final XMLLoader xml, final XMLLoaderTokenVisitor ptv, final StartTagToken stt ) {
85              super( xml, ptv, stt );
86          }
87  
88          /**
89           * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.CharDataToken)
90           */
91          @Override
92          public void visit( final CharDataToken token ) {
93              if ( token.getCharData().length() == 0 ) {
94                  // TODO - We might be able to remove this
95                  throw new Fault(
96                      "Appending empty character data"
97                  ).mishap();
98              }
99              // TODO - This needs to respect whitespace, CDATA and plain text
100             this.pushChildElement(
101                 token.getCharData()
102             );
103         }
104 
105         /**
106          * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.CommentToken)
107          */
108         @Override
109         public void visit( final CommentToken token ) {
110             if ( token.getCommentText().length() == 0 ) {
111                 // TODO - We might be able to remove this. I'm more certain about
112                 // this than the one for character data
113                 throw new Fault(
114                     "Appending empty comment"
115                 ).mishap();
116             }
117             // NOTE - We allow XML comments to behave like format strings, so we
118             // must run each one through the format function.
119             this.pushChildElement(
120                 new XmlComment( token.getCommentText() )
121             );
122         }
123 
124         /**
125          * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.EndTagToken)
126          */
127         @Override
128         public void visit( final EndTagToken token ) {
129             // Make the Expr for this XML element and push it into the parent
130             // elements body.
131             this.parentTokenVisitor.pushChildElement(
132                 new XmlElement(
133                     this.startTagToken.getName(),
134                     this.startTagToken.getAttributes(),
135                     body.toArray()
136                 )
137             );
138             // End this element
139             this.xmlLoader.tokenVisitor = this.parentTokenVisitor;
140         }
141 
142     }
143 
144     /**
145      * Constructs a new XML loader token visitor, with the specified
146      * controlling loader, parent token visitor and start tag.
147      *
148      * @param xml   the controlling loader
149      * @param ptv   the parent token visitor
150      * @param stt   the start tag token
151      */
152     public XMLLoaderTokenVisitor( final XMLLoader xml, final XMLLoaderTokenVisitor ptv, final StartTagToken stt ) {
153         this.parentTokenVisitor = ptv;
154         this.startTagToken = stt;
155         this.xmlLoader = xml;
156     }
157 
158     /**
159      * Returns the body expression for this element.
160      *
161      * @return  an Expr for this elements body
162      */
163     IList getBody() {
164         return this.body;
165     }
166 
167     /**
168      * Pushes the expression as part of this elements body.
169      *
170      * @param o the next body element for this element
171      */
172     void pushChildElement( final Object o ) {
173         this.body.addLast( o );
174     }
175 
176     /**
177      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.AttListDeclToken)
178      */
179     public void visit( final AttListDeclToken token ) {
180         // We don't handle processing instructions
181         throw(
182             Alerts.unimplemented(
183                 "Attribute list declarations cannot be compiled at the moment, sorry!"
184             ).decorate( token ).mishap()
185         );
186     }
187 
188     /**
189      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.CharDataToken)
190      */
191     public void visit( final CharDataToken token ) {
192         // TODO - In the future we might want to return this whitespace as part
193         // of the XML document
194         if ( ! token.isIgnorableWhitespace() ) {
195             throw(
196                 Alerts.unimplemented(
197                     "All character data must be within an element at the moment, sorry!"
198                 ).decorate( token ).mishap()
199             );
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         throw(
208             Alerts.unimplemented(
209                 "Comments must be within an element at the moment, sorry!"
210             ).decorate( token ).mishap()
211         );
212     }
213 
214     /**
215      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.DTDToken)
216      */
217     public void visit( final DTDToken token ) {
218         throw(
219             Alerts.unimplemented(
220                 "Document type declarations cannot be compiled at the moment, sorry!"
221             ).decorate( token ).mishap()
222         );
223     }
224 
225     /**
226      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.ElementDeclToken)
227      */
228     public void visit( final ElementDeclToken token ) {
229         throw(
230             Alerts.unimplemented(
231                 "Element declarations cannot be compiled at the moment, sorry!"
232             ).decorate( token ).mishap()
233         );
234     }
235 
236     /**
237      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.EmptyElementToken)
238      */
239     public void visit( final EmptyElementToken token ) {
240         // TODO - Keep it simple for now, we can make it a tiny bit faster soon
241         this.visit( (StartTagToken) token );
242         // The above visit will change the token visitor we should use for the
243         // end tag, so the following line is different from the previous one
244         this.xmlLoader.tokenVisitor.visit( (EndTagToken)token );
245     }
246 
247     /**
248      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.EndTagToken)
249      */
250     public void visit( final EndTagToken token ) {
251         throw new Fault(
252             "EndTagToken should never be reported on XMLLoaderTokenVisitor"
253         ).mishap();
254     }
255 
256     /**
257      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.EntityDeclToken)
258      */
259     public void visit( final EntityDeclToken token ) {
260         throw(
261             Alerts.unimplemented(
262                 "Entity declarations cannot be compiled at the moment, sorry!"
263             ).decorate( token ).mishap()
264         );
265     }
266 
267     /**
268      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.NotationDeclToken)
269      */
270     public void visit( final NotationDeclToken token ) {
271         throw(
272             Alerts.unimplemented(
273                 "Notation declarations cannot be compiled at the moment, sorry!"
274             ).decorate( token ).mishap()
275         );
276     }
277 
278     /**
279      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.PIToken)
280      */
281     public void visit( final PIToken token ) {
282         throw(
283             Alerts.unimplemented(
284                 "Processing instructions cannot be compiled at the moment, sorry!"
285             ).decorate( token ).mishap()
286         );
287     }
288 
289     /**
290      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.StartTagToken)
291      */
292     public void visit( final StartTagToken token ) {
293         // Start a new element
294         this.xmlLoader.tokenVisitor = new ElementTokenVisitor( this.xmlLoader, this.xmlLoader.tokenVisitor, token );
295     }
296 
297     /**
298      * @see org.millscript.commons.xml.api.token.TokenVisitor#visit(org.millscript.commons.xml.api.token.XmlDeclToken)
299      */
300     public void visit( final XmlDeclToken token ) {
301         // Ignore the XML declaration, we don't really care about it
302     }
303 
304 }