From 462801613746b7c9dab9bf3bc11dbaad1ae0175d Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Tue, 15 May 2012 11:06:53 +0000
Subject: [PATCH] Fix OPENDJ-495: Add support for AttributeDescription.withoutOption(String)

---
 opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/AttributeDescriptionTestCase.java |  276 ++++++++++++++++++++++++++++++---------
 opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AttributeDescription.java         |   87 ++++++++++++
 2 files changed, 297 insertions(+), 66 deletions(-)

diff --git a/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AttributeDescription.java b/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AttributeDescription.java
index 6921105..ebefad2 100644
--- a/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AttributeDescription.java
+++ b/opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/AttributeDescription.java
@@ -407,7 +407,6 @@
         if (impl instanceof ZeroOptionImpl) {
             return new AttributeDescription(newAttributeDescription, attributeType,
                     new SingleOptionImpl(option, normalizedOption));
-
         } else if (impl instanceof SingleOptionImpl) {
             final SingleOptionImpl simpl = (SingleOptionImpl) impl;
 
@@ -419,6 +418,9 @@
             if (normalizedOption.compareTo(simpl.normalizedOption) < 0) {
                 newNormalizedOptions[0] = normalizedOption;
                 newNormalizedOptions[1] = simpl.normalizedOption;
+            } else {
+                newNormalizedOptions[0] = simpl.normalizedOption;
+                newNormalizedOptions[1] = normalizedOption;
             }
 
             return new AttributeDescription(newAttributeDescription, attributeType,
@@ -461,6 +463,89 @@
     }
 
     /**
+     * Returns an attribute description having the same attribute type and
+     * options as this attribute description except for the provided option.
+     * <p>
+     * This method is idempotent: if this attribute description does not contain
+     * the provided option then this attribute description will be returned.
+     *
+     * @param option
+     *            The attribute option.
+     * @return The new attribute description excluding {@code option}.
+     * @throws NullPointerException
+     *             If {@code attributeDescription} or {@code option} was
+     *             {@code null}.
+     */
+    public AttributeDescription withoutOption(final String option) {
+        Validator.ensureNotNull(option);
+
+        final String normalizedOption = toLowerCase(option);
+        if (!pimpl.containsOption(normalizedOption)) {
+            return this;
+        }
+
+        final String oldAttributeDescription = attributeDescription;
+        final StringBuilder builder =
+                new StringBuilder(oldAttributeDescription.length() - option.length() - 1);
+
+        final String normalizedOldAttributeDescription = toLowerCase(oldAttributeDescription);
+        final int index =
+                normalizedOldAttributeDescription.indexOf(normalizedOption);
+        builder.append(oldAttributeDescription, 0, index - 1 /* to semi-colon */);
+        builder.append(oldAttributeDescription, index + option.length(), oldAttributeDescription
+                .length());
+        final String newAttributeDescription = builder.toString();
+
+        final Impl impl = pimpl;
+        if (impl instanceof ZeroOptionImpl) {
+            throw new IllegalStateException("ZeroOptionImpl unexpected");
+        } else if (impl instanceof SingleOptionImpl) {
+            return new AttributeDescription(newAttributeDescription, attributeType,
+                    ZERO_OPTION_IMPL);
+        } else {
+            final MultiOptionImpl mimpl = (MultiOptionImpl) impl;
+            if (mimpl.options.length == 2) {
+                final String remainingOption;
+                final String remainingNormalizedOption;
+
+                if (toLowerCase(mimpl.options[0]).equals(normalizedOption)) {
+                    remainingOption = mimpl.options[1];
+                } else {
+                    remainingOption = mimpl.options[0];
+                }
+
+                if (mimpl.normalizedOptions[0].equals(normalizedOption)) {
+                    remainingNormalizedOption = mimpl.normalizedOptions[1];
+                } else {
+                    remainingNormalizedOption = mimpl.normalizedOptions[0];
+                }
+
+                return new AttributeDescription(newAttributeDescription, attributeType,
+                        new SingleOptionImpl(remainingOption, remainingNormalizedOption));
+            } else {
+                final String[] newOptions = new String[mimpl.options.length - 1];
+                final String[] newNormalizedOptions =
+                        new String[mimpl.normalizedOptions.length - 1];
+
+                for (int i = 0, j = 0; i < mimpl.options.length; i++) {
+                    if (!toLowerCase(mimpl.options[i]).equals(normalizedOption)) {
+                        newOptions[j++] = mimpl.options[i];
+                    }
+                }
+
+                for (int i = 0, j = 0; i < mimpl.normalizedOptions.length; i++) {
+                    if (!mimpl.normalizedOptions[i].equals(normalizedOption)) {
+                        newNormalizedOptions[j++] = mimpl.normalizedOptions[i];
+                    }
+                }
+
+                return new AttributeDescription(newAttributeDescription, attributeType,
+                        new MultiOptionImpl(newOptions, newNormalizedOptions));
+            }
+        }
+    }
+
+    /**
      * Creates an attribute description having the provided attribute type and
      * no options.
      *
diff --git a/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/AttributeDescriptionTestCase.java b/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/AttributeDescriptionTestCase.java
index 83657c2..1f6d5b2 100644
--- a/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/AttributeDescriptionTestCase.java
+++ b/opendj3/opendj-ldap-sdk/src/test/java/org/forgerock/opendj/ldap/AttributeDescriptionTestCase.java
@@ -27,11 +27,15 @@
 
 package org.forgerock.opendj.ldap;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertSame;
+import static org.testng.Assert.assertTrue;
+
 import java.util.Iterator;
 
 import org.forgerock.i18n.LocalizedIllegalArgumentException;
 import org.forgerock.opendj.ldap.schema.Schema;
-import org.testng.Assert;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
@@ -127,37 +131,36 @@
                 AttributeDescription.valueOf(ad2, Schema.getCoreSchema());
 
         // Identity.
-        Assert.assertTrue(attributeDescription1.equals(attributeDescription1));
-        Assert.assertTrue(attributeDescription1.compareTo(attributeDescription1) == 0);
-        Assert.assertTrue(attributeDescription1.isSubTypeOf(attributeDescription1));
-        Assert.assertTrue(attributeDescription1.isSuperTypeOf(attributeDescription1));
+        assertTrue(attributeDescription1.equals(attributeDescription1));
+        assertTrue(attributeDescription1.compareTo(attributeDescription1) == 0);
+        assertTrue(attributeDescription1.isSubTypeOf(attributeDescription1));
+        assertTrue(attributeDescription1.isSuperTypeOf(attributeDescription1));
 
         if (compare == 0) {
-            Assert.assertTrue(attributeDescription1.equals(attributeDescription2));
-            Assert.assertTrue(attributeDescription2.equals(attributeDescription1));
-            Assert.assertTrue(attributeDescription1.compareTo(attributeDescription2) == 0);
-            Assert.assertTrue(attributeDescription2.compareTo(attributeDescription1) == 0);
+            assertTrue(attributeDescription1.equals(attributeDescription2));
+            assertTrue(attributeDescription2.equals(attributeDescription1));
+            assertTrue(attributeDescription1.compareTo(attributeDescription2) == 0);
+            assertTrue(attributeDescription2.compareTo(attributeDescription1) == 0);
 
-            Assert.assertTrue(attributeDescription1.isSubTypeOf(attributeDescription2));
-            Assert.assertTrue(attributeDescription1.isSuperTypeOf(attributeDescription2));
-            Assert.assertTrue(attributeDescription2.isSubTypeOf(attributeDescription1));
-            Assert.assertTrue(attributeDescription2.isSuperTypeOf(attributeDescription1));
+            assertTrue(attributeDescription1.isSubTypeOf(attributeDescription2));
+            assertTrue(attributeDescription1.isSuperTypeOf(attributeDescription2));
+            assertTrue(attributeDescription2.isSubTypeOf(attributeDescription1));
+            assertTrue(attributeDescription2.isSuperTypeOf(attributeDescription1));
         } else {
-            Assert.assertFalse(attributeDescription1.equals(attributeDescription2));
-            Assert.assertFalse(attributeDescription2.equals(attributeDescription1));
+            assertFalse(attributeDescription1.equals(attributeDescription2));
+            assertFalse(attributeDescription2.equals(attributeDescription1));
 
             if (compare < 0) {
-                Assert.assertTrue(attributeDescription1.compareTo(attributeDescription2) < 0);
-                Assert.assertTrue(attributeDescription2.compareTo(attributeDescription1) > 0);
+                assertTrue(attributeDescription1.compareTo(attributeDescription2) < 0);
+                assertTrue(attributeDescription2.compareTo(attributeDescription1) > 0);
             } else {
-                Assert.assertTrue(attributeDescription1.compareTo(attributeDescription2) > 0);
-                Assert.assertTrue(attributeDescription2.compareTo(attributeDescription1) < 0);
+                assertTrue(attributeDescription1.compareTo(attributeDescription2) > 0);
+                assertTrue(attributeDescription2.compareTo(attributeDescription1) < 0);
             }
 
-            Assert.assertEquals(attributeDescription1.isSubTypeOf(attributeDescription2), isSubType);
+            assertEquals(attributeDescription1.isSubTypeOf(attributeDescription2), isSubType);
 
-            Assert.assertEquals(attributeDescription1.isSuperTypeOf(attributeDescription2),
-                    isSuperType);
+            assertEquals(attributeDescription1.isSuperTypeOf(attributeDescription2), isSuperType);
         }
     }
 
@@ -171,37 +174,36 @@
                 AttributeDescription.valueOf(ad2, Schema.getEmptySchema());
 
         // Identity.
-        Assert.assertTrue(attributeDescription1.equals(attributeDescription1));
-        Assert.assertTrue(attributeDescription1.compareTo(attributeDescription1) == 0);
-        Assert.assertTrue(attributeDescription1.isSubTypeOf(attributeDescription1));
-        Assert.assertTrue(attributeDescription1.isSuperTypeOf(attributeDescription1));
+        assertTrue(attributeDescription1.equals(attributeDescription1));
+        assertTrue(attributeDescription1.compareTo(attributeDescription1) == 0);
+        assertTrue(attributeDescription1.isSubTypeOf(attributeDescription1));
+        assertTrue(attributeDescription1.isSuperTypeOf(attributeDescription1));
 
         if (compare == 0) {
-            Assert.assertTrue(attributeDescription1.equals(attributeDescription2));
-            Assert.assertTrue(attributeDescription2.equals(attributeDescription1));
-            Assert.assertTrue(attributeDescription1.compareTo(attributeDescription2) == 0);
-            Assert.assertTrue(attributeDescription2.compareTo(attributeDescription1) == 0);
+            assertTrue(attributeDescription1.equals(attributeDescription2));
+            assertTrue(attributeDescription2.equals(attributeDescription1));
+            assertTrue(attributeDescription1.compareTo(attributeDescription2) == 0);
+            assertTrue(attributeDescription2.compareTo(attributeDescription1) == 0);
 
-            Assert.assertTrue(attributeDescription1.isSubTypeOf(attributeDescription2));
-            Assert.assertTrue(attributeDescription1.isSuperTypeOf(attributeDescription2));
-            Assert.assertTrue(attributeDescription2.isSubTypeOf(attributeDescription1));
-            Assert.assertTrue(attributeDescription2.isSuperTypeOf(attributeDescription1));
+            assertTrue(attributeDescription1.isSubTypeOf(attributeDescription2));
+            assertTrue(attributeDescription1.isSuperTypeOf(attributeDescription2));
+            assertTrue(attributeDescription2.isSubTypeOf(attributeDescription1));
+            assertTrue(attributeDescription2.isSuperTypeOf(attributeDescription1));
         } else {
-            Assert.assertFalse(attributeDescription1.equals(attributeDescription2));
-            Assert.assertFalse(attributeDescription2.equals(attributeDescription1));
+            assertFalse(attributeDescription1.equals(attributeDescription2));
+            assertFalse(attributeDescription2.equals(attributeDescription1));
 
             if (compare < 0) {
-                Assert.assertTrue(attributeDescription1.compareTo(attributeDescription2) < 0);
-                Assert.assertTrue(attributeDescription2.compareTo(attributeDescription1) > 0);
+                assertTrue(attributeDescription1.compareTo(attributeDescription2) < 0);
+                assertTrue(attributeDescription2.compareTo(attributeDescription1) > 0);
             } else {
-                Assert.assertTrue(attributeDescription1.compareTo(attributeDescription2) > 0);
-                Assert.assertTrue(attributeDescription2.compareTo(attributeDescription1) < 0);
+                assertTrue(attributeDescription1.compareTo(attributeDescription2) > 0);
+                assertTrue(attributeDescription2.compareTo(attributeDescription1) < 0);
             }
 
-            Assert.assertEquals(attributeDescription1.isSubTypeOf(attributeDescription2), isSubType);
+            assertEquals(attributeDescription1.isSubTypeOf(attributeDescription2), isSubType);
 
-            Assert.assertEquals(attributeDescription1.isSuperTypeOf(attributeDescription2),
-                    isSuperType);
+            assertEquals(attributeDescription1.isSuperTypeOf(attributeDescription2), isSuperType);
         }
     }
 
@@ -210,17 +212,17 @@
         final AttributeDescription attributeDescription =
                 AttributeDescription.valueOf(ad, Schema.getCoreSchema());
 
-        Assert.assertEquals(attributeDescription.toString(), ad);
+        assertEquals(attributeDescription.toString(), ad);
 
-        Assert.assertEquals(attributeDescription.getAttributeType().getNameOrOID(), at);
+        assertEquals(attributeDescription.getAttributeType().getNameOrOID(), at);
 
-        Assert.assertEquals(attributeDescription.isObjectClass(), isObjectClass);
+        assertEquals(attributeDescription.isObjectClass(), isObjectClass);
 
-        Assert.assertFalse(attributeDescription.hasOptions());
-        Assert.assertFalse(attributeDescription.containsOption("dummy"));
+        assertFalse(attributeDescription.hasOptions());
+        assertFalse(attributeDescription.containsOption("dummy"));
 
         final Iterator<String> iterator = attributeDescription.getOptions().iterator();
-        Assert.assertFalse(iterator.hasNext());
+        assertFalse(iterator.hasNext());
     }
 
     // FIXME: none of these pass! The valueOf method is far to lenient.
@@ -236,38 +238,182 @@
         final AttributeDescription attributeDescription =
                 AttributeDescription.valueOf(ad, Schema.getEmptySchema());
 
-        Assert.assertEquals(attributeDescription.toString(), ad);
+        assertEquals(attributeDescription.toString(), ad);
 
-        Assert.assertEquals(attributeDescription.getAttributeType().getNameOrOID(), at);
+        assertEquals(attributeDescription.getAttributeType().getNameOrOID(), at);
 
-        Assert.assertFalse(attributeDescription.isObjectClass());
+        assertFalse(attributeDescription.isObjectClass());
 
         if (options.length == 0) {
-            Assert.assertFalse(attributeDescription.hasOptions());
+            assertFalse(attributeDescription.hasOptions());
         } else {
-            Assert.assertTrue(attributeDescription.hasOptions());
+            assertTrue(attributeDescription.hasOptions());
         }
 
-        Assert.assertFalse(attributeDescription.containsOption("dummy"));
+        assertFalse(attributeDescription.containsOption("dummy"));
         if (containsFoo) {
-            Assert.assertTrue(attributeDescription.containsOption("foo"));
-            Assert.assertTrue(attributeDescription.containsOption("FOO"));
-            Assert.assertTrue(attributeDescription.containsOption("FoO"));
+            assertTrue(attributeDescription.containsOption("foo"));
+            assertTrue(attributeDescription.containsOption("FOO"));
+            assertTrue(attributeDescription.containsOption("FoO"));
         } else {
-            Assert.assertFalse(attributeDescription.containsOption("foo"));
-            Assert.assertFalse(attributeDescription.containsOption("FOO"));
-            Assert.assertFalse(attributeDescription.containsOption("FoO"));
+            assertFalse(attributeDescription.containsOption("foo"));
+            assertFalse(attributeDescription.containsOption("FOO"));
+            assertFalse(attributeDescription.containsOption("FoO"));
         }
 
         for (final String option : options) {
-            Assert.assertTrue(attributeDescription.containsOption(option));
+            assertTrue(attributeDescription.containsOption(option));
         }
 
         final Iterator<String> iterator = attributeDescription.getOptions().iterator();
         for (final String option : options) {
-            Assert.assertTrue(iterator.hasNext());
-            Assert.assertEquals(iterator.next(), option);
+            assertTrue(iterator.hasNext());
+            assertEquals(iterator.next(), option);
         }
-        Assert.assertFalse(iterator.hasNext());
+        assertFalse(iterator.hasNext());
     }
+
+    @Test
+    public void testWithOptionAddFirstOption() {
+        AttributeDescription ad1 = AttributeDescription.valueOf("cn");
+        AttributeDescription ad2 = ad1.withOption("test");
+        assertTrue(ad2.hasOptions());
+        assertTrue(ad2.containsOption("test"));
+        assertFalse(ad2.containsOption("dummy"));
+        assertEquals(ad2.toString(), "cn;test");
+        assertEquals(ad2.getOptions().iterator().next(), "test");
+    }
+
+    @Test
+    public void testWithOptionAddExistingFirstOption() {
+        AttributeDescription ad1 = AttributeDescription.valueOf("cn;test");
+        AttributeDescription ad2 = ad1.withOption("test");
+        assertSame(ad1, ad2);
+    }
+
+    @Test
+    public void testWithOptionAddSecondOption() {
+        AttributeDescription ad1 = AttributeDescription.valueOf("cn;test1");
+        AttributeDescription ad2 = ad1.withOption("test2");
+        assertTrue(ad2.hasOptions());
+        assertTrue(ad2.containsOption("test1"));
+        assertTrue(ad2.containsOption("test2"));
+        assertFalse(ad2.containsOption("dummy"));
+        assertEquals(ad2.toString(), "cn;test1;test2");
+        Iterator<String> i = ad2.getOptions().iterator();
+        assertEquals(i.next(), "test1");
+        assertEquals(i.next(), "test2");
+    }
+
+    @Test
+    public void testWithOptionAddExistingSecondOption() {
+        AttributeDescription ad1 = AttributeDescription.valueOf("cn;test1;test2");
+        AttributeDescription ad2 = ad1.withOption("test1");
+        AttributeDescription ad3 = ad1.withOption("test2");
+        assertSame(ad1, ad2);
+        assertSame(ad1, ad3);
+    }
+
+    @Test
+    public void testWithoutOptionEmpty() {
+        AttributeDescription ad1 = AttributeDescription.valueOf("cn");
+        AttributeDescription ad2 = ad1.withoutOption("test");
+        assertSame(ad1, ad2);
+    }
+
+    @Test
+    public void testWithoutOptionFirstOption() {
+        AttributeDescription ad1 = AttributeDescription.valueOf("cn;test");
+        AttributeDescription ad2 = ad1.withoutOption("test");
+        assertFalse(ad2.hasOptions());
+        assertFalse(ad2.containsOption("test"));
+        assertEquals(ad2.toString(), "cn");
+        assertFalse(ad2.getOptions().iterator().hasNext());
+    }
+
+    @Test
+    public void testWithoutOptionFirstOptionMissing() {
+        AttributeDescription ad1 = AttributeDescription.valueOf("cn;test");
+        AttributeDescription ad2 = ad1.withoutOption("dummy");
+        assertSame(ad1, ad2);
+    }
+
+    @Test
+    public void testWithoutOptionSecondOption1() {
+        AttributeDescription ad1 = AttributeDescription.valueOf("cn;test1;test2");
+        AttributeDescription ad2 = ad1.withoutOption("test1");
+        assertTrue(ad2.hasOptions());
+        assertFalse(ad2.containsOption("test1"));
+        assertTrue(ad2.containsOption("test2"));
+        assertEquals(ad2.toString(), "cn;test2");
+        assertEquals(ad2.getOptions().iterator().next(), "test2");
+    }
+
+    @Test
+    public void testWithoutOptionSecondOption2() {
+        AttributeDescription ad1 = AttributeDescription.valueOf("cn;test1;test2");
+        AttributeDescription ad2 = ad1.withoutOption("test2");
+        assertTrue(ad2.hasOptions());
+        assertTrue(ad2.containsOption("test1"));
+        assertFalse(ad2.containsOption("test2"));
+        assertEquals(ad2.toString(), "cn;test1");
+        assertEquals(ad2.getOptions().iterator().next(), "test1");
+    }
+
+    @Test
+    public void testWithoutOptionSecondOptionMissing() {
+        AttributeDescription ad1 = AttributeDescription.valueOf("cn;test1;test2");
+        AttributeDescription ad2 = ad1.withoutOption("dummy");
+        assertSame(ad1, ad2);
+    }
+
+    @Test
+    public void testWithoutOptionThirdOption1() {
+        AttributeDescription ad1 = AttributeDescription.valueOf("cn;test1;test2;test3");
+        AttributeDescription ad2 = ad1.withoutOption("test1");
+        assertTrue(ad2.hasOptions());
+        assertFalse(ad2.containsOption("test1"));
+        assertTrue(ad2.containsOption("test2"));
+        assertTrue(ad2.containsOption("test3"));
+        assertEquals(ad2.toString(), "cn;test2;test3");
+        Iterator<String> i = ad2.getOptions().iterator();
+        assertEquals(i.next(), "test2");
+        assertEquals(i.next(), "test3");
+    }
+
+    @Test
+    public void testWithoutOptionThirdOption2() {
+        AttributeDescription ad1 = AttributeDescription.valueOf("cn;test1;test2;test3");
+        AttributeDescription ad2 = ad1.withoutOption("test2");
+        assertTrue(ad2.hasOptions());
+        assertTrue(ad2.containsOption("test1"));
+        assertFalse(ad2.containsOption("test2"));
+        assertTrue(ad2.containsOption("test3"));
+        assertEquals(ad2.toString(), "cn;test1;test3");
+        Iterator<String> i = ad2.getOptions().iterator();
+        assertEquals(i.next(), "test1");
+        assertEquals(i.next(), "test3");
+    }
+
+    @Test
+    public void testWithoutOptionThirdOption3() {
+        AttributeDescription ad1 = AttributeDescription.valueOf("cn;test1;test2;test3");
+        AttributeDescription ad2 = ad1.withoutOption("test3");
+        assertTrue(ad2.hasOptions());
+        assertTrue(ad2.containsOption("test1"));
+        assertTrue(ad2.containsOption("test2"));
+        assertFalse(ad2.containsOption("test3"));
+        assertEquals(ad2.toString(), "cn;test1;test2");
+        Iterator<String> i = ad2.getOptions().iterator();
+        assertEquals(i.next(), "test1");
+        assertEquals(i.next(), "test2");
+    }
+
+    @Test
+    public void testWithoutOptionThirdOptionMissing() {
+        AttributeDescription ad1 = AttributeDescription.valueOf("cn;test1;test2;test3");
+        AttributeDescription ad2 = ad1.withoutOption("dummy");
+        assertSame(ad1, ad2);
+    }
+
 }

--
Gitblit v1.10.0