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 return args.get(index); 136 } 137 138 protected Completor getParamCompletor(String prefix) { 139 ArrayList<String> result = new ArrayList<String>(); 140 for (String key : params.keySet()) { 141 if (key.startsWith(prefix)) { 142 result.add(key); 143 } 144 } 145 return result.isEmpty() ? null : new SimpleCompletor(result.toArray(new String[result.size()])); 146 } 147 148 public Completor getLastTokenCompletor(Shell shell, String... line) { 149 // check first for a param key completor 150 String last = line[line.length - 1]; 151 if (last.startsWith("-")) { // may be a param 152 Completor c = getParamCompletor(last); 153 if (c != null) { 154 return c; 155 } 156 } 157 // check now for a value completor 158 Token arg = getLastToken(line); 159 if (arg == null) { 160 return null; 161 } 162 if (arg.completor != null && !arg.completor.isInterface()) { 163 try { 164 return arg.completor.newInstance(); 165 } catch (Throwable t) { 166 throw new ShellException("Failed to load completor: " + arg.completor, t); 167 } 168 } 169 return shell.getCompletorProvider().getCompletor(shell, this, arg.setter.getType()); 170 } 171 172 protected void inject(Shell shell, Runnable cmd, String... line) throws ShellException { 173 for (Setter s : injectable) { 174 s.set(cmd, shell.getContextObject(s.getType())); 175 } 176 int index = 0; 177 int argCount = args.size(); 178 for (int i = 1; i < line.length; i++) { 179 String key = line[i]; 180 if (key.startsWith("-")) { 181 Token arg = params.get(key); 182 if (arg == null) { 183 throw new ShellException("Unknown parameter: " + key); 184 } 185 String v = null; 186 if (!arg.isRequired) { 187 v = "true"; 188 } else if (i == line.length - 1) { 189 throw new ShellException("Parameter " + key + " must have a value"); 190 } else { 191 v = line[++i]; 192 } 193 arg.setter.set(cmd, shell.getValueAdapter().getValue(shell, arg.setter.getType(), v)); 194 } else { 195 if (index >= argCount) { 196 throw new ShellException("Too many arguments"); 197 } 198 Token arg = args.get(index++); 199 arg.setter.set(cmd, shell.getValueAdapter().getValue(shell, arg.setter.getType(), key)); 200 } 201 } 202 for (int i = index; i < argCount; i++) { 203 if (args.get(i).isRequired) { 204 throw new ShellException("Required argument " + args.get(i).name + " is missing"); 205 } 206 } 207 } 208 209 public int compareTo(CommandType o) { 210 return getName().compareTo(o.getName()); 211 } 212}