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

com.redhat.ceylon.common.tools.help.CeylonDocToolTool 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.common.tools.help;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;

import com.redhat.ceylon.common.FileUtil;
import com.redhat.ceylon.common.tool.Argument;
import com.redhat.ceylon.common.tool.CeylonBaseTool;
import com.redhat.ceylon.common.tool.Description;
import com.redhat.ceylon.common.tool.Option;
import com.redhat.ceylon.common.tool.OptionArgument;
import com.redhat.ceylon.common.tool.RemainingSections;
import com.redhat.ceylon.common.tool.Summary;
import com.redhat.ceylon.common.tool.ToolLoader;
import com.redhat.ceylon.common.tool.ToolModel;
import com.redhat.ceylon.common.tool.Tools;
import com.redhat.ceylon.common.tool.WordWrap;
import com.redhat.ceylon.common.tools.CeylonTool;
import com.redhat.ceylon.common.tools.help.model.Doc;
import com.redhat.ceylon.common.tools.help.model.Visitor;


@Summary("Generates documentation about a tool")
@Description(
"Generates documentation about the named ``s in the directory " +
"named by the `--output` option.")
@RemainingSections(
"## See Also\n\n" +
"* `ceylon help` For generating help about ceylon tools at the command line\n" +
"* `ceylon doc` For generating API documentation about ceylon modules\n"
)
public class CeylonDocToolTool extends CeylonBaseTool {

    
    public static enum Format {
        html(".html") {
            @Override
            HtmlVisitor newOutput(CeylonDocToolTool tool, Writer writer) {
                return new HtmlVisitor(writer, tool.omitDoctype);
            }

            @Override
            URL[] supportingResources() {
                return new URL[]{getClass().getResource("resources/doc-tool.css"),
                        getClass().getResource("resources/bootstrap.min.css"),
                        getClass().getResource("resources/bootstrap.min.js"),
                        getClass().getResource("resources/jquery-1.8.2.min.js"),
                        getClass().getResource("resources/NOTICE.txt"),
                        getClass().getResource("resources/doc-tool.js"),
                        getClass().getResource("resources/ceylondoc-icons.png"),
                        getClass().getResource("resources/ceylondoc-logo.png")};
            }
        },
        docbook(".xml") {
            @Override
            DocBookVisitor newOutput(CeylonDocToolTool tool, Writer writer) {
                return new DocBookVisitor(writer, tool.omitDoctype);
            }

            @Override
            URL[] supportingResources() {
                return null;
            }
        },
        txt(".txt") {
            @Override
            PlainVisitor newOutput(CeylonDocToolTool tool, Writer writer) {
                return new PlainVisitor(new WordWrap(writer, tool.width));
            }

            @Override
            URL[] supportingResources() {
                return null;
            }
        };
        private final String extension;
        private Format(String extension) {
            this.extension = extension;
        }
        abstract Visitor newOutput(CeylonDocToolTool tool, Writer file);
        abstract URL[] supportingResources();
    }
    
    private List> tools;
    private File dir = new File(".");
    private Format format = Format.html;
    private int width = 80;
    private boolean index = false;
    private boolean allPlumbing;
    private boolean allPorcelain;
    protected ToolLoader toolLoader;
    private DocBuilder docBuilder;
    private boolean omitDoctype;
        
    public final void setToolLoader(ToolLoader toolLoader) {
        this.toolLoader = toolLoader;
        this.docBuilder = new DocBuilder(toolLoader);
    }

    @Argument(argumentName="tool", multiplicity="*")
    public void setTool(List> tools) {
        this.tools = tools;
    }
    
    @Option
    @Description("Generate documentation about all low level tools, in " +
            "addition to the tools named by the `` argument")
    public void setAllPlumbing(boolean allPlumbing) {
        this.allPlumbing = allPlumbing;
    }
    
    @Option
    @Description("Generate documentation about all high level tools, in " +
            "addition to the tools named by the `` argument")
    public void setAllPorcelain(boolean allPorcelain) {
        this.allPorcelain = allPorcelain;
    }
    
    @Option
    @Description("Generate an `index.html` file when `--format=html`")
    public void setIndex(boolean index) {
        this.index = index;
    }
    
    @Option
    @Description("Omit the doctype when outputting XML-based formats")
    public void setOmitDoctype(boolean omitDoctype) {
        this.omitDoctype = omitDoctype;
    }
    
    @OptionArgument(shortName='o', argumentName="dir")
    @Description("Directory to generate the output files in " +
            "(default: The current directory)")
    public void setOutput(File dir) {
        this.dir = dir;
    }
    
    @OptionArgument(argumentName="format")
    @Description("The format to generate the documentation in " +
            "(allowable values: `html`, `docbook` or `txt`, default: `html`)")
    public void setFormat(Format format) {
        this.format = format;
    }
    
    @OptionArgument(argumentName="cols")
    @Description("The line length to use for word wrapping " +
            "when `--format=txt` " +
            "(default: 80)")
    public void setWidth(int width) {
        if (width <= 0) {
            throw new IllegalArgumentException();
        }
        this.width = width;
    }
    
    @Override
    public void initialize(CeylonTool mainTool) {
        if (!allPlumbing && !allPorcelain && (tools == null || tools.isEmpty())) {
            throw new IllegalStateException("No tools to process");
        }
        if (index &&  format != Format.html) {
            throw new IllegalStateException("--index is only supported when --format=html");
        }
    }
    
    @Override
    public void run() throws IOException {
        List models = loadModels();
        prepareDirectory();
        generateDoc(models);
        copyResources();
    }

