001/*
002 * Copyright 2016-2017 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2016-2017 UnboundID Corp.
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 = new HashMap<String,Attribute>(10);
158    for (final Attribute a : attributes)
159    {
160      final String baseName = StaticUtils.toLowerCase(a.getBaseName());
161      attrMap.put(baseName, a);
162
163      if (s != null)
164      {
165        final AttributeTypeDefinition at = s.getAttributeType(baseName);
166        if (at != null)
167        {
168          attrMap.put(StaticUtils.toLowerCase(at.getOID()),
169               new Attribute(at.getOID(), s, a.getValues()));
170          for (final String name : at.getNames())
171          {
172            final String lowerName = StaticUtils.toLowerCase(name);
173            if (! attrMap.containsKey(lowerName))
174            {
175              attrMap.put(lowerName, new Attribute(name, s, a.getValues()));
176            }
177          }
178        }
179      }
180    }
181    this.attributes = Collections.unmodifiableMap(attrMap);
182  }
183
184
185
186  /**
187   * {@inheritDoc}
188   */
189  public Entry transformEntry(final Entry e)
190  {
191    if (e == null)
192    {
193      return null;
194    }
195
196
197    // First, see if the entry has any of the target attributes.  If not, we can
198    // just return the provided entry.
199    boolean hasAttributeToReplace = false;
200    final Collection<Attribute> originalAttributes = e.getAttributes();
201    for (final Attribute a : originalAttributes)
202    {
203      if (attributes.containsKey(StaticUtils.toLowerCase(a.getBaseName())))
204      {
205        hasAttributeToReplace = true;
206        break;
207      }
208    }
209
210    if (! hasAttributeToReplace)
211    {
212      return e;
213    }
214
215
216    // Create a copy of the entry with all appropriate attributes replaced with
217    // the appropriate default versions.
218    final ArrayList<Attribute> newAttributes =
219         new ArrayList<Attribute>(originalAttributes.size());
220    for (final Attribute a : originalAttributes)
221    {
222      final Attribute replacement =
223           attributes.get(StaticUtils.toLowerCase(a.getBaseName()));
224      if (replacement == null)
225      {
226        newAttributes.add(a);
227      }
228      else
229      {
230        if (a.hasOptions())
231        {
232          newAttributes.add(new Attribute(a.getName(), schema,
233               replacement.getRawValues()));
234        }
235        else
236        {
237          newAttributes.add(replacement);
238        }
239      }
240    }
241
242    return new Entry(e.getDN(), schema, newAttributes);
243  }
244
245
246
247  /**
248   * {@inheritDoc}
249   */
250  public Entry translate(final Entry original, final long firstLineNumber)
251  {
252    return transformEntry(original);
253  }
254
255
256
257  /**
258   * {@inheritDoc}
259   */
260  public Entry translateEntryToWrite(final Entry original)
261  {
262    return transformEntry(original);
263  }
264}