001/*
002 * (C) Copyright 2006-2010 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     bstefanescu
018 */
019package org.nuxeo.shell.cmds;
020
021import java.io.File;
022import java.io.FileWriter;
023import java.io.IOException;
024import java.io.InputStream;
025import java.io.PrintWriter;
026import java.util.Collection;
027import java.util.HashMap;
028import java.util.HashSet;
029import java.util.List;
030
031import jline.ANSIBuffer;
032
033import org.nuxeo.shell.Argument;
034import org.nuxeo.shell.Command;
035import org.nuxeo.shell.CommandRegistry;
036import org.nuxeo.shell.CommandType;
037import org.nuxeo.shell.Context;
038import org.nuxeo.shell.Parameter;
039import org.nuxeo.shell.Shell;
040import org.nuxeo.shell.ShellConsole;
041import org.nuxeo.shell.ShellException;
042import org.nuxeo.shell.CommandType.Token;
043import org.nuxeo.shell.cmds.completors.CommandRegistryCompletor;
044import org.nuxeo.shell.fs.FileCompletor;
045import org.nuxeo.shell.fs.FileSystem;
046import org.nuxeo.shell.utils.ANSICodes;
047import org.nuxeo.shell.utils.StringUtils;
048
049/**
050 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
051 */
052@Command(name = "help", help = "The help command")
053public class Help implements Runnable {
054
055    @Context
056    protected Shell shell;
057
058    @Argument(name = "command", required = false, index = 0, help = "the name of the command to get help for")
059    protected CommandType cmd;
060
061    @Parameter(name = "-export", hasValue = true, completor = FileCompletor.class, help = "If used export all the commands available in a wiki formatto the given file. If adirectory is given the export will be made in file help.wiki in that directory.")
062    protected File export;
063
064    @Parameter(name = "-ns", hasValue = true, completor = CommandRegistryCompletor.class, help = "[optional] - to be used to filter commands by namespaces when generating the documentation. By default all namespaces are dumped.")
065    protected CommandRegistry ns;
066
067    public void run() {
068        ShellConsole console = shell.getConsole();
069        if (export != null) {
070            if (export.isDirectory()) {
071                export = new File(export, "help.wiki");
072            }
073            try {
074                if (ns == null) {
075                    exportCommands(shell, export);
076                } else {
077                    exportRegistry(shell, ns, export);
078                }
079                console.println("Commands wiki exported in " + export);
080            } catch (Throwable t) {
081                throw new ShellException("Failed to export commands wiki", t);
082            }
083            return;
084        }
085        if (cmd == null) {
086            showMainPage(console);
087        } else {
088            console.println(getCommandHelp(cmd, false).toString());
089        }
090    }
091
092    public void showMainPage(ShellConsole console) {
093        ANSIBuffer buf = shell.newANSIBuffer();
094        try (InputStream in = getClass().getClassLoader().getResourceAsStream("META-INF/help.txt")) {
095            if (in != null) {
096                String content = FileSystem.readContent(in);
097                String versionVar = "${version}";
098                int i = content.indexOf(versionVar);
099                if (i > -1) {
100                    content = content.substring(0, i) + Shell.class.getPackage().getImplementationVersion()
101                            + content.substring(i + versionVar.length());
102                }
103                ANSICodes.appendTemplate(buf, content, false);
104                buf.append(ShellConsole.CRLF);
105            }
106        } catch (IOException e) {
107            throw new ShellException(e);
108        }
109
110        console.println(buf.toString());
111
112    }
113
114    public void exportRegistry(Shell shell, CommandRegistry reg, File file) throws Exception {
115        StringBuilder sb = new StringBuilder();
116        writeRegistry(reg, sb);
117        PrintWriter w = new PrintWriter(new FileWriter(file));
118        try {
119            w.println(sb.toString());
120        } finally {
121            w.close();
122        }
123    }
124
125    public void exportCommands(Shell shell, File file) throws Exception {
126        HashMap<String, StringBuilder> wikis = new HashMap<String, StringBuilder>();
127        for (String name : shell.getRegistryNames()) {
128            StringBuilder sb = new StringBuilder();
129            writeRegistry(shell.getRegistry(name), sb);
130            wikis.put(name, sb);
131        }
132        // sort registries: first global, then local, then remote, exclude
133        // automation?
134        PrintWriter w = new PrintWriter(new FileWriter(file));
135        try {
136            for (StringBuilder sb : wikis.values()) {
137                w.println(sb.toString());
138            }
139        } finally {
140            w.close();
141        }
142    }
143
144    protected void writeRegistry(CommandRegistry reg, StringBuilder sb) {
145        sb.append("{info:title=Namespce: *" + reg.getName() + "*}" + reg.getDescription());
146        sb.append("{info}\nh1. Index\n{toc:minLevel=2|maxLevel=2}\n\n");
147        HashSet<String> aliases = new HashSet<String>();
148        for (CommandType cmd : reg.getLocalCommandTypes()) {
149            if (!aliases.contains(cmd.getName())) {
150                writeCommand(cmd, sb);
151                aliases.add(cmd.getName());
152            }
153        }
154    }
155
156    protected void writeCommand(CommandType cmd, StringBuilder sb) {
157        ANSIBuffer buf = getCommandHelp(cmd, true);
158        sb.append("h2. ").append(cmd.getName()).append("\n").append(buf.toString(false));
159    }
160
161    protected ANSIBuffer getCommandHelp(CommandType cmd, boolean wiki) {
162        ANSIBuffer buf = shell.newANSIBuffer();
163        header(buf, "NAME", wiki).append(ShellConsole.CRLF).append("\t");
164        buf.append(cmd.getName()).append(" -- ").append(cmd.getHelp());
165        buf.append(ShellConsole.CRLF).append(ShellConsole.CRLF);
166        header(buf, "SYNTAX", wiki).append(ShellConsole.CRLF).append("\t");
167        syntax(buf, cmd.getSyntax(), wiki);
168        buf.append(ShellConsole.CRLF).append(ShellConsole.CRLF);
169
170        String[] aliases = cmd.getAliases();
171        if (aliases != null && aliases.length > 0) {
172            header(buf, "ALIASES", wiki).append(ShellConsole.CRLF).append("\t").append(StringUtils.join(aliases, ", "));
173            buf.append(ShellConsole.CRLF).append(ShellConsole.CRLF);
174        }
175
176        List<Token> args = cmd.getArguments();
177        Collection<Token> opts = cmd.getParameters().values();
178
179        if (!opts.isEmpty()) {
180            header(buf, "OPTIONS", wiki).append(ShellConsole.CRLF);
181            for (Token tok : opts) {
182                if (wiki) {
183                    buf.append("*");
184                }
185                String flag = tok.isRequired ? " - " : " - [flag] - ";
186                buf.append("\t" + tok.name + flag + tok.help).append(ShellConsole.CRLF);
187            }
188            buf.append(ShellConsole.CRLF);
189        }
190        if (!args.isEmpty()) {
191            header(buf, "ARGUMENTS", wiki).append(ShellConsole.CRLF);
192            for (Token tok : args) {
193                if (wiki) {
194                    buf.append("*");
195                }
196                String flag = tok.isRequired ? " - [required] - " : " - [optional] -";
197                buf.append("\t" + tok.name + flag + tok.help).append(ShellConsole.CRLF);
198            }
199            buf.append(ShellConsole.CRLF);
200        }
201
202        try (InputStream in = cmd.getCommandClass().getResourceAsStream(cmd.getCommandClass().getSimpleName() + ".help")) {
203            if (in != null) {
204                String content = FileSystem.readContent(in);
205                ANSICodes.appendTemplate(buf, content, wiki);
206                buf.append(ShellConsole.CRLF);
207            }
208        } catch (IOException e) {
209            throw new ShellException(e);
210        }
211        return buf;
212    }
213
214    protected ANSIBuffer header(ANSIBuffer buf, String text, boolean wiki) {
215        if (wiki) {
216            buf.append("*").append(text).append("*");
217        } else {
218            buf.bold(text);
219        }
220        return buf;
221    }
222
223    protected ANSIBuffer boldify(ANSIBuffer buf, String text, boolean wiki) {
224        if (wiki) {
225            buf.append("*").append(text).append("*");
226        } else {
227            buf.bold(text);
228        }
229        return buf;
230    }
231
232    protected ANSIBuffer syntax(ANSIBuffer buf, String syntax, boolean wiki) {
233        if (wiki) {
234            buf.append("{code}").append(syntax).append("{code}");
235        } else {
236            buf.append(syntax);
237        }
238        return buf;
239    }
240
241    protected void formatCommandForWiki() {
242
243    }
244
245}