mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

matthew_swift
30.41.2009 f0bf1ccdaa227588d497594e4962993e4e2fde2c
Copy OpenDS build tools to sdk.
13 files added
4142 ■■■■■ changed files
sdk/build-tools/org/opends/build/tools/CheckPrecommit.java 402 ●●●●● patch | view | raw | blame | history
sdk/build-tools/org/opends/build/tools/ConcatSchema.java 272 ●●●●● patch | view | raw | blame | history
sdk/build-tools/org/opends/build/tools/CoverageDiff.java 969 ●●●●● patch | view | raw | blame | history
sdk/build-tools/org/opends/build/tools/CreateVersionString.java 98 ●●●●● patch | view | raw | blame | history
sdk/build-tools/org/opends/build/tools/GenerateMessageFile.java 995 ●●●●● patch | view | raw | blame | history
sdk/build-tools/org/opends/build/tools/GenerateRpm.java 251 ●●●●● patch | view | raw | blame | history
sdk/build-tools/org/opends/build/tools/GetSubversionRevision.java 143 ●●●●● patch | view | raw | blame | history
sdk/build-tools/org/opends/build/tools/GetSubversionUrlRepo.java 145 ●●●●● patch | view | raw | blame | history
sdk/build-tools/org/opends/build/tools/MessagePropertyKey.java 267 ●●●●● patch | view | raw | blame | history
sdk/build-tools/org/opends/build/tools/PrepTestNG.java 337 ●●●●● patch | view | raw | blame | history
sdk/build-tools/org/opends/build/tools/Utilities.java 159 ●●●●● patch | view | raw | blame | history
sdk/build-tools/org/opends/build/tools/ValidJavaVersion.java 68 ●●●●● patch | view | raw | blame | history
sdk/build-tools/org/opends/build/tools/package-info.java 36 ●●●●● patch | view | raw | blame | history
sdk/build-tools/org/opends/build/tools/CheckPrecommit.java
New file
@@ -0,0 +1,402 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 */
package org.opends.build.tools;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.LinkedList;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.SVNPropertyData;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.ISVNStatusHandler;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNStatus;
import org.tmatesoft.svn.core.wc.SVNWCClient;
/**
 * This class provides an implementation of an Ant task that may be used to
 * perform various checks to deteermine whether a file is suitable to be
 * committed.  This includes:
 * <UL>
 *   <LI>Make sure that the file has the correct "svn:eol-style" property
 *       value.</LI>
 *   <LI>If a file contains a line that appears to be a comment and includes the
 *       word "copyright", then it should contain the current year.</LI>
 * </UL>
 */
public class CheckPrecommit
       extends Task
       implements ISVNStatusHandler
{
  /**
   * The name of the system property that may be used to prevent copyright date
   * problems from failing the build.
   */
  public static final String IGNORE_COPYRIGHT_ERRORS_PROPERTY =
       "org.opends.server.IgnoreCopyrightDateErrors";
  /**
   * The name of the system property that may be used to prevent svn eol-style
   * problems from failing the build.
   */
  public static final String IGNORE_EOLSTYLE_ERRORS_PROPERTY =
       "org.opends.server.IgnoreEOLStyleErrors";
  /**
   *
   */
  public static final HashSet<String> CHECKED_EXTENSIONS =
       new HashSet<String>();
  static
  {
    CHECKED_EXTENSIONS.add("java");
    CHECKED_EXTENSIONS.add("xml");
    CHECKED_EXTENSIONS.add("xsd");
    CHECKED_EXTENSIONS.add("xsl");
    CHECKED_EXTENSIONS.add("html");
    CHECKED_EXTENSIONS.add("sh");
    CHECKED_EXTENSIONS.add("bat");
    CHECKED_EXTENSIONS.add("ldif");
    CHECKED_EXTENSIONS.add("txt");
    CHECKED_EXTENSIONS.add("c");
    CHECKED_EXTENSIONS.add("h");
    CHECKED_EXTENSIONS.add("mc");
    CHECKED_EXTENSIONS.add("Makefile");
  }
  // The path to the directory that is the base of the workspace.
  private File workspacePath;
  // The set of files that appear to have problems with the EOL style.
  private LinkedList<String> eolStyleProblemFiles = new LinkedList<String>();
  // The set of files that appear to have problems with the copyright date.
  private LinkedList<String> copyrightProblemFiles = new LinkedList<String>();
  // The path to the root of the Subversion workspace to check.
  private String workspace = null;
  // The string representation of the current year.
  private String yearString;
  // The overall SVN Client Manager. required with svnkit 1.2.x
  private static SVNClientManager ourClientManager =
          SVNClientManager.newInstance();
  // The property client used to look at file properties.
  private SVNWCClient propertyClient;
  /**
   * Specifies the path to the root of the Subversion workspace for which to
   * retrieve the revision number.
   *
   * @param  workspace  The path to the root of the Subversion workspace for
   *                    which to retrieve the revision number.
   */
  public void setWorkspace(String workspace)
  {
    this.workspace = workspace;
  }
  /**
   * Performs the appropriate processing needed for this task.  In this case,
   * it uses SVNKit to identify all modified files in the current workspace.
   * For all source files, look for comment lines containing the word
   * "copyright" and make sure at least one of them contains the current year.
   */
  @Override()
  public void execute()
  {
    if ((workspace == null) || (workspace.length() == 0))
    {
      workspacePath = getProject().getBaseDir();
    }
    else
    {
      workspacePath = new File(workspace);
    }
    // Get the year to use in the determination.
    GregorianCalendar calendar = new GregorianCalendar();
    int year = calendar.get(GregorianCalendar.YEAR);
    yearString = String.valueOf(year);
    // Process the base directory and all of its subdirectories.
    propertyClient = ourClientManager.getWCClient();
    try
    {
      long status = ourClientManager.getStatusClient().doStatus(workspacePath, SVNRevision.WORKING,
              SVNDepth.INFINITY, false, false, false, false, this, null);
    }
    catch (Exception e)
    {
      e.printStackTrace();
      System.err.println("WARNING:  Encountered an error while examining " +
                         "Subversion status:  " + e);
      System.err.println("No further checks will be performed.");
      return;
    }
    boolean fail = false;
    if (! eolStyleProblemFiles.isEmpty())
    {
      System.err.println("WARNING:  Potential svn:eol-style updates needed " +
                         "for the following files:");
      for (String filename : eolStyleProblemFiles)
      {
        System.err.println("     " + filename);
      }
      String ignoreProp =
           getProject().getProperty(IGNORE_EOLSTYLE_ERRORS_PROPERTY);
      if ((ignoreProp == null) || (! ignoreProp.equalsIgnoreCase("true")))
      {
        fail = true;
        System.err.println("Fix svn:eol-style problems before proceeding, or " +
                           "use '-D" + IGNORE_EOLSTYLE_ERRORS_PROPERTY +
                           "=true' to ignore svn eol-style warnings.");
      }
    }
    if (! copyrightProblemFiles.isEmpty())
    {
      System.err.println("WARNING:  Potential copyright year updates needed " +
                         "for the following files:");
      for (String filename : copyrightProblemFiles)
      {
        System.err.println("     " + filename);
      }
      String ignoreProp =
           getProject().getProperty(IGNORE_COPYRIGHT_ERRORS_PROPERTY);
      if ((ignoreProp == null) || (! ignoreProp.equalsIgnoreCase("true")))
      {
        fail = true;
        System.err.println("Fix copyright date problems before proceeding, " +
                           "or use '-D" + IGNORE_COPYRIGHT_ERRORS_PROPERTY +
                           "=true' to ignore copyright warnings.");
      }
    }
    if (fail)
    {
      throw new BuildException();
    }
  }
  /**
   * Examines the provided status item to determine whether the associated file
   * is acceptable.
   *
   * @param  status  The SVN status information for the file of interest.
   */
  public void handleStatus(SVNStatus status)
  {
    File file = status.getFile();
    if ((! file.exists()) || (! file.isFile()))
    {
      // The file doesn't exist (which probably means it's been deleted) or
      // isn't a regular file, so we'll ignore it.
      return;
    }
    String fileName = file.getName();
    int lastPeriodPos = fileName.lastIndexOf('.');
    if (lastPeriodPos > 0)
    {
      String extension = fileName.substring(lastPeriodPos+1);
      if (! CHECKED_EXTENSIONS.contains(extension.toLowerCase()))
      {
        // The file doesn't have an extension that we care about, so skip it.
        return;
      }
    }
    else
    {
      // The file doesn't have an extension.  We'll still want to check it if
      // it's in a resource/bin directory.
      File parentDirectory = file.getParentFile();
      if ((parentDirectory == null) ||
          (! parentDirectory.getName().equals("bin")))
      {
        return;
      }
      parentDirectory = parentDirectory.getParentFile();
      if ((parentDirectory == null) ||
          (! parentDirectory.getName().equals("resource")))
      {
        return;
      }
    }
    String filePath = file.getAbsolutePath();
    if (filePath.startsWith(workspacePath.getPath() + "/"))
    {
      filePath = filePath.substring(workspacePath.getPath().length() + 1);
    }
    // Check to make sure that the file has the correct EOL style.
    try
    {
      SVNPropertyData propertyData =
           propertyClient.doGetProperty(file, "svn:eol-style",
                                        SVNRevision.BASE,
                                        SVNRevision.WORKING);
      if ((propertyData == null) ||
          (! propertyData.getValue().getString().equals("native")))
      {
        eolStyleProblemFiles.add(filePath);
      }
    }
    catch (SVNException se)
    {
      // This could happen if the file isn't under version control.  If so, then
      // we can't check the eol-style but we should at least be able to check
      // the copyright dates, so keep going.
    }
    // Check to see whether the file has a comment line containing a copyright
    // without the current year.
    BufferedReader reader = null;
    try
    {
      boolean copyrightFound   = false;
      boolean correctYearFound = false;
      reader = new BufferedReader(new FileReader(file));
      String line = reader.readLine();
      while (line != null)
      {
        String lowerLine = line.toLowerCase().trim();
        if (isCommentLine(lowerLine))
        {
          int copyrightPos = lowerLine.indexOf("copyright");
          if (copyrightPos > 0)
          {
            copyrightFound = true;
            if (lowerLine.indexOf(yearString) > 0)
            {
              correctYearFound = true;
              break;
            }
          }
        }
        line = reader.readLine();
      }
      if (copyrightFound && (! correctYearFound))
      {
        copyrightProblemFiles.add(filePath);
      }
    }
    catch (IOException ioe)
    {
      System.err.println("ERROR:  Could not read file " + filePath +
                         " to check copyright date.");
      System.err.println("No further copyright date checking will be " +
                         "performed.");
      throw new RuntimeException();
    }
    finally
    {
      try
      {
        if (reader != null)
        {
          reader.close();
        }
      } catch (Exception e) {}
    }
  }
  /**
   * Indicates whether the provided line appears to be a comment line.  It will
   * check for a number of common comment indicators in Java source files,
   * shell scripts, XML files, and LDIF files.
   *
   * @param  lowerLine  The line to be checked.  It should have been coverted to
   *                    all lowercase characters and any leading spaces
   *                    removed.
   *
   * @return  {@code true} if it appears that the line is a comment line, or
   *          {@code false} if not.
   */
  private static boolean isCommentLine(String lowerLine)
  {
    if (lowerLine.startsWith("/*") ||
        lowerLine.startsWith("*") ||
        lowerLine.startsWith("//") ||
        lowerLine.startsWith("#") ||
        lowerLine.startsWith("rem") ||
        lowerLine.startsWith("<!--") ||
        lowerLine.startsWith("!"))
    {
      return true;
    }
    else
    {
      return false;
    }
  }
}
sdk/build-tools/org/opends/build/tools/ConcatSchema.java
New file
@@ -0,0 +1,272 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 */
package org.opends.build.tools;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.LinkedList;
import java.util.TreeSet;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
/**
 * This class provides an implementation of an Ant task that concatenates the
 * contents of the files in the schema directory to create a base schema that
 * may be used during the upgrade process.  Each element will also include the
 * X-SCHEMA-FILE extension to indicate the source schema file.
 */
public class ConcatSchema
       extends Task
{
  // The path to the directory containing the schema files.
  private String schemaDirectory;
  // The path to the concatenated schema file to create.
  private String toFile;
  /**
   * Specifies the path to the directory containing the schema files.
   *
   * @param  schemaDirectory  The path to the directory containing the schema
   *                          files.
   */
  public void setSchemaDirectory(String schemaDirectory)
  {
    this.schemaDirectory = schemaDirectory;
  }
  /**
   * Specifies the path to the file to create containing the concatenated schema
   * elements.
   *
   * @param  toFile  The path to the file containing the concatenated schema
   *                 elements.
   */
  public void setToFile(String toFile)
  {
    this.toFile = toFile;
  }
  /**
   * Performs the appropriate processing needed for this task.  In this case,
   * it uses SVNKit to identify all modified files in the current workspace.
   * For all source files, look for comment lines containing the word
   * "copyright" and make sure at least one of them contains the current year.
   */
  @Override()
  public void execute()
  {
    // Get a sorted list of the files in the schema directory.
    TreeSet<String> schemaFileNames = new TreeSet<String>();
    for (File f : new File(schemaDirectory).listFiles())
    {
      if (f.isFile())
      {
        schemaFileNames.add(f.getName());
      }
    }
    // Create a set of lists that will hold the schema elements read from the
    // files.
    LinkedList<String> attributeTypes    = new LinkedList<String>();
    LinkedList<String> objectClasses     = new LinkedList<String>();
    LinkedList<String> nameForms         = new LinkedList<String>();
    LinkedList<String> ditContentRules   = new LinkedList<String>();
    LinkedList<String> ditStructureRules = new LinkedList<String>();
    LinkedList<String> matchingRuleUses  = new LinkedList<String>();
    // Open each of the files in order and read the elements that they contain,
    // appending them to the appropriate lists.
    for (String name : schemaFileNames)
    {
      // Read the contents of the file into a list with one schema element per
      // list element.
      LinkedList<StringBuilder> lines = new LinkedList<StringBuilder>();
      try
      {
        BufferedReader reader = new BufferedReader(new FileReader(
                                         new File(schemaDirectory, name)));
        while (true)
        {
          String line = reader.readLine();
          if (line == null)
          {
            break;
          }
          else if (line.startsWith("#") || (line.length() == 0))
          {
            continue;
          }
          else if (line.startsWith(" "))
          {
            lines.getLast().append(line.substring(1));
          }
          else
          {
            lines.add(new StringBuilder(line));
          }
        }
        reader.close();
      }
      catch (Exception e)
      {
        throw new BuildException("Error while reading schema file " + name +
                                 ":  " + e, e);
      }
      // Iterate through each line in the list.  Find the colon and get the
      // attribute name at the beginning.  If it's someting that we don't
      // recognize, then skip it.  Otherwise, add the X-SCHEMA-FILE extension
      // and add it to the appropriate schema element list.
      for (StringBuilder buffer : lines)
      {
        // Get the line and add the X-SCHEMA-FILE extension to the end of it.
        // All of them should end with " )" but some might have the parenthesis
        // crammed up against the last character so deal with that as well.
        String line = buffer.toString().trim();
        if (line.endsWith(" )"))
        {
         line = line.substring(0, line.length()-1) + "X-SCHEMA-FILE '" + name +
            "' )";
        }
        else if (line.endsWith(")"))
        {
         line = line.substring(0, line.length()-1) + " X-SCHEMA-FILE '" + name +
            "' )";
        }
        else
        {
          continue;
        }
        String lowerLine = line.toLowerCase();
        if (lowerLine.startsWith("attributetypes:"))
        {
          attributeTypes.add(line);
        }
        else if (lowerLine.startsWith("objectclasses:"))
        {
          objectClasses.add(line);
        }
        else if (lowerLine.startsWith("nameforms:"))
        {
          nameForms.add(line);
        }
        else if (lowerLine.startsWith("ditcontentrules:"))
        {
          ditContentRules.add(line);
        }
        else if (lowerLine.startsWith("ditstructurerules:"))
        {
          ditStructureRules.add(line);
        }
        else if (lowerLine.startsWith("matchingruleuse:"))
        {
          matchingRuleUses.add(line);
        }
      }
    }
    // Write the resulting output to the merged schema file.
    try
    {
      BufferedWriter writer = new BufferedWriter(new FileWriter(toFile));
      writer.write("dn: cn=schema");
      writer.newLine();
      writer.write("objectClass: top");
      writer.newLine();
      writer.write("objectClass: ldapSubentry");
      writer.newLine();
      writer.write("objectClass: subschema");
      writer.newLine();
      for (String line : attributeTypes)
      {
        writer.write(line);
        writer.newLine();
      }
      for (String line : objectClasses)
      {
        writer.write(line);
        writer.newLine();
      }
      for (String line : nameForms)
      {
        writer.write(line);
        writer.newLine();
      }
      for (String line : ditContentRules)
      {
        writer.write(line);
        writer.newLine();
      }
      for (String line : ditStructureRules)
      {
        writer.write(line);
        writer.newLine();
      }
      for (String line : matchingRuleUses)
      {
        writer.write(line);
        writer.newLine();
      }
      writer.close();
    }
    catch (Exception e)
    {
      throw new BuildException("Error while writing concatenated schema file " +
                               toFile + ":  " + e, e);
    }
  }
}
sdk/build-tools/org/opends/build/tools/CoverageDiff.java
New file
@@ -0,0 +1,969 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 */
package org.opends.build.tools;
import com.vladium.emma.report.*;
import com.vladium.emma.report.html.doc.*;
import com.vladium.emma.data.*;
import com.vladium.util.IntObjectMap;
import java.io.*;
import java.util.*;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.BuildException;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNDiffClient;
import org.tmatesoft.svn.core.wc.SVNRevision;
public class CoverageDiff extends Task {
  private static SVNClientManager ourClientManager =
          SVNClientManager.newInstance();
  private static final String EOL = System.getProperty("line.separator");
  private boolean verbose = false;
  private boolean enabled = true;
  private final int COVERED_MOD_EXE_LINES = 0;
  private final int MOD_EXE_LINES = 1;
  private final int MOD_LINES = 2;
  private final int DEL_LINES = 3;
  private final String ENCODING = "ISO-8859-1";
  private final int IO_BUF_SIZE = 32 * 1024;
  private final LinkedHashMap<String, SrcFileItem> emmaSrcMap =
      new LinkedHashMap<String, SrcFileItem>();
  private final LinkedHashMap<String, Double[]> modCoverageMap =
      new LinkedHashMap<String, Double[]>();
  private final String CSS = "TABLE,TD,TH {border-style:solid; border-color:black;} " +
            "TD,TH {background:white;margin:0;line-height:100%;padding-left:0.5em;padding-right:0.5em;} " +
            "TD {border-width:0 1px 0 0;} TH {border-width:1px 1px 1px 0;} " +
            "TR TD.h {color:red;} " +
            "TABLE {border-spacing:0; border-collapse:collapse;border-width:0 0 1px 1px;} " +
            "P,H1,H2,H3,TH {font-family:verdana,arial,sans-serif;font-size:10pt;} " +
            "TD {font-family:courier,monospace;font-size:10pt;} " +
            "TABLE.hdft {border-spacing:0;border-collapse:collapse;border-style:none;} " +
            "TABLE.hdft TH,TABLE.hdft TD {border-style:none;line-height:normal;} " +
            "TABLE.hdft TH.tl,TABLE.hdft TD.tl {background:#6699CC;color:white;} " +
            "TABLE.hdft TD.nv {background:#6633DD;color:white;} " +
            ".nv A:link {color:white;} .nv A:visited {color:white;} " +
            ".nv A:active {color:yellow;} " +
            "TABLE.hdft A:link {color:white;} " +
            "TABLE.hdft A:visited {color:white;} " +
            "TABLE.hdft A:active {color:yellow;} " +
            ".in {color:#356085;} " +
            "TABLE.s TD {padding-left:0.25em;padding-right:0.25em;} " +
            "TABLE.s TD.ddt {padding-left:0.25em;padding-right:0.25em;color:#AAAAAA;}" +
            "TABLE.s TD.ds {padding-left:0.25em;padding-right:0.25em;text-align:right;background:#F0F0F0;} " +
            "TABLE.s TD.dm {padding-left:0.25em;padding-right:0.25em;text-align:right;background:#BCCFF9;} " +
            "TABLE.s TD.dd {padding-left:0.25em;padding-right:0.25em;text-align:right;background:#AAAAAA;color:#FFFFFF} " +
            "TABLE.s TH {padding-left:0.25em;padding-right:0.25em;text-align:left;background:#F0F0F0;} " +
            "TABLE.s TD.cz {background:#FF9999;} " +
            "TABLE.s TD.cp {background:#FFFF88;} " +
            "TABLE.s TD.cc {background:#CCFFCC;} " +
            "A:link {color:#0000EE;text-decoration:none;} " +
            "A:visited {color:#0000EE;text-decoration:none;} " +
            "A:hover {color:#0000EE;text-decoration:underline;} " +
            "TABLE.cn {border-width:0 0 1px 0;} " +
            "TABLE.s {border-width:1px 0 1px 1px;} " +
            "TD.h {color:red;border-width:0 1px 0 0;} " +
            "TD.f {border-width:0 1px 0 1px;} " +
            "TD.hf {color:red;border-width:0 1px 0 1px;} " +
            "TH.f {border-width:1px 1px 1px 1px;} " +
            "TR.cis TD {background:#F0F0F0;} " +
            "TR.cis TD {border-width:1px 1px 1px 0;} " +
            "TR.cis TD.h {color:red;border-width:1px 1px 1px 0;} " +
            "TR.cis TD.f {border-width:1px 1px 1px 1px;} " +
            "TR.cis TD.hf {color:red;border-width:1px 1px 1px 1px;} " +
            "TD.b {border-style:none;background:transparent;line-height:50%;}  " +
            "TD.bt {border-width:1px 0 0 0;background:transparent;line-height:50%;} " +
            "TR.o TD {background:#F0F0F0;}" +
            "TABLE.it {border-style:none;}" +
            "TABLE.it TD,TABLE.it TH {border-style:none;}";
  private File emmaDataPath;
  private File outputPath;
  private String diffPath;
  //   The SVN revision to perform the diff against when calculating
  //   the coverage diff.  It can be a revision number, a timestamp,
  //   or a revision keyword (BASE, COMMITTED, and PREV make the
  //   most sense).  The primary use case for this setting is to do
  //   a coverage diff against the previous revision when there are
  //   no changes in the working copy.  It defaults to BASE.
  private String fromRevision;
  public void setEmmaDataPath(String file)
  {
    emmaDataPath = new File(file);
  }
  public void setOutputPath(String file)
  {
    outputPath = new File(file);
  }
  public void setDiffPath(String diffArgs)
  {
    diffPath = diffArgs;
  }
  public void setVerbose(String bol)
  {
    verbose = bol.toLowerCase().equals("true");
  }
  public void setEnabled(String bol)
  {
    enabled = bol.toLowerCase().equals("true");
  }
  public void setFromRevision(String fromRevision)
  {
    this.fromRevision = fromRevision;
  }
  public void execute() throws BuildException {
    try {
      innerExecute();
    } catch (BuildException e) {
      throw e;
    } catch (Throwable e) {
      e.printStackTrace();
    }
  }
  private void innerExecute() throws BuildException, SVNException
  {
    long start = System.currentTimeMillis();
    verboseOut("Starting to execute coveragediff.");
    verboseOut("diffPath='" + diffPath +"'");
    if(emmaDataPath == null)
    {
      throw new BuildException("emmaDataPath attribute is not set. It must be set to the path of the EMMA data directory");
    }
    if(outputPath == null)
    {
      throw new BuildException("outputPath attribute is not set. It must be set to a valid directory where the report will be generated");
    }
    if(fromRevision == null)
    {
      throw new BuildException("fromRevision attribute is not set. It must be set to the revision from which the diff is generated (e.g. BASE).");
    }
    if(!enabled)
    {
      return;
    }
    // So we can go over http:// and https:// when diff'ing against previous versions
    DAVRepositoryFactory.setup();
    IReportDataView emmaDataView = null;
    try
    {
        emmaDataView = loadEmmaData(emmaDataPath);
        verboseOut("Loaded EMMA data.");
    }
    catch(IOException ie)
    {
      System.out.println("WARNING: An error occurred while loading EMMA " +
          "data. Report will not contain any coverage information.");
    }
    try
    {
      processDiffOutput(getDiffOutputReader(), emmaDataView);
    }
    catch(IOException ie)
    {
      System.out.println("ERROR: An error occurred while processing diff output: " + ie.toString() + " Quitting...");
      ie.printStackTrace();
      return;
    }
    System.out.println("Coverage diff completed in " + (System.currentTimeMillis() - start) + " ms.");
  }
  private IReportDataView loadEmmaData(File emmaCoverageDataDir) throws IOException
  {
    if(emmaCoverageDataDir == null)
    {
      throw new IOException("Emma Converage Data Directory is null");
    }
    File[] emmaCoverageDataFiles = emmaCoverageDataDir.listFiles();
    int emmaCoverageDataFileCount = 0;
    IReportDataView m_view;
    IMetaData mdata = null;
    ICoverageData cdata = null;
    if(emmaCoverageDataFiles == null || emmaCoverageDataFiles.length <= 0)
    {
      throw new IOException("No EMMA data files found");
    }
    verboseOut("processing input files ...");
    final long start = System.currentTimeMillis();
    // merge all data files:
    for (final File dataFile : emmaCoverageDataFiles) {
      verboseOut("processing input file [" + dataFile.getAbsolutePath() + "] ...");
      final IMergeable[] fileData = DataFactory.load(dataFile);
      final IMetaData _mdata = (IMetaData) fileData[DataFactory.TYPE_METADATA];
      if (_mdata != null) {
        verboseOut("  loaded " + _mdata.size() + " metadata entries");
        if (mdata == null)
          mdata = _mdata;
        else
          mdata = (IMetaData) mdata.merge(_mdata); // note: later datapath entries override earlier ones
      }
      final ICoverageData _cdata = (ICoverageData) fileData[DataFactory.TYPE_COVERAGEDATA];
      if (_cdata != null) {
        verboseOut("  loaded " + _cdata.size() + " coverage data entries");
        if (cdata == null)
          cdata = _cdata;
        else
          cdata = (ICoverageData) cdata.merge(_cdata); // note: later datapath entries override earlier ones
      }
      ++emmaCoverageDataFileCount;
    }
    verboseOut(emmaCoverageDataFileCount + " file(s) read and merged in " + (System.currentTimeMillis() - start) + " ms");
    if ((mdata == null) || mdata.isEmpty()) {
      System.out.println("nothing to do: no metadata found in any of the data files");
      return null;
    }
    if (cdata == null) {
      System.out.println("nothing to do: no runtime coverage data found in any of the data files");
      return null;
    }
    if (cdata.isEmpty()) {
      System.out.println("no collected coverage data found in any of the data files [Diff output will not include coverage data]");
      return null;
    }
    if (!mdata.hasLineNumberData() || !mdata.hasSrcFileData()) {
      System.out.println("no collected line coverage data found in any of the data files [Diff output will not include coverage data]");
      return null;
    }
    final IReportDataModel model = IReportDataModel.Factory.create (mdata, cdata);
    m_view = model.getView (IReportDataView.HIER_SRC_VIEW);
    verboseOut("  merged metadata contains " + mdata.size() + " entries");
    verboseOut("  merged coverage data contains " + cdata.size() + " entries");
    return m_view;
  }
  private BufferedReader getDiffOutputReader()
          throws IOException, SVNException {
    File workspaceRoot = getProject().getBaseDir();
    File diffFile = new File(outputPath, "svn.diff");
    // Most often this will be 'BASE' but it could also be 'PREVIOUS'
    SVNRevision baseRevision = SVNRevision.parse(fromRevision);
    System.out.println("Doing coverage diff from revision: " + baseRevision.toString());
    ourClientManager.getDiffClient().doDiff(workspaceRoot, baseRevision,
            workspaceRoot, SVNRevision.WORKING, SVNDepth.INFINITY, false,
            new FileOutputStream(diffFile), null);
    return new BufferedReader(new InputStreamReader(new FileInputStream(
                                                             diffFile)));
  }
  private void processDiffOutput(BufferedReader diffOutput,
                                        IReportDataView emmaDataView)
      throws IOException {
    File file = new File(outputPath, "index.html");
    BufferedWriter writer =
        new BufferedWriter (new OutputStreamWriter (
            new FileOutputStream (file), ENCODING), IO_BUF_SIZE);
    HTMLWriter out = new HTMLWriter(writer);
    System.out.println("Writing report to [" + file.toString() + "]");
    String title = "Coverage Diff Report (generated ";
    title = title + new Date(System.currentTimeMillis ());
    title = title + " )";
    HTMLDocument page = new HTMLDocument (title, ENCODING);
    page.addStyle (CSS);
    String line = diffOutput.readLine();
    ArrayList<String> diffOutputFile = new ArrayList<String>();
    while(line != null)
    {
      //Diffed file
      if(line.length() >6 && line.substring(0, 6).equals("Index:"))
      {
        processDiffOutputFile(page, diffOutputFile, emmaDataView);
        diffOutputFile = new ArrayList<String>();
        diffOutputFile.add(line);
      }
      else
      {
        diffOutputFile.add(line);
      }
      line = diffOutput.readLine();
    }
    processDiffOutputFile(page, diffOutputFile, emmaDataView);
    IElementList overallStats = new ElementList();
    final IElement statTitle = IElement.Factory.create (Tag.Hs[1]);
    statTitle.setText("OVERALL STATS SUMMARY", true);
    overallStats.add(statTitle);
    final HTMLTable statsTable = new HTMLTable (null, null, null, "0");
    statsTable.setClass ("it");
    {
      HTMLTable.IRow row = statsTable.newRow ();
      row.newCell ().setText ("svn diff arg(s):", true);
      row.newCell ().setText ("" + diffPath.toString(), true);
      row = statsTable.newRow ();
      row.newCell ().setText ("total files modified:", true);
      row.newCell ().setText ("" + emmaSrcMap.keySet().size(), false);
      Double[] overallModCoverage = new Double[4];
      overallModCoverage[COVERED_MOD_EXE_LINES] = 0.0;
      overallModCoverage[MOD_EXE_LINES] = 0.0;
      overallModCoverage[MOD_LINES] = 0.0;
      overallModCoverage[DEL_LINES] = 0.0;
      Double[] modCoverage;
      for (Double[] doubles : modCoverageMap.values()) {
        modCoverage = doubles;
        if (modCoverage != null) {
          overallModCoverage[COVERED_MOD_EXE_LINES] += modCoverage[COVERED_MOD_EXE_LINES];
          overallModCoverage[MOD_EXE_LINES] += modCoverage[MOD_EXE_LINES];
          overallModCoverage[MOD_LINES] += modCoverage[MOD_LINES];
          overallModCoverage[DEL_LINES] += modCoverage[DEL_LINES];
        }
      }
      String modCoverageStr = "";
      if(overallModCoverage[MOD_EXE_LINES] > 0)
      {
        modCoverageStr = String.format("%d%% (%.1f/%.1f)",
            (int)(overallModCoverage[COVERED_MOD_EXE_LINES]/overallModCoverage[MOD_EXE_LINES]*100),
            overallModCoverage[COVERED_MOD_EXE_LINES],
            overallModCoverage[MOD_EXE_LINES]);
      }
      else
      {
        modCoverageStr = String.format("%d%% (%.1f/%.1f)", 100,
            overallModCoverage[COVERED_MOD_EXE_LINES],
            overallModCoverage[MOD_EXE_LINES]);
      }
      row = statsTable.newRow ();
      row.newCell ().setText ("total lines modified:", true);
      row.newCell ().setText ("" + overallModCoverage[MOD_LINES].intValue(), true);
      row = statsTable.newRow ();
      row.newCell ().setText ("total lines removed:", true);
      row.newCell ().setText ("" + overallModCoverage[DEL_LINES].intValue(), true);
      row = statsTable.newRow ();
      row.newCell ().setText ("coverage for modified executable lines:", true);
      row.newCell ().setText ("" + modCoverageStr, true);
    }
    overallStats.add(statsTable);
    final IElement coverageTitle = IElement.Factory.create (Tag.Hs[1]);
    statTitle.setText("OVERALL DIFF SUMMARY", true);
    overallStats.add(coverageTitle);
    HTMLTable summaryTable = new HTMLTable ("100%", null, null, "0");
    if(emmaDataView != null)
    {
      addHeaderRow(emmaDataView.getRoot(), summaryTable, true);
    }
    else
    {
      addHeaderRow(null, summaryTable, true);
    }
    Set<Map.Entry<String, SrcFileItem>> items = emmaSrcMap.entrySet();
    boolean odd = true;
    int count = 0;
    for (Map.Entry<String, SrcFileItem> item : items) {
      if (item != null) {
        final String fileName = item.getKey();
        final SrcFileItem srcFileItem = item.getValue();
        final Double[] modCoverage = modCoverageMap.get(fileName);
        addItemRow(fileName, srcFileItem, modCoverage, odd, summaryTable,
            "s" + count, true, true);
        odd = !odd;
        count++;
      }
    }
    overallStats.add(summaryTable);
    page.setHeader(overallStats);
    page.emit(out);
    out.flush();
  }
  private void processDiffOutputFile(HTMLDocument html,
                                            ArrayList<String> diffFile,
                                            IReportDataView emmaDataView)
      throws IOException
  {
    if(diffFile.size() <= 0)
    {
      return;
    }
    Double[] modCoverage = new Double[4];
    modCoverage[COVERED_MOD_EXE_LINES] = 0.0;
    modCoverage[MOD_EXE_LINES] = 0.0;
    modCoverage[MOD_LINES] = 0.0;
    modCoverage[DEL_LINES] = 0.0;
    String fileHeader = diffFile.get(0);
    verboseOut("fileHeader: " + diffFile);
    //Try to get the package information if its a Java file
    File srcFilePath = new File(fileHeader.substring(7));
    SrcFileItem emmaSourceItem = null;
    if(srcFilePath.isFile())
    {
      FileInputStream srcFile = new FileInputStream(srcFilePath);
      String srcFilePackage = parseJavaPackage(srcFile);
      if(emmaDataView != null)
      {
        emmaSourceItem = getEmmaSrcItem(emmaDataView.getRoot(),
                  srcFilePackage, srcFilePath.getName());
      }
    }
    //Figure out the flag for the working copy.
    String workingCopyFlag = null;
    String otherCopyFlag = null;
    String firstFileLine = diffFile.get(2);
    String secondFileLine = diffFile.get(3);
    verboseOut("firstFileLine=" + firstFileLine);
    verboseOut("secondFileLine=" + secondFileLine);
    String revisionStr = "unknown";
    // Skip over binary files
    if (firstFileLine.contains("Cannot display")) {
      return;
    }
    HTMLTable srcTable = null;
    if(firstFileLine.endsWith("(working copy)"))
    {
      workingCopyFlag = firstFileLine.substring(0, 1);
    }
    else
    {
      otherCopyFlag = firstFileLine.substring(0, 1);
      revisionStr = firstFileLine.substring(firstFileLine.lastIndexOf("("));
    }
    if(secondFileLine.endsWith("(working copy)"))
    {
      workingCopyFlag = secondFileLine.substring(0, 1);
    }
    else
    {
      otherCopyFlag = secondFileLine.substring(0, 1);
      revisionStr = secondFileLine.substring(secondFileLine.lastIndexOf("("));
    }
    if(firstFileLine.endsWith("(revision 0)") ||
        secondFileLine.endsWith("(revision 0)"))
    {
      workingCopyFlag = "+";
      otherCopyFlag = "-";
    }
    if(workingCopyFlag == null || otherCopyFlag == null)
    {
      throw new IOException("Error occurred while parsing diff output." + EOL +
        "firstFileLine= '" + firstFileLine + "'" + EOL +
        "secondFileLine= '" + secondFileLine + "'");
    }
    else
    {
      srcTable = new HTMLTable ("100%", null, null, "0");
      srcTable.setClass("s");
      ArrayList<String> diffOutputChunk = new ArrayList<String>();
      Double[] chunkModCoverage;
      for(int i = 4; i < diffFile.size(); i++)
      {
        //Found a chunk indicator.
        if(diffFile.get(i).startsWith("@@"))
        {
          chunkModCoverage = processDiffOutputFileChunk(srcTable, diffOutputChunk, workingCopyFlag,
                otherCopyFlag, emmaSourceItem);
          if(chunkModCoverage != null)
          {
            modCoverage[COVERED_MOD_EXE_LINES] += chunkModCoverage[COVERED_MOD_EXE_LINES];
            modCoverage[MOD_EXE_LINES] += chunkModCoverage[MOD_EXE_LINES];
            modCoverage[MOD_LINES] += chunkModCoverage[MOD_LINES];
            modCoverage[DEL_LINES] += chunkModCoverage[DEL_LINES];
          }
          diffOutputChunk = new ArrayList<String>();
          diffOutputChunk.add(diffFile.get(i));
        }
        //Not any of the above so this line must be diffed text
        else
        {
          diffOutputChunk.add(diffFile.get(i));
        }
      }
      //Finishing process whatever we have queued up
      chunkModCoverage = processDiffOutputFileChunk(srcTable, diffOutputChunk, workingCopyFlag,
          otherCopyFlag, emmaSourceItem);
      if(chunkModCoverage != null)
      {
        modCoverage[COVERED_MOD_EXE_LINES] += chunkModCoverage[COVERED_MOD_EXE_LINES];
        modCoverage[MOD_EXE_LINES] += chunkModCoverage[MOD_EXE_LINES];
        modCoverage[MOD_LINES] += chunkModCoverage[MOD_LINES];
        modCoverage[DEL_LINES] += chunkModCoverage[DEL_LINES];
      }
    }
    final IElement a = IElement.Factory.create (Tag.A);
    a.getAttributes ().set (Attribute.NAME, "s" + emmaSrcMap.keySet().size());
    html.add(a);
    final IElement itemname = IElement.Factory.create (Tag.SPAN);
    {
      itemname.setText (srcFilePath.toString(), true);
      itemname.setClass ("in");
    }
    final IElementList title = new ElementList ();
    {
      title.add (new Text ("DIFF SUMMARY FOR SOURCE FILE [", true));
      title.add (itemname);
      title.add (new Text ("] against ", true));
      title.add (new Text (revisionStr, true));
    }
    html.addH (1, title, null);
    if(emmaSourceItem != null)
    {
      final HTMLTable coverageTable = new HTMLTable ("100%", null, null, "0");
      addHeaderRow(emmaSourceItem, coverageTable, false);
      addItemRow(srcFilePath.toString(), emmaSourceItem, modCoverage, false, coverageTable, null, false, false);
      html.add(coverageTable);
      html.addEmptyP();
    }
    else
    {
      html.addH(2, "Coverage Information Not Available (e.g. file is not in src/, is not java, is an interface, or was deleted)", null);
    }
    if(srcTable != null)
    {
      html.add(srcTable);
    }
    emmaSrcMap.put(srcFilePath.toString(), emmaSourceItem);
    modCoverageMap.put(srcFilePath.toString(), modCoverage);
  }
  private Double[] processDiffOutputFileChunk(HTMLTable table,
                                                 ArrayList<String> diffChunk,
                                                 String workingCopyFlag,
                                                 String otherCopyFlag,
                                                 SrcFileItem emmaSrcItem)
  {
    if(diffChunk.size() <= 0)
    {
      return null;
    }
    int workingCopyBegin;
    int workingCopyRange;
    int otherCopyBegin;
    int otherCopyRange;
    Double[] modCoverage = new Double[4];
    modCoverage[COVERED_MOD_EXE_LINES] = 0.0;
    modCoverage[MOD_EXE_LINES] = 0.0;
    modCoverage[MOD_LINES] = 0.0;
    modCoverage[DEL_LINES] = 0.0;
    IntObjectMap lineCoverageMap = null;
    if(emmaSrcItem != null)
    {
      lineCoverageMap = emmaSrcItem.getLineCoverage ();
    }
    String chunkHeader = diffChunk.get(0);
    int workingCopyBeginIdx = chunkHeader.indexOf(workingCopyFlag);
    int workingCopyCommaIdx = chunkHeader.indexOf(",", workingCopyBeginIdx);
    int workingCopyEndIdx = chunkHeader.indexOf(" ", workingCopyCommaIdx);
    int otherCopyBeginIdx = chunkHeader.indexOf(otherCopyFlag);
    int otherCopyCommaIdx = chunkHeader.indexOf(",", otherCopyBeginIdx);
    int otherCopyEndIdx = chunkHeader.indexOf(" ", otherCopyCommaIdx);
    workingCopyBegin = Integer.parseInt(
        chunkHeader.substring(workingCopyBeginIdx + 1, workingCopyCommaIdx));
    workingCopyRange = Integer.parseInt(
        chunkHeader.substring(workingCopyCommaIdx + 1, workingCopyEndIdx));
    otherCopyBegin = Integer.parseInt(
        chunkHeader.substring(otherCopyBeginIdx + 1, otherCopyCommaIdx));
    otherCopyRange = Integer.parseInt(
        chunkHeader.substring(otherCopyCommaIdx + 1, otherCopyEndIdx));
    String chunkLine;
    SrcFileItem.LineCoverageData lCoverageData = null;
    int workingCopyLine = workingCopyBegin;
    int otherCopyLine = otherCopyBegin;
    final HTMLTable.IRow chunkRow = table.newTitleRow();
    final HTMLTable.ICell chunkCell = chunkRow.newCell();
    chunkCell.setColspan(2);
    chunkCell.setText("Lines " + workingCopyBegin + " - " +
        String.valueOf(workingCopyLine + workingCopyRange), true);
    for(int i = 1; i < diffChunk.size(); i++)
    {
      chunkLine = diffChunk.get(i);
      if(lineCoverageMap != null)
      {
        lCoverageData = (SrcFileItem.LineCoverageData) lineCoverageMap.get (workingCopyLine);
      }
      final HTMLTable.IRow srcRow = table.newRow();
      final HTMLTable.ICell lineNumCell = srcRow.newCell();
      final HTMLTable.ICell lineTxtCell = srcRow.newCell();
      if (chunkLine.length() == 0) {
        lineTxtCell.setText(" ", true);
      } else {
        lineTxtCell.setText(chunkLine.substring(1), true);
      }
      //This line is either a modified line or a unchanged line
      if(!chunkLine.startsWith(otherCopyFlag))
      {
        lineNumCell.setText(String.valueOf(workingCopyLine), true);
        //Determine if this line is a modified line or a unchange line
        if(chunkLine.startsWith(workingCopyFlag))
        {
          lineNumCell.setClass("dm");
          modCoverage[MOD_LINES] ++;
          if(lCoverageData != null)
          {
            modCoverage[MOD_EXE_LINES] ++;
            switch(lCoverageData.m_coverageStatus)
            {
              case SrcFileItem.LineCoverageData.LINE_COVERAGE_ZERO:
                lineTxtCell.setClass ("cz");
                break;
              case SrcFileItem.LineCoverageData.LINE_COVERAGE_PARTIAL:
                lineTxtCell.setClass ("cp");
                modCoverage[COVERED_MOD_EXE_LINES] += 0.5;
                break;
              case SrcFileItem.LineCoverageData.LINE_COVERAGE_COMPLETE:
                lineTxtCell.setClass ("cc");
                modCoverage[COVERED_MOD_EXE_LINES] ++;
                break;
              default:
            }
          }
        }
        else
        {
          lineNumCell.setClass("ds");
        }
      }
      else
      {
        lineNumCell.setClass("dd");
        lineNumCell.setText(String.valueOf(otherCopyLine), true);
        lineTxtCell.setClass("ddt");
        modCoverage[DEL_LINES] ++;
      }
      if(!chunkLine.startsWith(otherCopyFlag))
      {
        workingCopyLine++;
      }
      if(!chunkLine.startsWith(workingCopyFlag))
      {
        otherCopyLine++;
      }
    }
    return modCoverage;
  }
  private String parseJavaPackage(FileInputStream srcFile)
      throws IOException {
    BufferedReader srcFileReader = new BufferedReader(
        new InputStreamReader(srcFile));
    String line = srcFileReader.readLine();
    while(line != null)
    {
      int beginIdx = line.indexOf("package");
      if(beginIdx > -1)
      {
        int endIdx = line.indexOf(";", beginIdx);
        if(endIdx > -1)
        {
          return line.substring(beginIdx + 7, endIdx).trim();
        }
      }
      line = srcFileReader.readLine();
    }
    return null;
  }
  private  SrcFileItem getEmmaSrcItem(IItem rootItem,
                                      String srcPackageName,
                                      String srcFileName)
  {
    if(rootItem == null || srcPackageName == null || srcFileName == null)
    {
      return null;
    }
    for(Iterator packages = rootItem.getChildren(); packages.hasNext();)
    {
      IItem packageItem = (IItem)packages.next();
      if(packageItem.getName().equals(srcPackageName))
      {
        for(Iterator sources = packageItem.getChildren(); sources.hasNext();)
        {
          SrcFileItem sourceItem = (SrcFileItem)sources.next();
          if(sourceItem.getName().equals(srcFileName))
          {
            return sourceItem;
          }
        }
      }
    }
    return null;
  }
  private void addHeaderRow (final IItem item, final HTMLTable table, boolean includeName)
  {
    // header row:
    final HTMLTable.IRow header = table.newTitleRow ();
    if(includeName)
    {
      final HTMLTable.ICell nameCell = header.newCell();
      nameCell.setText("File", true);
    }
    for (int c = 1; c <= 4; ++ c)
    {
      IItemAttribute attr = null;
      if(item != null)
      {
        attr = item.getAttribute (c, 0);
      }
      if (attr != null)
      {
        final HTMLTable.ICell cell = header.newCell ();
        cell.setText (attr.getName (), true);
      }
      else
      {
        final HTMLTable.ICell cell = header.newCell ();
        cell.setText (" ", true);
      }
    }
    if(item != null)
    {
      final HTMLTable.ICell cell = header.newCell();
      cell.setText("mod lines, %", true);
    }
    else
    {
      final HTMLTable.ICell cell = header.newCell ();
      cell.setText (" ", true);
    }
  }
  /*
     * No header row, just data rows.
     */
  private void addItemRow (final String fileName,
                           final IItem item,
                           final Double[] modCoverage,
                           final boolean odd,
                           final HTMLTable table,
                           final String nameHREF,
                           final boolean anchor,
                           final boolean includeName)
  {
    final HTMLTable.IRow row = table.newRow ();
    if (odd) row.setClass ("o");
    if(includeName)
    {
      final HTMLTable.ICell nameCell = row.newCell();
      if(nameHREF != null)
      {
        final String fullHREFName = anchor ? "#".concat (nameHREF) : nameHREF;
        nameCell.add(new HyperRef(fullHREFName, fileName, true));
      }
      else
      {
        nameCell.setText(fileName, true);
      }
    }
    final StringBuffer buf = new StringBuffer (11);
    for (int c = 1; c <=4; ++ c)
    {
      IItemAttribute attr = null;
      if(item != null)
      {
        attr = item.getAttribute (c, 0);
      }
      if (attr != null)
      {
        final HTMLTable.ICell cell = row.newCell ();
        //final boolean fail = (m_metrics [attrID] > 0) && ! attr.passes (item, m_metrics [attrID]);
        buf.setLength (0);
        attr.format (item, buf);
        cell.setText (buf.toString (), true);
        //if (fail) cell.setClass (CSS_DATA_HIGHLIGHT);
      }
      else
      {
        final HTMLTable.ICell cell = row.newCell ();
        cell.setText (" ", true);
      }
    }
    if(item != null && modCoverage != null)
    {
      String modCoverageStr = "";
      if(modCoverage[1] > 0)
      {
        modCoverageStr = String.format("%d%% (%.1f/%.1f)",
            (int)(modCoverage[COVERED_MOD_EXE_LINES]/modCoverage[MOD_EXE_LINES]*100),
            modCoverage[COVERED_MOD_EXE_LINES], modCoverage[MOD_EXE_LINES]);
      }
      else
      {
        modCoverageStr = String.format("%d%% (%.1f/%.1f)", 100,
            modCoverage[COVERED_MOD_EXE_LINES],
            modCoverage[MOD_EXE_LINES]);
      }
      final HTMLTable.ICell cell = row.newCell();
      cell.setText(modCoverageStr, true);
    }
    else
    {
      final HTMLTable.ICell cell = row.newCell ();
      cell.setText (" ", true);
    }
  }
  // Enable this with -Dtest.diff.verbose=true from the commandline
  private void verboseOut(Object msg)
  {
    if (verbose)
    {
      System.out.println(msg.toString());
    }
  }
}
sdk/build-tools/org/opends/build/tools/CreateVersionString.java
New file
@@ -0,0 +1,98 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 */
package org.opends.build.tools;
import java.text.DecimalFormat;
import org.apache.tools.ant.Task;
/**
 * This class provides an implemenation of an Ant task that may be used to
 * construct the full version number string that the Directory Server should
 * use.  The value of the version number string will be stored in an Ant
 * property.
 */
