From eda03abb06f41962772d6439f14a3395ca99bd5e Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Tue, 15 May 2012 16:16:52 +0000
Subject: [PATCH] Checkpoint dev.

---
 opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/IdentityAttributeMapper.java  |  112 +++++++++++
 opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EtagStrategy.java             |   26 ++
 opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java                    |   89 ++++++++
 opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPResource.java             |  117 ++++++-----
 opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AttributeMapper.java          |   39 +--
 opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java |  178 +++++++++++++++++
 opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java           |    6 
 7 files changed, 492 insertions(+), 75 deletions(-)

diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AttributeMapper.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AttributeMapper.java
index 32e593e..9387131 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AttributeMapper.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AttributeMapper.java
@@ -18,6 +18,7 @@
 
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import org.forgerock.json.fluent.JsonPointer;
@@ -33,30 +34,30 @@
 public interface AttributeMapper {
 
     /**
-     * Returns an unmodifiable set containing the names of the LDAP attributes
-     * required by this attribute mapper. The returned set should only contain
-     * the names of attributes found in the LDAP entry directly associated with
-     * the resource.
+     * Returns an unmodifiable collection containing the names of the LDAP
+     * attributes required by this attribute mapper. The returned collection
+     * should only contain the names of attributes found in the LDAP entry
+     * directly associated with the resource.
      *
-     * @return An unmodifiable set containing the names of the LDAP attributes
-     *         required by this attribute mapper.
+     * @return An unmodifiable collection containing the names of the LDAP
+     *         attributes required by this attribute mapper.
      */
-    Set<String> getAllLDAPAttributes();
+    Collection<String> getAllLDAPAttributes();
 
     /**
-     * Returns an unmodifiable collection containing the names of the LDAP
-     * attributes required by this attribute mapper and which are associated
-     * with the provided resource attribute. The returned set should only
-     * contain the names of attributes found in the LDAP entry directly
-     * associated with the resource.
+     * Adds the names of the LDAP attributes required by this attribute mapper
+     * which are associated with the provided resource attribute.
+     * <p>
+     * Implementations should only add the names of attributes found in the LDAP
+     * entry directly associated with the resource.
      *
      * @param resourceAttribute
      *            The name of the resource attribute requested by the client.
-     * @return An unmodifiable collection containing the names of the LDAP
-     *         attributes required by this attribute mapper and which are
-     *         associated with the provided resource attribute.
+     * @param ldapAttributes
+     *            The set into which the required LDAP attribute names should be
+     *            put.
      */
-    Collection<String> getLDAPAttributesFor(JsonPointer resourceAttribute);
+    void getLDAPAttributesFor(JsonPointer resourceAttribute, Set<String> ldapAttributes);
 
     /**
      * Transforms attributes contained in the provided LDAP entry to JSON
@@ -69,10 +70,9 @@
      *
      * @param c
      * @param e
-     * @param v
      * @param h
      */
-    void toJson(Context c, Entry e, JsonValue v, ResultHandler<JsonValue> h);
+    void toJson(Context c, Entry e, ResultHandler<Map<String, Object>> h);
 
     /**
      * Transforms JSON content in the provided JSON value to LDAP attributes,
@@ -84,10 +84,9 @@
      *
      * @param c
      * @param v
-     * @param a
      * @param h
      */
-    void toLDAP(Context c, JsonValue v, List<Attribute> a, ResultHandler<Entry> h);
+    void toLDAP(Context c, JsonValue v, ResultHandler<List<Attribute>> h);
 
     // TODO: methods for obtaining schema information (e.g. name, description,
     // type information).
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java
new file mode 100644
index 0000000..1bcfee1
--- /dev/null
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java
@@ -0,0 +1,178 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyrighted [year] [name of copyright owner]".
+ *
+ * Copyright 2012 ForgeRock AS. All rights reserved.
+ */
+
+package org.forgerock.opendj.rest2ldap;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.forgerock.json.fluent.JsonPointer;
+import org.forgerock.json.fluent.JsonValue;
+import org.forgerock.opendj.ldap.Attribute;
+import org.forgerock.opendj.ldap.Entry;
+import org.forgerock.opendj.ldap.ErrorResultException;
+import org.forgerock.opendj.ldap.ResultHandler;
+import org.forgerock.resource.provider.Context;
+
+/**
+ *
+ */
+public final class CompositeAttributeMapper implements AttributeMapper {
+    private final Set<String> allLDAPAttributes;
+    private final List<AttributeMapper> attributeMappers;
+
+    /**
+     * Creates a new composite attribute mapper.
+     *
+     * @param attributeMappers
+     *            The list of attribute mappers.
+     */
+    public CompositeAttributeMapper(final List<AttributeMapper> attributeMappers) {
+        this.attributeMappers = new ArrayList<AttributeMapper>(attributeMappers);
+        Set<String> tmp = new LinkedHashSet<String>(attributeMappers.size());
+        for (final AttributeMapper mapper : attributeMappers) {
+            tmp.addAll(mapper.getAllLDAPAttributes());
+        }
+        allLDAPAttributes = Collections.unmodifiableSet(tmp);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Set<String> getAllLDAPAttributes() {
+        return allLDAPAttributes;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void getLDAPAttributesFor(JsonPointer resourceAttribute, Set<String> ldapAttributes) {
+        for (AttributeMapper attribute : attributeMappers) {
+            attribute.getLDAPAttributesFor(resourceAttribute, ldapAttributes);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void toJson(Context c, Entry e, final ResultHandler<Map<String, Object>> h) {
+        ResultHandler<Map<String, Object>> resultAccumulater =
+                new ResultHandler<Map<String, Object>>() {
+                    private final AtomicInteger latch = new AtomicInteger(attributeMappers.size());
+                    private final List<Map<String, Object>> results =
+                            new ArrayList<Map<String, Object>>(latch.get());
+
+                    public void handleErrorResult(ErrorResultException error) {
+                        // Ensure that handler is only invoked once.
+                        if (latch.getAndSet(0) > 0) {
+                            h.handleErrorResult(error);
+                        }
+                    }
+
+                    public void handleResult(Map<String, Object> result) {
+                        synchronized (this) {
+                            results.add(result);
+                        }
+                        if (latch.decrementAndGet() == 0) {
+                            final Map<String, Object> mergeResult;
+                            switch (results.size()) {
+                            case 0:
+                                mergeResult = Collections.<String, Object> emptyMap();
+                                break;
+                            case 1:
+                                mergeResult = results.get(0);
+                                break;
+                            default:
+                                mergeResult = new LinkedHashMap<String, Object>();
+                                mergeJsonValues(results, mergeResult);
+                                break;
+                            }
+                            h.handleResult(mergeResult);
+                        }
+                    }
+                };
+
+        for (AttributeMapper mapper : attributeMappers) {
+            mapper.toJson(c, e, resultAccumulater);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void toLDAP(Context c, JsonValue v, ResultHandler<List<Attribute>> h) {
+        // TODO Auto-generated method stub
+
+    }
+
+    /**
+     * Merge the provided list of JSON values into a single value.
+     *
+     * @param srcValues
+     *            The source values.
+     * @param dstValue
+     *            The destination value, into which which the values should be
+     *            merged.
+     */
+    private void mergeJsonValues(List<Map<String, Object>> srcValues, Map<String, Object> dstValue) {
+        for (Map<String, Object> value : srcValues) {
+            mergeJsonValue(value, dstValue);
+        }
+    }
+
+    /**
+     * Merge one JSON value into another.
+     *
+     * @param srcValue
+     *            The source value.
+     * @param dstValue
+     *            The destination value, into which which the value should be
+     *            merged.
+     */
+    @SuppressWarnings("unchecked")
+    private void mergeJsonValue(Map<String, Object> srcValue, Map<String, Object> dstValue) {
+        for (Map.Entry<String, Object> record : srcValue.entrySet()) {
+            String key = record.getKey();
+            Object newValue = record.getValue();
+            Object existingValue = dstValue.get(key);
+            if (existingValue == null) {
+                // Value is new, so just add it.
+                dstValue.put(key, newValue);
+            } else if (existingValue instanceof Map && newValue instanceof Map) {
+                // Merge two maps - create a new Map, in case the existing one
+                // is unmodifiable.
+                existingValue =
+                        new LinkedHashMap<String, Object>((Map<String, Object>) existingValue);
+                mergeJsonValue((Map<String, Object>) newValue, (Map<String, Object>) existingValue);
+            } else if (existingValue instanceof List && newValue instanceof List) {
+                // Merge two lists- create a new List, in case the existing one
+                // is unmodifiable.
+                List<Object> tmp = new ArrayList<Object>((List<Object>) existingValue);
+                tmp.addAll((List<Object>) newValue);
+                existingValue = tmp;
+            }
+
+            // Replace the existing value.
+            dstValue.put(key, newValue);
+        }
+    }
+}
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java
index cb98f56..5b4e80d 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java
@@ -16,7 +16,7 @@
 
 package org.forgerock.opendj.rest2ldap;
 
-import java.util.Set;
+import java.util.Collection;
 
 import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.SearchResultHandler;
@@ -48,7 +48,7 @@
 
     /**
      * Reads the entry having the specified resource ID.
-     * 
+     *
      * @param c
      *            The request context.
      * @param id
@@ -58,7 +58,7 @@
      * @param h
      *            The result handler.
      */
-    public void readEntry(final Context c, final String id, final Set<String> attributes,
+    public void readEntry(final Context c, final String id, final Collection<String> attributes,
             final ResultHandler<SearchResultEntry> h) {
         // TODO Auto-generated method stub
 
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EtagStrategy.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EtagStrategy.java
new file mode 100644
index 0000000..5071e2c
--- /dev/null
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EtagStrategy.java
@@ -0,0 +1,26 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyrighted [year] [name of copyright owner]".
+ *
+ * Copyright 2012 ForgeRock AS. All rights reserved.
+ */
+
+package org.forgerock.opendj.rest2ldap;
+
+import org.forgerock.opendj.ldap.Entry;
+
+/**
+ *
+ */
+public interface EtagStrategy {
+    String getEtagFromEntry(final Entry entry);
+}
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/IdentityAttributeMapper.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/IdentityAttributeMapper.java
new file mode 100644
index 0000000..0ea36f9
--- /dev/null
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/IdentityAttributeMapper.java
@@ -0,0 +1,112 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyrighted [year] [name of copyright owner]".
+ *
+ * Copyright 2012 ForgeRock AS. All rights reserved.
+ */
+
+package org.forgerock.opendj.rest2ldap;
+
+import static org.forgerock.opendj.rest2ldap.Utils.getAttributeName;
+import static org.forgerock.opendj.rest2ldap.Utils.toLowerCase;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.forgerock.json.fluent.JsonPointer;
+import org.forgerock.json.fluent.JsonValue;
+import org.forgerock.opendj.ldap.Attribute;
+import org.forgerock.opendj.ldap.Entry;
+import org.forgerock.opendj.ldap.ResultHandler;
+import org.forgerock.resource.provider.Context;
+
+/**
+ *
+ */
+public final class IdentityAttributeMapper implements AttributeMapper {
+
+    // All user attributes by default.
+    private final Map<String, String> includedAttributes = new LinkedHashMap<String, String>();
+    private final Map<String, String> excludedAttributes = new LinkedHashMap<String, String>();
+
+    public IdentityAttributeMapper() {
+        // No implementation required.
+    }
+
+    public IdentityAttributeMapper includeAttribute(String... attributes) {
+        for (String attribute : attributes) {
+            includedAttributes.put(toLowerCase(attribute), attribute);
+        }
+        return this;
+    }
+
+    public IdentityAttributeMapper excludeAttribute(String... attributes) {
+        for (String attribute : attributes) {
+            excludedAttributes.put(toLowerCase(attribute), attribute);
+        }
+        return this;
+    }
+
+    public Collection<String> getAllLDAPAttributes() {
+        if (!includedAttributes.isEmpty()) {
+            return includedAttributes.values();
+        } else {
+            // All user attributes.
+            return Collections.emptySet();
+        }
+    }
+
+    public void getLDAPAttributesFor(JsonPointer resourceAttribute, Set<String> ldapAttributes) {
+        String name = resourceAttribute.leaf();
+        if (name != null) {
+            if (isIncludedAttribute(name)) {
+                ldapAttributes.add(name);
+            } else {
+                // FIXME: log something or return a ResourceException?
+            }
+        }
+    }
+
+    public void toJson(Context c, Entry e, ResultHandler<Map<String, Object>> h) {
+        Map<String, Object> result = new LinkedHashMap<String, Object>(e.getAttributeCount());
+        for (Attribute a : e.getAllAttributes()) {
+            String name = getAttributeName(a);
+            if (isIncludedAttribute(name)) {
+                result.put(name, Utils.attributeToJson(a));
+            }
+        }
+    }
+
+    private boolean isIncludedAttribute(String name) {
+        String lowerName = toLowerCase(name);
+
+        // Ignore the requested attribute if it has been excluded.
+        if (excludedAttributes.containsKey(lowerName)) {
+            return false;
+        }
+
+        // Include all attributes by default.
+        if (includedAttributes.isEmpty() || includedAttributes.containsKey(lowerName)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    public void toLDAP(Context c, JsonValue v, ResultHandler<List<Attribute>> h) {
+        // TODO:
+    }
+}
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPResource.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPResource.java
index 579148f..d67d9d4 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPResource.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPResource.java
@@ -17,16 +17,24 @@
 package org.forgerock.opendj.rest2ldap;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import org.forgerock.json.fluent.JsonPointer;
 import org.forgerock.json.fluent.JsonValue;
-import org.forgerock.opendj.ldap.Entry;
+import org.forgerock.opendj.ldap.AssertionFailureException;
+import org.forgerock.opendj.ldap.AuthenticationException;
+import org.forgerock.opendj.ldap.AuthorizationException;
+import org.forgerock.opendj.ldap.ConnectionException;
+import org.forgerock.opendj.ldap.EntryNotFoundException;
 import org.forgerock.opendj.ldap.ErrorResultException;
+import org.forgerock.opendj.ldap.MultipleEntriesFoundException;
 import org.forgerock.opendj.ldap.ResultHandler;
 import org.forgerock.opendj.ldap.SearchResultHandler;
+import org.forgerock.opendj.ldap.TimeoutResultException;
 import org.forgerock.opendj.ldap.responses.Result;
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.responses.SearchResultReference;
@@ -53,23 +61,27 @@
  *
  */
 public class LDAPResource implements Resource {
-    private Set<String> allLDAPAttributes;
-    private final EntryContainer container;
-    private final List<AttributeMapper> mappers;
+    private final EntryContainer entryContainer;
+    private final AttributeMapper attributeMapper;
+    private final EtagStrategy etagStrategy;
 
     /**
      * Creates a new LDAP resource.
-     * 
-     * @param container
+     *
+     * @param entryContainer
      *            The entry container which will be used to interact with the
      *            LDAP server.
-     * @param mappers
-     *            The list of attribute mappers.
+     * @param etagStrategy
+     *            The algorithm which should be used to obtain a resource's etag
+     *            from an entry.
+     * @param attributeMapper
+     *            The attribute mapper.
      */
-    public LDAPResource(final EntryContainer container, final List<AttributeMapper> mappers) {
-        this.container = container;
-        this.mappers = mappers;
-        cacheAllLDAPAttributes();
+    public LDAPResource(final EntryContainer entryContainer, final EtagStrategy etagStrategy,
+            final AttributeMapper attributeMapper) {
+        this.entryContainer = entryContainer;
+        this.etagStrategy = etagStrategy;
+        this.attributeMapper = attributeMapper;
     }
 
     /**
@@ -125,7 +137,7 @@
                 public boolean handleEntry(final SearchResultEntry entry) {
                     // TODO: should the resource or the container define the ID
                     // mapping?
-                    resourceIDs.add(container.getIDFromEntry(entry));
+                    resourceIDs.add(entryContainer.getIDFromEntry(entry));
                     return true;
                 }
 
@@ -135,7 +147,7 @@
 
                 public boolean handleReference(final SearchResultReference reference) {
                     // TODO: should this be classed as an error since rest2ldap
-                    // assumes entries are all colocated.
+                    // assumes entries are all colocated?
                     return true;
                 }
 
@@ -143,13 +155,13 @@
                     out.setResult(id, null, new JsonValue(resourceIDs));
                 }
             };
-            container.listEntries(context, handler);
+            entryContainer.listEntries(context, handler);
         } else {
             // Read a single entry.
 
             // TODO: Determine the set of LDAP attributes that need to be read.
             final Set<JsonPointer> requestedAttributes = new LinkedHashSet<JsonPointer>();
-            final Set<String> requestedLDAPAttributes =
+            final Collection<String> requestedLDAPAttributes =
                     getRequestedLDAPAttributes(requestedAttributes);
 
             final ResultHandler<SearchResultEntry> handler =
@@ -159,23 +171,22 @@
                         }
 
                         public void handleResult(final SearchResultEntry entry) {
-                            final String revision = getRevisionFromEntry(entry);
-
-                            final ResultHandler<JsonValue> mapHandler =
-                                    new ResultHandler<JsonValue>() {
+                            final String revision = etagStrategy.getEtagFromEntry(entry);
+                            final ResultHandler<Map<String, Object>> mapHandler =
+                                    new ResultHandler<Map<String, Object>>() {
                                         public void handleErrorResult(
                                                 final ErrorResultException error) {
                                             out.setFailure(adaptErrorResult(error));
                                         }
 
-                                        public void handleResult(final JsonValue result) {
-                                            out.setResult(id, revision, result);
+                                        public void handleResult(final Map<String, Object> result) {
+                                            out.setResult(id, revision, new JsonValue(result));
                                         }
                                     };
-                            mapEntryToJson(context, requestedAttributes, entry, mapHandler);
+                            attributeMapper.toJson(context, entry, mapHandler);
                         }
                     };
-            container.readEntry(context, id, requestedLDAPAttributes, handler);
+            entryContainer.readEntry(context, id, requestedLDAPAttributes, handler);
         }
     }
 
@@ -187,56 +198,58 @@
         out.setFailure(new NotSupportedException("Not yet implemented"));
     }
 
-    private ResourceException adaptErrorResult(final ErrorResultException error) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
     /**
-     * Caches the set of LDAP attributes associated with all of this resource's
-     * mappers.
+     * Adapts an LDAP result code to a resource exception.
+     *
+     * @param error
+     *            The LDAP error that should be adapted.
+     * @return The equivalent resource exception.
      */
-    private void cacheAllLDAPAttributes() {
-        allLDAPAttributes = new LinkedHashSet<String>(mappers.size());
-        for (final AttributeMapper mapper : mappers) {
-            allLDAPAttributes.addAll(mapper.getAllLDAPAttributes());
+    private ResourceException adaptErrorResult(final ErrorResultException error) {
+        int resourceResultCode;
+        try {
+            throw error;
+        } catch (AssertionFailureException e) {
+            resourceResultCode = ResourceException.VERSION_MISMATCH;
+        } catch (AuthenticationException e) {
+            resourceResultCode = 401;
+        } catch (AuthorizationException e) {
+            resourceResultCode = ResourceException.FORBIDDEN;
+        } catch (ConnectionException e) {
+            resourceResultCode = ResourceException.UNAVAILABLE;
+        } catch (EntryNotFoundException e) {
+            resourceResultCode = ResourceException.NOT_FOUND;
+        } catch (MultipleEntriesFoundException e) {
+            resourceResultCode = ResourceException.INTERNAL_ERROR;
+        } catch (TimeoutResultException e) {
+            resourceResultCode = 408;
+        } catch (ErrorResultException e) {
+            resourceResultCode = ResourceException.INTERNAL_ERROR;
         }
+        return ResourceException.getException(resourceResultCode, null, error.getMessage(), error);
     }
 
     /**
      * Determines the set of LDAP attributes to request in an LDAP read (search,
      * post-read), based on the provided set of JSON pointers.
-     * 
+     *
      * @param requestedAttributes
      *            The set of resource attributes to be read.
      * @return The set of LDAP attributes associated with the resource
      *         attributes.
      */
-    private Set<String> getRequestedLDAPAttributes(final Set<JsonPointer> requestedAttributes) {
+    private Collection<String> getRequestedLDAPAttributes(final Set<JsonPointer> requestedAttributes) {
         if (requestedAttributes.isEmpty()) {
             // Full read.
-            return allLDAPAttributes;
+            return attributeMapper.getAllLDAPAttributes();
         } else {
             // Partial read.
             final Set<String> requestedLDAPAttributes =
                     new LinkedHashSet<String>(requestedAttributes.size());
             for (final JsonPointer requestedAttribute : requestedAttributes) {
-                for (final AttributeMapper mapper : mappers) {
-                    requestedLDAPAttributes.addAll(mapper.getLDAPAttributesFor(requestedAttribute));
-                }
+                attributeMapper.getLDAPAttributesFor(requestedAttribute, requestedLDAPAttributes);
             }
             return requestedLDAPAttributes;
         }
     }
-
-    private String getRevisionFromEntry(final SearchResultEntry entry) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    private void mapEntryToJson(final Context c, final Set<JsonPointer> requestedAttributes,
-            final Entry result, final ResultHandler<JsonValue> h) {
-        // TODO Auto-generated method stub
-
-    }
 }
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java
new file mode 100644
index 0000000..676562d
--- /dev/null
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java
@@ -0,0 +1,89 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyrighted [year] [name of copyright owner]".
+ *
+ * Copyright 2012 ForgeRock AS. All rights reserved.
+ */
+
+package org.forgerock.opendj.rest2ldap;
+
+import static org.forgerock.opendj.ldap.schema.CoreSchema.getBooleanSyntax;
+import static org.forgerock.opendj.ldap.schema.CoreSchema.getGeneralizedTimeSyntax;
+import static org.forgerock.opendj.ldap.schema.CoreSchema.getIntegerSyntax;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.forgerock.opendj.ldap.Attribute;
+import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.Function;
+import org.forgerock.opendj.ldap.Functions;
+import org.forgerock.opendj.ldap.schema.Syntax;
+
+/**
+ *
+ */
+public final class Utils {
+
+    private static final Function<ByteString, Object, Syntax> BYTESTRING_TO_JSON =
+            new Function<ByteString, Object, Syntax>() {
+                public Object apply(final ByteString value, final Syntax syntax) {
+                    if (syntax.equals(getBooleanSyntax())) {
+                        return Functions.byteStringToBoolean().apply(value, null);
+                    } else if (syntax.equals(getIntegerSyntax())) {
+                        return Functions.byteStringToLong().apply(value, null);
+                    } else if (syntax.equals(getGeneralizedTimeSyntax())) {
+                        return DatatypeConverter.printDateTime(Functions
+                                .byteStringToGeneralizedTime().apply(value, null).toCalendar());
+                    } else if (syntax.isHumanReadable()) {
+                        return Functions.byteStringToString().apply(value, null);
+                    } else {
+                        // Base 64 encoded binary.
+                        return DatatypeConverter.printBase64Binary(value.toByteArray());
+                    }
+                }
+            };
+
+    // Prevent instantiation.
+    private Utils() {
+        // No implementation required.
+    }
+
+    private static <T> List<T> asList(Collection<T> c) {
+        if (c instanceof List) {
+            return (List<T>) c;
+        } else {
+            List<T> result = new ArrayList<T>(c.size());
+            result.addAll(c);
+            return result;
+        }
+    }
+
+    public static Object attributeToJson(Attribute a) {
+        Syntax syntax = a.getAttributeDescription().getAttributeType().getSyntax();
+        Function<ByteString, Object, Void> f = Functions.fixedFunction(BYTESTRING_TO_JSON, syntax);
+        boolean isSingleValued = a.getAttributeDescription().getAttributeType().isSingleValue();
+        return isSingleValued ? a.parse().as(f) : asList(a.parse().asSetOf(f));
+    }
+
+    public static String getAttributeName(Attribute a) {
+        return a.getAttributeDescription().withoutOption("binary").toString();
+    }
+
+    public static String toLowerCase(String s) {
+        return s != null ? s.toLowerCase(Locale.ENGLISH) : null;
+    }
+}

--
Gitblit v1.10.0