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.cli.impl.lang;
021    
022    import org.crsh.cli.descriptor.ArgumentDescriptor;
023    import org.crsh.cli.descriptor.CommandDescriptor;
024    import org.crsh.cli.descriptor.Description;
025    import org.crsh.cli.impl.descriptor.CommandDescriptorImpl;
026    import org.crsh.cli.impl.descriptor.IntrospectionException;
027    import org.crsh.cli.descriptor.OptionDescriptor;
028    import org.crsh.cli.descriptor.ParameterDescriptor;
029    import org.crsh.cli.SyntaxException;
030    import org.crsh.cli.impl.invocation.CommandInvoker;
031    import org.crsh.cli.impl.invocation.InvocationException;
032    import org.crsh.cli.impl.invocation.InvocationMatch;
033    import org.crsh.cli.impl.invocation.ParameterMatch;
034    import org.crsh.cli.impl.invocation.Resolver;
035    
036    import java.lang.reflect.Field;
037    import java.lang.reflect.Type;
038    import java.util.HashSet;
039    import java.util.Map;
040    import java.util.Set;
041    
042    class ClassDescriptor<T> extends CommandDescriptorImpl<T> {
043    
044      /** . */
045      private final Class<T> type;
046    
047      /** . */
048      private final Map<String, MethodDescriptor<T>> methods;
049    
050      ClassDescriptor(Class<T> type, Map<String, MethodDescriptor<T>> methods, Description info) throws IntrospectionException {
051        super(type.getSimpleName().toLowerCase(), info);
052    
053        //
054        this.methods = methods;
055        this.type = type;
056      }
057    
058      @Override
059      protected void addParameter(ParameterDescriptor parameter) throws IntrospectionException {
060    
061        // Check we can add the option
062        if (parameter instanceof OptionDescriptor) {
063          OptionDescriptor option = (OptionDescriptor)parameter;
064          Set<String> blah = new HashSet<String>();
065          for (String optionName : option.getNames()) {
066            blah.add((optionName.length() == 1 ? "-" : "--") + optionName);
067          }
068          for (MethodDescriptor<T> method : methods.values()) {
069            Set<String> diff = new HashSet<String>(method.getOptionNames());
070            diff.retainAll(blah);
071            if (diff.size() > 0) {
072              throw new IntrospectionException("Cannot add method " + method.getName() + " because it has common "
073              + " options with its class: " + diff);
074            }
075          }
076        }
077    
078        //
079        super.addParameter(parameter);
080      }
081    
082      @Override
083      public CommandInvoker<T> getInvoker(final InvocationMatch<T> match) {
084    
085        if (Runnable.class.isAssignableFrom(type)) {
086          return new CommandInvoker<T>() {
087            @Override
088            public Class<?> getReturnType() {
089              return Void.class;
090            }
091            @Override
092            public Type getGenericReturnType() {
093              return Void.class;
094            }
095            @Override
096            public Class<?>[] getParameterTypes() {
097              return new Class<?>[0];
098            }
099            @Override
100            public Type[] getGenericParameterTypes() {
101              return new Type[0];
102            }
103            @Override
104            public Object invoke(Resolver resolver, T command) throws InvocationException, SyntaxException {
105              configure(match, command);
106              Runnable runnable = Runnable.class.cast(command);
107              try {
108                runnable.run();
109              }
110              catch (Exception e) {
111                throw new InvocationException(e);
112              }
113              return null;
114            }
115          };
116        } else {
117          return null;
118        }
119      }
120    
121      void configure(InvocationMatch<T> classMatch, T command) throws InvocationException, SyntaxException {
122        for (ParameterDescriptor parameter : getParameters()) {
123          ParameterMatch match = classMatch.getParameter(parameter);
124          if (match == null) {
125            if (parameter.isRequired()) {
126              if (parameter instanceof ArgumentDescriptor) {
127                ArgumentDescriptor argument = (ArgumentDescriptor)parameter;
128                throw new SyntaxException("Missing argument " + argument.getName());
129              } else {
130                OptionDescriptor option = (OptionDescriptor)parameter;
131                throw new SyntaxException("Missing option " + option.getNames());
132              }
133            }
134          } else {
135            Object value = match.computeValue();
136            Field f = ((ClassFieldBinding)parameter.getBinding()).getField();
137            try {
138              f.setAccessible(true);
139              f.set(command, value);
140            }
141            catch (Exception e) {
142              throw new InvocationException(e.getMessage(), e);
143            }
144          }
145        }
146      }
147    
148      @Override
149      public CommandDescriptor<T> getOwner() {
150        return null;
151      }
152    
153      @Override
154      public Class<T> getType() {
155        return type;
156      }
157    
158      @Override
159      public Map<String, ? extends MethodDescriptor<T>> getSubordinates() {
160        return methods;
161      }
162    
163      public MethodDescriptor<T> getSubordinate(String name) {
164        return methods.get(name);
165      }
166    }