
/*
 * Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
 */

package org.jsimpledb.core;

import com.google.common.base.Preconditions;

import org.jsimpledb.schema.CounterSchemaField;
import org.jsimpledb.schema.EnumSchemaField;
import org.jsimpledb.schema.ListSchemaField;
import org.jsimpledb.schema.MapSchemaField;
import org.jsimpledb.schema.ReferenceSchemaField;
import org.jsimpledb.schema.SchemaFieldSwitchAdapter;
import org.jsimpledb.schema.SetSchemaField;
import org.jsimpledb.schema.SimpleSchemaField;

/**
 * Builds {@link Field}s from {@link SchemaField}s.
 */
class FieldBuilder extends SchemaFieldSwitchAdapter<Field<?>> {

    final Schema schema;
    final FieldTypeRegistry fieldTypeRegistry;

    FieldBuilder(Schema schema, FieldTypeRegistry fieldTypeRegistry) {
        Preconditions.checkArgument(schema != null, "null schema");
        Preconditions.checkArgument(fieldTypeRegistry != null, "null fieldTypeRegistry");
        this.schema = schema;
        this.fieldTypeRegistry = fieldTypeRegistry;
    }

// SchemaFieldSwitchAdapter

    @Override
    public SetField<?> caseSetSchemaField(SetSchemaField field) {
        return this.buildSetField(field, (SimpleField<?>)field.getElementField().visit(this));
    }

    @Override
    public ListField<?> caseListSchemaField(ListSchemaField field) {
        return this.buildListField(field, (SimpleField<?>)field.getElementField().visit(this));
    }

    @Override
    public MapField<?, ?> caseMapSchemaField(MapSchemaField field) {
        return this.buildMapField(field,
          (SimpleField<?>)field.getKeyField().visit(this), (SimpleField<?>)field.getValueField().visit(this));
    }

    @Override
    public SimpleField<?> caseSimpleSchemaField(SimpleSchemaField field) {
        final String fieldTypeName = field.getType();
        final long signature = field.getEncodingSignature();
        final FieldType<?> fieldType = this.fieldTypeRegistry.getFieldType(fieldTypeName, signature);
        if (fieldType == null) {
            throw new IllegalArgumentException("unknown field type `" + fieldTypeName + "'"
              + (signature != 0 ? " with signature " + signature : "")
              + " for field `" + field.getName() + "' in " + this);
        }
        return this.buildSimpleField(field, field.getName(), fieldType);
    }

    @Override
    public SimpleField<?> caseReferenceSchemaField(ReferenceSchemaField field) {
        Preconditions.checkArgument(field.getEncodingSignature() == 0, "encoding signature must be zero");
        return new ReferenceField(field.getName(), field.getStorageId(), this.schema, field.getOnDelete(),
          field.isCascadeDelete(), field.isAllowDeleted(), field.isAllowDeletedSnapshot(), field.getObjectTypes());
    }

    @Override
    public EnumField caseEnumSchemaField(EnumSchemaField field) {
        Preconditions.checkArgument(field.getEncodingSignature() == 0, "encoding signature must be zero");
        return new EnumField(field.getName(), field.getStorageId(), this.schema, field.isIndexed(), field.getIdentifiers());
    }

    @Override
    public CounterField caseCounterSchemaField(CounterSchemaField field) {
        return new CounterField(field.getName(), field.getStorageId(), this.schema);
    }

// Internal methods

    // This method exists solely to bind the generic type parameters
    private <T> SimpleField<T> buildSimpleField(SimpleSchemaField field, String fieldName, FieldType<T> fieldType) {
        assert field.getEncodingSignature() == fieldType.getEncodingSignature();
        return new SimpleField<>(fieldName, field.getStorageId(), this.schema, fieldType, field.isIndexed());
    }

    // This method exists solely to bind the generic type parameters
    private <E> SetField<E> buildSetField(SetSchemaField field, SimpleField<E> elementField) {
        return new SetField<>(field.getName(), field.getStorageId(), this.schema, elementField);
    }

    // This method exists solely to bind the generic type parameters
    private <E> ListField<E> buildListField(ListSchemaField field, SimpleField<E> elementField) {
        return new ListField<>(field.getName(), field.getStorageId(), this.schema, elementField);
    }

    // This method exists solely to bind the generic type parameters
    private <K, V> MapField<K, V> buildMapField(MapSchemaField field, SimpleField<K> keyField, SimpleField<V> valueField) {
        return new MapField<>(field.getName(), field.getStorageId(), this.schema, keyField, valueField);
    }
}


@@ -2025,42 +2025,51 @@
     
-throw new IllegalArgumentException
+final StringBuilder buf = new StringBuilder
 (%22un
@@ -2109,16 +2109,18 @@
 me + %22'%22
+);
 %0A       
@@ -2124,19 +2124,18 @@
         
-  +
+if
  (signat
@@ -2146,113 +2146,883 @@
 != 0
- ? %22 with signature %22 + signature : %22%22)%0A              + %22 for field %60%22 + field.getName() + %22' in %22 + this
+)%0A                buf.append(%22 with signature %22).append(signature);%0A            buf.append(%22 for field %60%22).append(field.getName()).append('%5C'');%0A            boolean foundAny = false;%0A            for (FieldType%3C?%3E otherFieldType : this.fieldTypeRegistry.getAll()) %7B%0A                if (otherFieldType.getName().equals(fieldTypeName)) %7B%0A                    if (!foundAny) %7B%0A                        buf.append(%22 (note: field type(s) named %60%22).append(fieldTypeName)%0A                          .append(%22' exist but with different signature(s): %22);%0A                        foundAny = true;%0A                    %7D else%0A                        buf.append(%22, %22);%0A                    buf.append(otherFieldType.getEncodingSignature());%0A                %7D%0A            %7D%0A            if (foundAny)%0A                buf.append(')');%0A            throw new IllegalArgumentException(buf.toString()
 );%0A 

Fix issues with exception message when field type not found.