com.google.javascript.jscomp.JSDocInfoPrinter.java Source code

Java tutorial

Introduction

Here is the source code for com.google.javascript.jscomp.JSDocInfoPrinter.java

Source

/*
 * Copyright 2014 The Closure Compiler Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.javascript.jscomp;

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.JSDocInfo.Visibility;
import com.google.javascript.rhino.JSTypeExpression;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;

import java.util.Iterator;

/**
 * Prints a JSDocInfo, used for preserving type annotations in ES6 transpilation.
 *
 */
public final class JSDocInfoPrinter {
    public static String print(JSDocInfo info) {
        StringBuilder sb = new StringBuilder("/**");
        if (info.isConstructor()) {
            sb.append("@constructor ");
        }
        if (info.isInterface() && !info.usesImplicitMatch()) {
            sb.append("@interface ");
        }
        if (info.isInterface() && info.usesImplicitMatch()) {
            sb.append("@record ");
        }
        if (info.makesDicts()) {
            sb.append("@dict ");
        }
        if (info.makesStructs()) {
            sb.append("@struct ");
        }
        if (info.makesUnrestricted()) {
            sb.append("@unrestricted ");
        }
        if (info.isDefine()) {
            sb.append("@define {");
            appendTypeNode(sb, info.getType().getRoot());
            sb.append("} ");
        }

        if (info.isOverride()) {
            sb.append("@override ");
        }
        if (info.isConstant()) {
            sb.append("@const ");
        }
        if (info.isExport()) {
            sb.append("@export ");
        }
        if (info.isDeprecated()) {
            sb.append("@deprecated ");
            sb.append(info.getDeprecationReason() + " ");
        }
        if (info.getVisibility() != null && info.getVisibility() != Visibility.INHERITED) {
            sb.append("@" + info.getVisibility().toString().toLowerCase() + " ");
        }

        Iterator<String> suppressions = info.getSuppressions().iterator();
        if (suppressions.hasNext()) {
            sb.append("@suppress {");
            while (suppressions.hasNext()) {
                sb.append(suppressions.next());
                if (suppressions.hasNext()) {
                    sb.append(",");
                }
            }
            sb.append("} ");
        }

        ImmutableList<String> names = info.getTemplateTypeNames();
        if (!names.isEmpty()) {
            sb.append("@template ");
            Joiner.on(',').appendTo(sb, names);
            sb.append("\n"); // @template needs a newline afterwards
        }

        if (info.getParameterCount() > 0) {
            for (String name : info.getParameterNames()) {
                sb.append("@param ");
                if (info.getParameterType(name) != null) {
                    sb.append("{");
                    appendTypeNode(sb, info.getParameterType(name).getRoot());
                    sb.append("} ");
                }
                sb.append(name);
                sb.append(' ');
            }
        }
        if (info.hasReturnType()) {
            sb.append("@return {");
            appendTypeNode(sb, info.getReturnType().getRoot());
            sb.append("} ");
        }
        if (info.hasThisType()) {
            sb.append("@this {");
            Node typeNode = info.getThisType().getRoot();
            if (typeNode.getType() == Token.BANG) {
                appendTypeNode(sb, typeNode.getFirstChild());
            } else {
                appendTypeNode(sb, typeNode);
            }
            sb.append("} ");
        }
        if (info.hasBaseType()) {
            sb.append("@extends {");
            Node typeNode = info.getBaseType().getRoot();
            if (typeNode.getType() == Token.BANG) {
                appendTypeNode(sb, typeNode.getFirstChild());
            } else {
                appendTypeNode(sb, typeNode);
            }
            sb.append("} ");
        }
        for (JSTypeExpression type : info.getImplementedInterfaces()) {
            sb.append("@implements {");
            Node typeNode = type.getRoot();
            if (typeNode.getType() == Token.BANG) {
                appendTypeNode(sb, typeNode.getFirstChild());
            } else {
                appendTypeNode(sb, typeNode);
            }
            sb.append("} ");
        }
        if (info.hasTypedefType()) {
            sb.append("@typedef {");
            appendTypeNode(sb, info.getTypedefType().getRoot());
            sb.append("} ");
        }
        if (info.hasType()) {
            if (info.isInlineType()) {
                sb.append(" ");
                appendTypeNode(sb, info.getType().getRoot());
                sb.append(" ");
            } else {
                sb.append("@type {");
                appendTypeNode(sb, info.getType().getRoot());
                sb.append("} ");
            }
        }
        if (!info.getThrownTypes().isEmpty()) {
            sb.append("@throws {");
            appendTypeNode(sb, info.getThrownTypes().get(0).getRoot());
            sb.append("} ");
        }
        if (info.hasEnumParameterType()) {
            sb.append("@enum {");
            appendTypeNode(sb, info.getEnumParameterType().getRoot());
            sb.append("} ");
        }
        sb.append("*/");
        return sb.toString();
    }

