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