// Path: core/src/main/java/com/contentful/vault/ModelHelper.java
// public abstract class ModelHelper<T extends Resource> {
//   public abstract String getTableName();
// 
//   public abstract List<FieldMeta> getFields();
// 
//   public abstract List<String> getCreateStatements(SpaceHelper spaceHelper);
// 
//   public abstract T fromCursor(Cursor cursor);
// 
//   protected abstract boolean setField(T resource, String name, Object value);
// 
//   protected final <E extends Serializable> E fieldFromBlob(Class<E> clazz, Cursor cursor,
//       int columnIndex) {
//     byte[] blob = cursor.getBlob(columnIndex);
//     if (blob == null || blob.length == 0) {
//       return null;
//     }
//     E result = null;
//     Exception exception = null;
//     try {
//       result = BlobUtils.fromBlob(clazz, blob);
//     } catch (IOException | ClassNotFoundException e) {
//       exception = e;
//     }
//     if (exception != null) {
//       throw new RuntimeException(String.format("Failed creating BLOB from column %d of %s.",
//           columnIndex, getTableName()), exception);
//     }
//     return result;
//   }
// 
//   protected final void setContentType(T resource, String type) {
//     resource.setContentType(type);
//   }
// }
// 
// Path: core/src/main/java/com/contentful/vault/Resource.java
// public abstract class Resource {
//   String remoteId;
// 
//   String createdAt;
// 
//   String updatedAt;
// 
//   String contentType;
// 
//   public String remoteId() {
//     return remoteId;
//   }
// 
//   public String createdAt() {
//     return createdAt;
//   }
// 
//   public String updatedAt() {
//     return updatedAt;
//   }
// 
//   void setRemoteId(String remoteId) {
//     this.remoteId = remoteId;
//   }
// 
//   void setCreatedAt(String createdAt) {
//     this.createdAt = createdAt;
//   }
// 
//   void setUpdatedAt(String updatedAt) {
//     this.updatedAt = updatedAt;
//   }
// 
//   void setContentType(String contentType) {
//     this.contentType = contentType;
//   }
// 
//   String contentType() {
//     return contentType;
//   }
// 
//   String getIdPrefix() {
//     return null;
//   }
// 
//   @Override public boolean equals(Object o) {
//     if (this == o) return true;
//     if (!(o instanceof Resource)) return false;
// 
//     Resource resource = (Resource) o;
//     String prefix = StringUtils.defaultString(getIdPrefix(), "");
//     if (!prefix.equals(StringUtils.defaultString(resource.getIdPrefix(), ""))) return false;
//     return (prefix + remoteId()).equals(prefix + resource.remoteId());
//   }
// 
//   @Override public int hashCode() {
//     return (StringUtils.defaultString(getIdPrefix(), "") + remoteId).hashCode();
//   }
// }
// 
// Path: core/src/main/java/com/contentful/vault/SpaceHelper.java
// public abstract class SpaceHelper {
//   public abstract String getDatabaseName();
// 
//   public abstract int getDatabaseVersion();
// 
//   public abstract Map<Class<?>, ModelHelper<?>> getModels();
// 
//   public abstract Map<String, Class<? extends Resource>> getTypes();
// 
//   public abstract String getCopyPath();
// 
//   public abstract String getSpaceId();
// 
//   public abstract List<String> getLocales();
// 
//   public final String getDefaultLocale() {
//     return getLocales().get(0);
//   }
// }


