You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by sc...@apache.org on 2005/04/07 01:35:44 UTC

svn commit: r160343 - in jakarta/commons/proper/io/trunk/src: java/org/apache/commons/io/FilenameUtils.java test/org/apache/commons/io/FilenameUtilsTestCase.java test/org/apache/commons/io/FilenameUtilsWildcardTestCase.java

Author: scolebourne
Date: Wed Apr  6 16:35:43 2005
New Revision: 160343

URL: http://svn.apache.org/viewcvs?view=rev&rev=160343
Log:
Make FilenameUtils equals methods case-sensitive, with some optional system case matching

Modified:
    jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/FilenameUtils.java
    jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/FilenameUtilsTestCase.java
    jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/FilenameUtilsWildcardTestCase.java

Modified: jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/FilenameUtils.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/FilenameUtils.java?view=diff&r1=160342&r2=160343
==============================================================================
--- jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/FilenameUtils.java (original)
+++ jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/FilenameUtils.java Wed Apr  6 16:35:43 2005
@@ -18,6 +18,7 @@
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.Stack;
 
 /**
@@ -28,10 +29,10 @@
  * This class aims to help avoid those problems.
  * <p>
  * Most methods on this class are designed to work the same on both Unix and Windows.
- * Both separators (forward and back) are recognised, and both sets of prefixes.
- * The comparison methods do differ by machine however, comparing case insensitive
- * on Windows and case sensitive on Unix.
- * See the javadoc of each method for details.
+ * Those that don't include 'System', 'Unix' or 'Windows' in their name.
+ * <p>
+ * Most methods recognise both separators (forward and back), and both
+ * sets of prefixes. See the javadoc of each method for details.
  * <p>
  * This class defines six components within a filename (example C:\dev\project\file.txt):
  * <ul>
@@ -681,27 +682,46 @@
 
     //-----------------------------------------------------------------------
     /**
+     * Checks whether two filenames are equal exactly.
+     * <p>
+     * No processing is performed on the filenames other than comparison,
+     * thus this is merely a null-safe case-sensitive equals.
+     *
+     * @param filename1  the first filename to query, may be null
+     * @param filename2  the second filename to query, may be null
+     * @return true if the filenames are equal, null equals null
+     */
+    public static boolean equals(String filename1, String filename2) {
+        return equals(filename1, filename2, false, false);
+    }
+
+    /**
      * Checks whether two filenames are equal using the case rules of the system.
      * <p>
      * No processing is performed on the filenames other than comparison.
-     * The check is case sensitive on Unix and case insensitive on Windows.
+     * The check is case-sensitive on Unix and case-insensitive on Windows.
      *
      * @param filename1  the first filename to query, may be null
      * @param filename2  the second filename to query, may be null
      * @return true if the filenames are equal, null equals null
      */
