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