package me.tomassetti.turin.parser.ast.relations;
import com.google.common.collect.ImmutableList;
import me.tomassetti.jvm.JvmNameUtils;
import me.tomassetti.turin.compiler.errorhandling.ErrorCollector;
import me.tomassetti.turin.definitions.TypeDefinition;
import me.tomassetti.turin.resolvers.SymbolResolver;
import me.tomassetti.turin.parser.ast.Node;
import me.tomassetti.turin.parser.ast.typeusage.TypeUsageNode;
import turin.relations.ManyToManyRelation;
import turin.relations.OneToManyRelation;
import turin.relations.OneToOneRelation;
import java.util.List;
import java.util.stream.Collectors;
public class RelationDefinition extends Node {
public List<RelationFieldDefinition> getFieldsApplicableTo(TypeDefinition typeDefinition, SymbolResolver resolver) {
return fields.stream().filter((f)->f.isApplicableTo(typeDefinition, resolver)).collect(Collectors.toList());
}
List<TypeUsageNode> getTypeParameters() {
return ImmutableList.of(
firstField().getType().copy(),
secondField().getType().copy()
);
}
public String getGeneratedClassQualifiedName() {
return contextName() + "." + CLASS_PREFIX + getName();
}
public String staticFieldDescriptor() {
switch (getRelationType()) {
case MANY_TO_MANY:
return JvmNameUtils.descriptor(ManyToManyRelation.class);
case ONE_TO_MANY:
return JvmNameUtils.descriptor(OneToManyRelation.class);
case ONE_TO_ONE:
return JvmNameUtils.descriptor(OneToOneRelation.class);
default:
throw new UnsupportedOperationException();
}
}
public String relationClassInternalName() {
switch (getRelationType()) {
case MANY_TO_MANY:
return JvmNameUtils.internalName(ManyToManyRelation.class);
case ONE_TO_MANY:
return JvmNameUtils.internalName(OneToManyRelation.class);
case ONE_TO_ONE:
return JvmNameUtils.internalName(OneToOneRelation.class);
default:
throw new UnsupportedOperationException();
}
}
public enum Type {
ONE_TO_ONE,
ONE_TO_MANY,
MANY_TO_MANY
}
public static final String CLASS_PREFIX = "Relation_";
private String name;
private List<RelationFieldDefinition> fields;
public String getName() {
return name;
}
public RelationFieldDefinition firstField() {
return fields.get(0);
}
public RelationFieldDefinition secondField() {
return fields.get(1);
}
public RelationFieldDefinition singleField() {
if (getRelationType() != Type.ONE_TO_MANY) {
throw new IllegalStateException();
}
if (fields.get(0).getCardinality() == RelationFieldDefinition.Cardinality.SINGLE) {
return fields.get(0);
} else {
return fields.get(1);
}
}
public RelationFieldDefinition manyField() {
if (getRelationType() != Type.ONE_TO_MANY) {
throw new IllegalStateException();
}
if (fields.get(0).getCardinality() == RelationFieldDefinition.Cardinality.MANY) {
return fields.get(0);
} else {
return fields.get(1);
}
}
public List<RelationFieldDefinition> getFields() {
return fields;
}
public RelationDefinition(String name, List<RelationFieldDefinition> fields) {
this.name = name;
this.fields = fields;
this.fields.forEach((f)->f.setParent(RelationDefinition.this));
}
@Override
protected boolean specificValidate(SymbolResolver resolver, ErrorCollector errorCollector) {
if (fields.size() != 2) {
errorCollector.recordSemanticError(getPosition(), "Each relation should have exactly 2 fields");
return false;
}
return super.specificValidate(resolver, errorCollector);
}
public Type getRelationType() {
int nMany = 0;
int nSingle = 0;
for (RelationFieldDefinition field : fields) {
if (field.getCardinality() == RelationFieldDefinition.Cardinality.SINGLE) {
nSingle++;
} else if (field.getCardinality() == RelationFieldDefinition.Cardinality.MANY) {
nMany++;
} else {
throw new UnsupportedOperationException();
}
}
if (nMany == 2 && nSingle == 0) {
return Type.MANY_TO_MANY;
} else if (nMany == 0 && nSingle == 2) {
return Type.ONE_TO_ONE;
} else if (nMany == 1 && nSingle == 1) {
return Type.ONE_TO_MANY;
} else {
throw new UnsupportedOperationException();
}
}
@Override
public Iterable<Node> getChildren() {
return ImmutableList.copyOf(fields);
}
}