| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| |
| package org.apache.directory.shared.ldap.ldif; |
| |
| import java.io.Externalizable; |
| import java.io.IOException; |
| import java.io.ObjectInput; |
| import java.io.ObjectOutput; |
| import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.naming.InvalidNameException; |
| import javax.naming.NamingException; |
| import javax.naming.ldap.Control; |
| |
| import org.apache.directory.shared.ldap.entry.Entry; |
| import org.apache.directory.shared.ldap.entry.EntryAttribute; |
| import org.apache.directory.shared.ldap.entry.Modification; |
| import org.apache.directory.shared.ldap.entry.ModificationOperation; |
| import org.apache.directory.shared.ldap.entry.Value; |
| import org.apache.directory.shared.ldap.entry.client.ClientEntry; |
| import org.apache.directory.shared.ldap.entry.client.ClientModification; |
| import org.apache.directory.shared.ldap.entry.client.ClientStringValue; |
| import org.apache.directory.shared.ldap.entry.client.DefaultClientAttribute; |
| import org.apache.directory.shared.ldap.entry.client.DefaultClientEntry; |
| import org.apache.directory.shared.ldap.name.LdapDN; |
| import org.apache.directory.shared.ldap.name.Rdn; |
| import org.apache.directory.shared.ldap.util.StringTools; |
| |
| |
| /** |
| * A entry to be populated by an ldif parser. |
| * |
| * We will have different kind of entries : |
| * - added entries |
| * - deleted entries |
| * - modified entries |
| * - RDN modified entries |
| * - DN modified entries |
| * |
| * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> |
| * @version $Rev$, $Date$ |
| */ |
| public class LdifEntry implements Cloneable, Externalizable |
| { |
| private static final long serialVersionUID = 2L; |
| |
| /** Used in toArray() */ |
| public static final Modification[] EMPTY_MODS = new Modification[0]; |
| |
| /** the change type */ |
| private ChangeType changeType; |
| |
| /** the modification item list */ |
| private List<Modification> modificationList; |
| |
| private Map<String, Modification> modificationItems; |
| |
| /** The new superior */ |
| private String newSuperior; |
| |
| /** The new rdn */ |
| private String newRdn; |
| |
| /** The delete old rdn flag */ |
| private boolean deleteOldRdn; |
| |
| /** the entry */ |
| private ClientEntry entry; |
| |
| |
| /** The control */ |
| private Control control; |
| |
| /** |
| * Creates a new Entry object. |
| */ |
| public LdifEntry() |
| { |
| changeType = ChangeType.Add; // Default LDIF content |
| modificationList = new LinkedList<Modification>(); |
| modificationItems = new HashMap<String, Modification>(); |
| entry = new DefaultClientEntry( null ); |
| control = null; |
| } |
| |
| |
| /** |
| * Set the Distinguished Name |
| * |
| * @param dn |
| * The Distinguished Name |
| */ |
| public void setDn( LdapDN dn ) |
| { |
| entry.setDn( (LdapDN)dn.clone() ); |
| } |
| |
| |
| /** |
| * Set the Distinguished Name |
| * |
| * @param dn |
| * The Distinguished Name |
| */ |
| public void setDn( String dn ) throws InvalidNameException |
| { |
| LdapDN ldapDn = new LdapDN( dn ); |
| entry.setDn( ldapDn ); |
| } |
| |
| |
| /** |
| * Set the modification type |
| * |
| * @param changeType |
| * The change type |
| * |
| */ |
| public void setChangeType( ChangeType changeType ) |
| { |
| this.changeType = changeType; |
| } |
| |
| /** |
| * Set the change type |
| * |
| * @param changeType |
| * The change type |
| */ |
| public void setChangeType( String changeType ) |
| { |
| if ( "add".equals( changeType ) ) |
| { |
| this.changeType = ChangeType.Add; |
| } |
| else if ( "modify".equals( changeType ) ) |
| { |
| this.changeType = ChangeType.Modify; |
| } |
| else if ( "moddn".equals( changeType ) ) |
| { |
| this.changeType = ChangeType.ModDn; |
| } |
| else if ( "modrdn".equals( changeType ) ) |
| { |
| this.changeType = ChangeType.ModRdn; |
| } |
| else if ( "delete".equals( changeType ) ) |
| { |
| this.changeType = ChangeType.Delete; |
| } |
| } |
| |
| /** |
| * Add a modification item (used by modify operations) |
| * |
| * @param modification The modification to be added |
| */ |
| public void addModificationItem( Modification modification ) |
| { |
| if ( changeType == ChangeType.Modify ) |
| { |
| modificationList.add( modification ); |
| modificationItems.put( modification.getAttribute().getId(), modification ); |
| } |
| } |
| |
| /** |
| * Add a modification item (used by modify operations) |
| * |
| * @param modOp The operation. One of : |
| * - ModificationOperation.ADD_ATTRIBUTE |
| * - ModificationOperation.REMOVE_ATTRIBUTE |
| * - ModificationOperation.REPLACE_ATTRIBUTE |
| * |
| * @param attr The attribute to be added |
| */ |
| public void addModificationItem( ModificationOperation modOp, EntryAttribute attr ) |
| { |
| if ( changeType == ChangeType.Modify ) |
| { |
| Modification item = new ClientModification( modOp, attr ); |
| modificationList.add( item ); |
| modificationItems.put( attr.getId(), item ); |
| } |
| } |
| |
| |
| /** |
| * Add a modification item |
| * |
| * @param modOp The operation. One of : |
| * - ModificationOperation.ADD_ATTRIBUTE |
| * - ModificationOperation.REMOVE_ATTRIBUTE |
| * - ModificationOperation.REPLACE_ATTRIBUTE |
| * |
| * @param modOp The modification operation value |
| * @param id The attribute's ID |
| * @param value The attribute's value |
| */ |
| public void addModificationItem( ModificationOperation modOp, String id, Object value ) |
| { |
| if ( changeType == ChangeType.Modify ) |
| { |
| EntryAttribute attr = null; |
| |
| if ( value == null ) |
| { |
| value = new ClientStringValue( null ); |
| attr = new DefaultClientAttribute( id, (Value<?>)value ); |
| } |
| else |
| { |
| attr = (EntryAttribute)value; |
| } |
| |
| Modification item = new ClientModification( modOp, attr ); |
| modificationList.add( item ); |
| modificationItems.put( id, item ); |
| } |
| } |
| |
| |
| /** |
| * Add an attribute to the entry |
| * |
| * @param attr |
| * The attribute to be added |
| */ |
| public void addAttribute( EntryAttribute attr ) throws NamingException |
| { |
| entry.put( attr ); |
| } |
| |
| /** |
| * Add an attribute to the entry |
| * |
| * @param id |
| * The attribute ID |
| * |
| * @param value |
| * The attribute value |
| * |
| */ |
| public void addAttribute( String id, Object value ) throws NamingException |
| { |
| if ( value instanceof String ) |
| { |
| entry.add( id, (String)value ); |
| } |
| else |
| { |
| entry.add( id, (byte[])value ); |
| } |
| } |
| |
| /** |
| * Add an attribute value to an existing attribute |
| * |
| * @param id |
| * The attribute ID |
| * |
| * @param value |
| * The attribute value |
| * |
| */ |
| public void putAttribute( String id, Object value ) throws NamingException |
| { |
| if ( value instanceof String ) |
| { |
| entry.add( id, (String)value ); |
| } |
| else |
| { |
| entry.add( id, (byte[])value ); |
| } |
| } |
| |
| /** |
| * Get the change type |
| * |
| * @return The change type. One of : ADD = 0; MODIFY = 1; MODDN = 2; MODRDN = |
| * 3; DELETE = 4; |
| */ |
| public ChangeType getChangeType() |
| { |
| return changeType; |
| } |
| |
| /** |
| * @return The list of modification items |
| */ |
| public List<Modification> getModificationItems() |
| { |
| return modificationList; |
| } |
| |
| |
| /** |
| * Gets the modification items as an array. |
| * |
| * @return modification items as an array. |
| */ |
| public Modification[] getModificationItemsArray() |
| { |
| return modificationList.toArray( EMPTY_MODS ); |
| } |
| |
| |
| /** |
| * @return The entry Distinguished name |
| */ |
| public LdapDN getDn() |
| { |
| return entry.getDn(); |
| } |
| |
| /** |
| * @return The number of entry modifications |
| */ |
| public int size() |
| { |
| return modificationList.size(); |
| } |
| |
| /** |
| * Returns a attribute given it's id |
| * |
| * @param attributeId |
| * The attribute Id |
| * @return The attribute if it exists |
| */ |
| public EntryAttribute get( String attributeId ) |
| { |
| if ( "dn".equalsIgnoreCase( attributeId ) ) |
| { |
| return new DefaultClientAttribute( "dn", entry.getDn().getName() ); |
| } |
| |
| return entry.get( attributeId ); |
| } |
| |
| /** |
| * Get the entry's entry |
| * |
| * @return the stored Entry |
| */ |
| public Entry getEntry() |
| { |
| if ( isEntry() ) |
| { |
| return entry; |
| } |
| else |
| { |
| return null; |
| } |
| } |
| |
| /** |
| * @return True, if the old RDN should be deleted. |
| */ |
| public boolean isDeleteOldRdn() |
| { |
| return deleteOldRdn; |
| } |
| |
| /** |
| * Set the flage deleteOldRdn |
| * |
| * @param deleteOldRdn |
| * True if the old RDN should be deleted |
| */ |
| public void setDeleteOldRdn( boolean deleteOldRdn ) |
| { |
| this.deleteOldRdn = deleteOldRdn; |
| } |
| |
| /** |
| * @return The new RDN |
| */ |
| public String getNewRdn() |
| { |
| return newRdn; |
| } |
| |
| /** |
| * Set the new RDN |
| * |
| * @param newRdn |
| * The new RDN |
| */ |
| public void setNewRdn( String newRdn ) |
| { |
| this.newRdn = newRdn; |
| } |
| |
| /** |
| * @return The new superior |
| */ |
| public String getNewSuperior() |
| { |
| return newSuperior; |
| } |
| |
| /** |
| * Set the new superior |
| * |
| * @param newSuperior |
| * The new Superior |
| */ |
| public void setNewSuperior( String newSuperior ) |
| { |
| this.newSuperior = newSuperior; |
| } |
| |
| /** |
| * @return True if the entry is an ADD entry |
| */ |
| public boolean isChangeAdd() |
| { |
| return changeType == ChangeType.Add; |
| } |
| |
| /** |
| * @return True if the entry is a DELETE entry |
| */ |
| public boolean isChangeDelete() |
| { |
| return changeType == ChangeType.Delete; |
| } |
| |
| /** |
| * @return True if the entry is a MODDN entry |
| */ |
| public boolean isChangeModDn() |
| { |
| return changeType == ChangeType.ModDn; |
| } |
| |
| /** |
| * @return True if the entry is a MODRDN entry |
| */ |
| public boolean isChangeModRdn() |
| { |
| return changeType == ChangeType.ModRdn; |
| } |
| |
| /** |
| * @return True if the entry is a MODIFY entry |
| */ |
| public boolean isChangeModify() |
| { |
| return changeType == ChangeType.Modify; |
| } |
| |
| /** |
| * Tells if the current entry is a added one |
| * |
| * @return <code>true</code> if the entry is added |
| */ |
| public boolean isEntry() |
| { |
| return changeType == ChangeType.Add; |
| } |
| |
| /** |
| * @return The associated control, if any |
| */ |
| public Control getControl() |
| { |
| return control; |
| } |
| |
| /** |
| * Add a control to the entry |
| * |
| * @param control |
| * The control |
| */ |
| public void setControl( Control control ) |
| { |
| this.control = control; |
| } |
| |
| /** |
| * Clone method |
| * @return a clone of the current instance |
| * @exception CloneNotSupportedException If there is some problem while cloning the instance |
| */ |
| public LdifEntry clone() throws CloneNotSupportedException |
| { |
| LdifEntry clone = (LdifEntry) super.clone(); |
| |
| if ( modificationList != null ) |
| { |
| for ( Modification modif:modificationList ) |
| { |
| Modification modifClone = new ClientModification( modif.getOperation(), |
| (EntryAttribute) modif.getAttribute().clone() ); |
| clone.modificationList.add( modifClone ); |
| } |
| } |
| |
| if ( modificationItems != null ) |
| { |
| for ( String key:modificationItems.keySet() ) |
| { |
| Modification modif = modificationItems.get( key ); |
| Modification modifClone = new ClientModification( modif.getOperation(), |
| (EntryAttribute) modif.getAttribute().clone() ); |
| clone.modificationItems.put( key, modifClone ); |
| } |
| |
| } |
| |
| if ( entry != null ) |
| { |
| clone.entry = (ClientEntry)entry.clone(); |
| } |
| |
| return clone; |
| } |
| |
| /** |
| * Dumps the attributes |
| * @return A String representing the attributes |
| */ |
| private String dumpAttributes() |
| { |
| StringBuffer sb = new StringBuffer(); |
| |
| for ( EntryAttribute attribute:entry ) |
| { |
| if ( attribute == null ) |
| { |
| sb.append( " Null attribute\n" ); |
| continue; |
| } |
| |
| sb.append( " ").append( attribute.getId() ).append( ":\n" ); |
| |
| for ( Value<?> value:attribute ) |
| { |
| if ( !value.isBinary() ) |
| { |
| sb.append( " " ).append( value.getString() ).append('\n' ); |
| } |
| else |
| { |
| sb.append( " " ).append( StringTools.dumpBytes( value.getBytes() ) ).append('\n' ); |
| } |
| } |
| } |
| |
| return sb.toString(); |
| } |
| |
| /** |
| * Dumps the modifications |
| * @return A String representing the modifications |
| */ |
| private String dumpModificationItems() |
| { |
| StringBuffer sb = new StringBuffer(); |
| |
| for ( Modification modif:modificationList ) |
| { |
| sb.append( " Operation: " ); |
| |
| switch ( modif.getOperation() ) |
| { |
| case ADD_ATTRIBUTE : |
| sb.append( "ADD\n" ); |
| break; |
| |
| case REMOVE_ATTRIBUTE : |
| sb.append( "REMOVE\n" ); |
| break; |
| |
| case REPLACE_ATTRIBUTE : |
| sb.append( "REPLACE \n" ); |
| break; |
| |
| default : |
| break; // Do nothing |
| } |
| |
| EntryAttribute attribute = modif.getAttribute(); |
| |
| sb.append( " Attribute: " ).append( attribute.getId() ).append( '\n' ); |
| |
| if ( attribute.size() != 0 ) |
| { |
| for ( Value<?> value:attribute ) |
| { |
| if ( !value.isBinary() ) |
| { |
| sb.append( " " ).append( value.getString() ).append('\n' ); |
| } |
| else |
| { |
| sb.append( " " ).append( StringTools.dumpBytes( value.getBytes() ) ).append('\n' ); |
| } |
| } |
| } |
| } |
| |
| return sb.toString(); |
| } |
| |
| |
| /** |
| * @return a String representing the Entry, as a LDIF |
| */ |
| public String toString() |
| { |
| try |
| { |
| return LdifUtils.convertToLdif( this ); |
| } |
| catch ( NamingException ne ) |
| { |
| return null; |
| } |
| } |
| |
| |
| /** |
| * @see Object#hashCode() |
| * |
| * @return the instance's hash code |
| */ |
| public int hashCode() |
| { |
| int result = 37; |
| |
| if ( entry.getDn() != null ) |
| { |
| result = result*17 + entry.getDn().hashCode(); |
| } |
| |
| if ( changeType != null ) |
| { |
| result = result*17 + changeType.hashCode(); |
| |
| // Check each different cases |
| switch ( changeType ) |
| { |
| case Add : |
| // Checks the attributes |
| if ( entry != null ) |
| { |
| result = result * 17 + entry.hashCode(); |
| } |
| |
| break; |
| |
| case Delete : |
| // Nothing to compute |
| break; |
| |
| case Modify : |
| if ( modificationList != null ) |
| { |
| result = result * 17 + modificationList.hashCode(); |
| |
| for ( Modification modification:modificationList ) |
| { |
| result = result * 17 + modification.hashCode(); |
| } |
| } |
| |
| break; |
| |
| case ModDn : |
| case ModRdn : |
| result = result * 17 + ( deleteOldRdn ? 1 : -1 ); |
| |
| if ( newRdn != null ) |
| { |
| result = result*17 + newRdn.hashCode(); |
| } |
| |
| if ( newSuperior != null ) |
| { |
| result = result*17 + newSuperior.hashCode(); |
| } |
| |
| break; |
| |
| default : |
| break; // do nothing |
| } |
| } |
| |
| if ( control != null ) |
| { |
| result = result * 17 + control.hashCode(); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * @see Object#equals(Object) |
| * @return <code>true</code> if both values are equal |
| */ |
| public boolean equals( Object o ) |
| { |
| // Basic equals checks |
| if ( this == o ) |
| { |
| return true; |
| } |
| |
| if ( o == null ) |
| { |
| return false; |
| } |
| |
| if ( ! (o instanceof LdifEntry ) ) |
| { |
| return false; |
| } |
| |
| LdifEntry otherEntry = (LdifEntry)o; |
| |
| // Check the DN |
| LdapDN thisDn = entry.getDn(); |
| LdapDN dnEntry = otherEntry.getDn(); |
| |
| if ( !thisDn.equals( dnEntry ) ) |
| { |
| return false; |
| } |
| |
| |
| // Check the changeType |
| if ( changeType != otherEntry.changeType ) |
| { |
| return false; |
| } |
| |
| // Check each different cases |
| switch ( changeType ) |
| { |
| case Add : |
| // Checks the attributes |
| if ( entry == null ) |
| { |
| if ( otherEntry.entry != null ) |
| { |
| return false; |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| if ( otherEntry.entry == null ) |
| { |
| return false; |
| } |
| |
| if ( entry.size() != otherEntry.entry.size() ) |
| { |
| return false; |
| } |
| |
| if ( !entry.equals( otherEntry.entry ) ) |
| { |
| return false; |
| } |
| |
| break; |
| |
| case Delete : |
| // Nothing to do, if the DNs are equals |
| break; |
| |
| case Modify : |
| // Check the modificationItems list |
| |
| // First, deal with special cases |
| if ( modificationList == null ) |
| { |
| if ( otherEntry.modificationList != null ) |
| { |
| return false; |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| if ( otherEntry.modificationList == null ) |
| { |
| return false; |
| } |
| |
| if ( modificationList.size() != otherEntry.modificationList.size() ) |
| { |
| return false; |
| } |
| |
| // Now, compares the contents |
| int i = 0; |
| |
| for ( Modification modification:modificationList ) |
| { |
| if ( ! modification.equals( otherEntry.modificationList.get( i ) ) ) |
| { |
| return false; |
| } |
| |
| i++; |
| } |
| |
| break; |
| |
| case ModDn : |
| case ModRdn : |
| // Check the deleteOldRdn flag |
| if ( deleteOldRdn != otherEntry.deleteOldRdn ) |
| { |
| return false; |
| } |
| |
| // Check the newRdn value |
| try |
| { |
| Rdn thisNewRdn = new Rdn( newRdn ); |
| Rdn entryNewRdn = new Rdn( otherEntry.newRdn ); |
| |
| if ( !thisNewRdn.equals( entryNewRdn ) ) |
| { |
| return false; |
| } |
| } |
| catch ( InvalidNameException ine ) |
| { |
| return false; |
| } |
| |
| // Check the newSuperior value |
| try |
| { |
| LdapDN thisNewSuperior = new LdapDN( newSuperior ); |
| LdapDN entryNewSuperior = new LdapDN( otherEntry.newSuperior ); |
| |
| if ( ! thisNewSuperior.equals( entryNewSuperior ) ) |
| { |
| return false; |
| } |
| } |
| catch ( InvalidNameException ine ) |
| { |
| return false; |
| } |
| |
| break; |
| |
| default : |
| break; // do nothing |
| } |
| |
| if ( control != null ) |
| { |
| return control.equals( otherEntry.control ); |
| } |
| else |
| { |
| return otherEntry.control == null; |
| } |
| } |
| |
| |
| /** |
| * @see Externalizable#readExternal(ObjectInput) |
| * |
| * @param in The stream from which the LdifEntry is read |
| * @throws IOException If the stream can't be read |
| * @throws ClassNotFoundException If the LdifEntry can't be created |
| */ |
| public void readExternal( ObjectInput in ) throws IOException , ClassNotFoundException |
| { |
| // Read the changeType |
| int type = in.readInt(); |
| changeType = ChangeType.getChangeType( type ); |
| entry = (ClientEntry)in.readObject(); |
| |
| switch ( changeType ) |
| { |
| case Add : |
| // Fallback |
| case Delete : |
| // we don't have anything to read, but the control |
| break; |
| |
| case ModDn : |
| // Fallback |
| case ModRdn : |
| deleteOldRdn = in.readBoolean(); |
| |
| if ( in.readBoolean() ) |
| { |
| newRdn = in.readUTF(); |
| } |
| |
| if ( in.readBoolean() ) |
| { |
| newSuperior = in.readUTF(); |
| } |
| |
| break; |
| |
| case Modify : |
| // Read the modification |
| int nbModifs = in.readInt(); |
| |
| |
| for ( int i = 0; i < nbModifs; i++ ) |
| { |
| int operation = in.readInt(); |
| String modStr = in.readUTF(); |
| DefaultClientAttribute value = (DefaultClientAttribute)in.readObject(); |
| |
| addModificationItem( ModificationOperation.getOperation( operation ), modStr, value ); |
| } |
| |
| break; |
| } |
| |
| if ( in.available() > 0 ) |
| { |
| // We have a control |
| control = (Control)in.readObject(); |
| } |
| } |
| |
| |
| /** |
| * @see Externalizable#readExternal(ObjectInput)<p> |
| * |
| *@param out The stream in which the ChangeLogEvent will be serialized. |
| * |
| *@throws IOException If the serialization fail |
| */ |
| public void writeExternal( ObjectOutput out ) throws IOException |
| { |
| // Write the changeType |
| out.writeInt( changeType.getChangeType() ); |
| |
| // Write the entry |
| out.writeObject( entry ); |
| |
| // Write the data |
| switch ( changeType ) |
| { |
| case Add : |
| // Fallback |
| case Delete : |
| // we don't have anything to write, but the control |
| break; |
| |
| case ModDn : |
| // Fallback |
| case ModRdn : |
| out.writeBoolean( deleteOldRdn ); |
| |
| if ( newRdn != null ) |
| { |
| out.writeBoolean( true ); |
| out.writeUTF( newRdn ); |
| } |
| else |
| { |
| out.writeBoolean( false ); |
| } |
| |
| if ( newSuperior != null ) |
| { |
| out.writeBoolean( true ); |
| out.writeUTF( newSuperior ); |
| } |
| else |
| { |
| out.writeBoolean( false ); |
| } |
| break; |
| |
| case Modify : |
| // Read the modification |
| out.writeInt( modificationList.size() ); |
| |
| for ( Modification modification:modificationList ) |
| { |
| out.writeInt( modification.getOperation().getValue() ); |
| out.writeUTF( modification.getAttribute().getId() ); |
| |
| EntryAttribute attribute = modification.getAttribute(); |
| out.writeObject( attribute ); |
| } |
| |
| break; |
| } |
| |
| if ( control != null ) |
| { |
| // Write the control |
| out.writeObject( control ); |
| |
| } |
| |
| // and flush the result |
| out.flush(); |
| } |
| } |