001/* 002 * (C) Copyright 2006-2010 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * bstefanescu 018 */ 019package org.nuxeo.shell.impl; 020 021import java.lang.reflect.Field; 022import java.lang.reflect.Method; 023import java.util.ArrayList; 024import java.util.Collections; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028 029import org.nuxeo.shell.Argument; 030import org.nuxeo.shell.Command; 031import org.nuxeo.shell.Context; 032import org.nuxeo.shell.Parameter; 033import org.nuxeo.shell.ShellException; 034 035/** 036 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 037 */ 038public class DefaultCommandType extends AbstractCommandType { 039 040 protected List<Setter> injectable; 041 042 protected Map<String, Token> params; 043 044 protected List<Token> args; 045 046 @SuppressWarnings("unchecked") 047 public static DefaultCommandType fromAnnotatedClass(String className) throws ShellException { 048 Class<Runnable> cls; 049 try { 050 cls = (Class<Runnable>) Class.forName(className); 051 } catch (Exception e) { 052 throw new ShellException(e); 053 } 054 return fromAnnotatedClass(cls); 055 } 056 057 public static DefaultCommandType fromAnnotatedClass(Class<? extends Runnable> cls) throws ShellException { 058 HashMap<String, Token> params = new HashMap<String, Token>(); 059 ArrayList<Token> args = new ArrayList<Token>(); 060 ArrayList<Setter> injectable = new ArrayList<Setter>(); 061 Command cmd = cls.getAnnotation(Command.class); 062 if (cmd == null) { 063 throw new ShellException("Class " + cls + " is not a command. You must annotated it with @Command"); 064 } 065 for (Field field : cls.getDeclaredFields()) { 066 Parameter param = field.getAnnotation(Parameter.class); 067 if (param != null) { 068 Token a = new Token(); 069 a.name = param.name(); 070 a.help = param.help(); 071 a.isRequired = param.hasValue(); 072 a.setter = new FieldSetter(field); 073 a.completor = param.completor(); 074 params.put(a.name, a); 075 continue; 076 } 077 Argument arg = field.getAnnotation(Argument.class); 078 if (arg != null) { 079 Token a = new Token(); 080 a.name = arg.name(); 081 a.index = arg.index(); 082 a.help = arg.help(); 083 a.completor = arg.completor(); 084 a.isRequired = arg.required(); 085 a.setter = new FieldSetter(field); 086 args.add(a); 087 continue; 088 } 089 Context ctx = field.getAnnotation(Context.class); 090 if (ctx != null) { 091 injectable.add(new FieldSetter(field)); 092 } 093 } 094 for (Method method : cls.getDeclaredMethods()) { 095 Parameter param = method.getAnnotation(Parameter.class); 096 if (param != null) { 097 Token a = new Token(); 098 a.name = param.name(); 099 a.help = param.help(); 100 a.isRequired = param.hasValue(); 101 a.setter = new MethodSetter(method); 102 a.completor = param.completor(); 103 params.put(a.name, a); 104 continue; 105 } 106 Argument arg = method.getAnnotation(Argument.class); 107 if (arg != null) { 108 Token a = new Token(); 109 a.name = arg.name(); 110 a.index = arg.index(); 111 a.help = arg.help(); 112 a.isRequired = arg.required(); 113 a.setter = new MethodSetter(method); 114 a.completor = arg.completor(); 115 args.add(a); 116 } 117 } 118 Collections.sort(args); 119 return new DefaultCommandType(cls, injectable, params, args); 120 } 121 122 public DefaultCommandType(Class<? extends Runnable> cmdClass, List<Setter> injectable, Map<String, Token> params, 123 List<Token> args) { 124 super(cmdClass, injectable, params, args); 125 } 126 127 public String getHelp() { 128 return cmdClass.getAnnotation(Command.class).help(); 129 } 130 131 public String getName() { 132 return cmdClass.getAnnotation(Command.class).name(); 133 } 134 135 public String[] getAliases() { 136 return cmdClass.getAnnotation(Command.class).aliases(); 137 } 138 139 public static class MethodSetter implements Setter { 140 protected Method method; 141 142 protected Class<?> type; 143 144 public MethodSetter(Method method) { 145 this.method = method; 146 method.setAccessible(true); 147 Class<?>[] types = method.getParameterTypes(); 148 if (types.length != 1) { 149 throw new IllegalArgumentException("Invalid method setter should take one argument. Method: " + method); 150 } 151 type = types[0]; 152 } 153 154 public void set(Object obj, Object value) throws ShellException { 155 try { 156 method.invoke(obj, value); 157 } catch (Exception e) { 158 throw new ShellException(e); 159 } 160 } 161 162 public Class<?> getType() { 163 return type; 164 } 165 } 166 167 public static class FieldSetter implements Setter { 168 protected Field field; 169 170 protected Class<?> type; 171 172 public FieldSetter(Field field) { 173 this.field = field; 174 field.setAccessible(true); 175 this.type = field.getType(); 176 } 177 178 public void set(Object obj, Object value) throws ShellException { 179 try { 180 field.set(obj, value); 181 } catch (Exception e) { 182 throw new ShellException(e); 183 } 184 } 185 186 public Class<?> getType() { 187 return type; 188 } 189 } 190 191}