001 /* 002 * Copyright (C) 2012 eXo Platform SAS. 003 * 004 * This is free software; you can redistribute it and/or modify it 005 * under the terms of the GNU Lesser General Public License as 006 * published by the Free Software Foundation; either version 2.1 of 007 * the License, or (at your option) any later version. 008 * 009 * This software 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 * You should have received a copy of the GNU Lesser General Public 015 * License along with this software; if not, write to the Free 016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 018 */ 019 020 /* 021 * Copyright (C) 2012 eXo Platform SAS. 022 * 023 * This is free software; you can redistribute it and/or modify it 024 * under the terms of the GNU Lesser General Public License as 025 * published by the Free Software Foundation; either version 2.1 of 026 * the License, or (at your option) any later version. 027 * 028 * This software is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 031 * Lesser General Public License for more details. 032 * 033 * You should have received a copy of the GNU Lesser General Public 034 * License along with this software; if not, write to the Free 035 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 036 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 037 */ 038 package org.crsh.cli.descriptor; 039 040 import org.crsh.cli.impl.lang.Util; 041 042 import java.io.IOException; 043 import java.util.ArrayList; 044 import java.util.Collection; 045 import java.util.Formatter; 046 import java.util.List; 047 048 /** 049 * Format the command descriptor for producing documentation. 050 * 051 * @author Julien Viet 052 */ 053 public abstract class Format { 054 055 /** . */ 056 public static final Describe DESCRIBE = new Describe(); 057 058 /** . */ 059 public static final Usage USAGE = new Usage(); 060 061 /** . */ 062 public static final Man MAN = new Man(); 063 064 /** 065 * Print the specified <code>command</code> to the <code>stream</code> 066 * @param command the command to print 067 * @param stream the output 068 * @throws IOException 069 */ 070 public abstract void print(CommandDescriptor<?> command, Appendable stream) throws IOException; 071 072 /** 073 * Print the full qualified name of the command. 074 * 075 * @param command the command 076 * @param stream the output 077 * @throws IOException any io exception 078 */ 079 protected void printFQN(CommandDescriptor<?> command, Appendable stream) throws IOException { 080 CommandDescriptor<?> owner = command.getOwner(); 081 if (owner != null) { 082 printFQN(owner, stream); 083 stream.append(' '); 084 } 085 stream.append(command.getName()); 086 } 087 088 protected void printFQNWithOptions(CommandDescriptor<?> command, Appendable stream) throws IOException { 089 CommandDescriptor<?> owner = command.getOwner(); 090 if (owner != null) { 091 printFQNWithOptions(owner, stream); 092 stream.append(' '); 093 } 094 stream.append(command.getName()); 095 for (OptionDescriptor option : command.getOptions()) { 096 stream.append(' '); 097 option.printUsage(stream); 098 } 099 } 100 101 /** 102 * The command description in one line. 103 */ 104 public static class Describe extends Format { 105 @Override 106 public void print(CommandDescriptor<?> command, Appendable stream) throws IOException { 107 stream.append(command.getUsage()); 108 } 109 } 110 111 /** 112 * The command manual. 113 */ 114 public static class Man extends Format { 115 116 public void print(CommandDescriptor<?> command, Appendable stream) throws IOException { 117 printNameSection(command, stream); 118 printSynopsisSection(command, stream); 119 printDescriptionSection(command, stream); 120 printParametersSection(command, stream); 121 } 122 123 public void printNameSection(CommandDescriptor<?> command, Appendable stream) throws IOException { 124 stream.append("NAME\n"); 125 stream.append(Util.MAN_TAB); 126 printFQN(command, stream); 127 String usage = command.getUsage(); 128 if (usage.length() > 0) { 129 stream.append(" - ").append(usage); 130 } 131 stream.append("\n\n"); 132 } 133 134 public void printSynopsisSection(CommandDescriptor<?> command, Appendable stream) throws IOException { 135 stream.append("SYNOPSIS\n"); 136 stream.append(Util.MAN_TAB); 137 printFQNWithOptions(command, stream); 138 if (command.getSubordinates().size() > 0) { 139 stream.append(" COMMAND [ARGS]"); 140 } else { 141 for (ArgumentDescriptor argument : command.getArguments()) { 142 stream.append(' '); 143 argument.printUsage(stream); 144 } 145 } 146 stream.append("\n\n"); 147 } 148 149 public void printDescriptionSection(CommandDescriptor<?> command, Appendable stream) throws IOException { 150 String man = command.getDescription().getMan(); 151 if (man.length() > 0) { 152 stream.append("DESCRIPTION\n"); 153 Util.indent(Util.MAN_TAB, man, stream); 154 stream.append("\n\n"); 155 } 156 } 157 158 public void printParametersSection(CommandDescriptor<?> command, Appendable stream) throws IOException { 159 boolean printed = printOptions(false, command, stream); 160 if (command.getSubordinates().size() > 0) { 161 stream.append("COMMANDS\n"); 162 printSubordinates(command, stream); 163 } else { 164 printParameters(printed, command, stream); 165 } 166 } 167 168 protected void printSubordinates(CommandDescriptor<?> command, Appendable stream) throws IOException { 169 for (CommandDescriptor<?> subordinate : command.getSubordinates().values()) { 170 stream.append(Util.MAN_TAB).append(subordinate.getName()); 171 String methodText = subordinate.getDescription().getBestEffortMan(); 172 if (methodText.length() > 0) { 173 stream.append("\n"); 174 Util.indent(Util.MAN_TAB_EXTRA, methodText, stream); 175 } 176 stream.append("\n\n"); 177 } 178 } 179 180 protected boolean printOptions(boolean printed, CommandDescriptor<?> command, Appendable stream) throws IOException { 181 CommandDescriptor<?> owner = command.getOwner(); 182 if (owner != null) { 183 printed = printOptions(printed, owner, stream); 184 } 185 for (OptionDescriptor option : command.getOptions()) { 186 printed = printParameter(printed, option, stream); 187 } 188 return printed; 189 } 190 191 protected boolean printParameters(boolean printed, CommandDescriptor<?> command, Appendable stream) throws IOException { 192 for (ArgumentDescriptor argument : command.getArguments()) { 193 printed = printParameter(printed, argument, stream); 194 } 195 return printed; 196 } 197 198 protected boolean printParameter(boolean printed, ParameterDescriptor parameter, Appendable stream) throws IOException { 199 if (!printed) { 200 stream.append("PARAMETERS\n"); 201 } 202 stream.append(Util.MAN_TAB); 203 parameter.printUsage(stream); 204 String parameterText = parameter.getDescription().getBestEffortMan(); 205 if (parameterText.length() > 0) { 206 stream.append("\n"); 207 Util.indent(Util.MAN_TAB_EXTRA, parameterText, stream); 208 } 209 stream.append("\n\n"); 210 return true; 211 } 212 } 213 214 /** 215 * The command usage. 216 */ 217 public static class Usage extends Format { 218 219 public void print(CommandDescriptor<?> command, Appendable stream) throws IOException { 220 printUsageSection(command, stream); 221 printDetailsSection(command, stream); 222 } 223 224 public void printUsageSection(CommandDescriptor<?> command, Appendable stream) throws IOException { 225 stream.append("usage: "); 226 printFQNWithOptions(command, stream); 227 if (command.getSubordinates().size() > 0) { 228 stream.append(" COMMAND [ARGS]"); 229 } else { 230 for (ArgumentDescriptor argument : command.getArguments()) { 231 stream.append(' '); 232 argument.printUsage(stream); 233 } 234 } 235 stream.append("\n\n"); 236 } 237 238 private List<String[]> collectParametersTuples(CommandDescriptor<?> command) throws IOException { 239 CommandDescriptor<?> owner = command.getOwner(); 240 List<String[]> tuples; 241 Collection<? extends ParameterDescriptor> parameters; 242 if (owner != null) { 243 tuples = collectParametersTuples(owner); 244 parameters = command.getOptions(); 245 } else { 246 tuples = new ArrayList<String[]>(); 247 parameters = command.getParameters(); 248 } 249 for (ParameterDescriptor parameter : parameters) { 250 StringBuilder sb = new StringBuilder(); 251 parameter.printUsage(sb); 252 String usage = sb.toString(); 253 tuples.add(new String[]{usage, parameter.getUsage()}); 254 } 255 return tuples; 256 } 257 258 public void printDetailsSection(CommandDescriptor<?> command, Appendable stream) throws IOException { 259 if (command.getSubordinates().isEmpty()) { 260 List<String[]> tt = collectParametersTuples(command); 261 int length = 0; 262 for (String[] s : tt) { 263 length = Math.max(s[0].length(), length); 264 } 265 String format = " %1$-" + length + "s %2$s\n"; 266 for (String[] tuple : tt) { 267 Formatter formatter = new Formatter(stream); 268 formatter.format(format, tuple[0], tuple[1]); 269 } 270 } else { 271 stream.append("The most commonly used ").append(command.getName()).append(" commands are:\n"); 272 String format = " %1$-16s %2$s\n"; 273 for (CommandDescriptor<?> subordinate : command.getSubordinates().values()) { 274 Formatter formatter = new Formatter(stream); 275 formatter.format(format, subordinate.getName(), subordinate.getUsage()); 276 } 277 } 278 stream.append("\n\n"); 279 } 280 } 281 }