package org.archstudio.schematron.core; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import javax.xml.parsers.ParserConfigurationException; import org.archstudio.archlight.ArchlightTest; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.resource.URIConverter; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; public class SchematronTestFile { public static final String SCHEMATRON_NSURI = "http://www.ascc.net/xml/schematron"; protected String toolID; protected String sourceURL; protected Document document; protected List<ArchlightTest> archlightTests; protected List<String> parseWarnings; private SchematronTestFile(SchematronTestFile copyMe) { this.toolID = copyMe.toolID; this.sourceURL = copyMe.sourceURL; if (copyMe.archlightTests != null) { archlightTests = new ArrayList<ArchlightTest>(copyMe.archlightTests); } if (copyMe.parseWarnings != null) { parseWarnings = new ArrayList<String>(copyMe.parseWarnings); } if (copyMe.document != null) { document = (Document) copyMe.document.cloneNode(true); } } protected SchematronTestFile(String toolID, String sourceURL, String xmlContents) throws SchematronTestFileParseException { this.toolID = toolID; this.sourceURL = sourceURL; parseContents(xmlContents); } protected void parseContents(String xmlContents) throws SchematronTestFileParseException { try { List<ArchlightTest> newArchlightTests = new ArrayList<ArchlightTest>(); List<String> newParseWarnings = new ArrayList<String>(); StringReader r = new StringReader(xmlContents); this.document = SchematronUtils.parseToDocument(r); Element rootElement = this.document.getDocumentElement(); if (rootElement == null) { throw new SchematronTestFileParseException("Tests file missing root element" + (sourceURL == null ? "" : ": " + sourceURL)); } String nsuri = rootElement.getAttribute("xmlns"); if (nsuri == null) { throw new SchematronTestFileParseException("Tests file missing xmlns declaration" + (sourceURL == null ? "" : ": " + sourceURL)); } nsuri = nsuri.trim(); if (!nsuri.equals(SCHEMATRON_NSURI)) { throw new SchematronTestFileParseException("Tests file URI must be " + SCHEMATRON_NSURI + ": " + (sourceURL == null ? "" : sourceURL)); } //Find all the patterns and get their IDs NodeList topLevelChildren = rootElement.getChildNodes(); for (int i = 0; i < topLevelChildren.getLength(); i++) { Node child = topLevelChildren.item(i); if (child instanceof Element) { Element childElt = (Element) child; String tagName = childElt.getTagName(); if (tagName != null && tagName.equals("pattern")) { String testUID = childElt.getAttribute("id"); if (testUID == null || testUID.trim().length() == 0) { newParseWarnings.add("Warning: Schematron tests file has pattern with no UID" + (sourceURL == null ? "" : ": " + sourceURL)); } else { String testCategory = childElt.getAttribute("name"); if (testCategory == null || testCategory.trim().length() == 0) { newParseWarnings .add("Warning: Schematron tests file has pattern with no category (name)" + (sourceURL == null ? "" : ": " + sourceURL)); testCategory = "UnknownTest/" + testUID; } String testDescription = childElt.getAttribute("description"); if (testDescription == null || testDescription.trim().length() == 0) { newParseWarnings.add("Warning: Schematron tests file has pattern with no description" + (sourceURL == null ? "" : ": " + sourceURL) + " has pattern with no description."); testDescription = "[No Description]"; } ArchlightTest tt = new ArchlightTest(testUID.trim(), toolID, testCategory.trim(), testDescription.trim()); newArchlightTests.add(tt); } } } } archlightTests = newArchlightTests; parseWarnings = newParseWarnings; } catch (ParserConfigurationException pce) { throw new SchematronTestFileParseException("XML Parser Configuration Error parsing document" + (sourceURL == null ? "" : ": " + sourceURL), pce); } catch (SAXException se) { String loc = ""; if (se instanceof SAXParseException) { SAXParseException spe = (SAXParseException) se; int lineNo = spe.getLineNumber(); int colNo = spe.getColumnNumber(); loc = " at line " + lineNo + ", column " + colNo; } throw new SchematronTestFileParseException("XML Parse Error parsing document" + loc + (sourceURL == null ? "" : ": " + sourceURL), se); } catch (IOException ioe) { throw new SchematronTestFileParseException("I/O Error parsing document" + (sourceURL == null ? "" : ": " + sourceURL), ioe); } } public List<? extends ArchlightTest> getArchlightTests() { return Collections.unmodifiableList(new ArrayList<ArchlightTest>(archlightTests)); } public Document getDocument() { return this.document; } public String getSourceURL() { return sourceURL; } public List<String> getParseWarnings() { return Collections.unmodifiableList(new ArrayList<String>(parseWarnings)); } public static SchematronTestFile create(String toolID, String sourceURL, InputStream is) throws SchematronTestFileParseException, IOException { //Note: the sourceURL here is only used to identify the document, //not to reload it or anything. StringBuffer sb = new StringBuffer(); byte[] buf = new byte[2048]; while (true) { int bytesRead = is.read(buf); if (bytesRead == -1) { break; } sb.append(new String(buf, 0, bytesRead)); } is.close(); SchematronTestFile stf = new SchematronTestFile(toolID, sourceURL, sb.toString()); return stf; } private static InputStream openURI(String uriString) throws IOException { URI uri = URI.createURI(uriString); InputStream is = URIConverter.INSTANCE.createInputStream(uri); return is; } public static SchematronTestFile create(String toolID, String urlString) throws SchematronTestFileParseException, IOException, FileNotFoundException, MalformedURLException { InputStream is = openURI(urlString); return create(toolID, urlString, is); } public static SchematronTestFile create(SchematronTestFile originalTestFile, List<String> testUIDs) throws SchematronTestFileParseException { //For faster lookups HashSet<String> testUIDSet = new HashSet<String>(testUIDs); SchematronTestFile newFile = new SchematronTestFile(originalTestFile); Document newFileDoc = newFile.getDocument(); if (newFileDoc == null) { return newFile; } Element rootElement = newFileDoc.getDocumentElement(); if (rootElement == null) { //This shoudln't happen throw new SchematronTestFileParseException("Tests file missing root element" + (newFile.sourceURL == null ? "" : ": " + newFile.sourceURL)); } //Find all the patterns and get their IDs; //remove the IDs that don't correspond to tests we have. NodeList topLevelChildren = rootElement.getChildNodes(); for (int i = 0; i < topLevelChildren.getLength(); i++) { Node child = topLevelChildren.item(i); if (child instanceof Element) { Element childElt = (Element) child; String tagName = childElt.getTagName(); if (tagName != null && tagName.equals("pattern")) { String testUID = childElt.getAttribute("id"); boolean found = false; if (testUID != null) { if (testUIDSet.contains(testUID)) { found = true; } } if (!found) { rootElement.removeChild(childElt); } } } } return newFile; } }