Moved globbing support into new type
Use Glob everywhere
New patch: DeleteClasses
diff --git a/data/input/deleteClasses/m2repo/org/apache/batik/org.apache.batik.pdf/1.6.0.v201105071520/org.apache.batik.pdf-1.6.0.v201105071520.jar b/data/input/deleteClasses/m2repo/org/apache/batik/org.apache.batik.pdf/1.6.0.v201105071520/org.apache.batik.pdf-1.6.0.v201105071520.jar
new file mode 100644
index 0000000..b6dce60
--- /dev/null
+++ b/data/input/deleteClasses/m2repo/org/apache/batik/org.apache.batik.pdf/1.6.0.v201105071520/org.apache.batik.pdf-1.6.0.v201105071520.jar
Binary files differ
diff --git a/data/input/deleteClasses/m2repo/org/apache/batik/org.apache.batik.pdf/1.6.0.v201105071520/org.apache.batik.pdf-1.6.0.v201105071520.pom b/data/input/deleteClasses/m2repo/org/apache/batik/org.apache.batik.pdf/1.6.0.v201105071520/org.apache.batik.pdf-1.6.0.v201105071520.pom
new file mode 100644
index 0000000..9098adb
--- /dev/null
+++ b/data/input/deleteClasses/m2repo/org/apache/batik/org.apache.batik.pdf/1.6.0.v201105071520/org.apache.batik.pdf-1.6.0.v201105071520.pom
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.apache.batik</groupId>
+  <artifactId>org.apache.batik.pdf</artifactId>
+  <version>1.6.0.v201105071520</version>
+  <name>Apache Batik PDF</name>
+</project>
diff --git a/src/main/groovy/m4e/AnalyzeCmd.groovy b/src/main/groovy/m4e/AnalyzeCmd.groovy
index 914ef8f..7f2e819 100644
--- a/src/main/groovy/m4e/AnalyzeCmd.groovy
+++ b/src/main/groovy/m4e/AnalyzeCmd.groovy
@@ -59,8 +59,8 @@
     File repo
     File reportFile
     Calendar timestamp
-    Set<Pattern> ignores = new HashSet()
-    Set<Pattern> ignoreMissingSources = new HashSet()
+    Set<Glob> ignores = new HashSet()
+    Set<Glob> ignoreMissingSources = new HashSet()
     
     Analyzer( File repo, Calendar timestamp ) {
         this.repo = repo.canonicalFile
@@ -74,6 +74,8 @@
     }
     
     void loadIgnores( File file ) {
+        String manyRegexp = '[^ :]*'
+        
         file.eachLine {
             String line = it.substringBefore( '#' ).trim()
             
@@ -81,13 +83,13 @@
                 return
             }
             
-            line = line.replace( '.', '\\.' ).replace( '*', '[^ :]*' ).replaceAll( '\\s+', ' ' )
+            line = line.replaceAll( '\\s+', ' ' )
             
             if( line.startsWith( 'MissingSources ' ) ) {
                 line = line.substringAfter( ' ' )
-                ignoreMissingSources << Pattern.compile( line )
+                ignoreMissingSources << new Glob( line, manyRegexp )
             } else {
-                ignores << Pattern.compile( line )
+                ignores << new Glob( line, manyRegexp )
             }
         }
     }