    private static void appendTypeNode(StringBuilder sb, Node typeNode) {
        if (typeNode.getType() == Token.BANG) {
            sb.append("!");
            appendTypeNode(sb, typeNode.getFirstChild());
        } else if (typeNode.getType() == Token.EQUALS) {
            appendTypeNode(sb, typeNode.getFirstChild());
            sb.append("=");
        } else if (typeNode.getType() == Token.PIPE) {
            sb.append("(");
            for (int i = 0; i < typeNode.getChildCount() - 1; i++) {
                appendTypeNode(sb, typeNode.getChildAtIndex(i));
                sb.append("|");
            }
            appendTypeNode(sb, typeNode.getLastChild());
            sb.append(")");
        } else if (typeNode.getType() == Token.ELLIPSIS) {
            sb.append("...");
            if (typeNode.hasChildren()) {
                appendTypeNode(sb, typeNode.getFirstChild());
            }
        } else if (typeNode.getType() == Token.STAR) {
            sb.append("*");
        } else if (typeNode.getType() == Token.QMARK) {
            sb.append("?");
        } else if (typeNode.isFunction()) {
            appendFunctionNode(sb, typeNode);
        } else if (typeNode.getType() == Token.LC) {
            sb.append("{");
            Node lb = typeNode.getFirstChild();
            for (int i = 0; i < lb.getChildCount() - 1; i++) {
                Node colon = lb.getChildAtIndex(i);
                if (colon.hasChildren()) {
                    sb.append(colon.getFirstChild().getString() + ":");
                    appendTypeNode(sb, colon.getLastChild());
                } else {
                    sb.append(colon.getString());
                }
                sb.append(",");
            }
            Node lastColon = lb.getLastChild();
            if (lastColon.hasChildren()) {
                sb.append(lastColon.getFirstChild().getString() + ":");
                appendTypeNode(sb, lastColon.getLastChild());
            } else {
                sb.append(lastColon.getString());
            }
            sb.append("}");
        } else if (typeNode.getType() == Token.VOID) {
            sb.append("void");
        } else {
            if (typeNode.hasChildren()) {
                sb.append(typeNode.getString()).append("<");
                Node child = typeNode.getFirstChild();
                appendTypeNode(sb, child.getFirstChild());
                for (int i = 1; i < child.getChildCount(); i++) {
                    sb.append(",");
                    appendTypeNode(sb, child.getChildAtIndex(i));
                }
                sb.append(">");
            } else {
                sb.append(typeNode.getString());
            }
        }
    }

    private static void appendFunctionNode(StringBuilder sb, Node function) {
        boolean hasNewOrThis = false;
        sb.append("function(");
        Node first = function.getFirstChild();
        if (first.isNew()) {
            sb.append("new:");
            appendTypeNode(sb, first.getFirstChild());
            hasNewOrThis = true;
        } else if (first.isThis()) {
            sb.append("this:");
            appendTypeNode(sb, first.getFirstChild());
            hasNewOrThis = true;
        } else if (first.isEmpty()) {
            sb.append(")");
            return;
        } else if (!first.isParamList()) {
            sb.append("):");
            appendTypeNode(sb, first);
            return;
        }
        Node paramList = null;
        if (first.isParamList()) {
            paramList = first;
        } else if (first.getNext().isParamList()) {
            paramList = first.getNext();
        }
        if (paramList != null) {
            boolean firstParam = true;
            for (Node param : paramList.children()) {
                if (!firstParam || hasNewOrThis) {
                    sb.append(",");
                }
                appendTypeNode(sb, param);
                firstParam = false;
            }
        }
        sb.append(")");

        Node returnType = function.getLastChild();
        if (!returnType.isEmpty()) {
            sb.append(":");
            appendTypeNode(sb, returnType);
        }
    }
}