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 2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.admin;
028    
029    
030    
031    import org.opends.server.types.AttributeValue;
032    import org.opends.server.types.DN;
033    import org.opends.server.types.DirectoryException;
034    import org.opends.server.types.RDN;
035    import org.opends.server.util.StaticUtils;
036    
037    
038    
039    /**
040     * A reference to another managed object.
041     *
042     * @param <C>
043     *          The type of client managed object configuration that this
044     *          reference refers to.
045     * @param <S>
046     *          The type of server managed object configuration that this
047     *          reference refers to.
048     */
049    public final class Reference<C extends ConfigurationClient,
050                                 S extends Configuration> {
051    
052      /**
053       * Parses a DN string value as a reference using the provided
054       * managed object path and relation definition.
055       *
056       * @param <C>
057       *          The type of client managed object configuration that
058       *          this reference refers to.
059       * @param <S>
060       *          The type of server managed object configuration that
061       *          this reference refers to.
062       * @param p
063       *          The path of the referenced managed object's parent.
064       * @param rd
065       *          The instantiable relation in the parent which contains
066       *          the referenced managed object.
067       * @param s
068       *          The DN string value.
069       * @return Returns the new reference based on the provided DN string
070       *         value.
071       * @throws IllegalArgumentException
072       *           If the DN string value could not be decoded as a DN or
073       *           if the provided DN did not correspond to the provided
074       *           path and relation.
075       */
076      public static <C extends ConfigurationClient, S extends Configuration>
077      Reference<C, S> parseDN(
078          ManagedObjectPath<?, ?> p, InstantiableRelationDefinition<C, S> rd,
079          String s) throws IllegalArgumentException {
080        AbstractManagedObjectDefinition<?, ?> d = p.getManagedObjectDefinition();
081        RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
082        if (tmp != rd) {
083          throw new IllegalArgumentException("The relation \"" + rd.getName()
084              + "\" is not associated with the definition \"" + d.getName() + "\"");
085        }
086    
087        DN dn;
088        try {
089          dn = DN.decode(s);
090        } catch (DirectoryException e) {
091          throw new IllegalArgumentException("Unabled to decode the DN string: \""
092              + s + "\"");
093        }
094    
095        RDN rdn = dn.getRDN();
096        if (rdn == null) {
097          throw new IllegalArgumentException("Unabled to decode the DN string: \""
098              + s + "\"");
099        }
100    
101        AttributeValue av = rdn.getAttributeValue(0);
102        if (av == null) {
103          throw new IllegalArgumentException("Unabled to decode the DN string: \""
104              + s + "\"");
105        }
106    
107        String name = av.getStringValue();
108    
109        // Check that the DN was valid.
110        DN expected = p.child(rd, name).toDN();
111        if (!dn.equals(expected)) {
112          throw new IllegalArgumentException("Unabled to decode the DN string: \""
113              + s + "\"");
114        }
115    
116        return new Reference<C, S>(p, rd, name);
117      }
118    
119    
120    
121      /**
122       * Parses a name as a reference using the provided managed object
123       * path and relation definition.
124       *
125       * @param <C>
126       *          The type of client managed object configuration that
127       *          this reference refers to.
128       * @param <S>
129       *          The type of server managed object configuration that
130       *          this reference refers to.
131       * @param p
132       *          The path of the referenced managed object's parent.
133       * @param rd
134       *          The instantiable relation in the parent which contains
135       *          the referenced managed object.
136       * @param s
137       *          The name of the referenced managed object.
138       * @return Returns the new reference based on the provided name.
139       * @throws IllegalArgumentException
140       *           If the relation is not associated with the provided
141       *           parent's definition, or if the provided name is empty.
142       */
143      public static <C extends ConfigurationClient, S extends Configuration>
144      Reference<C, S> parseName(
145          ManagedObjectPath<?, ?> p, InstantiableRelationDefinition<C, S> rd,
146          String s) throws IllegalArgumentException {
147        // Sanity checks.
148        AbstractManagedObjectDefinition<?, ?> d = p.getManagedObjectDefinition();
149        RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
150        if (tmp != rd) {
151          throw new IllegalArgumentException("The relation \"" + rd.getName()
152              + "\" is not associated with the definition \"" + d.getName() + "\"");
153        }
154    
155        if (s.trim().length() == 0) {
156          throw new IllegalArgumentException("Empty names are not allowed");
157        }
158    
159        return new Reference<C, S>(p, rd, s);
160      }
161    
162      // The name of the referenced managed object.
163      private final String name;
164    
165      // The path of the referenced managed object.
166      private final ManagedObjectPath<C, S> path;
167    
168      // The instantiable relation in the parent which contains the
169      // referenced managed object.
170      private final InstantiableRelationDefinition<C, S> relation;
171    
172    
173    
174      // Private constructor.
175      private Reference(ManagedObjectPath<?, ?> parent,
176          InstantiableRelationDefinition<C, S> relation, String name)
177          throws IllegalArgumentException {
178        this.relation = relation;
179        this.name = name;
180        this.path = parent.child(relation, name);
181      }
182    
183    
184    
185      /**
186       * Gets the name of the referenced managed object.
187       *
188       * @return Returns the name of the referenced managed object.
189       */
190      public String getName() {
191        return name;
192      }
193    
194    
195    
196      /**
197       * Gets the normalized name of the referenced managed object.
198       *
199       * @return Returns the normalized name of the referenced managed
200       *         object.
201       */
202      public String getNormalizedName() {
203        PropertyDefinition<?> pd = relation.getNamingPropertyDefinition();
204        return normalizeName(pd);
205      }
206    
207    
208    
209      /**
210       * Gets the DN of the referenced managed object.
211       *
212       * @return Returns the DN of the referenced managed object.
213       */
214      public DN toDN() {
215        return path.toDN();
216      }
217    
218    
219    
220      /**
221       * {@inheritDoc}
222       */
223      public String toString() {
224        return name;
225      }
226    
227    
228    
229      // Normalize a value using the specified naming property definition
230      // if defined.
231      private <T> String normalizeName(PropertyDefinition<T> pd) {
232        if (pd != null) {
233          try {
234            T tvalue = pd.decodeValue(name);
235            return pd.normalizeValue(tvalue);
236          } catch (IllegalPropertyValueStringException e) {
237            // Fall through to default normalization.
238          }
239        }
240    
241        // FIXME: should really use directory string normalizer.
242        String s = name.trim().replaceAll(" +", " ");
243        return StaticUtils.toLowerCase(s);
244      }
245    }