1344N/A/*
1344N/A * CDDL HEADER START
1344N/A *
1344N/A * The contents of this file are subject to the terms of the
1344N/A * Common Development and Distribution License, Version 1.0 only
1344N/A * (the "License"). You may not use this file except in compliance
1344N/A * with the License.
1344N/A *
1344N/A * You can obtain a copy of the license at
1344N/A * trunk/opends/resource/legal-notices/OpenDS.LICENSE
1344N/A * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
1344N/A * See the License for the specific language governing permissions
1344N/A * and limitations under the License.
1344N/A *
1344N/A * When distributing Covered Code, include this CDDL HEADER in each
1344N/A * file and include the License file at
1344N/A * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
1344N/A * add the following below this CDDL HEADER, with the fields enclosed
1344N/A * by brackets "[]" replaced with your own identifying information:
1344N/A * Portions Copyright [yyyy] [name of copyright owner]
1344N/A *
1344N/A * CDDL HEADER END
1344N/A *
1344N/A *
3231N/A * Copyright 2008 Sun Microsystems, Inc.
5902N/A * Portions copyright 2012 ForgeRock AS.
1344N/A */
1344N/Apackage org.opends.build.tools;
1344N/A
1344N/A
1344N/A
1344N/Aimport java.io.BufferedReader;
1344N/Aimport java.io.File;
1344N/Aimport java.io.FileReader;
1344N/Aimport java.io.IOException;
1344N/Aimport java.util.GregorianCalendar;
1344N/Aimport java.util.HashSet;
1344N/Aimport java.util.LinkedList;
1344N/A
1344N/Aimport org.apache.tools.ant.BuildException;
1344N/Aimport org.apache.tools.ant.Task;
3852N/Aimport org.tmatesoft.svn.core.SVNDepth;
1344N/Aimport org.tmatesoft.svn.core.SVNException;
1344N/Aimport org.tmatesoft.svn.core.wc.SVNPropertyData;
1344N/Aimport org.tmatesoft.svn.core.wc.SVNRevision;
1344N/Aimport org.tmatesoft.svn.core.wc.ISVNStatusHandler;
3852N/Aimport org.tmatesoft.svn.core.wc.SVNClientManager;
1344N/Aimport org.tmatesoft.svn.core.wc.SVNStatus;
1344N/Aimport org.tmatesoft.svn.core.wc.SVNWCClient;
1344N/A
1344N/A
1344N/A
1344N/A/**
1344N/A * This class provides an implementation of an Ant task that may be used to
1344N/A * perform various checks to deteermine whether a file is suitable to be
1344N/A * committed. This includes:
1344N/A * <UL>
1344N/A * <LI>Make sure that the file has the correct "svn:eol-style" property
1344N/A * value.</LI>
1344N/A * <LI>If a file contains a line that appears to be a comment and includes the
1344N/A * word "copyright", then it should contain the current year.</LI>
1344N/A * </UL>
1344N/A */
1344N/Apublic class CheckPrecommit
1344N/A extends Task
1344N/A implements ISVNStatusHandler
1344N/A{
1344N/A /**
1344N/A * The name of the system property that may be used to prevent copyright date
1344N/A * problems from failing the build.
1344N/A */
1344N/A public static final String IGNORE_COPYRIGHT_ERRORS_PROPERTY =
1344N/A "org.opends.server.IgnoreCopyrightDateErrors";
1344N/A
1344N/A
1344N/A
1344N/A /**
1520N/A * The name of the system property that may be used to prevent svn eol-style
1344N/A * problems from failing the build.
1344N/A */
1344N/A public static final String IGNORE_EOLSTYLE_ERRORS_PROPERTY =
1344N/A "org.opends.server.IgnoreEOLStyleErrors";
1344N/A
1344N/A
1344N/A
1344N/A /**
1344N/A *
1344N/A */
1344N/A public static final HashSet<String> CHECKED_EXTENSIONS =
1344N/A new HashSet<String>();
1344N/A static
1344N/A {
1344N/A CHECKED_EXTENSIONS.add("java");
1344N/A CHECKED_EXTENSIONS.add("xml");
1344N/A CHECKED_EXTENSIONS.add("xsd");
1344N/A CHECKED_EXTENSIONS.add("xsl");
1344N/A CHECKED_EXTENSIONS.add("html");
1344N/A CHECKED_EXTENSIONS.add("sh");
1344N/A CHECKED_EXTENSIONS.add("bat");
1344N/A CHECKED_EXTENSIONS.add("ldif");
1344N/A CHECKED_EXTENSIONS.add("txt");
1344N/A CHECKED_EXTENSIONS.add("c");
1344N/A CHECKED_EXTENSIONS.add("h");
1344N/A CHECKED_EXTENSIONS.add("mc");
1344N/A CHECKED_EXTENSIONS.add("Makefile");
1344N/A }
1344N/A
1344N/A
1344N/A
1344N/A // The path to the directory that is the base of the workspace.
1344N/A private File workspacePath;
1344N/A
1344N/A // The set of files that appear to have problems with the EOL style.
1344N/A private LinkedList<String> eolStyleProblemFiles = new LinkedList<String>();
1344N/A
1344N/A // The set of files that appear to have problems with the copyright date.
1344N/A private LinkedList<String> copyrightProblemFiles = new LinkedList<String>();
1344N/A
1344N/A // The path to the root of the Subversion workspace to check.
1344N/A private String workspace = null;
1344N/A
1344N/A // The string representation of the current year.
1344N/A private String yearString;
1344N/A
3852N/A // The overall SVN Client Manager. required with svnkit 1.2.x
3852N/A private static SVNClientManager ourClientManager =
3852N/A SVNClientManager.newInstance();
1344N/A // The property client used to look at file properties.
1344N/A private SVNWCClient propertyClient;
1344N/A
1344N/A
1344N/A
1344N/A /**
1344N/A * Specifies the path to the root of the Subversion workspace for which to
1344N/A * retrieve the revision number.
1344N/A *
1344N/A * @param workspace The path to the root of the Subversion workspace for
1344N/A * which to retrieve the revision number.
1344N/A */
1344N/A public void setWorkspace(String workspace)
1344N/A {
1344N/A this.workspace = workspace;
1344N/A }
1344N/A
1344N/A
1344N/A
1344N/A /**
1344N/A * Performs the appropriate processing needed for this task. In this case,
1344N/A * it uses SVNKit to identify all modified files in the current workspace.
1344N/A * For all source files, look for comment lines containing the word
1344N/A * "copyright" and make sure at least one of them contains the current year.
1344N/A */
1344N/A @Override()
1344N/A public void execute()
1344N/A {
1344N/A if ((workspace == null) || (workspace.length() == 0))
1344N/A {
1344N/A workspacePath = getProject().getBaseDir();
1344N/A }
1344N/A else
1344N/A {
1344N/A workspacePath = new File(workspace);
1344N/A }
1344N/A
1344N/A
1344N/A // Get the year to use in the determination.
1344N/A GregorianCalendar calendar = new GregorianCalendar();
1344N/A int year = calendar.get(GregorianCalendar.YEAR);
1344N/A yearString = String.valueOf(year);
1344N/A
1344N/A
1344N/A // Process the base directory and all of its subdirectories.
3852N/A propertyClient = ourClientManager.getWCClient();
1344N/A
1344N/A try
1344N/A {
5902N/A ourClientManager.getStatusClient().doStatus(workspacePath, SVNRevision.WORKING,
3852N/A SVNDepth.INFINITY, false, false, false, false, this, null);
1344N/A }
1344N/A catch (Exception e)
1344N/A {
1344N/A e.printStackTrace();
1344N/A System.err.println("WARNING: Encountered an error while examining " +
1344N/A "Subversion status: " + e);
1344N/A System.err.println("No further checks will be performed.");
1344N/A return;
1344N/A }
1344N/A
1344N/A boolean fail = false;
1344N/A
1344N/A if (! eolStyleProblemFiles.isEmpty())
1344N/A {
1344N/A System.err.println("WARNING: Potential svn:eol-style updates needed " +
1344N/A "for the following files:");
1344N/A for (String filename : eolStyleProblemFiles)
1344N/A {
1344N/A System.err.println(" " + filename);
1344N/A }
1344N/A
1707N/A String ignoreProp =
1707N/A getProject().getProperty(IGNORE_EOLSTYLE_ERRORS_PROPERTY);
1707N/A if ((ignoreProp == null) || (! ignoreProp.equalsIgnoreCase("true")))
1707N/A {
1707N/A fail = true;
1707N/A System.err.println("Fix svn:eol-style problems before proceeding, or " +
1707N/A "use '-D" + IGNORE_EOLSTYLE_ERRORS_PROPERTY +
1707N/A "=true' to ignore svn eol-style warnings.");
1707N/A }
1344N/A }
1344N/A
1344N/A if (! copyrightProblemFiles.isEmpty())
1344N/A {
1344N/A System.err.println("WARNING: Potential copyright year updates needed " +
1344N/A "for the following files:");
1344N/A for (String filename : copyrightProblemFiles)
1344N/A {
1344N/A System.err.println(" " + filename);
1344N/A }
1344N/A
1707N/A String ignoreProp =
1707N/A getProject().getProperty(IGNORE_COPYRIGHT_ERRORS_PROPERTY);
1707N/A if ((ignoreProp == null) || (! ignoreProp.equalsIgnoreCase("true")))
1707N/A {
1707N/A fail = true;
1707N/A System.err.println("Fix copyright date problems before proceeding, " +
1707N/A "or use '-D" + IGNORE_COPYRIGHT_ERRORS_PROPERTY +
1707N/A "=true' to ignore copyright warnings.");
1707N/A }
1344N/A }
1344N/A
1344N/A if (fail)
1344N/A {
1344N/A throw new BuildException();
1344N/A }
1344N/A }
1344N/A
1344N/A
1344N/A
1344N/A /**
1344N/A * Examines the provided status item to determine whether the associated file
1344N/A * is acceptable.
1344N/A *
1344N/A * @param status The SVN status information for the file of interest.
1344N/A */
1344N/A public void handleStatus(SVNStatus status)
1344N/A {
1344N/A File file = status.getFile();
1344N/A if ((! file.exists()) || (! file.isFile()))
1344N/A {
1344N/A // The file doesn't exist (which probably means it's been deleted) or
1344N/A // isn't a regular file, so we'll ignore it.
1344N/A return;
1344N/A }
1344N/A
1344N/A String fileName = file.getName();
1344N/A int lastPeriodPos = fileName.lastIndexOf('.');
1344N/A if (lastPeriodPos > 0)
1344N/A {
1344N/A String extension = fileName.substring(lastPeriodPos+1);
1344N/A if (! CHECKED_EXTENSIONS.contains(extension.toLowerCase()))
1344N/A {
1344N/A // The file doesn't have an extension that we care about, so skip it.
1344N/A return;
1344N/A }
1344N/A }
1344N/A else
1344N/A {
1344N/A // The file doesn't have an extension. We'll still want to check it if
1344N/A // it's in a resource/bin directory.
1344N/A File parentDirectory = file.getParentFile();
1344N/A if ((parentDirectory == null) ||
1344N/A (! parentDirectory.getName().equals("bin")))
1344N/A {
1344N/A return;
1344N/A }
1344N/A
1344N/A parentDirectory = parentDirectory.getParentFile();
1344N/A if ((parentDirectory == null) ||
1344N/A (! parentDirectory.getName().equals("resource")))
1344N/A {
1344N/A return;
1344N/A }
1344N/A }
1344N/A
1344N/A
1344N/A String filePath = file.getAbsolutePath();
1707N/A if (filePath.startsWith(workspacePath.getPath() + "/"))
1344N/A {
1707N/A filePath = filePath.substring(workspacePath.getPath().length() + 1);
1344N/A }
1344N/A
1344N/A
1344N/A // Check to make sure that the file has the correct EOL style.
1344N/A try
1344N/A {
1344N/A SVNPropertyData propertyData =
1344N/A propertyClient.doGetProperty(file, "svn:eol-style",
1344N/A SVNRevision.BASE,
3852N/A SVNRevision.WORKING);
1344N/A if ((propertyData == null) ||
3852N/A (! propertyData.getValue().getString().equals("native")))
1344N/A {
1344N/A eolStyleProblemFiles.add(filePath);
1344N/A }
1344N/A }
1344N/A catch (SVNException se)
1344N/A {
1344N/A // This could happen if the file isn't under version control. If so, then
1344N/A // we can't check the eol-style but we should at least be able to check
1344N/A // the copyright dates, so keep going.
1344N/A }
1344N/A
1344N/A
1344N/A // Check to see whether the file has a comment line containing a copyright
1344N/A // without the current year.
1344N/A BufferedReader reader = null;
1344N/A try
1344N/A {
1344N/A boolean copyrightFound = false;
1344N/A boolean correctYearFound = false;
1344N/A reader = new BufferedReader(new FileReader(file));
1344N/A String line = reader.readLine();
1344N/A while (line != null)
1344N/A {
1344N/A String lowerLine = line.toLowerCase().trim();
1344N/A if (isCommentLine(lowerLine))
1344N/A {
1344N/A int copyrightPos = lowerLine.indexOf("copyright");
1344N/A if (copyrightPos > 0)
1344N/A {
1344N/A copyrightFound = true;
1344N/A if (lowerLine.indexOf(yearString) > 0)
1344N/A {
1344N/A correctYearFound = true;
1344N/A break;
1344N/A }
1344N/A }
1344N/A }
1344N/A
1344N/A line = reader.readLine();
1344N/A }
1344N/A
1344N/A if (copyrightFound && (! correctYearFound))
1344N/A {
1344N/A copyrightProblemFiles.add(filePath);
1344N/A }
1344N/A }
1344N/A catch (IOException ioe)
1344N/A {
1344N/A System.err.println("ERROR: Could not read file " + filePath +
1344N/A " to check copyright date.");
1344N/A System.err.println("No further copyright date checking will be " +
1344N/A "performed.");
1344N/A throw new RuntimeException();
1344N/A }
1344N/A finally
1344N/A {
1344N/A try
1344N/A {
1344N/A if (reader != null)
1344N/A {
1344N/A reader.close();
1344N/A }
1344N/A } catch (Exception e) {}
1344N/A }
1344N/A }
1344N/A
1344N/A
1344N/A
1344N/A /**
1344N/A * Indicates whether the provided line appears to be a comment line. It will
1344N/A * check for a number of common comment indicators in Java source files,
1344N/A * shell scripts, XML files, and LDIF files.
1344N/A *
1344N/A * @param lowerLine The line to be checked. It should have been coverted to
1344N/A * all lowercase characters and any leading spaces
1344N/A * removed.
1344N/A *
1344N/A * @return {@code true} if it appears that the line is a comment line, or
1344N/A * {@code false} if not.
1344N/A */
1344N/A private static boolean isCommentLine(String lowerLine)
1344N/A {
1344N/A if (lowerLine.startsWith("/*") ||
1344N/A lowerLine.startsWith("*") ||
1344N/A lowerLine.startsWith("//") ||
1344N/A lowerLine.startsWith("#") ||
1344N/A lowerLine.startsWith("rem") ||
1344N/A lowerLine.startsWith("<!--") ||
1344N/A lowerLine.startsWith("!"))
1344N/A {
1344N/A return true;
1344N/A }
1344N/A else
1344N/A {
1344N/A return false;
1344N/A }
1344N/A }
1344N/A}
1344N/A