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.hc.core5.http.message;
29  
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.NoSuchElementException;
33  
34  import org.apache.hc.core5.http.Header;
35  import org.apache.hc.core5.util.Args;
36  import org.apache.hc.core5.util.Asserts;
37  
38  /**
39   * {@link java.util.Iterator} of {@link org.apache.hc.core5.http.Header}s. For use by {@link HeaderGroup}.
40   *
41   * @since 4.0
42   */
43  class BasicListHeaderIterator implements Iterator<Header> {
44  
45      /**
46       * A list of headers to iterate over.
47       * Not all elements of this array are necessarily part of the iteration.
48       */
49      private final List<Header> allHeaders;
50  
51      /**
52       * The position of the next header in {@link #allHeaders allHeaders}.
53       * Negative if the iteration is over.
54       */
55      private int currentIndex;
56  
57      /**
58       * The position of the last returned header.
59       * Negative if none has been returned so far.
60       */
61      private int lastIndex;
62  
63      /**
64       * The header name to filter by.
65       * {@code null} to iterate over all headers in the array.
66       */
67      private final String headerName;
68  
69      /**
70       * Creates a new header iterator.
71       *
72       * @param headers   a list of headers over which to iterate
73       * @param name      the name of the headers over which to iterate, or
74       *                  {@code null} for any
75       */
76      public BasicListHeaderIterator(final List<Header> headers, final String name) {
77          super();
78          this.allHeaders = Args.notNull(headers, "Header list");
79          this.headerName = name;
80          this.currentIndex = findNext(-1);
81          this.lastIndex = -1;
82      }
83  
84      /**
85       * Determines the index of the next header.
86       *
87       * @param pos       one less than the index to consider first,
88       *                  -1 to search for the first header
89       *
90       * @return  the index of the next header that matches the filter name,
91       *          or negative if there are no more headers
92       */
93      protected int findNext(final int pos) {
94          int from = pos;
95          if (from < -1) {
96              return -1;
97          }
98  
99          final int to = this.allHeaders.size()-1;
100         boolean found = false;
101         while (!found && (from < to)) {
102             from++;
103             found = filterHeader(from);
104         }
105         return found ? from : -1;
106     }
107 
108     /**
109      * Checks whether a header is part of the iteration.
110      *
111      * @param index     the index of the header to check
112      *
113      * @return  {@code true} if the header should be part of the
114      *          iteration, {@code false} to skip
115      */
116     private boolean filterHeader(final int index) {
117         if (this.headerName == null) {
118             return true;
119         }
120 
121         // non-header elements, including null, will trigger exceptions
122         final String name = (this.allHeaders.get(index)).getName();
123 
124         return this.headerName.equalsIgnoreCase(name);
125     }
126 
127     @Override
128     public boolean hasNext() {
129         return this.currentIndex >= 0;
130     }
131 
132     /**
133      * Obtains the next header from this iteration.
134      *
135      * @return  the next header in this iteration
136      *
137      * @throws NoSuchElementException   if there are no more headers
138      */
139     @Override
140     public Header next() throws NoSuchElementException {
141         final int current = this.currentIndex;
142         if (current < 0) {
143             throw new NoSuchElementException("Iteration already finished.");
144         }
145 
146         this.lastIndex    = current;
147         this.currentIndex = findNext(current);
148 
149         return this.allHeaders.get(current);
150     }
151 
152     /**
153      * Removes the header that was returned last.
154      */
155     @Override
156     public void remove() throws UnsupportedOperationException {
157         Asserts.check(this.lastIndex >= 0, "No header to remove");
158         this.allHeaders.remove(this.lastIndex);
159         this.lastIndex = -1;
160         this.currentIndex--; // adjust for the removed element
161     }
162 
163 }