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}