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