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