From 92942bc98880793237d38d87db45abd5537d3f62 Mon Sep 17 00:00:00 2001
From: jvergara <jvergara@localhost>
Date: Thu, 17 Dec 2009 21:11:04 +0000
Subject: [PATCH] Fix for issue 3601 (Control Panel: unable to modify schema objects or attributes)

---
 opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/task/DeleteSchemaElementsTask.java |  444 +++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 409 insertions(+), 35 deletions(-)

diff --git a/opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/task/DeleteSchemaElementsTask.java b/opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/task/DeleteSchemaElementsTask.java
index de93f67..2e27b76 100644
--- a/opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/task/DeleteSchemaElementsTask.java
+++ b/opendj-sdk/opends/src/guitools/org/opends/guitools/controlpanel/task/DeleteSchemaElementsTask.java
@@ -32,8 +32,12 @@
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import javax.naming.NamingException;
@@ -60,6 +64,7 @@
 import org.opends.server.types.ModificationType;
 import org.opends.server.types.ObjectClass;
 import org.opends.server.types.OpenDsException;
+import org.opends.server.types.Schema;
 import org.opends.server.types.SchemaFileElement;
 import org.opends.server.util.LDIFReader;
 import org.opends.server.util.LDIFWriter;
@@ -69,24 +74,94 @@
  */
 public class DeleteSchemaElementsTask extends Task
 {
-  ArrayList<ObjectClass> ocsToDelete = new ArrayList<ObjectClass>();
-  ArrayList<AttributeType> attrsToDelete = new ArrayList<AttributeType>();
-  private Set<String> backendSet;
+  // The list of object classes that the user asked to delete.
+  LinkedHashSet<ObjectClass> providedOcsToDelete =
+    new LinkedHashSet<ObjectClass>();
+  // The list of attributes that the user asked to delete.
+  LinkedHashSet<AttributeType> providedAttrsToDelete =
+    new LinkedHashSet<AttributeType>();
+  // The list of object classes that will be actually deleted (some might be
+  // recreated).
+  LinkedHashSet<ObjectClass> ocsToDelete = new LinkedHashSet<ObjectClass>();
+  // The list of attributes that will be actually deleted (some might be
+  // recreated).
+  LinkedHashSet<AttributeType> attrsToDelete =
+    new LinkedHashSet<AttributeType>();
+  // The list of object classes that will be recreated.
+  LinkedHashSet<ObjectClass> ocsToAdd = new LinkedHashSet<ObjectClass>();
+  // The list of attributes that will be recreated.
+  LinkedHashSet<AttributeType> attrsToAdd = new LinkedHashSet<AttributeType>();
 
   /**
    * Constructor of the task.
    * @param info the control panel information.
    * @param dlg the progress dialog where the task progress will be displayed.
-   * @param ocsToDelete the object classes that must be deleted.
-   * @param attrsToDelete the attributes that must be deleted.
+   * @param ocsToDelete the object classes that must be deleted (ordered).
+   * @param attrsToDelete the attributes that must be deleted (ordered).
    */
   public DeleteSchemaElementsTask(ControlPanelInfo info, ProgressDialog dlg,
-      List<ObjectClass> ocsToDelete, List<AttributeType> attrsToDelete)
+      LinkedHashSet<ObjectClass> ocsToDelete,
+      LinkedHashSet<AttributeType> attrsToDelete)
   {
     super(info, dlg);
-    this.ocsToDelete.addAll(ocsToDelete);
-    this.attrsToDelete.addAll(attrsToDelete);
-    backendSet = new HashSet<String>();
+
+    this.providedOcsToDelete.addAll(ocsToDelete);
+    this.providedAttrsToDelete.addAll(attrsToDelete);
+
+    Schema schema = info.getServerDescriptor().getSchema();
+    LinkedHashSet<AttributeType> allAttrsToDelete =
+      DeleteSchemaElementsTask.getOrderedAttributesToDelete(attrsToDelete,
+          schema);
+    LinkedHashSet<ObjectClass> allOcsToDelete = null;
+    if (!attrsToDelete.isEmpty())
+    {
+      allOcsToDelete =
+        DeleteSchemaElementsTask.getOrderedObjectClassesToDeleteFromAttrs(
+          attrsToDelete, schema);
+    }
+    if (!ocsToDelete.isEmpty())
+    {
+      if (allOcsToDelete == null)
+      {
+      allOcsToDelete =
+        DeleteSchemaElementsTask.getOrderedObjectClassesToDelete(
+            ocsToDelete, schema);
+      }
+      else
+      {
+        allOcsToDelete.addAll(
+            DeleteSchemaElementsTask.getOrderedObjectClassesToDelete(
+                ocsToDelete, schema));
+      }
+    }
+    ArrayList<AttributeType> lAttrsToDelete =
+      new ArrayList<AttributeType>(allAttrsToDelete);
+    for (int i = lAttrsToDelete.size() - 1; i >= 0; i--)
+    {
+      AttributeType attrToDelete = lAttrsToDelete.get(i);
+      if (!attrsToDelete.contains(attrToDelete))
+      {
+        AttributeType attrToAdd = getAttributeToAdd(attrToDelete);
+        if (attrToAdd != null)
+        {
+          attrsToAdd.add(attrToAdd);
+        }
+      }
+    }
+
+    ArrayList<ObjectClass> lOcsToDelete =
+      new ArrayList<ObjectClass>(allOcsToDelete);
+    for (int i = lOcsToDelete.size() - 1; i >= 0; i--)
+    {
+      ObjectClass ocToDelete = lOcsToDelete.get(i);
+      if (!ocsToDelete.contains(ocToDelete))
+      {
+        ocsToAdd.add(getObjectClassToAdd(lOcsToDelete.get(i)));
+      }
+    }
+
+    this.ocsToDelete.addAll(allOcsToDelete);
+    this.attrsToDelete.addAll(allAttrsToDelete);
   }
 
   /**
@@ -94,7 +169,7 @@
    */
   public Set<String> getBackends()
   {
-    return backendSet;
+    return Collections.emptySet();
   }
 
   /**
@@ -103,7 +178,17 @@
   public boolean canLaunch(Task taskToBeLaunched,
       Collection<Message> incompatibilityReasons)
   {
-    return true;
+    boolean canLaunch = true;
+    if (state == State.RUNNING &&
+        (taskToBeLaunched.getType() == Task.Type.DELETE_SCHEMA_ELEMENT ||
+         taskToBeLaunched.getType() == Task.Type.MODIFY_SCHEMA_ELEMENT ||
+         taskToBeLaunched.getType() == Task.Type.NEW_SCHEMA_ELEMENT))
+    {
+      incompatibilityReasons.add(getIncompatibilityMessage(this,
+            taskToBeLaunched));
+      canLaunch = false;
+    }
+    return canLaunch;
   }
 
   /**
@@ -111,18 +196,7 @@
    */
   public Type getType()
   {
-    if (attrsToDelete.isEmpty())
-    {
-      return Type.DELETE_OBJECTCLASS;
-    }
-    else if (ocsToDelete.isEmpty())
-    {
-      return Type.DELETE_ATTRIBUTE;
-    }
-    else
-    {
-      return Type.DELETE_OBJECTCLASS;
-    }
+    return Type.NEW_SCHEMA_ELEMENT;
   }
 
   /**
@@ -156,9 +230,9 @@
   /**
    * {@inheritDoc}
    */
