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 javax.naming.NamingException;
032
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
110 /**
111 * A constructor for a SchemaObject instance. It must be
112 * invoked by the inherited class.
113 *
114 * @param objectType The SchemaObjectType to create
115 */
116 protected AbstractSchemaObject( SchemaObjectType objectType, String oid )
117 {
118 this.objectType = objectType;
119 this.oid = oid;
120 extensions = new HashMap<String, List<String>>();
121 names = new ArrayList<String>();
122 }
123
124
125 /**
126 * Constructor used when a generic reusable SchemaObject is assigned an
127 * OID after being instantiated.
128 *
129 * @param objectType The SchemaObjectType to create
130 */
131 protected AbstractSchemaObject( SchemaObjectType objectType )
132 {
133 this.objectType = objectType;
134 extensions = new HashMap<String, List<String>>();
135 names = new ArrayList<String>();
136 }
137
138
139 /**
140 * Gets usually what is the numeric object identifier assigned to this
141 * SchemaObject. All schema objects except for MatchingRuleUses have an OID
142 * assigned specifically to then. A MatchingRuleUse's OID really is the OID
143 * of it's MatchingRule and not specific to the MatchingRuleUse. This
144 * effects how MatchingRuleUse objects are maintained by the system.
145 *
146 * @return an OID for this SchemaObject or its MatchingRule if this
147 * SchemaObject is a MatchingRuleUse object
148 */
149 public String getOid()
150 {
151 return oid;
152 }
153
154
155 /**
156 * A special method used when renaming an SchemaObject: we may have to
157 * change it's OID
158 * @param oid The new OID
159 */
160 public void setOid( String oid )
161 {
162 this.oid = oid;
163 }
164
165
166 /**
167 * Gets short names for this SchemaObject if any exists for it, otherwise,
168 * returns an empty list.
169 *
170 * @return the names for this SchemaObject
171 */
172 public List<String> getNames()
173 {
174 if ( names != null )
175 {
176 return Collections.unmodifiableList( names );
177 }
178 else
179 {
180 return Collections.emptyList();
181 }
182 }
183
184
185 /**
186 * Gets the first name in the set of short names for this SchemaObject if
187 * any exists for it.
188 *
189 * @return the first of the names for this SchemaObject or the oid
190 * if one does not exist
191 */
192 public String getName()
193 {
194 if ( ( names != null ) && ( names.size() != 0 ) )
195 {
196 return names.get( 0 );
197 }
198 else
199 {
200 return oid;
201 }
202 }
203
204
205 /**
206 * Inject this SchemaObject to the given registries, updating the references to
207 * other SchemaObject
208 *
209 * @param errors The errors we got
210 * @param registries The Registries
211 */
212 public void addToRegistries( List<Throwable> errors, Registries registries ) throws NamingException
213 {
214 // do nothing
215 }
216
217
218 /**
219 * Remove this SchemaObject from the given registries, updating the references to
220 * other SchemaObject
221 *
222 * @param errors The errors we got
223 * @param registries The Registries
224 */
225 public void removeFromRegistries( List<Throwable> errors, Registries registries ) throws NamingException
226 {
227 // do nothing
228 }
229
230
231 /**
232 * Inject the Registries into the SchemaObject
233 *
234 * @param registries The Registries
235 */
236 public void setRegistries( Registries registries )
237 {
238 // do nothing
239 }
240
241
242 /**
243 * Add a new name to the list of names for this SchemaObject. The name
244 * is lowercased and trimmed.
245 *
246 * @param names The names to add
247 */
248 public void addName( String... names )
249 {
250 if ( !isReadOnly )
251 {
252 // We must avoid duplicated names, as names are case insensitive
253 Set<String> lowerNames = new HashSet<String>();
254
255 // Fills a set with all the existing names
256 for ( String name : this.names )
257 {
258 lowerNames.add( StringTools.toLowerCase( name ) );
259 }
260
261 for ( String name : names )
262 {
263 if ( name != null )
264 {
265 String lowerName = StringTools.toLowerCase( name );
266 // Check that the lower cased names is not already present
267 if ( !lowerNames.contains( lowerName ) )
268 {
269 this.names.add( name );
270 lowerNames.add( lowerName );
271 }
272 }
273 }
274 }
275 }
276
277
278 /**
279 * Sets the list of names for this SchemaObject. The names are
280 * lowercased and trimmed.
281 *
282 * @param names The list of names. Can be empty
283 */
284 public void setNames( List<String> names )
285 {
286 if ( names == null )
287 {
288 return;
289 }
290
291 if ( !isReadOnly )
292 {
293 this.names = new ArrayList<String>( names.size() );
294
295 for ( String name : names )
296 {
297 if ( name != null )
298 {
299 this.names.add( name );
300 }
301 }
302 }
303 }
304
305
306 /**
307 * Sets the list of names for this SchemaObject. The names are
308 * lowercased and trimmed.
309 *
310 * @param names The list of names.
311 */
312 public void setNames( String... names )
313 {
314 if ( names == null )
315 {
316 return;
317 }
318
319 if ( !isReadOnly )
320 {
321 for ( String name : names )
322 {
323 if ( name != null )
324 {
325 this.names.add( name );
326 }
327 }
328 }
329 }
330
331
332 /**
333 * Gets a short description about this SchemaObject.
334 *
335 * @return a short description about this SchemaObject
336 */
337 public String getDescription()
338 {
339 return description;
340 }
341
342
343 /**
344 * Sets the SchemaObject's description
345 *
346 * @param description The SchemaObject's description
347 */
348 public void setDescription( String description )
349 {
350 if ( !isReadOnly )
351 {
352 this.description = description;
353 }
354 }
355
356
357 /**
358 * Gets the SchemaObject specification.
359 *
360 * @return the SchemaObject specification
361 */
362 public String getSpecification()
363 {
364 return specification;
365 }
366
367
368 /**
369 * Sets the SchemaObject's specification
370 *
371 * @param specification The SchemaObject's specification
372 */
373 public void setSpecification( String specification )
374 {
375 if ( !isReadOnly )
376 {
377 this.specification = specification;
378 }
379 }
380
381
382 /**
383 * Tells if this SchemaObject is enabled.
384 *
385 * @param schemaEnabled the associated schema status
386 * @return true if the SchemaObject is enabled, or if it depends on
387 * an enabled schema
388 */
389 public boolean isEnabled()
390 {
391 return isEnabled;
392 }
393
394
395 /**
396 * Tells if this SchemaObject is disabled.
397 *
398 * @return true if the SchemaObject is disabled
399 */
400 public boolean isDisabled()
401 {
402 return !isEnabled;
403 }
404
405
406 /**
407 * Sets the SchemaObject state, either enabled or disabled.
408 *
409 * @param enabled The current SchemaObject state
410 */
411 public void setEnabled( boolean enabled )
412 {
413 if ( !isReadOnly )
414 {
415 isEnabled = enabled;
416 }
417 }
418
419
420 /**
421 * Tells if this SchemaObject is ReadOnly.
422 *
423 * @return true if the SchemaObject is not modifiable
424 */
425 public boolean isReadOnly()
426 {
427 return isReadOnly;
428 }
429
430
431 /**
432 * Sets the SchemaObject readOnly flag
433 *
434 * @param enabled The current SchemaObject ReadOnly status
435 */
436 public void setReadOnly( boolean isReadOnly )
437 {
438 this.isReadOnly = isReadOnly;
439 }
440
441
442 /**
443 * Gets whether or not this SchemaObject has been inactivated. All
444 * SchemaObjects except Syntaxes allow for this parameter within their
445 * definition. For Syntaxes this property should always return false in
446 * which case it is never included in the description.
447 *
448 * @return true if inactive, false if active
449 */
450 public boolean isObsolete()
451 {
452 return isObsolete;
453 }
454
455
456 /**
457 * Sets the Obsolete flag.
458 *
459 * @param obsolete The Obsolete flag state
460 */
461 public void setObsolete( boolean obsolete )
462 {
463 if ( !isReadOnly )
464 {
465 this.isObsolete = obsolete;
466 }
467 }
468
469
470 /**
471 * @return The SchemaObject extensions, as a Map of [extension, values]
472 */
473 public Map<String, List<String>> getExtensions()
474 {
475 return extensions;
476 }
477
478
479 /**
480 * Add an extension with its values
481 * @param key The extension key
482 * @param values The associated values
483 */
484 public void addExtension( String key, List<String> values )
485 {
486 if ( !isReadOnly )
487 {
488 extensions.put( key, values );
489 }
490 }
491
492
493 /**
494 * Add an extensions with their values. (Actually do a copy)
495 *
496 * @param key The extension key
497 * @param values The associated values
498 */
499 public void setExtensions( Map<String, List<String>> extensions )
500 {
501 if ( !isReadOnly && ( extensions != null ) )
502 {
503 this.extensions = new HashMap<String, List<String>>();
504
505 for ( String key : extensions.keySet() )
506 {
507 List<String> values = new ArrayList<String>();
508
509 for ( String value : extensions.get( key ) )
510 {
511 values.add( value );
512 }
513
514 this.extensions.put( key, values );
515 }
516
517 }
518 }
519
520
521 /**
522 * The SchemaObject type :
523 * <li> AttributeType
524 * <li> DitCOntentRule
525 * <li> DitStructureRule
526 * <li> LdapComparator (specific to ADS)
527 * <li> LdapSyntaxe
528 * <li> MatchingRule
529 * <li> MatchingRuleUse
530 * <li> NameForm
531 * <li> Normalizer (specific to ADS)
532 * <li> ObjectClass
533 * <li> SyntaxChecker (specific to ADS)
534 *
535 * @return the SchemaObject type
536 */
537 public SchemaObjectType getObjectType()
538 {
539 return objectType;
540 }
541
542
543 /**
544 * Gets the name of the schema this SchemaObject is associated with.
545 *
546 * @return the name of the schema associated with this schemaObject
547 */
548 public String getSchemaName()
549 {
550 return schemaName;
551 }
552
553
554 /**
555 * Sets the name of the schema this SchemaObject is associated with.
556 *
557 * @param schemaName the new schema name
558 */
559 public void setSchemaName( String schemaName )
560 {
561 if ( !isReadOnly )
562 {
563 this.schemaName = schemaName;
564 }
565 }
566
567
568 /**
569 * @see Object#hashCode()
570 */
571 public int hashCode()
572 {
573 int h = 37;
574
575 // The OID
576 h += h * 17 + oid.hashCode();
577
578 // The SchemaObject type
579 h += h * 17 + objectType.getValue();
580
581 // The Names, if any
582 if ( ( names != null ) && ( names.size() != 0 ) )
583 {
584 for ( String name : names )
585 {
586 h += h * 17 + name.hashCode();
587 }
588 }
589
590 // The schemaName if any
591 if ( schemaName != null )
592 {
593 h += h * 17 + schemaName.hashCode();
594 }
595
596 h += h * 17 + ( isEnabled ? 1 : 0 );
597 h += h * 17 + ( isReadOnly ? 1 : 0 );
598
599 // The description, if any
600 if ( description != null )
601 {
602 h += h * 17 + description.hashCode();
603 }
604
605 // The extensions, if any
606 for ( String key : extensions.keySet() )
607 {
608 h += h * 17 + key.hashCode();
609
610 List<String> values = extensions.get( key );
611
612 if ( values != null )
613 {
614 for ( String value : values )
615 {
616 h += h * 17 + value.hashCode();
617 }
618 }
619 }
620
621 return h;
622 }
623
624
625 /**
626 * @see Object#equals(Object)
627 */
628 public boolean equals( Object o1 )
629 {
630 if ( this == o1 )
631 {
632 return true;
633 }
634
635 if ( !( o1 instanceof AbstractSchemaObject ) )
636 {
637 return false;
638 }
639
640 AbstractSchemaObject that = ( AbstractSchemaObject ) o1;
641
642 // Two schemaObject are equals if their oid is equal,
643 // their ObjectType is equal, their names are equals
644 // their schema name is the same, all their flags are equals,
645 // the description is the same and their extensions are equals
646 if ( !compareOid( oid, that.oid ) )
647 {
648 return false;
649 }
650
651 // Compare the names
652 if ( names == null )
653 {
654 if ( that.names != null )
655 {
656 return false;
657 }
658 }
659 else if ( that.names == null )
660 {
661 return false;
662 }
663 else
664 {
665 int nbNames = 0;
666
667 for ( String name : names )
668 {
669 if ( !that.names.contains( name ) )
670 {
671 return false;
672 }
673
674 nbNames++;
675 }
676
677 if ( nbNames != names.size() )
678 {
679 return false;
680 }
681 }
682
683 if ( schemaName == null )
684 {
685 if ( that.schemaName != null )
686 {
687 return false;
688 }
689 }
690 else
691 {
692 if ( !schemaName.equalsIgnoreCase( that.schemaName ) )
693 {
694 return false;
695 }
696 }
697
698 if ( objectType != that.objectType )
699 {
700 return false;
701 }
702
703 if ( extensions != null )
704 {
705 if ( that.extensions == null )
706 {
707 return false;
708 }
709 else
710 {
711 for ( String key : extensions.keySet() )
712 {
713 if ( !that.extensions.containsKey( key ) )
714 {
715 return false;
716 }
717
718 List<String> thisValues = extensions.get( key );
719 List<String> thatValues = that.extensions.get( key );
720
721 if ( thisValues != null )
722 {
723 if ( thatValues == null )
724 {
725 return false;
726 }
727 else
728 {
729 if ( thisValues.size() != thatValues.size() )
730 {
731 return false;
732 }
733
734 // TODO compare the values
735 }
736 }
737 else if ( thatValues != null )
738 {
739 return false;
740 }
741 }
742 }
743 }
744 else if ( that.extensions != null )
745 {
746 return false;
747 }
748
749 if ( this.isEnabled != that.isEnabled )
750 {
751 return false;
752 }
753
754 if ( this.isObsolete != that.isObsolete )
755 {
756 return false;
757 }
758
759 if ( this.isReadOnly != that.isReadOnly )
760 {
761 return false;
762 }
763
764 if ( this.description == null )
765 {
766 return that.description == null;
767 }
768 else
769 {
770 return this.description.equalsIgnoreCase( that.description );
771 }
772 }
773
774
775 /**
776 * Register the given SchemaObject into the given registries' globalOidRegistry
777 *
778 * @param schemaObject the SchemaObject we want to register
779 * @param registries The registries in which we want it to be stored
780 * @throws NamingException If the OID is invalid
781 */
782 public void registerOid( SchemaObject schemaObject, Registries registries ) throws NamingException
783 {
784 // Add the SchemaObject into the globalOidRegistry
785 registries.getGlobalOidRegistry().register( schemaObject );
786 }
787
788
789 /**
790 * Copy the current SchemaObject on place
791 *
792 * @return The copied SchemaObject
793 */
794 public abstract SchemaObject copy();
795
796
797 /**
798 * Compare two oids, and return true if they are both null or
799 * equals
800 */
801 protected boolean compareOid( String oid1, String oid2 )
802 {
803 if ( oid1 == null )
804 {
805 return oid2 == null;
806 }
807 else
808 {
809 return oid1.equals( oid2 );
810 }
811 }
812
813
814 /**
815 * Copy a SchemaObject.
816 *
817 * @return A copy of the current SchemaObject
818 */
819 public SchemaObject copy( SchemaObject original )
820 {
821 // copy the description
822 description = original.getDescription();
823
824 // copy the flags
825 isEnabled = original.isEnabled();
826 isObsolete = original.isObsolete();
827 isReadOnly = original.isReadOnly();
828
829 // copy the names
830 names = new ArrayList<String>();
831
832 for ( String name : original.getNames() )
833 {
834 names.add( name );
835 }
836
837 // copy the extensions
838 extensions = new HashMap<String, List<String>>();
839
840 for ( String key : original.getExtensions().keySet() )
841 {
842 List<String> extensionValues = original.getExtensions().get( key );
843
844 List<String> cloneExtension = new ArrayList<String>();
845
846 for ( String value : extensionValues )
847 {
848 cloneExtension.add( value );
849 }
850
851 extensions.put( key, cloneExtension );
852 }
853
854 // The SchemaName
855 schemaName = original.getSchemaName();
856
857 // The specification
858 specification = original.getSpecification();
859
860 return this;
861 }
862
863
864 /**
865 * Clear the current SchemaObject : remove all the references to other objects,
866 * and all the Maps.
867 */
868 public void clear()
869 {
870 // Clear the extensions
871 for ( String extension : extensions.keySet() )
872 {
873 List<String> extensionList = extensions.get( extension );
874
875 extensionList.clear();
876 }
877
878 extensions.clear();
879
880 // Clear the names
881 names.clear();
882 }
883 }