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
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.types;
028    
029    import java.util.HashSet;
030    import java.util.Iterator;
031    import java.util.NoSuchElementException;
032    
033    
034    
035    /**
036     * An iterable read-only view of of a set of attribute values returned
037     * from methods such as
038     * {@link org.opends.server.types.Entry#getAttribute(AttributeType)}.
039     * <p>
040     * Using instances of this class it is possible to filter out
041     * attribute values which do not have the correct options set. This is
042     * achieved without having to duplicate the set of attributes.
043     */
044    @org.opends.server.types.PublicAPI(
045         stability=org.opends.server.types.StabilityLevel.VOLATILE,
046         mayInstantiate=false,
047         mayExtend=false,
048         mayInvoke=true)
049    public final class AttributeValueIterable implements
050        Iterable<AttributeValue> {
051    
052      // The set of attributes having the same type and options.
053      private Iterable<Attribute> attributes;
054    
055      // The set of options which all values must contain.
056      private HashSet<String> options;
057    
058    
059    
060      /**
061       * Create a new attribute value iterable object.
062       *
063       * @param  attributes  The set of attributes having the same type.
064       *                     Can be {@code null}.
065       */
066      public AttributeValueIterable(Iterable<Attribute> attributes) {
067        this(attributes, null);
068    
069      }
070    
071      /**
072       * Create a new attribute value iterable object.
073       *
074       * @param  attributes  The set of attributes having the same type.
075       *                     Can be {@code null}.
076       * @param  options     The set of options which all values must
077       *                     contain, or {@code null} if no options are
078       *                     required.
079       */
080      public AttributeValueIterable(Iterable<Attribute> attributes,
081                                    HashSet<String> options) {
082    
083        this.attributes = attributes;
084        this.options = options;
085      }
086    
087      /**
088       * Retrieves an iterator that can be used to cursor through the set
089       * of attribute values.
090       *
091       * @return  An iterator that can be used to cursor through the set
092       *          of attribute values.
093       */
094      public Iterator<AttributeValue> iterator() {
095    
096        return new AttributeValueIterator();
097      }
098    
099      /**
100       * Private iterator implementation.
101       */
102      private class AttributeValueIterator
103              implements Iterator<AttributeValue> {
104        // Flag indicating whether iteration can proceed.
105        private boolean hasNext;
106    
107        // The current attribute iterator.
108        private Iterator<Attribute> attributeIterator;
109    
110        // The current value iterator.
111        private Iterator<AttributeValue> valueIterator;
112    
113        /**
114         * Create a new attribute value iterator over the attribute set.
115         */
116        private AttributeValueIterator() {
117    
118          this.valueIterator = null;
119    
120          if (attributes != null) {
121            this.attributeIterator = attributes.iterator();
122            this.hasNext = skipNonMatchingAttributes();
123          } else {
124            this.attributeIterator = null;
125            this.hasNext = false;
126          }
127        }
128    
129        /**
130         * Indicates whether there are more attribute values to return.
131         *
132         * @return  {@code true} if there are more attribute values to
133         *          return, or {@code false} if not.
134         */
135        public boolean hasNext() {
136    
137          return hasNext;
138        }
139    
140        /**
141         * Retrieves the next attribute value in the set.
142         *
143         * @return  The next attribute value in the set.
144         *
145         * @throws  NoSuchElementException  If there are no more values to
146         *                                  return.
147         */
148        public AttributeValue next()
149               throws NoSuchElementException
150        {
151          if (hasNext == false) {
152            throw new NoSuchElementException();
153          }
154    
155          AttributeValue value = valueIterator.next();
156    
157          // We've reached the end of this array list, so skip to the next
158          // non-empty one.
159          if (valueIterator.hasNext() == false) {
160            hasNext = skipNonMatchingAttributes();
161          }
162    
163          return value;
164        }
165    
166        /**
167         * Removes the last attribute value retrieved from the set.  Note
168         * that this operation is not supported and will always cause an
169         * {@code UnsupportedOperationException} to be thrown.
170         *
171         * @throws  UnsupportedOperationException  If the last value
172         *                                         cannot be removed.
173         */
174        public void remove()
175               throws UnsupportedOperationException
176        {
177          throw new UnsupportedOperationException();
178        }
179    
180        /**
181         * Skip past any empty attributes or attributes that do not have
182         * the correct set of options until we find one that contains some
183         * values.
184         *
185         * @return  {@code true} if iteration can continue, or
186         *          {@code false} if not.
187         */
188        private boolean skipNonMatchingAttributes() {
189    
190          while (attributeIterator.hasNext()) {
191            Attribute attribute = attributeIterator.next();
192    
193            if (attribute.hasOptions(options)) {
194              valueIterator = attribute.getValues().iterator();
195              if (valueIterator.hasNext()) {
196                return true;
197              }
198            }
199          }
200    
201          return false;
202        }
203      }
204    }