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 }