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