001/* 002 * Copyright 2016-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2016-2020 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2016-2020 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk.transformations; 037 038 039 040import java.util.ArrayList; 041import java.util.Arrays; 042import java.util.Collection; 043import java.util.List; 044 045import com.unboundid.ldap.sdk.Attribute; 046import com.unboundid.ldap.sdk.DN; 047import com.unboundid.ldap.sdk.Entry; 048import com.unboundid.ldap.sdk.Modification; 049import com.unboundid.ldap.sdk.RDN; 050import com.unboundid.ldif.LDIFAddChangeRecord; 051import com.unboundid.ldif.LDIFChangeRecord; 052import com.unboundid.ldif.LDIFDeleteChangeRecord; 053import com.unboundid.ldif.LDIFModifyChangeRecord; 054import com.unboundid.ldif.LDIFModifyDNChangeRecord; 055import com.unboundid.util.ThreadSafety; 056import com.unboundid.util.ThreadSafetyLevel; 057 058 059 060/** 061 * This class provides an implementation of an entry and LDIF change record 062 * transformation that will alter DNs at or below a specified base DN to replace 063 * that base DN with a different base DN. This replacement will be applied to 064 * the DNs of entries that are transformed, as well as in any attribute values 065 * that represent DNs at or below the specified base DN. 066 */ 067@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 068public final class MoveSubtreeTransformation 069 implements EntryTransformation, LDIFChangeRecordTransformation 070{ 071 // The source base DN to be replaced. 072 private final DN sourceDN; 073 074 // A list of the RDNs in the target base DN. 075 private final List<RDN> targetRDNs; 076 077 078 079 /** 080 * Creates a new move subtree transformation with the provided information. 081 * 082 * @param sourceDN The source base DN to be replaced with the target base 083 * DN. It must not be {@code null}. 084 * @param targetDN The target base DN to use to replace the source base DN. 085 * It must not be {@code null}. 086 */ 087 public MoveSubtreeTransformation(final DN sourceDN, final DN targetDN) 088 { 089 this.sourceDN = sourceDN; 090 091 targetRDNs = Arrays.asList(targetDN.getRDNs()); 092 } 093 094 095 096 /** 097 * {@inheritDoc} 098 */ 099 @Override() 100 public Entry transformEntry(final Entry e) 101 { 102 if (e == null) 103 { 104 return null; 105 } 106 107 108 // Iterate through the attributes in the entry and make any appropriate DN 109 // replacements 110 final Collection<Attribute> originalAttributes = e.getAttributes(); 111 final ArrayList<Attribute> newAttributes = 112 new ArrayList<>(originalAttributes.size()); 113 for (final Attribute a : originalAttributes) 114 { 115 final String[] originalValues = a.getValues(); 116 final String[] newValues = new String[originalValues.length]; 117 for (int i=0; i < originalValues.length; i++) 118 { 119 newValues[i] = processString(originalValues[i]); 120 } 121 122 newAttributes.add(new Attribute(a.getName(), newValues)); 123 } 124 125 return new Entry(processString(e.getDN()), newAttributes); 126 } 127 128 129 130 /** 131 * {@inheritDoc} 132 */ 133 @Override() 134 public LDIFChangeRecord transformChangeRecord(final LDIFChangeRecord r) 135 { 136 if (r == null) 137 { 138 return null; 139 } 140 141 142 if (r instanceof LDIFAddChangeRecord) 143 { 144 // Just use the same processing as for an entry. 145 final LDIFAddChangeRecord addRecord = (LDIFAddChangeRecord) r; 146 return new LDIFAddChangeRecord(transformEntry(addRecord.getEntryToAdd()), 147 addRecord.getControls()); 148 } 149 if (r instanceof LDIFDeleteChangeRecord) 150 { 151 return new LDIFDeleteChangeRecord(processString(r.getDN()), 152 r.getControls()); 153 } 154 else if (r instanceof LDIFModifyChangeRecord) 155 { 156 final LDIFModifyChangeRecord modRecord = (LDIFModifyChangeRecord) r; 157 final Modification[] originalMods = modRecord.getModifications(); 158 final Modification[] newMods = new Modification[originalMods.length]; 159 for (int i=0; i < originalMods.length; i++) 160 { 161 final Modification m = originalMods[i]; 162 if (m.hasValue()) 163 { 164 final String[] originalValues = m.getValues(); 165 final String[] newValues = new String[originalValues.length]; 166 for (int j=0; j < originalValues.length; j++) 167 { 168 newValues[j] = processString(originalValues[j]); 169 } 170 newMods[i] = new Modification(m.getModificationType(), 171 m.getAttributeName(), newValues); 172 } 173 else 174 { 175 newMods[i] = originalMods[i]; 176 } 177 } 178 179 return new LDIFModifyChangeRecord(processString(modRecord.getDN()), 180 newMods, modRecord.getControls()); 181 } 182 else if (r instanceof LDIFModifyDNChangeRecord) 183 { 184 final LDIFModifyDNChangeRecord modDNRecord = (LDIFModifyDNChangeRecord) r; 185 return new LDIFModifyDNChangeRecord(processString(modDNRecord.getDN()), 186 modDNRecord.getNewRDN(), modDNRecord.deleteOldRDN(), 187 processString(modDNRecord.getNewSuperiorDN()), 188 modDNRecord.getControls()); 189 } 190 else 191 { 192 // This should never happen. 193 return r; 194 } 195 } 196 197 198 199 /** 200 * Identifies whether the provided string represents a DN that is at or below 201 * the specified source base DN. If so, then it will be updated to replace 202 * the old base DN with the new base DN. Otherwise, the original string will 203 * be returned. 204 * 205 * @param s The string to process. 206 * 207 * @return A new string if the provided value was a valid DN at or below the 208 * source DN, or the original string if it was not a valid DN or was 209 * not below the source DN. 210 */ 211 String processString(final String s) 212 { 213 if (s == null) 214 { 215 return null; 216 } 217 218 try 219 { 220 final DN dn = new DN(s); 221 if (! dn.isDescendantOf(sourceDN, true)) 222 { 223 return s; 224 } 225 226 final RDN[] originalRDNs = dn.getRDNs(); 227 final RDN[] sourceRDNs = sourceDN.getRDNs(); 228 final ArrayList<RDN> newRDNs = new ArrayList<>(2*originalRDNs.length); 229 final int numComponentsToKeep = originalRDNs.length - sourceRDNs.length; 230 for (int i=0; i < numComponentsToKeep; i++) 231 { 232 newRDNs.add(originalRDNs[i]); 233 } 234 235 newRDNs.addAll(targetRDNs); 236 return new DN(newRDNs).toString(); 237 } 238 catch (final Exception e) 239 { 240 // This is fine. The value isn't a DN. 241 return s; 242 } 243 } 244 245 246 247 /** 248 * {@inheritDoc} 249 */ 250 @Override() 251 public Entry translate(final Entry original, final long firstLineNumber) 252 { 253 return transformEntry(original); 254 } 255 256 257 258 /** 259 * {@inheritDoc} 260 */ 261 @Override() 262 public LDIFChangeRecord translate(final LDIFChangeRecord original, 263 final long firstLineNumber) 264 { 265 return transformChangeRecord(original); 266 } 267 268 269 270 /** 271 * {@inheritDoc} 272 */ 273 @Override() 274 public Entry translateEntryToWrite(final Entry original) 275 { 276 return transformEntry(original); 277 } 278 279 280 281 /** 282 * {@inheritDoc} 283 */ 284 @Override() 285 public LDIFChangeRecord translateChangeRecordToWrite( 286 final LDIFChangeRecord original) 287 { 288 return transformChangeRecord(original); 289 } 290}