package scotch.symbol; import static org.apache.commons.lang.StringUtils.capitalize; import java.util.Optional; import scotch.symbol.descriptor.DataConstructorDescriptor; import scotch.symbol.descriptor.DataTypeDescriptor; import scotch.symbol.descriptor.TypeClassDescriptor; import scotch.symbol.type.TypeDescriptor; public abstract class SymbolEntry { public static ImmutableEntryBuilder immutableEntry(Symbol symbol) { return new ImmutableEntryBuilder(symbol); } public static SymbolEntry mutableEntry(Symbol symbol) { return new MutableEntry(symbol); } private SymbolEntry() { // intentionally empty } public abstract void defineDataConstructor(DataConstructorDescriptor dataConstructor); public abstract void defineDataType(DataTypeDescriptor dataTypeDescriptor); public abstract void defineOperator(Operator operator); public abstract void defineSignature(TypeDescriptor type); public abstract void defineValue(TypeDescriptor type, MethodSignature valueMethod); public Optional<DataConstructorDescriptor> getConstructor(Symbol symbol) { return getDataType().flatMap(dataType -> dataType.getConstructor(symbol)); } public abstract Optional<DataConstructorDescriptor> getDataConstructor(); public abstract Optional<DataTypeDescriptor> getDataType(); public abstract Optional<Symbol> getMemberOf(); public abstract Optional<Operator> getOperator(); public abstract Optional<TypeDescriptor> getSignature(); public abstract Symbol getSymbol(); public abstract Optional<TypeDescriptor> getTypeDescriptor(); public abstract Optional<TypeClassDescriptor> getTypeClass(); public abstract Optional<TypeDescriptor> getValue(); public abstract Optional<MethodSignature> getValueMethod(); public abstract boolean isDataConstructor(); public abstract boolean isMember(); public abstract boolean isOperator(); public abstract void redefineDataConstructor(DataConstructorDescriptor descriptor); public abstract void redefineDataType(DataTypeDescriptor descriptor); public abstract void redefineSignature(TypeDescriptor type); public abstract void redefineValue(TypeDescriptor type, MethodSignature valueMethod); public static final class ImmutableEntry extends SymbolEntry { private final Symbol symbol; private final Optional<TypeDescriptor> optionalValue; private final Optional<Operator> optionalOperator; private final Optional<TypeDescriptor> optionalTypeDescriptor; private final Optional<MethodSignature> optionalValueMethod; private final Optional<TypeClassDescriptor> optionalTypeClass; private final Optional<Symbol> optionalMemberOf; private final Optional<DataTypeDescriptor> optionalDataType; private final Optional<DataConstructorDescriptor> optionalDataConstructor; private ImmutableEntry(ImmutableEntryBuilder builder) { this.symbol = builder.symbol; optionalValue = builder.optionalValue; optionalOperator = builder.optionalOperator; optionalTypeDescriptor = builder.optionalTypeDescriptor; optionalValueMethod = builder.optionalValueMethod; optionalTypeClass = builder.optionalTypeClass; optionalMemberOf = builder.optionalMemberOf; optionalDataType = builder.dataTypeBuilder.map(DataTypeDescriptor.Builder::build); optionalDataConstructor = builder.dataConstructorBuilder.map(DataConstructorDescriptor.Builder::build); } @Override public void defineDataConstructor(DataConstructorDescriptor dataConstructor) { throw existingSymbol("data constructor"); } @Override public void defineDataType(DataTypeDescriptor dataTypeDescriptor) { throw existingSymbol("data type"); } @Override public void defineOperator(Operator operator) { throw existingSymbol("operator"); } @Override public void defineSignature(TypeDescriptor type) { throw existingSymbol("signature"); } @Override public void defineValue(TypeDescriptor type, MethodSignature valueMethod) { throw existingSymbol("value"); } @Override public Optional<DataConstructorDescriptor> getDataConstructor() { return optionalDataConstructor; } @Override public Optional<DataTypeDescriptor> getDataType() { return optionalDataType; } @Override public Optional<Symbol> getMemberOf() { return optionalMemberOf; } @Override public Optional<Operator> getOperator() { return optionalOperator; } @Override public Optional<TypeDescriptor> getSignature() { return optionalValue; } @Override public Symbol getSymbol() { return symbol; } @Override public Optional<TypeDescriptor> getTypeDescriptor() { return optionalTypeDescriptor; } @Override public Optional<TypeClassDescriptor> getTypeClass() { return optionalTypeClass; } @Override public Optional<TypeDescriptor> getValue() { return optionalValue; } @Override public Optional<MethodSignature> getValueMethod() { return optionalValueMethod; } @Override public boolean isDataConstructor() { return optionalDataConstructor.isPresent(); } @Override public boolean isMember() { return optionalMemberOf.isPresent(); } @Override public boolean isOperator() { return optionalOperator.isPresent(); } @Override public void redefineDataConstructor(DataConstructorDescriptor descriptor) { throw existingSymbol("data constructor"); } @Override public void redefineDataType(DataTypeDescriptor descriptor) { throw existingSymbol("data type"); } @Override public void redefineSignature(TypeDescriptor type) { throw existingSymbol("value"); } @Override public void redefineValue(TypeDescriptor type, MethodSignature valueMethod) { throw existingSymbol("value"); } private IllegalStateException existingSymbol(String kind) { return new IllegalStateException("Can't define " + kind + " for existing symbol " + symbol.quote()); } } public static final class ImmutableEntryBuilder { private final Symbol symbol; private Optional<TypeDescriptor> optionalValue = Optional.empty(); private Optional<Operator> optionalOperator = Optional.empty(); private Optional<TypeDescriptor> optionalTypeDescriptor = Optional.empty(); private Optional<TypeClassDescriptor> optionalTypeClass = Optional.empty(); private Optional<Symbol> optionalMemberOf = Optional.empty(); private Optional<DataTypeDescriptor.Builder> dataTypeBuilder = Optional.empty(); private Optional<MethodSignature> optionalValueMethod = Optional.empty(); private Optional<DataConstructorDescriptor.Builder> dataConstructorBuilder = Optional.empty(); private ImmutableEntryBuilder(Symbol symbol) { this.symbol = symbol; } public ImmutableEntry build() { return new ImmutableEntry(this); } public DataConstructorDescriptor.Builder dataConstructor(int ordinal, Symbol dataTypeDescriptor, String className) { if (!dataConstructorBuilder.isPresent()) { dataConstructorBuilder = Optional.of(DataConstructorDescriptor.builder(ordinal, dataTypeDescriptor, symbol, className)); } return dataConstructorBuilder.get(); } public DataTypeDescriptor.Builder dataType() { if (!dataTypeBuilder.isPresent()) { dataTypeBuilder = Optional.of(DataTypeDescriptor.builder(symbol)); } return dataTypeBuilder.get(); } public Symbol getSymbol() { return symbol; } public ImmutableEntryBuilder withMemberOf(Symbol memberOf) { optionalMemberOf = Optional.of(memberOf); return this; } public ImmutableEntryBuilder withOperator(Operator operator) { optionalOperator = Optional.of(operator); return this; } public ImmutableEntryBuilder withType(TypeDescriptor type) { optionalTypeDescriptor = Optional.of(type); return this; } public ImmutableEntryBuilder withTypeClass(TypeClassDescriptor typeClass) { optionalTypeClass = Optional.of(typeClass); return this; } public ImmutableEntryBuilder withValueType(TypeDescriptor type) { optionalValue = Optional.of(type); return this; } public ImmutableEntryBuilder withValueMethod(MethodSignature valueMethod) { optionalValueMethod = Optional.of(valueMethod); return this; } } public static final class MutableEntry extends SymbolEntry { private final Symbol symbol; private Optional<TypeDescriptor> optionalValue = Optional.empty(); private Optional<Operator> optionalOperator = Optional.empty(); private Optional<TypeDescriptor> optionalTypeDescriptor = Optional.empty(); private Optional<TypeDescriptor> optionalSignature = Optional.empty(); private Optional<TypeClassDescriptor> optionalTypeClass = Optional.empty(); private Optional<Symbol> optionalMemberOf = Optional.empty(); private Optional<DataTypeDescriptor> optionalDataType = Optional.empty(); private Optional<DataConstructorDescriptor> optionalDataConstructor = Optional.empty(); private Optional<MethodSignature> optionalValueMethod = Optional.empty(); private MutableEntry(Symbol symbol) { this.symbol = symbol; } @Override public void defineDataConstructor(DataConstructorDescriptor dataConstructor) { if (optionalDataConstructor.isPresent()) { throw alreadyDefined("data constructor"); } else { optionalDataConstructor = Optional.of(dataConstructor); } } @Override public void defineDataType(DataTypeDescriptor dataTypeDescriptor) { if (optionalDataType.isPresent()) { throw alreadyDefined("data type"); } else { optionalDataType = Optional.of(dataTypeDescriptor); } } @Override public void defineOperator(Operator operator) { if (optionalOperator.isPresent()) { throw alreadyDefined("operator"); } else { optionalOperator = Optional.of(operator); } } @Override public void defineSignature(TypeDescriptor type) { if (optionalValue.isPresent()) { throw alreadyDefined("value"); } else if (optionalSignature.isPresent()) { throw alreadyDefined("signature"); } else { optionalSignature = Optional.of(type); } } @Override public void defineValue(TypeDescriptor type, MethodSignature valueMethod) { if (optionalValue.isPresent()) { throw alreadyDefined("value"); } else { optionalValue = Optional.of(type); } } @Override public Optional<DataConstructorDescriptor> getDataConstructor() { return optionalDataConstructor; } @Override public Optional<DataTypeDescriptor> getDataType() { return optionalDataType; } @Override public Optional<Symbol> getMemberOf() { return optionalMemberOf; } @Override public Optional<Operator> getOperator() { return optionalOperator; } @Override public Optional<TypeDescriptor> getSignature() { return optionalSignature; } @Override public Symbol getSymbol() { return symbol; } @Override public Optional<TypeDescriptor> getTypeDescriptor() { return optionalTypeDescriptor; } @Override public Optional<TypeClassDescriptor> getTypeClass() { return optionalTypeClass; } @Override public Optional<TypeDescriptor> getValue() { return optionalValue; } @Override public Optional<MethodSignature> getValueMethod() { return optionalValueMethod; } @Override public boolean isDataConstructor() { return optionalDataConstructor.isPresent(); } @Override public boolean isMember() { return optionalMemberOf.isPresent(); } @Override public boolean isOperator() { return optionalOperator.isPresent(); } @Override public void redefineDataConstructor(DataConstructorDescriptor descriptor) { if (optionalDataConstructor.isPresent()) { optionalDataConstructor = Optional.of(descriptor); } else { throw new IllegalStateException("Can't redefine non-existent data constructor " + symbol.quote()); } } @Override public void redefineDataType(DataTypeDescriptor descriptor) { if (optionalDataType.isPresent()) { optionalDataType = Optional.of(descriptor); } else { throw new IllegalStateException("Can't redefine non-existent data type " + symbol.quote()); } } @Override public void redefineSignature(TypeDescriptor type) { if (optionalSignature.isPresent()) { optionalSignature = Optional.of(type); } else { throw new IllegalStateException("Can't redefine non-existent signature " + symbol.quote()); } } @Override public void redefineValue(TypeDescriptor type, MethodSignature valueMethod) { if (optionalValue.isPresent()) { optionalValue = Optional.of(type); optionalValueMethod = Optional.of(valueMethod); } else { throw new IllegalStateException("Can't redefine non-existent value " + symbol.quote()); } } private IllegalStateException alreadyDefined(String thing) { return new IllegalStateException(capitalize(thing) + " is already defined for " + symbol.quote()); } } }