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

com.redhat.ceylon.ceylondoc.ClassOrPackageDoc Maven / Gradle / Ivy

There is a newer version: 1.3.3
Show newest version
/*
 * Copyright Red Hat Inc. and/or its affiliates and other contributors
 * as indicated by the authors tag. All rights reserved.
 *
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU General Public License version 2.
 * 
 * This particular file is subject to the "Classpath" exception as provided in the 
 * LICENSE file that accompanied this code.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License,
 * along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 */

package com.redhat.ceylon.ceylondoc;

import static com.redhat.ceylon.ceylondoc.Util.findBottomMostRefinedDeclaration;
import static com.redhat.ceylon.ceylondoc.Util.getDoc;
import static com.redhat.ceylon.ceylondoc.Util.getModifiers;
import static com.redhat.ceylon.ceylondoc.Util.getNameWithContainer;
import static com.redhat.ceylon.ceylondoc.Util.isAbbreviatedType;
import static com.redhat.ceylon.ceylondoc.Util.isEmpty;
import static com.redhat.ceylon.compiler.typechecker.tree.TreeUtil.buildAnnotations;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.antlr.runtime.CommonToken;

import com.redhat.ceylon.compiler.java.codegen.Decl;
import com.redhat.ceylon.compiler.typechecker.context.PhasedUnit;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;
import com.redhat.ceylon.model.typechecker.model.Annotation;
import com.redhat.ceylon.model.typechecker.model.Class;
import com.redhat.ceylon.model.typechecker.model.ClassOrInterface;
import com.redhat.ceylon.model.typechecker.model.Constructor;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Function;
import com.redhat.ceylon.model.typechecker.model.FunctionOrValue;
import com.redhat.ceylon.model.typechecker.model.Functional;
import com.redhat.ceylon.model.typechecker.model.Generic;
import com.redhat.ceylon.model.typechecker.model.ModelUtil;
import com.redhat.ceylon.model.typechecker.model.Module;
import com.redhat.ceylon.model.typechecker.model.Parameter;
import com.redhat.ceylon.model.typechecker.model.ParameterList;
import com.redhat.ceylon.model.typechecker.model.Referenceable;
import com.redhat.ceylon.model.typechecker.model.Scope;
import com.redhat.ceylon.model.typechecker.model.Setter;
import com.redhat.ceylon.model.typechecker.model.Type;
import com.redhat.ceylon.model.typechecker.model.TypeAlias;
import com.redhat.ceylon.model.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.model.typechecker.model.TypeParameter;
import com.redhat.ceylon.model.typechecker.model.TypedDeclaration;
import com.redhat.ceylon.model.typechecker.model.Unit;
import com.redhat.ceylon.model.typechecker.model.Value;

public abstract class ClassOrPackageDoc extends CeylonDoc {
    
    private static final Set simpleDefaultValues = new HashSet();
    static {
        simpleDefaultValues.add("null");
        simpleDefaultValues.add("true");
        simpleDefaultValues.add("false");
        simpleDefaultValues.add("0");
        simpleDefaultValues.add("1");
        simpleDefaultValues.add("-1");
        simpleDefaultValues.add("0.0");
        simpleDefaultValues.add("1.0");
        simpleDefaultValues.add("-1.0");
        simpleDefaultValues.add("[]");
        simpleDefaultValues.add("\"\"");
    }
    
    public ClassOrPackageDoc(Module module, CeylonDocTool tool, Writer writer) {
		super(module, tool, writer);
	}
    
    protected final void doc(String name, TypeAlias alias) throws IOException {
        boolean isAlias = Util.nullSafeCompare(name, alias.getName()) != 0;
        open("tr");
        
        open("td id='" + name + "' nowrap");
        writeIcon(alias);
        around("code class='decl-label'", name);
        close("td");
        
        open("td");
        writeLinkOneSelf(name);
        if(isAlias){
            writeTagged(alias);
            writeAlias(alias);
        }else{
            writeLinkSource(alias);
            writeTagged(alias);
            open("code class='signature'");
            around("span class='modifiers'", getModifiers(alias));
            write(" ");
            open("span class='type-identifier'");
            write(alias.getName());
            close("span");
            if (!alias.getTypeParameters().isEmpty()) {
                writeTypeParameters(alias.getTypeParameters(), alias);
                writeTypeParametersConstraints(alias.getTypeParameters(), alias);
                open("div class='type-alias-specifier'");
            }
            around("span class='specifier'", "=> ");
            linkRenderer().to(alias.getExtendedType()).useScope(alias).write();
            if (!alias.getTypeParameters().isEmpty()) {
                close("div"); // type-alias-specifier
            }
            close("code"); // signature
            writeDescription(alias);
        }
        close("td");
        
        close("tr");
    }

