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 }