/**
 * Copyright 2013 Cloudera Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.kitesdk.data.hbase.avro;

import java.util.ArrayList;
import java.util.List;

import org.apache.avro.Schema;
import org.apache.avro.Schema.Field;

import org.kitesdk.data.ValidationException;
import org.kitesdk.data.spi.FieldPartitioner;
import org.kitesdk.data.PartitionStrategy;
import org.kitesdk.data.hbase.impl.KeySchema;
import org.kitesdk.data.spi.SchemaUtil;
import org.kitesdk.data.spi.partition.IdentityFieldPartitioner;

/**
 * A KeySchema implementation powered by Avro.
 */
public class AvroKeySchema extends KeySchema {

  private final Schema schema;

  public AvroKeySchema(Schema entitySchema,
      PartitionStrategy partitionStrategy) {
    super(null, partitionStrategy);
    List<Field> fieldsPartOfKey = new ArrayList<Field>();
    for (FieldPartitioner<?, ?> fieldPartitioner : partitionStrategy.getFieldPartitioners()) {
      Schema fieldSchema;
      if (fieldPartitioner instanceof IdentityFieldPartitioner) {
        // copy the schema directly from the entity to preserve annotations
        fieldSchema = entitySchema
            .getField(fieldPartitioner.getSourceName()).schema();
      } else {
        Class<?> fieldType = SchemaUtil
            .getPartitionType(fieldPartitioner, entitySchema);
        if (fieldType == Integer.class) {
          fieldSchema = Schema.create(Schema.Type.INT);
        } else if (fieldType == Long.class) {
          fieldSchema = Schema.create(Schema.Type.LONG);
        } else if (fieldType == String.class) {
          fieldSchema = Schema.create(Schema.Type.STRING);
        } else {
          throw new ValidationException(
              "Cannot encode partition " + fieldPartitioner.getName() +
                  " with type " + fieldPartitioner.getSourceType()
          );
        }
      }
      fieldsPartOfKey.add(new Field(fieldPartitioner.getSourceName(), fieldSchema, null, null));
    }
    this.schema = Schema.createRecord(fieldsPartOfKey);
  }

  @Override
  public boolean compatible(KeySchema keySchema) {
    List<FieldPartitioner> fieldPartitioners = getPartitionStrategy()
        .getFieldPartitioners();
    List<FieldPartitioner> otherFieldPartitioners = keySchema
        .getPartitionStrategy().getFieldPartitioners();
    if (fieldPartitioners.size() != otherFieldPartitioners.size()) {
      return false;
    }
    return AvroUtils.avroSchemaTypesEqual(schema, ((AvroKeySchema)keySchema).schema);
  }

  @Override
  public String getRawSchema() {
    return getAvroSchema().toString();
  }

  /**
   * Get the Avro Schema that underlies this KeySchema implementation.
   * 
   * @return The Avro Schema
   */
  public Schema getAvroSchema() {
    return schema;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((schema == null) ? 0 : schema.hashCode());
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    AvroKeySchema other = (AvroKeySchema) obj;
    if (schema == null) {
      if (other.schema != null)
        return false;
    } else if (!schema.equals(other.schema))
      return false;
    return true;
  }
}

@@ -646,43 +646,14 @@
 til.
-Array
 List;%0A
-import java.util.List;%0A%0A
 impo
@@ -683,137 +683,8 @@
 ma;%0A
-import org.apache.avro.Schema.Field;%0A%0Aimport org.kitesdk.data.ValidationException;%0Aimport org.kitesdk.data.spi.FieldPartitioner;%0A
 impo
@@ -796,26 +796,32 @@
 ata.spi.
-SchemaUtil
+FieldPartitioner
 ;%0Aimport
@@ -846,42 +846,18 @@
 spi.
-partition.IdentityFieldPartitioner
+SchemaUtil
 ;%0A%0A/
@@ -1123,1235 +1123,73 @@
     
-List%3CField%3E fieldsPartOfKey = new ArrayList%3CField%3E();%0A    for (FieldPartitioner%3C?, ?%3E fieldPartitioner : partitionStrategy.getFieldPartitioners()) %7B%0A      Schema fieldSchema;%0A      if (fieldPartitioner instanceof IdentityFieldPartitioner) %7B%0A        // copy the schema directly from the entity to preserve annotations%0A        fieldSchema = entitySchema%0A            .getField(fieldPartitioner.getSourceName()).schema();%0A      %7D else %7B%0A        Class%3C?%3E fieldType = SchemaUtil%0A            .getPartitionType(fieldPartitioner, entitySchema);%0A        if (fieldType == Integer.class) %7B%0A          fieldSchema = Schema.create(Schema.Type.INT);%0A        %7D else if (fieldType == Long.class) %7B%0A          fieldSchema = Schema.create(Schema.Type.LONG);%0A        %7D else if (fieldType == String.class) %7B%0A          fieldSchema = Schema.create(Schema.Type.STRING);%0A        %7D else %7B%0A          throw new ValidationException(%0A              %22Cannot encode partition %22 + fieldPartitioner.getName() +%0A                  %22 with type %22 + fieldPartitioner.getSourceType()%0A          );%0A        %7D%0A      %7D%0A      fieldsPartOfKey.add(new Field(fieldPartitioner.getSourceName(), fieldSchema, null, null));%0A    %7D%0A    this.schema = Schema.createRecord(fieldsPartOfKe
+this.schema = SchemaUtil.keySchema(entitySchema, partitionStrateg
 y);%0A

Update AvroKeySchema to use SchemaUtil#keySchema.