/* * 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 Copyright [year] [name of copyright owner]". * * Copyright 2012-2013 ForgeRock AS. */ package org.forgerock.opendj.rest2ldap; import static org.forgerock.opendj.rest2ldap.Utils.accumulate; import static org.forgerock.opendj.rest2ldap.Utils.transform; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; 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.json.resource.ResultHandler; import org.forgerock.opendj.ldap.Attribute; import org.forgerock.opendj.ldap.Entry; import org.forgerock.opendj.ldap.Filter; import org.forgerock.opendj.ldap.Function; /** * An attribute mapper which combines the results of a set of subordinate * attribute mappers into a single JSON object. */ 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} */ @Override public void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute, final Set ldapAttributes) { for (final AttributeMapper attribute : attributeMappers) { attribute.getLDAPAttributes(c, jsonAttribute, ldapAttributes); } } /** * {@inheritDoc} */ @Override public void getLDAPFilter(final Context c, final FilterType type, final JsonPointer jsonAttribute, final String operator, final Object valueAssertion, final ResultHandler h) { final ResultHandler handler = accumulate(attributeMappers.size(), transform( new Function, Filter, Void>() { @Override public Filter apply(final List value, final Void p) { // Remove unmapped filters and combine using logical-OR. final Iterator i = value.iterator(); while (i.hasNext()) { final Filter f = i.next(); if (f == null) { // No mapping so remove. i.remove(); } else if (f == c.getConfig().getFalseFilter()) { return c.getConfig().getFalseFilter(); } else if (f == c.getConfig().getTrueFilter()) { return c.getConfig().getTrueFilter(); } } switch (value.size()) { case 0: // No mappings found. return null; case 1: return value.get(0); default: return Filter.or(value); } } }, h)); for (final AttributeMapper subMapper : attributeMappers) { subMapper.getLDAPFilter(c, type, jsonAttribute, operator, valueAssertion, handler); } } /** * {@inheritDoc} */ @Override public void toJSON(final Context c, final Entry e, final ResultHandler> h) { final ResultHandler> handler = accumulate(attributeMappers.size(), transform( new Function>, Map, Void>() { @Override public Map apply(final List> value, final Void p) { switch (value.size()) { case 0: return Collections. emptyMap(); case 1: return value.get(0) != null ? value.get(0) : Collections . emptyMap(); default: return mergeJsonValues(value, new LinkedHashMap()); } } }, h)); for (final AttributeMapper mapper : attributeMappers) { mapper.toJSON(c, e, handler); } } /** * {@inheritDoc} */ @Override public void toLDAP(final Context c, final JsonValue v, final ResultHandler> h) { // TODO Auto-generated method stub } @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); } } private Map mergeJsonValues(final List> srcValues, final Map dstValue) { for (final Map value : srcValues) { if (value != null) { mergeJsonValue(value, dstValue); } } return dstValue; } }