/*
* Copyright (C) 2012 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
/*
* Copyright (C) 2012 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.crsh.cli.descriptor;
import org.crsh.cli.impl.lang.Util;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Formatter;
import java.util.List;
/**
* Format the command descriptor for producing documentation.
*
* @author Julien Viet
*/
public abstract class Format {
/** . */
public static final Describe DESCRIBE = new Describe();
/** . */
public static final Usage USAGE = new Usage();
/** . */
public static final Man MAN = new Man();
/**
* Print the specified <code>command</code> to the <code>stream</code>
* @param command the command to print
* @param stream the output
* @throws IOException
*/
public abstract void print(CommandDescriptor<?> command, Appendable stream) throws IOException;
/**
* Print the full qualified name of the command.
*
* @param command the command
* @param stream the output
* @throws IOException any io exception
*/
protected void printFQN(CommandDescriptor<?> command, Appendable stream) throws IOException {
CommandDescriptor<?> owner = command.getOwner();
if (owner != null) {
printFQN(owner, stream);
stream.append(' ');
}
stream.append(command.getName());
}
protected void printFQNWithOptions(CommandDescriptor<?> command, Appendable stream) throws IOException {
CommandDescriptor<?> owner = command.getOwner();
if (owner != null) {
printFQNWithOptions(owner, stream);
stream.append(' ');
}
stream.append(command.getName());
for (OptionDescriptor option : command.getOptions()) {
stream.append(' ');
option.printUsage(stream);
}
}
/**
* The command description in one line.
*/
public static class Describe extends Format {
@Override
public void print(CommandDescriptor<?> command, Appendable stream) throws IOException {
stream.append(command.getUsage());
}
}
/**
* The command manual.
*/
public static class Man extends Format {
public void print(CommandDescriptor<?> command, Appendable stream) throws IOException {
printNameSection(command, stream);
printSynopsisSection(command, stream);
printDescriptionSection(command, stream);
printParametersSection(command, stream);
}
public void printNameSection(CommandDescriptor<?> command, Appendable stream) throws IOException {
stream.append("NAME\n");
stream.append(Util.MAN_TAB);
printFQN(command, stream);
String usage = command.getUsage();
if (usage.length() > 0) {
stream.append(" - ").append(usage);
}
stream.append("\n\n");
}
public void printSynopsisSection(CommandDescriptor<?> command, Appendable stream) throws IOException {
stream.append("SYNOPSIS\n");
stream.append(Util.MAN_TAB);
printFQNWithOptions(command, stream);
if (command.getSubordinates().size() > 0) {
stream.append(" COMMAND [ARGS]");
} else {
for (ArgumentDescriptor argument : command.getArguments()) {
stream.append(' ');
argument.printUsage(stream);
}
}
stream.append("\n\n");
}
public void printDescriptionSection(CommandDescriptor<?> command, Appendable stream) throws IOException {
String man = command.getDescription().getMan();
if (man.length() > 0) {
stream.append("DESCRIPTION\n");
Util.indent(Util.MAN_TAB, man, stream);
stream.append("\n\n");
}
}
public void printParametersSection(CommandDescriptor<?> command, Appendable stream) throws IOException {
boolean printed = printOptions(false, command, stream);
if (command.getSubordinates().size() > 0) {
stream.append("COMMANDS\n");
printSubordinates(command, stream);
} else {
printParameters(printed, command, stream);
}
}
protected void printSubordinates(CommandDescriptor<?> command, Appendable stream) throws IOException {
for (CommandDescriptor<?> subordinate : command.getSubordinates().values()) {
stream.append(Util.MAN_TAB).append(subordinate.getName());
String methodText = subordinate.getDescription().getBestEffortMan();
if (methodText.length() > 0) {
stream.append("\n");
Util.indent(Util.MAN_TAB_EXTRA, methodText, stream);
}
stream.append("\n\n");
}
}
protected boolean printOptions(boolean printed, CommandDescriptor<?> command, Appendable stream) throws IOException {
CommandDescriptor<?> owner = command.getOwner();
if (owner != null) {
printed = printOptions(printed, owner, stream);
}
for (OptionDescriptor option : command.getOptions()) {
printed = printParameter(printed, option, stream);
}
return printed;
}
protected boolean printParameters(boolean printed, CommandDescriptor<?> command, Appendable stream) throws IOException {
for (ArgumentDescriptor argument : command.getArguments()) {
printed = printParameter(printed, argument, stream);
}
return printed;
}
protected boolean printParameter(boolean printed, ParameterDescriptor parameter, Appendable stream) throws IOException {
if (!printed) {
stream.append("PARAMETERS\n");
}
stream.append(Util.MAN_TAB);
parameter.printUsage(stream);
String parameterText = parameter.getDescription().getBestEffortMan();
if (parameterText.length() > 0) {
stream.append("\n");
Util.indent(Util.MAN_TAB_EXTRA, parameterText, stream);
}
stream.append("\n\n");
return true;
}
}
/**
* The command usage.
*/
public static class Usage extends Format {
public void print(CommandDescriptor<?> command, Appendable stream) throws IOException {
printUsageSection(command, stream);
printDetailsSection(command, stream);
}
public void printUsageSection(CommandDescriptor<?> command, Appendable stream) throws IOException {
stream.append("usage: ");
printFQNWithOptions(command, stream);
if (command.getSubordinates().size() > 0) {
stream.append(" COMMAND [ARGS]");
} else {
for (ArgumentDescriptor argument : command.getArguments()) {
stream.append(' ');
argument.printUsage(stream);
}
}
stream.append("\n\n");
}
private List<String[]> collectParametersTuples(CommandDescriptor<?> command) throws IOException {
CommandDescriptor<?> owner = command.getOwner();
List<String[]> tuples;
Collection<? extends ParameterDescriptor> parameters;
if (owner != null) {
tuples = collectParametersTuples(owner);
parameters = command.getOptions();
} else {
tuples = new ArrayList<String[]>();
parameters = command.getParameters();
}
for (ParameterDescriptor parameter : parameters) {
StringBuilder sb = new StringBuilder();
parameter.printUsage(sb);
String usage = sb.toString();
tuples.add(new String[]{usage, parameter.getUsage()});
}
return tuples;
}
public void printDetailsSection(CommandDescriptor<?> command, Appendable stream) throws IOException {
if (command.getSubordinates().isEmpty()) {
List<String[]> tt = collectParametersTuples(command);
int length = 0;
for (String[] s : tt) {
length = Math.max(s[0].length(), length);
}
String format = " %1$-" + length + "s %2$s\n";
for (String[] tuple : tt) {
Formatter formatter = new Formatter(stream);
formatter.format(format, tuple[0], tuple[1]);
}
} else {
stream.append("The most commonly used ").append(command.getName()).append(" commands are:\n");
String format = " %1$-16s %2$s\n";
for (CommandDescriptor<?> subordinate : command.getSubordinates().values()) {
Formatter formatter = new Formatter(stream);
formatter.format(format, subordinate.getName(), subordinate.getUsage());
}
}
stream.append("\n\n");
}
}
}