/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.sun.facelets.compiler; import com.sun.facelets.FaceletException; import com.sun.facelets.FaceletHandler; import com.sun.facelets.tag.Location; import com.sun.facelets.tag.Tag; import com.sun.facelets.tag.TagAttribute; import com.sun.facelets.tag.TagAttributes; import net.jakubholy.jeeutils.jsfelcheck.expressionfinder.pagenodes.PageNodeListener; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.XMLReader; import org.xml.sax.ext.LexicalHandler; import org.xml.sax.helpers.DefaultHandler; import javax.el.ELException; import javax.faces.FacesException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Copy of MyFaces SAXCompiler with slight adjustments, from MyFaces v1.2.10 * * The differences are: 1) it uses {@link org.apache.myfaces.view.facelets.compiler.JsfelcheckCompilationManager}/ * {@link NotifyingCompilationManager} instead of the original {@link CompilationManager}, * 2) It has a tagListener: PageNodeListener (shared w/ the C.M.) and uses it * to notify about page entering. * *

* Notice we must be in the myfaces package to be able to see package-private classes that the * compiler and c. manager use. *

* * @version Id: SAXCompiler.java,v 1.14 2008/07/13 19:01:33 rlubke Exp */ public final class JsfelcheckSAXCompiler extends Compiler { private final PageNodeListener tagListener; public JsfelcheckSAXCompiler(PageNodeListener tagListener) { this.tagListener = tagListener; } private final static Pattern XmlDeclaration = Pattern.compile("^<\\?xml.+?version=['\"](.+?)['\"](.+?encoding=['\"]((.+?))['\"])?.*?\\?>"); private static class CompilationHandler extends DefaultHandler implements LexicalHandler { private final String alias; private boolean inDocument = false; private Locator locator; private StringBuffer charactersBuffer = null; private final JsfelcheckCompilationManager unit; public CompilationHandler(JsfelcheckCompilationManager unit, String alias) { this.unit = unit; this.alias = alias; } public void characters(char[] ch, int start, int length) throws SAXException { if (this.inDocument) { if (charactersBuffer != null) { charactersBuffer.append(ch, start, length); } } } public void comment(char[] ch, int start, int length) throws SAXException { if (this.inDocument) { this.unit.writeComment(new String(ch, start, length)); } } protected TagAttributes createAttributes(Attributes attrs) { int len = attrs.getLength(); TagAttribute[] ta = new TagAttribute[len]; for (int i = 0; i < len; i++) { ta[i] = new TagAttribute(this.createLocation(), attrs.getURI(i), attrs.getLocalName(i), attrs .getQName(i), attrs.getValue(i)); } return new TagAttributes(ta); } protected Location createLocation() { return new Location(this.alias, this.locator.getLineNumber(), this.locator.getColumnNumber()); } public void endCDATA() throws SAXException { if (this.inDocument) { this.unit.writeInstruction("]]>"); } } public void endDocument() throws SAXException { super.endDocument(); } public void endDTD() throws SAXException { this.inDocument = true; } public void endElement(String uri, String localName, String qName) throws SAXException { if (charactersBuffer != null && charactersBuffer.length() > 0) { String characters = charactersBuffer.toString(); this.unit.writeText(characters); charactersBuffer = null; } this.unit.popTag(); } public void endEntity(String name) throws SAXException { } public void endPrefixMapping(String prefix) throws SAXException { this.unit.popNamespace(prefix); } public void fatalError(SAXParseException e) throws SAXException { if (this.locator != null) { throw new SAXException("Error Traced[line: " + this.locator.getLineNumber() + "] " + e.getMessage()); } else { throw e; } } public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { if (this.inDocument) { this.unit.writeWhitespace(new String(ch, start, length)); } } public InputSource resolveEntity(String publicId, String systemId) throws SAXException { String dtd = "default.dtd"; /*if ("-//W3C//DTD XHTML 1.0 Transitional//EN".equals(publicId)) { dtd = "xhtml1-transitional.dtd"; } else if (systemId != null && systemId.startsWith("file:/")) { return new InputSource(systemId); }*/ URL url = Thread.currentThread().getContextClassLoader() .getResource(dtd); return new InputSource(url.toString()); } public void setDocumentLocator(Locator locator) { this.locator = locator; } public void startCDATA() throws SAXException { if (this.inDocument) { this.unit.writeInstruction("\n"); this.unit.writeInstruction(sb.toString()); } this.inDocument = false; } public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { this.unit.pushTag(new Tag(this.createLocation(), uri, localName, qName, this.createAttributes(attributes))); charactersBuffer = new StringBuffer(); } public void startEntity(String name) throws SAXException { } public void startPrefixMapping(String prefix, String uri) throws SAXException { this.unit.pushNamespace(prefix, uri); } public void processingInstruction(String target, String data) throws SAXException { if (this.inDocument) { StringBuffer sb = new StringBuffer(64); sb.append("\n"); this.unit.writeInstruction(sb.toString()); } } } public FaceletHandler doCompile(URL src, String alias) throws IOException, FaceletException, ELException, FacesException { tagListener.fileEntered(alias); JsfelcheckCompilationManager mngr = null; InputStream is = null; String encoding = "UTF-8"; try { is = new BufferedInputStream(src.openStream(), 1024); mngr = new NotifyingCompilationManager(alias, this, tagListener); encoding = writeXmlDecl(is, mngr); CompilationHandler handler = new CompilationHandler(mngr, alias); SAXParser parser = this.createSAXParser(handler); parser.parse(is, handler); } catch (SAXException e) { throw new FaceletException("Error Parsing " + alias + ": " + e.getMessage(), e.getCause()); } catch (ParserConfigurationException e) { throw new FaceletException("Error Configuring Parser " + alias + ": " + e.getMessage(), e.getCause()); } finally { if (is != null) { is.close(); } } tagListener.fileExited(alias); return new EncodingHandler(mngr.createFaceletHandler(), encoding); } protected static final String writeXmlDecl(InputStream is, JsfelcheckCompilationManager mngr) throws IOException { is.mark(128); String encoding = "UTF-8"; try { byte[] b = new byte[128]; if (is.read(b) > 0) { String r = new String(b); Matcher m = XmlDeclaration.matcher(r); if (m.find()) { mngr.writeInstruction(m.group(0) + "\n"); if (m.group(3) != null) { encoding = m.group(3); } } } } finally { is.reset(); } return encoding; } private final SAXParser createSAXParser(CompilationHandler handler) throws SAXException, ParserConfigurationException { SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setNamespaceAware(true); factory.setFeature("http://xml.org/sax/features/namespace-prefixes", true); factory.setFeature("http://xml.org/sax/features/validation", this .isValidating()); factory.setValidating(this.isValidating()); SAXParser parser = factory.newSAXParser(); XMLReader reader = parser.getXMLReader(); reader.setProperty("http://xml.org/sax/properties/lexical-handler", handler); reader.setErrorHandler(handler); reader.setEntityResolver(handler); return parser; } }