From 1f6f4598cae318e881119af7a206c7c5899aff30 Mon Sep 17 00:00:00 2001
From: Jean-Noel Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Fri, 14 Mar 2014 08:25:59 +0000
Subject: [PATCH] OPENDJ-1368 (CR-3177) Remove AttributeValue

---
 opendj3-server-dev/src/server/org/opends/server/admin/server/ServerManagementContext.java                                           |   37 
 opendj3-server-dev/src/server/org/opends/server/schema/SubtreeSpecificationSyntax.java                                              |   66 -
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java                        |    6 
 opendj3-server-dev/src/server/org/opends/server/schema/DistinguishedNameSyntax.java                                                 |   40 
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestAttributeValue.java                               |   33 -
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestEntry.java                                        |  124 +--
 opendj3-server-dev/src/server/org/opends/server/types/AttributeParser.java                                                          |  494 ++++++++++++++++++
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/InitOnLineTest.java                             |   32 
 /dev/null                                                                                                                           |  167 ------
 opendj3-server-dev/src/server/org/opends/server/schema/DirectoryStringSyntax.java                                                   |   54 -
 opendj3-server-dev/src/server/org/opends/server/schema/BinarySyntax.java                                                            |   25 
 opendj3-server-dev/src/server/org/opends/server/schema/BooleanSyntax.java                                                           |   60 -
 opendj3-server-dev/src/server/org/opends/server/types/Entry.java                                                                    |   94 --
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/crypto/GetSymmetricKeyExtendedOperationTestCase.java        |   14 
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntityTagVirtualAttributeProviderTestCase.java   |   56 -
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java |   11 
 opendj3-server-dev/src/server/org/opends/server/schema/IntegerSyntax.java                                                           |   40 -
 opendj3-server-dev/src/server/org/opends/server/crypto/CryptoManagerImpl.java                                                       |  149 ++---
 opendj3-server-dev/src/server/org/opends/server/admin/AdministrationDataSync.java                                                   |   46 -
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java                        |   39 
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/tasks/TasksTestCase.java                                    |   29 
 21 files changed, 801 insertions(+), 815 deletions(-)

diff --git a/opendj3-server-dev/src/server/org/opends/server/admin/AdministrationDataSync.java b/opendj3-server-dev/src/server/org/opends/server/admin/AdministrationDataSync.java
index 1610aa0..f1627cb 100644
--- a/opendj3-server-dev/src/server/org/opends/server/admin/AdministrationDataSync.java
+++ b/opendj3-server-dev/src/server/org/opends/server/admin/AdministrationDataSync.java
@@ -26,33 +26,28 @@
  */
 package org.opends.server.admin;
 
-
-
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 
+import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.DereferenceAliasesPolicy;
+import org.forgerock.opendj.ldap.ModificationType;
+import org.forgerock.opendj.ldap.ResultCode;
+import org.forgerock.opendj.ldap.SearchScope;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.protocols.internal.InternalSearchOperation;
 import org.opends.server.protocols.ldap.LDAPFilter;
-import org.opends.server.schema.DirectoryStringSyntax;
 import org.opends.server.types.Attribute;
 import org.opends.server.types.AttributeType;
 import org.opends.server.types.Attributes;
-import org.forgerock.opendj.ldap.ByteString;
 import org.opends.server.types.DN;
-import org.forgerock.opendj.ldap.DereferenceAliasesPolicy;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.Entry;
 import org.opends.server.types.LDAPException;
 import org.opends.server.types.Modification;
-import org.forgerock.opendj.ldap.ModificationType;
-import org.forgerock.opendj.ldap.ResultCode;
 import org.opends.server.types.SearchResultEntry;
-import org.forgerock.opendj.ldap.SearchScope;
-
-
 
 /**
  * Check if information found in "cn=admin data" is coherent with
@@ -197,12 +192,6 @@
     }
 
     // Look for a local server with the Ldap Port.
-    String attrName = "hostname";
-    AttributeType hostnameType = DirectoryServer.getAttributeType(attrName);
-    if (hostnameType == null)
-    {
-      hostnameType = DirectoryServer.getDefaultAttributeType(attrName);
-    }
     try
     {
       InternalSearchOperation op = internalConnection.processSearch(
@@ -213,8 +202,8 @@
         Entry entry = null;
         for (Entry currentEntry : op.getSearchEntries())
         {
-          String currentHostname = currentEntry.getAttributeValue(hostnameType,
-              DirectoryStringSyntax.DECODER);
+          String currentHostname =
+              currentEntry.parseAttribute("hostname").asString();
           try
           {
             String currentIPAddress = java.net.InetAddress.getByName(
@@ -222,15 +211,8 @@
             if (currentIPAddress.equals(hostName))
             {
               // Check if one of the port match
-              attrName = "ldapport";
-              AttributeType portType = DirectoryServer
-                  .getAttributeType(attrName);
-              if (portType == null)
-              {
-                portType = DirectoryServer.getDefaultAttributeType(attrName);
-              }
-              String currentport = currentEntry.getAttributeValue(portType,
-                  DirectoryStringSyntax.DECODER);
+              String currentport =
+                  currentEntry.parseAttribute("ldapport").asString();
               if (currentport.equals(ldapPort))
               {
                 entry = currentEntry;
@@ -238,14 +220,8 @@
               }
               if (ldapsPortEnable)
               {
-                attrName = "ldapsport";
-                portType = DirectoryServer.getAttributeType(attrName);
-                if (portType == null)
-                {
-                  portType = DirectoryServer.getDefaultAttributeType(attrName);
-                }
-                currentport = currentEntry.getAttributeValue(portType,
-                    DirectoryStringSyntax.DECODER);
+                currentport =
+                    currentEntry.parseAttribute("ldapsport").asString();
                 if (currentport.equals(ldapsPort))
                 {
                   entry = currentEntry;
diff --git a/opendj3-server-dev/src/server/org/opends/server/admin/server/ServerManagementContext.java b/opendj3-server-dev/src/server/org/opends/server/admin/server/ServerManagementContext.java
index 115b1d7..eb2a6fc 100644
--- a/opendj3-server-dev/src/server/org/opends/server/admin/server/ServerManagementContext.java
+++ b/opendj3-server-dev/src/server/org/opends/server/admin/server/ServerManagementContext.java
@@ -45,6 +45,8 @@
 import java.util.TreeSet;
 
 import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.i18n.slf4j.LocalizedLogger;
+import org.forgerock.opendj.config.server.ConfigException;
 import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
 import org.opends.server.admin.AbstractManagedObjectDefinition;
 import org.opends.server.admin.AggregationPropertyDefinition;
@@ -72,17 +74,13 @@
 import org.opends.server.admin.DefinitionDecodingException.Reason;
 import org.opends.server.admin.std.meta.RootCfgDefn;
 import org.opends.server.admin.std.server.RootCfg;
-import org.opends.server.api.AttributeValueDecoder;
 import org.opends.server.config.ConfigEntry;
-import org.forgerock.opendj.config.server.ConfigException;
 import org.opends.server.core.DirectoryServer;
-import org.forgerock.i18n.slf4j.LocalizedLogger;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeValueIterable;
 import org.opends.server.types.AttributeType;
 import org.opends.server.types.AttributeValue;
 import org.opends.server.types.DN;
-import org.opends.server.types.DirectoryException;
-
-
 
 /**
  * Server management connection context.
@@ -125,6 +123,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public Collection<T> visitAbsoluteInherited(
         AbsoluteInheritedDefaultBehaviorProvider<T> d, Void p) {
       try {
@@ -141,6 +140,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public Collection<T> visitAlias(AliasDefaultBehaviorProvider<T> d, Void p) {
       return Collections.emptySet();
     }
@@ -150,6 +150,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public Collection<T> visitDefined(DefinedDefaultBehaviorProvider<T> d,
         Void p) {
       Collection<String> stringValues = d.getDefaultValues();
@@ -172,6 +173,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public Collection<T> visitRelativeInherited(
         RelativeInheritedDefaultBehaviorProvider<T> d, Void p) {
       try {
@@ -188,6 +190,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public Collection<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d,
         Void p) {
       return Collections.emptySet();
@@ -313,6 +316,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public boolean matches(AbstractManagedObjectDefinition<?, ?> d) {
       String oc = LDAPProfile.getInstance().getObjectClass(d);
       return entry.hasObjectClass(oc);
@@ -899,23 +903,14 @@
     // since the attribute should have been defined.
     String attrID = LDAPProfile.getInstance().getAttributeName(d, pd);
     AttributeType type = DirectoryServer.getAttributeType(attrID, true);
-    AttributeValueDecoder<AttributeValue> decoder =
-      new AttributeValueDecoder<AttributeValue>() {
+    List<Attribute> attributes = configEntry.getEntry().getAttribute(type, true);
 
-      public AttributeValue decode(AttributeValue value)
-          throws DirectoryException {
-        return value;
-      }
-    };
-
-    List<AttributeValue> values = new LinkedList<AttributeValue>();
-    try {
-      configEntry.getEntry().getAttributeValues(type, decoder, values);
-    } catch (DirectoryException e) {
-      // Should not happen.
-      throw new RuntimeException(e);
+    List<AttributeValue> results = new LinkedList<AttributeValue>();
+    for (AttributeValue v : new AttributeValueIterable(attributes))
+    {
+      results.add(v);
     }
-    return values;
+    return results;
   }
 
 
diff --git a/opendj3-server-dev/src/server/org/opends/server/api/AttributeValueDecoder.java b/opendj3-server-dev/src/server/org/opends/server/api/AttributeValueDecoder.java
deleted file mode 100644
index 0ceaaf5..0000000
--- a/opendj3-server-dev/src/server/org/opends/server/api/AttributeValueDecoder.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
- * or http://forgerock.org/license/CDDLv1.0.html.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at legal-notices/CDDLv1_0.txt.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
- */
-package org.opends.server.api;
-
-
-
-import org.opends.server.types.AttributeValue;
-import org.opends.server.types.DirectoryException;
-
-
-
-/**
- * A factory interface for decoding attribute values into objects.
- *
- * @param  <T>  Decode the attribute value to an object of this type.
- */
-@org.opends.server.types.PublicAPI(
-     stability=org.opends.server.types.StabilityLevel.VOLATILE,
-     mayInstantiate=false,
-     mayExtend=true,
-     mayInvoke=true)
-public interface AttributeValueDecoder<T>
-{
-  /**
-   * Decode the specified attribute value to an object of type T.
-   *
-   * @param value
-   *          The attribute value.
-   * @return The decoded attribute value.
-   * @throws DirectoryException
-   *           If the value could not be decoded successfully.
-   */
-  T decode(AttributeValue value) throws DirectoryException;
-}
-
diff --git a/opendj3-server-dev/src/server/org/opends/server/crypto/CryptoManagerImpl.java b/opendj3-server-dev/src/server/org/opends/server/crypto/CryptoManagerImpl.java
index 296ec77..fc78a51 100644
--- a/opendj3-server-dev/src/server/org/opends/server/crypto/CryptoManagerImpl.java
+++ b/opendj3-server-dev/src/server/org/opends/server/crypto/CryptoManagerImpl.java
@@ -49,6 +49,7 @@
 
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
+import org.forgerock.opendj.config.server.ConfigException;
 import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.opendj.ldap.DereferenceAliasesPolicy;
 import org.forgerock.opendj.ldap.ModificationType;
@@ -61,7 +62,6 @@
 import org.opends.server.api.Backend;
 import org.opends.server.backends.TrustStoreBackend;
 import org.opends.server.config.ConfigConstants;
-import org.forgerock.opendj.config.server.ConfigException;
 import org.opends.server.core.AddOperation;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.ModifyOperation;
@@ -72,20 +72,24 @@
 import org.opends.server.protocols.ldap.ExtendedResponseProtocolOp;
 import org.opends.server.protocols.ldap.LDAPMessage;
 import org.opends.server.protocols.ldap.LDAPResultCode;
-import org.opends.server.schema.BinarySyntax;
-import org.opends.server.schema.DirectoryStringSyntax;
-import org.opends.server.schema.IntegerSyntax;
 import org.opends.server.tools.LDAPConnection;
 import org.opends.server.tools.LDAPConnectionOptions;
 import org.opends.server.tools.LDAPReader;
 import org.opends.server.tools.LDAPWriter;
 import org.opends.server.types.*;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.Attributes;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.Modification;
+import org.opends.server.types.RDN;
 import org.opends.server.util.Base64;
 import org.opends.server.util.SelectableCertificateKeyManager;
 import org.opends.server.util.ServerConstants;
 import org.opends.server.util.StaticUtils;
 
 import static org.opends.messages.CoreMessages.*;
+import static org.opends.server.config.ConfigConstants.*;
 import static org.opends.server.util.ServerConstants.*;
 import static org.opends.server.util.StaticUtils.*;
 
@@ -534,8 +538,8 @@
           for (Entry e : searchOp.getSearchEntries()) {
             /* attribute ds-cfg-public-key-certificate is a MUST in
                the schema */
-            certificate = e.getAttributeValue(
-                  attrPublicKeyCertificate, BinarySyntax.DECODER).toByteArray();
+            certificate = e.parseAttribute(
+                ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE).asByteString().toByteArray();
           }
           break;
         }
@@ -744,10 +748,9 @@
       for (Entry e : searchOp.getSearchEntries()) {
         /* attribute ds-cfg-key-id is the RDN and attribute
            ds-cfg-public-key-certificate is a MUST in the schema */
-        final String keyID = e.getAttributeValue(
-                attrKeyID, DirectoryStringSyntax.DECODER);
-        final byte[] certificate = e.getAttributeValue(
-                attrPublicKeyCertificate, BinarySyntax.DECODER).toByteArray();
+        final String keyID = e.parseAttribute(ATTR_CRYPTO_KEY_ID).asString();
+        final byte[] certificate = e.parseAttribute(
+            ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE).asByteString().toByteArray();
         certificateMap.put(keyID, certificate);
       }
     }
@@ -1001,7 +1004,7 @@
    * @return The symmetric key value for this server, or null if
    *         none could be obtained.
    */
-  private String getSymmetricKey(List<String> symmetricKeys)
+  private String getSymmetricKey(Set<String> symmetricKeys)
   {
     InternalClientConnection internalConnection =
          InternalClientConnection.getRootConnection();
@@ -1028,14 +1031,8 @@
              internalSearch.getSearchEntries();
         for (SearchResultEntry resultEntry : resultEntries)
         {
-          AttributeType hostnameAttr =
-               DirectoryServer.getAttributeType("hostname", true);
-          String hostname = resultEntry.getAttributeValue(
-               hostnameAttr, DirectoryStringSyntax.DECODER);
-          AttributeType ldapPortAttr =
-               DirectoryServer.getAttributeType("ldapport", true);
-          Integer ldapPort = resultEntry.getAttributeValue(
-               ldapPortAttr, IntegerSyntax.DECODER);
+          String hostname = resultEntry.parseAttribute("hostname").asString();
+          Integer ldapPort = resultEntry.parseAttribute("ldapport").asInteger();
 
           // Connect to the server.
           AtomicInteger nextMessageID = new AtomicInteger(1);
@@ -1127,28 +1124,20 @@
 
     try
     {
-      String keyID =
-           entry.getAttributeValue(attrKeyID,
-                                   DirectoryStringSyntax.DECODER);
-      int ivLengthBits =
-           entry.getAttributeValue(attrInitVectorLength,
-                                   IntegerSyntax.DECODER);
-      int keyLengthBits =
-           entry.getAttributeValue(attrKeyLength,
-                                   IntegerSyntax.DECODER);
-      String transformation =
-           entry.getAttributeValue(attrTransformation,
-                                   DirectoryStringSyntax.DECODER);
-      String compromisedTime =
-           entry.getAttributeValue(attrCompromisedTime,
-                                   DirectoryStringSyntax.DECODER);
+      String keyID = entry.parseAttribute(ATTR_CRYPTO_KEY_ID).asString();
+      int ivLengthBits = entry.parseAttribute(
+          ATTR_CRYPTO_INIT_VECTOR_LENGTH_BITS).asInteger();
+      int keyLengthBits = entry.parseAttribute(
+          ATTR_CRYPTO_KEY_LENGTH_BITS).asInteger();
+      String transformation = entry.parseAttribute(
+          ATTR_CRYPTO_CIPHER_TRANSFORMATION_NAME).asString();
+      String compromisedTime = entry.parseAttribute(
+          ATTR_CRYPTO_KEY_COMPROMISED_TIME).asString();
 
       boolean isCompromised = compromisedTime != null;
 
-      ArrayList<String> symmetricKeys = new ArrayList<String>();
-      entry.getAttributeValues(attrSymmetricKey,
-                             DirectoryStringSyntax.DECODER,
-                             symmetricKeys);
+      Set<String> symmetricKeys =
+          entry.parseAttribute(ATTR_CRYPTO_SYMMETRIC_KEY).asSetOfString();
 
       // Find the symmetric key value that was wrapped using
       // our instance key.
@@ -1194,7 +1183,11 @@
                 ERR_CRYPTOMGR_IMPORT_KEY_ENTRY_FAILED_TO_ADD_KEY.get(entry.getName()));
       }
     }
-    catch (DirectoryException ex)
+    catch (CryptoManagerException e)
+    {
+      throw e;
+    }
+    catch (Exception ex)
     {
       logger.traceException(ex);
       throw new CryptoManagerException(
@@ -1226,25 +1219,18 @@
 
     try
     {
-      String keyID =
-           entry.getAttributeValue(attrKeyID,
-                                   DirectoryStringSyntax.DECODER);
-      int keyLengthBits =
-           entry.getAttributeValue(attrKeyLength,
-                                   IntegerSyntax.DECODER);
-      String algorithm =
-           entry.getAttributeValue(attrMacAlgorithm,
-                                   DirectoryStringSyntax.DECODER);
-      String compromisedTime =
-           entry.getAttributeValue(attrCompromisedTime,
-                                   DirectoryStringSyntax.DECODER);
+      String keyID = entry.parseAttribute(ATTR_CRYPTO_KEY_ID).asString();
+      int keyLengthBits = entry.parseAttribute(
+          ATTR_CRYPTO_KEY_LENGTH_BITS).asInteger();
+      String algorithm = entry.parseAttribute(
+          ATTR_CRYPTO_MAC_ALGORITHM_NAME).asString();
+      String compromisedTime = entry.parseAttribute(
+          ATTR_CRYPTO_KEY_COMPROMISED_TIME).asString();
 
       boolean isCompromised = compromisedTime != null;
 
-      ArrayList<String> symmetricKeys = new ArrayList<String>();
-      entry.getAttributeValues(attrSymmetricKey,
-                             DirectoryStringSyntax.DECODER,
-                             symmetricKeys);
+      Set<String> symmetricKeys =
+          entry.parseAttribute(ATTR_CRYPTO_SYMMETRIC_KEY).asSetOfString();
 
       // Find the symmetric key value that was wrapped using our
       // instance key.
@@ -1294,9 +1280,12 @@
                                       secretKey, keyLengthBits,
                                       isCompromised);
       }
-
     }
-    catch (DirectoryException ex)
+    catch (CryptoManagerException e)
+    {
+      throw e;
+    }
+    catch (Exception ex)
     {
       logger.traceException(ex);
       throw new CryptoManagerException(
@@ -1602,6 +1591,15 @@
     private boolean fIsCompromised = false;
   }
 
+  private static void putSingleValueAttribute(
+      Map<AttributeType, List<Attribute>> attrs, AttributeType type,
+      String value)
+  {
+    ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
+    attrList.add(Attributes.create(type, AttributeValues.create(type, value)));
+    attrs.put(type, attrList);
+  }
+
   /**
    * This class corresponds to the cipher key entry in ADS. It is
    * used in the local cache of key entries that have been requested
@@ -1704,27 +1702,15 @@
       userAttrs.put(attrKeyID, attrList);
 
       // Add the transformation name attribute.
-      attrList = new ArrayList<Attribute>(1);
-      attrList.add(Attributes.create(attrTransformation,
-          AttributeValues.create(attrTransformation,
-              keyEntry.getType())));
-      userAttrs.put(attrTransformation, attrList);
+      putSingleValueAttribute(userAttrs, attrTransformation, keyEntry.getType());
 
       // Add the init vector length attribute.
-      attrList = new ArrayList<Attribute>(1);
-      attrList.add(Attributes.create(attrInitVectorLength,
-          AttributeValues.create(attrInitVectorLength,
-              String.valueOf(keyEntry
-              .getIVLengthBits()))));
-      userAttrs.put(attrInitVectorLength, attrList);
-
+      putSingleValueAttribute(userAttrs, attrInitVectorLength,
+          String.valueOf(keyEntry.getIVLengthBits()));
 
       // Add the key length attribute.
-      attrList = new ArrayList<Attribute>(1);
-      attrList.add(Attributes.create(attrKeyLength,
-          AttributeValues.create(attrKeyLength,
-              String.valueOf(keyEntry.getKeyLengthBits()))));
-      userAttrs.put(attrKeyLength, attrList);
+      putSingleValueAttribute(userAttrs, attrKeyLength,
+          String.valueOf(keyEntry.getKeyLengthBits()));
 
 
       // Get the trusted certificates.
@@ -2243,23 +2229,14 @@
 
       // Add the key ID attribute.
       ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
-      attrList.add(Attributes.create(attrKeyID,
-                                 distinguishedValue));
+      attrList.add(Attributes.create(attrKeyID, distinguishedValue));
       userAttrs.put(attrKeyID, attrList);
 
       // Add the mac algorithm name attribute.
-      attrList = new ArrayList<Attribute>(1);
-      attrList.add(Attributes.create(attrMacAlgorithm,
-          AttributeValues.create(attrMacAlgorithm, keyEntry.getType())));
-      userAttrs.put(attrMacAlgorithm, attrList);
-
+      putSingleValueAttribute(userAttrs, attrMacAlgorithm, keyEntry.getType());
 
       // Add the key length attribute.
-      attrList = new ArrayList<Attribute>(1);
-      attrList.add(Attributes.create(attrKeyLength, AttributeValues.create(
-          attrKeyLength, String.valueOf(keyEntry.getKeyLengthBits()))));
-      userAttrs.put(attrKeyLength, attrList);
-
+      putSingleValueAttribute(userAttrs, attrKeyLength, String.valueOf(keyEntry.getKeyLengthBits()));
 
       // Get the trusted certificates.
       Map<String, byte[]> trustedCerts =
diff --git a/opendj3-server-dev/src/server/org/opends/server/schema/BinarySyntax.java b/opendj3-server-dev/src/server/org/opends/server/schema/BinarySyntax.java
index 889f0c0..fa57459 100644
--- a/opendj3-server-dev/src/server/org/opends/server/schema/BinarySyntax.java
+++ b/opendj3-server-dev/src/server/org/opends/server/schema/BinarySyntax.java
@@ -28,14 +28,11 @@
 
 import org.forgerock.i18n.LocalizableMessageBuilder;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
+import org.forgerock.opendj.config.server.ConfigException;
 import org.forgerock.opendj.ldap.ByteSequence;
-import org.forgerock.opendj.ldap.ByteString;
 import org.opends.server.admin.std.server.AttributeSyntaxCfg;
 import org.opends.server.api.*;
-import org.forgerock.opendj.config.server.ConfigException;
 import org.opends.server.core.DirectoryServer;
-import org.opends.server.types.AttributeValue;
-import org.opends.server.types.DirectoryException;
 
 import static org.opends.messages.SchemaMessages.*;
 import static org.opends.server.schema.SchemaConstants.*;
@@ -61,26 +58,6 @@
   private SubstringMatchingRule defaultSubstringMatchingRule;
 
 
-
-  /**
-   * A {@code byte[]} attribute value decoder for this syntax.
-   */
-  public static final AttributeValueDecoder<ByteString> DECODER =
-    new AttributeValueDecoder<ByteString>()
-  {
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public ByteString decode(AttributeValue value) throws DirectoryException {
-      // Make sure that the value is valid.
-      value.getNormalizedValue();
-      return value.getValue();
-    }
-  };
-
-
-
   /**
    * Creates a new instance of this syntax.  Note that the only thing that
    * should be done here is to invoke the default constructor for the
diff --git a/opendj3-server-dev/src/server/org/opends/server/schema/BooleanSyntax.java b/opendj3-server-dev/src/server/org/opends/server/schema/BooleanSyntax.java
index e409036..197c508 100644
--- a/opendj3-server-dev/src/server/org/opends/server/schema/BooleanSyntax.java
+++ b/opendj3-server-dev/src/server/org/opends/server/schema/BooleanSyntax.java
@@ -26,27 +26,24 @@
  */
 package org.opends.server.schema;
 
+import org.forgerock.i18n.LocalizableMessageBuilder;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
-
+import org.forgerock.opendj.config.server.ConfigException;
+import org.forgerock.opendj.ldap.ByteSequence;
+import org.forgerock.opendj.ldap.ByteString;
 import org.opends.server.admin.std.server.AttributeSyntaxCfg;
 import org.opends.server.api.ApproximateMatchingRule;
 import org.opends.server.api.AttributeSyntax;
-import org.opends.server.api.AttributeValueDecoder;
 import org.opends.server.api.EqualityMatchingRule;
 import org.opends.server.api.OrderingMatchingRule;
 import org.opends.server.api.SubstringMatchingRule;
-import org.forgerock.opendj.config.server.ConfigException;
 import org.opends.server.core.DirectoryServer;
-
-import org.opends.server.types.*;
-import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.ByteString;
-import org.forgerock.opendj.ldap.ByteSequence;
-import static org.opends.messages.SchemaMessages.*;
-import org.forgerock.i18n.LocalizableMessageBuilder;
-import static org.opends.server.schema.SchemaConstants.*;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.AttributeValues;
 import org.opends.server.util.ServerConstants;
 
+import static org.opends.messages.SchemaMessages.*;
+import static org.opends.server.schema.SchemaConstants.*;
 
 /**
  * This class defines the Boolean attribute syntax, which only allows values of
@@ -67,36 +64,6 @@
 
 
   /**
-   * A {@link Boolean} attribute value decoder for this syntax.
-   */
-  public static final AttributeValueDecoder<Boolean> DECODER =
-    new AttributeValueDecoder<Boolean>()
-  {
-    /**
-     * {@inheritDoc}
-     */
-    public Boolean decode(AttributeValue value) throws DirectoryException
-    {
-      ByteString normalizedValue = value.getNormalizedValue();
-      if (normalizedValue.equals(ServerConstants.TRUE_VALUE))
-      {
-        return true;
-      }
-      else if (normalizedValue.equals(ServerConstants.FALSE_VALUE))
-      {
-        return false;
-      }
-      else
-      {
-        throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
-            WARN_ATTR_SYNTAX_ILLEGAL_BOOLEAN.get(normalizedValue));
-      }
-    }
-  };
-
-
-
-  /**
    * Creates a new instance of this syntax.  Note that the only thing that
    * should be done here is to invoke the default constructor for the
    * superclass.  All initialization should be performed in the
@@ -112,6 +79,7 @@
   /**
    * {@inheritDoc}
    */
