@Override
  public void enhanceThisClass(final ApplicationClass applicationClass) throws Exception {
    final CtClass ctClass = makeClass(applicationClass);
    if (ctClass.isInterface()) {
      return;
    }

    final Map<CtField, InjectionInfo> fieldsToInject = scanForInjections(ctClass);

    // in all methods, replace the field accesses with a call to spring
    for (final CtMethod ctMethod : ctClass.getDeclaredMethods()) {
      ctMethod.instrument(
          new ExprEditor() {
            @Override
            public void edit(final FieldAccess fieldAccess) {
              try {
                final InjectionInfo injectionInfo = fieldsToInject.get(fieldAccess.getField());

                if (injectionInfo != null && fieldAccess.isReader()) {

                  switch (injectionInfo.injectionMethod) {
                    case BY_NAME:
                      fieldAccess.replace(
                          "$_ = ($r)play.utils.Java.invokeStatic(play.modules.spring.Spring.class, \"getBean\", new Object[] {\""
                              + injectionInfo.beanName
                              + "\"});");
                      break;
                    case BY_TYPE:
                      fieldAccess.replace(
                          "$_ = ($r)play.utils.Java.invokeStatic(play.modules.spring.Spring.class, \"getBeanOfType\", new Object[] {$type});");
                      break;
                  }
                }
              } catch (final Exception e) {
                Logger.error(
                    e,
                    "Error in SpringEnhancer. %s.%s has not been properly enhanced (fieldAccess %s).",
                    applicationClass.name,
                    ctMethod.getName(),
                    fieldAccess);
                throw new UnexpectedException("Error enhancing injected field", e);
              }
            }
          });
    }

    applicationClass.enhancedByteCode = ctClass.toBytecode();
    ctClass.defrost();
  }
  /**
   * Filters the classes to be transformed.
   *
   * @param definition
   * @param classMetaData the meta-data for the class
   * @param cg the class to filter
   * @return boolean true if the method should be filtered away
   */
  private boolean classFilter(
      final SystemDefinition definition, final ClassMetaData classMetaData, final CtClass cg) {
    if (cg.isInterface()) {
      return true;
    }

    String className = cg.getName().replace('/', '.');

    if (definition.inExcludePackage(className)) {
      return true;
    }

    if (!definition.inIncludePackage(className)) {
      return true;
    }

    if (definition.hasCallPointcut(classMetaData)) {
      return false;
    }

    return true;
  }
Exemplo n.º 3
0
  @Override
  public void enhanceThisClass(ApplicationClass applicationClass) throws Exception {

    final CtClass ctClass = makeClass(applicationClass);
    if (ctClass.isInterface()) {
      return;
    }
    if (ctClass.getName().endsWith(".package")) {
      return;
    }

    // Add a default constructor if needed
    try {
      boolean hasDefaultConstructor = false;
      for (CtConstructor constructor : ctClass.getDeclaredConstructors()) {
        if (constructor.getParameterTypes().length == 0) {
          hasDefaultConstructor = true;
          break;
        }
      }
      if (!hasDefaultConstructor && !ctClass.isInterface()) {
        CtConstructor defaultConstructor =
            CtNewConstructor.make("public " + ctClass.getSimpleName() + "() {}", ctClass);
        ctClass.addConstructor(defaultConstructor);
      }
    } catch (Exception e) {
      Logger.error(e, "Error in PropertiesEnhancer");
      throw new UnexpectedException("Error in PropertiesEnhancer", e);
    }

    if (isScala(applicationClass)) {
      // Temporary hack for Scala. Done.
      applicationClass.enhancedByteCode = ctClass.toBytecode();
      ctClass.defrost();
      return;
    }

    for (CtField ctField : ctClass.getDeclaredFields()) {
      try {

        if (isProperty(ctField)) {

          // Property name
          String propertyName =
              ctField.getName().substring(0, 1).toUpperCase() + ctField.getName().substring(1);
          String getter = "get" + propertyName;
          String setter = "set" + propertyName;

          try {
            CtMethod ctMethod = ctClass.getDeclaredMethod(getter);
            if (ctMethod.getParameterTypes().length > 0
                || Modifier.isStatic(ctMethod.getModifiers())) {
              throw new NotFoundException("it's not a getter !");
            }
          } catch (NotFoundException noGetter) {

            // Créé le getter
            String code =
                "public "
                    + ctField.getType().getName()
                    + " "
                    + getter
                    + "() { return this."
                    + ctField.getName()
                    + "; }";
            CtMethod getMethod = CtMethod.make(code, ctClass);
            getMethod.setModifiers(getMethod.getModifiers() | AccessFlag.SYNTHETIC);
            ctClass.addMethod(getMethod);
          }

          if (!isFinal(ctField)) {
            try {
              CtMethod ctMethod = ctClass.getDeclaredMethod(setter);
              if (ctMethod.getParameterTypes().length != 1
                  || !ctMethod.getParameterTypes()[0].equals(ctField.getType())
                  || Modifier.isStatic(ctMethod.getModifiers())) {
                throw new NotFoundException("it's not a setter !");
              }
            } catch (NotFoundException noSetter) {
              // Créé le setter
              CtMethod setMethod =
                  CtMethod.make(
                      "public void "
                          + setter
                          + "("
                          + ctField.getType().getName()
                          + " value) { this."
                          + ctField.getName()
                          + " = value; }",
                      ctClass);
              setMethod.setModifiers(setMethod.getModifiers() | AccessFlag.SYNTHETIC);
              ctClass.addMethod(setMethod);
              createAnnotation(getAnnotations(setMethod), PlayPropertyAccessor.class);
            }
          }
        }

      } catch (Exception e) {
        Logger.error(e, "Error in PropertiesEnhancer");
        throw new UnexpectedException("Error in PropertiesEnhancer", e);
      }
    }

    // Add a default constructor if needed
    try {
      boolean hasDefaultConstructor = false;
      for (CtConstructor constructor : ctClass.getDeclaredConstructors()) {
        if (constructor.getParameterTypes().length == 0) {
          hasDefaultConstructor = true;
          break;
        }
      }
      if (!hasDefaultConstructor) {
        CtConstructor defaultConstructor = CtNewConstructor.defaultConstructor(ctClass);
        ctClass.addConstructor(defaultConstructor);
      }
    } catch (Exception e) {
      Logger.error(e, "Error in PropertiesEnhancer");
      throw new UnexpectedException("Error in PropertiesEnhancer", e);
    }

    // Intercept all fields access
    for (final CtBehavior ctMethod : ctClass.getDeclaredBehaviors()) {
      ctMethod.instrument(
          new ExprEditor() {

            @Override
            public void edit(FieldAccess fieldAccess) throws CannotCompileException {
              try {

                // Acces à une property ?
                if (isProperty(fieldAccess.getField())) {

                  // TODO : vérifier que c'est bien un champ d'une classe de l'application
                  // (fieldAccess.getClassName())

                  // Si c'est un getter ou un setter
                  String propertyName = null;
                  if (fieldAccess
                          .getField()
                          .getDeclaringClass()
                          .equals(ctMethod.getDeclaringClass())
                      || ctMethod
                          .getDeclaringClass()
                          .subclassOf(fieldAccess.getField().getDeclaringClass())) {
                    if ((ctMethod.getName().startsWith("get")
                            || (!isFinal(fieldAccess.getField())
                                && ctMethod.getName().startsWith("set")))
                        && ctMethod.getName().length() > 3) {
                      propertyName = ctMethod.getName().substring(3);
                      propertyName =
                          propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1);
                    }
                  }

                  // On n'intercepte pas le getter de sa propre property
                  if (propertyName == null || !propertyName.equals(fieldAccess.getFieldName())) {

                    String invocationPoint =
                        ctClass.getName()
                            + "."
                            + ctMethod.getName()
                            + ", line "
                            + fieldAccess.getLineNumber();

                    if (fieldAccess.isReader()) {

                      // Réécris l'accés en lecture à la property
                      fieldAccess.replace(
                          "$_ = ($r)play.classloading.enhancers.PropertiesEnhancer.FieldAccessor.invokeReadProperty($0, \""
                              + fieldAccess.getFieldName()
                              + "\", \""
                              + fieldAccess.getClassName()
                              + "\", \""
                              + invocationPoint
                              + "\");");

                    } else if (!isFinal(fieldAccess.getField()) && fieldAccess.isWriter()) {

                      // Réécris l'accés en ecriture à la property
                      fieldAccess.replace(
                          "play.classloading.enhancers.PropertiesEnhancer.FieldAccessor.invokeWriteProperty($0, \""
                              + fieldAccess.getFieldName()
                              + "\", "
                              + fieldAccess.getField().getType().getName()
                              + ".class, $1, \""
                              + fieldAccess.getClassName()
                              + "\", \""
                              + invocationPoint
                              + "\");");
                    }
                  }
                }

              } catch (Exception e) {
                throw new UnexpectedException("Error in PropertiesEnhancer", e);
              }
            }
          });
    }

    // Done.
    applicationClass.enhancedByteCode = ctClass.toBytecode();
    ctClass.defrost();
  }
  private CtClass instrumentClass(
      AspectManager manager, AOPClassPool pool, CtClass clazz, boolean isLoadedClass)
      throws NotFoundException, Exception {
    if (pool.isClassLoadedButNotWoven(clazz.getName())) {
      return null;
    }
    try {
      CtClass superClass = clazz.getSuperclass();
      if (superClass != null && !Instrumentor.implementsAdvised(clazz)) {
        ClassPool superPool = superClass.getClassPool();
        if (superPool instanceof AOPClassPool) {
          AspectManager aspectManager = manager;
          if (manager instanceof Domain && superPool != pool) {
            // We are in a scoped classloader and the superclass is not
            aspectManager = AspectManager.instance(superPool.getClassLoader());
          }
          instrumentClass(aspectManager, (AOPClassPool) superPool, superClass, false);
        }
      }

      if (manager.isNonAdvisableClassName(clazz.getName())) {
        return null;
      }

      if (clazz.isArray()) {
        if (verbose && logger.isDebugEnabled())
          logger.debug("cannot compile, isArray: " + clazz.getName());
        pool.flushClass(clazz.getName());
        return null;
      }
      if (clazz.isInterface()) {
        if (verbose && logger.isDebugEnabled())
          logger.debug("cannot compile, isInterface: " + clazz.getName());
        // pool.flushClass(info.getClassName());
        clazz.prune();
        return null;
      }
      if (clazz.isFrozen()) {
        if (isAdvised(pool, clazz)) return null;
        if (verbose && logger.isDebugEnabled())
          logger.debug("warning, isFrozen: " + clazz.getName() + " " + clazz.getClassPool());
        if (!isLoadedClass) {
          // What's the point of this?
          clazz = obtainCtClassInfo(pool, clazz.getName(), null);
        } else return null;
        // info.getClazz().defrost();
      }

      boolean transformed = clazz.isModified();
      if (!transformed) {
        ClassAdvisor advisor = AdvisorFactory.getClassAdvisor(clazz, manager);
        Instrumentor instrumentor =
            InstrumentorFactory.getInstrumentor(
                pool,
                manager,
                manager.dynamicStrategy.getJoinpointClassifier(),
                manager.dynamicStrategy.getDynamicTransformationObserver(clazz));

        if (!Instrumentor.isTransformable(clazz)) {
          if (verbose && logger.isDebugEnabled())
            logger.debug("cannot compile, implements Untransformable: " + clazz.getName());
          // Flushing the generated invocation classes breaks things further down the line
          // pool.flushClass(info.getClassName());
          return null;
        }

        manager.attachMetaData(advisor, clazz, true);
        manager.applyInterfaceIntroductions(advisor, clazz);
        transformed = instrumentor.transform(clazz, advisor);
      }
      if (transformed) {
        return clazz;
      }

      if (isLoadedClass) {
        pool.setClassLoadedButNotWoven(clazz.getName());
      }

      return null;
    } catch (Exception e) {
      throw new RuntimeException("Error converting class ", e);
    } finally {
    }
  }
  @Override
  public void enhanceThisClass(ApplicationClass applicationClass) throws Exception {
    CtClass ctClass = makeClass(applicationClass);
    String entityName = ctClass.getName();
    Logger.debug("Enhance class " + entityName);

    // Only enhance Neo4jModel classes.
    if (!ctClass.subtypeOf(classPool.get("play.modules.neo4j.model.Neo4jModel"))) {
      return;
    }

    // Add a default constructor if needed
    try {
      for (CtConstructor constructor : ctClass.getDeclaredConstructors()) {
        if (constructor.getParameterTypes().length == 0) {
          ctClass.removeConstructor(constructor);
        }
        if (constructor.getParameterTypes().length == 1
            && constructor.getParameterTypes()[0].getClass().isInstance(Node.class)) {
          ctClass.removeConstructor(constructor);
        }
      }
      if (!ctClass.isInterface()) {
        Logger.debug("Adding default constructor");
        CtConstructor defaultConstructor =
            CtNewConstructor.make("public " + ctClass.getSimpleName() + "() { super();}", ctClass);
        ctClass.addConstructor(defaultConstructor);
      }
    } catch (Exception e) {
      Logger.error(e, "Error in PropertiesEnhancer");
      throw new UnexpectedException("Error in PropertiesEnhancer", e);
    }

    // for all field, we add getter / setter
    for (CtField ctField : ctClass.getDeclaredFields()) {
      try {
        // Property name
        String propertyName =
            ctField.getName().substring(0, 1).toUpperCase() + ctField.getName().substring(1);
        String getter = "get" + propertyName;
        String setter = "set" + propertyName;

        Logger.debug("Field " + ctField.getName() + " is a property ?");
        if (isProperty(ctField)) {
          Logger.debug("true");

          // ~~~~~~~~~
          // GETTER
          // ~~~~~~~
          try {
            CtMethod ctMethod = ctClass.getDeclaredMethod(getter);
            if (!ctMethod.getName().equalsIgnoreCase("getShouldBeSave")) {
              ctClass.removeMethod(ctMethod);
              throw new NotFoundException("it's not a true getter !");
            }
          } catch (NotFoundException noGetter) {
            // create getter
            Logger.debug("Adding getter " + getter + " for class " + entityName);
            // @formatter:off
            String code =
                "public "
                    + ctField.getType().getName()
                    + " "
                    + getter
                    + "() {"
                    + "if(this.shouldBeSave == Boolean.FALSE && this.node != null){"
                    + "return (("
                    + ctField.getType().getName()
                    + ") play.modules.neo4j.util.Binder.bindFromNeo4jFormat(this.node.getProperty(\""
                    + ctField.getName()
                    + "\", null),"
                    + ctField.getType().getName()
                    + ".class ));"
                    + "}else{"
                    + "return "
                    + ctField.getName()
                    + ";"
                    + "}"
                    + "}";
            // @formatter:on
            Logger.debug(code);
            CtMethod getMethod = CtMethod.make(code, ctClass);
            ctClass.addMethod(getMethod);
          }

          // ~~~~~~~~~
          // SETTER
          // ~~~~~~~
          try {
            CtMethod ctMethod = ctClass.getDeclaredMethod(setter);
            if (ctMethod.getParameterTypes().length != 1
                || !ctMethod.getParameterTypes()[0].equals(ctField.getType())
                || Modifier.isStatic(ctMethod.getModifiers())
                || hasPlayPropertiesAccessorAnnotation(ctMethod)) {
              if (hasPlayPropertiesAccessorAnnotation(ctMethod)) {
                ctClass.removeMethod(ctMethod);
              }
              throw new NotFoundException("it's not a true setter !");
            }
          } catch (NotFoundException noSetter) {
            // create setter
            Logger.debug("Adding setter " + setter + " for class " + entityName);
            // @formatter:off
            String code =
                "public void "
                    + setter
                    + "("
                    + ctField.getType().getName()
                    + " value) { "
                    + "this."
                    + ctField.getName()
                    + " = value;"
                    + "this.shouldBeSave = Boolean.TRUE;"
                    + "}";
            // formatter:on
            CtMethod setMethod = CtMethod.make(code, ctClass);
            Logger.debug(code);
            ctClass.addMethod(setMethod);
          }
        } else {
          // ~~~~~~~~~
          // GETTER for neo4j relation property
          // ~~~~~~~
          if (hasNeo4jRelationAnnotation(ctField)) {
            // test for related annotation
            Neo4jRelatedTo relatedTo = getRelatedAnnotation(ctField);
            if (relatedTo != null) {
              CtMethod ctMethod = ctClass.getDeclaredMethod(getter);
              ctClass.removeMethod(ctMethod);
              String code;
              if (relatedTo.lazy()) {
                // @formatter:off
                code =
                    "public "
                        + ctField.getType().getName()
                        + " "
                        + getter
                        + "() {"
                        + "if(this."
                        + ctField.getName()
                        + " == null){"
                        + "java.lang.reflect.Field field = this.getClass().getField(\""
                        + ctField.getName()
                        + "\");"
                        + "this."
                        + ctField.getName()
                        + "=play.modules.neo4j.relationship.Neo4jRelationFactory.getModelsFromRelation(\""
                        + relatedTo.value()
                        + "\", \""
                        + relatedTo.direction()
                        + "\", field, this.node);"
                        + "}"
                        + "return "
                        + ctField.getName()
                        + ";"
                        + "}";
                // @formatter:on
              } else {
                // @formatter:off
                code =
                    "public "
                        + ctField.getType().getName()
                        + " "
                        + getter
                        + "() {"
                        + "return "
                        + ctField.getName()
                        + ";"
                        + "}";
                // @formatter:on
              }
              Logger.debug(code);
              CtMethod method = CtMethod.make(code, ctClass);
              ctClass.addMethod(method);
            }
            // test for unique relation annotation
            Neo4jUniqueRelation uniqueRelation = getUniqueRelationAnnotation(ctField);
            if (uniqueRelation != null) {
              CtMethod ctMethod = ctClass.getDeclaredMethod(getter);
              ctClass.removeMethod(ctMethod);
              String code;
              // @formatter:off
              code =
                  "public "
                      + ctField.getType().getName()
                      + " "
                      + getter
                      + "() {"
                      + "return ("
                      + ctField.getType().getName()
                      + ")"
                      + ctField.getName()
                      + ";"
                      + "}";
              // @formatter:on
              Logger.debug(code);
              CtMethod method = CtMethod.make(code, ctClass);
              ctClass.addMethod(method);
            }
          }
        }
      } catch (Exception e) {
        Logger.error(e, "Error in PropertiesEnhancer");
        throw new UnexpectedException("Error in PropertiesEnhancer", e);
      }
    }

    // Adding getByKey() method
    Logger.debug("Adding getByKey() method for class " + entityName);
    // @formatter:off
    String codeGetByKey =
        "public static play.modules.neo4j.model.Neo4jModel getByKey(Long key) throws play.modules.neo4j.exception.Neo4jException {"
            + "return ("
            + entityName
            + ")_getByKey(key, \""
            + entityName
            + "\");"
            + "}";
    // @formatter:on
    Logger.debug(codeGetByKey);
    CtMethod getByKeyMethod = CtMethod.make(codeGetByKey, ctClass);
    ctClass.addMethod(getByKeyMethod);

    // ~~~~~~~~~~~~~~~
    // Adding findAll() method
    // @formatter:off
    String codeFindAll =
        "public static java.util.List findAll() {"
            + "return "
            + entityName
            + "._findAll(\""
            + entityName
            + "\");"
            + "}";
    // @formatter:on
    Logger.debug(codeFindAll);
    CtMethod findAllMethod = CtMethod.make(codeFindAll, ctClass);
    ctClass.addMethod(findAllMethod);

    // ~~~~~~~~~~~~~~~
    // Adding queryIndex() method
    // @formatter:off
    String queryIndex =
        "public static java.util.List queryIndex(String indexname, String query) {"
            + "return "
            + entityName
            + "._queryIndex(indexname, query);"
            + "}";
    // @formatter:on
    Logger.debug(queryIndex);
    CtMethod queryIndexMethod = CtMethod.make(queryIndex, ctClass);
    ctClass.addMethod(queryIndexMethod);

    // Done.
    applicationClass.enhancedByteCode = ctClass.toBytecode();
    ctClass.defrost();
  }
Exemplo n.º 6
0
  /**
   * Given a bean-style interface, generate an instance of the interface by implementing getters and
   * setters for each property. It will also add implementations to support the {@link
   * SupportsRdfId} interface and generate simple, default equals, toString and hashCode methods.
   *
   * <p>If there are other non-bean style (getter and/or setter's for properties) methods on the
   * interface, this will likely fail to generate the instance.
   *
   * @param theInterface the interface to build an instance of
   * @param <T> the type of the interface
   * @return New dynamically generated bytecode of a class that implements the given interface.
   * @throws Exception if there is an error while generating the bytecode of the new class.
   */
  public static synchronized <T> Class<T> generateInstanceClass(Class<T> theInterface)
      throws Exception {
    processedMethods.clear();

    // TODO: can we use some sort of template language for this?

    ClassPool aPool = ClassPool.getDefault();

    aPool.appendClassPath(new LoaderClassPath(theInterface.getClassLoader()));

    CtClass aInterface = aPool.get(theInterface.getName());
    CtClass aSupportsRdfIdInterface = aPool.get(SupportsRdfId.class.getName());
    CtClass aEmpireGeneratedInterface = aPool.get(EmpireGenerated.class.getName());

    if (!Arrays.asList(aInterface.getInterfaces()).contains(aSupportsRdfIdInterface)
        && !SupportsRdfId.class.isAssignableFrom(theInterface)) {
      throw new IllegalArgumentException(
          "Class '"
              + theInterface.getName()
              + "' does not implement SupportsRdfId, cannot generate Empire suitable implementation.");
    }

    String aName = aInterface.getPackageName() + ".impl." + aInterface.getSimpleName() + "Impl";
    CtClass aClass = null;

    try {
      //  i had a good reason for doing this, but i dont remember what it is.  when i do, i'll
      // explain it here =)

      aClass = aPool.get(aName);
      return (Class<T>) BeanReflectUtil.loadClass(aName);
    } catch (NotFoundException e) {
      aClass =
          aPool.makeClass(
              aInterface.getPackageName() + ".impl." + aInterface.getSimpleName() + "Impl");
    } catch (ClassNotFoundException e) {
      throw new Exception("Previously created class cannot be loaded.", e);
    }

    if (aClass.isFrozen()) {
      aClass.defrost();
    }

    if (aInterface.isInterface()) {
      aClass.addInterface(aInterface);
    } else {
      aClass.setSuperclass(aInterface);
    }

    aClass.addInterface(aSupportsRdfIdInterface);
    aClass.addInterface(aEmpireGeneratedInterface);

    CtField aInterfaceField =
        new CtField(aPool.get(Class.class.getName()), "mInterfaceClass", aClass);
    aClass.addField(
        aInterfaceField, CtField.Initializer.byExpr(theInterface.getName() + ".class;"));

    CtField aAllTriplesField = new CtField(aPool.get(Graph.class.getName()), "mAllTriples", aClass);
    aClass.addField(
        aAllTriplesField, CtField.Initializer.byExpr("new com.clarkparsia.openrdf.ExtGraph();"));

    CtField aInstanceTriplesField =
        new CtField(aPool.get(Graph.class.getName()), "mInstanceTriples", aClass);
    aClass.addField(
        aInstanceTriplesField,
        CtField.Initializer.byExpr("new com.clarkparsia.openrdf.ExtGraph();"));

    aClass.addConstructor(CtNewConstructor.defaultConstructor(aClass));

    generateMethods(theInterface, aPool, aClass);
    generateMethodsForSuperInterfaces(theInterface, aPool, aClass);

    CtField aIdField = new CtField(aPool.get(SupportsRdfId.class.getName()), "supportsId", aClass);
    aClass.addField(
        aIdField,
        CtField.Initializer.byExpr("new com.clarkparsia.empire.annotation.SupportsRdfIdImpl();"));

    if (!hasMethod(aClass, "getRdfId")) {
      aClass.addMethod(
          CtNewMethod.make(
              "public com.clarkparsia.empire.SupportsRdfId.RdfKey getRdfId() { return supportsId.getRdfId(); } ",
              aClass));
    }

    if (!hasMethod(aClass, "setRdfId")) {
      aClass.addMethod(
          CtNewMethod.make(
              "public void setRdfId(com.clarkparsia.empire.SupportsRdfId.RdfKey theURI) { supportsId.setRdfId(theURI); } ",
              aClass));
    }

    if (!hasMethod(aClass, "getAllTriples")) {
      aClass.addMethod(
          CtNewMethod.make(
              "public org.openrdf.model.Graph getAllTriples() { return mAllTriples; } ", aClass));
    }

    if (!hasMethod(aClass, "setAllTriples")) {
      aClass.addMethod(
          CtNewMethod.make(
              "public void setAllTriples(org.openrdf.model.Graph theGraph) { mAllTriples = theGraph; } ",
              aClass));
    }

    if (!hasMethod(aClass, "getInstanceTriples")) {
      aClass.addMethod(
          CtNewMethod.make(
              "public org.openrdf.model.Graph getInstanceTriples() { return mInstanceTriples; } ",
              aClass));
    }

    if (!hasMethod(aClass, "setInstanceTriples")) {
      aClass.addMethod(
          CtNewMethod.make(
              "public void setInstanceTriples(org.openrdf.model.Graph theGraph) { mInstanceTriples = theGraph; } ",
              aClass));
    }

    String equalsMethodBody =
        "public boolean equals(Object theObj) {\n"
            + "  if (theObj == this) return true;\n"
            + "  if (!(theObj instanceof com.clarkparsia.empire.SupportsRdfId)) return false;\n"
            + "  if (!(mInterfaceClass.isAssignableFrom(theObj.getClass()))) return false;\n"
            + "  return getRdfId().equals( ((com.clarkparsia.empire.SupportsRdfId) theObj).getRdfId()) && super.equals(theObj);\n"
            + "}\n";

    aClass.addMethod(CtNewMethod.make(equalsMethodBody, aClass));

    if (theInterface.isInterface()) {
      aClass.addMethod(
          CtNewMethod.make(
              "public String toString() { return getRdfId() != null ? getRdfId().toString() : super.toString(); } ",
              aClass));
      aClass.addMethod(
          CtNewMethod.make(
              "public int hashCode() { return getRdfId() != null ? getRdfId().hashCode() : 0; } ",
              aClass));
    }

    aClass.freeze();

    Class<T> aResult = (Class<T>) aClass.toClass();

    try {
      // make sure this is a valid class, that is, we can create instances of it!
      aResult.newInstance();
    } catch (Exception ex) {
      // TODO: log this?
      throw ex;
    }

    return aResult;
  }