001/* 002 * (C) Copyright 2006-2010 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library 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 * Contributors: 015 * bstefanescu 016 */ 017package org.nuxeo.shell.impl; 018 019import java.lang.reflect.Field; 020import java.lang.reflect.Method; 021import java.util.ArrayList; 022import java.util.Collections; 023import java.util.HashMap; 024import java.util.List; 025import java.util.Map; 026 027import org.nuxeo.shell.Argument; 028import org.nuxeo.shell.Command; 029import org.nuxeo.shell.Context; 030import org.nuxeo.shell.Parameter; 031import org.nuxeo.shell.ShellException; 032 033/** 034 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 035 */ 036public class DefaultCommandType extends AbstractCommandType { 037 038 protected List<Setter> injectable; 039 040 protected Map<String, Token> params; 041 042 protected List<Token> args; 043 044 @SuppressWarnings("unchecked") 045 public static DefaultCommandType fromAnnotatedClass(String className) throws ShellException { 046 Class<Runnable> cls; 047 try { 048 cls = (Class<Runnable>) Class.forName(className); 049 } catch (Exception e) { 050 throw new ShellException(e); 051 } 052 return fromAnnotatedClass(cls); 053 } 054 055 public static DefaultCommandType fromAnnotatedClass(Class<? extends Runnable> cls) throws ShellException { 056 HashMap<String, Token> params = new HashMap<String, Token>(); 057 ArrayList<Token> args = new ArrayList<Token>(); 058 ArrayList<Setter> injectable = new ArrayList<Setter>(); 059 Command cmd = cls.getAnnotation(Command.class); 060 if (cmd == null) { 061 throw new ShellException("Class " + cls + " is not a command. You must annotated it with @Command"); 062 } 063 for (Field field : cls.getDeclaredFields()) { 064 Parameter param = field.getAnnotation(Parameter.class); 065 if (param != null) { 066 Token a = new Token(); 067 a.name = param.name(); 068 a.help = param.help(); 069 a.isRequired = param.hasValue(); 070 a.setter = new FieldSetter(field); 071 a.completor = param.completor(); 072 params.put(a.name, a); 073 continue; 074 } 075 Argument arg = field.getAnnotation(Argument.class); 076 if (arg != null) { 077 Token a = new Token(); 078 a.name = arg.name(); 079 a.index = arg.index(); 080 a.help = arg.help(); 081 a.completor = arg.completor(); 082 a.isRequired = arg.required(); 083 a.setter = new FieldSetter(field); 084 args.add(a); 085 continue; 086 } 087 Context ctx = field.getAnnotation(Context.class); 088 if (ctx != null) { 089 injectable.add(new FieldSetter(field)); 090 } 091 } 092 for (Method method : cls.getDeclaredMethods()) { 093 Parameter param = method.getAnnotation(Parameter.class); 094 if (param != null) { 095 Token a = new Token(); 096 a.name = param.name(); 097 a.help = param.help(); 098 a.isRequired = param.hasValue(); 099 a.setter = new MethodSetter(method); 100 a.completor = param.completor(); 101 params.put(a.name, a); 102 continue; 103 } 104 Argument arg = method.getAnnotation(Argument.class); 105 if (arg != null) { 106 Token a = new Token(); 107 a.name = arg.name(); 108 a.index = arg.index(); 109 a.help = arg.help(); 110 a.isRequired = arg.required(); 111 a.setter = new MethodSetter(method); 112 a.completor = arg.completor(); 113 args.add(a); 114 } 115 } 116 Collections.sort(args); 117 return new DefaultCommandType(cls, injectable, params, args); 118 } 119 120 public DefaultCommandType(Class<? extends Runnable> cmdClass, List<Setter> injectable, Map<String, Token> params, 121 List<Token> args) { 122 super(cmdClass, injectable, params, args); 123 } 124 125 public String getHelp() { 126 return cmdClass.getAnnotation(Command.class).help(); 127 } 128 129 public String getName() { 130 return cmdClass.getAnnotation(Command.class).name(); 131 } 132 133 public String[] getAliases() { 134 return cmdClass.getAnnotation(Command.class).aliases(); 135 } 136 137 public static class MethodSetter implements Setter { 138 protected Method method; 139 140 protected Class<?> type; 141 142 public MethodSetter(Method method) { 143 this.method = method; 144 method.setAccessible(true); 145 Class<?>[] types = method.getParameterTypes(); 146 if (types.length != 1) { 147 throw new IllegalArgumentException("Invalid method setter should take one argument. Method: " + method); 148 } 149 type = types[0]; 150 } 151 152 public void set(Object obj, Object value) throws ShellException { 153 try { 154 method.invoke(obj, value); 155 } catch (Exception e) { 156 throw new ShellException(e); 157 } 158 } 159 160 public Class<?> getType() { 161 return type; 162 } 163 } 164 165 public static class FieldSetter implements Setter { 166 protected Field field; 167 168 protected Class<?> type; 169 170 public FieldSetter(Field field) { 171 this.field = field; 172 field.setAccessible(true); 173 this.type = field.getType(); 174 } 175 176 public void set(Object obj, Object value) throws ShellException { 177 try { 178 field.set(obj, value); 179 } catch (Exception e) { 180 throw new ShellException(e); 181 } 182 } 183 184 public Class<?> getType() { 185 return type; 186 } 187 } 188 189}