package com.hwlcn.ldap.ldap.sdk; import com.hwlcn.core.annotation.Mutable; import com.hwlcn.core.annotation.NotExtensible; import com.hwlcn.core.annotation.ThreadSafety; import com.hwlcn.ldap.asn1.ASN1OctetString; import com.hwlcn.ldap.ldap.matchingrules.MatchingRule; import com.hwlcn.ldap.ldap.sdk.schema.AttributeTypeDefinition; import com.hwlcn.ldap.ldap.sdk.schema.Schema; import com.hwlcn.ldap.ldif.LDIFException; import com.hwlcn.ldap.ldif.LDIFReader; import com.hwlcn.ldap.ldif.LDIFRecord; import com.hwlcn.ldap.ldif.LDIFWriter; import com.hwlcn.ldap.util.ByteStringBuffer; import com.hwlcn.ldap.util.ThreadSafetyLevel; import java.math.BigInteger; import java.util.*; import static com.hwlcn.ldap.ldap.sdk.LDAPMessages.*; import static com.hwlcn.ldap.util.Debug.debugException; import static com.hwlcn.ldap.util.StaticUtils.*; import static com.hwlcn.ldap.util.Validator.*; /** * This class provides a data structure for holding information about an LDAP * entry. An entry contains a distinguished name (DN) and a set of attributes. * An entry can be created from these components, and it can also be created * from its LDIF representation as described in * <A HREF="http://www.ietf.org/rfc/rfc2849.txt">RFC 2849</A>. For example: * <BR><BR> * <PRE> * Entry entry = new Entry( * "dn: dc=example,dc=com", * "objectClass: top", * "objectClass: domain", * "dc: example"); * </PRE> * <BR><BR> * This class also provides methods for retrieving the LDIF representation of * an entry, either as a single string or as an array of strings that make up * the LDIF lines. * <BR><BR> * The {@link com.hwlcn.ldap.ldap.sdk.Entry#diff} method may be used to obtain the set of differences * between two entries, and to retrieve a list of {@link Modification} objects * that can be used to modify one entry so that it contains the same set of * data as another. The {@link com.hwlcn.ldap.ldap.sdk.Entry#applyModifications} method may be used to * apply a set of modifications to an entry. * <BR><BR> * Entry objects are mutable, and the DN, set of attributes, and individual * attribute values can be altered. */ @Mutable() @NotExtensible() @ThreadSafety(level = ThreadSafetyLevel.NOT_THREADSAFE) public class Entry implements LDIFRecord { private static final long serialVersionUID = -4438809025903729197L; private volatile DN parsedDN; private final LinkedHashMap<String, Attribute> attributes; private final Schema schema; private String dn; public Entry(final String dn) { this(dn, (Schema) null); } public Entry(final String dn, final Schema schema) { ensureNotNull(dn); this.dn = dn; this.schema = schema; attributes = new LinkedHashMap<String, Attribute>(); } public Entry(final DN dn) { this(dn, (Schema) null); } public Entry(final DN dn, final Schema schema) { ensureNotNull(dn); parsedDN = dn; this.dn = parsedDN.toString(); this.schema = schema; attributes = new LinkedHashMap<String, Attribute>(); } public Entry(final String dn, final Attribute... attributes) { this(dn, null, attributes); } public Entry(final String dn, final Schema schema, final Attribute... attributes) { ensureNotNull(dn, attributes); this.dn = dn; this.schema = schema; this.attributes = new LinkedHashMap<String, Attribute>(attributes.length); for (final Attribute a : attributes) { final String name = toLowerCase(a.getName()); final Attribute attr = this.attributes.get(name); if (attr == null) { this.attributes.put(name, a); } else { this.attributes.put(name, Attribute.mergeAttributes(attr, a)); } } } public Entry(final DN dn, final Attribute... attributes) { this(dn, null, attributes); } public Entry(final DN dn, final Schema schema, final Attribute... attributes) { ensureNotNull(dn, attributes); parsedDN = dn; this.dn = parsedDN.toString(); this.schema = schema; this.attributes = new LinkedHashMap<String, Attribute>(attributes.length); for (final Attribute a : attributes) { final String name = toLowerCase(a.getName()); final Attribute attr = this.attributes.get(name); if (attr == null) { this.attributes.put(name, a); } else { this.attributes.put(name, Attribute.mergeAttributes(attr, a)); } } } public Entry(final String dn, final Collection<Attribute> attributes) { this(dn, null, attributes); } public Entry(final String dn, final Schema schema, final Collection<Attribute> attributes) { ensureNotNull(dn, attributes); this.dn = dn; this.schema = schema; this.attributes = new LinkedHashMap<String, Attribute>(attributes.size()); for (final Attribute a : attributes) { final String name = toLowerCase(a.getName()); final Attribute attr = this.attributes.get(name); if (attr == null) { this.attributes.put(name, a); } else { this.attributes.put(name, Attribute.mergeAttributes(attr, a)); } } } public Entry(final DN dn, final Collection<Attribute> attributes) { this(dn, null, attributes); } public Entry(final DN dn, final Schema schema, final Collection<Attribute> attributes) { ensureNotNull(dn, attributes); parsedDN = dn; this.dn = parsedDN.toString(); this.schema = schema; this.attributes = new LinkedHashMap<String, Attribute>(attributes.size()); for (final Attribute a : attributes) { final String name = toLowerCase(a.getName()); final Attribute attr = this.attributes.get(name); if (attr == null) { this.attributes.put(name, a); } else { this.attributes.put(name, Attribute.mergeAttributes(attr, a)); } } } public Entry(final String... entryLines) throws LDIFException { this(null, entryLines); } public Entry(final Schema schema, final String... entryLines) throws LDIFException { final Entry e = LDIFReader.decodeEntry(entryLines); this.schema = schema; dn = e.dn; parsedDN = e.parsedDN; attributes = e.attributes; } public final String getDN() { return dn; } public void setDN(final String dn) { ensureNotNull(dn); this.dn = dn; parsedDN = null; } public void setDN(final DN dn) { ensureNotNull(dn); parsedDN = dn; this.dn = parsedDN.toString(); } public final DN getParsedDN() throws LDAPException { if (parsedDN == null) { parsedDN = new DN(dn, schema); } return parsedDN; } public final RDN getRDN() throws LDAPException { return getParsedDN().getRDN(); } public final DN getParentDN() throws LDAPException { if (parsedDN == null) { parsedDN = new DN(dn, schema); } return parsedDN.getParent(); } public final String getParentDNString() throws LDAPException { if (parsedDN == null) { parsedDN = new DN(dn, schema); } final DN parentDN = parsedDN.getParent(); if (parentDN == null) { return null; } else { return parentDN.toString(); } } protected Schema getSchema() { return schema; } public final boolean hasAttribute(final String attributeName) { return hasAttribute(attributeName, schema); } public final boolean hasAttribute(final String attributeName, final Schema schema) { ensureNotNull(attributeName); if (attributes.containsKey(toLowerCase(attributeName))) { return true; } if (schema != null) { final String baseName; final String options; final int semicolonPos = attributeName.indexOf(';'); if (semicolonPos > 0) { baseName = attributeName.substring(0, semicolonPos); options = toLowerCase(attributeName.substring(semicolonPos)); } else { baseName = attributeName; options = ""; } final AttributeTypeDefinition at = schema.getAttributeType(baseName); if (at != null) { if (attributes.containsKey(toLowerCase(at.getOID()) + options)) { return true; } for (final String name : at.getNames()) { if (attributes.containsKey(toLowerCase(name) + options)) { return true; } } } } return false; } public final boolean hasAttribute(final Attribute attribute) { ensureNotNull(attribute); final String lowerName = toLowerCase(attribute.getName()); final Attribute attr = attributes.get(lowerName); return ((attr != null) && attr.equals(attribute)); } public final boolean hasAttributeValue(final String attributeName, final String attributeValue) { ensureNotNull(attributeName, attributeValue); final Attribute attr = attributes.get(toLowerCase(attributeName)); return ((attr != null) && attr.hasValue(attributeValue)); } public final boolean hasAttributeValue(final String attributeName, final String attributeValue, final MatchingRule matchingRule) { ensureNotNull(attributeName, attributeValue); final Attribute attr = attributes.get(toLowerCase(attributeName)); return ((attr != null) && attr.hasValue(attributeValue, matchingRule)); } public final boolean hasAttributeValue(final String attributeName, final byte[] attributeValue) { ensureNotNull(attributeName, attributeValue); final Attribute attr = attributes.get(toLowerCase(attributeName)); return ((attr != null) && attr.hasValue(attributeValue)); } public final boolean hasAttributeValue(final String attributeName, final byte[] attributeValue, final MatchingRule matchingRule) { ensureNotNull(attributeName, attributeValue); final Attribute attr = attributes.get(toLowerCase(attributeName)); return ((attr != null) && attr.hasValue(attributeValue, matchingRule)); } public final boolean hasObjectClass(final String objectClassName) { return hasAttributeValue("objectClass", objectClassName); } public final Collection<Attribute> getAttributes() { return Collections.unmodifiableCollection(attributes.values()); } public final Attribute getAttribute(final String attributeName) { return getAttribute(attributeName, schema); } public final Attribute getAttribute(final String attributeName, final Schema schema) { ensureNotNull(attributeName); Attribute a = attributes.get(toLowerCase(attributeName)); if ((a == null) && (schema != null)) { final String baseName; final String options; final int semicolonPos = attributeName.indexOf(';'); if (semicolonPos > 0) { baseName = attributeName.substring(0, semicolonPos); options = toLowerCase(attributeName.substring(semicolonPos)); } else { baseName = attributeName; options = ""; } final AttributeTypeDefinition at = schema.getAttributeType(baseName); if (at == null) { return null; } a = attributes.get(toLowerCase(at.getOID() + options)); if (a == null) { for (final String name : at.getNames()) { a = attributes.get(toLowerCase(name) + options); if (a != null) { return a; } } } return a; } else { return a; } } public final List<Attribute> getAttributesWithOptions(final String baseName, final Set<String> options) { ensureNotNull(baseName); final ArrayList<Attribute> attrList = new ArrayList<Attribute>(10); for (final Attribute a : attributes.values()) { if (a.getBaseName().equalsIgnoreCase(baseName)) { if ((options == null) || options.isEmpty()) { attrList.add(a); } else { boolean allFound = true; for (final String option : options) { if (!a.hasOption(option)) { allFound = false; break; } } if (allFound) { attrList.add(a); } } } } return Collections.unmodifiableList(attrList); } public String getAttributeValue(final String attributeName) { ensureNotNull(attributeName); final Attribute a = attributes.get(toLowerCase(attributeName)); if (a == null) { return null; } else { return a.getValue(); } } public byte[] getAttributeValueBytes(final String attributeName) { ensureNotNull(attributeName); final Attribute a = attributes.get(toLowerCase(attributeName)); if (a == null) { return null; } else { return a.getValueByteArray(); } } public Boolean getAttributeValueAsBoolean(final String attributeName) { ensureNotNull(attributeName); final Attribute a = attributes.get(toLowerCase(attributeName)); if (a == null) { return null; } else { return a.getValueAsBoolean(); } } public Date getAttributeValueAsDate(final String attributeName) { ensureNotNull(attributeName); final Attribute a = attributes.get(toLowerCase(attributeName)); if (a == null) { return null; } else { return a.getValueAsDate(); } } public DN getAttributeValueAsDN(final String attributeName) { ensureNotNull(attributeName); final Attribute a = attributes.get(toLowerCase(attributeName)); if (a == null) { return null; } else { return a.getValueAsDN(); } } public Integer getAttributeValueAsInteger(final String attributeName) { ensureNotNull(attributeName); final Attribute a = attributes.get(toLowerCase(attributeName)); if (a == null) { return null; } else { return a.getValueAsInteger(); } } public Long getAttributeValueAsLong(final String attributeName) { ensureNotNull(attributeName); final Attribute a = attributes.get(toLowerCase(attributeName)); if (a == null) { return null; } else { return a.getValueAsLong(); } } public String[] getAttributeValues(final String attributeName) { ensureNotNull(attributeName); final Attribute a = attributes.get(toLowerCase(attributeName)); if (a == null) { return null; } else { return a.getValues(); } } public byte[][] getAttributeValueByteArrays(final String attributeName) { ensureNotNull(attributeName); final Attribute a = attributes.get(toLowerCase(attributeName)); if (a == null) { return null; } else { return a.getValueByteArrays(); } } public final Attribute getObjectClassAttribute() { return getAttribute("objectClass"); } public final String[] getObjectClassValues() { return getAttributeValues("objectClass"); } public boolean addAttribute(final Attribute attribute) { ensureNotNull(attribute); final String lowerName = toLowerCase(attribute.getName()); final Attribute attr = attributes.get(lowerName); if (attr == null) { attributes.put(lowerName, attribute); return true; } else { final Attribute newAttr = Attribute.mergeAttributes(attr, attribute); attributes.put(lowerName, newAttr); return (attr.getRawValues().length != newAttr.getRawValues().length); } } public boolean addAttribute(final String attributeName, final String attributeValue) { ensureNotNull(attributeName, attributeValue); return addAttribute(new Attribute(attributeName, schema, attributeValue)); } public boolean addAttribute(final String attributeName, final byte[] attributeValue) { ensureNotNull(attributeName, attributeValue); return addAttribute(new Attribute(attributeName, schema, attributeValue)); } public boolean addAttribute(final String attributeName, final String... attributeValues) { ensureNotNull(attributeName, attributeValues); return addAttribute(new Attribute(attributeName, schema, attributeValues)); } public boolean addAttribute(final String attributeName, final byte[]... attributeValues) { ensureNotNull(attributeName, attributeValues); return addAttribute(new Attribute(attributeName, schema, attributeValues)); } public boolean removeAttribute(final String attributeName) { ensureNotNull(attributeName); if (schema == null) { return (attributes.remove(toLowerCase(attributeName)) != null); } else { final Attribute a = getAttribute(attributeName, schema); if (a == null) { return false; } else { attributes.remove(toLowerCase(a.getName())); return true; } } } public boolean removeAttributeValue(final String attributeName, final String attributeValue) { return removeAttributeValue(attributeName, attributeValue, null); } public boolean removeAttributeValue(final String attributeName, final String attributeValue, final MatchingRule matchingRule) { ensureNotNull(attributeName, attributeValue); final Attribute attr = getAttribute(attributeName, schema); if (attr == null) { return false; } else { final String lowerName = toLowerCase(attr.getName()); final Attribute newAttr = Attribute.removeValues(attr, new Attribute(attributeName, attributeValue), matchingRule); if (newAttr.hasValue()) { attributes.put(lowerName, newAttr); } else { attributes.remove(lowerName); } return (attr.getRawValues().length != newAttr.getRawValues().length); } } public boolean removeAttributeValue(final String attributeName, final byte[] attributeValue) { return removeAttributeValue(attributeName, attributeValue, null); } public boolean removeAttributeValue(final String attributeName, final byte[] attributeValue, final MatchingRule matchingRule) { ensureNotNull(attributeName, attributeValue); final Attribute attr = getAttribute(attributeName, schema); if (attr == null) { return false; } else { final String lowerName = toLowerCase(attr.getName()); final Attribute newAttr = Attribute.removeValues(attr, new Attribute(attributeName, attributeValue), matchingRule); if (newAttr.hasValue()) { attributes.put(lowerName, newAttr); } else { attributes.remove(lowerName); } return (attr.getRawValues().length != newAttr.getRawValues().length); } } public boolean removeAttributeValues(final String attributeName, final String... attributeValues) { ensureNotNull(attributeName, attributeValues); final Attribute attr = getAttribute(attributeName, schema); if (attr == null) { return false; } else { final String lowerName = toLowerCase(attr.getName()); final Attribute newAttr = Attribute.removeValues(attr, new Attribute(attributeName, attributeValues)); if (newAttr.hasValue()) { attributes.put(lowerName, newAttr); } else { attributes.remove(lowerName); } return (attr.getRawValues().length != newAttr.getRawValues().length); } } public boolean removeAttributeValues(final String attributeName, final byte[]... attributeValues) { ensureNotNull(attributeName, attributeValues); final Attribute attr = getAttribute(attributeName, schema); if (attr == null) { return false; } else { final String lowerName = toLowerCase(attr.getName()); final Attribute newAttr = Attribute.removeValues(attr, new Attribute(attributeName, attributeValues)); if (newAttr.hasValue()) { attributes.put(lowerName, newAttr); } else { attributes.remove(lowerName); } return (attr.getRawValues().length != newAttr.getRawValues().length); } } public void setAttribute(final Attribute attribute) { ensureNotNull(attribute); final String lowerName; final Attribute a = getAttribute(attribute.getName(), schema); if (a == null) { lowerName = toLowerCase(attribute.getName()); } else { lowerName = toLowerCase(a.getName()); } attributes.put(lowerName, attribute); } public void setAttribute(final String attributeName, final String attributeValue) { ensureNotNull(attributeName, attributeValue); setAttribute(new Attribute(attributeName, schema, attributeValue)); } public void setAttribute(final String attributeName, final byte[] attributeValue) { ensureNotNull(attributeName, attributeValue); setAttribute(new Attribute(attributeName, schema, attributeValue)); } public void setAttribute(final String attributeName, final String... attributeValues) { ensureNotNull(attributeName, attributeValues); setAttribute(new Attribute(attributeName, schema, attributeValues)); } public void setAttribute(final String attributeName, final byte[]... attributeValues) { ensureNotNull(attributeName, attributeValues); setAttribute(new Attribute(attributeName, schema, attributeValues)); } public boolean matchesBaseAndScope(final String baseDN, final SearchScope scope) throws LDAPException { return getParsedDN().matchesBaseAndScope(new DN(baseDN), scope); } public boolean matchesBaseAndScope(final DN baseDN, final SearchScope scope) throws LDAPException { return getParsedDN().matchesBaseAndScope(baseDN, scope); } public static List<Modification> diff(final Entry sourceEntry, final Entry targetEntry, final boolean ignoreRDN, final String... attributes) { return diff(sourceEntry, targetEntry, ignoreRDN, true, attributes); } public static List<Modification> diff(final Entry sourceEntry, final Entry targetEntry, final boolean ignoreRDN, final boolean reversible, final String... attributes) { HashSet<String> compareAttrs = null; if ((attributes != null) && (attributes.length > 0)) { compareAttrs = new HashSet<String>(attributes.length); for (final String s : attributes) { compareAttrs.add(toLowerCase(s)); } } final LinkedHashMap<String, Attribute> sourceOnlyAttrs = new LinkedHashMap<String, Attribute>(); final LinkedHashMap<String, Attribute> targetOnlyAttrs = new LinkedHashMap<String, Attribute>(); final LinkedHashMap<String, Attribute> commonAttrs = new LinkedHashMap<String, Attribute>(); for (final Map.Entry<String, Attribute> e : sourceEntry.attributes.entrySet()) { final String lowerName = toLowerCase(e.getKey()); if ((compareAttrs != null) && (!compareAttrs.contains(lowerName))) { continue; } sourceOnlyAttrs.put(lowerName, e.getValue()); commonAttrs.put(lowerName, e.getValue()); } for (final Map.Entry<String, Attribute> e : targetEntry.attributes.entrySet()) { final String lowerName = toLowerCase(e.getKey()); if ((compareAttrs != null) && (!compareAttrs.contains(lowerName))) { continue; } if (sourceOnlyAttrs.remove(lowerName) == null) { targetOnlyAttrs.put(lowerName, e.getValue()); } } for (final String lowerName : sourceOnlyAttrs.keySet()) { commonAttrs.remove(lowerName); } RDN sourceRDN = null; RDN targetRDN = null; if (ignoreRDN) { try { sourceRDN = sourceEntry.getRDN(); } catch (Exception e) { debugException(e); } try { targetRDN = targetEntry.getRDN(); } catch (Exception e) { debugException(e); } } final ArrayList<Modification> mods = new ArrayList<Modification>(10); for (final Attribute a : sourceOnlyAttrs.values()) { if (reversible) { ASN1OctetString[] values = a.getRawValues(); if ((sourceRDN != null) && (sourceRDN.hasAttribute(a.getName()))) { final ArrayList<ASN1OctetString> newValues = new ArrayList<ASN1OctetString>(values.length); for (final ASN1OctetString value : values) { if (!sourceRDN.hasAttributeValue(a.getName(), value.getValue())) { newValues.add(value); } } if (newValues.isEmpty()) { continue; } else { values = new ASN1OctetString[newValues.size()]; newValues.toArray(values); } } mods.add(new Modification(ModificationType.DELETE, a.getName(), values)); } else { mods.add(new Modification(ModificationType.REPLACE, a.getName())); } } for (final Attribute a : targetOnlyAttrs.values()) { ASN1OctetString[] values = a.getRawValues(); if ((targetRDN != null) && (targetRDN.hasAttribute(a.getName()))) { final ArrayList<ASN1OctetString> newValues = new ArrayList<ASN1OctetString>(values.length); for (final ASN1OctetString value : values) { if (!targetRDN.hasAttributeValue(a.getName(), value.getValue())) { newValues.add(value); } } if (newValues.isEmpty()) { continue; } else { values = new ASN1OctetString[newValues.size()]; newValues.toArray(values); } } if (reversible) { mods.add(new Modification(ModificationType.ADD, a.getName(), values)); } else { mods.add(new Modification(ModificationType.REPLACE, a.getName(), values)); } } for (final Attribute sourceAttr : commonAttrs.values()) { final Attribute targetAttr = targetEntry.getAttribute(sourceAttr.getName()); if (sourceAttr.equals(targetAttr)) { continue; } if (reversible || ((targetRDN != null) && targetRDN.hasAttribute(targetAttr.getName()))) { final ASN1OctetString[] sourceValueArray = sourceAttr.getRawValues(); final LinkedHashMap<ASN1OctetString, ASN1OctetString> sourceValues = new LinkedHashMap<ASN1OctetString, ASN1OctetString>( sourceValueArray.length); for (final ASN1OctetString s : sourceValueArray) { try { sourceValues.put(sourceAttr.getMatchingRule().normalize(s), s); } catch (final Exception e) { debugException(e); sourceValues.put(s, s); } } final ASN1OctetString[] targetValueArray = targetAttr.getRawValues(); final LinkedHashMap<ASN1OctetString, ASN1OctetString> targetValues = new LinkedHashMap<ASN1OctetString, ASN1OctetString>( targetValueArray.length); for (final ASN1OctetString s : targetValueArray) { try { targetValues.put(sourceAttr.getMatchingRule().normalize(s), s); } catch (final Exception e) { debugException(e); targetValues.put(s, s); } } final Iterator<Map.Entry<ASN1OctetString, ASN1OctetString>> sourceIterator = sourceValues.entrySet().iterator(); while (sourceIterator.hasNext()) { final Map.Entry<ASN1OctetString, ASN1OctetString> e = sourceIterator.next(); if (targetValues.remove(e.getKey()) != null) { sourceIterator.remove(); } else if ((sourceRDN != null) && sourceRDN.hasAttributeValue(sourceAttr.getName(), e.getValue().getValue())) { sourceIterator.remove(); } } final Iterator<Map.Entry<ASN1OctetString, ASN1OctetString>> targetIterator = targetValues.entrySet().iterator(); while (targetIterator.hasNext()) { final Map.Entry<ASN1OctetString, ASN1OctetString> e = targetIterator.next(); if ((targetRDN != null) && targetRDN.hasAttributeValue(targetAttr.getName(), e.getValue().getValue())) { targetIterator.remove(); } } final ArrayList<ASN1OctetString> addValues = new ArrayList<ASN1OctetString>(targetValues.values()); final ArrayList<ASN1OctetString> delValues = new ArrayList<ASN1OctetString>(sourceValues.values()); if (!addValues.isEmpty()) { final ASN1OctetString[] addArray = new ASN1OctetString[addValues.size()]; mods.add(new Modification(ModificationType.ADD, targetAttr.getName(), addValues.toArray(addArray))); } if (!delValues.isEmpty()) { final ASN1OctetString[] delArray = new ASN1OctetString[delValues.size()]; mods.add(new Modification(ModificationType.DELETE, sourceAttr.getName(), delValues.toArray(delArray))); } } else { mods.add(new Modification(ModificationType.REPLACE, targetAttr.getName(), targetAttr.getRawValues())); } } return mods; } public static Entry mergeEntries(final Entry... entries) { ensureNotNull(entries); ensureTrue(entries.length > 0); final Entry newEntry = entries[0].duplicate(); for (int i = 1; i < entries.length; i++) { for (final Attribute a : entries[i].attributes.values()) { newEntry.addAttribute(a); } } return newEntry; } public static Entry intersectEntries(final Entry... entries) { ensureNotNull(entries); ensureTrue(entries.length > 0); final Entry newEntry = entries[0].duplicate(); for (final Attribute a : entries[0].attributes.values()) { final String name = a.getName(); for (final byte[] v : a.getValueByteArrays()) { for (int i = 1; i < entries.length; i++) { if (!entries[i].hasAttributeValue(name, v)) { newEntry.removeAttributeValue(name, v); break; } } } } return newEntry; } public static Entry applyModifications(final Entry entry, final boolean lenient, final Modification... modifications) throws LDAPException { ensureNotNull(entry, modifications); ensureFalse(modifications.length == 0); return applyModifications(entry, lenient, Arrays.asList(modifications)); } public static Entry applyModifications(final Entry entry, final boolean lenient, final List<Modification> modifications) throws LDAPException { ensureNotNull(entry, modifications); ensureFalse(modifications.isEmpty()); final Entry e = entry.duplicate(); final ArrayList<String> errors = new ArrayList<String>(modifications.size()); ResultCode resultCode = null; RDN rdn = null; try { rdn = entry.getRDN(); } catch (final LDAPException le) { debugException(le); } for (final Modification m : modifications) { final String name = m.getAttributeName(); final byte[][] values = m.getValueByteArrays(); switch (m.getModificationType().intValue()) { case ModificationType.ADD_INT_VALUE: if (lenient) { e.addAttribute(m.getAttribute()); } else { if (values.length == 0) { errors.add(ERR_ENTRY_APPLY_MODS_ADD_NO_VALUES.get(name)); } for (int i = 0; i < values.length; i++) { if (!e.addAttribute(name, values[i])) { if (resultCode == null) { resultCode = ResultCode.ATTRIBUTE_OR_VALUE_EXISTS; } errors.add(ERR_ENTRY_APPLY_MODS_ADD_EXISTING.get( m.getValues()[i], name)); } } } break; case ModificationType.DELETE_INT_VALUE: if (values.length == 0) { if ((rdn != null) && rdn.hasAttribute(name)) { final String msg = ERR_ENTRY_APPLY_MODS_TARGETS_RDN.get(entry.getDN()); if (!errors.contains(msg)) { errors.add(msg); } if (resultCode == null) { resultCode = ResultCode.NOT_ALLOWED_ON_RDN; } break; } final boolean removed = e.removeAttribute(name); if (!(lenient || removed)) { if (resultCode == null) { resultCode = ResultCode.NO_SUCH_ATTRIBUTE; } errors.add(ERR_ENTRY_APPLY_MODS_DELETE_NONEXISTENT_ATTR.get( name)); } } else { deleteValueLoop: for (int i = 0; i < values.length; i++) { if ((rdn != null) && rdn.hasAttributeValue(name, values[i])) { final String msg = ERR_ENTRY_APPLY_MODS_TARGETS_RDN.get(entry.getDN()); if (!errors.contains(msg)) { errors.add(msg); } if (resultCode == null) { resultCode = ResultCode.NOT_ALLOWED_ON_RDN; } break deleteValueLoop; } final boolean removed = e.removeAttributeValue(name, values[i]); if (!(lenient || removed)) { if (resultCode == null) { resultCode = ResultCode.NO_SUCH_ATTRIBUTE; } errors.add(ERR_ENTRY_APPLY_MODS_DELETE_NONEXISTENT_VALUE.get( m.getValues()[i], name)); } } } break; case ModificationType.REPLACE_INT_VALUE: if ((rdn != null) && rdn.hasAttribute(name)) { final String msg = ERR_ENTRY_APPLY_MODS_TARGETS_RDN.get(entry.getDN()); if (!errors.contains(msg)) { errors.add(msg); } if (resultCode == null) { resultCode = ResultCode.NOT_ALLOWED_ON_RDN; } continue; } if (values.length == 0) { e.removeAttribute(name); } else { e.setAttribute(m.getAttribute()); } break; case ModificationType.INCREMENT_INT_VALUE: final Attribute a = e.getAttribute(name); if ((a == null) || (!a.hasValue())) { errors.add(ERR_ENTRY_APPLY_MODS_INCREMENT_NO_SUCH_ATTR.get(name)); continue; } if (a.size() > 1) { errors.add(ERR_ENTRY_APPLY_MODS_INCREMENT_NOT_SINGLE_VALUED.get( name)); continue; } if ((rdn != null) && rdn.hasAttribute(name)) { final String msg = ERR_ENTRY_APPLY_MODS_TARGETS_RDN.get(entry.getDN()); if (!errors.contains(msg)) { errors.add(msg); } if (resultCode == null) { resultCode = ResultCode.NOT_ALLOWED_ON_RDN; } continue; } final BigInteger currentValue; try { currentValue = new BigInteger(a.getValue()); } catch (NumberFormatException nfe) { debugException(nfe); errors.add( ERR_ENTRY_APPLY_MODS_INCREMENT_ENTRY_VALUE_NOT_INTEGER.get( name, a.getValue())); continue; } if (values.length == 0) { errors.add(ERR_ENTRY_APPLY_MODS_INCREMENT_NO_MOD_VALUES.get(name)); continue; } else if (values.length > 1) { errors.add(ERR_ENTRY_APPLY_MODS_INCREMENT_MULTIPLE_MOD_VALUES.get( name)); continue; } final BigInteger incrementValue; final String incrementValueStr = m.getValues()[0]; try { incrementValue = new BigInteger(incrementValueStr); } catch (NumberFormatException nfe) { debugException(nfe); errors.add(ERR_ENTRY_APPLY_MODS_INCREMENT_MOD_VALUE_NOT_INTEGER.get( name, incrementValueStr)); continue; } final BigInteger newValue = currentValue.add(incrementValue); e.setAttribute(name, newValue.toString()); break; default: errors.add(ERR_ENTRY_APPLY_MODS_UNKNOWN_TYPE.get( String.valueOf(m.getModificationType()))); break; } } if (errors.isEmpty()) { return e; } if (resultCode == null) { resultCode = ResultCode.CONSTRAINT_VIOLATION; } throw new LDAPException(resultCode, ERR_ENTRY_APPLY_MODS_FAILURE.get(e.getDN(), concatenateStrings(errors))); } @Override() public int hashCode() { int hashCode = 0; try { hashCode += getParsedDN().hashCode(); } catch (LDAPException le) { debugException(le); hashCode += dn.hashCode(); } for (final Attribute a : attributes.values()) { hashCode += a.hashCode(); } return hashCode; } @Override() public boolean equals(final Object o) { if (o == null) { return false; } if (o == this) { return true; } if (!(o instanceof Entry)) { return false; } final Entry e = (Entry) o; try { final DN thisDN = getParsedDN(); final DN thatDN = e.getParsedDN(); if (!thisDN.equals(thatDN)) { return false; } } catch (LDAPException le) { debugException(le); if (!dn.equals(e.dn)) { return false; } } if (attributes.size() != e.attributes.size()) { return false; } for (final Attribute a : attributes.values()) { if (!e.hasAttribute(a)) { return false; } } return true; } public Entry duplicate() { return new Entry(dn, schema, attributes.values()); } public final String[] toLDIF() { return toLDIF(0); } public final String[] toLDIF(final int wrapColumn) { List<String> ldifLines = new ArrayList<String>(2 * attributes.size()); ldifLines.add(LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(dn))); for (final Attribute a : attributes.values()) { final String name = a.getName(); for (final ASN1OctetString value : a.getRawValues()) { ldifLines.add(LDIFWriter.encodeNameAndValue(name, value)); } } if (wrapColumn > 2) { ldifLines = LDIFWriter.wrapLines(wrapColumn, ldifLines); } final String[] lineArray = new String[ldifLines.size()]; ldifLines.toArray(lineArray); return lineArray; } public final void toLDIF(final ByteStringBuffer buffer) { toLDIF(buffer, 0); } public final void toLDIF(final ByteStringBuffer buffer, final int wrapColumn) { LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(dn), buffer, wrapColumn); buffer.append(EOL_BYTES); for (final Attribute a : attributes.values()) { final String name = a.getName(); for (final ASN1OctetString value : a.getRawValues()) { LDIFWriter.encodeNameAndValue(name, value, buffer, wrapColumn); buffer.append(EOL_BYTES); } } } public final String toLDIFString() { final StringBuilder buffer = new StringBuilder(); toLDIFString(buffer, 0); return buffer.toString(); } public final String toLDIFString(final int wrapColumn) { final StringBuilder buffer = new StringBuilder(); toLDIFString(buffer, wrapColumn); return buffer.toString(); } public final void toLDIFString(final StringBuilder buffer) { toLDIFString(buffer, 0); } public final void toLDIFString(final StringBuilder buffer, final int wrapColumn) { LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(dn), buffer, wrapColumn); buffer.append(EOL); for (final Attribute a : attributes.values()) { final String name = a.getName(); for (final ASN1OctetString value : a.getRawValues()) { LDIFWriter.encodeNameAndValue(name, value, buffer, wrapColumn); buffer.append(EOL); } } } @Override() public final String toString() { final StringBuilder buffer = new StringBuilder(); toString(buffer); return buffer.toString(); } public void toString(final StringBuilder buffer) { buffer.append("Entry(dn='"); buffer.append(dn); buffer.append("', attributes={"); final Iterator<Attribute> iterator = attributes.values().iterator(); while (iterator.hasNext()) { iterator.next().toString(buffer); if (iterator.hasNext()) { buffer.append(", "); } } buffer.append("})"); } }