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.binding.ClassFieldBinding; 023 import static org.crsh.cmdline.Util.indent; 024 025 import org.slf4j.Logger; 026 import org.slf4j.LoggerFactory; 027 028 import java.io.IOException; 029 import java.lang.reflect.Method; 030 import java.util.ArrayList; 031 import java.util.Collections; 032 import java.util.Formatter; 033 import java.util.HashSet; 034 import java.util.LinkedHashMap; 035 import java.util.List; 036 import java.util.Map; 037 import java.util.Set; 038 039 /** 040 * A command backed by a class. 041 * 042 * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> 043 * @version $Revision$ 044 */ 045 public class ClassDescriptor<T> extends CommandDescriptor<T, ClassFieldBinding> { 046 047 /** . */ 048 private static final Logger log = LoggerFactory.getLogger(ClassDescriptor.class); 049 050 /** . */ 051 private final Class<T> type; 052 053 /** . */ 054 private final Map<String, MethodDescriptor<T>> methodMap; 055 056 public ClassDescriptor(Class<T> type, Description info) throws IntrospectionException { 057 super(type.getSimpleName().toLowerCase(), info); 058 059 // Make sure we can add it 060 Map<String, MethodDescriptor<T>> methodMap = new LinkedHashMap<String, MethodDescriptor<T>>(); 061 for (MethodDescriptor<T> method : commands(type)) { 062 // 063 methodMap.put(method.getName(), method); 064 } 065 066 // 067 this.methodMap = methodMap; 068 this.type = type; 069 } 070 071 @Override 072 void addParameter(ParameterDescriptor<ClassFieldBinding> parameter) throws IntrospectionException { 073 074 // Check we can add the option 075 if (parameter instanceof OptionDescriptor<?>) { 076 OptionDescriptor<ClassFieldBinding> option = (OptionDescriptor<ClassFieldBinding>)parameter; 077 Set<String> blah = new HashSet<String>(); 078 for (String optionName : option.getNames()) { 079 blah.add((optionName.length() == 1 ? "-" : "--") + optionName); 080 } 081 for (MethodDescriptor<T> method : methodMap.values()) { 082 Set<String> diff = new HashSet<String>(method.getOptionNames()); 083 diff.retainAll(blah); 084 if (diff.size() > 0) { 085 throw new IntrospectionException("Cannot add method " + method.getName() + " because it has common " 086 + " options with its class: " + diff); 087 } 088 } 089 } 090 091 // 092 super.addParameter(parameter); 093 } 094 095 @Override 096 public Class<T> getType() { 097 return type; 098 } 099 100 @Override 101 public Map<String, ? extends CommandDescriptor<T, ?>> getSubordinates() { 102 return methodMap; 103 } 104 105 @Override 106 public OptionDescriptor<?> findOption(String name) { 107 return getOption(name); 108 } 109 110 @Override 111 public void printUsage(Appendable writer) throws IOException { 112 if (methodMap.size() == 1) { 113 methodMap.values().iterator().next().printUsage(writer); 114 } else { 115 writer.append("usage: ").append(getName()); 116 for (OptionDescriptor<?> option : getOptions()) { 117 option.printUsage(writer); 118 } 119 writer.append(" COMMAND [ARGS]\n\n"); 120 writer.append("The most commonly used ").append(getName()).append(" commands are:\n"); 121 String format = " %1$-16s %2$s\n"; 122 for (MethodDescriptor<T> method : getMethods()) { 123 Formatter formatter = new Formatter(writer); 124 formatter.format(format, method.getName(), method.getUsage()); 125 } 126 } 127 } 128 129 public void printMan(Appendable writer) throws IOException { 130 if (methodMap.size() == 1) { 131 methodMap.values().iterator().next().printMan(writer); 132 } else { 133 134 // Name 135 writer.append("NAME\n"); 136 writer.append(Util.MAN_TAB).append(getName()); 137 if (getUsage().length() > 0) { 138 writer.append(" - ").append(getUsage()); 139 } 140 writer.append("\n\n"); 141 142 // Synopsis 143 writer.append("SYNOPSIS\n"); 144 writer.append(Util.MAN_TAB).append(getName()); 145 for (OptionDescriptor<?> option : getOptions()) { 146 writer.append(" "); 147 option.printUsage(writer); 148 } 149 writer.append(" COMMAND [ARGS]\n\n"); 150 151 // 152 String man = getDescription().getMan(); 153 if (man.length() > 0) { 154 writer.append("DESCRIPTION\n"); 155 indent(Util.MAN_TAB, man, writer); 156 writer.append("\n\n"); 157 } 158 159 // Common options 160 if (getOptions().size() > 0) { 161 writer.append("PARAMETERS\n"); 162 for (OptionDescriptor<?> option : getOptions()) { 163 writer.append(Util.MAN_TAB); 164 option.printUsage(writer); 165 String optionText = option.getDescription().getBestEffortMan(); 166 if (optionText.length() > 0) { 167 writer.append("\n"); 168 indent(Util.MAN_TAB_EXTRA, optionText, writer); 169 } 170 writer.append("\n\n"); 171 } 172 } 173 174 // 175 writer.append("COMMANDS\n"); 176 for (MethodDescriptor<T> method : getMethods()) { 177 writer.append(Util.MAN_TAB).append(method.getName()); 178 String methodText = method.getDescription().getBestEffortMan(); 179 if (methodText.length() > 0) { 180 writer.append("\n"); 181 indent(Util.MAN_TAB_EXTRA, methodText, writer); 182 } 183 writer.append("\n\n"); 184 } 185 } 186 } 187 188 public Iterable<MethodDescriptor<T>> getMethods() { 189 return methodMap.values(); 190 } 191 192 public MethodDescriptor<T> getMethod(String name) { 193 return methodMap.get(name); 194 } 195 196 private List<MethodDescriptor<T>> commands(Class<?> introspected) throws IntrospectionException { 197 List<MethodDescriptor<T>> commands; 198 Class<?> superIntrospected = introspected.getSuperclass(); 199 if (superIntrospected == null) { 200 commands = new ArrayList<MethodDescriptor<T>>(); 201 } else { 202 commands = commands(superIntrospected); 203 for (Method m : introspected.getDeclaredMethods()) { 204 MethodDescriptor<T> mDesc = CommandFactory.create(this, m); 205 if (mDesc != null) { 206 commands.add(mDesc); 207 } 208 } 209 } 210 return commands; 211 } 212 }