directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From elecha...@apache.org
Subject svn commit: r721052 [1/3] - in /directory: apacheds/branches/apacheds-mina2/core/src/main/java/org/apache/directory/server/core/changelog/ apacheds/branches/apacheds-mina2/core/src/main/java/org/apache/directory/server/core/interceptor/context/ shared/...
Date Thu, 27 Nov 2008 01:00:32 GMT
Author: elecharny
Date: Wed Nov 26 17:00:32 2008
New Revision: 721052

URL: http://svn.apache.org/viewvc?rev=721052&view=rev
Log:
Started to fix DIRSERVER-1231 :
o Created a LdifRevertor class to contain all the revert operations
o Created the associated tests
o Removed most of the associated revert from LdifUtils and test
o Modified the ChangeLog interceptor to reflect these modifications
o Removed the need to transform a entry to an Attributes

Added:
    directory/shared/branches/shared-mina2/ldap/src/main/java/org/apache/directory/shared/ldap/ldif/LdifRevertor.java
    directory/shared/branches/shared-mina2/ldap/src/test/java/org/apache/directory/shared/ldap/ldif/LdifRevertorTest.java
Modified:
    directory/apacheds/branches/apacheds-mina2/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogInterceptor.java
    directory/apacheds/branches/apacheds-mina2/core/src/main/java/org/apache/directory/server/core/interceptor/context/RenameOperationContext.java
    directory/shared/branches/shared-mina2/ldap/src/main/java/org/apache/directory/shared/ldap/ldif/LdifUtils.java
    directory/shared/branches/shared-mina2/ldap/src/test/java/org/apache/directory/shared/ldap/ldif/LdifUtilsTest.java

Modified: directory/apacheds/branches/apacheds-mina2/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogInterceptor.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-mina2/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogInterceptor.java?rev=721052&r1=721051&r2=721052&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-mina2/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogInterceptor.java
(original)
+++ directory/apacheds/branches/apacheds-mina2/core/src/main/java/org/apache/directory/server/core/changelog/ChangeLogInterceptor.java
Wed Nov 26 17:00:32 2008
@@ -47,6 +47,7 @@
 import org.apache.directory.shared.ldap.entry.client.DefaultClientEntry;
 import org.apache.directory.shared.ldap.ldif.ChangeType;
 import org.apache.directory.shared.ldap.ldif.LdifEntry;
+import org.apache.directory.shared.ldap.ldif.LdifRevertor;
 import org.apache.directory.shared.ldap.ldif.LdifUtils;
 import org.apache.directory.shared.ldap.name.LdapDN;
 import org.apache.directory.shared.ldap.name.Rdn;
@@ -126,7 +127,7 @@
             forward.addAttribute( ((ServerAttribute)addEntry.get( attributeType) ).toClientAttribute()
);
         }
         
-        LdifEntry reverse = LdifUtils.reverseAdd( opContext.getDn() );
+        LdifEntry reverse = LdifRevertor.reverseAdd( opContext.getDn() );
         opContext.setChangeLogEvent( changeLog.log( getPrincipal(), forward, reverse ) );
     }
 
@@ -170,7 +171,7 @@
             reverseEntry.add( ((ServerAttribute)attribute).toClientAttribute() );
         }
 
-        LdifEntry reverse = LdifUtils.reverseDel( opContext.getDn(), reverseEntry );
+        LdifEntry reverse = LdifRevertor.reverseDel( opContext.getDn(), reverseEntry );
         opContext.setChangeLogEvent( changeLog.log( getPrincipal(), forward, reverse ) );
     }
 
@@ -263,7 +264,7 @@
             clientEntry.add( ((ServerAttribute)attribute).toClientAttribute() );
         }
 
-        LdifEntry reverse = LdifUtils.reverseModify( 
+        LdifEntry reverse = LdifRevertor.reverseModify( 
             opContext.getDn(), 
             mods, 
             clientEntry );
@@ -300,8 +301,8 @@
         forward.setNewRdn( renameContext.getNewRdn().getUpName() );
         forward.setDeleteOldRdn( renameContext.getDelOldDn() );
 
-        List<LdifEntry> reverses = LdifUtils.reverseModifyRdn( ServerEntryUtils.toBasicAttributes(
serverEntry ), 
-            null, renameContext.getDn(), new Rdn( renameContext.getNewRdn() ) );
+        List<LdifEntry> reverses = LdifRevertor.reverseRename( 
+            serverEntry, renameContext.getNewRdn(), renameContext.getDelOldDn() );
         
         renameContext.setChangeLogEvent( changeLog.log( getPrincipal(), forward, reverses
) );
     }
@@ -352,7 +353,7 @@
         forward.setDn( opCtx.getDn() );
         forward.setNewSuperior( opCtx.getParent().getUpName() );
 
-        LdifEntry reverse = LdifUtils.reverseModifyDn( opCtx.getParent(), opCtx.getDn() );
+        LdifEntry reverse = LdifRevertor.reverseMove( opCtx.getParent(), opCtx.getDn() );
         opCtx.setChangeLogEvent( changeLog.log( getPrincipal(), forward, reverse ) );
     }
 }
