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.impl.client;
029
030import java.net.URI;
031import java.util.AbstractList;
032import java.util.ArrayList;
033import java.util.HashSet;
034import java.util.Iterator;
035import java.util.List;
036import java.util.Set;
037
038/**
039 * This class represents a collection of {@link java.net.URI}s used
040 * as redirect locations.
041 *
042 * @since 4.0
043 */
044public class RedirectLocations extends AbstractList<Object> {
045
046    private final Set<URI> unique;
047    private final List<URI> all;
048
049    public RedirectLocations() {
050        super();
051        this.unique = new HashSet<URI>();
052        this.all = new ArrayList<URI>();
053    }
054
055    /**
056     * Test if the URI is present in the collection.
057     */
058    public boolean contains(final URI uri) {
059        return this.unique.contains(uri);
060    }
061
062    /**
063     * Adds a new URI to the collection.
064     */
065    public void add(final URI uri) {
066        this.unique.add(uri);
067        this.all.add(uri);
068    }
069
070    /**
071     * Removes a URI from the collection.
072     */
073    public boolean remove(final URI uri) {
074        final boolean removed = this.unique.remove(uri);
075        if (removed) {
076            final Iterator<URI> it = this.all.iterator();
077            while (it.hasNext()) {
078                final URI current = it.next();
079                if (current.equals(uri)) {
080                    it.remove();
081                }
082            }
083        }
084        return removed;
085    }
086
087    /**
088     * Returns all redirect {@link URI}s in the order they were added to the collection.
089     *
090     * @return list of all URIs
091     *
092     * @since 4.1
093     */
094    public List<URI> getAll() {
095        return new ArrayList<URI>(this.all);
096    }
097
098    /**
099     * Returns the URI at the specified position in this list.
100     *
101     * @param index
102     *            index of the location to return
103     * @return the URI at the specified position in this list
104     * @throws IndexOutOfBoundsException
105     *             if the index is out of range (
106     *             {@code index &lt; 0 || index &gt;= size()})
107     * @since 4.3
108     */
109    @Override
110    public URI get(final int index) {
111        return this.all.get(index);
112    }
113
114    /**
115     * Returns the number of elements in this list. If this list contains more
116     * than {@code Integer.MAX_VALUE} elements, returns
117     * {@code Integer.MAX_VALUE}.
118     *
119     * @return the number of elements in this list
120     * @since 4.3
121     */
122    @Override
123    public int size() {
124        return this.all.size();
125    }
126
127    /**
128     * Replaces the URI at the specified position in this list with the
129     * specified element (must be a URI).
130     *
131     * @param index
132     *            index of the element to replace
133     * @param element
134     *            URI to be stored at the specified position
135     * @return the URI previously at the specified position
136     * @throws UnsupportedOperationException
137     *             if the {@code set} operation is not supported by this list
138     * @throws ClassCastException
139     *             if the element is not a {@link URI}
140     * @throws NullPointerException
141     *             if the specified element is null and this list does not
142     *             permit null elements
143     * @throws IndexOutOfBoundsException
144     *             if the index is out of range (
145     *             {@code index &lt; 0 || index &gt;= size()})
146     * @since 4.3
147     */
148    @Override
149    public Object set(final int index, final Object element) {
150        final URI removed = this.all.set(index, (URI) element);
151        this.unique.remove(removed);
152        this.unique.add((URI) element);
153        if (this.all.size() != this.unique.size()) {
154            this.unique.addAll(this.all);
155        }
156        return removed;
157    }
158
159    /**
160     * Inserts the specified element at the specified position in this list
161     * (must be a URI). Shifts the URI currently at that position (if any) and
162     * any subsequent URIs to the right (adds one to their indices).
163     *
164     * @param index
165     *            index at which the specified element is to be inserted
166     * @param element
167     *            URI to be inserted
168     * @throws UnsupportedOperationException
169     *             if the {@code add} operation is not supported by this list
170     * @throws ClassCastException
171     *             if the element is not a {@link URI}
172     * @throws NullPointerException
173     *             if the specified element is null and this list does not
174     *             permit null elements
175     * @throws IndexOutOfBoundsException
176     *             if the index is out of range (
177     *             {@code index &lt; 0 || index &gt; size()})
178     * @since 4.3
179     */
180    @Override
181    public void add(final int index, final Object element) {
182        this.all.add(index, (URI) element);
183        this.unique.add((URI) element);
184    }
185
186    /**
187     * Removes the URI at the specified position in this list. Shifts any
188     * subsequent URIs to the left (subtracts one from their indices). Returns
189     * the URI that was removed from the list.
190     *
191     * @param index
192     *            the index of the URI to be removed
193     * @return the URI previously at the specified position
194     * @throws IndexOutOfBoundsException
195     *             if the index is out of range (
196     *             {@code index &lt; 0 || index &gt;= size()})
197     * @since 4.3
198     */
199    @Override
200    public URI remove(final int index) {
201        final URI removed = this.all.remove(index);
202        this.unique.remove(removed);
203        if (this.all.size() != this.unique.size()) {
204            this.unique.addAll(this.all);
205        }
206        return removed;
207    }
208
209    /**
210     * Returns {@code true} if this collection contains the specified element.
211     * More formally, returns {@code true} if and only if this collection
212     * contains at least one element {@code e} such that
213     * {@code (o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))}.
214     *
215     * @param o element whose presence in this collection is to be tested
216     * @return {@code true} if this collection contains the specified
217     *         element
218     */
219    @Override
220    public boolean contains(final Object o) {
221        return this.unique.contains(o);
222    }
223
224}