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.List; 031import java.util.NoSuchElementException; 032 033import org.apache.http.Header; 034import org.apache.http.HeaderIterator; 035import org.apache.http.util.Args; 036import org.apache.http.util.Asserts; 037 038/** 039 * Implementation of a {@link HeaderIterator} based on a {@link List}. 040 * For use by {@link HeaderGroup}. 041 * 042 * @since 4.0 043 */ 044public class BasicListHeaderIterator implements HeaderIterator { 045 046 /** 047 * A list of headers to iterate over. 048 * Not all elements of this array are necessarily part of the iteration. 049 */ 050 protected final List<Header> allHeaders; 051 052 053 /** 054 * The position of the next header in {@link #allHeaders allHeaders}. 055 * Negative if the iteration is over. 056 */ 057 protected int currentIndex; 058 059 060 /** 061 * The position of the last returned header. 062 * Negative if none has been returned so far. 063 */ 064 protected int lastIndex; 065 066 067 /** 068 * The header name to filter by. 069 * {@code null} to iterate over all headers in the array. 070 */ 071 protected String headerName; 072 073 074 075 /** 076 * Creates a new header iterator. 077 * 078 * @param headers a list of headers over which to iterate 079 * @param name the name of the headers over which to iterate, or 080 * {@code null} for any 081 */ 082 public BasicListHeaderIterator(final List<Header> headers, final String name) { 083 super(); 084 this.allHeaders = Args.notNull(headers, "Header list"); 085 this.headerName = name; 086 this.currentIndex = findNext(-1); 087 this.lastIndex = -1; 088 } 089 090 091 /** 092 * Determines the index of the next header. 093 * 094 * @param pos one less than the index to consider first, 095 * -1 to search for the first header 096 * 097 * @return the index of the next header that matches the filter name, 098 * or negative if there are no more headers 099 */ 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}