diff options
Diffstat (limited to 'bundles/org.eclipse.releng.tools/src/org/eclipse/releng/tools/AdvancedCopyrightComment.java')
-rw-r--r-- | bundles/org.eclipse.releng.tools/src/org/eclipse/releng/tools/AdvancedCopyrightComment.java | 574 |
1 files changed, 371 insertions, 203 deletions
diff --git a/bundles/org.eclipse.releng.tools/src/org/eclipse/releng/tools/AdvancedCopyrightComment.java b/bundles/org.eclipse.releng.tools/src/org/eclipse/releng/tools/AdvancedCopyrightComment.java index 2528e465..6b255460 100644 --- a/bundles/org.eclipse.releng.tools/src/org/eclipse/releng/tools/AdvancedCopyrightComment.java +++ b/bundles/org.eclipse.releng.tools/src/org/eclipse/releng/tools/AdvancedCopyrightComment.java @@ -4,236 +4,404 @@ * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html - * + * * Contributors: * IBM Corporation - initial API and implementation - * Martin Oberhuber (Wind River) - [276255] fix insertion of extra space chars + * Martin Oberhuber (Wind River) - [276255] fix insertion of extra space chars + * Leo Ufimtsev lufimtse@redhat.com - [369991] Major re-write to handle multiple years. + added test cases. *******************************************************************************/ package org.eclipse.releng.tools; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; -import java.util.List; import java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.eclipse.releng.tools.preferences.RelEngCopyrightConstants; - import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.text.TextUtilities; +import org.eclipse.releng.tools.preferences.RelEngCopyrightConstants; +/** + * <h2>Handle incomming 'raw' comments and convert them <br> + * into a comment format with access to creation/revision year. <br> + * When retrieving the comment, update the revision year or append one if it's not there.</h2> + * + * <p> + * Tested in {@link org.eclipse.releng.tests.AdvancedCopyrightCommentTestsJunit4}<br> + * Please verify that tests run after modifications. + * </p> + * + */ public class AdvancedCopyrightComment extends CopyrightComment { - private static final String DATE_VAR = "${date}"; //$NON-NLS-1$ - private static final String NEW_LINE = "\n"; //$NON-NLS-1$ + /** A regex mattern to match years in the range {@code 19** to 23** } */ + private static final String YEAR_REGEX = "(19|20|21|22|23)\\d{2}"; //$NON-NLS-1$ + + private static final String DATE_VAR = "${date}"; //$NON-NLS-1$ + private static final String NEW_LINE = "\n"; //$NON-NLS-1$ + + /** Everything before the line with the year(s) on it. */ + private String preYearLinesString = null; + + /** The line with the year(s) on it. */ + private String yearLineString = null; // this is updated when we return a comment. + + /** Everything after the line with the year(s) on it. */ + private String postYearLineString = null; + + /** Number of year units matching {@link #YEAR_REGEX YEAR_REGEX} in the comment. e.g '2000, 2011-2014' has 3 */ + private int yearsCount; + + /** + * Return the body of this copyright comment or null if it cannot be built. + */ + public String getCopyrightComment() { + + if ((preYearLinesString != null || postYearLineString != null)) { + StringBuilder copyrightComment = new StringBuilder(); + + // Pre-append everything before the years + if (preYearLinesString != null) { + copyrightComment.append(preYearLinesString); + } + + // Check if the comment has a revised year. Fix the years on the line if so. + if (hasRevisionYear() && (getRevisionYear() != getCreationYear())) { + + String fixedYearLine; + if (yearsCount == 1) { + // Insert a 2nd year '2000' -> '2000-2010'. + fixedYearLine = insertRevisedYear(yearLineString, getRevisionYear()); + } else { + // update the last found year on line: '2000 ... 2005' -> '2000 ... 2015' + fixedYearLine = updateLastYear(yearLineString, getRevisionYear()); + if (fixedYearLine == null) { + return null; //failed to update last year. + } + } - private String preYearComment = null; - private String postYearComment = null; + copyrightComment.append(fixedYearLine); + } else { + // Otherwise put back the original year line. + copyrightComment.append(yearLineString); + } - private AdvancedCopyrightComment(int commentStyle, int creationYear, int revisionYear, List contributors, String preYearComment, String postYearComment) { - super(commentStyle, creationYear == -1 ? getPreferenceStore().getInt(RelEngCopyrightConstants.CREATION_YEAR_KEY) : creationYear, revisionYear); - this.preYearComment = preYearComment; - this.postYearComment = postYearComment; + // Post append everything after the year line + copyrightComment.append(postYearLineString); + + return copyrightComment.toString(); + } + + String linePrefix = getCommentPrefix(); + if (linePrefix == null) + return null; + + StringWriter out = new StringWriter(); + PrintWriter writer = new PrintWriter(out); + try { + writeCommentStart(writer); + writeLegal(writer, linePrefix); + writeCommentEnd(writer); + return out.toString(); + } finally { + writer.close(); + } } - - private AdvancedCopyrightComment(int commentStyle, int creationYear, int revisionYear, List contributors) { - this(commentStyle, creationYear, revisionYear, contributors, null, null); + + /** + * <h1>Parse a raw comment.</h1> + * <p> + * Create an instance the same as the argument comment but with the revision year <br> + * updated if needed. Return the default comment if the argument comment is null <br> + * or an empty string. + * </p> + * + * @param comment + * the original comment from the file. + * @param commentStyle + * the comment style. {@link CopyrightComment} + * @return {@link AdvancedCopyrightComment} an copyright comment with the year updated. + * + */ + public static AdvancedCopyrightComment parse(BlockComment commentBock, int commentStyle) { + // If the given comment is empty, return the default comment. + if (commentBock == null) { + return defaultComment(commentStyle); + } + + String comment = commentBock.getContents(); + + // identify which line delimiter is used. (for writing back to file ) + String fileLineDelimiter = TextUtilities.determineLineDelimiter(comment, "\n"); //$NON-NLS-1$ + + // Split Comment into Seperate lines for easier proccessing: + String commentLines[] = comment.split("\\r?\\n"); //$NON-NLS-1$ + + // lines before the line with the year comment on it. + StringBuilder preYearLines = new StringBuilder(); + + // line with the year(s) on it. 'copyright 2002, 2010-2011 ... etc.. + String yearLine = null; + + // Lines after the line with the year comment on it. + StringBuilder postYearLines = new StringBuilder(); + + // Break down the comment into the three sections. + boolean yearFound = false; + String line; + for (int i = 0; i < commentLines.length; i++) { + + line = commentLines[i]; // for clarity. + + if (yearFound) { + // We have already found the year line and are just appending the last lines. + + // Conditionally append a newline delimiter. + if (i != (commentLines.length - 1)) { + // normally, append a new line. + postYearLines.append(line + fileLineDelimiter); + } else { + // for the last line, only append if the original comment had a newline delimiter. + Character lastchar = comment.charAt(comment.length() - 1); + if (Character.isWhitespace(lastchar)) { + postYearLines.append(line + lastchar); + } else { + postYearLines.append(line); + } + } + + } else if (line.matches(".*" + YEAR_REGEX + ".*")) { //$NON-NLS-1$ //$NON-NLS-2$ + // We found the line with the copy-right years on it. + yearFound = true; + yearLine = line + fileLineDelimiter; + } else { + // We are parsting the top part of the comment and have not reached the year-line yet. + preYearLines.append(line + fileLineDelimiter); + } + } + + // The comment didn't contain any years that we can update. + if (!yearFound) { + return null; + } + + // Determine first year. + int createdYear = getFirstYear(yearLine); + if (createdYear == 0) { + return null; //Failed to read a year. + } + + + int yearsOnLine = countYearsOnLine(yearLine); + // Determine the last year + int revisedYear; + if (yearsOnLine == 1) { + revisedYear = -1; + } else { + revisedYear = getLastYear(yearLine); + } + + return new AdvancedCopyrightComment(commentStyle, createdYear, revisedYear, yearsOnLine, + preYearLines.toString(), yearLine, postYearLines.toString()); } + /** + * <p> Construct a new default comment for the file.</p> + * + * @param commentStyle As defined in: CopyrightComment + * @return a newly created comment. + */ public static AdvancedCopyrightComment defaultComment(int commentStyle) { - return new AdvancedCopyrightComment(commentStyle, -1, -1, null); + return new AdvancedCopyrightComment(commentStyle, -1, -1, 1, null, null, null); } - - /** - * Get the copyright tool preference store - * @return - */ + + + private AdvancedCopyrightComment(int commentStyle, int creationYear, int revisionYear, + int yearsCount, String preYearComment, String middleYearsComment, String postYearComment) { + super(commentStyle, creationYear == -1 ? getPreferenceStore().getInt( + RelEngCopyrightConstants.CREATION_YEAR_KEY) : creationYear, revisionYear); + this.preYearLinesString = preYearComment; + this.yearLineString = middleYearsComment; + this.postYearLineString = postYearComment; + this.yearsCount = yearsCount; + } + + /** + * Get the copyright tool preference store. + * + * @return preference store used the releng plugin. + */ private static IPreferenceStore getPreferenceStore() { - return RelEngPlugin.getDefault().getPreferenceStore(); + return RelEngPlugin.getDefault().getPreferenceStore(); + } + + /** + * Get the copyright statement in form of an array of Strings where + * each item is a line of the copyright statement. + * + * @return String[] array of lines making up the comment. Containing $date template. + */ + private static String[] getLegalLines() { + StringTokenizer st = new StringTokenizer(getPreferenceStore().getString( + RelEngCopyrightConstants.COPYRIGHT_TEMPLATE_KEY), NEW_LINE, true); + ArrayList<String> lines = new ArrayList<String>(); + String previous = NEW_LINE; + while (st.hasMoreTokens()) { + String current = st.nextToken(); + // add empty lines to array as well + if (NEW_LINE.equals(previous)) { + lines.add(current); + } + previous = current; + } + String[] stringLines = new String[lines.size()]; + stringLines = lines.toArray(stringLines); + return stringLines; } - - /** - * Get the copyright statement in form of an array of Strings where - * each item is a line of the copyright statement. - * @return String[] - */ - private static String[] getLegalLines() { - StringTokenizer st = new StringTokenizer(getPreferenceStore().getString(RelEngCopyrightConstants.COPYRIGHT_TEMPLATE_KEY), NEW_LINE, true); - ArrayList lines = new ArrayList(); - String previous = NEW_LINE; - while (st.hasMoreTokens()) { - String current = st.nextToken(); - // add empty lines to array as well - if (NEW_LINE.equals(previous)) { - lines.add(current); - } - previous = current; - } - String[] stringLines = new String[lines.size()]; - stringLines = (String[])lines.toArray(stringLines); - return stringLines; - } - - /** - * Return the body of this copyright comment or null if it cannot be built. - */ - public String getCopyrightComment() { - // instead of overwriting an existing comment, just try to insert the new year - // disable fix up existing copyright till it works better -// if ((preYearComment != null || postYearComment != null) && (!getPreferenceStore().getBoolean(RelEngCopyrightConstants.FIX_UP_EXISTING_KEY))) { - if ((preYearComment != null || postYearComment != null)) { - String copyrightString = preYearComment == null ? "" : preYearComment; //$NON-NLS-1$ - copyrightString = copyrightString + getCreationYear(); - - if (hasRevisionYear() && getRevisionYear() != getCreationYear()) - copyrightString = copyrightString + ", " + getRevisionYear(); //$NON-NLS-1$ - - String endString = postYearComment == null ? "" : postYearComment; //$NON-NLS-1$ - copyrightString = copyrightString + endString; - return copyrightString; - } - - String linePrefix = getCommentPrefix(); - if (linePrefix == null) - return null; - - StringWriter out = new StringWriter(); - PrintWriter writer = new PrintWriter(out); - try { - writeCommentStart(writer); - writeLegal(writer, linePrefix); - // dont do anything special with contributors right now -// writeContributions(writer, linePrefix); - writeCommentEnd(writer); - - return out.toString(); - } finally { - writer.close(); - } - } - - /** - * Write out the copyright statement, line by line, adding in the created/revision - * year as well as comment line prefixes. - * - * @param writer - * @param linePrefix - */ - private void writeLegal(PrintWriter writer, String linePrefix) { - String[] legalLines = getLegalLines(); - for (int i=0; i < legalLines.length; ++i) { - String currentLine = legalLines[i]; - int offset = currentLine.indexOf(DATE_VAR); - // if this is the line, containing the ${date}, add in the year - if (offset > -1) { - writer.print(linePrefix + ' ' + currentLine.substring(0, offset) + getCreationYear()); - if (hasRevisionYear() && getRevisionYear() != getCreationYear()) { - writer.print(", " + getRevisionYear()); //$NON-NLS-1$ - } - println(writer, currentLine.substring(offset+DATE_VAR.length(), currentLine.length())); - } else { - // just write out the line - if (NEW_LINE.equals(currentLine)) { - // handle empty lines - println(writer, linePrefix); - } else { - println(writer, linePrefix + ' ' + currentLine); - } - } - } - } - + + /** + * Write out the copyright statement, line by line, adding in the created/revision + * year as well as comment line prefixes. + * + * @param writer + * @param linePrefix + */ + private void writeLegal(PrintWriter writer, String linePrefix) { + String[] legalLines = getLegalLines(); + for (int i = 0; i < legalLines.length; ++i) { + String currentLine = legalLines[i]; + int offset = currentLine.indexOf(DATE_VAR); + // if this is the line, containing the ${date}, add in the year + if (offset > -1) { + writer.print(linePrefix + ' ' + currentLine.substring(0, offset) + + getCreationYear()); + if (hasRevisionYear() && getRevisionYear() != getCreationYear()) { + writer.print(", " + getRevisionYear()); //$NON-NLS-1$ + } + println(writer, + currentLine.substring(offset + DATE_VAR.length(), currentLine.length())); + } else { + // just write out the line + if (NEW_LINE.equals(currentLine)) { + // handle empty lines + println(writer, linePrefix); + } else { + println(writer, linePrefix + ' ' + currentLine); + } + } + } + } + /** - * Create an instance the same as the argument comment but with the revision year - * updated if needed. Return the default comment if the argument comment is null - * or an empty string. Return null if the argument comment is not recognized as - * an IBM copyright comment. + * Replace the last year in the provided string that matches {@link #YEAR_REGEX}. + * + * @param line + * the line that contains the year that you want to update. + * @param newYear + * the new year that you want to update to. + * @return the string with the last year updated or null if it fails to find a year. */ - public static AdvancedCopyrightComment parse(BlockComment comment, int commentStyle) { - AdvancedCopyrightComment copyright = null; - - if (comment == null) { - copyright = defaultComment(commentStyle); - } else { - // To make the comment search a little more flexible, the parse algorithm will - // only parse the line containing the ${date} - String body = comment.getContents(); - - // find the line with ${date} - String[] legalLines = getLegalLines(); - int i = 0; - int yearOffset = -1; - while (i < legalLines.length && yearOffset == -1) { - String line = legalLines[i]; - yearOffset = line.indexOf(DATE_VAR); - ++i; - } - // ${date} found - if (yearOffset != -1) { - String yearLine = legalLines[i-1]; - // split that line up and just search for the contents before and after - // NOTE: this won't really work well if the text surrounding the year is - // generic, or if the year is at the beginning or end of the line - String preYear = yearLine.substring(0, yearOffset); - - int preYearOffset = body.toLowerCase().indexOf(preYear.toLowerCase()); - if (preYearOffset != -1) { - int preYearEnd= preYearOffset + preYear.length(); - - // match "2000", "2000,2001", "2000-2001", all with arbitrary whitespace - Pattern yearsPattern= Pattern.compile("\\s*(\\d+)(?:\\s*[,-]\\s*(\\d+))?"); //$NON-NLS-1$ - - Matcher yearsMatcher= yearsPattern.matcher(body.substring(preYearEnd)); - if (yearsMatcher.find()) { - int startYear = -1; - try { - startYear = Integer.parseInt(yearsMatcher.group(1)); - } catch(NumberFormatException e) { - // do nothing - } - - int endYear = -1; - String endYearString= yearsMatcher.group(2); - if (endYearString != null) { - try { - endYear = Integer.parseInt(endYearString); - } catch(NumberFormatException e) { - // do nothing - } - } - // save the copyright comment's contents before and after the year(s) so that - // the comment will remain untouched rather than overwritten if the template is - // almost the same - String pre = body.substring(0, preYearEnd); - String post = body.substring(preYearEnd + yearsMatcher.group().length()); - - copyright = new AdvancedCopyrightComment(commentStyle, startYear, endYear, null, pre, post); - } - } - } - } - - // don't do anything special with contributors right now -// int contrib = body.indexOf("Contributors:", start); //$NON-NLS-1$ -// String contribComment = body.substring(contrib); -// StringTokenizer tokens = new StringTokenizer(contribComment, "\r\n"); //$NON-NLS-1$ -// tokens.nextToken(); -// ArrayList contributors = new ArrayList(); -// String linePrefix = getLinePrefix(commentStyle); -// while(tokens.hasMoreTokens()) { -// String contributor = tokens.nextToken(); -// if (contributor.indexOf("***********************************") == -1 //$NON-NLS-1$ -// && contributor.indexOf("###################################") == -1) { //$NON-NLS-1$ -// int c = contributor.indexOf(linePrefix); -// if (c != -1) -// contributor = contributor.substring(c + linePrefix.length()); -// contributors.add(contributor.trim()); -// } -// } -// -// return new IBMCopyrightComment(commentStyle, startYear, endYear, contributors); - return copyright; + private static String updateLastYear(String line, int newYear) { + + Matcher matcher = Pattern.compile(YEAR_REGEX).matcher(line); + + // Find position of last year in the string. + int lastStart = -1; + while (matcher.find()) { + lastStart = matcher.start(); + } + + // Failed to find a year. Return the original line. + if (lastStart == -1) { + return null; + } + + // Insert new year + String before = line.substring(0, lastStart); + String after = line.substring(lastStart + 4); + String updatedLine = before + Integer.toString(newYear) + after; + + return updatedLine; } + /** + * In the situation that a line only has a single year 'Copyright 2000 IBM ... '. <br> + * append the revision year to make it like: 'Copyright 2000-2010 IBM ... '. <br> + * + * <p> + * This should <b>only</b> be used for lines that have a single year. + * </p> + * + * @param line + * @param year + * @return + */ + private static String insertRevisedYear(String line, int year) { + Matcher matcher = Pattern.compile(YEAR_REGEX).matcher(line); + + if (!matcher.find()) + return line; // no year found. Return original. + + // Insert new year. + String before = line.substring(0, matcher.end()); + String after = line.substring(matcher.end()); + String updatedLine = before + ", " + Integer.toString(year) + after; //$NON-NLS-1$ + + return updatedLine; + } + + /** + * Given a line with one or multiple years on it, count how many years occur on it. + * + * @param line + * @return + */ + private static int countYearsOnLine(String line) { + Matcher yearMatcher = Pattern.compile(YEAR_REGEX).matcher(line); + int count = 0; + while (yearMatcher.find()) { + count++; + } + return count; + } + + /** + * <h1>Get first year in line. </h1> + * <p> For example given a line like '2000, 2012, 2011, IMB..' it would return 2000. <br> + * + * Pre-condition: The line must contain a valid year in the range YEAR_REGEX + * + * @see #YEAR_REGEX + * @param line Line containing a year. + * @return the first found year. 0 if none found. (caller should check). + */ + private static int getFirstYear(String line) { + Matcher yearMatcher = Pattern.compile(YEAR_REGEX).matcher(line); + if (yearMatcher.find()) { + return Integer.parseInt(yearMatcher.group()); // exception never thrown since match only matches integer. + } + return 0; //No year was found on this line. + } + + /** + * <h2>Get the last year in a line. '2000, 2012, 2011, IMB..'</h2> Pre-condition: The line must contain a valid year + * in the range YEAR_REGEX + * + * @see #YEAR_REGEX + * @param line + * @return e.g 2011 + */ + private static int getLastYear(String line) { + Matcher yearMatcher = Pattern.compile(YEAR_REGEX).matcher(line); + + int lastYear = -1; + // loop till the last occurance. + while (yearMatcher.find()) { + lastYear = Integer.parseInt(yearMatcher.group()); // exception never thrown since match only matches + // integer. + } + return lastYear; + } } |