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