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