001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2008-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2013-2016 ForgeRock AS. 016 */ 017package org.opends.guitools.controlpanel.task; 018 019import static org.opends.messages.AdminToolMessages.*; 020 021import java.io.File; 022import java.io.IOException; 023import java.util.ArrayList; 024import java.util.Collection; 025import java.util.Collections; 026import java.util.HashMap; 027import java.util.HashSet; 028import java.util.LinkedHashSet; 029import java.util.List; 030import java.util.Map; 031import java.util.Set; 032 033import javax.naming.NamingException; 034import javax.naming.directory.BasicAttribute; 035import javax.naming.directory.DirContext; 036import javax.naming.directory.ModificationItem; 037import javax.swing.SwingUtilities; 038 039import org.forgerock.i18n.LocalizableMessage; 040import org.forgerock.i18n.LocalizableMessageDescriptor.Arg1; 041import org.forgerock.opendj.ldap.ModificationType; 042import org.forgerock.opendj.ldap.schema.AttributeType; 043import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo; 044import org.opends.guitools.controlpanel.ui.ColorAndFontConstants; 045import org.opends.guitools.controlpanel.ui.ProgressDialog; 046import org.opends.guitools.controlpanel.util.Utilities; 047import org.opends.server.config.ConfigConstants; 048import org.opends.server.core.DirectoryServer; 049import org.opends.server.schema.SomeSchemaElement; 050import org.opends.server.types.Attributes; 051import org.opends.server.types.CommonSchemaElements; 052import org.opends.server.types.Entry; 053import org.opends.server.types.ExistingFileBehavior; 054import org.opends.server.types.LDIFExportConfig; 055import org.opends.server.types.LDIFImportConfig; 056import org.opends.server.types.Modification; 057import org.opends.server.types.ObjectClass; 058import org.opends.server.types.OpenDsException; 059import org.opends.server.types.Schema; 060import org.opends.server.util.LDIFReader; 061import org.opends.server.util.LDIFWriter; 062import org.opends.server.util.StaticUtils; 063 064/** The task that is launched when a schema element must be deleted. */ 065public class DeleteSchemaElementsTask extends Task 066{ 067 /** The list of object classes that the user asked to delete. */ 068 private Set<ObjectClass> providedOcsToDelete = new LinkedHashSet<>(); 069 /** The list of attributes that the user asked to delete. */ 070 private Set<AttributeType> providedAttrsToDelete = new LinkedHashSet<>(); 071 /** The list of object classes that will be actually deleted (some might be recreated). */ 072 private Set<ObjectClass> ocsToDelete = new LinkedHashSet<>(); 073 /** The list of attributes that will be actually deleted (some might be recreated). */ 074 private Set<AttributeType> attrsToDelete = new LinkedHashSet<>(); 075 /** The list of object classes that will be recreated. */ 076 private Set<ObjectClass> ocsToAdd = new LinkedHashSet<>(); 077 /** The list of attributes that will be recreated. */ 078 private Set<AttributeType> attrsToAdd = new LinkedHashSet<>(); 079 080 /** 081 * Constructor of the task. 082 * @param info the control panel information. 083 * @param dlg the progress dialog where the task progress will be displayed. 084 * @param ocsToDelete the object classes that must be deleted (ordered). 085 * @param attrsToDelete the attributes that must be deleted (ordered). 086 */ 087 public DeleteSchemaElementsTask(ControlPanelInfo info, ProgressDialog dlg, 088 Set<ObjectClass> ocsToDelete, Set<AttributeType> attrsToDelete) 089 { 090 super(info, dlg); 091 092 this.providedOcsToDelete.addAll(ocsToDelete); 093 this.providedAttrsToDelete.addAll(attrsToDelete); 094 095 Schema schema = info.getServerDescriptor().getSchema(); 096 LinkedHashSet<AttributeType> allAttrsToDelete = 097 DeleteSchemaElementsTask.getOrderedAttributesToDelete(attrsToDelete, 098 schema); 099 LinkedHashSet<ObjectClass> allOcsToDelete = null; 100 if (!attrsToDelete.isEmpty()) 101 { 102 allOcsToDelete = 103 DeleteSchemaElementsTask.getOrderedObjectClassesToDeleteFromAttrs( 104 attrsToDelete, schema); 105 } 106 if (!ocsToDelete.isEmpty()) 107 { 108 LinkedHashSet<ObjectClass> orderedOCs = 109 DeleteSchemaElementsTask.getOrderedObjectClassesToDelete(ocsToDelete, schema); 110 if (allOcsToDelete == null) 111 { 112 allOcsToDelete = orderedOCs; 113 } 114 else 115 { 116 allOcsToDelete.addAll(orderedOCs); 117 } 118 } 119 ArrayList<AttributeType> lAttrsToDelete = new ArrayList<>(allAttrsToDelete); 120 for (int i = lAttrsToDelete.size() - 1; i >= 0; i--) 121 { 122 AttributeType attrToDelete = lAttrsToDelete.get(i); 123 if (!attrsToDelete.contains(attrToDelete)) 124 { 125 AttributeType attrToAdd = getAttributeToAdd(attrToDelete); 126 if (attrToAdd != null) 127 { 128 attrsToAdd.add(attrToAdd); 129 } 130 } 131 } 132 133 assert allOcsToDelete != null; 134 ArrayList<ObjectClass> lOcsToDelete = new ArrayList<>(allOcsToDelete); 135 for (int i = lOcsToDelete.size() - 1; i >= 0; i--) 136 { 137 ObjectClass ocToDelete = lOcsToDelete.get(i); 138 if (!ocsToDelete.contains(ocToDelete)) 139 { 140 ocsToAdd.add(getObjectClassToAdd(lOcsToDelete.get(i))); 141 } 142 } 143 144 this.ocsToDelete.addAll(allOcsToDelete); 145 this.attrsToDelete.addAll(allAttrsToDelete); 146 } 147 148 /** {@inheritDoc} */ 149 @Override 150 public Set<String> getBackends() 151 { 152 return Collections.emptySet(); 153 } 154 155 /** {@inheritDoc} */ 156 @Override 157 public boolean canLaunch(Task taskToBeLaunched, 158 Collection<LocalizableMessage> incompatibilityReasons) 159 { 160 boolean canLaunch = true; 161 if (state == State.RUNNING && 162 (taskToBeLaunched.getType() == Task.Type.DELETE_SCHEMA_ELEMENT || 163 taskToBeLaunched.getType() == Task.Type.MODIFY_SCHEMA_ELEMENT || 164 taskToBeLaunched.getType() == Task.Type.NEW_SCHEMA_ELEMENT)) 165 { 166 incompatibilityReasons.add(getIncompatibilityMessage(this, 167 taskToBeLaunched)); 168 canLaunch = false; 169 } 170 return canLaunch; 171 } 172 173 /** {@inheritDoc} */ 174 @Override 175 public Type getType() 176 { 177 return Type.NEW_SCHEMA_ELEMENT; 178 } 179 180 /** {@inheritDoc} */ 181 @Override 182 public void runTask() 183 { 184 state = State.RUNNING; 185 lastException = null; 186 187 try 188 { 189 updateSchema(); 190 state = State.FINISHED_SUCCESSFULLY; 191 } 192 catch (Throwable t) 193 { 194 lastException = t; 195 state = State.FINISHED_WITH_ERROR; 196 } 197 } 198 199 /** {@inheritDoc} */ 200 @Override 201 protected String getCommandLinePath() 202 { 203 return null; 204 } 205 206 /** {@inheritDoc} */ 207 @Override 208 protected List<String> getCommandLineArguments() 209 { 210 return Collections.emptyList(); 211 } 212 213 /** {@inheritDoc} */ 214 @Override 215 public LocalizableMessage getTaskDescription() 216 { 217 return INFO_CTRL_PANEL_DELETE_SCHEMA_ELEMENT_TASK_DESCRIPTION.get(); 218 } 219 220 /** 221 * Updates the schema. 222 * @throws OpenDsException if an error occurs. 223 */ 224 private void updateSchema() throws OpenDsException 225 { 226 final int totalNumber = ocsToDelete.size() + attrsToDelete.size(); 227 int numberDeleted = 0; 228 for (ObjectClass objectClass : ocsToDelete) 229 { 230 final SomeSchemaElement element = new SomeSchemaElement(objectClass); 231 deleteSchemaElement(element, numberDeleted, totalNumber, INFO_CTRL_PANEL_DELETING_OBJECTCLASS); 232 numberDeleted++; 233 } 234 235 for (AttributeType attribute : attrsToDelete) 236 { 237 final SomeSchemaElement element = new SomeSchemaElement(attribute); 238 deleteSchemaElement(element, numberDeleted, totalNumber, INFO_CTRL_PANEL_DELETING_ATTRIBUTE); 239 numberDeleted++; 240 } 241 242 if (!ocsToAdd.isEmpty() || !attrsToAdd.isEmpty()) 243 { 244 SwingUtilities.invokeLater(new Runnable() 245 { 246 @Override 247 public void run() 248 { 249 getProgressDialog().appendProgressHtml(Utilities.applyFont( 250 "<br><br>"+ 251 INFO_CTRL_PANEL_EXPLANATION_TO_DELETE_REFERENCED_ELEMENTS.get()+ 252 "<br><br>", 253 ColorAndFontConstants.progressFont)); 254 } 255 }); 256 257 NewSchemaElementsTask createTask = 258 new NewSchemaElementsTask(getInfo(), getProgressDialog(), ocsToAdd, 259 attrsToAdd); 260 createTask.runTask(); 261 } 262 } 263 264 private void deleteSchemaElement(final SomeSchemaElement element, final int numberDeleted, final int totalNumber, 265 final Arg1<Object> deletingElementMsg) throws OnlineUpdateException, OpenDsException 266 { 267 SwingUtilities.invokeLater(new Runnable() 268 { 269 @Override 270 public void run() 271 { 272 final boolean isFirst = numberDeleted == 0; 273 if (!isFirst) 274 { 275 getProgressDialog().appendProgressHtml("<br><br>"); 276 } 277 printEquivalentCommandToDelete(element); 278 getProgressDialog().appendProgressHtml( 279 Utilities.getProgressWithPoints( 280 deletingElementMsg.get(element.getNameOrOID()), ColorAndFontConstants.progressFont)); 281 } 282 }); 283 284 if (isServerRunning()) 285 { 286 try 287 { 288 BasicAttribute attr = new BasicAttribute(getSchemaFileAttributeName(element)); 289 attr.add(getSchemaFileAttributeValue(element)); 290 ModificationItem mod = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, attr); 291 getInfo().getDirContext().modifyAttributes( 292 ConfigConstants.DN_DEFAULT_SCHEMA_ROOT, 293 new ModificationItem[] { mod }); 294 } 295 catch (NamingException ne) 296 { 297 throw new OnlineUpdateException(ERR_CTRL_PANEL_ERROR_UPDATING_SCHEMA.get(ne), ne); 298 } 299 } 300 else 301 { 302 updateSchemaFile(element); 303 } 304 305 final int fNumberDeleted = numberDeleted + 1; 306 SwingUtilities.invokeLater(new Runnable() 307 { 308 @Override 309 public void run() 310 { 311 getProgressDialog().getProgressBar().setIndeterminate(false); 312 getProgressDialog().getProgressBar().setValue((fNumberDeleted * 100) / totalNumber); 313 getProgressDialog().appendProgressHtml( 314 Utilities.getProgressDone(ColorAndFontConstants.progressFont)); 315 } 316 }); 317 } 318 319 /** 320 * Updates the schema file by deleting the provided schema element. 321 * @param schemaElement the schema element to be deleted. 322 * @throws OpenDsException if an error occurs. 323 */ 324 private void updateSchemaFile(SomeSchemaElement schemaElement) 325 throws OpenDsException 326 { 327 String schemaFile = getSchemaFile(schemaElement); 328 LDIFExportConfig exportConfig = 329 new LDIFExportConfig(schemaFile, 330 ExistingFileBehavior.OVERWRITE); 331 LDIFReader reader = null; 332 LDIFWriter writer = null; 333 try 334 { 335 reader = new LDIFReader(new LDIFImportConfig(schemaFile)); 336 Entry schemaEntry = reader.readEntry(); 337 338 Modification mod = new Modification(ModificationType.DELETE, 339 Attributes.create( 340 getSchemaFileAttributeName(schemaElement).toLowerCase(), 341 getSchemaFileAttributeValue(schemaElement))); 342 schemaEntry.applyModification(mod); 343 writer = new LDIFWriter(exportConfig); 344 writer.writeEntry(schemaEntry); 345 exportConfig.getWriter().newLine(); 346 } 347 catch (IOException e) 348 { 349 throw new OfflineUpdateException( 350 ERR_CTRL_PANEL_ERROR_UPDATING_SCHEMA.get(e), e); 351 } 352 finally 353 { 354 StaticUtils.close(reader, exportConfig, writer); 355 } 356 } 357 358 /** 359 * Returns the schema file for a given schema element. 360 * @param element the schema element. 361 * @return the schema file for a given schema element. 362 */ 363 private String getSchemaFile(SomeSchemaElement element) 364 { 365 String schemaFile = element.getSchemaFile(); 366 if (schemaFile == null) 367 { 368 schemaFile = ConfigConstants.FILE_USER_SCHEMA_ELEMENTS; 369 } 370 File f = new File(schemaFile); 371 if (!f.isAbsolute()) 372 { 373 f = new File( 374 DirectoryServer.getEnvironmentConfig().getSchemaDirectory(), 375 schemaFile); 376 } 377 return f.getAbsolutePath(); 378 } 379 380 /** 381 * Returns the attribute name in the schema entry that corresponds to the 382 * provided schema element. 383 * @param element the schema element. 384 * @return the attribute name in the schema entry that corresponds to the 385 * provided schema element. 386 */ 387 private String getSchemaFileAttributeName(SomeSchemaElement element) 388 { 389 if (element.isAttributeType()) 390 { 391 return "attributeTypes"; 392 } 393 else 394 { 395 return "objectClasses"; 396 } 397 } 398 399 /** 400 * Returns the value in the schema file for the provided element. 401 * @param element the schema element. 402 * @return the value in the schema file for the provided element. 403 */ 404 private String getSchemaFileAttributeValue(SomeSchemaElement element) 405 { 406 return element.toString(); 407 } 408 409 /** 410 * Prints the equivalent command-line to delete the schema element in the 411 * progress dialog. 412 * @param element the schema element to be deleted. 413 */ 414 private void printEquivalentCommandToDelete(SomeSchemaElement element) 415 { 416 String schemaFile = getSchemaFile(element); 417 String attrName = getSchemaFileAttributeName(element); 418 String attrValue = getSchemaFileAttributeValue(element); 419 if (!isServerRunning()) 420 { 421 LocalizableMessage msg; 422 if (element.isAttributeType()) 423 { 424 msg = INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_DELETE_ATTRIBUTE_OFFLINE.get( 425 element.getNameOrOID(), schemaFile); 426 } 427 else 428 { 429 msg = INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_DELETE_OBJECTCLASS_OFFLINE.get( 430 element.getNameOrOID(), schemaFile); 431 } 432 getProgressDialog().appendProgressHtml(Utilities.applyFont( 433 msg+"<br><b>"+ 434 attrName+": "+attrValue+"</b><br><br>", 435 ColorAndFontConstants.progressFont)); 436 } 437 else 438 { 439 ArrayList<String> args = new ArrayList<>(); 440 args.add("-a"); 441 args.addAll(getObfuscatedCommandLineArguments( 442 getConnectionCommandLineArguments(true, true))); 443 args.add(getNoPropertiesFileArgument()); 444 String equiv = getEquivalentCommandLine(getCommandLinePath("ldapmodify"), 445 args); 446 447 LocalizableMessage msg; 448 if (element.isAttributeType()) 449 { 450 msg = INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_DELETE_ATTRIBUTE_ONLINE.get( 451 element.getNameOrOID()); 452 } 453 else 454 { 455 msg = INFO_CTRL_PANEL_EQUIVALENT_CMD_TO_DELETE_OBJECTCLASS_ONLINE.get( 456 element.getNameOrOID()); 457 } 458 459 StringBuilder sb = new StringBuilder(); 460 sb.append(msg).append("<br><b>"); 461 sb.append(equiv); 462 sb.append("<br>"); 463 sb.append("dn: cn=schema<br>"); 464 sb.append("changetype: modify<br>"); 465 sb.append("delete: ").append(attrName).append("<br>"); 466 sb.append(attrName).append(": ").append(attrValue); 467 sb.append("</b><br><br>"); 468 getProgressDialog().appendProgressHtml(Utilities.applyFont(sb.toString(), 469 ColorAndFontConstants.progressFont)); 470 } 471 } 472 473 private AttributeType getAttributeToAdd(AttributeType attrToDelete) 474 { 475 boolean isSuperior = false; 476 for (AttributeType attr : providedAttrsToDelete) 477 { 478 if (attr.equals(attrToDelete.getSuperiorType())) 479 { 480 isSuperior = true; 481 break; 482 } 483 } 484 if (isSuperior) 485 { 486 // get a new attribute without the superior type 487 return SomeSchemaElement.changeSuperiorType(attrToDelete, null); 488 } 489 else 490 { 491 // Nothing to be changed in the definition of the attribute itself. 492 return attrToDelete; 493 } 494 } 495 496 private ObjectClass getObjectClassToAdd(ObjectClass ocToDelete) 497 { 498 boolean containsAttribute = false; 499 for (AttributeType attr : providedAttrsToDelete) 500 { 501 if(ocToDelete.getRequiredAttributeChain().contains(attr) || 502 ocToDelete.getOptionalAttributeChain().contains(attr)) 503 { 504 containsAttribute = true; 505 break; 506 } 507 } 508 boolean hasSuperior = false; 509 Set<ObjectClass> newSuperiors = new LinkedHashSet<>(); 510 for (ObjectClass sup : ocToDelete.getSuperiorClasses()) 511 { 512 boolean isFound = false; 513 for (ObjectClass oc: providedOcsToDelete) 514 { 515 if(sup.equals(oc)) 516 { 517 hasSuperior = true; 518 isFound = true; 519 newSuperiors.addAll(getNewSuperiors(oc)); 520 break; 521 } 522 } 523 if (!isFound) 524 { 525 //Use the same super if not found in the list. 526 newSuperiors.add(sup); 527 } 528 } 529 530 if (containsAttribute || hasSuperior) 531 { 532 ArrayList<String> allNames = new ArrayList<>(ocToDelete.getNormalizedNames()); 533 Map<String, List<String>> extraProperties = 534 cloneExtraProperties(ocToDelete); 535 Set<AttributeType> required; 536 Set<AttributeType> optional; 537 if (containsAttribute) 538 { 539 required = new HashSet<>(ocToDelete.getRequiredAttributes()); 540 optional = new HashSet<>(ocToDelete.getOptionalAttributes()); 541 required.removeAll(providedAttrsToDelete); 542 optional.removeAll(providedAttrsToDelete); 543 } 544 else 545 { 546 required = ocToDelete.getRequiredAttributes(); 547 optional = ocToDelete.getOptionalAttributes(); 548 } 549 return new ObjectClass("", 550 ocToDelete.getPrimaryName(), 551 allNames, 552 ocToDelete.getOID(), 553 ocToDelete.getDescription(), 554 newSuperiors, 555 required, 556 optional, 557 ocToDelete.getObjectClassType(), 558 ocToDelete.isObsolete(), 559 extraProperties); 560 } 561 else 562 { 563 // Nothing to be changed in the definition of the object class itself. 564 return ocToDelete; 565 } 566 } 567 568 private Set<ObjectClass> getNewSuperiors(ObjectClass currentSup) 569 { 570 Set<ObjectClass> newSuperiors = new LinkedHashSet<>(); 571 if (currentSup.getSuperiorClasses() != null && 572 !currentSup.getSuperiorClasses().isEmpty()) 573 { 574 for (ObjectClass o : currentSup.getSuperiorClasses()) 575 { 576 if (providedOcsToDelete.contains(o)) 577 { 578 newSuperiors.addAll(getNewSuperiors(o)); 579 } 580 else 581 { 582 newSuperiors.add(o); 583 } 584 } 585 } 586 return newSuperiors; 587 } 588 589 /** 590 * Returns an ordered set of the attributes that must be deleted. 591 * @param attrsToDelete the attributes to be deleted. 592 * @param schema the server schema. 593 * @return an ordered list of the attributes that must be deleted. 594 */ 595 public static LinkedHashSet<AttributeType> getOrderedAttributesToDelete( 596 Collection<AttributeType> attrsToDelete, Schema schema) 597 { 598 LinkedHashSet<AttributeType> orderedAttributes = new LinkedHashSet<>(); 599 for (AttributeType attribute : attrsToDelete) 600 { 601 orderedAttributes.addAll(getOrderedChildrenToDelete(attribute, schema)); 602 orderedAttributes.add(attribute); 603 } 604 return orderedAttributes; 605 } 606 607 /** 608 * Returns an ordered list of the object classes that must be deleted. 609 * @param ocsToDelete the object classes to be deleted. 610 * @param schema the server schema. 611 * @return an ordered list of the object classes that must be deleted. 612 */ 613 public static LinkedHashSet<ObjectClass> getOrderedObjectClassesToDelete( 614 Collection<ObjectClass> ocsToDelete, Schema schema) 615 { 616 LinkedHashSet<ObjectClass> orderedOcs = new LinkedHashSet<>(); 617 for (ObjectClass oc : ocsToDelete) 618 { 619 orderedOcs.addAll(getOrderedChildrenToDelete(oc, schema)); 620 orderedOcs.add(oc); 621 } 622 return orderedOcs; 623 } 624 625 /** 626 * Returns an ordered list of the object classes that must be deleted when 627 * deleting a list of attributes that must be deleted. 628 * @param attrsToDelete the attributes to be deleted. 629 * @param schema the server schema. 630 * @return an ordered list of the object classes that must be deleted when 631 * deleting a list of attributes that must be deleted. 632 */ 633 public static LinkedHashSet<ObjectClass> 634 getOrderedObjectClassesToDeleteFromAttrs( 635 Collection<AttributeType> attrsToDelete, Schema schema) 636 { 637 LinkedHashSet<ObjectClass> orderedOcs = new LinkedHashSet<>(); 638 ArrayList<ObjectClass> dependentClasses = new ArrayList<>(); 639 for (AttributeType attr : attrsToDelete) 640 { 641 for (ObjectClass oc : schema.getObjectClasses().values()) 642 { 643 if (oc.getRequiredAttributeChain().contains(attr)) 644 { 645 dependentClasses.add(oc); 646 } 647 else if (oc.getOptionalAttributeChain().contains(attr)) 648 { 649 dependentClasses.add(oc); 650 } 651 } 652 } 653 for (ObjectClass oc : dependentClasses) 654 { 655 orderedOcs.addAll(getOrderedChildrenToDelete(oc, schema)); 656 orderedOcs.add(oc); 657 } 658 return orderedOcs; 659 } 660 661 /** 662 * Clones the extra properties of the provided schema element. This can 663 * be used when copying schema elements. 664 * @param element the schema element. 665 * @return the extra properties of the provided schema element. 666 */ 667 public static Map<String, List<String>> cloneExtraProperties( 668 CommonSchemaElements element) 669 { 670 Map<String, List<String>> extraProperties = new HashMap<>(); 671 Map<String, List<String>> props = element.getExtraProperties(); 672 for (String name : props.keySet()) 673 { 674 List<String> values = new ArrayList<>(props.get(name)); 675 extraProperties.put(name, values); 676 } 677 return extraProperties; 678 } 679 680 private static LinkedHashSet<AttributeType> getOrderedChildrenToDelete( 681 AttributeType attribute, Schema schema) 682 { 683 LinkedHashSet<AttributeType> children = new LinkedHashSet<>(); 684 for (AttributeType attr : schema.getAttributeTypes()) 685 { 686 if (attribute.equals(attr.getSuperiorType())) 687 { 688 children.addAll(getOrderedChildrenToDelete(attr, schema)); 689 children.add(attr); 690 } 691 } 692 return children; 693 } 694 695 private static LinkedHashSet<ObjectClass> getOrderedChildrenToDelete( 696 ObjectClass objectClass, Schema schema) 697 { 698 LinkedHashSet<ObjectClass> children = new LinkedHashSet<>(); 699 for (ObjectClass oc : schema.getObjectClasses().values()) 700 { 701 if (oc.getSuperiorClasses().contains(objectClass)) 702 { 703 children.addAll(getOrderedChildrenToDelete(oc, schema)); 704 children.add(oc); 705 } 706 } 707 return children; 708 } 709}