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    import org.opends.messages.Message;
029    
030    
031    
032    import java.util.Collection;
033    import java.util.List;
034    import java.util.Map;
035    
036    import org.opends.server.api.ApproximateMatchingRule;
037    import org.opends.server.api.AttributeSyntax;
038    import org.opends.server.api.EqualityMatchingRule;
039    import org.opends.server.api.OrderingMatchingRule;
040    import org.opends.server.api.SubstringMatchingRule;
041    import org.opends.server.core.DirectoryServer;
042    import org.opends.server.schema.AttributeTypeSyntax;
043    
044    import static org.opends.server.loggers.debug.DebugLogger.*;
045    import org.opends.server.loggers.debug.DebugTracer;
046    import static org.opends.messages.CoreMessages.*;
047    import static org.opends.server.util.ServerConstants.*;
048    import static org.opends.server.util.Validator.*;
049    
050    
051    
052    /**
053     * This class defines a data structure for storing and interacting
054     * with an attribute type, which contains information about the format
055     * of an attribute and the syntax and matching rules that should be
056     * used when interacting with it.
057     * <p>
058     * Any methods which accesses the set of names associated with this
059     * attribute type, will retrieve the primary name as the first name,
060     * regardless of whether or not it was contained in the original set
061     * of <code>names</code> passed to the constructor.
062     * <p>
063     * Where ordered sets of names, or extra properties are provided, the
064     * ordering will be preserved when the associated fields are accessed
065     * via their getters or via the {@link #toString()} methods.
066     */
067    @org.opends.server.types.PublicAPI(
068         stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
069         mayInstantiate=false,
070         mayExtend=false,
071         mayInvoke=true)
072    public final class AttributeType
073           extends CommonSchemaElements
074           implements SchemaFileElement
075    {
076      /**
077       * The tracer object for the debug logger.
078       */
079      private static final DebugTracer TRACER = getTracer();
080    
081      // The approximate matching rule for this attribute type.
082      private final ApproximateMatchingRule approximateMatchingRule;
083    
084      // The syntax for this attribute type.
085      private final AttributeSyntax syntax;
086    
087      // The superior attribute type from which this attribute type
088      // inherits.
089      private final AttributeType superiorType;
090    
091      // The attribute usage for this attribute type.
092      private final AttributeUsage attributeUsage;
093    
094      // Indicates whether this attribute type is declared "collective".
095      private final boolean isCollective;
096    
097      // Indicates whether this attribute type is declared
098      // "no-user-modification".
099      private final boolean isNoUserModification;
100    
101      // Indicates whether this attribute type is the objectclass type.
102      private final boolean isObjectClassType;
103    
104      // Indicates whether this attribute type is operational.
105      private final boolean isOperational;
106    
107      // Indicates whether this attribute type is declared "single-value".
108      private final boolean isSingleValue;
109    
110      // Indicates whether there is a possibility that this attribute type
111      // may have one or more subtypes that list this type or one of its
112      // subtypes as a superior.  Note that this variable is intentional
113      // not declared "final", but if it ever gets set to "true", then it
114      // should never be unset back to "false".
115      private boolean mayHaveSubordinateTypes;
116    
117      // The equality matching rule for this attribute type.
118      private final EqualityMatchingRule equalityMatchingRule;
119    
120      // The ordering matching rule for this attribute type.
121      private final OrderingMatchingRule orderingMatchingRule;
122    
123      // The definition string used to create this attribute type.
124      private final String definition;
125    
126      // The OID for the associated syntax.
127      private final String syntaxOID;
128    
129      // The substring matching rule for this attribute type.
130      private final SubstringMatchingRule substringMatchingRule;
131    
132    
133    
134      /**
135       * Creates a new attribute type with the provided information.
136       * <p>
137       * If no <code>primaryName</code> is specified, but a set of
138       * <code>names</code> is specified, then the first name retrieved
139       * from the set of <code>names</code> will be used as the primary
140       * name.
141       *
142       * @param definition
143       *          The definition string used to create this attribute
144       *          type.  It must not be {@code null}.
145       * @param primaryName
146       *          The primary name for this attribute type, or
147       *          <code>null</code> if there is no primary name.
148       * @param typeNames
149       *          The full set of names for this attribute type, or
150       *          <code>null</code> if there are no names.
151       * @param oid
152       *          The OID for this attribute type.  It must not be
153       *          {@code null}.
154       * @param description
155       *          The description for the attribute type, or
156       *          <code>null</code> if there is no description.
157       * @param superiorType
158       *          The reference to the superior type for this attribute
159       *          type, or <code>null</code> if there is no superior
160       *          type.
161       * @param syntax
162       *          The syntax for this attribute type, or <code>null</code>
163       *          if there is no syntax.
164       * @param attributeUsage
165       *          The attribute usage for this attribute type, or
166       *          <code>null</code> to default to user applications.
167       * @param isCollective
168       *          Indicates whether this attribute type is declared
169       *          "collective".
170       * @param isNoUserModification
171       *          Indicates whether this attribute type is declared
172       *          "no-user-modification".
173       * @param isObsolete
174       *          Indicates whether this attribute type is declared
175       *          "obsolete".
176       * @param isSingleValue
177       *          Indicates whether this attribute type is declared
178       *          "single-value".
179       */
180      public AttributeType(String definition, String primaryName,
181                           Collection<String> typeNames,
182                           String oid, String description,
183                           AttributeType superiorType,
184                           AttributeSyntax syntax,
185                           AttributeUsage attributeUsage,
186                           boolean isCollective,
187                           boolean isNoUserModification,
188                           boolean isObsolete, boolean isSingleValue)
189      {
190        this(definition, primaryName, typeNames, oid, description,
191            superiorType, syntax, null, null, null,
192            null, attributeUsage, isCollective,
193            isNoUserModification, isObsolete, isSingleValue, null);
194      }
195    
196    
197    
198      /**
199       * Creates a new attribute type with the provided information.
200       * <p>
201       * If no <code>primaryName</code> is specified, but a set of
202       * <code>names</code> is specified, then the first name retrieved
203       * from the set of <code>names</code> will be used as the primary
204       * name.
205       *
206       * @param definition
207       *          The definition string used to create this attribute
208       *          type.  It must not be {@code null}.
209       * @param primaryName
210       *          The primary name for this attribute type, or
211       *          <code>null</code> if there is no primary name.
212       * @param typeNames
213       *          The full set of names for this attribute type, or
214       *          <code>null</code> if there are no names.
215       * @param oid
216       *          The OID for this attribute type.  It must not be
217       *          {@code null}.
218       * @param description
219       *          The description for the attribute type, or
220       *          <code>null</code> if there is no description.
221       * @param superiorType
222       *          The reference to the superior type for this attribute
223       *          type, or <code>null</code> if there is no superior
224       *          type.
225       * @param syntax
226       *          The syntax for this attribute type, or <code>null</code>
227       *          if there is no syntax.
228       * @param approximateMatchingRule
229       *          The approximate matching rule for this attribute type,
230       *          or <code>null</code> if there is no rule.
231       * @param equalityMatchingRule
232       *          The equality matching rule for this attribute type, or
233       *          <code>null</code> if there is no rule.
234       * @param orderingMatchingRule
235       *          The ordering matching rule for this attribute type, or
236       *          <code>null</code> if there is no rule.
237       * @param substringMatchingRule
238       *          The substring matching rule for this attribute type, or
239       *          <code>null</code> if there is no rule.
240       * @param attributeUsage
241       *          The attribute usage for this attribute type, or
242       *          <code>null</code> to default to user applications.
243       * @param isCollective
244       *          Indicates whether this attribute type is declared
245       *          "collective".
246       * @param isNoUserModification
247       *          Indicates whether this attribute type is declared
248       *          "no-user-modification".
249       * @param isObsolete
250       *          Indicates whether this attribute type is declared
251       *          "obsolete".
252       * @param isSingleValue
253       *          Indicates whether this attribute type is declared
254       *          "single-value".
255       * @param extraProperties
256       *          A set of extra properties for this attribute type, or
257       *          <code>null</code> if there are no extra properties.
258       */
259      public AttributeType(String definition, String primaryName,
260                           Collection<String> typeNames,
261                           String oid, String description,
262                           AttributeType superiorType,
263                           AttributeSyntax syntax,
264                           ApproximateMatchingRule
265                                approximateMatchingRule,
266                           EqualityMatchingRule equalityMatchingRule,
267                           OrderingMatchingRule orderingMatchingRule,
268                           SubstringMatchingRule substringMatchingRule,
269                           AttributeUsage attributeUsage,
270                           boolean isCollective,
271                           boolean isNoUserModification,
272                           boolean isObsolete, boolean isSingleValue,
273                           Map<String,List<String>> extraProperties)
274      {
275        super(primaryName, typeNames, oid, description, isObsolete,
276            extraProperties);
277    
278    
279        ensureNotNull(definition, oid);
280    
281        this.superiorType = superiorType;
282        this.isCollective = isCollective;
283        this.isNoUserModification = isNoUserModification;
284        this.isSingleValue = isSingleValue;
285    
286        mayHaveSubordinateTypes = false;
287    
288        int schemaFilePos = definition.indexOf(SCHEMA_PROPERTY_FILENAME);
289        if (schemaFilePos > 0)
290        {
291          String defStr;
292          try
293          {
294            int firstQuotePos = definition.indexOf('\'', schemaFilePos);
295            int secondQuotePos = definition.indexOf('\'',
296                                                    firstQuotePos+1);
297    
298            defStr = definition.substring(0, schemaFilePos).trim() + " " +
299                     definition.substring(secondQuotePos+1).trim();
300          }
301          catch (Exception e)
302          {
303            if (debugEnabled())
304            {
305              TRACER.debugCaught(DebugLogLevel.ERROR, e);
306            }
307    
308            defStr = definition;
309          }
310    
311          this.definition = defStr;
312        }
313        else
314        {
315          this.definition = definition;
316        }
317    
318        if (syntax == null)
319        {
320          if (superiorType != null)
321          {
322            this.syntax = superiorType.getSyntax();
323          }
324          else
325          {
326            this.syntax = DirectoryServer.getDefaultAttributeSyntax();
327          }
328        }
329        else
330        {
331          this.syntax = syntax;
332        }
333        syntaxOID = this.syntax.getOID();
334    
335    
336        if (approximateMatchingRule == null)
337        {
338          this.approximateMatchingRule =
339                 this.syntax.getApproximateMatchingRule();
340        }
341        else
342        {
343          this.approximateMatchingRule = approximateMatchingRule;
344        }
345    
346    
347        if (equalityMatchingRule == null)
348        {
349          this.equalityMatchingRule =
350            this.syntax.getEqualityMatchingRule();
351        }
352        else
353        {
354          this.equalityMatchingRule = equalityMatchingRule;
355        }
356    
357    
358        if (orderingMatchingRule == null)
359        {
360          this.orderingMatchingRule =
361            this.syntax.getOrderingMatchingRule();
362        }
363        else
364        {
365          this.orderingMatchingRule = orderingMatchingRule;
366        }
367    
368    
369        if (substringMatchingRule == null)
370        {
371          this.substringMatchingRule =
372            this.syntax.getSubstringMatchingRule();
373        }
374        else
375        {
376          this.substringMatchingRule = substringMatchingRule;
377        }
378    
379        if (attributeUsage != null)
380        {
381          this.attributeUsage = attributeUsage;
382        }
383        else
384        {
385          this.attributeUsage = AttributeUsage.USER_APPLICATIONS;
386        }
387    
388        if (oid.equals(OBJECTCLASS_ATTRIBUTE_TYPE_OID))
389        {
390          isObjectClassType = true;
391        }
392        else
393        {
394          isObjectClassType = hasName(OBJECTCLASS_ATTRIBUTE_TYPE_NAME);
395        }
396    
397        isOperational = this.attributeUsage.isOperational();
398      }
399    
400    
401    
402      /**
403       * Retrieves the definition string used to create this attribute
404       * type.
405       *
406       * @return  The definition string used to create this attribute
407       *          type.
408       */
409      public String getDefinition()
410      {
411        return definition;
412      }
413    
414      /**
415       * Retrieves the definition string used to create this attribute
416       * type and including the X-SCHEMA-FILE extension.
417       *
418       * @return  The definition string used to create this attribute
419       *          type including the X-SCHEMA-FILE extension.
420       */
421      public String getDefinitionWithFileName()
422      {
423        if (getSchemaFile() != null)
424        {
425          int pos = definition.lastIndexOf(')');
426          String defStr = definition.substring(0, pos).trim() + " " +
427                          SCHEMA_PROPERTY_FILENAME + " '" +
428                          getSchemaFile() + "' )";
429          return defStr;
430        }
431        else
432          return definition;
433      }
434    
435      /**
436       * Creates a new instance of this attribute type based on the
437       * definition string.  It will also preserve other state information
438       * associated with this attribute type that is not included in the
439       * definition string (e.g., the name of the schema file with which
440       * it is associated).
441       *
442       * @return  The new instance of this attribute type based on the
443       *          definition string.
444       *
445       * @throws  DirectoryException  If a problem occurs while attempting
446       *                              to create a new attribute type
447       *                              instance from the definition string.
448       */
449      public AttributeType recreateFromDefinition()
450             throws DirectoryException
451      {
452        ByteString value  = ByteStringFactory.create(definition);
453        Schema     schema = DirectoryServer.getSchema();
454    
455        AttributeType at =
456             AttributeTypeSyntax.decodeAttributeType(value, schema,
457                                                  false);
458        at.setSchemaFile(getSchemaFile());
459        at.mayHaveSubordinateTypes = mayHaveSubordinateTypes;
460    
461        return at;
462      }
463    
464    
465    
466      /**
467       * Retrieves the superior type for this attribute type.
468       *
469       * @return  The superior type for this attribute type, or
470       *          <CODE>null</CODE> if it does not have one.
471       */
472      public AttributeType getSuperiorType()
473      {
474        return superiorType;
475      }
476    
477    
478    
479      /**
480       * Indicates whether there is a possibility that this attribute type
481       * may have one or more subordinate attribute types defined in the
482       * server schema.  This is only intended for use by the
483       * {@code org.opends.server.types.Entry} class for the purpose of
484       * determining whether to check for subtypes when retrieving
485       * attributes.  Note that it is possible for this method to report
486       * false positives (if an attribute type that previously had one or
487       * more subordinate types no longer has any), but not false
488       * negatives.
489       *
490       * @return  {@code true} if the {@code hasSubordinateTypes} flag has
491       *          been set for this attribute type at any time since
492       *          startup, or {@code false} if not.
493       */
494      boolean mayHaveSubordinateTypes()
495      {
496        return mayHaveSubordinateTypes;
497      }
498    
499    
500    
501      /**
502       * Sets a flag indicating that this attribute type may have one or
503       * more subordinate attribute types defined in the server schema.
504       * This is only intended for use by the
505       * {@code org.opends.server.types.Schema} class.
506       */
507      void setMayHaveSubordinateTypes()
508      {
509        mayHaveSubordinateTypes = true;
510      }
511    
512    
513    
514      /**
515       * Retrieves the syntax for this attribute type.
516       *
517       * @return  The syntax for this attribute type.
518       */
519      public AttributeSyntax getSyntax()
520      {
521        return syntax;
522      }
523    
524    
525    
526      /**
527       * Retrieves the OID for this syntax associated with this attribute
528       * type.
529       *
530       * @return  The OID for this syntax associated with this attribute
531       *          type.
532       */
533      public String getSyntaxOID()
534      {
535        return syntaxOID;
536      }
537    
538    
539    
540      /**
541       * Retrieves the matching rule that should be used for approximate
542       * matching with this attribute type.
543       *
544       * @return  The matching rule that should be used for approximate
545       *          matching with this attribute type.
546       */
547      public ApproximateMatchingRule getApproximateMatchingRule()
548      {
549        return approximateMatchingRule;
550      }
551    
552    
553    
554      /**
555       * Retrieves the matching rule that should be used for equality
556       * matching with this attribute type.
557       *
558       * @return  The matching rule that should be used for equality
559       *          matching with this attribute type.
560       */
561      public EqualityMatchingRule getEqualityMatchingRule()
562      {
563        return equalityMatchingRule;
564      }
565    
566    
567    
568      /**
569       * Retrieves the matching rule that should be used for ordering with
570       * this attribute type.
571       *
572       * @return  The matching rule that should be used for ordering with
573       *          this attribute type.
574       */
575      public OrderingMatchingRule getOrderingMatchingRule()
576      {
577        return orderingMatchingRule;
578      }
579    
580    
581    
582      /**
583       * Retrieves the matching rule that should be used for substring
584       * matching with this attribute type.
585       *
586       * @return  The matching rule that should be used for substring
587       *          matching with this attribute type.
588       */
589      public SubstringMatchingRule getSubstringMatchingRule()
590      {
591        return substringMatchingRule;
592      }
593    
594    
595    
596      /**
597       * Retrieves the usage indicator for this attribute type.
598       *
599       * @return  The usage indicator for this attribute type.
600       */
601      public AttributeUsage getUsage()
602      {
603        return attributeUsage;
604      }
605    
606    
607    
608      /**
609       * Indicates whether this is an operational attribute.  An
610       * operational attribute is one with a usage of
611       * "directoryOperation", "distributedOperation", or "dSAOperation"
612       * (i.e., only userApplications is not operational).
613       *
614       * @return  <CODE>true</CODE> if this is an operational attribute,
615       *          or <CODE>false</CODE> if not.
616       */
617      public boolean isOperational()
618      {
619        return isOperational;
620      }
621    
622    
623    
624      /**
625       * Indicates whether this attribute type is declared "collective".
626       *
627       * @return  <CODE>true</CODE> if this attribute type is declared
628       * "collective", or <CODE>false</CODE> if not.
629       */
630      public boolean isCollective()
631      {
632        return isCollective;
633      }
634    
635    
636    
637      /**
638       * Indicates whether this attribute type is declared
639       * "no-user-modification".
640       *
641       * @return  <CODE>true</CODE> if this attribute type is declared
642       *          "no-user-modification", or <CODE>false</CODE> if not.
643       */
644      public boolean isNoUserModification()
645      {
646        return isNoUserModification;
647      }
648    
649    
650    
651      /**
652       * Indicates whether this attribute type is declared "single-value".
653       *
654       * @return  <CODE>true</CODE> if this attribute type is declared
655       *          "single-value", or <CODE>false</CODE> if not.
656       */
657      public boolean isSingleValue()
658      {
659        return isSingleValue;
660      }
661    
662    
663    
664      /**
665       * Indicates whether this attribute type represents the
666       * "objectclass" attribute.  The determination will be made based on
667       * the name and/or OID.
668       *
669       * @return  <CODE>true</CODE> if this attribute type is the
670       *          objectclass type, or <CODE>false</CODE> if not.
671       */
672      public boolean isObjectClassType()
673      {
674        return isObjectClassType;
675      }
676    
677    
678    
679      /**
680       * Attempts to normalize the provided value using the equality
681       * matching rule associated with this attribute type.
682       *
683       * @param  value  The value to be normalized.
684       *
685       * @return  The normalized form of the provided value.
686       *
687       * @throws  DirectoryException  If this attribute type does not have
688       *                              an equality matching rule, or if the
689       *                              provided value could not be
690       *                              normalized.
691       */
692      public ByteString normalize(ByteString value)
693             throws DirectoryException
694      {
695        if (equalityMatchingRule == null)
696        {
697          Message message = ERR_ATTR_TYPE_NORMALIZE_NO_MR.get(
698              String.valueOf(value), getNameOrOID());
699          throw new DirectoryException(ResultCode.INAPPROPRIATE_MATCHING,
700                                       message);
701        }
702    
703        return equalityMatchingRule.normalizeValue(value);
704      }
705    
706    
707    
708      /**
709       * Generates a hash code for the specified attribute value.  If an
710       * equality matching rule is defined for this type, then it will be
711       * used to generate the hash code.  If the value does not have an
712       * equality matching rule but does have a normalized form, then that
713       * will be used to obtain the hash code.  Otherwise, it will simply
714       * be the hash code of the provided value.
715       *
716       * @param  value  The attribute value for which to generate the hash
717       *                code.
718       *
719       * @return  The generated hash code for the provided value.
720       */
721      public int generateHashCode(AttributeValue value)
722      {
723        try
724        {
725          if (equalityMatchingRule == null)
726          {
727            ByteString normalizedValue = value.getNormalizedValue();
728            if (normalizedValue == null)
729            {
730              return value.getValue().hashCode();
731            }
732            else
733            {
734              return normalizedValue.hashCode();
735            }
736          }
737          else
738          {
739            return equalityMatchingRule.generateHashCode(value);
740          }
741        }
742        catch (Exception e)
743        {
744          if (debugEnabled())
745          {
746            TRACER.debugCaught(DebugLogLevel.ERROR, e);
747          }
748    
749          try
750          {
751            return value.getValue().hashCode();
752          }
753          catch (Exception e2)
754          {
755            if (debugEnabled())
756            {
757              TRACER.debugCaught(DebugLogLevel.ERROR, e2);
758            }
759    
760            return 0;
761          }
762        }
763      }
764    
765    
766    
767      /**
768       * Appends a string representation of this schema definition's
769       * non-generic properties to the provided buffer.
770       *
771       * @param  buffer  The buffer to which the information should be
772       *                 appended.
773       */
774      protected void toStringContent(StringBuilder buffer)
775      {
776        if (superiorType != null)
777        {
778          buffer.append(" SUP ");
779          buffer.append(superiorType.getNameOrOID());
780        }
781    
782        if (equalityMatchingRule != null)
783        {
784          buffer.append(" EQUALITY ");
785          buffer.append(equalityMatchingRule.getNameOrOID());
786        }
787    
788        if (orderingMatchingRule != null)
789        {
790          buffer.append(" ORDERING ");
791          buffer.append(orderingMatchingRule.getNameOrOID());
792        }
793    
794        if (substringMatchingRule != null)
795        {
796          buffer.append(" SUBSTR ");
797          buffer.append(substringMatchingRule.getNameOrOID());
798        }
799    
800        // NOTE -- We will not include any approximate matching rule
801        // information here because it would break the standard and
802        // anything that depends on it.
803        // FIXME -- Should we encode this into one of the "extra"
804        // properties?
805    
806        if (syntax != null)
807        {
808          buffer.append(" SYNTAX ");
809          buffer.append(syntax.getOID());
810        }
811    
812        if (isSingleValue)
813        {
814          buffer.append(" SINGLE-VALUE");
815        }
816    
817        if (isCollective)
818        {
819          buffer.append(" COLLECTIVE");
820        }
821    
822        if (isNoUserModification)
823        {
824          buffer.append(" NO-USER-MODIFICATION");
825        }
826    
827        if (attributeUsage != null)
828        {
829          buffer.append(" USAGE ");
830          buffer.append(attributeUsage.toString());
831        }
832      }
833    }
834