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 */
038package org.crsh.cli.descriptor;
039
040import org.crsh.cli.impl.lang.Util;
041
042import java.io.IOException;
043import java.util.ArrayList;
044import java.util.Collection;
045import java.util.Formatter;
046import java.util.List;
047
048/**
049 * Format the command descriptor for producing documentation.
050 *
051 * @author Julien Viet
052 */
053public 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}