/* * 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.LinkedList; 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.json.resource.ResultHandler; import org.forgerock.json.resource.ServerContext; import org.forgerock.json.resource.ResourceException; import org.forgerock.opendj.ldap.Attribute; import org.forgerock.opendj.ldap.Entry; /** * A collection of one or more attribute mappers whose content will be combined * into a single JSON array. */ public final class CompositeAttributeMapper implements AttributeMapper { private final List attributeMappers = new LinkedList(); /** * Creates a new composite attribute mapper. */ public CompositeAttributeMapper() { // No implementation required. } /** * Adds a subordinate attribute mapper to this composite attribute mapper. * * @param mapper * The subordinate attribute mapper. * @return This composite attribute mapper. */ public CompositeAttributeMapper addMapper(final AttributeMapper mapper) { attributeMappers.add(mapper); return this; } /** * {@inheritDoc} */ public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set ldapAttributes) { for (final AttributeMapper attribute : attributeMappers) { attribute.getLDAPAttributes(jsonAttribute, ldapAttributes); } } /** * {@inheritDoc} */ public void toJson(final ServerContext c, final Entry e, final ResultHandler> h) { final ResultHandler> resultAccumulater = new ResultHandler>() { private final AtomicInteger latch = new AtomicInteger(attributeMappers.size()); private final List> results = new ArrayList>( latch.get()); public void handleError(final ResourceException e) { // Ensure that handler is only invoked once. if (latch.getAndSet(0) > 0) { h.handleError(e); } } public void handleResult(final 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 (final AttributeMapper mapper : attributeMappers) { mapper.toJson(c, e, resultAccumulater); } } /** * {@inheritDoc} */ public void toLDAP(final ServerContext c, final JsonValue v, final ResultHandler> h) { // TODO Auto-generated method stub } /** * 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(final Map srcValue, final Map dstValue) { for (final Map.Entry record : srcValue.entrySet()) { final String key = record.getKey(); final 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. final List tmp = new ArrayList((List) existingValue); tmp.addAll((List) newValue); existingValue = tmp; } // Replace the existing value. dstValue.put(key, newValue); } } /** * 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(final List> srcValues, final Map dstValue) { for (final Map value : srcValues) { mergeJsonValue(value, dstValue); } } }