001 /*
002 * Copyright 2007-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2014 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.ldap.sdk.schema;
022
023
024
025 import java.io.File;
026 import java.io.IOException;
027 import java.io.InputStream;
028 import java.io.Serializable;
029 import java.util.ArrayList;
030 import java.util.Arrays;
031 import java.util.Collections;
032 import java.util.LinkedHashMap;
033 import java.util.LinkedHashSet;
034 import java.util.List;
035 import java.util.Map;
036 import java.util.Set;
037 import java.util.concurrent.atomic.AtomicReference;
038
039 import com.unboundid.ldap.sdk.Attribute;
040 import com.unboundid.ldap.sdk.Entry;
041 import com.unboundid.ldap.sdk.LDAPConnection;
042 import com.unboundid.ldap.sdk.LDAPException;
043 import com.unboundid.ldap.sdk.ReadOnlyEntry;
044 import com.unboundid.ldap.sdk.ResultCode;
045 import com.unboundid.ldif.LDIFException;
046 import com.unboundid.ldif.LDIFReader;
047
048 import static com.unboundid.ldap.sdk.schema.SchemaMessages.*;
049 import static com.unboundid.util.Debug.*;
050 import static com.unboundid.util.StaticUtils.*;
051 import static com.unboundid.util.Validator.*;
052
053
054
055 /**
056 * This class provides a data structure for representing a directory server
057 * subschema subentry. This includes information about the attribute syntaxes,
058 * matching rules, attribute types, object classes, name forms, DIT content
059 * rules, DIT structure rules, and matching rule uses defined in the server
060 * schema.
061 */
062 public final class Schema
063 implements Serializable
064 {
065 /**
066 * The name of the attribute used to hold the attribute syntax definitions.
067 */
068 public static final String ATTR_ATTRIBUTE_SYNTAX = "ldapSyntaxes";
069
070
071
072 /**
073 * The name of the attribute used to hold the attribute type definitions.
074 */
075 public static final String ATTR_ATTRIBUTE_TYPE = "attributeTypes";
076
077
078
079 /**
080 * The name of the attribute used to hold the DIT content rule definitions.
081 */
082 public static final String ATTR_DIT_CONTENT_RULE = "dITContentRules";
083
084
085
086 /**
087 * The name of the attribute used to hold the DIT structure rule definitions.
088 */
089 public static final String ATTR_DIT_STRUCTURE_RULE = "dITStructureRules";
090
091
092
093 /**
094 * The name of the attribute used to hold the matching rule definitions.
095 */
096 public static final String ATTR_MATCHING_RULE = "matchingRules";
097
098
099
100 /**
101 * The name of the attribute used to hold the matching rule use definitions.
102 */
103 public static final String ATTR_MATCHING_RULE_USE = "matchingRuleUse";
104
105
106
107 /**
108 * The name of the attribute used to hold the name form definitions.
109 */
110 public static final String ATTR_NAME_FORM = "nameForms";
111
112
113
114 /**
115 * The name of the attribute used to hold the object class definitions.
116 */
117 public static final String ATTR_OBJECT_CLASS = "objectClasses";
118
119
120
121 /**
122 * The name of the attribute used to hold the DN of the subschema subentry
123 * with the schema information that governs a specified entry.
124 */
125 public static final String ATTR_SUBSCHEMA_SUBENTRY = "subschemaSubentry";
126
127
128
129 /**
130 * The default standard schema available for use in the LDAP SDK.
131 */
132 private static final AtomicReference<Schema> DEFAULT_STANDARD_SCHEMA =
133 new AtomicReference<Schema>();
134
135
136
137 /**
138 * The set of request attributes that will be used when retrieving the server
139 * subschema subentry in order to retrieve all of the schema elements.
140 */
141 private static final String[] SCHEMA_REQUEST_ATTRS =
142 {
143 ATTR_ATTRIBUTE_SYNTAX,
144 ATTR_ATTRIBUTE_TYPE,
145 ATTR_DIT_CONTENT_RULE,
146 ATTR_DIT_STRUCTURE_RULE,
147 ATTR_MATCHING_RULE,
148 ATTR_MATCHING_RULE_USE,
149 ATTR_NAME_FORM,
150 ATTR_OBJECT_CLASS
151 };
152
153
154
155 /**
156 * The set of request attributes that will be used when retrieving the
157 * subschema subentry attribute from a specified entry in order to determine
158 * the location of the server schema definitions.
159 */
160 private static final String[] SUBSCHEMA_SUBENTRY_REQUEST_ATTRS =
161 {
162 ATTR_SUBSCHEMA_SUBENTRY
163 };
164
165
166
167 /**
168 * Retrieves the resource path that may be used to obtain a file with a number
169 * of standard schema definitions.
170 */
171 private static final String DEFAULT_SCHEMA_RESOURCE_PATH =
172 "com/unboundid/ldap/sdk/schema/standard-schema.ldif";
173
174
175
176 /**
177 * The serial version UID for this serializable class.
178 */
179 private static final long serialVersionUID = 8081839633831517925L;
180
181
182
183 // A map of all subordinate attribute type definitions for each attribute
184 // type definition.
185 private final Map<AttributeTypeDefinition,List<AttributeTypeDefinition>>
186 subordinateAttributeTypes;
187
188 // The set of attribute syntaxes mapped from lowercase name/OID to syntax.
189 private final Map<String,AttributeSyntaxDefinition> asMap;
190
191 // The set of attribute types mapped from lowercase name/OID to type.
192 private final Map<String,AttributeTypeDefinition> atMap;
193
194 // The set of DIT content rules mapped from lowercase name/OID to rule.
195 private final Map<String,DITContentRuleDefinition> dcrMap;
196
197 // The set of DIT structure rules mapped from rule ID to rule.
198 private final Map<Integer,DITStructureRuleDefinition> dsrMapByID;
199
200 // The set of DIT structure rules mapped from lowercase name to rule.
201 private final Map<String,DITStructureRuleDefinition> dsrMapByName;
202
203 // The set of DIT structure rules mapped from lowercase name to rule.
204 private final Map<String,DITStructureRuleDefinition> dsrMapByNameForm;
205
206 // The set of matching rules mapped from lowercase name/OID to rule.
207 private final Map<String,MatchingRuleDefinition> mrMap;
208
209 // The set of matching rule uses mapped from matching rule OID to use.
210 private final Map<String,MatchingRuleUseDefinition> mruMap;
211
212 // The set of name forms mapped from lowercase name/OID to name form.
213 private final Map<String,NameFormDefinition> nfMapByName;
214
215 // The set of name forms mapped from structural class OID to name form.
216 private final Map<String,NameFormDefinition> nfMapByOC;
217
218 // The set of object classes mapped from lowercase name/OID to class.
219 private final Map<String,ObjectClassDefinition> ocMap;
220
221 // The entry used to create this schema object.
222 private final ReadOnlyEntry schemaEntry;
223
224 // The set of attribute syntaxes defined in the schema.
225 private final Set<AttributeSyntaxDefinition> asSet;
226
227 // The set of attribute types defined in the schema.
228 private final Set<AttributeTypeDefinition> atSet;
229
230 // The set of operational attribute types defined in the schema.
231 private final Set<AttributeTypeDefinition> operationalATSet;
232
233 // The set of user attribute types defined in the schema.
234 private final Set<AttributeTypeDefinition> userATSet;
235
236 // The set of DIT content rules defined in the schema.
237 private final Set<DITContentRuleDefinition> dcrSet;
238
239 // The set of DIT structure rules defined in the schema.
240 private final Set<DITStructureRuleDefinition> dsrSet;
241
242 // The set of matching rules defined in the schema.
243 private final Set<MatchingRuleDefinition> mrSet;
244
245 // The set of matching rule uses defined in the schema.
246 private final Set<MatchingRuleUseDefinition> mruSet;
247
248 // The set of name forms defined in the schema.
249 private final Set<NameFormDefinition> nfSet;
250
251 // The set of object classes defined in the schema.
252 private final Set<ObjectClassDefinition> ocSet;
253
254 // The set of abstract object classes defined in the schema.
255 private final Set<ObjectClassDefinition> abstractOCSet;
256
257 // The set of auxiliary object classes defined in the schema.
258 private final Set<ObjectClassDefinition> auxiliaryOCSet;
259
260 // The set of structural object classes defined in the schema.
261 private final Set<ObjectClassDefinition> structuralOCSet;
262
263
264
265 /**
266 * Creates a new schema object by decoding the information in the provided
267 * entry.
268 *
269 * @param schemaEntry The schema entry to decode.
270 */
271 public Schema(final Entry schemaEntry)
272 {
273 this.schemaEntry = new ReadOnlyEntry(schemaEntry);
274
275 // Decode the attribute syntaxes from the schema entry.
276 String[] defs = schemaEntry.getAttributeValues(ATTR_ATTRIBUTE_SYNTAX);
277 if (defs == null)
278 {
279 asMap = Collections.emptyMap();
280 asSet = Collections.emptySet();
281 }
282 else
283 {
284 final LinkedHashMap<String,AttributeSyntaxDefinition> m =
285 new LinkedHashMap<String,AttributeSyntaxDefinition>(defs.length);
286 final LinkedHashSet<AttributeSyntaxDefinition> s =
287 new LinkedHashSet<AttributeSyntaxDefinition>(defs.length);
288
289 for (final String def : defs)
290 {
291 try
292 {
293 final AttributeSyntaxDefinition as =
294 new AttributeSyntaxDefinition(def);
295 s.add(as);
296 m.put(toLowerCase(as.getOID()), as);
297 }
298 catch (final LDAPException le)
299 {
300 debugException(le);
301 }
302 }
303
304 asMap = Collections.unmodifiableMap(m);
305 asSet = Collections.unmodifiableSet(s);
306 }
307
308
309 // Decode the attribute types from the schema entry.
310 defs = schemaEntry.getAttributeValues(ATTR_ATTRIBUTE_TYPE);
311 if (defs == null)
312 {
313 atMap = Collections.emptyMap();
314 atSet = Collections.emptySet();
315 operationalATSet = Collections.emptySet();
316 userATSet = Collections.emptySet();
317 }
318 else
319 {
320 final LinkedHashMap<String,AttributeTypeDefinition> m =
321 new LinkedHashMap<String,AttributeTypeDefinition>(2*defs.length);
322 final LinkedHashSet<AttributeTypeDefinition> s =
323 new LinkedHashSet<AttributeTypeDefinition>(defs.length);
324 final LinkedHashSet<AttributeTypeDefinition> sUser =
325 new LinkedHashSet<AttributeTypeDefinition>(defs.length);
326 final LinkedHashSet<AttributeTypeDefinition> sOperational =
327 new LinkedHashSet<AttributeTypeDefinition>(defs.length);
328
329 for (final String def : defs)
330 {
331 try
332 {
333 final AttributeTypeDefinition at = new AttributeTypeDefinition(def);
334 s.add(at);
335 m.put(toLowerCase(at.getOID()), at);
336 for (final String name : at.getNames())
337 {
338 m.put(toLowerCase(name), at);
339 }
340
341 if (at.isOperational())
342 {
343 sOperational.add(at);
344 }
345 else
346 {
347 sUser.add(at);
348 }
349 }
350 catch (final LDAPException le)
351 {
352 debugException(le);
353 }
354 }
355
356 atMap = Collections.unmodifiableMap(m);
357 atSet = Collections.unmodifiableSet(s);
358 operationalATSet = Collections.unmodifiableSet(sOperational);
359 userATSet = Collections.unmodifiableSet(sUser);
360 }
361
362
363 // Decode the DIT content rules from the schema entry.
364 defs = schemaEntry.getAttributeValues(ATTR_DIT_CONTENT_RULE);
365 if (defs == null)
366 {
367 dcrMap = Collections.emptyMap();
368 dcrSet = Collections.emptySet();
369 }
370 else
371 {
372 final LinkedHashMap<String,DITContentRuleDefinition> m =
373 new LinkedHashMap<String,DITContentRuleDefinition>(2*defs.length);
374 final LinkedHashSet<DITContentRuleDefinition> s =
375 new LinkedHashSet<DITContentRuleDefinition>(defs.length);
376
377 for (final String def : defs)
378 {
379 try
380 {
381 final DITContentRuleDefinition dcr =
382 new DITContentRuleDefinition(def);
383 s.add(dcr);
384 m.put(toLowerCase(dcr.getOID()), dcr);
385 for (final String name : dcr.getNames())
386 {
387 m.put(toLowerCase(name), dcr);
388 }
389 }
390 catch (final LDAPException le)
391 {
392 debugException(le);
393 }
394 }
395
396 dcrMap = Collections.unmodifiableMap(m);
397 dcrSet = Collections.unmodifiableSet(s);
398 }
399
400
401 // Decode the DIT structure rules from the schema entry.
402 defs = schemaEntry.getAttributeValues(ATTR_DIT_STRUCTURE_RULE);
403 if (defs == null)
404 {
405 dsrMapByID = Collections.emptyMap();
406 dsrMapByName = Collections.emptyMap();
407 dsrMapByNameForm = Collections.emptyMap();
408 dsrSet = Collections.emptySet();
409 }
410 else
411 {
412 final LinkedHashMap<Integer,DITStructureRuleDefinition> mID =
413 new LinkedHashMap<Integer,DITStructureRuleDefinition>(defs.length);
414 final LinkedHashMap<String,DITStructureRuleDefinition> mN =
415 new LinkedHashMap<String,DITStructureRuleDefinition>(defs.length);
416 final LinkedHashMap<String,DITStructureRuleDefinition> mNF =
417 new LinkedHashMap<String,DITStructureRuleDefinition>(defs.length);
418 final LinkedHashSet<DITStructureRuleDefinition> s =
419 new LinkedHashSet<DITStructureRuleDefinition>(defs.length);
420
421 for (final String def : defs)
422 {
423 try
424 {
425 final DITStructureRuleDefinition dsr =
426 new DITStructureRuleDefinition(def);
427 s.add(dsr);
428 mID.put(dsr.getRuleID(), dsr);
429 mNF.put(toLowerCase(dsr.getNameFormID()), dsr);
430 for (final String name : dsr.getNames())
431 {
432 mN.put(toLowerCase(name), dsr);
433 }
434 }
435 catch (final LDAPException le)
436 {
437 debugException(le);
438 }
439 }
440
441 dsrMapByID = Collections.unmodifiableMap(mID);
442 dsrMapByName = Collections.unmodifiableMap(mN);
443 dsrMapByNameForm = Collections.unmodifiableMap(mNF);
444 dsrSet = Collections.unmodifiableSet(s);
445 }
446
447
448 // Decode the matching rules from the schema entry.
449 defs = schemaEntry.getAttributeValues(ATTR_MATCHING_RULE);
450 if (defs == null)
451 {
452 mrMap = Collections.emptyMap();
453 mrSet = Collections.emptySet();
454 }
455 else
456 {
457 final LinkedHashMap<String,MatchingRuleDefinition> m =
458 new LinkedHashMap<String,MatchingRuleDefinition>(2*defs.length);
459 final LinkedHashSet<MatchingRuleDefinition> s =
460 new LinkedHashSet<MatchingRuleDefinition>(defs.length);
461
462 for (final String def : defs)
463 {
464 try
465 {
466 final MatchingRuleDefinition mr = new MatchingRuleDefinition(def);
467 s.add(mr);
468 m.put(toLowerCase(mr.getOID()), mr);
469 for (final String name : mr.getNames())
470 {
471 m.put(toLowerCase(name), mr);
472 }
473 }
474 catch (final LDAPException le)
475 {
476 debugException(le);
477 }
478 }
479
480 mrMap = Collections.unmodifiableMap(m);
481 mrSet = Collections.unmodifiableSet(s);
482 }
483
484
485 // Decode the matching rule uses from the schema entry.
486 defs = schemaEntry.getAttributeValues(ATTR_MATCHING_RULE_USE);
487 if (defs == null)
488 {
489 mruMap = Collections.emptyMap();
490 mruSet = Collections.emptySet();
491 }
492 else
493 {
494 final LinkedHashMap<String,MatchingRuleUseDefinition> m =
495 new LinkedHashMap<String,MatchingRuleUseDefinition>(2*defs.length);
496 final LinkedHashSet<MatchingRuleUseDefinition> s =
497 new LinkedHashSet<MatchingRuleUseDefinition>(defs.length);
498
499 for (final String def : defs)
500 {
501 try
502 {
503 final MatchingRuleUseDefinition mru =
504 new MatchingRuleUseDefinition(def);
505 s.add(mru);
506 m.put(toLowerCase(mru.getOID()), mru);
507 for (final String name : mru.getNames())
508 {
509 m.put(toLowerCase(name), mru);
510 }
511 }
512 catch (final LDAPException le)
513 {
514 debugException(le);
515 }
516 }
517
518 mruMap = Collections.unmodifiableMap(m);
519 mruSet = Collections.unmodifiableSet(s);
520 }
521
522
523 // Decode the name forms from the schema entry.
524 defs = schemaEntry.getAttributeValues(ATTR_NAME_FORM);
525 if (defs == null)
526 {
527 nfMapByName = Collections.emptyMap();
528 nfMapByOC = Collections.emptyMap();
529 nfSet = Collections.emptySet();
530 }
531 else
532 {
533 final LinkedHashMap<String,NameFormDefinition> mN =
534 new LinkedHashMap<String,NameFormDefinition>(2*defs.length);
535 final LinkedHashMap<String,NameFormDefinition> mOC =
536 new LinkedHashMap<String,NameFormDefinition>(defs.length);
537 final LinkedHashSet<NameFormDefinition> s =
538 new LinkedHashSet<NameFormDefinition>(defs.length);
539
540 for (final String def : defs)
541 {
542 try
543 {
544 final NameFormDefinition nf = new NameFormDefinition(def);
545 s.add(nf);
546 mOC.put(toLowerCase(nf.getStructuralClass()), nf);
547 mN.put(toLowerCase(nf.getOID()), nf);
548 for (final String name : nf.getNames())
549 {
550 mN.put(toLowerCase(name), nf);
551 }
552 }
553 catch (final LDAPException le)
554 {
555 debugException(le);
556 }
557 }
558
559 nfMapByName = Collections.unmodifiableMap(mN);
560 nfMapByOC = Collections.unmodifiableMap(mOC);
561 nfSet = Collections.unmodifiableSet(s);
562 }
563
564
565 // Decode the object classes from the schema entry.
566 defs = schemaEntry.getAttributeValues(ATTR_OBJECT_CLASS);
567 if (defs == null)
568 {
569 ocMap = Collections.emptyMap();
570 ocSet = Collections.emptySet();
571 abstractOCSet = Collections.emptySet();
572 auxiliaryOCSet = Collections.emptySet();
573 structuralOCSet = Collections.emptySet();
574 }
575 else
576 {
577 final LinkedHashMap<String,ObjectClassDefinition> m =
578 new LinkedHashMap<String,ObjectClassDefinition>(2*defs.length);
579 final LinkedHashSet<ObjectClassDefinition> s =
580 new LinkedHashSet<ObjectClassDefinition>(defs.length);
581 final LinkedHashSet<ObjectClassDefinition> sAbstract =
582 new LinkedHashSet<ObjectClassDefinition>(defs.length);
583 final LinkedHashSet<ObjectClassDefinition> sAuxiliary =
584 new LinkedHashSet<ObjectClassDefinition>(defs.length);
585 final LinkedHashSet<ObjectClassDefinition> sStructural =
586 new LinkedHashSet<ObjectClassDefinition>(defs.length);
587
588 for (final String def : defs)
589 {
590 try
591 {
592 final ObjectClassDefinition oc = new ObjectClassDefinition(def);
593 s.add(oc);
594 m.put(toLowerCase(oc.getOID()), oc);
595 for (final String name : oc.getNames())
596 {
597 m.put(toLowerCase(name), oc);
598 }
599
600 switch (getOCType(oc, m))
601 {
602 case ABSTRACT:
603 sAbstract.add(oc);
604 break;
605 case AUXILIARY:
606 sAuxiliary.add(oc);
607 break;
608 case STRUCTURAL:
609 sStructural.add(oc);
610 break;
611 }
612 }
613 catch (final LDAPException le)
614 {
615 debugException(le);
616 }
617 }
618
619 ocMap = Collections.unmodifiableMap(m);
620 ocSet = Collections.unmodifiableSet(s);
621 abstractOCSet = Collections.unmodifiableSet(sAbstract);
622 auxiliaryOCSet = Collections.unmodifiableSet(sAuxiliary);
623 structuralOCSet = Collections.unmodifiableSet(sStructural);
624 }
625
626
627 // Populate the map of subordinate attribute types.
628 final LinkedHashMap<AttributeTypeDefinition,List<AttributeTypeDefinition>>
629 subAttrTypes = new LinkedHashMap<AttributeTypeDefinition,
630 List<AttributeTypeDefinition>>(atSet.size());
631 for (final AttributeTypeDefinition d : atSet)
632 {
633 AttributeTypeDefinition sup = d.getSuperiorType(this);
634 while (sup != null)
635 {
636 List<AttributeTypeDefinition> l = subAttrTypes.get(sup);
637 if (l == null)
638 {
639 l = new ArrayList<AttributeTypeDefinition>(1);
640 subAttrTypes.put(sup, l);
641 }
642 l.add(d);
643
644 sup = sup.getSuperiorType(this);
645 }
646 }
647 subordinateAttributeTypes = Collections.unmodifiableMap(subAttrTypes);
648 }
649
650
651
652 /**
653 * Retrieves the directory server schema over the provided connection. The
654 * root DSE will first be retrieved in order to get its subschemaSubentry DN,
655 * and then that entry will be retrieved from the server and its contents
656 * decoded as schema elements. This should be sufficient for directories that
657 * only provide a single schema, but for directories with multiple schemas it
658 * may be necessary to specify the DN of an entry for which to retrieve the
659 * subschema subentry.
660 *
661 * @param connection The connection to use in order to retrieve the server
662 * schema. It must not be {@code null}.
663 *
664 * @return A decoded representation of the server schema.
665 *
666 * @throws LDAPException If a problem occurs while obtaining the server
667 * schema.
668 */
669 public static Schema getSchema(final LDAPConnection connection)
670 throws LDAPException
671 {
672 return getSchema(connection, "");
673 }
674
675
676
677 /**
678 * Retrieves the directory server schema that governs the specified entry.
679 * In some servers, different portions of the DIT may be served by different
680 * schemas, and in such cases it will be necessary to provide the DN of the
681 * target entry in order to ensure that the appropriate schema which governs
682 * that entry is returned. For servers that support only a single schema,
683 * any entry DN (including that of the root DSE) should be sufficient.
684 *
685 * @param connection The connection to use in order to retrieve the server
686 * schema. It must not be {@code null}.
687 * @param entryDN The DN of the entry for which to retrieve the governing
688 * schema. It may be {@code null} or an empty string in
689 * order to retrieve the schema that governs the server's
690 * root DSE.
691 *
692 * @return A decoded representation of the server schema, or {@code null} if
693 * it is not available for some reason (e.g., the client does not
694 * have permission to read the server schema).
695 *
696 * @throws LDAPException If a problem occurs while obtaining the server
697 * schema.
698 */
699 public static Schema getSchema(final LDAPConnection connection,
700 final String entryDN)
701 throws LDAPException
702 {
703 ensureNotNull(connection);
704
705 final String subschemaSubentryDN;
706 if (entryDN == null)
707 {
708 subschemaSubentryDN = getSubschemaSubentryDN(connection, "");
709 }
710 else
711 {
712 subschemaSubentryDN = getSubschemaSubentryDN(connection, entryDN);
713 }
714
715 if (subschemaSubentryDN == null)
716 {
717 return null;
718 }
719
720 final Entry schemaEntry =
721 connection.getEntry(subschemaSubentryDN, SCHEMA_REQUEST_ATTRS);
722 if (schemaEntry == null)
723 {
724 return null;
725 }
726
727 return new Schema(schemaEntry);
728 }
729
730
731
732 /**
733 * Reads schema information from one or more files containing the schema
734 * represented in LDIF form, with the definitions represented in the form
735 * described in section 4.1 of RFC 4512. Each file should contain a single
736 * entry.
737 *
738 * @param schemaFiles The paths to the LDIF files containing the schema
739 * information to be read. At least one file must be
740 * specified. If multiple files are specified, then they
741 * will be processed in the order in which they have been
742 * listed.
743 *
744 * @return The schema read from the specified schema files, or {@code null}
745 * if none of the files contains any LDIF data to be read.
746 *
747 * @throws IOException If a problem occurs while attempting to read from
748 * any of the specified files.
749 *
750 * @throws LDIFException If a problem occurs while attempting to parse the
751 * contents of any of the schema files.
752 */
753 public static Schema getSchema(final String... schemaFiles)
754 throws IOException, LDIFException
755 {
756 ensureNotNull(schemaFiles);
757 ensureFalse(schemaFiles.length == 0);
758
759 final ArrayList<File> files = new ArrayList<File>(schemaFiles.length);
760 for (final String s : schemaFiles)
761 {
762 files.add(new File(s));
763 }
764
765 return getSchema(files);
766 }
767
768
769
770 /**
771 * Reads schema information from one or more files containing the schema
772 * represented in LDIF form, with the definitions represented in the form
773 * described in section 4.1 of RFC 4512. Each file should contain a single
774 * entry.
775 *
776 * @param schemaFiles The paths to the LDIF files containing the schema
777 * information to be read. At least one file must be
778 * specified. If multiple files are specified, then they
779 * will be processed in the order in which they have been
780 * listed.
781 *
782 * @return The schema read from the specified schema files, or {@code null}
783 * if none of the files contains any LDIF data to be read.
784 *
785 * @throws IOException If a problem occurs while attempting to read from
786 * any of the specified files.
787 *
788 * @throws LDIFException If a problem occurs while attempting to parse the
789 * contents of any of the schema files.
790 */
791 public static Schema getSchema(final File... schemaFiles)
792 throws IOException, LDIFException
793 {
794 ensureNotNull(schemaFiles);
795 ensureFalse(schemaFiles.length == 0);
796
797 return getSchema(Arrays.asList(schemaFiles));
798 }
799
800
801
802 /**
803 * Reads schema information from one or more files containing the schema
804 * represented in LDIF form, with the definitions represented in the form
805 * described in section 4.1 of RFC 4512. Each file should contain a single
806 * entry.
807 *
808 * @param schemaFiles The paths to the LDIF files containing the schema
809 * information to be read. At least one file must be
810 * specified. If multiple files are specified, then they
811 * will be processed in the order in which they have been
812 * listed.
813 *
814 * @return The schema read from the specified schema files, or {@code null}
815 * if none of the files contains any LDIF data to be read.
816 *
817 * @throws IOException If a problem occurs while attempting to read from
818 * any of the specified files.
819 *
820 * @throws LDIFException If a problem occurs while attempting to parse the
821 * contents of any of the schema files.
822 */
823 public static Schema getSchema(final List<File> schemaFiles)
824 throws IOException, LDIFException
825 {
826 ensureNotNull(schemaFiles);
827 ensureFalse(schemaFiles.isEmpty());
828
829 Entry schemaEntry = null;
830 for (final File f : schemaFiles)
831 {
832 final LDIFReader ldifReader = new LDIFReader(f);
833
834 try
835 {
836 final Entry e = ldifReader.readEntry();
837 if (e == null)
838 {
839 continue;
840 }
841
842 if (schemaEntry == null)
843 {
844 schemaEntry = e;
845 }
846 else
847 {
848 for (final Attribute a : e.getAttributes())
849 {
850 schemaEntry.addAttribute(a);
851 }
852 }
853 }
854 finally
855 {
856 ldifReader.close();
857 }
858 }
859
860 if (schemaEntry == null)
861 {
862 return null;
863 }
864
865 return new Schema(schemaEntry);
866 }
867
868
869
870 /**
871 * Retrieves a schema containing all of the elements of each of the provided
872 * schemas.
873 *
874 * @param schemas The schemas to be merged. It must not be {@code null} or
875 * empty.
876 *
877 * @return A merged representation of the provided schemas.
878 */
879 public static Schema mergeSchemas(final Schema... schemas)
880 {
881 if ((schemas == null) || (schemas.length == 0))
882 {
883 return null;
884 }
885 else if (schemas.length == 1)
886 {
887 return schemas[0];
888 }
889
890 final LinkedHashMap<String,String> asMap =
891 new LinkedHashMap<String,String>();
892 final LinkedHashMap<String,String> atMap =
893 new LinkedHashMap<String,String>();
894 final LinkedHashMap<String,String> dcrMap =
895 new LinkedHashMap<String,String>();
896 final LinkedHashMap<Integer,String> dsrMap =
897 new LinkedHashMap<Integer,String>();
898 final LinkedHashMap<String,String> mrMap =
899 new LinkedHashMap<String,String>();
900 final LinkedHashMap<String,String> mruMap =
901 new LinkedHashMap<String,String>();
902 final LinkedHashMap<String,String> nfMap =
903 new LinkedHashMap<String,String>();
904 final LinkedHashMap<String,String> ocMap =
905 new LinkedHashMap<String,String>();
906
907 for (final Schema s : schemas)
908 {
909 for (final AttributeSyntaxDefinition as : s.asSet)
910 {
911 asMap.put(toLowerCase(as.getOID()), as.toString());
912 }
913
914 for (final AttributeTypeDefinition at : s.atSet)
915 {
916 atMap.put(toLowerCase(at.getOID()), at.toString());
917 }
918
919 for (final DITContentRuleDefinition dcr : s.dcrSet)
920 {
921 dcrMap.put(toLowerCase(dcr.getOID()), dcr.toString());
922 }
923
924 for (final DITStructureRuleDefinition dsr : s.dsrSet)
925 {
926 dsrMap.put(dsr.getRuleID(), dsr.toString());
927 }
928
929 for (final MatchingRuleDefinition mr : s.mrSet)
930 {
931 mrMap.put(toLowerCase(mr.getOID()), mr.toString());
932 }
933
934 for (final MatchingRuleUseDefinition mru : s.mruSet)
935 {
936 mruMap.put(toLowerCase(mru.getOID()), mru.toString());
937 }
938
939 for (final NameFormDefinition nf : s.nfSet)
940 {
941 nfMap.put(toLowerCase(nf.getOID()), nf.toString());
942 }
943
944 for (final ObjectClassDefinition oc : s.ocSet)
945 {
946 ocMap.put(toLowerCase(oc.getOID()), oc.toString());
947 }
948 }
949
950 final Entry e = new Entry(schemas[0].getSchemaEntry().getDN());
951
952 final Attribute ocAttr =
953 schemas[0].getSchemaEntry().getObjectClassAttribute();
954 if (ocAttr == null)
955 {
956 e.addAttribute("objectClass", "top", "ldapSubEntry", "subschema");
957 }
958 else
959 {
960 e.addAttribute(ocAttr);
961 }
962
963 if (! asMap.isEmpty())
964 {
965 final String[] values = new String[asMap.size()];
966 e.addAttribute(ATTR_ATTRIBUTE_SYNTAX, asMap.values().toArray(values));
967 }
968
969 if (! mrMap.isEmpty())
970 {
971 final String[] values = new String[mrMap.size()];
972 e.addAttribute(ATTR_MATCHING_RULE, mrMap.values().toArray(values));
973 }
974
975 if (! atMap.isEmpty())
976 {
977 final String[] values = new String[atMap.size()];
978 e.addAttribute(ATTR_ATTRIBUTE_TYPE, atMap.values().toArray(values));
979 }
980
981 if (! ocMap.isEmpty())
982 {
983 final String[] values = new String[ocMap.size()];
984 e.addAttribute(ATTR_OBJECT_CLASS, ocMap.values().toArray(values));
985 }
986
987 if (! dcrMap.isEmpty())
988 {
989 final String[] values = new String[dcrMap.size()];
990 e.addAttribute(ATTR_DIT_CONTENT_RULE, dcrMap.values().toArray(values));
991 }
992
993 if (! dsrMap.isEmpty())
994 {
995 final String[] values = new String[dsrMap.size()];
996 e.addAttribute(ATTR_DIT_STRUCTURE_RULE, dsrMap.values().toArray(values));
997 }
998
999 if (! nfMap.isEmpty())
1000 {
1001 final String[] values = new String[nfMap.size()];
1002 e.addAttribute(ATTR_NAME_FORM, nfMap.values().toArray(values));
1003 }
1004
1005 if (! mruMap.isEmpty())
1006 {
1007 final String[] values = new String[mruMap.size()];
1008 e.addAttribute(ATTR_MATCHING_RULE_USE, mruMap.values().toArray(values));
1009 }
1010
1011 return new Schema(e);
1012 }
1013
1014
1015
1016 /**
1017 * Retrieves the entry used to create this schema object.
1018 *
1019 * @return The entry used to create this schema object.
1020 */
1021 public ReadOnlyEntry getSchemaEntry()
1022 {
1023 return schemaEntry;
1024 }
1025
1026
1027
1028 /**
1029 * Retrieves the object class type for the specified object class, recursively
1030 * checking its parents as needed.
1031 *
1032 * @param oc The object class definition for which to make the
1033 * determination.
1034 * @param m The map of defined object classes.
1035 *
1036 * @return The object class type for the object class.
1037 */
1038 private static ObjectClassType getOCType(final ObjectClassDefinition oc,
1039 final Map<String,ObjectClassDefinition> m)
1040 {
1041 ObjectClassType t = oc.getObjectClassType();
1042 if (t != null)
1043 {
1044 return t;
1045 }
1046
1047 for (final String s : oc.getSuperiorClasses())
1048 {
1049 final ObjectClassDefinition d = m.get(toLowerCase(s));
1050 if (d != null)
1051 {
1052 t = getOCType(d, m);
1053 if (t != null)
1054 {
1055 return t;
1056 }
1057 }
1058 }
1059
1060 return ObjectClassType.STRUCTURAL;
1061 }
1062
1063
1064
1065 /**
1066 * Retrieves the value of the subschemaSubentry attribute from the specified
1067 * entry using the provided connection.
1068 *
1069 * @param connection The connection to use in order to perform the search.
1070 * It must not be {@code null}.
1071 * @param entryDN The DN of the entry from which to retrieve the
1072 * subschemaSubentry attribute. It may be {@code null} or
1073 * an empty string in order to retrieve the value from the
1074 * server's root DSE.
1075 *
1076 * @return The value of the subschemaSubentry attribute from the specified
1077 * entry, or {@code null} if it is not available for some reason
1078 * (e.g., the client does not have permission to read the target
1079 * entry or the subschemaSubentry attribute).
1080 *
1081 * @throws LDAPException If a problem occurs while attempting to retrieve
1082 * the specified entry.
1083 */
1084 public static String getSubschemaSubentryDN(final LDAPConnection connection,
1085 final String entryDN)
1086 throws LDAPException
1087 {
1088 ensureNotNull(connection);
1089
1090 final Entry e;
1091 if (entryDN == null)
1092 {
1093 e = connection.getEntry("", SUBSCHEMA_SUBENTRY_REQUEST_ATTRS);
1094 }
1095 else
1096 {
1097 e = connection.getEntry(entryDN, SUBSCHEMA_SUBENTRY_REQUEST_ATTRS);
1098 }
1099
1100 if (e == null)
1101 {
1102 return null;
1103 }
1104
1105 return e.getAttributeValue(ATTR_SUBSCHEMA_SUBENTRY);
1106 }
1107
1108
1109
1110 /**
1111 * Retrieves the set of attribute syntax definitions contained in the server
1112 * schema.
1113 *
1114 * @return The set of attribute syntax definitions contained in the server
1115 * schema.
1116 */
1117 public Set<AttributeSyntaxDefinition> getAttributeSyntaxes()
1118 {
1119 return asSet;
1120 }
1121
1122
1123
1124 /**
1125 * Retrieves the attribute syntax with the specified OID from the server
1126 * schema.
1127 *
1128 * @param oid The OID of the attribute syntax to retrieve. It must not be
1129 * {@code null}. It may optionally include a minimum upper bound
1130 * (as may appear when the syntax OID is included in an attribute
1131 * type definition), but if it does then that portion will be
1132 * ignored when retrieving the attribute syntax.
1133 *
1134 * @return The requested attribute syntax, or {@code null} if there is no
1135 * such syntax defined in the server schema.
1136 */
1137 public AttributeSyntaxDefinition getAttributeSyntax(final String oid)
1138 {
1139 ensureNotNull(oid);
1140
1141 final String lowerOID = toLowerCase(oid);
1142 final int curlyPos = lowerOID.indexOf('{');
1143
1144 if (curlyPos > 0)
1145 {
1146 return asMap.get(lowerOID.substring(0, curlyPos));
1147 }
1148 else
1149 {
1150 return asMap.get(lowerOID);
1151 }
1152 }
1153
1154
1155
1156 /**
1157 * Retrieves the set of attribute type definitions contained in the server
1158 * schema.
1159 *
1160 * @return The set of attribute type definitions contained in the server
1161 * schema.
1162 */
1163 public Set<AttributeTypeDefinition> getAttributeTypes()
1164 {
1165 return atSet;
1166 }
1167
1168
1169
1170 /**
1171 * Retrieves the set of operational attribute type definitions (i.e., those
1172 * definitions with a usage of directoryOperation, distributedOperation, or
1173 * dSAOperation) contained in the server schema.
1174 *
1175 * @return The set of operational attribute type definitions contained in the
1176 * server schema.
1177 */
1178 public Set<AttributeTypeDefinition> getOperationalAttributeTypes()
1179 {
1180 return operationalATSet;
1181 }
1182
1183
1184
1185 /**
1186 * Retrieves the set of user attribute type definitions (i.e., those
1187 * definitions with a usage of userApplications) contained in the server
1188 * schema.
1189 *
1190 * @return The set of user attribute type definitions contained in the server
1191 * schema.
1192 */
1193 public Set<AttributeTypeDefinition> getUserAttributeTypes()
1194 {
1195 return userATSet;
1196 }
1197
1198
1199
1200 /**
1201 * Retrieves the attribute type with the specified name or OID from the server
1202 * schema.
1203 *
1204 * @param name The name or OID of the attribute type to retrieve. It must
1205 * not be {@code null}.
1206 *
1207 * @return The requested attribute type, or {@code null} if there is no
1208 * such attribute type defined in the server schema.
1209 */
1210 public AttributeTypeDefinition getAttributeType(final String name)
1211 {
1212 ensureNotNull(name);
1213
1214 return atMap.get(toLowerCase(name));
1215 }
1216
1217
1218
1219 /**
1220 * Retrieves a list of all subordinate attribute type definitions for the
1221 * provided attribute type definition.
1222 *
1223 * @param d The attribute type definition for which to retrieve all
1224 * subordinate attribute types. It must not be {@code null}.
1225 *
1226 * @return A list of all subordinate attribute type definitions for the
1227 * provided attribute type definition, or an empty list if it does
1228 * not have any subordinate types or the provided attribute type is
1229 * not defined in the schema.
1230 */
1231 public List<AttributeTypeDefinition> getSubordinateAttributeTypes(
1232 final AttributeTypeDefinition d)
1233 {
1234 ensureNotNull(d);
1235
1236 final List<AttributeTypeDefinition> l = subordinateAttributeTypes.get(d);
1237 if (l == null)
1238 {
1239 return Collections.emptyList();
1240 }
1241 else
1242 {
1243 return Collections.unmodifiableList(l);
1244 }
1245 }
1246
1247
1248
1249 /**
1250 * Retrieves the set of DIT content rule definitions contained in the server
1251 * schema.
1252 *
1253 * @return The set of DIT content rule definitions contained in the server
1254 * schema.
1255 */
1256 public Set<DITContentRuleDefinition> getDITContentRules()
1257 {
1258 return dcrSet;
1259 }
1260
1261
1262
1263 /**
1264 * Retrieves the DIT content rule with the specified name or OID from the
1265 * server schema.
1266 *
1267 * @param name The name or OID of the DIT content rule to retrieve. It must
1268 * not be {@code null}.
1269 *
1270 * @return The requested DIT content rule, or {@code null} if there is no
1271 * such rule defined in the server schema.
1272 */
1273 public DITContentRuleDefinition getDITContentRule(final String name)
1274 {
1275 ensureNotNull(name);
1276
1277 return dcrMap.get(toLowerCase(name));
1278 }
1279
1280
1281
1282 /**
1283 * Retrieves the set of DIT structure rule definitions contained in the server
1284 * schema.
1285 *
1286 * @return The set of DIT structure rule definitions contained in the server
1287 * schema.
1288 */
1289 public Set<DITStructureRuleDefinition> getDITStructureRules()
1290 {
1291 return dsrSet;
1292 }
1293
1294
1295
1296 /**
1297 * Retrieves the DIT content rule with the specified rule ID from the server
1298 * schema.
1299 *
1300 * @param ruleID The rule ID for the DIT structure rule to retrieve.
1301 *
1302 * @return The requested DIT structure rule, or {@code null} if there is no
1303 * such rule defined in the server schema.
1304 */
1305 public DITStructureRuleDefinition getDITStructureRuleByID(final int ruleID)
1306 {
1307 return dsrMapByID.get(ruleID);
1308 }
1309
1310
1311
1312 /**
1313 * Retrieves the DIT content rule with the specified name from the server
1314 * schema.
1315 *
1316 * @param ruleName The name of the DIT structure rule to retrieve. It must
1317 * not be {@code null}.
1318 *
1319 * @return The requested DIT structure rule, or {@code null} if there is no
1320 * such rule defined in the server schema.
1321 */
1322 public DITStructureRuleDefinition getDITStructureRuleByName(
1323 final String ruleName)
1324 {
1325 ensureNotNull(ruleName);
1326
1327 return dsrMapByName.get(toLowerCase(ruleName));
1328 }
1329
1330
1331
1332 /**
1333 * Retrieves the DIT content rule associated with the specified name form from
1334 * the server schema.
1335 *
1336 * @param nameForm The name or OID of the name form for which to retrieve
1337 * the associated DIT structure rule.
1338 *
1339 * @return The requested DIT structure rule, or {@code null} if there is no
1340 * such rule defined in the server schema.
1341 */
1342 public DITStructureRuleDefinition getDITStructureRuleByNameForm(
1343 final String nameForm)
1344 {
1345 ensureNotNull(nameForm);
1346
1347 return dsrMapByNameForm.get(toLowerCase(nameForm));
1348 }
1349
1350
1351
1352 /**
1353 * Retrieves the set of matching rule definitions contained in the server
1354 * schema.
1355 *
1356 * @return The set of matching rule definitions contained in the server
1357 * schema.
1358 */
1359 public Set<MatchingRuleDefinition> getMatchingRules()
1360 {
1361 return mrSet;
1362 }
1363
1364
1365
1366 /**
1367 * Retrieves the matching rule with the specified name or OID from the server
1368 * schema.
1369 *
1370 * @param name The name or OID of the matching rule to retrieve. It must
1371 * not be {@code null}.
1372 *
1373 * @return The requested matching rule, or {@code null} if there is no
1374 * such rule defined in the server schema.
1375 */
1376 public MatchingRuleDefinition getMatchingRule(final String name)
1377 {
1378 ensureNotNull(name);
1379
1380 return mrMap.get(toLowerCase(name));
1381 }
1382
1383
1384
1385 /**
1386 * Retrieves the set of matching rule use definitions contained in the server
1387 * schema.
1388 *
1389 * @return The set of matching rule use definitions contained in the server
1390 * schema.
1391 */
1392 public Set<MatchingRuleUseDefinition> getMatchingRuleUses()
1393 {
1394 return mruSet;
1395 }
1396
1397
1398
1399 /**
1400 * Retrieves the matching rule use with the specified name or OID from the
1401 * server schema.
1402 *
1403 * @param name The name or OID of the matching rule use to retrieve. It
1404 * must not be {@code null}.
1405 *
1406 * @return The requested matching rule, or {@code null} if there is no
1407 * such matching rule use defined in the server schema.
1408 */
1409 public MatchingRuleUseDefinition getMatchingRuleUse(final String name)
1410 {
1411 ensureNotNull(name);
1412
1413 return mruMap.get(toLowerCase(name));
1414 }
1415
1416
1417
1418 /**
1419 * Retrieves the set of name form definitions contained in the server schema.
1420 *
1421 * @return The set of name form definitions contained in the server schema.
1422 */
1423 public Set<NameFormDefinition> getNameForms()
1424 {
1425 return nfSet;
1426 }
1427
1428
1429
1430 /**
1431 * Retrieves the name form with the specified name or OID from the server
1432 * schema.
1433 *
1434 * @param name The name or OID of the name form to retrieve. It must not be
1435 * {@code null}.
1436 *
1437 * @return The requested name form, or {@code null} if there is no
1438 * such rule defined in the server schema.
1439 */
1440 public NameFormDefinition getNameFormByName(final String name)
1441 {
1442 ensureNotNull(name);
1443
1444 return nfMapByName.get(toLowerCase(name));
1445 }
1446
1447
1448
1449 /**
1450 * Retrieves the name form associated with the specified structural object
1451 * class from the server schema.
1452 *
1453 * @param objectClass The name or OID of the structural object class for
1454 * which to retrieve the associated name form. It must
1455 * not be {@code null}.
1456 *
1457 * @return The requested name form, or {@code null} if there is no
1458 * such rule defined in the server schema.
1459 */
1460 public NameFormDefinition getNameFormByObjectClass(final String objectClass)
1461 {
1462 ensureNotNull(objectClass);
1463
1464 return nfMapByOC.get(toLowerCase(objectClass));
1465 }
1466
1467
1468
1469 /**
1470 * Retrieves the set of object class definitions contained in the server
1471 * schema.
1472 *
1473 * @return The set of object class definitions contained in the server
1474 * schema.
1475 */
1476 public Set<ObjectClassDefinition> getObjectClasses()
1477 {
1478 return ocSet;
1479 }
1480
1481
1482
1483 /**
1484 * Retrieves the set of abstract object class definitions contained in the
1485 * server schema.
1486 *
1487 * @return The set of abstract object class definitions contained in the
1488 * server schema.
1489 */
1490 public Set<ObjectClassDefinition> getAbstractObjectClasses()
1491 {
1492 return abstractOCSet;
1493 }
1494
1495
1496
1497 /**
1498 * Retrieves the set of auxiliary object class definitions contained in the
1499 * server schema.
1500 *
1501 * @return The set of auxiliary object class definitions contained in the
1502 * server schema.
1503 */
1504 public Set<ObjectClassDefinition> getAuxiliaryObjectClasses()
1505 {
1506 return auxiliaryOCSet;
1507 }
1508
1509
1510
1511 /**
1512 * Retrieves the set of structural object class definitions contained in the
1513 * server schema.
1514 *
1515 * @return The set of structural object class definitions contained in the
1516 * server schema.
1517 */
1518 public Set<ObjectClassDefinition> getStructuralObjectClasses()
1519 {
1520 return structuralOCSet;
1521 }
1522
1523
1524
1525 /**
1526 * Retrieves the object class with the specified name or OID from the server
1527 * schema.
1528 *
1529 * @param name The name or OID of the object class to retrieve. It must
1530 * not be {@code null}.
1531 *
1532 * @return The requested object class, or {@code null} if there is no such
1533 * class defined in the server schema.
1534 */
1535 public ObjectClassDefinition getObjectClass(final String name)
1536 {
1537 ensureNotNull(name);
1538
1539 return ocMap.get(toLowerCase(name));
1540 }
1541
1542
1543
1544 /**
1545 * Retrieves a hash code for this schema object.
1546 *
1547 * @return A hash code for this schema object.
1548 */
1549 @Override()
1550 public int hashCode()
1551 {
1552 int hc;
1553 try
1554 {
1555 hc = schemaEntry.getParsedDN().hashCode();
1556 }
1557 catch (final Exception e)
1558 {
1559 debugException(e);
1560 hc = toLowerCase(schemaEntry.getDN()).hashCode();
1561 }
1562
1563 Attribute a = schemaEntry.getAttribute(ATTR_ATTRIBUTE_SYNTAX);
1564 if (a != null)
1565 {
1566 hc += a.hashCode();
1567 }
1568
1569 a = schemaEntry.getAttribute(ATTR_MATCHING_RULE);
1570 if (a != null)
1571 {
1572 hc += a.hashCode();
1573 }
1574
1575 a = schemaEntry.getAttribute(ATTR_ATTRIBUTE_TYPE);
1576 if (a != null)
1577 {
1578 hc += a.hashCode();
1579 }
1580
1581 a = schemaEntry.getAttribute(ATTR_OBJECT_CLASS);
1582 if (a != null)
1583 {
1584 hc += a.hashCode();
1585 }
1586
1587 a = schemaEntry.getAttribute(ATTR_NAME_FORM);
1588 if (a != null)
1589 {
1590 hc += a.hashCode();
1591 }
1592
1593 a = schemaEntry.getAttribute(ATTR_DIT_CONTENT_RULE);
1594 if (a != null)
1595 {
1596 hc += a.hashCode();
1597 }
1598
1599 a = schemaEntry.getAttribute(ATTR_DIT_STRUCTURE_RULE);
1600 if (a != null)
1601 {
1602 hc += a.hashCode();
1603 }
1604
1605 a = schemaEntry.getAttribute(ATTR_MATCHING_RULE_USE);
1606 if (a != null)
1607 {
1608 hc += a.hashCode();
1609 }
1610
1611 return hc;
1612 }
1613
1614
1615
1616 /**
1617 * Indicates whether the provided object is equal to this schema object.
1618 *
1619 * @param o The object for which to make the determination.
1620 *
1621 * @return {@code true} if the provided object is equal to this schema
1622 * object, or {@code false} if not.
1623 */
1624 @Override()
1625 public boolean equals(final Object o)
1626 {
1627 if (o == null)
1628 {
1629 return false;
1630 }
1631
1632 if (o == this)
1633 {
1634 return true;
1635 }
1636
1637 if (! (o instanceof Schema))
1638 {
1639 return false;
1640 }
1641
1642 final Schema s = (Schema) o;
1643
1644 try
1645 {
1646 if (! schemaEntry.getParsedDN().equals(s.schemaEntry.getParsedDN()))
1647 {
1648 return false;
1649 }
1650 }
1651 catch (final Exception e)
1652 {
1653 debugException(e);
1654 if (! schemaEntry.getDN().equalsIgnoreCase(s.schemaEntry.getDN()))
1655 {
1656 return false;
1657 }
1658 }
1659
1660 return (asSet.equals(s.asSet) &&
1661 mrSet.equals(s.mrSet) &&
1662 atSet.equals(s.atSet) &&
1663 ocSet.equals(s.ocSet) &&
1664 nfSet.equals(s.nfSet) &&
1665 dcrSet.equals(s.dcrSet) &&
1666 dsrSet.equals(s.dsrSet) &&
1667 mruSet.equals(s.mruSet));
1668 }
1669
1670
1671
1672 /**
1673 * Retrieves a string representation of the associated schema entry.
1674 *
1675 * @return A string representation of the associated schema entry.
1676 */
1677 @Override()
1678 public String toString()
1679 {
1680 return schemaEntry.toString();
1681 }
1682 }