View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.http.message;
29  
30  import java.util.List;
31  import java.util.NoSuchElementException;
32  
33  import org.apache.http.Header;
34  import org.apache.http.HeaderIterator;
35  import org.apache.http.util.Args;
36  import org.apache.http.util.Asserts;
37  
38  /**
39   * Implementation of a {@link HeaderIterator} based on a {@link List}.
40   * For use by {@link HeaderGroup}.
41   *
42   * @since 4.0
43   */
44  public class BasicListHeaderIterator implements HeaderIterator {
45  
46      /**
47       * A list of headers to iterate over.
48       * Not all elements of this array are necessarily part of the iteration.
49       */
50      protected final List<Header> allHeaders;
51  
52  
53      /**
54       * The position of the next header in {@link #allHeaders allHeaders}.
55       * Negative if the iteration is over.
56       */
57      protected int currentIndex;
58  
59  
60      /**
61       * The position of the last returned header.
62       * Negative if none has been returned so far.
63       */
64      protected int lastIndex;
65  
66  
67      /**
68       * The header name to filter by.
69       * {@code null} to iterate over all headers in the array.
70       */
71      protected String headerName;
72  
73  
74  
75      /**
76       * Creates a new header iterator.
77       *
78       * @param headers   a list of headers over which to iterate
79       * @param name      the name of the headers over which to iterate, or
80       *                  {@code null} for any
81       */
82      public BasicListHeaderIterator(final List<Header> headers, final String name) {
83          super();
84          this.allHeaders = Args.notNull(headers, "Header list");
85          this.headerName = name;
86          this.currentIndex = findNext(-1);
87          this.lastIndex = -1;
88      }
89  
90  
91      /**
92       * Determines the index of the next header.
93       *
94       * @param pos       one less than the index to consider first,
95       *                  -1 to search for the first header
96       *
97       * @return  the index of the next header that matches the filter name,
98       *          or negative if there are no more headers
99       */
100     protected int findNext(final int pos) {
101         int from = pos;
102         if (from < -1) {
103             return -1;
104         }
105 
106         final int to = this.allHeaders.size()-1;
107         boolean found = false;
108         while (!found && (from < to)) {
109             from++;
110             found = filterHeader(from);
111         }
112         return found ? from : -1;
113     }
114 
115 
116     /**
117      * Checks whether a header is part of the iteration.
118      *
119      * @param index     the index of the header to check
120      *
121      * @return  {@code true} if the header should be part of the
122      *          iteration, {@code false} to skip
123      */
124     protected boolean filterHeader(final int index) {
125         if (this.headerName == null) {
126             return true;
127         }
128 
129         // non-header elements, including null, will trigger exceptions
130         final String name = (this.allHeaders.get(index)).getName();
131 
132         return this.headerName.equalsIgnoreCase(name);
133     }
134 
135 
136     // non-javadoc, see interface HeaderIterator
137     @Override
138     public boolean hasNext() {
139         return (this.currentIndex >= 0);
140     }
141 
142 
143     /**
144      * Obtains the next header from this iteration.
145      *
146      * @return  the next header in this iteration
147      *
148      * @throws NoSuchElementException   if there are no more headers
149      */
150     @Override
151     public Header nextHeader()
152         throws NoSuchElementException {
153 
154         final int current = this.currentIndex;
155         if (current < 0) {
156             throw new NoSuchElementException("Iteration already finished.");
157         }
158 
159         this.lastIndex    = current;
160         this.currentIndex = findNext(current);
161 
162         return this.allHeaders.get(current);
163     }
164 
165 
166     /**
167      * Returns the next header.
168      * Same as {@link #nextHeader nextHeader}, but not type-safe.
169      *
170      * @return  the next header in this iteration
171      *
172      * @throws NoSuchElementException   if there are no more headers
173      */
174     @Override
175     public final Object next()
176         throws NoSuchElementException {
177         return nextHeader();
178     }
179 
180 
181     /**
182      * Removes the header that was returned last.
183      */
184     @Override
185     public void remove()
186         throws UnsupportedOperationException {
187         Asserts.check(this.lastIndex >= 0, "No header to remove");
188         this.allHeaders.remove(this.lastIndex);
189         this.lastIndex = -1;
190         this.currentIndex--; // adjust for the removed element
191     }
192 }