From f0bf1ccdaa227588d497594e4962993e4e2fde2c Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Mon, 30 Nov 2009 20:41:05 +0000
Subject: [PATCH] Copy OpenDS build tools to sdk.
---
sdk/build-tools/org/opends/build/tools/PrepTestNG.java | 337 ++++
sdk/build-tools/org/opends/build/tools/CreateVersionString.java | 98 +
sdk/build-tools/org/opends/build/tools/ValidJavaVersion.java | 68
sdk/build-tools/org/opends/build/tools/CoverageDiff.java | 969 +++++++++++++
sdk/build-tools/org/opends/build/tools/CheckPrecommit.java | 402 +++++
sdk/build-tools/org/opends/build/tools/GenerateMessageFile.java | 995 ++++++++++++++
sdk/build-tools/org/opends/build/tools/MessagePropertyKey.java | 267 +++
sdk/build-tools/org/opends/build/tools/GetSubversionUrlRepo.java | 145 ++
sdk/build-tools/org/opends/build/tools/package-info.java | 36
sdk/build-tools/org/opends/build/tools/Utilities.java | 159 ++
sdk/build-tools/org/opends/build/tools/ConcatSchema.java | 272 +++
sdk/build-tools/org/opends/build/tools/GenerateRpm.java | 251 +++
sdk/build-tools/org/opends/build/tools/GetSubversionRevision.java | 143 ++
13 files changed, 4,142 insertions(+), 0 deletions(-)
diff --git a/sdk/build-tools/org/opends/build/tools/CheckPrecommit.java b/sdk/build-tools/org/opends/build/tools/CheckPrecommit.java
new file mode 100644
index 0000000..257b534
--- /dev/null
+++ b/sdk/build-tools/org/opends/build/tools/CheckPrecommit.java
@@ -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;
+ }
+ }
+}
+
diff --git a/sdk/build-tools/org/opends/build/tools/ConcatSchema.java b/sdk/build-tools/org/opends/build/tools/ConcatSchema.java
new file mode 100644
index 0000000..3137504
--- /dev/null
+++ b/sdk/build-tools/org/opends/build/tools/ConcatSchema.java
@@ -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);
+ }
+ }
+}
+
diff --git a/sdk/build-tools/org/opends/build/tools/CoverageDiff.java b/sdk/build-tools/org/opends/build/tools/CoverageDiff.java
new file mode 100644
index 0000000..2e456de
--- /dev/null
+++ b/sdk/build-tools/org/opends/build/tools/CoverageDiff.java
@@ -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());
+ }
+ }
+}
diff --git a/sdk/build-tools/org/opends/build/tools/CreateVersionString.java b/sdk/build-tools/org/opends/build/tools/CreateVersionString.java
new file mode 100644
index 0000000..b23b2da
--- /dev/null
+++ b/sdk/build-tools/org/opends/build/tools/CreateVersionString.java
@@ -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());
+ }
+}
+
diff --git a/sdk/build-tools/org/opends/build/tools/GenerateMessageFile.java b/sdk/build-tools/org/opends/build/tools/GenerateMessageFile.java
new file mode 100644
index 0000000..dd3371e
--- /dev/null
+++ b/sdk/build-tools/org/opends/build/tools/GenerateMessageFile.java
@@ -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();
+ }
+
+}
diff --git a/sdk/build-tools/org/opends/build/tools/GenerateRpm.java b/sdk/build-tools/org/opends/build/tools/GenerateRpm.java
new file mode 100644
index 0000000..d839f85
--- /dev/null
+++ b/sdk/build-tools/org/opends/build/tools/GenerateRpm.java
@@ -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);
+ }
+ }
+}
diff --git a/sdk/build-tools/org/opends/build/tools/GetSubversionRevision.java b/sdk/build-tools/org/opends/build/tools/GetSubversionRevision.java
new file mode 100644
index 0000000..5469b10
--- /dev/null
+++ b/sdk/build-tools/org/opends/build/tools/GetSubversionRevision.java
@@ -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");
+ }
+ }
+}
+
diff --git a/sdk/build-tools/org/opends/build/tools/GetSubversionUrlRepo.java b/sdk/build-tools/org/opends/build/tools/GetSubversionUrlRepo.java
new file mode 100644
index 0000000..ed4aad0
--- /dev/null
+++ b/sdk/build-tools/org/opends/build/tools/GetSubversionUrlRepo.java
@@ -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");
+ }
+ }
+}
+
diff --git a/sdk/build-tools/org/opends/build/tools/MessagePropertyKey.java b/sdk/build-tools/org/opends/build/tools/MessagePropertyKey.java
new file mode 100644
index 0000000..93ee9e5
--- /dev/null
+++ b/sdk/build-tools/org/opends/build/tools/MessagePropertyKey.java
@@ -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);
+ }
+ }
+
+}
diff --git a/sdk/build-tools/org/opends/build/tools/PrepTestNG.java b/sdk/build-tools/org/opends/build/tools/PrepTestNG.java
new file mode 100644
index 0000000..51cf38e
--- /dev/null
+++ b/sdk/build-tools/org/opends/build/tools/PrepTestNG.java
@@ -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;
+ }
+
+}
diff --git a/sdk/build-tools/org/opends/build/tools/Utilities.java b/sdk/build-tools/org/opends/build/tools/Utilities.java
new file mode 100644
index 0000000..2ad4f22
--- /dev/null
+++ b/sdk/build-tools/org/opends/build/tools/Utilities.java
@@ -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();
+ }
+
+}
diff --git a/sdk/build-tools/org/opends/build/tools/ValidJavaVersion.java b/sdk/build-tools/org/opends/build/tools/ValidJavaVersion.java
new file mode 100644
index 0000000..aafc80b
--- /dev/null
+++ b/sdk/build-tools/org/opends/build/tools/ValidJavaVersion.java
@@ -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;
+ }
+}
diff --git a/sdk/build-tools/org/opends/build/tools/package-info.java b/sdk/build-tools/org/opends/build/tools/package-info.java
new file mode 100644
index 0000000..2c234a5
--- /dev/null
+++ b/sdk/build-tools/org/opends/build/tools/package-info.java
@@ -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;
+
--
Gitblit v1.10.0