public String getShortName(String fullName, boolean imported) {
    ClassNode node =
        DecompilerContext.getClassProcessor().getMapRootClasses().get(fullName.replace('.', '/'));

    String result = null;
    if (node != null && node.classStruct.isOwn()) {
      result = node.simpleName;

      while (node.parent != null && node.type == ClassNode.CLASS_MEMBER) {
        result = node.parent.simpleName + '.' + result;
        node = node.parent;
      }

      if (node.type == ClassNode.CLASS_ROOT) {
        fullName = node.classStruct.qualifiedName;
        fullName = fullName.replace('/', '.');
      } else {
        return result;
      }
    } else {
      fullName = fullName.replace('$', '.');
    }

    String shortName = fullName;
    String packageName = "";

    int lastDot = fullName.lastIndexOf('.');
    if (lastDot >= 0) {
      shortName = fullName.substring(lastDot + 1);
      packageName = fullName.substring(0, lastDot);
    }

    StructContext context = DecompilerContext.getStructContext();

    // check for another class which could 'shadow' this one. Two cases:
    // 1) class with the same short name in the current package
    // 2) class with the same short name in the default package
    boolean existsDefaultClass =
        (context.getClass(currentPackageSlash + shortName) != null
                && !packageName.equals(currentPackagePoint))
            || // current package
            (context.getClass(shortName) != null
                && !currentPackagePoint.isEmpty()); // default package

    if (existsDefaultClass
        || (mapSimpleNames.containsKey(shortName)
            && !packageName.equals(mapSimpleNames.get(shortName)))) {
      //  don't return full name because if the class is a inner class, full name refers to the
      // parent full name, not the child full name
      return result == null ? fullName : (packageName + "." + result);
    } else if (!mapSimpleNames.containsKey(shortName)) {
      mapSimpleNames.put(shortName, packageName);
      if (!imported) {
        setNotImportedNames.add(shortName);
      }
    }

    return result == null ? shortName : result;
  }
  @Override
  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {
    tracer.addMapping(bytecode);

    if (exitType == EXIT_RETURN) {
      TextBuffer buffer = new TextBuffer("return");

      if (retType.type != CodeConstants.TYPE_VOID) {
        buffer.append(' ');
        ExprProcessor.getCastedExprent(value, retType, buffer, indent, false, tracer);
      }

      return buffer;
    } else {
      MethodWrapper method =
          (MethodWrapper) DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);
      ClassNode node =
          ((ClassNode) DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE));

      if (method != null && node != null) {
        StructExceptionsAttribute attr =
            (StructExceptionsAttribute)
                method.methodStruct.getAttributes().getWithKey("Exceptions");

        if (attr != null) {
          String classname = null;

          for (int i = 0; i < attr.getThrowsExceptions().size(); i++) {
            String exClassName = attr.getExcClassname(i, node.classStruct.getPool());
            if ("java/lang/Throwable".equals(exClassName)) {
              classname = exClassName;
              break;
            } else if ("java/lang/Exception".equals(exClassName)) {
              classname = exClassName;
            }
          }

          if (classname != null) {
            VarType exType = new VarType(classname, true);
            TextBuffer buffer = new TextBuffer("throw ");
            ExprProcessor.getCastedExprent(value, exType, buffer, indent, false, tracer);
            return buffer;
          }
        }
      }

      return value.toJava(indent, tracer).prepend("throw ");
    }
  }
  @Override
  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {
    TextBuffer buffer = new TextBuffer();

    tracer.addMapping(bytecode);

    if (classdef) {
      ClassNode child =
          DecompilerContext.getClassProcessor().getMapRootClasses().get(vartype.value);
      new ClassWriter().classToJava(child, buffer, indent, tracer);
    } else {
      String name = null;
      if (processor != null) {
        name = processor.getVarName(new VarVersionPaar(index, version));
      }

      if (definition) {
        if (processor != null
            && processor.getVarFinal(new VarVersionPaar(index, version))
                == VarTypeProcessor.VAR_FINALEXPLICIT) {
          buffer.append("final ");
        }
        buffer.append(ExprProcessor.getCastTypeName(getVartype())).append(" ");
      }
      buffer.append(name == null ? ("var" + index + (version == 0 ? "" : "_" + version)) : name);
    }

    return buffer;
  }
  @Override
  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {
    TextBuffer buffer = new TextBuffer();

    buffer.appendIndent(indent);
    buffer.append('@');
    buffer.append(
        DecompilerContext.getImportCollector()
            .getShortName(ExprProcessor.buildJavaClassName(className)));

    int type = getAnnotationType();

    if (type != ANNOTATION_MARKER) {
      buffer.append('(');

      boolean oneLiner = type == ANNOTATION_SINGLE_ELEMENT || indent < 0;

      for (int i = 0; i < parNames.size(); i++) {
        if (!oneLiner) {
          buffer.appendLineSeparator().appendIndent(indent + 1);
        }

        if (type != ANNOTATION_SINGLE_ELEMENT) {
          buffer.append(parNames.get(i));
          buffer.append(" = ");
        }

        buffer.append(parValues.get(i).toJava(0, tracer));

        if (i < parNames.size() - 1) {
          buffer.append(',');
        }
      }

      if (!oneLiner) {
        buffer.appendLineSeparator().appendIndent(indent);
      }

      buffer.append(')');
    }

    return buffer;
  }
  public void setVarVersions(RootStatement root) {
    StructMethod mt =
        (StructMethod) DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD);

    SSAConstructorSparseEx ssa = new SSAConstructorSparseEx();
    ssa.splitVariables(root, mt);

    FlattenStatementsHelper flattenHelper = new FlattenStatementsHelper();
    DirectGraph graph = flattenHelper.buildDirectGraph(root);

    mergePhiVersions(ssa, graph);

    typeProcessor = new VarTypeProcessor();
    typeProcessor.calculateVarTypes(root, graph);

    simpleMerge(typeProcessor, graph, mt);

    // FIXME: advanced merging

    eliminateNonJavaTypes(typeProcessor);

    setNewVarIndices(typeProcessor, graph);
  }
  private void setNewVarIndices(VarTypeProcessor typeProcessor, DirectGraph graph) {
    final Map<VarVersionPair, VarType> mapExprentMaxTypes = typeProcessor.getMapExprentMaxTypes();
    Map<VarVersionPair, VarType> mapExprentMinTypes = typeProcessor.getMapExprentMinTypes();
    Map<VarVersionPair, Integer> mapFinalVars = typeProcessor.getMapFinalVars();

    CounterContainer counters = DecompilerContext.getCounterContainer();

    final Map<VarVersionPair, Integer> mapVarPaar = new HashMap<VarVersionPair, Integer>();
    Map<Integer, Integer> mapOriginalVarIndices = new HashMap<Integer, Integer>();

    // map var-version pairs on new var indexes
    Set<VarVersionPair> set = new HashSet<VarVersionPair>(mapExprentMinTypes.keySet());
    for (VarVersionPair pair : set) {

      if (pair.version >= 0) {
        int newIndex =
            pair.version == 1
                ? pair.var
                : counters.getCounterAndIncrement(CounterContainer.VAR_COUNTER);

        VarVersionPair newVar = new VarVersionPair(newIndex, 0);

        mapExprentMinTypes.put(newVar, mapExprentMinTypes.get(pair));
        mapExprentMaxTypes.put(newVar, mapExprentMaxTypes.get(pair));

        if (mapFinalVars.containsKey(pair)) {
          mapFinalVars.put(newVar, mapFinalVars.remove(pair));
        }

        mapVarPaar.put(pair, newIndex);
        mapOriginalVarIndices.put(newIndex, pair.var);
      }
    }

    // set new vars
    graph.iterateExprents(
        new DirectGraph.ExprentIterator() {
          @Override
          public int processExprent(Exprent exprent) {
            List<Exprent> lst = exprent.getAllExprents(true);
            lst.add(exprent);

            for (Exprent expr : lst) {
              if (expr.type == Exprent.EXPRENT_VAR) {
                VarExprent newVar = (VarExprent) expr;
                Integer newVarIndex = mapVarPaar.get(new VarVersionPair(newVar));
                if (newVarIndex != null) {
                  newVar.setIndex(newVarIndex);
                  newVar.setVersion(0);
                }
              } else if (expr.type == Exprent.EXPRENT_CONST) {
                VarType maxType = mapExprentMaxTypes.get(new VarVersionPair(expr.id, -1));
                if (maxType != null && maxType.equals(VarType.VARTYPE_CHAR)) {
                  ((ConstExprent) expr).setConstType(maxType);
                }
              }
            }

            return 0;
          }
        });

    this.mapOriginalVarIndices = mapOriginalVarIndices;
  }
 {
   // set statement id
   id =
       DecompilerContext.getCounterContainer()
           .getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER);
 }