import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import com.contentful.vault.ModelHelper;
import com.contentful.vault.Resource;
import com.contentful.vault.SpaceHelper;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.WildcardTypeName;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
    appendDbVersion(builder);
    appendModels(builder);
    appendTypes(builder);
    appendCopyPath(builder);
    appendSpaceId(builder);
    appendLocales(builder);
    appendConstructor(builder);

    return builder;
  }

  private void appendConstructor(TypeSpec.Builder builder) {
    MethodSpec.Builder ctor = MethodSpec.constructorBuilder()
        .addModifiers(Modifier.PUBLIC);

    for (ModelInjection model : models) {
      ClassName enclosingClassName = ClassName.get(model.originatingElement);

      ctor.addStatement("$N.put($L.class, new $L())", specModels, enclosingClassName,
          model.className);

      ctor.addStatement("$N.put($S, $L.class)", specTypes, model.remoteId, enclosingClassName);
    }

    builder.addMethod(ctor.build());
  }

  private void appendTypes(TypeSpec.Builder builder) {
    // Field
    TypeName classTypeName = ParameterizedTypeName.get(ClassName.get(Class.class),
// Path: core/src/main/java/com/contentful/vault/ModelHelper.java
// public abstract class ModelHelper<T extends Resource> {
//   public abstract String getTableName();
// 
//   public abstract List<FieldMeta> getFields();
// 
//   public abstract List<String> getCreateStatements(SpaceHelper spaceHelper);
// 
//   public abstract T fromCursor(Cursor cursor);
// 
//   protected abstract boolean setField(T resource, String name, Object value);
// 
//   protected final <E extends Serializable> E fieldFromBlob(Class<E> clazz, Cursor cursor,
//       int columnIndex) {
//     byte[] blob = cursor.getBlob(columnIndex);
//     if (blob == null || blob.length == 0) {
//       return null;
//     }
//     E result = null;
//     Exception exception = null;
//     try {
//       result = BlobUtils.fromBlob(clazz, blob);
//     } catch (IOException | ClassNotFoundException e) {
//       exception = e;
//     }
//     if (exception != null) {
//       throw new RuntimeException(String.format("Failed creating BLOB from column %d of %s.",
//           columnIndex, getTableName()), exception);
//     }
//     return result;
//   }
// 
//   protected final void setContentType(T resource, String type) {
//     resource.setContentType(type);
//   }
// }
// 
// Path: core/src/main/java/com/contentful/vault/Resource.java
// public abstract class Resource {
//   String remoteId;
// 
//   String createdAt;
// 
//   String updatedAt;
// 
//   String contentType;
// 
//   public String remoteId() {
//     return remoteId;
//   }
// 
//   public String createdAt() {
//     return createdAt;
//   }
// 
//   public String updatedAt() {
//     return updatedAt;
//   }
// 
//   void setRemoteId(String remoteId) {
//     this.remoteId = remoteId;
//   }
// 
//   void setCreatedAt(String createdAt) {
//     this.createdAt = createdAt;
//   }
// 
//   void setUpdatedAt(String updatedAt) {
//     this.updatedAt = updatedAt;
//   }
// 
//   void setContentType(String contentType) {
//     this.contentType = contentType;
//   }
// 
//   String contentType() {
//     return contentType;
//   }
// 
//   String getIdPrefix() {
//     return null;
//   }
// 
//   @Override public boolean equals(Object o) {
//     if (this == o) return true;
//     if (!(o instanceof Resource)) return false;
// 
//     Resource resource = (Resource) o;
//     String prefix = StringUtils.defaultString(getIdPrefix(), "");
//     if (!prefix.equals(StringUtils.defaultString(resource.getIdPrefix(), ""))) return false;
//     return (prefix + remoteId()).equals(prefix + resource.remoteId());
//   }
// 
//   @Override public int hashCode() {
//     return (StringUtils.defaultString(getIdPrefix(), "") + remoteId).hashCode();
//   }
// }
// 
// Path: core/src/main/java/com/contentful/vault/SpaceHelper.java
// public abstract class SpaceHelper {
//   public abstract String getDatabaseName();
// 
//   public abstract int getDatabaseVersion();
// 
//   public abstract Map<Class<?>, ModelHelper<?>> getModels();
// 
//   public abstract Map<String, Class<? extends Resource>> getTypes();
// 
//   public abstract String getCopyPath();
// 
//   public abstract String getSpaceId();
// 
//   public abstract List<String> getLocales();
// 
//   public final String getDefaultLocale() {
//     return getLocales().get(0);
//   }
// }


// Path: compiler/src/main/java/com/contentful/vault/compiler/SpaceInjection.java
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import com.contentful.vault.ModelHelper;
import com.contentful.vault.Resource;
import com.contentful.vault.SpaceHelper;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.WildcardTypeName;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;

    appendDbVersion(builder);
    appendModels(builder);
    appendTypes(builder);
    appendCopyPath(builder);
    appendSpaceId(builder);
    appendLocales(builder);
    appendConstructor(builder);

    return builder;
  }

  private void appendConstructor(TypeSpec.Builder builder) {
    MethodSpec.Builder ctor = MethodSpec.constructorBuilder()
        .addModifiers(Modifier.PUBLIC);

    for (ModelInjection model : models) {
      ClassName enclosingClassName = ClassName.get(model.originatingElement);

      ctor.addStatement("$N.put($L.class, new $L())", specModels, enclosingClassName,
          model.className);

      ctor.addStatement("$N.put($S, $L.class)", specTypes, model.remoteId, enclosingClassName);
    }

    builder.addMethod(ctor.build());
  }

  private void appendTypes(TypeSpec.Builder builder) {
    // Field
    TypeName classTypeName = ParameterizedTypeName.get(ClassName.get(Class.class),
        WildcardTypeName.subtypeOf(Resource.class));