You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by br...@apache.org on 2007/08/29 04:43:59 UTC
svn commit: r570618 [3/14] - in /maven/sandbox/trunk/archetypeng: ./
archetype-common/ archetype-common/src/ archetype-common/src/main/
archetype-common/src/main/java/ archetype-common/src/main/java/org/
archetype-common/src/main/java/org/apache/ arche...
Added: maven/sandbox/trunk/archetypeng/archetype-common/src/main/java/org/apache/maven/archetype/common/DefaultPomManager.java
URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/archetypeng/archetype-common/src/main/java/org/apache/maven/archetype/common/DefaultPomManager.java?rev=570618&view=auto
==============================================================================
--- maven/sandbox/trunk/archetypeng/archetype-common/src/main/java/org/apache/maven/archetype/common/DefaultPomManager.java (added)
+++ maven/sandbox/trunk/archetypeng/archetype-common/src/main/java/org/apache/maven/archetype/common/DefaultPomManager.java Tue Aug 28 19:43:33 2007
@@ -0,0 +1,455 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.maven.archetype.common;
+
+import org.apache.maven.archetype.exception.InvalidPackaging;
+import org.apache.maven.model.Build;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Model;
+import org.apache.maven.model.Parent;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.model.ReportPlugin;
+import org.apache.maven.model.Reporting;
+import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
+import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
+
+import org.codehaus.plexus.logging.AbstractLogEnabled;
+import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.dom4j.Node;
+
+import org.dom4j.io.SAXReader;
+import org.dom4j.io.XMLWriter;
+
+import org.jdom.JDOMException;
+
+import org.jdom.input.SAXBuilder;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @plexus.component
+ */
+public class DefaultPomManager
+extends AbstractLogEnabled
+implements PomManager
+{
+ public void addModule ( File pom, String artifactId )
+ throws FileNotFoundException,
+ IOException,
+ XmlPullParserException,
+ DocumentException,
+ InvalidPackaging
+ {
+ boolean found = false;
+
+ StringWriter writer = new StringWriter ();
+ Reader fileReader = new FileReader ( pom );
+
+ try
+ {
+ fileReader = new FileReader ( pom );
+
+ SAXReader reader = new SAXReader ();
+ Document document = reader.read ( fileReader );
+ Element project = document.getRootElement ();
+
+ String packaging = null;
+ Element packagingElement = project.element ( "packaging" );
+ if ( packagingElement != null )
+ {
+ packaging = packagingElement.getStringValue ();
+ }
+ if ( !"pom".equals ( packaging ) )
+ {
+ throw new InvalidPackaging (
+ "Unable to add module to the current project as it is not of packaging type 'pom'"
+ );
+ }
+
+ Element modules = project.element ( "modules" );
+ if ( modules == null )
+ {
+ modules = project.addText ( " " ).addElement ( "modules" );
+ modules.setText ( "\n " );
+ project.addText ( "\n" );
+ }
+ // TODO: change to while loop
+ for ( Iterator i = modules.elementIterator ( "module" ); i.hasNext () && !found; )
+ {
+ Element module = (Element) i.next ();
+ if ( module.getText ().equals ( artifactId ) )
+ {
+ found = true;
+ }
+ }
+ if ( !found )
+ {
+ Node lastTextNode = null;
+ for ( Iterator i = modules.nodeIterator (); i.hasNext (); )
+ {
+ Node node = (Node) i.next ();
+ if ( node.getNodeType () == Node.ELEMENT_NODE )
+ {
+ lastTextNode = null;
+ }
+ else if ( node.getNodeType () == Node.TEXT_NODE )
+ {
+ lastTextNode = node;
+ }
+ }
+
+ if ( lastTextNode != null )
+ {
+ modules.remove ( lastTextNode );
+ }
+
+ modules.addText ( "\n " );
+ modules.addElement ( "module" ).setText ( artifactId );
+ modules.addText ( "\n " );
+
+ XMLWriter xmlWriter = new XMLWriter ( writer );
+ xmlWriter.write ( document );
+
+ FileUtils.fileWrite ( pom.getAbsolutePath (), writer.toString () );
+ } // end if
+ }
+ finally
+ {
+ IOUtil.close ( fileReader );
+ }
+ }
+
+ public void addParent ( File pom, File parentPom )
+ throws FileNotFoundException, IOException, XmlPullParserException
+ {
+ Model generatedModel = readPom ( pom );
+
+ Model parentModel = readPom ( parentPom );
+
+ Parent parent = new Parent ();
+ parent.setGroupId ( parentModel.getGroupId () );
+ if ( parent.getGroupId () == null )
+ {
+ parent.setGroupId ( parentModel.getParent ().getGroupId () );
+ }
+ parent.setArtifactId ( parentModel.getArtifactId () );
+ parent.setVersion ( parentModel.getVersion () );
+ if ( parent.getVersion () == null )
+ {
+ parent.setVersion ( parentModel.getParent ().getVersion () );
+ }
+ generatedModel.setParent ( parent );
+
+ writePom ( generatedModel, pom );
+ }
+
+ public void mergePoms ( File pom, File temporaryPom )
+ throws FileNotFoundException, IOException, XmlPullParserException
+ {
+ Model model = readPom ( pom );
+ Model generatedModel = readPom ( temporaryPom );
+ mergeDependencies ( model, generatedModel );
+ mergeBuildPlugins ( model, generatedModel );
+ mergeReportPlugins ( model, generatedModel );
+
+//
+// // Potential merging
+//
+// model.getModelEncoding ();
+// model.getModelVersion ();
+//
+// model.getGroupId ();
+// model.getArtifactId ();
+// model.getVersion ();
+// model.getParent ();
+//
+// model.getId ();
+// model.getName ();
+// model.getInceptionYear ();
+// model.getDescription ();
+// model.getUrl ();
+// model.getLicenses ();
+// model.getProperties ();
+//
+// model.getOrganization ();
+// model.getMailingLists ();
+// model.getContributors ();
+// model.getDevelopers ();
+//
+// model.getScm ();
+// model.getCiManagement ();
+// model.getDistributionManagement ();
+// model.getIssueManagement ();
+//
+// model.getPackaging ();
+//// model.getDependencies (); // done
+// model.getDependencyManagement ();
+// model.getPrerequisites ().getMaven ();
+// model.getPrerequisites ().getModelEncoding ();
+//
+// model.getProfiles ();
+// model.getModules ();
+// model.getRepositories ();
+// model.getPluginRepositories ();
+//
+// model.getBuild ().getDefaultGoal ();
+// model.getBuild ().getFinalName ();
+// model.getBuild ().getModelEncoding ();
+// model.getBuild ().getFilters ();
+// model.getBuild ().getDirectory ();
+// model.getBuild ().getOutputDirectory ();
+// model.getBuild ().getSourceDirectory ();
+// model.getBuild ().getResources ();
+// model.getBuild ().getScriptSourceDirectory ();
+// model.getBuild ().getTestOutputDirectory ();
+// model.getBuild ().getTestResources ();
+// model.getBuild ().getTestSourceDirectory ();
+// model.getBuild ().getPluginManagement ();
+// model.getBuild ().getExtensions ();
+//// model.getBuild ().getPluginsAsMap (); // done
+//
+// model.getReporting ().getModelEncoding ();
+// model.getReporting ().getOutputDirectory ();
+//// model.getReporting ().getReportPluginsAsMap (); // done
+//
+
+ writePom ( model, pom );
+ }
+
+ public Model readPom ( final File pomFile )
+ throws FileNotFoundException, IOException, XmlPullParserException
+ { // TODO ensure correct encoding by using default one from method argument !!!
+
+ Model model;
+ Reader pomReader = null;
+ try
+ {
+ FileCharsetDetector detector = new FileCharsetDetector ( pomFile );
+
+ String fileEncoding = detector.isFound () ? detector.getCharset () : "UTF-8";
+
+ pomReader = new InputStreamReader ( new FileInputStream ( pomFile ), fileEncoding );
+
+ MavenXpp3Reader reader = new MavenXpp3Reader ();
+
+ model = reader.read ( pomReader );
+
+ if ( StringUtils.isEmpty ( model.getModelEncoding () ) )
+ {
+ model.setModelEncoding ( fileEncoding );
+ }
+ }
+ finally
+ {
+ IOUtil.close ( pomReader );
+ pomReader = null;
+ }
+ return model;
+ }
+
+ public void writePom ( final Model model, final File pomFile )
+ throws IOException
+ {
+ InputStream inputStream = null;
+ Writer outputStreamWriter = null;
+// FileCharsetDetector detector = new FileCharsetDetector ( pomFile );
+
+ String fileEncoding =
+ StringUtils.isEmpty ( model.getModelEncoding () ) ? model.getModelEncoding () : "UTF-8";
+
+ try
+ {
+ inputStream = new FileInputStream ( pomFile );
+
+ SAXBuilder builder = new SAXBuilder ();
+ org.jdom.Document doc = builder.build ( inputStream );
+ inputStream.close ();
+ inputStream = null;
+
+ MavenJDOMWriter writer = new MavenJDOMWriter ();
+
+ outputStreamWriter =
+ new OutputStreamWriter ( new FileOutputStream ( pomFile ), fileEncoding );
+
+ org.jdom.output.Format form =
+ org.jdom.output.Format.getRawFormat ().setEncoding ( fileEncoding );
+ writer.write ( model, doc, outputStreamWriter, form );
+ outputStreamWriter.close ();
+ outputStreamWriter = null;
+ }
+ catch ( JDOMException exc )
+ {
+ throw (IOException) new IOException ( "Cannot parse the POM by JDOM." );
+ }
+ catch ( FileNotFoundException e )
+ {
+ getLogger ().debug ( "Creating pom file " + pomFile );
+
+ Writer pomWriter = null;
+
+ try
+ {
+// pomWriter = new FileWriter ( pomFile );
+ pomWriter =
+ new OutputStreamWriter ( new FileOutputStream ( pomFile ), fileEncoding );
+
+ MavenXpp3Writer writer = new MavenXpp3Writer ();
+ writer.write ( pomWriter, model );
+ pomWriter.close ();
+ pomWriter = null;
+ }
+ finally
+ {
+ IOUtil.close ( pomWriter );
+ }
+ }
+ finally
+ {
+ IOUtil.close ( inputStream );
+ IOUtil.close ( outputStreamWriter );
+ }
+ }
+
+ private Map createDependencyMap ( List dependencies )
+ {
+ Map dependencyMap = new HashMap ();
+ Iterator dependenciesIterator = dependencies.iterator ();
+ while ( dependenciesIterator.hasNext () )
+ {
+ Dependency dependency = (Dependency) dependenciesIterator.next ();
+
+ dependencyMap.put (
+ dependency.getGroupId () + ":" + dependency.getArtifactId (),
+ dependency
+ );
+ }
+
+ return dependencyMap;
+ }
+
+ private void mergeBuildPlugins ( Model model, Model generatedModel )
+ {
+ if ( generatedModel.getBuild () != null )
+ {
+ if ( model.getBuild () == null )
+ {
+ model.setBuild ( new Build () );
+ }
+
+ Map pluginsByIds = model.getBuild ().getPluginsAsMap ();
+ Map generatedPluginsByIds = generatedModel.getBuild ().getPluginsAsMap ();
+
+ Iterator generatedPluginsIds = generatedPluginsByIds.keySet ().iterator ();
+ while ( generatedPluginsIds.hasNext () )
+ {
+ String generatedPluginsId = (String) generatedPluginsIds.next ();
+
+ if ( !pluginsByIds.containsKey ( generatedPluginsId ) )
+ {
+ model.getBuild ().addPlugin (
+ (Plugin) generatedPluginsByIds.get ( generatedPluginsId )
+ );
+ }
+ else
+ {
+ getLogger ().warn ( "Can not override plugin: " + generatedPluginsId );
+ }
+ }
+ }
+ }
+
+ private void mergeDependencies ( Model model, Model generatedModel )
+ {
+ Map dependenciesByIds = createDependencyMap ( model.getDependencies () );
+ Map generatedDependenciesByIds = createDependencyMap ( generatedModel.getDependencies () );
+
+ Iterator generatedDependencyIds = generatedDependenciesByIds.keySet ().iterator ();
+ while ( generatedDependencyIds.hasNext () )
+ {
+ String generatedDependencyId = (String) generatedDependencyIds.next ();
+
+ if ( !dependenciesByIds.containsKey ( generatedDependencyId ) )
+ {
+ model.addDependency (
+ (Dependency) generatedDependenciesByIds.get ( generatedDependencyId )
+ );
+ }
+ else
+ {
+ getLogger ().warn ( "Can not override property: " + generatedDependencyId );
+ }
+ }
+ }
+
+ private void mergeReportPlugins ( Model model, Model generatedModel )
+ {
+ if ( generatedModel.getReporting () != null )
+ {
+ if ( model.getReporting () == null )
+ {
+ model.setReporting ( new Reporting () );
+ }
+
+ Map reportPluginsByIds = model.getReporting ().getReportPluginsAsMap ();
+ Map generatedReportPluginsByIds =
+ generatedModel.getReporting ().getReportPluginsAsMap ();
+
+ Iterator generatedReportPluginsIds = generatedReportPluginsByIds.keySet ().iterator ();
+ while ( generatedReportPluginsIds.hasNext () )
+ {
+ String generatedReportPluginsId = (String) generatedReportPluginsIds.next ();
+
+ if ( !reportPluginsByIds.containsKey ( generatedReportPluginsId ) )
+ {
+ model.getReporting ().addPlugin (
+ (ReportPlugin) generatedReportPluginsByIds.get ( generatedReportPluginsId )
+ );
+ }
+ else
+ {
+ getLogger ().warn ( "Can not override report: " + generatedReportPluginsId );
+ }
+ }
+ }
+ }
+}
Added: maven/sandbox/trunk/archetypeng/archetype-common/src/main/java/org/apache/maven/archetype/common/FileCharsetDetector.java
URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/archetypeng/archetype-common/src/main/java/org/apache/maven/archetype/common/FileCharsetDetector.java?rev=570618&view=auto
==============================================================================
--- maven/sandbox/trunk/archetypeng/archetype-common/src/main/java/org/apache/maven/archetype/common/FileCharsetDetector.java (added)
+++ maven/sandbox/trunk/archetypeng/archetype-common/src/main/java/org/apache/maven/archetype/common/FileCharsetDetector.java Tue Aug 28 19:43:33 2007
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.maven.archetype.common;
+
+import org.codehaus.plexus.logging.AbstractLogEnabled;
+
+import org.mozilla.intl.chardet.nsDetector;
+import org.mozilla.intl.chardet.nsICharsetDetectionObserver;
+import org.mozilla.intl.chardet.nsPSMDetector;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+/**
+ * @author rafale
+ */
+public class FileCharsetDetector
+extends AbstractLogEnabled
+{
+ private String charset = null;
+
+ private boolean found = false;
+
+ public FileCharsetDetector ( File detectedFile )
+ throws FileNotFoundException, IOException
+ {
+ nsDetector det = new nsDetector ( nsPSMDetector.ALL );
+
+ det.Init (
+ new nsICharsetDetectionObserver ()
+ {
+ public void Notify ( String charset )
+ {
+ FileCharsetDetector.this.charset = charset;
+ FileCharsetDetector.this.found = true;
+ }
+ }
+ );
+
+ BufferedInputStream imp = new BufferedInputStream ( new FileInputStream ( detectedFile ) );
+
+ byte[] buf = new byte[1024];
+ int len;
+ boolean done = false;
+ boolean isAscii = true;
+
+ while ( ( len = imp.read ( buf, 0, buf.length ) ) != -1 )
+ {
+ // Check if the stream is only ascii.
+ if ( isAscii )
+ {
+ isAscii = det.isAscii ( buf, len );
+ }
+
+ // DoIt if non-ascii and not done yet.
+ if ( !isAscii && !done )
+ {
+ done = det.DoIt ( buf, len, false );
+ found = done;
+ }
+ }
+ det.DataEnd ();
+
+ if ( !isFound () )
+ {
+ String[] prob = det.getProbableCharsets ();
+
+// if ( Arrays.asList ( prob ).contains ( "windows-1252" ) )
+// {
+// charset = "ISO-8859-1";
+// }
+// else
+ if ( prob.length > 0 )
+ {
+ charset = prob[0];
+ }
+ }
+
+ if ( isAscii )
+ {
+ charset = "ASCII";
+ }
+ }
+
+ public String getCharset ()
+ {
+ return charset;
+ }
+
+ public boolean isFound ()
+ {
+ return found;
+ }
+}
Added: maven/sandbox/trunk/archetypeng/archetype-common/src/main/java/org/apache/maven/archetype/common/ListScanner.java
URL: http://svn.apache.org/viewvc/maven/sandbox/trunk/archetypeng/archetype-common/src/main/java/org/apache/maven/archetype/common/ListScanner.java?rev=570618&view=auto
==============================================================================
--- maven/sandbox/trunk/archetypeng/archetype-common/src/main/java/org/apache/maven/archetype/common/ListScanner.java (added)
+++ maven/sandbox/trunk/archetypeng/archetype-common/src/main/java/org/apache/maven/archetype/common/ListScanner.java Tue Aug 28 19:43:33 2007
@@ -0,0 +1,568 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.maven.archetype.common;
+
+import org.codehaus.plexus.util.SelectorUtils;
+import org.codehaus.plexus.util.StringUtils;
+
+import java.io.File;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Class for scanning a directory for files/directories which match certain criteria.
+ *
+ * <p>These criteria consist of selectors and patterns which have been specified. With the selectors
+ * you can select which files you want to have included. Files which are not selected are excluded.
+ * With patterns you can include or exclude files based on their filename.</p>
+ *
+ * <p>The idea is simple. A given directory is recursively scanned for all files and directories.
+ * Each file/directory is matched against a set of selectors, including special support for matching
+ * against filenames with include and and exclude patterns. Only files/directories which match at
+ * least one pattern of the include pattern list or other file selector, and don't match any pattern
+ * of the exclude pattern list or fail to match against a required selector will be placed in the
+ * list of files/directories found.</p>
+ *
+ * <p>When no list of include patterns is supplied, "**" will be used, which means that everything
+ * will be matched. When no list of exclude patterns is supplied, an empty list is used, such that
+ * nothing will be excluded. When no selectors are supplied, none are applied.</p>
+ *
+ * <p>The filename pattern matching is done as follows: The name to be matched is split up in path
+ * segments. A path segment is the name of a directory or file, which is bounded by <code>
+ * File.separator</code> ('/' under UNIX, '\' under Windows). For example, "abc/def/ghi/xyz.java" is
+ * split up in the segments "abc", "def","ghi" and "xyz.java". The same is done for the pattern
+ * against which should be matched.</p>
+ *
+ * <p>The segments of the name and the pattern are then matched against each other. When '**' is
+ * used for a path segment in the pattern, it matches zero or more path segments of the name.</p>
+ *
+ * <p>There is a special case regarding the use of <code>File.separator</code>s at the beginning of
+ * the pattern and the string to match:<br>
+ * When a pattern starts with a <code>File.separator</code>, the string to match must also start
+ * with a <code>File.separator</code>. When a pattern does not start with a <code>
+ * File.separator</code>, the string to match may not start with a <code>File.separator</code>. When
+ * one of these rules is not obeyed, the string will not match.</p>
+ *
+ * <p>When a name path segment is matched against a pattern path segment, the following special
+ * characters can be used:<br>
+ * '*' matches zero or more characters<br>
+ * '?' matches one character.</p>
+ *
+ * <p>Examples:</p>
+ *
+ * <p>"**\*.class" matches all .class files/dirs in a directory tree.</p>
+ *
+ * <p>"test\a??.java" matches all files/dirs which start with an 'a', then two more characters and
+ * then ".java", in a directory called test.</p>
+ *
+ * <p>"**" matches everything in a directory tree.</p>
+ *
+ * <p>"**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where there is a parent
+ * directory called test (e.g. "abc\test\def\ghi\XYZ123").</p>
+ *
+ * <p>Case sensitivity may be turned off if necessary. By default, it is turned on.</p>
+ *
+ * <p>Example of usage:</p>
+ *
+ * <pre>
+ String[] includes = {"**\\*.class"};
+ String[] excludes = {"modules\\*\\**"};
+ ds.setIncludes(includes);
+ ds.setExcludes(excludes);
+ ds.setBasedir(new File("test"));
+ ds.setCaseSensitive(true);
+ ds.scan();
+
+ System.out.println("FILES:");
+ String[] files = ds.getIncludedFiles();
+ for (int i = 0; i < files.length; i++) {
+ System.out.println(files[i]);
+ }
+ * </pre>
+ *
+ * <p>This will scan a directory called test for .class files, but excludes all files in all proper
+ * subdirectories of a directory called "modules"</p>
+ *
+ * <p>This class was stealed from rg.coudehaus.plexus.util.DirectoryScanner and adapted to search
+ * from a List<String></p>
+ *
+ * @author Arnout J. Kuiper <a href="mailto:ajkuiper@wxs.nl">ajkuiper@wxs.nl</a>
+ * @author Magesh Umasankar
+ * @author <a href="mailto:bruce@callenish.com">Bruce Atherton</a>
+ * @author <a href="mailto:levylambert@tiscali-dsl.de">Antoine Levy-Lambert</a>
+ */
+public class ListScanner
+{
+ /**
+ * Patterns which should be excluded by default.
+ *
+ * @see #addDefaultExcludes()
+ */
+ public static final String[] DEFAULTEXCLUDES =
+ { // Miscellaneous typical temporary files
+ "**/*~", "**/#*#", "**/.#*", "**/%*%", "**/._*",
+
+ // CVS
+ "**/CVS", "**/CVS/**", "**/.cvsignore",
+
+ // SCCS
+ "**/SCCS", "**/SCCS/**",
+
+ // Visual SourceSafe
+ "**/vssver.scc",
+
+ // Subversion
+ "**/.svn", "**/.svn/**",
+
+ // Arch
+ "**/.arch-ids", "**/.arch-ids/**",
+
+ // Bazaar
+ "**/.bzr", "**/.bzr/**",
+
+ // SurroundSCM
+ "**/.MySCMServerInfo",
+
+ // Mac
+ "**/.DS_Store"
+ };
+
+ /**
+ * The base directory to be scanned.
+ */
+ protected String basedir;
+
+ /**
+ * Whether or not everything tested so far has been included.
+ */
+ protected boolean everythingIncluded = true;
+
+ /**
+ * The patterns for the files to be excluded.
+ */
+ protected String[] excludes;
+
+ /**
+ * The patterns for the files to be included.
+ */
+ protected String[] includes;
+
+ /**
+ * Whether or not the file system should be treated as a case sensitive one.
+ */
+ protected boolean isCaseSensitive = true;
+
+ /**
+ * Sole constructor.
+ */
+ public ListScanner ()
+ { }
+
+ public static String getDefaultExcludes ()
+ {
+ return StringUtils.join ( DEFAULTEXCLUDES, "," );
+ }
+
+ /**
+ * Tests whether or not a string matches against a pattern. The pattern may contain two special
+ * characters:<br>
+ * '*' means zero or more characters<br>
+ * '?' means one and only one character
+ *
+ * @param pattern The pattern to match against. Must not be <code>null</code>.
+ * @param str The string which must be matched against the pattern. Must not be <code>
+ * null</code>.
+ *
+ * @return <code>true</code> if the string matches against the pattern, or <code>false</code>
+ * otherwise.
+ */
+ public static boolean match ( String pattern, String str )
+ {
+ // default matches the SelectorUtils default
+ return match ( pattern, str, true );
+ }
+
+ /**
+ * Tests whether or not a string matches against a pattern. The pattern may contain two special
+ * characters:<br>
+ * '*' means zero or more characters<br>
+ * '?' means one and only one character
+ *
+ * @param pattern The pattern to match against. Must not be <code>null</code>.
+ * @param str The string which must be matched against the pattern. Must not be
+ * <code>null</code>.
+ * @param isCaseSensitive Whether or not matching should be performed case sensitively.
+ *
+ * @return <code>true</code> if the string matches against the pattern, or <code>false</code>
+ * otherwise.
+ */
+ protected static boolean match ( String pattern, String str, boolean isCaseSensitive )
+ {
+ return SelectorUtils.match ( pattern, str, isCaseSensitive );
+ }
+
+ /**
+ * Tests whether or not a given path matches a given pattern.
+ *
+ * @param pattern The pattern to match against. Must not be <code>null</code>.
+ * @param str The path to match, as a String. Must not be <code>null</code>.
+ *
+ * @return <code>true</code> if the pattern matches against the string, or <code>false</code>
+ * otherwise.
+ */
+ protected static boolean matchPath ( String pattern, String str )
+ {
+ // default matches the SelectorUtils default
+ return matchPath ( pattern, str, true );
+ }
+
+ /**
+ * Tests whether or not a given path matches a given pattern.
+ *
+ * @param pattern The pattern to match against. Must not be <code>null</code>.
+ * @param str The path to match, as a String. Must not be <code>null</code>.
+ * @param isCaseSensitive Whether or not matching should be performed case sensitively.
+ *
+ * @return <code>true</code> if the pattern matches against the string, or <code>false</code>
+ * otherwise.
+ */
+ protected static boolean matchPath ( String pattern, String str, boolean isCaseSensitive )
+ {
+ return
+ SelectorUtils.matchPath (
+ PathUtils.convertPathForOS ( pattern ),
+ PathUtils.convertPathForOS ( str ),
+ isCaseSensitive
+ );
+ }
+
+ /**
+ * Tests whether or not a given path matches the start of a given pattern up to the first "**".
+ *
+ * <p>This is not a general purpose test and should only be used if you can live with false
+ * positives. For example, <code>pattern=**\a</code> and <code>str=b</code> will yield <code>
+ * true</code>.</p>
+ *
+ * @param pattern The pattern to match against. Must not be <code>null</code>.
+ * @param str The path to match, as a String. Must not be <code>null</code>.
+ *
+ * @return whether or not a given path matches the start of a given pattern up to the first
+ * "**".
+ */
+ protected static boolean matchPatternStart ( String pattern, String str )
+ {
+ // default matches SelectorUtils default
+ return matchPatternStart ( pattern, str, true );
+ }
+
+ /**
+ * Tests whether or not a given path matches the start of a given pattern up to the first "**".
+ *
+ * <p>This is not a general purpose test and should only be used if you can live with false
+ * positives. For example, <code>pattern=**\a</code> and <code>str=b</code> will yield <code>
+ * true</code>.</p>
+ *
+ * @param pattern The pattern to match against. Must not be <code>null</code>.
+ * @param str The path to match, as a String. Must not be <code>null</code>.
+ * @param isCaseSensitive Whether or not matching should be performed case sensitively.
+ *
+ * @return whether or not a given path matches the start of a given pattern up to the first
+ * "**".
+ */
+ protected static boolean matchPatternStart (
+ String pattern,
+ String str,
+ boolean isCaseSensitive
+ )
+ {
+ return
+ SelectorUtils.matchPatternStart (
+ PathUtils.convertPathForOS ( pattern ),
+ PathUtils.convertPathForOS ( str ),
+ isCaseSensitive
+ );
+ }
+
+ /**
+ * Adds default exclusions to the current exclusions set.
+ */
+ public void addDefaultExcludes ()
+ {
+ int excludesLength = ( excludes == null ) ? 0 : excludes.length;
+ String[] newExcludes;
+ newExcludes = new String[excludesLength + DEFAULTEXCLUDES.length];
+ if ( excludesLength > 0 )
+ {
+ System.arraycopy ( excludes, 0, newExcludes, 0, excludesLength );
+ }
+ for ( int i = 0; i < DEFAULTEXCLUDES.length; i++ )
+ {
+ newExcludes[i + excludesLength] =
+ DEFAULTEXCLUDES[i].replace ( '/', File.separatorChar ).replace (
+ '\\',
+ File.separatorChar
+ );
+ }
+ excludes = newExcludes;
+ }
+
+ /**
+ * Returns the base directory to be scanned. This is the directory which is scanned recursively.
+ *
+ * @return the base directory to be scanned
+ */
+ public String getBasedir ()
+ {
+ return basedir;
+ }
+
+ /**
+ * Sets the base directory to be scanned. This is the directory which is scanned recursively.
+ * This directory is normalized for multiple os's (all / and \\ are replaced with
+ * File.separatorChar
+ *
+ * @param basedir The base directory for scanning. Should not be <code>null</code>.
+ */
+ public void setBasedir ( String basedir )
+ {
+ this.basedir = basedir;
+ }
+
+ /**
+ * Sets whether or not the file system should be regarded as case sensitive.
+ *
+ * @param isCaseSensitive whether or not the file system should be regarded as a case
+ * sensitive one
+ */
+ public void setCaseSensitive ( boolean isCaseSensitive )
+ {
+ this.isCaseSensitive = isCaseSensitive;
+ }
+
+ /**
+ * Sets the list of exclude patterns to use. All '/' and '\' characters are replaced by <code>
+ * File.separatorChar</code>, so the separator used need not match <code>
+ * File.separatorChar</code>.
+ *
+ * <p>When a pattern ends with a '/' or '\', "**" is appended.</p>
+ *
+ * @param excludes A list of exclude patterns. May be <code>null</code>, indicating that no
+ * files should be excluded. If a non-<code>null</code> list is given, all
+ * elements must be non-<code>null</code>.
+ */
+ public void setExcludes ( List excludesList )
+ {
+ String[] excludes = (String[]) excludesList.toArray ( new String[excludesList.size ()] );
+ if ( excludes == null )
+ {
+ this.excludes = null;
+ }
+ else
+ {
+ setExcludes ( excludes );
+ }
+ }
+
+ public void setExcludes ( String excludes )
+ {
+ if ( excludes == null )
+ {
+ this.excludes = null;
+ }
+ else
+ {
+ setExcludes ( StringUtils.split ( excludes, "," ) );
+ }
+ }
+
+ /**
+ * Sets the list of include patterns to use. All '/' and '\' characters are replaced by <code>
+ * File.separatorChar</code>, so the separator used need not match <code>
+ * File.separatorChar</code>.
+ *
+ * <p>When a pattern ends with a '/' or '\', "**" is appended.</p>
+ *
+ * @param includes A list of include patterns. May be <code>null</code>, indicating that all
+ * files should be included. If a non-<code>null</code> list is given, all
+ * elements must be non-<code>null</code>.
+ */
+ public void setIncludes ( List includesList )
+ {
+ String[] includes = (String[]) includesList.toArray ( new String[includesList.size ()] );
+ if ( includes == null )
+ {
+ this.includes = null;
+ }
+ else
+ {
+ setIncludes ( includes );
+ }
+ }
+
+ public void setIncludes ( String includes )
+ {
+ if ( includes == null )
+ {
+ this.includes = null;
+ }
+ else
+ {
+ setIncludes ( StringUtils.split ( includes, "," ) );
+ }
+ }
+
+ /**
+ * Scans the base directory for files which match at least one include pattern and don't match
+ * any exclude patterns. If there are selectors then the files must pass muster there, as well.
+ *
+ * @exception IllegalStateException if the base directory was set incorrectly (i.e. if it is
+ * <code>null</code>, doesn't exist, or isn't a directory).
+ */
+ public List scan ( List files )
+ throws IllegalStateException
+ {
+// System.err.println("Scanning \nbasedir="+basedir +
+// " \nincludes=" + java.util.Arrays.toString(includes) +
+// " \nexcludes=" + java.util.Arrays.toString(excludes) +
+// " \non files="+files);
+ if ( basedir == null )
+ {
+ throw new IllegalStateException ( "No basedir set" );
+ }
+
+ if ( includes == null )
+ {
+ // No includes supplied, so set it to 'matches all'
+ includes = new String[1];
+ includes[0] = "**";
+ }
+ if ( excludes == null )
+ {
+ excludes = new String[0];
+ }
+
+ List result = new ArrayList ();
+
+ Iterator iterator = files.iterator ();
+ while ( iterator.hasNext () )
+ {
+ String fileName = (String) iterator.next ();
+// System.err.println("Checking "+(isIncluded ( fileName )?"I":"-")+(isExcluded ( fileName )?"E":"-")+fileName);
+ if ( isIncluded ( fileName ) && !isExcluded ( fileName ) )
+ {
+ result.add ( fileName );
+ }
+ }
+// System.err.println("Result "+result+"\n\n\n");
+ return result;
+ }
+
+ /**
+ * Tests whether or not a name matches against at least one exclude pattern.
+ *
+ * @param name The name to match. Must not be <code>null</code>.
+ *
+ * @return <code>true</code> when the name matches against at least one exclude pattern, or
+ * <code>false</code> otherwise.
+ */
+ protected boolean isExcluded ( String name )
+ {
+ return matchesPatterns ( name, excludes );
+ }
+
+ /**
+ * Tests whether or not a name matches against at least one include pattern.
+ *
+ * @param name The name to match. Must not be <code>null</code>.
+ *
+ * @return <code>true</code> when the name matches against at least one include pattern, or
+ * <code>false</code> otherwise.
+ */
+ protected boolean isIncluded ( String name )
+ {
+ return matchesPatterns ( name, includes );
+ }
+
+ /**
+ * Tests whether or not a name matches against at least one include pattern.
+ *
+ * @param name The name to match. Must not be <code>null</code>.
+ * @param patterns The list of patterns to match.
+ *
+ * @return <code>true</code> when the name matches against at least one include pattern, or
+ * <code>false</code> otherwise.
+ */
+ protected boolean matchesPatterns ( String name, String[] patterns )
+ {
+ // avoid extra object creation in the loop
+ String path = null;
+
+ String baseDir = getBasedir ();
+ if ( baseDir.length () > 0 )
+ {
+ baseDir = baseDir.concat ( File.separator );
+ }
+
+ for ( int i = 0; i < patterns.length; i++ )
+ {
+ path = PathUtils.convertPathForOS ( baseDir + patterns[i] );
+// System.err.println("path="+path);
+ if ( matchPath ( path, name, isCaseSensitive ) )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void setExcludes ( String[] excludes )
+ {
+ this.excludes = setPatterns ( excludes );
+ }
+
+ private void setIncludes ( String[] includes )
+ {
+ this.includes = setPatterns ( includes );
+ }
+
+ private String[] setPatterns ( String[] patterns )
+ {
+ String[] result = null;
+ if ( ( patterns != null ) && ( patterns.length > 0 ) )
+ {
+ result = new String[patterns.length];
+ for ( int i = 0; i < patterns.length; i++ )
+ {
+ String pattern = patterns[i].trim ();
+
+ // don't normalize the pattern here, we internalize the normalization
+ // just normalize for comparison purposes
+ if ( PathUtils.convertPathForOS ( pattern ).endsWith ( File.separator ) )
+ {
+ pattern += "**";
+ }
+ result[i] = pattern;
+ }
+ }
+ return result;
+ }
+}