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 2006-2008 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.server.types;
028
029import java.util.Collection;
030import java.util.Set;
031
032import org.forgerock.opendj.ldap.ByteString;
033import org.forgerock.opendj.ldap.DecodeException;
034import org.forgerock.opendj.ldap.schema.MatchingRule;
035
036import static org.opends.server.util.StaticUtils.*;
037
038/** An abstract base class for implementing new types of {@link Attribute}. */
039@org.opends.server.types.PublicAPI(
040    stability = org.opends.server.types.StabilityLevel.UNCOMMITTED,
041    mayInstantiate = false,
042    mayExtend = false,
043    mayInvoke = true)
044public abstract class AbstractAttribute implements Attribute
045{
046  /** Creates a new abstract attribute. */
047  protected AbstractAttribute()
048  {
049    // No implementation required.
050  }
051
052  /**
053   * {@inheritDoc}
054   * <p>
055   * This implementation iterates through each attribute value in the
056   * provided collection, checking to see if this attribute contains
057   * the value using {@link #contains(ByteString)}.
058   */
059  @Override
060  public boolean containsAll(Collection<ByteString> values)
061  {
062    for (ByteString value : values)
063    {
064      if (!contains(value))
065      {
066        return false;
067      }
068    }
069    return true;
070  }
071
072  @Override
073  public final boolean equals(Object o)
074  {
075    if (this == o)
076    {
077      return true;
078    }
079    if (!(o instanceof Attribute))
080    {
081      return false;
082    }
083
084    Attribute a = (Attribute) o;
085    return getAttributeType().equals(a.getAttributeType())
086        && valuesEqual(a)
087        && optionsEqual(a.getOptions());
088  }
089
090  private boolean valuesEqual(Attribute a)
091  {
092    if (size() != a.size())
093    {
094      return false;
095    }
096
097    for (ByteString v : a)
098    {
099      if (!contains(v))
100      {
101        return false;
102      }
103    }
104    return true;
105  }
106
107  /**
108   * {@inheritDoc}
109   * <p>
110   * This implementation returns the primary name associated with this
111   * attribute's attribute type or, if there is no primary name, the
112   * attribute type's OID.
113   */
114  @Override
115  public String getName()
116  {
117    return getAttributeType().getNameOrOID();
118  }
119
120  /**
121   * {@inheritDoc}
122   * <p>
123   * This implementation returns this attribute's name if there are no
124   * attribute options, otherwise it constructs a string comprising of
125   * this attribute's name followed by a semi-colon and a semi-colon
126   * separated list of its attribute options.
127   */
128  @Override
129  public String getNameWithOptions()
130  {
131    if (!hasOptions())
132    {
133      return getName();
134    }
135
136    StringBuilder buffer = new StringBuilder();
137    buffer.append(getName());
138    for (String option : getOptions())
139    {
140      buffer.append(';');
141      buffer.append(option);
142    }
143    return buffer.toString();
144  }
145
146  /**
147   * {@inheritDoc}
148   * <p>
149   * This implementation returns <code>true</code> if the provided
150   * collection of options is <code>null</code> or empty. If the
151   * collection is non-empty and this attribute does not have any
152   * options then it returns <code>false</code>. Otherwise, each
153   * option in the provided collection is checked using
154   * {@link #hasOption(String)} and <code>true</code> is
155   * returned if all the provided options are present.
156   */
157  @Override
158  public boolean hasAllOptions(Collection<String> options)
159  {
160    if (options == null || options.isEmpty())
161    {
162      return true;
163    }
164
165    if (hasOptions())
166    {
167      return hasAllOptions0(options);
168    }
169    return false;
170  }
171
172  @Override
173  public int hashCode()
174  {
175    int hashCode = getAttributeType().hashCode();
176    for (ByteString value : this)
177    {
178      try
179      {
180        MatchingRule eqRule = getAttributeType().getEqualityMatchingRule();
181        hashCode += eqRule.normalizeAttributeValue(value).hashCode();
182      }
183      catch (DecodeException e)
184      {
185        hashCode += value.hashCode();
186      }
187    }
188    return hashCode;
189  }
190
191  /**
192   * {@inheritDoc}
193   * <p>
194   * This implementation calls {@link #getOptions()} to
195   * retrieve this attribute's set of options and then compares them
196   * one at a time against the provided option. All comparisons are
197   * case insensitive (this is why we iterate through the set of
198   * options, rather than doing a simpler set membership test).
199   */
200  @Override
201  public boolean hasOption(String option)
202  {
203    String noption = toLowerCase(option);
204
205    // Cannot use Set.contains() because the options are not normalized.
206    for (String o : getOptions())
207    {
208      String no = toLowerCase(o);
209      if (no.equals(noption))
210      {
211        return true;
212      }
213    }
214    return false;
215  }
216
217  /**
218   * {@inheritDoc}
219   * <p>
220   * This implementation retrieves the set of options associated with
221   * this attribute and tests to see if it is empty.
222   */
223  @Override
224  public boolean hasOptions()
225  {
226    return !getOptions().isEmpty();
227  }
228
229  /**
230   * {@inheritDoc}
231   * <p>
232   * This implementation returns <code>true</code> if the
233   * {@link #size()} of this attribute is zero.
234   */
235  @Override
236  public boolean isEmpty()
237  {
238    return size() == 0;
239  }
240
241  @Override
242  public boolean isReal()
243  {
244    return !isVirtual();
245  }
246
247  /**
248   * {@inheritDoc}
249   * <p>
250   * This implementation returns !{@link #hasOptions()} if the
251   * provided set of options is <code>null</code>. Otherwise it
252   * checks that the size of the provided set of options is equal to
253   * the size of this attribute's options, return <code>false</code>
254   * if the sizes differ. If the sizes are the same then each option
255   * in the provided set is checked using
256   * {@link #hasOption(String)} and <code>true</code> is
257   * returned if all the provided options are present.
258   */
259  @Override
260  public boolean optionsEqual(Set<String> options)
261  {
262    if (options == null)
263    {
264      return !hasOptions();
265    }
266
267    if (getOptions().size() == options.size())
268    {
269      return hasAllOptions0(options);
270    }
271    return false;
272  }
273
274  /** Cannot use Set.containsAll() because the set of options are not normalized. */
275  private boolean hasAllOptions0(Collection<String> options)
276  {
277    for (String option : options)
278    {
279      if (!hasOption(option))
280      {
281        return false;
282      }
283    }
284    return true;
285  }
286
287  @Override
288  public final String toString()
289  {
290    StringBuilder buffer = new StringBuilder();
291    toString(buffer);
292    return buffer.toString();
293  }
294}