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 2015-2016 ForgeRock AS.
016 */
017package org.forgerock.opendj.config;
018
019import static com.forgerock.opendj.util.StaticUtils.*;
020
021import org.forgerock.opendj.ldap.DN;
022import org.forgerock.opendj.ldap.RDN;
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, S extends Configuration> {
035
036    /**
037     * Parses a DN string value as a reference using the provided managed object
038     * path and relation definition.
039     *
040     * @param <C>
041     *            The type of client managed object configuration that this
042     *            reference refers to.
043     * @param <S>
044     *            The type of server managed object configuration that this
045     *            reference refers to.
046     * @param path
047     *            The path of the referenced managed object's parent.
048     * @param relationDef
049     *            The instantiable relation in the parent which contains the
050     *            referenced managed object.
051     * @param dnAsString
052     *            The DN string value.
053     * @return Returns the new reference based on the provided DN string value.
054     * @throws IllegalArgumentException
055     *             If the DN string value could not be decoded as a DN or if the
056     *             provided DN did not correspond to the provided path and
057     *             relation.
058     */
059    public static <C extends ConfigurationClient, S extends Configuration> Reference<C, S> parseDN(
060        ManagedObjectPath<?, ?> path, InstantiableRelationDefinition<C, S> relationDef, String dnAsString) {
061        AbstractManagedObjectDefinition<?, ?> definition = path.getManagedObjectDefinition();
062        RelationDefinition<?, ?> tmp = definition.getRelationDefinition(relationDef.getName());
063        if (tmp != relationDef) {
064            // TODO : i18n ?
065            throw new IllegalArgumentException("The relation \"" + relationDef.getName()
066                + "\" is not associated with the definition \"" + definition.getName() + "\"");
067        }
068
069        DN dn = DN.valueOf(dnAsString);
070        RDN rdn = dn.rdn();
071        if (rdn == null) {
072            // TODO : i18n ?
073            throw new IllegalArgumentException("Unabled to decode the DN string: \"" + dnAsString + "\"");
074        }
075
076        // Check that the DN was valid.
077        String name = rdn.getFirstAVA().getAttributeValue().toString();
078        DN expected = path.child(relationDef, name).toDN();
079        if (!dn.equals(expected)) {
080            // TODO : i18n ?
081            throw new IllegalArgumentException("Unabled to decode the DN string: \"" + dnAsString + "\"");
082        }
083
084        return new Reference<>(path, relationDef, name);
085    }
086
087    /**
088     * Parses a name as a reference using the provided managed object path and
089     * relation definition.
090     *
091     * @param <C>
092     *            The type of client managed object configuration that this
093     *            reference refers to.
094     * @param <S>
095     *            The type of server managed object configuration that this
096     *            reference refers to.
097     * @param p
098     *            The path of the referenced managed object's parent.
099     * @param rd
100     *            The instantiable relation in the parent which contains the
101     *            referenced managed object.
102     * @param s
103     *            The name of the referenced managed object.
104     * @return Returns the new reference based on the provided name.
105     * @throws IllegalArgumentException
106     *             If the relation is not associated with the provided parent's
107     *             definition, or if the provided name is empty.
108     */
109    public static <C extends ConfigurationClient, S extends Configuration> Reference<C, S> parseName(
110        ManagedObjectPath<?, ?> p, InstantiableRelationDefinition<C, S> rd, String s) {
111        // Sanity checks.
112        AbstractManagedObjectDefinition<?, ?> d = p.getManagedObjectDefinition();
113        RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
114        if (tmp != rd) {
115            throw new IllegalArgumentException("The relation \"" + rd.getName()
116                + "\" is not associated with the definition \"" + d.getName() + "\"");
117        }
118
119        if (s.trim().length() == 0) {
120            throw new IllegalArgumentException("Empty names are not allowed");
121        }
122
123        return new Reference<>(p, rd, s);
124    }
125
126    /** The name of the referenced managed object. */
127    private final String name;
128
129    /** The path of the referenced managed object. */
130    private final ManagedObjectPath<C, S> path;
131
132    /** The instantiable relation in the parent which contains the referenced managed object. */
133    private final InstantiableRelationDefinition<C, S> relation;
134
135    /** Private constructor. */
136    private Reference(ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> relation, String name) {
137        this.relation = relation;
138        this.name = name;
139        this.path = parent.child(relation, name);
140    }
141
142    /**
143     * Gets the name of the referenced managed object.
144     *
145     * @return Returns the name of the referenced managed object.
146     */
147    public String getName() {
148        return name;
149    }
150
151    /**
152     * Gets the normalized name of the referenced managed object.
153     *
154     * @return Returns the normalized name of the referenced managed object.
155     */
156    public String getNormalizedName() {
157        PropertyDefinition<?> pd = relation.getNamingPropertyDefinition();
158        return normalizeName(pd);
159    }
160
161    /**
162     * Gets the DN of the referenced managed object.
163     *
164     * @return Returns the DN of the referenced managed object.
165     */
166    public DN toDN() {
167        return path.toDN();
168    }
169
170    @Override
171    public String toString() {
172        return name;
173    }
174
175    /** Normalize a value using the specified naming property definition if defined. */
176    private <T> String normalizeName(PropertyDefinition<T> pd) {
177        if (pd != null) {
178            try {
179                // TODO : is it correct to have no validation ?
180                T tvalue = pd.decodeValue(name);
181                return pd.normalizeValue(tvalue);
182            } catch (PropertyException e) {
183                // Fall through to default normalization.
184            }
185        }
186
187        // FIXME: should really use directory string normalizer.
188        String s = name.trim().replaceAll(" +", " ");
189        return toLowerCase(s);
190    }
191}