001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020 package org.apache.directory.shared.ldap.schema; 021 022 023 import java.util.ArrayList; 024 import java.util.Collections; 025 import java.util.HashMap; 026 import java.util.HashSet; 027 import java.util.List; 028 import java.util.Map; 029 import java.util.Set; 030 031 import org.apache.directory.shared.i18n.I18n; 032 import org.apache.directory.shared.ldap.exception.LdapException; 033 import org.apache.directory.shared.ldap.schema.registries.Registries; 034 import org.apache.directory.shared.ldap.util.StringTools; 035 036 037 /** 038 * Most schema objects have some common attributes. This class 039 * contains the minimum set of properties exposed by a SchemaObject.<br> 040 * We have 11 types of SchemaObjects : 041 * <li> AttributeType 042 * <li> DitCOntentRule 043 * <li> DitStructureRule 044 * <li> LdapComparator (specific to ADS) 045 * <li> LdapSyntaxe 046 * <li> MatchingRule 047 * <li> MatchingRuleUse 048 * <li> NameForm 049 * <li> Normalizer (specific to ADS) 050 * <li> ObjectClass 051 * <li> SyntaxChecker (specific to ADS) 052 * <br> 053 * <br> 054 * This class provides accessors and setters for the following attributes, 055 * which are common to all those SchemaObjects : 056 * <li>oid : The numeric OID 057 * <li>description : The SchemaObject description 058 * <li>obsolete : Tells if the schema object is obsolete 059 * <li>extensions : The extensions, a key/Values map 060 * <li>schemaObjectType : The SchemaObject type (see upper) 061 * <li>schema : The schema the SchemaObject is associated with (it's an extension). 062 * Can be null 063 * <li>isEnabled : The SchemaObject status (it's related to the schema status) 064 * <li>isReadOnly : Tells if the SchemaObject can be modified or not 065 * <br><br> 066 * Some of those attributes are not used by some Schema elements, even if they should 067 * have been used. Here is the list : 068 * <b>name</b> : LdapSyntax, Comparator, Normalizer, SyntaxChecker 069 * <b>numericOid</b> : DitStructureRule, 070 * <b>obsolete</b> : LdapSyntax, Comparator, Normalizer, SyntaxChecker 071 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 072 * @version $Rev: 885381 $ 073 */ 074 public abstract class AbstractSchemaObject implements SchemaObject 075 { 076 /** The serialVersionUID */ 077 public static final long serialVersionUID = 1L; 078 079 /** The SchemaObject numeric OID */ 080 protected String oid; 081 082 /** The optional names for this SchemaObject */ 083 protected List<String> names; 084 085 /** Whether or not this SchemaObject is enabled */ 086 protected boolean isEnabled = true; 087 088 /** Whether or not this SchemaObject can be modified */ 089 protected boolean isReadOnly = false; 090 091 /** Whether or not this SchemaObject is obsolete */ 092 protected boolean isObsolete = false; 093 094 /** A short description of this SchemaObject */ 095 protected String description; 096 097 /** The SchemaObject specification */ 098 protected String specification; 099 100 /** The name of the schema this object is associated with */ 101 protected String schemaName; 102 103 /** The SchemaObjectType */ 104 protected SchemaObjectType objectType; 105 106 /** A map containing the list of supported extensions */ 107 protected Map<String, List<String>> extensions; 108 109 /** A locked to avoid modifications when set to true */ 110 protected volatile boolean locked; 111 112 /** The hashcoe for this schemaObject */ 113 private int h; 114 115 116 /** 117 * A constructor for a SchemaObject instance. It must be 118 * invoked by the inherited class. 119 * 120 * @param objectType The SchemaObjectType to create 121 */ 122 protected AbstractSchemaObject( SchemaObjectType objectType, String oid ) 123 { 124 this.objectType = objectType; 125 this.oid = oid; 126 extensions = new HashMap<String, List<String>>(); 127 names = new ArrayList<String>(); 128 } 129 130 131 /** 132 * Constructor used when a generic reusable SchemaObject is assigned an 133 * OID after being instantiated. 134 * 135 * @param objectType The SchemaObjectType to create 136 */ 137 protected AbstractSchemaObject( SchemaObjectType objectType ) 138 { 139 this.objectType = objectType; 140 extensions = new HashMap<String, List<String>>(); 141 names = new ArrayList<String>(); 142 } 143 144 145 /** 146 * Gets usually what is the numeric object identifier assigned to this 147 * SchemaObject. All schema objects except for MatchingRuleUses have an OID 148 * assigned specifically to then. A MatchingRuleUse's OID really is the OID 149 * of it's MatchingRule and not specific to the MatchingRuleUse. This 150 * effects how MatchingRuleUse objects are maintained by the system. 151 * 152 * @return an OID for this SchemaObject or its MatchingRule if this 153 * SchemaObject is a MatchingRuleUse object 154 */ 155 public String getOid() 156 { 157 return oid; 158 } 159 160 161 /** 162 * A special method used when renaming an SchemaObject: we may have to 163 * change it's OID 164 * @param oid The new OID 165 */ 166 public void setOid( String oid ) 167 { 168 if ( locked ) 169 { 170 throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) ); 171 } 172 173 this.oid = oid; 174 } 175 176 177 /** 178 * Gets short names for this SchemaObject if any exists for it, otherwise, 179 * returns an empty list. 180 * 181 * @return the names for this SchemaObject 182 */ 183 public List<String> getNames() 184 { 185 if ( names != null ) 186 { 187 return Collections.unmodifiableList( names ); 188 } 189 else 190 { 191 return Collections.emptyList(); 192 } 193 } 194 195 196 /** 197 * Gets the first name in the set of short names for this SchemaObject if 198 * any exists for it. 199 * 200 * @return the first of the names for this SchemaObject or the oid 201 * if one does not exist 202 */ 203 public String getName() 204 { 205 if ( ( names != null ) && ( names.size() != 0 ) ) 206 { 207 return names.get( 0 ); 208 } 209 else 210 { 211 return oid; 212 } 213 } 214 215 216 /** 217 * Inject this SchemaObject to the given registries, updating the references to 218 * other SchemaObject 219 * 220 * @param errors The errors we got 221 * @param registries The Registries 222 */ 223 public void addToRegistries( List<Throwable> errors, Registries registries ) throws LdapException 224 { 225 // do nothing 226 } 227 228 229 /** 230 * Remove this SchemaObject from the given registries, updating the references to 231 * other SchemaObject 232 * 233 * @param errors The errors we got 234 * @param registries The Registries 235 */ 236 public void removeFromRegistries( List<Throwable> errors, Registries registries ) throws LdapException 237 { 238 // do nothing 239 } 240 241 242 /** 243 * Inject the Registries into the SchemaObject 244 * 245 * @param registries The Registries 246 */ 247 public void setRegistries( Registries registries ) 248 { 249 // do nothing 250 } 251 252 253 /** 254 * Add a new name to the list of names for this SchemaObject. The name 255 * is lowercased and trimmed. 256 * 257 * @param names The names to add 258 */ 259 public void addName( String... names ) 260 { 261 if ( locked ) 262 { 263 throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) ); 264 } 265 266 if ( !isReadOnly ) 267 { 268 // We must avoid duplicated names, as names are case insensitive 269 Set<String> lowerNames = new HashSet<String>(); 270 271 // Fills a set with all the existing names 272 for ( String name : this.names ) 273 { 274 lowerNames.add( StringTools.toLowerCase( name ) ); 275 } 276 277 for ( String name : names ) 278 { 279 if ( name != null ) 280 { 281 String lowerName = StringTools.toLowerCase( name ); 282 // Check that the lower cased names is not already present 283 if ( !lowerNames.contains( lowerName ) ) 284 { 285 this.names.add( name ); 286 lowerNames.add( lowerName ); 287 } 288 } 289 } 290 } 291 } 292 293 294 /** 295 * Sets the list of names for this SchemaObject. The names are 296 * lowercased and trimmed. 297 * 298 * @param names The list of names. Can be empty 299 */ 300 public void setNames( List<String> names ) 301 { 302 if ( locked ) 303 { 304 throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) ); 305 } 306 307 if ( names == null ) 308 { 309 return; 310 } 311 312 if ( !isReadOnly ) 313 { 314 this.names = new ArrayList<String>( names.size() ); 315 316 for ( String name : names ) 317 { 318 if ( name != null ) 319 { 320 this.names.add( name ); 321 } 322 } 323 } 324 } 325 326 327 /** 328 * Sets the list of names for this SchemaObject. The names are 329 * lowercased and trimmed. 330 * 331 * @param names The list of names. 332 */ 333 public void setNames( String... names ) 334 { 335 if ( locked ) 336 { 337 throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) ); 338 } 339 340 if ( names == null ) 341 { 342 return; 343 } 344 345 if ( !isReadOnly ) 346 { 347 for ( String name : names ) 348 { 349 if ( name != null ) 350 { 351 this.names.add( name ); 352 } 353 } 354 } 355 } 356 357 358 /** 359 * Gets a short description about this SchemaObject. 360 * 361 * @return a short description about this SchemaObject 362 */ 363 public String getDescription() 364 { 365 return description; 366 } 367 368 369 /** 370 * Sets the SchemaObject's description 371 * 372 * @param description The SchemaObject's description 373 */ 374 public void setDescription( String description ) 375 { 376 if ( locked ) 377 { 378 throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) ); 379 } 380 381 if ( !isReadOnly ) 382 { 383 this.description = description; 384 } 385 } 386 387 388 /** 389 * Gets the SchemaObject specification. 390 * 391 * @return the SchemaObject specification 392 */ 393 public String getSpecification() 394 { 395 return specification; 396 } 397 398 399 /** 400 * Sets the SchemaObject's specification 401 * 402 * @param specification The SchemaObject's specification 403 */ 404 public void setSpecification( String specification ) 405 { 406 if ( locked ) 407 { 408 throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) ); 409 } 410 411 if ( !isReadOnly ) 412 { 413 this.specification = specification; 414 } 415 } 416 417 418 /** 419 * Tells if this SchemaObject is enabled. 420 * 421 * @param schemaEnabled the associated schema status 422 * @return true if the SchemaObject is enabled, or if it depends on 423 * an enabled schema 424 */ 425 public boolean isEnabled() 426 { 427 return isEnabled; 428 } 429 430 431 /** 432 * Tells if this SchemaObject is disabled. 433 * 434 * @return true if the SchemaObject is disabled 435 */ 436 public boolean isDisabled() 437 { 438 return !isEnabled; 439 } 440 441 442 /** 443 * Sets the SchemaObject state, either enabled or disabled. 444 * 445 * @param enabled The current SchemaObject state 446 */ 447 public void setEnabled( boolean enabled ) 448 { 449 if ( !isReadOnly ) 450 { 451 isEnabled = enabled; 452 } 453 } 454 455 456 /** 457 * Tells if this SchemaObject is ReadOnly. 458 * 459 * @return true if the SchemaObject is not modifiable 460 */ 461 public boolean isReadOnly() 462 { 463 return isReadOnly; 464 } 465 466 467 /** 468 * Sets the SchemaObject readOnly flag 469 * 470 * @param enabled The current SchemaObject ReadOnly status 471 */ 472 public void setReadOnly( boolean isReadOnly ) 473 { 474 if ( locked ) 475 { 476 throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) ); 477 } 478 479 this.isReadOnly = isReadOnly; 480 } 481 482 483 /** 484 * Gets whether or not this SchemaObject has been inactivated. All 485 * SchemaObjects except Syntaxes allow for this parameter within their 486 * definition. For Syntaxes this property should always return false in 487 * which case it is never included in the description. 488 * 489 * @return true if inactive, false if active 490 */ 491 public boolean isObsolete() 492 { 493 return isObsolete; 494 } 495 496 497 /** 498 * Sets the Obsolete flag. 499 * 500 * @param obsolete The Obsolete flag state 501 */ 502 public void setObsolete( boolean obsolete ) 503 { 504 if ( locked ) 505 { 506 throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) ); 507 } 508 509 if ( !isReadOnly ) 510 { 511 this.isObsolete = obsolete; 512 } 513 } 514 515 516 /** 517 * @return The SchemaObject extensions, as a Map of [extension, values] 518 */ 519 public Map<String, List<String>> getExtensions() 520 { 521 return extensions; 522 } 523 524 525 /** 526 * Add an extension with its values 527 * @param key The extension key 528 * @param values The associated values 529 */ 530 public void addExtension( String key, List<String> values ) 531 { 532 if ( locked ) 533 { 534 throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) ); 535 } 536 537 if ( !isReadOnly ) 538 { 539 extensions.put( key, values ); 540 } 541 } 542 543 544 /** 545 * Add an extensions with their values. (Actually do a copy) 546 * 547 * @param key The extension key 548 * @param values The associated values 549 */ 550 public void setExtensions( Map<String, List<String>> extensions ) 551 { 552 if ( locked ) 553 { 554 throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) ); 555 } 556 557 if ( !isReadOnly && ( extensions != null ) ) 558 { 559 this.extensions = new HashMap<String, List<String>>(); 560 561 for ( String key : extensions.keySet() ) 562 { 563 List<String> values = new ArrayList<String>(); 564 565 for ( String value : extensions.get( key ) ) 566 { 567 values.add( value ); 568 } 569 570 this.extensions.put( key, values ); 571 } 572 573 } 574 } 575 576 577 /** 578 * The SchemaObject type : 579 * <li> AttributeType 580 * <li> DitCOntentRule 581 * <li> DitStructureRule 582 * <li> LdapComparator (specific to ADS) 583 * <li> LdapSyntaxe 584 * <li> MatchingRule 585 * <li> MatchingRuleUse 586 * <li> NameForm 587 * <li> Normalizer (specific to ADS) 588 * <li> ObjectClass 589 * <li> SyntaxChecker (specific to ADS) 590 * 591 * @return the SchemaObject type 592 */ 593 public SchemaObjectType getObjectType() 594 { 595 return objectType; 596 } 597 598 599 /** 600 * Gets the name of the schema this SchemaObject is associated with. 601 * 602 * @return the name of the schema associated with this schemaObject 603 */ 604 public String getSchemaName() 605 { 606 return schemaName; 607 } 608 609 610 /** 611 * Sets the name of the schema this SchemaObject is associated with. 612 * 613 * @param schemaName the new schema name 614 */ 615 public void setSchemaName( String schemaName ) 616 { 617 if ( locked ) 618 { 619 throw new UnsupportedOperationException( I18n.err( I18n.ERR_04441, getName() ) ); 620 } 621 622 if ( !isReadOnly ) 623 { 624 this.schemaName = schemaName; 625 } 626 } 627 628 629 /** 630 * @see Object#hashCode() 631 */ 632 public int hashCode() 633 { 634 return h; 635 } 636 637 638 /** 639 * @see Object#equals(Object) 640 */ 641 public boolean equals( Object o1 ) 642 { 643 if ( this == o1 ) 644 { 645 return true; 646 } 647 648 if ( !( o1 instanceof AbstractSchemaObject ) ) 649 { 650 return false; 651 } 652 653 AbstractSchemaObject that = ( AbstractSchemaObject ) o1; 654 655 // Two schemaObject are equals if their oid is equal, 656 // their ObjectType is equal, their names are equals 657 // their schema name is the same, all their flags are equals, 658 // the description is the same and their extensions are equals 659 if ( !compareOid( oid, that.oid ) ) 660 { 661 return false; 662 } 663 664 // Compare the names 665 if ( names == null ) 666 { 667 if ( that.names != null ) 668 { 669 return false; 670 } 671 } 672 else if ( that.names == null ) 673 { 674 return false; 675 } 676 else 677 { 678 int nbNames = 0; 679 680 for ( String name : names ) 681 { 682 if ( !that.names.contains( name ) ) 683 { 684 return false; 685 } 686 687 nbNames++; 688 } 689 690 if ( nbNames != names.size() ) 691 { 692 return false; 693 } 694 } 695 696 if ( schemaName == null ) 697 { 698 if ( that.schemaName != null ) 699 { 700 return false; 701 } 702 } 703 else 704 { 705 if ( !schemaName.equalsIgnoreCase( that.schemaName ) ) 706 { 707 return false; 708 } 709 } 710 711 if ( objectType != that.objectType ) 712 { 713 return false; 714 } 715 716 if ( extensions != null ) 717 { 718 if ( that.extensions == null ) 719 { 720 return false; 721 } 722 else 723 { 724 for ( String key : extensions.keySet() ) 725 { 726 if ( !that.extensions.containsKey( key ) ) 727 { 728 return false; 729 } 730 731 List<String> thisValues = extensions.get( key ); 732 List<String> thatValues = that.extensions.get( key ); 733 734 if ( thisValues != null ) 735 { 736 if ( thatValues == null ) 737 { 738 return false; 739 } 740 else 741 { 742 if ( thisValues.size() != thatValues.size() ) 743 { 744 return false; 745 } 746 747 // TODO compare the values 748 } 749 } 750 else if ( thatValues != null ) 751 { 752 return false; 753 } 754 } 755 } 756 } 757 else if ( that.extensions != null ) 758 { 759 return false; 760 } 761 762 if ( this.isEnabled != that.isEnabled ) 763 { 764 return false; 765 } 766 767 if ( this.isObsolete != that.isObsolete ) 768 { 769 return false; 770 } 771 772 if ( this.isReadOnly != that.isReadOnly ) 773 { 774 return false; 775 } 776 777 if ( this.description == null ) 778 { 779 return that.description == null; 780 } 781 else 782 { 783 return this.description.equalsIgnoreCase( that.description ); 784 } 785 } 786 787 788 /** 789 * Register the given SchemaObject into the given registries' globalOidRegistry 790 * 791 * @param schemaObject the SchemaObject we want to register 792 * @param registries The registries in which we want it to be stored 793 * @throws LdapException If the OID is invalid 794 */ 795 public void registerOid( SchemaObject schemaObject, Registries registries ) throws LdapException 796 { 797 // Add the SchemaObject into the globalOidRegistry 798 registries.getGlobalOidRegistry().register( schemaObject ); 799 } 800 801 802 /** 803 * Copy the current SchemaObject on place 804 * 805 * @return The copied SchemaObject 806 */ 807 public abstract SchemaObject copy(); 808 809 810 /** 811 * Compare two oids, and return true if they are both null or 812 * equals 813 */ 814 protected boolean compareOid( String oid1, String oid2 ) 815 { 816 if ( oid1 == null ) 817 { 818 return oid2 == null; 819 } 820 else 821 { 822 return oid1.equals( oid2 ); 823 } 824 } 825 826 827 /** 828 * Copy a SchemaObject. 829 * 830 * @return A copy of the current SchemaObject 831 */ 832 public SchemaObject copy( SchemaObject original ) 833 { 834 // copy the description 835 description = original.getDescription(); 836 837 // copy the flags 838 isEnabled = original.isEnabled(); 839 isObsolete = original.isObsolete(); 840 isReadOnly = original.isReadOnly(); 841 842 // copy the names 843 names = new ArrayList<String>(); 844 845 for ( String name : original.getNames() ) 846 { 847 names.add( name ); 848 } 849 850 // copy the extensions 851 extensions = new HashMap<String, List<String>>(); 852 853 for ( String key : original.getExtensions().keySet() ) 854 { 855 List<String> extensionValues = original.getExtensions().get( key ); 856 857 List<String> cloneExtension = new ArrayList<String>(); 858 859 for ( String value : extensionValues ) 860 { 861 cloneExtension.add( value ); 862 } 863 864 extensions.put( key, cloneExtension ); 865 } 866 867 // The SchemaName 868 schemaName = original.getSchemaName(); 869 870 // The specification 871 specification = original.getSpecification(); 872 873 return this; 874 } 875 876 877 /** 878 * Clear the current SchemaObject : remove all the references to other objects, 879 * and all the Maps. 880 */ 881 public void clear() 882 { 883 // Clear the extensions 884 for ( String extension : extensions.keySet() ) 885 { 886 List<String> extensionList = extensions.get( extension ); 887 888 extensionList.clear(); 889 } 890 891 extensions.clear(); 892 893 // Clear the names 894 names.clear(); 895 } 896 897 898 /** 899 * {@inheritDoc} 900 */ 901 public final void lock() 902 { 903 if ( locked ) 904 { 905 return; 906 } 907 908 h = 37; 909 910 // The OID 911 h += h * 17 + oid.hashCode(); 912 913 // The SchemaObject type 914 h += h * 17 + objectType.getValue(); 915 916 // The Names, if any 917 if ( ( names != null ) && ( names.size() != 0 ) ) 918 { 919 for ( String name : names ) 920 { 921 h += h * 17 + name.hashCode(); 922 } 923 } 924 925 // The schemaName if any 926 if ( schemaName != null ) 927 { 928 h += h * 17 + schemaName.hashCode(); 929 } 930 931 h += h * 17 + ( isEnabled ? 1 : 0 ); 932 h += h * 17 + ( isReadOnly ? 1 : 0 ); 933 934 // The description, if any 935 if ( description != null ) 936 { 937 h += h * 17 + description.hashCode(); 938 } 939 940 // The extensions, if any 941 for ( String key : extensions.keySet() ) 942 { 943 h += h * 17 + key.hashCode(); 944 945 List<String> values = extensions.get( key ); 946 947 if ( values != null ) 948 { 949 for ( String value : values ) 950 { 951 h += h * 17 + value.hashCode(); 952 } 953 } 954 } 955 956 locked = true; 957 } 958 }