Package org.jvnet.jaxb2_commons.javaforkmlapi.equals_hashcode

Source Code of org.jvnet.jaxb2_commons.javaforkmlapi.equals_hashcode.CreateEqualsAndHashCode

package org.jvnet.jaxb2_commons.javaforkmlapi.equals_hashcode;

import java.util.ArrayList;

import org.apache.log4j.Logger;
import org.jvnet.jaxb2_commons.javaforkmlapi.ClazzPool;
import org.jvnet.jaxb2_commons.javaforkmlapi.command.Command;
import org.xml.sax.ErrorHandler;

import com.sun.codemodel.JConditional;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JExpression;
import com.sun.codemodel.JFieldRef;
import com.sun.codemodel.JFieldVar;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JOp;
import com.sun.codemodel.JType;
import com.sun.codemodel.JVar;
import com.sun.tools.xjc.Options;
import com.sun.tools.xjc.Plugin;
import com.sun.tools.xjc.generator.bean.ClassOutlineImpl;
import com.sun.tools.xjc.outline.ClassOutline;
import com.sun.tools.xjc.outline.Outline;

/**
* {@link Plugin} This plugin modifies XJC's code generation behavior so that you get an equals() and a hashCode()-method in each genereated
* class (as descripted in Bloch: Effective Java 2nd Edt. (page 33-50)).
*
* @author Florian Bachmann
*/
public class CreateEqualsAndHashCode extends Command {
  private static final Logger LOG = Logger.getLogger(CreateEqualsAndHashCode.class.getName());

  public CreateEqualsAndHashCode(final Outline outline, final Options opts, final ErrorHandler errorHandler, final ClazzPool pool) {
    super(outline, opts, errorHandler, pool);
  }

  @Override
  public void execute() {
    for (final ClassOutline classOutline : outline.getClasses()) {
      createHashCodeAndEqualsMethod(classOutline.implClass, classOutline);
    }

  }

  public static void createHashCodeAndEqualsMethod(final JDefinedClass implClass, final ClassOutline classOutline) {
    // if no fields are present return
    if (implClass.fields().isEmpty()) {
      return;
    }
    // check for suitable fields
    final ArrayList<JFieldVar> fields = getRelevantFields(implClass);

    // generate only if suitable fields exist
    if (fields.size() == 0) {
      return;
    }
    LOG.info("generateEquals and hashCode field#: "+ implClass.name() + " (" + fields.size() + ")");

    // generate hashCode() and equals()-method
    final JMethod hashCodeMethod = implClass.method(JMod.PUBLIC, implClass.owner().INT, "hashCode");
    final JMethod equalsMethod = implClass.method(JMod.PUBLIC, implClass.owner().BOOLEAN, "equals");

    boolean isSuperClass = false;
    if (classOutline != null) {
      isSuperClass = checkForSuperClass((ClassOutlineImpl)classOutline);
    }
    // annotate with @Override
    hashCodeMethod.annotate(Override.class);
    equalsMethod.annotate(Override.class);

    // add paramter for equals()-method
    final JVar vObj = equalsMethod.param(Object.class, "obj");

    // is it me? this==obj -> true
    final JConditional condMe = equalsMethod.body()._if(JExpr._this().eq(vObj));
    condMe._then()._return(JExpr.TRUE);

    // it's null? obj==null -> false
    final JConditional condNull = equalsMethod.body()._if(vObj.eq(JExpr._null()));
    condNull._then()._return(JExpr.FALSE);

    // paternity test (only add if equals in superclass isn't overridden)?
    // !super.equals(obj) -> false --> (super.equals(obj) == false)
    if (isSuperClass) {
      final JConditional condSuper = equalsMethod.body()._if(JExpr._super().invoke("equals").arg(vObj).eq(JExpr.FALSE));
      condSuper._then()._return(JExpr.FALSE);
    }

    // suit the class? !(obj instanceof TypeB) -> false --> if ((obj instanceof TypeB) == false)
    final JConditional condInstance = equalsMethod.body()._if(vObj._instanceof(implClass).eq(JExpr.FALSE));
    condInstance._then()._return(JExpr.FALSE);

    // cast: TYPE other = (TYPE) obj;
    final JVar vOther = equalsMethod.body().decl(implClass, "other", JExpr.cast(implClass, vObj));

    /*
     * check for each declared field distinguish between primitive types:
     *
     * <pre> if (field != other.field) { return false; } </pre>
     *
     * and reference types: <pre> if (field == null) { if (other.field != null) { return false; } } else if (!field.equals(other.field)) {
     * // --> if (field.equals(other.field) == false) return false; } </code>
     */
    JConditional condFieldCheck;
    boolean containsDouble = false;
    for (final JFieldVar jFieldVar : fields) {
      if (jFieldVar.type().fullName().equals("java.lang.Double") || jFieldVar.type().fullName().equals("double")) {
        jFieldVar.type(implClass.owner().DOUBLE);
        containsDouble = true;
      //  break;
      }
    }

    for (final JFieldVar jFieldVar : fields) {
      if (jFieldVar.type().isPrimitive()) {
        // LOG.info("JConditional: " + jFieldVar.name() +
        // " is PRIMITIVE");
        condFieldCheck = equalsMethod.body()._if(JExpr.ref(jFieldVar.name()).ne(vOther.ref(jFieldVar.name())));
        condFieldCheck._then()._return(JExpr.FALSE);
      } else {
        // LOG.info("JConditional: " + jFieldVar.name() +
        // " is REFERENCE");
        condFieldCheck = equalsMethod.body()._if(JExpr.ref(jFieldVar.name()).eq(JExpr._null()));
        condFieldCheck._then()._if(vOther.ref(jFieldVar.name()).ne(JExpr._null()))._then()._return(JExpr.FALSE);
        condFieldCheck._elseif(JExpr.ref(jFieldVar.name()).invoke("equals").arg(vOther.ref(jFieldVar.name())).eq(JExpr.FALSE))._then()
            ._return(JExpr.FALSE);
      }
    }

    // ir all works out, the objects are equal, return true
    equalsMethod.body()._return(JExpr.TRUE);

    // useful fields
    final JFieldRef vPrime = JExpr.ref("prime");
    final JFieldRef vResult = JExpr.ref("result");

    // ==> final int prime = 31;
    hashCodeMethod.body().assign(JExpr.ref("final int prime"), JExpr.ref("31"));

    // ==> int result = 1; || int result = super.hashCode();
    JExpression initializeResult = null;
    if (isSuperClass) {
      initializeResult = JExpr._super().invoke("hashCode");
    } else {
      initializeResult = JExpr.ref("1");
    }
    hashCodeMethod.body().assign(JExpr.ref("int result"), initializeResult);

    // if class contains double
    // long temp;
    JVar tempVariableForDoubleChecks = null;
    JType doubleClass = null;
    if (containsDouble == true) {
      tempVariableForDoubleChecks = hashCodeMethod.body().decl(implClass.owner().LONG, "temp");
      doubleClass = implClass.owner()._ref(Double.class);
    }

    JExpression tenaryCond;
    for (final JFieldVar jFieldVar : fields) {
      if (jFieldVar.type().isPrimitive()) {
        if (jFieldVar.type().fullName().equals("double")) {
          // temp = Double.doubleToLongBits(altitude);
          // result = prime * result + (int) (temp ^ (temp >>> 32));
          // LOG.info("implClass: " + implClass.name());
          // LOG.info("jFieldVar: " + jFieldVar.name());
          // LOG.info(" is " + jFieldVar.type().fullName());
          // LOG.info(" + " + tempVariableForDoubleChecks.name());
          // LOG.info(" + " + doubleClass.boxify().fullName());
          hashCodeMethod.body().assign(tempVariableForDoubleChecks, doubleClass.boxify().staticInvoke("doubleToLongBits").arg(jFieldVar));
          tenaryCond = JExpr.cast(implClass.owner().INT, tempVariableForDoubleChecks.xor(tempVariableForDoubleChecks.shrz(JExpr
              .direct("32"))));
        } else {
          // if field is primitive:
          // ==> result = prime * result + field;
          tenaryCond = JExpr.ref(jFieldVar.name());
        }
      } else {
        // else:
        // ==> result = prime * result + ((field == null) ? 0 :
        tenaryCond = JOp.cond(JOp.eq(JExpr.ref(jFieldVar.name()), JExpr._null()), JExpr.ref("0"), JExpr.invoke(JExpr.ref(jFieldVar.name()),
            "hashCode"));
      }

      final JExpression hashCodeCollection = JOp.mul(vPrime, vResult);
      final JExpression addition = JOp.plus(hashCodeCollection, tenaryCond);

      // add each value that influence to the method
      hashCodeMethod.body().assign(vResult, addition);
    }

    // the returned hashCode
    hashCodeMethod.body()._return(vResult);
  }

  /*
   * check if a superclass exist and if it defines some hashCode- and equals-worthy fields
   */
  private static boolean checkForSuperClass(final ClassOutlineImpl classOutline) {
    if (classOutline.getSuperClass() == null) {
      return false;
    }

    if (getRelevantFields(classOutline.getSuperClass().implClass).size() == 0) {
      return false;
    }

    return true;
  }

  /*
   * returns all fields that could be used in hashCode and equals
   */
  private static ArrayList<JFieldVar> getRelevantFields(final JDefinedClass implClass) {
    final ArrayList<JFieldVar> fields = new ArrayList<JFieldVar>();
    int mods = 0;
    for (final JFieldVar field : implClass.fields().values()) {
      mods = field.mods().getValue();

      if ((mods & JMod.PRIVATE) != 0) {
        continue;
      }
      if ((mods & JMod.STATIC) != 0) {
        continue;
      }
      fields.add(field);
    }
    return fields;
  }

}
TOP

Related Classes of org.jvnet.jaxb2_commons.javaforkmlapi.equals_hashcode.CreateEqualsAndHashCode

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.