    protected final void doc(String name, ClassOrInterface d) throws IOException {
        boolean alias = Util.nullSafeCompare(name, d.getName()) != 0;
        open("tr");
        
        open("td id='" + name + "' nowrap");
        writeIcon(d);
        open("a class='decl-label' href='"+ linkRenderer().to(d).useScope(d).getUrl() +"'");
        around("code", name);
        close("a");
        close("td");
        
        open("td");
        writeLinkOneSelf(name);
        if(alias){
            writeTagged(d);
            writeAlias(d);
        }else{
            writeLinkSourceCode(d);
            writeTagged(d);
            open("code class='signature'");
            around("span class='modifiers'", getModifiers(d));
            write(" ");
            if (d.isDynamic()) {
                around("span class='dynamic'", "dynamic");
                write(" ");
            }
            linkRenderer().to(d.getType()).useScope(d).printAbbreviated(!isAbbreviatedType(d)).printTypeParameterDetail(true).write();
            writeTypeParametersConstraints(d.getTypeParameters(), d);
            close("code");
            writeDescription(d);
        }
        close("td");
        
        close("tr");
    }

    private void writeAlias(Declaration decl) throws IOException {
        write("See ");
        open("code class='signature'");
        String cssClass = decl instanceof TypeDeclaration ? "type-identifier" : "identifier";
        open("span class='"+cssClass+"'");
        linkRenderer().to(decl).useScope(decl).write();
        close("span");
        close("code");
    }
    
    protected final void doc(String name, Declaration d) throws IOException {
        doc(null, name, d);
    }

    protected final void doc(String id, String name, Declaration d) throws IOException {
        String declarationName = Util.getDeclarationName(d);
        id = (id != null ? id : name);
        boolean alias = Util.nullSafeCompare(name, declarationName) != 0;
        
        // put the id on the td because IE8 doesn't support id attributes on tr (yeah right)
        open("tr");
        
        open("td id='" + id + "' nowrap");
        writeIcon(d);
        if( !(d instanceof Constructor) ) {
            around("code class='decl-label'", name);
            close("td");
            open("td");
        }
        
        writeLinkOneSelf(id);
        if(alias){
            writeTagged(d);
            writeAlias(d);
        }else{
            writeLinkSource(d);
            writeTagged(d);

            if(d instanceof Functional) {
                writeParameterLinksIfRequired((Functional) d);
            }
            open("code class='signature'");
            around("span class='modifiers'", getModifiers(d));
            write(" ");

            if( !ModelUtil.isConstructor(d) ) {
                if ( !Decl.isDynamic(d) ) {
                    if( d instanceof Functional && ((Functional) d).isDeclaredVoid() ) {
                        around("span class='void'", "void");
                    } else if (d instanceof TypedDeclaration) {
                        linkRenderer().to(((TypedDeclaration) d).getType()).useScope(d).write();
                    } else {
                        linkRenderer().to(d).useScope(d).write();
                    }
                } else {
                    around("span class='dynamic'", "dynamic");
                }
            }

            write(" ");
            open("span class='identifier'");
            write(name);
            close("span");
            if( isConstantValue(d) ) {
                writeConstantValue((Value) d);
            }
            if( d instanceof Generic ) {
                Generic f = (Generic) d;
                writeTypeParameters(f.getTypeParameters(), d);
            }
            if( d instanceof Functional ) {
                writeParameterList((Functional) d, d);
            }
            if( d instanceof Generic ) {
                Generic f = (Generic) d;
                writeTypeParametersConstraints(f.getTypeParameters(), d);
            }
            if (d instanceof Value) {
                Setter setter = ((Value) d).getSetter();
                if (setter != null && Util.getAnnotation(setter.getUnit(), setter.getAnnotations(), "doc") != null) {
                    tool.warningSetterDoc(d.getQualifiedNameString(), d);
                }
            }
            close("code");
            writeDescription(d);
        }
        close("td");
        close("tr");
    }
    
