1 /* WellFormednessFilter.java --
2 Copyright (C) 1999,2000,2001 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package gnu
.xml
.pipeline
;
40 import java
.util
.EmptyStackException
;
41 import java
.util
.Stack
;
43 import org
.xml
.sax
.Attributes
;
44 import org
.xml
.sax
.ErrorHandler
;
45 import org
.xml
.sax
.Locator
;
46 import org
.xml
.sax
.SAXException
;
47 import org
.xml
.sax
.SAXParseException
;
50 * This filter reports fatal exceptions in the case of event streams that
51 * are not well formed. The rules currently tested include: <ul>
53 * <li>setDocumentLocator ... may be called only before startDocument
55 * <li>startDocument/endDocument ... must be paired, and all other
56 * calls (except setDocumentLocator) must be nested within these.
58 * <li>startElement/endElement ... must be correctly paired, and
59 * may never appear within CDATA sections.
61 * <li>comment ... can't contain "--"
63 * <li>character data ... can't contain "]]>"
65 * <li>whitespace ... can't contain CR
67 * <li>whitespace and character data must be within an element
69 * <li>processing instruction ... can't contain "?>" or CR
71 * <li>startCDATA/endCDATA ... must be correctly paired.
75 * <p> Other checks for event stream correctness may be provided in
76 * the future. For example, insisting that
77 * entity boundaries nest correctly,
78 * namespace scopes nest correctly,
79 * namespace values never contain relative URIs,
80 * attributes don't have "<" characters;
83 * @author David Brownell
85 public final class WellFormednessFilter
extends EventFilter
87 private boolean startedDoc
;
88 private Stack elementStack
= new Stack ();
89 private boolean startedCDATA
;
90 private String dtdState
= "before";
94 * Swallows all events after performing well formedness checks.
96 // constructor used by PipelineFactory
97 public WellFormednessFilter ()
102 * Passes events through to the specified consumer, after first
105 // constructor used by PipelineFactory
106 public WellFormednessFilter (EventConsumer consumer
)
110 setContentHandler (this);
111 setDTDHandler (this);
114 setProperty (LEXICAL_HANDLER
, this);
115 } catch (SAXException e
) { /* can't happen */ }
119 * Resets state as if any preceding event stream was well formed.
120 * Particularly useful if it ended through some sort of error,
121 * and the endDocument call wasn't made.
126 startedCDATA
= false;
127 elementStack
.removeAllElements ();
131 private SAXParseException
getException (String message
)
134 Locator locator
= getDocumentLocator ();
137 return new SAXParseException (message
, null, null, -1, -1);
139 return new SAXParseException (message
, locator
);
142 private void fatalError (String message
)
145 SAXParseException e
= getException (message
);
146 ErrorHandler handler
= getErrorHandler ();
149 handler
.fatalError (e
);
154 * Throws an exception when called after startDocument.
156 * @param locator the locator, to be used in error reporting or relative
159 * @exception IllegalStateException when called after the document
160 * has already been started
162 public void setDocumentLocator (Locator locator
)
165 throw new IllegalStateException (
166 "setDocumentLocator called after startDocument");
167 super.setDocumentLocator (locator
);
170 public void startDocument () throws SAXException
173 fatalError ("startDocument called more than once");
175 startedCDATA
= false;
176 elementStack
.removeAllElements ();
177 super.startDocument ();
180 public void startElement (
181 String uri
, String localName
,
182 String qName
, Attributes atts
183 ) throws SAXException
186 fatalError ("callback outside of document?");
187 if ("inside".equals (dtdState
))
188 fatalError ("element inside DTD?");
192 fatalError ("element inside CDATA section");
193 if (qName
== null || "".equals (qName
))
194 fatalError ("startElement name missing");
195 elementStack
.push (qName
);
196 super.startElement (uri
, localName
, qName
, atts
);
199 public void endElement (String uri
, String localName
, String qName
)
203 fatalError ("callback outside of document?");
205 fatalError ("element inside CDATA section");
206 if (qName
== null || "".equals (qName
))
207 fatalError ("endElement name missing");
210 String top
= (String
) elementStack
.pop ();
212 if (!qName
.equals (top
))
213 fatalError ("<" + top
+ " ...>...</" + qName
+ ">");
214 // XXX could record/test namespace info
215 } catch (EmptyStackException e
) {
216 fatalError ("endElement without startElement: </" + qName
+ ">");
218 super.endElement (uri
, localName
, qName
);
221 public void endDocument () throws SAXException
224 fatalError ("callback outside of document?");
227 super.endDocument ();
231 public void startDTD (String root
, String publicId
, String systemId
)
235 fatalError ("callback outside of document?");
236 if ("before" != dtdState
)
237 fatalError ("two DTDs?");
238 if (!elementStack
.empty ())
239 fatalError ("DTD must precede root element");
241 super.startDTD (root
, publicId
, systemId
);
244 public void notationDecl (String name
, String publicId
, String systemId
)
247 // FIXME: not all parsers will report startDTD() ...
248 // we'd rather insist we're "inside".
249 if ("after" == dtdState
)
250 fatalError ("not inside DTD");
251 super.notationDecl (name
, publicId
, systemId
);
254 public void unparsedEntityDecl (String name
,
255 String publicId
, String systemId
, String notationName
)
258 // FIXME: not all parsers will report startDTD() ...
259 // we'd rather insist we're "inside".
260 if ("after" == dtdState
)
261 fatalError ("not inside DTD");
262 super.unparsedEntityDecl (name
, publicId
, systemId
, notationName
);
265 // FIXME: add the four DeclHandler calls too
267 public void endDTD ()
271 fatalError ("callback outside of document?");
272 if ("inside" != dtdState
)
273 fatalError ("DTD ends without start?");
278 public void characters (char ch
[], int start
, int length
)
281 int here
= start
, end
= start
+ length
;
282 if (elementStack
.empty ())
283 fatalError ("characters must be in an element");
285 if (ch
[here
++] != ']')
287 if (here
== end
) // potential problem ...
289 if (ch
[here
++] != ']')
291 if (here
== end
) // potential problem ...
293 if (ch
[here
++] == '>')
294 fatalError ("character data can't contain \"]]>\"");
296 super.characters (ch
, start
, length
);
299 public void ignorableWhitespace (char ch
[], int start
, int length
)
302 int here
= start
, end
= start
+ length
;
303 if (elementStack
.empty ())
304 fatalError ("characters must be in an element");
306 if (ch
[here
++] == '\r')
307 fatalError ("whitespace can't contain CR");
309 super.ignorableWhitespace (ch
, start
, length
);
312 public void processingInstruction (String target
, String data
)
315 if (data
.indexOf ('\r') > 0)
316 fatalError ("PIs can't contain CR");
317 if (data
.indexOf ("?>") > 0)
318 fatalError ("PIs can't contain \"?>\"");
321 public void comment (char ch
[], int start
, int length
)
325 fatalError ("callback outside of document?");
327 fatalError ("comments can't nest in CDATA");
328 int here
= start
, end
= start
+ length
;
330 if (ch
[here
] == '\r')
331 fatalError ("comments can't contain CR");
332 if (ch
[here
++] != '-')
335 fatalError ("comments can't end with \"--->\"");
336 if (ch
[here
++] == '-')
337 fatalError ("comments can't contain \"--\"");
339 super.comment (ch
, start
, length
);
342 public void startCDATA ()
346 fatalError ("callback outside of document?");
348 fatalError ("CDATA starts can't nest");
353 public void endCDATA ()
357 fatalError ("callback outside of document?");
359 fatalError ("CDATA end without start?");
360 startedCDATA
= false;