\ No newline at end of file

Modified: directory/apacheds/branches/apacheds-mina2/core/src/main/java/org/apache/directory/server/core/interceptor/context/RenameOperationContext.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/apacheds-mina2/core/src/main/java/org/apache/directory/server/core/interceptor/context/RenameOperationContext.java?rev=721052&r1=721051&r2=721052&view=diff
==============================================================================
--- directory/apacheds/branches/apacheds-mina2/core/src/main/java/org/apache/directory/server/core/interceptor/context/RenameOperationContext.java
(original)
+++ directory/apacheds/branches/apacheds-mina2/core/src/main/java/org/apache/directory/server/core/interceptor/context/RenameOperationContext.java
Wed Nov 26 17:00:32 2008
@@ -33,7 +33,7 @@
  * A RenameService context used for Interceptors. It contains all the informations
  * needed for the modify DN operation, and used by all the interceptors
  * 
- * This is used whne the modifyDN is about changing the RDN, not the base DN.
+ * This is used when the modifyDN is about changing the RDN, not the base DN.
  *
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  * @version $Rev$, $Date$

Added: directory/shared/branches/shared-mina2/ldap/src/main/java/org/apache/directory/shared/ldap/ldif/LdifRevertor.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/shared-mina2/ldap/src/main/java/org/apache/directory/shared/ldap/ldif/LdifRevertor.java?rev=721052&view=auto
==============================================================================
--- directory/shared/branches/shared-mina2/ldap/src/main/java/org/apache/directory/shared/ldap/ldif/LdifRevertor.java
(added)
+++ directory/shared/branches/shared-mina2/ldap/src/main/java/org/apache/directory/shared/ldap/ldif/LdifRevertor.java
Wed Nov 26 17:00:32 2008
@@ -0,0 +1,644 @@
+/*
+ *  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.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.naming.NamingException;
+
+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.client.ClientModification;
+import org.apache.directory.shared.ldap.entry.client.DefaultClientAttribute;
+import org.apache.directory.shared.ldap.name.AttributeTypeAndValue;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.name.Rdn;
+import org.apache.directory.shared.ldap.util.AttributeUtils;
+
+
+/**
+ * A helper class which provides methods to reverse a LDIF modification operation. 
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class LdifRevertor
+{
+    /** Two constants for the deleteOldRdn flag */
+    public static final boolean DELETE_OLD_RDN = true;
+    public static final boolean KEEP_OLD_RDN = false;
+    
+    /**
+     * Compute a reverse LDIF of an AddRequest. It's simply a delete request
+     * of the added entry
+     *
+     * @param dn the dn of the added entry
+     * @return a reverse LDIF
+     */
+    public static LdifEntry reverseAdd( LdapDN dn )
+    {
+        LdifEntry entry = new LdifEntry();
+        entry.setChangeType( ChangeType.Delete );
+        entry.setDn( dn );
+        return entry;
+    }
+
+
+    /**
+     * Compute a reverse LDIF of a DeleteRequest. We have to get the previous
+     * entry in order to restore it.
+     *
+     * @param dn The deleted entry DN
+     * @param deletedEntry The entry which has been deleted
+     * @return A reverse LDIF
+     */
+    public static LdifEntry reverseDel( LdapDN dn, Entry deletedEntry ) throws NamingException
+    {
+        LdifEntry entry = new LdifEntry();
+
+        entry.setDn( dn );
+        entry.setChangeType( ChangeType.Add );
+
+        for ( EntryAttribute attribute : deletedEntry )
+        {
+            entry.addAttribute( attribute );
+        }
+
+        return entry;
+    }
+
+
+    /**
+    *
+    * Compute the reversed LDIF for a modify request. We will deal with the
+    * three kind of modifications :
+    * - add
+    * - remove
+    * - replace
+    *
+    * As the modifications should be issued in a reversed order ( ie, for
+    * the initials modifications {A, B, C}, the reversed modifications will
+    * be ordered like {C, B, A}), we will change the modifications order.
+    *
+    * @param dn the dn of the modified entry
+    * @param forwardModifications the modification items for the forward change
+    * @param modifiedEntry The modified entry. Necessary for the destructive modifications
+    * @return A reversed LDIF
+    * @throws NamingException If something went wrong
+    */
+    public static LdifEntry reverseModify( LdapDN dn, List<Modification> forwardModifications,
Entry modifiedEntry )
+        throws NamingException
+    {
+        // First, protect the original entry by cloning it : we will modify it
+        Entry clonedEntry = ( Entry ) modifiedEntry.clone();
+
+        LdifEntry entry = new LdifEntry();
+        entry.setChangeType( ChangeType.Modify );
+
+        entry.setDn( dn );
+
+        // As the reversed modifications should be pushed in reversed order,
+        // we create a list to temporarily store the modifications.
+        List<Modification> reverseModifications = new ArrayList<Modification>();
+
+        // Loop through all the modifications. For each modification, we will
+        // have to apply it to the modified entry in order to be able to generate
+        // the reversed modification
+        for ( Modification modification : forwardModifications )
+        {
+            switch ( modification.getOperation() )
+            {
+                case ADD_ATTRIBUTE:
+                    EntryAttribute mod = modification.getAttribute();
+
+                    EntryAttribute previous = modifiedEntry.get( mod.getId() );
+
+                    if ( mod.equals( previous ) )
+                    {
+                        continue;
+                    }
+
+                    Modification reverseModification = new ClientModification( ModificationOperation.REMOVE_ATTRIBUTE,
+                        mod );
+                    reverseModifications.add( 0, reverseModification );
+                    break;
+
+                case REMOVE_ATTRIBUTE:
+                    mod = modification.getAttribute();
+
+                    previous = modifiedEntry.get( mod.getId() );
+
+                    if ( previous == null )
+                    {
+                        // Nothing to do if the previous attribute didn't exist
+                        continue;
+                    }
+
+                    if ( mod.get() == null )
+                    {
+                        reverseModification = new ClientModification( ModificationOperation.ADD_ATTRIBUTE,
previous );
+                        reverseModifications.add( 0, reverseModification );
+                        continue;
+                    }
+
+                    reverseModification = new ClientModification( ModificationOperation.ADD_ATTRIBUTE,
mod );
+                    reverseModifications.add( 0, reverseModification );
+                    break;
+
+                case REPLACE_ATTRIBUTE:
+                    mod = modification.getAttribute();
+
+                    previous = modifiedEntry.get( mod.getId() );
+
+                    /*
+                     * The server accepts without complaint replace 
+                     * modifications to non-existing attributes in the 
+                     * entry.  When this occurs nothing really happens
+                     * but this method freaks out.  To prevent that we
+                     * make such no-op modifications produce the same
+                     * modification for the reverse direction which should
+                     * do nothing as well.  
+                     */
+                    if ( mod.get() == null && previous == null )
+                    {
+                        reverseModification = new ClientModification( ModificationOperation.REPLACE_ATTRIBUTE,
+                            new DefaultClientAttribute( mod.getId() ) );
+                        reverseModifications.add( 0, reverseModification );
+                        continue;
+                    }
+
+                    if ( mod.get() == null )
+                    {
+                        reverseModification = new ClientModification( ModificationOperation.REPLACE_ATTRIBUTE,
previous );
+                        reverseModifications.add( 0, reverseModification );
+                        continue;
+                    }
+
+                    if ( previous == null )
+                    {
+                        EntryAttribute emptyAttribute = new DefaultClientAttribute( mod.getId()
);
+                        reverseModification = new ClientModification( ModificationOperation.REPLACE_ATTRIBUTE,
+                            emptyAttribute );
+                        reverseModifications.add( 0, reverseModification );
+                        continue;
+                    }
+
+                    reverseModification = new ClientModification( ModificationOperation.REPLACE_ATTRIBUTE,
previous );
+                    reverseModifications.add( 0, reverseModification );
+                    break;
+
+                default:
+                    break; // Do nothing
+
+            }
+
+            AttributeUtils.applyModification( clonedEntry, modification );
+
+        }
+
+        // Special case if we don't have any reverse modifications
+        if ( reverseModifications.size() == 0 )
+        {
+            throw new IllegalArgumentException( "Could not deduce reverse modifications from
provided modifications: "
+                + forwardModifications );
+        }
+
+        // Now, push the reversed list into the entry
+        for ( Modification modification : reverseModifications )
+        {
+            entry.addModificationItem( modification );
+        }
+
+        // Return the reverted entry
+        return entry;
+    }
+
+
+    /**
+     * Compute a reverse LDIF for a forward change which if in LDIF format
+     * would represent a Move operation. Hence there is no newRdn in the
+     * picture here.
+     *
+     * @param newSuperiorDn the new parent dn to be (must not be null)
+     * @param modifiedDn the dn of the entry being moved (must not be null)
+     * @return a reverse LDIF
+     * @throws NamingException if something went wrong
+     */
+    public static LdifEntry reverseMove( LdapDN newSuperiorDn, LdapDN modifiedDn ) throws
NamingException
+    {
+        LdifEntry entry = new LdifEntry();
+        LdapDN currentParent = null;
+        Rdn currentRdn = null;
+        LdapDN newDn = null;
+
+        if ( newSuperiorDn == null )
+        {
+            throw new NullPointerException( "newSuperiorDn must not be null" );
+        }
+
+        if ( modifiedDn == null )
+        {
+            throw new NullPointerException( "modifiedDn must not be null" );
+        }
+
+        if ( modifiedDn.size() == 0 )
+        {
+            throw new IllegalArgumentException( "Don't think about moving the rootDSE." );
+        }
+
+        currentParent = ( LdapDN ) modifiedDn.clone();
+        currentRdn = currentParent.getRdn();
+        currentParent.remove( currentParent.size() - 1 );
+
+        newDn = ( LdapDN ) newSuperiorDn.clone();
+        newDn.add( modifiedDn.getRdn() );
+
+        entry.setChangeType( ChangeType.ModDn );
+        entry.setDn( newDn );
+        entry.setNewRdn( currentRdn.getUpName() );
+        entry.setNewSuperior( currentParent.getUpName() );
+        entry.setDeleteOldRdn( false );
+        return entry;
+    }
+
+
+    /**
+     * Revert a DN to it's previous version by removing the first RDN and adding the given
RDN.
+     * It's a rename operation. The biggest issue is that we have many corner cases, depending

+     * on the RDNs we are manipulating, and on the content of the initial entry.
+     * 
+     * @param entry The initial Entry
+     * @param newRdn The new RDN
+     * @param deleteOldRdn A flag which tells to delete the old RDN AVAs
+     * @return A list of LDIF reverted entries 
+     * @throws NamingException If the name reverting failed
+     */
+    public static List<LdifEntry> reverseRename( Entry entry, Rdn newRdn, boolean deleteOldRdn
) throws NamingException
+    {
+        LdapDN parentDn = entry.getDn();
+        LdapDN newDn = null;
+
+        if ( newRdn == null )
+        {
+            throw new NullPointerException( "The newRdn must not be null" );
+        }
+
+        if ( parentDn == null )
+        {
+            throw new NullPointerException( "The modified Dn must not be null" );
+        }
+
+        if ( parentDn.size() == 0 )
+        {
+            throw new IllegalArgumentException( "Don't think about renaming the rootDSE."
);
+        }
+
+        parentDn = ( LdapDN ) entry.getDn().clone();
+        Rdn oldRdn = parentDn.getRdn();
+
+        newDn = ( LdapDN ) parentDn.clone();
+        newDn.remove( newDn.size() - 1 );
+        newDn.add( newRdn );
+
+        List<LdifEntry> entries = new ArrayList<LdifEntry>( 1 );
+        LdifEntry reverted = new LdifEntry();
+
+        // Start with the cases here
+        if ( newRdn.size() == 1 )
+        {
+            // We have a simple new RDN, something like A=a
+            if ( oldRdn.size() == 1 ) 
+            {
+                // We have a simple old RDN, something like A=a
+                // Check if the values overlap
+                if ( oldRdn.equals( newRdn ) )
+                {
+                    // They do : we can't rename the entry, just get out
+                    // with an error
+                    // Case 0
+                    throw new NamingException( "Can't rename an entry using the same name
..." ); 
+                }
+                else
+                {
+                    // No overlapping (Case 1.1, 1.2, 2.1 and 2.2)
+                    reverted.setChangeType( ChangeType.ModRdn );
+                    reverted.setDn( newDn );
+                    reverted.setNewRdn( oldRdn.getUpName() );
+
+                    // Is the newRdn's value present in the entry ?
+                    // (cases 1.1 and 2.1 if keepOldRdn = false)
+                    // (cases 1.2 and 2.2 if keepOldRdn = true)
+                    boolean keepOldRdn = entry.contains( newRdn.getNormType(), newRdn.getNormValue()
);
+
+                    reverted.setDeleteOldRdn( !keepOldRdn );
+
+                    entries.add( reverted );
+                    return entries;
+                }
+            }
+            else
+            {
+                // We have a composite old RDN, something like A=a+B=b
+                // It does not matter if the RDNs overlap
+                reverted.setChangeType( ChangeType.ModRdn );
+                reverted.setDn( newDn );
+                reverted.setNewRdn( oldRdn.getUpName() );
+
+                // Is the newRdn's value present in the entry ?
+                // ( case 3, 4 and 5)
+                // If keepOldRdn = true, we cover case 4 and 5
+                boolean keepOldRdn = entry.contains( newRdn.getNormType(), newRdn.getNormValue()
);
+
+                reverted.setDeleteOldRdn( !keepOldRdn );
+
+                entries.add( reverted );
+                return entries;
+            }
+        }
+        else
+        {
+            // We have a composite new RDN, something like A=a+B=b
+            if ( oldRdn.size() == 1 )
+            {
+                // The old RDN is simple
+                boolean overlapping = false;
+                boolean existInEntry = false;
+                
+                // Does it overlap ?
+                // Is the new RDN AVAs contained into the entry?
+                for ( AttributeTypeAndValue atav:newRdn )
+                {
+                    if ( atav.equals( oldRdn.getAtav() ) )
+                    {
+                        // They overlap
+                        overlapping = true;
+                    }
+                    else
+                    {
+                        if ( entry.contains( atav.getNormType(), (String)atav.getNormValue()
) )
+                        {
+                            existInEntry = true;
+                        }
+                    }
+                }
+                
+                if ( overlapping )
+                {
+                    // The new RDN includes the old one
+                    if ( existInEntry )
+                    {
+                        // Some of the new RDN AVAs existed in the entry
+                        // We have to restore them, but we also have to remove
+                        // the new values
+                        reverted.setChangeType( ChangeType.ModRdn );
+                        reverted.setDn( newDn );
+                        reverted.setNewRdn( oldRdn.getUpName() );
+                        
+                        // Delete the newRDN values
+                        reverted.setDeleteOldRdn( KEEP_OLD_RDN );
+                        
+                        entries.add( reverted );
+                        
+                        // Now, restore the initial values
+                        LdifEntry restored = new LdifEntry();
+                        restored.setChangeType( ChangeType.Modify );
+                        
+                        // We have to use the parent DN, the entry has already
+                        // been renamed
+                        restored.setDn( parentDn );
+                        
+                        for ( AttributeTypeAndValue ava:newRdn )
+                        {
+                            // No need to add something which has already been added
+                            // in the previous modification
+                            if ( !entry.contains( ava.getNormType(), (String)ava.getNormValue()
) &&
+                                 !(ava.getNormType().equals( oldRdn.getNormType() ) &&
+                                   ava.getNormValue().equals( oldRdn.getNormValue() ) ) )
+                            {
+                                // Create the modification, which is an Add
+                                Modification modification = new ClientModification( 
+                                    ModificationOperation.REMOVE_ATTRIBUTE, 
+                                    new DefaultClientAttribute( oldRdn.getUpType(), (String)ava.getUpValue()
) );
+                                
+                                restored.addModificationItem( modification );
+                            }
+                        }
+                        
+                        entries.add( restored );
+                    }
+                    else
+                    {
+                        // This is the simplest case, we don't have to restore
+                        // some existing values (case 8.1 and 9.1)
+                        reverted.setChangeType( ChangeType.ModRdn );
+                        reverted.setDn( newDn );
+                        reverted.setNewRdn( oldRdn.getUpName() );
+                        
+                        // Delete the newRDN values
+                        reverted.setDeleteOldRdn( DELETE_OLD_RDN );
+                        
+                        entries.add( reverted );
+                    }
+                    
+                    return entries;
+                }
+                else
+                {
+                    if ( existInEntry )
+                    {
+                        // Some of the new RDN AVAs existed in the entry
+                        // We have to restore them, but we also have to remove
+                        // the new values
+                        reverted.setChangeType( ChangeType.ModRdn );
+                        reverted.setDn( newDn );
+                        reverted.setNewRdn( oldRdn.getUpName() );
+                        
+                        // Delete the newRDN values
+                        reverted.setDeleteOldRdn( KEEP_OLD_RDN );
+                        
+                        entries.add( reverted );
+                        
+                        // Now, restore the initial values
+                        LdifEntry restored = new LdifEntry();
+                        restored.setChangeType( ChangeType.Modify );
+                        
+                        // We have to use the parent DN, the entry has already
+                        // been renamed
+                        restored.setDn( parentDn );
+                        
+                        for ( AttributeTypeAndValue ava:newRdn )
+                        {
+                            // No need to add something which has already been added
+                            // in the previous modification
+                            if ( !entry.contains( ava.getNormType(), (String)ava.getNormValue()
) &&
+                                 !(ava.getNormType().equals( oldRdn.getNormType() ) &&
+                                   ava.getNormalizedValue().equals( oldRdn.getNormValue()
) ) )
+                            {
+                                // Create the modification, which is an Add
+                                Modification modification = new ClientModification( 
+                                    ModificationOperation.REMOVE_ATTRIBUTE, 
+                                    new DefaultClientAttribute( oldRdn.getUpType(), (String)ava.getUpValue()
) );
+                                
+                                restored.addModificationItem( modification );
+                            }
+                        }
+                        
+                        entries.add( restored );
+                    }
+                    else
+                    {
+                        // A much simpler case, as we just have to remove the newRDN
+                        reverted.setChangeType( ChangeType.ModRdn );
+                        reverted.setDn( newDn );
+                        reverted.setNewRdn( oldRdn.getUpName() );
+
+                        reverted.setDeleteOldRdn( DELETE_OLD_RDN );
+
+                        entries.add( reverted );
+                    }
+                    
+                    return entries;
+                }
+            }
+            else
+            {
+                // We have a composite new RDN, something like A=a+B=b
+                // Does the RDN overlap ?
+                boolean overlapping = false;
+                boolean existInEntry = false;
+                
+                Set<AttributeTypeAndValue> oldAtavs = new HashSet<AttributeTypeAndValue>();
+
+                // We first build a set with all the oldRDN ATAVs 
+                for ( AttributeTypeAndValue atav:oldRdn )
+                {
+                    oldAtavs.add( atav );
+                }
+                
+                // Now we loop on the newRDN ATAVs to evaluate if the Rdns are overlaping
+                // and if the newRdn ATAVs are present in the entry
+                for ( AttributeTypeAndValue atav:newRdn )
+                {
+                    if ( oldAtavs.contains( atav ) )
+                    {
+                        overlapping = true;
+                    }
+                    else if ( entry.contains( atav.getNormType(), (String)atav.getNormValue()
) )
+                    {
+                        existInEntry = true;
+                    }
+                }
+                
+                if ( overlapping ) 
+                {
+                    // They overlap
+                    if ( existInEntry )
+                    {
+                        // In this case, we have to reestablish the removed ATAVs
+                        // (Cases 12.2 and 13.2)
+                        reverted.setChangeType( ChangeType.ModRdn );
+                        reverted.setDn( newDn );
+                        reverted.setNewRdn( oldRdn.getUpName() );
+    
+                        reverted.setDeleteOldRdn( KEEP_OLD_RDN );
+    
+                        entries.add( reverted );
+                    }
+                    else
+                    {
+                        // We can simply remove all the new RDN atavs, as the
+                        // overlapping values will be re-created.
+                        // (Cases 12.1 and 13.1)
+                        reverted.setChangeType( ChangeType.ModRdn );
+                        reverted.setDn( newDn );
+                        reverted.setNewRdn( oldRdn.getUpName() );
+    
+                        reverted.setDeleteOldRdn( DELETE_OLD_RDN );
+    
+                        entries.add( reverted );
+                    }
+                }
+                else
+                {
+                    // No overlapping
+                    if ( existInEntry )
+                    {
+                        // In this case, we have to reestablish the removed ATAVs
+                        // (Cases 10.2 and 11.2)
+                        reverted.setChangeType( ChangeType.ModRdn );
+                        reverted.setDn( newDn );
+                        reverted.setNewRdn( oldRdn.getUpName() );
+    
+                        reverted.setDeleteOldRdn( KEEP_OLD_RDN );
+    
+                        entries.add( reverted );
+                        
+                        // Now, restore the initial values
+                        LdifEntry restored = new LdifEntry();
+                        restored.setChangeType( ChangeType.Modify );
+                        
+                        // We have to use the parent DN, the entry has already
+                        // been renamed
+                        restored.setDn( parentDn );
+                        
+                        for ( AttributeTypeAndValue ava:newRdn )
+                        {
+                            // No need to add something which has already been added
+                            // in the previous modification
+                            if ( !entry.contains( ava.getNormType(), (String)ava.getNormValue()
) &&
+                                 !(ava.getNormType().equals( oldRdn.getNormType() ) &&
+                                   ava.getNormalizedValue().equals( oldRdn.getNormValue()
) ) )
+                            {
+                                // Create the modification, which is an Add
+                                Modification modification = new ClientModification( 
+                                    ModificationOperation.REMOVE_ATTRIBUTE, 
+                                    new DefaultClientAttribute( oldRdn.getUpType(), (String)ava.getUpValue()
) );
+                                
+                                restored.addModificationItem( modification );
+                            }
+                        }
+                        
+                        entries.add( restored );
+                    }
+                    else
+                    {
+                        // We are safe ! We can delete all the new Rdn ATAVs
+                        // (Cases 10.1 and 11.1)
+                        reverted.setChangeType( ChangeType.ModRdn );
+                        reverted.setDn( newDn );
+                        reverted.setNewRdn( oldRdn.getUpName() );
+    
+                        reverted.setDeleteOldRdn( DELETE_OLD_RDN );
+    
+                        entries.add( reverted );
+                    }
+                }
+            }
+        }
+
+        return entries;
+    }
+}

