001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.types;
028    
029    
030    
031    import java.util.Iterator;
032    import java.util.LinkedHashMap;
033    import java.util.LinkedHashSet;
034    import java.util.LinkedList;
035    import java.util.List;
036    import java.util.Map;
037    import java.util.Set;
038    
039    import org.opends.server.schema.DITStructureRuleSyntax;
040    
041    import static org.opends.server.loggers.debug.DebugLogger.*;
042    import org.opends.server.loggers.debug.DebugTracer;
043    import static org.opends.server.util.ServerConstants.*;
044    import static org.opends.server.util.Validator.*;
045    
046    
047    
048    /**
049     * This class defines a DIT structure rule, which is used to indicate
050     * the types of children that entries may have.
051     */
052    @org.opends.server.types.PublicAPI(
053         stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
054         mayInstantiate=false,
055         mayExtend=false,
056         mayInvoke=true)
057    public final class DITStructureRule
058           implements SchemaFileElement
059    {
060      /**
061       * The tracer object for the debug logger.
062       */
063      private static final DebugTracer TRACER = getTracer();
064    
065      // Indicates whether this DIT structure rule is declared "obsolete".
066      private final boolean isObsolete;
067    
068      // The rule ID for this DIT structure rule.
069      private final int ruleID;
070    
071      // The name form for this DIT structure rule.
072      private final NameForm nameForm;
073    
074      // The set of additional name-value pairs associated with this DIT
075      // structure rule.
076      private final Map<String,List<String>> extraProperties;
077    
078      // The set of names for this DIT structure rule, in a mapping
079      // between the all-lowercase form and the user-defined form.
080      private final Map<String,String> names;
081    
082      // The set of superior DIT structure rules.
083      private final Set<DITStructureRule> superiorRules;
084    
085      // The definition string for this DIT structure rule.
086      private final String definition;
087    
088      // The description for this DIT structure rule.
089      private final String description;
090    
091    
092    
093      /**
094       * Creates a new DIT structure rule with the provided information.
095       *
096       * @param  definition       The definition string used to create
097       *                          this DIT structure rule.  It must not be
098       *                          {@code null}.
099       * @param  names            The set of names for this DIT structure
100       *                          rule, mapping the lowercase names to the
101       *                          user-defined values.
102       * @param  ruleID           The rule ID for this DIT structure rule.
103       * @param  description      The description for this DIT structure
104       *                          rule.
105       * @param  isObsolete       Indicates whether this DIT structure
106       *                          rule is declared "obsolete".
107       * @param  nameForm         The name form for this DIT structure
108       *                          rule.
109       * @param  superiorRules    References to the superior rules for
110       *                          this DIT structure rule.
111       * @param  extraProperties  The set of "extra" properties associated
112       *                          with this DIT structure rules.
113       */
114      public DITStructureRule(String definition, Map<String,String> names,
115                              int ruleID, String description,
116                              boolean isObsolete, NameForm nameForm,
117                              Set<DITStructureRule> superiorRules,
118                              Map<String,List<String>> extraProperties)
119      {
120        ensureNotNull(definition);
121    
122        this.ruleID      = ruleID;
123        this.description = description;
124        this.isObsolete  = isObsolete;
125        this.nameForm    = nameForm;
126    
127        int schemaFilePos = definition.indexOf(SCHEMA_PROPERTY_FILENAME);
128        if (schemaFilePos > 0)
129        {
130          String defStr;
131          try
132          {
133            int firstQuotePos = definition.indexOf('\'', schemaFilePos);
134            int secondQuotePos = definition.indexOf('\'',
135                                                    firstQuotePos+1);
136    
137            defStr = definition.substring(0, schemaFilePos).trim() + " " +
138                     definition.substring(secondQuotePos+1).trim();
139          }
140          catch (Exception e)
141          {
142            if (debugEnabled())
143            {
144              TRACER.debugCaught(DebugLogLevel.ERROR, e);
145            }
146    
147            defStr = definition;
148          }
149    
150          this.definition = defStr;
151        }
152        else
153        {
154          this.definition = definition;
155        }
156    
157        if ((names == null) || names.isEmpty())
158        {
159          this.names = new LinkedHashMap<String,String>(0);
160        }
161        else
162        {
163          this.names = new LinkedHashMap<String,String>(names);
164        }
165    
166        if ((superiorRules == null) || superiorRules.isEmpty())
167        {
168          this.superiorRules = new LinkedHashSet<DITStructureRule>(0);
169        }
170        else
171        {
172          this.superiorRules =
173               new LinkedHashSet<DITStructureRule>(superiorRules);
174        }
175    
176        if ((extraProperties == null) || extraProperties.isEmpty())
177        {
178          this.extraProperties =
179               new LinkedHashMap<String,List<String>>(0);
180        }
181        else
182        {
183          this.extraProperties =
184               new LinkedHashMap<String,List<String>>(extraProperties);
185        }
186      }
187    
188    
189    
190      /**
191       * Retrieves the definition string used to create this DIT structure
192       * rule.
193       *
194       * @return  The definition string used to create this DIT structure
195       *          rule.
196       */
197      public String getDefinition()
198      {
199        return definition;
200      }
201    
202    
203    
204      /**
205       * Creates a new instance of this DIT structure rule based on the
206       * definition string.  It will also preserve other state information
207       * associated with this DIT structure rule that is not included in
208       * the definition string (e.g., the name of the schema file with
209       * which it is associated).
210       *
211       * @return  The new instance of this DIT structure rule based on the
212       *          definition string.
213       *
214       * @throws  DirectoryException  If a problem occurs while attempting
215       *                              to create a new DIT structure rule
216       *                              instance from the definition string.
217       */
218      public DITStructureRule recreateFromDefinition()
219             throws DirectoryException
220      {
221        ByteString value  = ByteStringFactory.create(definition);
222        Schema     schema = DirectoryConfig.getSchema();
223    
224        DITStructureRule dsr =
225             DITStructureRuleSyntax.decodeDITStructureRule(value, schema,
226                                                           false);
227        dsr.setSchemaFile(getSchemaFile());
228    
229        return dsr;
230      }
231    
232    
233    
234      /**
235       * Retrieves the set of names that may be used to reference this DIT
236       * structure rule.  The returned mapping will be between an all
237       * lower-case form of the name and a name in the user-defined form
238       * (which may include mixed capitalization).
239       *
240       * @return  The set of names that may be used to reference this DIT
241       *          structure rule.
242       */
243      public Map<String,String> getNames()
244      {
245        return names;
246      }
247    
248    
249    
250      /**
251       * Indicates whether this DIT structure rule has the specified name.
252       *
253       * @param  lowerName  The lowercase name for which to make the
254       *                    determination.
255       *
256       * @return  {@code true} if the specified name is assigned to this
257       *          DIT structure rule, or {@code false} if not.
258       */
259      public boolean hasName(String lowerName)
260      {
261        return names.containsKey(lowerName);
262      }
263    
264    
265    
266      /**
267       * Retrieves the rule ID for this DIT structure rule.
268       *
269       * @return  The rule ID for this DIT structure rule.
270       */
271      public int getRuleID()
272      {
273        return ruleID;
274      }
275    
276    
277    
278      /**
279       * Retrieves the name or rule ID for this DIT structure rule.  If it
280       * has one or more names, then the primary name will be returned.
281       * If it does not have any names, then the rule ID will be returned.
282       *
283       * @return  The name or rule ID for this DIT structure rule.
284       */
285      public String getNameOrRuleID()
286      {
287        if (names.isEmpty())
288        {
289          return String.valueOf(ruleID);
290        }
291        else
292        {
293          return names.values().iterator().next();
294        }
295      }
296    
297    
298    
299      /**
300       * Retrieves the path to the schema file that contains the
301       * definition for this DIT structure rule.
302       *
303       * @return  The path to the schema file that contains the definition
304       *          for this DIT structure rule, or {@code null} if it
305       *          is not known or if it is not stored in any schema file.
306       */
307      public String getSchemaFile()
308      {
309        List<String> values =
310             extraProperties.get(SCHEMA_PROPERTY_FILENAME);
311        if ((values == null) || values.isEmpty())
312        {
313          return null;
314        }
315    
316        return values.get(0);
317      }
318    
319    
320    
321      /**
322       * Specifies the path to the schema file that contains the
323       * definition for this DIT structure rule.
324       *
325       * @param  schemaFile  The path to the schema file that contains the
326       *                     definition for this DIT structure rule.
327       */
328      public void setSchemaFile(String schemaFile)
329      {
330        setExtraProperty(SCHEMA_PROPERTY_FILENAME, schemaFile);
331      }
332    
333    
334    
335      /**
336       * Retrieves the description for this DIT structure rule.
337       *
338       * @return  The description for this DIT structure rule.
339       */
340      public String getDescription()
341      {
342        return description;
343      }
344    
345    
346    
347      /**
348       * Retrieves the name form for this DIT structure rule.
349       *
350       * @return  The name form for this DIT structure rule.
351       */
352      public NameForm getNameForm()
353      {
354        return nameForm;
355      }
356    
357    
358    
359      /**
360       * Retrieves the structural objectclass for the name form with which
361       * this DIT structure rule is associated.
362       *
363       * @return  The structural objectclass for the name form with which
364       *          this DIT structure rule is associated.
365       */
366      public ObjectClass getStructuralClass()
367      {
368        return nameForm.getStructuralClass();
369      }
370    
371    
372    
373      /**
374       * Retrieves the set of superior rules for this DIT structure rule.
375       *
376       * @return  The set of superior rules for this DIT structure rule.
377       */
378      public Set<DITStructureRule> getSuperiorRules()
379      {
380        return superiorRules;
381      }
382    
383    
384    
385      /**
386       * Indicates whether this DIT structure rule has one or more
387       * superior rules.
388       *
389       * @return  {@code true} if this DIT structure rule has one or more
390       *          superior rules, or {@code false} if not.
391       */
392      public boolean hasSuperiorRules()
393      {
394        return ((superiorRules != null) && (! superiorRules.isEmpty()));
395      }
396    
397    
398    
399      /**
400       * Indicates whether this DIT structure rule is declared "obsolete".
401       *
402       * @return  {@code true} if this DIT structure rule is declared
403       *          "obsolete", or {@code false} if not.
404       */
405      public boolean isObsolete()
406      {
407        return isObsolete;
408      }
409    
410    
411    
412      /**
413       * Retrieves a mapping between the names of any extra non-standard
414       * properties that may be associated with this DIT structure rule
415       * and the value for that property.
416       *
417       * @return  A mapping between the names of any extra non-standard
418       *          properties that may be associated with this DIT
419       *          structure rule and the value for that property.
420       */
421      public Map<String,List<String>> getExtraProperties()
422      {
423        return extraProperties;
424      }
425    
426    
427    
428      /**
429       * Retrieves the value of the specified "extra" property for this
430       * DIT structure rule.
431       *
432       * @param  propertyName  The name of the "extra" property for which
433       *                       to retrieve the value.
434       *
435       * @return  The value of the specified "extra" property for this DIT
436       *          structure rule, or {@code null} if no such property is
437       *          defined.
438       */
439      public List<String> getExtraProperty(String propertyName)
440      {
441        return extraProperties.get(propertyName);
442      }
443    
444    
445    
446      /**
447       * Specifies the provided "extra" property for this DIT structure
448       * rule.
449       *
450       * @param  name   The name for the "extra" property.  It must not be
451       *                {@code null}.
452       * @param  value  The value for the "extra" property, or
453       *                {@code null} if the property is to be removed.
454       */
455      public void setExtraProperty(String name, String value)
456      {
457        ensureNotNull(name);
458    
459        if (value == null)
460        {
461          extraProperties.remove(name);
462        }
463        else
464        {
465          LinkedList<String> values = new LinkedList<String>();
466          values.add(value);
467    
468          extraProperties.put(name, values);
469        }
470      }
471    
472    
473    
474      /**
475       * Specifies the provided "extra" property for this DIT structure
476       * rule.
477       *
478       * @param  name    The name for the "extra" property.  It must not
479       *                 be {@code null}.
480       * @param  values  The set of value for the "extra" property, or
481       *                 {@code null} if the property is to be removed.
482       */
483      public void setExtraProperty(String name, List<String> values)
484      {
485        ensureNotNull(name);
486    
487        if ((values == null) || values.isEmpty())
488        {
489          extraProperties.remove(name);
490        }
491        else
492        {
493          LinkedList<String> valuesCopy = new LinkedList<String>(values);
494          extraProperties.put(name, valuesCopy);
495        }
496      }
497    
498    
499    
500      /**
501       * Indicates whether the provided object is equal to this DIT
502       * structure rule.  The object will be considered equal if it is a
503       * DIT structure rule with the same OID as the current type.
504       *
505       * @param  o  The object for which to make the determination.
506       *
507       * @return  {@code true} if the provided object is equal to this
508       *          attribute, or {@code false} if not.
509       */
510      public boolean equals(Object o)
511      {
512        if (this == o)
513        {
514          return true;
515        }
516    
517        if ((o == null) || (! (o instanceof DITStructureRule)))
518        {
519          return false;
520        }
521    
522        return (ruleID == ((DITStructureRule) o).ruleID);
523      }
524    
525    
526    
527      /**
528       * Retrieves the hash code for this DIT structure rule.  It will be
529       * equal to the rule ID.
530       *
531       * @return  The hash code for this DIT structure rule.
532       */
533      public int hashCode()
534      {
535        return ruleID;
536      }
537    
538    
539    
540      /**
541       * Retrieves the string representation of this attribute type in the
542       * form specified in RFC 2252.
543       *
544       * @return  The string representation of this attribute type in the
545       *          form specified in RFC 2252.
546       */
547      public String toString()
548      {
549        StringBuilder buffer = new StringBuilder();
550        toString(buffer, true);
551        return buffer.toString();
552      }
553    
554    
555    
556      /**
557       * Appends a string representation of this attribute type in the
558       * form specified in RFC 2252 to the provided buffer.
559       *
560       * @param  buffer              The buffer to which the information
561       *                             should be appended.
562       * @param  includeFileElement  Indicates whether to include an
563       *                             "extra" property that specifies the
564       *                             path to the schema file from which
565       *                             this DIT structure rule was loaded.
566       */
567      public void toString(StringBuilder buffer,
568                           boolean includeFileElement)
569      {
570        buffer.append("( ");
571        buffer.append(ruleID);
572    
573        if (! names.isEmpty())
574        {
575          Iterator<String> iterator = names.values().iterator();
576    
577          String firstName = iterator.next();
578          if (iterator.hasNext())
579          {
580            buffer.append(" NAME ( '");
581            buffer.append(firstName);
582    
583            while (iterator.hasNext())
584            {
585              buffer.append("' '");
586              buffer.append(iterator.next());
587            }
588    
589            buffer.append("' )");
590          }
591          else
592          {
593            buffer.append(" NAME '");
594            buffer.append(firstName);
595            buffer.append("'");
596          }
597        }
598    
599        if ((description != null) && (description.length() > 0))
600        {
601          buffer.append(" DESC '");
602          buffer.append(description);
603          buffer.append("'");
604        }
605    
606        if (isObsolete)
607        {
608          buffer.append(" OBSOLETE");
609        }
610    
611        buffer.append(" FORM ");
612        buffer.append(nameForm.getNameOrOID());
613    
614        if ((superiorRules != null) && (! superiorRules.isEmpty()))
615        {
616          Iterator<DITStructureRule> iterator = superiorRules.iterator();
617    
618          int firstRule = iterator.next().getRuleID();
619          if (iterator.hasNext())
620          {
621            buffer.append(" SUP ( ");
622            buffer.append(firstRule);
623    
624            while (iterator.hasNext())
625            {
626              buffer.append(" ");
627              buffer.append(iterator.next().getRuleID());
628            }
629    
630            buffer.append(" )");
631          }
632          else
633          {
634            buffer.append(" SUP ");
635            buffer.append(firstRule);
636          }
637        }
638    
639        if (! extraProperties.isEmpty())
640        {
641          for (String property : extraProperties.keySet())
642          {
643            if ((! includeFileElement) &&
644                property.equals(SCHEMA_PROPERTY_FILENAME))
645            {
646              continue;
647            }
648    
649            List<String> valueList = extraProperties.get(property);
650    
651            buffer.append(" ");
652            buffer.append(property);
653    
654            if (valueList.size() == 1)
655            {
656              buffer.append(" '");
657              buffer.append(valueList.get(0));
658              buffer.append("'");
659            }
660            else
661            {
662              buffer.append(" ( ");
663    
664              for (String value : valueList)
665              {
666                buffer.append("'");
667                buffer.append(value);
668                buffer.append("' ");
669              }
670    
671              buffer.append(")");
672            }
673          }
674        }
675    
676        buffer.append(" )");
677      }
678    }
679