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

    if (anonymous) {
      ClassNode child = DecompilerContext.getClassProcessor().getMapRootClasses().get(newType.getValue());

      boolean selfReference = DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE) == child;

      // IDEA-204310 - avoid backtracking later on for lambdas (causes spurious imports)
      if (!enumConst && (!lambda || DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS))) {
        String enclosing = null;

        if (!lambda && constructor != null) {
          enclosing = getQualifiedNewInstance(child.anonymousClassType.getValue(), constructor.getParameters(), indent, tracer);
          if (enclosing != null) {
            buf.append(enclosing).append('.');
          }
        }

        buf.append("new ");

        if (selfReference) {
          buf.append("<anonymous constructor>");
        } else {
          String typename = ExprProcessor.getCastTypeName(child.anonymousClassType, Collections.emptyList());
          if (enclosing != null) {
            ClassNode anonymousNode = DecompilerContext.getClassProcessor().getMapRootClasses().get(child.anonymousClassType.getValue());
            if (anonymousNode != null) {
              typename = anonymousNode.simpleName;
            }
            else {
              typename = typename.substring(typename.lastIndexOf('.') + 1);
            }
          }

          GenericClassDescriptor descriptor = ClassWriter.getGenericClassDescriptor(child.classStruct);
          if (descriptor != null) {
            if (descriptor.superinterfaces.isEmpty()) {
              buf.append(GenericMain.getGenericCastTypeName(descriptor.superclass, Collections.emptyList()));
            }
            else {
              if (descriptor.superinterfaces.size() > 1 && !lambda) {
                DecompilerContext.getLogger().writeMessage("Inconsistent anonymous class signature: " + child.classStruct.qualifiedName,
                                                           IFernflowerLogger.Severity.WARN);
              }
              buf.append(GenericMain.getGenericCastTypeName(descriptor.superinterfaces.get(0), Collections.emptyList()));
            }
          }
          else {
            buf.append(typename);
          }
        }
      }

      buf.append('(');

      if (!lambda && constructor != null) {
        List<Exprent> parameters = constructor.getParameters();
        List<VarVersionPair> mask = child.getWrapper().getMethodWrapper(CodeConstants.INIT_NAME, constructor.getStringDescriptor()).synthParameters;
        if (mask == null) {
          InvocationExprent superCall = child.superInvocation;
          mask = ExprUtil.getSyntheticParametersMask(superCall.getClassName(), superCall.getStringDescriptor(), parameters.size());
        }

        int start = enumConst ? 2 : 0;
        boolean firstParam = true;
        for (int i = start; i < parameters.size(); i++) {
          if (mask == null || mask.get(i) == null) {
            if (!firstParam) {
              buf.append(", ");
            }

            ExprProcessor.getCastedExprent(parameters.get(i), constructor.getDescriptor().params[i], buf, indent, true, tracer);

            firstParam = false;
          }
        }
      }

      buf.append(')');

      if (enumConst && buf.length() == 2) {
        buf.setLength(0);
      }

      if (lambda) {
        if (!DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS)) {
          buf.setLength(0);  // remove the usual 'new <class>()', it will be replaced with lambda style '() ->'
        }
        Exprent methodObject = constructor == null ? null : constructor.getInstance();
        TextBuffer clsBuf = new TextBuffer();
        new ClassWriter().classLambdaToJava(child, clsBuf, methodObject, indent, tracer);
        buf.append(clsBuf);
        tracer.incrementCurrentSourceLine(clsBuf.countLines());
      }
      else if (!selfReference) {
        TextBuffer clsBuf = new TextBuffer();
        new ClassWriter().classToJava(child, clsBuf, indent, tracer);
        buf.append(clsBuf);
        tracer.incrementCurrentSourceLine(clsBuf.countLines());
      }
    }
    else if (directArrayInit) {
      VarType leftType = newType.decreaseArrayDim();
      buf.append('{');
      for (int i = 0; i < lstArrayElements.size(); i++) {
        if (i > 0) {
          buf.append(", ");
        }
        ExprProcessor.getCastedExprent(lstArrayElements.get(i), leftType, buf, indent, false, tracer);
      }
      buf.append('}');
    }
    else if (newType.getArrayDim() == 0) {
      if (!enumConst) {
        String enclosing = null;

        if (constructor != null) {
          enclosing = getQualifiedNewInstance(newType.getValue(), constructor.getParameters(), indent, tracer);
          if (enclosing != null) {
            buf.append(enclosing).append('.');
          }
        }

        buf.append("new ");

        String typename = ExprProcessor.getTypeName(newType, Collections.emptyList());
        if (enclosing != null) {
          ClassNode newNode = DecompilerContext.getClassProcessor().getMapRootClasses().get(newType.getValue());
          if (newNode != null) {
            typename = newNode.simpleName;
          }
          else {
            typename = typename.substring(typename.lastIndexOf('.') + 1);
          }
        }
        buf.append(typename);
      }

      if (constructor != null) {
        List<Exprent> parameters = constructor.getParameters();
        List<VarVersionPair> mask = ExprUtil.getSyntheticParametersMask(constructor.getClassName(), constructor.getStringDescriptor(), parameters.size());

        int start = enumConst ? 2 : 0;
        if (!enumConst || start < parameters.size()) {
          buf.append('(');

          boolean firstParam = true;
          for (int i = start; i < parameters.size(); i++) {
            if (mask == null || mask.get(i) == null) {
              Exprent expr = parameters.get(i);
              VarType leftType = constructor.getDescriptor().params[i];

              if (i == parameters.size() - 1 && expr.getExprType() == VarType.VARTYPE_NULL && probablySyntheticParameter(
                leftType.getValue())) {
                break;  // skip last parameter of synthetic constructor call
              }

              if (!firstParam) {
                buf.append(", ");
              }

              ExprProcessor.getCastedExprent(expr, leftType, buf, indent, true, false, true, true, tracer);

              firstParam = false;
            }
          }

          buf.append(')');
        }
      }
    }
    else if (isVarArgParam) {
      // just print the array elements
      VarType leftType = newType.decreaseArrayDim();
      for (int i = 0; i < lstArrayElements.size(); i++) {
        if (i > 0) {
          buf.append(", ");
        }

        // new String[][]{{"abc"}, {"DEF"}} => new String[]{"abc"}, new String[]{"DEF"}
        Exprent element = lstArrayElements.get(i);
        if (element.type == EXPRENT_NEW) {
          ((NewExprent) element).setDirectArrayInit(false);
        }
        ExprProcessor.getCastedExprent(element, leftType, buf, indent, false, tracer);
      }

      // if there is just one element of Object[] type it needs to be casted to resolve ambiguity
      if (lstArrayElements.size() == 1) {
        VarType elementType = lstArrayElements.get(0).getExprType();
        if (elementType.getType() == CodeConstants.TYPE_OBJECT && elementType.getValue().equals("java/lang/Object") && elementType.getArrayDim() >= 1) {
          buf.prepend("(Object)");
        }
      }
    }
    else {
      buf.append("new ").append(ExprProcessor.getTypeName(newType, Collections.emptyList()));

      if (lstArrayElements.isEmpty()) {
        for (int i = 0; i < newType.getArrayDim(); i++) {
          buf.append('[');
          if (i < lstDims.size()) {
            buf.append(lstDims.get(i).toJava(indent, tracer));
          }
          buf.append(']');
        }
      }
      else {
        for (int i = 0; i < newType.getArrayDim(); i++) {
          buf.append("[]");
        }

        VarType leftType = newType.decreaseArrayDim();
        buf.append('{');
        for (int i = 0; i < lstArrayElements.size(); i++) {
          if (i > 0) {
            buf.append(", ");
          }
          ExprProcessor.getCastedExprent(lstArrayElements.get(i), leftType, buf, indent, false, tracer);
        }
        buf.append('}');
      }
    }

    return buf;
  }
@Override
  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer)