package pt.ist.fenixframework.dml;
import java.util.*;
import java.io.Serializable;
import java.net.URL;
public class DomainModel implements Serializable {
protected Map<String, ValueType> valueTypes = new HashMap<String, ValueType>();
protected Map<String, DomainEntity> external = new HashMap<String, DomainEntity>();
protected Map<String, DomainClass> classes = new HashMap<String, DomainClass>();
protected Map<String, DomainRelation> relations = new HashMap<String, DomainRelation>();
protected Map<String, List<AnnotatedSlot>> annotatedSlots = new HashMap<String, List<AnnotatedSlot>>();
private boolean finalized = false;
public DomainModel() {
initializeBuiltinValueTypes();
initializeBuiltinEntities();
}
private static String[] NON_NULLABLE_TYPES = { "boolean", "byte", "char", "short", "int", "float", "long", "double" };
public static boolean isNullableType(ValueType vt) {
String vtFullName = vt.getFullname();
for (String nonNullableType : NON_NULLABLE_TYPES) {
if (nonNullableType.equals(vtFullName)) {
return false;
}
}
return true;
}
public static boolean isNullableTypeFullName(String vtFullName) {
for (String nonNullableType : NON_NULLABLE_TYPES) {
if (nonNullableType.equals(vtFullName)) {
return false;
}
}
return true;
}
private static String[][] builtinValueTypes = { /* { fullname, alias } */
// primitive types
{ "boolean", "boolean" },
{ "byte", "byte" },
{ "char", "char" },
{ "short", "short" },
{ "int", "int" },
{ "float", "float" },
{ "long", "long" },
{ "double", "double" },
// their wrappers
{ "java.lang.Boolean", "Boolean" },
{ "java.lang.Byte", "Byte" },
{ "java.lang.Character", "Character" },
{ "java.lang.Short", "Short" },
{ "java.lang.Integer", "Integer" },
{ "java.lang.Float", "Float" },
{ "java.lang.Long", "Long" },
{ "java.lang.Double", "Double" },
// String is, of course, essential
{ "java.lang.String", "String" },
// we need something binary, also
{ "byte[]", "bytearray" },
// JodaTime types
{ "org.joda.time.DateTime", "DateTime" },
{ "org.joda.time.LocalDate", "LocalDate" },
{ "org.joda.time.LocalTime", "LocalTime" },
{ "org.joda.time.Partial", "Partial" },
// also anything Serializable is acceptable
{ "java.io.Serializable", "Serializable" }
};
protected void initializeBuiltinValueTypes() {
for (String[] valueType : builtinValueTypes) {
newValueType(valueType[1], valueType[0]);
}
// // primitive types
// newValueType("boolean", "boolean");
// newValueType("byte", "byte");
// newValueType("char", "char");
// newValueType("short", "short");
// newValueType("int", "int");
// newValueType("float", "float");
// newValueType("long", "long");
// newValueType("double", "double");
// // their wrappers
// newValueType("Boolean", "java.lang.Boolean");
// newValueType("Byte", "java.lang.Byte");
// newValueType("Character", "java.lang.Character");
// newValueType("Short", "java.lang.Short");
// newValueType("Integer", "java.lang.Integer");
// newValueType("Float", "java.lang.Float");
// newValueType("Long", "java.lang.Long");
// newValueType("Double", "java.lang.Double");
// // String is, of course, essential
// newValueType("String", "java.lang.String");
// // we need something binary, also
// newValueType("bytearray", "byte[]");
// // JodaTime types
// newValueType("DateTime", "org.joda.time.DateTime");
// newValueType("LocalDate", "org.joda.time.LocalDate");
// newValueType("LocalTime", "org.joda.time.LocalTime");
// newValueType("Partial", "org.joda.time.Partial");
// // also anything Serializable is acceptable
// newValueType("Serializable", "java.io.Serializable");
}
public static boolean isBuiltinValueTypeFullName(String name) {
for (String[] valueType : builtinValueTypes) {
if (valueType[0].equals(name)) {
return true;
}
}
return false;
}
private static String[][] builtinEntities = { /* { fullname, alias } */
{ "pt.ist.fenixframework.DomainObject", "DomainObject" },
{ "pt.ist.fenixframework.core.AbstractDomainObject", "AbstractDomainObject" }
};
protected void initializeBuiltinEntities() {
for (String[] entity : builtinEntities) {
addExternalEntity(null, entity[0], entity[1]);
}
}
protected boolean isBuiltinEntity(String name) {
for (String[] entity : builtinEntities) {
if (entity[0].equals(name) || entity[1].equals(name)) {
return true;
}
}
return false;
}
public DomainEntity findClassOrExternal(String name) {
DomainEntity domClass = findClass(name);
if (domClass == null) {
domClass = external.get(name);
}
return domClass;
}
public DomainClass findClass(String name) {
return classes.get(name);
}
public DomainRelation findRelation(String name) {
return relations.get(name);
}
public void addClass(DomainClass domClass) {
checkNotFinalized();
checkNameUnique(domClass.getFullName());
classes.put(domClass.getFullName(), domClass);
}
public void addRelation(DomainRelation domRelation) {
checkNotFinalized();
checkNameUnique(domRelation.getFullName());
relations.put(domRelation.getFullName(), domRelation);
}
public void addExternalEntity(URL sourceFile, String name) {
addExternalEntity(sourceFile, name, name);
}
public void addExternalEntity(URL sourceFile, String name, String aliasName) {
if (aliasName == null) {
aliasName = name;
}
checkNotFinalized();
DomainExternalEntity ent = new DomainExternalEntity(sourceFile, name);
external.put(aliasName, ent);
if (!aliasName.equals(name)) {
external.put(name, ent);
}
}
public Iterator<DomainClass> getClasses() {
return classes.values().iterator();
}
public Collection<DomainClass> getDomainClasses() {
return classes.values();
}
public Iterator<DomainRelation> getRelations() {
return relations.values().iterator();
}
public Collection<DomainRelation> getDomainRelations() {
return relations.values();
}
public Map<String, List<AnnotatedSlot>> getAnnotatedSlots() {
return annotatedSlots;
}
public void newValueType(String domainName, String fullName) {
ValueType valueType = new PlainValueType(fullName);
newValueType(domainName, valueType);
}
public void newValueType(String domainName, ValueType valueType) {
if (domainName == null) {
domainName = valueType.getFullname();
}
valueType.getBaseType().setDomainName(domainName);
checkValueTypeName(domainName);
valueTypes.put(domainName, valueType);
}
public void newEnumType(String domainName, String fullName) {
if (domainName == null) {
domainName = fullName;
}
checkValueTypeName(domainName);
valueTypes.put(domainName, new EnumValueType(domainName, fullName));
}
public Collection<ValueType> getAllValueTypes() {
return valueTypes.values();
}
public ValueType findValueType(String name) {
return valueTypes.get(name);
}
public boolean isEnumType(String valueTypeName) {
ValueType vt = findValueType(valueTypeName);
return ((vt != null) && vt.isEnum());
}
public void finalizeDomain() {
finalizeDomain(false);
}
public void finalizeDomain(boolean checkForMissingExternals) {
// go through each of the relations and add their slots to the
// corresponding classes...
for (DomainRelation rel : relations.values()) {
List<Role> roles = rel.getRoles();
int numRoles = roles.size();
if (numRoles != 2) {
if (numRoles > 2) {
throw new RuntimeException("Can't handle with more than two roles yet!");
}
}
Role r0 = roles.get(0);
Role r1 = roles.get(1);
r0.getType().addRoleSlot(r1);
r1.getType().addRoleSlot(r0);
}
checkForRepeatedSlots();
registerAnnotatedSlots();
if (checkForMissingExternals) {
for (String externalName : external.keySet()) {
if (!isBuiltinEntity(externalName) && !classes.containsKey(externalName)) {
throw new RuntimeException(externalName
+ " was defined as an external entity but there is no concrete definition of it!");
}
}
}
finalized = true;
}
private void registerAnnotatedSlots() {
for (DomainClass domClass : classes.values()) {
for (Slot slot : domClass.getSlotsList()) {
for (Annotation ann : slot.getAnnotations()) {
List<AnnotatedSlot> annotatedSlotsList = this.annotatedSlots.get(ann.getName());
if (annotatedSlotsList == null) {
annotatedSlotsList = new ArrayList<AnnotatedSlot>();
this.annotatedSlots.put(ann.getName(), annotatedSlotsList);
}
annotatedSlotsList.add(new AnnotatedSlot(domClass, slot));
}
}
}
}
protected void checkForRepeatedSlots() {
for (DomainClass domClass : classes.values()) {
DomainEntity superDomClass = domClass.getSuperclass();
if (superDomClass != null) {
for (Slot slot : domClass.getSlotsList()) {
if (superDomClass.findSlot(slot.getName()) != null) {
System.err.printf("WARNING: Slot named '%s' in class '%s' already exists in a superclass\n", slot
.getName(), domClass.getName());
}
if (superDomClass.findRoleSlot(slot.getName()) != null) {
System.err.printf("WARNING: Slot named '%s' in class '%s' already exists in a superclass as role slot\n",
slot.getName(), domClass.getName());
}
}
for (Role role : domClass.getRoleSlotsList()) {
if (superDomClass.findSlot(role.getName()) != null) {
System.err.printf(
"WARNING: Role slot named '%s' in class '%s' already exists in a superclass as a slot\n", role
.getName(), domClass.getName());
}
if (superDomClass.findRoleSlot(role.getName()) != null) {
System.err.printf(
"WARNING: Role slot named '%s' in class '%s' already exists in a superclass as role slot\n", role
.getName(), domClass.getName());
}
}
}
}
}
public String toString() {
return "{ classes = " + classes + ", relations = " + relations + " }";
}
private void checkNotFinalized() {
if (finalized) {
throw new RuntimeException("Cannot change Domain after finalization");
}
}
private void checkValueTypeName(String name) {
if (valueTypes.containsKey(name)) {
throw new RuntimeException("Duplicate name for value type: " + name);
}
}
private void checkNameUnique(String name) {
if (classes.containsKey(name) || relations.containsKey(name)) {
throw new RuntimeException("Duplicate name: " + name);
}
}
}