KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openlaszlo > compiler > Parser


1 /* *****************************************************************************
2  * Parser.java
3 * ****************************************************************************/

4
5 /* J_LZ_COPYRIGHT_BEGIN *******************************************************
6 * Copyright 2001-2004 Laszlo Systems, Inc. All Rights Reserved. *
7 * Use is subject to license terms. *
8 * J_LZ_COPYRIGHT_END *********************************************************/

9
10 package org.openlaszlo.compiler;
11 import java.io.*;
12 import java.lang.*;
13 import java.util.*;
14 import org.apache.log4j.Logger;
15 import org.iso_relax.verifier.*;
16 import org.jdom.Attribute;
17 import org.jdom.Document;
18 import org.jdom.Element;
19 import org.jdom.JDOMException;
20 import org.jdom.Namespace;
21 import org.jdom.input.JDOMFactory;
22 import org.jdom.input.SAXBuilder;
23 import org.jdom.input.SAXHandler;
24 import org.jdom.output.SAXOutputter;
25 import org.jdom.output.XMLOutputter;
26 import org.xml.sax.InputSource JavaDoc;
27 import org.xml.sax.SAXException JavaDoc;
28 import org.xml.sax.XMLFilter JavaDoc;
29 import org.xml.sax.helpers.XMLFilterImpl JavaDoc;
30 import org.apache.commons.collections.LRUMap;
31 import org.openlaszlo.server.*;
32 import org.openlaszlo.utils.*;
33 import org.openlaszlo.xml.internal.*;
34
35 import javax.xml.transform.TransformerConfigurationException JavaDoc;
36 import javax.xml.transform.TransformerFactory JavaDoc;
37 import javax.xml.transform.sax.SAXResult JavaDoc;
38 import javax.xml.transform.sax.SAXTransformerFactory JavaDoc;
39 import javax.xml.transform.sax.TransformerHandler JavaDoc;
40 import javax.xml.transform.stream.StreamSource JavaDoc;
41
42 import org.xml.sax.SAXException JavaDoc;
43
44 /** Parses and validates an XML file. XML elements are annotated with
45  * their source locations.
46  *
47  * A new parser should be used for each compilation, but shared across
48  * all XML file reads within a compilation. This assures that the
49  * parser caches are active during compilation, but are not reused
50  * for subsequent compilations when the file may have been modified.
51  */

