198 lines
7.9 KiB
Java
198 lines
7.9 KiB
Java
|
package linda;
|
||
|
import java.io.ByteArrayInputStream;
|
||
|
import java.io.ByteArrayOutputStream;
|
||
|
import java.io.ObjectInputStream;
|
||
|
import java.io.ObjectOutputStream;
|
||
|
import java.io.Serializable;
|
||
|
import java.util.Iterator;
|
||
|
import java.util.LinkedList;
|
||
|
import java.util.StringTokenizer;
|
||
|
|
||
|
/** Representation of a tuple.
|
||
|
* @author philippe.queinnec@enseeiht.fr
|
||
|
*/
|
||
|
public class Tuple extends LinkedList<Serializable> implements Serializable {
|
||
|
|
||
|
private static final long serialVersionUID = 1L;
|
||
|
|
||
|
/** Creates a new tuple.
|
||
|
* Example :
|
||
|
* new Tuple(4, 5, "foo", true) -> [ 4 5 "foo" true ]
|
||
|
* new Tuple(4, Integer.class, "foo".getclass(), Boolean.class) -> [ 4 ?Integer ?String ?Boolean ]
|
||
|
*/
|
||
|
public Tuple(Serializable... components) {
|
||
|
for (Serializable c : components) {
|
||
|
this.add(c);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static boolean matches(Serializable thiscomponent, Serializable templatecomponent) {
|
||
|
if (templatecomponent instanceof Tuple) {
|
||
|
if (! (thiscomponent instanceof Tuple))
|
||
|
return false;
|
||
|
else
|
||
|
return ((Tuple)thiscomponent).matches((Tuple)templatecomponent);
|
||
|
} else if (templatecomponent instanceof Class) {
|
||
|
if (thiscomponent instanceof Class)
|
||
|
return ((Class<?>)templatecomponent).isAssignableFrom((Class<?>)thiscomponent);
|
||
|
else
|
||
|
return ((Class<?>) templatecomponent).isInstance(thiscomponent);
|
||
|
} else {
|
||
|
return thiscomponent.equals(templatecomponent);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/** Returns true if this tuple matches the given template.
|
||
|
* Matching rules : a tuple matches a template if all their components match two by two.
|
||
|
* Two components match :
|
||
|
* - if they are both values and are equals;
|
||
|
* - if the template component is a class/interface, and the tuple component is an instance/implementation of this class/interface (Class.isInstance);
|
||
|
* - if the template component is a class/interface, and the tuple component is a subclass/subinterface of this class/interface (Class.isAsssignableFrom);
|
||
|
* - recursively if both are tuples.
|
||
|
*
|
||
|
* Examples:
|
||
|
* [ 3 5 "foo" ] matches [ 3 5 "foo" ], [ ?Integer 5 "foo" ], [ ?Integer ?Integer ?String ]
|
||
|
* [ 3 ?Integer [ 6 7 ] [ 7 8 ] ] matches [ ?Integer ?Integer [ ?Integer 7 ] ?Tuple ], [3 ?Integer ?Tuple ?Tuple ]
|
||
|
*
|
||
|
* @param template the template which this tuple is compared to.
|
||
|
*/
|
||
|
public boolean matches(Tuple template) {
|
||
|
if (this.size() != template.size())
|
||
|
return false;
|
||
|
Iterator<Serializable> itthis = this.iterator();
|
||
|
Iterator<Serializable> itmotif = template.iterator();
|
||
|
while (itthis.hasNext()) {
|
||
|
Serializable othis = itthis.next();
|
||
|
Serializable omotif = itmotif.next();
|
||
|
if (! matches(othis, omotif))
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/** Returns true if this tuple (seen as a template) contains <code>t</code>.
|
||
|
* This is the reverse of {@link #matches(Tuple)}. */
|
||
|
public boolean contains(Tuple t) {
|
||
|
return t.matches(this);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a deep copy of the tuple.
|
||
|
* @return a deep copy of this object
|
||
|
*/
|
||
|
/* Les éléments types sont représentés par des instances de Class, qui n'est pas cloneable.
|
||
|
* Le plus simple de passer par une sérialisation/desérialisation ce qui marche pour toutes les classes qui implantent serializable.
|
||
|
*/
|
||
|
public Tuple deepclone() {
|
||
|
Tuple copy = null;
|
||
|
try {
|
||
|
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||
|
ObjectOutputStream out = new ObjectOutputStream (buf);
|
||
|
out.writeObject (this);
|
||
|
ObjectInputStream in = new ObjectInputStream (new ByteArrayInputStream (buf.toByteArray()));
|
||
|
copy = (Tuple) in.readObject();
|
||
|
} catch (Exception e) {
|
||
|
e.printStackTrace();
|
||
|
}
|
||
|
return copy;
|
||
|
}
|
||
|
|
||
|
/** Returns a string representation of this tuple.
|
||
|
* @return a string representation of this tuple.
|
||
|
*/
|
||
|
public String toString() {
|
||
|
StringBuilder sb = new StringBuilder();
|
||
|
sb.append("[");
|
||
|
for (Object o : this) {
|
||
|
if (o instanceof Class) {
|
||
|
sb.append(" ?" + ((Class<?>)o).getName());
|
||
|
} else if (o instanceof String) {
|
||
|
sb.append(" \"" + o + "\"");
|
||
|
} else if (o instanceof Character) {
|
||
|
sb.append(" '" + o + "'");
|
||
|
} else {
|
||
|
sb.append(" " + o.toString());
|
||
|
}
|
||
|
}
|
||
|
sb.append(" ]");
|
||
|
return sb.toString();
|
||
|
}
|
||
|
|
||
|
/** Parses a sequence of words to form a tuple.
|
||
|
* The initial [ must be missing.
|
||
|
* @param stk
|
||
|
* @return
|
||
|
* @throws TupleFormatException
|
||
|
*/
|
||
|
private static Tuple valueOf(StringTokenizer stk) throws TupleFormatException {
|
||
|
Tuple res = new Tuple();
|
||
|
while (stk.hasMoreTokens()) {
|
||
|
String token = stk.nextToken();
|
||
|
if (token.equals("]"))
|
||
|
return res; // BEWARE
|
||
|
if (token.startsWith("\"") && token.endsWith("\"")) {
|
||
|
String val = token.substring(1, token.length()-1);
|
||
|
res.add(val);
|
||
|
} else if (token.startsWith("'") && token.endsWith("'") && (token.length() > 2)) {
|
||
|
res.add(Character.valueOf(token.charAt(1)));
|
||
|
} else if (token.startsWith("?")) {
|
||
|
String classname = token.substring(1);
|
||
|
Class<?> c = null;
|
||
|
final String[] prefixes = { "", "java.lang.", "linda." };
|
||
|
for (String prefix : prefixes) {
|
||
|
try {
|
||
|
c = Class.forName(prefix + classname);
|
||
|
break; // oh !
|
||
|
} catch (ClassNotFoundException e) {
|
||
|
// ignore and try next prefix
|
||
|
}
|
||
|
}
|
||
|
if (c != null)
|
||
|
res.add(c);
|
||
|
else
|
||
|
throw new TupleFormatException("Unknown class ?"+classname);
|
||
|
} else if ("-0123456789".indexOf(token.charAt(0)) != -1) {
|
||
|
int val;
|
||
|
try {
|
||
|
val = Integer.valueOf(token);
|
||
|
} catch (NumberFormatException e) {
|
||
|
throw new TupleFormatException("NumberFormatException on '"+token+"'");
|
||
|
}
|
||
|
res.add(val);
|
||
|
} else if (token.equals("true")) {
|
||
|
res.add(true);
|
||
|
} else if (token.equals("false")) {
|
||
|
res.add(false);
|
||
|
} else if (token.equals("[")) {
|
||
|
Tuple val = valueOf(stk); // yeepi!
|
||
|
res.add(val);
|
||
|
} else {
|
||
|
throw new TupleFormatException("Unhandled chars: '"+token+"'");
|
||
|
}
|
||
|
}
|
||
|
throw new TupleFormatException("Missing closing ']'");
|
||
|
}
|
||
|
|
||
|
/** Returns a Tuple with a value represented by the specified String.
|
||
|
* Known values: integer (45, -67), boolean (true, false), string ("toto"), classname (?Integer, ?java.awt.Rectangle), recursive tuple.
|
||
|
* Examples: [ 3 4 ], [ ?Integer "toto" true 78 ?Boolean ], [ ?Integer ?Tuple ], [ [ true 78 ] [ 3 4 [ 5 6 ] 7 ] ]
|
||
|
* For these components, the parsable strings are identical to the printed strings.
|
||
|
* Note: do not expect the parser to be resilient to arbitrary strings.
|
||
|
*
|
||
|
* @param s the string to be parsed.
|
||
|
* @return a Tuple object holding the value represented by the string argument.
|
||
|
* @throws TupleFormatException
|
||
|
*/
|
||
|
public static Tuple valueOf(String s) throws TupleFormatException {
|
||
|
StringTokenizer stk = new StringTokenizer(s);
|
||
|
if (! stk.hasMoreTokens() || !stk.nextToken().equals("["))
|
||
|
throw new TupleFormatException("Missing initial '['");
|
||
|
Tuple res = valueOf(stk);
|
||
|
if (stk.hasMoreTokens())
|
||
|
throw new TupleFormatException("Trailing chars after ']'");
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
}
|