private void extractIMethod(IMethod method, boolean annotationElement) {
    try {
      StringBuilder fqnBuilder = new StringBuilder(fqnStack.peek());
      if (method.isConstructor()) {
        fqnBuilder.append('.').append("<init>");
      } else {
        fqnBuilder.append('.').append(method.getElementName());
      }
      fqnBuilder.append('(');
      boolean first = true;
      for (String param : method.getParameterTypes()) {
        if (first) {
          first = false;
        } else {
          fqnBuilder.append(',');
        }
        String sig = typeSignatureToFqn(param);
        fqnBuilder.append(sig);
      }
      fqnBuilder.append(')');

      String fqn = fqnBuilder.toString();

      // Write the entity
      if (annotationElement) {
        entityWriter.writeAnnotationElement(fqn, method.getFlags(), path);
      } else if (method.isConstructor()) {
        entityWriter.writeConstructor(fqn, method.getFlags(), path);
      } else {
        entityWriter.writeMethod(fqn, method.getFlags(), path);
      }

      // Write the inside relation
      relationWriter.writeInside(fqn, fqnStack.peek(), path);

      // Write the returns relation
      relationWriter.writeReturns(fqn, typeSignatureToFqn(method.getReturnType()), path);

      // Write the receives relation
      String[] paramTypes = method.getParameterTypes();
      for (int i = 0; i < paramTypes.length; i++) {
        localVariableWriter.writeClassParameter(
            "arg" + i, typeSignatureToFqn(paramTypes[i]), fqn, i, path);
        //        relationWriter.writeReceives(fqn, typeSignatureToFqn(paramTypes[i]), "arg" + i,
        // i);
      }

      int pos = 0;
      for (ITypeParameter param : method.getTypeParameters()) {
        relationWriter.writeParametrizedBy(fqn, getTypeParam(param), pos++, path);
      }
    } catch (JavaModelException e) {
      logger.log(Level.SEVERE, "Error in extracting class file", e);
    }
  }
  public void extractClassFile(IClassFile classFile) {
    // Verify that it's a top-level type, or a subtype of a top-level type
    //    try {
    //      IType declaring = classFile.getType();
    //      while (declaring != null) {
    //        if (declaring.isLocal() || declaring.isAnonymous()) {
    //          return;
    //        }
    //        declaring = declaring.getDeclaringType();
    //      }
    //    } catch (JavaModelException e) {
    //      logger.log(Level.SEVERE, "Error in extracting class file", e);
    //      return;
    //    }

    IJavaElement parent = classFile.getParent();
    while (true) {
      if (parent == null) {
        logger.log(Level.SEVERE, "Unable to find package for: " + classFile.getElementName());
        break;
      } else if (parent.getElementType() == IJavaElement.PACKAGE_FRAGMENT) {
        // Write the class file
        name = classFile.getElementName();
        path = parent.getElementName() + "." + name;
        fileWriter.writeClassFile(name, path);

        try {
          if (classFile.getType().isAnonymous()) {
            String fqn = classFile.getType().getFullyQualifiedName();
            String containingFqn = fqn.substring(0, fqn.lastIndexOf('$'));
            relationWriter.writeInside(
                classFile.getType().getFullyQualifiedName(), containingFqn, path);
          } else {
            relationWriter.writeInside(
                classFile.getType().getFullyQualifiedName(), parent.getElementName(), path);
            entityWriter.writePackage(parent.getElementName());
          }
        } catch (JavaModelException e) {
          logger.log(Level.SEVERE, "Error in extracting class file", e);
        }
        break;
      } else {
        logger.log(
            Level.SEVERE, classFile.getType().getFullyQualifiedName() + " should be top-level!");
        parent = parent.getParent();
      }
    }

    extractIType(classFile.getType());
    name = null;
    path = null;
  }
  private void extractIField(IField field) {
    try {
      String fqn = fqnStack.peek() + "." + field.getElementName();
      // Write the entity
      if (field.isEnumConstant()) {
        entityWriter.writeEnumConstant(fqn, field.getFlags(), path);
      } else {
        entityWriter.writeField(fqn, field.getFlags(), path);
      }

      // Write the inside relation
      relationWriter.writeInside(fqn, fqnStack.peek(), path);

      // Write the holds relation
      relationWriter.writeHolds(fqn, typeSignatureToFqn(field.getTypeSignature()), path);
    } catch (JavaModelException e) {
      logger.log(Level.SEVERE, "Error in extracting class file", e);
    }
  }
  private void extractIType(IType type) {
    try {
      String fqn = type.getFullyQualifiedName();

      // Write the entity
      if (type.isClass()) {
        entityWriter.writeClass(fqn, type.getFlags(), path);

        // Write the superclass
        String superSig = type.getSuperclassTypeSignature();
        if (superSig != null) {
          relationWriter.writeExtends(fqn, typeSignatureToFqn(superSig), path);
        }
      } else if (type.isAnnotation()) {
        entityWriter.writeAnnotation(fqn, type.getFlags(), path);
      } else if (type.isInterface()) {
        entityWriter.writeInterface(fqn, type.getFlags(), path);
      } else if (type.isEnum()) {
        entityWriter.writeEnum(fqn, type.getFlags(), path);
      }

      // Write the superinterfaces
      for (String superIntSig : type.getSuperInterfaceTypeSignatures()) {
        relationWriter.writeImplements(fqn, typeSignatureToFqn(superIntSig), path);
      }

      if (!fqnStack.isEmpty()) {
        relationWriter.writeInside(type.getFullyQualifiedName(), fqnStack.peek(), path);
      }

      fqnStack.push(type.getFullyQualifiedName());

      for (IType child : type.getTypes()) {
        extractIType(child);
      }

      for (IField field : type.getFields()) {
        if (!Flags.isSynthetic(field.getFlags())) {
          extractIField(field);
        }
      }

      for (IMethod method : type.getMethods()) {
        if (!Flags.isSynthetic(method.getFlags())
            || (Flags.isSynthetic(method.getFlags())
                && method.isConstructor()
                && method.getParameterTypes().length == 0)) {
          extractIMethod(method, type.isAnnotation());
        }
      }

      int pos = 0;
      for (ITypeParameter param : type.getTypeParameters()) {
        relationWriter.writeParametrizedBy(fqn, getTypeParam(param), pos++, path);
      }

      fqnStack.pop();
    } catch (Exception e) {
      logger.log(Level.SEVERE, "Error in extracting class file", e);
    }
  }