+  @Override
   public void initializeSyntax(AttributeSyntaxCfg configuration)
          throws ConfigException
   {
@@ -130,6 +98,7 @@
    *
    * @return  The common name for this attribute syntax.
    */
+  @Override
   public String getName()
   {
     return SYNTAX_BOOLEAN_NAME;
@@ -142,6 +111,7 @@
    *
    * @return  The OID for this attribute syntax.
    */
+  @Override
   public String getOID()
   {
     return SYNTAX_BOOLEAN_OID;
@@ -154,6 +124,7 @@
    *
    * @return  A description for this attribute syntax.
    */
+  @Override
   public String getDescription()
   {
     return SYNTAX_BOOLEAN_DESCRIPTION;
@@ -169,6 +140,7 @@
    *          attributes with this syntax, or <CODE>null</CODE> if equality
    *          matches will not be allowed for this type by default.
    */
+  @Override
   public EqualityMatchingRule getEqualityMatchingRule()
   {
     return defaultEqualityMatchingRule;
@@ -184,6 +156,7 @@
    *          attributes with this syntax, or <CODE>null</CODE> if ordering
    *          matches will not be allowed for this type by default.
    */
+  @Override
   public OrderingMatchingRule getOrderingMatchingRule()
   {
     // Ordering matches are not allowed by default.
@@ -200,6 +173,7 @@
    *          attributes with this syntax, or <CODE>null</CODE> if substring
    *          matches will not be allowed for this type by default.
    */
+  @Override
   public SubstringMatchingRule getSubstringMatchingRule()
   {
     // Substring matches are not allowed by default.
@@ -216,6 +190,7 @@
    *          attributes with this syntax, or <CODE>null</CODE> if approximate
    *          matches will not be allowed for this type by default.
    */
+  @Override
   public ApproximateMatchingRule getApproximateMatchingRule()
   {
     // Approximate matches are not allowed by default.
@@ -236,6 +211,7 @@
    * @return  <CODE>true</CODE> if the provided value is acceptable for use with
    *          this syntax, or <CODE>false</CODE> if not.
    */
+  @Override
   public boolean valueIsAcceptable(ByteSequence value,
                                    LocalizableMessageBuilder invalidReason)
   {
@@ -281,6 +257,7 @@
   /**
    * {@inheritDoc}
    */
+  @Override
   public boolean isBEREncodingRequired()
   {
     return false;
@@ -291,6 +268,7 @@
   /**
    * {@inheritDoc}
    */
+  @Override
   public boolean isHumanReadable()
   {
     return true;
diff --git a/opendj3-server-dev/src/server/org/opends/server/schema/DirectoryStringSyntax.java b/opendj3-server-dev/src/server/org/opends/server/schema/DirectoryStringSyntax.java
index 76ea94c..ee53e5c 100644
--- a/opendj3-server-dev/src/server/org/opends/server/schema/DirectoryStringSyntax.java
+++ b/opendj3-server-dev/src/server/org/opends/server/schema/DirectoryStringSyntax.java
@@ -25,31 +25,25 @@
  *      Portions Copyright 2012-2014 ForgeRock AS
  */
 package org.opends.server.schema;
-import org.forgerock.i18n.LocalizableMessage;
-import org.forgerock.i18n.slf4j.LocalizedLogger;
-
-
-
 import java.util.List;
 
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.i18n.LocalizableMessageBuilder;
+import org.forgerock.i18n.slf4j.LocalizedLogger;
+import org.forgerock.opendj.config.server.ConfigException;
+import org.forgerock.opendj.ldap.ByteSequence;
+import org.forgerock.opendj.ldap.ResultCode;
 import org.opends.server.admin.server.ConfigurationChangeListener;
 import org.opends.server.admin.std.server.DirectoryStringAttributeSyntaxCfg;
 import org.opends.server.api.ApproximateMatchingRule;
 import org.opends.server.api.AttributeSyntax;
-import org.opends.server.api.AttributeValueDecoder;
 import org.opends.server.api.EqualityMatchingRule;
 import org.opends.server.api.OrderingMatchingRule;
 import org.opends.server.api.SubstringMatchingRule;
-import org.forgerock.opendj.config.server.ConfigException;
 import org.opends.server.core.DirectoryServer;
+import org.opends.server.types.ConfigChangeResult;
 
-
-import org.opends.server.types.*;
-import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.ByteSequence;
 import static org.opends.messages.SchemaMessages.*;
-
-import org.forgerock.i18n.LocalizableMessageBuilder;
 import static org.opends.server.schema.SchemaConstants.*;
 
 
@@ -85,26 +79,6 @@
   private SubstringMatchingRule defaultSubstringMatchingRule;
 
 
-
-  /**
-   * A {@link String} attribute value decoder for this syntax.
-   */
-  public static final AttributeValueDecoder<String> DECODER =
-    new AttributeValueDecoder<String>()
-  {
-    /**
-     * {@inheritDoc}
-     */
-    public String decode(AttributeValue value) throws DirectoryException
-    {
-      // Make sure that the value is valid.
-      value.getNormalizedValue();
-      return value.getValue().toString();
-    }
-  };
-
-
-
   /**
    * Creates a new instance of this syntax.  Note that the only thing that
    * should be done here is to invoke the default constructor for the
@@ -121,6 +95,7 @@
   /**
    * {@inheritDoc}
    */
+  @Override
   public void initializeSyntax(DirectoryStringAttributeSyntaxCfg configuration)
          throws ConfigException
   {
@@ -176,6 +151,7 @@
   /**
    * Performs any finalization that may be necessary for this attribute syntax.
    */
+  @Override
   public void finalizeSyntax()
   {
     currentConfig.removeDirectoryStringChangeListener(this);
@@ -188,6 +164,7 @@
    *
    * @return  The common name for this attribute syntax.
    */
+  @Override
   public String getName()
   {
     return SYNTAX_DIRECTORY_STRING_NAME;
@@ -200,6 +177,7 @@
    *
    * @return  The OID for this attribute syntax.
    */
+  @Override
   public String getOID()
   {
     return SYNTAX_DIRECTORY_STRING_OID;
@@ -212,6 +190,7 @@
    *
    * @return  A description for this attribute syntax.
    */
+  @Override
   public String getDescription()
   {
     return SYNTAX_DIRECTORY_STRING_DESCRIPTION;
@@ -227,6 +206,7 @@
    *          attributes with this syntax, or <CODE>null</CODE> if equality
    *          matches will not be allowed for this type by default.
    */
+  @Override
   public EqualityMatchingRule getEqualityMatchingRule()
   {
     return defaultEqualityMatchingRule;
@@ -242,6 +222,7 @@
    *          attributes with this syntax, or <CODE>null</CODE> if ordering
    *          matches will not be allowed for this type by default.
    */
+  @Override
   public OrderingMatchingRule getOrderingMatchingRule()
   {
     return defaultOrderingMatchingRule;
@@ -257,6 +238,7 @@
    *          attributes with this syntax, or <CODE>null</CODE> if substring
    *          matches will not be allowed for this type by default.
    */
+  @Override
   public SubstringMatchingRule getSubstringMatchingRule()
   {
     return defaultSubstringMatchingRule;
@@ -272,6 +254,7 @@
    *          attributes with this syntax, or <CODE>null</CODE> if approximate
    *          matches will not be allowed for this type by default.
    */
+  @Override
   public ApproximateMatchingRule getApproximateMatchingRule()
   {
     return defaultApproximateMatchingRule;
@@ -291,6 +274,7 @@
    * @return  <CODE>true</CODE> if the provided value is acceptable for use with
    *          this syntax, or <CODE>false</CODE> if not.
    */
+  @Override
   public boolean valueIsAcceptable(ByteSequence value,
                                    LocalizableMessageBuilder invalidReason)
   {
@@ -328,6 +312,7 @@
   /**
    * {@inheritDoc}
    */
+  @Override
   public boolean isConfigurationChangeAcceptable(
                       DirectoryStringAttributeSyntaxCfg configuration,
                       List<LocalizableMessage> unacceptableReasons)
@@ -341,6 +326,7 @@
   /**
    * {@inheritDoc}
    */
+  @Override
   public ConfigChangeResult applyConfigurationChange(
               DirectoryStringAttributeSyntaxCfg configuration)
   {
@@ -355,6 +341,7 @@
   /**
    * {@inheritDoc}
    */
+  @Override
   public boolean isBEREncodingRequired()
   {
     return false;
@@ -365,6 +352,7 @@
   /**
    * {@inheritDoc}
    */
+  @Override
   public boolean isHumanReadable()
   {
     return true;
diff --git a/opendj3-server-dev/src/server/org/opends/server/schema/DistinguishedNameSyntax.java b/opendj3-server-dev/src/server/org/opends/server/schema/DistinguishedNameSyntax.java
index ce9e690..f2888a1 100644
--- a/opendj3-server-dev/src/server/org/opends/server/schema/DistinguishedNameSyntax.java
+++ b/opendj3-server-dev/src/server/org/opends/server/schema/DistinguishedNameSyntax.java
@@ -28,21 +28,21 @@
 
 
 
+import org.forgerock.i18n.LocalizableMessageBuilder;
+import org.forgerock.i18n.slf4j.LocalizedLogger;
+import org.forgerock.opendj.config.server.ConfigException;
+import org.forgerock.opendj.ldap.ByteSequence;
 import org.opends.server.admin.std.server.AttributeSyntaxCfg;
 import org.opends.server.api.ApproximateMatchingRule;
 import org.opends.server.api.AttributeSyntax;
-import org.opends.server.api.AttributeValueDecoder;
 import org.opends.server.api.EqualityMatchingRule;
 import org.opends.server.api.OrderingMatchingRule;
 import org.opends.server.api.SubstringMatchingRule;
-import org.forgerock.opendj.config.server.ConfigException;
 import org.opends.server.core.DirectoryServer;
+import org.opends.server.types.DN;
+import org.opends.server.types.DirectoryException;
 
-import org.forgerock.i18n.slf4j.LocalizedLogger;
-import org.opends.server.types.*;
-import org.forgerock.opendj.ldap.ByteSequence;
 import static org.opends.messages.SchemaMessages.*;
-import org.forgerock.i18n.LocalizableMessageBuilder;
 import static org.opends.server.schema.SchemaConstants.*;
 
 
@@ -67,23 +67,6 @@
 
 
   /**
-   * A {@link DN} attribute value decoder for this syntax.
-   */
-  public static final AttributeValueDecoder<DN> DECODER =
-    new AttributeValueDecoder<DN>()
-  {
-    /**
-     * {@inheritDoc}
-     */
-    public DN decode(AttributeValue value) throws DirectoryException
-    {
-      return DN.valueOf(value.getValue().toString());
-    }
-  };
-
-
-
-  /**
    * Creates a new instance of this syntax.  Note that the only thing that
    * should be done here is to invoke the default constructor for the
    * superclass.  All initialization should be performed in the
@@ -99,6 +82,7 @@
   /**
    * {@inheritDoc}
    */
+  @Override
   public void initializeSyntax(AttributeSyntaxCfg configuration)
          throws ConfigException
   {
@@ -124,6 +108,7 @@
    *
    * @return  The common name for this attribute syntax.
    */
+  @Override
   public String getName()
   {
     return SYNTAX_DN_NAME;
@@ -136,6 +121,7 @@
    *
    * @return  The OID for this attribute syntax.
    */
+  @Override
   public String getOID()
   {
     return SYNTAX_DN_OID;
@@ -148,6 +134,7 @@
    *
    * @return  A description for this attribute syntax.
    */
+  @Override
   public String getDescription()
   {
     return SYNTAX_DN_DESCRIPTION;
@@ -163,6 +150,7 @@
    *          attributes with this syntax, or <CODE>null</CODE> if equality
    *          matches will not be allowed for this type by default.
    */
+  @Override
   public EqualityMatchingRule getEqualityMatchingRule()
   {
     return defaultEqualityMatchingRule;
@@ -178,6 +166,7 @@
    *          attributes with this syntax, or <CODE>null</CODE> if ordering
    *          matches will not be allowed for this type by default.
    */
+  @Override
   public OrderingMatchingRule getOrderingMatchingRule()
   {
     // There is no ordering matching rule by default.
@@ -194,6 +183,7 @@
    *          attributes with this syntax, or <CODE>null</CODE> if substring
    *          matches will not be allowed for this type by default.
    */
+  @Override
   public SubstringMatchingRule getSubstringMatchingRule()
   {
     return defaultSubstringMatchingRule;
@@ -209,6 +199,7 @@
    *          attributes with this syntax, or <CODE>null</CODE> if approximate
    *          matches will not be allowed for this type by default.
    */
+  @Override
   public ApproximateMatchingRule getApproximateMatchingRule()
   {
     // There is no approximate matching rule by default.
@@ -229,6 +220,7 @@
    * @return  <CODE>true</CODE> if the provided value is acceptable for use with
    *          this syntax, or <CODE>false</CODE> if not.
    */
+  @Override
   public boolean valueIsAcceptable(ByteSequence value,
                                    LocalizableMessageBuilder invalidReason)
   {
@@ -259,6 +251,7 @@
   /**
    * {@inheritDoc}
    */
+  @Override
   public boolean isBEREncodingRequired()
   {
     return false;
@@ -269,6 +262,7 @@
   /**
    * {@inheritDoc}
    */
+  @Override
   public boolean isHumanReadable()
   {
     return true;
diff --git a/opendj3-server-dev/src/server/org/opends/server/schema/IntegerSyntax.java b/opendj3-server-dev/src/server/org/opends/server/schema/IntegerSyntax.java
index 594f0c8..14abf7b 100644
--- a/opendj3-server-dev/src/server/org/opends/server/schema/IntegerSyntax.java
+++ b/opendj3-server-dev/src/server/org/opends/server/schema/IntegerSyntax.java
@@ -26,24 +26,19 @@
  */
 package org.opends.server.schema;
 
+import org.forgerock.i18n.LocalizableMessageBuilder;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
-
+import org.forgerock.opendj.config.server.ConfigException;
+import org.forgerock.opendj.ldap.ByteSequence;
 import org.opends.server.admin.std.server.AttributeSyntaxCfg;
 import org.opends.server.api.ApproximateMatchingRule;
 import org.opends.server.api.AttributeSyntax;
-import org.opends.server.api.AttributeValueDecoder;
 import org.opends.server.api.EqualityMatchingRule;
 import org.opends.server.api.OrderingMatchingRule;
 import org.opends.server.api.SubstringMatchingRule;
-import org.forgerock.opendj.config.server.ConfigException;
 import org.opends.server.core.DirectoryServer;
 
-import org.opends.server.types.*;
-import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.ByteString;
-import org.forgerock.opendj.ldap.ByteSequence;
 import static org.opends.messages.SchemaMessages.*;
-import org.forgerock.i18n.LocalizableMessageBuilder;
 import static org.opends.server.schema.SchemaConstants.*;
 
 
@@ -68,34 +63,6 @@
   private SubstringMatchingRule defaultSubstringMatchingRule;
 
 
-
-  /**
-   * An {@link Integer} attribute value decoder for this syntax.
-   */
-  public static final AttributeValueDecoder<Integer> DECODER =
-    new AttributeValueDecoder<Integer>()
-  {
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public Integer decode(AttributeValue value) throws DirectoryException
-    {
-      ByteString nvalue = value.getNormalizedValue();
-      try
-      {
-        return Integer.valueOf(nvalue.toString());
-      }
-      catch (NumberFormatException e)
-      {
-        throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
-            WARN_ATTR_SYNTAX_ILLEGAL_INTEGER.get(nvalue));
-      }
-    }
-  };
-
-
-
   /**
    * Creates a new instance of this syntax. Note that the only thing
    * that should be done here is to invoke the default constructor for
@@ -396,6 +363,7 @@
   /**
    * {@inheritDoc}
    */
+  @Override
   public boolean isHumanReadable()
   {
     return true;
diff --git a/opendj3-server-dev/src/server/org/opends/server/schema/SubtreeSpecificationSyntax.java b/opendj3-server-dev/src/server/org/opends/server/schema/SubtreeSpecificationSyntax.java
index 9f5584f..208a032 100644
--- a/opendj3-server-dev/src/server/org/opends/server/schema/SubtreeSpecificationSyntax.java
+++ b/opendj3-server-dev/src/server/org/opends/server/schema/SubtreeSpecificationSyntax.java
@@ -26,22 +26,23 @@
  */
 package org.opends.server.schema;
 
-import org.forgerock.i18n.slf4j.LocalizedLogger;
 import static org.opends.messages.SchemaMessages.*;
-import org.forgerock.i18n.LocalizableMessageBuilder;
 import static org.opends.server.schema.SchemaConstants.*;
 
+import org.forgerock.i18n.LocalizableMessageBuilder;
+import org.forgerock.i18n.slf4j.LocalizedLogger;
+import org.forgerock.opendj.config.server.ConfigException;
+import org.forgerock.opendj.ldap.ByteSequence;
 import org.opends.server.admin.std.server.AttributeSyntaxCfg;
 import org.opends.server.api.ApproximateMatchingRule;
 import org.opends.server.api.AttributeSyntax;
-import org.opends.server.api.AttributeValueDecoder;
 import org.opends.server.api.EqualityMatchingRule;
 import org.opends.server.api.OrderingMatchingRule;
 import org.opends.server.api.SubstringMatchingRule;
-import org.forgerock.opendj.config.server.ConfigException;
 import org.opends.server.core.DirectoryServer;
-import org.opends.server.types.*;
-import org.forgerock.opendj.ldap.ByteSequence;
+import org.opends.server.types.DN;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.SubtreeSpecification;
 
 
 /**
@@ -63,48 +64,6 @@
   private SubstringMatchingRule defaultSubstringMatchingRule;
 
   /**
-   * Create a new attribute value decoder with the specified root DN.
-   *
-   * @param rootDN
-   *          The root DN for all decoded subtree specifications.
-   * @return The attribute value decoder.
-   */
-  public static AttributeValueDecoder<SubtreeSpecification>
-      createAttributeValueDecoder(DN rootDN) {
-    return new Decoder(rootDN);
-  }
-
-  /**
-   * Internal class implementing an attribute value decoder.
-   */
-  private static class Decoder implements
-      AttributeValueDecoder<SubtreeSpecification> {
-
-    // The root DN for all decoded relative subtree specifications.
-    private DN rootDN;
-
-    /**
-     * Create a new decoder with the specified root DN.
-     *
-     * @param rootDN
-     *          The root DN for all decoded relative subtree
-     *          specifications.
-     */
-    public Decoder(DN rootDN) {
-      this.rootDN = rootDN;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public SubtreeSpecification decode(AttributeValue value)
-        throws DirectoryException {
-      return SubtreeSpecification.valueOf(rootDN, value
-          .getValue().toString());
-    }
-  }
-
-  /**
    * Creates a new instance of this syntax. Note that the only thing
    * that should be done here is to invoke the default constructor for
    * the superclass. All initialization should be performed in the
@@ -117,6 +76,7 @@
   /**
    * {@inheritDoc}
    */
+  @Override
   public void initializeSyntax(AttributeSyntaxCfg configuration)
       throws ConfigException {
 
@@ -147,6 +107,7 @@
    *
    * @return The common name for this attribute syntax.
    */
+  @Override
   public String getName() {
 
     return SYNTAX_SUBTREE_SPECIFICATION_NAME;
@@ -157,6 +118,7 @@
    *
    * @return The OID for this attribute syntax.
    */
+  @Override
   public String getOID() {
 
     return SYNTAX_SUBTREE_SPECIFICATION_OID;
@@ -167,6 +129,7 @@
    *
    * @return A description for this attribute syntax.
    */
+  @Override
   public String getDescription() {
 
     return SYNTAX_SUBTREE_SPECIFICATION_DESCRIPTION;
@@ -181,6 +144,7 @@
    *         equality matches will not be allowed for this type by
    *         default.
    */
+  @Override
   public EqualityMatchingRule getEqualityMatchingRule() {
 
     return defaultEqualityMatchingRule;
@@ -195,6 +159,7 @@
    *         ordering matches will not be allowed for this type by
    *         default.
    */
+  @Override
   public OrderingMatchingRule getOrderingMatchingRule() {
 
     return defaultOrderingMatchingRule;
@@ -209,6 +174,7 @@
    *         substring matches will not be allowed for this type by
    *         default.
    */
+  @Override
   public SubstringMatchingRule getSubstringMatchingRule() {
 
     return defaultSubstringMatchingRule;
@@ -223,6 +189,7 @@
    *         approximate matches will not be allowed for this type by
    *         default.
    */
+  @Override
   public ApproximateMatchingRule getApproximateMatchingRule() {
 
     // There is no approximate matching rule by default.
@@ -241,6 +208,7 @@
    * @return <CODE>true</CODE> if the provided value is acceptable for
    *         use with this syntax, or <CODE>false</CODE> if not.
    */
+  @Override
   public boolean valueIsAcceptable(ByteSequence value,
                                    LocalizableMessageBuilder invalidReason) {
 
@@ -260,6 +228,7 @@
  /**
    * {@inheritDoc}
    */
+  @Override
   public boolean isBEREncodingRequired()
   {
     return false;
@@ -270,6 +239,7 @@
   /**
    * {@inheritDoc}
    */
+  @Override
   public boolean isHumanReadable()
   {
     return true;
diff --git a/opendj3-server-dev/src/server/org/opends/server/types/AttributeParser.java b/opendj3-server-dev/src/server/org/opends/server/types/AttributeParser.java
new file mode 100644
index 0000000..7d4ef75
--- /dev/null
+++ b/opendj3-server-dev/src/server/org/opends/server/types/AttributeParser.java
@@ -0,0 +1,494 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2012-2014 ForgeRock AS.
+ */
+package org.opends.server.types;
+
+import java.util.*;
+
+import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.Function;
+import org.forgerock.opendj.ldap.Functions;
+import org.forgerock.opendj.ldap.GeneralizedTime;
+import org.forgerock.opendj.ldap.schema.Schema;
+
+/**
+ * A fluent API for parsing attributes as different types of object. An
+ * attribute parser is obtained from an entry using the method
+ * {@link Entry#parseAttribute} or from an attribute using
+ * {@link Attribute#parse}.
+ * <p>
+ * Methods throw an {@code IllegalArgumentException} when a value cannot be
+ * parsed (e.g. because its syntax is invalid). Methods which return a
+ * {@code Set} always return a modifiable non-{@code null} result, even if the
+ * attribute is {@code null} or empty.
+ * <p>
+ * Examples:
+ *
+ * <pre>
+ * Entry entry = ...;
+ *
+ * Calendar timestamp = entry.parseAttribute("createTimestamp").asCalendar();
+ * boolean isEnabled = entry.parseAttribute("enabled").asBoolean(false);
+ *
+ * Entry group = ...;
+ * Schema schema = ...;
+ *
+ * Set&lt;DN&gt; members = group.parseAttribute("member").usingSchema(schema).asSetOfDN();
+ * </pre>
+ *
+ * @see Entry#parseAttribute
+ * @see Attribute#parse
+ */
+public final class AttributeParser {
+    // TODO: enums, filters, rdns?
+
+    private static final AttributeParser NULL_INSTANCE = new AttributeParser(null);
+
+    /**
+     * Returns an attribute parser for the provided attribute. {@code null}
+     * attributes are permitted and will be treated as if an empty attribute was
+     * provided.
+     *
+     * @param attribute
+     *            The attribute to be parsed, which may be {@code null}.
+     * @return The attribute parser.
+     */
+    public static AttributeParser parseAttribute(final Attribute attribute) {
+        return isEmpty(attribute) ? NULL_INSTANCE : new AttributeParser(attribute);
+    }
+
+    private static boolean isEmpty(final Attribute attribute) {
+        return (attribute == null) || attribute.isEmpty();
+    }
+
+    private final Attribute attribute;
+    private Schema schema;
+
+    private AttributeParser(final Attribute attribute) {
+        this.attribute = attribute;
+    }
+
+    /**
+     * Returns the first value decoded as a {@code T} using the provided
+     * {@link Function}, or {@code null} if the attribute does not contain any
+     * values.
+     *
+     * @param <T>
+     *            The type of the value to be decoded.
+     * @param f
+     *            The function which should be used to decode the value.
+     * @return The first value decoded as a {@code T}.
+     */
+    public <T> T as(final Function<ByteString, ? extends T, Void> f) {
+        return as(f, null);
+    }
+
+    /**
+     * Returns the first value decoded as a {@code T} using the provided
+     * {@link Function}, or {@code defaultValue} if the attribute does not
+     * contain any values.
+     *
+     * @param <T>
+     *            The type of the value to be decoded.
+     * @param f
+     *            The function which should be used to decode the value.
+     * @param defaultValue
+     *            The default value to return if the attribute is empty.
+     * @return The first value decoded as a {@code T}.
+     */
+    public <T> T as(final Function<ByteString, ? extends T, Void> f, final T defaultValue) {
+        if (!isEmpty(attribute)) {
+            return f.apply(attribute.iterator().next().getValue(), null);
+        } else {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the first value decoded as a boolean, or {@code null} if the
+     * attribute does not contain any values.
+     *
+     * @return The first value decoded as a boolean.
+     */
+    public Boolean asBoolean() {
+        return isEmpty(attribute) ? null : asBoolean(false /* ignored */);
+    }
+
+    /**
+     * Returns the first value decoded as an {@code Boolean}, or
+     * {@code defaultValue} if the attribute does not contain any values.
+     *
+     * @param defaultValue
+     *            The default value to return if the attribute is empty.
+     * @return The first value decoded as an {@code Boolean}.
+     */
+    public boolean asBoolean(final boolean defaultValue) {
+        return as(Functions.byteStringToBoolean(), defaultValue);
+    }
+
+    /**
+     * Returns the first value, or {@code null} if the attribute does not
+     * contain any values.
+     *
+     * @return The first value.
+     */
+    public ByteString asByteString() {
+        return asByteString(null);
+    }
+
+    /**
+     * Returns the first value, or {@code defaultValue} if the attribute does
+     * not contain any values.
+     *
+     * @param defaultValue
+     *            The default value to return if the attribute is empty.
+     * @return The first value.
+     */
+    public ByteString asByteString(final ByteString defaultValue) {
+        return as(Functions.<ByteString> identityFunction(), defaultValue);
+    }
+
+    /**
+     * Returns the first value decoded as a {@code GeneralizedTime} using the
+     * generalized time syntax, or {@code null} if the attribute does not
+     * contain any values.
+     *
+     * @return The first value decoded as a {@code GeneralizedTime}.
+     */
+    public GeneralizedTime asGeneralizedTime() {
+        return asGeneralizedTime(null);
+    }
+
+    /**
+     * Returns the first value decoded as an {@code GeneralizedTime} using the
+     * generalized time syntax, or {@code defaultValue} if the attribute does
+     * not contain any values.
+     *
+     * @param defaultValue
+     *            The default value to return if the attribute is empty.
+     * @return The first value decoded as an {@code GeneralizedTime}.
+     */
+    public GeneralizedTime asGeneralizedTime(final GeneralizedTime defaultValue) {
+        return as(Functions.byteStringToGeneralizedTime(), defaultValue);
+    }
+
+    /**
+     * Returns the first value decoded as an {@code Integer}, or {@code null} if
+     * the attribute does not contain any values.
+     *
+     * @return The first value decoded as an {@code Integer}.
+     */
+    public Integer asInteger() {
+        return isEmpty(attribute) ? null : asInteger(0 /* ignored */);
+    }
+
+    /**
+     * Returns the first value decoded as an {@code Integer}, or
+     * {@code defaultValue} if the attribute does not contain any values.
+     *
+     * @param defaultValue
+     *            The default value to return if the attribute is empty.
+     * @return The first value decoded as an {@code Integer}.
+     */
+    public int asInteger(final int defaultValue) {
+        return as(Functions.byteStringToInteger(), defaultValue);
+    }
+
+    /**
+     * Returns the first value decoded as a {@code Long}, or {@code null} if the
+     * attribute does not contain any values.
+     *
+     * @return The first value decoded as a {@code Long}.
+     */
+    public Long asLong() {
+        return isEmpty(attribute) ? null : asLong(0L /* ignored */);
+    }
+
+    /**
+     * Returns the first value decoded as a {@code Long}, or
+     * {@code defaultValue} if the attribute does not contain any values.
+     *
+     * @param defaultValue
+     *            The default value to return if the attribute is empty.
+     * @return The first value decoded as a {@code Long}.
+     */
+    public long asLong(final long defaultValue) {
+        return as(Functions.byteStringToLong(), defaultValue);
+    }
+
+    /**
+     * Returns the values decoded as a set of {@code T}s using the provided
+     * {@link Function}, or {@code defaultValues} if the attribute does not
+     * contain any values.
+     *
+     * @param <T>
+     *            The type of the values to be decoded.
+     * @param f
+     *            The function which should be used to decode values.
+     * @param defaultValues
+     *            The default values to return if the attribute is empty.
+     * @return The values decoded as a set of {@code T}s.
+     */
+    public <T> Set<T> asSetOf(final Function<ByteString, ? extends T, Void> f,
+            final Collection<? extends T> defaultValues) {
+        if (!isEmpty(attribute)) {
+            final LinkedHashSet<T> result = new LinkedHashSet<T>(attribute.size());
+            for (final AttributeValue v : attribute) {
+                result.add(f.apply(v.getValue(), null));
+            }
+            return result;
+        } else if (defaultValues != null) {
+            return new LinkedHashSet<T>(defaultValues);
+        } else {
+            return new LinkedHashSet<T>(0);
+        }
+    }
+
+    /**
+     * Returns the values decoded as a set of {@code T}s using the provided
+     * {@link Function}, or {@code defaultValues} if the attribute does not
+     * contain any values.
+     *
+     * @param <T>
+     *            The type of the values to be decoded.
+     * @param f
+     *            The function which should be used to decode values.
+     * @param defaultValues
+     *            The default values to return if the attribute is empty.
+     * @return The values decoded as a set of {@code T}s.
+     */
+    public <T> Set<T> asSetOf(final Function<ByteString, ? extends T, Void> f,
+            final T... defaultValues) {
+        return asSetOf(f, Arrays.asList(defaultValues));
+    }
+
+    /**
+     * Returns the values decoded as a set of {@code Boolean}s, or
+     * {@code defaultValues} if the attribute does not contain any values.
+     *
+     * @param defaultValues
+     *            The default values to return if the attribute is empty.
+     * @return The values decoded as a set of {@code Boolean}s.
+     */
+    public Set<Boolean> asSetOfBoolean(final Boolean... defaultValues) {
+        return asSetOfBoolean(Arrays.asList(defaultValues));
+    }
+
+    /**
+     * Returns the values decoded as a set of {@code Boolean}s, or
+     * {@code defaultValues} if the attribute does not contain any values.
+     *
+     * @param defaultValues
+     *            The default values to return if the attribute is empty.
+     * @return The values decoded as a set of {@code Boolean}s.
+     */
+    public Set<Boolean> asSetOfBoolean(final Collection<Boolean> defaultValues) {
+        return asSetOf(Functions.byteStringToBoolean(), defaultValues);
+    }
+
+    /**
+     * Returns the values contained in the attribute, or {@code defaultValues}
+     * if the attribute does not contain any values.
+     *
+     * @param defaultValues
+     *            The default values to return if the attribute is empty.
+     * @return The values contained in the attribute.
+     */
+    public Set<ByteString> asSetOfByteString(final ByteString... defaultValues) {
+        return asSetOfByteString(Arrays.asList(defaultValues));
+    }
+
+    /**
+     * Returns the values contained in the attribute, or {@code defaultValues}
+     * if the attribute does not contain any values.
+     *
+     * @param defaultValues
+     *            The default values to return if the attribute is empty.
+     * @return The values contained in the attribute.
+     */
+    public Set<ByteString> asSetOfByteString(final Collection<ByteString> defaultValues) {
+        return asSetOf(Functions.<ByteString> identityFunction(), defaultValues);
+    }
+
+    /**
+     * Returns the values decoded as a set of {@code GeneralizedTime}s using the
+     * generalized time syntax, or {@code defaultValues} if the attribute does
+     * not contain any values.
+     *
+     * @param defaultValues
+     *            The default values to return if the attribute is empty.
+     * @return The values decoded as a set of {@code GeneralizedTime}s.
+     */
+    public Set<GeneralizedTime> asSetOfGeneralizedTime(
+            final Collection<GeneralizedTime> defaultValues) {
+        return asSetOf(Functions.byteStringToGeneralizedTime(), defaultValues);
+    }
+
+    /**
+     * Returns the values decoded as a set of {@code GeneralizedTime}s using the
+     * generalized time syntax, or {@code defaultValues} if the attribute does
+     * not contain any values.
+     *
+     * @param defaultValues
+     *            The default values to return if the attribute is empty.
+     * @return The values decoded as a set of {@code GeneralizedTime}s.
+     */
+    public Set<GeneralizedTime> asSetOfGeneralizedTime(final GeneralizedTime... defaultValues) {
+        return asSetOfGeneralizedTime(Arrays.asList(defaultValues));
+    }
+
+    /**
+     * Returns the values decoded as a set of {@code Integer}s, or
+     * {@code defaultValues} if the attribute does not contain any values.
+     *
+     * @param defaultValues
+     *            The default values to return if the attribute is empty.
+     * @return The values decoded as a set of {@code Integer}s.
+     */
+    public Set<Integer> asSetOfInteger(final Collection<Integer> defaultValues) {
+        return asSetOf(Functions.byteStringToInteger(), defaultValues);
+    }
+
+    /**
+     * Returns the values decoded as a set of {@code Integer}s, or
+     * {@code defaultValues} if the attribute does not contain any values.
+     *
+     * @param defaultValues
+     *            The default values to return if the attribute is empty.
+     * @return The values decoded as a set of {@code Integer}s.
+     */
+    public Set<Integer> asSetOfInteger(final Integer... defaultValues) {
+        return asSetOfInteger(Arrays.asList(defaultValues));
+    }
+
+    /**
+     * Returns the values decoded as a set of {@code Long}s, or
+     * {@code defaultValues} if the attribute does not contain any values.
+     *
+     * @param defaultValues
+     *            The default values to return if the attribute is empty.
+     * @return The values decoded as a set of {@code Long}s.
+     */
+    public Set<Long> asSetOfLong(final Collection<Long> defaultValues) {
+        return asSetOf(Functions.byteStringToLong(), defaultValues);
+    }
+
+    /**
+     * Returns the values decoded as a set of {@code Long}s, or
+     * {@code defaultValues} if the attribute does not contain any values.
+     *
+     * @param defaultValues
+     *            The default values to return if the attribute is empty.
+     * @return The values decoded as a set of {@code Long}s.
+     */
+    public Set<Long> asSetOfLong(final Long... defaultValues) {
+        return asSetOfLong(Arrays.asList(defaultValues));
+    }
+
+    /**
+     * Returns the values decoded as a set of {@code String}s, or
+     * {@code defaultValues} if the attribute does not contain any values.
+     *
+     * @param defaultValues
+     *            The default values to return if the attribute is empty.
+     * @return The values decoded as a set of {@code String}s.
+     */
+    public Set<String> asSetOfString(final Collection<String> defaultValues) {
+        return asSetOf(Functions.byteStringToString(), defaultValues);
+    }
+
+    /**
+     * Returns the values decoded as a set of {@code String}s, or
+     * {@code defaultValues} if the attribute does not contain any values.
+     *
+     * @param defaultValues
+     *            The default values to return if the attribute is empty.
+     * @return The values decoded as a set of {@code String}s.
+     */
+    public Set<String> asSetOfString(final String... defaultValues) {
+        return asSetOfString(Arrays.asList(defaultValues));
+    }
+
+    /**
+     * Returns the first value decoded as a {@code String}, or {@code null} if
+     * the attribute does not contain any values.
+     *
+     * @return The first value decoded as a {@code String}.
+     */
+    public String asString() {
+        return asString(null);
+    }
+
+    /**
+     * Returns the first value decoded as a {@code String}, or
+     * {@code defaultValue} if the attribute does not contain any values.
+     *
+     * @param defaultValue
+     *            The default value to return if the attribute is empty.
+     * @return The first value decoded as a {@code String}.
+     */
+    public String asString(final String defaultValue) {
+        return as(Functions.byteStringToString(), defaultValue);
+    }
+
+    /**
+     * Throws a {@code NoSuchElementException} if the attribute referenced by
+     * this parser is {@code null} or empty.
+     *
+     * @return A reference to this attribute parser.
+     * @throws NoSuchElementException
+     *             If the attribute referenced by this parser is {@code null} or
+     *             empty.
+     */
+    public AttributeParser requireValue() throws NoSuchElementException {
+        if (isEmpty(attribute)) {
+            throw new NoSuchElementException();
+        } else {
+            return this;
+        }
+    }
+
+    /**
+     * Sets the {@code Schema} which will be used when parsing schema sensitive
+     * values such as DNs and attribute descriptions.
+     *
+     * @param schema
+     *            The {@code Schema} which will be used when parsing schema
+     *            sensitive values.
+     * @return This attribute parser.
+     */
+    public AttributeParser usingSchema(final Schema schema) {
+        // Avoid modifying the null instance: a schema will not be needed
+        // anyway.
+        if (this != NULL_INSTANCE) {
+            this.schema = schema;
+        }
+        return this;
+    }
+
+    private Schema getSchema() {
+        return schema == null ? Schema.getDefaultSchema() : schema;
+    }
+}
diff --git a/opendj3-server-dev/src/server/org/opends/server/types/Entry.java b/opendj3-server-dev/src/server/org/opends/server/types/Entry.java
index be133e3..9ce5042 100644
--- a/opendj3-server-dev/src/server/org/opends/server/types/Entry.java
+++ b/opendj3-server-dev/src/server/org/opends/server/types/Entry.java
@@ -33,6 +33,7 @@
 
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.LocalizableMessageBuilder;
+import org.forgerock.i18n.LocalizedIllegalArgumentException;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.opendj.ldap.ByteSequence;
 import org.forgerock.opendj.ldap.ByteSequenceReader;
@@ -42,7 +43,6 @@
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.SearchScope;
 import org.forgerock.opendj.ldap.schema.ObjectClassType;
-import org.opends.server.api.AttributeValueDecoder;
 import org.opends.server.api.CompressedSchema;
 import org.opends.server.api.MatchingRule;
 import org.opends.server.api.ProtocolElement;
@@ -861,87 +861,27 @@
   }
 
 
-
   /**
-   * Retrieves the requested attribute type from the entry and decodes
-   * a single value as an object of type T.
+   * Returns a parser for the named attribute contained in this entry.
    * <p>
-   * If the requested attribute type is not present then
-   * <code>null</code> is returned. If more than one attribute value
-   * is present, then the first value found will be decoded and
-   * returned.
-   * <p>
-   * The attribute value is decoded using the specified
-   * {@link org.opends.server.api.AttributeValueDecoder}.
+   * The attribute description will be decoded using the schema associated
+   * with this entry (usually the default schema).
    *
-   * @param <T>
-   *          Decode the attribute value to an object of this type.
-   * @param attributeType
-   *          The attribute type to retrieve.
-   * @param decoder
-   *          The attribute value decoder.
-   * @return The decoded attribute value or <code>null</code> if no
-   *         attribute value having the specified attribute type was
-   *         found.
-   * @throws DirectoryException
-   *           If the requested attribute value could not be decoded
-   *           successfully.
+   * @param attributeDescription
+   *            The name of the attribute to be parsed.
+   * @return A parser for the named attribute.
+   * @throws LocalizedIllegalArgumentException
+   *             If {@code attributeDescription} could not be decoded using
+   *             the schema associated with this entry.
+   * @throws NullPointerException
+   *             If {@code attributeDescription} was {@code null}.
    */
-  public final <T> T getAttributeValue(AttributeType attributeType,
-      AttributeValueDecoder<T> decoder) throws DirectoryException
+  public AttributeParser parseAttribute(String attributeDescription)
+      throws LocalizedIllegalArgumentException, NullPointerException
   {
-    List<Attribute> attributes = getAttribute(attributeType, true);
-    AttributeValueIterable values = new AttributeValueIterable(attributes);
-    Iterator<AttributeValue> iterator = values.iterator();
-    if (iterator.hasNext())
-    {
-      return decoder.decode(iterator.next());
-    }
-    return null;
-  }
-
-
-
-  /**
-   * Retrieves the requested attribute type from the entry and decodes
-   * any values as objects of type T and then places them in the
-   * specified collection.
-   * <p>
-   * If the requested attribute type is not present then no decoded
-   * values will be added to the container.
-   * <p>
-   * The attribute value is decoded using the specified
-   * {@link org.opends.server.api.AttributeValueDecoder}.
-   *
-   * @param <T>
-   *          Decode the attribute values to objects of this type.
-   * @param attributeType
-   *          The attribute type to retrieve.
-   * @param decoder
-   *          The attribute value decoder.
-   * @param collection
-   *          The collection to which decoded values should be added.
-   * @return The collection containing the decoded attribute value.
-   * @throws DirectoryException
-   *           If one or more of the requested attribute values could
-   *           not be decoded successfully.
-   */
-  public final <T> Collection<T> getAttributeValues(
-      AttributeType attributeType,
-      AttributeValueDecoder<? extends T> decoder,
-      Collection<T> collection)
-      throws DirectoryException
-  {
-    List<Attribute> attributes = getAttribute(attributeType, true);
-    AttributeValueIterable values =
-         new AttributeValueIterable(attributes);
-
-    for (AttributeValue value : values)
-    {
-      collection.add(decoder.decode(value));
-    }
-
-    return collection;
+    final List<Attribute> attribute = getAttribute(attributeDescription);
+    boolean notEmpty = attribute != null && !attribute.isEmpty();
+    return AttributeParser.parseAttribute(notEmpty ? attribute.get(0) : null);
   }
 
 
diff --git a/opendj3-server-dev/src/server/org/opends/server/types/SubtreeSpecificationSet.java b/opendj3-server-dev/src/server/org/opends/server/types/SubtreeSpecificationSet.java
deleted file mode 100644
index a223fbf..0000000
--- a/opendj3-server-dev/src/server/org/opends/server/types/SubtreeSpecificationSet.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
- * or http://forgerock.org/license/CDDLv1.0.html.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at legal-notices/CDDLv1_0.txt.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
- */
-package org.opends.server.types;
-
-import java.util.AbstractSet;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
-
-
-
-
-/**
- * This class implements the {@code Set} interface for
- * {@link org.opends.server.types.SubtreeSpecification}s.
- * <p>
- * It is backed by a {@code HashSet} but provides additional
- * functionality, {@link #isWithinScope(Entry)}, for determining
- * whether or not an entry is within the scope of one or more
- * contained {@code SubtreeSpecification}s.
- */
-@org.opends.server.types.PublicAPI(
-     stability=org.opends.server.types.StabilityLevel.VOLATILE,
-     mayInstantiate=true,
-     mayExtend=false,
-     mayInvoke=true)
-public final class SubtreeSpecificationSet
-       extends AbstractSet<SubtreeSpecification>
-{
-  // Underlying implementation is simply a set.
-  private HashSet<SubtreeSpecification> pimpl;
-
-
-
-  /**
-   * Constructs a new empty subtree specification set.
-   */
-  public SubtreeSpecificationSet()
-  {
-    this.pimpl = new HashSet<SubtreeSpecification>();
-  }
-
-
-
-  /**
-   * Constructs a new subtree specification set containing the
-   * elements in the specified collection.
-   *
-   * @param  c  The subtree specification collection whose elements
-   *            are to be placed into this set.
-   */
-  public SubtreeSpecificationSet(
-              Collection<? extends SubtreeSpecification> c)
-  {
-    this.pimpl = new HashSet<SubtreeSpecification>(c);
-  }
-
-
-
-  /**
-   * Returns {@code true} if the specified entry is within the scope
-   * of a subtree specifications contained in the set.
-   *
-   * @param  entry  The entry to be checked for containment.
-   *
-   * @return  Returns {@code true} if the set contains the specified
-   *          entry.
-   */
-  public boolean isWithinScope(Entry entry)
-  {
-    for (SubtreeSpecification subtreeSpecification : pimpl)
-    {
-      if (subtreeSpecification.isWithinScope(entry))
-      {
-        return true;
-      }
-    }
-
-    return false;
-  }
-
-
-
-  /**
-   * Adds the provided subtree specification object to this set.
-   *
-   * @param  e  The subtree specification object to be added.
-   *
-   * @return  {@code true} if the element was added to the set, or
-   *          {@code false} if the element was already contained in
-   *          the set.
-   */
-  @Override
-  public boolean add(SubtreeSpecification e)
-  {
-    return pimpl.add(e);
-  }
-
-
-
-  /**
-   * Retrieves an iterator that may be used to step through the values
-   * in this set.
-   *
-   * @return  An iterator that may be used to step through the values
-   *          in this set.
-   */
-  @Override
-  public Iterator<SubtreeSpecification> iterator()
-  {
-    return pimpl.iterator();
-  }
-
-
-
-  /**
-   * Indicates whether this set contains the provided object.
-   *
-   * @param  o  The object for which to make the determination.
-   *
-   * @return  {@code true} if this set contains the provided object,
-   *          or {@code false} if not.
-   */
-  @Override
-  public boolean contains(Object o)
-  {
-    return pimpl.contains(o);
-  }
-
-
-
-  /**
-   * Retrieves the number of elements contained in this set.
-   *
-   * @return  The number of elements contained in this set.
-   */
-  @Override
-  public int size()
-  {
-    return pimpl.size();
-  }
-}
-
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/crypto/GetSymmetricKeyExtendedOperationTestCase.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/crypto/GetSymmetricKeyExtendedOperationTestCase.java
index 1993170..4afef34 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/crypto/GetSymmetricKeyExtendedOperationTestCase.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/crypto/GetSymmetricKeyExtendedOperationTestCase.java
@@ -30,6 +30,7 @@
 
 import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.opendj.ldap.DereferenceAliasesPolicy;
+import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.SearchScope;
 import org.opends.admin.ads.ADSContext;
 import org.opends.server.TestCaseUtils;
@@ -38,13 +39,14 @@
 import org.opends.server.core.ExtendedOperation;
 import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.protocols.internal.InternalSearchOperation;
-import org.opends.server.schema.DirectoryStringSyntax;
-import org.opends.server.types.*;
-import org.forgerock.opendj.ldap.ResultCode;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.SearchFilter;
 import org.opends.server.util.ServerConstants;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+import static org.opends.server.config.ConfigConstants.*;
 import static org.testng.Assert.*;
 
 /**
@@ -124,11 +126,9 @@
     final InternalClientConnection internalConnection =
          InternalClientConnection.getRootConnection();
     final String instanceKeyID = cm.getInstanceKeyID();
-    final AttributeType attrSymmetricKey = DirectoryServer.getAttributeType(
-         ConfigConstants.ATTR_CRYPTO_SYMMETRIC_KEY);
     for (Entry e : searchOp.getSearchEntries()) {
-      final String symmetricKeyAttributeValue
-              = e.getAttributeValue(attrSymmetricKey, DirectoryStringSyntax.DECODER);
+      final String symmetricKeyAttributeValue =
+          e.parseAttribute(ATTR_CRYPTO_SYMMETRIC_KEY).asString();
       final ByteString requestValue =
            GetSymmetricKeyExtendedOperation.encodeRequestValue(
                 symmetricKeyAttributeValue, instanceKeyID);
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntityTagVirtualAttributeProviderTestCase.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntityTagVirtualAttributeProviderTestCase.java
index fc2fbff..6693b4e 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntityTagVirtualAttributeProviderTestCase.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/EntityTagVirtualAttributeProviderTestCase.java
@@ -37,6 +37,10 @@
 import org.forgerock.i18n.LocalizableMessageBuilder;
 import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.opendj.ldap.ConditionResult;
+import org.forgerock.opendj.ldap.DereferenceAliasesPolicy;
+import org.forgerock.opendj.ldap.ModificationType;
+import org.forgerock.opendj.ldap.ResultCode;
+import org.forgerock.opendj.ldap.SearchScope;
 import org.opends.server.TestCaseUtils;
 import org.opends.server.admin.server.ConfigurationChangeListener;
 import org.opends.server.admin.std.meta.EntityTagVirtualAttributeCfgDefn.ChecksumAlgorithm;
@@ -58,21 +62,16 @@
 import org.opends.server.protocols.internal.InternalSearchOperation;
 import org.opends.server.protocols.ldap.LDAPFilter;
 import org.opends.server.protocols.ldap.LDAPModification;
-import org.opends.server.schema.DirectoryStringSyntax;
 import org.opends.server.types.AttributeType;
 import org.opends.server.types.AttributeValue;
 import org.opends.server.types.AttributeValues;
 import org.opends.server.types.Control;
 import org.opends.server.types.DN;
-import org.forgerock.opendj.ldap.DereferenceAliasesPolicy;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.Entry;
-import org.forgerock.opendj.ldap.ModificationType;
 import org.opends.server.types.RawAttribute;
 import org.opends.server.types.RawModification;
-import org.forgerock.opendj.ldap.ResultCode;
 import org.opends.server.types.SearchFilter;
-import org.forgerock.opendj.ldap.SearchScope;
 import org.opends.server.types.VirtualAttributeRule;
 import org.opends.server.util.StaticUtils;
 import org.testng.annotations.BeforeClass;
@@ -623,8 +622,6 @@
   public void testOptimisticConcurrency() throws Exception
   {
     // Use an internal connection.
-    AttributeType etagType = DirectoryServer.getAttributeType(ETAG);
-    AttributeType descrType = DirectoryServer.getAttributeType(DESCRIPTION);
     String userDN = "uid=test.user,ou=People,o=test";
     InternalClientConnection conn = InternalClientConnection
         .getRootConnection();
@@ -653,8 +650,7 @@
 
     // Read the user entry and get the etag.
     Entry e1 = readEntry(conn, userDN);
-    String etag1 = e1
-        .getAttributeValue(etagType, DirectoryStringSyntax.DECODER);
+    String etag1 = e1.parseAttribute(ETAG).asString();
     assertNotNull(etag1);
 
     // Apply a change using the assertion control for optimistic concurrency.
@@ -672,13 +668,11 @@
     // the etag has changed.
     Entry e2 = readEntry(conn, userDN);
 
-    String etag2 = e2
-        .getAttributeValue(etagType, DirectoryStringSyntax.DECODER);
+    String etag2 = e2.parseAttribute(ETAG).asString();
     assertNotNull(etag2);
     assertFalse(etag1.equals(etag2));
 
-    String description2 = e2.getAttributeValue(descrType,
-        DirectoryStringSyntax.DECODER);
+    String description2 = e2.parseAttribute(DESCRIPTION).asString();
     assertNotNull(description2);
     assertEquals(description2, "first modify");
 
@@ -693,13 +687,11 @@
     // changed.
     Entry e3 = readEntry(conn, userDN);
 
-    String etag3 = e3
-        .getAttributeValue(etagType, DirectoryStringSyntax.DECODER);
+    String etag3 = e3.parseAttribute(ETAG).asString();
     assertNotNull(etag3);
     assertEquals(etag2, etag3);
 
-    String description3 = e3.getAttributeValue(descrType,
-        DirectoryStringSyntax.DECODER);
+    String description3 = e3.parseAttribute(DESCRIPTION).asString();
     assertNotNull(description3);
     assertEquals(description3, description2);
   }
@@ -716,8 +708,6 @@
   @Test
   public void testPreReadControl() throws Exception
   {
-    AttributeType etagType = DirectoryServer.getAttributeType(ETAG);
-    AttributeType descrType = DirectoryServer.getAttributeType(DESCRIPTION);
     String userDN = "uid=test.user,ou=People,o=test";
 
     // Use an internal connection.
@@ -749,8 +739,7 @@
 
     // Read the user entry and get the etag.
     Entry e1 = readEntry(conn, userDN);
-    String etag1 = e1
-        .getAttributeValue(etagType, DirectoryStringSyntax.DECODER);
+    String etag1 = e1.parseAttribute(ETAG).asString();
     assertNotNull(etag1);
 
     // Apply a change using the pre and post read controls.
@@ -767,13 +756,11 @@
     // the etag has changed.
     Entry e2 = readEntry(conn, userDN);
 
-    String etag2 = e2
-        .getAttributeValue(etagType, DirectoryStringSyntax.DECODER);
+    String etag2 = e2.parseAttribute(ETAG).asString();
     assertNotNull(etag2);
     assertFalse(etag1.equals(etag2));
 
-    String description2 = e2.getAttributeValue(descrType,
-        DirectoryStringSyntax.DECODER);
+    String description2 = e2.parseAttribute(DESCRIPTION).asString();
     assertNotNull(description2);
     assertEquals(description2, "modified value");
 
@@ -788,8 +775,8 @@
       }
     }
     assertNotNull(preReadControl);
-    String etagPreRead = preReadControl.getSearchEntry().getAttributeValue(
-        etagType, DirectoryStringSyntax.DECODER);
+    String etagPreRead =
+        preReadControl.getSearchEntry().parseAttribute(ETAG).asString();
     assertEquals(etagPreRead, etag1);
   }
 
@@ -805,8 +792,6 @@
   @Test
   public void testPostReadControl() throws Exception
   {
-    AttributeType etagType = DirectoryServer.getAttributeType(ETAG);
-    AttributeType descrType = DirectoryServer.getAttributeType(DESCRIPTION);
     String userDN = "uid=test.user,ou=People,o=test";
 
     // Use an internal connection.
@@ -838,8 +823,7 @@
 
     // Read the user entry and get the etag.
     Entry e1 = readEntry(conn, userDN);
-    String etag1 = e1
-        .getAttributeValue(etagType, DirectoryStringSyntax.DECODER);
+    String etag1 = e1.parseAttribute(ETAG).asString();
     assertNotNull(etag1);
 
     // Apply a change using the pre and post read controls.
@@ -856,13 +840,11 @@
     // the etag has changed.
     Entry e2 = readEntry(conn, userDN);
 
-    String etag2 = e2
-        .getAttributeValue(etagType, DirectoryStringSyntax.DECODER);
+    String etag2 = e2.parseAttribute(ETAG).asString();
     assertNotNull(etag2);
     assertFalse(etag1.equals(etag2));
 
-    String description2 = e2.getAttributeValue(descrType,
-        DirectoryStringSyntax.DECODER);
+    String description2 = e2.parseAttribute(DESCRIPTION).asString();
     assertNotNull(description2);
     assertEquals(description2, "modified value");
 
@@ -877,8 +859,8 @@
       }
     }
     assertNotNull(postReadControl);
-    String etagPostRead = postReadControl.getSearchEntry().getAttributeValue(
-        etagType, DirectoryStringSyntax.DECODER);
+    String etagPostRead =
+        postReadControl.getSearchEntry().parseAttribute(ETAG).asString();
     assertEquals(etagPostRead, etag2);
   }
 
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java
index 88d80d6..bbb73fc 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/LDAPPassThroughAuthenticationPolicyTestCase.java
@@ -4334,13 +4334,10 @@
     Entry updatedTestUser = DirectoryServer.getEntry(DN
         .valueOf("cn=test user,o=test"));
 
-    String newCachedPassword = updatedTestUser.getAttributeValue(
-        DirectoryServer.getAttributeType("ds-pta-cached-password"),
-        DirectoryStringSyntax.DECODER);
-
-    String newCachedPasswordTime = updatedTestUser.getAttributeValue(
-        DirectoryServer.getAttributeType("ds-pta-cached-password-time"),
-        DirectoryStringSyntax.DECODER);
+    String newCachedPassword =
+        updatedTestUser.parseAttribute("ds-pta-cached-password").asString();
+    String newCachedPasswordTime =
+        updatedTestUser.parseAttribute("ds-pta-cached-password-time").asString();
 
     if (expectCacheInfo)
     {
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/InitOnLineTest.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/InitOnLineTest.java
index 988cdde..6979a66 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/InitOnLineTest.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/InitOnLineTest.java
@@ -31,11 +31,11 @@
 import org.assertj.core.api.Assertions;
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
+import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.SearchScope;
 import org.opends.server.TestCaseUtils;
 import org.opends.server.backends.task.TaskState;
 import org.opends.server.core.AddOperation;
-import org.opends.server.core.DirectoryServer;
 import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.protocols.internal.InternalSearchOperation;
 import org.opends.server.replication.common.ServerStatus;
@@ -45,9 +45,10 @@
 import org.opends.server.replication.server.ReplicationServer;
 import org.opends.server.replication.server.ReplicationServerDomain;
 import org.opends.server.replication.service.ReplicationBroker;
-import org.opends.server.schema.DirectoryStringSyntax;
-import org.opends.server.types.*;
-import org.forgerock.opendj.ldap.ResultCode;
+import org.opends.server.types.DN;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.Entry;
+import org.opends.server.types.SearchFilter;
 import org.opends.server.util.Base64;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
@@ -230,22 +231,15 @@
       Entry resultEntry = getCompletionTime(taskEntry);
 
       // Check that the task state is as expected.
-      AttributeType taskStateType =
-          DirectoryServer.getAttributeType(ATTR_TASK_STATE.toLowerCase());
       String stateString =
-          resultEntry.getAttributeValue(taskStateType,
-              DirectoryStringSyntax.DECODER);
+          resultEntry.parseAttribute(ATTR_TASK_STATE.toLowerCase()).asString();
       TaskState taskState = TaskState.fromString(stateString);
       assertEquals(taskState, expectedState,
           "The task completed in an unexpected state");
 
       // Check that the task contains some log messages.
-      AttributeType logMessagesType = DirectoryServer.getAttributeType(
-          ATTR_TASK_LOG_MESSAGES.toLowerCase());
-      List<String> logMessages = new ArrayList<String>();
-      resultEntry.getAttributeValues(logMessagesType,
-          DirectoryStringSyntax.DECODER,
-          logMessages);
+      Set<String> logMessages = resultEntry.parseAttribute(
+          ATTR_TASK_LOG_MESSAGES.toLowerCase()).asSetOfString();
       if (taskState != TaskState.COMPLETED_SUCCESSFULLY &&
           logMessages.isEmpty())
       {
@@ -268,8 +262,6 @@
     // Wait until the task completes.
     int timeout = 2000;
 
-    AttributeType completionTimeType = DirectoryServer.getAttributeType(
-        ATTR_TASK_COMPLETION_TIME.toLowerCase());
     SearchFilter filter =
         SearchFilter.createFilterFromString("(objectclass=*)");
 
@@ -280,9 +272,8 @@
           taskEntry.getName(), SearchScope.BASE_OBJECT, filter);
       Entry resultEntry = searchOperation.getSearchEntries().getFirst();
 
-      String completionTime = resultEntry.getAttributeValue(
-          completionTimeType, DirectoryStringSyntax.DECODER);
-
+      String completionTime = resultEntry.parseAttribute(
+          ATTR_TASK_COMPLETION_TIME.toLowerCase()).asString();
       if (completionTime != null)
       {
         return resultEntry;
@@ -300,8 +291,7 @@
   private void assertAttributeValue(Entry resultEntry, String lowerAttrName,
       long expected, String message) throws DirectoryException
   {
-    AttributeType type = DirectoryServer.getAttributeType(lowerAttrName, true);
-    String value = resultEntry.getAttributeValue(type, DirectoryStringSyntax.DECODER);
+    String value = resultEntry.parseAttribute(lowerAttrName).asString();
     assertEquals(Long.decode(value).longValue(), expected, message);
   }
 
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java
index 9b766b7..0161151 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/ReplicationTestCase.java
@@ -32,13 +32,14 @@
 import org.assertj.core.api.Assertions;
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
+import org.forgerock.opendj.config.server.ConfigException;
 import org.forgerock.opendj.ldap.ModificationType;
+import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.SearchScope;
 import org.opends.server.DirectoryServerTestCase;
 import org.opends.server.TestCaseUtils;
 import org.opends.server.admin.std.server.ReplicationDomainCfg;
 import org.opends.server.backends.task.TaskState;
-import org.forgerock.opendj.config.server.ConfigException;
 import org.opends.server.core.AddOperation;
 import org.opends.server.core.DeleteOperation;
 import org.opends.server.core.DirectoryServer;
@@ -52,18 +53,15 @@
 import org.opends.server.replication.server.ReplicationServer;
 import org.opends.server.replication.server.changelog.je.JEChangelogDB;
 import org.opends.server.replication.service.ReplicationBroker;
-import org.opends.server.schema.IntegerSyntax;
 import org.opends.server.types.*;
-import org.forgerock.opendj.ldap.ResultCode;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+import static org.forgerock.opendj.ldap.ResultCode.*;
 import static org.forgerock.opendj.ldap.SearchScope.*;
 import static org.opends.server.TestCaseUtils.*;
 import static org.opends.server.config.ConfigConstants.*;
-import static org.opends.server.schema.DirectoryStringSyntax.*;
-import static org.forgerock.opendj.ldap.ResultCode.*;
 import static org.testng.Assert.*;
 
 /**
@@ -499,8 +497,7 @@
     assertFalse(op.getSearchEntries().isEmpty(), "Could not read monitoring information");
 
     SearchResultEntry entry = op.getSearchEntries().getFirst();
-    AttributeType attrType = DirectoryServer.getDefaultAttributeType(attr);
-    return entry.getAttributeValue(attrType, IntegerSyntax.DECODER).longValue();
+    return entry.parseAttribute(attr).asLong();
   }
 
   /**
@@ -632,8 +629,6 @@
                  "Add of the task definition was not successful");
 
     // Wait until the task completes.
-    AttributeType completionTimeType = DirectoryServer.getAttributeType(
-         ATTR_TASK_COMPLETION_TIME.toLowerCase());
     SearchFilter filter = SearchFilter.createFilterFromString("(objectclass=*)");
     Entry resultEntry = null;
     String completionTime = null;
@@ -649,7 +644,8 @@
         continue;
       }
       resultEntry = searchOperation.getSearchEntries().get(0);
-      completionTime = resultEntry.getAttributeValue(completionTimeType, DECODER);
+      completionTime = resultEntry.parseAttribute(
+          ATTR_TASK_COMPLETION_TIME.toLowerCase()).asString();
       if (completionTime == null)
       {
         if (System.currentTimeMillis() - startMillisecs > 1000*30)
@@ -663,9 +659,8 @@
     assertNotNull(completionTime, "The task has not completed after 30 seconds.");
 
     // Check that the task state is as expected.
-    AttributeType taskStateType =
-         DirectoryServer.getAttributeType(ATTR_TASK_STATE.toLowerCase());
-    String stateString = resultEntry.getAttributeValue(taskStateType, DECODER);
+    String stateString = resultEntry.parseAttribute(
+        ATTR_TASK_STATE.toLowerCase()).asString();
     TaskState taskState = TaskState.fromString(stateString);
     assertEquals(taskState, TaskState.COMPLETED_SUCCESSFULLY,
                  "The task completed in an unexpected state");
@@ -744,9 +739,8 @@
       resultEntry = searchOperation.getSearchEntries().getFirst();
 
       // Check that the task state is as expected.
-      AttributeType taskStateType =
-          DirectoryServer.getAttributeType(ATTR_TASK_STATE.toLowerCase());
-      String stateString = resultEntry.getAttributeValue(taskStateType, DECODER);
+      String stateString = resultEntry.parseAttribute(
+          ATTR_TASK_STATE.toLowerCase()).asString();
       taskState = TaskState.fromString(stateString);
 
       Thread.sleep(100);
@@ -757,10 +751,8 @@
         && (System.currentTimeMillis() - startTime < maxWaitTimeInMillis));
 
     // Check that the task contains some log messages.
-    AttributeType logMessagesType =
-        DirectoryServer.getAttributeType(ATTR_TASK_LOG_MESSAGES.toLowerCase());
-    List<String> logMessages = new ArrayList<String>();
-    resultEntry.getAttributeValues(logMessagesType, DECODER, logMessages);
+    Set<String> logMessages = resultEntry.parseAttribute(
+        ATTR_TASK_LOG_MESSAGES.toLowerCase()).asSetOfString();
 
     if (taskState != TaskState.COMPLETED_SUCCESSFULLY
         && taskState != TaskState.RUNNING)
@@ -768,13 +760,14 @@
       assertFalse(logMessages.isEmpty(),
           "No log messages were written to the task entry on a failed task");
     }
-    if (logMessages.size() != 0)
+    if (!logMessages.isEmpty())
     {
-      logger.trace(logMessages.get(0));
+      String firstLogMsg = logMessages.iterator().next();
+      logger.trace(firstLogMsg);
       if (expectedMessage != null)
       {
         logger.trace(expectedMessage);
-        assertTrue(logMessages.get(0).indexOf(expectedMessage.toString()) > 0);
+        assertTrue(firstLogMsg.indexOf(expectedMessage.toString()) > 0);
       }
     }
 
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java
index fedc09b..e1eb8fd 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/replication/UpdateOperationTest.java
@@ -35,6 +35,7 @@
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.opendj.ldap.ModificationType;
+import org.forgerock.opendj.ldap.ResultCode;
 import org.opends.server.TestCaseUtils;
 import org.opends.server.core.AddOperation;
 import org.opends.server.core.DeleteOperation;
@@ -47,9 +48,7 @@
 import org.opends.server.replication.plugin.LDAPReplicationDomain;
 import org.opends.server.replication.protocol.*;
 import org.opends.server.replication.service.ReplicationBroker;
-import org.opends.server.schema.DirectoryStringSyntax;
 import org.opends.server.types.*;
-import org.forgerock.opendj.ldap.ResultCode;
 import org.opends.server.util.TimeThread;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
@@ -1315,8 +1314,7 @@
 
       // Get the UUID of the test entry.
       Entry resultEntry = getEntry(tmp.getName(), 1, true);
-      AttributeType uuidType = DirectoryServer.getAttributeType("entryuuid");
-      String uuid = resultEntry.getAttributeValue(uuidType, DirectoryStringSyntax.DECODER);
+      String uuid = resultEntry.parseAttribute("entryuuid").asString();
 
       // Register a short circuit that will fake a no-such-object result code
       // on a delete.  This will cause a replication replay loop.
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/tasks/TasksTestCase.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/tasks/TasksTestCase.java
index e2e91a2..a789d13 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/tasks/TasksTestCase.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/tasks/TasksTestCase.java
@@ -26,8 +26,9 @@
  */
 package org.opends.server.tasks;
 
-import java.util.ArrayList;
+import java.util.Set;
 
+import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.SearchScope;
 import org.opends.server.DirectoryServerTestCase;
 import org.opends.server.backends.task.Task;
@@ -37,9 +38,9 @@
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.protocols.internal.InternalSearchOperation;
-import org.opends.server.schema.DirectoryStringSyntax;
-import org.opends.server.types.*;
-import org.forgerock.opendj.ldap.ResultCode;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.SearchFilter;
 import org.testng.annotations.Test;
 
 import static org.opends.server.config.ConfigConstants.*;
@@ -88,8 +89,6 @@
                  "Add of the task definition was not successful");
 
     // Wait until the task completes.
-    AttributeType completionTimeType = DirectoryServer.getAttributeType(
-         ATTR_TASK_COMPLETION_TIME.toLowerCase());
     SearchFilter filter =
          SearchFilter.createFilterFromString("(objectclass=*)");
     Entry resultEntry = null;
@@ -110,9 +109,8 @@
 //        fail("Task entry was not returned from the search.");
         continue;
       }
-      completionTime =
-           resultEntry.getAttributeValue(completionTimeType,
-                                         DirectoryStringSyntax.DECODER);
+      completionTime = resultEntry.parseAttribute(
+          ATTR_TASK_COMPLETION_TIME.toLowerCase()).asString();
 
       if (completionTime == null)
       {
@@ -130,22 +128,15 @@
     }
 
     // Check that the task state is as expected.
-    AttributeType taskStateType =
-         DirectoryServer.getAttributeType(ATTR_TASK_STATE.toLowerCase());
     String stateString =
-         resultEntry.getAttributeValue(taskStateType,
-                                       DirectoryStringSyntax.DECODER);
+        resultEntry.parseAttribute(ATTR_TASK_STATE.toLowerCase()).asString();
     TaskState taskState = TaskState.fromString(stateString);
     assertEquals(taskState, expectedState,
                  "The task completed in an unexpected state");
 
     // Check that the task contains some log messages.
-    AttributeType logMessagesType = DirectoryServer.getAttributeType(
-         ATTR_TASK_LOG_MESSAGES.toLowerCase());
-    ArrayList<String> logMessages = new ArrayList<String>();
-    resultEntry.getAttributeValues(logMessagesType,
-                                   DirectoryStringSyntax.DECODER,
-                                   logMessages);
+    Set<String> logMessages = resultEntry.parseAttribute(
+        ATTR_TASK_LOG_MESSAGES.toLowerCase()).asSetOfString();
     if (taskState != TaskState.COMPLETED_SUCCESSFULLY &&
         logMessages.size() == 0)
     {
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestAttributeValue.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestAttributeValue.java
index 5a58c9a..5cb00fa 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestAttributeValue.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestAttributeValue.java
@@ -26,11 +26,10 @@
  */
 package org.opends.server.types;
 
-import org.forgerock.opendj.ldap.ByteString;
+import org.opends.server.core.DirectoryServer;
+import org.testng.Assert;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
-import org.testng.Assert;
-import org.opends.server.core.DirectoryServer;
 
 /**
  * Test case for AttributeValues
@@ -81,32 +80,4 @@
     Assert.assertEquals(h1 == h2, result);
   }
 
-
-
-  /**
-   * Check that the {@link AttributeValue#getNormalizedValue()} method
-   * works as expected.
-   *
-   * @param value1
-   *          The first test value.
-   * @param value2
-   *          The second test value.
-   * @param result
-   *          The expected result.
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test(dataProvider = "generateHashCodeTestData")
-  public void testGetNormalizedValue(String value1, String value2,
-      boolean result) throws Exception {
-    AttributeType type = DirectoryServer.getDefaultAttributeType("test");
-
-    AttributeValue av1 = AttributeValues.create(type, value1);
-    AttributeValue av2 = AttributeValues.create(type, value2);
-
-    ByteString r1 = av1.getNormalizedValue();
-    ByteString r2 = av2.getNormalizedValue();
-
-    Assert.assertEquals(r1.equals(r2), result);
-  }
 }
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestEntry.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestEntry.java
index 571032f..5d15f16 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestEntry.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/types/TestEntry.java
@@ -26,27 +26,29 @@
  */
 package org.opends.server.types;
 
-import org.forgerock.opendj.ldap.ByteString;
-import static org.testng.Assert.*;
-
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
 
-import org.opends.server.TestCaseUtils;
+import org.assertj.core.api.Assertions;
 import org.forgerock.i18n.LocalizableMessageBuilder;
+import org.forgerock.i18n.LocalizedIllegalArgumentException;
+import org.forgerock.opendj.ldap.ByteString;
+import org.opends.server.TestCaseUtils;
 import org.opends.server.core.DirectoryServer;
-import org.opends.server.schema.*;
-import org.testng.annotations.Test;
+import org.opends.server.schema.AttributeTypeSyntax;
 import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.*;
 
 /**
  * This class defines a set of tests for the {@link Entry} class.
  * <p>
- * At the moment this test suite only tests the getAttributeValue and
- * getAttributeValues methods.
+ * At the moment this test suite only tests the parseAttribute method.
  */
 public final class TestEntry extends TypesTestCase {
 
@@ -60,9 +62,7 @@
    * @return The test entry.
    */
   private Entry createTestEntry(AttributeType type, String value) {
-    String[] values = new String[1];
-    values[0] = value;
-
+    String[] values = new String[] { value };
     return createTestEntry(type, values);
   }
 
@@ -131,88 +131,68 @@
   }
 
   /**
-   * Test the
-   * {@link Entry#getAttributeValue(AttributeType, AttributeValueDecoder)}
-   * method.
-   *
-   * @throws Exception
-   *           If the test failed unexpectedly.
+   * Test the {@link Entry#parseAttribute(String)} method.
    */
   @Test
-  public void testGetAttributeValueNotFound() throws Exception {
+  public void testParseAttributeNotFound() throws Exception {
     AttributeType type1 = DirectoryServer.getAttributeType("description");
     AttributeType type2 = DirectoryServer.getAttributeType("inheritable");
 
     Entry entry = createTestEntry(type1, "hello world");
 
-    assertEquals(null, entry
-        .getAttributeValue(type2, BooleanSyntax.DECODER));
+    assertEquals(null, entry.parseAttribute(type2.getNameOrOID()).asString());
   }
 
   /**
-   * Test the
-   * {@link Entry#getAttributeValue(AttributeType, AttributeValueDecoder)}
-   * method.
-   *
-   * @throws Exception
-   *           If the test failed unexpectedly.
+   * Test the {@link Entry#parseAttribute(String)} method.
    */
   @Test
-  public void testGetAttributeValueBooleanTrue() throws Exception {
+  public void testParseAttributeBooleanTrue() throws Exception {
     AttributeType type = DirectoryServer.getAttributeType("inheritable");
 
     Entry entry = createTestEntry(type, "true");
 
-    assertEquals(Boolean.TRUE, entry.getAttributeValue(type,
-        BooleanSyntax.DECODER));
+    assertEquals(entry.parseAttribute(type.getNameOrOID()).asBoolean(),
+        Boolean.TRUE);
   }
 
   /**
-   * Test the
-   * {@link Entry#getAttributeValue(AttributeType, AttributeValueDecoder)}
-   * method.
-   *
-   * @throws Exception
-   *           If the test failed unexpectedly.
+   * Test the {@link Entry#parseAttribute(String)} method.
    */
   @Test
-  public void testGetAttributeValueBooleanFalse() throws Exception {
+  public void testParseAttributeBooleanFalse() throws Exception
+  {
     AttributeType type = DirectoryServer.getAttributeType("inheritable");
 
     Entry entry = createTestEntry(type, "false");
 
-    assertEquals(Boolean.FALSE, entry.getAttributeValue(type,
-        BooleanSyntax.DECODER));
+    assertEquals(entry.parseAttribute(type.getNameOrOID()).asBoolean(),
+        Boolean.FALSE);
   }
 
   /**
-   * Test the
-   * {@link Entry#getAttributeValue(AttributeType, AttributeValueDecoder)}
-   * method.
-   *
-   * @throws Exception
-   *           If the test failed unexpectedly.
+   * Test the {@link Entry#parseAttribute(String)} method.
    */
-  @Test(expectedExceptions = DirectoryException.class)
-  public void testGetAttributeValueBooleanBad() throws Exception {
+  @Test(expectedExceptions = LocalizedIllegalArgumentException.class)
+  public void testParseAttributeBooleanBad() throws Exception
+  {
     AttributeType type = DirectoryServer.getAttributeType("inheritable");
 
     Entry entry = createTestEntry(type, "bad-value");
-    entry.getAttributeValue(type, BooleanSyntax.DECODER);
+    entry.parseAttribute(type.getNameOrOID()).asBoolean();
     throw new RuntimeException(
          "An illegal boolean value did not throw an exception");
   }
 
   /**
-   * Test the
-   * {@link Entry#getAttributeValue(AttributeType, AttributeValueDecoder)}
-   * method.
+   * Test the {@link Entry#parseAttribute(String)} method.
    *
    * @throws Exception
    *           If the test failed unexpectedly.
    */
   @Test
-  public void testGetAttributeValuesInteger() throws Exception {
+  public void testParseAttributesInteger() throws Exception
+  {
     AttributeType type = DirectoryServer
         .getAttributeType("supportedldapversion");
     String[] values = new String[] { "-4", "-2", "0", "1", "3" };
@@ -223,43 +203,33 @@
     }
 
     Entry entry = createTestEntry(type, values);
-    HashSet<Integer> result = new HashSet<Integer>();
-    entry.getAttributeValues(type, IntegerSyntax.DECODER, result);
-
-    assertEquals(expected, result);
+    Set<Integer> result =
+        entry.parseAttribute("supportedldapversion").asSetOfInteger();
+    Assertions.assertThat(result).isEqualTo(expected);
   }
 
   /**
-   * Test the
-   * {@link Entry#getAttributeValue(AttributeType, AttributeValueDecoder)}
-   * method.
+   * Test the {@link Entry#parseAttribute(String)} method.
    *
    * @throws Exception
    *           If the test failed unexpectedly.
    */
-  @Test(expectedExceptions = DirectoryException.class)
-  public void testGetAttributeValueIntegerBad() throws Exception {
+  @Test(expectedExceptions = LocalizedIllegalArgumentException.class)
+  public void testParseAttributeIntegerBad() throws Exception
+  {
     AttributeType type = DirectoryServer
         .getAttributeType("supportedldapversion");
     String[] values = new String[] { "-4", "-2", "xxx", "1", "3" };
 
-    HashSet<Integer> result = new HashSet<Integer>();
     Entry entry = createTestEntry(type, values);
-    entry.getAttributeValues(type, IntegerSyntax.DECODER, result);
-    throw new RuntimeException(
-         "An illegal integer value did not throw an exception");
+    entry.parseAttribute("supportedldapversion").asSetOfInteger();
   }
 
   /**
-   * Test the
-   * {@link Entry#getAttributeValue(AttributeType, AttributeValueDecoder)}
-   * method.
-   *
-   * @throws Exception
-   *           If the test failed unexpectedly.
+   * Test the {@link Entry#parseAttribute(String)} method.
    */
   @Test
-  public void testGetAttributeValuesSubtreeSpecification()
+  public void testParseAttributesSubtreeSpecification()
       throws Exception {
     // Define a dummy attribute type, in case there is not one already
     // in the core schema.
@@ -279,15 +249,19 @@
     // Relative to the root DN.
     DN rootDN = DN.rootDN();
 
-    SubtreeSpecificationSet expected = new SubtreeSpecificationSet();
+    Set<SubtreeSpecification> expected = new HashSet<SubtreeSpecification>();
     for (String value : values) {
       expected.add(SubtreeSpecification.valueOf(rootDN, value));
     }
 
     Entry entry = createTestEntry(type, values);
-    SubtreeSpecificationSet result = new SubtreeSpecificationSet();
-    entry.getAttributeValues(type, SubtreeSpecificationSyntax
-        .createAttributeValueDecoder(rootDN), result);
+    Set<SubtreeSpecification> result = new HashSet<SubtreeSpecification>();
+    List<Attribute> attributes = entry.getAttribute(type, true);
+    for (AttributeValue value : new AttributeValueIterable(attributes))
+    {
+      String v = value.getValue().toString();
+      result.add(SubtreeSpecification.valueOf(rootDN, v));
+    }
 
     assertEquals(expected, result);
   }

--
Gitblit v1.10.0