All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.unboundid.ldap.sdk.transformations.ReplaceWithCounterTransformation Maven / Gradle / Ivy

/*
 * Copyright 2016-2019 Ping Identity Corporation
 * All Rights Reserved.
 */
/*
 * Copyright (C) 2016-2019 Ping Identity Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see .
 */
package com.unboundid.ldap.sdk.transformations;



import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;

import com.unboundid.ldap.sdk.Attribute;
import com.unboundid.ldap.sdk.DN;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.RDN;
import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition;
import com.unboundid.ldap.sdk.schema.Schema;
import com.unboundid.util.Debug;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;



/**
 * This class provides an implementation of an entry transformation that will
 * replace the existing set of values for a given attribute with a value that
 * contains a numeric counter (optionally along with additional static text)
 * that increments for each entry that contains the target attribute.  The
 * resulting attribute will only have a single value, even if it originally had
 * multiple values.
 */
@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
public final class ReplaceWithCounterTransformation
       implements EntryTransformation
{
  // The counter to use to obtain the values.
  private final AtomicLong counter;

  // Indicates whether to update the DN of the target entry if its RDN includes
  // the target attribute.
  private final boolean replaceInRDN;

  // The amount by which to increment the counter for each entry.
  private final long incrementAmount;

  // The schema to use when processing.
  private final Schema schema;

  // The names that may be used to reference the attribute to replace.
  private final Set names;

  // The static text that will appear after the number in generated values.
  private final String afterText;

  // The static text that will appear before the number in generated values.
  private final String beforeText;



  /**
   * Creates a new replace with counter transformation using the provided
   * information.
   *
   * @param  schema           The schema to use to identify alternate names for
   *                          the target attribute.  This may be {@code null} if
   *                          a default standard schema should be used.
   * @param  attributeName    The name of the attribute that should be replaced
   *                          with the generated value.
   * @param  initialValue     The initial value to use for the counter.
   * @param  incrementAmount  The amount by which the counter should be
   *                          incremented for each entry containing the target
   *                          attribute.
   * @param  beforeText       An optional string that should appear before the
   *                          counter in generated values.  It may be
   *                          {@code null} if no before text should be used.
   * @param  afterText        An optional string that should appear after the
   *                          counter in generated values.  It may be
   *                          {@code null} if no after text should be used.
   * @param  replaceInRDN     Indicates whether to update the DN of the target
   *                          entry if its RDN includes the target attribute.
   */
  public ReplaceWithCounterTransformation(final Schema schema,
                                          final String attributeName,
                                          final long initialValue,
                                          final long incrementAmount,
                                          final String beforeText,
                                          final String afterText,
                                          final boolean replaceInRDN)
  {
    this.incrementAmount = incrementAmount;
    this.replaceInRDN = replaceInRDN;

    counter = new AtomicLong(initialValue);

    if (beforeText == null)
    {
      this.beforeText = "";
    }
    else
    {
      this.beforeText = beforeText;
    }

    if (afterText == null)
    {
      this.afterText = "";
    }
    else
    {
      this.afterText = afterText;
    }


    // If a schema was provided, then use it.  Otherwise, use the default
    // standard schema.
    Schema s = schema;
    if (s == null)
    {
      try
      {
        s = Schema.getDefaultStandardSchema();
      }
      catch (final Exception e)
      {
        // This should never happen.
        Debug.debugException(e);
      }
    }
    this.schema = s;


    // Get all names that can be used to reference the target attribute.
    final HashSet nameSet =
         new HashSet<>(StaticUtils.computeMapCapacity(5));
    final String baseName =
         StaticUtils.toLowerCase(Attribute.getBaseName(attributeName));
    nameSet.add(baseName);
    if (s != null)
    {
      final AttributeTypeDefinition at = s.getAttributeType(baseName);
      if (at != null)
      {
        nameSet.add(StaticUtils.toLowerCase(at.getOID()));
        for (final String name : at.getNames())
        {
          nameSet.add(StaticUtils.toLowerCase(name));
        }
      }
    }
    names = Collections.unmodifiableSet(nameSet);
  }



  /**
   * {@inheritDoc}
   */
  @Override()
  public Entry transformEntry(final Entry e)
  {
    if (e == null)
    {
      return null;
    }


    // See if the DN contains the target attribute in the RDN.  If so, then
    // replace its value.
    String dn = e.getDN();
    String newValue = null;
    if (replaceInRDN)
    {
      try
      {
        final DN parsedDN = new DN(dn);
        final RDN rdn = parsedDN.getRDN();
        for (final String name : names)
        {
          if (rdn.hasAttribute(name))
          {
            newValue =
                 beforeText + counter.getAndAdd(incrementAmount) + afterText;
            break;
          }
        }

        if (newValue != null)
        {
          if (rdn.isMultiValued())
          {
            final String[] attrNames = rdn.getAttributeNames();
            final byte[][] originalValues = rdn.getByteArrayAttributeValues();
            final byte[][] newValues = new byte[originalValues.length][];
            for (int i=0; i < attrNames.length; i++)
            {
              if (names.contains(StaticUtils.toLowerCase(attrNames[i])))
              {
                newValues[i] = StaticUtils.getBytes(newValue);
              }
              else
              {
                newValues[i] = originalValues[i];
              }
            }
            dn = new DN(new RDN(attrNames, newValues, schema),
                 parsedDN.getParent()).toString();
          }
          else
          {
            dn = new DN(new RDN(rdn.getAttributeNames()[0], newValue, schema),
                 parsedDN.getParent()).toString();
          }
        }
      }
      catch (final Exception ex)
      {
        Debug.debugException(ex);
      }
    }


    // If the RDN doesn't contain the target attribute, then see if the entry
    // contains the target attribute.  If not, then just return the provided
    // entry.
    if (newValue == null)
    {
      boolean hasAttribute = false;
      for (final String name : names)
      {
        if (e.hasAttribute(name))
        {
          hasAttribute = true;
          break;
        }
      }

      if (! hasAttribute)
      {
        return e;
      }
    }


    // If we haven't computed the new value for this entry, then do so now.
    if (newValue == null)
    {
      newValue = beforeText + counter.getAndAdd(incrementAmount) + afterText;
    }


    // Iterate through the attributes in the entry and make the appropriate
    // updates.
    final Collection originalAttributes = e.getAttributes();
    final ArrayList updatedAttributes =
         new ArrayList<>(originalAttributes.size());
    for (final Attribute a : originalAttributes)
    {
      if (names.contains(StaticUtils.toLowerCase(a.getBaseName())))
      {
        updatedAttributes.add(new Attribute(a.getName(), schema, newValue));
      }
      else
      {
        updatedAttributes.add(a);
      }
    }


    // Return the updated entry.
    return new Entry(dn, schema, updatedAttributes);
  }



  /**
   * {@inheritDoc}
   */
  @Override()
  public Entry translate(final Entry original, final long firstLineNumber)
  {
    return transformEntry(original);
  }



  /**
   * {@inheritDoc}
   */
  @Override()
  public Entry translateEntryToWrite(final Entry original)
  {
    return transformEntry(original);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy