All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mockit.internal.util.MethodFormatter Maven / Gradle / Ivy

/*
 * Copyright (c) 2006 JMockit developers
 * This file is subject to the terms of the MIT license (see LICENSE.txt).
 */
package mockit.internal.util;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import mockit.internal.state.ParameterNames;

public final class MethodFormatter {
    private static final Pattern CONSTRUCTOR_NAME = Pattern.compile("");
    private static final Pattern JAVA_LANG_PREFIX = Pattern.compile("java/lang/");

    @NonNull
    private final StringBuilder out;
    @NonNull
    private final List parameterTypes;
    @NonNull
    private final String classDesc;
    @NonNull
    private String methodDesc;

    // Auxiliary fields for handling method parameters:
    private int parameterIndex;
    private int typeDescPos;
    private char typeCode;
    private int arrayDimensions;

    public MethodFormatter(@Nullable String classDesc) {
        out = new StringBuilder();
        parameterTypes = new ArrayList<>(5);
        this.classDesc = classDesc;
        methodDesc = "";
    }

    public MethodFormatter(@NonNull String classDesc, @NonNull String methodNameAndDesc) {
        this(classDesc, methodNameAndDesc, true);
    }

    public MethodFormatter(@NonNull String classDesc, @NonNull String methodNameAndDesc,
            boolean withParametersAppended) {
        out = new StringBuilder();
        parameterTypes = new ArrayList<>(5);
        this.classDesc = classDesc;
        methodDesc = "";
        methodDesc = methodNameAndDesc;
        appendFriendlyMethodSignature(withParametersAppended);
    }

    @Override
    public String toString() {
        return out.toString();
    }

    @NonNull
    public List getParameterTypes() {
        return parameterTypes;
    }

    private void appendFriendlyMethodSignature(boolean withParametersAppended) {
        String className = classDesc.replace('/', '.');
        out.append(className).append('#');

        String constructorName = getConstructorName(className);
        String friendlyDesc = CONSTRUCTOR_NAME.matcher(methodDesc).replaceFirst(constructorName);

        int leftParenNextPos = friendlyDesc.indexOf('(') + 1;
        int rightParenPos = friendlyDesc.indexOf(')');

        if (leftParenNextPos < rightParenPos) {
            out.append(friendlyDesc, 0, leftParenNextPos);

            String concatenatedParameterTypes = friendlyDesc.substring(leftParenNextPos, rightParenPos);

            if (withParametersAppended) {
                parameterIndex = 0;
                appendParameterTypesAndNames(concatenatedParameterTypes);
                out.append(')');
            } else {
                addParameterTypes(concatenatedParameterTypes);
            }
        } else {
            out.append(friendlyDesc, 0, rightParenPos + 1);
        }
    }

    @NonNull
    private static String getConstructorName(@NonNull String className) {
        int p = className.lastIndexOf('.');
        String constructorName = p < 0 ? className : className.substring(p + 1);

        // noinspection ReuseOfLocalVariable
        p = constructorName.lastIndexOf('$');

        if (p > 0) {
            constructorName = constructorName.substring(p + 1);
        }

        return constructorName;
    }

    private void appendParameterTypesAndNames(@NonNull String typeDescs) {
        String sep = "";

        for (String typeDesc : typeDescs.split(";")) {
            out.append(sep);

            if (typeDesc.charAt(0) == 'L') {
                appendParameterType(friendlyReferenceType(typeDesc));
                appendParameterName();
            } else {
                appendPrimitiveParameterTypesAndNames(typeDesc);
            }

            sep = ", ";
        }
    }

    @NonNull
    private static String friendlyReferenceType(@NonNull String typeDesc) {
        return JAVA_LANG_PREFIX.matcher(typeDesc.substring(1)).replaceAll("").replace('/', '.');
    }

    private void appendParameterType(@NonNull String friendlyTypeDesc) {
        out.append(friendlyTypeDesc);
        parameterTypes.add(friendlyTypeDesc);
    }

    private void appendParameterName() {
        String name = ParameterNames.getName(classDesc, methodDesc, parameterIndex);

        if (name != null) {
            out.append(' ').append(name);
        }

        parameterIndex++;
    }

    private void appendPrimitiveParameterTypesAndNames(@NonNull String typeDesc) {
        String sep = "";

        for (typeDescPos = 0; typeDescPos < typeDesc.length(); typeDescPos++) {
            typeCode = typeDesc.charAt(typeDescPos);
            advancePastArrayDimensionsIfAny(typeDesc);

            out.append(sep);

            String paramType = getTypeNameForTypeDesc(typeDesc) + getArrayBrackets();
            appendParameterType(paramType);
            appendParameterName();

            sep = ", ";
        }
    }

    private void addParameterTypes(@NonNull String typeDescs) {
        for (String typeDesc : typeDescs.split(";")) {
            if (typeDesc.charAt(0) == 'L') {
                parameterTypes.add(friendlyReferenceType(typeDesc));
            } else {
                addPrimitiveParameterTypes(typeDesc);
            }
        }
    }

    private void addPrimitiveParameterTypes(@NonNull String typeDesc) {
        for (typeDescPos = 0; typeDescPos < typeDesc.length(); typeDescPos++) {
            typeCode = typeDesc.charAt(typeDescPos);
            advancePastArrayDimensionsIfAny(typeDesc);

            String paramType = getTypeNameForTypeDesc(typeDesc) + getArrayBrackets();
            parameterTypes.add(paramType);
        }
    }

    @NonNull
    @SuppressWarnings("OverlyComplexMethod")
    private String getTypeNameForTypeDesc(@NonNull String typeDesc) {
        String paramType;

        switch (typeCode) {
            case 'B':
                return "byte";
            case 'C':
                return "char";
            case 'D':
                return "double";
            case 'F':
                return "float";
            case 'I':
                return "int";
            case 'J':
                return "long";
            case 'S':
                return "short";
            case 'V':
                return "void";
            case 'Z':
                return "boolean";
            case 'L':
                paramType = friendlyReferenceType(typeDesc.substring(typeDescPos));
                typeDescPos = typeDesc.length();
                break;
            default:
                paramType = typeDesc.substring(typeDescPos);
                typeDescPos = typeDesc.length();
        }

        return paramType;
    }

    private void advancePastArrayDimensionsIfAny(@NonNull String param) {
        arrayDimensions = 0;

        while (typeCode == '[') {
            typeDescPos++;
            typeCode = param.charAt(typeDescPos);
            arrayDimensions++;
        }
    }

    @NonNull
    private String getArrayBrackets() {
        @SuppressWarnings("NonConstantStringShouldBeStringBuffer")
        StringBuilder result = new StringBuilder();

        for (int i = 0; i < arrayDimensions; i++) {
            // noinspection StringContatenationInLoop
            result.append("[]");
        }

        return result.toString();
    }

    public void append(@NonNull String text) {
        out.append(text);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy