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.HashSet;
029 import java.util.Set;
030
031 import com.unboundid.ldap.sdk.Attribute;
032 import com.unboundid.ldap.sdk.Entry;
033 import com.unboundid.ldap.sdk.Modification;
034 import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition;
035 import com.unboundid.ldap.sdk.schema.Schema;
036 import com.unboundid.ldif.LDIFAddChangeRecord;
037 import com.unboundid.ldif.LDIFChangeRecord;
038 import com.unboundid.ldif.LDIFModifyChangeRecord;
039 import com.unboundid.util.Debug;
040 import com.unboundid.util.StaticUtils;
041 import com.unboundid.util.ThreadSafety;
042 import com.unboundid.util.ThreadSafetyLevel;
043
044
045
046 /**
047 * This class provides an implementation of an entry and LDIF change record
048 * transformation that will remove a specified set of attributes from entries
049 * or change records. Note that this transformation will not alter entry DNs,
050 * so if an attribute to exclude is included in an entry's DN, that value will
051 * still be visible in the DN even if it is removed from the set of attributes
052 * in the entry.
053 */
054 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
055 public final class ExcludeAttributeTransformation
056 implements EntryTransformation, LDIFChangeRecordTransformation
057 {
058 // The schema to use when processing.
059 private final Schema schema;
060
061 // The set of attributes to exclude from entries.
062 private final Set<String> attributes;
063
064
065
066 /**
067 * Creates a new exclude attribute transformation that will strip the
068 * specified attributes out of entries and change records.
069 *
070 * @param schema The scheme to use to identify alternate names that
071 * may be used to reference the attributes to exclude from
072 * entries. It may be {@code null} to use a default
073 * standard schema.
074 * @param attributes The names of the attributes to strip from entries and
075 * change records. It must not be {@code null} or empty.
076 */
077 public ExcludeAttributeTransformation(final Schema schema,
078 final String... attributes)
079 {
080 this(schema, StaticUtils.toList(attributes));
081 }
082
083
084
085 /**
086 * Creates a new exclude attribute transformation that will strip the
087 * specified attributes out of entries and change records.
088 *
089 * @param schema The scheme to use to identify alternate names that
090 * may be used to reference the attributes to exclude from
091 * entries. It may be {@code null} to use a default
092 * standard schema.
093 * @param attributes The names of the attributes to strip from entries and
094 * change records. It must not be {@code null} or empty.
095 */
096 public ExcludeAttributeTransformation(final Schema schema,
097 final Collection<String> attributes)
098 {
099 // If a schema was provided, then use it. Otherwise, use the default
100 // standard schema.
101 Schema s = schema;
102 if (s == null)
103 {
104 try
105 {
106 s = Schema.getDefaultStandardSchema();
107 }
108 catch (final Exception e)
109 {
110 // This should never happen.
111 Debug.debugException(e);
112 }
113 }
114 this.schema = s;
115
116
117 // Identify all of the names that may be used to reference the attributes
118 // to suppress.
119 final HashSet<String> attrNames = new HashSet<String>(3*attributes.size());
120 for (final String attrName : attributes)
121 {
122 final String baseName =
123 Attribute.getBaseName(StaticUtils.toLowerCase(attrName));
124 attrNames.add(baseName);
125
126 if (s != null)
127 {
128 final AttributeTypeDefinition at = s.getAttributeType(baseName);
129 if (at != null)
130 {
131 attrNames.add(StaticUtils.toLowerCase(at.getOID()));
132 for (final String name : at.getNames())
133 {
134 attrNames.add(StaticUtils.toLowerCase(name));
135 }
136 }
137 }
138 }
139 this.attributes = Collections.unmodifiableSet(attrNames);
140 }
141
142
143
144 /**
145 * {@inheritDoc}
146 */
147 public Entry transformEntry(final Entry e)
148 {
149 if (e == null)
150 {
151 return null;
152 }
153
154
155 // First, see if the entry has any of the target attributes. If not, we can
156 // just return the provided entry.
157 boolean hasAttributeToRemove = false;
158 final Collection<Attribute> originalAttributes = e.getAttributes();
159 for (final Attribute a : originalAttributes)
160 {
161 if (attributes.contains(StaticUtils.toLowerCase(a.getBaseName())))
162 {
163 hasAttributeToRemove = true;
164 break;
165 }
166 }
167
168 if (! hasAttributeToRemove)
169 {
170 return e;
171 }
172
173
174 // Create a copy of the entry with all appropriate attributes removed.
175 final ArrayList<Attribute> attributesToKeep =
176 new ArrayList<Attribute>(originalAttributes.size());
177 for (final Attribute a : originalAttributes)
178 {
179 if (! attributes.contains(StaticUtils.toLowerCase(a.getBaseName())))
180 {
181 attributesToKeep.add(a);
182 }
183 }
184
185 return new Entry(e.getDN(), schema, attributesToKeep);
186 }
187
188
189
190 /**
191 * {@inheritDoc}
192 */
193 public LDIFChangeRecord transformChangeRecord(final LDIFChangeRecord r)
194 {
195 if (r == null)
196 {
197 return null;
198 }
199
200
201 // If it's an add change record, then just use the same processing as for an
202 // entry, except we will suppress the entire change record if all of the
203 // attributes end up getting suppressed.
204 if (r instanceof LDIFAddChangeRecord)
205 {
206 final LDIFAddChangeRecord addRecord = (LDIFAddChangeRecord) r;
207 final Entry updatedEntry = transformEntry(addRecord.getEntryToAdd());
208 if (updatedEntry.getAttributes().isEmpty())
209 {
210 return null;
211 }
212
213 return new LDIFAddChangeRecord(updatedEntry, addRecord.getControls());
214 }
215
216
217 // If it's a modify change record, then suppress all modifications targeting
218 // any of the appropriate attributes. If there are no more modifications
219 // left, then suppress the entire change record.
220 if (r instanceof LDIFModifyChangeRecord)
221 {
222 final LDIFModifyChangeRecord modifyRecord = (LDIFModifyChangeRecord) r;
223
224 final Modification[] originalMods = modifyRecord.getModifications();
225 final ArrayList<Modification> modsToKeep =
226 new ArrayList<Modification>(originalMods.length);
227 for (final Modification m : originalMods)
228 {
229 final String attrName = StaticUtils.toLowerCase(
230 Attribute.getBaseName(m.getAttributeName()));
231 if (! attributes.contains(attrName))
232 {
233 modsToKeep.add(m);
234 }
235 }
236
237 if (modsToKeep.isEmpty())
238 {
239 return null;
240 }
241
242 return new LDIFModifyChangeRecord(modifyRecord.getDN(), modsToKeep,
243 modifyRecord.getControls());
244 }
245
246
247 // If it's some other type of change record (which should just be delete or
248 // modify DN), then don't do anything.
249 return r;
250 }
251
252
253
254 /**
255 * {@inheritDoc}
256 */
257 public Entry translate(final Entry original, final long firstLineNumber)
258 {
259 return transformEntry(original);
260 }
261
262
263
264 /**
265 * {@inheritDoc}
266 */
267 public LDIFChangeRecord translate(final LDIFChangeRecord original,
268 final long firstLineNumber)
269 {
270 return transformChangeRecord(original);
271 }
272
273
274
275 /**
276 * {@inheritDoc}
277 */
278 public Entry translateEntryToWrite(final Entry original)
279 {
280 return transformEntry(original);
281 }
282
283
284
285 /**
286 * {@inheritDoc}
287 */
288 public LDIFChangeRecord translateChangeRecordToWrite(
289 final LDIFChangeRecord original)
290 {
291 return transformChangeRecord(original);
292 }
293 }