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 2006/06/26 01:18:34 UTC

svn commit: r417090 - in /jakarta/commons/proper/io/trunk/src: java/org/apache/commons/io/IOCase.java test/org/apache/commons/io/IOCaseTestCase.java test/org/apache/commons/io/PackageTestSuite.java

Author: scolebourne
Date: Sun Jun 25 16:18:33 2006
New Revision: 417090

URL: http://svn.apache.org/viewvc?rev=417090&view=rev
Log:
Add IOCase to handle case-sensitivity

Added:
    jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/IOCase.java   (with props)
    jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/IOCaseTestCase.java   (with props)
Modified:
    jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/PackageTestSuite.java

Added: jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/IOCase.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/IOCase.java?rev=417090&view=auto
==============================================================================
--- jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/IOCase.java (added)
+++ jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/IOCase.java Sun Jun 25 16:18:33 2006
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ *
+ * Licensed 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.commons.io;
+
+import java.io.Serializable;
+
+/**
+ * Enumeration of IO case sensitivity.
+ * <p>
+ * Different filing systems have different rules for case-sensitivity.
+ * Windows is case-insensitive, Unix is case-sensitive.
+ * <p>
+ * This class captures that difference, providing an enumeration to
+ * control how filename comparisons should be performed. It also provides
+ * methods that use the enumeration to perform comparisons.
+ * <p>
+ * Wherever possible, you should use the <code>check</code> methods in this
+ * class to compare filenames.
+ *
+ * @author Stephen Colebourne
+ * @version $Id$
+ * @since Commons IO 1.3
+ */
+public final class IOCase implements Serializable {
+
+    /**
+     * The constant for case sensitive regardless of operating system.
+     */
+    public static final IOCase SENSITIVE = new IOCase("Sensitive", true);
+    /**
+     * The constant for case insensitive regardless of operating system.
+     */
+    public static final IOCase INSENSITIVE = new IOCase("Insensitive", false);
+    /**
+     * The constant for case sensitivity determined by the current operating system.
+     * Windows is case-insensitive when comparing filenames, Unix is case-sensitive.
+     * <p>
+     * If you derialize this constant of Windows, and deserialize on Unix, or vice
+     * versa, then the value of the case-sensitivity flag will change.
+     */
+    public static final IOCase SYSTEM = new IOCase("System", !FilenameUtils.isSystemWindows());
+
+    /** Serialization version. */
+    private static final long serialVersionUID = -6343169151696340687L;
+
+    /** The enumeration name. */
+    private final String name;
+    /** The sensitivity flag. */
+    private final transient boolean sensitive;
+
+    //-----------------------------------------------------------------------
+    /**
+     * Factory method to create an IOCase from a name.
+     * 
+     * @param name  the name to find
+     * @return the IOCase object
+     * @throws IllegalArgumentException if the name is invalid
+     */
+    public static IOCase forName(String name) {
+        if (IOCase.SENSITIVE.name.equals(name)){
+            return IOCase.SENSITIVE;
+        }
+        if (IOCase.INSENSITIVE.name.equals(name)){
+            return IOCase.INSENSITIVE;
+        }
+        if (IOCase.SYSTEM.name.equals(name)){
+            return IOCase.SYSTEM;
+        }
+        throw new IllegalArgumentException("Invalid IOCase name: " + name);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Private constructor.
+     * 
+     * @param name  the name
+     * @param sensitive  the sensitivity
+     */
+    private IOCase(String name, boolean sensitive) {
+        this.name = name;
+        this.sensitive = sensitive;
+    }
+
+    /**
+     * Replaces the enumeration from the stream with a real one.
+     * This ensures that the correct flag is set for SYSTEM.
+     * 
+     * @return the resolved object
+     */
+    private Object readResolve() {
+        return forName(name);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Gets the name of the constant.
+     * 
+     * @return the name of the constant
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Does the object represent case sensitive comparison.
+     * 
+     * @return true if case sensitive
+     */
+    public boolean isCaseSensitive() {
+        return sensitive;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Compares two strings using the case-sensitivity rule.
+     * <p>
+     * This method mimics {@link String#equals} but takes case-sensitivity
+     * into account.
+     * 
+     * @param str1  the first string to compare, not null
+     * @param str2  the second string to compare, not null
+     * @return true if equal using the case rules
+     * @throws NullPointerException if either string is null
+     */
+    public boolean checkEquals(String str1, String str2) {
+        if (str1 == null || str2 == null) {
+            throw new NullPointerException("The strings must not be null");
+        }
+        return sensitive ? str1.equals(str2) : str1.equalsIgnoreCase(str2);
+    }
+
+    /**
+     * Checks if one string starts with another using the case-sensitivity rule.
+     * <p>
+     * This method mimics {@link String#startsWith} but takes case-sensitivity
+     * into account.
+     * 
+     * @param str  the string to check, not null
+     * @param start  the start to compare against, not null
+     * @return true if equal using the case rules
+     * @throws NullPointerException if either string is null
+     */
+    public boolean checkStartsWith(String str, String start) {
+        return str.regionMatches(!sensitive, 0, start, 0, start.length());
+    }
+
+    /**
+     * Checks if one string ends with another using the case-sensitivity rule.
+     * <p>
+     * This method mimics {@link String#endsWith} but takes case-sensitivity
+     * into account.
+     * 
+     * @param str  the string to check, not null
+     * @param end  the end to compare against, not null
+     * @return true if equal using the case rules
+     * @throws NullPointerException if either string is null
+     */
+    public boolean checkEndsWith(String str, String end) {
+        int endLen = end.length();
+        return str.regionMatches(!sensitive, str.length() - endLen, end, 0, endLen);
+    }
+
+    /**
+     * Checks if one string contains another at a specific index using the case-sensitivity rule.
+     * <p>
+     * This method mimics parts of {@link String#regionMatches} but takes case-sensitivity
+     * into account.
+     * 
+     * @param str  the string to check, not null
+     * @param strStartIndex  the index to start at in str
+     * @param search  the start to search for, not null
+     * @return true if equal using the case rules
+     * @throws NullPointerException if either string is null
+     */
+    public boolean checkRegionMatches(String str, int strStartIndex, String search) {
+        return str.regionMatches(!sensitive, strStartIndex, search, 0, search.length());
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Gets a string describing the sensitivity.
+     * 
+     * @return a string describing the sensitivity
+     */
+    public String toString() {
+        return name;
+    }
+
+}

Propchange: jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/IOCase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/IOCase.java
------------------------------------------------------------------------------
    svn:keywords = "author date id revision"

Added: jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/IOCaseTestCase.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/IOCaseTestCase.java?rev=417090&view=auto
==============================================================================
--- jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/IOCaseTestCase.java (added)
+++ jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/IOCaseTestCase.java Sun Jun 25 16:18:33 2006
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2006 The Apache Software Foundation.
+ * 
+ * Licensed 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.commons.io;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+
+import org.apache.commons.io.testtools.FileBasedTestCase;
+
+/**
+ * This is used to test IOCase for correctness.
+ *
+ * @author Stephen Colebourne
+ * @version $Id$
+ */
+public class IOCaseTestCase extends FileBasedTestCase {
+
+    private static final boolean WINDOWS = (File.separatorChar == '\\');
+
+    public static void main(String[] args) {
+        TestRunner.run(suite());
+    }
+
+    public static Test suite() {
+        return new TestSuite(IOCaseTestCase.class);
+    }
+
+    public IOCaseTestCase(String name) throws IOException {
+        super(name);
+    }
+
+    protected void setUp() throws Exception {
+
+    }
+
+    protected void tearDown() throws Exception {
+    }
+
+    //-----------------------------------------------------------------------
+    public void test_forName() throws Exception {
+        assertEquals(IOCase.SENSITIVE, IOCase.forName("Sensitive"));
+        assertEquals(IOCase.INSENSITIVE, IOCase.forName("Insensitive"));
+        assertEquals(IOCase.SYSTEM, IOCase.forName("System"));
+        try {
+            IOCase.forName("Blah");
+            fail();
+        } catch (IllegalArgumentException ex) {}
+        try {
+            IOCase.forName(null);
+            fail();
+        } catch (IllegalArgumentException ex) {}
+    }
+
+    public void test_serialization() throws Exception {
+        assertSame(IOCase.SENSITIVE, serialize(IOCase.SENSITIVE));
+        assertSame(IOCase.INSENSITIVE, serialize(IOCase.INSENSITIVE));
+        assertSame(IOCase.SYSTEM, serialize(IOCase.SYSTEM));
+    }
+
+    public void test_getName() throws Exception {
+        assertEquals("Sensitive", IOCase.SENSITIVE.getName());
+        assertEquals("Insensitive", IOCase.INSENSITIVE.getName());
+        assertEquals("System", IOCase.SYSTEM.getName());
+    }
+
+    public void test_toString() throws Exception {
+        assertEquals("Sensitive", IOCase.SENSITIVE.toString());
+        assertEquals("Insensitive", IOCase.INSENSITIVE.toString());
+        assertEquals("System", IOCase.SYSTEM.toString());
+    }
+
+    public void test_isCaseSensitive() throws Exception {
+        assertEquals(true, IOCase.SENSITIVE.isCaseSensitive());
+        assertEquals(false, IOCase.INSENSITIVE.isCaseSensitive());
+        assertEquals(!WINDOWS, IOCase.SYSTEM.isCaseSensitive());
+    }
+
+    //-----------------------------------------------------------------------
+    public void test_checkEquals_functionality() throws Exception {
+        assertEquals(false, IOCase.SENSITIVE.checkEquals("ABC", ""));
+        assertEquals(false, IOCase.SENSITIVE.checkEquals("ABC", "A"));
+        assertEquals(false, IOCase.SENSITIVE.checkEquals("ABC", "AB"));
+        assertEquals(true, IOCase.SENSITIVE.checkEquals("ABC", "ABC"));
+        assertEquals(false, IOCase.SENSITIVE.checkEquals("ABC", "BC"));
+        assertEquals(false, IOCase.SENSITIVE.checkEquals("ABC", "C"));
+        assertEquals(false, IOCase.SENSITIVE.checkEquals("ABC", "ABCD"));
+        assertEquals(false, IOCase.SENSITIVE.checkEquals("", "ABC"));
+        assertEquals(true, IOCase.SENSITIVE.checkEquals("", ""));
+        
+        try {
+            IOCase.SENSITIVE.checkEquals("ABC", null);
+            fail();
+        } catch (NullPointerException ex) {}
+        try {
+            IOCase.SENSITIVE.checkEquals(null, "ABC");
+            fail();
+        } catch (NullPointerException ex) {}
+        try {
+            IOCase.SENSITIVE.checkEquals(null, null);
+            fail();
+        } catch (NullPointerException ex) {}
+    }
+
+    public void test_checkEquals_case() throws Exception {
+        assertEquals(true, IOCase.SENSITIVE.checkEquals("ABC", "ABC"));
+        assertEquals(false, IOCase.SENSITIVE.checkEquals("ABC", "Abc"));
+        
+        assertEquals(true, IOCase.INSENSITIVE.checkEquals("ABC", "ABC"));
+        assertEquals(true, IOCase.INSENSITIVE.checkEquals("ABC", "Abc"));
+        
+        assertEquals(true, IOCase.SYSTEM.checkEquals("ABC", "ABC"));
+        assertEquals(WINDOWS, IOCase.SYSTEM.checkEquals("ABC", "Abc"));
+    }
+
+    //-----------------------------------------------------------------------
+    public void test_checkStartsWith_functionality() throws Exception {
+        assertEquals(true, IOCase.SENSITIVE.checkStartsWith("ABC", ""));
+        assertEquals(true, IOCase.SENSITIVE.checkStartsWith("ABC", "A"));
+        assertEquals(true, IOCase.SENSITIVE.checkStartsWith("ABC", "AB"));
+        assertEquals(true, IOCase.SENSITIVE.checkStartsWith("ABC", "ABC"));
+        assertEquals(false, IOCase.SENSITIVE.checkStartsWith("ABC", "BC"));
+        assertEquals(false, IOCase.SENSITIVE.checkStartsWith("ABC", "C"));
+        assertEquals(false, IOCase.SENSITIVE.checkStartsWith("ABC", "ABCD"));
+        assertEquals(false, IOCase.SENSITIVE.checkStartsWith("", "ABC"));
+        assertEquals(true, IOCase.SENSITIVE.checkStartsWith("", ""));
+        
+        try {
+            IOCase.SENSITIVE.checkStartsWith("ABC", null);
+            fail();
+        } catch (NullPointerException ex) {}
+        try {
+            IOCase.SENSITIVE.checkStartsWith(null, "ABC");
+            fail();
+        } catch (NullPointerException ex) {}
+        try {
+            IOCase.SENSITIVE.checkStartsWith(null, null);
+            fail();
+        } catch (NullPointerException ex) {}
+    }
+
+    public void test_checkStartsWith_case() throws Exception {
+        assertEquals(true, IOCase.SENSITIVE.checkStartsWith("ABC", "AB"));
+        assertEquals(false, IOCase.SENSITIVE.checkStartsWith("ABC", "Ab"));
+        
+        assertEquals(true, IOCase.INSENSITIVE.checkStartsWith("ABC", "AB"));
+        assertEquals(true, IOCase.INSENSITIVE.checkStartsWith("ABC", "Ab"));
+        
+        assertEquals(true, IOCase.SYSTEM.checkStartsWith("ABC", "AB"));
+        assertEquals(WINDOWS, IOCase.SYSTEM.checkStartsWith("ABC", "Ab"));
+    }
+
+    //-----------------------------------------------------------------------
+    public void test_checkEndsWith_functionality() throws Exception {
+        assertEquals(true, IOCase.SENSITIVE.checkEndsWith("ABC", ""));
+        assertEquals(false, IOCase.SENSITIVE.checkEndsWith("ABC", "A"));
+        assertEquals(false, IOCase.SENSITIVE.checkEndsWith("ABC", "AB"));
+        assertEquals(true, IOCase.SENSITIVE.checkEndsWith("ABC", "ABC"));
+        assertEquals(true, IOCase.SENSITIVE.checkEndsWith("ABC", "BC"));
+        assertEquals(true, IOCase.SENSITIVE.checkEndsWith("ABC", "C"));
+        assertEquals(false, IOCase.SENSITIVE.checkEndsWith("ABC", "ABCD"));
+        assertEquals(false, IOCase.SENSITIVE.checkEndsWith("", "ABC"));
+        assertEquals(true, IOCase.SENSITIVE.checkEndsWith("", ""));
+        
+        try {
+            IOCase.SENSITIVE.checkEndsWith("ABC", null);
+            fail();
+        } catch (NullPointerException ex) {}
+        try {
+            IOCase.SENSITIVE.checkEndsWith(null, "ABC");
+            fail();
+        } catch (NullPointerException ex) {}
+        try {
+            IOCase.SENSITIVE.checkEndsWith(null, null);
+            fail();
+        } catch (NullPointerException ex) {}
+    }
+
+    public void test_checkEndsWith_case() throws Exception {
+        assertEquals(true, IOCase.SENSITIVE.checkEndsWith("ABC", "BC"));
+        assertEquals(false, IOCase.SENSITIVE.checkEndsWith("ABC", "Bc"));
+        
+        assertEquals(true, IOCase.INSENSITIVE.checkEndsWith("ABC", "BC"));
+        assertEquals(true, IOCase.INSENSITIVE.checkEndsWith("ABC", "Bc"));
+        
+        assertEquals(true, IOCase.SYSTEM.checkEndsWith("ABC", "BC"));
+        assertEquals(WINDOWS, IOCase.SYSTEM.checkEndsWith("ABC", "Bc"));
+    }
+
+    //-----------------------------------------------------------------------
+    public void test_checkRegionMatches_functionality() throws Exception {
+        assertEquals(true, IOCase.SENSITIVE.checkRegionMatches("ABC", 0, ""));
+        assertEquals(true, IOCase.SENSITIVE.checkRegionMatches("ABC", 0, "A"));
+        assertEquals(true, IOCase.SENSITIVE.checkRegionMatches("ABC", 0, "AB"));
+        assertEquals(true, IOCase.SENSITIVE.checkRegionMatches("ABC", 0, "ABC"));
+        assertEquals(false, IOCase.SENSITIVE.checkRegionMatches("ABC", 0, "BC"));
+        assertEquals(false, IOCase.SENSITIVE.checkRegionMatches("ABC", 0, "C"));
+        assertEquals(false, IOCase.SENSITIVE.checkRegionMatches("ABC", 0, "ABCD"));
+        assertEquals(false, IOCase.SENSITIVE.checkRegionMatches("", 0, "ABC"));
+        assertEquals(true, IOCase.SENSITIVE.checkRegionMatches("", 0, ""));
+        
+        assertEquals(true, IOCase.SENSITIVE.checkRegionMatches("ABC", 1, ""));
+        assertEquals(false, IOCase.SENSITIVE.checkRegionMatches("ABC", 1, "A"));
+        assertEquals(false, IOCase.SENSITIVE.checkRegionMatches("ABC", 1, "AB"));
+        assertEquals(false, IOCase.SENSITIVE.checkRegionMatches("ABC", 1, "ABC"));
+        assertEquals(true, IOCase.SENSITIVE.checkRegionMatches("ABC", 1, "BC"));
+        assertEquals(false, IOCase.SENSITIVE.checkRegionMatches("ABC", 1, "C"));
+        assertEquals(false, IOCase.SENSITIVE.checkRegionMatches("ABC", 1, "ABCD"));
+        assertEquals(false, IOCase.SENSITIVE.checkRegionMatches("", 1, "ABC"));
+        assertEquals(false, IOCase.SENSITIVE.checkRegionMatches("", 1, ""));
+        
+        try {
+            IOCase.SENSITIVE.checkRegionMatches("ABC", 0, null);
+            fail();
+        } catch (NullPointerException ex) {}
+        try {
+            IOCase.SENSITIVE.checkRegionMatches(null, 0, "ABC");
+            fail();
+        } catch (NullPointerException ex) {}
+        try {
+            IOCase.SENSITIVE.checkRegionMatches(null, 0, null);
+            fail();
+        } catch (NullPointerException ex) {}
+        try {
+            IOCase.SENSITIVE.checkRegionMatches("ABC", 1, null);
+            fail();
+        } catch (NullPointerException ex) {}
+        try {
+            IOCase.SENSITIVE.checkRegionMatches(null, 1, "ABC");
+            fail();
+        } catch (NullPointerException ex) {}
+        try {
+            IOCase.SENSITIVE.checkRegionMatches(null, 1, null);
+            fail();
+        } catch (NullPointerException ex) {}
+    }
+
+    public void test_checkRegionMatches_case() throws Exception {
+        assertEquals(true, IOCase.SENSITIVE.checkRegionMatches("ABC", 0, "AB"));
+        assertEquals(false, IOCase.SENSITIVE.checkRegionMatches("ABC", 0, "Ab"));
+        
+        assertEquals(true, IOCase.INSENSITIVE.checkRegionMatches("ABC", 0, "AB"));
+        assertEquals(true, IOCase.INSENSITIVE.checkRegionMatches("ABC", 0, "Ab"));
+        
+        assertEquals(true, IOCase.SYSTEM.checkRegionMatches("ABC", 0, "AB"));
+        assertEquals(WINDOWS, IOCase.SYSTEM.checkRegionMatches("ABC", 0, "Ab"));
+    }
+
+    //-----------------------------------------------------------------------
+    private IOCase serialize(IOCase value) throws Exception {
+        ByteArrayOutputStream buf = new ByteArrayOutputStream();
+        ObjectOutputStream out = new ObjectOutputStream(buf);
+        out.writeObject(value);
+        out.flush();
+        out.close();
+
+        ByteArrayInputStream bufin = new ByteArrayInputStream(buf.toByteArray());
+        ObjectInputStream in = new ObjectInputStream(bufin);
+        return (IOCase) in.readObject();
+    }
+
+}

Propchange: jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/IOCaseTestCase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/IOCaseTestCase.java
------------------------------------------------------------------------------
    svn:keywords = "author date id revision"

Modified: jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/PackageTestSuite.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/PackageTestSuite.java?rev=417090&r1=417089&r2=417090&view=diff
==============================================================================
--- jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/PackageTestSuite.java (original)
+++ jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/PackageTestSuite.java Sun Jun 25 16:18:33 2006
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003-2004 The Apache Software Foundation.
+ * Copyright 2003-2004,2006 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -48,6 +48,7 @@
         suite.addTest(new TestSuite(FileUtilsTestCase.class));
         suite.addTest(new TestSuite(FileFilterTestCase.class));
         suite.addTest(new TestSuite(HexDumpTest.class));
+        suite.addTest(new TestSuite(IOCaseTestCase.class));
         suite.addTest(new TestSuite(IOUtilsCopyTestCase.class));
         suite.addTest(new TestSuite(IOUtilsTestCase.class));
         suite.addTest(new TestSuite(IOUtilsWriteTestCase.class));



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