1 4 5 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 ; 27 import org.xml.sax.SAXException ; 28 import org.xml.sax.XMLFilter ; 29 import org.xml.sax.helpers.XMLFilterImpl ; 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 ; 36 import javax.xml.transform.TransformerFactory ; 37 import javax.xml.transform.sax.SAXResult ; 38 import javax.xml.transform.sax.SAXTransformerFactory ; 39 import javax.xml.transform.sax.TransformerHandler ; 40 import javax.xml.transform.stream.StreamSource ; 41 42 import org.xml.sax.SAXException ; 43 44 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 64 protected final Map fileCache = new HashMap(); 65 67 List basePathnames = new Vector(); 68 69 private static javax.xml.transform.Templates sPreprocessorTemplates; 72 private static SAXTransformerFactory sSaxFactory; 73 private static long sPreprocessorLastModified; 74 75 protected static synchronized SAXTransformerFactory getSaxFactory() { 76 String 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 javax.xml.transform.TransformerFactory factory = 91 new com.icl.saxon.TransformerFactoryImpl(); 92 93 javax.xml.transform.Templates templates = null; 94 try { 95 templates = factory.newTemplates( 96 new StreamSource ("file:///" + stylePath)); 97 } catch (TransformerConfigurationException e) { 98 throw new ChainedException(e); 99 } 100 101 if (!factory.getFeature(SAXTransformerFactory.FEATURE)) 102 throw new RuntimeException ( 103 "TransformerFactory doesn't implement SAXTransformerFactory"); 104 105 SAXTransformerFactory saxFactory = (SAXTransformerFactory ) 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 124 public String getUserPathname(String pathname) { 125 String sourceDir = new File(pathname).getParent(); 126 if (sourceDir == null) 127 sourceDir = ""; 128 sourceDir = sourceDir.replace(File.separatorChar, '/'); 129 String best = pathname.replace(File.separatorChar, '/'); 130 int bestLength = StringUtils.split(best, "/").length; 131 for (Iterator iter = basePathnames.iterator(); iter.hasNext(); ) { 132 String item = (String ) iter.next(); 133 String base = item.replace(File.separatorChar, '/'); 134 try { 135 String 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 } 149 } 150 return best; 151 } 152 153 155 protected Document read(File file) 156 throws JDOMException, IOException 157 { 158 File key = file.getCanonicalFile(); 164 if (fileCache.containsKey(key)) { 165 return (Document) fileCache.get(key); 166 } 167 168 172 final String pathname = file.getPath(); 176 final String messagePathname = getUserPathname(pathname); 177 178 class SourceLocatorHandler extends org.jdom.input.SAXHandler { 181 org.xml.sax.Locator 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 196 { 197 startLineNumber = locator.getLineNumber(); 198 startColumnNumber = locator.getColumnNumber(); 199 super.characters(ch, start, length); 200 } 201 202 public void endElement(String namespaceURI, String localName, 203 String qName) 204 throws SAXException 205 { 206 209 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 locator) { 220 this.locator = locator; 221 } 222 223 public void startElement(String namespaceURI, String localName, 224 String qName, 225 org.xml.sax.Attributes atts) 226 throws SAXException 227 { 228 super.startElement(namespaceURI, localName, qName, atts); 229 230 233 saveStartLocation(this.getCurrentElement(), 235 pathname, 236 messagePathname, 237 locator.getLineNumber(), 238 locator.getColumnNumber()); 239 } 240 } 241 242 251 class SourceLocatorSAXBuilder extends SAXBuilder { 252 SourceLocatorSAXBuilder (String saxDriverClass) { 253 super(saxDriverClass); 254 } 255 256 SourceLocatorSAXBuilder () { 257 super(); 258 } 259 260 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(); 275 builder.setFactory(new SourceLocatorJDOMFactory()); 276 277 builder.setEntityResolver( 283 new org.xml.sax.helpers.DefaultHandler () { 284 public InputSource resolveEntity(String publicId, String systemId) { 285 return new InputSource (new StringReader("")); 286 } 287 }); 288 289 java.io.Reader reader = FileUtils.makeXMLReaderForFile( 291 pathname, "UTF-8"); 292 InputSource source = new InputSource (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 , org.jdom.JDOMException 303 { 304 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 namespaceURI, String localName, 315 String qName) 316 throws SAXException 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 TransformerHandler handler; 334 try { 335 handler = getSaxFactory(). 336 newTransformerHandler(sPreprocessorTemplates); 337 } catch (TransformerConfigurationException e) { 338 throw new ChainedException(e); 339 } 340 341 SAXHandler resultHandler = 342 new SourceLocatorHandler(new SourceLocatorJDOMFactory()); 343 SAXResult result = 344 new javax.xml.transform.sax.SAXResult (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 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 399 protected void expandIncludes(Element element, Set currentFiles) 400 throws IOException, JDOMException 401 { 402 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 base = new File(getSourcePathname(element)).getParent(); 417 String type = XMLUtils.getAttributeValue(child, "type", "xml"); 418 String 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 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 Document doc = read(target); 436 if (CompilerUtils.isAtToplevel(child) && 445 LibraryCompiler.isElement(doc.getRootElement())) { 446 child.setName(doc.getRootElement().getName()); 450 } else { 451 doc = readExpanded(target, currentFiles); 452 replacements.put(child, doc.getRootElement()); 453 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 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 value = entry.getValue(); 473 contents.set(index, value); 474 } 475 } 476 477 479 public Document parse(File file) 480 throws CompilationError 481 { 482 String pathname = file.getPath(); 483 try { 484 Document doc = readExpanded(file, new HashSet()); 485 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 solution = SolutionMessages.findSolution(e.getMessage(), SolutionMessages.PARSER); 494 CompilationError ce = new CompilationError(e, solution); 495 throw ce; 496 497 } 498 } 499 500 503 private static LRUMap mSchemaCache = new LRUMap(); 504 505 513 public static void validate(Document doc, String 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 Verifier verifier = env.getCachedVerifier(); 530 531 if (verifier == null) { 532 534 Object value; 536 String schemaXMLstring = new XMLOutputter().outputString(viewschema.getSchemaDOM()); 537 String 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 schemaInputSource = new InputSource (schemaXMLSource); 546 547 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 } 557 558 verifier = schema.newVerifier(); 560 env.setCachedVerifier(verifier); 561 } else { 562 } 564 565 VerifierHandler handler = verifier.getVerifierHandler(); 566 567 verifier.setErrorHandler( 568 new org.xml.sax.ErrorHandler () { 569 protected void reportError(org.xml.sax.SAXParseException e) 570 { 571 String 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 env.warn(cerr); 582 } 583 public void fatalError(org.xml.sax.SAXParseException e) { 584 reportError(e); 585 } 586 public void error(org.xml.sax.SAXParseException e) { 587 reportError(e); 588 } 589 public void warning(org.xml.sax.SAXParseException e) { 590 reportError(e); 591 } 592 } 593 ); 594 595 600 601 SourceLocatorSAXOutputter outputter = 602 new SourceLocatorSAXOutputter(); 603 604 606 outputter.setContentHandler(handler); 607 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 e) { 616 env.warn("The validator had a fatal error, but proceeding with compilation", doc.getRootElement()); 617 } catch (SAXException e) { 618 String 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 pathname, 630 String 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 pathname, 641 String 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 651 static final int LINENO = 1; 652 static final int COLNO = 2; 653 654 static String getSourcePathname(Element elt) { 655 SourceLocator info = ((ElementWithLocationInfo) elt).locator; 656 return info.pathname; 657 } 658 659 static String getSourceMessagePathname(Element elt) { 660 SourceLocator info = ((ElementWithLocationInfo) elt).locator; 661 return info.messagePathname; 662 } 663 664 static Integer getSourceLocation(Element elt, int coord, boolean start) { 665 if (elt == null) { 666 return null; 667 } 669 670 SourceLocator info = ((ElementWithLocationInfo) elt).locator; 671 672 673 if (coord == LINENO) { 674 return new Integer (start ? info.startLineNumber : info.endLineNumber); 675 } else { 676 return new Integer (start ? info.startColumnNumber : info.endColumnNumber); 677 } 678 } 679 680 static Integer 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 name) { 692 return new ElementWithLocationInfo(name); 693 } 694 695 public Element element(String name, Namespace namespace) { 696 return new ElementWithLocationInfo(name, namespace); 697 } 698 699 public Element element(String name, String uri) { 700 return new ElementWithLocationInfo(name, uri); 701 } 702 703 public Element element(String name, String prefix, String uri) { 704 return new ElementWithLocationInfo(name, prefix, uri); 705 } 706 } 707 } 708 | Popular Tags |