This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tomcat-jakartaee-migration.git

commit e963951d3f3a7ac0785815dfe44bd008425ccd43
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Tue Feb 9 17:00:03 2021 +0000

    Fix #3 Add support for excluding files from conversion
---
 CHANGES.md                                         |   3 +-
 .../org/apache/tomcat/jakartaee/GlobMatcher.java   | 243 +++++++++++++++++++++
 .../org/apache/tomcat/jakartaee/Migration.java     |  65 +++++-
 .../org/apache/tomcat/jakartaee/MigrationCLI.java  |   7 +-
 .../org/apache/tomcat/jakartaee/MigrationTask.java |  17 ++
 .../tomcat/jakartaee/LocalStrings.properties       |  12 +-
 6 files changed, 338 insertions(+), 9 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 9b23746..4c6b15d 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -22,7 +22,8 @@
 - Ensure that all the Manifest attributes are processed during the migration 
process. (markt)
 - Include `.properties` and `.json` files in the conversion process. (markt)
 - Replace `-verbose` with `-logLevel=` to provide more control over logging 
level. (markt)
-- Fix [#13](https://github.com/apache/tomcat-jakartaee-migration/issues/13). 
Refactor mapping of log messages to log levels. (markt) 
+- Fix [#13](https://github.com/apache/tomcat-jakartaee-migration/issues/13). 
Refactor mapping of log messages to log levels. (markt)
+- Fix [#3](https://github.com/apache/tomcat-jakartaee-migration/issues/3). Add 
support for excluding files from conversion. (markt)
 
 ## 0.1.0
 
diff --git a/src/main/java/org/apache/tomcat/jakartaee/GlobMatcher.java 
b/src/main/java/org/apache/tomcat/jakartaee/GlobMatcher.java
new file mode 100644
index 0000000..33dc338
--- /dev/null
+++ b/src/main/java/org/apache/tomcat/jakartaee/GlobMatcher.java
@@ -0,0 +1,243 @@
+/*
+ *  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.tomcat.jakartaee;
+
+import java.util.Set;
+
+/**
+ * <p>This is a utility class to match file globs.
+ * The class has been derived from
+ * org.apache.tools.ant.types.selectors.SelectorUtils.
+ * </p>
+ * <p>All methods are static.</p>
+ */
+public final class GlobMatcher {
+
+    /**
+     * Tests whether or not a given file name matches any file name pattern in
+     * the given set. The match is performed case-sensitively.
+     *
+     * @see #match(String, String, boolean)
+     *
+     * @param patternSet The pattern set to match against. Must not be
+     *                <code>null</code>.
+     * @param fileName The file name to match, as a String. Must not be
+     *                <code>null</code>. It must be just a file name, without
+     *                a path.
+     * @param caseSensitive Whether or not matching should be performed
+     *                        case sensitively.
+     *
+     * @return <code>true</code> if any pattern in the set matches against the
+     *         file name, or <code>false</code> otherwise.
+     */
+    public static boolean matchName(Set<String> patternSet, String fileName, 
boolean caseSensitive) {
+        char[] fileNameArray = fileName.toCharArray();
+        for (String pattern: patternSet) {
+            if (match(pattern, fileNameArray, caseSensitive)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * 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 caseSensitive 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.
+     */
+    public static boolean match(String pattern, String str,
+            boolean caseSensitive) {
+
+        return match(pattern, str.toCharArray(), caseSensitive);
+    }
+
+
+    /**
+     * 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 strArr  The character array which must be matched against the
+     *                pattern. Must not be <code>null</code>.
+     * @param caseSensitive 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.
+     */
+    private static boolean match(String pattern, char[] strArr,
+                                boolean caseSensitive) {
+        char[] patArr = pattern.toCharArray();
+        int patIdxStart = 0;
+        int patIdxEnd = patArr.length - 1;
+        int strIdxStart = 0;
+        int strIdxEnd = strArr.length - 1;
+        char ch;
+
+        boolean containsStar = false;
+        for (char c : patArr) {
+            if (c == '*') {
+                containsStar = true;
+                break;
+            }
+        }
+
+        if (!containsStar) {
+            // No '*'s, so we make a shortcut
+            if (patIdxEnd != strIdxEnd) {
+                return false; // Pattern and string do not have the same size
+            }
+            for (int i = 0; i <= patIdxEnd; i++) {
+                ch = patArr[i];
+                if (ch != '?') {
+                    if (different(caseSensitive, ch, strArr[i])) {
+                        return false; // Character mismatch
+                    }
+                }
+            }
+            return true; // String matches against pattern
+        }
+
+        if (patIdxEnd == 0) {
+            return true; // Pattern contains only '*', which matches anything
+        }
+
+        // Process characters before first star
+        while (true) {
+            ch = patArr[patIdxStart];
+            if (ch == '*' || strIdxStart > strIdxEnd) {
+                break;
+            }
+            if (ch != '?') {
+                if (different(caseSensitive, ch, strArr[strIdxStart])) {
+                    return false; // Character mismatch
+                }
+            }
+            patIdxStart++;
+            strIdxStart++;
+        }
+        if (strIdxStart > strIdxEnd) {
+            // All characters in the string are used. Check if only '*'s are
+            // left in the pattern. If so, we succeeded. Otherwise failure.
+            return allStars(patArr, patIdxStart, patIdxEnd);
+        }
+
+        // Process characters after last star
+        while (true) {
+            ch = patArr[patIdxEnd];
+            if (ch == '*' || strIdxStart > strIdxEnd) {
+                break;
+            }
+            if (ch != '?') {
+                if (different(caseSensitive, ch, strArr[strIdxEnd])) {
+                    return false; // Character mismatch
+                }
+            }
+            patIdxEnd--;
+            strIdxEnd--;
+        }
+        if (strIdxStart > strIdxEnd) {
+            // All characters in the string are used. Check if only '*'s are
+            // left in the pattern. If so, we succeeded. Otherwise failure.
+            return allStars(patArr, patIdxStart, patIdxEnd);
+        }
+
+        // process pattern between stars. padIdxStart and patIdxEnd point
+        // always to a '*'.
+        while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
+            int patIdxTmp = -1;
+            for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
+                if (patArr[i] == '*') {
+                    patIdxTmp = i;
+                    break;
+                }
+            }
+            if (patIdxTmp == patIdxStart + 1) {
+                // Two stars next to each other, skip the first one.
+                patIdxStart++;
+                continue;
+            }
+            // Find the pattern between padIdxStart & padIdxTmp in str between
+            // strIdxStart & strIdxEnd
+            int patLength = (patIdxTmp - patIdxStart - 1);
+            int strLength = (strIdxEnd - strIdxStart + 1);
+            int foundIdx = -1;
+            strLoop:
+            for (int i = 0; i <= strLength - patLength; i++) {
+                for (int j = 0; j < patLength; j++) {
+                    ch = patArr[patIdxStart + j + 1];
+                    if (ch != '?') {
+                        if (different(caseSensitive, ch,
+                                      strArr[strIdxStart + i + j])) {
+                            continue strLoop;
+                        }
+                    }
+                }
+
+                foundIdx = strIdxStart + i;
+                break;
+            }
+
+            if (foundIdx == -1) {
+                return false;
+            }
+
+            patIdxStart = patIdxTmp;
+            strIdxStart = foundIdx + patLength;
+        }
+
+        // All characters in the string are used. Check if only '*'s are left
+        // in the pattern. If so, we succeeded. Otherwise failure.
+        return allStars(patArr, patIdxStart, patIdxEnd);
+    }
+
+    private static boolean allStars(char[] chars, int start, int end) {
+        for (int i = start; i <= end; ++i) {
+            if (chars[i] != '*') {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static boolean different(
+        boolean caseSensitive, char ch, char other) {
+        return caseSensitive
+            ? ch != other
+            : Character.toUpperCase(ch) != Character.toUpperCase(other);
+    }
+
+}
diff --git a/src/main/java/org/apache/tomcat/jakartaee/Migration.java 
b/src/main/java/org/apache/tomcat/jakartaee/Migration.java
index 153345a..e8c39b6 100644
--- a/src/main/java/org/apache/tomcat/jakartaee/Migration.java
+++ b/src/main/java/org/apache/tomcat/jakartaee/Migration.java
@@ -26,7 +26,9 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Enumeration;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.jar.JarEntry;
 import java.util.logging.Level;
@@ -49,11 +51,46 @@ public class Migration {
     private static final Logger logger = 
Logger.getLogger(Migration.class.getCanonicalName());
     private static final StringManager sm = 
StringManager.getManager(Migration.class);
 
+    private static final Set<String> DEFAULT_EXCLUDES = new HashSet<>();
+
+    static {
+        // Apache Commons
+        DEFAULT_EXCLUDES.add("commons-codec-*.jar");
+        DEFAULT_EXCLUDES.add("commons-lang-*.jar");
+        // Apache HTTP Components
+        DEFAULT_EXCLUDES.add("httpclient-*.jar");
+        DEFAULT_EXCLUDES.add("httpcore-*.jar");
+        // ASM
+        DEFAULT_EXCLUDES.add("asm-*.jar");
+        // AspectJ
+        DEFAULT_EXCLUDES.add("aspectjweaver-*.jar");
+        // Bouncy Castle JCE provider
+        DEFAULT_EXCLUDES.add("bcprov*.jar");
+        DEFAULT_EXCLUDES.add("bcpkix*.jar");
+        // Hystrix
+        DEFAULT_EXCLUDES.add("hystrix-core-*.jar");
+        DEFAULT_EXCLUDES.add("hystrix-serialization-*.jar");
+        // Jackson
+        DEFAULT_EXCLUDES.add("jackson-annotations-*.jar");
+        DEFAULT_EXCLUDES.add("jackson-core-*.jar");
+        DEFAULT_EXCLUDES.add("jackson-module-afterburner-*.jar");
+        // Logging
+        DEFAULT_EXCLUDES.add("jul-to-slf4j-*.jar");
+        DEFAULT_EXCLUDES.add("log4j-to-slf4j-*.jar");
+        DEFAULT_EXCLUDES.add("slf4j-api-*.jar");
+        // Spring
+        DEFAULT_EXCLUDES.add("spring-aop-*.jar");
+        DEFAULT_EXCLUDES.add("spring-expression-*.jar");
+        DEFAULT_EXCLUDES.add("spring-security-crypto-*.jar");
+        DEFAULT_EXCLUDES.add("spring-security-rsa-*.jar");
+    }
+
     private EESpecProfile profile = EESpecProfile.TOMCAT;
     private boolean zipInMemory;
     private File source;
     private File destination;
     private final List<Converter> converters;
+    private final Set<String> excludes = new HashSet<>();
 
     public Migration() {
         // Initialise the converters
@@ -89,6 +126,10 @@ public class Migration {
         this.zipInMemory = zipInMemory;
     }
 
+    public void addExclude(String exclude) {
+        this.excludes.add(exclude);
+    }
+
     public void setSource(File source) {
         if (!source.canRead()) {
             throw new 
IllegalArgumentException(sm.getString("migration.cannotReadSource",
@@ -181,7 +222,7 @@ public class Migration {
                 String destName = profile.convert(sourceName);
                 JarEntry destEntry = new JarEntry(destName);
                 zipOs.putNextEntry(destEntry);
-                migrateStream(destEntry.getName(), zipIs, zipOs);
+                migrateStream(sourceName, zipIs, zipOs);
             }
         } catch (ZipException ze) {
             logger.log(Level.SEVERE, sm.getString("migration.archiveFailed", 
name), ze);
@@ -231,7 +272,10 @@ public class Migration {
 
 
     private void migrateStream(String name, InputStream src, OutputStream 
dest) throws IOException {
-        if (isArchive(name)) {
+        if (isExcluded(name)) {
+            Util.copy(src, dest);
+            logger.log(Level.INFO, sm.getString("migration.skip", name));
+        } else if (isArchive(name)) {
             if (zipInMemory) {
                 logger.log(Level.INFO, 
sm.getString("migration.archive.memory", name));
                 migrateArchiveInMemory(src, dest);
@@ -252,11 +296,26 @@ public class Migration {
     }
 
 
-    private static boolean isArchive(String fileName) {
+    private boolean isArchive(String fileName) {
         return fileName.endsWith(".jar") || fileName.endsWith(".war") || 
fileName.endsWith(".zip");
     }
 
 
+    private boolean isExcluded(String name) {
+        File f = new File(name);
+        String filename = f.getName();
+
+        if (GlobMatcher.matchName(DEFAULT_EXCLUDES, filename, true)) {
+            return true;
+        }
+
+        if (GlobMatcher.matchName(excludes, filename, true)) {
+            return true;
+        }
+
+        return false;
+    }
+
     private static class RenamableZipArchiveEntry extends ZipArchiveEntry {
 
         public RenamableZipArchiveEntry(ZipArchiveEntry entry) throws 
ZipException {
diff --git a/src/main/java/org/apache/tomcat/jakartaee/MigrationCLI.java 
b/src/main/java/org/apache/tomcat/jakartaee/MigrationCLI.java
index 64af9b9..36e9dc5 100644
--- a/src/main/java/org/apache/tomcat/jakartaee/MigrationCLI.java
+++ b/src/main/java/org/apache/tomcat/jakartaee/MigrationCLI.java
@@ -30,6 +30,7 @@ public class MigrationCLI {
 
     private static final StringManager sm = 
StringManager.getManager(MigrationCLI.class);
 
+    private static final String EXCLUDE_ARG = "-exclude=";
     private static final String LOGLEVEL_ARG = "-logLevel=";
     private static final String PROFILE_ARG = "-profile=";
     // Will be removed for 1.0.0
@@ -51,7 +52,11 @@ public class MigrationCLI {
         Iterator<String> iter = arguments.iterator();
         while (iter.hasNext()) {
             String argument = iter.next();
-            if (argument.startsWith(LOGLEVEL_ARG)) {
+            if (argument.startsWith(EXCLUDE_ARG)) {
+                iter.remove();
+                String exclude = argument.substring(EXCLUDE_ARG.length());
+                migration.addExclude(exclude);
+            } else if (argument.startsWith(LOGLEVEL_ARG)) {
                 iter.remove();
                 String logLevelName = 
argument.substring(LOGLEVEL_ARG.length());
                 Level level = null;
diff --git a/src/main/java/org/apache/tomcat/jakartaee/MigrationTask.java 
b/src/main/java/org/apache/tomcat/jakartaee/MigrationTask.java
index c19599e..39f31dd 100644
--- a/src/main/java/org/apache/tomcat/jakartaee/MigrationTask.java
+++ b/src/main/java/org/apache/tomcat/jakartaee/MigrationTask.java
@@ -33,6 +33,7 @@ public class MigrationTask extends Task {
     private File dest;
     private String profile = EESpecProfile.TOMCAT.toString();
     private boolean zipInMemory = false;
+    private String excludes;
 
     public void setSrc(File src) {
         this.src = src;
@@ -50,6 +51,16 @@ public class MigrationTask extends Task {
         this.zipInMemory = zipInMemory;
     }
 
+    /**
+     * Set exclusion patterns.
+     *
+     * @param excludes  Comma separated, case sensitive list of glob patterns
+     *                  for files to exclude
+     */
+    public void setExcludes(String excludes) {
+        this.excludes = excludes;
+    }
+
     @Override
     public void execute() throws BuildException {
         // redirect the log messages to Ant
@@ -73,6 +84,12 @@ public class MigrationTask extends Task {
         migration.setDestination(dest);
         migration.setEESpecProfile(profile);
         migration.setZipInMemory(zipInMemory);
+        if (this.excludes != null) {
+            String[] excludes= this.excludes.split(",");
+            for (String exclude : excludes) {
+                migration.addExclude(exclude);
+            }
+        }
 
         try {
             migration.execute();
diff --git 
a/src/main/resources/org/apache/tomcat/jakartaee/LocalStrings.properties 
b/src/main/resources/org/apache/tomcat/jakartaee/LocalStrings.properties
index 5515874..655bc10 100644
--- a/src/main/resources/org/apache/tomcat/jakartaee/LocalStrings.properties
+++ b/src/main/resources/org/apache/tomcat/jakartaee/LocalStrings.properties
@@ -16,20 +16,24 @@
 classConverter.converted=Migrated class [{0}]
 classConverter.noConversion=No conversion necessary for [{0}] 
 
-migration.archive.complete=Migration of archive [{0}] is complete
-migration.archive.memory=Migrating archive [{0}] using in memory copy
-migration.archive.stream=Migrating archive [{0}] using streaming
+migration.archive.complete=Migration finished for archive [{0}]
+migration.archive.memory=Migration starting for archive [{0}] using in memory 
copy
+migration.skip=Migration skipped for archive [{0}] because it is excluded (the 
archive was copied unchanged)
+migration.archive.stream=Migration starting for archive [{0}] using streaming
 migration.archiveFailed=Failed to migrate archive [{0}]. Using the 
"-zipInMemory" option may help. 
 migration.cannotReadSource=Cannot read source location [{0}]
 migration.done=Migration completed successfully in [{0}] milliseconds
-migration.entry=Migrating Jar entry [{0}]
 migration.error=Error performing migration
 migration.execute=Performing migration from source [{0}] to destination [{1}] 
with Jakarta EE specification profile [{2}]
 migration.mkdirError=Error creating destination directory [{0}]
 migration.removeSignature=Remove cryptographic signature for [{0}]
+migration.skip=Migration skipped for archive [{0}] because it is excluded (the 
archive was copied unchanged)
 migration.skipSignatureFile=Drop cryptographic signature file [{0}]
 migration.usage=Usage: Migration [options] <source> <destination>\n\
 where options includes:\n\
+\    -exclude=<glob pattern to exclude>\n\
+\                This option may be used multiple times. Wild cards '*'\n\
+\                and '?' are supported. Matching is case sensitive.\n\
 \    -logLevel=<name of java.util.logging.level enum value>\n\
 \                Useful values are INFO (default), FINE or FINEST\n\
 \    -profile=<profile name>\n\


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to