    private boolean isConstantValue(Declaration d) {
        if(Decl.isValue(d)) {
            Value value = (Value) d;
            if( value.isShared() && !value.isVariable() && !value.isDynamicallyTyped()) {
                Unit unit = value.getUnit();
                Type type = value.getType();
                
                if (type.isSequential()) {
                    type = unit.getSequentialElementType(type);
                }

                if (type.isString() || type.isInteger() || type.isFloat() || type.isCharacter()) {
                    return true;
                }
            }
        }
        return false;
    }

    private void writeConstantValue(Value v) throws IOException {
        Node node = tool.getNode(v);
        PhasedUnit pu = tool.getUnit(v);
        if (pu == null || !(node instanceof Tree.AttributeDeclaration)) {
            return;
        }
        
        Tree.AttributeDeclaration attribute = (Tree.AttributeDeclaration) node;
        Tree.SpecifierOrInitializerExpression specifierExpression = attribute.getSpecifierOrInitializerExpression();
        if (specifierExpression == null) {
            return;
        }
        
        String value = getSourceCode(pu, specifierExpression);
        int newLineIndex = value.indexOf("\n");
        String valueFirstLine = newLineIndex != -1 ? value.substring(0, newLineIndex) : value;

        around("span class='specifier'", valueFirstLine);
        if (newLineIndex != -1) {
            around("a class='specifier-ellipsis' href='#' title='Click for expand the rest of value.'", "...");
            open("div class='specifier-rest'");
            write(value.substring(newLineIndex + 1));
            close("div");
        }
    }

    private void writeDescription(Declaration d) throws IOException {
        open("div class='description'");
        writeDeprecated(d);
        String doc = getDoc(d, linkRenderer());
        if (isEmpty(doc)) {
            tool.warningMissingDoc(d.getQualifiedNameString(), d);
        }
        around("div class='doc section'", doc);
        if( d instanceof FunctionOrValue ) {
            writeAnnotations(d);
        	writeParameters(d);
            writeThrows(d);        
            writeBy(d);
            writeSee(d);
            writeLinkToRefinedDeclaration((FunctionOrValue)d);
        }
        if (d instanceof TypeAlias) {
            writeAnnotations(d);
            writeBy(d);
            writeSee(d);
        }
        writeAliases(d);
        close("div"); // description
    }
    
    private void writeLinkOneSelf(String id) throws IOException {
        String url = linkRenderer().to(getFromObject()).useAnchor(id).getUrl();
        if (url != null) {
            open("a class='link-one-self' title='Link to this declaration' href='" + url + "'");
            write("");
            close("a");
        }
    }

    private void writeLinkSource(Declaration d) throws IOException {
        if (!tool.isIncludeSourceCode()) {
            return;
        }
        String srcUrl;
        if (d.isToplevel()) {
            srcUrl = linkRenderer().getSrcUrl(d);
        } else {
            srcUrl = linkRenderer().getSrcUrl(d.getContainer());
        }
        int[] lines = tool.getDeclarationSrcLocation(d);
        if(lines != null){
            open("a class='link-source-code' title='Link to source code' href='" + srcUrl + "#" + lines[0] + "," + lines[1] + "'");
            write("");
            write("Source Code");
            close("a");
        }
    }

    private void writeLinkToRefinedDeclaration(FunctionOrValue d) throws IOException {
        Declaration topMostRefinedDecl = d.getRefinedDeclaration();
        if (topMostRefinedDecl != null && topMostRefinedDecl != d) {
            Declaration bottomMostRefinedDecl = findBottomMostRefinedDeclaration(d);
            open("div class='refined section'");
            around("span class='title'", "Refines ");
            if (bottomMostRefinedDecl != null && bottomMostRefinedDecl != topMostRefinedDecl) {
                linkRenderer().to(bottomMostRefinedDecl).withinText(true)
                    .useCustomText(getNameWithContainer(bottomMostRefinedDecl)).write();
                around("span class='title'", " ultimately refines ");
                linkRenderer().to(topMostRefinedDecl).withinText(true)
                    .useCustomText(getNameWithContainer(topMostRefinedDecl)).write();
            } else {
                linkRenderer().to(topMostRefinedDecl).withinText(true)
                    .useCustomText(getNameWithContainer(topMostRefinedDecl)).write();
            }
            close("div");
        }
    }

