com.mirth.connect.client.ui.reference.ClassVisitor.java Source code

Java tutorial

Introduction

Here is the source code for com.mirth.connect.client.ui.reference.ClassVisitor.java

Source

/*
 * Copyright (c) Mirth Corporation. All rights reserved.
 * 
 * http://www.mirthcorp.com
 * 
 * The software in this package is published under the terms of the MPL license a copy of which has
 * been included with this distribution in the LICENSE.txt file.
 */

package com.mirth.connect.client.ui.reference;

import japa.parser.ast.CompilationUnit;
import japa.parser.ast.PackageDeclaration;
import japa.parser.ast.body.BodyDeclaration;
import japa.parser.ast.body.ClassOrInterfaceDeclaration;
import japa.parser.ast.body.ConstructorDeclaration;
import japa.parser.ast.body.JavadocComment;
import japa.parser.ast.body.MethodDeclaration;
import japa.parser.ast.body.ModifierSet;
import japa.parser.ast.body.Parameter;
import japa.parser.ast.expr.NameExpr;
import japa.parser.ast.expr.QualifiedNameExpr;
import japa.parser.ast.type.ClassOrInterfaceType;
import japa.parser.ast.visitor.VoidVisitorAdapter;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.map.CaseInsensitiveMap;
import org.apache.commons.lang3.StringUtils;
import org.fife.rsta.ac.js.IconFactory;

import com.mirth.connect.client.ui.PlatformUI;
import com.mirth.connect.client.ui.UIConstants;
import com.mirth.connect.model.CodeTemplateContextSet;
import com.mirth.connect.model.CodeTemplateFunctionDefinition;
import com.mirth.connect.model.Parameters;

public class ClassVisitor extends VoidVisitorAdapter<Object> {

    private static final Pattern JAVADOC_ANNOTATION_PATTERN = Pattern
            .compile("(?<!\\{)@(?<key>\\w+)\\s+(?<value>([^@]|(?<=\\{)@)*)");

    private List<String> inputTextList;
    private List<Reference> references = new ArrayList<Reference>();

    public ClassVisitor(List<String> inputTextList) {
        this.inputTextList = inputTextList;
    }

    public static List<Reference> getReferencesByCompilationUnit(CompilationUnit compilationUnit,
            List<String> inputTextList) {
        ClassVisitor visitor = new ClassVisitor(inputTextList);
        visitor.visit(compilationUnit, null);
        return visitor.getReferences();
    }

    @Override
    public void visit(PackageDeclaration n, Object arg) {
        n.getName().accept(this, arg);
    }

    public void visit(QualifiedNameExpr n, Object arg) {
        n.getQualifier().accept(this, arg);
        if (arg instanceof StringBuilder) {
            ((StringBuilder) arg).append('.');
        }
        visit((NameExpr) n, arg);
    }

    @Override
    public void visit(NameExpr n, Object arg) {
        if (arg instanceof StringBuilder) {
            ((StringBuilder) arg).append(n.getName());
        }
    }

    @Override
    public void visit(CompilationUnit n, Object arg) {
        String packageName = null;
        if (n.getPackage() != null) {
            StringBuilder builder = new StringBuilder();
            visit(n.getPackage(), builder);
            packageName = builder.toString();
        }

        super.visit(n, packageName);
    }

    @Override
    public void visit(ClassOrInterfaceDeclaration n, Object arg) {
        String className = n.getName();
        String packageName = (String) arg;

        StringBuilder builder = new StringBuilder();
        builder.append("<html><body><b>");

        if (StringUtils.isNotBlank(packageName)) {
            builder.append("package ");
            builder.append(packageName);
        }

        builder.append("<br/><h3><a href=\"");
        builder.append(PlatformUI.SERVER_URL);
        builder.append(UIConstants.USER_API_LOCATION);

        if (StringUtils.isNotBlank(packageName)) {
            builder.append(packageName.replace(".", "/"));
            builder.append('/');
            builder.append(className);
            builder.append(".html");
        }

        builder.append("\">");
        builder.append(n.isInterface() ? "Interface " : "Class ");
        builder.append(className);
        builder.append("</a></h3></b><hr/><code>");

        if (ModifierSet.isPublic(n.getModifiers())) {
            builder.append("public ");
        } else {
            // Don't add references for non-public classes
            return;
        }

        if (ModifierSet.isStatic(n.getModifiers())) {
            builder.append("static ");
        }
        if (ModifierSet.isAbstract(n.getModifiers())) {
            builder.append("abstract ");
        }
        if (ModifierSet.isFinal(n.getModifiers())) {
            builder.append("final ");
        }
        if (ModifierSet.isNative(n.getModifiers())) {
            builder.append("native ");
        }
        if (ModifierSet.isSynchronized(n.getModifiers())) {
            builder.append("synchronized ");
        }
        if (ModifierSet.isTransient(n.getModifiers())) {
            builder.append("transient ");
        }
        if (ModifierSet.isVolatile(n.getModifiers())) {
            builder.append("volatile ");
        }

        if (n.isInterface()) {
            builder.append("interface ");
        } else {
            builder.append("class ");
        }

        builder.append(className);

        List<ClassOrInterfaceType> extendsList = n.getExtends();
        if (CollectionUtils.isNotEmpty(extendsList)) {
            ClassOrInterfaceType type = extendsList.get(0);
            builder.append("<br/>extends ");
            builder.append(type.getName());
        }

        List<ClassOrInterfaceType> implementsList = n.getImplements();
        if (CollectionUtils.isNotEmpty(implementsList)) {
            builder.append("<br/>implements ");
            for (Iterator<ClassOrInterfaceType> it = implementsList.iterator(); it.hasNext();) {
                ClassOrInterfaceType type = it.next();
                builder.append(type.getName());
                if (it.hasNext()) {
                    builder.append(", ");
                }
            }
        }

        builder.append("</code><br/><br/>");

        if (n.getJavaDoc() != null) {
            String comment = StringUtils.trim(n.getJavaDoc().getContent());
            if (StringUtils.isNotBlank(comment)) {
                builder.append(encode(convertComment(comment)));
            }
        }

        String summary = builder.toString();

        references.add(new ClassReference(CodeTemplateContextSet.getGlobalContextSet(), null, className,
                inputTextList, summary));

        if (n.getMembers() != null) {
            for (BodyDeclaration member : n.getMembers()) {
                member.accept(this, className);
            }
        }
    }