-  protected ArrayList<String> getCommandLineArguments()
+  protected List<String> getCommandLineArguments()
   {
-    return new ArrayList<String>();
+    return Collections.emptyList();
   }
 
   /**
@@ -178,8 +252,9 @@
     final boolean[] isFirst = {true};
     final int totalNumber = ocsToDelete.size() + attrsToDelete.size();
     int numberDeleted = 0;
-    for (final ObjectClass objectClass : ocsToDelete)
+    for (ObjectClass objectClass : ocsToDelete)
     {
+      final ObjectClass fObjectclass = objectClass;
       SwingUtilities.invokeLater(new Runnable()
       {
         public void run()
@@ -189,11 +264,11 @@
             getProgressDialog().appendProgressHtml("<br><br>");
           }
           isFirst[0] = false;
-          printEquivalentCommandToDelete(objectClass);
+          printEquivalentCommandToDelete(fObjectclass);
           getProgressDialog().appendProgressHtml(
               Utilities.getProgressWithPoints(
                   INFO_CTRL_PANEL_DELETING_OBJECTCLASS.get(
-                  objectClass.getNameOrOID()),
+                      fObjectclass.getNameOrOID()),
                   ColorAndFontConstants.progressFont));
         }
       });
@@ -236,8 +311,9 @@
       });
     }
 
-    for (final AttributeType attribute : attrsToDelete)
+    for (AttributeType attribute : attrsToDelete)
     {
+      final AttributeType fAttribute = attribute;
       SwingUtilities.invokeLater(new Runnable()
       {
         public void run()
@@ -247,11 +323,11 @@
             getProgressDialog().appendProgressHtml("<br><br>");
           }
           isFirst[0] = false;
-          printEquivalentCommandToDelete(attribute);
+          printEquivalentCommandToDelete(fAttribute);
           getProgressDialog().appendProgressHtml(
               Utilities.getProgressWithPoints(
                   INFO_CTRL_PANEL_DELETING_ATTRIBUTE.get(
-                  attribute.getNameOrOID()),
+                      fAttribute.getNameOrOID()),
                   ColorAndFontConstants.progressFont));
         }
       });
@@ -296,6 +372,25 @@
       });
     }
 
+    if (!ocsToAdd.isEmpty() || !attrsToAdd.isEmpty())
+    {
+      SwingUtilities.invokeLater(new Runnable()
+      {
+        public void run()
+        {
+          getProgressDialog().appendProgressHtml(Utilities.applyFont(
+              "<br><br>"+
+              INFO_CTRL_PANEL_EXPLANATION_TO_DELETE_REFERENCED_ELEMENTS.get()+
+              "<br><br>",
+              ColorAndFontConstants.progressFont));
+        }
+      });
+
+      NewSchemaElementsTask createTask =
+        new NewSchemaElementsTask(getInfo(), getProgressDialog(), ocsToAdd,
+            attrsToAdd);
+      createTask.runTask();
+    }
   }
 
   /**
@@ -431,9 +526,19 @@
     String attrValue = getSchemaFileAttributeValue(element);
     if (!isServerRunning())
     {
+      Message msg;
+      if (element instanceof AttributeType)
+      {
+        msg = INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_DELETE_ATTRIBUTE_OFFLINE.get(
+            element.getNameOrOID(), schemaFile);
+      }
+      else
+      {
+        msg = INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_DELETE_OBJECTCLASS_OFFLINE.get(
+            element.getNameOrOID(), schemaFile);
+      }
       getProgressDialog().appendProgressHtml(Utilities.applyFont(
-          INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_DELETE_SCHEMA_ELEMENT_OFFLINE.get(
-              schemaFile)+"<br><b>"+
+          msg+"<br><b>"+
           attrName+": "+attrValue+"</b><br><br>",
           ColorAndFontConstants.progressFont));
     }
@@ -447,10 +552,21 @@
       String equiv = getEquivalentCommandLine(getCommandLinePath("ldapmodify"),
           args);
 
+      Message msg;
+      if (element instanceof AttributeType)
+      {
+        msg = INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_DELETE_ATTRIBUTE_ONLINE.get(
+            element.getNameOrOID());
+      }
+      else
+      {
+        msg = INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_DELETE_OBJECTCLASS_ONLINE.get(
+            element.getNameOrOID());
+      }
+
       StringBuilder sb = new StringBuilder();
       sb.append(
-          INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_DELETE_SCHEMA_ELEMENT_ONLINE.get()+
-          "<br><b>");
+          msg+"<br><b>");
       sb.append(equiv);
       sb.append("<br>");
       sb.append("dn: cn=schema<br>");
@@ -462,4 +578,262 @@
           ColorAndFontConstants.progressFont));
     }
   }
+
+  private AttributeType getAttributeToAdd(AttributeType attrToDelete)
+  {
+    AttributeType attrToAdd;
+    boolean isSuperior = false;
+    AttributeType newSuperior = attrToDelete.getSuperiorType();
+    for (AttributeType attr : providedAttrsToDelete)
+    {
+      if (attr.equals(attrToDelete.getSuperiorType()))
+      {
+        isSuperior = true;
+        newSuperior = attr.getSuperiorType();
+        while (newSuperior != null &&
+            providedAttrsToDelete.contains(newSuperior))
+        {
+          newSuperior = newSuperior.getSuperiorType();
+        }
+        break;
+      }
+    }
+    if (isSuperior)
+    {
+      ArrayList<String> allNames = new ArrayList<String>();
+      for (String str : attrToDelete.getNormalizedNames())
+      {
+        allNames.add(str);
+      }
+      Map<String, List<String>> extraProperties =
+        cloneExtraProperties(attrToDelete);
+      attrToAdd = new AttributeType(
+          "",
+          attrToDelete.getPrimaryName(),
+          allNames,
+          attrToDelete.getOID(),
+          attrToDelete.getDescription(),
+          null,
+          attrToDelete.getSyntax(),
+          attrToDelete.getApproximateMatchingRule(),
+          attrToDelete.getEqualityMatchingRule(),
+          attrToDelete.getOrderingMatchingRule(),
+          attrToDelete.getSubstringMatchingRule(),
+          attrToDelete.getUsage(),
+          attrToDelete.isCollective(),
+          attrToDelete.isNoUserModification(),
+          attrToDelete.isObsolete(),
+          attrToDelete.isSingleValue(),
+          extraProperties);
+    }
+    else
+    {
+      // Nothing to be changed in the definition of the attribute itself.
+      attrToAdd = attrToDelete;
+    }
+    return attrToAdd;
+  }
+
+  private ObjectClass getObjectClassToAdd(ObjectClass ocToDelete)
+  {
+    ObjectClass ocToAdd;
+    boolean containsAttribute = false;
+    for (AttributeType attr : providedAttrsToDelete)
+    {
+      if(ocToDelete.getRequiredAttributeChain().contains(attr) ||
+      ocToDelete.getOptionalAttributeChain().contains(attr))
+      {
+        containsAttribute = true;
+        break;
+      }
+    }
+    boolean hasSuperior = false;
+    ObjectClass newSuperior = ocToDelete.getSuperiorClass();
+    for (ObjectClass oc : providedOcsToDelete)
+    {
+      if (ocToDelete.getSuperiorClass().equals(oc))
+      {
+        hasSuperior = true;
+        newSuperior = oc.getSuperiorClass();
+        while (newSuperior != null &&
+            providedOcsToDelete.contains(newSuperior))
+        {
+          newSuperior = newSuperior.getSuperiorClass();
+        }
+        break;
+      }
+    }
+    if (containsAttribute || hasSuperior)
+    {
+      ArrayList<String> allNames = new ArrayList<String>();
+      for (String str : ocToDelete.getNormalizedNames())
+      {
+        allNames.add(str);
+      }
+      Map<String, List<String>> extraProperties =
+        cloneExtraProperties(ocToDelete);
+      Set<AttributeType> required;
+      Set<AttributeType> optional;
+      if (containsAttribute)
+      {
+        required = new HashSet<AttributeType>(
+            ocToDelete.getRequiredAttributes());
+        optional = new HashSet<AttributeType>(
+            ocToDelete.getOptionalAttributes());
+        required.removeAll(providedAttrsToDelete);
+        optional.removeAll(providedAttrsToDelete);
+      }
+      else
+      {
+        required = ocToDelete.getRequiredAttributes();
+        optional = ocToDelete.getOptionalAttributes();
+      }
+      ocToAdd = new ObjectClass("",
+          ocToDelete.getPrimaryName(),
+          allNames,
+          ocToDelete.getOID(),
+          ocToDelete.getDescription(),
+          newSuperior,
+          required,
+          optional,
+          ocToDelete.getObjectClassType(),
+          ocToDelete.isObsolete(),
+          extraProperties);
+    }
+    else
+    {
+      // Nothing to be changed in the definition of the object class itself.
+      ocToAdd = ocToDelete;
+    }
+    return ocToAdd;
+  }
+
+
+  /**
+   * Returns an ordered set of the attributes that must be deleted.
+   * @param attrsToDelete the attributes to be deleted.
+   * @param schema the server schema.
+   * @return an ordered list of the attributes that must be deleted.
+   */
+  public static LinkedHashSet<AttributeType> getOrderedAttributesToDelete(
+      Collection<AttributeType> attrsToDelete, Schema schema)
+  {
+    LinkedHashSet<AttributeType> orderedAttributes =
+      new LinkedHashSet<AttributeType>();
+    for (AttributeType attribute : attrsToDelete)
+    {
+      orderedAttributes.addAll(getOrderedChildrenToDelete(attribute, schema));
+      orderedAttributes.add(attribute);
+    }
+    return orderedAttributes;
+  }
+
+  /**
+   * Returns an ordered list of the object classes that must be deleted.
+   * @param ocsToDelete the object classes to be deleted.
+   * @param schema the server schema.
+   * @return an ordered list of the object classes that must be deleted.
+   */
+  public static LinkedHashSet<ObjectClass> getOrderedObjectClassesToDelete(
+      Collection<ObjectClass> ocsToDelete, Schema schema)
+  {
+    LinkedHashSet<ObjectClass> orderedOcs = new LinkedHashSet<ObjectClass>();
+    for (ObjectClass oc : ocsToDelete)
+    {
+      orderedOcs.addAll(getOrderedChildrenToDelete(oc, schema));
+      orderedOcs.add(oc);
+    }
+    return orderedOcs;
+  }
+
+  /**
+   * Returns an ordered list of the object classes that must be deleted when
+   * deleting a list of attributes that must be deleted.
+   * @param attrsToDelete the attributes to be deleted.
+   * @param schema the server schema.
+   * @return an ordered list of the object classes that must be deleted when
+   * deleting a list of attributes that must be deleted.
+   */
+  public static LinkedHashSet<ObjectClass>
+  getOrderedObjectClassesToDeleteFromAttrs(
+      Collection<AttributeType> attrsToDelete, Schema schema)
+  {
+    LinkedHashSet<ObjectClass> orderedOcs = new LinkedHashSet<ObjectClass>();
+    ArrayList<ObjectClass> dependentClasses = new ArrayList<ObjectClass>();
+    for (AttributeType attr : attrsToDelete)
+    {
+      for (ObjectClass oc : schema.getObjectClasses().values())
+      {
+        if (oc.getRequiredAttributeChain().contains(attr))
+        {
+          dependentClasses.add(oc);
+        }
+        else if (oc.getOptionalAttributeChain().contains(attr))
+        {
+          dependentClasses.add(oc);
+        }
+      }
+    }
+    for (ObjectClass oc : dependentClasses)
+    {
+      orderedOcs.addAll(getOrderedChildrenToDelete(oc, schema));
+      orderedOcs.add(oc);
+    }
+    return orderedOcs;
+  }
+
+  /**
+   * Clones the extra properties of the provided schema element.  This can
+   * be used when copying schema elements.
+   * @param element the schema element.
+   * @return the extra properties of the provided schema element.
+   */
+  public static Map<String, List<String>> cloneExtraProperties(
+      CommonSchemaElements element)
+  {
+    Map<String, List<String>> extraProperties =
+      new HashMap<String, List<String>>();
+    for (String name : element.getExtraPropertyNames())
+    {
+      List<String> values = new ArrayList<String>();
+      Iterable<String> properties = element.getExtraProperty(name);
+      for (String v : properties)
+      {
+        values.add(v);
+      }
+      extraProperties.put(name, values);
+    }
+    return extraProperties;
+  }
+
+
+  private static LinkedHashSet<AttributeType> getOrderedChildrenToDelete(
+      AttributeType attribute, Schema schema)
+  {
+    LinkedHashSet<AttributeType> children = new LinkedHashSet<AttributeType>();
+    for (AttributeType attr : schema.getAttributeTypes().values())
+    {
+      if (attribute.equals(attr.getSuperiorType()))
+      {
+        children.addAll(getOrderedChildrenToDelete(attr, schema));
+        children.add(attr);
+      }
+    }
+    return children;
+  }
+
+  private static LinkedHashSet<ObjectClass> getOrderedChildrenToDelete(
+      ObjectClass objectClass, Schema schema)
+  {
+    LinkedHashSet<ObjectClass> children = new LinkedHashSet<ObjectClass>();
+    for (ObjectClass oc : schema.getObjectClasses().values())
+    {
+      if (objectClass.equals(oc.getSuperiorClass()))
+      {
+        children.addAll(getOrderedChildrenToDelete(oc, schema));
+        children.add(oc);
+      }
+    }
+    return children;
+  }
 }

--
Gitblit v1.10.0