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:[EMAIL PROTECTED]">[EMAIL PROTECTED]</a> + * @author Magesh Umasankar + * @author <a href="mailto:[EMAIL PROTECTED]">Bruce Atherton</a> + * @author <a href="mailto:[EMAIL PROTECTED]">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; + } +}