@@ -104,8 +106,8 @@
             def l = missingSource.findResults {
                 def key = it.key()
                 
-                for( Pattern p : ignoreMissingSources ) {
-                    if( p.matcher( key ).matches() ) {
+                for( Glob g : ignoreMissingSources ) {
+                    if( g.matches( key ) ) {
                         return null
                     }
                 }
@@ -330,14 +332,14 @@
     }
     
     void applyIgnores() {
-        Set<Pattern> unused = new HashSet( ignores )
+        Set<Glob> unused = new HashSet( ignores )
         
         problems = problems.findResults {
             String key = it.key()
             
-            for( Pattern p : ignores ) {
-                if( p.matcher( key ).matches() ) {
-                    unused.remove( p )
+            for( Glob g : ignores ) {
+                if( g.matches( key ) ) {
+                    unused.remove( g )
                     return null
                 }
             }
diff --git a/src/main/groovy/m4e/Glob.groovy b/src/main/groovy/m4e/Glob.groovy
new file mode 100644
index 0000000..6cff427
--- /dev/null
+++ b/src/main/groovy/m4e/Glob.groovy
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 27.07.2011 Aaron Digulla.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Aaron Digulla - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+package m4e
+
+import java.util.regex.Pattern
+
+class Glob {
+
+    private String pattern
+    private Pattern compiled
+    
+    Glob( String pattern, String manyRegexp = '.*' ) {
+        
+        this.pattern = pattern
+        
+        def parts = pattern.split( '\\*', -1 )
+        if( parts.size() > 1 ) {
+            pattern = parts.collect { Pattern.quote( it ) }.join( manyRegexp )
+            compiled = Pattern.compile( pattern )
+        }
+    }
+    
+    boolean matches( String text ) {
+        if( compiled ) {
+            return compiled.matcher( text ).matches()
+        }
+        
+        return pattern == text
+    }
+    
+    String toString() {
+        return pattern
+    }
+}
diff --git a/src/main/groovy/m4e/patch/DeleteClasses.groovy b/src/main/groovy/m4e/patch/DeleteClasses.groovy
new file mode 100644
index 0000000..8494c0e
--- /dev/null
+++ b/src/main/groovy/m4e/patch/DeleteClasses.groovy
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * Copyright (c) 27.07.2011 Aaron Digulla.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Aaron Digulla - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+package m4e.patch
+
+import java.util.jar.Manifest
+import java.util.regex.Pattern
+import java.util.zip.ZipEntry
+import java.util.zip.ZipFile
+import java.util.zip.ZipOutputStream;
+import m4e.Glob
+import m4e.MavenRepositoryTools;
+import m4e.Pom;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class DeleteClasses extends Patch {
+    
+    Logger log = LoggerFactory.getLogger( DeleteClasses ) 
+
+    File repo
+    Glob keyPattern
+    List<Glob> patterns = []
+    
+    /** Delete class files from a bundle.
+     * 
+     *  <p>This patch is applied before Orbit bundles are renamed
+     */
+    DeleteClasses( File repo, String key, List<String> patterns ) {
+        
+        this.repo = repo
+        this.keyPattern = new Glob( key )
+        
+        patterns.each {
+            def p = new Glob( it )
+            this.patterns << p
+        }
+        
+        patterns << new Glob( 'META-INF/ECLIPSEF.SF' )
+        patterns << new Glob( 'META-INF/ECLIPSEF.RSA' )
+    }
+    
+    @Override
+    public void apply( Pom pom ) {
+        String key = pom.key()
+        if( !keyPattern.matches( key ) ) {
+            return
+        }
+        
+        File jarFile = MavenRepositoryTools.buildPath( repo, key, 'jar' )
+        assert jarFile.exists()
+        
+        log.info( 'Deleting classes from {}', jarFile )
+        
+        File tmp = MavenRepositoryTools.buildPath( repo, key, 'tmp' )
+        
+        tmp.withOutputStream {
+            def out = new ZipOutputStream( it )
+            
+            filterBundle( new ZipFile( jarFile ), out )
+            
+            out.close()
+        }
+        
+        File backup = new File( jarFile.toString() + '.bak' )
+        if( !backup.exists() ) {
+            jarFile.usefulRename( backup )
+        } else {
+            jarFile.usefulDelete()
+        }
+        
+        tmp.usefulRename( jarFile )
+    }
+
+    int count
+    
+    void filterBundle( ZipFile archive, ZipOutputStream out ) {
+        
+        archive.eachEntry { ZipEntry entry ->
+            
+            ZipEntry clone = new ZipEntry( entry.name )
+            clone.time = entry.time
+            clone.comment = entry.comment
+            clone.extra = entry.extra
+            
+            if( entry.name == 'META-INF/MANIFEST.MF' ) {
+                def m
+                archive.withInputStream( entry ) {
+                    m = new Manifest( it )
+                }
+                
+                m.entries.clear()
+                
+                clone.time = System.currentTimeMillis()
+                
+                out.putNextEntry( clone )
+                m.write( out )
+                out.closeEntry()
+                
+                return
+            }
+            
+            for( Glob g : patterns ) {
+                if( g.matches( entry.name ) ) {
+                    log.debug( 'Deleting {}', entry.name )
+                    count ++
+                    return
+                }
+            }
+            
+            out.putNextEntry( clone )
+            archive.withInputStream( entry ) {
+                out << it
+            }
+            out.closeEntry()
+        }
+        
+    }
+}
diff --git a/src/main/groovy/m4e/patch/OrbitPatch.groovy b/src/main/groovy/m4e/patch/OrbitPatch.groovy
index 565762b..588d39a 100644
--- a/src/main/groovy/m4e/patch/OrbitPatch.groovy
+++ b/src/main/groovy/m4e/patch/OrbitPatch.groovy
@@ -14,6 +14,7 @@
 import de.pdark.decentxml.Element
 import java.util.regex.Pattern
 import m4e.Dependency;
+import m4e.Glob
 import m4e.Pom
 
 class OrbitPatch extends Patch {
@@ -21,7 +22,7 @@
     GlobalPatches globalPatches
     File target
     
-    private List<Pattern> exclusionPatterns = null
+    private List<Glob> exclusionPatterns = null
     
     private final static String ORBIT_GROUP_ID = 'org.eclipse.orbit'
     private final static String ORBIT_ARTIFACT_ID_PREFIX = 'orbit.'
@@ -60,9 +61,9 @@
         }
     }
     
-    boolean excluded( String groupId ) {
-        for( Pattern p : exclusionPatterns ) {
-            if( p.matcher( groupId ).matches() ) {
+    boolean excluded( String text ) {
+        for( Glob g : exclusionPatterns ) {
+            if( g.matches( text ) ) {
                 return true
             }
         }
@@ -77,8 +78,7 @@
         
         def l = []
         for( String text : globalPatches.orbitExclusions ) {
-            text = text.replace( '.', '\\.' ).replace( '*', '.*' )
-            l << Pattern.compile( text )
+            l << new Glob( text )
         }
         
         exclusionPatterns = l
diff --git a/src/main/groovy/m4e/patch/QualifierPatch.groovy b/src/main/groovy/m4e/patch/QualifierPatch.groovy
index 4acec68..f5ef5d7 100644
--- a/src/main/groovy/m4e/patch/QualifierPatch.groovy
+++ b/src/main/groovy/m4e/patch/QualifierPatch.groovy
@@ -12,10 +12,11 @@
 package m4e.patch
 
 import java.util.regex.Pattern;
+import m4e.Glob;
 
 class QualifierPatch {
     /** Apply this patch to these POMs */
-    Pattern pattern
+    Glob pattern
     /** The new version string for matching POMs */
     String version
     
@@ -24,12 +25,12 @@
         this.version = version
     }
     
-    Pattern compile( String text ) {
-        return Pattern.compile( text.replace( '.', '\\.' ).replace( '*', '[^:]*' ) )
+    Glob compile( String text ) {
+        return new Glob( text, '[^:]*' )
     }
     
     boolean appliesTo( String key ) {
-        if( pattern.matcher( key ).matches() ) {
+        if( pattern.matches() ) {
             return true
         }
         
diff --git a/src/test/groovy/m4e/GlobTest.groovy b/src/test/groovy/m4e/GlobTest.groovy
new file mode 100644
index 0000000..2bc233e
--- /dev/null
+++ b/src/test/groovy/m4e/GlobTest.groovy
@@ -0,0 +1,51 @@
+package m4e;
+
+import static org.junit.Assert.*;
+import org.junit.Test;
+
+class GlobTest {
+
+    @Test
+    public void testNoPattern() throws Exception {
+        
+        def text = 'a6,.;:-_<>\\+"*%&/(){}'
+        def g = new Glob( text )
+        
+        assert g.matches( text )
+    }
+    
+    @Test
+    public void testPattern() throws Exception {
+        
+        def text = 'a*6,.;:-_<>\\+"*%&/(){}'
+        def g = new Glob( text )
+        
+        assert g.matches( 'a6,.;:-_<>\\+"*%&/(){}' )
+        assert g.matches( 'aa6,.;:-_<>\\+"*%&/(){}' )
+        assert g.matches( 'a66,.;:-_<>\\+"*%&/(){}' )
+        assert g.matches( 'ax6,.;:-_<>\\+"*%&/(){}' )
+    }
+    
+    @Test
+    public void testPatternPomKey() throws Exception {
+        
+        def text = 'org.apache.batik:org.apache.batik.pdf:1.6.0*'
+        def g = new Glob( text )
+        
+        assert g.matches( 'org.apache.batik:org.apache.batik.pdf:1.6.0' )
+        assert g.matches( 'org.apache.batik:org.apache.batik.pdf:1.6.0_v2012...' )
+    }
+    
+    @Test
+    public void testPatternPath() throws Exception {
+        
+        def text = 'org/apache/commons/*'
+        def g = new Glob( text )
+        
+        assert g.matches( 'org/apache/commons/io/CopyUtils.class' )
+        assert g.matches( 'org/apache/commons/io/output/ByteArrayOutputStream.class' )
+        assert g.matches( 'org/apache/commons/logging/Log.class' )
+        assert g.matches( 'org/apache/commons/' )
+        assert g.matches( 'org/apache/commons/logging/' )
+    }
+}
diff --git a/src/test/groovy/m4e/patch/DeleteClassesTest.groovy b/src/test/groovy/m4e/patch/DeleteClassesTest.groovy
new file mode 100644
index 0000000..7bda6f4
--- /dev/null
+++ b/src/test/groovy/m4e/patch/DeleteClassesTest.groovy
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 27.07.2011 Aaron Digulla.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Aaron Digulla - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+package m4e.patch;
+
+import static org.junit.Assert.*;
+import m4e.CommonTestCode;
+import m4e.Pom;
+import org.junit.Test;
+
+class DeleteClassesTest {
+
+    @Test
+    public void testDeleteCommonsFromBatikPDF() throws Exception {
+        
+        File root = CommonTestCode.prepareRepo( new File( 'data/input/deleteClasses' ), 'testDeleteCommonsFromBatikPDF' )
+        File repo = new File( root, 'm2repo' )
+        
+        def pom = Pom.load( new File( repo, 'org/apache/batik/org.apache.batik.pdf/1.6.0.v201105071520/org.apache.batik.pdf-1.6.0.v201105071520.pom' ) )
+        
+        def pattern = [ 'org/apache/commons/*' ]
+        def tool = new DeleteClasses( repo, 'org.apache.batik:org.apache.batik.pdf:1.6.0*', pattern )
+        
+        tool.apply( pom )
+        
+        assertEquals( 29, tool.count )
+    }
+}