| | |
| | | */ |
| | | package org.forgerock.maven; |
| | | |
| | | import static java.util.regex.Pattern.*; |
| | | |
| | | import static org.apache.maven.plugins.annotations.LifecyclePhase.*; |
| | | |
| | | import java.io.BufferedReader; |
| | |
| | | import java.io.IOException; |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | | import java.util.regex.Matcher; |
| | | import java.util.regex.Pattern; |
| | | |
| | | import org.apache.maven.plugin.MojoExecutionException; |
| | | import org.apache.maven.plugin.MojoFailureException; |
| | |
| | | * Copyright sections must respect the following format: |
| | | * <pre> |
| | | * (.)* //This line references 0..N lines. |
| | | * [COMMMENT_CHAR][lineBeforeCopyrightToken] |
| | | * [COMMMENT_CHAR]* //This line references 0..N commented emptylines. |
| | | * [COMMMENT_CHAR][lineBeforeCopyrightRegExp] |
| | | * [COMMMENT_CHAR]* //This line references 0..N commented empty lines. |
| | | * ([COMMMENT_CHAR][oldCopyrightToken])* |
| | | * ([COMMMENT_CHAR][indent][copyrightStartToken | portionsCopyrightStartToken] [YEAR] [copyrightEndToken])? |
| | | * ([COMMMENT_CHAR] [YEAR] [copyrightEndToken])? |
| | | * </pre> |
| | | * <p> |
| | | * Formatter details: |
| | | * <ul> |
| | | * <li>COMMENT_CHAR: Auto-detected by plugin. |
| | | * Comment character used in comment blocks ('*' for Java, '!' for xml...)</li> |
| | | * <li>lineBeforeCopyrightToken: Parameter |
| | | * |
| | | * <li>lineBeforeCopyrightRegExp: Parameter regExp case insensitive |
| | | * Used by the plugin to start it's inspection for the copyright line. |
| | | * Next non blank commented lines after this lines must be |
| | | * old copyright owner lines or/and old copyright lines.</li> |
| | | * old copyright owner lines or/and old ForgeRock copyright lines.</li> |
| | | * |
| | | * <li>oldCopyrightToken: Detected by plugin ('copyright' keyword non case-sensitive and non copyrightEndToken) |
| | | * <li>oldCopyrightToken: Detected by plugin ('copyright' keyword case insensitive) |
| | | * If one line contains this token, the plugin will use |
| | | * the portionsCopyrightStartToken instead of copyrightStartToken</li> |
| | | * the newPortionsCopyrightLabel instead of the newCopyrightLabel |
| | | * if there is no ForgeRock copyrighted line.</li> |
| | | * |
| | | * <li>nbLinesToSkip: Parameter (int) |
| | | * Used only if a new copyright line is needed (not if a new portion copyright section is needed). |
| | | * It gives the number of lines to add after the line which contains the lineBeforeCopyrightToken.</li> |
| | | * |
| | | * <li>indent: Parameter 'numberSpaceIdentation' (int) |
| | | * Used only if a new copyright or portion copyright line is needed. |
| | | * It gives the number of space to add after the COMMENT_CHAR. |
| | | * If there is already a copyright line, the existing indentation will be used.</li> |
| | | * |
| | | * <li>copyrightStartToken: Parameter |
| | | * Used to recognize the copyright line. If the copyright section is |
| | | * missing, the plugin will add the line.</li> |
| | | * |
| | | * <li>portionsCopyrightStartToken: Parameter |
| | | * Same as copyrightStartToken, but if the oldCopyrightToken is present.</li> |
| | | * |
| | | * <li>copyrightEndToken: Parameter |
| | | * Same as copyrightStartToken, but for the end of the line.</li> |
| | | * <li>forgerockCopyrightRegExp: Parameter regExp case insensitive |
| | | * The regular expression which identifies a copyrighted line as a ForgeRock one.</li> |
| | | * |
| | | * <li>YEAR: Computed by plugin |
| | | * Current year if there is no existing copyright line. |
| | |
| | | * <li>VERY_OLD_YEAR-OLD_YEAR => VERY_OLD_YEAR-CURRENT_YEAR</li> |
| | | * </ul></li> |
| | | * </ul> |
| | | * </p> |
| | | * <p> |
| | | * If no ForgeRock copyrighted line is detected, the plugin will add according to the following format |
| | | * <ul> |
| | | * <li> If there is one or more old copyright lines: |
| | | * <pre> |
| | | * [COMMMENT_CHAR][lineBeforeCopyrightRegExp] |
| | | * [COMMMENT_CHAR]* //This line references 0..N commented empty lines. |
| | | * ([COMMMENT_CHAR][oldCopyrightToken])* |
| | | * [indent][newPortionsCopyrightLabel] [YEAR] [forgerockCopyrightLabel] |
| | | * </pre></li><br> |
| | | * <li> If there is no old copyright lines: |
| | | * <pre> |
| | | * [COMMMENT_CHAR][lineBeforeCopyrightRegExp] |
| | | * [COMMMENT_CHAR]*{nbLinesToSkip} //This line nbLinesToSkip commented empty lines. |
| | | * [indent][newCopyrightLabel] [YEAR] [forgerockCopyrightLabel] |
| | | * </pre></li> |
| | | * </ul> |
| | | * |
| | | */ |
| | | @Mojo(name = "update-copyright", defaultPhase = VALIDATE) |
| | | public class UpdateCopyrightMojo extends CopyrightAbstractMojo { |
| | | |
| | | private final class UpdateCopyrightFile { |
| | | private String filePath; |
| | | private final String filePath; |
| | | private final List<String> bufferedLines = new LinkedList<String>(); |
| | | private boolean copyrightUpdated; |
| | | private boolean lineBeforeCopyrightReaded; |
| | |
| | | private String curLowerLine; |
| | | private Integer startYear; |
| | | private Integer endYear; |
| | | private BufferedReader reader; |
| | | private BufferedWriter writer; |
| | | private final BufferedReader reader; |
| | | private final BufferedWriter writer; |
| | | |
| | | private UpdateCopyrightFile(String filePath) throws IOException { |
| | | this.filePath = filePath; |
| | | reader = new BufferedReader(new FileReader(filePath)); |
| | | File tmpFile = new File(filePath + ".tmp"); |
| | | final File tmpFile = new File(filePath + ".tmp"); |
| | | if (!tmpFile.exists()) { |
| | | tmpFile.createNewFile(); |
| | | } |
| | |
| | | copyrightSectionPresent = readCopyrightLine(); |
| | | writeCopyrightLine(); |
| | | writeChanges(); |
| | | } catch (Exception e) { |
| | | } catch (final Exception e) { |
| | | throw new MojoExecutionException(e.getMessage(), e); |
| | | } finally { |
| | | Utils.closeSilently(reader, writer); |
| | |
| | | } |
| | | reader.close(); |
| | | |
| | | for (String line : bufferedLines) { |
| | | for (final String line : bufferedLines) { |
| | | writer.write(line); |
| | | writer.newLine(); |
| | | } |
| | | writer.close(); |
| | | |
| | | if (!dryRun) { |
| | | File updatedFile = new File(filePath); |
| | | final File updatedFile = new File(filePath); |
| | | if (!updatedFile.delete()) { |
| | | throw new Exception("impossible to perform rename on the file."); |
| | | } |
| | |
| | | } |
| | | |
| | | int indexAdd = bufferedLines.size() - 1; |
| | | String stopToken = portionsCopyrightNeeded ? OLD_COPYRIGHT_TOKEN : lineBeforeCopyrightToken; |
| | | final Pattern stopRegExp = portionsCopyrightNeeded ? OLD_COPYRIGHT_REGEXP |
| | | : lineBeforeCopyrightCompiledRegExp; |
| | | String previousLine = curLine; |
| | | while (!previousLine.toLowerCase().contains(stopToken.toLowerCase())) { |
| | | while (!lineMatches(previousLine, stopRegExp)) { |
| | | indexAdd--; |
| | | previousLine = bufferedLines.get(indexAdd); |
| | | } |
| | |
| | | } |
| | | } |
| | | final String newCopyrightLine = getNewCommentedLine() |
| | | + indent() + (portionsCopyrightNeeded ? portionsCopyrightStartToken : copyrightStartToken) |
| | | + " " + currentYear + " " + copyrightEndToken; |
| | | + indent() + (portionsCopyrightNeeded ? newPortionsCopyrightLabel : newCopyrightLabel) |
| | | + " " + currentYear + " " + forgeRockCopyrightLabel; |
| | | bufferedLines.add(indexAdd, newCopyrightLine); |
| | | copyrightUpdated = true; |
| | | } |
| | | |
| | | private void updateExistingCopyrightLine() throws Exception { |
| | | if (portionsCopyrightNeeded && copyrightSectionPresent |
| | | // Is it a new copyright line? |
| | | && curLine.contains(copyrightStartToken) && !curLine.contains(portionsCopyrightStartToken)) { |
| | | getLog().warn("File " + filePath + " contains old copyright line and coyright line. " |
| | | + "The copyright line will be replaced by a portions copyright line."); |
| | | curLine.replace(copyrightStartToken, portionsCopyrightStartToken); |
| | | } |
| | | readYearSection(); |
| | | final String newCopyrightLine; |
| | | if (endYear == null) { |
| | | //OLD_YEAR => OLD_YEAR-CURRENT_YEAR |
| | | // OLD_YEAR => OLD_YEAR-CURRENT_YEAR |
| | | newCopyrightLine = curLine.replace(startYear.toString(), intervalToString(startYear, currentYear)); |
| | | } else { |
| | | //VERY_OLD_YEAR-OLD_YEAR => VERY_OLD_YEAR-CURRENT_YEAR |
| | | // VERY_OLD_YEAR-OLD_YEAR => VERY_OLD_YEAR-CURRENT_YEAR |
| | | newCopyrightLine = curLine.replace(intervalToString(startYear, endYear), |
| | | intervalToString(startYear, currentYear)); |
| | | } |
| | |
| | | } |
| | | |
| | | private void readYearSection() throws Exception { |
| | | try { |
| | | String startToken = portionsCopyrightNeeded ? portionsCopyrightStartToken : copyrightStartToken; |
| | | String yearSection = curLine.substring(curLine.indexOf(startToken) + startToken.length(), |
| | | curLine.indexOf(copyrightEndToken)).trim(); |
| | | if (yearSection.contains("-")) { |
| | | startYear = Integer.parseInt(yearSection.split("-")[0].trim()); |
| | | endYear = Integer.parseInt(yearSection.split("-")[1].trim()); |
| | | } else { |
| | | startYear = Integer.parseInt(yearSection); |
| | | final String copyrightLineRegExp = ".*\\s+(\\d{4})(-(\\d{4}))?\\s+" + forgerockCopyrightRegExp + ".*"; |
| | | final Matcher copyrightMatcher = Pattern.compile(copyrightLineRegExp, CASE_INSENSITIVE).matcher(curLine); |
| | | if (copyrightMatcher.matches()) { |
| | | startYear = Integer.parseInt(copyrightMatcher.group(1)); |
| | | final String endYearString = copyrightMatcher.group(3); |
| | | if (endYearString != null) { |
| | | endYear = Integer.parseInt(endYearString); |
| | | } |
| | | } catch (NumberFormatException nfe) { |
| | | } else { |
| | | throw new Exception("Malformed year section in copyright line " + curLine); |
| | | } catch (Exception e) { |
| | | throw new Exception("Malformed copyright line " + curLine); |
| | | } |
| | | } |
| | | |
| | | private void readLineBeforeCopyrightToken() throws Exception { |
| | | nextLine(); |
| | | while (curLine != null) { |
| | | if (curLine.contains(lineBeforeCopyrightToken)) { |
| | | if (curLineMatches(lineBeforeCopyrightCompiledRegExp)) { |
| | | if (!isCommentLine(curLowerLine)) { |
| | | throw new Exception("The line before copyright token must be a commented line"); |
| | | } |
| | |
| | | } |
| | | |
| | | private boolean isOldCopyrightOwnerLine() { |
| | | return curLowerLine.contains(OLD_COPYRIGHT_TOKEN) && !curLine.contains(copyrightEndToken); |
| | | return curLineMatches(OLD_COPYRIGHT_REGEXP) && !curLineMatches(copyrightOwnerCompiledRegExp); |
| | | } |
| | | |
| | | private boolean isCopyrightLine() { |
| | | return (curLine.contains(copyrightStartToken) || curLine.contains(portionsCopyrightStartToken)) |
| | | && curLine.contains(copyrightEndToken); |
| | | return curLineMatches(copyrightOwnerCompiledRegExp); |
| | | } |
| | | |
| | | private boolean curLineMatches(Pattern compiledRegExp) { |
| | | return lineMatches(curLine, compiledRegExp); |
| | | } |
| | | |
| | | private boolean lineMatches(String line, Pattern compiledRegExp) { |
| | | return compiledRegExp.matcher(line).matches(); |
| | | } |
| | | |
| | | private void nextLine() throws Exception { |
| | |
| | | |
| | | } |
| | | |
| | | private static final String OLD_COPYRIGHT_TOKEN = "copyright"; |
| | | |
| | | /** The last non empty commented line before the copyright section. */ |
| | | @Parameter(required = true, defaultValue = "CDDL HEADER END") |
| | | private String lineBeforeCopyrightToken; |
| | | private static final Pattern OLD_COPYRIGHT_REGEXP = Pattern.compile(".*copyright.*", CASE_INSENSITIVE); |
| | | |
| | | /** |
| | | * Number of lines to add after the line which contains the lineBeforeCopyrightToken. |
| | |
| | | @Parameter(required = true, defaultValue = "6") |
| | | private Integer numberSpaceIdentation; |
| | | |
| | | /** Copyright start line token. */ |
| | | /** The last non empty commented line before the copyright section. */ |
| | | @Parameter(required = true, defaultValue = "CDDL\\s+HEADER\\s+END") |
| | | private String lineBeforeCopyrightRegExp; |
| | | |
| | | /** The regular expression which identifies a copyrighted line. */ |
| | | @Parameter(required = true, defaultValue = "ForgeRock\\s+AS") |
| | | private String forgerockCopyrightRegExp; |
| | | |
| | | /** Line to add if there is no existing copyright. */ |
| | | @Parameter(required = true, defaultValue = "Copyright") |
| | | private String copyrightStartToken; |
| | | private String newCopyrightLabel; |
| | | |
| | | @Parameter(required = true, defaultValue = "Portions Copyright") |
| | | /** Portions copyright start line token. */ |
| | | private String portionsCopyrightStartToken; |
| | | @Parameter(required = true, defaultValue = "Portions Copyright") |
| | | private String newPortionsCopyrightLabel; |
| | | |
| | | /** Copyright end line token. */ |
| | | @Parameter(required = true, defaultValue = "ForgeRock AS") |
| | | private String copyrightEndToken; |
| | | /** ForgeRock copyright label to print if a new (portions) copyright line is needed. */ |
| | | @Parameter(required = true, defaultValue = "ForgeRock AS.") |
| | | private String forgeRockCopyrightLabel; |
| | | |
| | | /** A dry run will not change source code. It creates new files with '.tmp' extension */ |
| | | /** A dry run will not change source code. It creates new files with '.tmp' extension. */ |
| | | @Parameter(required = true, defaultValue = "false") |
| | | private boolean dryRun; |
| | | |
| | | /** RegExps corresponding to user token. */ |
| | | private Pattern lineBeforeCopyrightCompiledRegExp; |
| | | private Pattern copyrightOwnerCompiledRegExp; |
| | | |
| | | private boolean buildOK = true; |
| | | |
| | | |
| | | /** |
| | | * Updates copyright of modified files. |
| | | * |
| | |
| | | */ |
| | | @Override |
| | | public void execute() throws MojoExecutionException, MojoFailureException { |
| | | compileRegExps(); |
| | | checkCopyrights(); |
| | | for (String filePath : getIncorrectCopyrightFilePaths()) { |
| | | for (final String filePath : getIncorrectCopyrightFilePaths()) { |
| | | try { |
| | | new UpdateCopyrightFile(filePath).updateCopyrightForFile(); |
| | | getLog().info("Copyright of file " + filePath + " has been successfully updated."); |
| | | } catch (Exception e) { |
| | | } catch (final Exception e) { |
| | | getLog().error("Impossible to update copyright of file " + filePath); |
| | | getLog().error(" Details: " + e.getMessage()); |
| | | getLog().error(" No modification has been performed on this file"); |
| | |
| | | } |
| | | } |
| | | |
| | | private void compileRegExps() { |
| | | lineBeforeCopyrightCompiledRegExp = compileRegExp(lineBeforeCopyrightRegExp); |
| | | copyrightOwnerCompiledRegExp = compileRegExp(forgerockCopyrightRegExp); |
| | | } |
| | | |
| | | private Pattern compileRegExp(String regExp) { |
| | | return Pattern.compile(".*" + regExp + ".*", CASE_INSENSITIVE); |
| | | } |
| | | |
| | | private String intervalToString(Integer startYear, Integer endYear) { |
| | | return startYear + "-" + endYear; |
| | | } |
| | |
| | | // Setters to allow tests |
| | | |
| | | void setLineBeforeCopyrightToken(String lineBeforeCopyrightToken) { |
| | | this.lineBeforeCopyrightToken = lineBeforeCopyrightToken; |
| | | this.lineBeforeCopyrightRegExp = lineBeforeCopyrightToken; |
| | | } |
| | | |
| | | void setNbLinesToSkip(Integer nbLinesToSkip) { |
| | |
| | | this.numberSpaceIdentation = numberSpaceIdentation; |
| | | } |
| | | |
| | | void setPortionsCopyrightStartToken(String portionsCopyrightStartToken) { |
| | | this.portionsCopyrightStartToken = portionsCopyrightStartToken; |
| | | void setNewPortionsCopyrightString(String portionsCopyrightString) { |
| | | this.newPortionsCopyrightLabel = portionsCopyrightString; |
| | | } |
| | | |
| | | void setCopyrightStartToken(String copyrightStartToken) { |
| | | this.copyrightStartToken = copyrightStartToken; |
| | | void setNewCopyrightOwnerString(String newCopyrightOwnerString) { |
| | | this.forgeRockCopyrightLabel = newCopyrightOwnerString; |
| | | } |
| | | |
| | | void setNewCopyrightStartToken(String copyrightStartString) { |
| | | this.newCopyrightLabel = copyrightStartString; |
| | | } |
| | | |
| | | void setCopyrightEndToken(String copyrightEndToken) { |
| | | this.copyrightEndToken = copyrightEndToken; |
| | | this.forgerockCopyrightRegExp = copyrightEndToken; |
| | | } |
| | | |
| | | void setDryRun(final boolean dryRun) { |