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 2009-2010 Sun Microsystems, Inc.
025 *      Portions copyright 2012 ForgeRock AS.
026 */
027
028package org.forgerock.opendj.ldap;
029
030import java.util.TreeMap;
031
032import org.forgerock.i18n.LocalizedIllegalArgumentException;
033import org.forgerock.opendj.ldap.requests.Requests;
034
035import org.forgerock.util.Reject;
036
037/**
038 * An implementation of the {@code Entry} interface which uses a {@code TreeMap}
039 * for storing attributes. Attributes are returned in ascending order of
040 * attribute description, with {@code objectClass} first, then all user
041 * attributes, and finally any operational attributes. All operations are
042 * supported by this implementation. For example, you can build an entry like
043 * this:
044 *
045 * <pre>
046 * Entry entry = new TreeMapEntry("cn=Bob,ou=People,dc=example,dc=com")
047 *    .addAttribute("cn", "Bob")
048 *    .addAttribute("objectclass", "top")
049 *    .addAttribute("objectclass", "person")
050 *    .addAttribute("objectclass", "organizationalPerson")
051 *    .addAttribute("objectclass", "inetOrgPerson")
052 *    .addAttribute("mail", "subgenius@example.com")
053 *    .addAttribute("sn", "Dobbs");
054 * </pre>
055 *
056 * <p>
057 * A {@code TreeMapEntry} stores references to attributes which have been added
058 * using the {@link #addAttribute} methods. Attributes sharing the same
059 * attribute description are merged by adding the values of the new attribute to
060 * the existing attribute. More specifically, the existing attribute must be
061 * modifiable for the merge to succeed. Similarly, the {@link #removeAttribute}
062 * methods remove the specified values from the existing attribute. The
063 * {@link #replaceAttribute} methods remove the existing attribute (if present)
064 * and store a reference to the new attribute - neither the new or existing
065 * attribute need to be modifiable in this case.
066 */
067public final class TreeMapEntry extends AbstractMapEntry {
068    /**
069     * An entry factory which can be used to create new tree map entries.
070     */
071    public static final EntryFactory FACTORY = new EntryFactory() {
072        public Entry newEntry(final DN name) {
073            return new TreeMapEntry(name);
074        }
075    };
076
077    /**
078     * Creates an entry having the same distinguished name, attributes, and
079     * object classes of the provided entry. This constructor performs a deep
080     * copy of {@code entry} and will copy each attribute as a
081     * {@link LinkedAttribute}.
082     * <p>
083     * A shallow copy constructor is provided by {@link #TreeMapEntry(Entry)}.
084     *
085     * @param entry
086     *            The entry to be copied.
087     * @return A deep copy of {@code entry}.
088     * @throws NullPointerException
089     *             If {@code entry} was {@code null}.
090     * @see #TreeMapEntry(Entry)
091     */
092    public static TreeMapEntry deepCopyOfEntry(final Entry entry) {
093        TreeMapEntry copy = new TreeMapEntry(entry.getName());
094        for (final Attribute attribute : entry.getAllAttributes()) {
095            copy.addAttribute(new LinkedAttribute(attribute));
096        }
097        return copy;
098    }
099
100    /**
101     * Creates an entry with an empty (root) distinguished name and no
102     * attributes.
103     */
104    public TreeMapEntry() {
105        this(DN.rootDN());
106    }
107
108    /**
109     * Creates an empty entry using the provided distinguished name and no
110     * attributes.
111     *
112     * @param name
113     *            The distinguished name of this entry.
114     * @throws NullPointerException
115     *             If {@code name} was {@code null}.
116     */
117    public TreeMapEntry(final DN name) {
118        super(Reject.checkNotNull(name), new TreeMap<AttributeDescription, Attribute>());
119    }
120
121    /**
122     * Creates an entry having the same distinguished name, attributes, and
123     * object classes of the provided entry. This constructor performs a shallow
124     * copy of {@code entry} and will not copy the attributes contained in
125     * {@code entry}.
126     * <p>
127     * A deep copy constructor is provided by {@link #deepCopyOfEntry(Entry)}
128     *
129     * @param entry
130     *            The entry to be copied.
131     * @throws NullPointerException
132     *             If {@code entry} was {@code null}.
133     * @see #deepCopyOfEntry(Entry)
134     */
135    public TreeMapEntry(final Entry entry) {
136        this(entry.getName());
137        for (final Attribute attribute : entry.getAllAttributes()) {
138            addAttribute(attribute);
139        }
140    }
141
142    /**
143     * Creates an empty entry using the provided distinguished name decoded
144     * using the default schema.
145     *
146     * @param name
147     *            The distinguished name of this entry.
148     * @throws LocalizedIllegalArgumentException
149     *             If {@code name} could not be decoded using the default
150     *             schema.
151     * @throws NullPointerException
152     *             If {@code name} was {@code null}.
153     */
154    public TreeMapEntry(final String name) {
155        this(DN.valueOf(name));
156    }
157
158    /**
159     * Creates a new entry using the provided lines of LDIF decoded using the
160     * default schema.
161     *
162     * @param ldifLines
163     *            Lines of LDIF containing the an LDIF add change record or an
164     *            LDIF entry record.
165     * @throws LocalizedIllegalArgumentException
166     *             If {@code ldifLines} was empty, or contained invalid LDIF, or
167     *             could not be decoded using the default schema.
168     * @throws NullPointerException
169     *             If {@code ldifLines} was {@code null} .
170     */
171    public TreeMapEntry(final String... ldifLines) {
172        this(Requests.newAddRequest(ldifLines));
173    }
174
175}