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    package org.crsh.cmdline;
021    
022    import org.crsh.cmdline.binding.MethodArgumentBinding;
023    
024    import static org.crsh.cmdline.Util.indent;
025    import static org.crsh.cmdline.Util.tuples;
026    
027    import java.io.IOException;
028    import java.lang.reflect.Method;
029    import java.util.ArrayList;
030    import java.util.Collections;
031    import java.util.Formatter;
032    import java.util.List;
033    import java.util.Map;
034    import java.util.Set;
035    
036    public class MethodDescriptor<T> extends CommandDescriptor<T, MethodArgumentBinding> {
037    
038      /** . */
039      private static final Set<String> MAIN_SINGLETON = Collections.singleton("main");
040    
041      /** . */
042      private final ClassDescriptor<T> owner;
043    
044      /** . */
045      private final Method method;
046    
047      /** . */
048      private final int size;
049    
050      MethodDescriptor(
051        ClassDescriptor<T> owner,
052        Method method,
053        String name,
054        Description info) throws IntrospectionException {
055        super(name, info);
056    
057        //
058        this.owner = owner;
059        this.method = method;
060        this.size = method.getParameterTypes().length;
061      }
062    
063      /**
064       * Returns the parameter descriptor for the specified method parameter index.
065       *
066       * @param index the parameter index
067       * @return the parameter descriptor or null if none can be bound
068       * @throws IndexOutOfBoundsException if the index is not valid
069       */
070      public ParameterDescriptor<MethodArgumentBinding> getParameter(int index) throws IndexOutOfBoundsException {
071        if (index < 0 || index >= size) {
072          throw new IndexOutOfBoundsException("Bad index value " + index);
073        }
074        for (ParameterDescriptor<MethodArgumentBinding> argument : getParameters()) {
075          if (argument.getBinding().getIndex() == index) {
076            return argument;
077          }
078        }
079        return null;
080      }
081    
082      @Override
083      public Map<String, ? extends CommandDescriptor<T, ?>> getSubordinates() {
084        return Collections.emptyMap();
085      }
086    
087      public Method getMethod() {
088        return method;
089      }
090    
091      @Override
092      public Class<T> getType() {
093        return owner.getType();
094      }
095    
096      @Override
097      public OptionDescriptor<?> findOption(String name) {
098        OptionDescriptor<?> option = getOption(name);
099        if (option == null) {
100          option = owner.findOption(name);
101        }
102        return option;
103      }
104    
105      @Override
106      public void printUsage(Appendable writer) throws IOException {
107        int length = 0;
108        List<String> parameterUsages = new ArrayList<String>();
109        List<String> parameterBilto = new ArrayList<String>();
110        boolean printName = !owner.getSubordinates().keySet().equals(MAIN_SINGLETON);
111    
112        //
113        writer.append("usage: ").append(owner.getName());
114    
115        //
116        for (OptionDescriptor<?> option : owner.getOptions()) {
117          writer.append(" ");
118          StringBuilder sb = new StringBuilder();
119          option.printUsage(sb);
120          String usage = sb.toString();
121          writer.append(usage);
122    
123          length = Math.max(length, usage.length());
124          parameterUsages.add(usage);
125          parameterBilto.add(option.getUsage());
126        }
127    
128        //
129        writer.append(printName ? (" " + getName()) : "");
130    
131        //
132        for (ParameterDescriptor<?> parameter : getParameters()) {
133          writer.append(" ");
134          StringBuilder sb = new StringBuilder();
135          parameter.printUsage(sb);
136          String usage = sb.toString();
137          writer.append(usage);
138    
139          length = Math.max(length, usage.length());
140          parameterBilto.add(parameter.getUsage());
141          parameterUsages.add(usage);
142        }
143        writer.append("\n\n");
144    
145        //
146        String format = "   %1$-" + length + "s %2$s\n";
147        for (String[] tuple : tuples(String.class, parameterUsages, parameterBilto)) {
148          Formatter formatter = new Formatter(writer);
149          formatter.format(format, tuple[0], tuple[1]);
150        }
151    
152        //
153        writer.append("\n\n");
154      }
155    
156      public void printMan(Appendable writer) throws IOException {
157    
158        //
159        boolean printName = !owner.getSubordinates().keySet().equals(MAIN_SINGLETON);
160    
161        // Name
162        writer.append("NAME\n");
163        writer.append(Util.MAN_TAB).append(owner.getName());
164        if (printName) {
165          writer.append(" ").append(getName());
166        }
167        if (getUsage().length() > 0) {
168          writer.append(" - ").append(getUsage());
169        }
170        writer.append("\n\n");
171    
172        // Synopsis
173        writer.append("SYNOPSIS\n");
174        writer.append(Util.MAN_TAB).append(owner.getName());
175        for (OptionDescriptor<?> option : owner.getOptions()) {
176          writer.append(" ");
177          option.printUsage(writer);
178        }
179        if (printName) {
180          writer.append(" ").append(getName());
181        }
182        for (OptionDescriptor<?> option : getOptions()) {
183          writer.append(" ");
184          option.printUsage(writer);
185        }
186        for (ArgumentDescriptor<?> argument : getArguments()) {
187          writer.append(" ");
188          argument.printUsage(writer);
189        }
190        writer.append("\n\n");
191    
192        // Description
193        String man = getDescription().getMan();
194        if (man.length() > 0) {
195          writer.append("DESCRIPTION\n");
196          indent(Util.MAN_TAB, man, writer);
197          writer.append("\n\n");
198        }
199    
200        // Parameters
201        List<OptionDescriptor<?>> options = new ArrayList<OptionDescriptor<?>>();
202        options.addAll(owner.getOptions());
203        options.addAll(getOptions());
204        if (options.size() > 0) {
205          writer.append("\nPARAMETERS\n");
206          for (ParameterDescriptor<?> parameter : Util.join(owner.getOptions(), getParameters())) {
207            writer.append(Util.MAN_TAB);
208            parameter.printUsage(writer);
209            String parameterText = parameter.getDescription().getBestEffortMan();
210            if (parameterText.length() > 0) {
211              writer.append("\n");
212              indent(Util.MAN_TAB_EXTRA, parameterText, writer);
213            }
214            writer.append("\n\n");
215          }
216        }
217      }
218    }