52 public class Parser {
53     private static Logger mLogger = Logger.getLogger(Parser.class);
54     private static Logger mPostTransformationLogger =
55         Logger.getLogger("postTransformation");
56     private static Logger mPreValidationLogger =
57         Logger.getLogger("preValidation");
58     static public Namespace sNamespace =
59         Namespace.getNamespace("http://www.laszlosystems.com/2003/05/lzx");
60     
61     protected FileResolver resolver = FileResolver.DEFAULT_FILE_RESOLVER;
62     
63     /** Map(File, Document) */
64     protected final Map fileCache = new HashMap();
65     /** List<String>. Pathnames in messages are reported relative to
66      * one of these. */

67     List basePathnames = new Vector();
68
69     // Stylesheet templates and generators for updating old
70
// namespaces, and adding namespace declarations.
71
private static javax.xml.transform.Templates JavaDoc sPreprocessorTemplates;
72     private static SAXTransformerFactory JavaDoc sSaxFactory;
73     private static long sPreprocessorLastModified;
74     
75     protected static synchronized SAXTransformerFactory JavaDoc getSaxFactory() {
76         String JavaDoc stylePath = LPS.HOME().replace('\\', '/') + "/" +
77             "WEB-INF" + "/" +
78             "lps" + "/" +
79             "schema" + "/" + "preprocess.xsl";
80         File styleFile = new File(stylePath);
81         long lastModified = styleFile.lastModified();
82         
83         if (sSaxFactory != null && sPreprocessorLastModified == lastModified)
84             return sSaxFactory;
85         
86         // name the class instead of using
87
// TransformerFactory.newInstance(), to insure that we get
88
// saxon and thereby work around a failure on Tomcat 5 w/ jdk
89
// 1.4.2 Linux, and w/ Sun 1.4.1_05
90
javax.xml.transform.TransformerFactory JavaDoc factory =
91             new com.icl.saxon.TransformerFactoryImpl();
92         
93         javax.xml.transform.Templates JavaDoc templates = null;
94         try {
95             templates = factory.newTemplates(
96                 new StreamSource JavaDoc("file:///" + stylePath));
97         } catch (TransformerConfigurationException JavaDoc e) {
98             throw new ChainedException(e);
99         }
100         
101         if (!factory.getFeature(SAXTransformerFactory.FEATURE))
102             throw new RuntimeException JavaDoc(
103                 "TransformerFactory doesn't implement SAXTransformerFactory");
104         
105         SAXTransformerFactory JavaDoc saxFactory = (SAXTransformerFactory JavaDoc) factory;
106         
107         sSaxFactory = saxFactory;
108         sPreprocessorTemplates = templates;
109         sPreprocessorLastModified = lastModified;
110         return saxFactory;
111     }
112     
113     public Parser() {
114     }
115     
116     public void setResolver(FileResolver resolver) {
117         this.resolver = resolver;
118     }
119     
120     /** Returns the pathname to use in user error messages. This is
121      * the shortest pathname relative to a directory on the search
122      * path for this application.
123      */

124     public String JavaDoc getUserPathname(String JavaDoc pathname) {
125         String JavaDoc sourceDir = new File(pathname).getParent();
126         if (sourceDir == null)
127             sourceDir = "";
128         sourceDir = sourceDir.replace(File.separatorChar, '/');
129         String JavaDoc best = pathname.replace(File.separatorChar, '/');
130         int bestLength = StringUtils.split(best, "/").length;
131         for (Iterator iter = basePathnames.iterator(); iter.hasNext(); ) {
132             String JavaDoc item = (String JavaDoc) iter.next();
133             String JavaDoc base = item.replace(File.separatorChar, '/');
134             try {
135                 String JavaDoc candidate = FileUtils.adjustRelativePath(
136                     new File(pathname).getName(),
137                     base,
138                     sourceDir);
139                 int candidateLength =
140                     StringUtils.split(candidate, "/").length;
141                 if (candidateLength < bestLength) {
142                     best = candidate;
143                     bestLength = candidateLength;
144                 }
145             } catch (FileUtils.RelativizationError e) {
146                 // If it can't be relativized, it simply doesn't produce
147
// a candidate, and we do nothing.
148
}
149         }
150         return best;
151     }
152     
153     /** Reads an XML document and adds source location information to
154      * the elements. */

155     protected Document read(File file)
156         throws JDOMException, IOException
157     {
158         // See if we've already read the file. This is an
159
// optimization, and also assures that the same content is
160
// used across passes. We don't need to (and shouldn't) check
161
// the date, since the cache is an instance variable, and each
162
// compiler invocation uses a fresh parser.
163
File key = file.getCanonicalFile();
164         if (fileCache.containsKey(key)) {
165             return (Document) fileCache.get(key);
166         }
167         
168         // Use a custom subclass of SAXBuilder to build a JDOM tree
169
// containing custom Elements which contain source file
170
// location info (ElementWithLocationInfo).
171

172         // The following variables are used to add source location
173
// that reflects the input name, while the system identifier
174
// has been made absolute.
175
final String JavaDoc pathname = file.getPath();
176         final String JavaDoc messagePathname = getUserPathname(pathname);
177
178         // This is a ContentHandler which adds source location info to
179
// our own custom class of jdom.Element
180
class SourceLocatorHandler extends org.jdom.input.SAXHandler {
181             org.xml.sax.Locator JavaDoc locator;
182             int startLineNumber;
183             int startColumnNumber;
184             Element currentElement;
185             
186             SourceLocatorHandler() throws IOException {}
187             
188             SourceLocatorHandler(JDOMFactory factory)
189                 throws IOException
190             {
191                 super(factory);
192             }
193
194             public void characters(char[] ch, int start, int length)
195                 throws SAXException JavaDoc
196             {
197                 startLineNumber = locator.getLineNumber();
198                 startColumnNumber = locator.getColumnNumber();
199                 super.characters(ch, start, length);
200             }
201
202             public void endElement(String JavaDoc namespaceURI, String JavaDoc localName,
203                                    String JavaDoc qName)
204                 throws SAXException JavaDoc
205             {
206                 // You can only call this.getCurrentElement() before
207
// super.endElement
208

209                 // Save source location info for reporting compilation errors
210
saveEndLocation(this.getCurrentElement(),
211                                 pathname,
212                                 messagePathname,
213                                 locator.getLineNumber(),
214                                 locator.getColumnNumber());
215
216                 super.endElement(namespaceURI, localName, qName);
217             }
218
219             public void setDocumentLocator(org.xml.sax.Locator JavaDoc locator) {
220                 this.locator = locator;
221             }
222
223             public void startElement(String JavaDoc namespaceURI, String JavaDoc localName,
224                                      String JavaDoc qName,
225                                      org.xml.sax.Attributes JavaDoc atts)
226                 throws SAXException JavaDoc
227             {
228                 super.startElement(namespaceURI, localName, qName, atts);
229                     
230                 // You can only call this.getCurrentElement() after
231
// super.startElement
232

233                 // Save source location info for reporting compilation errors
234
saveStartLocation(this.getCurrentElement(),
235                                   pathname,
236                                   messagePathname,
237                                   locator.getLineNumber(),
238                                   locator.getColumnNumber());
239             }
240         }
241
242         /* We need a SAXBuilder that uses our custom Factory and
243            ContentHandler classes, but the stock
244            org.jdom.input.SAXBuilder has no API for setting which
245            ContentHandler is used.
246
247            To get what we need, we create a subclass of SAXBuilder
248            and override the createContentHandler method to
249            instantiate our custom handler with our custom factory .
250         */

251         class SourceLocatorSAXBuilder extends SAXBuilder {
252             SourceLocatorSAXBuilder (String JavaDoc saxDriverClass) {
253                 super(saxDriverClass);
254             }
255
256             SourceLocatorSAXBuilder () {
257                 super();
258             }
259
260             // We need to create our own special contentHandler,
261
// and you *must* pass the factory to the SaxHandler
262
// constructor, or else you get a default JDOM
263
// factory, which is not what you want!
264
protected org.jdom.input.SAXHandler createContentHandler() {
265                 try {
266                     return new SourceLocatorHandler(factory);
267                 } catch (IOException e) {
268                     throw new ChainedException(e);
269                 }
270             }
271         }
272
273         //SAXBuilder builder = new SourceLocatorSAXBuilder("org.apache.crimson.parser.XMLReaderImpl");
274
SAXBuilder builder = new SourceLocatorSAXBuilder();
275         builder.setFactory(new SourceLocatorJDOMFactory());
276
277         // ignore DOCTYPE declarations TODO [2004-25-05 ows]: parse
278
// entity references from internal declarations, and either
279
// warn about external declarations or add them to the
280
// dependency information. If the latter, use a library to
281
// cache and resolve non-file sources against a catalog file.
282
builder.setEntityResolver(
283             new org.xml.sax.helpers.DefaultHandler JavaDoc() {
284                 public InputSource JavaDoc resolveEntity(String JavaDoc publicId, String JavaDoc systemId) {
285                     return new InputSource JavaDoc(new StringReader(""));
286                 }
287             });
288         
289         // Parse the document
290
java.io.Reader JavaDoc reader = FileUtils.makeXMLReaderForFile(
291             pathname, "UTF-8");
292         InputSource JavaDoc source = new InputSource JavaDoc(reader);
293         source.setPublicId(messagePathname);
294         source.setSystemId(pathname);
295         Document doc = builder.build(source);
296         reader.close();
297         fileCache.put(key, doc);
298         return doc;
299     }
300     
301     protected Document preprocess(final Document sourceDoc)
302         throws java.io.IOException JavaDoc, org.jdom.JDOMException
303     {
304         // Fills location information from the metasource attribute.
305
class SourceLocatorHandler extends org.jdom.input.SAXHandler {
306             SourceLocatorHandler() throws IOException {}
307
308             SourceLocatorHandler(JDOMFactory factory)
309                 throws IOException
310             {
311                 super(factory);
312             }
313
314             public void endElement(String JavaDoc namespaceURI, String JavaDoc localName,
315                                    String JavaDoc qName)
316                 throws SAXException JavaDoc
317             {
318                 ElementWithLocationInfo element =
319                     (ElementWithLocationInfo) this.getCurrentElement();
320                 Attribute attr = element.getAttribute(
321                     SourceLocatorSAXOutputter.SOURCEINFO_ATTRIBUTE_NAME);
322                 if (attr != null) {
323                     SourceLocator locator = SourceLocator.fromString(attr.getValue());
324                     element.initSourceLocator(locator);
325                     element.removeAttribute(attr);
326                 }
327                 super.endElement(namespaceURI, localName, qName);
328             }
329         }
330
331         // Create a transformer that implements the 'preprocess'
332
// transformation.
333
TransformerHandler JavaDoc handler;
334         try {
335             handler = getSaxFactory().
336                 newTransformerHandler(sPreprocessorTemplates);
337         } catch (TransformerConfigurationException JavaDoc e) {
338             throw new ChainedException(e);
339         }
340         
341         SAXHandler resultHandler =
342             new SourceLocatorHandler(new SourceLocatorJDOMFactory());
343         SAXResult JavaDoc result =
344             new javax.xml.transform.sax.SAXResult JavaDoc(resultHandler);
345         handler.setResult(result);
346         
347         SourceLocatorSAXOutputter outputter = new SourceLocatorSAXOutputter();
348         outputter.setWriteMetaData(true);
349         outputter.setContentHandler(handler);
350         outputter.output(sourceDoc);
351         
352         Document resultDoc = resultHandler.getDocument();
353         
354         if (mPostTransformationLogger.isDebugEnabled()) {
355             org.jdom.output.XMLOutputter xmloutputter =
356                 new org.jdom.output.XMLOutputter();
357             mPostTransformationLogger.debug(
358                 xmloutputter.outputString(resultDoc));
359         }
360         
361         return resultDoc;
362     }
363     
364     /** Reads the XML document, and modifies it by replacing each
365      * include element by the root of the expanded document named by
366      * the include's href attribute. Pathames are resolved relative to
367      * the element's source location. If a pathname resolves to the
368      * current file or a file in the set that's passed as the second
369      * argument, a compilation error is thrown. A copy of the set of
370      * pathnames that additionally includes the current file is passed
371      * to each recursive call. (Since the set has stacklike behavior,
372      * a cons list would be appropriate, but I don't think Java has
373      * one.)
374      *
375      * This is a helper function for parse(), which is factored out
376      * so that expandIncludes can be apply it recursively to included
377      * files. */

378     protected Document readExpanded(File file, Set currentFiles)
379         throws IOException, JDOMException
380     {
381         File key = file.getCanonicalFile();
382         if (currentFiles.contains(key)) {
383             throw new CompilationError(file + " includes itself.");
384         }
385         Set newCurrentFiles = new HashSet(currentFiles);
386         newCurrentFiles.add(key);
387         Document doc = read(file);
388         Element root = doc.getRootElement();
389         expandIncludes(root, newCurrentFiles);
390         return doc;
391     }
392     
393     /** Replaces include statements by the content of the included
394      * file.
395      *
396      * This is a helper function for readExpanded, which is factored
397      * out so that readExpanded can apply it recursively to included
398      * files. */

399     protected void expandIncludes(Element element, Set currentFiles)
400         throws IOException, JDOMException
401     {
402         // Do this in two passes: collect the replacements, and then
403
// make them. This is necessary because it's impossible to
404
// modify a list that's being iterated.
405
//
406
// Replacements is a map of Element -> Maybe Element. The key
407
// is an Element instead of an index into the content, because
408
// deletions will alter the index. Replacements could be a
409
// list [(Element, Maybe Element)], but since there's no Pair
410
// type the map gets to use prefab types.
411
Map replacements = new HashMap();
412         for (Iterator iter = element.getChildren().iterator();
413              iter.hasNext(); ) {
414             Element child = (Element) iter.next();
415             if (child.getName().equals("include")) {
416                 String JavaDoc base = new File(getSourcePathname(element)).getParent();
417                 String JavaDoc type = XMLUtils.getAttributeValue(child, "type", "xml");
418                 String JavaDoc href = child.getAttributeValue("href");
419                 if (href == null) {
420                     throw new CompilationError("The <include> element requires an \"href\" attribute.", child);
421                 }
422                 File target = resolver.resolve(href, base);
423                 // An include whose href is a directory implicitly
424
// includes the library.lzx file in that directory.
425
if (type.equals("xml") && target.isDirectory()) {
426                     target = new File(target, "library.lzx");
427                 }
428                 if (type.equals("text")) {
429                     replacements.put(child,
430                                      new org.jdom.Text(
431                                          FileUtils.readFileString(target, "UTF-8")));
432                 } else if (type.equals("xml")) {
433                     // Pass the target, not the key, so that source
434
// location information is correct.
435
Document doc = read(target);
436                     // If it's a top-level library, the compiler will
437
// process it during the compilation phase. In
438
// that case change it to a <library HREF=""/>
439
// element, where the href is the <include>'s
440
// href, so that LibraryCompiler can resolve it.
441
//
442
// Otherwise replace the <include> element with
443
// the included file.
444
if (CompilerUtils.isAtToplevel(child) &&
445                         LibraryCompiler.isElement(doc.getRootElement())) {
446                         // Modify the existing element instead of
447
// creating a new one, so that source location
448
// information is preserved.
449
child.setName(doc.getRootElement().getName());
450                     } else {
451                         doc = readExpanded(target, currentFiles);
452                         replacements.put(child, doc.getRootElement());
453                         // The replacement below destroys this tree,
454
// so insure that the next call to read gets a
455
// fresh one.
456
File key = target.getCanonicalFile();
457                         fileCache.remove(key);
458                     }
459                 } else {
460                     throw new CompilationError("include type must be xml or text");
461                 }
462             } else {
463                 expandIncludes(child, currentFiles);
464             }
465         }
466         // Now make the replacements.
467
List contents = element.getContent();
468         for (Iterator iter = replacements.entrySet().iterator();
469              iter.hasNext(); ) {
470             Map.Entry entry = (Map.Entry) iter.next();
471             int index = contents.indexOf(entry.getKey());
472             Object JavaDoc value = entry.getValue();
473             contents.set(index, value);
474         }
475     }
476
477     /** Reads an XML file, expands includes, and validates it.
478      */

479     public Document parse(File file)
480       throws CompilationError
481     {
482         String JavaDoc pathname = file.getPath();
483         try {
484             Document doc = readExpanded(file, new HashSet());
485             // Apply the stylesheet
486
doc = preprocess(doc);
487             return doc;
488         } catch (IOException e) {
489             CompilationError ce = new CompilationError(e);
490             ce.initPathname(pathname);
491             throw ce;
492         } catch (JDOMException e) {
493             String JavaDoc solution = SolutionMessages.findSolution(e.getMessage(), SolutionMessages.PARSER);
494             CompilationError ce = new CompilationError(e, solution);
495             throw ce;
496
497         }
498     }
499     
500     /** Cache of compiled schema verifiers. Type Map<String,
501      * org.iso_relax.verifier.Schema>, where the key is the string
502      * serialization of the schema. */

503     private static LRUMap mSchemaCache = new LRUMap();
504     
505     /** Validates an XML file against the Laszlo schema. Errors are
506      * thrown as the text of a CompilationError.
507      *
508      * @param doc DOM of the source code to be validated
509      * @param pathname pathname of the source file, for error reporting
510      * @param env the compilation environment
511      * @throws CompilationError if a validation error occurs
512      */

513     public static void validate(Document doc, String JavaDoc pathname,
514                                 final CompilationEnvironment env)
515     {
516         final CompilationErrorHandler errorHandler = env.getErrorHandler();
517         final ViewSchema viewschema = env.getSchema();
518
519         if (mPreValidationLogger.isDebugEnabled()) {
520             org.jdom.output.XMLOutputter debugOutputter =
521                 new org.jdom.output.XMLOutputter();
522             mPreValidationLogger.debug("Pathname: " + pathname);
523             mPreValidationLogger.debug(debugOutputter.outputString(doc));
524         }
525         try {
526             org.iso_relax.verifier.Schema schema;
527
528             // Look up a cached version of the compiled verifier for this env
529
Verifier verifier = env.getCachedVerifier();
530
531             if (verifier == null) {
532                 // System.err.println("env verifier cache miss: " + pathname);
533

534                 // Look up schema XML signature in common pool of compiled verifiers
535
Object JavaDoc value;
536                 String JavaDoc schemaXMLstring = new XMLOutputter().outputString(viewschema.getSchemaDOM());
537                 String JavaDoc key = schemaXMLstring;
538
539                 synchronized (mSchemaCache) {
540                     schema = (org.iso_relax.verifier.Schema) mSchemaCache.get(key);
541                 }
542                 
543                 if (schema == null) {
544                     StringReader schemaXMLSource = new StringReader(schemaXMLstring);
545                     InputSource JavaDoc schemaInputSource = new InputSource JavaDoc(schemaXMLSource);
546
547                     // System.err.println("validator pool cache miss: " + pathname + " " + key.length());
548
VerifierFactory factory = VerifierFactory.newInstance(
549                         "http://relaxng.org/ns/structure/1.0");
550                     schema = factory.compileSchema(schemaInputSource);
551                     synchronized (mSchemaCache) {
552                         mSchemaCache.put(key, schema);
553                     }
554                 } else {
555                     // System.err.println("validator pool cache hit: " + pathname + " " + key.length());
556
}
557                 
558                 // Cache this in CompilationEnvironment
559
verifier = schema.newVerifier();
560                 env.setCachedVerifier(verifier);
561             } else {
562                 // System.err.println("env verifier cache hit: " + pathname);
563
}
564             
565             VerifierHandler handler = verifier.getVerifierHandler();
566
567             verifier.setErrorHandler(
568                 new org.xml.sax.ErrorHandler JavaDoc() {
569                         protected void reportError(org.xml.sax.SAXParseException JavaDoc e)
570                         {
571                             String JavaDoc msg = e.getMessage();
572                             msg = StringUtils.replace(msg, " from namespace \"" + sNamespace.getURI() + "\"", "");
573                             CompilationError cerr = new CompilationError(msg);
574                             cerr.initPathname(e.getPublicId());
575                             cerr.setLineNumber(e.getLineNumber());
576                             cerr.setColumnNumber(e.getColumnNumber());
577                             if (msg.endsWith("not allowed in this context"))
578                                 cerr.setSolution("Check whether it is spelled correctly, and whether a class with this name exists.");
579                             // TODO [2003-3-17 hqm]: find and add more
580
// solution message here]
581
env.warn(cerr);
582                         }
583                         public void fatalError(org.xml.sax.SAXParseException JavaDoc e) {
584                             reportError(e);
585                         }
586                         public void error(org.xml.sax.SAXParseException JavaDoc e) {
587                             reportError(e);
588                         }
589                         public void warning(org.xml.sax.SAXParseException JavaDoc e) {
590                             reportError(e);
591                         }
592                     }
593                 );
594             
595             /* Walk the DOM tree out as SAX events, as best we can. We
596              * can't just use org.jdom.output.SAXOutputter because it
597              * doesn't know how to convert the source location info from
598              * our custom Elements into SAX Events.
599              */

600
601             SourceLocatorSAXOutputter outputter =
602                 new SourceLocatorSAXOutputter();
603
604             /* Sets our validator (event handler) to be the handler
605              * for the SAX Outputter */

606             outputter.setContentHandler(handler);
607             /* Feed the the source file DOM, via the SAX event
608              * generator, to the validator. */

609             outputter.output(doc);
610
611         } catch (VerifierConfigurationException e) {
612             throw new ChainedException(e);
613         } catch (JDOMException e) {
614             throw new ChainedException(e);
615         } catch (StackOverflowError JavaDoc e) {
616             env.warn("The validator had a fatal error, but proceeding with compilation", doc.getRootElement());
617         } catch (SAXException JavaDoc e) {
618             String JavaDoc solution = SolutionMessages.findSolution(e.getMessage(), SolutionMessages.PARSER);
619             CompilationError err = new CompilationError(e, solution);
620             err.setFileBase(errorHandler.fileBase);
621             err.initPathname(pathname);
622             throw err;
623         } catch (IOException e) {
624             throw new ChainedException(e);
625         }
626     }
627
628     void saveStartLocation (Element elt,
629                             String JavaDoc pathname,
630                             String JavaDoc messagePathname,
631                             int startLineNumber,
632                             int startColumnNumber) {
633         SourceLocator info = ((ElementWithLocationInfo) elt).locator;
634         info.setPathname(pathname, messagePathname);
635         info.startLineNumber = startLineNumber;
636         info.startColumnNumber = startColumnNumber;
637     }
638
639     void saveEndLocation (Element elt,
640                           String JavaDoc pathname,
641                           String JavaDoc messagePathname,
642                           int endLineNumber,
643                           int endColumnNumber) {
644         SourceLocator info = ((ElementWithLocationInfo) elt).locator;
645         info.setPathname(pathname, messagePathname);
646         info.endLineNumber = endLineNumber;
647         info.endColumnNumber = endColumnNumber;
648     }
649     
650     /* Implement source location, on top of metadata */
651     static final int LINENO = 1;
652     static final int COLNO = 2;
653     
654     static String JavaDoc getSourcePathname(Element elt) {
655         SourceLocator info = ((ElementWithLocationInfo) elt).locator;
656         return info.pathname;
657     }
658
659     static String JavaDoc getSourceMessagePathname(Element elt) {
660         SourceLocator info = ((ElementWithLocationInfo) elt).locator;
661         return info.messagePathname;
662     }
663
664     static Integer JavaDoc getSourceLocation(Element elt, int coord, boolean start) {
665         if (elt == null) {
666             return null;
667             // +++ should we throw an error if elt == null?
668
}
669
670         SourceLocator info = ((ElementWithLocationInfo) elt).locator;
671
672
673         if (coord == LINENO) {
674             return new Integer JavaDoc(start ? info.startLineNumber : info.endLineNumber);
675         } else {
676             return new Integer JavaDoc(start ? info.startColumnNumber : info.endColumnNumber);
677         }
678     }
679     
680     static Integer JavaDoc getSourceLocation(Element elt, int coord) {
681         return getSourceLocation(elt, coord, true);
682     }
683
684
685     class SourceLocatorJDOMFactory extends org.jdom.input.DefaultJDOMFactory {
686                 
687         public SourceLocatorJDOMFactory () {
688             super();
689         }
690
691         public Element element(String JavaDoc name) {
692             return new ElementWithLocationInfo(name);
693         }
694                 
695         public Element element(String JavaDoc name, Namespace namespace) {
696             return new ElementWithLocationInfo(name, namespace);
697         }
698
699         public Element element(String JavaDoc name, String JavaDoc uri) {
700             return new ElementWithLocationInfo(name, uri);
701         }
702
703         public Element element(String JavaDoc name, String JavaDoc prefix, String JavaDoc uri) {
704             return new ElementWithLocationInfo(name, prefix, uri);
705         }
706     }
707 }
708
Popular Tags