/* $Id$ */
package ibis.ipl;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Properties;
/**
* Container for the capabilities of an {@link ibis.ipl.Ibis Ibis} or a
* representation of a port type. These are represented as boolean capabilities,
* its presence indicating that a certain capability is present.
* There are a number of predefined capabilities, but Ibis implementations may
* add new ones.
* A <code>CapabilitySet</code> object is immutable.
*/
public class CapabilitySet {
private transient byte[] codedForm;
private final HashSet<String> capabilities = new HashSet<String>();
/**
* Constructs an empty capabilities object.
*/
public CapabilitySet() {
codedForm = computeCodedForm();
}
/**
* Creates a capability set with the specified values.
* @param values
* the specified values.
*/
public CapabilitySet(String... values) {
for (String value : values) {
capabilities.add(value.toLowerCase());
}
codedForm = computeCodedForm();
}
/**
* Creates a copy of a capability set.
* @param capabilitySet
* the capability set to copy.
*/
public CapabilitySet(CapabilitySet capabilitySet) {
capabilities.addAll(capabilitySet.capabilities);
codedForm = capabilitySet.codedForm;
}
/**
* Creates a capability set with the values specified in a
* Properties object.
* @param properties
* the specified values.
*/
@SuppressWarnings("unchecked")
protected CapabilitySet(Properties properties) {
for (Enumeration<String> e = (Enumeration<String>)properties.propertyNames(); e.hasMoreElements();) {
String name = e.nextElement();
String value = properties.getProperty(name);
if (value != null
&& (value.equals("1") || value.equals("on")
|| value.equals("") || value.equals("true")
|| value.equals("yes"))) {
capabilities.add(name.toLowerCase());
}
}
codedForm = computeCodedForm();
}
/**
* Creates a capability set from a serialized form.
* @param codedForm
* the serialized form, as produced by the {@link #toBytes()}
* method.
* @exception IOException
* is thrown in case of trouble.
*/
public CapabilitySet(byte[] codedForm) throws IOException {
this(codedForm, 0, codedForm.length);
}
/**
* Creates a capability set from a serialized form.
* @param codedForm
* contains the serialized form, as produced by the
* {@link #toBytes()} method.
* @param offset
* offset where input for this method starts.
* @param length
* length of input for this method.
* @exception IOException
* is thrown in case of trouble.
*/
public CapabilitySet(byte[] codedForm, int offset, int length)
throws IOException {
this(new DataInputStream(
new ByteArrayInputStream(codedForm, offset, length)));
}
/**
* Creates a capability set by reading it from the specified input stream.
* @param dis
* the input stream to read from.
* @exception IOException
* is thrown in case of trouble.
*/
public CapabilitySet(DataInput dis) throws IOException {
doRead(dis);
codedForm = computeCodedForm();
}
/**
* Returns the capability set represented as a byte array.
* @return
* the byte array.
*/
public byte[] toBytes() {
return codedForm.clone();
}
private byte[] computeCodedForm() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
doWrite(dos);
dos.close();
return bos.toByteArray();
} catch(Exception e) {
// Should not happen. Ignored.
return new byte[0];
}
}
/**
* Writes the capability set to the specified output stream.
* @param dataOutput
* the output stream to write to.
* @exception IOException
* is thrown in case of trouble.
*/
public void writeTo(DataOutput dataOutput) throws IOException {
dataOutput.write(codedForm);
}
/**
* Returns true if the specified capability is set.
* @param capability
* the specified capability.
* @return
* true if the capability is set.
*/
public boolean hasCapability(String capability) {
return capabilities.contains(capability.toLowerCase());
}
/**
* Matches the current capabilities with the capabilities
* supplied. We have a match if the current capabilities are a subset
* of the specified capabilities.
* @param capabilitySet
* the capabilities to be matched with.
* @return
* <code>true</code> if we have a match, <code>false</code>
* otherwise.
*/
public boolean matchCapabilities(CapabilitySet capabilitySet) {
for (String capability : capabilities) {
if (! capabilitySet.hasCapability(capability)) {
return false;
}
}
return true;
}
/**
* Returns the subset of capabilities that start with the specified
* prefix.
* @param prefix
* the specified prefix.
* @return
* the capabilities that start with the specified prefix.
*/
public CapabilitySet getCapabilitiesWithPrefix(String prefix) {
CapabilitySet result = new CapabilitySet();
for (String capability : capabilities) {
if (capability.startsWith(prefix)) {
result.capabilities.add(capability);
}
}
result.codedForm = computeCodedForm();
return result;
}
/**
* Matches the current capabilities with the capabilities
* supplied. Returns a capability set with the unmatched capabilities.
* @param capabilitySet
* the capabilities to be matched with.
* @return
* the capabilities that don't match.
*/
public CapabilitySet unmatchedCapabilities(CapabilitySet capabilitySet) {
CapabilitySet result = new CapabilitySet();
for (String capability : capabilities) {
if (! capabilitySet.hasCapability(capability)) {
result.capabilities.add(capability);
}
}
result.codedForm = computeCodedForm();
return result;
}
/**
* Computes and returns the number of entries in this capability set.
* @return
* the number of entries.
*/
public int size() {
return capabilities.size();
}
/**
* Reads and returns the capabilities from the specified file name, which is
* searched for in the classpath.
* @param name
* the file name.
* @return
* the capabilities from the specified file.
* @exception IOException
* is thrown when an IO error occurs.
*/
public static CapabilitySet load(String name) throws IOException {
InputStream in
= ClassLoader.getSystemClassLoader().getResourceAsStream(name);
if (in == null) {
throw new IOException("Could not open " + name);
}
return load(in);
}
/**
* Reads and returns the capabilities from the specified input stream.
* @param input
* the input stream.
* @return
* the capabilities from the specified input stream.
* @exception IOException
* is thrown when an IO error occurs.
*/
public static CapabilitySet load(InputStream input) throws IOException {
Properties properties = new Properties();
properties.load(input);
input.close();
return new CapabilitySet(properties);
}
/**
* Returns <code>true</code> if <code>other</code> represents the same
* capability set.
* @param other
* the object to compare with.
* @return
* <code>true</code> if equal, <code>false</code> otherwise.
*/
public boolean equals(Object other) {
if (other == null) {
return false;
}
if (!(other instanceof CapabilitySet)) {
return false;
}
CapabilitySet capabilitySet = (CapabilitySet) other;
return capabilities.equals(capabilitySet.capabilities);
}
/**
* Returns the hashcode of this capability set.
* @return
* the hashcode.
*/
public int hashCode() {
return capabilities.hashCode();
}
private void writeObject(ObjectOutputStream output) throws IOException {
doWrite(output);
}
private void doWrite(DataOutput dataOutput) throws IOException {
dataOutput.writeInt(capabilities.size());
for (String capability : capabilities) {
dataOutput.writeUTF(capability);
}
}
private void readObject(ObjectInputStream input) throws IOException {
doRead(input);
}
private void doRead(DataInput dataInput) throws IOException {
int size = dataInput.readInt();
for (int i = 0; i < size; i++) {
capabilities.add(dataInput.readUTF());
}
codedForm = computeCodedForm();
}
/**
* Returns a string representation of this capability.
* @return a string representation.
*/
public String toString() {
StringBuffer result = new StringBuffer();
String append = "";
for (String capability : capabilities) {
result.append(append);
result.append(capability);
append = " ";
}
return result.toString();
}
/**
* Returns all the capabilities in this capability set as a String
* array, one element per capability.
* @return
* the capabilities.
*/
public String[] getCapabilities() {
int size = capabilities.size();
String[] result = new String[size];
int i = 0;
for (String capability : capabilities) {
result[i] = capability;
i++;
}
return result;
}
}