    private List loadModels() {
        List models = new ArrayList<>();
        if (allPlumbing) {
            for (String toolName : toolLoader.getToolNames()) {
                ToolModel model = toolLoader.loadToolModel(toolName);
                if (model.isPlumbing()) {
                    models.add(docBuilder.buildDoc(model));
                }
            }
        }
        if (allPorcelain) {
            for (String toolName : toolLoader.getToolNames()) {
                ToolModel model = toolLoader.loadToolModel(toolName);
                if (model.isPorcelain()) {
                    models.add(docBuilder.buildDoc(model));
                }
            }
        }
        
        if (tools != null) {
            for (ToolModel toolModel : tools) {
                models.add(docBuilder.buildDoc(toolModel));
            }
        }

        models.add(docBuilder.buildDoc(toolLoader.loadToolModel(""), true));
        
        return models;
    }

    private void generateDoc(List docs)
            throws IOException {
        for (Doc doc : docs) {
            File out = new File(applyCwd(dir), filename(doc));
            try (FileWriter writer = new FileWriter(out)) {
                Visitor visitor = format.newOutput(this, writer);
                doc.accept(visitor);
            }
        }
        if (index && format == Format.html) {
            generateIndexHtml(docs);
        }
    }

    private String filename(Doc doc) {
        if(doc.getName().isEmpty())
            return Tools.progName() + format.extension;
        else
            return Tools.progName() + "-" + doc.getName() + format.extension;
    }

    private void generateIndexHtml(List docs) throws IOException {
        File indexFile = new File(applyCwd(dir), "index" + format.extension);
        ResourceBundle bundle = CeylonHelpToolMessages.RESOURCE_BUNDLE;
        try (FileWriter writer = new FileWriter(indexFile)) {
            HtmlVisitor htmlOutput = (HtmlVisitor)Format.html.newOutput(this, writer);
            AbstractMl html = htmlOutput.getHtml();
            indexHeader(html, bundle.getString("index.title"), bundle.getString("index.overview"));
            
            List porcelain = new ArrayList<>();
            List plumbing = new ArrayList<>();
            for (Doc model : docs) {
                if (model.getToolModel().isPorcelain()
                        || model.getName().isEmpty()) {
                    porcelain.add(model);
                } else if (model.getToolModel().isPlumbing()) {
                    plumbing.add(model);
                }
            }
            if (!porcelain.isEmpty()) {
                generateToolList(porcelain, html, bundle.getString("index.porcelain.tools"));
            }
            if (!plumbing.isEmpty()) {
                generateToolList(plumbing, html, bundle.getString("index.plumbing.tools"));
            }
            indexFooter(html);
        }
    }

    private void indexHeader(AbstractMl html, String title, String overview) {
        html.doctype("html").text("\n");
        html.open("html", "head").text("\n");
        html.tag("meta charset='UTF-8'").text("\n");
        html.open("title").text(title).close("title").text("\n");
        html.tag("link rel='stylesheet' type='text/css' href='bootstrap.min.css'").text("\n");
        html.tag("link rel='stylesheet' type='text/css' href='doc-tool.css'").text("\n");
        html.close("head").text("\n");
        html.open("body").text("\n");
        html.open("div class='navbar navbar-inverse navbar-static-top'").text("\n");
        html.open("div class='navbar-inner'").text("\n");
        html.open("a class='tool-header' href='index.html'").text("\n");
        html.open("i class='tool-logo'").close("i").text("\n");
        html.open("span class='tool-label'").text(title).close("span").text("\n");
        html.open("span class='tool-name'").text(overview).close("span").text("\n");
        html.close("a").text("\n");
        html.close("div").text("\n");
        html.close("div").text("\n");
        html.tag("div class='tool-description'").text("\n");
        html.open("div class='container-fluid'").text("\n");
    }
    
    private void indexFooter(AbstractMl html) {
        html.close("div", "body", "html");
    }
    
    private void generateToolList(List docs, AbstractMl html, String title) {
        html.open("table class='table table-condensed table-bordered table-hover'").text("\n");
        html.open("thead").text("\n");
        html.open("tr class='table-header'");
        html.open("td colspan='2'").text(title).close("td");
        html.close("tr").text("\n");
        html.close("thead").text("\n");

        html.open("tbody");
        for (Doc doc : docs) {
            html.open("tr");
            html.open("td class='span3'", "a class='link' href='" + filename(doc) + "'");
            html.open("code").text(Tools.progName() + " " + doc.getName()).close("code");
            html.close("a", "td").text("\n");
            html.open("td", "p");
            html.text(doc.getSummary().getSummary());
            html.close("p", "td");
            html.close("tr").text("\n");
        }
        html.close("tbody").text("\n");
        html.close("table").text("\n");
    }

    private void prepareDirectory() {
        File actualDir = applyCwd(dir);
        if (!actualDir.exists()) {
            FileUtil.mkdirs(actualDir);
        }
        if (!actualDir.isDirectory()) {
            throw new RuntimeException(dir + " is not a directory");
        }
    }

    private void copyResources() throws IOException {
        URL[] resources = format.supportingResources();
        if (resources != null) {
            for (URL resource : resources) {
                copyResource(resource, applyCwd(dir));
            }
        }
    }

    private void copyResource(URL resource, File dir) throws IOException {
        File toFile = new File(dir, new File(resource.getPath()).getName());
        byte[] buf = new byte[1024];
        try  (InputStream in = resource.openStream()) {
            try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(toFile))) {
                int read = in.read(buf);
                while (read != -1) {
                    out.write(buf, 0, read);
                    read = in.read(buf);
                }
            }
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy