001/* 002 * (C) Copyright 2006-2010 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library 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 * Contributors: 015 * bstefanescu 016 */ 017package org.nuxeo.shell.impl; 018 019import java.util.ArrayList; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023 024import jline.Completor; 025import jline.SimpleCompletor; 026 027import org.nuxeo.shell.CommandType; 028import org.nuxeo.shell.Shell; 029import org.nuxeo.shell.ShellException; 030 031/** 032 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 033 */ 034public abstract class AbstractCommandType implements CommandType { 035 036 protected Class<? extends Runnable> cmdClass; 037 038 protected List<Setter> injectable; 039 040 protected Map<String, Token> params; 041 042 protected List<Token> args; 043 044 public AbstractCommandType(Class<? extends Runnable> cmdClass, List<Setter> injectable, Map<String, Token> params, 045 List<Token> args) { 046 this.cmdClass = cmdClass; 047 this.params = params == null ? new HashMap<String, Token>() : params; 048 this.args = args == null ? new ArrayList<Token>() : args; 049 this.injectable = injectable == null ? new ArrayList<Setter>() : injectable; 050 } 051 052 public Class<?> getCommandClass() { 053 return cmdClass; 054 } 055 056 public List<Token> getArguments() { 057 return args; 058 } 059 060 public Map<String, Token> getParameters() { 061 return params; 062 } 063 064 public String getSyntax() { 065 ArrayList<String> argNames = new ArrayList<String>(); 066 for (Token arg : args) { 067 if (arg.isArgument()) { 068 if (arg.isRequired) { 069 argNames.add(arg.name); 070 } else { 071 argNames.add("[" + arg.name + "]"); 072 } 073 } 074 } 075 StringBuilder buf = new StringBuilder(); 076 buf.append(getName()); 077 if (!params.isEmpty()) { 078 buf.append(" [options]"); 079 } 080 for (String name : argNames) { 081 buf.append(" ").append(name); 082 } 083 return buf.toString(); 084 } 085 086 public Runnable newInstance(Shell shell, String... line) throws ShellException { 087 Runnable cmd; 088 try { 089 cmd = createInstance(shell); 090 } catch (Throwable t) { 091 throw new ShellException(t); 092 } 093 inject(shell, cmd, line); 094 return cmd; 095 } 096 097 protected Runnable createInstance(Shell shell) throws Exception { 098 return cmdClass.newInstance(); 099 } 100 101 /** 102 * The last element in line must be an empty element (e.g. "") if you need the next argument that may match. If the 103 * last element is not empty then this element will be returned as an argument or parameter. 104 * 105 * @param line 106 * @return 107 */ 108 protected Token getLastToken(String... line) { 109 int index = -1; 110 Token last = null; 111 if (params != null) { 112 for (int i = 1; i < line.length; i++) { 113 String key = line[i]; 114 if (key.startsWith("-")) { // a param 115 Token arg = params.get(key); 116 if (arg != null && arg.isRequired) { 117 i++; 118 last = arg; 119 continue; 120 } 121 } else { // an arg 122 last = null; 123 index++; 124 } 125 } 126 } 127 if (last != null) { 128 return last; 129 } 130 if (index == -1 || index >= args.size()) { 131 return null; 132 } 133 if (args != null) { 134 return args.get(index); 135 } 136 return null; 137 } 138 139 protected Completor getParamCompletor(String prefix) { 140 ArrayList<String> result = new ArrayList<String>(); 141 for (String key : params.keySet()) { 142 if (key.startsWith(prefix)) { 143 result.add(key); 144 } 145 } 146 return result.isEmpty() ? null : new SimpleCompletor(result.toArray(new String[result.size()])); 147 } 148 149 public Completor getLastTokenCompletor(Shell shell, String... line) { 150 // check first for a param key completor 151 String last = line[line.length - 1]; 152 if (last.startsWith("-")) { // may be a param 153 Completor c = getParamCompletor(last); 154 if (c != null) { 155 return c; 156 } 157 } 158 // check now for a value completor 159 Token arg = getLastToken(line); 160 if (arg == null) { 161 return null; 162 } 163 if (arg.completor != null && !arg.completor.isInterface()) { 164 try { 165 return arg.completor.newInstance(); 166 } catch (Throwable t) { 167 throw new ShellException("Failed to load completor: " + arg.completor, t); 168 } 169 } 170 return shell.getCompletorProvider().getCompletor(shell, this, arg.setter.getType()); 171 } 172 173 protected void inject(Shell shell, Runnable cmd, String... line) throws ShellException { 174 for (Setter s : injectable) { 175 s.set(cmd, shell.getContextObject(s.getType())); 176 } 177 int index = 0; 178 int argCount = args.size(); 179 for (int i = 1; i < line.length; i++) { 180 String key = line[i]; 181 if (key.startsWith("-")) { 182 Token arg = params.get(key); 183 if (arg == null) { 184 throw new ShellException("Unknown parameter: " + key); 185 } 186 String v = null; 187 if (!arg.isRequired) { 188 v = "true"; 189 } else if (i == line.length - 1) { 190 throw new ShellException("Parameter " + key + " must have a value"); 191 } else { 192 v = line[++i]; 193 } 194 arg.setter.set(cmd, shell.getValueAdapter().getValue(shell, arg.setter.getType(), v)); 195 } else { 196 if (index >= argCount) { 197 throw new ShellException("Too many arguments"); 198 } 199 Token arg = args.get(index++); 200 arg.setter.set(cmd, shell.getValueAdapter().getValue(shell, arg.setter.getType(), key)); 201 } 202 } 203 for (int i = index; i < argCount; i++) { 204 if (args.get(i).isRequired) { 205 throw new ShellException("Required argument " + args.get(i).name + " is missing"); 206 } 207 } 208 } 209 210 public int compareTo(CommandType o) { 211 return getName().compareTo(o.getName()); 212 } 213}