001    /*
002     * Copyright 2007-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2016 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.util.ArrayList;
026    import java.util.Collection;
027    import java.util.Collections;
028    import java.util.Map;
029    import java.util.LinkedHashMap;
030    
031    import com.unboundid.ldap.sdk.LDAPException;
032    import com.unboundid.ldap.sdk.ResultCode;
033    
034    import static com.unboundid.ldap.sdk.schema.SchemaMessages.*;
035    import static com.unboundid.util.StaticUtils.*;
036    import static com.unboundid.util.Validator.*;
037    
038    
039    
040    /**
041     * This class provides a data structure that describes an LDAP DIT content rule
042     * schema element.
043     */
044    public final class DITContentRuleDefinition
045           extends SchemaElement
046    {
047      /**
048       * The serial version UID for this serializable class.
049       */
050      private static final long serialVersionUID = 3224440505307817586L;
051    
052    
053    
054      // Indicates whether this DIT content rule is declared obsolete.
055      private final boolean isObsolete;
056    
057      // The set of extensions for this DIT content rule.
058      private final Map<String,String[]> extensions;
059    
060      // The description for this DIT content rule.
061      private final String description;
062    
063      // The string representation of this DIT content rule.
064      private final String ditContentRuleString;
065    
066      // The OID of the structural object class with which this DIT content rule is
067      // associated.
068      private final String oid;
069    
070      // The names/OIDs of the allowed auxiliary classes.
071      private final String[] auxiliaryClasses;
072    
073      // The set of names for this DIT content rule.
074      private final String[] names;
075    
076      // The names/OIDs of the optional attributes.
077      private final String[] optionalAttributes;
078    
079      // The names/OIDs of the prohibited attributes.
080      private final String[] prohibitedAttributes;
081    
082      // The names/OIDs of the required attributes.
083      private final String[] requiredAttributes;
084    
085    
086    
087      /**
088       * Creates a new DIT content rule from the provided string representation.
089       *
090       * @param  s  The string representation of the DIT content rule to create,
091       *            using the syntax described in RFC 4512 section 4.1.6.  It must
092       *            not be {@code null}.
093       *
094       * @throws  LDAPException  If the provided string cannot be decoded as a DIT
095       *                         content rule definition.
096       */
097      public DITContentRuleDefinition(final String s)
098             throws LDAPException
099      {
100        ensureNotNull(s);
101    
102        ditContentRuleString = s.trim();
103    
104        // The first character must be an opening parenthesis.
105        final int length = ditContentRuleString.length();
106        if (length == 0)
107        {
108          throw new LDAPException(ResultCode.DECODING_ERROR,
109                                  ERR_DCR_DECODE_EMPTY.get());
110        }
111        else if (ditContentRuleString.charAt(0) != '(')
112        {
113          throw new LDAPException(ResultCode.DECODING_ERROR,
114                                  ERR_DCR_DECODE_NO_OPENING_PAREN.get(
115                                       ditContentRuleString));
116        }
117    
118    
119        // Skip over any spaces until we reach the start of the OID, then read the
120        // OID until we find the next space.
121        int pos = skipSpaces(ditContentRuleString, 1, length);
122    
123        StringBuilder buffer = new StringBuilder();
124        pos = readOID(ditContentRuleString, pos, length, buffer);
125        oid = buffer.toString();
126    
127    
128        // Technically, DIT content elements are supposed to appear in a specific
129        // order, but we'll be lenient and allow remaining elements to come in any
130        // order.
131        final ArrayList<String>    nameList = new ArrayList<String>(1);
132        final ArrayList<String>    reqAttrs = new ArrayList<String>();
133        final ArrayList<String>    optAttrs = new ArrayList<String>();
134        final ArrayList<String>    notAttrs = new ArrayList<String>();
135        final ArrayList<String>    auxOCs   = new ArrayList<String>();
136        final Map<String,String[]> exts     = new LinkedHashMap<String,String[]>();
137        Boolean obsolete = null;
138        String  descr    = null;
139    
140        while (true)
141        {
142          // Skip over any spaces until we find the next element.
143          pos = skipSpaces(ditContentRuleString, pos, length);
144    
145          // Read until we find the next space or the end of the string.  Use that
146          // token to figure out what to do next.
147          final int tokenStartPos = pos;
148          while ((pos < length) && (ditContentRuleString.charAt(pos) != ' '))
149          {
150            pos++;
151          }
152    
153          // It's possible that the token could be smashed right up against the
154          // closing parenthesis.  If that's the case, then extract just the token
155          // and handle the closing parenthesis the next time through.
156          String token = ditContentRuleString.substring(tokenStartPos, pos);
157          if ((token.length() > 1) && (token.endsWith(")")))
158          {
159            token = token.substring(0, token.length() - 1);
160            pos--;
161          }
162    
163          final String lowerToken = toLowerCase(token);
164          if (lowerToken.equals(")"))
165          {
166            // This indicates that we're at the end of the value.  There should not
167            // be any more closing characters.
168            if (pos < length)
169            {
170              throw new LDAPException(ResultCode.DECODING_ERROR,
171                                      ERR_DCR_DECODE_CLOSE_NOT_AT_END.get(
172                                           ditContentRuleString));
173            }
174            break;
175          }
176          else if (lowerToken.equals("name"))
177          {
178            if (nameList.isEmpty())
179            {
180              pos = skipSpaces(ditContentRuleString, pos, length);
181              pos = readQDStrings(ditContentRuleString, pos, length, nameList);
182            }
183            else
184            {
185              throw new LDAPException(ResultCode.DECODING_ERROR,
186                                      ERR_DCR_DECODE_MULTIPLE_ELEMENTS.get(
187                                           ditContentRuleString, "NAME"));
188            }
189          }
190          else if (lowerToken.equals("desc"))
191          {
192            if (descr == null)
193            {
194              pos = skipSpaces(ditContentRuleString, pos, length);
195    
196              buffer = new StringBuilder();
197              pos = readQDString(ditContentRuleString, pos, length, buffer);
198              descr = buffer.toString();
199            }
200            else
201            {
202              throw new LDAPException(ResultCode.DECODING_ERROR,
203                                      ERR_DCR_DECODE_MULTIPLE_ELEMENTS.get(
204                                           ditContentRuleString, "DESC"));
205            }
206          }
207          else if (lowerToken.equals("obsolete"))
208          {
209            if (obsolete == null)
210            {
211              obsolete = true;
212            }
213            else
214            {
215              throw new LDAPException(ResultCode.DECODING_ERROR,
216                                      ERR_DCR_DECODE_MULTIPLE_ELEMENTS.get(
217                                           ditContentRuleString, "OBSOLETE"));
218            }
219          }
220          else if (lowerToken.equals("aux"))
221          {
222            if (auxOCs.isEmpty())
223            {
224              pos = skipSpaces(ditContentRuleString, pos, length);
225              pos = readOIDs(ditContentRuleString, pos, length, auxOCs);
226            }
227            else
228            {
229              throw new LDAPException(ResultCode.DECODING_ERROR,
230                                      ERR_DCR_DECODE_MULTIPLE_ELEMENTS.get(
231                                           ditContentRuleString, "AUX"));
232            }
233          }
234          else if (lowerToken.equals("must"))
235          {
236            if (reqAttrs.isEmpty())
237            {
238              pos = skipSpaces(ditContentRuleString, pos, length);
239              pos = readOIDs(ditContentRuleString, pos, length, reqAttrs);
240            }
241            else
242            {
243              throw new LDAPException(ResultCode.DECODING_ERROR,
244                                      ERR_DCR_DECODE_MULTIPLE_ELEMENTS.get(
245                                           ditContentRuleString, "MUST"));
246            }
247          }
248          else if (lowerToken.equals("may"))
249          {
250            if (optAttrs.isEmpty())
251            {
252              pos = skipSpaces(ditContentRuleString, pos, length);
253              pos = readOIDs(ditContentRuleString, pos, length, optAttrs);
254            }
255            else
256            {
257              throw new LDAPException(ResultCode.DECODING_ERROR,
258                                      ERR_DCR_DECODE_MULTIPLE_ELEMENTS.get(
259                                           ditContentRuleString, "MAY"));
260            }
261          }
262          else if (lowerToken.equals("not"))
263          {
264            if (notAttrs.isEmpty())
265            {
266              pos = skipSpaces(ditContentRuleString, pos, length);
267              pos = readOIDs(ditContentRuleString, pos, length, notAttrs);
268            }
269            else
270            {
271              throw new LDAPException(ResultCode.DECODING_ERROR,
272                                      ERR_DCR_DECODE_MULTIPLE_ELEMENTS.get(
273                                           ditContentRuleString, "NOT"));
274            }
275          }
276          else if (lowerToken.startsWith("x-"))
277          {
278            pos = skipSpaces(ditContentRuleString, pos, length);
279    
280            final ArrayList<String> valueList = new ArrayList<String>();
281            pos = readQDStrings(ditContentRuleString, pos, length, valueList);
282    
283            final String[] values = new String[valueList.size()];
284            valueList.toArray(values);
285    
286            if (exts.containsKey(token))
287            {
288              throw new LDAPException(ResultCode.DECODING_ERROR,
289                                      ERR_DCR_DECODE_DUP_EXT.get(
290                                           ditContentRuleString, token));
291            }
292    
293            exts.put(token, values);
294          }
295          else
296          {
297            throw new LDAPException(ResultCode.DECODING_ERROR,
298                                    ERR_DCR_DECODE_DUP_EXT.get(
299                                         ditContentRuleString, token));
300          }
301        }
302    
303        description = descr;
304    
305        names = new String[nameList.size()];
306        nameList.toArray(names);
307    
308        auxiliaryClasses = new String[auxOCs.size()];
309        auxOCs.toArray(auxiliaryClasses);
310    
311        requiredAttributes = new String[reqAttrs.size()];
312        reqAttrs.toArray(requiredAttributes);
313    
314        optionalAttributes = new String[optAttrs.size()];
315        optAttrs.toArray(optionalAttributes);
316    
317        prohibitedAttributes = new String[notAttrs.size()];
318        notAttrs.toArray(prohibitedAttributes);
319    
320        isObsolete = (obsolete != null);
321    
322        extensions = Collections.unmodifiableMap(exts);
323      }
324    
325    
326    
327      /**
328       * Creates a new DIT content rule with the provided information.
329       *
330       * @param  oid                   The OID for the structural object class with
331       *                               which this DIT content rule is associated.
332       *                               It must not be {@code null}.
333       * @param  name                  The name for this DIT content rule.  It may
334       *                               be {@code null} if the DIT content rule
335       *                               should only be referenced by OID.
336       * @param  description           The description for this DIT content rule.
337       *                               It may be {@code null} if there is no
338       *                               description.
339       * @param  auxiliaryClasses      The names/OIDs of the auxiliary object
340       *                               classes that may be present in entries
341       *                               containing this DIT content rule.
342       * @param  requiredAttributes    The names/OIDs of the attributes which must
343       *                               be present in entries containing this DIT
344       *                               content rule.
345       * @param  optionalAttributes    The names/OIDs of the attributes which may be
346       *                               present in entries containing this DIT
347       *                               content rule.
348       * @param  prohibitedAttributes  The names/OIDs of the attributes which may
349       *                               not be present in entries containing this DIT
350       *                               content rule.
351       * @param  extensions            The set of extensions for this DIT content
352       *                               rule.  It may be {@code null} or empty if
353       *                               there should not be any extensions.
354       */
355      public DITContentRuleDefinition(final String oid, final String name,
356                                      final String description,
357                                      final String[] auxiliaryClasses,
358                                      final String[] requiredAttributes,
359                                      final String[] optionalAttributes,
360                                      final String[] prohibitedAttributes,
361                                      final Map<String,String[]> extensions)
362      {
363        this(oid, ((name == null) ? null : new String[] { name }), description,
364             false, auxiliaryClasses, requiredAttributes, optionalAttributes,
365             prohibitedAttributes, extensions);
366      }
367    
368    
369    
370      /**
371       * Creates a new DIT content rule with the provided information.
372       *
373       * @param  oid                   The OID for the structural object class with
374       *                               which this DIT content rule is associated.
375       *                               It must not be {@code null}.
376       * @param  name                  The name for this DIT content rule.  It may
377       *                               be {@code null} if the DIT content rule
378       *                               should only be referenced by OID.
379       * @param  description           The description for this DIT content rule.
380       *                               It may be {@code null} if there is no
381       *                               description.
382       * @param  auxiliaryClasses      The names/OIDs of the auxiliary object
383       *                               classes that may be present in entries
384       *                               containing this DIT content rule.
385       * @param  requiredAttributes    The names/OIDs of the attributes which must
386       *                               be present in entries containing this DIT
387       *                               content rule.
388       * @param  optionalAttributes    The names/OIDs of the attributes which may be
389       *                               present in entries containing this DIT
390       *                               content rule.
391       * @param  prohibitedAttributes  The names/OIDs of the attributes which may
392       *                               not be present in entries containing this DIT
393       *                               content rule.
394       * @param  extensions            The set of extensions for this DIT content
395       *                               rule.  It may be {@code null} or empty if
396       *                               there should not be any extensions.
397       */
398      public DITContentRuleDefinition(final String oid, final String name,
399                                      final String description,
400                                      final Collection<String> auxiliaryClasses,
401                                      final Collection<String> requiredAttributes,
402                                      final Collection<String> optionalAttributes,
403                                      final Collection<String> prohibitedAttributes,
404                                      final Map<String,String[]> extensions)
405      {
406        this(oid, ((name == null) ? null : new String[] { name }), description,
407             false, toArray(auxiliaryClasses), toArray(requiredAttributes),
408             toArray(optionalAttributes), toArray(prohibitedAttributes),
409             extensions);
410      }
411    
412    
413    
414      /**
415       * Creates a new DIT content rule with the provided information.
416       *
417       * @param  oid                   The OID for the structural object class with
418       *                               which this DIT content rule is associated.
419       *                               It must not be {@code null}.
420       * @param  names                 The set of names for this DIT content rule.
421       *                               It may be {@code null} or empty if the DIT
422       *                               content rule should only be referenced by
423       *                               OID.
424       * @param  description           The description for this DIT content rule.
425       *                               It may be {@code null} if there is no
426       *                               description.
427       * @param  isObsolete            Indicates whether this DIT content rule is
428       *                               declared obsolete.
429       * @param  auxiliaryClasses      The names/OIDs of the auxiliary object
430       *                               classes that may be present in entries
431       *                               containing this DIT content rule.
432       * @param  requiredAttributes    The names/OIDs of the attributes which must
433       *                               be present in entries containing this DIT
434       *                               content rule.
435       * @param  optionalAttributes    The names/OIDs of the attributes which may be
436       *                               present in entries containing this DIT
437       *                               content rule.
438       * @param  prohibitedAttributes  The names/OIDs of the attributes which may
439       *                               not be present in entries containing this DIT
440       *                               content rule.
441       * @param  extensions            The set of extensions for this DIT content
442       *                               rule.  It may be {@code null} or empty if
443       *                               there should not be any extensions.
444       */
445      public DITContentRuleDefinition(final String oid, final String[] names,
446                                      final String description,
447                                      final boolean isObsolete,
448                                      final String[] auxiliaryClasses,
449                                      final String[] requiredAttributes,
450                                      final String[] optionalAttributes,
451                                      final String[] prohibitedAttributes,
452                                      final Map<String,String[]> extensions)
453      {
454        ensureNotNull(oid);
455    
456        this.oid             = oid;
457        this.isObsolete      = isObsolete;
458        this.description     = description;
459    
460        if (names == null)
461        {
462          this.names = NO_STRINGS;
463        }
464        else
465        {
466          this.names = names;
467        }
468    
469        if (auxiliaryClasses == null)
470        {
471          this.auxiliaryClasses = NO_STRINGS;
472        }
473        else
474        {
475          this.auxiliaryClasses  = auxiliaryClasses;
476        }
477    
478        if (requiredAttributes == null)
479        {
480          this.requiredAttributes = NO_STRINGS;
481        }
482        else
483        {
484          this.requiredAttributes = requiredAttributes;
485        }
486    
487        if (optionalAttributes == null)
488        {
489          this.optionalAttributes = NO_STRINGS;
490        }
491        else
492        {
493          this.optionalAttributes = optionalAttributes;
494        }
495    
496        if (prohibitedAttributes == null)
497        {
498          this.prohibitedAttributes = NO_STRINGS;
499        }
500        else
501        {
502          this.prohibitedAttributes = prohibitedAttributes;
503        }
504    
505        if (extensions == null)
506        {
507          this.extensions = Collections.emptyMap();
508        }
509        else
510        {
511          this.extensions = Collections.unmodifiableMap(extensions);
512        }
513    
514        final StringBuilder buffer = new StringBuilder();
515        createDefinitionString(buffer);
516        ditContentRuleString = buffer.toString();
517      }
518    
519    
520    
521      /**
522       * Constructs a string representation of this DIT content rule definition in
523       * the provided buffer.
524       *
525       * @param  buffer  The buffer in which to construct a string representation of
526       *                 this DIT content rule definition.
527       */
528      private void createDefinitionString(final StringBuilder buffer)
529      {
530        buffer.append("( ");
531        buffer.append(oid);
532    
533        if (names.length == 1)
534        {
535          buffer.append(" NAME '");
536          buffer.append(names[0]);
537          buffer.append('\'');
538        }
539        else if (names.length > 1)
540        {
541          buffer.append(" NAME (");
542          for (final String name : names)
543          {
544            buffer.append(" '");
545            buffer.append(name);
546            buffer.append('\'');
547          }
548          buffer.append(" )");
549        }
550    
551        if (description != null)
552        {
553          buffer.append(" DESC '");
554          encodeValue(description, buffer);
555          buffer.append('\'');
556        }
557    
558        if (isObsolete)
559        {
560          buffer.append(" OBSOLETE");
561        }
562    
563        if (auxiliaryClasses.length == 1)
564        {
565          buffer.append(" AUX ");
566          buffer.append(auxiliaryClasses[0]);
567        }
568        else if (auxiliaryClasses.length > 1)
569        {
570          buffer.append(" AUX (");
571          for (int i=0; i < auxiliaryClasses.length; i++)
572          {
573            if (i >0)
574            {
575              buffer.append(" $ ");
576            }
577            else
578            {
579              buffer.append(' ');
580            }
581            buffer.append(auxiliaryClasses[i]);
582          }
583          buffer.append(" )");
584        }
585    
586        if (requiredAttributes.length == 1)
587        {
588          buffer.append(" MUST ");
589          buffer.append(requiredAttributes[0]);
590        }
591        else if (requiredAttributes.length > 1)
592        {
593          buffer.append(" MUST (");
594          for (int i=0; i < requiredAttributes.length; i++)
595          {
596            if (i >0)
597            {
598              buffer.append(" $ ");
599            }
600            else
601            {
602              buffer.append(' ');
603            }
604            buffer.append(requiredAttributes[i]);
605          }
606          buffer.append(" )");
607        }
608    
609        if (optionalAttributes.length == 1)
610        {
611          buffer.append(" MAY ");
612          buffer.append(optionalAttributes[0]);
613        }
614        else if (optionalAttributes.length > 1)
615        {
616          buffer.append(" MAY (");
617          for (int i=0; i < optionalAttributes.length; i++)
618          {
619            if (i > 0)
620            {
621              buffer.append(" $ ");
622            }
623            else
624            {
625              buffer.append(' ');
626            }
627            buffer.append(optionalAttributes[i]);
628          }
629          buffer.append(" )");
630        }
631    
632        if (prohibitedAttributes.length == 1)
633        {
634          buffer.append(" NOT ");
635          buffer.append(prohibitedAttributes[0]);
636        }
637        else if (prohibitedAttributes.length > 1)
638        {
639          buffer.append(" NOT (");
640          for (int i=0; i < prohibitedAttributes.length; i++)
641          {
642            if (i > 0)
643            {
644              buffer.append(" $ ");
645            }
646            else
647            {
648              buffer.append(' ');
649            }
650            buffer.append(prohibitedAttributes[i]);
651          }
652          buffer.append(" )");
653        }
654    
655        for (final Map.Entry<String,String[]> e : extensions.entrySet())
656        {
657          final String   name   = e.getKey();
658          final String[] values = e.getValue();
659          if (values.length == 1)
660          {
661            buffer.append(' ');
662            buffer.append(name);
663            buffer.append(" '");
664            encodeValue(values[0], buffer);
665            buffer.append('\'');
666          }
667          else
668          {
669            buffer.append(' ');
670            buffer.append(name);
671            buffer.append(" (");
672            for (final String value : values)
673            {
674              buffer.append(" '");
675              encodeValue(value, buffer);
676              buffer.append('\'');
677            }
678            buffer.append(" )");
679          }
680        }
681    
682        buffer.append(" )");
683      }
684    
685    
686    
687      /**
688       * Retrieves the OID for the structural object class associated with this
689       * DIT content rule.
690       *
691       * @return  The OID for the structural object class associated with this DIT
692       *          content rule.
693       */
694      public String getOID()
695      {
696        return oid;
697      }
698    
699    
700    
701      /**
702       * Retrieves the set of names for this DIT content rule.
703       *
704       * @return  The set of names for this DIT content rule, or an empty array if
705       *          it does not have any names.
706       */
707      public String[] getNames()
708      {
709        return names;
710      }
711    
712    
713    
714      /**
715       * Retrieves the primary name that can be used to reference this DIT content
716       * rule.  If one or more names are defined, then the first name will be used.
717       * Otherwise, the structural object class OID will be returned.
718       *
719       * @return  The primary name that can be used to reference this DIT content
720       *          rule.
721       */
722      public String getNameOrOID()
723      {
724        if (names.length == 0)
725        {
726          return oid;
727        }
728        else
729        {
730          return names[0];
731        }
732      }
733    
734    
735    
736      /**
737       * Indicates whether the provided string matches the OID or any of the names
738       * for this DIT content rule.
739       *
740       * @param  s  The string for which to make the determination.  It must not be
741       *            {@code null}.
742       *
743       * @return  {@code true} if the provided string matches the OID or any of the
744       *          names for this DIT content rule, or {@code false} if not.
745       */
746      public boolean hasNameOrOID(final String s)
747      {
748        for (final String name : names)
749        {
750          if (s.equalsIgnoreCase(name))
751          {
752            return true;
753          }
754        }
755    
756        return s.equalsIgnoreCase(oid);
757      }
758    
759    
760    
761      /**
762       * Retrieves the description for this DIT content rule, if available.
763       *
764       * @return  The description for this DIT content rule, or {@code null} if
765       *          there is no description defined.
766       */
767      public String getDescription()
768      {
769        return description;
770      }
771    
772    
773    
774      /**
775       * Indicates whether this DIT content rule is declared obsolete.
776       *
777       * @return  {@code true} if this DIT content rule is declared obsolete, or
778       *          {@code false} if it is not.
779       */
780      public boolean isObsolete()
781      {
782        return isObsolete;
783      }
784    
785    
786    
787      /**
788       * Retrieves the names or OIDs of the auxiliary object classes that may be
789       * present in entries containing the structural class for this DIT content
790       * rule.
791       *
792       * @return  The names or OIDs of the auxiliary object classes that may be
793       *          present in entries containing the structural class for this DIT
794       *          content rule.
795       */
796      public String[] getAuxiliaryClasses()
797      {
798        return auxiliaryClasses;
799      }
800    
801    
802    
803      /**
804       * Retrieves the names or OIDs of the attributes that are required to be
805       * present in entries containing the structural object class for this DIT
806       * content rule.
807       *
808       * @return  The names or OIDs of the attributes that are required to be
809       *          present in entries containing the structural object class for this
810       *          DIT content rule, or an empty array if there are no required
811       *          attributes.
812       */
813      public String[] getRequiredAttributes()
814      {
815        return requiredAttributes;
816      }
817    
818    
819    
820      /**
821       * Retrieves the names or OIDs of the attributes that are optionally allowed
822       * to be present in entries containing the structural object class for this
823       * DIT content rule.
824       *
825       * @return  The names or OIDs of the attributes that are optionally allowed to
826       *          be present in entries containing the structural object class for
827       *          this DIT content rule, or an empty array if there are no required
828       *          attributes.
829       */
830      public String[] getOptionalAttributes()
831      {
832        return optionalAttributes;
833      }
834    
835    
836    
837      /**
838       * Retrieves the names or OIDs of the attributes that are not allowed to be
839       * present in entries containing the structural object class for this DIT
840       * content rule.
841       *
842       * @return  The names or OIDs of the attributes that are not allowed to be
843       *          present in entries containing the structural object class for this
844       *          DIT content rule, or an empty array if there are no required
845       *          attributes.
846       */
847      public String[] getProhibitedAttributes()
848      {
849        return prohibitedAttributes;
850      }
851    
852    
853    
854      /**
855       * Retrieves the set of extensions for this DIT content rule.  They will be
856       * mapped from the extension name (which should start with "X-") to the set of
857       * values for that extension.
858       *
859       * @return  The set of extensions for this DIT content rule.
860       */
861      public Map<String,String[]> getExtensions()
862      {
863        return extensions;
864      }
865    
866    
867    
868      /**
869       * {@inheritDoc}
870       */
871      @Override()
872      public int hashCode()
873      {
874        return oid.hashCode();
875      }
876    
877    
878    
879      /**
880       * {@inheritDoc}
881       */
882      @Override()
883      public boolean equals(final Object o)
884      {
885        if (o == null)
886        {
887          return false;
888        }
889    
890        if (o == this)
891        {
892          return true;
893        }
894    
895        if (! (o instanceof DITContentRuleDefinition))
896        {
897          return false;
898        }
899    
900        final DITContentRuleDefinition d = (DITContentRuleDefinition) o;
901        return (oid.equals(d.oid) &&
902             stringsEqualIgnoreCaseOrderIndependent(names, d.names) &&
903             stringsEqualIgnoreCaseOrderIndependent(auxiliaryClasses,
904                  d.auxiliaryClasses) &&
905             stringsEqualIgnoreCaseOrderIndependent(requiredAttributes,
906                  d.requiredAttributes) &&
907             stringsEqualIgnoreCaseOrderIndependent(optionalAttributes,
908                  d.optionalAttributes) &&
909             stringsEqualIgnoreCaseOrderIndependent(prohibitedAttributes,
910                  d.prohibitedAttributes) &&
911             bothNullOrEqualIgnoreCase(description, d.description) &&
912             (isObsolete == d.isObsolete) &&
913             extensionsEqual(extensions, d.extensions));
914      }
915    
916    
917    
918      /**
919       * Retrieves a string representation of this DIT content rule definition, in
920       * the format described in RFC 4512 section 4.1.6.
921       *
922       * @return  A string representation of this DIT content rule definition.
923       */
924      @Override()
925      public String toString()
926      {
927        return ditContentRuleString;
928      }
929    }