/* * 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 allLDAPAttributes; private final List attributeMappers; /** * Creates a new composite attribute mapper. * * @param attributeMappers * The list of attribute mappers. */ public CompositeAttributeMapper(final List attributeMappers) { this.attributeMappers = new ArrayList(attributeMappers); Set tmp = new LinkedHashSet(attributeMappers.size()); for (final AttributeMapper mapper : attributeMappers) { tmp.addAll(mapper.getAllLDAPAttributes()); } allLDAPAttributes = Collections.unmodifiableSet(tmp); } /** * {@inheritDoc} */ public Set getAllLDAPAttributes() { return allLDAPAttributes; } /** * {@inheritDoc} */ public void getLDAPAttributesFor(JsonPointer resourceAttribute, Set ldapAttributes) { for (AttributeMapper attribute : attributeMappers) { attribute.getLDAPAttributesFor(resourceAttribute, ldapAttributes); } } /** * {@inheritDoc} */ public void toJson(Context c, Entry e, final ResultHandler> h) { ResultHandler> resultAccumulater = new ResultHandler>() { private final AtomicInteger latch = new AtomicInteger(attributeMappers.size()); private final List> results = new ArrayList>(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 result) { synchronized (this) { results.add(result); } if (latch.decrementAndGet() == 0) { final Map mergeResult; switch (results.size()) { case 0: mergeResult = Collections. emptyMap(); break; case 1: mergeResult = results.get(0); break; default: mergeResult = new LinkedHashMap(); 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> 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> srcValues, Map dstValue) { for (Map 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 srcValue, Map dstValue) { for (Map.Entry 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((Map) existingValue); mergeJsonValue((Map) newValue, (Map) existingValue); } else if (existingValue instanceof List && newValue instanceof List) { // Merge two lists- create a new List, in case the existing one // is unmodifiable. List tmp = new ArrayList((List) existingValue); tmp.addAll((List) newValue); existingValue = tmp; } // Replace the existing value. dstValue.put(key, newValue); } } }