public class CreateVersionString
       extends Task
{
  // The name of the property in which the revision number should be set.
  private String propertyName = null;
  /**
   * Specifies the name of the Ant property into which the Subversion revision
   * number will be stored.
   *
   * @param  propertyName  The name of the Ant property into which the
   *                       Subversion revision number will be stored.
   */
  public void setProperty(String propertyName)
  {
    this.propertyName = propertyName;
  }
  /**
   * Performs the appropriate processing needed for this task.  In this case,
   * it uses SVNKit to identify the current revision number for the local
   * workspace and store it in a specified property.
   */
  @Override()
  public void execute()
  {
    StringBuilder versionString = new StringBuilder();
    versionString.append(getProject().getProperty("MAJOR_VERSION"));
    versionString.append(".");
    versionString.append(getProject().getProperty("MINOR_VERSION"));
    versionString.append(".");
    versionString.append(getProject().getProperty("POINT_VERSION"));
    String versionQualifier = getProject().getProperty("VERSION_QUALIFIER");
    versionString.append(versionQualifier);
    try
    {
      int buildNumber =
           Integer.parseInt(getProject().getProperty("BUILD_NUMBER"));
      if (buildNumber > 0)
      {
        versionString.append("-build");
        versionString.append(new DecimalFormat("000").format(buildNumber));
      }
    } catch (NumberFormatException nfe) {}
    getProject().setNewProperty(propertyName, versionString.toString());
  }
}
sdk/build-tools/org/opends/build/tools/GenerateMessageFile.java
New file
@@ -0,0 +1,995 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 */
package org.opends.build.tools;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Location;
import static org.opends.build.tools.Utilities.*;
import org.opends.messages.Category;
import org.opends.messages.Severity;
import org.opends.messages.MessageDescriptor;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Properties;
import java.util.List;
import java.util.ArrayList;
import java.util.UnknownFormatConversionException;
import java.util.Calendar;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import java.util.HashSet;
import java.util.Set;
import java.util.EnumSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * Generates a Java class containing representations of messages
 * found in a properties file.
 */
