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 legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2008 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.server.admin;
028
029
030
031import org.forgerock.opendj.ldap.ByteString;
032import org.opends.server.types.DN;
033import org.opends.server.types.DirectoryException;
034import org.opends.server.types.RDN;
035import 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 */
049public 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.valueOf(s);
090    } catch (DirectoryException e) {
091      throw new IllegalArgumentException("Unabled to decode the DN string: \""
092          + s + "\"");
093    }
094
095    RDN rdn = dn.rdn();
096    if (rdn == null) {
097      throw new IllegalArgumentException("Unabled to decode the DN string: \""
098          + s + "\"");
099    }
100
101    ByteString 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.toString();
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<>(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<>(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  /**
169   * The instantiable relation in the parent which contains the
170   * referenced managed object.
171   */
172  private final InstantiableRelationDefinition<C, S> relation;
173
174
175
176  /** Private constructor. */
177  private Reference(ManagedObjectPath<?, ?> parent,
178      InstantiableRelationDefinition<C, S> relation, String name)
179      throws IllegalArgumentException {
180    this.relation = relation;
181    this.name = name;
182    this.path = parent.child(relation, name);
183  }
184
185
186
187  /**
188   * Gets the name of the referenced managed object.
189   *
190   * @return Returns the name of the referenced managed object.
191   */
192  public String getName() {
193    return name;
194  }
195
196
197
198  /**
199   * Gets the normalized name of the referenced managed object.
200   *
201   * @return Returns the normalized name of the referenced managed
202   *         object.
203   */
204  public String getNormalizedName() {
205    PropertyDefinition<?> pd = relation.getNamingPropertyDefinition();
206    return normalizeName(pd);
207  }
208
209
210
211  /**
212   * Gets the DN of the referenced managed object.
213   *
214   * @return Returns the DN of the referenced managed object.
215   */
216  public DN toDN() {
217    return path.toDN();
218  }
219
220
221
222  /** {@inheritDoc} */
223  public String toString() {
224    return name;
225  }
226
227
228
229  /**
230   * Normalize a value using the specified naming property definition
231   * if defined.
232   */
233  private <T> String normalizeName(PropertyDefinition<T> pd) {
234    if (pd != null) {
235      try {
236        T tvalue = pd.decodeValue(name);
237        return pd.normalizeValue(tvalue);
238      } catch (PropertyException e) {
239        // Fall through to default normalization.
240      }
241    }
242
243    // FIXME: should really use directory string normalizer.
244    String s = name.trim().replaceAll(" +", " ");
245    return StaticUtils.toLowerCase(s);
246  }
247}