Modified: directory/shared/branches/shared-mina2/ldap/src/main/java/org/apache/directory/shared/ldap/ldif/LdifUtils.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/shared-mina2/ldap/src/main/java/org/apache/directory/shared/ldap/ldif/LdifUtils.java?rev=721052&r1=721051&r2=721052&view=diff
==============================================================================
--- directory/shared/branches/shared-mina2/ldap/src/main/java/org/apache/directory/shared/ldap/ldif/LdifUtils.java
(original)
+++ directory/shared/branches/shared-mina2/ldap/src/main/java/org/apache/directory/shared/ldap/ldif/LdifUtils.java
Wed Nov 26 17:00:32 2008
@@ -20,7 +20,6 @@
 package org.apache.directory.shared.ldap.ldif;
 
 import java.io.UnsupportedEncodingException;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -31,10 +30,8 @@
 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.ClientBinaryValue;
-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.name.LdapDN;
@@ -531,140 +528,6 @@
     
     
     /**
-     * Compute a reverse LDIF of an AddRequest. It's simply a delete request
-     * of the added entry
-     *
-     * @param dn the dn of the added entry
-     * @return a reverse LDIF
-     */
-    public static LdifEntry reverseAdd( LdapDN dn )
-    {
-        LdifEntry entry = new LdifEntry();
-        entry.setChangeType( ChangeType.Delete );
-        entry.setDn( dn );
-        return entry;
-    }
-
-    
-    /**
-     * Compute a reverse LDIF of a DeleteRequest. We have to get the previous
-     * entry in order to restore it.
-     *
-     * @param dn The deleted entry DN
-     * @param deletedEntry The entry which has been deleted
-     * @return A reverse LDIF
-     */
-    public static LdifEntry reverseDel( LdapDN dn, Entry deletedEntry ) throws NamingException
-    {
-        LdifEntry entry = new LdifEntry();
-        
-        entry.setDn( dn );
-        entry.setChangeType( ChangeType.Add );
-        
-        for ( EntryAttribute attribute:deletedEntry )
-        {
-            entry.addAttribute( attribute );
-        }       
-
-        return entry;
-    }
-    
-    
-    /**
-     * Compute a reverse LDIF for a forward change which if in LDIF format
-     * would represent a moddn operation.  Hence there is no newRdn in the
-     * picture here.
-     *
-     * @param newSuperiorDn the new parent dn to be (must not be null)
-     * @param modifiedDn the dn of the entry being moved (must not be null)
-     * @return a reverse LDIF
-     * @throws NamingException if something went wrong
-     */
-    public static LdifEntry reverseModifyDn( LdapDN newSuperiorDn, LdapDN modifiedDn ) throws
NamingException
-    {
-        LdifEntry entry = new LdifEntry();
-        LdapDN currentParent = null;
-        LdapDN newDn = null;
-
-        if ( newSuperiorDn == null )
-        {
-            throw new NullPointerException( "newSuperiorDn must not be null" );
-        }
-
-        if ( modifiedDn == null )
-        {
-            throw new NullPointerException( "modifiedDn must not be null" );
-        }
-
-        if ( modifiedDn.size() == 0 )
-        {
-            throw new IllegalArgumentException( "Don't think about moving the rootDSE." );
-        }
-
-        currentParent = ( LdapDN ) modifiedDn.clone();
-        currentParent.remove( currentParent.size() - 1 );
-
-        newDn = ( LdapDN ) newSuperiorDn.clone();
-        newDn.add( modifiedDn.getRdn() );
-
-        entry.setChangeType( ChangeType.ModDn );
-        entry.setDn( newDn );
-        entry.setNewSuperior( currentParent.getUpName() );
-        entry.setDeleteOldRdn( false );
-        return entry;
-    }
-
-
-    /**
-     * Revert a DN to it's previous version by removing the first RDN and adding the given
RDN
-     *
-     * @param t0 The initial Attributes
-     * @param t0_dn The initial DN
-     * @param t1_rdn The new RDN
-     * @return A new LDIF entry with a reverted DN
-     * @throws NamingException If the name reverting failed
-     */
-    public static List<LdifEntry> reverseRename( Attributes t0, LdapDN t0_dn, Rdn t1_rdn
) throws NamingException
-    {
-        LdifEntry entry = new LdifEntry();
-        LdapDN parent = null;
-        LdapDN newDn = null;
-
-        if ( t1_rdn == null )
-        {
-            throw new NullPointerException( "newRdn must not be null" );
-        }
-
-        if ( t0_dn == null )
-        {
-            throw new NullPointerException( "modifiedDn must not be null" );
-        }
-
-        if ( t0_dn.size() == 0 )
-        {
-            throw new IllegalArgumentException( "Don't think about renaming the rootDSE."
);
-        }
-
-        parent = ( LdapDN ) t0_dn.clone();
-        parent.remove( parent.size() - 1 );
-
-        newDn = ( LdapDN ) parent.clone();
-        newDn.add( t1_rdn );
-
-        List<LdifEntry> entries = new ArrayList<LdifEntry>(1);
-        
-        entry.setChangeType( ChangeType.ModRdn );
-        entry.setDeleteOldRdn( reverseDoDeleteOldRdn( t0, t1_rdn ) );
-        entry.setDn( newDn );
-        entry.setNewRdn( t0_dn.getRdn().getUpName() );
-        
-        entries.add( entry );
-        return entries;
-    }
-
-
-
-    /**
      * Compute a reverse LDIF for a forward change which if in LDIF format
      * would represent a modrdn operation.
      *
@@ -688,21 +551,12 @@
             throw new IllegalArgumentException( "Don't think about a move op on the rootDSE."
);
         }
 
-        // if there is no new superior in the picture then this is a rename
-        // operation where the parent is retained and only the rdn is changed
-        // We still have to take care that the entry attributes are correctly
-        // restored if the new RDN has more than one AVAs
-        if ( t1_parentDn == null )
-        {
-            return reverseRename( t0, t0_dn, t1_rdn );
-        }
-
         // if there is no rdn change then this is a raw move operation without
         // a name change, we can delegate this to a simpler method
         if ( t1_rdn == null )
         {
             List<LdifEntry> entries = new ArrayList<LdifEntry>(1);
-            LdifEntry entry = reverseModifyDn( t1_parentDn, t0_dn );
+            LdifEntry entry = LdifRevertor.reverseMove( t1_parentDn, t0_dn );
             entries.add( entry );
             
             return entries;
@@ -774,149 +628,5 @@
         // if there we do not
         return t0_attr == null || ! t0_attr.contains( t1_rdn.getUpValue() );
     }
-
-
-    /**
-     *
-     * Compute the reversed LDIF for a modify request. We will deal with the
-     * three kind of modifications :
-     * - add
-     * - remove
-     * - replace
-     *
-     * As the modifications should be issued in a reversed order ( ie, for
-     * the initials modifications {A, B, C}, the reversed modifications will
-     * be ordered like {C, B, A}), we will change the modifications order.
-     *
-     * @param dn the dn of the modified entry
-     * @param forwardModifications the modification items for the forward change
-     * @param modifiedEntry The modified entry. Necessary for the destructive modifications
-     * @return A reversed LDIF
-     * @throws NamingException If something went wrong
-     */
-    public static LdifEntry reverseModify( LdapDN dn, List<Modification> forwardModifications,
-                                       Entry modifiedEntry ) throws NamingException
-    {
-        // First, protect the original entry by cloning it : we will modify it
-        Entry clonedEntry = ( Entry ) modifiedEntry.clone();
-
-        LdifEntry entry = new LdifEntry();
-        entry.setChangeType( ChangeType.Modify );
-
-        entry.setDn( dn );
-
-        // As the reversed modifications should be pushed in reversed order,
-        // we create a list to temporarily store the modifications.
-        List<Modification> reverseModifications = new ArrayList<Modification>();
-
-        // Loop through all the modifications. For each modification, we will
-        // have to apply it to the modified entry in order to be able to generate
-        // the reversed modification
-        for ( Modification modification : forwardModifications )
-        {
-            switch ( modification.getOperation() )
-            {
-                case ADD_ATTRIBUTE :
-                    EntryAttribute mod = modification.getAttribute();
-
-                    EntryAttribute previous = modifiedEntry.get( mod.getId() );
-
-                    if ( mod.equals( previous ) )
-                    {
-                        continue;
-                    }
-
-                    Modification reverseModification = new ClientModification( ModificationOperation.REMOVE_ATTRIBUTE,
mod );
-                    reverseModifications.add( 0, reverseModification );
-                    break;
-
-                case REMOVE_ATTRIBUTE :
-                    mod = modification.getAttribute();
-
-                    previous = modifiedEntry.get( mod.getId() );
-
-                    if ( previous == null )
-                    {
-                        // Nothing to do if the previous attribute didn't exist
-                        continue;
-                    }
-
-                    if ( mod.get() == null )
-                    {
-                        reverseModification = new ClientModification( ModificationOperation.ADD_ATTRIBUTE,
previous );
-                        reverseModifications.add( 0, reverseModification );
-                        continue;
-                    }
-
-                    reverseModification = new ClientModification( ModificationOperation.ADD_ATTRIBUTE,
mod );
-                    reverseModifications.add( 0, reverseModification );
-                    break;
-
-                case REPLACE_ATTRIBUTE :
-                    mod = modification.getAttribute();
-
-                    previous = modifiedEntry.get( mod.getId() );
-
-                    /*
-                     * The server accepts without complaint replace 
-                     * modifications to non-existing attributes in the 
-                     * entry.  When this occurs nothing really happens
-                     * but this method freaks out.  To prevent that we
-                     * make such no-op modifications produce the same
-                     * modification for the reverse direction which should
-                     * do nothing as well.  
-                     */
-                    if ( mod.get() == null && previous == null )
-                    {
-                        reverseModification = new ClientModification( ModificationOperation.REPLACE_ATTRIBUTE,

-                            new DefaultClientAttribute( mod.getId() ) );
-                        reverseModifications.add( 0, reverseModification );
-                        continue;
-                    }
-                    
-                    if ( mod.get() == null )
-                    {
-                        reverseModification = new ClientModification( ModificationOperation.REPLACE_ATTRIBUTE,
previous );
-                        reverseModifications.add( 0, reverseModification );
-                        continue;
-                    }
-
-                    if ( previous == null )
-                    {
-                        EntryAttribute emptyAttribute = new DefaultClientAttribute( mod.getId()
);
-                        reverseModification = new ClientModification( ModificationOperation.REPLACE_ATTRIBUTE,
emptyAttribute );
-                        reverseModifications.add( 0, reverseModification );
-                        continue;
-                    }
-
-                    reverseModification = new ClientModification( ModificationOperation.REPLACE_ATTRIBUTE,
previous );
-                    reverseModifications.add( 0, reverseModification );
-                    break;
-                    
-                default :
-                    break; // Do nothing
-                    
-            }
-
-            AttributeUtils.applyModification( clonedEntry, modification );
-
-        }
-
-        // Special case if we don't have any reverse modifications
-        if ( reverseModifications.size() == 0 )
-        {
-            throw new IllegalArgumentException( "Could not deduce reverse modifications from
provided modifications: "
-                    + forwardModifications );
-        }
-
-        // Now, push the reversed list into the entry
-        for ( Modification modification:reverseModifications )
-        {
-            entry.addModificationItem( modification );
-        }
-
-        // Return the reverted entry
-        return entry;
-    }
 }
 



Mime
View raw message