blob: 7120bdbabe31746b530d6e155471af793e2fe5ba [file] [log] [blame]
/*
* 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.netbeans.nbbuild;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.FileScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
/** Check source files for a license notice.
* @author Jesse Glick
*/
public class CheckLicense extends Task {
private final List<FileSet> filesets = new ArrayList<>(1);
private String fragment;
private List<Convert> fragments;
private FailType fail;
/** Add a file set of source files to check.
* @param fs set of files to check licenses of
*/
public void addFileSet (FileSet fs) {
filesets.add (fs);
}
/** Set the fragment of license notice which is expected
* to be found in each source file.
* @param f the fragment
*/
public void setFragment (String f) {
fragment = f;
}
public void setFail(FailType t) {
fail = t;
}
public Convert createConvert() {
Convert f = new Convert();
if (fragments == null) {
fragments = new ArrayList<>();
}
fragments.add(f);
return f;
}
public @Override void execute() throws BuildException {
if (fragment == null) {
if (fragments == null) {
throw new BuildException("You must supply a fragment", getLocation());
}
executeReplace();
return;
}
if (filesets.isEmpty()) {
throw new BuildException("You must supply at least one fileset", getLocation());
}
String failMsg = null;
try {
for (FileSet fileset : filesets) {
FileScanner scanner = fileset.getDirectoryScanner(getProject());
File baseDir = scanner.getBasedir ();
String[] files = scanner.getIncludedFiles ();
log ("Looking for " + fragment + " in " + files.length + " files in " + baseDir.getAbsolutePath ());
for (int i = 0; i < files.length; i++) {
File f = new File (baseDir, files[i]);
//log("Scanning " + f, Project.MSG_VERBOSE);
try (BufferedReader br = new BufferedReader (new FileReader (f))) {
String line;
while ((line = br.readLine ()) != null) {
if (line.indexOf (fragment) != -1) {
// Found it.
if (fail != null && "whenpresent".equals(fail.getValue())) {
if (failMsg != null) {
log(failMsg, Project.MSG_ERR);
}
failMsg = "License found in " + f;
}
break;
}
}
if (line == null) {
String msg = f.getAbsolutePath () + ":1: no license notice found";
if (fail != null && "whenmissing".equals(fail.getValue())) {
throw new BuildException(msg);
}
if (fail == null) {
// Scanned whole file without finding it.
log (msg, Project.MSG_ERR);
}
}
}
}
}
if (failMsg != null) {
throw new BuildException(failMsg);
}
} catch (IOException ioe) {
throw new BuildException("Could not open files to check licenses", ioe, getLocation());
}
}
private void executeReplace() throws BuildException {
try {
byte[] workingArray = new byte[1024];
for (FileSet fileset : filesets) {
FileScanner scanner = fileset.getDirectoryScanner(getProject());
File baseDir = scanner.getBasedir ();
String[] files = scanner.getIncludedFiles ();
log ("Replacing code in " + files.length + " files in " + baseDir.getAbsolutePath ());
for (int i = 0; i < files.length; i++) {
File file = new File (baseDir, files[i]);
log("Processing " + file, Project.MSG_VERBOSE);
FileInputStream is = new FileInputStream(file);
int workingLength = is.read(workingArray);
if (workingLength == -1) {
continue;
}
String workingString = new String(workingArray, 0, workingLength);
boolean changed = false;
String prefix = null;
for (Convert f : fragments) {
Matcher matcher = f.orig.matcher(workingString);
while (matcher.find()) {
if (f.prefix) {
if (prefix != null) {
throw new BuildException("Only one convert element can be prefix!");
}
if (matcher.groupCount() != 1) {
throw new BuildException("There should be one group for the prefix element. Was: " + matcher.groupCount());
}
prefix = matcher.group(1);
}
String before = workingString.substring(0, matcher.start());
String after = workingString.substring(matcher.end());
String middle = wrapWithPrefix(f.repl, prefix, before.length() == 0 || before.endsWith("\n"));
if (!middle.equals(matcher.group(0))) {
workingString = before + middle + after;
log("Matched " + middle, Project.MSG_VERBOSE);
changed = true;
} else {
log("Matched, but no change: " + middle, Project.MSG_VERBOSE);
}
if (!f.all) {
break;
} else {
matcher = f.orig.matcher(workingString);
}
}
}
byte[] rest = null;
if (is.available() > 0 && changed) {
rest = new byte[is.available()];
int read = is.read(rest);
assert read == rest.length;
}
is.close();
if (changed) {
log ("Rewriting " + file);
FileOutputStream os = new FileOutputStream(file);
workingString = Pattern.compile(" +$", Pattern.MULTILINE).matcher(workingString+"X").replaceAll("");
os.write(workingString.substring(0, workingString.length() - 1).getBytes());
if (rest != null) {
os.write(rest);
}
os.close();
}
}
}
} catch (IOException ioe) {
throw new BuildException("Could not open files to check licenses", ioe, getLocation());
}
}
private String wrapWithPrefix(String repl, String prefix, boolean startWithPrefix) {
if (prefix == null) {
return repl;
}
String[] all = repl.split("\n");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < all.length; i++) {
if (startWithPrefix) {
sb.append(prefix);
}
sb.append(all[i]);
if (i < all.length - 1) {
sb.append('\n');
}
startWithPrefix = true;
}
return sb.toString();
}
public static final class Convert {
Pattern orig;
String repl;
boolean prefix;
boolean all;
public void setToken(String orig) {
this.orig = Pattern.compile(orig, Pattern.DOTALL | Pattern.MULTILINE);
}
public void setReplace(String repl) {
this.repl = repl.replace("\\n", "\n").replace("\\t", "\t");
}
public void setPrefix(boolean b) {
prefix = b;
}
public void setReplaceAll(boolean b) {
all = b;
}
public Line createLine() {
return new Line();
}
public final class Line {
public void setText(String t) {
if (repl == null) {
repl = t;
} else {
repl = repl + "\n" + t;
}
}
}
}
public static final class FailType extends org.apache.tools.ant.types.EnumeratedAttribute {
public String[] getValues () {
return new String[] {
"whenmissing",
"whenpresent",
};
}
}
}