diff --git a/apache-rat-core/pom.xml b/apache-rat-core/pom.xml
index 6958dec9e..1b31b093b 100644
--- a/apache-rat-core/pom.xml
+++ b/apache-rat-core/pom.xml
@@ -25,8 +25,7 @@
apache-rat-corejarApache Creadur Rat::Core
- The core functionality, shared by the Ant tasks
- and the Maven plugin.
+ The core functionality of Rat that is used by all Clients.
diff --git a/apache-rat-core/src/main/java/org/apache/rat/BuilderParams.java b/apache-rat-core/src/main/java/org/apache/rat/BuilderParams.java
new file mode 100644
index 000000000..530d43cad
--- /dev/null
+++ b/apache-rat-core/src/main/java/org/apache/rat/BuilderParams.java
@@ -0,0 +1,64 @@
+/*
+ * 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;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.SortedSet;
+
+import org.apache.rat.analysis.IHeaderMatcher;
+import org.apache.rat.license.ILicenseFamily;
+
+/**
+ * Parameters that can be set by the BUILDER_PARAM ComponentType. The name of the method listed here
+ * should be the same as the name specified in the ConfigComponent.
+ */
+public interface BuilderParams {
+
+ /**
+ * Gets one of the contained methods by name.
+ *
+ * @param name the name of the method to get.
+ * @return the Method.
+ */
+ default Method get(String name) {
+ try {
+ return this.getClass().getMethod(name);
+ } catch (NoSuchMethodException e) {
+ throw new ImplementationException(String.format("method '%s' is not found in %s", name, this.getClass()));
+ } catch (IllegalArgumentException e) {
+ throw new ImplementationException(
+ String.format("method '%s' in %s can not be retrieved", name, this.getClass()), e);
+ }
+ }
+
+ /**
+ * Gets a mapping of matcher names to matchers.
+ *
+ * @return the mapping of matcher names to matchers.
+ */
+ Map matcherMap();
+
+ /**
+ * Gets a sorted set of registered license families.
+ *
+ * @return the sorted set of license families.
+ */
+ SortedSet licenseFamilies();
+}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/Defaults.java b/apache-rat-core/src/main/java/org/apache/rat/Defaults.java
index c5aff0748..cb747f03f 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/Defaults.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/Defaults.java
@@ -36,6 +36,7 @@
import org.apache.rat.license.ILicenseFamily;
import org.apache.rat.license.LicenseSetFactory;
import org.apache.rat.license.LicenseSetFactory.LicenseFilter;
+import org.apache.rat.utils.Log;
/**
* A class that holds the list of licenses and approved licenses from one or more configuration files.
@@ -70,8 +71,8 @@ public static void init() {
/**
* Builder constructs instances.
*/
- private Defaults(Set urls) {
- this.setFactory = Defaults.readConfigFiles(urls);
+ private Defaults(Log log, Set urls) {
+ this.setFactory = Defaults.readConfigFiles(log, urls);
}
/**
@@ -86,7 +87,7 @@ public static Builder builder() {
* Reads the configuration files.
* @param urls the URLs to read.
*/
- private static LicenseSetFactory readConfigFiles(Collection urls) {
+ private static LicenseSetFactory readConfigFiles(Log log, Collection urls) {
SortedSet licenses = LicenseSetFactory.emptyLicenseSet();
@@ -102,6 +103,7 @@ private static LicenseSetFactory readConfigFiles(Collection urls) {
LicenseReader lReader = fmt.licenseReader();
if (lReader != null) {
+ lReader.setLog(log);
lReader.addLicenses(url);
licenses.addAll(lReader.readLicenses());
lReader.approvedLicenseId().stream().map(ILicenseFamily::makeCategory).forEach(approvedLicenseIds::add);
@@ -241,10 +243,11 @@ public Builder noDefault() {
/**
* Builds the defaults object.
+ * @param log the Log to use to report errors when building the defaults.
* @return the current defaults object.
*/
- public Defaults build() {
- return new Defaults(fileNames);
+ public Defaults build(Log log) {
+ return new Defaults(log, fileNames);
}
}
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/ImplementationException.java b/apache-rat-core/src/main/java/org/apache/rat/ImplementationException.java
new file mode 100644
index 000000000..ed8deaf69
--- /dev/null
+++ b/apache-rat-core/src/main/java/org/apache/rat/ImplementationException.java
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+/**
+ * An exception thrown when there is an issue with the Configuration.
+ */
+public class ImplementationException extends RuntimeException {
+
+ private static final long serialVersionUID = 7257245932787579431L;
+
+ public static ImplementationException makeInstance(Exception e) {
+ if (e instanceof ImplementationException) {
+ return (ImplementationException) e;
+ }
+ return new ImplementationException(e);
+ }
+
+ public ImplementationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ImplementationException(String message) {
+ super(message);
+ }
+
+ public ImplementationException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/Report.java b/apache-rat-core/src/main/java/org/apache/rat/Report.java
index 624c06dfe..c69b3f2f1 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/Report.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/Report.java
@@ -31,6 +31,7 @@
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
+import java.util.function.Consumer;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;
@@ -115,7 +116,7 @@ public class Report {
private static final String LIST_FAMILIES = "list-families";
private static final String LOG_LEVEL = "log-level";
-
+ private static final String DRY_RUN = "dry-run";
/**
* Set unstyled XML output
*/
@@ -130,6 +131,33 @@ public class Report {
* @throws Exception on error.
*/
public static void main(String[] args) throws Exception {
+ ReportConfiguration configuration = parseCommands(args, Report::printUsage);
+ if (configuration != null) {
+ configuration.validate(DefaultLog.INSTANCE::error);
+ new Reporter(configuration).output();
+ }
+ }
+
+ /**
+ * Parses the standard options to create a ReportConfiguraton.
+ * @param args the arguments to parse
+ * @param helpCmd the help command to run when necessary.
+ * @return a ReportConfiguration or null if Help was printed.
+ * @throws IOException on error.
+ */
+ public static ReportConfiguration parseCommands(String[] args, Consumer helpCmd) throws IOException {
+ return parseCommands(args, helpCmd, false);
+ }
+
+ /**
+ * Parses the standard options to create a ReportConfiguraton.
+ * @param args the arguments to parse
+ * @param helpCmd the help command to run when necessary.
+ * @param noArgs If true then the commands do not need extra arguments
+ * @return a ReportConfiguration or null if Help was printed.
+ * @throws IOException on error.
+ */
+ public static ReportConfiguration parseCommands(String[] args, Consumer helpCmd, boolean noArgs) throws IOException {
Options opts = buildOptions();
CommandLine cl;
try {
@@ -138,7 +166,7 @@ public static void main(String[] args) throws Exception {
DefaultLog.INSTANCE.error(e.getMessage());
DefaultLog.INSTANCE.error("Please use the \"--help\" option to see a list of valid commands and options");
System.exit(1);
- return; // dummy return (won't be reached) to avoid Eclipse complaint about possible NPE
+ return null; // dummy return (won't be reached) to avoid Eclipse complaint about possible NPE
// for "cl"
}
@@ -152,14 +180,20 @@ public static void main(String[] args) throws Exception {
}
}
if (cl.hasOption(HELP)) {
- printUsage(opts);
+ helpCmd.accept(opts);
+ return null;
}
- args = cl.getArgs();
- if (args == null || args.length != 1) {
- printUsage(opts);
+ if (!noArgs) {
+ args = cl.getArgs();
+ if (args == null || args.length != 1) {
+ helpCmd.accept(opts);
+ return null;
+ }
} else {
- ReportConfiguration configuration = createConfiguration(args[0], cl);
+ args = new String[] {null};
+ }
+/* ReportConfiguration configuration = createConfiguration(args[0], cl);
configuration.validate(DefaultLog.INSTANCE::error);
boolean dryRun = false;
@@ -180,14 +214,25 @@ public static void main(String[] args) throws Exception {
}
if (!dryRun) {
- Reporter.report(configuration);
+ new Reporter(configuration).output();
}
}
+*/ ReportConfiguration configuration = createConfiguration(args[0], cl);
+ return configuration;
}
static ReportConfiguration createConfiguration(String baseDirectory, CommandLine cl) throws IOException {
final ReportConfiguration configuration = new ReportConfiguration(DefaultLog.INSTANCE);
+ configuration.setDryRun(cl.hasOption(DRY_RUN));
+ if (cl.hasOption(LIST_FAMILIES)) {
+ configuration.listFamilies( LicenseFilter.valueOf(cl.getOptionValue(LIST_FAMILIES).toLowerCase()));
+ }
+
+ if (cl.hasOption(LIST_LICENSES)) {
+ configuration.listFamilies( LicenseFilter.valueOf(cl.getOptionValue(LIST_LICENSES).toLowerCase()));
+ }
+
if (cl.hasOption('o')) {
configuration.setOut(new File(cl.getOptionValue('o')));
}
@@ -247,9 +292,11 @@ static ReportConfiguration createConfiguration(String baseDirectory, CommandLine
defaultBuilder.add(fn);
}
}
- Defaults defaults = defaultBuilder.build();
+ Defaults defaults = defaultBuilder.build(DefaultLog.INSTANCE);
configuration.setFrom(defaults);
- configuration.setReportable(getDirectory(baseDirectory, configuration));
+ if (baseDirectory != null) {
+ configuration.setReportable(getDirectory(baseDirectory, configuration));
+ }
return configuration;
}
@@ -288,7 +335,9 @@ static Options buildOptions() {
String licFilterValues = Arrays.stream(LicenseFilter.values()).map(LicenseFilter::name).collect(Collectors.joining(", "));
Options opts = new Options()
-
+ .addOption(Option.builder().longOpt(DRY_RUN)
+ .desc("If set do not update the files but generate the reports.")
+ .build())
.addOption(
Option.builder().hasArg(true).longOpt(LIST_FAMILIES)
.desc("List the defined license families (default is none). Valid options are: "+licFilterValues+".")
@@ -299,7 +348,6 @@ static Options buildOptions() {
.build())
.addOption(new Option(HELP, "help", false, "Print help for the RAT command line interface and exit."));
-
Option out = new Option("o", "out", true,
"Define the output file where to write a report to (default is System.out).");
@@ -368,13 +416,13 @@ static Options buildOptions() {
private static void printUsage(Options opts) {
HelpFormatter f = new HelpFormatter();
f.setOptionComparator(new OptionComparator());
- String header = "\nAvailable options";
+ String header = System.lineSeparator()+"Available options";
- String footer = "\nNOTE:\n" + "Rat is really little more than a grep ATM\n"
- + "Rat is also rather memory hungry ATM\n" + "Rat is very basic ATM\n"
- + "Rat highlights possible issues\n" + "Rat reports require interpretation\n"
- + "Rat often requires some tuning before it runs well against a project\n"
- + "Rat relies on heuristics: it may miss issues\n";
+ String footer = String.format("%nNOTE:%nRat is really little more than a grep ATM%n"
+ + "Rat is also rather memory hungry ATM%n" + "Rat is very basic ATM%n"
+ + "Rat highlights possible issues%n" + "Rat reports require interpretation%n"
+ + "Rat often requires some tuning before it runs well against a project%n"
+ + "Rat relies on heuristics: it may miss issues%n");
f.printHelp("java -jar apache-rat/target/apache-rat-CURRENT-VERSION.jar [options] [DIR|TARBALL]", header, opts,
footer, false);
@@ -420,7 +468,7 @@ private static IReportable getDirectory(String baseDirectory, ReportConfiguratio
/**
* This class implements the {@code Comparator} interface for comparing Options.
*/
- private static class OptionComparator implements Comparator
*/
-class HeaderCheckWorker {
+public class HeaderCheckWorker {
- /* TODO revisit this class. It is only used in one place and can be moved inline as the DocumentHeaderAnalyser states.
- * However, it may also be possible to make the entire set threadsafe so that multiple files can be checked simultaneously.
+ /*
+ * TODO revisit this class. It is only used in one place and can be moved inline
+ * as the DocumentHeaderAnalyser states. However, it may also be possible to
+ * make the entire set threadsafe so that multiple files can be checked
+ * simultaneously.
*/
/**
* The default number of header lines to read while looking for the license
@@ -47,98 +53,103 @@ class HeaderCheckWorker {
private final int numberOfRetainedHeaderLines;
private final BufferedReader reader;
- private final ILicense license;
+ private final Collection licenses;
private final Document document;
- private int headerLinesToRead;
- private boolean finished = false;
+ /**
+ * Read the input and perform the header check.
+ *
+ * The number of lines indicates how many lines from the top of the file will be read for processing
+ *
+ * @param reader The reader for the document.
+ * @param numberOfLines the number of lines to read from the header.
+ * @return The IHeaders instance for the header.
+ * @throws IOException on input failure
+ */
+ public static IHeaders readHeader(BufferedReader reader, int numberOfLines) throws IOException {
+ final StringBuilder headers = new StringBuilder();
+ int headerLinesRead = 0;
+ String line;
+
+ while (headerLinesRead < numberOfLines && (line = reader.readLine()) != null) {
+ headers.append(line).append(System.lineSeparator());
+ }
+ final String raw = headers.toString();
+ final String pruned = FullTextMatcher.prune(raw).toLowerCase(Locale.ENGLISH);
+ return new IHeaders() {
+ @Override
+ public String raw() {
+ return raw;
+ }
+
+ @Override
+ public String pruned() {
+ return pruned;
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName();
+ }
+ };
+ }
/**
* Convenience constructor wraps given Reader in a
* BufferedReader.
- *
+ *
* @param reader The reader on the document. not null.
- * @param license The license to check against. not null.
+ * @param licenses The licenses to check against. not null.
* @param name The document that is being checked. possibly null
*/
- public HeaderCheckWorker(Reader reader, final ILicense license, final Document name) {
- this(reader, DEFAULT_NUMBER_OF_RETAINED_HEADER_LINES, license, name);
+ public HeaderCheckWorker(Reader reader, final Collection licenses, final Document name) {
+ this(reader, DEFAULT_NUMBER_OF_RETAINED_HEADER_LINES, licenses, name);
}
/**
* Constructs a check worker for the license against the specified document.
- *
+ *
* @param reader The reader on the document. not null.
* @param numberOfRetainedHeaderLine the maximum number of lines to read to find
* the license information.
- * @param license The license to check against. not null.
- * @param name The document that is being checked. possibly null
+ * @param licenses The licenses to check against. not null.
+ * @param document The document that is being checked. possibly null
*/
- public HeaderCheckWorker(Reader reader, int numberOfRetainedHeaderLine, final ILicense license,
- final Document name) {
+ public HeaderCheckWorker(Reader reader, int numberOfRetainedHeaderLine, final Collection licenses,
+ final Document document) {
Objects.requireNonNull(reader, "Reader may not be null");
- Objects.requireNonNull(license, "License may not be null");
+ Objects.requireNonNull(licenses, "Licenses may not be null");
if (numberOfRetainedHeaderLine < 0) {
throw new ConfigurationException("numberOfRetainedHeaderLine may not be less than zero");
}
this.reader = reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader);
this.numberOfRetainedHeaderLines = numberOfRetainedHeaderLine;
- this.license = license;
- this.document = name;
- }
-
- /**
- * @return {@code true} if the header check is complete.
- */
- public boolean isFinished() {
- return finished;
+ this.licenses = licenses;
+ this.document = document;
}
/**
* Read the input and perform the header check.
- *
+ *
* @throws RatHeaderAnalysisException on IO Exception.
*/
public void read() throws RatHeaderAnalysisException {
- if (!finished) {
- final StringBuilder headers = new StringBuilder();
- headerLinesToRead = numberOfRetainedHeaderLines;
- try {
- while (readLine(headers)) {
- // do nothing
+ try {
+ final IHeaders headers = readHeader(reader, numberOfRetainedHeaderLines);
+ licenses.stream().filter(lic -> lic.matches(headers)).forEach(document.getMetaData()::reportOnLicense);
+ if (document.getMetaData().detectedLicense()) {
+ if (document.getMetaData().licenses().anyMatch(
+ lic -> ILicenseFamily.GENTERATED_CATEGORY.equals(lic.getLicenseFamily().getFamilyCategory()))) {
+ document.getMetaData().setDocumentType(Document.Type.GENERATED);
}
- if (license.finalizeState().asBoolean()) {
- document.getMetaData().reportOnLicense(license);
- } else {
- document.getMetaData().reportOnLicense(UnknownLicense.INSTANCE);
- document.getMetaData().set(new MetaData.Datum(MetaData.RAT_URL_HEADER_SAMPLE, headers.toString()));
- }
- } catch (IOException e) {
- throw new RatHeaderAnalysisException("Cannot read header for " + document, e);
- }
- license.reset();
- }
- finished = true;
- }
-
- boolean readLine(StringBuilder headers) throws IOException {
- String line = reader.readLine();
- boolean result = line != null;
- if (result) {
- if (headerLinesToRead-- > 0) {
- headers.append(line);
- headers.append('\n');
- }
- switch (license.matches(line)) {
- case t:
- result = false;
- break;
- case f:
- case i:
- result = true;
- break;
+ } else {
+ document.getMetaData().reportOnLicense(UnknownLicense.INSTANCE);
+ document.getMetaData().setSampleHeader(headers.raw());
}
+ } catch (IOException e) {
+ throw new RatHeaderAnalysisException("Cannot read header for " + document, e);
+ } finally {
+ licenses.forEach(ILicense::reset);
}
- return result;
}
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/analysis/IHeaderMatcher.java b/apache-rat-core/src/main/java/org/apache/rat/analysis/IHeaderMatcher.java
index a4aacf8f6..7e70ec29a 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/analysis/IHeaderMatcher.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/analysis/IHeaderMatcher.java
@@ -18,6 +18,9 @@
*/
package org.apache.rat.analysis;
+import org.apache.rat.ImplementationException;
+import org.apache.rat.config.parameters.Description;
+import org.apache.rat.config.parameters.DescriptionBuilder;
import org.apache.rat.configuration.builders.AllBuilder;
import org.apache.rat.configuration.builders.AnyBuilder;
import org.apache.rat.configuration.builders.CopyrightBuilder;
@@ -28,80 +31,45 @@
import org.apache.rat.configuration.builders.TextBuilder;
/**
- * Performs explicit checks against a line from the header of a file.
- * For implementations that need to check multiple lines the implementation must cache the earlier lines.
+ * Performs explicit checks against a line from the header of a file. For
+ * implementations that need to check multiple lines the implementation must
+ * cache the earlier lines.
*/
public interface IHeaderMatcher {
- /**
- * The state of the matcher.
- *
- *
{@code t} - The matcher has located a match.
- *
{@code f} - The matcher has determined that it will not match the
- * document.
- *
{@code i} - The matcher can not yet determine if a matche is made or
- * not.
- *
- */
- enum State {
- t("true"), f("false"), i("indeterminent");
-
- private final String desc;
-
- State(String desc) {
- this.desc = desc;
- }
-
- public boolean asBoolean() {
- switch (this) {
- case t : return true;
- case f : return false;
- default:
- case i : throw new IllegalStateException( "'asBoolean' should never be called on an indeterminate state");
- }
- }
-
- @Override
- public String toString() {
- return super.toString()+" "+desc;
- }
- }
-
/**
* Get the identifier for this matcher.
- *
All matchers must have unique identifiers
+ *
+ * All matchers must have unique identifiers
+ *
*
* @return the Identifier for this matcher.
*/
String getId();
/**
- * Resets this state {@code State.i}.
- * If text is being cached this method should clear that cache.
+ * Resets this state of this matcher to its initial state in preparation for
+ * use with another document scan. In most cases this method does not need to
+ * do anything.
*/
- void reset();
+ default void reset() {
+ // does nothing.
+ }
/**
- * Attempts to match {@code line} and returns the State after
- * the match is attempted.
+ * Attempts to match text in the IHeaders instance.
*
- * @param line next line of text, not null
- * @return the new state after the matching was attempted.
- */
- State matches(String line);
-
- /**
- * Gets the final state for this matcher. This is called after the EOF on the
- * input. At this point there should be no matchers in an {@code State.i} state.
+ * @param headers the representations of the headers to check
+ * @return {@code true} if the matcher matches the text, {@code false} otherwise.
*/
- State finalizeState();
-
+ boolean matches(IHeaders headers);
+
/**
- * Gets the the current state of the matcher. All matchers should be
- * in {@code State.i} at the start.
- *
- * @return the current state of the matcher.
+ * Generates the component Description.
+ * @return the component description.
*/
- State currentState();
+ default Description getDescription() {
+ return DescriptionBuilder.build(this);
+ }
/**
* An IHeaderMatcher builder.
@@ -110,10 +78,39 @@ public String toString() {
interface Builder {
/**
* Build the IHeaderMatcher.
+ *
+ * Implementations of this interface should return a specific child class of IHeaderMatcher.
+ *
* @return a new IHeaderMatcher.
*/
IHeaderMatcher build();
+ /**
+ * Gets the class that is build by this builder.
+ * @return The class that is build by this builder.
+ */
+ default Class> builtClass() throws SecurityException {
+ try {
+ return this.getClass().getMethod("build").getReturnType();
+ } catch (NoSuchMethodException | SecurityException e) {
+ throw new IllegalStateException("the 'build' method of the Builder interface must always be public");
+ }
+ }
+
+ /**
+ * Gets the Description for this builder.
+ * @return The description of the builder
+ */
+ default Description getDescription() {
+ Class> clazz = builtClass();
+ if (clazz == IHeaderMatcher.class) {
+ throw new ImplementationException(String.format(
+ "Class %s must implement buildClass() method to return a child class of IHeaderMatcher",
+ this.getClass()));
+ }
+ return DescriptionBuilder.buildMap(clazz);
+ }
+
/**
* @return an instance of the standard TextBuilder.
* @see TextBuilder
diff --git a/apache-rat-core/src/main/java/org/apache/rat/analysis/IHeaders.java b/apache-rat-core/src/main/java/org/apache/rat/analysis/IHeaders.java
new file mode 100644
index 000000000..be807574b
--- /dev/null
+++ b/apache-rat-core/src/main/java/org/apache/rat/analysis/IHeaders.java
@@ -0,0 +1,38 @@
+package org.apache.rat.analysis;
+/*
+ * 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. *
+ */
+
+/**
+ * The processed headers from a file.
+ */
+public interface IHeaders {
+ /**
+ * Gets raw header as found in the document.
+ * @return the raw header as read from the file.
+ */
+ public String raw();
+
+ /**
+ * Gets the pruned header. This is the header with all non letter
+ * and non number characters removed and the string converted to lower case.
+ * @return The pruned header.
+ */
+ public String pruned();
+
+}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/analysis/LicenseCollection.java b/apache-rat-core/src/main/java/org/apache/rat/analysis/LicenseCollection.java
deleted file mode 100644
index 57ee9de3d..000000000
--- a/apache-rat-core/src/main/java/org/apache/rat/analysis/LicenseCollection.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 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.analysis;
-
-import java.util.Collection;
-import java.util.Collections;
-
-import org.apache.rat.analysis.matchers.AbstractMatcherContainer;
-import org.apache.rat.license.ILicense;
-import org.apache.rat.license.ILicenseFamily;
-
-/**
- * A collection of ILicenses that acts as a single License for purposes of Analysis.
- *
- * This class process each license in turn on each {@code matches(String)} call. When a match is found the
- * ILicenseFamily for the matching license is captured and used as the family for this license. If no matching
- * license has been found the default {@code dummy} license category is used.
- */
-class LicenseCollection extends AbstractMatcherContainer implements ILicense {
-
- private static final ILicenseFamily DEFAULT = ILicenseFamily.builder().setLicenseFamilyCategory("Dummy")
- .setLicenseFamilyName("HeaderMatcherCollection default license family").build();
- private final Collection enclosed;
- private ILicense matchingLicense;
- private State lastState;
-
- /**
- * Constructs the LicenseCollection from the provided ILicense collection.
- * @param enclosed The collection of ILicenses to compose this License implementation from. May not be null.
- */
- public LicenseCollection(Collection enclosed) {
- super(enclosed);
- this.enclosed = Collections.unmodifiableCollection(enclosed);
- this.matchingLicense = null;
- this.lastState = State.i;
- }
-
- @Override
- public String getId() {
- return "Default License Collection";
- }
-
- @Override
- public void reset() {
- enclosed.forEach(ILicense::reset);
- this.lastState = State.i;
- this.matchingLicense = null;
- }
-
- @Override
- public State matches(String line) {
- State dflt = State.f;
- for (ILicense license : enclosed) {
- switch (license.matches(line)) {
- case t:
- this.matchingLicense = license;
- lastState = State.t;
- return State.t;
- case i:
- dflt = State.i;
- break;
- default:
- // do nothing
- break;
- }
- }
- lastState = dflt;
- return dflt;
- }
-
- @Override
- public State currentState() {
- if (lastState == State.t) {
- return lastState;
- }
- for (ILicense license : enclosed) {
- switch (license.currentState()) {
- case t:
- this.matchingLicense = license;
- lastState = State.t;
- return lastState;
- case i:
- lastState = State.i;
- return lastState;
- case f:
- // do nothing;
- break;
- }
- }
- lastState = State.f;
- return lastState;
- }
-
- @Override
- public int compareTo(ILicense arg0) {
- return getLicenseFamily().compareTo(arg0.getLicenseFamily());
- }
-
- @Override
- public ILicenseFamily getLicenseFamily() {
- return matchingLicense == null ? DEFAULT : matchingLicense.getLicenseFamily();
- }
-
- @Override
- public String getNotes() {
- return matchingLicense == null ? null : matchingLicense.getNotes();
- }
-
- @Override
- public String derivedFrom() {
- return matchingLicense == null ? null : matchingLicense.derivedFrom();
- }
-
- @Override
- public String getName() {
- return getLicenseFamily().getFamilyName();
- }
-}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/analysis/RatHeaderAnalysisException.java b/apache-rat-core/src/main/java/org/apache/rat/analysis/RatHeaderAnalysisException.java
index 0e713912e..cd1a05c02 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/analysis/RatHeaderAnalysisException.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/analysis/RatHeaderAnalysisException.java
@@ -15,7 +15,7 @@
* KIND, either express or implied. See the License for the *
* specific language governing permissions and limitations *
* under the License. *
- */
+ */
package org.apache.rat.analysis;
/**
diff --git a/apache-rat-core/src/main/java/org/apache/rat/analysis/RatReportAnalysisResultException.java b/apache-rat-core/src/main/java/org/apache/rat/analysis/RatReportAnalysisResultException.java
index 8ee9274dd..a31417cfd 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/analysis/RatReportAnalysisResultException.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/analysis/RatReportAnalysisResultException.java
@@ -15,17 +15,17 @@
* KIND, either express or implied. See the License for the *
* specific language governing permissions and limitations *
* under the License. *
- */
+ */
package org.apache.rat.analysis;
import org.apache.rat.api.RatException;
import org.apache.rat.document.RatDocumentAnalysisException;
public class RatReportAnalysisResultException extends RatDocumentAnalysisException {
-
+
private static final long serialVersionUID = 4018716722707721989L;
private static final String MESSAGE = "Could not report results of analysis";
-
+
public RatReportAnalysisResultException() {
super(MESSAGE);
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/analysis/UnknownLicense.java b/apache-rat-core/src/main/java/org/apache/rat/analysis/UnknownLicense.java
index 113771b09..1889d86f4 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/analysis/UnknownLicense.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/analysis/UnknownLicense.java
@@ -25,29 +25,30 @@
/**
* An ILicense implementation that represents an unknown license.
*
- * The UnknownLicense is used during processing to report that a document license can not be determined.
+ * The UnknownLicense is used during processing to report that a document
+ * license can not be determined.
*
*/
public class UnknownLicense implements ILicense {
-
+
/**
* The single instance of this class.
*/
- static final UnknownLicense INSTANCE = new UnknownLicense();
-
- private final ILicenseFamily family ;
-
+ public static final UnknownLicense INSTANCE = new UnknownLicense();
+
+ private final ILicenseFamily family;
+
/**
* Do not allow other constructions.
*/
private UnknownLicense() {
- family = new ILicenseFamilyBuilder().setLicenseFamilyCategory("?????")
+ family = new ILicenseFamilyBuilder().setLicenseFamilyCategory(ILicenseFamily.UNKNOWN_CATEGORY)
.setLicenseFamilyName("Unknown license").build();
}
-
+
@Override
public String getId() {
- return "?????";
+ return ILicenseFamily.UNKNOWN_CATEGORY;
}
@Override
@@ -56,18 +57,8 @@ public void reset() {
}
@Override
- public State matches(String line) {
- return State.f;
- }
-
- @Override
- public State finalizeState() {
- return State.f;
- }
-
- @Override
- public State currentState() {
- return State.f;
+ public boolean matches(IHeaders headers) {
+ return false;
}
@Override
@@ -81,7 +72,7 @@ public ILicenseFamily getLicenseFamily() {
}
@Override
- public String getNotes() {
+ public String getNote() {
return null;
}
@@ -91,7 +82,7 @@ public String getName() {
}
@Override
- public String derivedFrom() {
+ public IHeaderMatcher getMatcher() {
return null;
}
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/analysis/license/FullTextMatchingLicense.java b/apache-rat-core/src/main/java/org/apache/rat/analysis/license/FullTextMatchingLicense.java
index 6ce7ef7b3..aebb19b87 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/analysis/license/FullTextMatchingLicense.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/analysis/license/FullTextMatchingLicense.java
@@ -36,9 +36,14 @@ public class FullTextMatchingLicense extends BaseLicense {
private String text;
+ /** constructor */
public FullTextMatchingLicense() {
}
+ /**
+ * Set the text to match
+ * @param text the text to match
+ */
public final void setFullText(String text) {
this.text = text;
}
@@ -46,9 +51,9 @@ public final void setFullText(String text) {
@Override
public ILicense.Builder getLicense() {
return ILicense.builder()
- .setLicenseFamilyCategory(getLicenseFamilyCategory())
+ .setFamily(getLicenseFamilyCategory())
.setName(getLicenseFamilyName())
- .setMatcher( new TextBuilder().setText(text) )
- .setNotes(getNotes());
+ .setMatcher( new TextBuilder().setSimpleText(text) )
+ .setNote(getNotes());
}
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/analysis/license/SimplePatternBasedLicense.java b/apache-rat-core/src/main/java/org/apache/rat/analysis/license/SimplePatternBasedLicense.java
index 36363e698..95fb8931a 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/analysis/license/SimplePatternBasedLicense.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/analysis/license/SimplePatternBasedLicense.java
@@ -50,10 +50,10 @@ public void setPatterns(String[] pPatterns) {
private AbstractBuilder getMatcher() {
if (patterns.length == 1) {
- return new TextBuilder().setText(patterns[0]);
+ return new TextBuilder().setSimpleText(patterns[0]);
} else {
AnyBuilder any = new AnyBuilder();
- Arrays.stream(patterns).map(s -> new TextBuilder().setText(s)).forEach(b-> any.add(b));
+ Arrays.stream(patterns).map(s -> new TextBuilder().setSimpleText(s)).forEach(b-> any.addEnclosed(b));
return any;
}
}
@@ -61,9 +61,9 @@ private AbstractBuilder getMatcher() {
@Override
public ILicense.Builder getLicense() {
return ILicense.builder()
- .setLicenseFamilyCategory(getLicenseFamilyCategory())
+ .setFamily(getLicenseFamilyCategory())
.setName(getLicenseFamilyName())
.setMatcher( getMatcher() )
- .setNotes(getNotes());
+ .setNote(getNotes());
}
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/AbstractHeaderMatcher.java b/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/AbstractHeaderMatcher.java
index 64e2cc696..c97f88644 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/AbstractHeaderMatcher.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/AbstractHeaderMatcher.java
@@ -22,16 +22,24 @@
import org.apache.commons.lang3.StringUtils;
import org.apache.rat.analysis.IHeaderMatcher;
+import org.apache.rat.config.parameters.ComponentType;
+import org.apache.rat.config.parameters.ConfigComponent;
+import org.apache.rat.config.parameters.Description;
+import org.apache.rat.config.parameters.DescriptionBuilder;
/**
- * An abstract class to simplify IHeaderMatcher creation. This class ensures that the id is set.
+ * An abstract class to simplify IHeaderMatcher creation. This class ensures
+ * that the id is set
*/
public abstract class AbstractHeaderMatcher implements IHeaderMatcher {
+ @ConfigComponent(type = ComponentType.PARAMETER, desc = "The id of the matcher.")
private final String id;
/**
- * Constructs the IHeaderMatcher with an id value. If {@code id} is null then a unique random id is created.
+ * Constructs the IHeaderMatcher with an id value. If {@code id} is null then a
+ * unique random id is created.
+ *
* @param id the Id to use.
*/
protected AbstractHeaderMatcher(String id) {
diff --git a/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/AbstractMatcherContainer.java b/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/AbstractMatcherContainer.java
index d15dd4258..7847309f9 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/AbstractMatcherContainer.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/AbstractMatcherContainer.java
@@ -20,39 +20,54 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.Objects;
import org.apache.rat.analysis.IHeaderMatcher;
+import org.apache.rat.config.parameters.ComponentType;
+import org.apache.rat.config.parameters.ConfigComponent;
/**
- * A class that implements IHeaderMatcher as a collection of other IHeaderMatchers.
+ * A class that implements IHeaderMatcher as a collection of other
+ * IHeaderMatchers.
*/
public abstract class AbstractMatcherContainer extends AbstractHeaderMatcher {
- protected final Collection enclosed;
+ /** The collection of enclosed headers */
+ @ConfigComponent(desc = "enclosed Matchers", type = ComponentType.PARAMETER, parameterType = IHeaderMatcher.class, required=true)
+ private final Collection enclosed;
+
+ /** The resource the headers were read from. May be null */
+ @ConfigComponent(desc = "Resource to read matcher definitions from.", type = ComponentType.PARAMETER)
+ private final String resource;
/**
- * Constructs the abstract matcher container.
- * If the {@code id} is not set then a unique random identifier is created.
- * The {@code enclosed} collection is preserved in a new collection that retains the order of
- * of the original collection.
+ * Constructs the abstract matcher container. If the {@code id} is not set then
+ * a unique random identifier is created. The {@code enclosed} collection is
+ * preserved in a new collection that retains the order of of the original
+ * collection.
+ *
* @param id The id for the matcher.
- * @param enclosed the collection of enclosed matchers.
+ * @param enclosed the collection of enclosed matchers.
+ * @param resource the name of the resource if this container was read from a file or URL.
*/
- public AbstractMatcherContainer(String id, Collection extends IHeaderMatcher> enclosed) {
+ public AbstractMatcherContainer(String id, Collection extends IHeaderMatcher> enclosed, String resource) {
super(id);
Objects.requireNonNull(enclosed, "The collection of IHeaderMatcher may not be null");
this.enclosed = new ArrayList<>(enclosed);
+ this.resource = resource;
}
/**
- * Constructs the abstract matcher container with a unique random id.
- * The {@code enclosed} collection is preserved in a new collection that retains the order of
- * of the original collection.
- * @param enclosed the collection of enclosed matchers.
+ * Constructs the abstract matcher container with a unique random id. The
+ * {@code enclosed} collection is preserved in a new collection that retains the
+ * order of of the original collection.
+ *
+ * @param enclosed the collection of enclosed matchers.
+ * @param resource the name of the resource if this container was read from a file or URL.
*/
- public AbstractMatcherContainer(Collection extends IHeaderMatcher> enclosed) {
- this(null, enclosed);
+ public AbstractMatcherContainer(Collection extends IHeaderMatcher> enclosed, String resource) {
+ this(null, enclosed, resource);
}
@Override
@@ -60,9 +75,19 @@ public void reset() {
enclosed.forEach(IHeaderMatcher::reset);
}
- @Override
- public State finalizeState() {
- enclosed.forEach(IHeaderMatcher::finalizeState);
- return currentState();
+ /**
+ * Retrieves the collection of matchers that comprise the children of this matcher.
+ * @return the children of this matcher
+ */
+ public Collection getEnclosed() {
+ return Collections.unmodifiableCollection(enclosed);
+ }
+
+ /**
+ * Get the resource that was provided in the constructor.
+ * @return the resource or {@code null} if none was provided in the constructor.
+ */
+ public String getResource() {
+ return resource;
}
-}
+}
\ No newline at end of file
diff --git a/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/AbstractSimpleMatcher.java b/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/AbstractSimpleMatcher.java
deleted file mode 100644
index 71852c075..000000000
--- a/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/AbstractSimpleMatcher.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.analysis.matchers;
-
-/**
- * An abstract IHeaderMatcher that does simple matching. Implementations need to implement the {@code doMatch(String)}
- * method to perform the actual matching. All handling of the {@code State} is managed by this class. By default the
- * {@code finalizeState()} method will convert {@code State.i} to {@code State.f}.
- */
-public abstract class AbstractSimpleMatcher extends AbstractHeaderMatcher {
- private State lastState;
-
- /**
- * Constructs the AbstractSimpleMatcher with the specified id.
- * If the id is null or an empty string a unique random id will be generated.
- * @param id the Id to use. May be null.
- */
- protected AbstractSimpleMatcher(String id) {
- super(id);
- this.lastState = State.i;
- }
-
- /**
- * Performs the actual match test.
- * @param line the line to check.
- * @return {@code true} if the line matches, {@code false} otherwise.
- */
- abstract protected boolean doMatch(String line);
-
- @Override
- public final State matches(String line) {
- if (lastState == State.t) {
- return lastState;
- }
- if (line != null && doMatch(line)) {
- lastState = State.t;
- }
- return lastState;
- }
-
- @Override
- public void reset() {
- lastState = State.i;
- }
-
- @Override
- public State finalizeState() {
- if (lastState == State.i) {
- lastState = State.f;
- }
- return lastState;
- }
-
- @Override
- public final State currentState() {
- return lastState;
- }
-}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/AndMatcher.java b/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/AndMatcher.java
index fc64f6e43..51d66b302 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/AndMatcher.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/AndMatcher.java
@@ -21,56 +21,47 @@
import java.util.Collection;
import org.apache.rat.analysis.IHeaderMatcher;
+import org.apache.rat.analysis.IHeaders;
+import org.apache.rat.config.parameters.ComponentType;
+import org.apache.rat.config.parameters.ConfigComponent;
/**
- * A matcher that performs a logical {@code AND} across all the contained matchers.
+ * A matcher that performs a logical {@code AND} across all the contained
+ * matchers.
*/
+@ConfigComponent(type = ComponentType.MATCHER, name = "all", desc = "Returns true if all enclosed matchers return true.")
public class AndMatcher extends AbstractMatcherContainer {
/**
* Constructs the AndMatcher with the specified id and enclosed collection.
- * @param id the to use. If null or an empty string a unique random id will be created.
+ *
+ * @param id the to use. If null or an empty string a unique random id will be
+ * created.
* @param enclosed the enclosed collection.
+ * @param resource the name of the resource the collection was read from if any. may be null.
*/
- public AndMatcher(String id, Collection extends IHeaderMatcher> enclosed) {
- super(id, enclosed);
+ public AndMatcher(String id, Collection extends IHeaderMatcher> enclosed, String resource) {
+ super(id, enclosed, resource);
}
/**
- * Constructs the AndMatcher with the a unique random id and the enclosed collection.
+ * Constructs the AndMatcher with the a unique random id and the enclosed
+ * collection.
+ *
* @param enclosed the enclosed collection.
+ * @param resource the name of the resource the collection was read from if any. may be null.
*/
- public AndMatcher(Collection extends IHeaderMatcher> enclosed) {
- this(null, enclosed);
+ public AndMatcher(Collection extends IHeaderMatcher> enclosed, String resource) {
+ this(null, enclosed, resource);
}
@Override
- public State currentState() {
- State dflt = State.t;
- for (IHeaderMatcher matcher : enclosed) {
- switch (matcher.currentState()) {
- case f:
- return State.f;
- case i:
- dflt = State.i;
- break;
- default:
- // do nothing
- break;
+ public boolean matches(IHeaders headers) {
+ for (IHeaderMatcher matcher : getEnclosed()) {
+ if (!matcher.matches(headers)) {
+ return false;
}
}
- return dflt;
- }
-
- @Override
- public State matches(String line) {
- enclosed.stream().filter(x -> x.currentState() == State.i).forEach(x -> x.matches(line));
- return currentState();
- }
-
- @Override
- public State finalizeState() {
- enclosed.forEach(IHeaderMatcher::finalizeState);
- return currentState();
+ return true;
}
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/CopyrightMatcher.java b/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/CopyrightMatcher.java
index a03798b4d..76953a857 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/CopyrightMatcher.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/CopyrightMatcher.java
@@ -22,6 +22,10 @@
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
+import org.apache.rat.ConfigurationException;
+import org.apache.rat.analysis.IHeaders;
+import org.apache.rat.config.parameters.ComponentType;
+import org.apache.rat.config.parameters.ConfigComponent;
/**
* Matches a typical Copyright header line only based on a regex pattern which
@@ -38,18 +42,20 @@
*
copyright 2012 foobar
*
*
- * Note also that the copyright owner is appended to the regex pattern and so can
- * support additional regex but also requires escaping where needed,
- * e.g. use "FooBar \\(www\\.foobar\\.com\\)" or
- * "FooBar \\Q(www.foobar.com)\\E" to match "FooBar
- * (www.foobar.com)"
+ * Note also that the copyright owner is appended to the regex pattern and so
+ * can support additional regex but also requires escaping where needed,
+ * e.g. use "FooBar \\(www\\.foobar\\.com\\)" or "FooBar
+ * \\Q(www.foobar.com)\\E" to match "FooBar (www.foobar.com)"
+ *
*/
-public class FullTextMatcher extends AbstractSimpleMatcher {
-
- // Number of match characters assumed to be present on first line
- private static final int DEFAULT_INITIAL_LINE_LENGTH = 20;
+public class FullTextMatcher extends SimpleTextMatcher {
private final String fullText;
- private final String firstLine;
-
- private boolean seenFirstLine;
-
- private final StringBuilder buffer = new StringBuilder();
-
/**
- * Constructs the full text matcher with a unique random id and the specified text to match.
- * @param fullText the text to match
+ * Constructs the full text matcher with a unique random id and the specified
+ * text to match.
+ *
+ * @param simpleText the text to match
*/
- public FullTextMatcher(String fullText) {
- this(null, fullText);
+ public FullTextMatcher(String simpleText) {
+ this(null, simpleText);
}
/**
* Constructs the full text matcher for the specified text.
+ *
* @param id the id for the matcher
- * @param fullText the text to match
+ * @param simpleText the text to match
*/
- public FullTextMatcher(String id, String fullText) {
- super(id);
- Objects.requireNonNull(fullText, "fullText may not be null");
- int offset = fullText.indexOf('\n');
- if (offset == -1) {
- offset = Math.min(DEFAULT_INITIAL_LINE_LENGTH, fullText.length());
- }
- firstLine = prune(fullText.substring(0, offset)).toLowerCase(Locale.ENGLISH);
- this.fullText = prune(fullText).toLowerCase(Locale.ENGLISH);
- buffer.setLength(0);
- seenFirstLine = false;
+ public FullTextMatcher(String id, String simpleText) {
+ super(id, simpleText);
+ this.fullText = prune(simpleText).toLowerCase(Locale.ENGLISH);
}
/**
* Removes everything except letter or digit from text.
- *
+ *
* @param text The text to remove extra chars from.
* @return the pruned text.
*/
@@ -89,44 +76,10 @@ public static String prune(String text) {
}
@Override
- public boolean doMatch(String line) {
- final String inputToMatch = prune(line).toLowerCase(Locale.ENGLISH);
- if (seenFirstLine) { // Accumulate more input
- buffer.append(inputToMatch);
- } else {
- int offset = inputToMatch.indexOf(firstLine);
- if (offset >= 0) {
- // we have a match, save the text starting with the match
- buffer.append(inputToMatch.substring(offset));
- seenFirstLine = true;
- // Drop out to check whether full text is matched
- } else {
- // we assume that the first line must appear in a single line
- return false; // no more to do here
- }
- }
-
- if (buffer.length() >= fullText.length()) { // we have enough data to match
- if (buffer.toString().contains(fullText)) {
- return true;
- }
- // buffer contains first line but does not contain full text
- // It's possible that the buffer contains the first line again
- int offset = buffer.substring(1).indexOf(firstLine);
- if (offset >= 0) { // first line found again
- buffer.delete(0, offset); // reset buffer to the new start
- } else { // buffer does not even contain first line, so cannot be used to match full text
- reset();
- }
+ public boolean matches(IHeaders headers) {
+ if (headers.pruned().length() >= fullText.length()) { // we have enough data to match
+ return headers.pruned().contains(fullText);
}
return false;
}
-
- @Override
- public void reset() {
- super.reset();
- buffer.setLength(0);
- seenFirstLine = false;
- }
-
}
\ No newline at end of file
diff --git a/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/NotMatcher.java b/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/NotMatcher.java
index 8678f9257..2f00c9a5c 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/NotMatcher.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/NotMatcher.java
@@ -18,27 +18,28 @@
*/
package org.apache.rat.analysis.matchers;
+import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
import org.apache.rat.analysis.IHeaderMatcher;
+import org.apache.rat.analysis.IHeaders;
+import org.apache.rat.config.parameters.ComponentType;
+import org.apache.rat.config.parameters.ConfigComponent;
+
/**
* An IHeaderMatcher that reverses the result of an enclosed matcher.
*/
+@ConfigComponent(type = ComponentType.MATCHER, name = "not", desc = "Negates the enclosed matcher.")
public class NotMatcher extends AbstractHeaderMatcher {
+ @ConfigComponent(desc = "enclosed Matcher", type = ComponentType.PARAMETER, parameterType = IHeaderMatcher.class, required=true)
private final IHeaderMatcher enclosed;
- /**
- * Create the matcher with the enclosed matcher.
- * @param enclosed the enclosed matcher
- */
- public NotMatcher(IHeaderMatcher enclosed) {
- this(null, enclosed);
- }
-
/**
* Create the matcher with the enclosed matcher and id.
- * @param id the id for this matcher.
+ *
+ * @param id the id for this matcher. May be null
* @param enclosed the enclosed matcher
*/
public NotMatcher(String id, IHeaderMatcher enclosed) {
@@ -47,33 +48,17 @@ public NotMatcher(String id, IHeaderMatcher enclosed) {
this.enclosed = enclosed;
}
- @Override
- public State matches(String line) {
- enclosed.matches(line);
- return currentState();
- }
-
- @Override
- public void reset() {
- enclosed.reset();
+ public IHeaderMatcher getEnclosed() {
+ return enclosed;
}
@Override
- public State finalizeState() {
- enclosed.finalizeState();
- return currentState();
+ public boolean matches(IHeaders headers) {
+ return !enclosed.matches(headers);
}
@Override
- public State currentState() {
- switch (enclosed.currentState()) {
- case t:
- return State.f;
- case f:
- return State.t;
- default:
- case i:
- return State.i;
- }
+ public void reset() {
+ enclosed.reset();
}
-}
+}
\ No newline at end of file
diff --git a/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/OrMatcher.java b/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/OrMatcher.java
index 9b7bbae8a..4e2685850 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/OrMatcher.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/OrMatcher.java
@@ -21,75 +21,43 @@
import java.util.Collection;
import org.apache.rat.analysis.IHeaderMatcher;
+import org.apache.rat.analysis.IHeaders;
+import org.apache.rat.config.parameters.ComponentType;
+import org.apache.rat.config.parameters.ConfigComponent;
/**
* A matcher that performs a logical {@code OR} across all the contained matchers.
*/
+@ConfigComponent(type = ComponentType.MATCHER, name = "any", desc = "Returns true if at least one of the enclosed matchers returns true.")
public class OrMatcher extends AbstractMatcherContainer {
- private State lastState;
-
/**
* Constructs the matcher from the enclosed matchers.
+ *
* @param enclosed the enclosed matchers.
*/
- public OrMatcher(Collection extends IHeaderMatcher> enclosed) {
- this(null, enclosed);
+ public OrMatcher(Collection extends IHeaderMatcher> enclosed, String resource) {
+ this(null, enclosed, resource);
}
/**
* Constructs the matcher with the specified id from the enclosed matchers.
+ *
* @param id the id to use.
* @param enclosed the enclosed matchers.
*/
- public OrMatcher(String id, Collection extends IHeaderMatcher> enclosed) {
- super(id, enclosed);
- lastState = State.i;
+ public OrMatcher(String id, Collection extends IHeaderMatcher> enclosed, String resource) {
+ super(id, enclosed, resource);
}
@Override
- public State matches(String line) {
- if (lastState == State.t) {
- return State.t;
- }
- for (IHeaderMatcher matcher : enclosed) {
- switch (matcher.matches(line)) {
- case t:
- lastState = State.t;
- return lastState;
- case f:
- case i:
- lastState = State.i;
+ public boolean matches(IHeaders headers) {
+ for (IHeaderMatcher matcher : getEnclosed()) {
+ if (matcher.matches(headers)) {
+ return true;
}
- }
- return lastState;
- }
- @Override
- public State currentState() {
- if (lastState == State.t) {
- return lastState;
}
- for (IHeaderMatcher matcher : enclosed) {
- switch (matcher.currentState()) {
- case t:
- lastState = State.t;
- return lastState;
- case i:
- lastState = State.i;
- return lastState;
- case f:
- // do nothing;
- break;
- }
- }
- lastState = State.f;
- return lastState;
- }
-
- @Override
- public void reset() {
- super.reset();
- lastState = State.i;
+ return false;
}
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/SPDXMatcherFactory.java b/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/SPDXMatcherFactory.java
index 5766b5257..223680189 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/SPDXMatcherFactory.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/SPDXMatcherFactory.java
@@ -26,12 +26,14 @@
import org.apache.commons.lang3.StringUtils;
import org.apache.rat.ConfigurationException;
-import org.apache.rat.analysis.IHeaderMatcher;
+import org.apache.rat.analysis.IHeaders;
+import org.apache.rat.config.parameters.ComponentType;
+import org.apache.rat.config.parameters.ConfigComponent;
/**
- * Defines a factory to produce matchers for an SPDX tag. SPDX tag is of the format
- * {@code SPDX-License-Identifier: short-name} where {@code short-name} matches
- * the regex pattern [A-Za-z0-9\.\-]+
+ * Defines a factory to produce matchers for an SPDX tag. SPDX tag is of the
+ * format {@code SPDX-License-Identifier: short-name} where {@code short-name}
+ * matches the regex pattern [A-Za-z0-9\.\-]+
*
* SPDX identifiers are specified by the Software Package Data Exchange(R) also
* known as SPDX(R) project from the Linux foundation.
@@ -52,7 +54,8 @@ public class SPDXMatcherFactory {
public static final SPDXMatcherFactory INSTANCE = new SPDXMatcherFactory();
/**
- * The regular expression to locate the SPDX license identifier in the text stream
+ * The regular expression to locate the SPDX license identifier in the text
+ * stream
*/
private static Pattern groupSelector = Pattern.compile(".*SPDX-License-Identifier:\\s([A-Za-z0-9\\.\\-]+)");
@@ -67,16 +70,17 @@ public class SPDXMatcherFactory {
private SPDXMatcherFactory() {
lastLine = null;
- };
+ }
/**
- * Creates the spdx matcher.
- * @param spdxId the spdx name to match.
- * @return a spdx matcher.
+ * Creates the SPDX matcher.
+ *
+ * @param spdxId the SPDX name to match.
+ * @return a SPDX matcher.
*/
- public IHeaderMatcher create(String spdxId) {
+ public Match create(String spdxId) {
if (StringUtils.isBlank(spdxId)) {
- throw new ConfigurationException("'spdx' type matcher requires a name");
+ throw new ConfigurationException("'SPDX' type matcher requires a name");
}
Match matcher = matchers.get(spdxId);
if (matcher == null) {
@@ -88,13 +92,16 @@ public IHeaderMatcher create(String spdxId) {
/**
* Each matcher calls this method to present the line it is working on.
+ *
* @param line The line the caller is looking at.
* @param caller the Match that is calling this method.
* @return true if the caller matches the text.
*/
private boolean check(String line, Match caller) {
- // if the line has not been seen yet see if we can extract the SPDX id from the line.
- // if so then see if that name has been registered. If so then we have a match and set
+ // if the line has not been seen yet see if we can extract the SPDX id from the
+ // line.
+ // if so then see if that name has been registered. If so then we have a match
+ // and set
// lastMatch.
if ((lastLine == null || !lastLine.equals(line)) && line.contains("SPDX-License-Identifier")) {
Matcher matcher = groupSelector.matcher(line);
@@ -108,12 +115,18 @@ private boolean check(String line, Match caller) {
return (lastMatch != null) && caller.spdxId.equals(lastMatch.spdxId);
}
- public class Match extends AbstractSimpleMatcher {
-
+ @ConfigComponent(type = ComponentType.MATCHER, name = "spdx", desc = "Matches SPDX enclosed license identifier.")
+ public class Match extends AbstractHeaderMatcher {
+ @ConfigComponent(type = ComponentType.PARAMETER, name = "name", desc = "The SPDX identifier string")
String spdxId;
+
+ public String getName() {
+ return spdxId;
+ }
+
/**
* Constructor.
- *
+ *
* @param spdxId A regular expression that matches the @{short-name} of the SPDX
* Identifier.
*/
@@ -124,15 +137,14 @@ public class Match extends AbstractSimpleMatcher {
}
@Override
- protected boolean doMatch(String line) {
- return SPDXMatcherFactory.this.check(line, this);
+ public boolean matches(IHeaders headers) {
+ return SPDXMatcherFactory.this.check(headers.raw(), this);
}
-
+
@Override
public void reset() {
super.reset();
SPDXMatcherFactory.this.lastMatch = null;
-
}
}
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/SimpleRegexMatcher.java b/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/SimpleRegexMatcher.java
index 641f7ab43..991de867c 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/SimpleRegexMatcher.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/SimpleRegexMatcher.java
@@ -20,32 +20,38 @@
import java.util.regex.Pattern;
+import org.apache.rat.analysis.IHeaders;
+import org.apache.rat.config.parameters.ComponentType;
+import org.apache.rat.config.parameters.ConfigComponent;
+
/**
* A simple regular expression matching IHeaderMatcher
*/
-public class SimpleRegexMatcher extends AbstractSimpleMatcher {
+@ConfigComponent(type = ComponentType.MATCHER, name = "regex", desc = "Performs a regex match using the enclosed the text")
+public class SimpleRegexMatcher extends AbstractHeaderMatcher {
+
+ @ConfigComponent(type = ComponentType.PARAMETER, desc = "The pattern to match", name="expr", parameterType = String.class)
private final Pattern pattern;
/**
- * Constructs a regex pattern matcher with a unique random id and the specified Regex pattern.
- * @param pattern the pattern to match. Pattern will only match a single line from the input stream.
- */
- public SimpleRegexMatcher(Pattern pattern) {
- this(null, pattern);
- }
-
- /**
- * Constructs a regex pattern matcher with a unique random id and the specified Regex pattern.
- * @param id the id for this matcher
- * @param pattern the pattern to match. Pattern will only match a single line from the input stream.
+ * Constructs a regex pattern matcher with a unique random id and the specified
+ * Regex pattern.
+ *
+ * @param id the id for this matcher, may be null
+ * @param pattern the pattern to match. Pattern will only match a single line
+ * from the input stream.
*/
public SimpleRegexMatcher(String id, Pattern pattern) {
super(id);
this.pattern = pattern;
}
+ public String getPattern() {
+ return pattern.pattern();
+ }
+
@Override
- public boolean doMatch(String line) {
- return pattern.matcher(line).find();
+ public boolean matches(IHeaders headers) {
+ return pattern.matcher(headers.raw()).find();
}
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/SimpleTextMatcher.java b/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/SimpleTextMatcher.java
index f7ae6ef90..509ea929a 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/SimpleTextMatcher.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/analysis/matchers/SimpleTextMatcher.java
@@ -19,36 +19,50 @@
package org.apache.rat.analysis.matchers;
import org.apache.commons.lang3.StringUtils;
+import org.apache.rat.analysis.IHeaders;
+import org.apache.rat.config.parameters.ComponentType;
+import org.apache.rat.config.parameters.ConfigComponent;
/**
* A simple text matching IHeaderMatcher implementation.
*/
-public class SimpleTextMatcher extends AbstractSimpleMatcher {
- private final String pattern;
+@ConfigComponent(type = ComponentType.MATCHER, name = "text", desc = "Matches the enclosed text")
+public class SimpleTextMatcher extends AbstractHeaderMatcher {
+
+ @ConfigComponent(type = ComponentType.PARAMETER, name = "simpleText", desc = "The text to match", required=true)
+ private final String simpleText;
/**
* Constructs the simple text matcher for the simple string.
- * @param pattern The pattern to match. Will only match a single line from the input stream.
+ *
+ * @param simpleText The pattern to match. Will only match a single line from
+ * the input stream.
*/
- public SimpleTextMatcher(String pattern) {
- this(null, pattern);
+ public SimpleTextMatcher(String simpleText) {
+ this(null, simpleText);
}
/**
* Constructs the simple text matcher for the simple string.
+ *
* @param id The id for this matcher.
- * @param pattern The pattern to match. Will only match a single line from the input stream.
+ * @param simpleText The pattern to match. Will only match a single line from
+ * the input stream.
*/
- public SimpleTextMatcher(String id, String pattern) {
+ public SimpleTextMatcher(String id, String simpleText) {
super(id);
- if (StringUtils.isBlank(pattern)) {
- throw new IllegalArgumentException("Pattern may not be null, empty or blank");
+ if (StringUtils.isBlank(simpleText)) {
+ throw new IllegalArgumentException("Simple text may not be null, empty or blank");
}
- this.pattern = pattern;
+ this.simpleText = simpleText;
+ }
+
+ public String getSimpleText() {
+ return this.simpleText;
}
@Override
- public boolean doMatch(String line) {
- return line.contains(pattern);
+ public boolean matches(IHeaders headers) {
+ return headers.raw().contains(simpleText);
}
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/annotation/ApacheV2LicenseAppender.java b/apache-rat-core/src/main/java/org/apache/rat/annotation/ApacheV2LicenseAppender.java
index 8c4ccf7d0..34c6dec84 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/annotation/ApacheV2LicenseAppender.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/annotation/ApacheV2LicenseAppender.java
@@ -36,6 +36,7 @@ public class ApacheV2LicenseAppender extends AbstractLicenseAppender {
/**
* Create a license appender with the standard ASF license header.
+ * @param log The log to use during processing.
*/
public ApacheV2LicenseAppender(final Log log) {
super(log);
@@ -45,7 +46,8 @@ public ApacheV2LicenseAppender(final Log log) {
* Create a license appender with the given copyright line. This should be of
* the form "Copyright 2008 Foo"
*
- * @param copyright copyright line.
+ * @param log The log to use during processing.
+ * @param copyright copyright line to add to the headers.
*/
public ApacheV2LicenseAppender(final Log log, String copyright) {
super(log);
diff --git a/apache-rat-core/src/main/java/org/apache/rat/api/Document.java b/apache-rat-core/src/main/java/org/apache/rat/api/Document.java
index 9cf8d1413..71f8a47f5 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/api/Document.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/api/Document.java
@@ -24,7 +24,26 @@
import org.apache.rat.document.CompositeDocumentException;
+/**
+ * The representation of a document being scanned.
+ */
public interface Document {
+ /**
+ * An enumeraton of document types.
+ */
+ enum Type {
+ /** A generated document. */
+ GENERATED,
+ /** An unknown document type. */
+ UNKNOWN,
+ /** An archive type document. */
+ ARCHIVE,
+ /** A notice document (e.g. LICENSE file) */
+ NOTICE,
+ /** A binary file */
+ BINARY,
+ /** A standard document */
+ STANDARD}
/**
* @return the name of the current document.
@@ -42,19 +61,19 @@ public interface Document {
/**
* Streams the document's contents.
- * @return not null
+ * @return a non null input stream of the document.
* @throws IOException when stream could not be opened
*/
InputStream inputStream() throws IOException;
/**
* Gets data describing this resource.
- * @return not null
+ * @return a non null MetaData object.
*/
MetaData getMetaData();
/**
- * Is this a composite document?
+ * Tests if this a composite document.
* @return true if composite, false otherwise
*/
boolean isComposite();
diff --git a/apache-rat-core/src/main/java/org/apache/rat/api/MetaData.java b/apache-rat-core/src/main/java/org/apache/rat/api/MetaData.java
index 201f7f783..4acbbe3d4 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/api/MetaData.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/api/MetaData.java
@@ -18,340 +18,128 @@
*/
package org.apache.rat.api;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.stream.Stream;
-import org.apache.commons.lang3.StringUtils;
import org.apache.rat.license.ILicense;
+import org.apache.rat.license.ILicenseFamily;
/**
* Data about the document under test..
*/
public class MetaData {
- public static final String RAT_BASE_URL = "http://org/apache/rat/meta-data";
-
- // Document Categories
- public static final String RAT_URL_DOCUMENT_CATEGORY = RAT_BASE_URL + "#FileCategory";
- public static final String RAT_DOCUMENT_CATEGORY_VALUE_GENERATED = "GEN ";
- public static final String RAT_DOCUMENT_CATEGORY_VALUE_UNKNOWN = "?????";
- public static final String RAT_DOCUMENT_CATEGORY_VALUE_ARCHIVE = "archive";
- public static final String RAT_DOCUMENT_CATEGORY_VALUE_NOTICE = "notice";
- public static final String RAT_DOCUMENT_CATEGORY_VALUE_BINARY = "binary";
- public static final String RAT_DOCUMENT_CATEGORY_VALUE_STANDARD = "standard";
- public static final Datum RAT_DOCUMENT_CATEGORY_DATUM_GENERATED = new Datum(RAT_URL_DOCUMENT_CATEGORY, RAT_DOCUMENT_CATEGORY_VALUE_GENERATED);
- public static final Datum RAT_DOCUMENT_CATEGORY_DATUM_UNKNOWN = new Datum(RAT_URL_DOCUMENT_CATEGORY, RAT_DOCUMENT_CATEGORY_VALUE_UNKNOWN);
- public static final Datum RAT_DOCUMENT_CATEGORY_DATUM_ARCHIVE = new Datum(RAT_URL_DOCUMENT_CATEGORY, RAT_DOCUMENT_CATEGORY_VALUE_ARCHIVE);
- public static final Datum RAT_DOCUMENT_CATEGORY_DATUM_NOTICE = new Datum(RAT_URL_DOCUMENT_CATEGORY, RAT_DOCUMENT_CATEGORY_VALUE_NOTICE);
- public static final Datum RAT_DOCUMENT_CATEGORY_DATUM_BINARY = new Datum(RAT_URL_DOCUMENT_CATEGORY, RAT_DOCUMENT_CATEGORY_VALUE_BINARY);
- public static final Datum RAT_DOCUMENT_CATEGORY_DATUM_STANDARD = new Datum(RAT_URL_DOCUMENT_CATEGORY, RAT_DOCUMENT_CATEGORY_VALUE_STANDARD);
-
- // Header Categories
- public static final String RAT_URL_HEADER_CATEGORY = RAT_BASE_URL + "#HeaderCategory";
-
- // License Family Categories
- public static final String RAT_URL_LICENSE_FAMILY_CATEGORY= RAT_BASE_URL + "#LicenseFamilyCategory";
- // Shortcuts used in report output, must be exactly 5 characters
- public static final String RAT_LICENSE_FAMILY_CATEGORY_VALUE_GEN = "GEN ";
- public static final String RAT_LICENSE_FAMILY_CATEGORY_VALUE_UNKNOWN = "?????";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_CATEGORY_VALUE_ASL = "AL ";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_CATEGORY_VALUE_OASIS = "OASIS";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_CATEGORY_VALUE_W3CD = "W3CD ";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_CATEGORY_VALUE_W3C = "W3C ";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_CATEGORY_VALUE_DOJO = "DOJO ";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_CATEGORY_VALUE_TMF = "TMF ";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_CATEGORY_VALUE_GPL1 ="GPL1 ";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_CATEGORY_VALUE_GPL2 ="GPL2 ";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_CATEGORY_VALUE_GPL3 = "GPL3 ";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_CATEGORY_VALUE_MIT = "MIT ";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_CATEGORY_VALUE_CDDL1 = "CDDL1";
-
- public static final Datum RAT_LICENSE_FAMILY_CATEGORY_DATUM_GEN = new Datum(RAT_URL_LICENSE_FAMILY_CATEGORY, RAT_LICENSE_FAMILY_CATEGORY_VALUE_GEN);
- public static final Datum RAT_LICENSE_FAMILY_CATEGORY_DATUM_UNKNOWN = new Datum(RAT_URL_LICENSE_FAMILY_CATEGORY, RAT_LICENSE_FAMILY_CATEGORY_VALUE_UNKNOWN);
- @Deprecated
- public static final Datum RAT_LICENSE_FAMILY_CATEGORY_DATUM_ASL = new Datum(RAT_URL_LICENSE_FAMILY_CATEGORY, RAT_LICENSE_FAMILY_CATEGORY_VALUE_ASL);
- @Deprecated
- public static final Datum RAT_LICENSE_FAMILY_CATEGORY_DATUM_OASIS = new Datum(RAT_URL_LICENSE_FAMILY_CATEGORY, RAT_LICENSE_FAMILY_CATEGORY_VALUE_OASIS);
- @Deprecated
- public static final Datum RAT_LICENSE_FAMILY_CATEGORY_DATUM_W3CD = new Datum(RAT_URL_LICENSE_FAMILY_CATEGORY, RAT_LICENSE_FAMILY_CATEGORY_VALUE_W3CD);
- @Deprecated
- public static final Datum RAT_LICENSE_FAMILY_CATEGORY_DATUM_W3C = new Datum(RAT_URL_LICENSE_FAMILY_CATEGORY, RAT_LICENSE_FAMILY_CATEGORY_VALUE_W3C);
- @Deprecated
- public static final Datum RAT_LICENSE_FAMILY_CATEGORY_DATUM_DOJO = new Datum(RAT_URL_LICENSE_FAMILY_CATEGORY, RAT_LICENSE_FAMILY_CATEGORY_VALUE_DOJO);
- @Deprecated
- public static final Datum RAT_LICENSE_FAMILY_CATEGORY_DATUM_TMF = new Datum(RAT_URL_LICENSE_FAMILY_CATEGORY, RAT_LICENSE_FAMILY_CATEGORY_VALUE_TMF);
- @Deprecated
- public static final Datum RAT_LICENSE_FAMILY_CATEGORY_DATUM_GPL1 = new Datum(RAT_URL_LICENSE_FAMILY_CATEGORY,RAT_LICENSE_FAMILY_CATEGORY_VALUE_GPL1);
- @Deprecated
- public static final Datum RAT_LICENSE_FAMILY_CATEGORY_DATUM_GPL2 = new Datum(RAT_URL_LICENSE_FAMILY_CATEGORY,RAT_LICENSE_FAMILY_CATEGORY_VALUE_GPL2);
- @Deprecated
- public static final Datum RAT_LICENSE_FAMILY_CATEGORY_DATUM_GPL3 = new Datum(RAT_URL_LICENSE_FAMILY_CATEGORY,RAT_LICENSE_FAMILY_CATEGORY_VALUE_GPL3);
- @Deprecated
- public static final Datum RAT_LICENSE_FAMILY_CATEGORY_DATUM_MIT = new Datum(RAT_URL_LICENSE_FAMILY_CATEGORY,RAT_LICENSE_FAMILY_CATEGORY_VALUE_MIT);
- @Deprecated
- public static final Datum RAT_LICENSE_FAMILY_CATEGORY_DATUM_CDLL1 = new Datum(RAT_URL_LICENSE_FAMILY_CATEGORY,RAT_LICENSE_FAMILY_CATEGORY_VALUE_CDDL1);
-
- // License Family Standard Names
- public static final String RAT_URL_LICENSE_FAMILY_NAME= RAT_BASE_URL + "#LicenseFamilyName";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_NAME_VALUE_W3C_SOFTWARE_COPYRIGHT = "W3C Software Copyright";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_NAME_VALUE_W3C_DOCUMENT_COPYRIGHT = "W3C Document Copyright";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_NAME_VALUE_OASIS_OPEN_LICENSE = "OASIS Open License";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_NAME_VALUE_MODIFIED_BSD_LICENSE = "Modified BSD License";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_NAME_VALUE_APACHE_LICENSE_VERSION_2_0 = "Apache License Version 2.0";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_NAME_VALUE_GPL_VERSION_1 =
- "GNU General Public License, version 1";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_NAME_VALUE_GPL_VERSION_2 =
- "GNU General Public License, version 2";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_NAME_VALUE_GPL_VERSION_3 =
- "GNU General Public License, version 3";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_NAME_VALUE_MIT =
- "The MIT License";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_NAME_VALUE_CDDL1 =
- "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE Version 1.0";
- @Deprecated
- public static final String RAT_LICENSE_FAMILY_NAME_VALUE_ACADEMIC_FREE_LICENSE_VERSION_2_1 = "Academic Free License, Version 2.1";
-
- public static final String RAT_LICENSE_FAMILY_NAME_VALUE_UNKNOWN = "?????";
- @Deprecated
- public static final Datum RAT_LICENSE_FAMILY_NAME_DATUM_W3C_SOFTWARE_COPYRIGHT
- = new Datum(RAT_URL_LICENSE_FAMILY_NAME, RAT_LICENSE_FAMILY_NAME_VALUE_W3C_SOFTWARE_COPYRIGHT);
- @Deprecated
- public static final Datum RAT_LICENSE_FAMILY_NAME_DATUM_W3C_DOCUMENT_COPYRIGHT
- = new Datum(RAT_URL_LICENSE_FAMILY_NAME, RAT_LICENSE_FAMILY_NAME_VALUE_W3C_DOCUMENT_COPYRIGHT);
- @Deprecated
- public static final Datum RAT_LICENSE_FAMILY_NAME_DATUM_OASIS_OPEN_LICENSE
- = new Datum(RAT_URL_LICENSE_FAMILY_NAME, RAT_LICENSE_FAMILY_NAME_VALUE_OASIS_OPEN_LICENSE);
- @Deprecated
- public static final Datum RAT_LICENSE_FAMILY_NAME_DATUM_MODIFIED_BSD_LICENSE
- = new Datum(RAT_URL_LICENSE_FAMILY_NAME, RAT_LICENSE_FAMILY_NAME_VALUE_MODIFIED_BSD_LICENSE);
- @Deprecated
- public static final Datum RAT_LICENSE_FAMILY_NAME_DATUM_APACHE_LICENSE_VERSION_2_0
- = new Datum(RAT_URL_LICENSE_FAMILY_NAME, RAT_LICENSE_FAMILY_NAME_VALUE_APACHE_LICENSE_VERSION_2_0);
- @Deprecated
- public static final Datum
- RAT_LICENSE_FAMILY_NAME_DATUM_GPL_VERSION_1 = new Datum(RAT_URL_LICENSE_FAMILY_NAME, RAT_LICENSE_FAMILY_NAME_VALUE_GPL_VERSION_1);
- @Deprecated
- public static final Datum
- RAT_LICENSE_FAMILY_NAME_DATUM_GPL_VERSION_2 = new Datum(RAT_URL_LICENSE_FAMILY_NAME, RAT_LICENSE_FAMILY_NAME_VALUE_GPL_VERSION_2);
- @Deprecated
- public static final Datum
- RAT_LICENSE_FAMILY_NAME_DATUM_GPL_VERSION_3 = new Datum(RAT_URL_LICENSE_FAMILY_NAME, RAT_LICENSE_FAMILY_NAME_VALUE_GPL_VERSION_3);
- @Deprecated
- public static final Datum
- RAT_LICENSE_FAMILY_NAME_DATUM_MIT = new Datum(RAT_URL_LICENSE_FAMILY_NAME, RAT_LICENSE_FAMILY_NAME_VALUE_MIT);
- @Deprecated
- public static final Datum
- RAT_LICENSE_FAMILY_NAME_DATUM_CDDL1 = new Datum(RAT_URL_LICENSE_FAMILY_NAME, RAT_LICENSE_FAMILY_NAME_VALUE_CDDL1);
- @Deprecated
- public static final Datum RAT_LICENSE_FAMILY_NAME_DATUM_ACADEMIC_FREE_LICENSE_VERSION_2_1
- = new Datum(RAT_URL_LICENSE_FAMILY_NAME, RAT_LICENSE_FAMILY_NAME_VALUE_ACADEMIC_FREE_LICENSE_VERSION_2_1);
+ /** The list of matched licenses */
+ private final SortedSet matchedLicenses;
+ /** The list of License Family Categories that are approved */
+ private final Set approvedLicenses;
- public static final Datum RAT_LICENSE_FAMILY_NAME_DATUM_UNKNOWN
- = new Datum(RAT_URL_LICENSE_FAMILY_NAME, RAT_LICENSE_FAMILY_NAME_VALUE_UNKNOWN);
-
- // Header sample
- public static final String RAT_URL_HEADER_SAMPLE = RAT_BASE_URL + "#HeaderSample";
-
- // License Approval
- public static final String RAT_URL_APPROVED_LICENSE = RAT_BASE_URL + "#ApprovedLicense";
- public static final String RAT_APPROVED_LICENSE_VALUE_TRUE = Boolean.TRUE.toString();
- public static final String RAT_APPROVED_LICENSE_VALUE_FALSE = Boolean.FALSE.toString();
- public static final Datum RAT_APPROVED_LICENSE_DATUM_TRUE = new Datum(RAT_URL_APPROVED_LICENSE, RAT_APPROVED_LICENSE_VALUE_TRUE);
- public static final Datum RAT_APPROVED_LICENSE_DATUM_FALSE = new Datum(RAT_URL_APPROVED_LICENSE, RAT_APPROVED_LICENSE_VALUE_FALSE);
- @Deprecated
- public static final Datum RAT_APPROVED_LICENSE_DATIM_TRUE = RAT_APPROVED_LICENSE_DATUM_TRUE;
- @Deprecated
- public static final Datum RAT_APPROVED_LICENSE_DATIM_FALSE = RAT_APPROVED_LICENSE_DATUM_FALSE;
-
- /**
- * Only likely to be a small quantity of data
- * so trade some performance for simplicity.
- */
- private final List data;
+ private Document.Type documentType;
+ private String sampleHeader;
/**
* Create metadata without a content type.
*/
public MetaData() {
- this.data = new ArrayList<>(16);
+ this.matchedLicenses = new TreeSet<>();
+ this.approvedLicenses = new HashSet<>();
}
-
+
/**
- * Add the license information to the metadata.
- * @param license the license to add metadata for.
+ * Determines if a matching license has been detected.
+ * @return true if there is a matching license.
*/
- public void reportOnLicense(ILicense license) {
-
- if (StringUtils.isNotBlank(license.getNotes())) {
- set(new MetaData.Datum(MetaData.RAT_URL_HEADER_SAMPLE, license.getNotes()));
- }
- set(new MetaData.Datum(MetaData.RAT_URL_HEADER_CATEGORY, license.getLicenseFamily().getFamilyCategory()));
- set(new MetaData.Datum(MetaData.RAT_URL_LICENSE_FAMILY_CATEGORY, license.getLicenseFamily().getFamilyCategory()));
- set(new MetaData.Datum(MetaData.RAT_URL_LICENSE_FAMILY_NAME, license.getLicenseFamily().getFamilyName()));
+ public boolean detectedLicense() {
+ return !matchedLicenses.isEmpty();
}
-
/**
- * Gets all data.
- * @return unmodifiable view of the meta data.
+ * Sets the set of approved licenses.
+ * @param approvedLicenseFamilies the set of approved license families.
*/
- public Collection getData() {
- return Collections.unmodifiableCollection(data);
+ public void setApprovedLicenses(Set approvedLicenseFamilies) {
+ licenses().filter(lic -> approvedLicenseFamilies.contains(lic.getLicenseFamily()))
+ .forEach(lic -> approvedLicenses.add(lic.getId()));
}
-
+
/**
- * Adds a new datum.
- * Existing data with the same name are not replaced.
- * @param datum datum to add.
- * @see #set(org.apache.rat.api.MetaData.Datum)
+ * Gets the stream of licenses that have been matched.
+ * @return the stream of licenses that have been matched.
*/
- public void add(final Datum datum) {
- data.add(datum);
+ public Stream licenses() {
+ return matchedLicenses.stream();
}
-
+
/**
- * Puts in a new datum replacing any existing data.
- * Any current data matching the name are removed.
- * @param datum not null
- * @see #add(org.apache.rat.api.MetaData.Datum)
+ * Gets the stream of approved licenses that have been matched.
+ * @return the stream of approved licenses that have been matched.
*/
- public void set(final Datum datum) {
- clear(datum.getName());
- add(datum);
+ public Stream approvedLicenses() {
+ return licenses().filter(this::isApproved);
}
-
+
/**
- * Gets the first datum matching the given name.
- * @param name not null
- * @return the matching datum first added when there is any matching data,
- * null otherwise
+ * Determine if the license is an approved license.
+ * @param license the license to check;
+ * @return {@code true} if the license is in the list of approved licenses, {@code false} otherwise.
*/
- public Datum get(final String name) {
- Datum result = null;
- for (Datum next : data) {
- if (name.equals(next.getName())) {
- result = next;
- break;
- }
- }
- return result;
+ public boolean isApproved(ILicense license) {
+ return approvedLicenses.contains(license.getId());
}
-
+
/**
- * Gets the value of the first datum matching the given name.
- * @param name not null
- * @return the value of the matchin datum first added when there is any matching data,
- * null otherwise
+ * Gets the stream of unapproved licenses that have been matched.
+ * @return the stream of unapproved licenses that have been matched.
*/
- public String value(final String name) {
- final Datum datum = get(name);
- final String result;
- if (datum == null) {
- result = null;
- } else {
- result = datum.getValue();
- }
- return result;
+ public Stream unapprovedLicenses() {
+ return licenses().filter(lic -> !isApproved(lic));
}
-
+
/**
- * Removes all data matching the given name.
- * @param name not null
- * @return true if any data match, false otherwise
+ * Sets the sample header. This is the header that was collected during processing.
+ * @param sampleHeader the sample header to use.
*/
- public boolean clear(final String name) {
- boolean dataRemoved = false;
- for (final Iterator it = data.iterator();it.hasNext();) {
- final Datum datum = it.next();
- if (datum.getName().equals(name)) {
- it.remove();
- dataRemoved = true;
- }
- }
- return dataRemoved;
+ public void setSampleHeader(String sampleHeader) {
+ this.sampleHeader = sampleHeader;
}
-
+
/**
- * Clears all data.
+ * Gets the sample header.
+ * @return the smaple header.
*/
- public void clear() {
- data.clear();
+ public String getSampleHeader() {
+ return sampleHeader;
}
-
+
+ /**
+ * Sets the document type.
+ * @param type the document type for the document being recorded.
+ */
+ public void setDocumentType(Document.Type type) {
+ this.documentType = type;
+ }
+
/**
- * A datum.
+ * Gets the document type.
+ * @return the document type of the document that was recorded.
*/
- public static final class Datum {
- private final String name;
- private final String value;
-
- /**
- * Constructs a datum.
- * @param name not null
- * @param value not null
- */
- public Datum(final String name, final String value) {
- super();
- this.name = name;
- this.value = value;
- }
-
- /**
- * Gets the name of the data type.
- * To avoid collisions, it is recommended that URLs are used.
- * @return not null
- */
- public String getName() {
- return name;
- }
-
- /**
- * Data type value.
- * @return not null
- */
- public String getValue() {
- return value;
- }
+ public Document.Type getDocumentType() {
+ return this.documentType;
+ }
- /**
- * Constructs a String with all attributes
- * in name = value format.
- *
- * @return a String representation
- * of this object.
- */
- @Override
- public String toString()
- {
- return "Datum [ "
- + "name ='" + this.name + "',"
- + "value ='" + this.value + " "
- + "']";
- }
+ /**
+ * Add the license information to the metadata.
+ * @param license the license to add metadata for.
+ */
+ public void reportOnLicense(ILicense license) {
+ this.matchedLicenses.add(license);
+ }
+
+ @Override
+ public String toString() {
+ return String.format( "MetaData[%s license, %s approved]", matchedLicenses.size(), approvedLicenses.size());
}
}
-
\ No newline at end of file
diff --git a/apache-rat-core/src/main/java/org/apache/rat/api/RatException.java b/apache-rat-core/src/main/java/org/apache/rat/api/RatException.java
index 9d5c2c3a0..019a21124 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/api/RatException.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/api/RatException.java
@@ -19,9 +19,20 @@
package org.apache.rat.api;
public class RatException extends Exception {
+
+ public static RatException asRatException(Exception e) {
+ return e instanceof RatException ? (RatException) e : new RatException(e);
+ }
private static final long serialVersionUID = 4940711222435919034L;
+ public static RatException makeInstance(Exception e) {
+ if (e instanceof RatException) {
+ return (RatException) e;
+ }
+ return new RatException(e);
+ }
+
public RatException() {
super();
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/config/AddLicenseHeaders.java b/apache-rat-core/src/main/java/org/apache/rat/config/AddLicenseHeaders.java
index 523aa6eb0..b64361b95 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/config/AddLicenseHeaders.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/config/AddLicenseHeaders.java
@@ -23,8 +23,11 @@
* Value of addLicenseHeaders configuration option.
*/
public enum AddLicenseHeaders {
- TRUE, //
- FALSE, //
+ /** Add license headers to a *.new file*/
+ TRUE,
+ /** Do not add headers */
+ FALSE,
+ /** Add headers and overwrite the old file */
FORCED;
private static final char SEPARATOR = '|';
diff --git a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/TextCaptureBuilder.java b/apache-rat-core/src/main/java/org/apache/rat/config/parameters/ComponentType.java
similarity index 59%
rename from apache-rat-core/src/main/java/org/apache/rat/configuration/builders/TextCaptureBuilder.java
rename to apache-rat-core/src/main/java/org/apache/rat/config/parameters/ComponentType.java
index 8ad765974..bdddbb576 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/TextCaptureBuilder.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/config/parameters/ComponentType.java
@@ -16,11 +16,18 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.rat.configuration.builders;
+package org.apache.rat.config.parameters;
-/**
- * A marker interface for builders that accept text plain text to match.
+/**
+ * Types of components
*/
-public interface TextCaptureBuilder {
- T setText(String text);
-}
+public enum ComponentType {
+ /** A License, the top level component. May not be used as a child of any component type. */
+ LICENSE,
+ /** A Matcher */
+ MATCHER,
+ /** A Parameter for example the "id" parameter found in every component */
+ PARAMETER,
+ /** A parameter that is supplied by the environment. Currently systems using builders have to handle seting this. For example the list of matchers for the "MatcherRefBuilder" */
+ BULID_PARAMETER
+}
\ No newline at end of file
diff --git a/apache-rat-core/src/main/java/org/apache/rat/config/parameters/ConfigComponent.java b/apache-rat-core/src/main/java/org/apache/rat/config/parameters/ConfigComponent.java
new file mode 100644
index 000000000..c13260432
--- /dev/null
+++ b/apache-rat-core/src/main/java/org/apache/rat/config/parameters/ConfigComponent.java
@@ -0,0 +1,61 @@
+/*
+ * 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.config.parameters;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that marks a configuration component.
+ *
+ */
+@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ConfigComponent {
+ /**
+ * The common name for the component. If not specified the name of the field or class is used.
+ * @return the component name.
+ */
+ String name() default "";
+
+ /**
+ * The description of the component.
+ * @return the component description.
+ */
+ String desc() default "";
+ /**
+ * The component type
+ * @return the component type.
+ */
+ ComponentType type();
+
+ /**
+ * For collections defines the enclosed type.
+ * @return the enclosed type.
+ */
+ Class> parameterType() default void.class;
+
+ /**
+ * if {@code true} this component can be child of the containing component
+ * @return {@code true} if this component can be child of the containing component
+ */
+ boolean required() default false;
+}
\ No newline at end of file
diff --git a/apache-rat-core/src/main/java/org/apache/rat/config/parameters/Description.java b/apache-rat-core/src/main/java/org/apache/rat/config/parameters/Description.java
new file mode 100644
index 000000000..bbf01f3f1
--- /dev/null
+++ b/apache-rat-core/src/main/java/org/apache/rat/config/parameters/Description.java
@@ -0,0 +1,363 @@
+/*
+ * 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.config.parameters;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.rat.BuilderParams;
+import org.apache.rat.ConfigurationException;
+import org.apache.rat.analysis.IHeaderMatcher;
+import org.apache.rat.utils.Log;
+
+/**
+ * A description of a component.
+ */
+public class Description {
+ /** The type of component this describes */
+ private final ComponentType type;
+ /**
+ * The common name for the component. Set by ConfigComponent.name() or
+ * class/field name.
+ */
+ private final String name;
+ /** The description for the component */
+ private final String desc;
+ /** The class of the getter/setter parameter */
+ private final Class> childClass;
+ /** True if the getter/setter expects a collection of childClass objects */
+ private final boolean isCollection;
+ /** True if this component is required. */
+ private final boolean required;
+ /**
+ * A map of name to Description for all the components that are children of the
+ * described component.
+ */
+ private final Map children;
+
+ public static Predicate typePredicate(ComponentType type) {
+ return (d) -> d.getType() == type;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param type the type of the component.
+ * @param name the name of the component.
+ * @param desc the description of the component.
+ * @param isCollection true if the getter/setter expects a collection
+ * @param childClass the class for expected for the getter/setter.
+ * @param children the collection of descriptions for all the components that
+ * are children of the described component.
+ */
+ public Description(ComponentType type, String name, String desc, boolean isCollection, Class> childClass,
+ Collection children, boolean required) {
+ this.type = type;
+ this.name = name;
+ this.desc = desc;
+ this.isCollection = isCollection;
+ this.required = required;
+ if (type == ComponentType.BULID_PARAMETER) {
+ Method m;
+ try {
+ m = BuilderParams.class.getMethod(name);
+ } catch (NoSuchMethodException | SecurityException e) {
+ throw new ConfigurationException(String.format("'%s' is not a valid BuildParams method", name));
+ }
+ this.childClass = m.getReturnType();
+ } else {
+ this.childClass = childClass;
+ }
+ this.children = new TreeMap<>();
+ if (children != null) {
+ children.forEach(d -> {
+ this.children.put(d.name, d);
+ });
+ }
+ }
+
+ /**
+ * Constructor
+ *
+ * @param configComponent the configuration component
+ * @param isCollection the collection flag.
+ * @param childClass the type of object that the method getter/setter expects.
+ * @param children the collection of descriptions for all the components that
+ * are children the described component.
+ */
+ public Description(ConfigComponent configComponent, boolean isCollection, Class> childClass,
+ Collection children) {
+ this(configComponent.type(), configComponent.name(), configComponent.desc(), isCollection, childClass, children,
+ configComponent.required());
+ }
+
+ /**
+ * Get the canBeChild flag.
+ *
+ * @return {@code true} if this item can be a child of the containing item.
+ */
+ public boolean isRequired() {
+ return required;
+ }
+
+ /**
+ * Gets the type of the component.
+ *
+ * @return the component type.
+ */
+ public ComponentType getType() {
+ return type;
+ }
+
+ /**
+ * Get the isCollection flag.
+ *
+ * @return true if this is a collection.
+ */
+ public boolean isCollection() {
+ return isCollection;
+ }
+
+ /**
+ * Get the class of the objects for the getter/setter methods.
+ *
+ * @return the getter/setter param class.
+ */
+ public Class> getChildType() {
+ return childClass;
+ }
+
+ /**
+ * Gets the common name for the matcher. (e.g. 'text', 'spdx', etc.) May not be
+ * null.
+ *
+ * @return The common name for the item being inspected.
+ */
+ public String getCommonName() {
+ return name;
+ }
+
+ /**
+ * Gets the description of descriptive text for the component. May be an empty
+ * string or null.
+ *
+ * @return the descriptive text;
+ */
+ public String getDescription() {
+ return desc;
+ }
+
+ /**
+ * Retrieve the value of a the described parameter from the specified object.
+ *
+ * If the parameter is a collection return {@code null}.
+ *
+ * @param log the Log to log issues to.
+ * @param object the object that contains the value.
+ * @return the string value.
+ */
+ public String getParamValue(Log log, Object object) {
+ if (isCollection) {
+ return null;
+ }
+ try {
+ Object val = getter(object.getClass()).invoke(object);
+ return val == null ? null : val.toString();
+ } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException | NoSuchMethodException
+ | SecurityException e) {
+ log.error(System.err.format("Can not retrieve value for '%s' from %s%n", name, object.getClass().getName()),
+ e);
+ return null;
+ }
+ }
+
+ /**
+ * Gets a map of the parameters that the object contains. For example Copyright
+ * has 'start', 'stop', and 'owner' parameters. Some IHeaderMatchers have simple
+ * text values (e.g. 'regex' or 'text' types) these should list an unnamed
+ * parameter (empty string) with the text value.
+ *
+ * @return the map of parameters to the objects that represent them.
+ */
+ public Map getChildren() {
+ return children;
+ }
+
+ /**
+ * Get all the children of a specific type
+ *
+ * @param type the type to return
+ * @return the collection of children of the specified type.
+ */
+ public Collection childrenOfType(ComponentType type) {
+ return filterChildren(d -> d.getType() == type);
+ }
+
+ /**
+ * Get a filtered collection of the child descriptions.
+ *
+ * @param filter the filter to apply to the child descriptions.
+ * @return the collection of children that matche the filter..
+ */
+ public Collection filterChildren(Predicate filter) {
+ return children.values().stream().filter(filter).collect(Collectors.toList());
+ }
+
+ /**
+ * Generate a method name for this description.
+ *
+ * @param prefix the start of the method name (e.g. "set", "get" )
+ * @return the method name.
+ */
+ public String methodName(String prefix) {
+ return prefix + StringUtils.capitalize(name);
+ }
+
+ /**
+ * Returns the getter for the component in the specified class.
+ *
+ * @param clazz the Class to get the getter from.
+ * @return the getter Method.
+ * @throws NoSuchMethodException if the class does not have the getter.
+ * @throws SecurityException if the getter can not be accessed.
+ */
+ public Method getter(Class> clazz) throws NoSuchMethodException, SecurityException {
+ return clazz.getMethod(methodName("get"));
+ }
+
+ /**
+ * Returns the setter for the component in the specified class. Notes:
+ *
+ *
License can not be set in components. They are top level components.
+ *
Matcher expects an "add" method that accepts an
+ * IHeaderMatcher.Builder.
+ *
Parameter expects a {@code set(String)} method.
+ *
Unlabeled expects a {@code set(String)} method.
+ *
BuilderParam expects a {@code set} method that takes a
+ * {@code childeClass} argument.
+ *
+ *
+ * @param clazz the Class to get the getter from, generally a Builder class..
+ * @return the getter Method.
+ * @throws NoSuchMethodException if the class does not have the getter.
+ * @throws SecurityException if the getter can not be accessed.
+ */
+ public Method setter(Class> clazz) throws NoSuchMethodException, SecurityException {
+ String methodName = methodName(isCollection ? "add" : "set");
+ switch (type) {
+ case LICENSE:
+ throw new NoSuchMethodException("Can not set a License as a child");
+ case MATCHER:
+ return clazz.getMethod(methodName, IHeaderMatcher.Builder.class);
+ case PARAMETER:
+ return clazz.getMethod(methodName,
+ IHeaderMatcher.class.isAssignableFrom(childClass) ? IHeaderMatcher.Builder.class : childClass);
+ case BULID_PARAMETER:
+ return clazz.getMethod(methodName, childClass);
+ }
+ // should not happen
+ throw new IllegalStateException("Type " + type + " not valid.");
+ }
+
+ private void callSetter(Log log, Description description, IHeaderMatcher.Builder builder, String value) {
+ try {
+ description.setter(builder.getClass()).invoke(builder, value);
+ } catch (NoSuchMethodException e) {
+ String msg = String.format("No setter for '%s' on %s", description.getCommonName(),
+ builder.getClass().getCanonicalName());
+ log.error(msg);
+ throw new ConfigurationException(msg);
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException e) {
+ String msg = String.format("Unable to call setter for '%s' on %s", description.getCommonName(),
+ builder.getClass().getCanonicalName());
+ log.error(msg, e);
+ throw new ConfigurationException(msg, e);
+ }
+ }
+
+ /**
+ * Sets the children of values in the builder. Sets the parameters to the values
+ * specified in the map. Only children that accept string arguments should be
+ * specified.
+ *
+ * @param log The log to write messages to.
+ * @param builder The Matcher builder to set the values in.
+ * @param attributes a Map of parameter names to values.
+ */
+ public void setChildren(Log log, IHeaderMatcher.Builder builder, Map attributes) {
+ attributes.entrySet().forEach(entry -> setChild(log, builder, entry.getKey(), entry.getValue()));
+ }
+
+ /**
+ * Sets the child value in the builder.
+ *
+ * @param log The log to write messages to.
+ * @param builder The Matcher builder to set the values in.
+ * @param name the name of the child to set
+ * @param value the value of the parameter.
+ */
+ public void setChild(Log log, IHeaderMatcher.Builder builder, String name, String value) {
+ Description d = getChildren().get(name);
+ if (d == null) {
+ log.error(String.format("%s does not define a ConfigComponent for a member %s.",
+ builder.getClass().getCanonicalName(), name));
+ } else {
+ callSetter(log, d, builder, value);
+ }
+ }
+
+ @Override
+ public String toString() {
+ String childList = children.isEmpty() ? ""
+ : String.join(", ",
+ children.values().stream().map(Description::getCommonName).collect(Collectors.toList()));
+
+ return String.format("Description[%s t:%s c:%s %s children: [%s]] ", name, type, isCollection, childClass,
+ childList);
+ }
+
+ /**
+ * Write a description with indentation.
+ *
+ * @param indent the number of spaces to indent.
+ * @return the string with the formatted data.
+ */
+ public String toString(int indent) {
+ char[] spaces = new char[indent];
+ Arrays.fill(spaces, ' ');
+ String padding = String.copyValueOf(spaces);
+ String top = String.format("%sDescription[ t:%s n:%s c:%s %s%n%s %s] ", padding, type, name, isCollection,
+ childClass, padding, desc);
+ if (children.isEmpty()) {
+ return top;
+ }
+ StringBuilder sb = new StringBuilder(top);
+ for (Description child : children.values()) {
+ sb.append(System.lineSeparator()).append(child.toString(indent + 2));
+ }
+ return sb.toString();
+ }
+}
\ No newline at end of file
diff --git a/apache-rat-core/src/main/java/org/apache/rat/config/parameters/DescriptionBuilder.java b/apache-rat-core/src/main/java/org/apache/rat/config/parameters/DescriptionBuilder.java
new file mode 100644
index 000000000..8bd97e252
--- /dev/null
+++ b/apache-rat-core/src/main/java/org/apache/rat/config/parameters/DescriptionBuilder.java
@@ -0,0 +1,140 @@
+/*
+ * 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.config.parameters;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.rat.ConfigurationException;
+import org.apache.rat.ImplementationException;
+import org.apache.rat.analysis.IHeaderMatcher;
+import org.apache.rat.license.ILicense;
+
+/**
+ * Builds Description objects for the various Component instances.
+ */
+public class DescriptionBuilder {
+ /* do not instantiate */
+ private DescriptionBuilder() {}
+
+ /**
+ * Create the description for the object.
+ * The object must have a ConfigComponent annotation or null will be returned.
+ * @param obj the object to process.
+ * @return the Description of the object.
+ */
+ public static Description build(Object obj) {
+ if (obj instanceof ILicense) {
+ ILicense license = (ILicense)obj;
+ Class> clazz = obj.getClass();
+ ConfigComponent configComponent = clazz.getAnnotation(ConfigComponent.class);
+ if (configComponent == null || configComponent.type() != ComponentType.LICENSE) {
+ throw new ConfigurationException(String.format("Licenses must have License type specified in ConfigComponent annotation. Annotation missing or incorrect in %s", clazz));
+ }
+ List children = getConfigComponents(obj.getClass());
+ return new Description(ComponentType.LICENSE, license.getId(), license.getName(), false, null, children, false);
+ }
+ return buildMap(obj.getClass());
+ }
+
+ private static String fixupMethodName(Method method) {
+ String name = method.getName();
+ if (name.startsWith("get") || name.startsWith("set") || name.startsWith("add")) {
+ if (name.length() > 3) {
+ String retval = name.substring(3,4).toLowerCase();
+ if (name.length() > 4) {
+ retval+=name.substring(4);
+ }
+ return retval;
+ }
+ }
+ throw new ImplementationException(String.format("'%s' is not a recognized method name", name));
+ }
+ /**
+ * Build the list of descriptions for children of the class.
+ * @param clazz
+ * @return the Descriptions. of the child elements.
+ */
+ private static List getConfigComponents(Class> clazz) {
+ if (clazz == null || clazz == String.class || clazz == Object.class) {
+ return Collections.emptyList();
+ }
+ List result = new ArrayList<>();
+ for (Field field : clazz.getDeclaredFields()) {
+ ConfigComponent configComponent = field.getAnnotation(ConfigComponent.class);
+ if (configComponent != null) {
+ String name = StringUtils.isBlank(configComponent.name()) ? field.getName() : configComponent.name();
+ Class> childClazz = configComponent.parameterType() == void.class ? field.getType()
+ : configComponent.parameterType();
+ boolean isCollection = Iterable.class.isAssignableFrom(field.getType());
+
+ Description desc = new Description(configComponent.type(), name, configComponent.desc(), isCollection,
+ childClazz, getConfigComponents(childClazz), configComponent.required());
+ result.add(desc);
+ }
+ }
+ for (Method method : clazz.getDeclaredMethods()) {
+ ConfigComponent configComponent = method.getAnnotation(ConfigComponent.class);
+ if (configComponent != null) {
+ String name = StringUtils.isBlank(configComponent.name()) ? fixupMethodName(method) : configComponent.name();
+ Class> childClazz = configComponent.parameterType() == void.class ? method.getReturnType()
+ : configComponent.parameterType();
+ boolean isCollection = Iterable.class.isAssignableFrom(method.getReturnType());
+
+ Description desc = new Description(configComponent.type(), name, configComponent.desc(), isCollection,
+ childClazz, getConfigComponents(childClazz), configComponent.required());
+ result.add(desc);
+ }
+ }
+ result.addAll(getConfigComponents(clazz.getSuperclass()));
+ Arrays.stream(clazz.getInterfaces()).forEach(c -> result.addAll(getConfigComponents(c)));
+ return result;
+ }
+
+ private static ConfigComponent findConfigComponent(Class> clazz) {
+ if (clazz == null || clazz == String.class || clazz == Object.class) {
+ return null;
+ }
+ ConfigComponent configComponent = clazz.getAnnotation(ConfigComponent.class);
+ return configComponent == null ? findConfigComponent(clazz.getSuperclass()) : configComponent;
+ }
+ /**
+ * Create a description for a class.
+ * @param clazz the class to build the description for.
+ * @return the Description of the class or null if no ConfigComponent annotation was found on the class.
+ */
+ public static Description buildMap(Class> clazz) {
+ if (clazz == IHeaderMatcher.class) {
+ throw new ImplementationException("'clazz' parameter must not be IHeaderMatcher.class but may be a child of it");
+ }
+ ConfigComponent configComponent = findConfigComponent(clazz);
+ if (configComponent == null) {
+ return null;
+ }
+ List children = getConfigComponents(clazz);
+
+ return new Description(configComponent, false, null, children);
+ }
+}
\ No newline at end of file
diff --git a/apache-rat-core/src/main/java/org/apache/rat/configuration/LicenseReader.java b/apache-rat-core/src/main/java/org/apache/rat/configuration/LicenseReader.java
index 428137072..91fbec282 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/configuration/LicenseReader.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/configuration/LicenseReader.java
@@ -23,6 +23,7 @@
import org.apache.rat.license.ILicense;
import org.apache.rat.license.ILicenseFamily;
+import org.apache.rat.utils.Log;
/**
* An interface describing the methods of a LicenseReader.
@@ -41,7 +42,7 @@ public interface LicenseReader {
* @return A collection of ILicense.
*/
SortedSet readLicenses();
-
+
/**
* Reads the configuration and extracts instances of ILicenseFamily.
*
@@ -56,4 +57,10 @@ public interface LicenseReader {
* empty list if none specified.
*/
SortedSet approvedLicenseId();
+
+ /**
+ * Sets the logger to use during parsing.
+ * @param log the log to use.
+ */
+ void setLog(Log log);
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/configuration/MatcherBuilderTracker.java b/apache-rat-core/src/main/java/org/apache/rat/configuration/MatcherBuilderTracker.java
index 447ad82e3..93a2cb61f 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/configuration/MatcherBuilderTracker.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/configuration/MatcherBuilderTracker.java
@@ -19,6 +19,8 @@
package org.apache.rat.configuration;
import java.lang.reflect.InvocationTargetException;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@@ -34,7 +36,8 @@
*/
public class MatcherBuilderTracker {
- private static MatcherBuilderTracker INSTANCE;
+ /** The instance of the BuildTracker. */
+ public static MatcherBuilderTracker INSTANCE;
private final Map> matcherBuilders;
@@ -64,9 +67,9 @@ public static void addBuilder(String className, String name) {
public static AbstractBuilder getMatcherBuilder(String name) {
Class extends AbstractBuilder> clazz = instance().matcherBuilders.get(name);
if (clazz == null) {
- StringBuilder sb = new StringBuilder("\nValid builders\n");
- instance().matcherBuilders.keySet().forEach(x -> sb.append(x).append("\n"));
- sb.append("ERROR MSG\n");
+ StringBuilder sb = new StringBuilder(System.lineSeparator()).append("Valid builders").append(System.lineSeparator());
+ instance().matcherBuilders.keySet().forEach(x -> sb.append(x).append(System.lineSeparator()));
+ sb.append("ERROR MSG").append(System.lineSeparator());
throw new ConfigurationException(sb.append("No matcher builder named ").append(name).toString());
}
try {
@@ -81,6 +84,15 @@ public static AbstractBuilder getMatcherBuilder(String name) {
private MatcherBuilderTracker() {
matcherBuilders = new HashMap<>();
}
+
+
+ /**
+ * Gets a collection of classes that are recognized as builders.
+ * @return the collection of builder classes
+ */
+ public Collection> getClasses() {
+ return Collections.unmodifiableCollection(matcherBuilders.values());
+ }
private void addBuilderImpl(String className, String name) {
Objects.requireNonNull(className, "className may not be null");
@@ -93,7 +105,6 @@ private void addBuilderImpl(String className, String name) {
if (AbstractBuilder.class.isAssignableFrom(clazz)) {
@SuppressWarnings("unchecked")
Class extends AbstractBuilder> candidate = (Class extends AbstractBuilder>) clazz;
- // String name = attributes.get(AttributeName.name);
if (StringUtils.isBlank(name)) {
name = candidate.getSimpleName();
if (!name.endsWith("Builder")) {
diff --git a/apache-rat-core/src/main/java/org/apache/rat/configuration/MatcherReader.java b/apache-rat-core/src/main/java/org/apache/rat/configuration/MatcherReader.java
index d6e42253d..43e235a26 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/configuration/MatcherReader.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/configuration/MatcherReader.java
@@ -35,4 +35,5 @@ public interface MatcherReader {
* Reads the configuration and MatcherBuilder classes and adds them to Readers.
*/
void readMatcherBuilders();
+
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/configuration/XMLConfig.java b/apache-rat-core/src/main/java/org/apache/rat/configuration/XMLConfig.java
new file mode 100644
index 000000000..178ffd039
--- /dev/null
+++ b/apache-rat-core/src/main/java/org/apache/rat/configuration/XMLConfig.java
@@ -0,0 +1,109 @@
+/*
+ * 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.util.Arrays;
+
+/**
+ * Configuration definitions for XMLConfiguration reader and writer.
+ */
+public class XMLConfig {
+
+ /** id attribute name */
+ public final static String ATT_ID = "id";
+ /** name attribute name */
+ public final static String ATT_NAME = "name";
+ /** license reference attribute name */
+ public final static String ATT_LICENSE_REF = "license_ref";
+ /** class name attribute name */
+ public final static String ATT_CLASS_NAME = "class";
+ /** resource file name attribute name. */
+ public final static String ATT_RESOURCE = "resource";
+ /** root of the configuration file */
+ public final static String ROOT = "rat-config";
+ /** families element name */
+ public final static String FAMILIES = "families";
+ /** licenses element name */
+ public final static String LICENSES = "licenses";
+ /** license element name */
+ public final static String LICENSE = "license";
+ /** approved element name */
+ public final static String APPROVED = "approved";
+ /** family element name */
+ public final static String FAMILY = "family";
+ /** note element name */
+ public final static String NOTE = "note";
+ /** matchers element name */
+ public final static String MATCHERS = "matchers";
+ /** matcher element name */
+ public final static String MATCHER = "matcher";
+
+ /** License property names that should be children */
+ static final String[] LICENSE_CHILDREN = { "note", "matcher" };
+ /**
+ * License property names that should not be displayed contents should be placed
+ * inline
+ */
+ static final String[] LICENSE_INLINE = { "matcher" };
+
+ /**
+ * Matcher properties that should be directly inlined Entries are matcher node
+ * name / property name pairs. A matcher may only have one inline node and then
+ * only if there is no other non-property node.
+ */
+ static final String[][] INLINE_NODES = { { "any", "enclosed" }, { "all", "enclosed" }, { "not", "enclosed" },
+ { "text", "simpleText" } };
+
+ private XMLConfig() {
+ // do not instantiate
+ }
+
+ /**
+ * Returns true if the specified child node should be placed inline in the XML
+ * document.
+ *
+ * @param parent the parent node name.
+ * @param child the child node name.
+ * @return true if the child should be inlined.
+ */
+ public static boolean isInlineNode(String parent, String child) {
+ return Arrays.stream(INLINE_NODES).filter(s -> s[0].equals(parent) && s[1].equals(child)).findAny().isPresent();
+ }
+
+ /**
+ * Returns true if the child should be a child node of a license node, as
+ * opposed to a attribute of the license.
+ *
+ * @param child the name of the child node.
+ * @return true if the child should be a child node.
+ */
+ public static boolean isLicenseChild(String child) {
+ return Arrays.stream(LICENSE_CHILDREN).filter(s -> s.equals(child)).findAny().isPresent();
+ }
+
+ /**
+ * Return true if the child should be inlined in the parent node.
+ *
+ * @param child the name of the child node.
+ * @return true if the child should be inlined.
+ */
+ public static boolean isLicenseInline(String child) {
+ return Arrays.stream(LICENSE_INLINE).filter(s -> s.equals(child)).findAny().isPresent();
+ }
+}
\ No newline at end of file
diff --git a/apache-rat-core/src/main/java/org/apache/rat/configuration/XMLConfigurationReader.java b/apache-rat-core/src/main/java/org/apache/rat/configuration/XMLConfigurationReader.java
index b05711b07..fdacc1a6f 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/configuration/XMLConfigurationReader.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/configuration/XMLConfigurationReader.java
@@ -19,86 +19,58 @@
package org.apache.rat.configuration;
import java.io.IOException;
+import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.net.URL;
+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.beanutils.MethodUtils;
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.analysis.matchers.FullTextMatcher;
-import org.apache.rat.analysis.matchers.SimpleTextMatcher;
+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.configuration.builders.ChildContainerBuilder;
-import org.apache.rat.configuration.builders.MatcherRefBuilder;
-import org.apache.rat.configuration.builders.TextCaptureBuilder;
import org.apache.rat.license.ILicense;
import org.apache.rat.license.ILicenseFamily;
import org.apache.rat.license.LicenseFamilySetFactory;
import org.apache.rat.license.LicenseSetFactory;
+import org.apache.rat.utils.DefaultLog;
+import org.apache.rat.utils.Log;
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 class XMLConfigurationReader implements LicenseReader, MatcherReader {
- private final static String ATT_ID = "id";
- private final static String ATT_NAME = "name";
- private final static String ATT_DERIVED_FROM = "derived_from";
- private final static String ATT_LICENSE_REF = "license_ref";
- private final static String ATT_CLASS_NAME = "class";
-
- private final static String ROOT = "rat-config";
- private final static String FAMILIES = "families";
- private final static String LICENSES = "licenses";
- private final static String LICENSE = "license";
- private final static String APPROVED = "approved";
- private final static String FAMILY = "family";
- private final static String NOTE = "note";
- private final static String MATCHERS = "matchers";
- private final static String MATCHER = "matcher";
-
+ private Log log;
private Document document;
private final Element rootElement;
private final Element familiesElement;
@@ -107,7 +79,9 @@ public class XMLConfigurationReader implements LicenseReader, MatcherReader {
private final Element matchersElement;
private final SortedSet licenses;
+
private final Map matchers;
+ private final BuilderParams builderParams;
private final SortedSet licenseFamilies;
private final SortedSet approvedFamilies;
@@ -120,20 +94,40 @@ public XMLConfigurationReader() {
} catch (ParserConfigurationException e) {
throw new IllegalStateException("No XML parser defined", e);
}
- rootElement = document.createElement(ROOT);
+ rootElement = document.createElement(XMLConfig.ROOT);
document.appendChild(rootElement);
- familiesElement = document.createElement(FAMILIES);
+ familiesElement = document.createElement(XMLConfig.FAMILIES);
rootElement.appendChild(familiesElement);
- licensesElement = document.createElement(LICENSES);
+ licensesElement = document.createElement(XMLConfig.LICENSES);
rootElement.appendChild(licensesElement);
- approvedElement = document.createElement(APPROVED);
+ approvedElement = document.createElement(XMLConfig.APPROVED);
rootElement.appendChild(approvedElement);
- matchersElement = document.createElement(MATCHERS);
+ matchersElement = document.createElement(XMLConfig.MATCHERS);
rootElement.appendChild(matchersElement);
licenses = LicenseSetFactory.emptyLicenseSet();
licenseFamilies = LicenseFamilySetFactory.emptyLicenseFamilySet();
approvedFamilies = new TreeSet<>();
matchers = new HashMap<>();
+ builderParams = new BuilderParams() {
+ @Override
+ public Map matcherMap() {
+ return matchers;
+ }
+
+ @Override
+ public SortedSet licenseFamilies() {
+ return licenseFamilies;
+ }
+ };
+ }
+
+ @Override
+ public void setLog(Log log) {
+ this.log = log;
+ }
+
+ public Log getLog() {
+ return log == null ? DefaultLog.INSTANCE : log;
}
@Override
@@ -142,8 +136,29 @@ public void addLicenses(URL url) {
}
/**
- * Read the urls and create a single document to process.
+ * Read xml from a reader.
*
+ * @param reader the reader to read XML from.
+ */
+ public void read(Reader reader) {
+ DocumentBuilder builder;
+ try {
+ builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ } catch (ParserConfigurationException e) {
+ throw new ConfigurationException("Unable to create DOM builder", e);
+ }
+
+ try {
+ add(builder.parse(new InputSource(reader)));
+ } catch (SAXException | IOException e) {
+ throw new ConfigurationException("Unable to read inputSource", e);
+ }
+
+ }
+
+ /**
+ * Read the urls and extract the DOM information to create new objects.
+ *
* @param urls The URLs to read.
*/
public void read(URL... urls) {
@@ -164,7 +179,7 @@ public void read(URL... urls) {
/**
* Applies the {@code consumer} to each node in the {@code list}
- *
+ *
* @param list the NodeList to process
* @param consumer the consumer to apply to each node in the list.
*/
@@ -176,25 +191,23 @@ private void nodeListConsumer(NodeList list, Consumer consumer) {
/**
* Merge the new document into the document that this reader processes.
- *
+ *
* @param newDoc the Document to merge.
*/
public void add(Document newDoc) {
- nodeListConsumer(newDoc.getElementsByTagName(FAMILIES),
- nl -> nodeListConsumer( nl.getChildNodes(),
+ nodeListConsumer(newDoc.getElementsByTagName(XMLConfig.FAMILIES), nl -> nodeListConsumer(nl.getChildNodes(),
n -> familiesElement.appendChild(rootElement.getOwnerDocument().adoptNode(n.cloneNode(true)))));
- nodeListConsumer(newDoc.getElementsByTagName(LICENSE),
+ nodeListConsumer(newDoc.getElementsByTagName(XMLConfig.LICENSE),
n -> licensesElement.appendChild(rootElement.getOwnerDocument().adoptNode(n.cloneNode(true))));
- nodeListConsumer(newDoc.getElementsByTagName(APPROVED),
- nl -> nodeListConsumer( nl.getChildNodes(),
+ nodeListConsumer(newDoc.getElementsByTagName(XMLConfig.APPROVED), nl -> nodeListConsumer(nl.getChildNodes(),
n -> approvedElement.appendChild(rootElement.getOwnerDocument().adoptNode(n.cloneNode(true)))));
- nodeListConsumer(newDoc.getElementsByTagName(MATCHERS),
+ nodeListConsumer(newDoc.getElementsByTagName(XMLConfig.MATCHERS),
n -> matchersElement.appendChild(rootElement.getOwnerDocument().adoptNode(n.cloneNode(true))));
}
/**
* Get a map of Node attribute names to values.
- *
+ *
* @param node The node to process
* @return the map of attributes on the node
*/
@@ -208,87 +221,246 @@ private Map attributes(Node node) {
return result;
}
+ private void callSetter(Description desc, IHeaderMatcher.Builder builder, Object value) {
+ try {
+ desc.setter(builder.getClass()).invoke(builder, value);
+ } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
+ | SecurityException e) {
+ throw new ConfigurationException(e.getMessage(), e);
+ }
+ }
+
+ private void processBuilderParams(Description description, IHeaderMatcher.Builder builder) {
+ for (Description desc : description.childrenOfType(ComponentType.BULID_PARAMETER)) {
+ Method m = builderParams.get(desc.getCommonName());
+ try {
+ callSetter(desc, builder, m.invoke(builderParams));
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+ ImplementationException.makeInstance(e);
+ }
+ }
+ }
+
/**
- * Create a text matcher. Will construct a FullTextMatcher or a
- * SimpleTextMatcher depending on the complexity of the text.
+ * 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 id the id for the Matcher.
- * @param txt the text to match
- * @return the IHeaderMatcher that matches the text.
+ * @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.
*/
- public static IHeaderMatcher createTextMatcher(String id, String txt) {
- boolean complex = txt.contains(" ") | txt.contains("\\t") | txt.contains("\\n") | txt.contains("\\r")
- | txt.contains("\\f") | txt.contains("\\v");
- return complex ? new FullTextMatcher(id, txt) : new SimpleTextMatcher(id, txt);
+ private void processChildren(Description description, List children,
+ BiPredicate childProcessor) {
+ Iterator iter = children.iterator();
+ while (iter.hasNext()) {
+ Node child = iter.next();
+ Description childDescription = description.getChildren().get(child.getNodeName());
+ if (childDescription != null) {
+ if (childProcessor.test(child, childDescription)) {
+ iter.remove();
+ }
+ }
+ }
}
- private AbstractBuilder parseMatcher(Node matcherNode) {
- AbstractBuilder builder = MatcherBuilderTracker.getMatcherBuilder(matcherNode.getNodeName());
-
- NamedNodeMap nnm = matcherNode.getAttributes();
- for (int i = 0; i < nnm.getLength(); i++) {
- Node n = nnm.item(i);
- String methodName = "set" + StringUtils.capitalize(n.getNodeName());
- try {
- MethodUtils.invokeExactMethod(builder, methodName, n.getNodeValue());
- } catch (NoSuchMethodException e) {
- throw new ConfigurationException(
- String.format("'%s' does not have a setter '%s' that takes a String argument",
- matcherNode.getNodeName(), methodName));
- } catch (IllegalAccessException | InvocationTargetException | DOMException e) {
- throw new ConfigurationException(e);
+ private BiPredicate matcherChildNodeProcessor(AbstractBuilder builder, Description description) {
+ return (child, childDescription) -> {
+ switch (childDescription.getType()) {
+ case LICENSE:
+ case BULID_PARAMETER:
+ throw new ConfigurationException(String.format(
+ "%s may not be used as an enclosed matcher. %s '%s' found in '%s'", childDescription.getType(),
+ childDescription.getType(), childDescription.getCommonName(), description.getCommonName()));
+ case MATCHER:
+ AbstractBuilder b = parseMatcher(child);
+ callSetter(b.getDescription(), builder, b);
+ return true;
+ case PARAMETER:
+ if (!XMLConfig.isInlineNode(description.getCommonName(), childDescription.getCommonName())
+ || childDescription.getChildType() == String.class) {
+ callSetter(childDescription, builder, child.getTextContent());
+ } else {
+ callSetter(childDescription, builder, parseMatcher(child));
+ }
+ return true;
}
+ return false;
+ };
+ }
+
+ private void setValue(Description description, Description childDescription, IHeaderMatcher.Builder builder,
+ Node child) {
+ if (childDescription.getChildType() == String.class) {
+ callSetter(description, builder, child.getTextContent());
+ } else {
+ callSetter(description, builder, parseMatcher(child));
}
- if (builder instanceof ChildContainerBuilder) {
- ChildContainerBuilder ccb = (ChildContainerBuilder) builder;
- nodeListConsumer(matcherNode.getChildNodes(), x -> {
- if (x.getNodeType() == Node.ELEMENT_NODE) {
- ccb.add(parseMatcher(x));
+ }
+
+ /**
+ * Process the ELEEMENT_NODEs children of the parent whos names match child
+ * descriptions. All children 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> processChildNodes(Description description, Node parent,
+ BiPredicate childProcessor) {
+ boolean foundChildren = false;
+ List children = new ArrayList<>();
+ // check XML child nodes.
+ if (parent.hasChildNodes()) {
+
+ nodeListConsumer(parent.getChildNodes(), (n) -> {
+ if (n.getNodeType() == Node.ELEMENT_NODE) {
+ children.add(n);
}
});
+ foundChildren = !children.isEmpty();
+ if (foundChildren) {
+ processChildren(description, children, childProcessor);
+ }
}
- if (builder instanceof TextCaptureBuilder) {
- ((TextCaptureBuilder) builder).setText(matcherNode.getTextContent().trim());
- }
+ return new ImmutablePair>(foundChildren, children);
+ }
- if (builder instanceof MatcherRefBuilder) {
- ((MatcherRefBuilder) builder).setMatchers(matchers);
+ private AbstractBuilder parseMatcher(Node matcherNode) {
+ final AbstractBuilder builder = MatcherBuilderTracker.getMatcherBuilder(matcherNode.getNodeName());
+ if (builder == null) {
+ throw new ConfigurationException(String.format("No builder found for: %s", matcherNode.getNodeName()));
}
+ try {
+ final Description description = DescriptionBuilder.buildMap(builder.builtClass());
+
+ processBuilderParams(description, builder);
+// for (Description desc : description.childrenOfType(Component.Type.BuilderParam)) {
+// Method m = builderParams.get(desc.getCommonName());
+// try {
+// callSetter(desc, builder, m.invoke(builderParams));
+// } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+// ImplementationException.makeInstance(e);
+// }
+// }
+
+ // process the attributes
+ description.setChildren(getLog(), builder, attributes(matcherNode));
+
+ // check XML child nodes.
+ Pair> pair = processChildNodes(description, matcherNode,
+ matcherChildNodeProcessor(builder, description));
+ boolean foundChildren = pair.getLeft();
+ List children = pair.getRight();
+
+ // check for inline nodes that can accept child nodes.
+ List childDescriptions = description.getChildren().values().stream()
+ .filter(d -> XMLConfig.isInlineNode(description.getCommonName(), d.getCommonName()))
+ .collect(Collectors.toList());
+
+ for (Description childDescription : childDescriptions) {
+ if (XMLConfig.isInlineNode(description.getCommonName(), childDescription.getCommonName())) {
+ // can only process text inline if there were not child nodes.
+ if (childDescription.getChildType() == String.class) {
+ if (!foundChildren) {
+ callSetter(childDescription, builder, matcherNode.getTextContent());
+ }
+ } else {
+ Iterator iter = children.iterator();
+ while (iter.hasNext()) {
+ Node child = iter.next();
+ callSetter(childDescription, builder, parseMatcher(child));
+ iter.remove();
+ }
+ }
- if (builder.hasId()) {
- builder = new DelegatingBuilder(builder) {
- @Override
- public IHeaderMatcher build() {
- IHeaderMatcher result = delegate.build();
- matchers.put(result.getId(), result);
- return result;
+ } else {
+ processChildren(description, children, (child, childD) -> {
+ if (childD.getChildType().equals(description.getChildType())) {
+ setValue(childDescription, childD, builder, child);
+ return true;
+ }
+ return false;
+ });
}
- };
+
+ }
+
+ if (!children.isEmpty()) {
+ children.forEach(n -> getLog().warn(String.format("unrecognised child node '%s' in node '%s'%n",
+ n.getNodeName(), matcherNode.getNodeName())));
+ }
+
+ } catch (DOMException e) {
+ throw new ConfigurationException(e);
}
- return builder;
+ return builder.hasId() ? new IDRecordingBuilder(matchers, builder) : builder;
+ }
+
+ private BiPredicate licenseChildNodeProcessor(ILicense.Builder builder,
+ Description description) {
+ return (child, childDescription) -> {
+ switch (childDescription.getType()) {
+ case LICENSE:
+ throw new ConfigurationException(String.format(
+ "%s may not be enclosed in another license. %s '%s' found in '%s'", childDescription.getType(),
+ childDescription.getType(), childDescription.getCommonName(), description.getCommonName()));
+ case BULID_PARAMETER:
+ break;
+ case MATCHER:
+ AbstractBuilder b = parseMatcher(child);
+ callSetter(b.getDescription(), builder, b);
+ return true;
+ case PARAMETER:
+ if ((!XMLConfig.isLicenseChild(childDescription.getCommonName()))
+ || childDescription.getChildType() == String.class) {
+ callSetter(childDescription, builder, child.getTextContent());
+ } else {
+ callSetter(childDescription, builder, parseMatcher(child));
+ }
+ return true;
+ }
+ return false;
+ };
}
private ILicense parseLicense(Node licenseNode) {
- Map attributes = attributes(licenseNode);
ILicense.Builder builder = ILicense.builder();
-
- builder.setLicenseFamilyCategory(attributes.get(FAMILY));
- builder.setName(attributes.get(ATT_NAME));
- builder.setId(attributes.get(ATT_ID));
-
- StringBuilder notesBuilder = new StringBuilder();
- nodeListConsumer(licenseNode.getChildNodes(), x -> {
- if (x.getNodeType() == Node.ELEMENT_NODE) {
- if (x.getNodeName().equals(NOTE)) {
- notesBuilder.append(x.getTextContent()).append("\n");
- } else {
- builder.setMatcher(parseMatcher(x));
+ Description description = builder.getDescription();
+
+ processBuilderParams(description, builder);
+
+ description.setChildren(getLog(), builder, attributes(licenseNode));
+
+ Pair> pair = processChildNodes(description, licenseNode,
+ licenseChildNodeProcessor(builder, description));
+ List children = pair.getRight();
+
+ // check for inline nodes that can accept child nodes.
+ List childDescriptions = description.getChildren().values().stream()
+ .filter(d -> XMLConfig.isLicenseInline(d.getCommonName())).collect(Collectors.toList());
+ for (Description childDescription : childDescriptions) {
+ Iterator iter = children.iterator();
+ while (iter.hasNext()) {
+ Node child = iter.next();
+ if (MatcherBuilderTracker.getMatcherBuilder(child.getNodeName()) != null) {
+ callSetter(childDescription, builder, parseMatcher(child));
+ iter.remove();
}
}
- });
- builder.setDerivedFrom(StringUtils.defaultIfBlank(attributes.get(ATT_DERIVED_FROM), null));
- builder.setNotes(StringUtils.defaultIfBlank(notesBuilder.toString().trim(), null));
- return builder.build(licenseFamilies);
+ }
+
+ if (!children.isEmpty()) {
+ children.forEach(n -> getLog().warn(String.format("unrecognised child node '%s' in node '%s'%n",
+ n.getNodeName(), licenseNode.getNodeName())));
+ }
+ return builder.build();
}
@Override
@@ -296,60 +468,62 @@ public SortedSet readLicenses() {
readFamilies();
readMatcherBuilders();
if (licenses.isEmpty()) {
- nodeListConsumer(document.getElementsByTagName(LICENSE), x -> licenses.add(parseLicense(x)));
+ nodeListConsumer(document.getElementsByTagName(XMLConfig.LICENSE), x -> licenses.add(parseLicense(x)));
document = null;
}
return Collections.unmodifiableSortedSet(licenses);
}
-
@Override
public SortedSet readFamilies() {
if (licenseFamilies.isEmpty()) {
- nodeListConsumer(document.getElementsByTagName(FAMILIES),
+ nodeListConsumer(document.getElementsByTagName(XMLConfig.FAMILIES),
x -> nodeListConsumer(x.getChildNodes(), this::parseFamily));
- nodeListConsumer(document.getElementsByTagName(APPROVED),
+ nodeListConsumer(document.getElementsByTagName(XMLConfig.APPROVED),
x -> nodeListConsumer(x.getChildNodes(), this::parseApproved));
}
return Collections.unmodifiableSortedSet(licenseFamilies);
}
-
+
private ILicenseFamily parseFamily(Map attributes) {
- if (attributes.containsKey(ATT_ID)) {
+ if (attributes.containsKey(XMLConfig.ATT_ID)) {
ILicenseFamily.Builder builder = ILicenseFamily.builder();
- builder.setLicenseFamilyCategory(attributes.get(ATT_ID));
- builder.setLicenseFamilyName(StringUtils.defaultIfBlank(attributes.get(ATT_NAME), attributes.get(ATT_ID)));
+ builder.setLicenseFamilyCategory(attributes.get(XMLConfig.ATT_ID));
+ builder.setLicenseFamilyName(
+ StringUtils.defaultIfBlank(attributes.get(XMLConfig.ATT_NAME), attributes.get(XMLConfig.ATT_ID)));
return builder.build();
}
return null;
}
private void parseFamily(Node familyNode) {
- if (FAMILY.equals(familyNode.getNodeName())) {
+ if (XMLConfig.FAMILY.equals(familyNode.getNodeName())) {
ILicenseFamily result = parseFamily(attributes(familyNode));
if (result == null) {
- throw new ConfigurationException(String.format("families/family tag requires %s attribute", ATT_ID));
+ throw new ConfigurationException(
+ String.format("families/family tag requires %s attribute", XMLConfig.ATT_ID));
}
licenseFamilies.add(result);
}
}
private void parseApproved(Node approvedNode) {
- if (FAMILY.equals(approvedNode.getNodeName())) {
+ if (XMLConfig.FAMILY.equals(approvedNode.getNodeName())) {
Map attributes = attributes(approvedNode);
- if (attributes.containsKey(ATT_LICENSE_REF)) {
- approvedFamilies.add(attributes.get(ATT_LICENSE_REF));
- } else if (attributes.containsKey(ATT_ID)) {
+ if (attributes.containsKey(XMLConfig.ATT_LICENSE_REF)) {
+ approvedFamilies.add(attributes.get(XMLConfig.ATT_LICENSE_REF));
+ } else if (attributes.containsKey(XMLConfig.ATT_ID)) {
ILicenseFamily target = parseFamily(attributes);
licenseFamilies.add(target);
approvedFamilies.add(target.getFamilyCategory());
} else {
- throw new ConfigurationException(
- String.format("family tag requires %s or %s attribute", ATT_LICENSE_REF, ATT_ID));
+ throw new ConfigurationException(String.format("family tag requires %s or %s attribute",
+ XMLConfig.ATT_LICENSE_REF, XMLConfig.ATT_ID));
}
}
}
+ ////////////////////////////////////////// MatcherReader methods
@Override
public SortedSet approvedLicenseId() {
if (licenses.isEmpty()) {
@@ -365,15 +539,15 @@ public SortedSet approvedLicenseId() {
private void parseMatcherBuilder(Node classNode) {
Map attributes = attributes(classNode);
- if (attributes.get(ATT_CLASS_NAME) == null) {
- throw new ConfigurationException("matcher must have a " + ATT_CLASS_NAME + " attribute");
+ if (attributes.get(XMLConfig.ATT_CLASS_NAME) == null) {
+ throw new ConfigurationException("matcher must have a " + XMLConfig.ATT_CLASS_NAME + " attribute");
}
- MatcherBuilderTracker.addBuilder(attributes.get(ATT_CLASS_NAME), attributes.get(ATT_NAME));
+ MatcherBuilderTracker.addBuilder(attributes.get(XMLConfig.ATT_CLASS_NAME), attributes.get(XMLConfig.ATT_NAME));
}
@Override
public void readMatcherBuilders() {
- nodeListConsumer(document.getElementsByTagName(MATCHER), this::parseMatcherBuilder);
+ nodeListConsumer(document.getElementsByTagName(XMLConfig.MATCHER), this::parseMatcherBuilder);
}
@Override
@@ -381,14 +555,35 @@ public void addMatchers(URL url) {
read(url);
}
+ ////////////// Helper classes
/**
* An abstract builder that delegates to another abstract builder.
*/
- abstract static class DelegatingBuilder extends AbstractBuilder {
- protected final AbstractBuilder delegate;
+ static class IDRecordingBuilder extends AbstractBuilder {
+ private final AbstractBuilder delegate;
+ private final Map matchers;
- DelegatingBuilder(AbstractBuilder delegate) {
+ IDRecordingBuilder(final Map matchers, final AbstractBuilder delegate) {
this.delegate = delegate;
+ this.matchers = matchers;
+ setId(delegate.getId());
+ }
+
+ @Override
+ public IHeaderMatcher build() {
+ IHeaderMatcher result = delegate.build();
+ matchers.put(result.getId(), result);
+ return result;
+ }
+
+ @Override
+ public Class> builtClass() throws SecurityException {
+ return delegate.builtClass();
+ }
+
+ @Override
+ public Description getDescription() {
+ return delegate.getDescription();
}
}
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/configuration/XMLConfigurationWriter.java b/apache-rat-core/src/main/java/org/apache/rat/configuration/XMLConfigurationWriter.java
new file mode 100644
index 000000000..1beb45f6d
--- /dev/null
+++ b/apache-rat-core/src/main/java/org/apache/rat/configuration/XMLConfigurationWriter.java
@@ -0,0 +1,296 @@
+/*
+ * 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.Writer;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.UUID;
+import java.util.function.Predicate;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.rat.ImplementationException;
+import org.apache.rat.ReportConfiguration;
+import org.apache.rat.analysis.IHeaderMatcher;
+import org.apache.rat.api.RatException;
+import org.apache.rat.config.parameters.ComponentType;
+import org.apache.rat.config.parameters.Description;
+import org.apache.rat.configuration.builders.MatcherRefBuilder;
+import org.apache.rat.license.ILicense;
+import org.apache.rat.license.ILicenseFamily;
+import org.apache.rat.license.LicenseSetFactory.LicenseFilter;
+import org.apache.rat.report.xml.writer.IXmlWriter;
+import org.apache.rat.report.xml.writer.impl.base.XmlWriter;
+
+/**
+ * A class that writes the XML configuration file format.
+ */
+public class XMLConfigurationWriter {
+ private ReportConfiguration configuration;
+ private Set matchers;
+ private Set licenseChildren;
+
+ /**
+ * Constructor
+ * @param configuration the configuration to write.
+ */
+ public XMLConfigurationWriter(ReportConfiguration configuration) {
+ this.configuration = configuration;
+ this.matchers = new HashSet<>();
+ licenseChildren = new HashSet<>(Arrays.asList(XMLConfig.LICENSE_CHILDREN));
+ }
+
+ private Predicate attributeFilter(Description parent) {
+ return d -> {
+ if (d.getType() == ComponentType.PARAMETER) {
+ switch (parent.getType()) {
+ case MATCHER:
+ return !XMLConfig.isInlineNode(parent.getCommonName(), d.getCommonName());
+ case LICENSE:
+ return !licenseChildren.contains(d.getCommonName());
+ default:
+ return true;
+ }
+ }
+ return false;
+ };
+ }
+
+ /**
+ * Writes the configuration to the specified writer.
+ * @param plainWriter a writer to write the XML to.
+ * @throws RatException on error.
+ */
+ public void write(Writer plainWriter) throws RatException {
+ write(new XmlWriter(plainWriter));
+ }
+
+ /**
+ * Writes the configuration to an IXmlWriter instance.
+ * @param writer the IXmlWriter to write to.
+ * @throws RatException on error.
+ */
+ public void write(IXmlWriter writer) throws RatException {
+ if (configuration.listFamilies() != LicenseFilter.NONE || configuration.listLicenses() != LicenseFilter.NONE) {
+ try {
+ writer.openElement(XMLConfig.ROOT);
+
+ // write Families section
+ SortedSet families = configuration.getLicenseFamilies(configuration.listFamilies());
+ if (!families.isEmpty()) {
+ writer.openElement(XMLConfig.FAMILIES);
+ for (ILicenseFamily family : families) {
+ writeFamily(writer, family);
+ }
+ writer.closeElement(); // FAMILIES
+ }
+
+ // write licenses section
+ SortedSet licenses = configuration.getLicenses(configuration.listLicenses());
+ if (!licenses.isEmpty()) {
+ writer.openElement(XMLConfig.LICENSES);
+ for (ILicense license : licenses) {
+ writeDescription(writer, license.getDescription(), license);
+ }
+ writer.closeElement();// LICENSES
+ }
+
+ // write approved section
+ writer.openElement(XMLConfig.APPROVED);
+ for (String family : configuration.getApprovedLicenseCategories()) {
+ writer.openElement(XMLConfig.APPROVED).attribute(XMLConfig.ATT_LICENSE_REF, family.trim())
+ .closeElement();
+ }
+ writer.closeElement(); // APPROVED
+
+ // write matchers section
+ MatcherBuilderTracker tracker = MatcherBuilderTracker.INSTANCE;
+ writer.openElement(XMLConfig.MATCHERS);
+ for (Class> clazz : tracker.getClasses()) {
+ writer.openElement(XMLConfig.MATCHER).attribute(XMLConfig.ATT_CLASS_NAME, clazz.getCanonicalName())
+ .closeElement();
+ }
+ writer.closeElement(); // MATCHERS
+
+ writer.closeElement(); // ROOT
+ } catch (IOException e) {
+ throw new RatException(e);
+ }
+ }
+ }
+
+ private void writeFamily(IXmlWriter writer, ILicenseFamily family) throws RatException {
+ try {
+ writer.openElement(XMLConfig.FAMILY).attribute(XMLConfig.ATT_ID, family.getFamilyCategory().trim())
+ .attribute(XMLConfig.ATT_NAME, family.getFamilyName());
+ writer.closeElement();
+ } catch (IOException e) {
+ throw new RatException(e);
+ }
+ }
+
+ private void writeDescriptions(IXmlWriter writer, Collection descriptions, IHeaderMatcher component)
+ throws RatException {
+ for (Description description : descriptions) {
+ writeDescription(writer, description, component);
+ }
+ }
+
+ private void writeChildren(IXmlWriter writer, Description description, IHeaderMatcher component)
+ throws RatException {
+ writeAttributes(writer, description.filterChildren(attributeFilter(component.getDescription())), component);
+ writeDescriptions(writer, description.filterChildren(attributeFilter(component.getDescription()).negate()),
+ component);
+ }
+
+ private void writeAttributes(IXmlWriter writer, Collection descriptions, IHeaderMatcher component)
+ throws RatException {
+ for (Description d : descriptions) {
+ try {
+ writeAttribute(writer, d, component);
+ } catch (IOException e) {
+ throw new RatException(e);
+ }
+ }
+ }
+
+ private void writeComment(IXmlWriter writer, Description description) throws IOException {
+ if (StringUtils.isNotBlank(description.getDescription())) {
+ writer.comment(description.getDescription());
+ }
+ }
+
+ private void writeAttribute(IXmlWriter writer, Description description, IHeaderMatcher component)
+ throws IOException {
+ String paramValue = description.getParamValue(configuration.getLog(), component);
+ if (paramValue != null) {
+ writer.attribute(description.getCommonName(), paramValue);
+ }
+ }
+
+ /* package private for testing */
+ @SuppressWarnings("unchecked")
+ void writeDescription(IXmlWriter writer, Description description, IHeaderMatcher component) throws RatException {
+ try {
+ switch (description.getType()) {
+ case MATCHER:
+ // see if id was registered
+ Optional id = description.childrenOfType(ComponentType.PARAMETER).stream()
+ .filter(i -> XMLConfig.ATT_ID.equals(i.getCommonName())).findFirst();
+
+ // id will not be present in matcherRef
+ if (id.isPresent()) {
+ String matcherId = id.get().getParamValue(configuration.getLog(), component);
+ // if we have seen the ID before just put a reference to the other one.
+ if (matchers.contains(matcherId.toString())) {
+ component = new MatcherRefBuilder.IHeaderMatcherProxy(matcherId.toString(), null);
+ description = component.getDescription();
+ } else {
+ matchers.add(matcherId.toString());
+ }
+ // remove the matcher id if it is a UUID
+ try {
+ UUID.fromString(matcherId);
+ description.getChildren().remove(XMLConfig.ATT_ID);
+ } catch (IllegalArgumentException expected) {
+ if (description.getCommonName().equals("spdx")) {
+ description.getChildren().remove(XMLConfig.ATT_ID);
+ }
+ }
+ }
+
+ // if resource only list the resource not the contents of the matcher
+ Optional resource = description.childrenOfType(ComponentType.PARAMETER).stream()
+ .filter(i -> XMLConfig.ATT_RESOURCE.equals(i.getCommonName())).findFirst();
+ if (resource.isPresent()) {
+ String resourceStr = resource.get().getParamValue(configuration.getLog(), component);
+ if (StringUtils.isNotBlank(resourceStr)) {
+ description.getChildren().remove("enclosed");
+ }
+ }
+ writeComment(writer, description);
+ writer.openElement(description.getCommonName());
+ writeChildren(writer, description, component);
+ writer.closeElement();
+ break;
+ case LICENSE:
+ writer.openElement(XMLConfig.LICENSE);
+ writeChildren(writer, description, component);
+ writer.closeElement();
+ break;
+ case PARAMETER:
+ if ("id".equals(description.getCommonName())) {
+ try {
+ String paramId = description.getParamValue(configuration.getLog(), component);
+ // if a UUID skip it.
+ if (paramId != null) {
+ UUID.fromString(paramId.toString());
+ return;
+ }
+ } catch (IllegalArgumentException expected) {
+ // do nothing.
+ }
+ }
+ if (description.getChildType() == String.class) {
+
+ boolean inline = XMLConfig.isInlineNode(component.getDescription().getCommonName(),
+ description.getCommonName());
+ String s = description.getParamValue(configuration.getLog(), component);
+ if (StringUtils.isNotBlank(s)) {
+ if (!inline) {
+ writer.openElement(description.getCommonName());
+ }
+ writer.content(description.getParamValue(configuration.getLog(), component));
+ if (!inline) {
+ writer.closeElement();
+ }
+ }
+ } else {
+ try {
+ if (description.isCollection()) {
+ for (IHeaderMatcher matcher : (Collection) description
+ .getter(component.getClass()).invoke(component)) {
+ writeDescription(writer, matcher.getDescription(), matcher);
+ }
+ } else {
+ IHeaderMatcher matcher = (IHeaderMatcher) description.getter(component.getClass())
+ .invoke(component);
+ writeDescription(writer, matcher.getDescription(), matcher);
+ }
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
+ | NoSuchMethodException | SecurityException | RatException e) {
+ throw new ImplementationException(e);
+ }
+ }
+ break;
+ case BULID_PARAMETER:
+ // ignore;
+ break;
+ }
+ } catch (IOException e) {
+ throw new RatException(e);
+ }
+ }
+}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/AbstractBuilder.java b/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/AbstractBuilder.java
index 7d546a03a..897e8e4d0 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/AbstractBuilder.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/AbstractBuilder.java
@@ -39,28 +39,29 @@ protected AbstractBuilder() {
* @param id the id to use.
* @return this builder for chaining.
*/
- public final AbstractBuilder setId(String id) {
+ public AbstractBuilder setId(String id) {
this.id = id;
return this;
}
/**
+ * Returns true if this builder has an ID specified.
* @return {@code true} if the id is not null and not blank.
*/
public final boolean hasId() {
return !StringUtils.isBlank(id);
}
-
+
/**
+ * Gets the ID as specified in the builder.
* @return the id as specified in the builder.
*/
- protected String getId() {
+ public final String getId() {
return id;
}
-
+
@Override
public String toString() {
- return String.format( "%s with id %s", this.getClass(), id);
+ return String.format("%s with id %s", this.getClass(), id);
}
-
}
\ No newline at end of file
diff --git a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/AllBuilder.java b/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/AllBuilder.java
index 9c0980e5f..91235246d 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/AllBuilder.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/AllBuilder.java
@@ -18,7 +18,6 @@
*/
package org.apache.rat.configuration.builders;
-import org.apache.rat.analysis.IHeaderMatcher;
import org.apache.rat.analysis.matchers.AndMatcher;
/**
@@ -27,7 +26,7 @@
public class AllBuilder extends ChildContainerBuilder {
@Override
- public IHeaderMatcher build() {
- return new AndMatcher(getId(), getChildren());
+ public AndMatcher build() {
+ return new AndMatcher(getId(), getEnclosed(), resource);
}
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/AnyBuilder.java b/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/AnyBuilder.java
index 6ecb73e97..929f41661 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/AnyBuilder.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/AnyBuilder.java
@@ -18,7 +18,6 @@
*/
package org.apache.rat.configuration.builders;
-import org.apache.rat.analysis.IHeaderMatcher;
import org.apache.rat.analysis.matchers.OrMatcher;
/**
@@ -27,7 +26,7 @@
public class AnyBuilder extends ChildContainerBuilder {
@Override
- public IHeaderMatcher build() {
- return new OrMatcher(getId(), getChildren());
+ public OrMatcher build() {
+ return new OrMatcher(getId(), getEnclosed(), resource);
}
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/ChildContainerBuilder.java b/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/ChildContainerBuilder.java
index d67264b42..bb23e2b57 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/ChildContainerBuilder.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/ChildContainerBuilder.java
@@ -26,24 +26,25 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.rat.ConfigurationException;
import org.apache.rat.analysis.IHeaderMatcher;
-import org.apache.rat.analysis.IHeaderMatcher.Builder;
/**
* Constructs a builder that contains other builders.
*/
public abstract class ChildContainerBuilder extends AbstractBuilder {
- /**
- * The list of builders that will build the enclosed matchers.
- */
+ /** The list of builders that will build the enclosed matchers. */
protected final List children = new ArrayList<>();
+ /** The resource the builders came from if it was read from a resource */
+ protected String resource;
+
/**
* Empty default constructor.
*/
@@ -52,58 +53,64 @@ protected ChildContainerBuilder() {
/**
* Reads a text file. Each line becomes a text matcher in the resulting list.
- *
+ *
* @param resourceName the name of the resource to read.
* @return a List of Matchers, one for each non-empty line in the input file.
*/
public AbstractBuilder setResource(String resourceName) {
- URL url = this.getClass().getResource(resourceName);
- try (final InputStream in = url.openStream()) {
- BufferedReader buffer = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
- String txt;
- while (null != (txt = buffer.readLine())) {
- txt = txt.trim();
- if (StringUtils.isNotBlank(txt)) {
- children.add(Builder.text().setText(txt));
- }
+ URL url = this.getClass().getResource(resourceName);
+ try (final InputStream in = url.openStream()) {
+ BufferedReader buffer = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
+ String txt;
+ while (null != (txt = buffer.readLine())) {
+ txt = txt.trim();
+ if (StringUtils.isNotBlank(txt)) {
+ children.add(new TextBuilder().setSimpleText(txt));
}
- return this;
- } catch (IOException e) {
- throw new ConfigurationException("Unable to read matching text file: " + resourceName, e);
}
+ this.resource = resourceName;
+ return this;
+ } catch (IOException e) {
+ throw new ConfigurationException("Unable to read matching text file: " + resourceName, e);
+ }
}
-
+
/**
* Adds a builder to the list of builders.
+ *
* @param child the child builder to add.
* @return this for chaining.
*/
- public AbstractBuilder add(IHeaderMatcher.Builder child) {
+ public AbstractBuilder addEnclosed(IHeaderMatcher.Builder child) {
children.add(child);
return this;
}
-
+
/**
* Adds a collection of builders to the list of child builders.
+ *
* @param children the children to add.
* @return this for chaining.
*/
- public AbstractBuilder add(Collection children) {
+ public AbstractBuilder addEnclosed(Collection children) {
this.children.addAll(children);
return this;
}
+ public List getEnclosedBuilders() {
+ return Collections.unmodifiableList(children);
+ }
/**
* @return the list of child builders for this builder.
*/
- public List getChildren() {
+ public List getEnclosed() {
return children.stream().map(IHeaderMatcher.Builder::build).collect(Collectors.toList());
}
-
+
@Override
public String toString() {
- StringBuilder sb = new StringBuilder(this.getClass().getSimpleName()).append( ":");
- children.stream().map(Object::toString).forEach( x -> sb.append("\n").append(x));
+ StringBuilder sb = new StringBuilder(this.getClass().getSimpleName()).append(":");
+ children.stream().map(Object::toString).forEach(x -> sb.append(System.lineSeparator()).append(x));
return sb.toString();
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/CopyrightBuilder.java b/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/CopyrightBuilder.java
index f38220ade..e74b270f3 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/CopyrightBuilder.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/CopyrightBuilder.java
@@ -18,7 +18,6 @@
*/
package org.apache.rat.configuration.builders;
-import org.apache.rat.analysis.IHeaderMatcher;
import org.apache.rat.analysis.matchers.CopyrightMatcher;
/**
@@ -60,10 +59,10 @@ public CopyrightBuilder setOwner(String owner) {
}
@Override
- public IHeaderMatcher build() {
+ public CopyrightMatcher build() {
return new CopyrightMatcher(getId(), start, end, owner);
}
-
+
@Override
public String toString() {
return String.format("Copyright Builder: s:%s e:%s o:%s", start, end, owner);
diff --git a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/MatcherRefBuilder.java b/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/MatcherRefBuilder.java
index 17918096a..06624ffb3 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/MatcherRefBuilder.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/MatcherRefBuilder.java
@@ -20,22 +20,32 @@
import java.util.Map;
+import org.apache.rat.ConfigurationException;
import org.apache.rat.analysis.IHeaderMatcher;
+import org.apache.rat.analysis.IHeaders;
+import org.apache.rat.config.parameters.ComponentType;
+import org.apache.rat.config.parameters.ConfigComponent;
/**
* A reference matching Matcher builder.
*
- * This class stores a matcher id as a reference to the matcher. It also has a map of matcher ids to the matcher
- * instances. When build is called the matcher reference is looked up in the map. If it is found then it is returned
- * value from the {@code build()} call. If the reference is not located then a IHeaderMatcherProxy is returned.
- * the IHeaderMatcherProxy is resolved in a later configuration construction phase.
+ * This class stores a matcher id as a reference to the matcher. It also has a
+ * map of matcher ids to the matcher instances. When {@code build()} is called the matcher
+ * reference is looked up in the map. If it is found then its value is returned
+ * from the {@code build()} call. If the reference is not located then a
+ * IHeaderMatcherProxy is returned. the IHeaderMatcherProxy is resolved in a
+ * later configuration construction phase.
*/
public class MatcherRefBuilder extends AbstractBuilder {
private String referenceId;
private Map matchers;
+ /** the reference id attribute */
+ public static final String ATT_REF_ID = "refId";
+
/**
* Constructs the MatcherReferenceBuilder using the provided reference id.
+ *
* @param refId the reverence to the matcher id.
* @return this builder for chaining.
*/
@@ -44,43 +54,74 @@ public MatcherRefBuilder setRefId(String refId) {
return this;
}
+ @Override
+ public Class> builtClass() {
+ return IHeaderMatcherProxy.class;
+ }
+
/**
* Set the Map of matcher ids to matcher instances.
+ *
* @param matchers the Map of ids to instances.
* @return this builder for chaining.
*/
- public MatcherRefBuilder setMatchers(Map matchers) {
+ public MatcherRefBuilder setMatcherMap(Map matchers) {
this.matchers = matchers;
return this;
}
@Override
public IHeaderMatcher build() {
+ if (matchers == null) {
+ throw new ConfigurationException("'matchers' not set");
+ }
IHeaderMatcher result = matchers.get(referenceId);
return result != null ? result : new IHeaderMatcherProxy(referenceId, matchers);
}
@Override
public String toString() {
- return "MathcerRefBuilder: "+referenceId;
+ return "MathcerRefBuilder: " + referenceId;
}
-
+
/**
- * A class that is a proxy to the actual matcher. It retrieves the actual matcher from the map of
- * matcher ids to matcher instances one the first use of the matcher. This allows earlier read matchers
- * to reference later constructed matchers as long as all the matchers are constructed before the earlier one is
- * used.
+ * A class that is a proxy to the actual matcher. It retrieves the actual
+ * matcher from the map of matcher ids to matcher instances on the first use of
+ * the matcher. This allows earlier read matchers to reference later constructed
+ * matchers as long as all the matchers are constructed before the earlier one
+ * is used.
*/
- private class IHeaderMatcherProxy implements IHeaderMatcher {
+ @ConfigComponent(type = ComponentType.MATCHER, name = "matcherRef", desc = "A pointer to another Matcher")
+ public static class IHeaderMatcherProxy implements IHeaderMatcher {
+
+ @ConfigComponent(type = ComponentType.PARAMETER, name = "refId", desc = "Reference to an existing matcher")
private final String proxyId;
private IHeaderMatcher wrapped;
+
+ @ConfigComponent(type = ComponentType.BULID_PARAMETER, name = "matcherMap", desc = "Map of matcher names to matcher instances")
private Map matchers;
- private IHeaderMatcherProxy(String proxyId, Map matchers) {
+ /**
+ * Constuctor. The matchers map should be a reference to an object that will be
+ * updated by later processing of matcher definitions.
+ *
+ * @param proxyId the id of the matcher to find.
+ * @param matchers a mapping of matchers that have been found.
+ */
+ public IHeaderMatcherProxy(String proxyId, Map matchers) {
this.proxyId = proxyId;
this.matchers = matchers;
}
+ /**
+ * Get the reference ID that this proxy is using.
+ *
+ * @return the reference id that points to the actual matcher.
+ */
+ public String getRefId() {
+ return proxyId;
+ }
+
private void checkProxy() {
if (wrapped == null) {
wrapped = matchers.get(proxyId);
@@ -104,21 +145,9 @@ public void reset() {
}
@Override
- public State matches(String line) {
- checkProxy();
- return wrapped.matches(line);
- }
-
- @Override
- public State currentState() {
- checkProxy();
- return wrapped.currentState();
- }
-
- @Override
- public State finalizeState() {
+ public boolean matches(IHeaders header) {
checkProxy();
- return wrapped.finalizeState();
+ return wrapped.matches(header);
}
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/NotBuilder.java b/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/NotBuilder.java
index 61e264b8b..a5d444875 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/NotBuilder.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/NotBuilder.java
@@ -26,17 +26,31 @@
* A builder for the NotMatcher.
*/
public class NotBuilder extends ChildContainerBuilder {
-
+
@Override
- public IHeaderMatcher build() {
- if (children.size() != 1) {
+ public NotMatcher build() {
+ if (children.isEmpty()) {
throw new ConfigurationException("'not' type matcher requires one and only one enclosed matcher");
}
return new NotMatcher(getId(), children.get(0).build());
}
-
+
@Override
public String toString() {
- return String.format( "NotBuilder: %s", !children.isEmpty() ? children.get(0) : null );
+ return String.format("NotBuilder: %s", !children.isEmpty() ? children.get(0) : null);
+ }
+
+ /**
+ * Sets the enclosed matcher. Prior to this call the builder is invalid and the {@code build()} will fail.
+ * @param enclosed The matcher to negate.
+ * @return this.
+ */
+ public NotBuilder setEnclosed(IHeaderMatcher.Builder enclosed) {
+ if (children.isEmpty()) {
+ children.add(enclosed);
+ } else {
+ children.set(0, enclosed);
+ }
+ return this;
}
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/RegexBuilder.java b/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/RegexBuilder.java
index a132ba6c6..df12c2e78 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/RegexBuilder.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/RegexBuilder.java
@@ -21,7 +21,6 @@
import java.util.regex.Pattern;
import org.apache.rat.ConfigurationException;
-import org.apache.rat.analysis.IHeaderMatcher;
import org.apache.rat.analysis.matchers.SimpleRegexMatcher;
/**
@@ -32,9 +31,8 @@ public class RegexBuilder extends AbstractBuilder {
private Pattern pattern;
/**
- * Sets the regex expression.
- * This method compiles the string into a pattern and may throw any exception thrown by the
- * {@code Pattern.compile(String)} method.
+ * Sets the regex expression. This method compiles the string into a pattern and
+ * may throw any exception thrown by the {@code Pattern.compile(String)} method.
* @param exp the expression as a string.
* @return this builder for chaining.
* @see Pattern#compile(String)
@@ -45,16 +43,16 @@ public RegexBuilder setExpr(String exp) {
}
@Override
- public IHeaderMatcher build() {
+ public SimpleRegexMatcher build() {
if (null == pattern) {
throw new ConfigurationException("'regex' type matcher requires an expression");
}
- return new SimpleRegexMatcher(pattern);
+ return new SimpleRegexMatcher(getId(), pattern);
}
-
+
@Override
public String toString() {
- return String.format("RegexBuilder: %s", pattern==null? null: pattern.pattern());
+ return String.format("RegexBuilder: %s", pattern == null ? null : pattern.pattern());
}
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/SpdxBuilder.java b/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/SpdxBuilder.java
index 42530147d..c0ad37207 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/SpdxBuilder.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/SpdxBuilder.java
@@ -20,7 +20,8 @@
import java.util.Objects;
-import org.apache.rat.analysis.IHeaderMatcher;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.rat.ConfigurationException;
import org.apache.rat.analysis.matchers.SPDXMatcherFactory;
/**
@@ -31,23 +32,41 @@ public class SpdxBuilder extends AbstractBuilder {
private String name;
/**
- * sets the name for the SPDX matcher
- * @param name
- * @return
+ * Sets the name for the SPDX matcher. This is the same as the identifier in the SPDX license list.
+ *
+ * @param name The text that follows the colon ':' in the SPDX tag.
+ * @return this builder.
+ * @see SPDX license list
*/
public SpdxBuilder setName(String name) {
- Objects.requireNonNull(name, "name must not be null");
+ Objects.requireNonNull(name, "SPDX name must not be null");
this.name = name;
+ super.setId("SPDX:" + name);
return this;
}
+ /**
+ * Set the id for the matcher.
+ *
+ * @param id the id to use.
+ * @return this builder.
+ */
@Override
- public IHeaderMatcher build() {
+ public AbstractBuilder setId(String id) {
+ if (StringUtils.isNotBlank(id)) {
+ throw new ConfigurationException("'id' is not supported for SPDX matchers. "
+ + "SPXD matchers always have 'SPDX:' as their id");
+ }
+ return this;
+ }
+
+ @Override
+ public SPDXMatcherFactory.Match build() {
return SPDXMatcherFactory.INSTANCE.create(name);
}
-
+
@Override
public String toString() {
- return "SpdxBuilder: "+name;
+ return "SpdxBuilder: " + name;
}
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/TextBuilder.java b/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/TextBuilder.java
index 456afe03b..9b9fe10de 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/TextBuilder.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/configuration/builders/TextBuilder.java
@@ -18,31 +18,33 @@
*/
package org.apache.rat.configuration.builders;
-import java.util.Objects;
-
import org.apache.commons.lang3.StringUtils;
import org.apache.rat.ConfigurationException;
-import org.apache.rat.analysis.IHeaderMatcher;
import org.apache.rat.analysis.matchers.FullTextMatcher;
import org.apache.rat.analysis.matchers.SimpleTextMatcher;
/**
- * Builds text based matcher based on the complexity of the text to match.
+ * Builds text matcher. The specific implementation is based on the complexity of the text to match.
*/
-public class TextBuilder extends AbstractBuilder implements TextCaptureBuilder {
+public class TextBuilder extends AbstractBuilder {
private String text;
- @SuppressWarnings("unchecked")
- @Override
- public TextBuilder setText(String text) {
- Objects.requireNonNull(text, "text may not be null");
- this.text = text;
+ /**
+ * Sets the text to match.
+ * @param text the text to match
+ * @return this builder.
+ */
+ public TextBuilder setSimpleText(String text) {
+ this.text = text.trim();
+ if (StringUtils.isBlank(text)) {
+ throw new ConfigurationException("'text' may not be null");
+ }
return this;
}
@Override
- public IHeaderMatcher build() {
+ public SimpleTextMatcher build() {
if (StringUtils.isBlank(text)) {
throw new ConfigurationException("text value is required");
}
diff --git a/apache-rat-core/src/main/java/org/apache/rat/document/ToNameTransformer.java b/apache-rat-core/src/main/java/org/apache/rat/document/ToNameTransformer.java
index 860e8a914..ccfb359b3 100644
--- a/apache-rat-core/src/main/java/org/apache/rat/document/ToNameTransformer.java
+++ b/apache-rat-core/src/main/java/org/apache/rat/document/ToNameTransformer.java
@@ -20,7 +20,7 @@
import org.apache.commons.collections4.Transformer;
import org.apache.rat.api.Document;
-
+@Deprecated // since 0.17 - Not used in the codebase.
public class ToNameTransformer implements Transformer