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.InvocationTargetException;
037 import java.lang.reflect.Method;
038 import java.lang.reflect.Type;
039 import java.util.Collections;
040 import java.util.Map;
041
042 class MethodDescriptor<T> extends CommandDescriptorImpl<T> {
043
044 /** . */
045 private final ClassDescriptor<T> owner;
046
047 /** . */
048 private final Method method;
049
050 /** . */
051 private final int size;
052
053 public MethodDescriptor(
054 ClassDescriptor<T> owner,
055 Method method,
056 String name,
057 Description info) throws IntrospectionException {
058 super(name, info);
059
060 //
061 this.owner = owner;
062 this.method = method;
063 this.size = method.getParameterTypes().length;
064 }
065
066 /**
067 * Returns the parameter descriptor for the specified method parameter index.
068 *
069 * @param index the parameter index
070 * @return the parameter descriptor or null if none can be bound
071 * @throws IndexOutOfBoundsException if the index is not valid
072 */
073 ParameterDescriptor getParameter(int index) throws IndexOutOfBoundsException {
074 if (index < 0 || index >= size) {
075 throw new IndexOutOfBoundsException("Bad index value " + index);
076 }
077 for (ParameterDescriptor argument : getParameters()) {
078 if (((MethodArgumentBinding)argument.getBinding()).getIndex() == index) {
079 return argument;
080 }
081 }
082 return null;
083 }
084
085 @Override
086 protected void addParameter(ParameterDescriptor parameter) throws IntrospectionException, NullPointerException, IllegalArgumentException {
087 super.addParameter(parameter);
088 }
089
090 @Override
091 public CommandDescriptor<T> getOwner() {
092 return owner;
093 }
094
095 @Override
096 public Map<String, ? extends CommandDescriptorImpl<T>> getSubordinates() {
097 return Collections.emptyMap();
098 }
099
100 @Override
101 public CommandDescriptorImpl<T> getSubordinate(String name) {
102 return null;
103 }
104
105 public Method getMethod() {
106 return method;
107 }
108
109 @Override
110 public Class<T> getType() {
111 return owner.getType();
112 }
113
114 public CommandInvoker<T> getInvoker(final InvocationMatch<T> _match) {
115 return new CommandInvoker<T>() {
116 @Override
117 public Class<?> getReturnType() {
118 return getMethod().getReturnType();
119 }
120
121 @Override
122 public Type getGenericReturnType() {
123 return getMethod().getGenericReturnType();
124 }
125
126 @Override
127 public Class<?>[] getParameterTypes() {
128 return getMethod().getParameterTypes();
129 }
130
131 @Override
132 public Type[] getGenericParameterTypes() {
133 return getMethod().getGenericParameterTypes();
134 }
135
136 @Override
137 public Object invoke(Resolver resolver, T command) throws InvocationException, SyntaxException {
138
139 //
140 owner.configure(_match.owner(), command);
141
142 // Prepare invocation
143 Method m = getMethod();
144 Class<?>[] parameterTypes = m.getParameterTypes();
145 Object[] mArgs = new Object[parameterTypes.length];
146 for (int i = 0;i < mArgs.length;i++) {
147 ParameterDescriptor parameter = getParameter(i);
148
149 //
150 Class<?> parameterType = parameterTypes[i];
151
152 Object v;
153 if (parameter == null) {
154 // Attempt to obtain from resolver
155 v = resolver.resolve(parameterType);
156 } else {
157 ParameterMatch match = _match.getParameter(parameter);
158 if (match != null) {
159 v = match.computeValue();
160 } else {
161 v = null;
162 }
163 }
164
165 //
166 if (v == null) {
167 if (parameterType.isPrimitive() || parameter.isRequired()) {
168 if (parameter instanceof ArgumentDescriptor) {
169 ArgumentDescriptor argument = (ArgumentDescriptor)parameter;
170 throw new SyntaxException("Missing argument " + argument.getName());
171 } else {
172 OptionDescriptor option = (OptionDescriptor)parameter;
173 throw new SyntaxException("Missing option " + option.getNames());
174 }
175 }
176 }
177
178 //
179 mArgs[i] = v;
180 }
181
182 // Perform method invocation
183 try {
184 return m.invoke(command, mArgs);
185 }
186 catch (InvocationTargetException e) {
187 Throwable t = e.getTargetException();
188 if (t instanceof Error) {
189 throw (Error)t;
190 } else {
191 throw new InvocationException(t);
192 }
193 }
194 catch (IllegalAccessException t) {
195 throw new InvocationException(t);
196 }
197 }
198 };
199 }
200 }