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        InputStream in = getClass().getClassLoader().getResourceAsStream("META-INF/help.txt");
095        if (in != null) {
096            try {
097                String content = FileSystem.readContent(in);
098                String versionVar = "${version}";
099                int i = content.indexOf(versionVar);
100                if (i > -1) {
101                    content = content.substring(0, i) + Shell.class.getPackage().getImplementationVersion()
102                            + content.substring(i + versionVar.length());
103                }
104                ANSICodes.appendTemplate(buf, content, false);
105                buf.append(ShellConsole.CRLF);
106            } catch (IOException e) {
107                throw new ShellException(e);
108            } finally {
109                try {
110                    in.close();
111                } catch (IOException e) {
112                    e.printStackTrace();
113                }
114            }
115        }
116
117        console.println(buf.toString());
118
119    }
120
121    public void exportRegistry(Shell shell, CommandRegistry reg, File file) throws Exception {
122        StringBuilder sb = new StringBuilder();
123        writeRegistry(reg, sb);
124        PrintWriter w = new PrintWriter(new FileWriter(file));
125        try {
126            w.println(sb.toString());
127        } finally {
128            w.close();
129        }
130    }
131
132    public void exportCommands(Shell shell, File file) throws Exception {
133        HashMap<String, StringBuilder> wikis = new HashMap<String, StringBuilder>();
134        for (String name : shell.getRegistryNames()) {
135            StringBuilder sb = new StringBuilder();
136            writeRegistry(shell.getRegistry(name), sb);
137            wikis.put(name, sb);
138        }
139        // sort registries: first global, then local, then remote, exclude
140        // automation?
141        PrintWriter w = new PrintWriter(new FileWriter(file));
142        try {
143            for (StringBuilder sb : wikis.values()) {
144                w.println(sb.toString());
145            }
146        } finally {
147            w.close();
148        }
149    }
150
151    protected void writeRegistry(CommandRegistry reg, StringBuilder sb) {
152        sb.append("{info:title=Namespce: *" + reg.getName() + "*}" + reg.getDescription());
153        sb.append("{info}\nh1. Index\n{toc:minLevel=2|maxLevel=2}\n\n");
154        HashSet<String> aliases = new HashSet<String>();
155        for (CommandType cmd : reg.getLocalCommandTypes()) {
156            if (!aliases.contains(cmd.getName())) {
157                writeCommand(cmd, sb);
158                aliases.add(cmd.getName());
159            }
160        }
161    }
162
163    protected void writeCommand(CommandType cmd, StringBuilder sb) {
164        ANSIBuffer buf = getCommandHelp(cmd, true);
165        sb.append("h2. ").append(cmd.getName()).append("\n").append(buf.toString(false));
166    }
167
168    protected ANSIBuffer getCommandHelp(CommandType cmd, boolean wiki) {
169        ANSIBuffer buf = shell.newANSIBuffer();
170        header(buf, "NAME", wiki).append(ShellConsole.CRLF).append("\t");
171        buf.append(cmd.getName()).append(" -- ").append(cmd.getHelp());
172        buf.append(ShellConsole.CRLF).append(ShellConsole.CRLF);
173        header(buf, "SYNTAX", wiki).append(ShellConsole.CRLF).append("\t");
174        syntax(buf, cmd.getSyntax(), wiki);
175        buf.append(ShellConsole.CRLF).append(ShellConsole.CRLF);
176
177        String[] aliases = cmd.getAliases();
178        if (aliases != null && aliases.length > 0) {
179            if (aliases != null && aliases.length > 0) {
180                header(buf, "ALIASES", wiki).append(ShellConsole.CRLF).append("\t").append(
181                        StringUtils.join(aliases, ", "));
182                buf.append(ShellConsole.CRLF).append(ShellConsole.CRLF);
183            }
184        }
185
186        List<Token> args = cmd.getArguments();
187        Collection<Token> opts = cmd.getParameters().values();
188
189        if (!opts.isEmpty()) {
190            header(buf, "OPTIONS", wiki).append(ShellConsole.CRLF);
191            for (Token tok : opts) {
192                if (wiki) {
193                    buf.append("*");
194                }
195                String flag = tok.isRequired ? " - " : " - [flag] - ";
196                buf.append("\t" + tok.name + flag + tok.help).append(ShellConsole.CRLF);
197            }
198            buf.append(ShellConsole.CRLF);
199        }
200        if (!args.isEmpty()) {
201            header(buf, "ARGUMENTS", wiki).append(ShellConsole.CRLF);
202            for (Token tok : args) {
203                if (wiki) {
204                    buf.append("*");
205                }
206                String flag = tok.isRequired ? " - [required] - " : " - [optional] -";
207                buf.append("\t" + tok.name + flag + tok.help).append(ShellConsole.CRLF);
208            }
209            buf.append(ShellConsole.CRLF);
210        }
211
212        InputStream in = cmd.getCommandClass().getResourceAsStream(cmd.getCommandClass().getSimpleName() + ".help");
213        if (in != null) {
214            try {
215                String content = FileSystem.readContent(in);
216                ANSICodes.appendTemplate(buf, content, wiki);
217                buf.append(ShellConsole.CRLF);
218            } catch (IOException e) {
219                throw new ShellException(e);
220            } finally {
221                try {
222                    in.close();
223                } catch (IOException e) {
224                    e.printStackTrace();
225                }
226            }
227        }
228        return buf;
229    }
230
231    protected ANSIBuffer header(ANSIBuffer buf, String text, boolean wiki) {
232        if (wiki) {
233            buf.append("*").append(text).append("*");
234        } else {
235            buf.bold(text);
236        }
237        return buf;
238    }
239
240    protected ANSIBuffer boldify(ANSIBuffer buf, String text, boolean wiki) {
241        if (wiki) {
242            buf.append("*").append(text).append("*");
243        } else {
244            buf.bold(text);
245        }
246        return buf;
247    }
248
249    protected ANSIBuffer syntax(ANSIBuffer buf, String syntax, boolean wiki) {
250        if (wiki) {
251            buf.append("{code}").append(syntax).append("{code}");
252        } else {
253            buf.append(syntax);
254        }
255        return buf;
256    }
257
258    protected void formatCommandForWiki() {
259
260    }
261
262}