/******************************************************************************* * Copyright (c) 2004, 2005 * Thomas Hallgren, Kenneth Olwing, Mitch Sonies * Pontus Rydin, Nils Unden, Peer Torngren * The code, documentation and other materials contained herein have been * licensed under the Eclipse Public License - v 1.0 by the individual * copyright holders listed above, as Initial Contributors under such license. * The text of such license is available at www.eclipse.org. *******************************************************************************/ package org.eclipse.buckminster.core.parser; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.File; import java.io.InputStream; import java.lang.reflect.Constructor; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.buckminster.core.CorePlugin; import org.eclipse.buckminster.core.Messages; import org.eclipse.buckminster.core.helpers.FileUtils; import org.eclipse.buckminster.runtime.BuckminsterException; import org.eclipse.buckminster.sax.AbstractHandler; import org.eclipse.buckminster.sax.ChildHandler; import org.eclipse.buckminster.sax.TopHandler; import org.eclipse.buckminster.sax.Utils; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.Path; import org.eclipse.osgi.util.NLS; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.XMLReader; /** * @author Thomas Hallgren */ public abstract class AbstractParser<T> extends TopHandler implements ErrorHandler, IParser<T> { private static Pattern saxParseCleaner = Pattern.compile("^cvc-[^:]+:(.*)$"); //$NON-NLS-1$ private static IFile[] noFiles = new IFile[0]; public static IFile[] clearMarkers(String systemId) { // If the systemId is represented as a resource in the workspace, then // remove // all problem markers from it. // IFile[] files = getFilesForSystemId(systemId); try { for (IFile file : files) file.deleteMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE); } catch (CoreException ce) { // Ignore, probably a locked workspace } return files; } public static XMLReader createXMLReader(boolean validating, boolean withNamespace) throws CoreException { try { return Utils.createXMLReader(validating, withNamespace); } catch (SAXException e) { throw BuckminsterException.wrap(e); } } public static void setMarkers(IFile[] files, SAXParseException e) { // Annotate the file if "systemId" denotes a resource in a project // try { String msg = e.getMessage(); Matcher match = saxParseCleaner.matcher(msg); if (match.matches()) msg = match.group(1); for (IFile file : files) { IMarker marker = file.createMarker(IMarker.PROBLEM); marker.setAttribute(IMarker.MESSAGE, msg); marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH); marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); marker.setAttribute(IMarker.LINE_NUMBER, e.getLineNumber()); } } catch (CoreException ce) { // Ignore } } private static IFile[] getFilesForSystemId(String systemId) { if (systemId == null || systemId.contains(".metadata")) //$NON-NLS-1$ return noFiles; try { URL url = new URL(systemId); File file = FileUtils.getFile(url); if (file == null) return noFiles; systemId = file.toString(); } catch (MalformedURLException murle) { // Apparently not a valid URL. That's expected } return ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(new Path(systemId).toFile().toURI()); } private final boolean validating; private final List<String> namespaceLocations; private final List<ParserFactory.ParserExtension> parserExtensions; private HashSet<String> printedWarnings; protected AbstractParser(List<ParserFactory.ParserExtension> parserExtensions, String[] namespaces, String[] schemaLocations, boolean validating) throws CoreException { super(createXMLReader(validating, true)); this.validating = validating; int top = namespaces.length; if (top != schemaLocations.length) throw new IllegalArgumentException(Messages.The_namespace_and_schemaLocation_arrays_must_be_equal_in_length); this.namespaceLocations = new ArrayList<String>(); for (int idx = 0; idx < top; ++idx) { String namespace = namespaces[idx]; String schemaFile = schemaLocations[idx]; URL schemaURL = getClass().getResource(schemaFile); if (schemaURL == null) throw BuckminsterException.fromMessage(NLS.bind(Messages.Unable_to_find_XMLSchema_for_namespace_0, namespace)); addNamespaceLocation(namespace, schemaURL); } if (parserExtensions != null) { for (ParserFactory.ParserExtension pe : parserExtensions) addNamespaceLocation(pe.getNamespace(), pe.getResource()); } this.parserExtensions = parserExtensions; setNamespaceAware(true); setErrorHandler(this); } public <H extends ChildHandler> H createContentHandler(AbstractHandler parent, Class<H> handlerClass, String namespace, String xsiType) throws SAXException { try { if (xsiType != null && parserExtensions != null) { String ns; int colonIndex = xsiType.indexOf(':'); if (colonIndex > 0) { String prefix = xsiType.substring(0, colonIndex); ns = getPrefixMapping(prefix); if (ns == null) throw new SAXParseException(NLS.bind(Messages.Unknown_namespace_prefix_0, prefix), getDocumentLocator()); xsiType = xsiType.substring(colonIndex + 1); } else ns = namespace; for (ParserFactory.ParserExtension pe : parserExtensions) { if (pe.getNamespace().equals(ns)) return handlerClass.cast(pe.getHandler(parent, xsiType)); } } try { Constructor<H> ctor = handlerClass.getConstructor(new Class[] { AbstractHandler.class }); return ctor.newInstance(new Object[] { parent }); } catch (Exception e) { throw BuckminsterException.wrap(e); } } catch (Exception e) { throw new SAXParseException(NLS.bind(Messages.Unable_to_create_extension_handler_0_1, namespace, xsiType), getDocumentLocator(), e); } } @Override public void error(SAXParseException e) throws SAXException { throw e; } @Override public void warning(SAXParseException e) throws SAXException { throw e; } public void warningOnce(String warning) { if (printedWarnings == null) printedWarnings = new HashSet<String>(); else if (printedWarnings.contains(warning)) return; printedWarnings.add(warning); CorePlugin.getLogger().warning(warning); } protected void addNamespaceLocation(String namespace, URL location) { namespaceLocations.add(namespace + ' ' + location.toString()); } protected void init() throws SAXException { XMLReader reader = getXMLReader(); if (validating) { reader.setFeature("http://apache.org/xml/features/validation/schema", true); //$NON-NLS-1$ reader.setFeature("http://apache.org/xml/features/validation/schema-full-checking", true); //$NON-NLS-1$ } int len = 0; int top = namespaceLocations.size(); for (int idx = 0; idx < top; ++idx) { len += namespaceLocations.get(idx).length(); len++; } StringBuilder bld = new StringBuilder(len); for (int idx = 0; idx < top; ++idx) { if (idx > 0) bld.append(' '); bld.append(namespaceLocations.get(idx)); } reader.setProperty("http://apache.org/xml/properties/schema/external-schemaLocation", bld.toString()); //$NON-NLS-1$ } protected void parseInput(String systemId, InputStream input) throws CoreException { IFile[] files = clearMarkers(systemId); try { init(); if (!(input instanceof BufferedInputStream || input instanceof ByteArrayInputStream)) input = new BufferedInputStream(input); InputSource source = new InputSource(input); if (systemId != null) source.setSystemId(systemId); getXMLReader().parse(source); } catch (SAXParseException e) { setMarkers(files, e); throw BuckminsterException.wrap(e); } catch (Exception e) { throw BuckminsterException.wrap(e); } finally { getXMLReader().setContentHandler(this); } } }