    @Override
    public void visit(ConstructorDeclaration n, Object arg) {
        addMethod(true, (String) arg, n.getName(), n.getName(), n.getModifiers(), n.getJavaDoc(),
                n.getParameters());
    }

    @Override
    public void visit(MethodDeclaration n, Object arg) {
        addMethod(false, (String) arg, n.getName(), n.getType().toString(), n.getModifiers(), n.getJavaDoc(),
                n.getParameters());
    }

    private void addMethod(boolean constructor, String className, String name, String type, int modifiers,
            JavadocComment javadoc, List<Parameter> parameters) {
        String iconName = null;
        if (ModifierSet.isPublic(modifiers)) {
            if (ModifierSet.isStatic(modifiers)) {
                iconName = IconFactory.PUBLIC_STATIC_FUNCTION_ICON;
            } else {
                iconName = IconFactory.PUBLIC_METHOD_ICON;
            }
        } else {
            // Don't add references for non-public methods
            return;
        }

        String comment = null;
        Map<String, String> parameterComments = new CaseInsensitiveMap();
        String returnComment = constructor ? "A new " + name + " object." : null;
        String deprecatedComment = null;

        if (javadoc != null) {
            comment = StringUtils.trim(javadoc.getContent());
            if (StringUtils.isNotBlank(comment)) {
                Matcher matcher = JAVADOC_ANNOTATION_PATTERN.matcher(comment);
                while (matcher.find() && matcher.groupCount() >= 2) {
                    String key = matcher.group("key");
                    String value = convertComment(matcher.group("value"));

                    if (key.equalsIgnoreCase("param")) {
                        int index = value.indexOf(' ');
                        if (index >= 0) {
                            key = value.substring(0, index).trim();
                            value = value.substring(index).trim();
                        } else {
                            key = value;
                            value = "";
                        }

                        parameterComments.put(key, value);
                    } else if (key.equalsIgnoreCase("return")) {
                        returnComment = value;
                    } else if (key.equalsIgnoreCase("deprecated")) {
                        deprecatedComment = value;
                    }
                }

                comment = convertComment(comment);

                if (StringUtils.isNotBlank(deprecatedComment)) {
                    comment = "<b>Deprecated.</b> <em>" + deprecatedComment + "</em><br/><br/>" + comment;
                }
            }
        }

        Parameters params = new Parameters();
        if (CollectionUtils.isNotEmpty(parameters)) {
            for (Parameter parameter : parameters) {
                String parameterName = parameter.getId().getName();
                params.add(parameterName, parameter.getType().toString(), parameterComments.get(parameterName));
            }
        }

        Reference reference;
        if (constructor) {
            reference = new ConstructorReference(CodeTemplateContextSet.getGlobalContextSet(), null, name, name,
                    comment, null, new CodeTemplateFunctionDefinition(name, params, type, returnComment));
        } else {
            reference = new FunctionReference(CodeTemplateContextSet.getGlobalContextSet(), null, className, name,
                    comment, null, new CodeTemplateFunctionDefinition(name, params, type, returnComment),
                    inputTextList);
        }

        if (StringUtils.isNotBlank(deprecatedComment)) {
            reference.setDeprecated(true);
        }

        reference.setIconName(iconName);
        references.add(reference);
    }

    public List<Reference> getReferences() {
        return references;
    }

    private String convertComment(String comment) {
        return comment.replaceAll("(?m)^[\\s*]*", "")
                .replaceAll("\\{@\\w*\\s*(#[^\\}\\s]*)?\\s*(?<value>[^\\}]*)\\s*\\}", "${value}")
                .replaceAll("(\r\n|\r|\n)@[\\S\\s]*", "").replaceAll("\r\n|\r|\n", " ").replaceAll("\\s{2,}", " ")
                .trim();
    }

    private String encode(String str) {
        return str.replace("<", "&lt;").replace(">", "&gt;");
    }
}