package org.reldb.rel.v0.types;
import java.util.*;
import org.reldb.rel.exceptions.*;
import org.reldb.rel.v0.generator.SelectAttributes;
public class Heading implements Comparable<Heading> {
private Vector<Attribute> attributes = new Vector<Attribute>();
/** Create a new Heading. */
public Heading() {
}
/** Create a new Heading based on an existing Heading. */
public Heading(Heading oldHeading) {
attributes.addAll(oldHeading.getAttributes());
}
/** Create a new Heading based on an existing TypeTuple. */
public Heading(TypeTuple tupleType) {
this(tupleType.getHeading());
}
/** Create a new Heading based on an existing TypeRelation. */
public Heading(TypeRelation relationType) {
this(relationType.getHeading());
}
/** Create a new Heading by the union (aka join) of this heading with another. Throw
* an exception if there are attributes in common. */
public Heading unionDisjoint(Heading rightHeading) {
Heading joinedHeading = new Heading(this);
for (Attribute rightAttribute: rightHeading.attributes) {
if (getIndexOf(rightAttribute.getName()) >= 0)
throw new ExceptionSemantic("RS0242: Attribute '" + rightAttribute.getName() + "' is found in both operands.");
joinedHeading.attributes.add(rightAttribute);
}
return joinedHeading;
}
/** Create a new Heading by the union (aka join) of this heading with another heading. Common attributes
* appear once in the result Heading. */
public Heading union(Heading rightHeading) {
Heading joinedHeading = new Heading(this);
for (Attribute rightAttribute: rightHeading.attributes) {
Attribute leftAttribute = getAttribute(rightAttribute.getName());
if (leftAttribute != null) {
if (!leftAttribute.getType().canAccept(rightAttribute.getType()))
throw new ExceptionSemantic("RS0243: An attribute named '" + rightAttribute.getName() + "' is found in both operands but differs in type.");
} else
joinedHeading.attributes.add(rightAttribute);
}
return joinedHeading;
}
/** Create a new Heading by intersecting this heading with another heading. */
public Heading intersect(Heading rightHeading) {
Heading intersectedHeading = new Heading();
for (Attribute leftAttribute: attributes) {
Attribute rightAttribute = rightHeading.getAttribute(leftAttribute.getName());
if (rightAttribute != null) {
if (!leftAttribute.getType().canAccept(rightAttribute.getType()))
throw new ExceptionSemantic("RS0244: An attribute named '" + rightAttribute.getName() + "' is found in both operands but differs in type.");
intersectedHeading.attributes.add(rightAttribute);
}
}
return intersectedHeading;
}
/** Create a new Heading by returning the left heading minus common attributes in the right heading. */
public Heading minus(Heading rightHeading) {
Heading minusHeading = new Heading();
for (Attribute leftAttribute: attributes) {
Attribute rightAttribute = rightHeading.getAttribute(leftAttribute.getName());
if (rightAttribute == null)
minusHeading.attributes.add(leftAttribute);
else if (!leftAttribute.getType().canAccept(rightAttribute.getType()))
throw new ExceptionSemantic("RS0245: An attribute named '" + rightAttribute.getName() + "' is found in both operands but differs in type.");
}
return minusHeading;
}
/** Project this Heading into a new Heading given an AttributeSelection. */
public Heading project(SelectAttributes selection) {
Heading heading;
if (selection.isAllBut()) {
heading = new Heading(this);
for (String name: selection.getNames())
heading.remove(name);
} else {
heading = new Heading();
for (String name: selection.getNames()) {
Attribute attribute = getAttribute(name);
if (attribute == null)
throw new ExceptionSemantic("RS0246: Attribute '" + name + "' not found.");
heading.attributes.add(attribute);
}
}
return heading;
}
/** Remove a given attribute. */
public void remove(String name) {
int index = getIndexOf(name);
if (index < 0)
throw new ExceptionSemantic("RS0247: Attribute '" + name + "' not found.");
attributes.remove(index);
}
/** Rename a given attribute. */
private void rename(Attribute oldAttribute, String newName) {
int index = getIndexOf(oldAttribute.getName());
if (index == -1)
throw new ExceptionSemantic("RS0248: Attribute '" + oldAttribute.getName() + "' not found.");
if (getAttribute(newName) != null)
throw new ExceptionSemantic("RS0249: Attribute '" + newName + "' has already been defined.");
attributes.set(index, new Attribute(newName, oldAttribute.getType()));
}
/** Rename an attribute. Return false if no match found. */
public boolean rename(String from, String to) {
Attribute oldAttribute = getAttribute(from);
if (oldAttribute == null)
return false;
rename(oldAttribute, to);
return true;
}
/** Rename all attributes with a given prefix. Return false if no match found. */
public boolean renamePrefix(String from, String to) {
boolean renamed = false;
for (Attribute attribute: attributes)
if (attribute.getName().startsWith(from)) {
rename(attribute, to + attribute.getName().substring(from.length()));
renamed = true;
}
return renamed;
}
/** Rename all attributes with a given suffix. Return false if no match found. */
public boolean renameSuffix(String from, String to) {
boolean renamed = false;
for (Attribute attribute: attributes)
if (attribute.getName().endsWith(from)) {
rename(attribute, attribute.getName().substring(0, attribute.getName().length() - from.length()) + to);
renamed = true;
}
return renamed;
}
public void add(String attributeName, Type attributeType) {
if (getAttribute(attributeName) != null)
throw new ExceptionSemantic("RS0250: Attribute '" + attributeName + "' has already been defined.");
attributes.add(new Attribute(attributeName, attributeType));
}
public void add(Attribute attribute) {
add(attribute.getName(), attribute.getType());
}
/** Return the attribute of a given name. Return null if not found. */
public Attribute getAttribute(String name) {
for (Attribute attribute: attributes)
if (attribute.getName().equals(name))
return attribute;
return null;
}
/** Return the index of a given attribute. Return -1 if not found. */
public int getIndexOf(String name) {
int index = 0;
for (Attribute attribute: attributes) {
if (attribute.getName().equals(name))
return index;
index++;
}
return -1;
}
public Vector<Attribute> getAttributes() {
return attributes;
}
/** Return true if this Heading can accept the given Heading. */
public boolean canAccept(Heading heading) {
if (getDegree() != heading.getDegree())
return false;
for (Attribute attribute: attributes) {
Attribute foreignAttribute = heading.getAttribute(attribute.getName());
if (foreignAttribute == null)
return false;
if (!attribute.getType().canAccept(foreignAttribute.getType()))
return false;
}
return true;
}
/** Return true if the source must be reformatted for this to receive it. */
public boolean requiresReformatOf(Heading source) {
// Type failure requires (at the very least!) a reformat.
if (!canAccept(source))
return true;
int i = 0;
for (Attribute attribute: attributes) {
if (attribute.getName().compareTo(source.attributes.get(i).getName()) != 0)
return true;
else if (attribute.getType().requiresReformatOf(source.attributes.get(i).getType()))
return true;
i++;
}
return false;
}
public Heading getMostSpecificCommonSupertype(Heading tupleHeading) {
if (getDegree() != tupleHeading.getDegree())
throw new ExceptionSemantic("RS0251: Heading degrees do not match.");
Heading newHeading = new Heading();
for (int i=0; i<attributes.size(); i++) {
Attribute attribute = attributes.get(i);
Type t1 = attribute.getType();
String name = attribute.getName();
Attribute attribute2 = tupleHeading.getAttribute(name);
if (attribute2 == null)
throw new ExceptionSemantic("RS0252: Attribute '" + name + "' not found.");
Type t2 = attribute2.getType();
if (!(t1 instanceof TypeAlpha))
throw new ExceptionFatal("RS0380: getMostSpecificCommonSupertype: heading1 element " + i + " not TypeAlpha or derivative.");
if (!(t2 instanceof TypeAlpha))
throw new ExceptionFatal("RS0381: getMostSpecificCommonSupertype: heading2 element " + i + " not TypeAlpha or derivative.");
TypeAlpha mostSpecificCommonSupertype = ((TypeAlpha)t1).getMostSpecificCommonSupertype((TypeAlpha)t2);
if (mostSpecificCommonSupertype == null || mostSpecificCommonSupertype.getSignature().equals("ALPHA"))
throw new ExceptionSemantic("RS0253: ALPHA is the most specific common supertype of " + t1.getSignature() + " and " + t2.getSignature() + ", but ALPHA can't be selected.");
newHeading.add(name, mostSpecificCommonSupertype);
}
return newHeading;
}
public void changeTypeOfAttribute(String attributeName, Type newType) {
for (int i=0; i<attributes.size(); i++) {
if (attributes.get(i).getName().equals(attributeName)) {
attributes.set(i, new Attribute(attributeName, newType));
return;
}
}
throw new ExceptionSemantic("RS0252: Attribute '" + attributeName + "' not found.");
}
public int compareTo(Heading heading) {
if (canAccept(heading))
return 0;
else
return 1;
}
public boolean equals(Object object) {
return (compareTo((Heading)object) == 0);
}
public int getDegree() {
return attributes.size();
}
// Get list of names from the ith attribute onward
public String getNameList(int i) {
String out = null;
int attributeNumber = 0;
for (Attribute attribute: attributes) {
if (attributeNumber >= i)
out = (out == null) ? attribute.getName() : (out + ", " + attribute.getName());
attributeNumber++;
}
return ((out == null) ? "" : out);
}
public String getNameList() {
return getNameList(0);
}
public String getSpecification() {
String out = null;
for (Attribute attribute: attributes)
out = (out == null) ? attribute.toString() : (out + ", " + attribute.toString());
return ((out == null) ? "" : out);
}
public String getSignature() {
return "{" + getSpecification() + "}";
}
public String toString() {
return getSignature();
}
public String getRandomFreeAttributeName() {
String uuid = UUID.randomUUID().toString();
while (getIndexOf(uuid) >= 0)
uuid = UUID.randomUUID().toString();
return uuid;
}
}