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 @SuppressWarnings("unchecked") 041 public static DefaultCommandType fromAnnotatedClass(String className) throws ShellException { 042 Class<Runnable> cls; 043 try { 044 cls = (Class<Runnable>) Class.forName(className); 045 } catch (Exception e) { 046 throw new ShellException(e); 047 } 048 return fromAnnotatedClass(cls); 049 } 050 051 public static DefaultCommandType fromAnnotatedClass(Class<? extends Runnable> cls) throws ShellException { 052 HashMap<String, Token> params = new HashMap<String, Token>(); 053 ArrayList<Token> args = new ArrayList<Token>(); 054 ArrayList<Setter> injectable = new ArrayList<Setter>(); 055 Command cmd = cls.getAnnotation(Command.class); 056 if (cmd == null) { 057 throw new ShellException("Class " + cls + " is not a command. You must annotated it with @Command"); 058 } 059 for (Field field : cls.getDeclaredFields()) { 060 Parameter param = field.getAnnotation(Parameter.class); 061 if (param != null) { 062 Token a = new Token(); 063 a.name = param.name(); 064 a.help = param.help(); 065 a.isRequired = param.hasValue(); 066 a.setter = new FieldSetter(field); 067 a.completor = param.completor(); 068 params.put(a.name, a); 069 continue; 070 } 071 Argument arg = field.getAnnotation(Argument.class); 072 if (arg != null) { 073 Token a = new Token(); 074 a.name = arg.name(); 075 a.index = arg.index(); 076 a.help = arg.help(); 077 a.completor = arg.completor(); 078 a.isRequired = arg.required(); 079 a.setter = new FieldSetter(field); 080 args.add(a); 081 continue; 082 } 083 Context ctx = field.getAnnotation(Context.class); 084 if (ctx != null) { 085 injectable.add(new FieldSetter(field)); 086 } 087 } 088 for (Method method : cls.getDeclaredMethods()) { 089 Parameter param = method.getAnnotation(Parameter.class); 090 if (param != null) { 091 Token a = new Token(); 092 a.name = param.name(); 093 a.help = param.help(); 094 a.isRequired = param.hasValue(); 095 a.setter = new MethodSetter(method); 096 a.completor = param.completor(); 097 params.put(a.name, a); 098 continue; 099 } 100 Argument arg = method.getAnnotation(Argument.class); 101 if (arg != null) { 102 Token a = new Token(); 103 a.name = arg.name(); 104 a.index = arg.index(); 105 a.help = arg.help(); 106 a.isRequired = arg.required(); 107 a.setter = new MethodSetter(method); 108 a.completor = arg.completor(); 109 args.add(a); 110 } 111 } 112 Collections.sort(args); 113 return new DefaultCommandType(cls, injectable, params, args); 114 } 115 116 public DefaultCommandType(Class<? extends Runnable> cmdClass, List<Setter> injectable, Map<String, Token> params, 117 List<Token> args) { 118 super(cmdClass, injectable, params, args); 119 } 120 121 public String getHelp() { 122 return cmdClass.getAnnotation(Command.class).help(); 123 } 124 125 public String getName() { 126 return cmdClass.getAnnotation(Command.class).name(); 127 } 128 129 public String[] getAliases() { 130 return cmdClass.getAnnotation(Command.class).aliases(); 131 } 132 133 public static class MethodSetter implements Setter { 134 protected Method method; 135 136 protected Class<?> type; 137 138 public MethodSetter(Method method) { 139 this.method = method; 140 method.setAccessible(true); 141 Class<?>[] types = method.getParameterTypes(); 142 if (types.length != 1) { 143 throw new IllegalArgumentException("Invalid method setter should take one argument. Method: " + method); 144 } 145 type = types[0]; 146 } 147 148 public void set(Object obj, Object value) throws ShellException { 149 try { 150 method.invoke(obj, value); 151 } catch (Exception e) { 152 throw new ShellException(e); 153 } 154 } 155 156 public Class<?> getType() { 157 return type; 158 } 159 } 160 161 public static class FieldSetter implements Setter { 162 protected Field field; 163 164 protected Class<?> type; 165 166 public FieldSetter(Field field) { 167 this.field = field; 168 field.setAccessible(true); 169 this.type = field.getType(); 170 } 171 172 public void set(Object obj, Object value) throws ShellException { 173 try { 174 field.set(obj, value); 175 } catch (Exception e) { 176 throw new ShellException(e); 177 } 178 } 179 180 public Class<?> getType() { 181 return type; 182 } 183 } 184 185}