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 020package org.crsh.cli.impl.lang; 021 022import org.crsh.cli.descriptor.ArgumentDescriptor; 023import org.crsh.cli.descriptor.CommandDescriptor; 024import org.crsh.cli.descriptor.Description; 025import org.crsh.cli.impl.descriptor.CommandDescriptorImpl; 026import org.crsh.cli.impl.descriptor.IntrospectionException; 027import org.crsh.cli.descriptor.OptionDescriptor; 028import org.crsh.cli.descriptor.ParameterDescriptor; 029import org.crsh.cli.SyntaxException; 030import org.crsh.cli.impl.invocation.CommandInvoker; 031import org.crsh.cli.impl.invocation.InvocationException; 032import org.crsh.cli.impl.invocation.InvocationMatch; 033import org.crsh.cli.impl.invocation.ParameterMatch; 034import org.crsh.cli.impl.invocation.Resolver; 035 036import java.lang.reflect.Field; 037import java.lang.reflect.Type; 038import java.util.HashSet; 039import java.util.Map; 040import java.util.Set; 041 042class 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, Void>() { 087 @Override 088 public InvocationMatch<T> getMatch() { 089 return match; 090 } 091 @Override 092 public Class<Void> getReturnType() { 093 return Void.class; 094 } 095 @Override 096 public Type getGenericReturnType() { 097 return Void.class; 098 } 099 @Override 100 public Class<?>[] getParameterTypes() { 101 return new Class<?>[0]; 102 } 103 @Override 104 public Type[] getGenericParameterTypes() { 105 return new Type[0]; 106 } 107 @Override 108 public Void invoke(Resolver resolver, T command) throws InvocationException, SyntaxException { 109 configure(match, command); 110 Runnable runnable = Runnable.class.cast(command); 111 try { 112 runnable.run(); 113 } 114 catch (Exception e) { 115 throw new InvocationException(e); 116 } 117 return null; 118 } 119 }; 120 } else { 121 return null; 122 } 123 } 124 125 void configure(InvocationMatch<T> classMatch, T command) throws InvocationException, SyntaxException { 126 for (ParameterDescriptor parameter : getParameters()) { 127 ParameterMatch match = classMatch.getParameter(parameter); 128 if (match == null) { 129 if (parameter.isRequired()) { 130 if (parameter instanceof ArgumentDescriptor) { 131 ArgumentDescriptor argument = (ArgumentDescriptor)parameter; 132 throw new SyntaxException("Missing argument " + argument.getName()); 133 } else { 134 OptionDescriptor option = (OptionDescriptor)parameter; 135 throw new SyntaxException("Missing option " + option.getNames()); 136 } 137 } 138 } else { 139 Object value = match.computeValue(); 140 Field f = ((ClassFieldBinding)parameter.getBinding()).getField(); 141 try { 142 f.setAccessible(true); 143 f.set(command, value); 144 } 145 catch (Exception e) { 146 throw new InvocationException(e.getMessage(), e); 147 } 148 } 149 } 150 } 151 152 @Override 153 public CommandDescriptor<T> getOwner() { 154 return null; 155 } 156 157 @Override 158 public Class<T> getType() { 159 return type; 160 } 161 162 @Override 163 public Map<String, ? extends MethodDescriptor<T>> getSubordinates() { 164 return methods; 165 } 166 167 public MethodDescriptor<T> getSubordinate(String name) { 168 return methods.get(name); 169 } 170}