package com.hwlcn.ldap.ldap.sdk; import java.io.Serializable; import java.util.ArrayList; import com.hwlcn.ldap.asn1.ASN1Buffer; import com.hwlcn.ldap.asn1.ASN1BufferSequence; import com.hwlcn.ldap.asn1.ASN1BufferSet; import com.hwlcn.ldap.asn1.ASN1Element; import com.hwlcn.ldap.asn1.ASN1Enumerated; import com.hwlcn.ldap.asn1.ASN1Exception; import com.hwlcn.ldap.asn1.ASN1OctetString; import com.hwlcn.ldap.asn1.ASN1Sequence; import com.hwlcn.ldap.asn1.ASN1Set; import com.hwlcn.ldap.asn1.ASN1StreamReader; import com.hwlcn.ldap.asn1.ASN1StreamReaderSet; import com.hwlcn.ldap.ldap.matchingrules.CaseIgnoreStringMatchingRule; import com.hwlcn.ldap.util.Base64; import com.hwlcn.core.annotation.NotMutable; import com.hwlcn.core.annotation.ThreadSafety; import com.hwlcn.ldap.util.ThreadSafetyLevel; import static com.hwlcn.ldap.ldap.sdk.LDAPMessages.*; import static com.hwlcn.ldap.util.Debug.*; import static com.hwlcn.ldap.util.StaticUtils.*; import static com.hwlcn.ldap.util.Validator.*; @NotMutable() @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) public final class Modification implements Serializable { private static final ASN1OctetString[] NO_VALUES = new ASN1OctetString[0]; private static final byte[][] NO_BYTE_VALUES = new byte[0][]; private static final long serialVersionUID = 5170107037390858876L; private final ASN1OctetString[] values; private final ModificationType modificationType; private final String attributeName; public Modification(final ModificationType modificationType, final String attributeName) { ensureNotNull(attributeName); this.modificationType = modificationType; this.attributeName = attributeName; values = NO_VALUES; } public Modification(final ModificationType modificationType, final String attributeName, final String attributeValue) { ensureNotNull(attributeName, attributeValue); this.modificationType = modificationType; this.attributeName = attributeName; values = new ASN1OctetString[] { new ASN1OctetString(attributeValue) }; } public Modification(final ModificationType modificationType, final String attributeName, final byte[] attributeValue) { ensureNotNull(attributeName, attributeValue); this.modificationType = modificationType; this.attributeName = attributeName; values = new ASN1OctetString[] { new ASN1OctetString(attributeValue) }; } public Modification(final ModificationType modificationType, final String attributeName, final String... attributeValues) { ensureNotNull(attributeName, attributeValues); this.modificationType = modificationType; this.attributeName = attributeName; values = new ASN1OctetString[attributeValues.length]; for (int i=0; i < values.length; i++) { values[i] = new ASN1OctetString(attributeValues[i]); } } public Modification(final ModificationType modificationType, final String attributeName, final byte[]... attributeValues) { ensureNotNull(attributeName, attributeValues); this.modificationType = modificationType; this.attributeName = attributeName; values = new ASN1OctetString[attributeValues.length]; for (int i=0; i < values.length; i++) { values[i] = new ASN1OctetString(attributeValues[i]); } } public Modification(final ModificationType modificationType, final String attributeName, final ASN1OctetString[] attributeValues) { this.modificationType = modificationType; this.attributeName = attributeName; values = attributeValues; } public ModificationType getModificationType() { return modificationType; } public Attribute getAttribute() { return new Attribute(attributeName, CaseIgnoreStringMatchingRule.getInstance(), values); } public String getAttributeName() { return attributeName; } public boolean hasValue() { return (values.length > 0); } public String[] getValues() { if (values.length == 0) { return NO_STRINGS; } else { final String[] stringValues = new String[values.length]; for (int i=0; i < values.length; i++) { stringValues[i] = values[i].stringValue(); } return stringValues; } } public byte[][] getValueByteArrays() { if (values.length == 0) { return NO_BYTE_VALUES; } else { final byte[][] byteValues = new byte[values.length][]; for (int i=0; i < values.length; i++) { byteValues[i] = values[i].getValue(); } return byteValues; } } public ASN1OctetString[] getRawValues() { return values; } public void writeTo(final ASN1Buffer buffer) { final ASN1BufferSequence modSequence = buffer.beginSequence(); buffer.addEnumerated(modificationType.intValue()); final ASN1BufferSequence attrSequence = buffer.beginSequence(); buffer.addOctetString(attributeName); final ASN1BufferSet valueSet = buffer.beginSet(); for (final ASN1OctetString v : values) { buffer.addElement(v); } valueSet.end(); attrSequence.end(); modSequence.end(); } public ASN1Sequence encode() { final ASN1Element[] attrElements = { new ASN1OctetString(attributeName), new ASN1Set(values) }; final ASN1Element[] modificationElements = { new ASN1Enumerated(modificationType.intValue()), new ASN1Sequence(attrElements) }; return new ASN1Sequence(modificationElements); } public static Modification readFrom(final ASN1StreamReader reader) throws LDAPException { try { ensureNotNull(reader.beginSequence()); final ModificationType modType = ModificationType.valueOf(reader.readEnumerated()); ensureNotNull(reader.beginSequence()); final String attrName = reader.readString(); final ArrayList<ASN1OctetString> valueList = new ArrayList<ASN1OctetString>(5); final ASN1StreamReaderSet valueSet = reader.beginSet(); while (valueSet.hasMoreElements()) { valueList.add(new ASN1OctetString(reader.readBytes())); } final ASN1OctetString[] values = new ASN1OctetString[valueList.size()]; valueList.toArray(values); return new Modification(modType, attrName, values); } catch (Exception e) { debugException(e); throw new LDAPException(ResultCode.DECODING_ERROR, ERR_MOD_CANNOT_DECODE.get(getExceptionMessage(e)), e); } } public static Modification decode(final ASN1Sequence modificationSequence) throws LDAPException { ensureNotNull(modificationSequence); final ASN1Element[] modificationElements = modificationSequence.elements(); if (modificationElements.length != 2) { throw new LDAPException(ResultCode.DECODING_ERROR, ERR_MOD_DECODE_INVALID_ELEMENT_COUNT.get( modificationElements.length)); } final int modType; try { final ASN1Enumerated typeEnumerated = ASN1Enumerated.decodeAsEnumerated(modificationElements[0]); modType = typeEnumerated.intValue(); } catch (final ASN1Exception ae) { debugException(ae); throw new LDAPException(ResultCode.DECODING_ERROR, ERR_MOD_DECODE_CANNOT_PARSE_MOD_TYPE.get(getExceptionMessage(ae)), ae); } final ASN1Sequence attrSequence; try { attrSequence = ASN1Sequence.decodeAsSequence(modificationElements[1]); } catch (final ASN1Exception ae) { debugException(ae); throw new LDAPException(ResultCode.DECODING_ERROR, ERR_MOD_DECODE_CANNOT_PARSE_ATTR.get(getExceptionMessage(ae)), ae); } final ASN1Element[] attrElements = attrSequence.elements(); if (attrElements.length != 2) { throw new LDAPException(ResultCode.DECODING_ERROR, ERR_MOD_DECODE_INVALID_ATTR_ELEMENT_COUNT.get( attrElements.length)); } final String attrName = ASN1OctetString.decodeAsOctetString(attrElements[0]).stringValue(); final ASN1Set valueSet; try { valueSet = ASN1Set.decodeAsSet(attrElements[1]); } catch (final ASN1Exception ae) { debugException(ae); throw new LDAPException(ResultCode.DECODING_ERROR, ERR_MOD_DECODE_CANNOT_PARSE_ATTR_VALUE_SET.get( getExceptionMessage(ae)), ae); } final ASN1Element[] valueElements = valueSet.elements(); final ASN1OctetString[] values = new ASN1OctetString[valueElements.length]; for (int i=0; i < values.length; i++) { values[i] = ASN1OctetString.decodeAsOctetString(valueElements[i]); } return new Modification(ModificationType.valueOf(modType), attrName, values); } @Override() public int hashCode() { int hashCode = modificationType.intValue() + toLowerCase(attributeName).hashCode(); for (final ASN1OctetString value : values) { hashCode += value.hashCode(); } return hashCode; } @Override() public boolean equals(final Object o) { if (o == null) { return false; } if (o == this) { return true; } if (! (o instanceof Modification)) { return false; } final Modification mod = (Modification) o; if (modificationType != mod.modificationType) { return false; } if (! attributeName.equalsIgnoreCase(mod.attributeName)) { return false; } if (values.length != mod.values.length) { return false; } for (final ASN1OctetString value : values) { boolean found = false; for (int j = 0; j < mod.values.length; j++) { if (value.equalsIgnoreType(mod.values[j])) { found = true; break; } } if (!found) { return false; } } return true; } @Override() public String toString() { final StringBuilder buffer = new StringBuilder(); toString(buffer); return buffer.toString(); } public void toString(final StringBuilder buffer) { buffer.append("LDAPModification(type="); switch (modificationType.intValue()) { case 0: buffer.append("add"); break; case 1: buffer.append("delete"); break; case 2: buffer.append("replace"); break; case 3: buffer.append("increment"); break; default: buffer.append(modificationType); break; } buffer.append(", attr="); buffer.append(attributeName); if (values.length == 0) { buffer.append(", values={"); } else if (needsBase64Encoding()) { buffer.append(", base64Values={'"); for (int i=0; i < values.length; i++) { if (i > 0) { buffer.append("', '"); } buffer.append(Base64.encode(values[i].getValue())); } buffer.append('\''); } else { buffer.append(", values={'"); for (int i=0; i < values.length; i++) { if (i > 0) { buffer.append("', '"); } buffer.append(values[i].stringValue()); } buffer.append('\''); } buffer.append("})"); } private boolean needsBase64Encoding() { for (final ASN1OctetString s : values) { if (Attribute.needsBase64Encoding(s.getValue())) { return true; } } return false; } }