    protected final void writeTypeParameters(List typeParameters, Referenceable scope) throws IOException {
        if (typeParameters != null && !typeParameters.isEmpty()) {
            write("<");
            write("");
            boolean first = true;
            for (TypeParameter typeParam : typeParameters) {
                if (first) {
                    first = false;
                } else {
                    write(", ");
                }
                if (typeParam.isContravariant()) {
                    write("in ");
                }
                if (typeParam.isCovariant()) {
                    write("out ");
                }
                write(typeParam.getName());
                if (typeParam.isDefaulted() && typeParam.getDefaultTypeArgument() != null){
                    write(" = ");
                    write("");
                    write(linkRenderer().to(typeParam.getDefaultTypeArgument()).useScope(scope).getLink());
                    write("");
                }
            }
            write("");
            write(">");
        }
    }
    
    protected final void writeTypeParametersConstraints(List typeParameters, Referenceable scope) throws IOException {
        for (TypeParameter typeParam : typeParameters) {
            if (typeParam.isConstrained()) {
                open("div class='type-parameter-constraint'");

                write("given");
                write(" ");
                around("span class='type-parameter'", typeParam.getName());
                
                writeSatisfiedTypes(typeParam, scope);
                writeCaseTypes(typeParam, scope);

                close("div");
            }
        }
    }

    protected final void writeInheritance(TypeDeclaration typeDeclaration) throws IOException {
        List caseTypes = typeDeclaration.getCaseTypes();
        if (caseTypes!=null && !caseTypes.isEmpty()) {
            open("div class='inheritance-satisfies'");
            writeCaseTypes(typeDeclaration, typeDeclaration);
            close("div");
        }
        if (typeDeclaration instanceof Class &&
                typeDeclaration.getExtendedType()!=null) {
            open("div class='inheritance-extends'");
            write("extends");
            write(" ");
            linkRenderer().to(typeDeclaration.getExtendedType()).useScope(typeDeclaration).write();
            close("div");
        }
        List satisfiedTypes = typeDeclaration.getSatisfiedTypes();
        if (satisfiedTypes!=null && !satisfiedTypes.isEmpty()) {
            open("div class='inheritance-of'");
            writeSatisfiedTypes(typeDeclaration, typeDeclaration);
            close("div");
        }
    }

    private void writeCaseTypes(TypeDeclaration typeDeclaration, Referenceable scope) throws IOException {
        List caseTypes = typeDeclaration.getCaseTypes();
        if (caseTypes != null && !caseTypes.isEmpty()) {
            write(" ");
            write("of");
            write(" ");
            boolean first = true;
            for (Type caseType : caseTypes) {
                if (first) {
                    first = false;
                } else {
                    write(" | ");
                }
                linkRenderer().to(caseType).useScope(scope).write();
            }
        }
    }

    private void writeSatisfiedTypes(TypeDeclaration typeDeclaration, Referenceable scope)
            throws IOException {
        List satisfiedTypes = typeDeclaration.getSatisfiedTypes();
        if (satisfiedTypes != null && !satisfiedTypes.isEmpty()) {
            write(" ");
            write("satisfies");
            write(" ");
            boolean first = true;
            for (Type satisfiedType : satisfiedTypes) {
                if (first) {
                    first = false;
                } else {
                    write(" & ");
                }
                linkRenderer().to(satisfiedType).useScope(scope).write();
            }
        }
    }
    
    protected final void writeParameterLinksIfRequired(Functional f) throws IOException {
        writeParameterLinksIfRequired(f, true, "");
    }

    private final void writeParameterLinksIfRequired(Functional f, boolean onlyIfNoDoc, String idPrefix) throws IOException {
        Map>> parametersAssertions = null;
        if (onlyIfNoDoc) {
            parametersAssertions = getParametersAssertions((Declaration) f);
        }
        
        for (ParameterList parameterList : f.getParameterLists()) {
            for (Parameter parameter : parameterList.getParameters()) {
                boolean isRequired = true;

                if (onlyIfNoDoc) {
                    ParameterDocData parameterDocData = getParameterDocData(parameter, parametersAssertions);
                    if (!parameterDocData.isEmpty()) {
                        isRequired = false;
                    }
                }

                if (isRequired) {
                    around("a id='" + idPrefix + f.getName() + "-" + parameter.getName() + "'", "");
                    
                    // if parameter is function, we need to produce links to its parameters
                    if (parameter.getModel() instanceof Function) {
                        writeParameterLinksIfRequired((Function) parameter.getModel(), false, idPrefix + f.getName() + "-");
                    }
                }
            }
        }
    }

