001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2008 Sun Microsystems, Inc.
015 * Portions Copyright 2014-2016 ForgeRock AS.
016 */
017package org.opends.server.admin;
018
019import org.forgerock.opendj.ldap.ByteString;
020import org.forgerock.opendj.ldap.DN;
021import org.forgerock.opendj.ldap.RDN;
022import org.opends.server.util.StaticUtils;
023
024/**
025 * A reference to another managed object.
026 *
027 * @param <C>
028 *          The type of client managed object configuration that this
029 *          reference refers to.
030 * @param <S>
031 *          The type of server managed object configuration that this
032 *          reference refers to.
033 */
034public final class Reference<C extends ConfigurationClient,
035                             S extends Configuration> {
036
037  /**
038   * Parses a DN string value as a reference using the provided
039   * managed object path and relation definition.
040   *
041   * @param <C>
042   *          The type of client managed object configuration that
043   *          this reference refers to.
044   * @param <S>
045   *          The type of server managed object configuration that
046   *          this reference refers to.
047   * @param p
048   *          The path of the referenced managed object's parent.
049   * @param rd
050   *          The instantiable relation in the parent which contains
051   *          the referenced managed object.
052   * @param s
053   *          The DN string value.
054   * @return Returns the new reference based on the provided DN string
055   *         value.
056   * @throws IllegalArgumentException
057   *           If the DN string value could not be decoded as a DN or
058   *           if the provided DN did not correspond to the provided
059   *           path and relation.
060   */
061  public static <C extends ConfigurationClient, S extends Configuration>
062  Reference<C, S> parseDN(
063      ManagedObjectPath<?, ?> p, InstantiableRelationDefinition<C, S> rd,
064      String s) throws IllegalArgumentException {
065    AbstractManagedObjectDefinition<?, ?> d = p.getManagedObjectDefinition();
066    RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
067    if (tmp != rd) {
068      throw new IllegalArgumentException("The relation \"" + rd.getName()
069          + "\" is not associated with the definition \"" + d.getName() + "\"");
070    }
071
072    DN dn = DN.valueOf(s);
073    RDN rdn = dn.rdn();
074    if (rdn == null) {
075      throw new IllegalArgumentException("Unabled to decode the DN string: \""
076          + s + "\"");
077    }
078
079    ByteString av = rdn.getFirstAVA().getAttributeValue();
080    if (av == null) {
081      throw new IllegalArgumentException("Unabled to decode the DN string: \""
082          + s + "\"");
083    }
084
085    String name = av.toString();
086
087    // Check that the DN was valid.
088    DN expected = p.child(rd, name).toDN();
089    if (!dn.equals(expected)) {
090      throw new IllegalArgumentException("Unabled to decode the DN string: \""
091          + s + "\"");
092    }
093
094    return new Reference<>(p, rd, name);
095  }
096
097
098
099  /**
100   * Parses a name as a reference using the provided managed object
101   * path and relation definition.
102   *
103   * @param <C>
104   *          The type of client managed object configuration that
105   *          this reference refers to.
106   * @param <S>
107   *          The type of server managed object configuration that
108   *          this reference refers to.
109   * @param p
110   *          The path of the referenced managed object's parent.
111   * @param rd
112   *          The instantiable relation in the parent which contains
113   *          the referenced managed object.
114   * @param s
115   *          The name of the referenced managed object.
116   * @return Returns the new reference based on the provided name.
117   * @throws IllegalArgumentException
118   *           If the relation is not associated with the provided
119   *           parent's definition, or if the provided name is empty.
120   */
121  public static <C extends ConfigurationClient, S extends Configuration>
122  Reference<C, S> parseName(
123      ManagedObjectPath<?, ?> p, InstantiableRelationDefinition<C, S> rd,
124      String s) throws IllegalArgumentException {
125    // Sanity checks.
126    AbstractManagedObjectDefinition<?, ?> d = p.getManagedObjectDefinition();
127    RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
128    if (tmp != rd) {
129      throw new IllegalArgumentException("The relation \"" + rd.getName()
130          + "\" is not associated with the definition \"" + d.getName() + "\"");
131    }
132
133    if (s.trim().length() == 0) {
134      throw new IllegalArgumentException("Empty names are not allowed");
135    }
136
137    return new Reference<>(p, rd, s);
138  }
139
140  /** The name of the referenced managed object. */
141  private final String name;
142
143  /** The path of the referenced managed object. */
144  private final ManagedObjectPath<C, S> path;
145
146  /**
147   * The instantiable relation in the parent which contains the
148   * referenced managed object.
149   */
150  private final InstantiableRelationDefinition<C, S> relation;
151
152
153
154  /** Private constructor. */
155  private Reference(ManagedObjectPath<?, ?> parent,
156      InstantiableRelationDefinition<C, S> relation, String name)
157      throws IllegalArgumentException {
158    this.relation = relation;
159    this.name = name;
160    this.path = parent.child(relation, name);
161  }
162
163
164
165  /**
166   * Gets the name of the referenced managed object.
167   *
168   * @return Returns the name of the referenced managed object.
169   */
170  public String getName() {
171    return name;
172  }
173
174
175
176  /**
177   * Gets the normalized name of the referenced managed object.
178   *
179   * @return Returns the normalized name of the referenced managed
180   *         object.
181   */
182  public String getNormalizedName() {
183    PropertyDefinition<?> pd = relation.getNamingPropertyDefinition();
184    return normalizeName(pd);
185  }
186
187
188
189  /**
190   * Gets the DN of the referenced managed object.
191   *
192   * @return Returns the DN of the referenced managed object.
193   */
194  public DN toDN() {
195    return path.toDN();
196  }
197
198  @Override
199  public String toString() {
200    return name;
201  }
202
203
204
205  /**
206   * Normalize a value using the specified naming property definition
207   * if defined.
208   */
209  private <T> String normalizeName(PropertyDefinition<T> pd) {
210    if (pd != null) {
211      try {
212        T tvalue = pd.decodeValue(name);
213        return pd.normalizeValue(tvalue);
214      } catch (PropertyException e) {
215        // Fall through to default normalization.
216      }
217    }
218
219    // FIXME: should really use directory string normalizer.
220    String s = name.trim().replaceAll(" +", " ");
221    return StaticUtils.toLowerCase(s);
222  }
223}