| <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang=""><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>XMLConfigurationReader.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Apache Creadur RAT::Core</a> > <a href="index.source.html" class="el_package">org.apache.rat.configuration</a> > <span class="el_source">XMLConfigurationReader.java</span></div><h1>XMLConfigurationReader.java</h1><pre class="source lang-java linenums">/* |
| * 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 org.apache.rat.configuration; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.Reader; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.net.URI; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.SortedSet; |
| import java.util.TreeSet; |
| import java.util.function.BiPredicate; |
| import java.util.function.Consumer; |
| import java.util.stream.Collectors; |
| |
| import javax.xml.parsers.DocumentBuilder; |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.ParserConfigurationException; |
| |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.commons.lang3.tuple.ImmutablePair; |
| import org.apache.commons.lang3.tuple.Pair; |
| import org.apache.rat.BuilderParams; |
| import org.apache.rat.ConfigurationException; |
| import org.apache.rat.ImplementationException; |
| import org.apache.rat.analysis.IHeaderMatcher; |
| import org.apache.rat.config.parameters.ComponentType; |
| import org.apache.rat.config.parameters.Description; |
| import org.apache.rat.config.parameters.DescriptionBuilder; |
| import org.apache.rat.configuration.builders.AbstractBuilder; |
| import org.apache.rat.license.ILicense; |
| import org.apache.rat.license.ILicenseFamily; |
| import org.apache.rat.utils.DefaultLog; |
| import org.w3c.dom.DOMException; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * A class that reads the XML configuration file format. |
| */ |
| public final class XMLConfigurationReader implements LicenseReader, MatcherReader { |
| /** The document we are building */ |
| private Document document; |
| /** The root element in the document */ |
| private final Element rootElement; |
| /** The families element in the document */ |
| private final Element familiesElement; |
| /** The licenses element in the document */ |
| private final Element licensesElement; |
| /** The approved element in the document */ |
| private final Element approvedElement; |
| /** The matchers element in the document */ |
| private final Element matchersElement; |
| /** The sorted set of licenses */ |
| private final SortedSet<ILicense> licenses; |
| /** The map of matcher ids to matcher */ |
| private final Map<String, IHeaderMatcher> matchers; |
| /** The builder parameters */ |
| private final BuilderParams builderParams; |
| /** The sorted set of license families */ |
| private final SortedSet<ILicenseFamily> licenseFamilies; |
| /** The sorted set of approved license family categories */ |
| private final SortedSet<String> approvedFamilies; |
| |
| /** |
| * Constructs the XML configuration reader. |
| */ |
| <span class="fc" id="L96"> public XMLConfigurationReader() {</span> |
| try { |
| <span class="fc" id="L98"> document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();</span> |
| <span class="nc" id="L99"> } catch (ParserConfigurationException e) {</span> |
| <span class="nc" id="L100"> throw new IllegalStateException("No XML parser defined", e);</span> |
| <span class="fc" id="L101"> }</span> |
| <span class="fc" id="L102"> rootElement = document.createElement(XMLConfig.ROOT);</span> |
| <span class="fc" id="L103"> document.appendChild(rootElement);</span> |
| <span class="fc" id="L104"> familiesElement = document.createElement(XMLConfig.FAMILIES);</span> |
| <span class="fc" id="L105"> rootElement.appendChild(familiesElement);</span> |
| <span class="fc" id="L106"> licensesElement = document.createElement(XMLConfig.LICENSES);</span> |
| <span class="fc" id="L107"> rootElement.appendChild(licensesElement);</span> |
| <span class="fc" id="L108"> approvedElement = document.createElement(XMLConfig.APPROVED);</span> |
| <span class="fc" id="L109"> rootElement.appendChild(approvedElement);</span> |
| <span class="fc" id="L110"> matchersElement = document.createElement(XMLConfig.MATCHERS);</span> |
| <span class="fc" id="L111"> rootElement.appendChild(matchersElement);</span> |
| <span class="fc" id="L112"> licenses = new TreeSet<>();</span> |
| <span class="fc" id="L113"> licenseFamilies = new TreeSet<>();</span> |
| <span class="fc" id="L114"> approvedFamilies = new TreeSet<>();</span> |
| <span class="fc" id="L115"> matchers = new HashMap<>();</span> |
| <span class="fc" id="L116"> builderParams = new BuilderParams() {</span> |
| @Override |
| public Map<String, IHeaderMatcher> matcherMap() { |
| <span class="fc" id="L119"> return matchers;</span> |
| } |
| |
| @Override |
| public SortedSet<ILicenseFamily> licenseFamilies() { |
| <span class="fc" id="L124"> return licenseFamilies;</span> |
| } |
| }; |
| <span class="fc" id="L127"> }</span> |
| |
| /** |
| * Creates textual representation of a node for display. |
| * @param node the node to create the textual representation of. |
| * @return The textual representation of the node for display. |
| */ |
| private String nodeText(final Node node) { |
| <span class="fc" id="L135"> StringBuilder stringBuilder = new StringBuilder().append("<").append(node.getNodeName());</span> |
| <span class="fc" id="L136"> NamedNodeMap attr = node.getAttributes();</span> |
| <span class="fc bfc" id="L137" title="All 2 branches covered."> for (int i = 0; i < attr.getLength(); i++) {</span> |
| <span class="fc" id="L138"> Node n = attr.item(i);</span> |
| <span class="fc" id="L139"> stringBuilder.append(String.format(" %s='%s'", n.getNodeName(), n.getNodeValue()));</span> |
| } |
| <span class="fc" id="L141"> return stringBuilder.append(">").toString();</span> |
| } |
| |
| @Override |
| public void addLicenses(final URI uri) { |
| <span class="fc" id="L146"> read(uri);</span> |
| <span class="fc" id="L147"> }</span> |
| |
| /** |
| * Read xml from a reader. |
| * @param reader the reader to read XML from. |
| */ |
| public void read(final Reader reader) { |
| DocumentBuilder builder; |
| try { |
| <span class="fc" id="L156"> builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();</span> |
| <span class="nc" id="L157"> } catch (ParserConfigurationException e) {</span> |
| <span class="nc" id="L158"> throw new ConfigurationException("Unable to create DOM builder", e);</span> |
| <span class="fc" id="L159"> }</span> |
| |
| try { |
| <span class="fc" id="L162"> add(builder.parse(new InputSource(reader)));</span> |
| <span class="nc" id="L163"> } catch (SAXException | IOException e) {</span> |
| <span class="nc" id="L164"> throw new ConfigurationException("Unable to read inputSource", e);</span> |
| <span class="fc" id="L165"> }</span> |
| <span class="fc" id="L166"> }</span> |
| |
| /** |
| * Read the uris and extract the DOM information to create new objects. |
| * @param uris The URIs to read. |
| */ |
| public void read(final URI... uris) { |
| DocumentBuilder builder; |
| try { |
| <span class="fc" id="L175"> builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();</span> |
| <span class="nc" id="L176"> } catch (ParserConfigurationException e) {</span> |
| <span class="nc" id="L177"> throw new ConfigurationException("Unable to create DOM builder", e);</span> |
| <span class="fc" id="L178"> }</span> |
| <span class="fc bfc" id="L179" title="All 2 branches covered."> for (URI uri : uris) {</span> |
| <span class="fc" id="L180"> try (InputStream inputStream = uri.toURL().openStream()) {</span> |
| <span class="fc" id="L181"> add(builder.parse(inputStream));</span> |
| <span class="nc" id="L182"> } catch (SAXException | IOException e) {</span> |
| <span class="nc" id="L183"> throw new ConfigurationException("Unable to read uri: " + uri, e);</span> |
| <span class="fc" id="L184"> }</span> |
| } |
| <span class="fc" id="L186"> }</span> |
| |
| /** |
| * Applies the {@code consumer} to each node in the {@code list}. Generally used for extracting info from a |
| * {@code NodeList}. |
| * @param list the NodeList to process |
| * @param consumer the consumer to apply to each node in the list. |
| */ |
| private void nodeListConsumer(final NodeList list, final Consumer<Node> consumer) { |
| <span class="fc bfc" id="L195" title="All 2 branches covered."> for (int i = 0; i < list.getLength(); i++) {</span> |
| <span class="fc" id="L196"> consumer.accept(list.item(i));</span> |
| } |
| <span class="fc" id="L198"> }</span> |
| |
| /** |
| * Merge the new document into the document that this reader processes is building. |
| * @param newDoc the Document to merge. |
| */ |
| public void add(final Document newDoc) { |
| <span class="fc" id="L205"> nodeListConsumer(newDoc.getElementsByTagName(XMLConfig.FAMILIES), nl -> nodeListConsumer(nl.getChildNodes(),</span> |
| <span class="fc" id="L206"> n -> familiesElement.appendChild(rootElement.getOwnerDocument().adoptNode(n.cloneNode(true)))));</span> |
| <span class="fc" id="L207"> nodeListConsumer(newDoc.getElementsByTagName(XMLConfig.LICENSE),</span> |
| <span class="fc" id="L208"> n -> licensesElement.appendChild(rootElement.getOwnerDocument().adoptNode(n.cloneNode(true))));</span> |
| <span class="fc" id="L209"> nodeListConsumer(newDoc.getElementsByTagName(XMLConfig.APPROVED), nl -> nodeListConsumer(nl.getChildNodes(),</span> |
| <span class="fc" id="L210"> n -> approvedElement.appendChild(rootElement.getOwnerDocument().adoptNode(n.cloneNode(true)))));</span> |
| <span class="fc" id="L211"> nodeListConsumer(newDoc.getElementsByTagName(XMLConfig.MATCHERS),</span> |
| <span class="fc" id="L212"> n -> matchersElement.appendChild(rootElement.getOwnerDocument().adoptNode(n.cloneNode(true))));</span> |
| <span class="fc" id="L213"> }</span> |
| |
| /** |
| * Get a map of Node attribute names to values. |
| * @param node The node to process |
| * @return the map of attributes on the node. |
| */ |
| private Map<String, String> attributes(final Node node) { |
| <span class="fc" id="L221"> NamedNodeMap nnm = node.getAttributes();</span> |
| <span class="fc" id="L222"> Map<String, String> result = new HashMap<>();</span> |
| <span class="fc bfc" id="L223" title="All 2 branches covered."> for (int i = 0; i < nnm.getLength(); i++) {</span> |
| <span class="fc" id="L224"> Node n = nnm.item(i);</span> |
| <span class="fc" id="L225"> result.put(n.getNodeName(), n.getNodeValue());</span> |
| } |
| <span class="fc" id="L227"> return result;</span> |
| } |
| |
| /** |
| * Finds the setter description property in the builder and set it with the value. |
| * @param desc The description for the setter. |
| * @param builder The builder to set the value in. |
| * @param value The value to set. |
| */ |
| private void callSetter(final Description desc, final IHeaderMatcher.Builder builder, final Object value) { |
| try { |
| <span class="fc" id="L238"> desc.setter(builder.getClass()).invoke(builder, value);</span> |
| <span class="nc" id="L239"> } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException</span> |
| | SecurityException e) { |
| <span class="nc" id="L241"> throw new ConfigurationException(e.getMessage(), e);</span> |
| <span class="fc" id="L242"> }</span> |
| <span class="fc" id="L243"> }</span> |
| |
| /** |
| * For any children of description that are BUILD_PARAMETERS set the builder property. |
| * @param description The description for the builder. |
| * @param builder the builder to set the properties in. |
| */ |
| private void processBuilderParams(final Description description, final IHeaderMatcher.Builder builder) { |
| <span class="fc bfc" id="L251" title="All 2 branches covered."> for (Description desc : description.childrenOfType(ComponentType.BUILD_PARAMETER)) {</span> |
| <span class="fc" id="L252"> Method m = builderParams.get(desc.getCommonName());</span> |
| try { |
| <span class="fc" id="L254"> callSetter(desc, builder, m.invoke(builderParams));</span> |
| <span class="nc" id="L255"> } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {</span> |
| <span class="nc" id="L256"> throw ImplementationException.makeInstance(e);</span> |
| <span class="fc" id="L257"> }</span> |
| <span class="fc" id="L258"> }</span> |
| <span class="fc" id="L259"> }</span> |
| |
| /** |
| * Processes a list of children by passing each child node and the description |
| * of the child (if any) to the BiPredicate. If there is not a child description |
| * for the node it is ignored. If the node is processed it is removed from list |
| * of children. |
| * @param description the Description of the node being processed |
| * @param children the child nodes of that node. |
| * @param childProcessor the function that handles the processing of the child |
| * node. |
| */ |
| private void processChildren(final Description description, final List<Node> children, |
| final BiPredicate<Node, Description> childProcessor) { |
| <span class="fc" id="L273"> Iterator<Node> iter = children.iterator();</span> |
| <span class="fc bfc" id="L274" title="All 2 branches covered."> while (iter.hasNext()) {</span> |
| <span class="fc" id="L275"> Node child = iter.next();</span> |
| <span class="fc" id="L276"> Description childDescription = description.getChildren().get(child.getNodeName());</span> |
| <span class="fc bfc" id="L277" title="All 2 branches covered."> if (childDescription != null) {</span> |
| <span class="pc bpc" id="L278" title="1 of 2 branches missed."> if (childProcessor.test(child, childDescription)) {</span> |
| <span class="fc" id="L279"> iter.remove();</span> |
| } |
| } |
| <span class="fc" id="L282"> }</span> |
| <span class="fc" id="L283"> }</span> |
| |
| /** |
| * Creates a child node processor for the builder described by the description. |
| * @param builder The builder to set properties in. |
| * @param description the description of the builder. |
| * @return child node. |
| */ |
| private BiPredicate<Node, Description> matcherChildNodeProcessor(final AbstractBuilder builder, final Description description) { |
| <span class="fc" id="L292"> return (child, childDescription) -> {</span> |
| <span class="nc bnc" id="L293" title="All 4 branches missed."> switch (childDescription.getType()) {</span> |
| case LICENSE: |
| case BUILD_PARAMETER: |
| <span class="nc" id="L296"> throw new ConfigurationException(String.format(</span> |
| <span class="nc" id="L297"> "%s may not be used as an enclosed matcher. %s '%s' found in '%s'", childDescription.getType(),</span> |
| <span class="nc" id="L298"> childDescription.getType(), childDescription.getCommonName(), description.getCommonName()));</span> |
| case MATCHER: |
| <span class="nc" id="L300"> AbstractBuilder b = parseMatcher(child);</span> |
| <span class="nc" id="L301"> callSetter(b.getDescription(), builder, b);</span> |
| <span class="nc" id="L302"> return true;</span> |
| case PARAMETER: |
| <span class="nc bnc" id="L304" title="All 2 branches missed."> if (!XMLConfig.isInlineNode(description.getCommonName(), childDescription.getCommonName())</span> |
| <span class="nc bnc" id="L305" title="All 2 branches missed."> || childDescription.getChildType() == String.class) {</span> |
| <span class="nc" id="L306"> callSetter(childDescription, builder, child.getTextContent());</span> |
| } else { |
| <span class="nc" id="L308"> callSetter(childDescription, builder, parseMatcher(child));</span> |
| } |
| <span class="nc" id="L310"> return true;</span> |
| } |
| <span class="nc" id="L312"> return false;</span> |
| }; |
| } |
| |
| /** |
| * Sets the value of the element described by the description in the builder with the value from childDescription. |
| * @param description the property in the builder to set. |
| * @param childDescription the description of the child property to extract. |
| * @param builder the builder to set the value in. |
| * @param child the child to extract the value from. |
| */ |
| private void setValue(final Description description, final Description childDescription, final IHeaderMatcher.Builder builder, |
| final Node child) { |
| <span class="nc bnc" id="L325" title="All 2 branches missed."> if (childDescription.getChildType() == String.class) {</span> |
| <span class="nc" id="L326"> callSetter(description, builder, child.getTextContent());</span> |
| } else { |
| <span class="nc" id="L328"> callSetter(description, builder, parseMatcher(child));</span> |
| } |
| <span class="nc" id="L330"> }</span> |
| |
| /** |
| * Process the ELEMENT_NODEs children of the parent whose names match child |
| * descriptions. All children of children are processed with the childProcessor. If |
| * the childProcessor handles the node it is not included in the resulting list. |
| * @param description the Description of the parent node. |
| * @param parent the node being processed |
| * @param childProcessor the BiProcessor to handle process each child. If the |
| * processor handles the child it must return {@code true}. |
| * @return A Pair comprising a boolean flag indicating children were found, and |
| * a list of all child nodes that were not processed by the childProcessor. |
| */ |
| private Pair<Boolean, List<Node>> processChildNodes(final Description description, final Node parent, |
| final BiPredicate<Node, Description> childProcessor) { |
| try { |
| <span class="fc" id="L346"> boolean foundChildren = false;</span> |
| <span class="fc" id="L347"> List<Node> children = new ArrayList<>();</span> |
| // check XML child nodes. |
| <span class="fc bfc" id="L349" title="All 2 branches covered."> if (parent.hasChildNodes()) {</span> |
| |
| <span class="fc" id="L351"> nodeListConsumer(parent.getChildNodes(), n -> {</span> |
| <span class="fc bfc" id="L352" title="All 2 branches covered."> if (n.getNodeType() == Node.ELEMENT_NODE) {</span> |
| <span class="fc" id="L353"> children.add(n);</span> |
| } |
| <span class="fc" id="L355"> });</span> |
| <span class="fc bfc" id="L356" title="All 2 branches covered."> foundChildren = !children.isEmpty();</span> |
| <span class="fc bfc" id="L357" title="All 2 branches covered."> if (foundChildren) {</span> |
| <span class="fc" id="L358"> processChildren(description, children, childProcessor);</span> |
| } |
| } |
| <span class="fc" id="L361"> return new ImmutablePair<>(foundChildren, children);</span> |
| <span class="nc" id="L362"> } catch (RuntimeException exception) {</span> |
| <span class="nc" id="L363"> DefaultLog.getInstance().error(String.format("Child node extraction error in: '%s'", nodeText(parent)));</span> |
| <span class="nc" id="L364"> throw exception;</span> |
| } |
| } |
| |
| /** |
| * Creates a Builder from a Matcher node. |
| * @param matcherNode the Matcher node to parse. |
| * @return The Builder for the matcher described by the node. |
| */ |
| private AbstractBuilder parseMatcher(final Node matcherNode) { |
| <span class="fc" id="L374"> final AbstractBuilder builder = MatcherBuilderTracker.getMatcherBuilder(matcherNode.getNodeName());</span> |
| |
| try { |
| <span class="fc" id="L377"> final Description description = DescriptionBuilder.buildMap(builder.getClass());</span> |
| <span class="pc bpc" id="L378" title="1 of 2 branches missed."> if (description == null) {</span> |
| <span class="nc" id="L379"> throw new ConfigurationException(String.format("Unable to build description for %s", builder.getClass()));</span> |
| } |
| <span class="fc" id="L381"> processBuilderParams(description, builder);</span> |
| |
| // process the attributes |
| <span class="fc" id="L384"> description.setChildren(builder, attributes(matcherNode));</span> |
| |
| // check XML child nodes. |
| <span class="fc" id="L387"> Pair<Boolean, List<Node>> pair = processChildNodes(description, matcherNode,</span> |
| <span class="fc" id="L388"> matcherChildNodeProcessor(builder, description));</span> |
| <span class="fc" id="L389"> boolean foundChildren = pair.getLeft();</span> |
| <span class="fc" id="L390"> List<Node> children = pair.getRight();</span> |
| |
| // check for inline nodes that can accept child nodes. |
| <span class="fc" id="L393"> List<Description> childDescriptions = description.getChildren().values().stream()</span> |
| <span class="fc" id="L394"> .filter(d -> XMLConfig.isInlineNode(description.getCommonName(), d.getCommonName()))</span> |
| <span class="fc" id="L395"> .collect(Collectors.toList());</span> |
| |
| <span class="fc bfc" id="L397" title="All 2 branches covered."> for (Description childDescription : childDescriptions) {</span> |
| <span class="pc bpc" id="L398" title="1 of 2 branches missed."> if (XMLConfig.isInlineNode(description.getCommonName(), childDescription.getCommonName())) {</span> |
| // can only process text inline if there were no child nodes. |
| <span class="fc bfc" id="L400" title="All 2 branches covered."> if (childDescription.getChildType() == String.class) {</span> |
| <span class="pc bpc" id="L401" title="1 of 2 branches missed."> if (!foundChildren) {</span> |
| <span class="fc" id="L402"> callSetter(childDescription, builder, matcherNode.getTextContent());</span> |
| } |
| } else { |
| <span class="fc" id="L405"> Iterator<Node> iter = children.iterator();</span> |
| <span class="fc bfc" id="L406" title="All 2 branches covered."> while (iter.hasNext()) {</span> |
| <span class="fc" id="L407"> Node child = iter.next();</span> |
| <span class="fc" id="L408"> callSetter(childDescription, builder, parseMatcher(child));</span> |
| <span class="fc" id="L409"> iter.remove();</span> |
| <span class="fc" id="L410"> }</span> |
| <span class="fc" id="L411"> }</span> |
| |
| } else { |
| <span class="nc" id="L414"> processChildren(description, children, (child, childD) -> {</span> |
| <span class="nc bnc" id="L415" title="All 2 branches missed."> if (childD.getChildType().equals(description.getChildType())) {</span> |
| <span class="nc" id="L416"> setValue(childDescription, childD, builder, child);</span> |
| <span class="nc" id="L417"> return true;</span> |
| } |
| <span class="nc" id="L419"> return false;</span> |
| }); |
| } |
| |
| <span class="fc" id="L423"> }</span> |
| |
| <span class="pc bpc" id="L425" title="1 of 2 branches missed."> if (!children.isEmpty()) {</span> |
| <span class="nc" id="L426"> children.forEach(n -> DefaultLog.getInstance().warn(String.format("unrecognised child node '%s' in node '%s'%n",</span> |
| <span class="nc" id="L427"> n.getNodeName(), matcherNode.getNodeName())));</span> |
| } |
| |
| <span class="nc" id="L430"> } catch (DOMException e) {</span> |
| <span class="nc" id="L431"> DefaultLog.getInstance().error(String.format("Matcher error in: '%s'", nodeText(matcherNode)));</span> |
| <span class="nc" id="L432"> throw new ConfigurationException(e);</span> |
| <span class="fc" id="L433"> }</span> |
| <span class="fc bfc" id="L434" title="All 2 branches covered."> return builder.hasId() ? new IDRecordingBuilder(matchers, builder) : builder;</span> |
| } |
| |
| private BiPredicate<Node, Description> licenseChildNodeProcessor(final ILicense.Builder builder, final Description description) { |
| <span class="fc" id="L438"> return (child, childDescription) -> {</span> |
| <span class="pc bpc" id="L439" title="4 of 5 branches missed."> switch (childDescription.getType()) {</span> |
| case LICENSE: |
| <span class="nc" id="L441"> throw new ConfigurationException(String.format(</span> |
| <span class="nc" id="L442"> "%s may not be enclosed in another license. %s '%s' found in '%s'", childDescription.getType(),</span> |
| <span class="nc" id="L443"> childDescription.getType(), childDescription.getCommonName(), description.getCommonName()));</span> |
| case BUILD_PARAMETER: |
| <span class="nc" id="L445"> break;</span> |
| case MATCHER: |
| <span class="nc" id="L447"> AbstractBuilder b = parseMatcher(child);</span> |
| <span class="nc" id="L448"> callSetter(b.getDescription(), builder, b);</span> |
| <span class="nc" id="L449"> return true;</span> |
| case PARAMETER: |
| <span class="pc bpc" id="L451" title="1 of 2 branches missed."> if (!XMLConfig.isLicenseChild(childDescription.getCommonName())</span> |
| <span class="pc bpc" id="L452" title="1 of 2 branches missed."> || childDescription.getChildType() == String.class) {</span> |
| <span class="fc" id="L453"> callSetter(childDescription, builder, child.getTextContent());</span> |
| } else { |
| <span class="nc" id="L455"> callSetter(childDescription, builder, parseMatcher(child));</span> |
| } |
| <span class="fc" id="L457"> return true;</span> |
| } |
| <span class="nc" id="L459"> return false;</span> |
| }; |
| } |
| |
| /** |
| * Parses a license from a license node. |
| * @param licenseNode the node to parse. |
| * @return the License definition. |
| */ |
| private ILicense parseLicense(final Node licenseNode) { |
| try { |
| <span class="fc" id="L470"> ILicense.Builder builder = ILicense.builder();</span> |
| // get the description for the builder |
| <span class="fc" id="L472"> Description description = builder.getDescription();</span> |
| // set the BUILDER_PARAM options from the description |
| <span class="fc" id="L474"> processBuilderParams(description, builder);</span> |
| // set the children from attributes. |
| <span class="fc" id="L476"> description.setChildren(builder, attributes(licenseNode));</span> |
| // set children from the child nodes |
| <span class="fc" id="L478"> Pair<Boolean, List<Node>> pair = processChildNodes(description, licenseNode,</span> |
| <span class="fc" id="L479"> licenseChildNodeProcessor(builder, description));</span> |
| <span class="fc" id="L480"> List<Node> children = pair.getRight();</span> |
| |
| // check for inline nodes that can accept child nodes. |
| <span class="fc" id="L483"> List<Description> childDescriptions = description.getChildren().values().stream()</span> |
| <span class="fc" id="L484"> .filter(d -> XMLConfig.isLicenseInline(d.getCommonName())).collect(Collectors.toList());</span> |
| <span class="fc bfc" id="L485" title="All 2 branches covered."> for (Description childDescription : childDescriptions) {</span> |
| <span class="fc" id="L486"> Iterator<Node> iter = children.iterator();</span> |
| <span class="fc bfc" id="L487" title="All 2 branches covered."> while (iter.hasNext()) {</span> |
| <span class="fc" id="L488"> callSetter(childDescription, builder, parseMatcher(iter.next()));</span> |
| <span class="fc" id="L489"> iter.remove();</span> |
| } |
| <span class="fc" id="L491"> }</span> |
| |
| <span class="pc bpc" id="L493" title="1 of 2 branches missed."> if (!children.isEmpty()) {</span> |
| <span class="nc" id="L494"> children.forEach(n -> DefaultLog.getInstance().warn(String.format("unrecognised child node '%s' in node '%s'%n",</span> |
| <span class="nc" id="L495"> n.getNodeName(), licenseNode.getNodeName())));</span> |
| } |
| <span class="fc" id="L497"> return builder.build();</span> |
| <span class="fc" id="L498"> } catch (RuntimeException exception) {</span> |
| <span class="fc" id="L499"> DefaultLog.getInstance().error(String.format("License error in: '%s'", nodeText(licenseNode)));</span> |
| <span class="fc" id="L500"> throw exception;</span> |
| } |
| } |
| |
| @Override |
| public SortedSet<ILicense> readLicenses() { |
| <span class="pc bpc" id="L506" title="1 of 2 branches missed."> if (this.document != null) {</span> |
| <span class="fc" id="L507"> readFamilies();</span> |
| <span class="fc" id="L508"> readMatcherBuilders();</span> |
| <span class="pc bpc" id="L509" title="1 of 2 branches missed."> if (licenses.isEmpty()) {</span> |
| <span class="fc" id="L510"> nodeListConsumer(document.getElementsByTagName(XMLConfig.LICENSE), x -> licenses.add(parseLicense(x)));</span> |
| <span class="fc" id="L511"> document = null;</span> |
| } |
| } |
| <span class="fc" id="L514"> return Collections.unmodifiableSortedSet(licenses);</span> |
| } |
| |
| @Override |
| public SortedSet<ILicenseFamily> readFamilies() { |
| <span class="pc bpc" id="L519" title="1 of 2 branches missed."> if (licenseFamilies.isEmpty()) {</span> |
| <span class="fc" id="L520"> nodeListConsumer(document.getElementsByTagName(XMLConfig.FAMILIES),</span> |
| <span class="fc" id="L521"> x -> nodeListConsumer(x.getChildNodes(), this::parseFamily));</span> |
| <span class="fc" id="L522"> nodeListConsumer(document.getElementsByTagName(XMLConfig.APPROVED),</span> |
| <span class="fc" id="L523"> x -> nodeListConsumer(x.getChildNodes(), this::parseApproved));</span> |
| } |
| <span class="fc" id="L525"> return Collections.unmodifiableSortedSet(licenseFamilies);</span> |
| } |
| |
| /** |
| * Parses a license family from a map that contains the ID and Name attributes. |
| * @param attributes the map of attributes. |
| * @return the license family defined in the map. |
| */ |
| private ILicenseFamily parseFamily(final Map<String, String> attributes) { |
| <span class="pc bpc" id="L534" title="1 of 2 branches missed."> if (attributes.containsKey(XMLConfig.ATT_ID)) {</span> |
| <span class="fc" id="L535"> ILicenseFamily.Builder builder = ILicenseFamily.builder();</span> |
| <span class="fc" id="L536"> builder.setLicenseFamilyCategory(attributes.get(XMLConfig.ATT_ID));</span> |
| <span class="fc" id="L537"> builder.setLicenseFamilyName(</span> |
| <span class="fc" id="L538"> StringUtils.defaultIfBlank(attributes.get(XMLConfig.ATT_NAME), attributes.get(XMLConfig.ATT_ID)));</span> |
| <span class="fc" id="L539"> return builder.build();</span> |
| } |
| <span class="nc" id="L541"> return null;</span> |
| } |
| |
| /** |
| * Parses a license family node into a license family and adds it to the license families set. |
| * @param familyNode the node to parse. |
| */ |
| private void parseFamily(final Node familyNode) { |
| <span class="fc bfc" id="L549" title="All 2 branches covered."> if (XMLConfig.FAMILY.equals(familyNode.getNodeName())) {</span> |
| try { |
| <span class="fc" id="L551"> ILicenseFamily result = parseFamily(attributes(familyNode));</span> |
| <span class="pc bpc" id="L552" title="1 of 2 branches missed."> if (result == null) {</span> |
| <span class="nc" id="L553"> throw new ConfigurationException(</span> |
| <span class="nc" id="L554"> String.format("families/family tag requires %s attribute", XMLConfig.ATT_ID));</span> |
| } |
| <span class="fc" id="L556"> licenseFamilies.add(result);</span> |
| <span class="nc" id="L557"> } catch (RuntimeException exception) {</span> |
| <span class="nc" id="L558"> DefaultLog.getInstance().error(String.format("Family error in: '%s'", nodeText(familyNode)));</span> |
| <span class="nc" id="L559"> throw exception;</span> |
| <span class="fc" id="L560"> }</span> |
| } |
| <span class="fc" id="L562"> }</span> |
| |
| /** |
| * Parse an approved License family and adds it to the set of license families as well as the |
| * set of approved license families. |
| * @param approvedNode the node to parse. |
| */ |
| private void parseApproved(final Node approvedNode) { |
| <span class="fc bfc" id="L570" title="All 2 branches covered."> if (XMLConfig.FAMILY.equals(approvedNode.getNodeName())) {</span> |
| try { |
| <span class="fc" id="L572"> Map<String, String> attributes = attributes(approvedNode);</span> |
| <span class="pc bpc" id="L573" title="1 of 2 branches missed."> if (attributes.containsKey(XMLConfig.ATT_LICENSE_REF)) {</span> |
| <span class="fc" id="L574"> approvedFamilies.add(attributes.get(XMLConfig.ATT_LICENSE_REF));</span> |
| <span class="nc bnc" id="L575" title="All 2 branches missed."> } else if (attributes.containsKey(XMLConfig.ATT_ID)) {</span> |
| <span class="nc" id="L576"> ILicenseFamily target = parseFamily(attributes);</span> |
| <span class="nc bnc" id="L577" title="All 2 branches missed."> if (target != null) {</span> |
| <span class="nc" id="L578"> licenseFamilies.add(target);</span> |
| <span class="nc" id="L579"> String familyCategory = target.getFamilyCategory();</span> |
| <span class="nc bnc" id="L580" title="All 2 branches missed."> if (StringUtils.isNotBlank(familyCategory)) {</span> |
| <span class="nc" id="L581"> approvedFamilies.add(familyCategory);</span> |
| } |
| } |
| <span class="nc" id="L584"> } else {</span> |
| <span class="nc" id="L585"> throw new ConfigurationException(String.format("family tag requires %s or %s attribute",</span> |
| XMLConfig.ATT_LICENSE_REF, XMLConfig.ATT_ID)); |
| } |
| <span class="nc" id="L588"> } catch (RuntimeException exception) {</span> |
| <span class="nc" id="L589"> DefaultLog.getInstance().error(String.format("Approved error in: '%s'", nodeText(approvedNode)));</span> |
| <span class="nc" id="L590"> throw exception;</span> |
| <span class="fc" id="L591"> }</span> |
| } |
| <span class="fc" id="L593"> }</span> |
| |
| ////////////////////////////////////////// MatcherReader methods |
| @Override |
| public SortedSet<String> approvedLicenseId() { |
| <span class="fc bfc" id="L598" title="All 2 branches covered."> if (licenses.isEmpty()) {</span> |
| <span class="fc" id="L599"> this.readLicenses();</span> |
| } |
| <span class="fc bfc" id="L601" title="All 2 branches covered."> if (approvedFamilies.isEmpty()) {</span> |
| <span class="fc" id="L602"> SortedSet<String> result = new TreeSet<>();</span> |
| <span class="fc" id="L603"> licenses.stream().map(x -> x.getLicenseFamily().getFamilyCategory()).forEach(result::add);</span> |
| <span class="fc" id="L604"> return result;</span> |
| } |
| <span class="fc" id="L606"> return Collections.unmodifiableSortedSet(approvedFamilies);</span> |
| } |
| |
| private void parseMatcherBuilder(final Node classNode) { |
| try { |
| <span class="fc" id="L611"> Map<String, String> attributes = attributes(classNode);</span> |
| <span class="pc bpc" id="L612" title="1 of 2 branches missed."> if (attributes.get(XMLConfig.ATT_CLASS_NAME) == null) {</span> |
| <span class="nc" id="L613"> throw new ConfigurationException("matcher must have a " + XMLConfig.ATT_CLASS_NAME + " attribute");</span> |
| } |
| <span class="fc" id="L615"> MatcherBuilderTracker.addBuilder(attributes.get(XMLConfig.ATT_CLASS_NAME), attributes.get(XMLConfig.ATT_NAME));</span> |
| <span class="nc" id="L616"> } catch (RuntimeException exception) {</span> |
| <span class="nc" id="L617"> DefaultLog.getInstance().error(String.format("Matcher error in: '%s'", nodeText(classNode)));</span> |
| <span class="nc" id="L618"> throw exception;</span> |
| <span class="fc" id="L619"> }</span> |
| <span class="fc" id="L620"> }</span> |
| |
| @Override |
| public void readMatcherBuilders() { |
| <span class="fc" id="L624"> nodeListConsumer(document.getElementsByTagName(XMLConfig.MATCHER), this::parseMatcherBuilder);</span> |
| <span class="fc" id="L625"> }</span> |
| |
| @Override |
| public void addMatchers(final URI uri) { |
| <span class="fc" id="L629"> read(uri);</span> |
| <span class="fc" id="L630"> }</span> |
| |
| /** |
| * An abstract builder that delegates to another abstract builder. |
| */ |
| static class IDRecordingBuilder extends AbstractBuilder { |
| /** The builder we are delegating to */ |
| private final AbstractBuilder delegate; |
| /** |
| * The map of matchers that the system is building during processing. |
| * We will utilize this to set the matcher value later. |
| */ |
| private final Map<String, IHeaderMatcher> matchers; |
| |
| <span class="fc" id="L644"> IDRecordingBuilder(final Map<String, IHeaderMatcher> matchers, final AbstractBuilder delegate) {</span> |
| <span class="fc" id="L645"> this.delegate = delegate;</span> |
| <span class="fc" id="L646"> this.matchers = matchers;</span> |
| <span class="fc" id="L647"> setId(delegate.getId());</span> |
| <span class="fc" id="L648"> }</span> |
| |
| @Override |
| public IHeaderMatcher build() { |
| <span class="fc" id="L652"> IHeaderMatcher result = delegate.build();</span> |
| <span class="fc" id="L653"> matchers.put(result.getId(), result);</span> |
| <span class="fc" id="L654"> return result;</span> |
| } |
| |
| @Override |
| public Description getDescription() { |
| <span class="nc" id="L659"> return delegate.getDescription();</span> |
| } |
| } |
| } |
| </pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.14.202510111229</span></div></body></html> |