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