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.api.MatchingRule;
040    import org.opends.server.schema.MatchingRuleUseSyntax;
041    
042    import static org.opends.server.loggers.debug.DebugLogger.*;
043    import org.opends.server.loggers.debug.DebugTracer;
044    import static org.opends.server.util.ServerConstants.*;
045    import static org.opends.server.util.StaticUtils.*;
046    import static org.opends.server.util.Validator.*;
047    
048    
049    
050    /**
051     * This class defines a data structure for storing and interacting
052     * with a matching rule use definition, which may be used to restrict
053     * the set of attribute types that may be used for a given matching
054     * rule.
055     */
056    @org.opends.server.types.PublicAPI(
057         stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
058         mayInstantiate=false,
059         mayExtend=false,
060         mayInvoke=true)
061    public final class MatchingRuleUse
062           implements SchemaFileElement
063    {
064      /**
065       * The tracer object for the debug logger.
066       */
067      private static final DebugTracer TRACER = getTracer();
068    
069      // Indicates whether this matching rule use is declared "obsolete".
070      private final boolean isObsolete;
071    
072      // The set of additional name-value pairs associated with this
073      // matching rule use definition.
074      private final Map<String,List<String>> extraProperties;
075    
076      // The set of names that may be used to refer to this matching rule
077      // use, mapped between their all-lowercase representations and the
078      // user-defined representations.
079      private final Map<String,String> names;
080    
081      // The matching rule with which this matching rule use is
082      // associated.
083      private final MatchingRule matchingRule;
084    
085      // The set of attribute types with which this matching rule use is
086      // associated.
087      private final Set<AttributeType> attributes;
088    
089      // The definition string used to create this matching rule use.
090      private final String definition;
091    
092      // The description for this matching rule use.
093      private final String description;
094    
095    
096    
097      /**
098       * Creates a new matching rule use definition with the provided
099       * information.
100       *
101       * @param  definition       The definition string used to create
102       *                          this matching rule use.  It must not be
103       *                          {@code null}.
104       * @param  matchingRule     The matching rule for this matching rule
105       *                          use.  It must not be {@code null}.
106       * @param  names            The set of names for this matching rule
107       *                          use.
108       * @param  description      The description for this matching rule
109       *                          use.
110       * @param  isObsolete       Indicates whether this matching rule use
111       *                          is declared "obsolete".
112       * @param  attributes       The set of attribute types for this
113       *                          matching rule use.
114       * @param  extraProperties  A set of "extra" properties that may be
115       *                          associated with this matching rule use.
116       */
117      public MatchingRuleUse(String definition, MatchingRule matchingRule,
118                             Map<String,String> names, String description,
119                             boolean isObsolete,
120                             Set<AttributeType> attributes,
121                             Map<String,List<String>> extraProperties)
122      {
123        ensureNotNull(definition, matchingRule);
124    
125        this.matchingRule = matchingRule;
126        this.description  = description;
127        this.isObsolete   = isObsolete;
128    
129        int schemaFilePos = definition.indexOf(SCHEMA_PROPERTY_FILENAME);
130        if (schemaFilePos > 0)
131        {
132          String defStr;
133          try
134          {
135            int firstQuotePos = definition.indexOf('\'', schemaFilePos);
136            int secondQuotePos = definition.indexOf('\'',
137                                                    firstQuotePos+1);
138    
139            defStr = definition.substring(0, schemaFilePos).trim() + " " +
140                     definition.substring(secondQuotePos+1).trim();
141          }
142          catch (Exception e)
143          {
144            if (debugEnabled())
145            {
146              TRACER.debugCaught(DebugLogLevel.ERROR, e);
147            }
148    
149            defStr = definition;
150          }
151    
152          this.definition = defStr;
153        }
154        else
155        {
156          this.definition = definition;
157        }
158    
159        if ((names == null) || names.isEmpty())
160        {
161          this.names = new LinkedHashMap<String,String>(0);
162        }
163        else
164        {
165          this.names = new LinkedHashMap<String,String>(names);
166        }
167    
168        if ((attributes == null) || attributes.isEmpty())
169        {
170          this.attributes = new LinkedHashSet<AttributeType>(0);
171        }
172        else
173        {
174          this.attributes = new LinkedHashSet<AttributeType>(attributes);
175        }
176    
177        if ((extraProperties == null) || extraProperties.isEmpty())
178        {
179          this.extraProperties =
180               new LinkedHashMap<String,List<String>>(0);
181        }
182        else
183        {
184          this.extraProperties =
185               new LinkedHashMap<String,List<String>>(extraProperties);
186        }
187      }
188    
189    
190    
191      /**
192       * Retrieves the definition string used to create this matching rule
193       * use.
194       *
195       * @return  The definition string used to create this matching rule
196       *          use.
197       */
198      public String getDefinition()
199      {
200        return definition;
201      }
202    
203    
204    
205      /**
206       * Creates a new instance of this matching rule use based on the
207       * definition string.  It will also preserve other state information
208       * associated with this matching rule use that is not included in
209       * the definition string (e.g., the name of the schema file with
210       * which it is associated).
211       *
212       * @return  The new instance of this matching rule use based on the
213       *          definition string.
214       *
215       * @throws  DirectoryException  If a problem occurs while attempting
216       *                              to create a new matching rule use
217       *                              instance from the definition string.
218       */
219      public MatchingRuleUse recreateFromDefinition()
220             throws DirectoryException
221      {
222        ByteString value  = ByteStringFactory.create(definition);
223        Schema     schema = DirectoryConfig.getSchema();
224    
225        MatchingRuleUse mru =
226             MatchingRuleUseSyntax.decodeMatchingRuleUse(value, schema,
227                                                         false);
228        mru.setSchemaFile(getSchemaFile());
229    
230        return mru;
231      }
232    
233    
234    
235      /**
236       * Retrieves the matching rule for this matching rule use.
237       *
238       * @return  The matching rule for this matching rule use.
239       */
240      public MatchingRule getMatchingRule()
241      {
242        return matchingRule;
243      }
244    
245    
246    
247      /**
248       * Retrieves the set of names for this matching rule use.  The
249       * mapping will be between the names in all lowercase form and the
250       * names in the user-defined form.
251       *
252       * @return  The set of names for this matching rule use.
253       */
254      public Map<String,String> getNames()
255      {
256        return names;
257      }
258    
259    
260    
261      /**
262       * Retrieves the primary name to use when referencing this matching
263       * rule use.
264       *
265       * @return  The primary name to use when referencing this matching
266       *          rule use, or {@code null} if there is none.
267       */
268      public String getName()
269      {
270        if (names.isEmpty())
271        {
272          return null;
273        }
274        else
275        {
276          return names.values().iterator().next();
277        }
278      }
279    
280    
281    
282      /**
283       * Indicates whether this matching rule use has the specified name.
284       *
285       * @param  lowerName  The name for which to make the determination,
286       *                    formatted in all lowercase characters.
287       *
288       * @return  {@code true} if this matching rule use has the specified
289       *          name, or {@code false} if not.
290       */
291      public boolean hasName(String lowerName)
292      {
293        return names.containsKey(lowerName);
294      }
295    
296    
297    
298      /**
299       * Retrieves the path to the schema file that contains the
300       * definition for this matching rule use.
301       *
302       * @return  The path to the schema file that contains the definition
303       *          for this matching rule use, or {@code null} if it is not
304       *          known or if it is not stored in any schema file.
305       */
306      public String getSchemaFile()
307      {
308        List<String> values =
309             extraProperties.get(SCHEMA_PROPERTY_FILENAME);
310        if ((values == null) || values.isEmpty())
311        {
312          return null;
313        }
314    
315        return values.get(0);
316      }
317    
318    
319    
320      /**
321       * Specifies the path to the schema file that contains the
322       * definition for this matching rule use.
323       *
324       * @param  schemaFile  The path to the schema file that contains the
325       *                     definition for this matching rule use.
326       */
327      public void setSchemaFile(String schemaFile)
328      {
329        setExtraProperty(SCHEMA_PROPERTY_FILENAME, schemaFile);
330      }
331    
332    
333    
334      /**
335       * Retrieves the description for this matching rule use.
336       *
337       * @return  The description for this matching rule use, or
338       *          {@code null} if there is none.
339       */
340      public String getDescription()
341      {
342        return description;
343      }
344    
345    
346    
347      /**
348       * Indicates whether this matching rule use is declared "obsolete".
349       *
350       * @return  {@code true} if this matching rule use is declared
351       *          "obsolete", or {@code false} if it is not.
352       */
353      public boolean isObsolete()
354      {
355        return isObsolete;
356      }
357    
358    
359    
360      /**
361       * Retrieves the set of attributes associated with this matching
362       * rule use.
363       *
364       * @return  The set of attributes associated with this matching
365       *          rule use.
366       */
367      public Set<AttributeType> getAttributes()
368      {
369        return attributes;
370      }
371    
372    
373    
374      /**
375       * Indicates whether the provided attribute type is referenced by
376       * this matching rule use.
377       *
378       * @param  attributeType  The attribute type for which to make the
379       *                        determination.
380       *
381       * @return  {@code true} if the provided attribute type is
382       *          referenced by this matching rule use, or {@code false}
383       *          if it is not.
384       */
385      public boolean appliesToAttribute(AttributeType attributeType)
386      {
387        return attributes.contains(attributeType);
388      }
389    
390    
391    
392      /**
393       * Retrieves a mapping between the names of any extra non-standard
394       * properties that may be associated with this matching rule use and
395       * the value for that property.
396       *
397       * @return  A mapping between the names of any extra non-standard
398       *          properties that may be associated with this matching
399       *          rule use and the value for that property.
400       */
401      public Map<String,List<String>> getExtraProperties()
402      {
403        return extraProperties;
404      }
405    
406    
407    
408      /**
409       * Retrieves the value of the specified "extra" property for this
410       * matching rule use.
411       *
412       * @param  propertyName  The name of the "extra" property for which
413       *                       to retrieve the value.
414       *
415       * @return  The value of the specified "extra" property for this
416       *          matching rule use, or {@code null} if no such property
417       *          is defined.
418       */
419      public List<String> getExtraProperty(String propertyName)
420      {
421        return extraProperties.get(propertyName);
422      }
423    
424    
425    
426      /**
427       * Specifies the provided "extra" property for this matching rule
428       * use.
429       *
430       * @param  name   The name for the "extra" property.  It must not be
431       *                {@code null}.
432       * @param  value  The value for the "extra" property, or
433       *                {@code null} if the property is to be removed.
434       */
435      public void setExtraProperty(String name, String value)
436      {
437        ensureNotNull(name);
438    
439        if (value == null)
440        {
441          extraProperties.remove(name);
442        }
443        else
444        {
445          LinkedList<String> values = new LinkedList<String>();
446          values.add(value);
447    
448          extraProperties.put(name, values);
449        }
450      }
451    
452    
453    
454      /**
455       * Specifies the provided "extra" property for this matching rule
456       * use.
457       *
458       * @param  name    The name for the "extra" property.  It must not
459       *                 be {@code null}.
460       * @param  values  The set of value for the "extra" property, or
461       *                 {@code null} if the property is to be removed.
462       */
463      public void setExtraProperty(String name, List<String> values)
464      {
465        ensureNotNull(name);
466    
467        if ((values == null) || values.isEmpty())
468        {
469          extraProperties.remove(name);
470        }
471        else
472        {
473          LinkedList<String> valuesCopy = new LinkedList<String>(values);
474          extraProperties.put(name, valuesCopy);
475        }
476      }
477    
478    
479    
480      /**
481       * Indicates whether the provided object is equal to this matching
482       * rule use.  The object will be considered equal if it is a
483       * matching rule use with the same matching rule.
484       *
485       * @param  o  The object for which to make the determination.
486       *
487       * @return  {@code true} if the provided object is equal to this
488       *          matching rule use, or {@code false} if not.
489       */
490      public boolean equals(Object o)
491      {
492        if (this == o)
493        {
494          return true;
495        }
496    
497        if ((o == null) || (! (o instanceof MatchingRuleUse)))
498        {
499          return false;
500        }
501    
502        return matchingRule.equals(((MatchingRuleUse) o).matchingRule);
503      }
504    
505    
506    
507      /**
508       * Retrieves the hash code for this matching rule use.  It will be
509       * equal to the hash code for the associated matching rule.
510       *
511       * @return  The hash code for this matching rule use.
512       */
513      public int hashCode()
514      {
515        return matchingRule.hashCode();
516      }
517    
518    
519    
520      /**
521       * Retrieves the string representation of this matching rule use in
522       * the form specified in RFC 2252.
523       *
524       * @return  The string representation of this matching rule use in
525       *          the form specified in RFC 2252.
526       */
527      public String toString()
528      {
529        StringBuilder buffer = new StringBuilder();
530        toString(buffer, true);
531        return buffer.toString();
532      }
533    
534    
535    
536      /**
537       * Appends a string representation of this matching rule use in the
538       * form specified in RFC 2252 to the provided buffer.
539       *
540       * @param  buffer              The buffer to which the information
541       *                             should be appended.
542       * @param  includeFileElement  Indicates whether to include an
543       *                             "extra" property that specifies the
544       *                             path to the schema file from which
545       *                             this matching rule use was loaded.
546       */
547      public void toString(StringBuilder buffer,
548                           boolean includeFileElement)
549      {
550        buffer.append("( ");
551        buffer.append(matchingRule.getOID());
552    
553        if (! names.isEmpty())
554        {
555          Iterator<String> iterator = names.values().iterator();
556    
557          String firstName = iterator.next();
558          if (iterator.hasNext())
559          {
560            buffer.append(" NAME ( '");
561            buffer.append(firstName);
562    
563            while (iterator.hasNext())
564            {
565              buffer.append("' '");
566              buffer.append(iterator.next());
567            }
568    
569            buffer.append("' )");
570          }
571          else
572          {
573            buffer.append(" NAME '");
574            buffer.append(firstName);
575            buffer.append("'");
576          }
577        }
578    
579        if ((description != null) && (description.length() > 0))
580        {
581          buffer.append(" DESC '");
582          buffer.append(description);
583          buffer.append("'");
584        }
585    
586        if (isObsolete)
587        {
588          buffer.append(" OBSOLETE");
589        }
590    
591        buffer.append(" APPLIES ");
592        Iterator<AttributeType> iterator = attributes.iterator();
593        String firstName = iterator.next().getNameOrOID();
594        if (iterator.hasNext())
595        {
596          buffer.append("( ");
597          buffer.append(firstName);
598    
599          while (iterator.hasNext())
600          {
601            buffer.append(" $ ");
602            buffer.append(iterator.next().getNameOrOID());
603          }
604    
605          buffer.append(" )");
606        }
607        else
608        {
609          buffer.append(firstName);
610        }
611    
612        if (! extraProperties.isEmpty())
613        {
614          for (String property : extraProperties.keySet())
615          {
616            if ((! includeFileElement) &&
617                property.equals(SCHEMA_PROPERTY_FILENAME))
618            {
619              continue;
620            }
621    
622            List<String> valueList = extraProperties.get(property);
623    
624            buffer.append(" ");
625            buffer.append(property);
626    
627            if (valueList.size() == 1)
628            {
629              buffer.append(" '");
630              buffer.append(valueList.get(0));
631              buffer.append("'");
632            }
633            else
634            {
635              buffer.append(" ( ");
636    
637              for (String value : valueList)
638              {
639                buffer.append("'");
640                buffer.append(value);
641                buffer.append("' ");
642              }
643    
644              buffer.append(")");
645            }
646          }
647        }
648    
649        buffer.append(" )");
650      }
651    }
652