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 2008-2009 Sun Microsystems, Inc.
015 * Portions Copyright 2014-2016 ForgeRock AS.
016 */
017package org.opends.server.types;
018
019import static org.opends.server.core.DirectoryServer.*;
020import static org.opends.server.util.CollectionUtils.*;
021
022import java.util.Arrays;
023import java.util.Collection;
024import java.util.Collections;
025import java.util.List;
026
027import org.forgerock.opendj.ldap.ByteString;
028import org.forgerock.opendj.ldap.schema.AttributeType;
029import org.opends.server.types.Attribute.RemoveOnceSwitchingAttributes;
030
031/**
032 * This class contains various methods for manipulating
033 * {@link Attribute}s as well as static factory methods for
034 * facilitating common {@link Attribute} construction use-cases.
035 * <p>
036 * Of particular interest are the following three factory methods:
037 *
038 * <pre>
039 * empty(String);
040 *
041 * create(String, String);
042 *
043 * create(String, String, String...);
044 * </pre>
045 *
046 * These are provided in order to facilitate construction of empty,
047 * single-valued, and multi-valued attributes respectively, for
048 * example, in unit tests. The last factory method is not designed for
049 * performance critical functionality and, instead, an
050 * {@link AttributeBuilder} should be used in order to incrementally
051 * construct multi-valued attributes.
052 */
053@RemoveOnceSwitchingAttributes
054public final class Attributes
055{
056
057  /**
058   * Creates a new single-valued attribute with the specified attribute type and value.
059   *
060   * @param attributeType
061   *          The attribute type to use.
062   * @param value
063   *          The attribute value.
064   * @return A new attribute with the attribute type and value.
065   */
066  public static Attribute create(AttributeType attributeType, ByteString value)
067  {
068    return create(attributeType, attributeType.getNameOrOID(), value);
069  }
070
071  /**
072   * Creates a new List with a single-valued attribute with the specified attribute type and value.
073   *
074   * @param attributeType
075   *          The attribute type to use.
076   * @param value
077   *          The attribute value.
078   * @return A new List with a single-valued attribute with the attribute type and value.
079   */
080  public static List<Attribute> createAsList(AttributeType attributeType, ByteString value)
081  {
082    return newArrayList(create(attributeType, value));
083  }
084
085  /**
086   * Creates a new single-valued attribute with the specified name and value.
087   *
088   * @param attributeType
089   *          The attribute type to use.
090   * @param valueString
091   *          The String representation of the attribute value.
092   * @return A new attribute with the specified name and value.
093   */
094  public static Attribute create(AttributeType attributeType, String valueString)
095  {
096    return create(attributeType, attributeType.getNameOrOID(), valueString);
097  }
098
099  /**
100   * Creates a new List with a single-valued attribute with the specified name and value.
101   *
102   * @param attributeType
103   *          The attribute type to use.
104   * @param valueString
105   *          The String representation of the attribute value.
106   * @return A new List with a attribute with the specified name and value.
107   */
108  public static List<Attribute> createAsList(AttributeType attributeType, String valueString)
109  {
110    return newArrayList(create(attributeType, valueString));
111  }
112
113  /**
114   * Creates a new single-valued attribute with the specified
115   * attribute type and value.
116   *
117   * @param attributeType
118   *          The attribute type to use.
119   * @param name
120   *          The user-provided name for this attribute.
121   * @param value
122   *          The attribute value.
123   * @return A new attribute with the attribute type and value.
124   */
125  public static Attribute create(AttributeType attributeType, String name, ByteString value)
126  {
127    return AttributeBuilder.create(attributeType, name, Collections.singleton(value));
128  }
129
130  /**
131   * Creates a new single-valued attribute with the attribute type and value.
132   *
133   * @param attributeType
134   *          The attribute type to use.
135   * @param name
136   *          The user-provided name for this attribute.
137   * @param valueString
138   *          The String representation of the attribute value.
139   * @return A new attribute with the attribute type and value.
140   */
141  public static Attribute create(AttributeType attributeType, String name, String valueString)
142  {
143    return create(attributeType, name, ByteString.valueOfUtf8(valueString));
144  }
145
146  /**
147   * Creates a new List with a single-valued attribute with the attribute type and value.
148   *
149   * @param attributeType
150   *          The attribute type to use.
151   * @param name
152   *          The user-provided name for this attribute.
153   * @param valueString
154   *          The String representation of the attribute value.
155   * @return A new List with a single-valued attribute with the attribute type and value.
156   */
157
158  public static List<Attribute> createAsList(AttributeType attributeType, String name, String valueString)
159  {
160    return newArrayList(create(attributeType, name, valueString));
161  }
162
163  /**
164   * Creates a new single-valued attribute with the specified
165   * attribute name and attribute value.
166   * <p>
167   * If the attribute name cannot be found in the schema, a new
168   * attribute type is created using the default attribute syntax.
169   *
170   * @param attributeName
171   *          The name or OID of the attribute type for this attribute
172   *          (can be mixed case).
173   * @param valueString
174   *          The String representation of the attribute value.
175   * @return A new attribute with the specified name and value.
176   */
177  public static Attribute create(String attributeName, String valueString)
178  {
179    return create(getSchema().getAttributeType(attributeName), attributeName, valueString);
180  }
181
182  /**
183   * Creates a new multi-valued attribute with the specified attribute
184   * name and attribute values.
185   * <p>
186   * If the attribute name cannot be found in the schema, a new
187   * attribute type is created using the default attribute syntax.
188   * <p>
189   * <b>NOTE:</b> this method is provided as a convenience and should
190   * typically be reserved for use in unit tests and places where
191   * performance is not an issue. In particular, this method will
192   * construct a temporary array containing the attribute's values.
193   * For performance critical purposes, incrementally construct an
194   * attribute using an {@link AttributeBuilder}.
195   *
196   * @param attributeName
197   *          The name or OID of the attribute type for this attribute
198   *          (can be mixed case).
199   * @param valueStrings
200   *          The string representation of the attribute values.
201   * @return A new attribute with the specified name and values.
202   */
203  public static Attribute create(String attributeName, String... valueStrings)
204  {
205    if (valueStrings.length == 0) {
206      return empty(attributeName);
207    }
208    AttributeBuilder builder = new AttributeBuilder(attributeName);
209    builder.addAllStrings(Arrays.asList(valueStrings));
210    return builder.toAttribute();
211  }
212
213  /**
214   * Creates a new attribute which has the same attribute type and
215   * attribute options as the provided attribute but no attribute
216   * values.
217   *
218   * @param attribute
219   *          The attribute to be copied.
220   * @return A new attribute which has the same attribute type and
221   *         attribute options as the provided attribute but no
222   *         attribute values.
223   */
224  public static Attribute empty(Attribute attribute)
225  {
226    return new AttributeBuilder(attribute.getAttributeDescription()).toAttribute();
227  }
228
229
230
231  /**
232   * Creates a new attribute with the provided attribute type and no
233   * values.
234   *
235   * @param attributeType
236   *          The attribute type to use.
237   * @return A new attribute with the provided attribute type and no
238   *         values.
239   */
240  public static Attribute empty(AttributeType attributeType)
241  {
242    return empty(attributeType, attributeType.getNameOrOID());
243  }
244
245
246
247  /**
248   * Creates a new attribute with the provided attribute type and no
249   * values.
250   *
251   * @param attributeType
252   *          The attribute type to use.
253   * @param name
254   *          The user-provided name for this attribute.
255   * @return A new attribute with the provided attribute type and no
256   *         values.
257   */
258  public static Attribute empty(AttributeType attributeType,
259      String name)
260  {
261    return AttributeBuilder.create(attributeType, name, Collections
262        .<ByteString> emptySet());
263  }
264
265
266
267  /**
268   * Creates a new attribute with the specified attribute name and no
269   * attribute values.
270   * <p>
271   * If the attribute name cannot be found in the schema, a new
272   * attribute type is created using the default attribute syntax.
273   *
274   * @param attributeName
275   *          The name or OID of the attribute type for this attribute
276   *          (can be mixed case).
277   * @return A new attribute with the specified name and no values.
278   */
279  public static Attribute empty(String attributeName)
280  {
281    return empty(getSchema().getAttributeType(attributeName), attributeName);
282  }
283
284
285
286  /**
287   * Creates a new attribute containing all the values from the two
288   * provided attributes. The returned attribute will use the name and
289   * options taken from the first attribute.
290   * <p>
291   * This method is logically equivalent to:
292   *
293   * <pre>
294   * merge(a1, a2, null);
295   * </pre>
296   *
297   * @param a1
298   *          The first attribute.
299   * @param a2
300   *          The second attribute.
301   * @return A new attribute containing all the values from the two
302   *         provided attributes.
303   */
304  public static Attribute merge(Attribute a1, Attribute a2)
305  {
306    return merge(a1, a2, null);
307  }
308
309
310
311  /**
312   * Creates a new attribute containing all the values from the two
313   * provided attributes and put any duplicate values into the
314   * provided collection. The returned attribute will use the name
315   * and options taken from the first attribute.
316   *
317   * @param a1
318   *          The first attribute.
319   * @param a2
320   *          The second attribute.
321   * @param duplicateValues
322   *          A collection which will be used to store any duplicate
323   *          values, or <code>null</code> if duplicate values should
324   *          not be stored.
325   * @return A new attribute containing all the values from the two
326   *         provided attributes.
327   */
328  public static Attribute merge(Attribute a1, Attribute a2,
329      Collection<ByteString> duplicateValues)
330  {
331    AttributeBuilder builder = new AttributeBuilder(a1);
332    for (ByteString av : a2)
333    {
334      if (!builder.add(av) && duplicateValues != null)
335      {
336        duplicateValues.add(av);
337      }
338    }
339    return builder.toAttribute();
340  }
341
342
343
344  /**
345   * Creates a new attribute containing the values from the first
346   * attribute which are not in the second attribute. The returned
347   * attribute will use the name and options taken from the first
348   * attribute.
349   * <p>
350   * This method is logically equivalent to:
351   *
352   * <pre>
353   * subtract(a1, a2, null);
354   * </pre>
355   *
356   * @param a1
357   *          The first attribute.
358   * @param a2
359   *          The second attribute.
360   * @return A new attribute containing the values from the first
361   *         attribute which are not in the second attribute.
362   */
363  public static Attribute subtract(Attribute a1, Attribute a2)
364  {
365    return subtract(a1, a2, null);
366  }
367
368
369
370  /**
371   * Creates a new attribute containing the values from the first
372   * attribute which are not in the second attribute. Any values which
373   * were present in the second attribute but which were not present
374   * in the first attribute will be put into the provided collection.
375   * The returned attribute will use the name and options taken from
376   * the first attribute.
377   *
378   * @param a1
379   *          The first attribute.
380   * @param a2
381   *          The second attribute.
382   * @param missingValues
383   *          A collection which will be used to store any missing
384   *          values, or <code>null</code> if missing values should
385   *          not be stored.
386   * @return A new attribute containing the values from the first
387   *         attribute which are not in the second attribute.
388   */
389  public static Attribute subtract(Attribute a1, Attribute a2,
390      Collection<ByteString> missingValues)
391  {
392    AttributeBuilder builder = new AttributeBuilder(a1);
393    for (ByteString av : a2)
394    {
395      if (!builder.remove(av) && missingValues != null)
396      {
397        missingValues.add(av);
398      }
399    }
400    return builder.toAttribute();
401  }
402}