code: /*
   * Copyright (c) 2008-2019 Emmanuel Dupuy.
   * This project is distributed under the GPLv3 license.
   * This is a Copyleft license that gives the user the right to use,
   * copy and modify the code freely for non-commercial purposes.
   */
  
  package org.jd.gui.util.parser.antlr;
  
  import org.antlr.v4.runtime.tree.ParseTree;
  import org.antlr.v4.runtime.tree.TerminalNode;
  import org.antlr.v4.runtime.tree.TerminalNodeImpl;
  import org.jd.gui.api.model.Container;
  import org.jd.gui.util.exception.ExceptionUtil;
  
  import java.util.HashMap;
  import java.util.List;
  
  public abstract class AbstractJavaListener extends JavaBaseListener {
      protected Container.Entry entry;
      protected String packageName = "";
      protected HashMap<String, String> nameToInternalTypeName = new HashMap<>();
      protected StringBuilder sb = new StringBuilder();
      protected HashMap<String, String> typeNameCache = new HashMap<>();
  
      public AbstractJavaListener(Container.Entry entry) {
          this.entry = entry;
      }
  
      public void enterPackageDeclaration(JavaParser.PackageDeclarationContext ctx) {
          packageName = concatIdentifiers(ctx.qualifiedName().Identifier());
      }
  
      public void enterImportDeclaration(JavaParser.ImportDeclarationContext ctx) {
          List<TerminalNode> identifiers = ctx.qualifiedName().Identifier();
          int size = identifiers.size();
  
          if (size > 1) {
              nameToInternalTypeName.put(identifiers.get(size - 1).getText(), concatIdentifiers(identifiers));
          }
      }
  
      protected String concatIdentifiers(List<TerminalNode> identifiers) {
          switch (identifiers.size()) {
              case 0:
                  return "";
              case 1:
                  return identifiers.get(0).getText();
              default:
                  sb.setLength(0);
  
                  for (TerminalNode identifier : identifiers) {
                      sb.append(identifier.getText()).append('/');
                  }
  
                  // Remove last separator
                  sb.setLength(sb.length() - 1);
  
                  return sb.toString();
          }
      }
  
      protected String resolveInternalTypeName(List<TerminalNode> identifiers) {
          switch (identifiers.size()) {
              case 0:
                  return null;
  
              case 1:
                  // Search in cache
                  String name = identifiers.get(0).getText();
                  String qualifiedName = typeNameCache.get(name);
  
                  if (qualifiedName != null) {
                      return qualifiedName;
                  }
  
                  // Search in imports
                  String imp = nameToInternalTypeName.get(name);
  
                  if (imp != null) {
                      // Import found
                      return imp;
                  }
  
                  // Search type in same package
                  String prefix = name + '.';
  
                  if (entry.getPath().indexOf('/') != -1) {
                      // Not in root package
                      Container.Entry parent = entry.getParent();
                      int packageLength = parent.getPath().length() + 1;
  
                      for (Container.Entry child : parent.getChildren()) {
                          if (!child.isDirectory() && child.getPath().substring(packageLength).startsWith(prefix)) {
                              qualifiedName = packageName + '/' + name;
                              typeNameCache.put(name, qualifiedName);
                              return qualifiedName;
                          }
                      }
                  }
  
                  // Search type in root package
                  for (Container.Entry child : entry.getContainer().getRoot().getChildren()) {
                      if (!child.isDirectory() && child.getPath().startsWith(prefix)) {
                          typeNameCache.put(name, name);
                          return name;
                      }
                  }
  
                  // Search type in 'java.lang'
                  try {
                      if (Class.forName("java.lang." + name) != null) {
                          qualifiedName = "java/lang/" + name;
                          typeNameCache.put(name, qualifiedName);
                          return qualifiedName;
                      }
                  } catch (ClassNotFoundException ignore) {
                      // Ignore class loading error
                  }
  
                  // Type not found
                  qualifiedName = "*/" + name;
                  typeNameCache.put(name, qualifiedName);
                  return qualifiedName;
  
              default:
                  // Qualified type name -> Nothing to do
                  return concatIdentifiers(identifiers);
          }
      }
  
      protected String createDescriptor(JavaParser.TypeContext typeContext, int dimension) {
          if (typeContext == null) {
              return "V";
          } else {
              dimension += countDimension(typeContext.children);
              JavaParser.PrimitiveTypeContext primitive = typeContext.primitiveType();
              String name;
  
              if (primitive == null) {
                  JavaParser.ClassOrInterfaceTypeContext type = typeContext.classOrInterfaceType();
                  List<JavaParser.TypeArgumentsContext> typeArgumentsContexts = type.typeArguments();
  
                  if (typeArgumentsContexts.size() == 1) {
                      JavaParser.TypeArgumentsContext typeArgumentsContext = typeArgumentsContexts.get(0);
                      List<JavaParser.TypeArgumentContext> typeArguments = typeArgumentsContext.typeArgument();
                  } else if (typeArgumentsContexts.size() > 1) {
                      throw new RuntimeException("UNEXPECTED");
                  }
  
                  name = "L" + resolveInternalTypeName(type.Identifier()) + ";";
              } else {
                  // Search primitive
                  switch (primitive.getText()) {
                      case "boolean": name = "Z"; break;
                      case "byte":    name = "B"; break;
                      case "char":    name = "C"; break;
                      case "double":  name = "D"; break;
                      case "float":   name = "F"; break;
                      case "int":     name = "I"; break;
                      case "long":    name = "J"; break;
                      case "short":   name = "S"; break;
                      case "void":    name = "V"; break;
                      default:
                          throw new RuntimeException("UNEXPECTED PRIMITIVE");
                  }
              }
  
              switch (dimension) {
                  case 0:  return name;
                  case 1:  return "[" + name;
                  case 2:  return "[[" + name;
                  default: return new String(new char[dimension]).replace('\0', '[') + name;
              }
          }
      }
  
      protected int countDimension(List<ParseTree> children) {
          int dimension = 0;
  
          for (ParseTree child : children) {
              if (child instanceof TerminalNodeImpl) {
                  if (((TerminalNodeImpl)child).getSymbol().getType() == JavaParser.LBRACK)
                      dimension++;
              }
          }
  
          return dimension;
      }
  }
  