/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2008-2010 Sun Microsystems, Inc.
* Portions Copyright 2013 ForgeRock AS.
*/
package org.opends.guitools.controlpanel.task;
import static org.opends.messages.AdminToolMessages.*;
import java.io.File;
import java.io.IOException;
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;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;
import javax.swing.SwingUtilities;
import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo;
import org.opends.guitools.controlpanel.ui.ColorAndFontConstants;
import org.opends.guitools.controlpanel.ui.ProgressDialog;
import org.opends.guitools.controlpanel.util.Utilities;
import org.opends.messages.Message;
import org.opends.server.config.ConfigConstants;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.Attributes;
import org.opends.server.types.AttributeType;
import org.opends.server.types.CommonSchemaElements;
import org.opends.server.types.Entry;
import org.opends.server.types.ExistingFileBehavior;
import org.opends.server.types.LDIFExportConfig;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.Modification;
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;
import org.opends.server.util.StaticUtils;
/**
* The task that is launched when a schema element must be deleted.
*/
public class DeleteSchemaElementsTask extends Task
{
// 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 (ordered).
* @param attrsToDelete the attributes that must be deleted (ordered).
*/
public DeleteSchemaElementsTask(ControlPanelInfo info, ProgressDialog dlg,
LinkedHashSet<ObjectClass> ocsToDelete,
LinkedHashSet<AttributeType> attrsToDelete)
{
super(info, dlg);
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);
}
}
}
assert allOcsToDelete != null;
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);
}
/**
* {@inheritDoc}
*/
public Set<String> getBackends()
{
return Collections.emptySet();
}
/**
* {@inheritDoc}
*/
public boolean canLaunch(Task taskToBeLaunched,
Collection<Message> incompatibilityReasons)
{
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;
}
/**
* {@inheritDoc}
*/
public Type getType()
{
return Type.NEW_SCHEMA_ELEMENT;
}
/**
* {@inheritDoc}
*/
public void runTask()
{
state = State.RUNNING;
lastException = null;
try
{
updateSchema();
state = State.FINISHED_SUCCESSFULLY;
}
catch (Throwable t)
{
lastException = t;
state = State.FINISHED_WITH_ERROR;
}
}
/**
* {@inheritDoc}
*/
protected String getCommandLinePath()
{
return null;
}
/**
* {@inheritDoc}
*/
protected List<String> getCommandLineArguments()
{
return Collections.emptyList();
}
/**
* {@inheritDoc}
*/
public Message getTaskDescription()
{
return INFO_CTRL_PANEL_DELETE_SCHEMA_ELEMENT_TASK_DESCRIPTION.get();
}
/**
* Updates the schema.
* @throws OpenDsException if an error occurs.
*/
private void updateSchema() throws OpenDsException
{
final boolean[] isFirst = {true};
final int totalNumber = ocsToDelete.size() + attrsToDelete.size();
int numberDeleted = 0;
for (ObjectClass objectClass : ocsToDelete)
{
final ObjectClass fObjectclass = objectClass;
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
if (!isFirst[0])
{
getProgressDialog().appendProgressHtml("<br><br>");
}
isFirst[0] = false;
printEquivalentCommandToDelete(fObjectclass);
getProgressDialog().appendProgressHtml(
Utilities.getProgressWithPoints(
INFO_CTRL_PANEL_DELETING_OBJECTCLASS.get(
fObjectclass.getNameOrOID()),
ColorAndFontConstants.progressFont));
}
});
if (isServerRunning())
{
try
{
BasicAttribute attr = new BasicAttribute(
getSchemaFileAttributeName(objectClass));
attr.add(getSchemaFileAttributeValue(objectClass));
ModificationItem mod =
new ModificationItem(DirContext.REMOVE_ATTRIBUTE, attr);
getInfo().getDirContext().modifyAttributes(
ConfigConstants.DN_DEFAULT_SCHEMA_ROOT,
new ModificationItem[] { mod });
}
catch (NamingException ne)
{
throw new OnlineUpdateException(
ERR_CTRL_PANEL_ERROR_UPDATING_SCHEMA.get(ne.toString()), ne);
}
}
else
{
updateSchemaFile(objectClass);
}
numberDeleted ++;
final int fNumberDeleted = numberDeleted;
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
getProgressDialog().getProgressBar().setIndeterminate(false);
getProgressDialog().getProgressBar().setValue(
(fNumberDeleted * 100) / totalNumber);
getProgressDialog().appendProgressHtml(
Utilities.getProgressDone(ColorAndFontConstants.progressFont));
}
});
}
for (AttributeType attribute : attrsToDelete)
{
final AttributeType fAttribute = attribute;
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
if (!isFirst[0])
{
getProgressDialog().appendProgressHtml("<br><br>");
}
isFirst[0] = false;
printEquivalentCommandToDelete(fAttribute);
getProgressDialog().appendProgressHtml(
Utilities.getProgressWithPoints(
INFO_CTRL_PANEL_DELETING_ATTRIBUTE.get(
fAttribute.getNameOrOID()),
ColorAndFontConstants.progressFont));
}
});
if (isServerRunning())
{
try
{
BasicAttribute attr = new BasicAttribute(
getSchemaFileAttributeName(attribute));
attr.add(getSchemaFileAttributeValue(attribute));
ModificationItem mod = new ModificationItem(
DirContext.REMOVE_ATTRIBUTE,
attr);
getInfo().getDirContext().modifyAttributes(
ConfigConstants.DN_DEFAULT_SCHEMA_ROOT,
new ModificationItem[] { mod });
}
catch (NamingException ne)
{
throw new OnlineUpdateException(
ERR_CTRL_PANEL_ERROR_UPDATING_SCHEMA.get(ne.toString()), ne);
}
}
else
{
updateSchemaFile(attribute);
}
numberDeleted ++;
final int fNumberDeleted = numberDeleted;
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
getProgressDialog().getProgressBar().setIndeterminate(false);
getProgressDialog().getProgressBar().setValue(
(fNumberDeleted * 100) / totalNumber);
getProgressDialog().appendProgressHtml(
Utilities.getProgressDone(ColorAndFontConstants.progressFont));
}
});
}
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();
}
}
/**
* Updates the schema file by deleting the provided schema element.
* @param schemaElement the schema element to be deleted.
* @throws OpenDsException if an error occurs.
*/
private void updateSchemaFile(CommonSchemaElements schemaElement)
throws OpenDsException
{
String schemaFile = getSchemaFile((SchemaFileElement)schemaElement);
LDIFExportConfig exportConfig =
new LDIFExportConfig(schemaFile,
ExistingFileBehavior.OVERWRITE);
LDIFReader reader = null;
LDIFWriter writer = null;
try
{
reader = new LDIFReader(new LDIFImportConfig(schemaFile));
Entry schemaEntry = reader.readEntry();
Modification mod = new Modification(ModificationType.DELETE,
Attributes.create(
getSchemaFileAttributeName(schemaElement).toLowerCase(),
getSchemaFileAttributeValue(schemaElement)));
schemaEntry.applyModification(mod);
writer = new LDIFWriter(exportConfig);
writer.writeEntry(schemaEntry);
exportConfig.getWriter().newLine();
}
catch (IOException e)
{
throw new OfflineUpdateException(
ERR_CTRL_PANEL_ERROR_UPDATING_SCHEMA.get(e.toString()), e);
}
finally
{
StaticUtils.close(reader, exportConfig, writer);
}
}
/**
* Returns the schema file for a given schema element.
* @param element the schema element.
* @return the schema file for a given schema element.
*/
private String getSchemaFile(SchemaFileElement element)
{
String schemaFile = element.getSchemaFile();
if (schemaFile == null)
{
schemaFile = ConfigConstants.FILE_USER_SCHEMA_ELEMENTS;
}
File f = new File(schemaFile);
if (!f.isAbsolute())
{
f = new File(
DirectoryServer.getEnvironmentConfig().getSchemaDirectory(),
schemaFile);
}
schemaFile = f.getAbsolutePath();
return schemaFile;
}
/**
* Returns the attribute name in the schema entry that corresponds to the
* provided schema element.
* @param element the schema element.
* @return the attribute name in the schema entry that corresponds to the
* provided schema element.
*/
private String getSchemaFileAttributeName(CommonSchemaElements element)
{
if (element instanceof AttributeType)
{
return "attributeTypes";
}
else
{
return "objectClasses";
}
}
/**
* Returns the value in the schema file for the provided element.
* @param element the schema element.
* @return the value in the schema file for the provided element.
*/
private String getSchemaFileAttributeValue(CommonSchemaElements element)
{
if (element instanceof AttributeType)
{
return ((AttributeType)element).getDefinition();
}
else
{
return ((ObjectClass)element).getDefinition();
}
}
/**
* Prints the equivalent command-line to delete the schema element in the
* progress dialog.
* @param element the schema element to be deleted.
*/
private void printEquivalentCommandToDelete(CommonSchemaElements element)
{
String schemaFile = getSchemaFile((SchemaFileElement)element);
String attrName = getSchemaFileAttributeName(element);
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(
msg+"<br><b>"+
attrName+": "+attrValue+"</b><br><br>",
ColorAndFontConstants.progressFont));
}
else
{
ArrayList<String> args = new ArrayList<String>();
args.add("-a");
args.addAll(getObfuscatedCommandLineArguments(
getConnectionCommandLineArguments(true, true)));
args.add(getNoPropertiesFileArgument());
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(msg).append("<br><b>");
sb.append(equiv);
sb.append("<br>");
sb.append("dn: cn=schema<br>");
sb.append("changetype: modify<br>");
sb.append("delete: ").append(attrName).append("<br>");
sb.append(attrName).append(": ").append(attrValue);
sb.append("</b><br><br>");
getProgressDialog().appendProgressHtml(Utilities.applyFont(sb.toString(),
ColorAndFontConstants.progressFont));
}
}
private AttributeType getAttributeToAdd(AttributeType attrToDelete)
{
AttributeType attrToAdd;
boolean isSuperior = false;
for (AttributeType attr : providedAttrsToDelete)
{
if (attr.equals(attrToDelete.getSuperiorType()))
{
isSuperior = true;
AttributeType 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;
Set<ObjectClass> newSuperiors = new LinkedHashSet<ObjectClass>();
for (ObjectClass sup : ocToDelete.getSuperiorClasses())
{
boolean isFound = false;
for (ObjectClass oc: providedOcsToDelete)
{
if(sup.equals(oc))
{
hasSuperior = true;
isFound = true;
newSuperiors.addAll(getNewSuperiors(oc));
break;
}
}
if (!isFound)
{
//Use the same super if not found in the list.
newSuperiors.add(sup);
}
}
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(),
newSuperiors,
required,
optional,
ocToDelete.getObjectClassType(),
ocToDelete.isObsolete(),
extraProperties);
}
else
{
// Nothing to be changed in the definition of the object class itself.
ocToAdd = ocToDelete;
}
return ocToAdd;
}
private Set<ObjectClass> getNewSuperiors(ObjectClass currentSup)
{
Set<ObjectClass> newSuperiors = new LinkedHashSet<ObjectClass>();
if (currentSup.getSuperiorClasses() != null &&
!currentSup.getSuperiorClasses().isEmpty())
{
for (ObjectClass o : currentSup.getSuperiorClasses())
{
if (providedOcsToDelete.contains(o))
{
newSuperiors.addAll(getNewSuperiors(o));
}
else
{
newSuperiors.add(o);
}
}
}
return newSuperiors;
}
/**
* 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 (oc.getSuperiorClasses().contains(objectClass))
{
children.addAll(getOrderedChildrenToDelete(oc, schema));
children.add(oc);
}
}
return children;
}
}