blob: a7ec563777289d74381fd37763553e261bc4fefe [file] [log] [blame]
/*
* 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();
}
}