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