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 2010 Sun Microsystems, Inc.
025 *      Portions copyright 2011-2013 ForgeRock AS.
026 */
027
028package org.forgerock.opendj.ldap;
029
030import java.util.Collection;
031import java.util.Iterator;
032
033import org.forgerock.i18n.LocalizedIllegalArgumentException;
034import org.forgerock.util.Reject;
035
036import com.forgerock.opendj.util.Iterators;
037
038/**
039 * This class contains methods for creating and manipulating attributes.
040 */
041public final class Attributes {
042
043    /**
044     * Empty attribute.
045     */
046    private static final class EmptyAttribute extends AbstractAttribute {
047
048        private final AttributeDescription attributeDescription;
049
050        private EmptyAttribute(final AttributeDescription attributeDescription) {
051            this.attributeDescription = attributeDescription;
052        }
053
054        @Override
055        public boolean add(final ByteString value) {
056            throw new UnsupportedOperationException();
057        }
058
059        @Override
060        public void clear() {
061            throw new UnsupportedOperationException();
062        }
063
064        @Override
065        public boolean contains(final Object value) {
066            return false;
067        }
068
069        @Override
070        public AttributeDescription getAttributeDescription() {
071            return attributeDescription;
072        }
073
074        @Override
075        public boolean isEmpty() {
076            return true;
077        }
078
079        @Override
080        public Iterator<ByteString> iterator() {
081            return Iterators.emptyIterator();
082        }
083
084        @Override
085        public boolean remove(final Object value) {
086            throw new UnsupportedOperationException();
087        }
088
089        @Override
090        public int size() {
091            return 0;
092        }
093
094    }
095
096    /**
097     * Renamed attribute.
098     */
099    private static final class RenamedAttribute implements Attribute {
100
101        private final Attribute attribute;
102        private final AttributeDescription attributeDescription;
103
104        private RenamedAttribute(final Attribute attribute,
105                final AttributeDescription attributeDescription) {
106            this.attribute = attribute;
107            this.attributeDescription = attributeDescription;
108        }
109
110        @Override
111        public boolean add(final ByteString value) {
112            return attribute.add(value);
113        }
114
115        @Override
116        public boolean add(final Object... values) {
117            return attribute.add(values);
118        }
119
120        @Override
121        public boolean addAll(final Collection<? extends ByteString> values) {
122            return attribute.addAll(values);
123        }
124
125        @Override
126        public <T> boolean addAll(final Collection<T> values,
127                final Collection<? super T> duplicateValues) {
128            return attribute.addAll(values, duplicateValues);
129        }
130
131        @Override
132        public void clear() {
133            attribute.clear();
134        }
135
136        @Override
137        public boolean contains(final Object value) {
138            return attribute.contains(value);
139        }
140
141        @Override
142        public boolean containsAll(final Collection<?> values) {
143            return attribute.containsAll(values);
144        }
145
146        @Override
147        public boolean equals(final Object object) {
148            return AbstractAttribute.equals(this, object);
149        }
150
151        @Override
152        public ByteString firstValue() {
153            return attribute.firstValue();
154        }
155
156        @Override
157        public String firstValueAsString() {
158            return attribute.firstValueAsString();
159        }
160
161        @Override
162        public AttributeDescription getAttributeDescription() {
163            return attributeDescription;
164        }
165
166        @Override
167        public String getAttributeDescriptionAsString() {
168            return attributeDescription.toString();
169        }
170
171        @Override
172        public int hashCode() {
173            return AbstractAttribute.hashCode(this);
174        }
175
176        @Override
177        public boolean isEmpty() {
178            return attribute.isEmpty();
179        }
180
181        @Override
182        public Iterator<ByteString> iterator() {
183            return attribute.iterator();
184        }
185
186        @Override
187        public AttributeParser parse() {
188            return attribute.parse();
189        }
190
191        @Override
192        public boolean remove(final Object value) {
193            return attribute.remove(value);
194        }
195
196        @Override
197        public boolean removeAll(final Collection<?> values) {
198            return attribute.removeAll(values);
199        }
200
201        @Override
202        public <T> boolean removeAll(final Collection<T> values,
203                final Collection<? super T> missingValues) {
204            return attribute.removeAll(values, missingValues);
205        }
206
207        @Override
208        public boolean retainAll(final Collection<?> values) {
209            return attribute.retainAll(values);
210        }
211
212        @Override
213        public <T> boolean retainAll(final Collection<T> values,
214                final Collection<? super T> missingValues) {
215            return attribute.retainAll(values, missingValues);
216        }
217
218        @Override
219        public int size() {
220            return attribute.size();
221        }
222
223        @Override
224        public ByteString[] toArray() {
225            return attribute.toArray();
226        }
227
228        @Override
229        public <T> T[] toArray(final T[] array) {
230            return attribute.toArray(array);
231        }
232
233        @Override
234        public String toString() {
235            return AbstractAttribute.toString(this);
236        }
237
238    }
239
240    /**
241     * Singleton attribute.
242     */
243    private static final class SingletonAttribute extends AbstractAttribute {
244
245        private final AttributeDescription attributeDescription;
246        private ByteString normalizedValue;
247        private final ByteString value;
248
249        private SingletonAttribute(final AttributeDescription attributeDescription,
250                final Object value) {
251            this.attributeDescription = attributeDescription;
252            this.value = ByteString.valueOf(value);
253        }
254
255        @Override
256        public boolean add(final ByteString value) {
257            throw new UnsupportedOperationException();
258        }
259
260        @Override
261        public void clear() {
262            throw new UnsupportedOperationException();
263        }
264
265        @Override
266        public boolean contains(final Object value) {
267            final ByteString normalizedValue = normalizeValue(this, ByteString.valueOf(value));
268            return normalizedSingleValue().equals(normalizedValue);
269        }
270
271        @Override
272        public AttributeDescription getAttributeDescription() {
273            return attributeDescription;
274        }
275
276        @Override
277        public boolean isEmpty() {
278            return false;
279        }
280
281        @Override
282        public Iterator<ByteString> iterator() {
283            return Iterators.singletonIterator(value);
284        }
285
286        @Override
287        public boolean remove(final Object value) {
288            throw new UnsupportedOperationException();
289        }
290
291        @Override
292        public int size() {
293            return 1;
294        }
295
296        /** Lazily computes the normalized single value. */
297        private ByteString normalizedSingleValue() {
298            if (normalizedValue == null) {
299                normalizedValue = normalizeValue(this, value);
300            }
301            return normalizedValue;
302        }
303
304    }
305
306    /**
307     * Unmodifiable attribute.
308     */
309    private static final class UnmodifiableAttribute implements Attribute {
310
311        private final Attribute attribute;
312
313        private UnmodifiableAttribute(final Attribute attribute) {
314            this.attribute = attribute;
315        }
316
317        @Override
318        public boolean add(final ByteString value) {
319            throw new UnsupportedOperationException();
320        }
321
322        @Override
323        public boolean add(final Object... values) {
324            throw new UnsupportedOperationException();
325        }
326
327        @Override
328        public boolean addAll(final Collection<? extends ByteString> values) {
329            throw new UnsupportedOperationException();
330        }
331
332        @Override
333        public <T> boolean addAll(final Collection<T> values,
334                final Collection<? super T> duplicateValues) {
335            throw new UnsupportedOperationException();
336        }
337
338        @Override
339        public void clear() {
340            throw new UnsupportedOperationException();
341        }
342
343        @Override
344        public boolean contains(final Object value) {
345            return attribute.contains(value);
346        }
347
348        @Override
349        public boolean containsAll(final Collection<?> values) {
350            return attribute.containsAll(values);
351        }
352
353        @Override
354        public boolean equals(final Object object) {
355            return object == this || attribute.equals(object);
356        }
357
358        @Override
359        public ByteString firstValue() {
360            return attribute.firstValue();
361        }
362
363        @Override
364        public String firstValueAsString() {
365            return attribute.firstValueAsString();
366        }
367
368        @Override
369        public AttributeDescription getAttributeDescription() {
370            return attribute.getAttributeDescription();
371        }
372
373        @Override
374        public String getAttributeDescriptionAsString() {
375            return attribute.getAttributeDescriptionAsString();
376        }
377
378        @Override
379        public int hashCode() {
380            return attribute.hashCode();
381        }
382
383        @Override
384        public boolean isEmpty() {
385            return attribute.isEmpty();
386        }
387
388        @Override
389        public Iterator<ByteString> iterator() {
390            return Iterators.unmodifiableIterator(attribute.iterator());
391        }
392
393        @Override
394        public AttributeParser parse() {
395            return attribute.parse();
396        }
397
398        @Override
399        public boolean remove(final Object value) {
400            throw new UnsupportedOperationException();
401        }
402
403        @Override
404        public boolean removeAll(final Collection<?> values) {
405            throw new UnsupportedOperationException();
406        }
407
408        @Override
409        public <T> boolean removeAll(final Collection<T> values,
410                final Collection<? super T> missingValues) {
411            throw new UnsupportedOperationException();
412        }
413
414        @Override
415        public boolean retainAll(final Collection<?> values) {
416            throw new UnsupportedOperationException();
417        }
418
419        @Override
420        public <T> boolean retainAll(final Collection<T> values,
421                final Collection<? super T> missingValues) {
422            throw new UnsupportedOperationException();
423        }
424
425        @Override
426        public int size() {
427            return attribute.size();
428        }
429
430        @Override
431        public ByteString[] toArray() {
432            return attribute.toArray();
433        }
434
435        @Override
436        public <T> T[] toArray(final T[] array) {
437            return attribute.toArray(array);
438        }
439
440        @Override
441        public String toString() {
442            return attribute.toString();
443        }
444    }
445
446    /**
447     * Returns a read-only empty attribute having the specified attribute
448     * description. Attempts to modify the returned attribute either directly,
449     * or indirectly via an iterator, result in an
450     * {@code UnsupportedOperationException}.
451     *
452     * @param attributeDescription
453     *            The attribute description.
454     * @return The empty attribute.
455     * @throws NullPointerException
456     *             If {@code attributeDescription} was {@code null}.
457     */
458    public static Attribute emptyAttribute(final AttributeDescription attributeDescription) {
459        return new EmptyAttribute(attributeDescription);
460    }
461
462    /**
463     * Returns a read-only empty attribute having the specified attribute
464     * description. The attribute description will be decoded using the default
465     * schema. Attempts to modify the returned attribute either directly, or
466     * indirectly via an iterator, result in an
467     * {@code UnsupportedOperationException}.
468     *
469     * @param attributeDescription
470     *            The attribute description.
471     * @return The empty attribute.
472     * @throws LocalizedIllegalArgumentException
473     *             If {@code attributeDescription} could not be decoded using
474     *             the default schema.
475     * @throws NullPointerException
476     *             If {@code attributeDescription} was {@code null}.
477     */
478    public static Attribute emptyAttribute(final String attributeDescription) {
479        return emptyAttribute(AttributeDescription.valueOf(attributeDescription));
480    }
481
482    /**
483     * Returns a view of {@code attribute} having a different attribute
484     * description. All operations on the returned attribute "pass-through" to
485     * the underlying attribute.
486     *
487     * @param attribute
488     *            The attribute to be renamed.
489     * @param attributeDescription
490     *            The new attribute description for {@code attribute}.
491     * @return A renamed view of {@code attribute}.
492     * @throws NullPointerException
493     *             If {@code attribute} or {@code attributeDescription} was
494     *             {@code null}.
495     */
496    public static Attribute renameAttribute(final Attribute attribute,
497            final AttributeDescription attributeDescription) {
498        Reject.ifNull(attribute, attributeDescription);
499
500        // Optimize for the case where no renaming is required.
501        if (attribute.getAttributeDescription() == attributeDescription) {
502            return attribute;
503        } else {
504            return new RenamedAttribute(attribute, attributeDescription);
505        }
506    }
507
508    /**
509     * Returns a view of {@code attribute} having a different attribute
510     * description. All operations on the returned attribute "pass-through" to
511     * the underlying attribute. The attribute description will be decoded using
512     * the default schema.
513     *
514     * @param attribute
515     *            The attribute to be renamed.
516     * @param attributeDescription
517     *            The new attribute description for {@code attribute}.
518     * @return A renamed view of {@code attribute}.
519     * @throws LocalizedIllegalArgumentException
520     *             If {@code attributeDescription} could not be decoded using
521     *             the default schema.
522     * @throws NullPointerException
523     *             If {@code attribute} or {@code attributeDescription} was
524     *             {@code null}.
525     */
526    public static Attribute renameAttribute(final Attribute attribute, final String attributeDescription) {
527        Reject.ifNull(attribute, attributeDescription);
528        return renameAttribute(attribute, AttributeDescription.valueOf(attributeDescription));
529    }
530
531    /**
532     * Returns a read-only single-valued attribute having the specified
533     * attribute description and value. Attempts to modify the returned
534     * attribute either directly, or indirectly via an iterator, result in an
535     * {@code UnsupportedOperationException}.
536     * <p>
537     * If {@code value} is not an instance of {@code ByteString} then it will be
538     * converted using the {@link ByteString#valueOf(Object)} method.
539     *
540     * @param attributeDescription
541     *            The attribute description.
542     * @param value
543     *            The single attribute value.
544     * @return The single-valued attribute.
545     * @throws NullPointerException
546     *             If {@code attributeDescription} or {@code value} was
547     *             {@code null}.
548     */
549    public static Attribute singletonAttribute(final AttributeDescription attributeDescription, final Object value) {
550        return new SingletonAttribute(attributeDescription, value);
551    }
552
553    /**
554     * Returns a read-only single-valued attribute having the specified
555     * attribute description. The attribute description will be decoded using
556     * the default schema. Attempts to modify the returned attribute either
557     * directly, or indirectly via an iterator, result in an
558     * {@code UnsupportedOperationException}.
559     * <p>
560     * If {@code value} is not an instance of {@code ByteString} then it will be
561     * converted using the {@link ByteString#valueOf(Object)} method.
562     *
563     * @param attributeDescription
564     *            The attribute description.
565     * @param value
566     *            The single attribute value.
567     * @return The single-valued attribute.
568     * @throws LocalizedIllegalArgumentException
569     *             If {@code attributeDescription} could not be decoded using
570     *             the default schema.
571     * @throws NullPointerException
572     *             If {@code attributeDescription} or {@code value} was
573     *             {@code null}.
574     */
575    public static Attribute singletonAttribute(final String attributeDescription, final Object value) {
576        return singletonAttribute(AttributeDescription.valueOf(attributeDescription), value);
577    }
578
579    /**
580     * Returns a read-only view of {@code attribute}. Query operations on the
581     * returned attribute "read-through" to the underlying attribute, and
582     * attempts to modify the returned attribute either directly or indirectly
583     * via an iterator result in an {@code UnsupportedOperationException}.
584     *
585     * @param attribute
586     *            The attribute for which a read-only view is to be returned.
587     * @return A read-only view of {@code attribute}.
588     * @throws NullPointerException
589     *             If {@code attribute} was {@code null}.
590     */
591    public static Attribute unmodifiableAttribute(final Attribute attribute) {
592        if (attribute instanceof UnmodifiableAttribute) {
593            return attribute;
594        }
595        return new UnmodifiableAttribute(attribute);
596    }
597
598    /** Prevent instantiation. */
599    private Attributes() {
600        // Nothing to do.
601    }
602}