// Copyright 2013 Square, Inc.
package repacked.com.squareup.javawriter;

import java.io.Closeable;
import java.io.IOException;
import java.io.Writer;
import java.lang.annotation.Annotation;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.lang.model.element.Modifier;

import static javax.lang.model.element.Modifier.ABSTRACT;

/** A utility class which aids in generating Java source files. */
public class JavaWriter implements Closeable {
  private static final Pattern TYPE_PATTERN = Pattern.compile("(?:[\\w$]+\\.)*([\\w\\.*$]+)");
  private static final int MAX_SINGLE_LINE_ATTRIBUTES = 3;
  private static final String INDENT = "  ";

  /** Map fully qualified type names to their short names. */
  private final Map<String, String> importedTypes = new LinkedHashMap<String, String>();

  private String packagePrefix;
  private final Deque<Scope> scopes = new ArrayDeque<Scope>();
  private final Deque<String> types = new ArrayDeque<String>();
  private final Writer out;
  private boolean isCompressingTypes = true;
  private String indent = INDENT;

  /**
   * @param out the stream to which Java source will be written. This should be a buffered stream.
   */
  public JavaWriter(Writer out) {
    this.out = out;
  }

  public void setCompressingTypes(boolean isCompressingTypes) {
    this.isCompressingTypes = isCompressingTypes;
  }

  public boolean isCompressingTypes() {
    return isCompressingTypes;
  }

  public void setIndent(String indent) {
    this.indent = indent;
  }

  public String getIndent() {
    return indent;
  }

  /** Emit a package declaration and empty line. */
  public JavaWriter emitPackage(String packageName) throws IOException {
    if (this.packagePrefix != null) {
      throw new IllegalStateException();
    }
    if (packageName.isEmpty()) {
      this.packagePrefix = "";
    } else {
      out.write("package ");
      out.write(packageName);
      out.write(";\n\n");
      this.packagePrefix = packageName + ".";
    }
    return this;
  }

  /**
   * Emit an import for each {@code type} provided. For the duration of the file, all references to
   * these classes will be automatically shortened.
   */
  public JavaWriter emitImports(String... types) throws IOException {
    return emitImports(Arrays.asList(types));
  }

  /**
   * Emit an import for each {@code type} provided. For the duration of the file, all references to
   * these classes will be automatically shortened.
   */
  public JavaWriter emitImports(Class<?>... types) throws IOException {
    List<String> classNames = new ArrayList<String>(types.length);
    for (Class<?> classToImport : types) {
      classNames.add(classToImport.getName());
    }
    return emitImports(classNames);
  }

  /**
   * Emit an import for each {@code type} in the provided {@code Collection}. For the duration of
   * the file, all references to these classes will be automatically shortened.
   */
  public JavaWriter emitImports(Collection<String> types) throws IOException {
    for (String type : new TreeSet<String>(types)) {
      Matcher matcher = TYPE_PATTERN.matcher(type);
      if (!matcher.matches()) {
        throw new IllegalArgumentException(type);
      }
      if (importedTypes.put(type, matcher.group(1)) != null) {
        throw new IllegalArgumentException(type);
      }
      out.write("import ");
      out.write(type);
      out.write(";\n");
    }
    return this;
  }

  /**
   * Emit a static import for each {@code type} provided. For the duration of the file,
   * all references to these classes will be automatically shortened.
   */
  public JavaWriter emitStaticImports(String... types) throws IOException {
    return emitStaticImports(Arrays.asList(types));
  }

  /**
   * Emit a static import for each {@code type} in the provided {@code Collection}. For the
   * duration of the file, all references to these classes will be automatically shortened.
   */
  public JavaWriter emitStaticImports(Collection<String> types) throws IOException {
    for (String type : new TreeSet<String>(types)) {
      Matcher matcher = TYPE_PATTERN.matcher(type);
      if (!matcher.matches()) {
        throw new IllegalArgumentException(type);
      }
      if (importedTypes.put(type, matcher.group(1)) != null) {
        throw new IllegalArgumentException(type);
      }
      out.write("import static ");
      out.write(type);
      out.write(";\n");
    }
    return this;
  }

  /**
   * Emits a name like {@code java.lang.String} or {@code java.util.List<java.lang.String>},
   * compressing it with imports if possible. Type compression will only be enabled if
   * {@link #isCompressingTypes} is true.
   */
  private JavaWriter emitCompressedType(String type) throws IOException {
    if (isCompressingTypes) {
      out.write(compressType(type));
    } else {
      out.write(type);
    }
    return this;
  }

  /** Try to compress a fully-qualified class name to only the class name. */
  public String compressType(String type) {
    StringBuilder sb = new StringBuilder();
    if (this.packagePrefix == null) {
      throw new IllegalStateException();
    }

    Matcher m = TYPE_PATTERN.matcher(type);
    int pos = 0;
    while (true) {
      boolean found = m.find(pos);

      // Copy non-matching characters like "<".
      int typeStart = found ? m.start() : type.length();
      sb.append(type, pos, typeStart);

      if (!found) {
        break;
      }

      // Copy a single class name, shortening it if possible.
      String name = m.group(0);
      String imported = importedTypes.get(name);
      if (imported != null) {
        sb.append(imported);
      } else if (isClassInPackage(name, packagePrefix)) {
        String compressed = name.substring(packagePrefix.length());
        if (isAmbiguous(compressed)) {
          sb.append(name);
        } else {
          sb.append(compressed);
        }
      } else if (isClassInPackage(name, "java.lang.")) {
        sb.append(name.substring("java.lang.".length()));
      } else {
        sb.append(name);
      }
      pos = m.end();
    }
    return sb.toString();
  }

  private static boolean isClassInPackage(String name, String packagePrefix) {
    if (name.startsWith(packagePrefix)) {
      if (name.indexOf('.', packagePrefix.length()) == -1) {
        return true;
      }
      // check to see if the part after the package looks like a class
      if (Character.isUpperCase(name.charAt(packagePrefix.length()))) {
        return true;
      }
    }
    return false;
  }

  /**
   * Returns true if the imports contain a class with same simple name as {@code compressed}.
   *
   * @param compressed simple name of the type
   */
  private boolean isAmbiguous(String compressed) {
    return importedTypes.values().contains(compressed);
  }

  /**
   * Emits an initializer declaration.
   *
   * @param isStatic true if it should be an static initializer, false for an instance initializer.
   */
  public JavaWriter beginInitializer(boolean isStatic) throws IOException {
    indent();
    if (isStatic) {
      out.write("static");
      out.write(" {\n");
    } else {
      out.write("{\n");
    }
    scopes.push(Scope.INITIALIZER);
    return this;
  }

  /** Ends the current initializer declaration. */
  public JavaWriter endInitializer() throws IOException {
    popScope(Scope.INITIALIZER);
    indent();
    out.write("}\n");
    return this;
  }

 /**
  * Emits a type declaration.
  *
  * @param kind such as "class", "interface" or "enum".
  */
  public JavaWriter beginType(String type, String kind) throws IOException {
    return beginType(type, kind, EnumSet.noneOf(Modifier.class), null);
  }

  /**
   * Emits a type declaration.
   *
   * @param kind such as "class", "interface" or "enum".
   */
  public JavaWriter beginType(String type, String kind, Set<Modifier> modifiers)
      throws IOException {
    return beginType(type, kind, modifiers, null);
  }

  /**
   * Emits a type declaration.
   *
   * @param kind such as "class", "interface" or "enum".
   * @param extendsType the class to extend, or null for no extends clause.
   */
  public JavaWriter beginType(String type, String kind, Set<Modifier> modifiers, String extendsType,
      String... implementsTypes) throws IOException {
    indent();
    emitModifiers(modifiers);
    out.write(kind);
    out.write(" ");
    emitCompressedType(type);
    if (extendsType != null) {
      out.write(" extends ");
      emitCompressedType(extendsType);
    }
    if (implementsTypes.length > 0) {
      out.write("\n");
      indent();
      out.write("    implements ");
      for (int i = 0; i < implementsTypes.length; i++) {
        if (i != 0) {
          out.write(", ");
        }
        emitCompressedType(implementsTypes[i]);
      }
    }
    out.write(" {\n");
    scopes.push("interface".equals(kind) ? Scope.INTERFACE_DECLARATION : Scope.TYPE_DECLARATION);
    types.push(type);
    return this;
  }

  /** Completes the current type declaration. */
  public JavaWriter endType() throws IOException {
    popScope(Scope.TYPE_DECLARATION, Scope.INTERFACE_DECLARATION);
    types.pop();
    indent();
    out.write("}\n");
    return this;
  }

  /** Emits a field declaration. */
  public JavaWriter emitField(String type, String name) throws IOException {
    return emitField(type, name, EnumSet.noneOf(Modifier.class), null);
  }

  /** Emits a field declaration. */
  public JavaWriter emitField(String type, String name, Set<Modifier> modifiers)
      throws IOException {
    return emitField(type, name, modifiers, null);
  }

  /** Emits a field declaration. */
  public JavaWriter emitField(String type, String name, Set<Modifier> modifiers,
      String initialValue) throws IOException {
    indent();
    emitModifiers(modifiers);
    emitCompressedType(type);
    out.write(" ");
    out.write(name);

    if (initialValue != null) {
      out.write(" =");
      if (!initialValue.startsWith("\n")) {
        out.write(" ");
      }

      String[] lines = initialValue.split("\n", -1);
      out.write(lines[0]);
      for (int i = 1; i < lines.length; i++) {
        out.write("\n");
        hangingIndent();
        out.write(lines[i]);
      }
    }
    out.write(";\n");
    return this;
  }

  /**
   * Emit a method declaration.
   *
   *
A {@code null} return type may be used to indicate a constructor, but
   * {@link #beginConstructor(Set, String...)} should be preferred. This behavior may be removed in
   * a future release.
   *
   * @param returnType the method's return type, or null for constructors
   * @param name the method name, or the fully qualified class name for constructors.
   * @param modifiers the set of modifiers to be applied to the method
   * @param parameters alternating parameter types and names.
   */
  public JavaWriter beginMethod(String returnType, String name, Set<Modifier> modifiers,
      String... parameters) throws IOException {
    return beginMethod(returnType, name, modifiers, Arrays.asList(parameters), null);
  }

