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    }