public class GenerateMessageFile extends Task {
  private File source;
  private File dest;
  private boolean overwrite;
  static private final String MESSAGES_FILE_STUB =
          "resource/Messages.java.stub";
  /*
   * The registry filename is the result of the concatenation of the
   * location of where the source are generated, the package name and the
   * DESCRIPTORS_REG value.
   */
  static private String REGISTRY_FILE_NAME;
  static private final String DESCRIPTORS_REG = "descriptors.reg";
  /**
   * Used to set a category for all messages in the property file.
   * If set, the category for each message need not be encoded in
   * the message's property file key.
   */
  static private final String GLOBAL_CATEGORY = "global.category";
  /**
   * Used to set a severity for all messages in the property file.
   * If set, the severity for each message need not be encoded in
   * the message's property file key.
   */
  static private final String GLOBAL_SEVERITY = "global.severity";
  /**
   * Used to set a category mask for all messages in the property
   * file.  If set, the category will automatically be assigned
   * USER_DEFINED and the value of <code>GLOBAL_CATEGORY</code>
   * will be ignored.
   */
  static private final String GLOBAL_CATEGORY_MASK = "global.mask";
  /**
   * When true generates messages that have no ordinals.
   */
  static private final String GLOBAL_ORDINAL = "global.ordinal";
  /**
   * When true and if the Java Web Start property is set use the class loader of
   * the jar where the MessageDescriptor is contained to retrieve the
   * ResourceBundle.
   */
  static private final String GLOBAL_USE_MESSAGE_JAR_IF_WEBSTART =
    "global.use.message.jar.if.webstart";
  static private final Set<String> DIRECTIVE_PROPERTIES = new HashSet<String>();
  static {
    DIRECTIVE_PROPERTIES.add(GLOBAL_CATEGORY);
    DIRECTIVE_PROPERTIES.add(GLOBAL_CATEGORY_MASK);
    DIRECTIVE_PROPERTIES.add(GLOBAL_SEVERITY);
    DIRECTIVE_PROPERTIES.add(GLOBAL_ORDINAL);
    DIRECTIVE_PROPERTIES.add(GLOBAL_USE_MESSAGE_JAR_IF_WEBSTART);
  }
  static private final String SPECIFIER_REGEX =
          "%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])";
  private final Pattern SPECIFIER_PATTERN = Pattern.compile(SPECIFIER_REGEX);
  /**
   * Message giving formatting rules for string keys.
   */
  static public String KEY_FORM_MSG;
  static {
    KEY_FORM_MSG = new StringBuilder()
            .append(".\n\nOpenDS message property keys must be of the form\n\n")
            .append("\t\'[CATEGORY]_[SEVERITY]_[DESCRIPTION]_[ORDINAL]\'\n\n")
            .append("where\n\n")
            .append("CATEGORY is one of ")
            .append(EnumSet.allOf(Category.class))
            .append("\n\nSEVERITY is one of ")
            .append(Severity.getPropertyKeyFormSet().toString())
            .append("\n\nDESCRIPTION is a descriptive string composed ")
            .append("of uppercase character, digits and underscores ")
            .append("describing the purpose of the message ")
            .append("\n\nORDINAL is an integer between 0 and 65535 that is ")
            .append("unique to other messages defined in this file.\n\n")
            .append("You can relax the mandate for including the CATEGORY, ")
            .append("SEVERITY, and/or ORDINAL by including one or more ")
            .append("of the following respective property directives in your ")
            .append("properties file:  ")
            .append(GLOBAL_CATEGORY)
            .append(", ")
            .append(GLOBAL_SEVERITY)
            .append(", ")
            .append(GLOBAL_ORDINAL)
            .append("and setting their value appropriately.")
            .toString();
  }
  /*
   * ISO_LANGUAGES contains all official supported languages for i18n
   */
  private static final List<String> ISO_LANGUAGES =
                                        Arrays.asList(Locale.getISOLanguages());
  /*
   * ISO_COUNTRIES contains all official supported countries for i18n
   */
  private static final List<String> ISO_COUNTRIES =
                                        Arrays.asList(Locale.getISOCountries());
  /*
   * A Pattern instance that matches "<label>_<language>_<country>.properties"
   * where <label> can be anything including '_'
   *       <language> a two characters code contained in the ISO_LANGUAGES list
   *       <country> a two characters code contained in the ISO_COUNTRIES list
   */
  private static final Pattern LANGUAGE_COUNTRY_MATCHER =
                       Pattern.compile("(.*)_([a-z]{2})_([A-Z]{2}).properties");
  /*
   * A Pattern instance that matches "<label>_<language>.properties"
   * where <label> and <language> have same definition as above.
   */
  private static final Pattern LANGUAGE_MATCHER =
                       Pattern.compile("(.*)_([a-z]{2}).properties");
  /**
   * Representation of a format specifier (for example %s).
   */
  private class FormatSpecifier {
    private String[] sa;
    /**
     * Creates a new specifier.
     * @param sa specifier components
     */
    FormatSpecifier(String[] sa) {
      this.sa = sa;
    }
    /**
     * Indicates whether or not the specifier uses arguement
     * indexes (for example 2$).
     * @return boolean true if this specifier uses indexing
     */
    public boolean specifiesArgumentIndex() {
      return this.sa[0] != null;
    }
    /**
     * Returns a java class associated with a particular formatter
     * based on the conversion type of the specifier.
     * @return Class for representing the type of arguement used
     *         as a replacement for this specifier.
     */
    public Class getSimpleConversionClass() {
      Class c = null;
      String sa4 = sa[4] != null ? sa[4].toLowerCase() : null;
      String sa5 = sa[5] != null ? sa[5].toLowerCase() : null;
      if ("t".equals(sa4)) {
        c = Calendar.class;
      } else if (
              "b".equals(sa5)) {
        c = Boolean.class;
      } else if (
              "h".equals(sa5)) {
        c = Integer.class;
      } else if (
              "s".equals(sa5)) {
        c = CharSequence.class;
      } else if (
              "c".equals(sa5)) {
        c = Character.class;
      } else if (
              "d".equals(sa5) ||
              "o".equals(sa5) ||
              "x".equals(sa5) ||
              "e".equals(sa5) ||
              "f".equals(sa5) ||
              "g".equals(sa5) ||
              "a".equals(sa5)) {
        c = Number.class;
      } else if (
              "n".equals(sa5) ||
              "%".equals(sa5)) {
        // ignore literals
      }
      return c;
    }
  }
  /**
   * Represents a message to be written into the messages files.
   */
  private class MessageDescriptorDeclaration {
    private MessagePropertyKey key;
    private String formatString;
    private List<FormatSpecifier> specifiers;
    private List<Class> classTypes;
    private String[] constructorArgs;
    /**
     * Creates a parameterized instance.
     * @param key of the message
     * @param formatString of the message
     */
    public MessageDescriptorDeclaration(MessagePropertyKey key,
                                     String formatString) {
      this.key = key;
      this.formatString = formatString;
      this.specifiers = parse(formatString);
      this.classTypes = new ArrayList<Class>();
      for (FormatSpecifier f : specifiers) {
        Class c = f.getSimpleConversionClass();
        if (c != null) {
          classTypes.add(c);
        }
      }
    }
    /**
     * Gets the name of the Java class that will be used to represent
     * this message's type.
     * @return String representing the Java class name
     */
    public String getDescriptorClassDeclaration() {
      StringBuilder sb = new StringBuilder();
      if (useGenericMessageTypeClass()) {
        sb.append(getShortClassName(MessageDescriptor.class));
        sb.append(".");
        sb.append(MessageDescriptor.DESCRIPTOR_CLASS_BASE_NAME);
        sb.append("N");
      } else {
        sb.append(getShortClassName(MessageDescriptor.class));
        sb.append(".");
        sb.append(MessageDescriptor.DESCRIPTOR_CLASS_BASE_NAME);
        sb.append(classTypes.size());
        sb.append(getClassTypeVariables());
      }
      return sb.toString();
    }
    /**
     * Gets a string representing the message type class' variable
     * information (for example '<String,Integer>') that is based on
     * the type of arguments specified  by the specifiers in this message.
     * @return String representing the message type class parameters
     */
    public String getClassTypeVariables() {
      StringBuilder sb = new StringBuilder();
      if (classTypes.size() > 0) {
        sb.append("<");
        for (int i = 0; i < classTypes.size(); i++) {
          Class c = classTypes.get(i);
          if (c != null) {
            sb.append(getShortClassName(c));
            if (i < classTypes.size() - 1) {
              sb.append(",");
            }
          }
        }
        sb.append(">");
      }
      return sb.toString();
    }
    /**
     * Gets the javadoc comments that will appear above the messages declaration
     * in the messages file.
     * @return String comment
     */
    public String getComment() {
      StringBuilder sb = new StringBuilder();
      sb.append(indent(1)).append("/**").append(EOL);
      // Unwrapped so that you can search through the descriptor
      // file for a message and not have to worry about line breaks
      String ws = formatString; // wrapText(formatString, 70);
      String[] sa = ws.split(EOL);
      for (String s : sa) {
        sb.append(indent(1)).append(" * ").append(s).append(EOL);
      }
      sb.append(indent(1)).append(" */").append(EOL);
      return sb.toString();
    }
    /**
     * Sets the arguments that will be supplied in the declaration
     * of the message.
     * @param s array of string arguments that will be passed
     *        in the constructor
     */
    public void setConstructorArguments(String... s) {
      this.constructorArgs = s;
    }
    /**
     * {@inheritDoc}
     */
    public String toString() {
      StringBuilder sb = new StringBuilder();
      sb.append(getComment());
      sb.append(indent(1));
      sb.append("public static final ");
      sb.append(getDescriptorClassDeclaration());
      sb.append(" ");
      sb.append(key.getMessageDescriptorName());
      sb.append(" =");
      sb.append(EOL);
      sb.append(indent(5));
      sb.append("new ");
      sb.append(getDescriptorClassDeclaration());
      sb.append("(");
      if (constructorArgs != null) {
        for (int i = 0; i < constructorArgs.length; i++) {
          sb.append(constructorArgs[i]);
          if (i < constructorArgs.length - 1) {
            sb.append(",");
          }
        }
        sb.append(", ");
      }
      sb.append("getClassLoader()");
      sb.append(");");
      return sb.toString();
    }
    /**
     * Indicates whether the generic message type class should
     * be used.  In general this is when a format specifier is
     * more complicated than we support or when the number of
     * arguments exceeeds the number of specific message type
     * classes (MessageType0, MessageType1 ...) that are defined.
     * @return boolean indicating
     */
    private boolean useGenericMessageTypeClass() {
      if (specifiers.size() > MessageDescriptor.DESCRIPTOR_MAX_ARG_HANDLER) {
        return true;
      } else if (specifiers != null) {
        for (FormatSpecifier s : specifiers) {
          if (s.specifiesArgumentIndex()) {
            return true;
          }
        }
      }
      return false;
    }
    /**
     * Look for format specifiers in the format string.
     * @param s format string
     * @return list of format specifiers
     */
    private List<FormatSpecifier> parse(String s) {
      List<FormatSpecifier> sl = new ArrayList<FormatSpecifier>();
      Matcher m = SPECIFIER_PATTERN.matcher(s);
      int i = 0;
      while (i < s.length()) {
        if (m.find(i)) {
          // Anything between the start of the string and the beginning
          // of the format specifier is either fixed text or contains
          // an invalid format string.
          if (m.start() != i) {
            // Make sure we didn't miss any invalid format specifiers
            checkText(s.substring(i, m.start()));
            // Assume previous characters were fixed text
            //al.add(new FixedString(s.substring(i, m.start())));
          }
          // Expect 6 groups in regular expression
          String[] sa = new String[6];
          for (int j = 0; j < m.groupCount(); j++) {
            sa[j] = m.group(j + 1);
          }
          sl.add(new FormatSpecifier(sa));
          i = m.end();
        } else {
          // No more valid format specifiers.  Check for possible invalid
          // format specifiers.
          checkText(s.substring(i));
          // The rest of the string is fixed text
          //al.add(new FixedString(s.substring(i)));
          break;
        }
      }
      return sl;
    }
    private void checkText(String s) {
      int idx;
      // If there are any '%' in the given string, we got a bad format
      // specifier.
      if ((idx = s.indexOf('%')) != -1) {
        char c = (idx > s.length() - 2 ? '%' : s.charAt(idx + 1));
        throw new UnknownFormatConversionException(String.valueOf(c));
      }
    }
  }
  /**
   * Sets the source of the messages.
   * @param source File representing the properties
   *        file containing messages
   */
  public void setSourceProps(File source) {
    this.source = source;
  }
  /**
   * Sets the file that will be generated containing
   * declarations of messages from <code>source</code>.
   * @param dest File destination
   */
  public void setDestJava(File dest) {
    this.dest = dest;
    /*
     * Set the descriptors.reg pathname to the same directory as the one used
     * to generate files and ensure all messages are generated in one place.
     */
    String projectBase = null;
    try {
      projectBase = getProject().getBaseDir().getCanonicalPath();
    } catch( java.io.IOException e) {
      throw new BuildException("Error processing " + dest +
            ": unable to retrieve project's directory of ant's project (" +
            e + ")");
    }
    String registry = dest.getAbsolutePath();
    // strip project directory prefix and replace properties filename with
    // $DESCRIPTORS_REG
    registry = registry.substring(projectBase.length()+1,
                                 registry.lastIndexOf(File.separator)+1)
                       .concat(DESCRIPTORS_REG);
    if ( REGISTRY_FILE_NAME == null ) {
      REGISTRY_FILE_NAME = registry;
    } else {
      if ( ! REGISTRY_FILE_NAME.equals(registry) ) {
        // multiple messages are generated in several packages
        StringBuilder sb = new StringBuilder();
        // full pathname of $REGISTRY_FILE_NAME
        sb.append(projectBase)
          .append(File.separator)
          .append(REGISTRY_FILE_NAME);
        // change from generated directory to properties files directory
        sb.replace(0,
                   getProject().getProperty("msg.javagen.dir").length(),
                   getProject().getProperty("msg.dir"));
        // replace properties filename with source filename
        sb.replace(sb.lastIndexOf(File.separator)+1,
                   sb.length(),
                   source.getName());
        throw new BuildException("Error processing " + dest +
              ": all messages must be located in the same package thus " +
              "name of the source file should be " + sb);
      }
    }
  }
  /**
   * Indicates when true that an existing destination
   * file will be overwritten.
   * @param o boolean where true means overwrite
   */
  public void setOverwrite(boolean o) {
    this.overwrite = o;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public void execute() throws BuildException {
    if ( this.dest == null ) {
      // this is an example-plugin call:
      // - check the source file is not a localization
      // - guess the destination filename from source filename
      String sourcefilename = source.getAbsolutePath();
      int filenameIndex = sourcefilename.lastIndexOf(File.separator)+1;
      String pathname = sourcefilename.substring(0, filenameIndex);
      String filename = sourcefilename.substring(filenameIndex);
      /*
       * Make sure only <label>.properties are generated thus avoiding to
       * generate messages for localized properties files.
       */
      Matcher matcher = LANGUAGE_COUNTRY_MATCHER.matcher(filename);
      if ( matcher.find() ) {
        if ( ISO_LANGUAGES.contains(matcher.group(2))
          && ISO_COUNTRIES.contains(matcher.group(3)) ) {
          // do not generate message for <label>_<language>_<country>.properties
          return;
        }
      }
      matcher = LANGUAGE_MATCHER.matcher(filename);
      if ( matcher.find() ) {
        if ( ISO_LANGUAGES.contains(matcher.group(2)) ) {
          // do not generate message for <label>_<language>.properties
          return;
        }
      }
      // filename without ".properties"
      filename = filename.substring(0, filename.length()-11);
      // change to src-generated directory keeping package name
      pathname = pathname.replace(getProject().getProperty("msg.dir"),
                                  getProject().getProperty("msg.javagen.dir"));
      // append characters from filename to pathname starting with an uppercase
      // letter, ignoring '_' and uppering all characters prefixed with "_"
      StringBuilder sb = new StringBuilder(pathname);
      boolean upperCaseNextChar = true;
      for(char c : filename.toCharArray()) {
        if ( c == '_' ) {
          upperCaseNextChar = true;
          continue;
        }
        if ( upperCaseNextChar ) {
          sb.append(Character.toUpperCase(c));
          upperCaseNextChar = false;
        } else {
          sb.append(c);
        }
      }
      sb.append("Messages.java");
      setDestJava(new File(sb.toString()));
    }
    BufferedReader stubReader = null;
    PrintWriter destWriter = null;
    try {
      // Decide whether to generate messages based on modification
      // times and print status messages.
      if (!source.exists()) {
        throw new BuildException("file " + source.getName() +
                " does not exist");
      }
      if (dest.exists()) {
        if (this.overwrite || source.lastModified() > dest.lastModified()) {
          dest.delete();
          log("Regenerating " + dest.getName() + " from " + source.getName());
        } else {
          log(dest.getName() + " is up to date");
          return;
        }
      } else {
        File javaGenDir = dest.getParentFile();
        if (!javaGenDir.exists()) {
          javaGenDir.mkdirs();
        }
        log("Generating " + dest.getName() + " from " + source.getName());
      }
      stubReader = new BufferedReader(new InputStreamReader(getStubFile(),
                                                            "UTF-8"));
      destWriter = new PrintWriter(dest, "UTF-8");
      String stubLine;
      Properties properties = new Properties();
      properties.load(new FileInputStream(source));
      while (null != (stubLine = stubReader.readLine())) {
        if (stubLine.contains("${MESSAGES}")) {
          Integer globalOrdinal = null;
          String go = properties.getProperty(GLOBAL_ORDINAL);
          if (go != null) {
            globalOrdinal = new Integer(go);
          }
          // Determine the value of the global category/mask if set
          Integer  globalMask = null;
          Category globalCategory = null;
          String gms = properties.getProperty(GLOBAL_CATEGORY_MASK);
          if (gms != null) {
            globalMask = Integer.parseInt(gms);
            globalCategory = Category.USER_DEFINED;
          } else {
            String gcs = properties.getProperty(GLOBAL_CATEGORY);
            if (gcs != null) {
              globalCategory = Category.valueOf(gcs);
            }
          }
          // Determine the value of the global severity
          Severity globalSeverity = null;
          String gss = properties.getProperty(GLOBAL_SEVERITY);
          if (gss != null) {
            globalSeverity = Severity.parseString(gss);
          }
          Map<MessagePropertyKey,String> keyMap =
                  new TreeMap<MessagePropertyKey,String>();
          for (Object propO : properties.keySet()) {
            String propKey = propO.toString();
            try {
              if (!DIRECTIVE_PROPERTIES.contains(propKey)) {
                MessagePropertyKey key =
                        MessagePropertyKey.parseString(
                                propKey,
                                globalCategory == null,
                                globalSeverity == null,
                                globalOrdinal == null);
                String formatString = properties.getProperty(propKey);
                keyMap.put(key, formatString);
              }
            } catch (IllegalArgumentException iae) {
              throw new BuildException(
                      "ERROR: invalid property key " + propKey +
                      ": " + iae.getMessage() +
                      KEY_FORM_MSG);
            }
          }
          int usesOfGenericDescriptor = 0;
          Category firstCategory = null;
          Set<Integer> usedOrdinals = new HashSet<Integer>();
          for (MessagePropertyKey key : keyMap.keySet()) {
            String formatString = keyMap.get(key);
            MessageDescriptorDeclaration message =
                    new MessageDescriptorDeclaration(key, formatString);
            Category c = (globalCategory != null ?
                    globalCategory : key.getCategory());
            // Check that this category is the same as all the
            // others in this file.  Maybe this should be an error?
            if (firstCategory != null) {
              if (!firstCategory.equals(c)) {
                log("WARNING: multiple categories defined in " + source);
              }
            } else {
              firstCategory = c;
            }
            Severity s = (globalSeverity != null ?
                    globalSeverity : key.getSeverity());
            if (c == null) {
              throw new BuildException(
                      "No category could be assigned to message " +
                              key + ".  The category " +
                              "must either be encoded in the property key or " +
                              "or must be set by including the property " +
                              GLOBAL_CATEGORY + " in the properties file" +
                              KEY_FORM_MSG);
            }
            if (c == null) {
              throw new BuildException(
                      "No severity could be assigned to message " +
                              key + ".  The severity " +
                              "must either be encoded in the property key or " +
                              "or must be set by including the property " +
                              GLOBAL_SEVERITY + " in the properties file" +
                              KEY_FORM_MSG);
            }
            if (globalOrdinal == null) {
              Integer ordinal = key.getOrdinal();
              if (usedOrdinals.contains(ordinal)) {
                throw new BuildException(
                        "The ordinal value \'" + ordinal + "\' in key " +
                                key + " has been previously defined in " +
                                source + KEY_FORM_MSG);
              } else {
                usedOrdinals.add(ordinal);
              }
            }
            message.setConstructorArguments(
                    "BASE",
                    quote(key.toString()),
                    globalMask != null ? globalMask.toString() : c.name(),
                    s.name(),
                    globalOrdinal != null ?
                            globalOrdinal.toString() :
                            key.getOrdinal().toString()
            );
            destWriter.println(message.toString());
            destWriter.println();
            // Keep track of when we use the generic descriptor
            // so that we can report it later
            if (message.useGenericMessageTypeClass()) {
              usesOfGenericDescriptor++;
            }
          }
          log("  Message Generated:" + keyMap.size(), Project.MSG_VERBOSE);
          log("  MessageDescriptor.ArgN:" + usesOfGenericDescriptor,
                  Project.MSG_VERBOSE);
        } else {
          stubLine = stubLine.replace("${PACKAGE}", getPackage());
          stubLine = stubLine.replace("${CLASS_NAME}",
                  dest.getName().substring(0, dest.getName().length() -
                          ".java".length()));
          stubLine = stubLine.replace("${BASE}", getBase());
          String useMessageJarIfWebstart =
            properties.getProperty(GLOBAL_USE_MESSAGE_JAR_IF_WEBSTART);
          if ((useMessageJarIfWebstart != null) &&
              ("true".equalsIgnoreCase(useMessageJarIfWebstart) ||
              "on".equalsIgnoreCase(useMessageJarIfWebstart) ||
              "true".equalsIgnoreCase(useMessageJarIfWebstart)))
          {
            useMessageJarIfWebstart = "true";
          }
          else
          {
            useMessageJarIfWebstart = "false";
          }
          stubLine = stubLine.replace("${USE_MESSAGE_JAR_IF_WEBSTART}",
              useMessageJarIfWebstart);
          destWriter.println(stubLine);
        }
      }
      registerMessageDescriptor(getMessageDescriptorFullClassName());
      stubReader.close();
      destWriter.close();
    } catch (Exception e) {
      // Don't leave a malformed file laying around. Delete
      // it so it will be forced to be regenerated.
      if (dest.exists()) {
        dest.deleteOnExit();
      }
      e.printStackTrace();
      throw new BuildException("Error processing " + source +
              ":  " + e.getMessage());
    } finally {
      if (stubReader != null) {
        try {
          stubReader.close();
        } catch (Exception e){
          // ignore
        }
      }
      if (destWriter != null) {
        try {
          destWriter.close();
        } catch (Exception e){
          // ignore
        }
      }
    }
  }
  private String getMessageDescriptorFullClassName() {
    return getPackage() + "." + getMessageDescriptorClassName();
  }
  private String getMessageDescriptorClassName() {
    return dest.getName().substring(
            0, dest.getName().length() - ".java".length());
  }
  private String getBase() {
    String srcPath = unixifyPath(source.getAbsolutePath());
    String base = srcPath.substring(srcPath.lastIndexOf("/") + 1,
                                    srcPath.length() - ".properties".length());
    return base;
  }
  private String getPackage() {
    String destPath = unixifyPath(dest.getAbsolutePath());
    String msgJavaGenDir = unixifyPath(
                                   getProject().getProperty("msg.javagen.dir"));
    String c = destPath.substring(msgJavaGenDir.length()+1);
    c = c.replace('/', '.');
    c = c.substring(0, c.lastIndexOf(".")); // strip .java
    c = c.substring(0, c.lastIndexOf(".")); // strip class name
    return c;
  }
  static private String indent(int indent) {
    char[] blankArray = new char[2 * indent];
    Arrays.fill(blankArray, ' ');
    return new String(blankArray);
  }
  static private String quote(String s) {
    return new StringBuilder()
            .append("\"")
            .append(s)
            .append("\"")
            .toString();
  }
  static private String getShortClassName(Class c) {
    String name;
    String fqName = c.getName();
    int i = fqName.lastIndexOf('.');
    if (i > 0) {
      name = fqName.substring(i + 1);
    } else {
      name = fqName;
    }
    return name;
  }
  /**
   * Writes a record in the messages registry for the specifed
   * class name.
   * @param descClassName name of the message descriptor class
   * @return true if the class was acutally added to the registry;
   *         false indicates that the class was already present.
   * @throws IOException if there is a problem with the file I/O
   */
  private boolean registerMessageDescriptor(String descClassName)
          throws IOException
  {
    boolean classAdded = false;
    File registry = getRegistryFile();
    if (!isDescriptorRegistered(descClassName)) {
      FileOutputStream file = new FileOutputStream(registry,true);
      DataOutputStream out   = new DataOutputStream(file);
      out.writeBytes(descClassName);
      out.writeBytes("\n");
      out.flush();
      out.close();
    }
    return classAdded;
  }
  private boolean isDescriptorRegistered(String descClassName)
          throws IOException
  {
    boolean isRegistered = false;
    BufferedReader reader = new BufferedReader(
            new FileReader(getRegistryFile()));
    String line;
    while(null != (line = reader.readLine())) {
      if (line.trim().equals(descClassName.trim())) {
        isRegistered = true;
        break;
      }
    }
    return isRegistered;
  }
  private File getRegistryFile() throws IOException {
    File registry = new File(getProjectBase(), REGISTRY_FILE_NAME);
    if (!registry.exists()) {
      File parent = registry.getParentFile();
      if (!parent.exists()) {
        parent.mkdirs();
      }
      registry.createNewFile();
    }
    return registry;
  }
  private File getProjectBase() {
    File projectBase;
    // Get the path to build.xml and return the parent
    // directory else just return the working directory.
    Location l = getLocation();
    String fileName = l.getFileName();
    if (fileName != null) {
      File f = new File(fileName);
      projectBase = f.getParentFile();
    } else {
      projectBase = new File(System.getProperty("user.dir"));
    }
    return projectBase;
  }
  private String unixifyPath(String path) {
    return path.replace("\\", "/");
  }
  /*
   * Returns the stub file ("resource/Messages.java.stub") from the appropriate
   * location: ant or jar file.
   */
  private InputStream getStubFile() {
    InputStream result = null;
    File stub = new File(getProjectBase(), MESSAGES_FILE_STUB);
    if ( stub.exists() ) {
      // this is the OpenDS's ant project calling
      // Stub is located at OPENDS_ROOT/resource/Messages.java.stub
      try {
        result = new FileInputStream(stub);
      } catch (FileNotFoundException e) {
        // should neven happen
        throw new BuildException("Unable to load template " +
              MESSAGES_FILE_STUB + ":  " + e.getMessage());
      }
    } else {
      // this is the example plugin's ant project calling
      // Stub is located at build-tools.jar:resource/Messages.java.stub
      result = getClass().getResourceAsStream(MESSAGES_FILE_STUB);
    }
    return result;
  }
  /**
   * For testing.
   * @param args from command line
   */
  public static void main(String[] args) {
    File source = new File("src/messages/messages/tools.properties");
    File dest = new File("/tmp/org/opends/XXX.java");
    GenerateMessageFile gmf = new GenerateMessageFile();
    gmf.setOverwrite(true);
    gmf.setDestJava(dest);
    gmf.setSourceProps(source);
    gmf.execute();
  }
}
sdk/build-tools/org/opends/build/tools/GenerateRpm.java
New file
@@ -0,0 +1,251 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2007-2008 Sun Microsystems, Inc.
 */
package org.opends.build.tools;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.BuildException;
import static org.opends.build.tools.Utilities.*;
import java.io.File;
import java.io.PrintWriter;
/**
 * Generates an RPM spec file.
 */
public class GenerateRpm extends Task {
  private File topDir;
  private String topDirAbsolutePath;
  private String sourceDirName;
  private File destFile;
  private String prefix;
  private String version;
  private String release;
  private boolean overwrite;
  private StringBuilder sb;
  private final String filePrefix="%{_prefix}";
  private final String dirPrefix="%dir %{_prefix}";
  /**
   * Sets the top directory for the rpm build.
   * @param topDir File representing top directory for rpm build directory
   */
  public void setTopDir(File topDir) {
    this.topDir = topDir;
    topDirAbsolutePath = topDir.getAbsolutePath();
  }
  /**
   * Sets the prefix for the RPM.
   * @param prefix Used for package relocation
   */
  public void setPrefix(String prefix) {
    this.prefix = prefix;
  }
  /**
   * Sets the name of the source directory.
   * @param sourceDirName name of the source directory.
   */
  public void setSourceDirName(String sourceDirName) {
    this.sourceDirName = sourceDirName;
  }
  /**
   * Sets the RPM spec file that will be generated.
   * @param dest The spec file
   *
   */
  public void setSpecFileName(File dest) {
    this.destFile = dest;
  }
  /**
   * Sets the version number.
   * @param version The version number
   *
   */
  public void setVersion(String version) {
    this.version = version;
  }
  /**
   * Sets the release number.
   * @param release The release number
   *
   */
  public void setRelease(String release) {
    this.release = release;
  }
  /**
   * Indicates when true that an existing destination
   * file will be overwritten.
   * @param o boolean where true means overwrite
   */
  public void setOverwrite(boolean o) {
    this.overwrite = o;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public void execute() throws BuildException {
    try {
      if (!topDir.exists()) {
        throw new BuildException("directory " + topDir.getName()
            + " does not exist");
      }
      if (!topDir.isDirectory()) {
        throw new BuildException(topDir.getName() + " is not a directory");
      }
      if (destFile.exists()) {
        if (this.overwrite) {
          destFile.delete();
          log("Regenerating " + destFile.getName() + " from "
              + topDir.getName());
        } else {
          log(destFile.getName() + " has not been regenerated");
        }
      }
      sb = new StringBuilder();
      File rootDir = new File(sourceDirName);
      String opendsDir = rootDir.getName();
      // Generate the package information
      sb.append("Summary            : OpenDS Directory Server"           + EOL);
      sb.append("Name               : opends"                            + EOL);
      sb.append("Version            : " + version                        + EOL);
      sb.append("Release            : " + release                        + EOL);
      sb.append("License            : CDDL"                              + EOL);
      sb.append("Group              : Applications/Network"              + EOL);
      sb.append("URL                : https://opends.org"                + EOL);
      sb.append(                                                           EOL);
      sb.append("BuildArchitectures : noarch"                            + EOL);
      sb.append("BuildRoot          : " + topDirAbsolutePath + "/SOURCES"+ EOL);
      sb.append("Prefix             : " + prefix                         + EOL);
      sb.append(                                                           EOL);
      sb.append("%define _prefix " + prefix                              + EOL);
      sb.append(                                                           EOL);
      sb.append("%Description"                                           + EOL);
      sb.append("OpenDS Directory Server"                                + EOL);
      sb.append(                                                           EOL);
      sb.append("# ========================="                            + EOL);
      sb.append("# pre/post installation"                                + EOL);
      sb.append("# ========================="                            + EOL);
      sb.append("# The order is:"                                        + EOL);
      sb.append("#     1. %pre new"                                      + EOL);
      sb.append("#     2. install new"                                   + EOL);
      sb.append("#     3. %post new"                                     + EOL);
      sb.append("#     4. %preun old"                                    + EOL);
      sb.append("#     5. delete old"                                    + EOL);
      sb.append("#     6. %postun old"                                   + EOL);
      sb.append("# Note: \"$1 equals \"1\" it means \"fresh install\""   + EOL);
      sb.append(                                                           EOL);
      sb.append("# PRE INSTALL"                                          + EOL);
      sb.append("%pre"                                                   + EOL);
      sb.append("if [ \"$1\" != \"1\" ]; then"                           + EOL);
      sb.append("echo \"  This version of the OpenDS RPM does not work\""+ EOL);
      sb.append("echo \"  with the standard RPM upgrade mechanism\""     + EOL);
      sb.append("echo \"  (rpm -U or rpm -F).\""                         + EOL);
      sb.append("echo \"  To perform an upgrade, use the OpenDS upgrade\""+ EOL);
      sb.append("echo \"  tool included in the package delivery.\""      + EOL);
      sb.append("echo \"  For more information about the upgrade process\""+ EOL);
      sb.append("echo \"  with RPM see https://www.opends.org/wiki//page/OpendsRPM.\""+ EOL);
      sb.append("exit 1"                                                 + EOL);
      sb.append("fi"                                                     + EOL);
      sb.append(                                                           EOL);
      sb.append("# POST INSTALL"                                         + EOL);
      sb.append("%post"                                                  + EOL);
      sb.append(                                                           EOL);
      sb.append("# PRE UNINSTALL"                                        + EOL);
      sb.append("%preun"                                                 + EOL);
      sb.append("${RPM_INSTALL_PREFIX}/OpenDS-1.0.0/bin/stop-ds"         + EOL);
      sb.append(                                                           EOL);
      sb.append("# POST UNINSTALL"                                       + EOL);
      sb.append("%postun"                                                + EOL);
      sb.append("rm -rf ${RPM_INSTALL_PREFIX}/" + opendsDir              + EOL);
      sb.append("rmdir --ignore-fail-on-non-empty ${RPM_INSTALL_PREFIX}" + EOL);
      sb.append(                                                           EOL);
      sb.append("# ========================="                            + EOL);
      sb.append("# Prepare, Build, Install"                              + EOL);
      sb.append("# ========================="                            + EOL);
      sb.append("%prep"                                                  + EOL);
      sb.append("cd "+ topDirAbsolutePath +"/SOURCES" + prefix           +
          " ; cp -r " + sourceDirName + " ."                             + EOL);
      sb.append("%build"                                                 + EOL);
      sb.append("%install"                                               + EOL);
      sb.append(                                                           EOL);
      sb.append("# ========================="                            + EOL);
      sb.append("# FILES LAYOUT"                                         + EOL);
      sb.append("# ========================="                            + EOL);
      sb.append("%files"                                                 + EOL);
      sb.append(dirPrefix                                                + EOL);
      generatedLevel("", rootDir);
      // flush the spec file.
      PrintWriter destWriter = new PrintWriter(destFile);
      destWriter.print(sb.toString());
      destWriter.close();
    } catch (Exception e) {
      // Don't leave a malformed file laying around. Delete
      // it so it will be forced to be regenerated.
      if (destFile.exists()) {
        destFile.deleteOnExit();
      }
      e.printStackTrace();
      throw new BuildException("Error processing " + topDir + ":  "
          + e.getMessage());
    }
  }
  private void generatedLevel (String parent, File source )
  {
    if (source.isDirectory())
    {
      sb.append(dirPrefix + parent + "/" +source.getName());
      sb.append(EOL);
      for (File child : source.listFiles())
      {
        generatedLevel( parent + "/" +source.getName(), child);
      }
    }
    else
    {
      sb.append(filePrefix + parent + "/" +source.getName());
      sb.append(EOL);
    }
  }
}
sdk/build-tools/org/opends/build/tools/GetSubversionRevision.java
New file
@@ -0,0 +1,143 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 */
package org.opends.build.tools;
import java.io.File;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNInfo;
import org.tmatesoft.svn.core.wc.SVNRevision;
/**
 * This class provides an implementation of an Ant task that may be used to
 * determine the current Subversion revision number of the current working
 * copy.  The value of the revision number will be stored in an Ant property.
 */
public class GetSubversionRevision
       extends Task
{
  // The name of the property in which the revision number should be set.
  private String propertyName = null;
  // The path to the root of the Subversion workspace for which to retrieve the
  // revision number.
  private String workspace = null;
  // The svn client manager. Required by svnkit 1.2.x
  private static SVNClientManager ourClientManager =
          SVNClientManager.newInstance();
  /**
   * Specifies the name of the Ant property into which the Subversion revision
   * number will be stored.
   *
   * @param  propertyName  The name of the Ant property into which the
   *                       Subversion revision number will be stored.
   */
  public void setProperty(String propertyName)
  {
    this.propertyName = propertyName;
  }
  /**
   * Specifies the path to the root of the Subversion workspace for which to
   * retrieve the revision number.
   *
   * @param  workspace  The path to the root of the Subversion workspace for
   *                    which to retrieve the revision number.
   */
  public void setWorkspace(String workspace)
  {
    this.workspace = workspace;
  }
  /**
   * Performs the appropriate processing needed for this task.  In this case,
   * it uses SVNKit to identify the current revision number for the local
   * workspace and store it in a specified property.
   */
  @Override()
  public void execute()
  {
    if ((propertyName == null) || (propertyName.length() == 0))
    {
      throw new BuildException("ERROR:  No property was specified for " +
                               "storing the revision number value.");
    }
    File workspacePath;
    if ((workspace == null) || (workspace.length() == 0))
    {
      workspacePath = getProject().getBaseDir();
    }
    else
    {
      workspacePath = new File(workspace);
    }
    try
    {
      SVNInfo svnInfo = ourClientManager.getWCClient().doInfo(workspacePath, SVNRevision.WORKING);
      SVNRevision revision = svnInfo.getCommittedRevision();
      if (revision == null)
      {
        System.err.println("WARNING:  Could not determine Subversion " +
                           "revision number for current workspace.");
        getProject().setNewProperty(propertyName, "-1");
      }
      else
      {
        getProject().setNewProperty(propertyName,
                                    String.valueOf(revision.getNumber()));
      }
    }
    catch (SVNException svnException)
    {
      System.err.println("WARNING:  Could not determine Subversion " +
                         "revision number for current workspace:  " +
                         svnException);
      getProject().setNewProperty(propertyName, "-1");
    }
  }
}
sdk/build-tools/org/opends/build/tools/GetSubversionUrlRepo.java
New file
@@ -0,0 +1,145 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.build.tools;
import java.io.File;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNInfo;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.SVNURL;
/**
 * This class provides an implementation of an Ant task that may be used to
 * determine the current Subversion revision number of the current working
 * copy.  The value of the revision number will be stored in an Ant property.
 */
public class GetSubversionUrlRepo
       extends Task
{
  // The name of the property in which the revision number should be set.
  private String propertyName = null;
  // The path to the root of the Subversion workspace for which to retrieve the
  // revision number.
  private String workspace = null;
  // The svn client manager. Required by svnkit 1.2.x
  private static SVNClientManager ourClientManager =
          SVNClientManager.newInstance();
  /**
   * Specifies the name of the Ant property into which the Subversion revision
   * number will be stored.
   *
   * @param  propertyName  The name of the Ant property into which the
   *                       Subversion revision number will be stored.
   */
  public void setProperty(String propertyName)
  {
    this.propertyName = propertyName;
  }
  /**
   * Specifies the path to the root of the Subversion workspace for which to
   * retrieve the revision number.
   *
   * @param  workspace  The path to the root of the Subversion workspace for
   *                    which to retrieve the revision number.
   */
  public void setWorkspace(String workspace)
  {
    this.workspace = workspace;
  }
  /**
   * Performs the appropriate processing needed for this task.  In this case,
   * it uses SVNKit to identify the current revision number for the local
   * workspace and store it in a specified property.
   */
  @Override()
  public void execute()
  {
    if ((propertyName == null) || (propertyName.length() == 0))
    {
      throw new BuildException("ERROR:  No property was specified for " +
                               "storing the revision number value.");
    }
    File workspacePath;
    if ((workspace == null) || (workspace.length() == 0))
    {
      workspacePath = getProject().getBaseDir();
    }
    else
    {
      workspacePath = new File(workspace);
    }
    try
    {
      SVNInfo svnInfo = ourClientManager.getWCClient().doInfo(workspacePath, SVNRevision.WORKING);
      SVNURL url_repo = svnInfo.getURL();
      if (url_repo == null)
      {
        System.err.println("WARNING:  Could not determine Subversion URL Repository " +
                           "for current workspace.");
        getProject().setNewProperty(propertyName, "-1");
      }
      else
      {
        getProject().setNewProperty(propertyName,
                                    String.valueOf(url_repo));
      }
    }
    catch (SVNException svnException)
    {
      System.err.println("WARNING:  Could not determine Subversion " +
                         "URL repository for current workspace:  " +
                         svnException);
      getProject().setNewProperty(propertyName, "-1");
    }
  }
}
sdk/build-tools/org/opends/build/tools/MessagePropertyKey.java
New file
@@ -0,0 +1,267 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 */
package org.opends.build.tools;
import org.opends.messages.Category;
import org.opends.messages.Severity;
import java.util.EnumSet;
/**
 * OpenDS messages defined in properties files must be defined
 * with the ordinal and in most cases category and severity encoded
 * in the message key.  This class helps with generating and
 * parsing of these keys.
 *
 * Keys must be of the form
 *
 *   CATEGORY_SEVERITY_DESCRIPTION_ORDINAL
 *
 * where:
 * <ul>
 * <li>
 * CATEGORY is the string representation of one of the
 * <code>Category</code> enums.
 * </li>
 * <li>
 * SEVERITY is the long or abbreviated form of one of
 * the <code>Severity</code> enums.
 * </li>
 * <li>
 * DESCRIPTION is an uppercase string containing characters
 * and the underscore character for describing the purpose
 * of the message.
 * </li>
 * <li>
 * ORDINAL is an integer that makes the message unique witin
 * the property file.
 * </li>
 * </ul>
 *
 */
// TODO: move this class to GenerateMessageFile when DirectoryServer
// no longer needs to support dumpMessages()
public class MessagePropertyKey
        implements Comparable<MessagePropertyKey> {
  private Category category;
  private Severity severity;
  private String description;
  private Integer ordinal;
  /**
   * Creates a message property key from a string value.
   * @param keyString from properties file
   * @param includesCategory when true expects ordinals to be encoded
   *        in the keystring; when false the mandate is relaxed
   * @param includesSeverity when true expects ordinals to be encoded
   *        in the keystring; when false the mandate is relaxed
   * @param includesOrdinal when true expects ordinals to be encoded
   *        in the keystring; when false the mandate is relaxed
   * @return MessagePropertyKey created from string
   */
  static public MessagePropertyKey parseString(
          String keyString,
          boolean includesCategory,
          boolean includesSeverity,
          boolean includesOrdinal) {
    Category category = null;
    Severity severity = null;
    String description;
    Integer ordinal = null;
    String k = keyString;
    for (Category c : EnumSet.allOf(Category.class)) {
      String cName = c.name();
      if (k.startsWith(cName)) {
        category = c;
        if ('_' != k.charAt(cName.length())) {
          throw new IllegalArgumentException(
                  "Error processing " + keyString + ".  Category must be " +
                          "separated from the rest of the " +
                          "key with an '_' character");
        }
        k = k.substring(cName.length() + 1);
        break;
      }
    }
    if (category == null && includesCategory) {
      throw new IllegalArgumentException("Category not included in key " +
              keyString);
    }
    for (Severity s : EnumSet.allOf(Severity.class)) {
      String sName = s.propertyKeyFormName();
      if (k.startsWith(sName)) {
        severity = s;
        if ('_' != k.charAt(sName.length())) {
          throw new IllegalArgumentException(
                  "Error processing " + keyString + ".  Severity must be " +
                          "separated from the rest of the " +
                          "key with an '_' character");
        }
        k = k.substring(sName.length() + 1);
        break;
      }
    }
    if (severity == null && includesSeverity) {
      throw new IllegalArgumentException("Severity not included in key " +
              keyString);
    }
    if (includesOrdinal) {
      int li = k.lastIndexOf("_");
      if (li != -1) {
        description = k.substring(0, li).toUpperCase();
      } else {
        throw new IllegalArgumentException(
                "Incorrectly formatted key " + keyString);
      }
      try {
        String ordString = k.substring(li + 1);
        ordinal = Integer.parseInt(ordString);
      } catch (Exception nfe) {
        throw new IllegalArgumentException("Error parsing ordinal for key " +
                keyString);
      }
    } else {
      description = k;
    }
    return new MessagePropertyKey(category, severity, description, ordinal);
  }
  /**
   * Creates a parameterized instance.
   * @param category of this key
   * @param severity of this key
   * @param description of this key
   * @param ordinal of this key
   */
  public MessagePropertyKey(Category category, Severity severity,
                           String description, Integer ordinal) {
    this.category = category;
    this.severity = severity;
    this.description = description;
    this.ordinal = ordinal;
  }
  /**
   * Gets the category of this key.
   * @return Category of this key
   */
  public Category getCategory() {
    return this.category;
  }
  /**
   * Gets the severity of this key.
   * @return Severity of this key
   */
  public Severity getSeverity() {
    return this.severity;
  }
  /**
   * Gets the description of this key.
   * @return description of this key
   */
  public String getDescription() {
    return this.description;
  }
  /**
   * Gets the ordinal of this key.
   * @return ordinal of this key
   */
  public Integer getOrdinal() {
    return this.ordinal;
  }
  /**
   * Gets the name of the MessageDescriptor as it should appear
   * in the messages file.
   * @return name of message descriptor
   */
  public String getMessageDescriptorName() {
    return new StringBuilder()
            .append(this.severity.messageDesciptorName())
            .append("_")
            .append(this.description).toString();
  }
  /**
   * {@inheritDoc}
   */
  public String toString() {
    return getPropertyKeyName(true, true, true);
  }
  /**
   * Gets the name of the key as it would appear in a properties file.
   * @param includeCategory in the name
   * @param includeSeverity in the name
   * @param includeOrdinal in the name
   * @return string representing the property key
   */
  public String getPropertyKeyName(boolean includeCategory,
                                   boolean includeSeverity,
                                   boolean includeOrdinal) {
    StringBuilder sb = new StringBuilder();
    if (category != null && includeCategory) {
      sb.append(category.name());
      sb.append("_");
    }
    if (severity != null && includeSeverity) {
      sb.append(severity.propertyKeyFormName());
      sb.append("_");
    }
    sb.append(description);
    if (ordinal != null && includeOrdinal) {
      sb.append("_");
      sb.append(ordinal);
    }
    return sb.toString();
  }
  /**
   * {@inheritDoc}
   */
  public int compareTo(MessagePropertyKey k) {
    if (ordinal == k.ordinal) {
      return description.compareTo(k.description);
    } else {
      return ordinal.compareTo(k.ordinal);
    }
  }
}
sdk/build-tools/org/opends/build/tools/PrepTestNG.java
New file
@@ -0,0 +1,337 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 */
package org.opends.build.tools;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.Arrays;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.BuildException;
public class PrepTestNG extends Task
{
  /** Template for inserting children elements of default test tag */
  static private final String DEFAULT_TAGS_TEMPLATE =
    "<!-- DO NOT REMOVE! - GENERATED DEFAULT TAGS (see PrepTestNG class) -->";
  /** Template for inserting global children elements of run tags */
  static private final String GLOBAL_RUN_TAGS_TEMPLATE =
    "<!-- DO NOT REMOVE! - GENERATED GLOBAL RUN TAGS (see PrepTestNG class) -->";
  /** Indentation used in testng.xml */
  static private final int INDENT = 4;
  private String file;
  private String toFile;
  private String groupList;
  private String packageList;
  private String classList;
  private String methodList;
  public void setFile(String file)
  {
    this.file = file;
  }
  public void setToFile(String toFile)
  {
    this.toFile = toFile;
  }
  public void setGroupList(String groupList)
  {
    this.groupList = groupList;
  }
  public void setPackageList(String packageList)
  {
    this.packageList = packageList;
  }
  public void setClassList(String classList)
  {
    this.classList = classList;
  }
  public void setMethodList(String methodList)
  {
    this.methodList = methodList;
  }
  public void execute() throws BuildException
  {
    if(file == null)
    {
      throw new BuildException("Attribute file must be set to the orginal " +
          "TestNG XML file");
    }
    if(toFile == null)
    {
      throw new BuildException("Attribute toFile must be set to the modified " +
          "TestNG XML file");
    }
    BufferedReader reader;
    FileOutputStream outFile;
    PrintStream writer;
    String line;
    String[] groups;
    String[] packages;
    String[] classes;
    String[] methods;
    String[] groupLine;
    String[] methodLine;
    String methodClass;
    String methodName;
    int methodNameStartIdx;
    int groupCount = 0;
    int packageCount = 0;
    int classCount = 0;
    int methodCount = 0;
    try
    {
      reader = new BufferedReader(new FileReader(file));
      outFile = new FileOutputStream(toFile);
      writer = new PrintStream(outFile);
      line = reader.readLine();
      if(groupList != null && !groupList.trim().equals("") &&
          !groupList.startsWith("${"))
      {
        groups = groupList.split(",");
      }
      else
      {
        groups = new String[0];
      }
      if(packageList != null && !packageList.trim().equals("") &&
          !packageList.startsWith("${"))
      {
        packages = packageList.split(",");
      }
      else
      {
        packages = new String[0];
      }
      if(classList != null && !classList.trim().equals("") &&
          !classList.startsWith("${"))
      {
        classes = classList.split(",");
      }
      else
      {
        classes = new String[0];
      }
      if(methodList != null && !methodList.trim().equals("") &&
          !methodList.startsWith("${"))
      {
        methods = methodList.split(";");
      }
      else
      {
        methods = new String[0];
      }
      while(line != null)
      {
        if(line.indexOf(DEFAULT_TAGS_TEMPLATE) >= 0)
        {
          int level = 2;
          if(groups.length > 0)
          {
            boolean windowsClause = false;
            println(writer, level, "<groups>");
            println(writer, ++level,   "<run>");
            level++;
            for(String group : groups)
            {
              groupLine = group.split("=");
              if(groupLine.length == 2)
              {
                String inc_exc = groupLine[0].trim();
                if (inc_exc == null ||
                        !("include".equals(inc_exc.toLowerCase()) ||
                                "exclude".equals(inc_exc.toLowerCase()))) {
                  System.out.println("Error:  illegal group clause " + group);
                } else {
                  String gr = groupLine[1].trim();
                  println(writer, level, "<" +inc_exc +" "+
                          "name=\""+gr+ "\" />");
                  windowsClause |= "windows".equals(gr);
                  groupCount++;
                }
              }
            }
            // Exclude windows specific tests if the user has not provided
            // an explicit windows clause and we're not on windows.
            if (!windowsClause && !isWindows()) {
              println(writer, level, "<exclude name=\"windows\"/>");
              groupCount++;
            }
            println(writer, --level,   "</run>");
            println(writer, --level, "</groups>");
          } else {
            // No explicit groups have been specified so see if we need
            // to exclude the windows tests.
            if (!isWindows()) {
              println(writer, level,   "<groups>");
              println(writer, ++level,   "<run>");
              println(writer, ++level,     "<exclude name=\"windows\"/>");
              println(writer, --level,   "</run>");
              println(writer, --level, "</groups>");
              groupCount++;
            }
          }
          if(packages.length > 0)
          {
            println(writer, level, "<packages>");
            level++;
            for(String pkg : packages)
            {
              println(writer, level, "<package name=\"" + pkg.trim() + "\" />");
              packageCount++;
            }
            println(writer, --level, "</packages>");
          }
          if(classes.length > 0 || methods.length > 0)
          {
            println(writer, level, "<classes>");
            if(classes.length > 0)
            {
              level++;
              for(String cls : classes)
              {
                println(writer, level, "<class name=\"" + cls.trim() + "\" />");
                classCount++;
              }
            }
            if(methods.length > 0)
            {
              level++;
              for(String mhd : methods)
              {
                methodLine = mhd.split(",");
                if(methodLine.length > 0)
                {
                  // Allow class.method or class#method
                  methodNameStartIdx = methodLine[0].lastIndexOf("#");
                  if (methodNameStartIdx == -1)
                  {
                    methodNameStartIdx = methodLine[0].lastIndexOf(".");
                  }
                  methodClass = methodLine[0].substring(0,
                                  methodNameStartIdx);
                  methodName = methodLine[0].substring(methodNameStartIdx + 1,
                                methodLine[0].length());
                  println(writer, level, "<class name=\"" +
                      methodClass.trim() + "\" >");
                  println(writer, ++level, "<methods>");
                  println(writer, ++level, "<include name=\"" +
                      methodName.trim() + "\" />");
                  methodCount++;
                  classCount++;
                  for(int i = 1; i < methodLine.length; i ++)
                  {
                    println(writer, level, "<include name=\"" +
                      methodLine[i].trim() + "\" />");
                    methodCount++;
                  }
                  println(writer, --level, "</methods>");
                  println(writer, --level, "</class>");
                }
              }
            }
            println(writer, --level, "</classes>");
          }
        }
        else if (line.indexOf(GLOBAL_RUN_TAGS_TEMPLATE) != -1)
        {
          if (!isWindows()) {
            int index = line.indexOf(GLOBAL_RUN_TAGS_TEMPLATE);
            println(writer, levelForIndex(index),
                    "<exclude name=\"windows\"/>");
          }
        }
        else
        {
          println(writer, 0, line);
        }
        line = reader.readLine();
      }
      System.out.println("Adding " + groupCount + " group tags, " +
          packageCount + " package tags, " + classCount + " class tags, " +
          methodCount + " method tags to " + toFile);
    }
    catch(Exception e)
    {
      throw new BuildException("File Error: " + e.toString());
    }
  }
  static private boolean isWindows() {
    String os = System.getProperty("os.name");
    return (os != null && os.toLowerCase().indexOf("windows") != -1);
  }
  static private String indent(int indent) {
    char[] blankArray = new char[indent];
    Arrays.fill(blankArray, ' ');
    return new String(blankArray);
  }
  static private void println(PrintStream writer, int level, String txt) {
    writer.print(indent(INDENT * level));
    writer.print(txt);
    writer.print(System.getProperty("line.separator"));
  }
  static private int levelForIndex(int index) {
    return index / INDENT;
  }
}
sdk/build-tools/org/opends/build/tools/Utilities.java
New file
@@ -0,0 +1,159 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 */
package org.opends.build.tools;
import org.opends.messages.Message;
import java.util.StringTokenizer;
/**
 */
public class Utilities {
  /**
   * The end-of-line character for this platform.
   */
  public static final String EOL = System.getProperty("line.separator");
  /**
   * Inserts line breaks into the provided buffer to wrap text at no more than
   * the specified column width.  Wrapping will only be done at space boundaries
   * and if there are no spaces within the specified width, then wrapping will
   * be performed at the first space after the specified column.
   *
   * @param  text   The text to be wrapped.
   * @param  width  The maximum number of characters to allow on a line if there
   *                is a suitable breaking point.
   *
   * @return  The wrapped text.
   */
  public static String wrapText(String text, int width)
  {
    StringBuilder   buffer        = new StringBuilder();
    StringTokenizer lineTokenizer = new StringTokenizer(text, "\r\n", true);
    while (lineTokenizer.hasMoreTokens())
    {
      String line = lineTokenizer.nextToken();
      if (line.equals("\r") || line.equals("\n"))
      {
        // It's an end-of-line character, so append it as-is.
        buffer.append(line);
      }
      else if (line.length() < width)
      {
        // The line fits in the specified width, so append it as-is.
        buffer.append(line);
      }
      else
      {
        // The line doesn't fit in the specified width, so it needs to be
        // wrapped.  Do so at space boundaries.
        StringBuilder   lineBuffer    = new StringBuilder();
        StringBuilder   delimBuffer   = new StringBuilder();
        StringTokenizer wordTokenizer = new StringTokenizer(line, " ", true);
        while (wordTokenizer.hasMoreTokens())
        {
          String word = wordTokenizer.nextToken();
          if (word.equals(" "))
          {
            // It's a space, so add it to the delim buffer only if the line
            // buffer is not empty.
            if (lineBuffer.length() > 0)
            {
              delimBuffer.append(word);
            }
          }
          else if (word.length() > width)
          {
            // This is a long word that can't be wrapped, so we'll just have to
            // make do.
            if (lineBuffer.length() > 0)
            {
              buffer.append(lineBuffer);
              buffer.append(EOL);
              lineBuffer = new StringBuilder();
            }
            buffer.append(word);
            if (wordTokenizer.hasMoreTokens())
            {
              // The next token must be a space, so remove it.  If there are
              // still more tokens after that, then append an EOL.
              wordTokenizer.nextToken();
              if (wordTokenizer.hasMoreTokens())
              {
                buffer.append(EOL);
              }
            }
            if (delimBuffer.length() > 0)
            {
              delimBuffer = new StringBuilder();
            }
          }
          else
          {
            // It's not a space, so see if we can fit it on the curent line.
            int newLineLength = lineBuffer.length() + delimBuffer.length() +
                                word.length();
            if (newLineLength < width)
            {
              // It does fit on the line, so add it.
              lineBuffer.append(delimBuffer).append(word);
              if (delimBuffer.length() > 0)
              {
                delimBuffer = new StringBuilder();
              }
            }
            else
            {
              // It doesn't fit on the line, so end the current line and start
              // a new one.
              buffer.append(lineBuffer);
              buffer.append(EOL);
              lineBuffer = new StringBuilder();
              lineBuffer.append(word);
              if (delimBuffer.length() > 0)
              {
                delimBuffer = new StringBuilder();
              }
            }
          }
        }
        // If there's anything left in the line buffer, then add it to the
        // final buffer.
        buffer.append(lineBuffer);
      }
    }
    return buffer.toString();
  }
}
sdk/build-tools/org/opends/build/tools/ValidJavaVersion.java
New file
@@ -0,0 +1,68 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 */
package org.opends.build.tools;
import org.apache.tools.ant.taskdefs.condition.Condition;
import org.apache.tools.ant.BuildException;
/**
 * Ant condition to check whether we have a minimum required Java version.
 */
public class ValidJavaVersion implements Condition
{
  // The minimum required Java version.
  String minVersion;
  /**
   * Set the minVersion attribute.
   * @param minVersion The minimum required Java version.
   */
  public void setMinVersion(String minVersion)
  {
    this.minVersion = minVersion;
  }
  /**
   * Evaluate the condition.
   */
  public boolean eval() throws BuildException
  {
    if (minVersion == null)
    {
      return true;
    }
    String version = System.getProperty("java.version");
    if (version == null)
    {
      return false;
    }
    return version.compareTo(minVersion) >= 0;
  }
}
sdk/build-tools/org/opends/build/tools/package-info.java
New file
@@ -0,0 +1,36 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 */
/**
 * This package contains source for a number of tools that are used in some way
 * during the OpenDS build process.  This includes custom Ant tasks, utilities
 * to format schema files, and to facilitate running server unit tests.
 */
package org.opends.build.tools;