  /**
   * Emit a method declaration.
   *
   *
A {@code null} return type may be used to indicate a constructor, but
   * {@link #beginConstructor(Set, List, List)} should be preferred. This behavior may be removed in
   * a future release.
   *
   * @param returnType the method's return type, or null for constructors.
   * @param name the method name, or the fully qualified class name for constructors.
   * @param modifiers the set of modifiers to be applied to the method
   * @param parameters alternating parameter types and names.
   * @param throwsTypes the classes to throw, or null for no throws clause.
   */
  public JavaWriter beginMethod(String returnType, String name, Set<Modifier> modifiers,
      List<String> parameters, List<String> throwsTypes) throws IOException {
    indent();
    emitModifiers(modifiers);
    if (returnType != null) {
      emitCompressedType(returnType);
      out.write(" ");
      out.write(name);
    } else {
      emitCompressedType(name);
    }
    out.write("(");
    if (parameters != null) {
      for (int p = 0; p < parameters.size();) {
        if (p != 0) {
          out.write(", ");
        }
        emitCompressedType(parameters.get(p++));
        out.write(" ");
        emitCompressedType(parameters.get(p++));
      }
    }
    out.write(")");
    if (throwsTypes != null && throwsTypes.size() > 0) {
      out.write("\n");
      indent();
      out.write("    throws ");
      for (int i = 0; i < throwsTypes.size(); i++) {
        if (i != 0) {
          out.write(", ");
        }
        emitCompressedType(throwsTypes.get(i));
      }
    }
    if (modifiers.contains(ABSTRACT) || Scope.INTERFACE_DECLARATION.equals(scopes.peek())) {
      out.write(";\n");
      scopes.push(Scope.ABSTRACT_METHOD);
    } else {
      out.write(" {\n");
      scopes.push(returnType == null ? Scope.CONSTRUCTOR : Scope.NON_ABSTRACT_METHOD);
    }
    return this;
  }

  public JavaWriter beginConstructor(Set<Modifier> modifiers, String... parameters)
      throws IOException {
    beginMethod(null, rawType(types.peekFirst()), modifiers, parameters);
    return this;
  }

  public JavaWriter beginConstructor(Set<Modifier> modifiers,
      List<String> parameters, List<String> throwsTypes)
      throws IOException {
    beginMethod(null, rawType(types.peekFirst()), modifiers, parameters, throwsTypes);
    return this;
  }

  /** Emits some Javadoc comments with line separated by {@code \n}. */
  public JavaWriter emitJavadoc(String javadoc, Object... params) throws IOException {
    String formatted = String.format(javadoc, params);

    indent();
    out.write("/**\n");
    for (String line : formatted.split("\n")) {
      indent();
      out.write(" *");
      if (!line.isEmpty()) {
        out.write(" ");
        out.write(line);
      }
      out.write("\n");
    }
    indent();
    out.write(" */\n");
    return this;
  }

  /** Emits a single line comment. */
  public JavaWriter emitSingleLineComment(String comment, Object... args) throws IOException {
    indent();
    out.write("// ");
    out.write(String.format(comment, args));
    out.write("\n");
    return this;
  }

  public JavaWriter emitEmptyLine() throws IOException {
    out.write("\n");
    return this;
  }

  public JavaWriter emitEnumValue(String name) throws IOException {
    indent();
    out.write(name);
    out.write(",\n");
    return this;
  }

  /**
   * A simple switch to emit the proper enum depending if its last causing it to be terminated
   * by a semi-colon ({@code ;}).
   */
  public JavaWriter emitEnumValue(String name, boolean isLast) throws IOException {
    return isLast ? emitLastEnumValue(name) : emitEnumValue(name);
  }

  private JavaWriter emitLastEnumValue(String name) throws IOException {
    indent();
    out.write(name);
    out.write(";\n");
    return this;
  }

  /** Emit a list of enum values followed by a semi-colon ({@code ;}). */
  public JavaWriter emitEnumValues(Iterable<String> names) throws IOException {
    Iterator<String> iterator = names.iterator();

    while (iterator.hasNext()) {
      String name = iterator.next();
      if (iterator.hasNext()) {
        emitEnumValue(name);
      } else {
        emitLastEnumValue(name);
      }
    }

    return this;
  }

  /** Equivalent to {@code annotation(annotation, emptyMap())}. */
  public JavaWriter emitAnnotation(String annotation) throws IOException {
    return emitAnnotation(annotation, Collections.<String, Object>emptyMap());
  }

  /** Equivalent to {@code annotation(annotationType.getName(), emptyMap())}. */
  public JavaWriter emitAnnotation(Class<? extends Annotation> annotationType) throws IOException {
    return emitAnnotation(type(annotationType), Collections.<String, Object>emptyMap());
  }

  /**
   * Annotates the next element with {@code annotationType} and a {@code value}.
   *
   * @param value an object used as the default (value) parameter of the annotation. The value will
   *     be encoded using Object.toString(); use {@link #stringLiteral} for String values. Object
   *     arrays are written one element per line.
   */
  public JavaWriter emitAnnotation(Class<? extends Annotation> annotationType, Object value)
      throws IOException {
    return emitAnnotation(type(annotationType), value);
  }

  /**
   * Annotates the next element with {@code annotation} and a {@code value}.
   *
   * @param value an object used as the default (value) parameter of the annotation. The value will
   *     be encoded using Object.toString(); use {@link #stringLiteral} for String values. Object
   *     arrays are written one element per line.
   */
  public JavaWriter emitAnnotation(String annotation, Object value) throws IOException {
    indent();
    out.write("@");
    emitCompressedType(annotation);
    out.write("(");
    emitAnnotationValue(value);
    out.write(")");
    out.write("\n");
    return this;
  }

  /** Equivalent to {@code annotation(annotationType.getName(), attributes)}. */
  public JavaWriter emitAnnotation(Class<? extends Annotation> annotationType,
      Map<String, ?> attributes) throws IOException {
    return emitAnnotation(type(annotationType), attributes);
  }

  /**
   * Annotates the next element with {@code annotation} and {@code attributes}.
   *
   * @param attributes a map from annotation attribute names to their values. Values are encoded
   *     using Object.toString(); use {@link #stringLiteral} for String values. Object arrays are
   *     written one element per line.
   */
  public JavaWriter emitAnnotation(String annotation, Map<String, ?> attributes)
      throws IOException {
    indent();
    out.write("@");
    emitCompressedType(annotation);
    switch (attributes.size()) {
      case 0:
        break;
      case 1:
        Entry<String, ?> onlyEntry = attributes.entrySet().iterator().next();
        out.write("(");
        if (!"value".equals(onlyEntry.getKey())) {
          out.write(onlyEntry.getKey());
          out.write(" = ");
        }
        emitAnnotationValue(onlyEntry.getValue());
        out.write(")");
        break;
      default:
        boolean split = attributes.size() > MAX_SINGLE_LINE_ATTRIBUTES
            || containsArray(attributes.values());
        out.write("(");
        scopes.push(Scope.ANNOTATION_ATTRIBUTE);
        String separator = split ? "\n" : "";
        for (Entry<String, ?> entry : attributes.entrySet()) {
          out.write(separator);
          separator = split ? ",\n" : ", ";
          if (split) {
            indent();
          }
          out.write(entry.getKey());
          out.write(" = ");
          Object value = entry.getValue();
          emitAnnotationValue(value);
        }
        popScope(Scope.ANNOTATION_ATTRIBUTE);
        if (split) {
          out.write("\n");
          indent();
        }
        out.write(")");
        break;
    }
    out.write("\n");
    return this;
  }

  private boolean containsArray(Collection<?> values) {
    for (Object value : values) {
      if (value instanceof Object[]) {
        return true;
      }
    }
    return false;
  }

  /**
   * Writes a single annotation value. If the value is an array, each element in the array will be
   * written to its own line.
   */
  private JavaWriter emitAnnotationValue(Object value) throws IOException {
    if (value instanceof Object[]) {
      out.write("{");
      boolean firstValue = true;
      scopes.push(Scope.ANNOTATION_ARRAY_VALUE);
      for (Object o : ((Object[]) value)) {
        if (firstValue) {
          firstValue = false;
          out.write("\n");
        } else {
          out.write(",\n");
        }
        indent();
        out.write(o.toString());
      }
      popScope(Scope.ANNOTATION_ARRAY_VALUE);
      out.write("\n");
      indent();
      out.write("}");
    } else {
      out.write(value.toString());
    }
    return this;
  }

  /**
   * @param pattern a code pattern like "int i = %s". Newlines will be further indented. Should not
   *     contain trailing semicolon.
   */
  public JavaWriter emitStatement(String pattern, Object... args) throws IOException {
    checkInMethod();
    String[] lines = String.format(pattern, args).split("\n", -1);
    indent();
    out.write(lines[0]);
    for (int i = 1; i < lines.length; i++) {
      out.write("\n");
      hangingIndent();
      out.write(lines[i]);
    }
    out.write(";\n");
    return this;
  }

  /**
   * @param controlFlow the control flow construct and its code, such as "if (foo == 5)". Shouldn't
   *     contain braces or newline characters.
   */
  public JavaWriter beginControlFlow(String controlFlow, Object... args) throws IOException {
    checkInMethod();
    indent();
    out.write(String.format(controlFlow, args));
    out.write(" {\n");
    scopes.push(Scope.CONTROL_FLOW);
    return this;
  }

  /**
   * @param controlFlow the control flow construct and its code, such as "else if (foo == 10)".
   *     Shouldn't contain braces or newline characters.
   */
  public JavaWriter nextControlFlow(String controlFlow, Object... args) throws IOException {
    popScope(Scope.CONTROL_FLOW);
    indent();
    scopes.push(Scope.CONTROL_FLOW);
    out.write("} ");
    out.write(String.format(controlFlow, args));
    out.write(" {\n");
    return this;
  }

  public JavaWriter endControlFlow() throws IOException {
    return endControlFlow(null);
  }

  /**
   * @param controlFlow the optional control flow construct and its code, such as
   *     "while(foo == 20)". Only used for "do/while" control flows.
   */
  public JavaWriter endControlFlow(String controlFlow, Object... args) throws IOException {
    popScope(Scope.CONTROL_FLOW);
    indent();
    if (controlFlow != null) {
      out.write("} ");
      out.write(String.format(controlFlow, args));
      out.write(";\n");
    } else {
      out.write("}\n");
    }
    return this;
  }

  /** Completes the current method declaration. */
  public JavaWriter endMethod() throws IOException {
    Scope popped = scopes.pop();
    // support calling a constructor a "method" to support the legacy code
    if (popped == Scope.NON_ABSTRACT_METHOD || popped == Scope.CONSTRUCTOR) {
      indent();
      out.write("}\n");
    } else if (popped != Scope.ABSTRACT_METHOD) {
      throw new IllegalStateException();
    }
    return this;
  }

  /** Completes the current constructor declaration. */
  public JavaWriter endConstructor() throws IOException {
    popScope(Scope.CONSTRUCTOR);
    indent();
    out.write("}\n");
    return this;
  }

  /** Returns the string literal representing {@code data}, including wrapping quotes. */
  public static String stringLiteral(String data) {
    StringBuilder result = new StringBuilder();
    result.append('"');
    for (int i = 0; i < data.length(); i++) {
      char c = data.charAt(i);
      switch (c) {
        case '"':
          result.append("\\\"");
          break;
        case '\\':
          result.append("\\\\");
          break;
        case '\b':
          result.append("\\b");
          break;
        case '\t':
          result.append("\\t");
          break;
        case '\n':
          result.append("\\n");
          break;
        case '\f':
          result.append("\\f");
          break;
        case '\r':
          result.append("\\r");
          break;
        default:
          if (Character.isISOControl(c)) {
            result.append(String.format("\\u%04x", (int) c));
          } else {
            result.append(c);
          }
      }
    }
    result.append('"');
    return result.toString();
  }

  /** Build a string representation of a type and optionally its generic type arguments. */
  public static String type(Class<?> raw, String... parameters) {
    if (parameters.length == 0) {
      return raw.getCanonicalName();
    }
    if (raw.getTypeParameters().length != parameters.length) {
      throw new IllegalArgumentException();
    }
    StringBuilder result = new StringBuilder();
    result.append(raw.getCanonicalName());
    result.append("<");
    result.append(parameters[0]);
    for (int i = 1; i < parameters.length; i++) {
      result.append(", ");
      result.append(parameters[i]);
    }
    result.append(">");
    return result.toString();
  }

  /** Build a string representation of the raw type for a (optionally generic) type. */
  public static String rawType(String type) {
    int lessThanIndex = type.indexOf('<');
    if (lessThanIndex != -1) {
      return type.substring(0, lessThanIndex);
    }
    return type;
  }

  @Override public void close() throws IOException {
    out.close();
  }

  /** Emits the modifiers to the writer. */
  private void emitModifiers(Set<Modifier> modifiers) throws IOException {
    if (modifiers.isEmpty()) {
      return;
    }
    // Use an EnumSet to ensure the proper ordering
    if (!(modifiers instanceof EnumSet)) {
      modifiers = EnumSet.copyOf(modifiers);
    }
    for (Modifier modifier : modifiers) {
      out.append(modifier.toString()).append(' ');
    }
  }

  private void indent() throws IOException {
    for (int i = 0, count = scopes.size(); i < count; i++) {
      out.write(indent);
    }
  }

  private void hangingIndent() throws IOException {
    for (int i = 0, count = scopes.size() + 2; i < count; i++) {
      out.write(indent);
    }
  }

  private static final EnumSet<Scope>
      METHOD_SCOPES = EnumSet.of(Scope.NON_ABSTRACT_METHOD, Scope.CONSTRUCTOR, Scope.CONTROL_FLOW,
      Scope.INITIALIZER);

  private void checkInMethod() {
    if (!METHOD_SCOPES.contains(scopes.peekFirst())) {
      throw new IllegalArgumentException();
    }
  }

  private void popScope(Scope... expected) {
    if (!EnumSet.copyOf(Arrays.asList(expected)).contains(scopes.pop())) {
      throw new IllegalStateException();
    }
  }

  private enum Scope {
    TYPE_DECLARATION,
    INTERFACE_DECLARATION,
    ABSTRACT_METHOD,
    NON_ABSTRACT_METHOD,
    CONSTRUCTOR,
    CONTROL_FLOW,
    ANNOTATION_ATTRIBUTE,
    ANNOTATION_ARRAY_VALUE,
    INITIALIZER
  }
}


package com.hannesdorfmann.parcelableplease.plugin;

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
/**
 * A simple plugin for generating the boilerplateCode of ParcelablePlease
 *
 * @author Hannes Dorfmann
 */
public class ParcelablePleaseAction extends AnAction {
  public void actionPerformed(AnActionEvent e) {

    final PsiClass psiClass = getPsiClassFromContext(e);

    // Generate Code
    new WriteCommandAction.Simple(psiClass.getProject(), psiClass.getContainingFile()) {
      @Override
      protected void run() throws Throwable {
        new CodeGenerator(psiClass).generate();
      }
    }.execute();
  }

  @Override
  public void update(AnActionEvent e) {
    PsiClass psiClass = getPsiClassFromContext(e);
    e.getPresentation().setEnabled(psiClass != null && !psiClass.isEnum() && !psiClass.isInterface());
  }

  /**
   * Get the class where currently the curser is
   */
  private PsiClass getPsiClassFromContext(AnActionEvent e) {
    PsiFile psiFile = e.getData(LangDataKeys.PSI_FILE);
    Editor editor = e.getData(PlatformDataKeys.EDITOR);

    if (psiFile == null || editor == null) {
      return null;
    }

    int offset = editor.getCaretModel().getOffset();
    PsiElement element = psiFile.findElementAt(offset);

    return PsiTreeUtil.getParentOfType(element, PsiClass.class);
  }

}


/*
 * Copyright (C) 2014 Hannes Dormfann (www.hannesdorfmann.com)
 *
 *
 * Parts of this code are taken from  Michał Charmas.
 *
 * Copyright (C) 2013 Michał Charmas (http://blog.charmas.pl)
 *
 * 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 com.hannesdorfmann.parcelableplease.plugin;

import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiElementFactory;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiImportList;
import com.intellij.psi.PsiImportStatementBase;
import com.intellij.psi.PsiJavaCodeReferenceElement;
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiParameter;
import com.intellij.psi.PsiParameterList;
import com.intellij.psi.PsiReferenceList;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;

/**
 * Generates the real code for ParcelablePlease that will be injected in intellij code editor
 * window
 *
 * @author Hannes Dorfmann
 */
public class CodeGenerator {

  private static final String PARCELABLE_PLEASE_SUFFIX = "ParcelablePlease";

  private static final String ANNOTATION_PACKAGE =
      "com.hannesdorfmann.parcelableplease.annotation";

  private static final String ANNOTATION_NAME = "ParcelablePlease";

  private PsiClass psiClass;

  public CodeGenerator(PsiClass psiClass) {
    this.psiClass = psiClass;
  }

  private String generateCreator() {

    String className = psiClass.getName();
    String parcelablePleaseClass = getParcelablePleaseClassName();

    StringBuilder sb = new StringBuilder("public static final android.os.Parcelable.Creator<");
    sb.append(className)
        .append("> CREATOR = new android.os.Parcelable.Creator<")
        .append(className)
        .append(">(){")
        .append("public ")
        .append(className)
        .append(" createFromParcel(android.os.Parcel source) {")
        .append(className)
        .append(" target =  new ")
        .append(className)
        .append("();")
        .append(parcelablePleaseClass)
        .append(".readFromParcel(target, source); return target;}")
        .append("public ")
        .append(className)
        .append("[] newArray(int size) {")
        .append("return new ")
        .append(className)
        .append("[size];}")
        .append("};");

    return sb.toString();
  }

  private String generateWriteToParcel() {

    String parcelablePleaseClass = getParcelablePleaseClassName();

    StringBuilder sb = new StringBuilder(
        "@Override public void writeToParcel(android.os.Parcel dest, int flags) {");

    sb.append(parcelablePleaseClass).append(".writeToParcel(this, dest, flags);").append("}");

    return sb.toString();
  }

  private String generateDescribeContents() {
    return "@Override public int describeContents() { return 0; }";
  }

  private void addImport(PsiElementFactory elementFactory, String fullyQualifiedName){
    final PsiFile file = psiClass.getContainingFile();
    if (!(file instanceof PsiJavaFile)) {
      return;
    }
    final PsiJavaFile javaFile = (PsiJavaFile)file;

    final PsiImportList importList = javaFile.getImportList();
    if (importList == null) {
      return;
    }

    // Check if already imported
    for (PsiImportStatementBase is : importList.getAllImportStatements()) {
      String impQualifiedName = is.getImportReference().getQualifiedName();
      if (fullyQualifiedName.equals(impQualifiedName)){
        return; // Already imported so nothing neede
      }

    }

    // Not imported yet so add it
    importList.add(elementFactory.createImportStatementOnDemand(fullyQualifiedName));
  }

  public void clearPrevious(){

    // Delete creator
    PsiField creatorField = psiClass.findFieldByName("CREATOR", false);
    if (creatorField != null) {
      creatorField.delete();
    }

    // Delete Methods
    findAndRemoveMethod("describeContents");
    findAndRemoveMethod("writeToParcel", "android.os.Parcel", "int");

  }

  /**
   * Finds and removes a given method
   * @param methodName
   * @param arguments
   */
  private void findAndRemoveMethod(String methodName, String... arguments) {
    // Maybe there's an easier way to do this with mClass.findMethodBySignature(), but I'm not an expert on Psi*
    PsiMethod[] methods = psiClass.findMethodsByName(methodName, false);

    for (PsiMethod method : methods) {
      PsiParameterList parameterList = method.getParameterList();

      if (parameterList.getParametersCount() == arguments.length) {
        boolean shouldDelete = true;

        PsiParameter[] parameters = parameterList.getParameters();

        for (int i = 0; i < arguments.length; i++) {
          if (!parameters[i].getType().getCanonicalText().equals(arguments[i])) {
            shouldDelete = false;
          }
        }

        if (shouldDelete) {
          method.delete();
        }
      }
    }
  }

  /**
   * Generate and insert the Parcel and ParcelablePlease code
   */
  public void generate() {

    PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(psiClass.getProject());
    JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(psiClass.getProject());

    // Clear any previous
    clearPrevious();

    // Implements parcelable
    makeClassImplementParcelable(elementFactory, styleManager);

    // @ParcelablePlease Annotation
    addAnnotation(elementFactory, styleManager);

    // Creator
    PsiField creatorField = elementFactory.createFieldFromText(generateCreator(), psiClass);

    // Describe contents method
    PsiMethod describeContentsMethod =
        elementFactory.createMethodFromText(generateDescribeContents(), psiClass);

    // Method for writing to the parcel
    PsiMethod writeToParcelMethod =
        elementFactory.createMethodFromText(generateWriteToParcel(), psiClass);

    styleManager.shortenClassReferences(
        psiClass.addBefore(describeContentsMethod, psiClass.getLastChild()));

    styleManager.shortenClassReferences(
        psiClass.addBefore(writeToParcelMethod, psiClass.getLastChild()));

    styleManager.shortenClassReferences(psiClass.addBefore(creatorField, psiClass.getLastChild()));
  }

  /**
   * Add the @Parcelable annotation if not already annotated
   */
  private void addAnnotation(PsiElementFactory elementFactory, JavaCodeStyleManager styleManager) {

    boolean annotated = AnnotationUtil.isAnnotated(psiClass, ANNOTATION_PACKAGE+"."+ANNOTATION_NAME, false);

    if (!annotated) {
      styleManager.shortenClassReferences(psiClass.getModifierList().addAnnotation(
          ANNOTATION_NAME));
    }
  }

  /**
   * Make the class implementing Parcelable
   */
  private void makeClassImplementParcelable(PsiElementFactory elementFactory, JavaCodeStyleManager styleManager) {
    final PsiClassType[] implementsListTypes = psiClass.getImplementsListTypes();
    final String implementsType = "android.os.Parcelable";

    for (PsiClassType implementsListType : implementsListTypes) {
      PsiClass resolved = implementsListType.resolve();

      // Already implements Parcelable, no need to add it
      if (resolved != null && implementsType.equals(resolved.getQualifiedName())) {
        return;
      }
    }

    PsiJavaCodeReferenceElement implementsReference =
        elementFactory.createReferenceFromText(implementsType, psiClass);
    PsiReferenceList implementsList = psiClass.getImplementsList();

    if (implementsList != null) {
      styleManager.shortenClassReferences(implementsList.add(implementsReference));
    }
  }

  private String getParcelablePleaseClassName() {
    String className = psiClass.getName();
    StringBuilder classNameBuilder = new StringBuilder();
    classNameBuilder.append(className);
    classNameBuilder.append(PARCELABLE_PLEASE_SUFFIX);
    PsiClass containingClass = psiClass.getContainingClass();
    while (containingClass != null) {
      classNameBuilder.insert(0, '$');
      classNameBuilder.insert(0, containingClass.getName());
      containingClass = containingClass.getContainingClass();
    }

    return classNameBuilder.toString();
  }
}


package com.hannesdorfmann.parcelableplease.processor;

import com.hannesdorfmann.parcelableplease.annotation.ParcelableNoThanks;
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
import com.hannesdorfmann.parcelableplease.annotation.ParcelableThisPlease;
import com.hannesdorfmann.parcelableplease.processor.codegenerator.CodeGenerator;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;

/**
 * @author Hannes Dorfmann
 */
@SupportedAnnotationTypes("com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease")
public class ParcelablePleaseProcessor extends AbstractProcessor {

  private static boolean BAGGERS_CREATED = false;

  private Elements elementUtils;
  private Types typeUtils;
  private Filer filer;

  @Override
  public synchronized void init(ProcessingEnvironment env) {
    super.init(env);

    elementUtils = env.getElementUtils();
    typeUtils = env.getTypeUtils();
    filer = env.getFiler();
    ProcessorMessage.init(env);
  }

  @Override
  public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {

    Element lastElement = null;
    CodeGenerator codeGenerator = new CodeGenerator(elementUtils, filer);

    for (Element element : env.getElementsAnnotatedWith(ParcelablePlease.class)) {

      if (!isClass(element)) {
        continue;
      }

      List<ParcelableField> fields = new ArrayList<ParcelableField>();

      lastElement = element;

      ParcelablePlease annotation = element.getAnnotation(ParcelablePlease.class);
      boolean allFields = annotation.allFields();
      boolean ignorePrivateFields = annotation.ignorePrivateFields();

      List<? extends Element> memberFields = elementUtils.getAllMembers((TypeElement) element);

      if (memberFields != null) {
        for (Element member : memberFields) {
          // Search for fields

          if (member.getKind() != ElementKind.FIELD || !(member instanceof VariableElement)) {
            continue; // Not a field, so go on
          }

          // it's a field, so go on

          ParcelableNoThanks skipFieldAnnotation = member.getAnnotation(ParcelableNoThanks.class);
          if (skipFieldAnnotation != null) {
            // Field is marked as not parcelabel, so continue with the next
            continue;
          }

          if (!allFields) {
            ParcelableThisPlease fieldAnnotated = member.getAnnotation(ParcelableThisPlease.class);
            if (fieldAnnotated == null) {
              // Not all fields should parcelable,
              // and this field is not annotated as parcelable, so skip this field
              continue;
            }
          }

          // Check the visibility of the field and modifiers
          Set<Modifier> modifiers = member.getModifiers();

          if (modifiers.contains(Modifier.STATIC)) {
            // Static fields are skipped
            continue;
          }

          if (modifiers.contains(Modifier.PRIVATE)) {

            if (ignorePrivateFields) {
              continue;
            }

            ProcessorMessage.error(member,
                "The field %s  in %s is private. At least default package visibility is required "
                    + "or annotate this field as not been parcelable with @%s "
                    + "or configure this class to ignore private fields "
                    + "with @%s( ignorePrivateFields = true )", member.getSimpleName(),
                element.getSimpleName(), ParcelableNoThanks.class.getSimpleName(),
                ParcelablePlease.class.getSimpleName());
          }

          if (modifiers.contains(Modifier.FINAL)) {
            ProcessorMessage.error(member,
                "The field %s in %s is final. Final can not be Parcelable", element.getSimpleName(),
                member.getSimpleName());
          }

          // If we are here the field is be parcelable
          fields.add(new ParcelableField((VariableElement) member, elementUtils, typeUtils));
        }
      }

      //
      // Generate the code
      //

      try {
        codeGenerator.generate((TypeElement) element, fields);
      } catch (Exception e) {
        e.printStackTrace();
        ProcessorMessage.error(lastElement, "An error has occurred while processing %s : %s",
            element.getSimpleName(), e.getMessage());
      }

    } // End for loop

    return true;
  }

  @Override
  public SourceVersion getSupportedSourceVersion() {
    return SourceVersion.latestSupported();
  }

  /**
   * Checks if the element is a class
   */
  private boolean isClass(Element element) {
    if (element.getKind() == ElementKind.CLASS) {

      if (element.getModifiers().contains(Modifier.ABSTRACT)) {
        ProcessorMessage.error(element,
            "Element %s is annotated with @%s but is an abstract class. "
                + "Abstract classes can not be annotated. Annotate the concrete class "
                + "that implements all abstract methods with @%s", element.getSimpleName(),
            ParcelablePlease.class.getSimpleName(), ParcelablePlease.class.getSimpleName());
        return false;
      }

      if (element.getModifiers().contains(Modifier.PRIVATE)) {
        ProcessorMessage.error(element, "The private class %s is annotated with @%s. "
                + "Private classes are not supported because of lacking visibility.",
            element.getSimpleName(), ParcelablePlease.class.getSimpleName());
        return false;
      }

      // Ok, its a valid class
      return true;
    } else {
      ProcessorMessage.error(element,
          "Element %s is annotated with @%s but is not a class. Only Classes are supported",
          element.getSimpleName(), ParcelablePlease.class.getSimpleName());
      return false;
    }
  }
}


package com.hannesdorfmann.parcelableplease.processor.util;

import java.io.IOException;
import java.util.List;
import javax.lang.model.element.Element;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;

/**
 * @author Hannes Dorfmann
 */
public class TypeUtils {

  /**
   * Checks if a element is of the type
   */
  public static boolean isTypeOf(Element element, Class<?> clazz) {
    return isTypeOf(element.asType(), clazz);
  }

  /**
   * Checks if a TypeMirror equals a class
   */
  public static boolean isTypeOf(TypeMirror type, Class<?> clazz) {
    return type.toString().equals(clazz.getCanonicalName());
  }

  /**
   * Returns the class this element is part of
   */
  public static Class<?> isTypeOf(TypeMirror type, List<Class<?>> classList) {

    for (Class<?> c : classList) {
      if (isTypeOf(type, c)) {
        return c;
      }
    }

    return null;
  }

  /**
   * Get the Package name
   *
   * @throws IOException
   */
  public static String getPackageName(Elements elementUtils, TypeElement type) throws IOException {
    PackageElement pkg = elementUtils.getPackageOf(type);
    if (!pkg.isUnnamed()) {
      return pkg.getQualifiedName().toString();
    } else {
      return ""; // Default package
    }
  }

  /**
   * Get the qualified name of a class. Also respects innner classes
   *
   * @param elementUtils Elements
   * @param type TypeElement
   * @return The qualified name of the class represented by the passed type element
   * @throws IOException
   */
  public static String getBinaryName(Elements elementUtils, TypeElement type) throws IOException {
    String packageName = getPackageName(elementUtils, type);
    String qualifiedName = type.getQualifiedName().toString();
    if (packageName.length() > 0) {
      return packageName + '.' + qualifiedName.substring(packageName.length() + 1).replace('.', '$');
    } else {
      return qualifiedName.replace('.', '$');
    }
  }
}


package com.hannesdorfmann.parcelableplease.processor.codegenerator;

import com.hannesdorfmann.parcelableplease.annotation.Bagger;
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
import com.hannesdorfmann.parcelableplease.processor.ParcelableField;
import com.hannesdorfmann.parcelableplease.processor.ProcessorMessage;
import com.hannesdorfmann.parcelableplease.processor.util.TypeUtils;
import java.io.IOException;
import java.io.Writer;
import java.util.EnumSet;
import java.util.List;
import javax.annotation.processing.Filer;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.JavaFileObject;
import repacked.com.squareup.javawriter.JavaWriter;

/**
 * Generates the javacode for each field
 *
 * @author Hannes Dorfmann
 */
public class CodeGenerator {

  public static final String PARAM_TARGET = "target";

  public static final String PARAM_SOURCE = "source";

  public static final String PARAM_FLAGS = "flags";

  /**
   * The parameter name for the
   */
  public static final String PARAM_PARCEL = "parcel";

  private Filer filer;
  private Elements elementUtils;

  public CodeGenerator(Elements elementUtils, Filer filer) {
    this.filer = filer;
    this.elementUtils = elementUtils;
  }

  public void generate(TypeElement classElement, List<ParcelableField> fields) throws Exception {

    String classSuffix = "ParcelablePlease";
    String packageName = TypeUtils.getPackageName(elementUtils, classElement);
    String binaryName = TypeUtils.getBinaryName(elementUtils, classElement);
    String originFullQualifiedName = classElement.getQualifiedName().toString();
    String className;
    if (packageName.length() > 0) {
      className = binaryName.substring(packageName.length() + 1) + classSuffix;
    } else {
      className = binaryName + classSuffix;
    }
    String qualifiedName = binaryName + classSuffix;

    //
    // Write code
    //

    JavaFileObject jfo = filer.createSourceFile(qualifiedName, classElement);
    Writer writer = jfo.openWriter();
    JavaWriter jw = new JavaWriter(writer);

    jw.emitPackage(packageName);
    jw.emitImports("android.os.Parcel");
    jw.emitEmptyLine();
    jw.emitJavadoc("Generated class by @%s . Do not modify this code!",
        ParcelablePlease.class.getSimpleName());
    jw.beginType(className, "class", EnumSet.of(Modifier.PUBLIC));
    jw.emitEmptyLine();

    generateWriteToParcel(jw, originFullQualifiedName, fields);
    jw.emitEmptyLine();
    generateReadFromParcel(jw, originFullQualifiedName, fields);

    jw.endType();
    jw.close();
  }

  /**
   * Generate the writeToParcel method
   *
   * @throws IOException
   */
  private void generateWriteToParcel(JavaWriter jw, String originClass,
      List<ParcelableField> fields) throws IOException {

    jw.beginMethod("void", "writeToParcel", EnumSet.of(Modifier.PUBLIC, Modifier.STATIC),
        originClass, PARAM_SOURCE, "Parcel", PARAM_PARCEL, "int", PARAM_FLAGS);

    for (ParcelableField field : fields) {
      FieldCodeGen gen = field.getCodeGenerator();

      if (gen == null) { // Already checked before, but let's check it again
        ProcessorMessage.error(field.getElement(),
            "The field %s is not Parcelable or of unsupported type. Use a @%s",
            field.getFieldName(),
            Bagger.class.getSimpleName() + " to provide your own serialisation mechanism");

        throw new IOException("Unparcelable Field " + field.getFieldName());
      }

      jw.emitEmptyLine();
      gen.generateWriteToParcel(field, jw);
    }

    jw.endMethod();
  }

  private void generateReadFromParcel(JavaWriter jw, String originClass,
      List<ParcelableField> fields) throws IOException {

    jw.beginMethod("void", "readFromParcel", EnumSet.of(Modifier.PUBLIC, Modifier.STATIC),
        originClass, PARAM_TARGET, "Parcel", PARAM_PARCEL);

    for (ParcelableField field : fields) {
      FieldCodeGen gen = field.getCodeGenerator();

      if (gen == null) { // Already checked before, but let's check it again
        ProcessorMessage.error(field.getElement(),
            "The field %s is not Parcelable or of unsupported type. Use a @%s",
            field.getFieldName(),
            Bagger.class.getSimpleName() + " to provide your own serialisation mechanism");

        throw new IOException("Unparcelable Field " + field.getFieldName());
      }

      jw.emitEmptyLine();
      gen.generateReadFromParcel(field, jw);
    }

    jw.endMethod();
  }
}


package com.hannesdorfmann.parcelableplease.processor;

import com.hannesdorfmann.parcelableplease.ParcelBagger;
import com.hannesdorfmann.parcelableplease.annotation.Bagger;
import com.hannesdorfmann.parcelableplease.processor.codegenerator.BaggerCodeGen;
import com.hannesdorfmann.parcelableplease.processor.codegenerator.FieldCodeGen;
import com.hannesdorfmann.parcelableplease.processor.codegenerator.SupportedTypes;
import com.hannesdorfmann.parcelableplease.processor.util.CodeGenInfo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.List;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.MirroredTypeException;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;

// TODO refactor this class in interfaces and sublacsses for BaggerAnnotated, normal and Generic fields

/**
 * Scans a field and collects information for the java code generating process
 *
 * @author Hannes Dorfmann
 */
public class ParcelableField {

  private String fieldName;
  private String type;
  private Element element;
  private FieldCodeGen codeGenerator;
  private TypeMirror genericsTypeArgument;
  private String baggerFullyQualifiedName;

  public ParcelableField(VariableElement element, Elements elementUtils, Types typeUtils) {

    this.element = element;
    fieldName = element.getSimpleName().toString();
    type = element.asType().toString();

    // Check for Bagger
    Bagger baggerAnnotation = element.getAnnotation(Bagger.class);
    if (baggerAnnotation != null) {
      // has a bagger annotation
      try {
        Class<? extends ParcelBagger> clazz = baggerAnnotation.value();
        baggerFullyQualifiedName = getFullQualifiedNameByClass(clazz);
      } catch (MirroredTypeException mte) {
        TypeMirror baggerClass = mte.getTypeMirror();
        baggerFullyQualifiedName = getFullQualifiedNameByTypeMirror(baggerClass);
      }

      // Everything is fine, so use the bagger
      codeGenerator = new BaggerCodeGen();
    } else {
      // Not using Bagger
      CodeGenInfo res = SupportedTypes.getCodeGenInfo(element, elementUtils, typeUtils);
      codeGenerator = res.getCodeGenerator();
      genericsTypeArgument = res.getGenericsType();

      // Check if type is supported
      if (codeGenerator == null) {
        ProcessorMessage.error(element,
            "Unsupported type %s for field %s. You could use @%s to provide your own serialization mechanism",
            element.asType().toString(), element.getSimpleName(), Bagger.class.getSimpleName());
      }
    }
  }

  private String getFullQualifiedNameByClass(Class<? extends ParcelBagger> clazz) {

    // Check public
    if (!Modifier.isPublic(clazz.getModifiers())) {

      ProcessorMessage.error(element, "The %s must be a public class to be a valid Bagger",
          clazz.getCanonicalName());
      return null;
    }

    // Check constructors
    Constructor<?>[] constructors = clazz.getConstructors();

    boolean foundDefaultConstructor = false;
    for (Constructor c : constructors) {
      boolean isPublicConstructor = Modifier.isPublic(c.getModifiers());
      Class<?>[] pType = c.getParameterTypes();

      if (pType.length == 0 && isPublicConstructor) {
        foundDefaultConstructor = true;
        break;
      }
    }

    if (!foundDefaultConstructor) {
      ProcessorMessage.error(element, "The %s must provide a public empty default constructor",
          clazz.getCanonicalName());
      return null;
    }

    return clazz.getCanonicalName();
  }

  /**
   * Checks if a class is public
   */
  private boolean isPublicClass(DeclaredType type) {
    Element element = type.asElement();

    return element.getModifiers().contains(javax.lang.model.element.Modifier.PUBLIC);
  }

  /**
   * Checks if an public empty constructor is available
   */
  private boolean hasPublicEmptyConstructor(DeclaredType type) {
    Element element = type.asElement();

    List<? extends Element> containing = element.getEnclosedElements();

    for (Element e : containing) {
      if (e.getKind() == ElementKind.CONSTRUCTOR) {
        ExecutableElement c = (ExecutableElement) e;

        if ((c.getParameters() == null || c.getParameters().isEmpty()) && c.getModifiers()
            .contains(javax.lang.model.element.Modifier.PUBLIC)) {
          return true;
        }
      }
    }

    return false;
  }

  private String getFullQualifiedNameByTypeMirror(TypeMirror baggerClass) {
    if (baggerClass == null) {
      ProcessorMessage.error(element, "Could not get the bagger class");
      return null;
    }

    if (baggerClass.getKind() != TypeKind.DECLARED) {
      ProcessorMessage.error(element, "@ %s  is not a class in %s ", Bagger.class.getSimpleName(),
          element.getSimpleName());
      return null;
    }

    if (!isPublicClass((DeclaredType) baggerClass)) {
      ProcessorMessage.error(element, "The %s must be a public class to be a valid Bagger",
          baggerClass.toString());
      return null;
    }

    // Check if the bagger class has a default constructor
    if (!hasPublicEmptyConstructor((DeclaredType) baggerClass)) {
      ProcessorMessage.error(element,
          "The %s must provide a public empty default constructor to be a valid Bagger",
          baggerClass.toString());
      return null;
    }

    return baggerClass.toString();
  }

  public Element getElement() {
    return element;
  }

  public String getFieldName() {
    return fieldName;
  }

  public String getType() {
    return type;
  }

  public String getFullQualifiedBaggerName() {
    return baggerFullyQualifiedName;
  }

  public FieldCodeGen getCodeGenerator() {
    return codeGenerator;
  }

  public TypeMirror getGenericsTypeArgument() {
    return genericsTypeArgument;
  }
}


/*
 * Copyright © 2017 <code@io7m.com> http://io7m.com
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.io7m.jtensors.orthonormalization;

import com.io7m.jtensors.core.parameterized.vectors.PVector3D;
import com.io7m.jtensors.core.parameterized.vectors.PVector4D;
import com.io7m.jtensors.core.parameterized.vectors.PVectors3D;
import com.io7m.jtensors.core.parameterized.vectors.PVectors4D;
import com.io7m.junreachable.UnreachableCodeException;

/**
 * Functions to orthonormalize sets of vectors.
 */

public final class POrthonormalization
{
  private POrthonormalization()
  {
    throw new UnreachableCodeException();
  }

  /**
   * Orthonormalize the given vectors.
   *
   * @param v0  Vector 0
   * @param v1  Vector 1
   * @param v2  Vector 2
   * @param <T> A phantom type parameter
   *
   * @return The set of vectors, orthonormalized
   */

  public static <T> POrthonormalized3D<T> orthonormalize3D(
    final PVector3D<T> v0,
    final PVector3D<T> v1,
    final PVector3D<T> v2)
  {
    final PVector3D<T> rv0 = PVectors3D.normalize(v0);
    final PVector3D<T> rv1;
    final PVector3D<T> rv2;

    {
      final double v0_dot_v1 = PVectors3D.dotProduct(rv0, v1);
      final PVector3D<T> v0_s = PVectors3D.scale(rv0, v0_dot_v1);
      rv1 = PVectors3D.normalize(PVectors3D.subtract(v1, v0_s));
    }

    {
      final double v1_dot_v2 = PVectors3D.dotProduct(rv1, v2);
      final double v0_dot_v2 = PVectors3D.dotProduct(rv0, v2);

      final PVector3D<T> v0_s = PVectors3D.scale(rv0, v0_dot_v2);
      final PVector3D<T> v2_s = PVectors3D.scale(rv1, v1_dot_v2);
      final PVector3D<T> vs = PVectors3D.add(v0_s, v2_s);
      rv2 = PVectors3D.normalize(PVectors3D.subtract(v2, vs));
    }

    return POrthonormalized3D.of(rv0, rv1, rv2);
  }

  /**
   * Orthonormalize the given vectors.
   *
   * @param v0  Vector 0
   * @param v1  Vector 1
   * @param v2  Vector 2
   * @param <T> A phantom type parameter
   *
   * @return The set of vectors, orthonormalized
   */

  public static <T> POrthonormalized4D<T> orthonormalize4D(
    final PVector4D<T> v0,
    final PVector4D<T> v1,
    final PVector4D<T> v2)
  {
    final PVector4D<T> rv0 = PVectors4D.normalize(v0);
    final PVector4D<T> rv1;
    final PVector4D<T> rv2;

    {
      final double v0_dot_v1 = PVectors4D.dotProduct(rv0, v1);
      final PVector4D<T> v0_s = PVectors4D.scale(rv0, v0_dot_v1);
      rv1 = PVectors4D.normalize(PVectors4D.subtract(v1, v0_s));
    }

    {
      final double v1_dot_v2 = PVectors4D.dotProduct(rv1, v2);
      final double v0_dot_v2 = PVectors4D.dotProduct(rv0, v2);

      final PVector4D<T> v0_s = PVectors4D.scale(rv0, v0_dot_v2);
      final PVector4D<T> v2_s = PVectors4D.scale(rv1, v1_dot_v2);
      final PVector4D<T> vs = PVectors4D.add(v0_s, v2_s);
      rv2 = PVectors4D.normalize(PVectors4D.subtract(v2, vs));
    }

    return POrthonormalized4D.of(rv0, rv1, rv2);
  }
}


/*
 * Copyright © 2017  http://io7m.com
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.io7m.jtensors.storage.bytebuffered;

import com.io7m.mutable.numbers.core.MutableLongType;

import java.nio.ByteBuffer;

/**
 *
A storage vector.

 *
Storage component type: {@code short}

 *
Storage component count: {@code 2}

 *
 * @param <T> A phantom type parameter
 */

public final class PVectorByteBufferedIntegral2s16<T>
  extends TensorByteBufferedIntegral16
  implements PVectorByteBufferedIntegral2Type<T>
{
  private PVectorByteBufferedIntegral2s16(
    final ByteBuffer in_buffer,
    final MutableLongType in_base,
    final int in_offset)
  {
    super(in_buffer, in_base, in_offset);
  }

  /**
   *
Return a new vector that is backed by the given byte buffer {@code
   * b}

   *
   *
The data for the instance will be taken from the data at the current
   * value of {@code base.get() + offset}, each time a field is requested or
   * set.

   *
   *
No initialization of the data is performed.

   *
   * @param <T>    A phantom type parameter
   * @param b      The byte buffer
   * @param base   The base address
   * @param offset A constant offset
   *
   * @return A new buffered vector
   */

  public static <T> PVectorByteBufferedIntegral2s16<T> createWithBase(
    final ByteBuffer b,
    final MutableLongType base,
    final int offset)
  {
    return new PVectorByteBufferedIntegral2s16<>(b, base, offset);
  }

  @Override
  protected int componentCount()
  {
    return 2;
  }

  @Override
  public long x()
  {
    return this.getValue(0);
  }

  @Override
  public long y()
  {
    return this.getValue(1);
  }

  @Override
  public void setX(final long x)
  {
    this.putValue(0, x);
  }

  @Override
  public void setY(final long y)
  {
    this.putValue(1, y);
  }
}


/*
 * Copyright © 2017 <code@io7m.com> http://io7m.com
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.io7m.jtensors.generators;

import java.util.Objects;
import com.io7m.jtensors.core.parameterized.vectors.PVector4F;
import net.java.quickcheck.Generator;
import net.java.quickcheck.generator.PrimitiveGenerators;

/**
 * A vector generator.
 *
 * @param <T>
 */

public final class PVector4FGenerator<T> implements Generator<PVector4F<T>>
{
  private final Generator<Double> gen;

  /**
   * Construct a generator.
   *
   * @param in_gen A component generator
   */

  public PVector4FGenerator(
    final Generator<Double> in_gen)
  {
    this.gen = Objects.requireNonNull(in_gen, "Generator");
  }

  /**
   * Create a generator initialized with a default component generator.
   *
   * @param <T> A phantom type parameter
   *
   * @return A generator
   */

  public static <T> Generator<PVector4F<T>> create()
  {
    return new PVector4FGenerator<>(PrimitiveGenerators.doubles(
      GeneratorConstants.BOUND_LARGE_FLOAT_LOWER,
      GeneratorConstants.BOUND_LARGE_FLOAT_UPPER
    ));
  }

  /**
   * Create a generator initialized with a default component generator that
   * produces values in the range {@code [-65536.0, 65536.0]}.
   *
   * @param <T> A phantom type parameter
   *
   * @return A generator
   */

  public static <T> Generator<PVector4F<T>> createSmall()
  {
    return new PVector4FGenerator<>(PrimitiveGenerators.doubles(
      GeneratorConstants.BOUND_SMALL_FLOAT_LOWER,
      GeneratorConstants.BOUND_SMALL_FLOAT_UPPER
    ));
  }

  /**
   * Create a generator initialized with a default component generator that
   * produces values in the range {@code [-1.0, 1.0]}.
   *
   * @param <T> A phantom type parameter
   *
   * @return A generator
   */

  public static <T> Generator<PVector4F<T>> createNormal()
  {
    return new PVector4FGenerator<>(PrimitiveGenerators.doubles(
      GeneratorConstants.BOUND_NORMAL_FLOAT_LOWER,
      GeneratorConstants.BOUND_NORMAL_FLOAT_UPPER
    ));
  }

  @Override
  public PVector4F<T> next()
  {
    return PVector4F.of(
      this.gen.next().floatValue(),
      this.gen.next().floatValue(),
      this.gen.next().floatValue(),
      this.gen.next().floatValue());
  }
}


/*
 * Copyright © 2017  http://io7m.com
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.io7m.jtensors.storage.bytebuffered;

import com.io7m.mutable.numbers.core.MutableLongType;

import java.nio.ByteBuffer;

/**
 *
A storage vector.

 *
Storage component type: {@code long}

 *
Storage component count: {@code 2}

 *
 * @param <T> A phantom type parameter
 */

public final class PVectorByteBufferedIntegral2s64<T>
  extends TensorByteBufferedIntegral64
  implements PVectorByteBufferedIntegral2Type<T>
{
  private PVectorByteBufferedIntegral2s64(
    final ByteBuffer in_buffer,
    final MutableLongType in_base,
    final int in_offset)
  {
    super(in_buffer, in_base, in_offset);
  }

  /**
   *
Return a new vector that is backed by the given byte buffer {@code
   * b}

   *
   *
The data for the instance will be taken from the data at the current
   * value of {@code base.get() + offset}, each time a field is requested or
   * set.

   *
   *
No initialization of the data is performed.

   *
   * @param <T>    A phantom type parameter
   * @param b      The byte buffer
   * @param base   The base address
   * @param offset A constant offset
   *
   * @return A new buffered vector
   */

  public static <T> PVectorByteBufferedIntegral2s64<T> createWithBase(
    final ByteBuffer b,
    final MutableLongType base,
    final int offset)
  {
    return new PVectorByteBufferedIntegral2s64<>(b, base, offset);
  }

  @Override
  protected int componentCount()
  {
    return 2;
  }

  @Override
  public long x()
  {
    return this.getValue(0);
  }

  @Override
  public long y()
  {
    return this.getValue(1);
  }

  @Override
  public void setX(final long x)
  {
    this.putValue(0, x);
  }

  @Override
  public void setY(final long y)
  {
    this.putValue(1, y);
  }
}


/*
 * Copyright © 2017 <code@io7m.com> http://io7m.com
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.io7m.jtensors.core.determinants;

import com.io7m.junreachable.UnreachableCodeException;

/**
 * Functions for calculating determinants.
 */

public final class Determinants
{
  private Determinants()
  {
    throw new UnreachableCodeException();
  }

  /**
   * Calculate the determinant of the given 2x2 matrix values.
   *
   * @param r0c0 The value at row 0 column 0
   * @param r0c1 The value at row 0 column 1
   * @param r1c0 The value at row 1 column 0
   * @param r1c1 The value at row 1 column 1
   *
   * @return The determinant
   */

  public static double determinant2x2(
    final double r0c0,
    final double r0c1,
    final double r1c0,
    final double r1c1)
  {
    return (r0c0 * r1c1) - (r0c1 * r1c0);
  }

  /**
   * Calculate the determinant of the given 3x3 matrix values.
   *
   * @param r0c0 The value at row 0 column 0
   * @param r0c1 The value at row 0 column 1
   * @param r0c2 The value at row 0 column 2
   * @param r1c0 The value at row 1 column 0
   * @param r1c1 The value at row 1 column 1
   * @param r1c2 The value at row 1 column 2
   * @param r2c0 The value at row 2 column 0
   * @param r2c1 The value at row 2 column 1
   * @param r2c2 The value at row 2 column 2
   *
   * @return The determinant
   */

  public static double determinant3x3(
    final double r0c0,
    final double r0c1,
    final double r0c2,
    final double r1c0,
    final double r1c1,
    final double r1c2,
    final double r2c0,
    final double r2c1,
    final double r2c2)
  {
    double sum = 0.0;
    sum += r0c0 * ((r1c1 * r2c2) - (r1c2 * r2c1));
    sum -= r0c1 * ((r1c0 * r2c2) - (r1c2 * r2c0));
    sum += r0c2 * ((r1c0 * r2c1) - (r1c1 * r2c0));
    return sum;
  }

  /**
   * Calculate the determinant of the given 4x4 matrix values.
   *
   * @param r0c0 The value at row 0 column 0
   * @param r0c1 The value at row 0 column 1
   * @param r0c2 The value at row 0 column 2
   * @param r0c3 The value at row 0 column 3
   * @param r1c0 The value at row 1 column 0
   * @param r1c1 The value at row 1 column 1
   * @param r1c2 The value at row 1 column 2
   * @param r1c3 The value at row 1 column 3
   * @param r2c0 The value at row 2 column 0
   * @param r2c1 The value at row 2 column 1
   * @param r2c2 The value at row 2 column 2
   * @param r2c3 The value at row 2 column 3
   * @param r3c0 The value at row 3 column 0
   * @param r3c1 The value at row 3 column 1
   * @param r3c2 The value at row 3 column 2
   * @param r3c3 The value at row 3 column 3
   *
   * @return The determinant
   */

  // CHECKSTYLE:OFF
  public static double determinant4x4(
    final double r0c0,
    final double r0c1,
    final double r0c2,
    final double r0c3,
    final double r1c0,
    final double r1c1,
    final double r1c2,
    final double r1c3,
    final double r2c0,
    final double r2c1,
    final double r2c2,
    final double r2c3,
    final double r3c0,
    final double r3c1,
    final double r3c2,
    final double r3c3)
  // CHECKSTYLE:ON
  {
    double sum = 0.0;

    sum += r0c0 * r1c1 * r2c2 * r3c3;
    sum -= r0c0 * r1c1 * r2c3 * r3c2;
    sum += r0c0 * r1c2 * r2c3 * r3c1;
    sum -= r0c0 * r1c2 * r2c1 * r3c3;

    sum += r0c0 * r1c3 * r2c1 * r3c2;
    sum -= r0c0 * r1c3 * r2c2 * r3c1;
    sum -= r0c1 * r1c2 * r2c3 * r3c0;
    sum += r0c1 * r1c2 * r2c0 * r3c3;

    sum -= r0c1 * r1c3 * r2c0 * r3c2;
    sum += r0c1 * r1c3 * r2c2 * r3c0;
    sum -= r0c1 * r1c0 * r2c2 * r3c3;
    sum += r0c1 * r1c0 * r2c3 * r3c2;

    sum += r0c2 * r1c3 * r2c0 * r3c1;
    sum -= r0c2 * r1c3 * r2c1 * r3c0;
    sum += r0c2 * r1c0 * r2c1 * r3c3;
    sum -= r0c2 * r1c0 * r2c3 * r3c1;

    sum += r0c2 * r1c1 * r2c3 * r3c0;
    sum -= r0c2 * r1c1 * r2c0 * r3c3;
    sum -= r0c3 * r1c0 * r2c1 * r3c2;
    sum += r0c3 * r1c0 * r2c2 * r3c1;

    sum -= r0c3 * r1c1 * r2c2 * r3c0;
    sum += r0c3 * r1c1 * r2c0 * r3c2;
    sum -= r0c3 * r1c2 * r2c0 * r3c1;
    sum += r0c3 * r1c2 * r2c1 * r3c0;

    return sum;
  }
}


/*
 * Copyright © 2017  http://io7m.com
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.io7m.jtensors.storage.bytebuffered;

import com.io7m.mutable.numbers.core.MutableLongType;

import java.nio.ByteBuffer;

/**
 *
A storage vector.

 *
Storage component type: {@code float}

 *
Storage component count: {@code 3}

 */

public final class VectorByteBufferedFloating3s32
  extends TensorByteBufferedFloating32
  implements VectorByteBufferedFloating3Type
{
  private VectorByteBufferedFloating3s32(
    final ByteBuffer in_buffer,
    final MutableLongType in_base,
    final int in_offset)
  {
    super(in_buffer, in_base, in_offset);
  }

  /**
   *
Return a new vector that is backed by the given byte buffer {@code
   * b}

   *
   *
The data for the instance will be taken from the data at the current
   * value of {@code base.get() + offset}, each time a field is requested or
   * set.

   *
   *
No initialization of the data is performed.

   *
   * @param b      The byte buffer
   * @param base   The base address
   * @param offset A constant offset
   *
   * @return A new buffered vector
   */

  public static VectorByteBufferedFloating3s32 createWithBase(
    final ByteBuffer b,
    final MutableLongType base,
    final int offset)
  {
    return new VectorByteBufferedFloating3s32(b, base, offset);
  }

  @Override
  protected int componentCount()
  {
    return 3;
  }

  @Override
  public double x()
  {
    return this.getValue(0);
  }

  @Override
  public double y()
  {
    return this.getValue(1);
  }

  @Override
  public double z()
  {
    return this.getValue(2);
  }

  @Override
  public void setX(final double x)
  {
    this.putValue(0, x);
  }

  @Override
  public void setY(final double y)
  {
    this.putValue(1, y);
  }

  @Override
  public void setZ(final double z)
  {
    this.putValue(2, z);
  }
}


/*
 * Copyright © 2017 <code@io7m.com> http://io7m.com
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.io7m.jtensors.generators;

import java.util.Objects;
import com.io7m.jtensors.core.unparameterized.vectors.Vector4F;
import net.java.quickcheck.Generator;
import net.java.quickcheck.generator.PrimitiveGenerators;

/**
 * A vector generator.
 */

public final class Vector4FGenerator implements Generator<Vector4F>
{
  private final Generator<Double> gen;

  /**
   * Construct a generator.
   *
   * @param in_gen A component generator
   */

  public Vector4FGenerator(
    final Generator<Double> in_gen)
  {
    this.gen = Objects.requireNonNull(in_gen, "Generator");
  }

  /**
   * Create a generator initialized with a default component generator.
   *
   * @return A generator
   */

  public static Generator<Vector4F> create()
  {
    return new Vector4FGenerator(PrimitiveGenerators.doubles(
      GeneratorConstants.BOUND_LARGE_FLOAT_LOWER,
      GeneratorConstants.BOUND_LARGE_FLOAT_UPPER
    ));
  }

  /**
   * Create a generator initialized with a default component generator that
   * produces values in the range {@code [-65536.0, 65536.0]}.
   *
   * @return A generator
   */

  public static Generator<Vector4F> createSmall()
  {
    return new Vector4FGenerator(PrimitiveGenerators.doubles(
      GeneratorConstants.BOUND_SMALL_FLOAT_LOWER,
      GeneratorConstants.BOUND_SMALL_FLOAT_UPPER
    ));
  }

  /**
   * Create a generator initialized with a default component generator that
   * produces values in the range {@code [-1.0, 1.0]}.
   *
   * @return A generator
   */

  public static Generator<Vector4F> createNormal()
  {
    return new Vector4FGenerator(PrimitiveGenerators.doubles(
      GeneratorConstants.BOUND_NORMAL_FLOAT_LOWER,
      GeneratorConstants.BOUND_NORMAL_FLOAT_UPPER
    ));
  }

  @Override
  public Vector4F next()
  {
    return Vector4F.of(
      this.gen.next().floatValue(),
      this.gen.next().floatValue(),
      this.gen.next().floatValue(),
      this.gen.next().floatValue());
  }
}


/*
 * Copyright © 2017  http://io7m.com
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.io7m.jtensors.storage.bytebuffered;

import com.io7m.mutable.numbers.core.MutableLongType;
import com.io7m.jtensors.core.unparameterized.matrices.Matrix2x2D;
import com.io7m.jtensors.core.unparameterized.matrices.Matrix2x2F;

import java.nio.ByteBuffer;

/**
 *
A storage matrix.

 *
Storage component type: {@code binary16}

 *
Storage component count: {@code 2x2}

 */

public final class MatrixByteBuffered2x2s16
  extends TensorByteBufferedFloating16
  implements MatrixByteBuffered2x2Type
{
  private MatrixByteBuffered2x2s16(
    final ByteBuffer in_buffer,
    final MutableLongType in_base,
    final int in_offset)
  {
    super(in_buffer, in_base, in_offset);
  }

  /**
   *
Return a new vector that is backed by the given byte buffer {@code
   * b}

   *
   *
The data for the instance will be taken from the data at the current
   * value of {@code base.get() + offset}, each time a field is requested or
   * set.

   *
   *
No initialization of the data is performed.

   *
   * @param b      The byte buffer
   * @param base   The base address
   * @param offset A constant offset
   *
   * @return A new buffered vector
   */

  public static MatrixByteBuffered2x2s16 createWithBase(
    final ByteBuffer b,
    final MutableLongType base,
    final int offset)
  {
    return new MatrixByteBuffered2x2s16(b, base, offset);
  }

  @Override
  protected int componentCount()
  {
    return 2 * 2;
  }

  @Override
  public double r0c0()
  {
    return this.getValue(ColumnMajor.index2x2(0, 0));
  }

  @Override
  public double r0c1()
  {
    return this.getValue(ColumnMajor.index2x2(0, 1));
  }

  @Override
  public double r1c0()
  {
    return this.getValue(ColumnMajor.index2x2(1, 0));
  }

  @Override
  public double r1c1()
  {
    return this.getValue(ColumnMajor.index2x2(1, 1));
  }

  @Override
  public void setMatrix2x2D(final Matrix2x2D m)
  {
    this.putValue(ColumnMajor.index2x2(0, 0), m.r0c0());
    this.putValue(ColumnMajor.index2x2(0, 1), m.r0c1());

    this.putValue(ColumnMajor.index2x2(1, 0), m.r1c0());
    this.putValue(ColumnMajor.index2x2(1, 1), m.r1c1());
  }

  @Override
  public void setMatrix2x2F(final Matrix2x2F m)
  {
    this.putValue(ColumnMajor.index2x2(0, 0), (double) m.r0c0());
    this.putValue(ColumnMajor.index2x2(0, 1), (double) m.r0c1());

    this.putValue(ColumnMajor.index2x2(1, 0), (double) m.r1c0());
    this.putValue(ColumnMajor.index2x2(1, 1), (double) m.r1c1());
  }
}


/*
 * Copyright © 2017  http://io7m.com
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.io7m.jtensors.storage.bytebuffered;

import com.io7m.mutable.numbers.core.MutableLongType;

import java.nio.ByteBuffer;

/**
 *
A storage vector.

 *
Storage component type: {@code double}

 *
Storage component count: {@code 2}

 */

public final class VectorByteBufferedFloating2s64
  extends TensorByteBufferedFloating64
  implements VectorByteBufferedFloating2Type
{
  private VectorByteBufferedFloating2s64(
    final ByteBuffer in_buffer,
    final MutableLongType in_base,
    final int in_offset)
  {
    super(in_buffer, in_base, in_offset);
  }

  /**
   *
Return a new vector that is backed by the given byte buffer {@code
   * b}

   *
   *
The data for the instance will be taken from the data at the current
   * value of {@code base.get() + offset}, each time a field is requested or
   * set.

   *
   *
No initialization of the data is performed.

   *
   * @param b      The byte buffer
   * @param base   The base address
   * @param offset A constant offset
   *
   * @return A new buffered vector
   */

  public static VectorByteBufferedFloating2s64 createWithBase(
    final ByteBuffer b,
    final MutableLongType base,
    final int offset)
  {
    return new VectorByteBufferedFloating2s64(b, base, offset);
  }

  @Override
  protected int componentCount()
  {
    return 2;
  }

  @Override
  public double x()
  {
    return this.getValue(0);
  }

  @Override
  public double y()
  {
    return this.getValue(1);
  }

  @Override
  public void setX(final double x)
  {
    this.putValue(0, x);
  }

  @Override
  public void setY(final double y)
  {
    this.putValue(1, y);
  }
}


/*
 * Copyright © 2017  http://io7m.com
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.io7m.jtensors.storage.bytebuffered;

import com.io7m.mutable.numbers.core.MutableLongType;

import java.nio.ByteBuffer;

/**
 *
A storage vector.

 *
Storage component type: {@code int}

 *
Storage component count: {@code 3}

 */

public final class VectorByteBufferedIntegral3s32
  extends TensorByteBufferedIntegral32
  implements VectorByteBufferedIntegral3Type
{
  private VectorByteBufferedIntegral3s32(
    final ByteBuffer in_buffer,
    final MutableLongType in_base,
    final int in_offset)
  {
    super(in_buffer, in_base, in_offset);
  }

  /**
   *
Return a new vector that is backed by the given byte buffer {@code
   * b}

   *
   *
The data for the instance will be taken from the data at the current
   * value of {@code base.get() + offset}, each time a field is requested or
   * set.

   *
   *
No initialization of the data is performed.

   *
   * @param b      The byte buffer
   * @param base   The base address
   * @param offset A constant offset
   *
   * @return A new buffered vector
   */

  public static VectorByteBufferedIntegral3s32 createWithBase(
    final ByteBuffer b,
    final MutableLongType base,
    final int offset)
  {
    return new VectorByteBufferedIntegral3s32(b, base, offset);
  }

  @Override
  protected int componentCount()
  {
    return 3;
  }

  @Override
  public long x()
  {
    return this.getValue(0);
  }

  @Override
  public long y()
  {
    return this.getValue(1);
  }

  @Override
  public long z()
  {
    return this.getValue(2);
  }

  @Override
  public void setX(final long x)
  {
    this.putValue(0, x);
  }

  @Override
  public void setY(final long y)
  {
    this.putValue(1, y);
  }

  @Override
  public void setZ(final long z)
  {
    this.putValue(2, z);
  }
}


/*
 * Copyright © 2017  http://io7m.com
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.io7m.jtensors.storage.bytebuffered;

import com.io7m.mutable.numbers.core.MutableLongType;

import java.nio.ByteBuffer;

/**
 *
A storage vector.

 *
Storage component type: {@code byte}

 *
Storage component count: {@code 2}

 */

public final class VectorByteBufferedIntegral2s8
  extends TensorByteBufferedIntegral8
  implements VectorByteBufferedIntegral2Type
{
  private VectorByteBufferedIntegral2s8(
    final ByteBuffer in_buffer,
    final MutableLongType in_base,
    final int in_offset)
  {
    super(in_buffer, in_base, in_offset);
  }

  /**
   *
Return a new vector that is backed by the given byte buffer {@code
   * b}

   *
   *
The data for the instance will be taken from the data at the current
   * value of {@code base.get() + offset}, each time a field is requested or
   * set.

   *
   *
No initialization of the data is performed.

   *
   * @param b      The byte buffer
   * @param base   The base address
   * @param offset A constant offset
   *
   * @return A new buffered vector
   */

  public static VectorByteBufferedIntegral2s8 createWithBase(
    final ByteBuffer b,
    final MutableLongType base,
    final int offset)
  {
    return new VectorByteBufferedIntegral2s8(b, base, offset);
  }

  @Override
  protected int componentCount()
  {
    return 2;
  }

  @Override
  public long x()
  {
    return this.getValue(0);
  }

  @Override
  public long y()
  {
    return this.getValue(1);
  }

  @Override
  public void setX(final long x)
  {
    this.putValue(0, x);
  }

  @Override
  public void setY(final long y)
  {
    this.putValue(1, y);
  }
}


/*
 * Copyright © 2017  http://io7m.com
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.io7m.jtensors.storage.bytebuffered;

import com.io7m.mutable.numbers.core.MutableLongType;

import java.nio.ByteBuffer;

/**
 *
A storage vector.

 *
Storage component type: {@code float}

 *
Storage component count: {@code 3}

 *
 * @param <T> A phantom type parameter
 */

public final class PVectorByteBufferedFloating3s32<T>
  extends TensorByteBufferedFloating32
  implements PVectorByteBufferedFloating3Type<T>
{
  private PVectorByteBufferedFloating3s32(
    final ByteBuffer in_buffer,
    final MutableLongType in_base,
    final int in_offset)
  {
    super(in_buffer, in_base, in_offset);
  }

  /**
   *
Return a new vector that is backed by the given byte buffer {@code
   * b}

   *
   *
The data for the instance will be taken from the data at the current
   * value of {@code base.get() + offset}, each time a field is requested or
   * set.

   *
   *
No initialization of the data is performed.

   *
   * @param <T>    A phantom type parameter
   * @param b      The byte buffer
   * @param base   The base address
   * @param offset A constant offset
   *
   * @return A new buffered vector
   */

  public static <T> PVectorByteBufferedFloating3s32<T> createWithBase(
    final ByteBuffer b,
    final MutableLongType base,
    final int offset)
  {
    return new PVectorByteBufferedFloating3s32<>(b, base, offset);
  }

  @Override
  protected int componentCount()
  {
    return 3;
  }

  @Override
  public double x()
  {
    return this.getValue(0);
  }

  @Override
  public double y()
  {
    return this.getValue(1);
  }

  @Override
  public double z()
  {
    return this.getValue(2);
  }

  @Override
  public void setX(final double x)
  {
    this.putValue(0, x);
  }

  @Override
  public void setY(final double y)
  {
    this.putValue(1, y);
  }

  @Override
  public void setZ(final double z)
  {
    this.putValue(2, z);
  }
}


/*
 * Copyright © 2017  http://io7m.com
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.io7m.jtensors.storage.bytebuffered;

import com.io7m.mutable.numbers.core.MutableLongType;

import java.nio.ByteBuffer;

/**
 *
A storage vector.

 *
Storage component type: {@code long}

 *
Storage component count: {@code 3}

 */

public final class VectorByteBufferedIntegral3s64
  extends TensorByteBufferedIntegral64
  implements VectorByteBufferedIntegral3Type
{
  private VectorByteBufferedIntegral3s64(
    final ByteBuffer in_buffer,
    final MutableLongType in_base,
    final int in_offset)
  {
    super(in_buffer, in_base, in_offset);
  }

  /**
   *
Return a new vector that is backed by the given byte buffer {@code
   * b}

   *
   *
The data for the instance will be taken from the data at the current
   * value of {@code base.get() + offset}, each time a field is requested or
   * set.

   *
   *
No initialization of the data is performed.

   *
   * @param b      The byte buffer
   * @param base   The base address
   * @param offset A constant offset
   *
   * @return A new buffered vector
   */

  public static VectorByteBufferedIntegral3s64 createWithBase(
    final ByteBuffer b,
    final MutableLongType base,
    final int offset)
  {
    return new VectorByteBufferedIntegral3s64(b, base, offset);
  }

  @Override
  protected int componentCount()
  {
    return 3;
  }

  @Override
  public long x()
  {
    return this.getValue(0);
  }

  @Override
  public long y()
  {
    return this.getValue(1);
  }

  @Override
  public long z()
  {
    return this.getValue(2);
  }

  @Override
  public void setX(final long x)
  {
    this.putValue(0, x);
  }

  @Override
  public void setY(final long y)
  {
    this.putValue(1, y);
  }

  @Override
  public void setZ(final long z)
  {
    this.putValue(2, z);
  }
}


/*
 * Copyright © 2017  http://io7m.com
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.io7m.jtensors.storage.bytebuffered;

import com.io7m.mutable.numbers.core.MutableLongType;

import java.nio.ByteBuffer;

/**
 *
A storage vector.

 *
Storage component type: {@code binary16}

 *
Storage component count: {@code 2}

 */

public final class VectorByteBufferedFloating2s16
  extends TensorByteBufferedFloating16
  implements VectorByteBufferedFloating2Type
{
  private VectorByteBufferedFloating2s16(
    final ByteBuffer in_buffer,
    final MutableLongType in_base,
    final int in_offset)
  {
    super(in_buffer, in_base, in_offset);
  }

  /**
   *
Return a new vector that is backed by the given byte buffer {@code
   * b}

   *
   *
The data for the instance will be taken from the data at the current
   * value of {@code base.get() + offset}, each time a field is requested or
   * set.

   *
   *
No initialization of the data is performed.

   *
   * @param b      The byte buffer
   * @param base   The base address
   * @param offset A constant offset
   *
   * @return A new buffered vector
   */

  public static VectorByteBufferedFloating2s16 createWithBase(
    final ByteBuffer b,
    final MutableLongType base,
    final int offset)
  {
    return new VectorByteBufferedFloating2s16(b, base, offset);
  }

  @Override
  protected int componentCount()
  {
    return 2;
  }

  @Override
  public double x()
  {
    return this.getValue(0);
  }

  @Override
  public double y()
  {
    return this.getValue(1);
  }

  @Override
  public void setX(final double x)
  {
    this.putValue(0, x);
  }

  @Override
  public void setY(final double y)
  {
    this.putValue(1, y);
  }

}


/*
 * Copyright © 2017  http://io7m.com
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.io7m.jtensors.storage.bytebuffered;

import com.io7m.mutable.numbers.core.MutableLongType;

import java.nio.ByteBuffer;

/**
 *
A storage vector.

 *
Storage component type: {@code byte}

 *
Storage component count: {@code 3}

 */

public final class VectorByteBufferedIntegral3s8
  extends TensorByteBufferedIntegral8
  implements VectorByteBufferedIntegral3Type
{
  private VectorByteBufferedIntegral3s8(
    final ByteBuffer in_buffer,
    final MutableLongType in_base,
    final int in_offset)
  {
    super(in_buffer, in_base, in_offset);
  }

  /**
   *
Return a new vector that is backed by the given byte buffer {@code
   * b}

   *
   *
The data for the instance will be taken from the data at the current
   * value of {@code base.get() + offset}, each time a field is requested or
   * set.

   *
   *
No initialization of the data is performed.

   *
   * @param b      The byte buffer
   * @param base   The base address
   * @param offset A constant offset
   *
   * @return A new buffered vector
   */

  public static VectorByteBufferedIntegral3s8 createWithBase(
    final ByteBuffer b,
    final MutableLongType base,
    final int offset)
  {
    return new VectorByteBufferedIntegral3s8(b, base, offset);
  }

  @Override
  protected int componentCount()
  {
    return 3;
  }

  @Override
  public long x()
  {
    return this.getValue(0);
  }

  @Override
  public long y()
  {
    return this.getValue(1);
  }

  @Override
  public long z()
  {
    return this.getValue(2);
  }

  @Override
  public void setX(final long x)
  {
    this.putValue(0, x);
  }

  @Override
  public void setY(final long y)
  {
    this.putValue(1, y);
  }

  @Override
  public void setZ(final long z)
  {
    this.putValue(2, z);
  }
}