001    /*
002     * Copyright 2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2016 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     */
021    package com.unboundid.ldap.sdk.transformations;
022    
023    
024    
025    import java.util.ArrayList;
026    import java.util.Collection;
027    import java.util.Collections;
028    import java.util.HashMap;
029    import java.util.Map;
030    
031    import com.unboundid.ldap.sdk.Attribute;
032    import com.unboundid.ldap.sdk.Entry;
033    import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition;
034    import com.unboundid.ldap.sdk.schema.Schema;
035    import com.unboundid.util.Debug;
036    import com.unboundid.util.StaticUtils;
037    import com.unboundid.util.ThreadSafety;
038    import 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)
049    public 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    }