    protected final void writeParameterList(Functional f, Referenceable scope) throws IOException {
        for (ParameterList lists : f.getParameterLists()) {
            write("(");
            boolean first = true;
            for (Parameter param : lists.getParameters()) {
                if (!first) {
                    write(", ");
                } else {
                    first = false;
                }
                
                if (param.getModel() instanceof Function) {
                    writeFunctionalParameter(param, scope);
                } else {
                    if (!Decl.isDynamic(param.getModel())) {
                        linkRenderer().to(param.getType()).useScope(scope).write();
                    } else {
                        around("span class='dynamic'", "dynamic");
                    }
                    write(" ");
                    around("span class='parameter'", param.getName());
                }
                
                if (param.isDefaulted()) {
                    String defaultValue = getParameterDefaultValue(param);
                    if (defaultValue != null) {
                        around("span class='parameter-default-value'", " = ");
                        if (simpleDefaultValues.contains(defaultValue)) {
                            around("span class='parameter-default-value' title='Parameter default value'", defaultValue);
                        } else {
                            around("a class='parameter-default-value' href='#" + f.getName() + "-" + param.getName() + "' title='Go to parameter default value'", "...");
                        }
                    }
                }
                
            }
            write(")");
        }
    }
    
    private String getParameterDefaultValue(Parameter param) throws IOException {
        String defaultValue = null;
        
        if( param.isDefaulted() ) {
            PhasedUnit pu = tool.getParameterUnit(param);
            Node paramNode = tool.getParameterNode(param);
            if (pu != null && paramNode instanceof Tree.Parameter) {
                Tree.SpecifierOrInitializerExpression defArg = getDefaultArgument((Tree.Parameter) paramNode);
                if (defArg != null) {
                    defaultValue = getSourceCode(pu, defArg.getExpression());
                    if (defaultValue != null) {
                        defaultValue = defaultValue.trim();
                    }
                }
            }
        }
        
        return defaultValue;
    }
    
    private Tree.SpecifierOrInitializerExpression getDefaultArgument(Tree.Parameter parameter) {
        if (parameter instanceof Tree.InitializerParameter) {
            return ((Tree.InitializerParameter)parameter).getSpecifierExpression();
        } else if (parameter instanceof Tree.ValueParameterDeclaration) {
            return ((Tree.AttributeDeclaration)((Tree.ValueParameterDeclaration)parameter).getTypedDeclaration()).getSpecifierOrInitializerExpression();
        } else if (parameter instanceof Tree.FunctionalParameterDeclaration) {
            return ((Tree.MethodDeclaration)((Tree.FunctionalParameterDeclaration)parameter).getTypedDeclaration()).getSpecifierExpression();
        }
        return null;
    }

    private void writeFunctionalParameter(Parameter functionParam, Referenceable scope) throws IOException {
        if ( !Decl.isDynamic(functionParam.getModel()) ) {
            if( functionParam.isDeclaredVoid() ) {
                around("span class='void'", "void");
            } else {
                linkRenderer().to(functionParam.getType()).useScope(scope).write();
            }
        } else {
            around("span class='dynamic'", "dynamic");
        }
        write(" ");
        write(functionParam.getName());
        writeParameterList((Function)functionParam.getModel(), scope);
    }

    protected final void writeParameters(Declaration decl) throws IOException {
        if( decl instanceof Functional ) {
            Map>> parametersAssertions = getParametersAssertions(decl);
            boolean first = true;
            List parameterLists = ((Functional)decl).getParameterLists();
            for (ParameterList parameterList : parameterLists) {
                for (Parameter parameter : parameterList.getParameters()) {
                    ParameterDocData parameterDocData = getParameterDocData(parameter, parametersAssertions);
                    if( !parameterDocData.isEmpty()) {
                        if( first ) {
                            first = false;
                            open("div class='parameters section'");
                            around("span class='title'", "Parameters: ");
                            open("ul");
                        }
                        open("li");
                        open("code");
                        
                        around("span class='parameter' id='" + decl.getName() + "-" + parameter.getName() + "'", parameter.getName());
                        
                        // if parameter is function, we need to produce links to its parameters
                        if (parameter.getModel() instanceof Function) {
                            writeParameterLinksIfRequired((Function) parameter.getModel(), false, decl.getName() + "-");
                        }
                        
                        if (!isEmpty(parameterDocData.defaultValue)) {
                            around("span class='parameter-default-value' title='Parameter default value'", " = " + parameterDocData.defaultValue);
                        }
                        
                        close("code");
                        
                        if (!isEmpty(parameterDocData.doc)) {
                            around("div class='doc section'", parameterDocData.doc);
                        }
                        writeParameterAssertions(decl, parameterDocData.parameterAssertions);
                        
                        close("li");
                    }
                }
            }    			
            if (!first) {
                close("ul");
                close("div");
            }
        }
    }

    private void writeParameterAssertions(Declaration decl, Map> parameterAssertions) throws IOException {
        if (parameterAssertions == null || parameterAssertions.isEmpty()) {
            return;
        }
        
        PhasedUnit pu = tool.getUnit(decl);
        
        open("div class='assertions' title='Parameter assertions'");
        open("ul");

        for (Tree.Assertion assertion : parameterAssertions.keySet()) {

            List annotations = new ArrayList();
            buildAnnotations(assertion.getAnnotationList(), annotations);

            String doc = Util.getRawDoc(decl.getUnit(), annotations);
            if (!Util.isEmpty(doc)) {
                open("li");
                write("");
                write(Util.wikiToHTML(doc, linkRenderer()));
                close("li");
            } else {
                for (Tree.Condition c : parameterAssertions.get(assertion)) {
                    String sourceCode = getSourceCode(pu, c);
                    open("li");
                    write("");
                    around("code", sourceCode);
                    close("li");
                }
            }
        }

        close("ul");
        close("div");
    }

	protected final void writeThrows(Declaration decl) throws IOException {
        boolean first = true;
        for (Annotation annotation : decl.getAnnotations()) {
            if (annotation.getName().equals("throws")) {

                String excType = annotation.getPositionalArguments().get(0);
                String excDesc = annotation.getPositionalArguments().size() == 2 ? annotation.getPositionalArguments().get(1) : null;
                
                if (first) {
                    first = false;
                    open("div class='throws section'");
                    around("span class='title'", "Throws ");
                    open("ul");
                }

                open("li");
                
                linkRenderer().to(excType).withinText(true).useScope(decl).write();
                
                if (excDesc != null) {
                    write(Util.wikiToHTML(excDesc, linkRenderer().useScope(decl)));
                }

                close("li");
            }
        }
        if (!first) {
            close("ul");
            close("div");
        }
        tool.warningMissingThrows(decl);
    }
    
    private void writeDeprecated(Declaration decl) throws IOException {
        Annotation deprecated = Util.findAnnotation(decl, "deprecated");
        if (deprecated != null) {
            open("div class='deprecated section'");
            String text = "Deprecated: ";
            if (!deprecated.getPositionalArguments().isEmpty()) {
                String reason = deprecated.getPositionalArguments().get(0);
                if (reason != null) {
                    text += reason;
                }
            }
            write(Util.wikiToHTML(text, linkRenderer().useScope(decl)));
            close("div");
        }
    }
    
    protected final void writeAliases(Declaration decl) throws IOException {
        Annotation see = Util.getAnnotation(decl.getUnit(), decl.getAnnotations(), "aliased");
        if(see == null)
            return;

        open("div class='aliased section'");
        around("span class='title'", "Aliases: ");
        
        open("span class='value'");
        boolean first = true;
        for (String target : see.getPositionalArguments()) {
            if (!first) {
                write(", ");
            } else {
                first = false;
            }
            open("code class='signature'");
            String cssClass = decl instanceof TypeDeclaration ? "type-identifier" : "identifier";
            open("span class='"+cssClass+"'");
            write(target);
            close("span");
            close("code");
        }
        close("span");
        
        close("div");
    }

