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 }