001/*
002 * ====================================================================
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *   http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied.  See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 * ====================================================================
020 *
021 * This software consists of voluntary contributions made by many
022 * individuals on behalf of the Apache Software Foundation.  For more
023 * information on the Apache Software Foundation, please see
024 * <http://www.apache.org/>.
025 *
026 */
027
028package org.apache.http.message;
029
030import java.util.NoSuchElementException;
031
032import org.apache.http.Header;
033import org.apache.http.HeaderIterator;
034import org.apache.http.util.Args;
035
036/**
037 * Basic implementation of a {@link HeaderIterator}.
038 *
039 * @since 4.0
040 */
041public class BasicHeaderIterator implements HeaderIterator {
042
043    /**
044     * An array of headers to iterate over.
045     * Not all elements of this array are necessarily part of the iteration.
046     * This array will never be modified by the iterator.
047     * Derived implementations are expected to adhere to this restriction.
048     */
049    protected final Header[] allHeaders;
050
051
052    /**
053     * The position of the next header in {@link #allHeaders allHeaders}.
054     * Negative if the iteration is over.
055     */
056    protected int currentIndex;
057
058
059    /**
060     * The header name to filter by.
061     * {@code null} to iterate over all headers in the array.
062     */
063    protected String headerName;
064
065
066
067    /**
068     * Creates a new header iterator.
069     *
070     * @param headers   an array of headers over which to iterate
071     * @param name      the name of the headers over which to iterate, or
072     *                  {@code null} for any
073     */
074    public BasicHeaderIterator(final Header[] headers, final String name) {
075        super();
076        this.allHeaders = Args.notNull(headers, "Header array");
077        this.headerName = name;
078        this.currentIndex = findNext(-1);
079    }
080
081
082    /**
083     * Determines the index of the next header.
084     *
085     * @param pos      one less than the index to consider first,
086     *                  -1 to search for the first header
087     *
088     * @return  the index of the next header that matches the filter name,
089     *          or negative if there are no more headers
090     */
091    protected int findNext(final int pos) {
092        int from = pos;
093        if (from < -1) {
094            return -1;
095        }
096
097        final int to = this.allHeaders.length-1;
098        boolean found = false;
099        while (!found && (from < to)) {
100            from++;
101            found = filterHeader(from);
102        }
103        return found ? from : -1;
104    }
105
106
107    /**
108     * Checks whether a header is part of the iteration.
109     *
110     * @param index     the index of the header to check
111     *
112     * @return  {@code true} if the header should be part of the
113     *          iteration, {@code false} to skip
114     */
115    protected boolean filterHeader(final int index) {
116        return (this.headerName == null) ||
117            this.headerName.equalsIgnoreCase(this.allHeaders[index].getName());
118    }
119
120
121    // non-javadoc, see interface HeaderIterator
122    @Override
123    public boolean hasNext() {
124        return (this.currentIndex >= 0);
125    }
126
127
128    /**
129     * Obtains the next header from this iteration.
130     *
131     * @return  the next header in this iteration
132     *
133     * @throws NoSuchElementException   if there are no more headers
134     */
135    @Override
136    public Header nextHeader()
137        throws NoSuchElementException {
138
139        final int current = this.currentIndex;
140        if (current < 0) {
141            throw new NoSuchElementException("Iteration already finished.");
142        }
143
144        this.currentIndex = findNext(current);
145
146        return this.allHeaders[current];
147    }
148
149
150    /**
151     * Returns the next header.
152     * Same as {@link #nextHeader nextHeader}, but not type-safe.
153     *
154     * @return  the next header in this iteration
155     *
156     * @throws NoSuchElementException   if there are no more headers
157     */
158    @Override
159    public final Object next()
160        throws NoSuchElementException {
161        return nextHeader();
162    }
163
164
165    /**
166     * Removing headers is not supported.
167     *
168     * @throws UnsupportedOperationException    always
169     */
170    @Override
171    public void remove()
172        throws UnsupportedOperationException {
173
174        throw new UnsupportedOperationException
175            ("Removing headers is not supported.");
176    }
177}