-    public static boolean equals(String filename1, String filename2) {
-        if (filename1 == filename2) {
-            return true;
-        }
-        if (filename1 == null || filename2 == null) {
-            return false;
-        }
-        if (SYSTEM_SEPARATOR == WINDOWS_SEPARATOR) {
-            return filename1.equalsIgnoreCase(filename2);
-        } else {
-            return filename1.equals(filename2);
-        }
+    public static boolean equalsOnSystem(String filename1, String filename2) {
+        return equals(filename1, filename2, true, false);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Checks whether two filenames are equal after both have been normalized.
+     * <p>
+     * Both filenames are first passed to {@link #normalize(String)}.
+     * The check is then performed in a case-sensitive manner.
+     *
+     * @param filename1  the first filename to query, may be null
+     * @param filename2  the second filename to query, may be null
+     * @return true if the filenames are equal, null equals null
+     */
+    public static boolean equalsNormalized(String filename1, String filename2) {
+        return equals(filename1, filename2, false, true);
     }
 
     /**
@@ -709,22 +729,43 @@
      * and using the case rules of the system.
      * <p>
      * Both filenames are first passed to {@link #normalize(String)}.
-     * The check is then performed case sensitive on Unix and case insensitive on Windows.
+     * The check is then performed case-sensitive on Unix and
+     * case-insensitive on Windows.
      *
      * @param filename1  the first filename to query, may be null
      * @param filename2  the second filename to query, may be null
      * @return true if the filenames are equal, null equals null
      */
-    public static boolean equalsNormalized(String filename1, String filename2) {
+    public static boolean equalsNormalizedOnSystem(String filename1, String filename2) {
+        return equals(filename1, filename2, true, true);
+    }
+
+    /**
+     * Checks whether two filenames are equal after both have been normalized
+     * and optionally using the case rules of the system.
+     * <p>
+     * Both filenames are first passed to {@link #normalize(String)}.
+     *
+     * @param filename1  the first filename to query, may be null
+     * @param filename2  the second filename to query, may be null
+     * @param system  whether to use the system (windows or unix)
+     * @param normalized  whether to normalize the filenames
+     * @return true if the filenames are equal, null equals null
+     */
+    private static boolean equals(
+            String filename1, String filename2,
+            boolean system, boolean normalized) {
         if (filename1 == filename2) {
             return true;
         }
         if (filename1 == null || filename2 == null) {
             return false;
         }
-        filename1 = normalize(filename1);
-        filename2 = normalize(filename2);
-        if (SYSTEM_SEPARATOR == WINDOWS_SEPARATOR) {
+        if (normalized) {
+            filename1 = normalize(filename1);
+            filename2 = normalize(filename2);
+        }
+        if (system && (SYSTEM_SEPARATOR == WINDOWS_SEPARATOR)) {
             return filename1.equalsIgnoreCase(filename2);
         } else {
             return filename1.equals(filename2);
@@ -733,12 +774,11 @@
 
     //-----------------------------------------------------------------------
     /**
-     * Checks whether the extension of the filename is that specified
-     * using the case rules of the system.
+     * Checks whether the extension of the filename is that specified.
      * <p>
      * This method obtains the extension as the textual part of the filename
      * after the last dot. There must be no directory separator after the dot.
-     * The extension check is case sensitive on Unix and case insensitive on Windows.
+     * The extension check is case-sensitive on all platforms.
      *
      * @param filename  the filename to query, null returns false
      * @param extension  the extension to check for, null or empty checks for no extension
@@ -752,20 +792,15 @@
             return (indexOfExtension(filename) == -1);
         }
         String fileExt = getExtension(filename);
-        if (SYSTEM_SEPARATOR == WINDOWS_SEPARATOR) {
-            return fileExt.equalsIgnoreCase(extension);
-        } else {
-            return fileExt.equals(extension);
-        }
+        return fileExt.equals(extension);
     }
 
     /**
-     * Checks whether the extension of the filename is one of those specified
-     * using the case rules of the system.
+     * Checks whether the extension of the filename is one of those specified.
      * <p>
      * This method obtains the extension as the textual part of the filename
      * after the last dot. There must be no directory separator after the dot.
-     * The extension check is case sensitive on Unix and case insensitive on Windows.
+     * The extension check is case-sensitive on all platforms.
      *
      * @param filename  the filename to query, null returns false
      * @param extensions  the extensions to check for, null checks for no extension
@@ -775,33 +810,24 @@
         if (filename == null) {
             return false;
         }
-        if (extensions == null) {
+        if (extensions == null || extensions.length == 0) {
             return (indexOfExtension(filename) == -1);
         }
         String fileExt = getExtension(filename);
-        if (SYSTEM_SEPARATOR == WINDOWS_SEPARATOR) {
-            for (int i = 0; i < extensions.length; i++) {
-                if (fileExt.equalsIgnoreCase(extensions[i])) {
-                    return true;
-                }
-            }
-        } else {
-            for (int i = 0; i < extensions.length; i++) {
-                if (fileExt.equals(extensions[i])) {
-                    return true;
-                }
+        for (int i = 0; i < extensions.length; i++) {
+            if (fileExt.equals(extensions[i])) {
+                return true;
             }
         }
         return false;
     }
 
     /**
-     * Checks whether the extension of the filename is one of those specified
-     * using the case rules of the system.
+     * Checks whether the extension of the filename is one of those specified.
      * <p>
      * This method obtains the extension as the textual part of the filename
      * after the last dot. There must be no directory separator after the dot.
-     * The extension check is case sensitive on Unix and case insensitive on Windows.
+     * The extension check is case-sensitive on all platforms.
      *
      * @param filename  the filename to query, null returns false
      * @param extensions  the extensions to check for, null checks for no extension
@@ -811,21 +837,27 @@
         if (filename == null) {
             return false;
         }
-        if (extensions == null) {
+        if (extensions == null || extensions.isEmpty()) {
             return (indexOfExtension(filename) == -1);
         }
-        String[] array = (String[]) extensions.toArray(new String[extensions.size()]);
-        return isExtension(filename, array);
+        String fileExt = getExtension(filename);
+        for (Iterator it = extensions.iterator(); it.hasNext();) {
+            if (fileExt.equals(it.next())) {
+                return true;
+            }
+        }
+        return false;
     }
 
     //-----------------------------------------------------------------------
     /**
-     * Checks a filename to see if it matches the specified wildcard matcher.
+     * Checks a filename to see if it matches the specified wildcard matcher,
+     * always testing case-sensitive.
      * <p>
      * The wildcard matcher uses the characters '?' and '*' to represent a
      * single or multiple wildcard characters.
      * This is the same as often found on Dos/Unix command lines.
-     * The extension check is case sensitive on Unix and case insensitive on Windows.
+     * The extension check is case-sensitive.
      * <pre>
      * wildcardMatch("c.txt", "*.txt")      --> true
      * wildcardMatch("c.txt", "*.jpg")      --> false
@@ -839,13 +871,52 @@
      * @return true if the filename matches the wilcard string
      */
     public static boolean wildcardMatch(String filename, String wildcardMatcher) {
+        return wildcardMatch(filename, wildcardMatcher, false);
+    }
+
+    /**
+     * Checks a filename to see if it matches the specified wildcard matcher
+     * using the case rules of the system.
+     * <p>
+     * The wildcard matcher uses the characters '?' and '*' to represent a
+     * single or multiple wildcard characters.
+     * This is the same as often found on Dos/Unix command lines.
+     * The check is case-sensitive on Unix and case-insensitive on Windows.
+     * <pre>
+     * wildcardMatch("c.txt", "*.txt")      --> true
+     * wildcardMatch("c.txt", "*.jpg")      --> false
+     * wildcardMatch("a/b/c.txt", "a/b/*")  --> true
+     * wildcardMatch("c.txt", "*.???")      --> true
+     * wildcardMatch("c.txt", "*.????")     --> false
+     * </pre>
+     * 
+     * @param filename  the filename to match on
+     * @param wildcardMatcher  the wildcard string to match against
+     * @return true if the filename matches the wilcard string
+     */
+    public static boolean wildcardMatchOnSystem(String filename, String wildcardMatcher) {
+        return wildcardMatch(filename, wildcardMatcher, true);
+    }
+
+    /**
+     * Checks a filename to see if it matches the specified wildcard matcher.
+     * <p>
+     * The wildcard matcher uses the characters '?' and '*' to represent a
+     * single or multiple wildcard characters.
+     * 
+     * @param filename  the filename to match on
+     * @param wildcardMatcher  the wildcard string to match against
+     * @param system  whether to use the system (windows or unix)
+     * @return true if the filename matches the wilcard string
+     */
+    private static boolean wildcardMatch(String filename, String wildcardMatcher, boolean system) {
         if (filename == null && wildcardMatcher == null) {
             return true;
         }
         if (filename == null || wildcardMatcher == null) {
             return false;
         }
-        if (SYSTEM_SEPARATOR == WINDOWS_SEPARATOR) {
+        if (system && (SYSTEM_SEPARATOR == WINDOWS_SEPARATOR)) {
             filename = filename.toLowerCase();
             wildcardMatcher = wildcardMatcher.toLowerCase();
         }

Modified: jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/FilenameUtilsTestCase.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/FilenameUtilsTestCase.java?view=diff&r1=160342&r2=160343
==============================================================================
--- jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/FilenameUtilsTestCase.java (original)
+++ jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/FilenameUtilsTestCase.java Wed Apr  6 16:35:43 2005
@@ -457,21 +457,43 @@
         assertEquals(false, FilenameUtils.equals("", null));
         assertEquals(true, FilenameUtils.equals("", ""));
         assertEquals(true, FilenameUtils.equals("file.txt", "file.txt"));
-        assertEquals(WINDOWS, FilenameUtils.equals("file.txt", "FILE.TXT"));
+        assertEquals(false, FilenameUtils.equals("file.txt", "FILE.TXT"));
         assertEquals(false, FilenameUtils.equals("a\\b\\file.txt", "a/b/file.txt"));
     }
 
+    public void testEqualsOnSystem() {
+        assertEquals(true, FilenameUtils.equalsOnSystem(null, null));
+        assertEquals(false, FilenameUtils.equalsOnSystem(null, ""));
+        assertEquals(false, FilenameUtils.equalsOnSystem("", null));
+        assertEquals(true, FilenameUtils.equalsOnSystem("", ""));
+        assertEquals(true, FilenameUtils.equalsOnSystem("file.txt", "file.txt"));
+        assertEquals(WINDOWS, FilenameUtils.equalsOnSystem("file.txt", "FILE.TXT"));
+        assertEquals(false, FilenameUtils.equalsOnSystem("a\\b\\file.txt", "a/b/file.txt"));
+    }
+
+    //-----------------------------------------------------------------------
     public void testEqualsNormalized() {
         assertEquals(true, FilenameUtils.equalsNormalized(null, null));
         assertEquals(false, FilenameUtils.equalsNormalized(null, ""));
         assertEquals(false, FilenameUtils.equalsNormalized("", null));
         assertEquals(true, FilenameUtils.equalsNormalized("", ""));
         assertEquals(true, FilenameUtils.equalsNormalized("file.txt", "file.txt"));
-        assertEquals(WINDOWS, FilenameUtils.equalsNormalized("file.txt", "FILE.TXT"));
+        assertEquals(false, FilenameUtils.equalsNormalized("file.txt", "FILE.TXT"));
         assertEquals(true, FilenameUtils.equalsNormalized("a\\b\\file.txt", "a/b/file.txt"));
         assertEquals(true, FilenameUtils.equalsNormalized("a\\b\\", "a/b"));
     }
 
+    public void testEqualsNormalizedOnSystem() {
+        assertEquals(true, FilenameUtils.equalsNormalizedOnSystem(null, null));
+        assertEquals(false, FilenameUtils.equalsNormalizedOnSystem(null, ""));
+        assertEquals(false, FilenameUtils.equalsNormalizedOnSystem("", null));
+        assertEquals(true, FilenameUtils.equalsNormalizedOnSystem("", ""));
+        assertEquals(true, FilenameUtils.equalsNormalizedOnSystem("file.txt", "file.txt"));
+        assertEquals(WINDOWS, FilenameUtils.equalsNormalizedOnSystem("file.txt", "FILE.TXT"));
+        assertEquals(true, FilenameUtils.equalsNormalizedOnSystem("a\\b\\file.txt", "a/b/file.txt"));
+        assertEquals(true, FilenameUtils.equalsNormalizedOnSystem("a\\b\\", "a/b"));
+    }
+
     //-----------------------------------------------------------------------
     public void testIsExtension() {
         assertEquals(false, FilenameUtils.isExtension(null, (String) null));
@@ -502,11 +524,7 @@
         assertEquals(true, FilenameUtils.isExtension("a.b\\file.txt", "txt"));
         assertEquals(false, FilenameUtils.isExtension("a.b\\file.txt", "rtf"));
         
-        if (WINDOWS) {
-            assertEquals(true, FilenameUtils.isExtension("a.b\\file.txt", "TXT"));
-        } else {
-            assertEquals(false, FilenameUtils.isExtension("a.b\\file.txt", "TXT"));
-        }
+        assertEquals(false, FilenameUtils.isExtension("a.b\\file.txt", "TXT"));
     }
 
     public void testIsExtensionArray() {
@@ -543,13 +561,8 @@
         assertEquals(false, FilenameUtils.isExtension("a.b\\file.txt", new String[] {"rtf"}));
         assertEquals(true, FilenameUtils.isExtension("a.b\\file.txt", new String[] {"rtf", "txt"}));
         
-        if (WINDOWS) {
-            assertEquals(true, FilenameUtils.isExtension("a.b\\file.txt", new String[] {"TXT"}));
-            assertEquals(true, FilenameUtils.isExtension("a.b\\file.txt", new String[] {"TXT", "RTF"}));
-        } else {
-            assertEquals(false, FilenameUtils.isExtension("a.b\\file.txt", new String[] {"TXT"}));
-            assertEquals(false, FilenameUtils.isExtension("a.b\\file.txt", new String[] {"TXT", "RTF"}));
-        }
+        assertEquals(false, FilenameUtils.isExtension("a.b\\file.txt", new String[] {"TXT"}));
+        assertEquals(false, FilenameUtils.isExtension("a.b\\file.txt", new String[] {"TXT", "RTF"}));
     }
 
     public void testIsExtensionCollection() {
@@ -586,13 +599,8 @@
         assertEquals(false, FilenameUtils.isExtension("a.b\\file.txt", new ArrayList(Arrays.asList(new String[] {"rtf"}))));
         assertEquals(true, FilenameUtils.isExtension("a.b\\file.txt", new ArrayList(Arrays.asList(new String[] {"rtf", "txt"}))));
         
-        if (WINDOWS) {
-            assertEquals(true, FilenameUtils.isExtension("a.b\\file.txt", new ArrayList(Arrays.asList(new String[] {"TXT"}))));
-            assertEquals(true, FilenameUtils.isExtension("a.b\\file.txt", new ArrayList(Arrays.asList(new String[] {"TXT", "RTF"}))));
-        } else {
-            assertEquals(false, FilenameUtils.isExtension("a.b\\file.txt", new ArrayList(Arrays.asList(new String[] {"TXT"}))));
-            assertEquals(false, FilenameUtils.isExtension("a.b\\file.txt", new ArrayList(Arrays.asList(new String[] {"TXT", "RTF"}))));
-        }
+        assertEquals(false, FilenameUtils.isExtension("a.b\\file.txt", new ArrayList(Arrays.asList(new String[] {"TXT"}))));
+        assertEquals(false, FilenameUtils.isExtension("a.b\\file.txt", new ArrayList(Arrays.asList(new String[] {"TXT", "RTF"}))));
     }
 
 }

Modified: jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/FilenameUtilsWildcardTestCase.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/FilenameUtilsWildcardTestCase.java?view=diff&r1=160342&r2=160343
==============================================================================
--- jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/FilenameUtilsWildcardTestCase.java (original)
+++ jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/FilenameUtilsWildcardTestCase.java Wed Apr  6 16:35:43 2005
@@ -32,21 +32,39 @@
     //   FilenameUtils.wildcardMatch(String,String)
 
     public void testMatch() {
-        assertEquals(false, FilenameUtils.wildcardMatch(null, "Foo") );
-        assertEquals(false, FilenameUtils.wildcardMatch("Foo", null) );
-        assertEquals(true, FilenameUtils.wildcardMatch(null, null) );
-        assertTrue( FilenameUtils.wildcardMatch("Foo", "Foo") );
-        assertTrue( FilenameUtils.wildcardMatch("", "") );
-        assertTrue( FilenameUtils.wildcardMatch("Foo", "Fo*") );
-        assertTrue( FilenameUtils.wildcardMatch("Foo", "Fo?") );
-        assertTrue( FilenameUtils.wildcardMatch("Foo Bar and Catflap", "Fo*") );
-        assertTrue( FilenameUtils.wildcardMatch("New Bookmarks", "N?w ?o?k??r?s") );
-        assertFalse( FilenameUtils.wildcardMatch("Foo", "Bar") );
-        assertTrue( FilenameUtils.wildcardMatch("Foo Bar Foo", "F*o Bar*") );
-        assertTrue( FilenameUtils.wildcardMatch("Adobe Acrobat Installer", "Ad*er") );
-        assertTrue( FilenameUtils.wildcardMatch("Foo", "*Foo") );
-        assertTrue( FilenameUtils.wildcardMatch("Foo", "Foo*") );
-        assertEquals(WINDOWS,  FilenameUtils.wildcardMatch("FOO", "Foo*") );
+        assertEquals(false, FilenameUtils.wildcardMatch(null, "Foo"));
+        assertEquals(false, FilenameUtils.wildcardMatch("Foo", null));
+        assertEquals(true, FilenameUtils.wildcardMatch(null, null));
+        assertEquals(true, FilenameUtils.wildcardMatch("Foo", "Foo"));
+        assertEquals(true, FilenameUtils.wildcardMatch("", ""));
+        assertEquals(true, FilenameUtils.wildcardMatch("Foo", "Fo*"));
+        assertEquals(true, FilenameUtils.wildcardMatch("Foo", "Fo?"));
+        assertEquals(true, FilenameUtils.wildcardMatch("Foo Bar and Catflap", "Fo*"));
+        assertEquals(true, FilenameUtils.wildcardMatch("New Bookmarks", "N?w ?o?k??r?s"));
+        assertEquals(false, FilenameUtils.wildcardMatch("Foo", "Bar"));
+        assertEquals(true, FilenameUtils.wildcardMatch("Foo Bar Foo", "F*o Bar*"));
+        assertEquals(true, FilenameUtils.wildcardMatch("Adobe Acrobat Installer", "Ad*er"));
+        assertEquals(true, FilenameUtils.wildcardMatch("Foo", "*Foo"));
+        assertEquals(true, FilenameUtils.wildcardMatch("Foo", "Foo*"));
+        assertEquals(false, FilenameUtils.wildcardMatch("FOO", "Foo*"));
+    }
+
+    public void testMatchOnSystem() {
+        assertEquals(false, FilenameUtils.wildcardMatchOnSystem(null, "Foo"));
+        assertEquals(false, FilenameUtils.wildcardMatchOnSystem("Foo", null));
+        assertEquals(true, FilenameUtils.wildcardMatchOnSystem(null, null));
+        assertEquals(true, FilenameUtils.wildcardMatchOnSystem("Foo", "Foo"));
+        assertEquals(true, FilenameUtils.wildcardMatchOnSystem("", ""));
+        assertEquals(true, FilenameUtils.wildcardMatchOnSystem("Foo", "Fo*"));
+        assertEquals(true, FilenameUtils.wildcardMatchOnSystem("Foo", "Fo?"));
+        assertEquals(true, FilenameUtils.wildcardMatchOnSystem("Foo Bar and Catflap", "Fo*"));
+        assertEquals(true, FilenameUtils.wildcardMatchOnSystem("New Bookmarks", "N?w ?o?k??r?s"));
+        assertEquals(false, FilenameUtils.wildcardMatchOnSystem("Foo", "Bar"));
+        assertEquals(true, FilenameUtils.wildcardMatchOnSystem("Foo Bar Foo", "F*o Bar*"));
+        assertEquals(true, FilenameUtils.wildcardMatchOnSystem("Adobe Acrobat Installer", "Ad*er"));
+        assertEquals(true, FilenameUtils.wildcardMatchOnSystem("Foo", "*Foo"));
+        assertEquals(true, FilenameUtils.wildcardMatchOnSystem("Foo", "Foo*"));
+        assertEquals(WINDOWS, FilenameUtils.wildcardMatchOnSystem("FOO", "Foo*"));
     }
 
     public void testSplitOnTokens() {



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org