001 /* 002 * Copyright (C) 2010 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.cmdline; 021 022 import org.crsh.cmdline.annotations.Argument; 023 import org.crsh.cmdline.annotations.Command; 024 import org.crsh.cmdline.annotations.Option; 025 import org.crsh.cmdline.annotations.Required; 026 import org.crsh.cmdline.binding.ClassFieldBinding; 027 import org.crsh.cmdline.binding.MethodArgumentBinding; 028 import org.crsh.cmdline.binding.TypeBinding; 029 import org.slf4j.Logger; 030 import org.slf4j.LoggerFactory; 031 032 import java.lang.annotation.Annotation; 033 import java.lang.reflect.Field; 034 import java.lang.reflect.Method; 035 import java.lang.reflect.Type; 036 import java.util.ArrayList; 037 import java.util.Arrays; 038 import java.util.Collections; 039 import java.util.List; 040 041 /** 042 * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> 043 * @version $Revision$ 044 */ 045 public class CommandFactory { 046 047 /** . */ 048 private static final Logger log = LoggerFactory.getLogger(CommandFactory.class); 049 050 public static <T> ClassDescriptor<T> create(Class<T> type) throws IntrospectionException { 051 ClassDescriptor<T> descriptor = new ClassDescriptor<T>(type, new Description(type)); 052 for (ParameterDescriptor<ClassFieldBinding> parameter : parameters(type)) { 053 descriptor.addParameter(parameter); 054 } 055 return descriptor; 056 } 057 058 protected static <B extends TypeBinding> ParameterDescriptor<B> create( 059 B binding, 060 Type type, 061 Argument argumentAnn, 062 Option optionAnn, 063 boolean required, 064 Description info, 065 Annotation ann) throws IntrospectionException { 066 067 // 068 if (argumentAnn != null) { 069 if (optionAnn != null) { 070 throw new IntrospectionException(); 071 } 072 073 // 074 return new ArgumentDescriptor<B>( 075 binding, 076 argumentAnn.name(), 077 type, 078 info, 079 required, 080 argumentAnn.password(), 081 argumentAnn.unquote(), 082 argumentAnn.completer(), 083 ann); 084 } else if (optionAnn != null) { 085 return new OptionDescriptor<B>( 086 binding, 087 type, 088 Collections.unmodifiableList(Arrays.asList(optionAnn.names())), 089 info, 090 required, 091 optionAnn.password(), 092 optionAnn.unquote(), 093 optionAnn.completer(), 094 ann); 095 } else { 096 return null; 097 } 098 } 099 100 protected static Tuple get(Annotation... ab) { 101 Argument argumentAnn = null; 102 Option optionAnn = null; 103 Boolean required = null; 104 Description description = new Description(ab); 105 Annotation info = null; 106 for (Annotation parameterAnnotation : ab) { 107 if (parameterAnnotation instanceof Option) { 108 optionAnn = (Option)parameterAnnotation; 109 } else if (parameterAnnotation instanceof Argument) { 110 argumentAnn = (Argument)parameterAnnotation; 111 } else if (parameterAnnotation instanceof Required) { 112 required = ((Required)parameterAnnotation).value(); 113 } else if (info == null) { 114 115 // Look at annotated annotations 116 Class<? extends Annotation> a = parameterAnnotation.annotationType(); 117 if (a.getAnnotation(Option.class) != null) { 118 optionAnn = a.getAnnotation(Option.class); 119 info = parameterAnnotation; 120 } else if (a.getAnnotation(Argument.class) != null) { 121 argumentAnn = a.getAnnotation(Argument.class); 122 info = parameterAnnotation; 123 } 124 125 // 126 if (info != null) { 127 128 // 129 description = new Description(description, new Description(a)); 130 131 // 132 if (required == null) { 133 Required metaReq = a.getAnnotation(Required.class); 134 if (metaReq != null) { 135 required = metaReq.value(); 136 } 137 } 138 } 139 } 140 } 141 142 // 143 return new Tuple(argumentAnn, optionAnn, required != null && required,description, info); 144 } 145 146 public static <T> MethodDescriptor<T> create(ClassDescriptor<T> owner, Method m) throws IntrospectionException { 147 Command command = m.getAnnotation(Command.class); 148 if (command != null) { 149 150 // 151 Description info = new Description(m); 152 MethodDescriptor<T> descriptor = new MethodDescriptor<T>( 153 owner, 154 m, 155 m.getName().toLowerCase(), 156 info); 157 158 Type[] parameterTypes = m.getGenericParameterTypes(); 159 Annotation[][] parameterAnnotationMatrix = m.getParameterAnnotations(); 160 for (int i = 0;i < parameterAnnotationMatrix.length;i++) { 161 162 Annotation[] parameterAnnotations = parameterAnnotationMatrix[i]; 163 Type parameterType = parameterTypes[i]; 164 Tuple tuple = get(parameterAnnotations); 165 166 MethodArgumentBinding binding = new MethodArgumentBinding(i); 167 ParameterDescriptor<MethodArgumentBinding> parameter = create( 168 binding, 169 parameterType, 170 tuple.argumentAnn, 171 tuple.optionAnn, 172 tuple.required, 173 tuple.descriptionAnn, 174 tuple.ann); 175 if (parameter != null) { 176 descriptor.addParameter(parameter); 177 } else { 178 log.debug("Method argument with index " + i + " of method " + m + " is not annotated"); 179 } 180 } 181 182 // 183 return descriptor; 184 } else { 185 return null; 186 } 187 } 188 189 /** 190 * Jus grouping some data for conveniency 191 */ 192 protected static class Tuple { 193 final Argument argumentAnn; 194 final Option optionAnn; 195 final boolean required; 196 final Description descriptionAnn; 197 final Annotation ann; 198 private Tuple(Argument argumentAnn, Option optionAnn, boolean required, Description info, Annotation ann) { 199 this.argumentAnn = argumentAnn; 200 this.optionAnn = optionAnn; 201 this.required = required; 202 this.descriptionAnn = info; 203 this.ann = ann; 204 } 205 } 206 207 private static List<ParameterDescriptor<ClassFieldBinding>> parameters(Class<?> introspected) throws IntrospectionException { 208 List<ParameterDescriptor<ClassFieldBinding>> parameters; 209 Class<?> superIntrospected = introspected.getSuperclass(); 210 if (superIntrospected == null) { 211 parameters = new ArrayList<ParameterDescriptor<ClassFieldBinding>>(); 212 } else { 213 parameters = parameters(superIntrospected); 214 for (Field f : introspected.getDeclaredFields()) { 215 Tuple tuple = CommandFactory.get(f.getAnnotations()); 216 ClassFieldBinding binding = new ClassFieldBinding(f); 217 ParameterDescriptor<ClassFieldBinding> parameter = CommandFactory.create( 218 binding, 219 f.getGenericType(), 220 tuple.argumentAnn, 221 tuple.optionAnn, 222 tuple.required, 223 tuple.descriptionAnn, 224 tuple.ann); 225 if (parameter != null) { 226 parameters.add(parameter); 227 } 228 } 229 } 230 return parameters; 231 } 232 }