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