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-2013 ForgeRock AS.
026 */
027
028package org.forgerock.opendj.ldap;
029
030import java.util.Collection;
031import java.util.Iterator;
032
033import com.forgerock.opendj.util.Iterables;
034import com.forgerock.opendj.util.Predicate;
035import org.forgerock.util.Reject;
036
037/**
038 * This class provides a skeletal implementation of the {@code Entry} interface,
039 * to minimize the effort required to implement this interface.
040 */
041public abstract class AbstractEntry implements Entry {
042
043    /** Predicate used for findAttributes. */
044    private static final Predicate<Attribute, AttributeDescription> FIND_ATTRIBUTES_PREDICATE =
045            new Predicate<Attribute, AttributeDescription>() {
046
047                @Override
048                public boolean matches(final Attribute value, final AttributeDescription p) {
049                    return value.getAttributeDescription().isSubTypeOf(p);
050                }
051
052            };
053
054    /**
055     * Sole constructor.
056     */
057    protected AbstractEntry() {
058        // No implementation required.
059    }
060
061    @Override
062    public boolean addAttribute(final Attribute attribute) {
063        return addAttribute(attribute, null);
064    }
065
066    @Override
067    public Entry addAttribute(final String attributeDescription, final Object... values) {
068        addAttribute(new LinkedAttribute(attributeDescription, values), null);
069        return this;
070    }
071
072    @Override
073    public boolean containsAttribute(final Attribute attribute,
074            final Collection<? super ByteString> missingValues) {
075        final Attribute a = getAttribute(attribute.getAttributeDescription());
076        if (a == null) {
077            if (missingValues != null) {
078                missingValues.addAll(attribute);
079            }
080            return false;
081        } else {
082            boolean result = true;
083            for (final ByteString value : attribute) {
084                if (!a.contains(value)) {
085                    if (missingValues != null) {
086                        missingValues.add(value);
087                    }
088                    result = false;
089                }
090            }
091            return result;
092        }
093    }
094
095    @Override
096    public boolean containsAttribute(final String attributeDescription, final Object... values) {
097        return containsAttribute(new LinkedAttribute(attributeDescription, values), null);
098    }
099
100    @Override
101    public boolean equals(final Object object) {
102        if (this == object) {
103            return true;
104        } else if (object instanceof Entry) {
105            final Entry other = (Entry) object;
106            if (!getName().equals(other.getName())) {
107                return false;
108            }
109            // Distinguished name is the same, compare attributes.
110            if (getAttributeCount() != other.getAttributeCount()) {
111                return false;
112            }
113            for (final Attribute attribute : getAllAttributes()) {
114                final Attribute otherAttribute =
115                        other.getAttribute(attribute.getAttributeDescription());
116                if (!attribute.equals(otherAttribute)) {
117                    return false;
118                }
119            }
120            return true;
121        } else {
122            return false;
123        }
124    }
125
126    @Override
127    public Iterable<Attribute> getAllAttributes(final AttributeDescription attributeDescription) {
128        Reject.ifNull(attributeDescription);
129
130        return Iterables.filteredIterable(getAllAttributes(), FIND_ATTRIBUTES_PREDICATE,
131                attributeDescription);
132    }
133
134    @Override
135    public Iterable<Attribute> getAllAttributes(final String attributeDescription) {
136        return getAllAttributes(AttributeDescription.valueOf(attributeDescription));
137    }
138
139    @Override
140    public Attribute getAttribute(final AttributeDescription attributeDescription) {
141        for (final Attribute attribute : getAllAttributes()) {
142            final AttributeDescription ad = attribute.getAttributeDescription();
143            if (isAssignable(attributeDescription, ad)) {
144                return attribute;
145            }
146        }
147        return null;
148    }
149
150    @Override
151    public Attribute getAttribute(final String attributeDescription) {
152        return getAttribute(AttributeDescription.valueOf(attributeDescription));
153    }
154
155    @Override
156    public int hashCode() {
157        int hashCode = getName().hashCode();
158        for (final Attribute attribute : getAllAttributes()) {
159            hashCode += attribute.hashCode();
160        }
161        return hashCode;
162    }
163
164    @Override
165    public AttributeParser parseAttribute(final AttributeDescription attributeDescription) {
166        return AttributeParser.parseAttribute(getAttribute(attributeDescription));
167    }
168
169    @Override
170    public AttributeParser parseAttribute(final String attributeDescription) {
171        return AttributeParser.parseAttribute(getAttribute(attributeDescription));
172    }
173
174    @Override
175    public boolean removeAttribute(final Attribute attribute,
176            final Collection<? super ByteString> missingValues) {
177        final Iterator<Attribute> i = getAllAttributes().iterator();
178        final AttributeDescription attributeDescription = attribute.getAttributeDescription();
179        while (i.hasNext()) {
180            final Attribute oldAttribute = i.next();
181            if (isAssignable(attributeDescription, oldAttribute.getAttributeDescription())) {
182                if (attribute.isEmpty()) {
183                    i.remove();
184                    return true;
185                } else {
186                    final boolean modified = oldAttribute.removeAll(attribute, missingValues);
187                    if (oldAttribute.isEmpty()) {
188                        i.remove();
189                        return true;
190                    }
191                    return modified;
192                }
193            }
194        }
195        // Not found.
196        if (missingValues != null) {
197            missingValues.addAll(attribute);
198        }
199        return false;
200    }
201
202    @Override
203    public boolean removeAttribute(final AttributeDescription attributeDescription) {
204        return removeAttribute(Attributes.emptyAttribute(attributeDescription), null);
205    }
206
207    @Override
208    public Entry removeAttribute(final String attributeDescription, final Object... values) {
209        removeAttribute(new LinkedAttribute(attributeDescription, values), null);
210        return this;
211    }
212
213    @Override
214    public boolean replaceAttribute(final Attribute attribute) {
215        if (attribute.isEmpty()) {
216            return removeAttribute(attribute.getAttributeDescription());
217        } else {
218            /*
219             * For consistency with addAttribute and removeAttribute, preserve
220             * the existing attribute if it already exists.
221             */
222            final Attribute oldAttribute = getAttribute(attribute.getAttributeDescription());
223            if (oldAttribute != null) {
224                oldAttribute.clear();
225                oldAttribute.addAll(attribute);
226            } else {
227                addAttribute(attribute, null);
228            }
229            return true;
230        }
231    }
232
233    @Override
234    public Entry replaceAttribute(final String attributeDescription, final Object... values) {
235        replaceAttribute(new LinkedAttribute(attributeDescription, values));
236        return this;
237    }
238
239    @Override
240    public Entry setName(final String dn) {
241        return setName(DN.valueOf(dn));
242    }
243
244    @Override
245    public String toString() {
246        final StringBuilder builder = new StringBuilder();
247        builder.append('"');
248        builder.append(getName());
249        builder.append("\":{");
250        boolean firstValue = true;
251        for (final Attribute attribute : getAllAttributes()) {
252            if (!firstValue) {
253                builder.append(',');
254            }
255            builder.append(attribute);
256            firstValue = false;
257        }
258        builder.append('}');
259        return builder.toString();
260    }
261
262    private boolean isAssignable(final AttributeDescription from, final AttributeDescription to) {
263        if (!from.isPlaceHolder()) {
264            return from.equals(to);
265        } else {
266            return from.matches(to);
267        }
268    }
269
270}