001/*
002 * Copyright 2016-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2016-2019 Ping Identity Corporation
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.sdk.transformations;
022
023
024
025import java.util.ArrayList;
026import java.util.Collection;
027import java.util.Collections;
028import java.util.HashMap;
029import java.util.Map;
030
031import com.unboundid.ldap.sdk.Attribute;
032import com.unboundid.ldap.sdk.Entry;
033import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition;
034import com.unboundid.ldap.sdk.schema.Schema;
035import com.unboundid.util.Debug;
036import com.unboundid.util.StaticUtils;
037import com.unboundid.util.ThreadSafety;
038import com.unboundid.util.ThreadSafetyLevel;
039
040
041
042/**
043 * This class provides an implementation of an entry transformation that can be
044 * used to replace existing attributes in entries with a default set of values.
045 * The default attributes will not be added to entries that do not have existing
046 * values for the target attributes.
047 */
048@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
049public final class ReplaceAttributeTransformation
050       implements EntryTransformation
051{
052  // The schema to use when processing.
053  private final Schema schema;
054
055  // The set of attributes to replace in entries.
056  private final Map<String,Attribute> attributes;
057
058
059
060  /**
061   * Creates a new replace attribute transformation that will replace existing
062   * values of the specified attribute with the provided set of default values.
063   *
064   * @param  schema         The schema to use to identify alternate names that
065   *                        may be used to reference the attributes to replace.
066   *                        It may be {@code null} to use a default standard
067   *                        schema.
068   * @param  attributeName  The name of the attribute for which to replace
069   *                        existing values.  It must not be {@code null}.
070   * @param  newValues      The new values to use in place of the existing
071   *                        values for the specified attribute.
072   */
073  public ReplaceAttributeTransformation(final Schema schema,
074                                        final String attributeName,
075                                        final String... newValues)
076  {
077    this(schema, new Attribute(attributeName, schema, newValues));
078  }
079
080
081
082  /**
083   * Creates a new replace attribute transformation that will replace existing
084   * values of the specified attribute with the provided set of default values.
085   *
086   * @param  schema         The schema to use to identify alternate names that
087   *                        may be used to reference the attributes to replace.
088   *                        It may be {@code null} to use a default standard
089   *                        schema.
090   * @param  attributeName  The name of the attribute for which to replace
091   *                        existing values.  It must not be {@code null}.
092   * @param  newValues      The new values to use in place of the existing
093   *                        values for the specified attribute.
094   */
095  public ReplaceAttributeTransformation(final Schema schema,
096                                        final String attributeName,
097                                        final Collection<String> newValues)
098  {
099    this(schema, new Attribute(attributeName, schema, newValues));
100  }
101
102
103
104  /**
105   * Creates a new replace attribute transformation that will replace existing
106   * copies of the specified attributes with the provided versions.
107   *
108   * @param  schema      The schema to use to identify alternate names that may
109   *                     be used to reference the attributes to replace.  It may
110   *                     be {@code null} to use a default standard schema.
111   * @param  attributes  The attributes to be used in place of existing
112   *                     attributes of the same type.  It must not be
113   *                     {@code null} or empty.
114   */
115  public ReplaceAttributeTransformation(final Schema schema,
116                                        final Attribute... attributes)
117  {
118    this(schema, StaticUtils.toList(attributes));
119  }
120
121
122
123  /**
124   * Creates a new replace attribute transformation that will replace existing
125   * copies of the specified attributes with the provided versions.
126   *
127   * @param  schema      The schema to use to identify alternate names that may
128   *                     be used to reference the attributes to replace.  It may
129   *                     be {@code null} to use a default standard schema.
130   * @param  attributes  The attributes to be used in place of existing
131   *                     attributes of the same type.  It must not be
132   *                     {@code null} or empty.
133   */
134  public ReplaceAttributeTransformation(final Schema schema,
135                                        final Collection<Attribute> attributes)
136  {
137    // If a schema was provided, then use it.  Otherwise, use the default
138    // standard schema.
139    Schema s = schema;
140    if (s == null)
141    {
142      try
143      {
144        s = Schema.getDefaultStandardSchema();
145      }
146      catch (final Exception e)
147      {
148        // This should never happen.
149        Debug.debugException(e);
150      }
151    }
152    this.schema = s;
153
154
155    // Identify all of the names that may be used to reference the attributes
156    // to replace.
157    final HashMap<String,Attribute> attrMap =
158         new HashMap<>(StaticUtils.computeMapCapacity(10));
159    for (final Attribute a : attributes)
160    {
161      final String baseName = StaticUtils.toLowerCase(a.getBaseName());
162      attrMap.put(baseName, a);
163
164      if (s != null)
165      {
166        final AttributeTypeDefinition at = s.getAttributeType(baseName);
167        if (at != null)
168        {
169          attrMap.put(StaticUtils.toLowerCase(at.getOID()),
170               new Attribute(at.getOID(), s, a.getValues()));
171          for (final String name : at.getNames())
172          {
173            final String lowerName = StaticUtils.toLowerCase(name);
174            if (! attrMap.containsKey(lowerName))
175            {
176              attrMap.put(lowerName, new Attribute(name, s, a.getValues()));
177            }
178          }
179        }
180      }
181    }
182    this.attributes = Collections.unmodifiableMap(attrMap);
183  }
184
185
186
187  /**
188   * {@inheritDoc}
189   */
190  @Override()
191  public Entry transformEntry(final Entry e)
192  {
193    if (e == null)
194    {
195      return null;
196    }
197
198
199    // First, see if the entry has any of the target attributes.  If not, we can
200    // just return the provided entry.
201    boolean hasAttributeToReplace = false;
202    final Collection<Attribute> originalAttributes = e.getAttributes();
203    for (final Attribute a : originalAttributes)
204    {
205      if (attributes.containsKey(StaticUtils.toLowerCase(a.getBaseName())))
206      {
207        hasAttributeToReplace = true;
208        break;
209      }
210    }
211
212    if (! hasAttributeToReplace)
213    {
214      return e;
215    }
216
217
218    // Create a copy of the entry with all appropriate attributes replaced with
219    // the appropriate default versions.
220    final ArrayList<Attribute> newAttributes =
221         new ArrayList<>(originalAttributes.size());
222    for (final Attribute a : originalAttributes)
223    {
224      final Attribute replacement =
225           attributes.get(StaticUtils.toLowerCase(a.getBaseName()));
226      if (replacement == null)
227      {
228        newAttributes.add(a);
229      }
230      else
231      {
232        if (a.hasOptions())
233        {
234          newAttributes.add(new Attribute(a.getName(), schema,
235               replacement.getRawValues()));
236        }
237        else
238        {
239          newAttributes.add(replacement);
240        }
241      }
242    }
243
244    return new Entry(e.getDN(), schema, newAttributes);
245  }
246
247
248
249  /**
250   * {@inheritDoc}
251   */
252  @Override()
253  public Entry translate(final Entry original, final long firstLineNumber)
254  {
255    return transformEntry(original);
256  }
257
258
259
260  /**
261   * {@inheritDoc}
262   */
263  @Override()
264  public Entry translateEntryToWrite(final Entry original)
265  {
266    return transformEntry(original);
267  }
268}