package org.reldb.rel.v0.values; import java.io.PrintStream; import java.util.*; import org.reldb.rel.exceptions.*; import org.reldb.rel.v0.generator.Generator; import org.reldb.rel.v0.types.*; import org.reldb.rel.v0.vm.Context; public class ValueArray extends ValueAbstract implements TupleIteratable { private static final long serialVersionUID = 0; private ArrayList<ValueTuple> values; private ValueRelation relation; /** Create a new ARRAY. */ public ValueArray(Generator generator) { super(generator); values = new ArrayList<ValueTuple>(); relation = null; } /** Create a new ARRAY given an ArrayList. */ ValueArray(Generator generator, ArrayList<ValueTuple> values) { super(generator); this.values = values; relation = null; } /** Create a new ARRAY as a wrapper around a ValueRelation. */ ValueArray(Generator generator, ValueRelation relation) { super(generator); this.values = null; this.relation = relation; } private void convertToArray() { TupleIterator iterator = relation.iterator(); try { values = new ArrayList<ValueTuple>(); while (iterator.hasNext()) values.add(iterator.next()); relation = null; } finally { iterator.close(); } } public Value project(final AttributeMap map) { return new ValueArray(getGenerator()) { private final static long serialVersionUID = 0; public TupleIterator iterator() { return new TupleIterator() { TupleIterator tuples = ValueArray.this.iterator(); public boolean hasNext() { return tuples.hasNext(); } public ValueTuple next() { return (ValueTuple)tuples.next().project(map); } public void close() { tuples.close(); } }; } }; } public ValueRelation toRelation() { return new ValueRelation(getGenerator()) { private final static long serialVersionUID = 0; public int hashCode() { return 0; } public TupleIterator newIterator() { return new TupleIteratorUnique(ValueArray.this.iterator()); } }; } public String getTypeName() { return "ARRAY"; } public long getCount() { if (values == null) return relation.getCardinality(); else return values.size(); } private void checkIndexOutOfBounds(int index) { if (index >= values.size()) if (values.size() == 0) throw new ExceptionSemantic("RS0270: Array index " + index + " is out of bounds; array is empty."); else throw new ExceptionSemantic("RS0271: Array index " + index + " is out of bounds; highest index is " + (values.size() - 1) + "."); else if (index < 0) throw new ExceptionSemantic("RS0272: Array index " + index + " is out of bounds; it's less than 0."); } public Value get(int index) { if (values == null) convertToArray(); checkIndexOutOfBounds(index); return values.get(index); } public void set(int index, ValueTuple value) { if (values == null) convertToArray(); checkIndexOutOfBounds(index); values.set(index, value); } public void append(ValueTuple value) { if (values == null) convertToArray(); values.add(value); } public TupleIterator iterator() { if (values == null) return relation.iterator(); else return new TupleIterator() { Iterator<ValueTuple> iterator = values.iterator(); public boolean hasNext() { return iterator.hasNext(); } public ValueTuple next() { return (ValueTuple)iterator.next(); } public void close() { } }; } private void toStreamFromRelation(Context context, Type type, PrintStream p, int depth) { TypeTuple tupleType = ((TypeArray)type).getElementType(); Heading heading = tupleType.getHeading(); if (depth == 0) p.print("ARRAY " + heading + " {"); else p.print("ARRAY {"); long count = 0; TupleIterator iterator = relation.iterator(); try { while (iterator.hasNext()) { ValueTuple tuple = iterator.next(); if (count++ > 0) p.print(','); p.print("\n\t"); tuple.toStream(context, tupleType, p, depth + 1); } } finally { iterator.close(); } p.print("\n}"); } private void toStreamFromArray(Context context, Type type, PrintStream p, int depth) { TypeTuple elementType = ((TypeArray)type).getElementType(); Heading heading = ((TypeHeading)elementType).getHeading(); p.print("ARRAY " + heading + " {"); long count = 0; for (Value value: values) { if (count++ > 0) p.print(','); p.print("\n\t"); value.toStream(context, elementType, p, depth + 1); } p.print("\n}"); } /** Output this Value to a PrintStream. */ public void toStream(Context context, Type type, PrintStream p, int depth) { if (values == null) toStreamFromRelation(context, type, p, depth); else toStreamFromArray(context, type, p, depth); } public int hashCode() { int code = 0; for (Value value: values) code += value.hashCode(); return code; } public int compareTo(Value v) { throw new ExceptionSemantic("RS0273: ARRAY does not support comparison."); } public String toString() { String s = ""; s += "ARRAY {"; long count = 0; TupleIterator iterator = iterator(); try { while (iterator.hasNext()) { ValueTuple tuple = iterator.next(); if (count++ > 0) s += ", "; s += tuple.toString(); } } finally { iterator.close(); } s += "}"; return s; } @Override public TupleIteratable map(TupleMap map) { return new ValueArray(getGenerator()) { private final static long serialVersionUID = 0; public int hashCode() { return 0; } public TupleIterator iterator() { return new TupleIterator() { TupleIterator iterator = ValueArray.this.iterator(); public boolean hasNext() { return iterator.hasNext(); } public ValueTuple next() { return map.map(iterator.next()); } public void close() { iterator.close(); } }; } }; } @Override public Value sort(OrderMap map) { if (map.getMap().length == 0) return this; else { // TODO - refactor to re-use code in ValueRelation's sort(OrderMap map) // TODO - MEM - fix so that high-cardinality relations don't run out of RAM final ArrayList<ValueTuple> array = new ArrayList<ValueTuple>(); (new TupleIteration(iterator()) { public void process(ValueTuple tuple) { array.add(tuple); } }).run(); Collections.sort(array, new Sorter(map)); return new ValueArray(getGenerator(), array); } } /** Aggregate operator */ public Value sumInteger(final int attributeIndex) { TupleFold folder = new TupleFold(iterator(), attributeIndex) { public Value getIdentity() { return ValueInteger.select(getGenerator(), 0); } public Value fold(Value left, Value right) { return ValueInteger.select(getGenerator(), left.longValue() + right.longValue()); } }; folder.run(); return folder.getResult(); } /** Aggregate operator */ public Value sumRational(final int attributeIndex) { TupleFold folder = new TupleFold(iterator(), attributeIndex) { public Value getIdentity() { return ValueRational.select(getGenerator(), 0); } public Value fold(Value left, Value right) { return ValueRational.select(getGenerator(), left.doubleValue() + right.doubleValue()); } }; folder.run(); return folder.getResult(); } /** Aggregate operator */ public ValueRational avgInteger(int attributeIndex) { TupleFold folder = new TupleFold(iterator(), attributeIndex) { public Value getIdentity() { return ValueInteger.select(getGenerator(), 0); } public Value fold(Value left, Value right) { return ValueInteger.select(getGenerator(), left.longValue() + right.longValue()); } }; folder.run(); Value sum = folder.getResult(); if (folder.getCount() == 0) throw new ExceptionSemantic("RS0276: Result of AVG on no values is undefined."); else return (ValueRational)ValueRational.select(getGenerator(), sum.doubleValue() / (double)folder.getCount()); } /** Aggregate operator */ public ValueRational avgRational(int attributeIndex) { TupleFold folder = new TupleFold(iterator(), attributeIndex) { public Value getIdentity() { return ValueRational.select(getGenerator(), 0); } public Value fold(Value left, Value right) { return ValueRational.select(getGenerator(), left.doubleValue() + right.doubleValue()); } }; folder.run(); Value sum = folder.getResult(); if (folder.getCount() == 0) throw new ExceptionSemantic("RS0277: Result of AVG on no values is undefined."); else return (ValueRational)ValueRational.select(getGenerator(), sum.doubleValue() / (double)folder.getCount()); } /** Aggregate operator */ public Value max(int attributeIndex) { TupleFold folder = new TupleFoldFirstIsIdentity("Result of MAX on no values is undefined.", iterator(), attributeIndex) { public Value fold(Value left, Value right) { if (left.compareTo(right) > 0) return left; else return right; } }; folder.run(); return folder.getResult(); } /** Aggregate operator */ public Value min(int attributeIndex) { TupleFold folder = new TupleFoldFirstIsIdentity("Result of MIN on no values is undefined.", iterator(), attributeIndex) { public Value fold(Value left, Value right) { if (left.compareTo(right) < 0) return left; else return right; } }; folder.run(); return folder.getResult(); } /** Aggregate operator */ public ValueBoolean and(int attributeIndex) { TupleFold folder = new TupleFold(iterator(), attributeIndex) { public Value getIdentity() { return ValueBoolean.select(getGenerator(), true); } public Value fold(Value left, Value right) { return ValueBoolean.select(getGenerator(), left.booleanValue() & right.booleanValue()); } }; folder.run(); return (ValueBoolean)folder.getResult(); } /** Aggregate operator */ public ValueBoolean or(int attributeIndex) { TupleFold folder = new TupleFold(iterator(), attributeIndex) { public Value getIdentity() { return ValueBoolean.select(getGenerator(), false); } public Value fold(Value left, Value right) { return ValueBoolean.select(getGenerator(), left.booleanValue() | right.booleanValue()); } }; folder.run(); return (ValueBoolean)folder.getResult(); } /** Aggregate operator */ public ValueBoolean xor(int attributeIndex) { TupleFold folder = new TupleFold(iterator(), attributeIndex) { public Value getIdentity() { return ValueBoolean.select(getGenerator(), false); } public Value fold(Value left, Value right) { return ValueBoolean.select(getGenerator(), left.booleanValue() ^ right.booleanValue()); } }; folder.run(); return (ValueBoolean)folder.getResult(); } /** Aggregate operator */ public Value equiv(int attributeIndex) { TupleFold folder = new TupleFold(iterator(), attributeIndex) { public Value getIdentity() { return ValueBoolean.select(getGenerator(), true); } public Value fold(Value left, Value right) { return ValueBoolean.select(getGenerator(), left.booleanValue() == right.booleanValue()); } }; folder.run(); return (ValueBoolean)folder.getResult(); } /** Aggregate operator */ public ValueRelation union(int attributeIndex) { TupleFold folder = new TupleFold(iterator(), attributeIndex) { public Value fold(Value left, Value right) { return ((ValueRelation)left).union((ValueRelation)right); } @Override public Value getIdentity() { return new ValueRelationLiteral(getGenerator()); } }; folder.run(); return (ValueRelation)folder.getResult(); } /** Aggregate operator */ public Value xunion(int attributeIndex) { TupleFold folder = new TupleFold(iterator(), attributeIndex) { public Value fold(Value left, Value right) { return ((ValueRelation)left).xunion((ValueRelation)right); } public Value getIdentity() { return new ValueRelationLiteral(getGenerator()); } }; folder.run(); return (ValueRelation)folder.getResult(); } /** Aggregate operator */ public ValueRelation d_union(int attributeIndex) { TupleFold folder = new TupleFold(iterator(), attributeIndex) { public Value fold(Value left, Value right) { return ((ValueRelation)left).dunion((ValueRelation)right); } @Override public Value getIdentity() { return new ValueRelationLiteral(getGenerator()); } }; folder.run(); return (ValueRelation)folder.getResult(); } /** Aggregate operator */ public ValueRelation intersect(int attributeIndex) { TupleFold folder = new TupleFoldFirstIsIdentity("Result of INTERSECT on no values is undefined.", iterator(), attributeIndex) { public Value fold(Value left, Value right) { return ((ValueRelation)left).intersect((ValueRelation)right); } }; folder.run(); return (ValueRelation)folder.getResult(); } }