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