All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.cloud.tools.opensource.classpath.ExclusionFiles Maven / Gradle / Ivy

There is a newer version: 1.5.13
Show newest version
/*
 * Copyright 2020 Google LLC.
 *
 * Licensed 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.google.cloud.tools.opensource.classpath;

import com.google.common.collect.ImmutableList;
import com.thaiopensource.xml.sax.DraconianErrorHandler;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.StartElement;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.iso_relax.verifier.Schema;
import org.iso_relax.verifier.Verifier;
import org.iso_relax.verifier.VerifierConfigurationException;
import org.iso_relax.verifier.VerifierFactory;
import org.iso_relax.verifier.VerifierFilter;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

/**
 * Utility for Linkage Checker exclusion files.
 *
 * 

The exclusion file for Linkage Checker is an XML file. Its top-level element is * LinkageCheckerFilter. The XML file contains the following structure: * *

    *
  • A LinkageCheckerFilter element has zero or more LinkageError elements. *
  • A LinkageError element has at least one of Target element and Source element. *
  • A Target element has a Package, Class, Method, or Field element. A Source element has an * Artifact, Package, or Class element. *
  • Method and Field elements have “className” attribute. *
* *

Each type of the element works as a corresponding matcher, such as LinkageErrorMatcher for a * LinkageError element and SourceMatcher for Source element. Given a linkage error, they work as * below: * *

    *
  • A LinkageErrorMatcher matches when all of its child elements match the linkage error. *
  • A SourceMatcher matches a linkage error when the source class of the error matches its * child element. *
  • A TargetMatcher matches a linkage error when the target symbol (class, method, or field) of * the error matches its child element. *
  • A PackageMatcher matches the classes that have Java package specified by its name field. * Prefix to specify child packages. *
  • A ClassMatcher matches the class specified by its name attribute. ArtifactMatcher, * PackageMatcher, and ClassMatcher also match methods and fields on their matching classes. *
  • A MethodMatcher matches method symbol specified by className and name attribute. *
  • A FieldMatcher matches field symbol specified by className and name attribute. *
*/ @SuppressWarnings("deprecation") class ExclusionFiles { private static final XMLEventFactory eventFactory = XMLEventFactory.newInstance(); private static final QName LINKAGE_CHECKER_FILTER_TAG = QName.valueOf("LinkageCheckerFilter"); private static final QName CLASS_TAG = QName.valueOf("Class"); private static final QName LINKAGE_ERROR_TAG = QName.valueOf("LinkageError"); private static final QName TARGET_TAG = QName.valueOf("Target"); private static final QName SOURCE_TAG = QName.valueOf("Source"); private static final QName METHOD_TAG = QName.valueOf("Method"); private static final QName FIELD_TAG = QName.valueOf("Field"); static ImmutableList parse(Path exclusionFile) throws SAXException, IOException, VerifierConfigurationException { InputSource inputSource = new InputSource(Files.newInputStream(exclusionFile)); inputSource.setSystemId(exclusionFile.toUri().toString()); return parse(inputSource); } static ImmutableList parse(URL exclusionFile) throws SAXException, IOException, VerifierConfigurationException { InputSource inputSource = new InputSource(exclusionFile.openStream()); inputSource.setSystemId(exclusionFile.toString()); return parse(inputSource); } private static ImmutableList parse(InputSource inputSource) throws SAXException, IOException, VerifierConfigurationException { XMLReader reader = createXmlReader(); ExclusionFileHandler handler = new ExclusionFileHandler(); reader.setContentHandler(handler); reader.parse(inputSource); return handler.getMatchers(); } private static XMLReader createXmlReader() throws SAXException, IOException, VerifierConfigurationException { // Validate and parse XML files in one pass using Jing validator as a filter. // http://iso-relax.sourceforge.net/JARV/JARV.html#use_42 VerifierFactory factory = VerifierFactory.newInstance("http://relaxng.org/ns/structure/1.0"); InputStream linkageCheckerSchema = ExclusionFiles.class.getClassLoader().getResourceAsStream("linkage-checker-exclusion.rng"); Schema schema = factory.compileSchema(linkageCheckerSchema); Verifier verifier = schema.newVerifier(); // DraconianErrorHandler throws SAXException upon invalid structure verifier.setErrorHandler(new DraconianErrorHandler()); VerifierFilter filter = verifier.getVerifierFilter(); filter.setParent(XMLReaderFactory.createXMLReader()); return filter; } /** * Writes {@code linkageProblems} as exclusion rules into {@code outputFile}. * It creates the file if it doesn't exist, and overwrites it if it does. */ static void write(Path outputFile, Set linkageProblems) throws IOException, XMLStreamException, TransformerException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); XMLEventWriter writer = null; try { writer = XMLOutputFactory.newInstance().createXMLEventWriter(buffer); writer.add(eventFactory.createStartDocument()); writer.add(eventFactory.createStartElement(LINKAGE_CHECKER_FILTER_TAG, null, null)); for (LinkageProblem linkageProblem : linkageProblems) { writeXmlLinkageProblem(writer, linkageProblem); } writer.add(eventFactory.createEndElement(LINKAGE_CHECKER_FILTER_TAG, null)); writer.add(eventFactory.createEndDocument()); } finally { if (writer != null) { writer.close(); } } try (OutputStream outputStream = Files.newOutputStream(outputFile)) { insertIndent(new ByteArrayInputStream(buffer.toByteArray()), outputStream); } } private static void insertIndent(InputStream inputStream, OutputStream outputStream) throws TransformerException { // Prefer OpenJDK's default Transformer, rather than the one in net.sf.saxon:Saxon-HE. The // latter does not recognize "{http://xml.apache.org/xslt}indent-amount" property. System.setProperty( "javax.xml.transform.TransformerFactory", "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"); TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer indentTransformer = transformerFactory.newTransformer(); indentTransformer.setOutputProperty(OutputKeys.INDENT, "yes"); // OpenJDK's default Transformer recognizes this property indentTransformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); // Add new line character after doctype declaration indentTransformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "yes"); indentTransformer.transform(new StreamSource(inputStream), new StreamResult(outputStream)); } private static void writeXmlLinkageProblem(XMLEventWriter writer, LinkageProblem linkageProblem) throws XMLStreamException { writer.add(eventFactory.createStartElement(LINKAGE_ERROR_TAG, null, null)); writer.add(eventFactory.createStartElement(TARGET_TAG, null, null)); writeXmlElement(writer, linkageProblem.getSymbol()); writer.add(eventFactory.createEndElement(TARGET_TAG, null)); writer.add(eventFactory.createStartElement(SOURCE_TAG, null, null)); writeXmlElement(writer, linkageProblem.getSourceClass()); writer.add(eventFactory.createEndElement(SOURCE_TAG, null)); writer.add(eventFactory.createEndElement(LINKAGE_ERROR_TAG, null)); } private static void writeXmlElement(XMLEventWriter writer, Symbol symbol) throws XMLStreamException { if (symbol instanceof ClassSymbol) { Attribute className = eventFactory.createAttribute("name", symbol.getClassBinaryName()); StartElement event = eventFactory.createStartElement(CLASS_TAG, ImmutableList.of(className).iterator(), null); writer.add(event); writer.add(eventFactory.createEndElement(CLASS_TAG, null)); } else if (symbol instanceof MethodSymbol) { MethodSymbol methodSymbol = (MethodSymbol) symbol; Attribute className = eventFactory.createAttribute("className", symbol.getClassBinaryName()); Attribute methodName = eventFactory.createAttribute("name", methodSymbol.getName()); StartElement event = eventFactory.createStartElement( METHOD_TAG, ImmutableList.of(className, methodName).iterator(), null); writer.add(event); writer.add(eventFactory.createEndElement(METHOD_TAG, null)); } else if (symbol instanceof FieldSymbol) { FieldSymbol fieldSymbol = (FieldSymbol) symbol; Attribute className = eventFactory.createAttribute("className", symbol.getClassBinaryName()); Attribute methodName = eventFactory.createAttribute("name", fieldSymbol.getName()); StartElement event = eventFactory.createStartElement( FIELD_TAG, ImmutableList.of(className, methodName).iterator(), null); writer.add(event); writer.add(eventFactory.createEndElement(FIELD_TAG, null)); } } private static void writeXmlElement(XMLEventWriter writer, ClassFile classFile) throws XMLStreamException { Attribute className = eventFactory.createAttribute("name", classFile.getBinaryName()); writer.add( eventFactory.createStartElement(CLASS_TAG, ImmutableList.of(className).iterator(), null)); writer.add(eventFactory.createEndElement(CLASS_TAG, null)); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy