Package org.stjs.generator.check.declaration

Source Code of org.stjs.generator.check.declaration.ClassDuplicateMemberNameCheck

package org.stjs.generator.check.declaration;

import java.util.Collection;

import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;

import org.stjs.generator.GenerationContext;
import org.stjs.generator.check.CheckContributor;
import org.stjs.generator.check.CheckVisitor;
import org.stjs.generator.javac.TreeUtils;
import org.stjs.generator.javac.TreeWrapper;
import org.stjs.generator.utils.JavaNodes;
import org.stjs.generator.writer.MemberWriters;

import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Multimap;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;

/**
* checks the a field name or method exists only once in the class and its hierchy
* @author acraciun
*/
public class ClassDuplicateMemberNameCheck implements CheckContributor<ClassTree> {

  private void checkCandidate(Element overrideCandidate, ExecutableElement methodElement, TypeElement classElement, Tree member,
      GenerationContext<Void> context) {

    boolean isMethod = overrideCandidate instanceof ExecutableElement;
    boolean isFieldOrInnerType = !isMethod;

    String name = methodElement.getSimpleName().toString();
    if (isFieldOrInnerType) {
      // it's a field or inner type -> this is illegal
      context.addError(member, "There is already a field with the same name as this method in the type or one of its parents: "
          + ((TypeElement) overrideCandidate.getEnclosingElement()).getQualifiedName() + "." + overrideCandidate.getSimpleName());
    } else if (!context.getElements().overrides(methodElement, (ExecutableElement) overrideCandidate, classElement)) {
      context.addError(member, "Only maximum one method with the name [" + name
          + "] is allowed to have a body. The other methods must be marked as native."
          + " The type (or one of its parents) may contain already the method: " + overrideCandidate
          + " that has a different signature");
    }

  }

  private void checkMethodName(TypeElement classElement, MethodTree method, ExecutableElement methodElement, GenerationContext<Void> context,
      Multimap<String, Element> existingNames) {
    String name = method.getName().toString();

    Collection<Element> sameName = existingNames.get(name);
    if (sameName.isEmpty()) {
      existingNames.put(name, methodElement);
    } else {
      // try to see if it's not in fact a method override
      for (Element overrideCandidate : sameName) {
        checkCandidate(overrideCandidate, methodElement, classElement, method, context);
      }
    }
  }

  private void checkMethod(TypeElement classElement, Tree member, GenerationContext<Void> context, Multimap<String, Element> existingNames) {
    if (member instanceof MethodTree) {
      MethodTree method = (MethodTree) member;
      ExecutableElement methodElement = TreeUtils.elementFromDeclaration(method);
      if (JavaNodes.isNative(methodElement)) {
        // do nothing with the native methods as no code will be generated.
        // the check will be done only for the method that has a body and that is supposed to me the most
        // generic version of the overloaded method
        return;
      }
      TreeWrapper<Tree, Void> tw = context.getCurrentWrapper().child(member);
      if (MemberWriters.shouldSkip(tw)) {
        return;
      }
      if (methodElement.getKind() != ElementKind.METHOD) {
        // skip the constructors
        return;
      }
      checkMethodName(classElement, method, methodElement, context, existingNames);
    }
  }

  private boolean hasOnlyFields(Collection<Element> sameName) {
    for (Element el : sameName) {
      if (!(el instanceof VariableElement)) {
        return false;
      }
    }
    return true;
  }

  private void checkField(Tree member, GenerationContext<Void> context, Multimap<String, Element> existingNames) {
    if (member instanceof VariableTree) {
      TreeWrapper<Tree, Void> tw = context.getCurrentWrapper().child(member);
      if (MemberWriters.shouldSkip(tw)) {
        return;
      }

      String name = ((VariableTree) member).getName().toString();
      Collection<Element> sameName = existingNames.get(name);
      if (sameName.isEmpty()) {
        Element variableElement = TreeUtils.elementFromDeclaration((VariableTree) member);
        existingNames.put(name, variableElement);
      } else {
        if (!hasOnlyFields(sameName)) {
          // accept fields with the same name, but not methods and fields
          context.addError(member, "The type (or one of its parents) contains already a method called [" + name
              + "]. Javascript cannot distinguish methods/fields with the same name");
        }
      }
    }
  }

  @Override
  public Void visit(CheckVisitor visitor, ClassTree tree, GenerationContext<Void> context) {
    Multimap<String, Element> names = LinkedListMultimap.create();

    // TypeElement classElement = TreeUtils.elementFromDeclaration(tree);
    TypeElement classElement = (TypeElement) context.getTrees().getElement(context.getCurrentPath());
    TypeMirror superType = classElement.getSuperclass();
    if (superType.getKind() != TypeKind.NONE) {
      // add the names from the super class
      TypeElement superclassElement = (TypeElement) ((DeclaredType) superType).asElement();
      for (Element memberElement : context.getElements().getAllMembers(superclassElement)) {
        if (!JavaNodes.isNative(memberElement)) {
          names.put(memberElement.getSimpleName().toString(), memberElement);
        }
      }
    }
    // check first the methods
    for (Tree member : tree.getMembers()) {
      checkMethod(classElement, member, context, names);
    }
    // check the fields
    for (Tree member : tree.getMembers()) {
      checkField(member, context, names);
    }
    return null;
  }
}
TOP

Related Classes of org.stjs.generator.check.declaration.ClassDuplicateMemberNameCheck

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.