    private String getSourceCode(PhasedUnit pu, Node node) throws IOException {
        int startIndex = ((CommonToken) node.getToken()).getStartIndex();
        int stopIndex = ((CommonToken) node.getEndToken()).getStopIndex();
    
        StringBuilder sourceCodeBuilder = new StringBuilder();
        BufferedReader sourceCodeReader = new BufferedReader(new InputStreamReader(pu.getUnitFile().getInputStream()));
        try {
            while (true) {
                int c = sourceCodeReader.read();
                if (c == -1 || sourceCodeBuilder.length() > stopIndex) {
                    break;
                }
                sourceCodeBuilder.append((char) c);
            }
        } finally {
            sourceCodeReader.close();
        }

        String sourceCode = sourceCodeBuilder.substring(startIndex, stopIndex + 1);
        sourceCode = sourceCode.replaceAll("&", "&");
        sourceCode = sourceCode.replaceAll("<", "<");
        sourceCode = sourceCode.replaceAll(">", ">");
        return sourceCode;
    }
    
    private Map>> getParametersAssertions(final Declaration decl) {
        final Map>> parametersAssertions = new LinkedHashMap>>();
        
        if (((Functional) decl).getParameterLists().isEmpty()) {
            return parametersAssertions;
        }

        Node node = tool.getNode(decl);
        PhasedUnit pu = tool.getUnit(decl);
        if (node == null || pu == null) {
            return parametersAssertions;
        }

        Tree.Body body = null;
        if (node instanceof Tree.MethodDefinition) {
            body = ((Tree.MethodDefinition) node).getBlock();
        } else if (node instanceof Tree.ClassDefinition) {
            body = ((Tree.ClassDefinition) node).getClassBody();
        }

        if (body == null) {
            return parametersAssertions;
        }

        final Map parametersNames = new HashMap();
        for (ParameterList parameterList : ((Functional) decl).getParameterLists()) {
            for (Parameter parameter : parameterList.getParameters()) {
                parametersNames.put(parameter.getName(), parameter);
            }
        }

        body.visitChildren(new Visitor() {

            private boolean stop = false;
            private Tree.Assertion assertion = null;
            private Set referencedParameters = new HashSet();

            @Override
            public void visit(Tree.Assertion that) {
                assertion = that;
                super.visit(that);
                assertion = null;
            }

            @Override
            public void visit(Tree.Condition that) {
                referencedParameters.clear();
                super.visit(that);
                if (assertion != null && !referencedParameters.isEmpty()) {
                    for (Parameter referencedParameter : referencedParameters) {
                        Map> parameterAssertions = parametersAssertions.get(referencedParameter);
                        if (parameterAssertions == null) {
                            parameterAssertions = new LinkedHashMap>();
                            parametersAssertions.put(referencedParameter, parameterAssertions);
                        }

                        List parameterConditions = parameterAssertions.get(assertion);
                        if (parameterConditions == null) {
                            parameterConditions = new ArrayList();
                            parameterAssertions.put(assertion, parameterConditions);
                        }

                        parameterConditions.add(that);
                    }
                }
            }

            @Override
            public void visit(Tree.BaseMemberExpression that) {
                if (assertion != null) {
                    Declaration d = that.getDeclaration();
                    Scope realScope = com.redhat.ceylon.model.typechecker.model.ModelUtil.getRealScope(d.getScope());
                    if (parametersNames.containsKey(d.getName()) && realScope == decl) {
                        referencedParameters.add(parametersNames.get(d.getName()));
                    }
                }
                super.visit(that);
            }

            @Override
            public void visit(Tree.Statement that) {
                if (assertion == null) {
                    stop = true;
                }
                super.visit(that);
            }

            @Override
            public void visitAny(Node that) {
                if (!stop) {
                    super.visitAny(that);
                }
            }

        });

        return parametersAssertions;
    }
    
    private ParameterDocData getParameterDocData(Parameter parameter, Map>> parametersAssertions) throws IOException {
        String doc = getDoc(parameter.getModel(), linkRenderer());
        String defaultValue = getParameterDefaultValue(parameter);
        Map> parameterAssertions = parametersAssertions.get(parameter);
        return new ParameterDocData(doc, defaultValue, parameterAssertions);
    }
    
    private static class ParameterDocData {
        
        final String doc;
        final String defaultValue;
        final Map> parameterAssertions;
        
        public ParameterDocData(String doc, String defaultValue, Map> parameterAssertions) {
            this.doc = doc;
            this.defaultValue = defaultValue;
            this.parameterAssertions = parameterAssertions;
        }
        
        boolean isEmpty() {
            return doc.isEmpty() && defaultValue == null && parameterAssertions == null;
        }
        
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy