package org.tigris.juxy.verifier; import org.tigris.juxy.util.ExceptionUtil; import org.tigris.juxy.util.JuxyURIResolver; import org.tigris.juxy.util.SAXUtil; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.XMLReader; import javax.xml.transform.*; import javax.xml.transform.stream.StreamSource; import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.*; /** */ public class VerifierImpl implements Verifier { private List urisToVerify = new ArrayList(20); private ErrorReporter er; private URIResolver resolver = new JuxyURIResolver(); private int numberOfVerifiedFiles = 0; private int numberOfNotVerifiedFiles = 0; private boolean wereParseErrors = false; private String transformerFactoryClassName; public void setErrorReporter(ErrorReporter er) { assert er != null; this.er = er; } public void setTransformerFactory(String className) { assert className != null && className.length() > 0; this.transformerFactoryClassName = className; } public void setURIResolver(URIResolver resolver) { assert resolver != null; this.resolver = resolver; } public void setFiles(List files) { assert files != null; Iterator it = files.iterator(); while (it.hasNext()) { File f = (File) it.next(); if (f.exists() && f.isFile()) try { urisToVerify.add(f.getCanonicalFile().toURI().normalize()); } catch (IOException e) { info("File " + f.getAbsolutePath() + " won't be verified: " + e.toString()); } } } public boolean verify(boolean failOnError) { info("Searching for stylesheets to verify ..."); Map stylesheets = new HashMap(); IncludeInstructionsHandler iih = new IncludeInstructionsHandler(); XMLReader reader = SAXUtil.newXMLReader(); reader.setContentHandler(iih); Iterator it = urisToVerify.iterator(); while (it.hasNext()) { URI fileURI = (URI) it.next(); boolean parsed = false; Source src = new StreamSource(fileURI.toString()); registerStylesheet(fileURI, src, stylesheets); iih.reset(); ErrorsCollector ec = new ErrorsCollector(); try { reader.setErrorHandler(ec); reader.parse(fileURI.toString()); parsed = true; } catch (IOException e) { reportError("Failed to parse " + fileURI + " due to error: " + e.getMessage()); } catch (ParseStoppedException e) { // parsing stopped by handler parsed = true; } catch (SAXException e) { } finally { if (ec.hasErrors()) { reportError("Failed to parse file " + fileURI); reportParserErrors(ec.getParseErrors()); reportParserWarnings(ec.getParseWarnings()); } else if (ec.hasWarnings()) { reportWarning("There were warnings while parsing file " + fileURI); reportParserWarnings(ec.getParseWarnings()); } } if (!parsed || ec.hasErrors()) { it.remove(); wereParseErrors = true; if (failOnError) return false; } else { processIncludes(fileURI, stylesheets, iih.getHrefs()); } } List topStylesheets = getTopStylesheets(stylesheets); info(topStylesheets.size() + " stylesheet(s) were selected for verification"); verifyStylesheets(topStylesheets, failOnError); return numberOfNotVerifiedFiles == 0 && !wereParseErrors; } private void reportParserWarnings(SAXParseException[] warnings) { for (int i = 0; i < warnings.length; i++) reportWarning(ExceptionUtil.exceptionToString(warnings[i], false)); } private void reportParserErrors(SAXParseException[] errors) { for (int i = 0; i < errors.length; i++) reportError(ExceptionUtil.exceptionToString(errors[i], false)); } public int getNumberOfVerifiedFiles() { return numberOfVerifiedFiles; } public int getNumberOfNotVerifiedFiles() { return numberOfNotVerifiedFiles; } private void verifyStylesheets(List topStylesheets, boolean failOnError) { Collections.sort(topStylesheets, new SourceComparator()); TransformerFactory trFactory = getTransformerFactory(); info("Obtained TransformerFactory: " + trFactory.getClass().getName()); trFactory.setURIResolver(resolver); info("Verifying found files:"); Iterator it = topStylesheets.iterator(); while (it.hasNext()) { Source src = (Source) it.next(); info(calculateRelativePath(src.getSystemId()) + " ..."); boolean verified = false; ErrorsCollector errorListener = new ErrorsCollector(); try { trFactory.setErrorListener(errorListener); trFactory.newTransformer(src); if (!errorListener.hasErrors()) verified = true; } catch (TransformerConfigurationException e) { e.printStackTrace(); //reportTransformerErrors(new TransformerException[] {e}); } finally { if (errorListener.hasErrors()) { reportTransformerErrors(errorListener.getTransformErrors()); reportTransformerWarnings(errorListener.getTransformWarnings()); } else if (errorListener.hasWarnings()) { reportTransformerWarnings(errorListener.getTransformWarnings()); } } if (!verified) { numberOfNotVerifiedFiles++; if (failOnError) throw new VerificationFailedException(); } else { numberOfVerifiedFiles++; } } } private String calculateRelativePath(String systemId) { String userDir = System.getProperty("user.dir"); try { return new File(userDir).getCanonicalFile().toURI().relativize(new URI(systemId)).toString(); } catch (URISyntaxException e) { return systemId; } catch (IOException e) { return systemId; } } private TransformerFactory getTransformerFactory() { if (transformerFactoryClassName != null) { try { Class factoryClass = Class.forName(transformerFactoryClassName); if (factoryClass != null) return (TransformerFactory) factoryClass.newInstance(); } catch (ClassNotFoundException e) { reportWarning("Failed to load class for specified TransformerFactory: " + transformerFactoryClassName); } catch (IllegalAccessException e) { reportWarning("Failed to instantiate class for specified TransformerFactory: " + transformerFactoryClassName); } catch (InstantiationException e) { reportWarning("Failed to instantiate class for specified TransformerFactory: " + transformerFactoryClassName); } } info("Using default TransformerFactory"); return TransformerFactory.newInstance(); } private void reportTransformerWarnings(TransformerException[] warnings) { for (int i = 0; i < warnings.length; i++) reportWarning(ExceptionUtil.exceptionToString(warnings[i], false)); } private void reportTransformerErrors(TransformerException[] errors) { for (int i = 0; i < errors.length; i++) reportError(ExceptionUtil.exceptionToString(errors[i], false)); } private List getTopStylesheets(Map links) { List result = new ArrayList(20); Iterator it = links.entrySet().iterator(); while (it.hasNext()) { Map.Entry me = (Map.Entry) it.next(); StylesheetInfo sinfo = (StylesheetInfo) me.getValue(); try { URI uri = new URI(sinfo.resolvedSource.getSystemId()); if (sinfo.referencesCounter == 0 && urisToVerify.contains(uri)) result.add(sinfo.resolvedSource); } catch (URISyntaxException e) { info("Invalid URI: " + sinfo.resolvedSource.getSystemId()); } } return result; } private void processIncludes(URI fileURI, Map links, Set hrefs) { Iterator it = hrefs.iterator(); while (it.hasNext()) { String href = (String) it.next(); String resolvedSystemId = ""; try { Source resolvedSource = resolver.resolve(href, fileURI.toString()); if (resolvedSource != null) { resolvedSystemId = resolvedSource.getSystemId(); assert resolvedSystemId != null; URI resolvedURI = new URI(resolvedSystemId); if (!links.containsKey(resolvedURI)) registerStylesheet(resolvedURI, resolvedSource, links); incrementReferences(links, resolvedURI); } else { reportWarning("Failed to resolve URI: " + href); } } catch (URISyntaxException e) { reportWarning("Invalid URI: " + resolvedSystemId); } catch (TransformerException e) { reportWarning("Failed to resolve URI: " + href); } } } private void registerStylesheet(URI uri, Source src, Map links) { if (!links.containsKey(uri)) links.put(uri, new StylesheetInfo(src, 0)); else incrementReferences(links, uri); } private void incrementReferences(Map links, URI uri) { StylesheetInfo sinfo = (StylesheetInfo) links.get(uri); sinfo.referencesCounter++; } private void reportError(String message) { assert er != null; er.error(message); } private void reportWarning(String message) { assert er != null; er.warning(message); } private void info(String message) { assert er != null; er.info(message); } class SourceComparator implements Comparator { public int compare(Object o1, Object o2) { Source s1 = (Source) o1; Source s2 = (Source) o2; return s1.getSystemId().compareTo(s2.getSystemId()); } } class StylesheetInfo { public Source resolvedSource; public int referencesCounter; public StylesheetInfo(Source resolverSource, int referencesCounter) { this.resolvedSource = resolverSource; this.referencesCounter = referencesCounter; } } public static class ErrorsCollector implements ErrorHandler, ErrorListener { private List errors = new ArrayList(); private List warnings = new ArrayList(); public void warning(SAXParseException exception) throws SAXException { warnings.add(exception); } public void error(SAXParseException exception) throws SAXException { errors.add(exception); } public void fatalError(SAXParseException exception) throws SAXException { errors.add(exception); } public SAXParseException[] getParseErrors() { return (SAXParseException[]) errors.toArray(new SAXParseException[]{}); } public SAXParseException[] getParseWarnings() { return (SAXParseException[]) warnings.toArray(new SAXParseException[]{}); } public boolean hasErrors() { return errors.size() > 0; } public boolean hasWarnings() { return warnings.size() > 0; } public void warning(TransformerException exception) throws TransformerException { warnings.add(exception); } public void error(TransformerException exception) throws TransformerException { errors.add(exception); } public void fatalError(TransformerException exception) throws TransformerException { errors.add(exception); } public TransformerException[] getTransformErrors() { return (TransformerException[]) errors.toArray(new TransformerException[]{}); } public TransformerException[] getTransformWarnings() { return (TransformerException[]) warnings.toArray(new TransformerException[]{}); } } }