blob: a2b2fea20bbd1be0439355eb3cb0caee4748041f [file] [log] [blame]
package org.eclipse.equinox.internal.p2.core.helpers;
/*******************************************************************************
* Copyright (c) 2003, 2007 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import org.eclipse.osgi.util.ManifestElement;
import org.osgi.framework.BundleException;
/**
* Headers classes. This class implements a Dictionary that has
* the following behaviour:
* <ul>
* <li>put and remove clear throw UnsupportedOperationException.
* The Dictionary is thus read-only to others.
* <li>The String keys in the Dictionary are case-preserved,
* but the get operation is case-insensitive.
* </ul>
* TODO: This class is copied from org.eclipse.osgi due to access restrictions. We
* can't make this API so we need to find a place for it.
* @since 3.1
*/
public class Headers extends Dictionary implements Map {
private boolean readOnly = false;
private Object[] headers;
private Object[] values;
private int size = 0;
/**
* Create an empty Headers dictionary.
*
* @param initialCapacity The initial capacity of this Headers object.
*/
public Headers(int initialCapacity) {
super();
headers = new Object[initialCapacity];
values = new Object[initialCapacity];
}
/**
* Create a Headers dictionary from a Dictionary.
*
* @param values The initial dictionary for this Headers object.
* @exception IllegalArgumentException If a case-variant of the key is
* in the dictionary parameter.
*/
public Headers(Dictionary values) {
this(values.size());
/* initialize the headers and values */
Enumeration keys = values.keys();
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
set(key, values.get(key));
}
}
/**
* Case-preserved keys.
*/
public synchronized Enumeration keys() {
return new ArrayEnumeration(headers, size);
}
/**
* Values.
*/
public synchronized Enumeration elements() {
return new ArrayEnumeration(values, size);
}
private int getIndex(Object key) {
boolean stringKey = key instanceof String;
for (int i = 0; i < size; i++) {
if (stringKey && (headers[i] instanceof String)) {
if (((String) headers[i]).equalsIgnoreCase((String) key))
return i;
} else {
if (headers[i].equals(key))
return i;
}
}
return -1;
}
private Object remove(int remove) {
Object removed = values[remove];
for (int i = remove; i < size; i++) {
if (i == headers.length - 1) {
headers[i] = null;
values[i] = null;
} else {
headers[i] = headers[i + 1];
values[i] = values[i + 1];
}
}
if (remove < size)
size--;
return removed;
}
private void add(Object header, Object value) {
if (size == headers.length) {
// grow the arrays
Object[] newHeaders = new Object[headers.length + 10];
Object[] newValues = new Object[values.length + 10];
System.arraycopy(headers, 0, newHeaders, 0, headers.length);
System.arraycopy(values, 0, newValues, 0, values.length);
headers = newHeaders;
values = newValues;
}
headers[size] = header;
values[size] = value;
size++;
}
/**
* Support case-insensitivity for keys.
*
* @param key name.
*/
public synchronized Object get(Object key) {
int i = -1;
if ((i = getIndex(key)) != -1)
return values[i];
return null;
}
/**
* Set a header value or optionally replace it if it already exists.
*
* @param key Key name.
* @param value Value of the key or null to remove key.
* @param replace A value of true will allow a previous
* value of the key to be replaced. A value of false
* will cause an IllegalArgumentException to be thrown
* if a previous value of the key exists.
* @return the previous value to which the key was mapped,
* or null if the key did not have a previous mapping.
*
* @exception IllegalArgumentException If a case-variant of the key is
* already present.
* @since 3.2
*/
public synchronized Object set(Object key, Object value, boolean replace) {
if (readOnly)
throw new UnsupportedOperationException();
if (key instanceof String)
key = ((String) key).intern();
int i = getIndex(key);
if (value == null) { /* remove */
if (i != -1)
return remove(i);
} else { /* put */
if (i != -1) { /* duplicate key */
if (!replace)
throw new IllegalArgumentException("HEADER_DUPLICATE_KEY_EXCEPTION: " + key); //$NON-NLS-1$
Object oldVal = values[i];
values[i] = value;
return oldVal;
}
add(key, value);
}
return null;
}
/**
* Set a header value.
*
* @param key Key name.
* @param value Value of the key or null to remove key.
* @return the previous value to which the key was mapped,
* or null if the key did not have a previous mapping.
*
* @exception IllegalArgumentException If a case-variant of the key is
* already present.
*/
public synchronized Object set(Object key, Object value) {
return set(key, value, false);
}
public synchronized void setReadOnly() {
readOnly = true;
}
/**
* Returns the number of entries (distinct keys) in this dictionary.
*
* @return the number of keys in this dictionary.
*/
public synchronized int size() {
return size;
}
/**
* Tests if this dictionary maps no keys to value. The general contract
* for the <tt>isEmpty</tt> method is that the result is true if and only
* if this dictionary contains no entries.
*
* @return <code>true</code> if this dictionary maps no keys to values;
* <code>false</code> otherwise.
*/
public synchronized boolean isEmpty() {
return size == 0;
}
/**
* Always throws UnsupportedOperationException.
*
* @param key header name.
* @param value header value.
* @throws UnsupportedOperationException
*/
public synchronized Object put(Object key, Object value) {
if (readOnly)
throw new UnsupportedOperationException();
return set(key, value, true);
}
/**
* Always throws UnsupportedOperationException.
*
* @param key header name.
* @throws UnsupportedOperationException
*/
public Object remove(Object key) {
throw new UnsupportedOperationException();
}
public String toString() {
return (values.toString());
}
public static Headers parseManifest(InputStream in) throws BundleException {
Headers headers = new Headers(10);
try {
ManifestElement.parseBundleManifest(in, headers);
} catch (IOException e) {
throw new BundleException("Error reading bundle manifest", e);
}
headers.setReadOnly();
return headers;
}
class ArrayEnumeration implements Enumeration {
private Object[] array;
int cur = 0;
public ArrayEnumeration(Object[] array, int size) {
this.array = new Object[size];
System.arraycopy(array, 0, this.array, 0, this.array.length);
}
public boolean hasMoreElements() {
return cur < array.length;
}
public Object nextElement() {
return array[cur++];
}
}
public synchronized void clear() {
if (readOnly)
throw new UnsupportedOperationException();
}
public synchronized boolean containsKey(Object key) {
return getIndex(key) >= 0;
}
public boolean containsValue(Object var0) {
throw new UnsupportedOperationException();
}
public Set entrySet() {
throw new UnsupportedOperationException();
}
public Set keySet() {
throw new UnsupportedOperationException();
}
public void putAll(Map var0) {
throw new UnsupportedOperationException();
}
public Collection values() {
throw new UnsupportedOperationException();
}
}