1 /* 2 * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.jdi; 27 28 import java.util.ArrayList; 29 import java.util.Collection; 30 import java.util.Collections; 31 import java.util.Iterator; 32 import java.util.LinkedHashMap; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.ResourceBundle; 36 37 import com.sun.jdi.InternalException; 38 import com.sun.jdi.connect.Connector; 39 import com.sun.jdi.connect.IllegalConnectorArgumentsException; 40 import com.sun.jdi.connect.LaunchingConnector; 41 42 abstract class ConnectorImpl implements Connector { 43 44 Map<String, Argument> defaultArguments = new LinkedHashMap<>(); 45 46 // Used by BooleanArgument 47 static String trueString = null; 48 static String falseString; 49 defaultArguments()50 public Map<String,Argument> defaultArguments() { 51 Map<String,Argument> defaults = new LinkedHashMap<>(); 52 Collection<Argument> values = defaultArguments.values(); 53 54 Iterator<Argument> iter = values.iterator(); 55 while (iter.hasNext()) { 56 ArgumentImpl argument = (ArgumentImpl)iter.next(); 57 defaults.put(argument.name(), (Argument)argument.clone()); 58 } 59 return defaults; 60 } 61 addStringArgument(String name, String label, String description, String defaultValue, boolean mustSpecify)62 void addStringArgument(String name, String label, String description, 63 String defaultValue, boolean mustSpecify) { 64 defaultArguments.put(name, 65 new StringArgumentImpl(name, label, 66 description, 67 defaultValue, 68 mustSpecify)); 69 } 70 addBooleanArgument(String name, String label, String description, boolean defaultValue, boolean mustSpecify)71 void addBooleanArgument(String name, String label, String description, 72 boolean defaultValue, boolean mustSpecify) { 73 defaultArguments.put(name, 74 new BooleanArgumentImpl(name, label, 75 description, 76 defaultValue, 77 mustSpecify)); 78 } 79 addIntegerArgument(String name, String label, String description, String defaultValue, boolean mustSpecify, int min, int max)80 void addIntegerArgument(String name, String label, String description, 81 String defaultValue, boolean mustSpecify, 82 int min, int max) { 83 defaultArguments.put(name, 84 new IntegerArgumentImpl(name, label, 85 description, 86 defaultValue, 87 mustSpecify, 88 min, max)); 89 } 90 addSelectedArgument(String name, String label, String description, String defaultValue, boolean mustSpecify, List<String> list)91 void addSelectedArgument(String name, String label, String description, 92 String defaultValue, boolean mustSpecify, 93 List<String> list) { 94 defaultArguments.put(name, 95 new SelectedArgumentImpl(name, label, 96 description, 97 defaultValue, 98 mustSpecify, list)); 99 } 100 argument(String name, Map<String, ? extends Argument> arguments)101 ArgumentImpl argument(String name, Map<String, ? extends Argument> arguments) 102 throws IllegalConnectorArgumentsException { 103 104 ArgumentImpl argument = (ArgumentImpl)arguments.get(name); 105 if (argument == null) { 106 throw new IllegalConnectorArgumentsException( 107 "Argument missing", name); 108 } 109 String value = argument.value(); 110 if (value == null || value.length() == 0) { 111 if (argument.mustSpecify()) { 112 throw new IllegalConnectorArgumentsException( 113 "Argument unspecified", name); 114 } 115 } else if(!argument.isValid(value)) { 116 throw new IllegalConnectorArgumentsException( 117 "Argument invalid", name); 118 } 119 120 return argument; 121 } 122 123 124 private ResourceBundle messages = null; 125 getString(String key)126 String getString(String key) { 127 if (messages == null) { 128 messages = ResourceBundle.getBundle("com.sun.tools.jdi.resources.jdi"); 129 } 130 return messages.getString(key); 131 } 132 toString()133 public String toString() { 134 String string = name() + " (defaults: "; 135 Iterator<Argument> iter = defaultArguments().values().iterator(); 136 boolean first = true; 137 while (iter.hasNext()) { 138 ArgumentImpl argument = (ArgumentImpl)iter.next(); 139 if (!first) { 140 string += ", "; 141 } 142 string += argument.toString(); 143 first = false; 144 } 145 string += ")"; 146 return string; 147 } 148 149 @SuppressWarnings("serial") // JDK implementation class 150 abstract class ArgumentImpl implements Connector.Argument, Cloneable { 151 private String name; 152 private String label; 153 private String description; 154 private String value; 155 private boolean mustSpecify; 156 ArgumentImpl(String name, String label, String description, String value, boolean mustSpecify)157 ArgumentImpl(String name, String label, String description, 158 String value, boolean mustSpecify) { 159 this.name = name; 160 this.label = label; 161 this.description = description; 162 this.value = value; 163 this.mustSpecify = mustSpecify; 164 } 165 isValid(String value)166 public abstract boolean isValid(String value); 167 name()168 public String name() { 169 return name; 170 } 171 label()172 public String label() { 173 return label; 174 } 175 description()176 public String description() { 177 return description; 178 } 179 value()180 public String value() { 181 return value; 182 } 183 setValue(String value)184 public void setValue(String value) { 185 if (value == null) { 186 throw new NullPointerException("Can't set null value"); 187 } 188 this.value = value; 189 } 190 mustSpecify()191 public boolean mustSpecify() { 192 return mustSpecify; 193 } 194 equals(Object obj)195 public boolean equals(Object obj) { 196 if ((obj != null) && (obj instanceof Connector.Argument)) { 197 Connector.Argument other = (Connector.Argument)obj; 198 return (name().equals(other.name())) && 199 (description().equals(other.description())) && 200 (mustSpecify() == other.mustSpecify()) && 201 (value().equals(other.value())); 202 } else { 203 return false; 204 } 205 } 206 hashCode()207 public int hashCode() { 208 return description().hashCode(); 209 } 210 clone()211 public Object clone() { 212 try { 213 return super.clone(); 214 } catch (CloneNotSupportedException e) { 215 // Object should always support clone 216 throw new InternalException(); 217 } 218 } 219 toString()220 public String toString() { 221 return name() + "=" + value(); 222 } 223 } 224 225 class BooleanArgumentImpl extends ConnectorImpl.ArgumentImpl 226 implements Connector.BooleanArgument { 227 private static final long serialVersionUID = 1624542968639361316L; BooleanArgumentImpl(String name, String label, String description, boolean value, boolean mustSpecify)228 BooleanArgumentImpl(String name, String label, String description, 229 boolean value, 230 boolean mustSpecify) { 231 super(name, label, description, null, mustSpecify); 232 if(trueString == null) { 233 trueString = getString("true"); 234 falseString = getString("false"); 235 } 236 setValue(value); 237 } 238 239 /** 240 * Sets the value of the argument. 241 */ setValue(boolean value)242 public void setValue(boolean value) { 243 setValue(stringValueOf(value)); 244 } 245 246 /** 247 * Performs basic sanity check of argument. 248 * @return <code>true</code> if value is a string 249 * representation of a boolean value. 250 * @see #stringValueOf(boolean) 251 */ isValid(String value)252 public boolean isValid(String value) { 253 return value.equals(trueString) || value.equals(falseString); 254 } 255 256 /** 257 * Return the string representation of the <code>value</code> 258 * parameter. 259 * Does not set or examine the value or the argument. 260 * @return the localized String representation of the 261 * boolean value. 262 */ stringValueOf(boolean value)263 public String stringValueOf(boolean value) { 264 return value? trueString : falseString; 265 } 266 267 /** 268 * Return the value of the argument as a boolean. Since 269 * the argument may not have been set or may have an invalid 270 * value {@link #isValid(String)} should be called on 271 * {@link #value()} to check its validity. If it is invalid 272 * the boolean returned by this method is undefined. 273 * @return the value of the argument as a boolean. 274 */ booleanValue()275 public boolean booleanValue() { 276 return value().equals(trueString); 277 } 278 } 279 280 class IntegerArgumentImpl extends ConnectorImpl.ArgumentImpl 281 implements Connector.IntegerArgument { 282 private static final long serialVersionUID = 763286081923797770L; 283 private final int min; 284 private final int max; 285 IntegerArgumentImpl(String name, String label, String description, String value, boolean mustSpecify, int min, int max)286 IntegerArgumentImpl(String name, String label, String description, 287 String value, 288 boolean mustSpecify, int min, int max) { 289 super(name, label, description, value, mustSpecify); 290 this.min = min; 291 this.max = max; 292 } 293 294 /** 295 * Sets the value of the argument. 296 * The value should be checked with {@link #isValid(int)} 297 * before setting it; invalid values will throw an exception 298 * when the connection is established - for example, 299 * on {@link LaunchingConnector#launch} 300 */ setValue(int value)301 public void setValue(int value) { 302 setValue(stringValueOf(value)); 303 } 304 305 /** 306 * Performs basic sanity check of argument. 307 * @return <code>true</code> if value represents an int that is 308 * <code>{@link #min()} <= value <= {@link #max()}</code> 309 */ isValid(String value)310 public boolean isValid(String value) { 311 if (value == null) { 312 return false; 313 } 314 try { 315 return isValid(Integer.decode(value).intValue()); 316 } catch (NumberFormatException exc) { 317 return false; 318 } 319 } 320 321 /** 322 * Performs basic sanity check of argument. 323 * @return <code>true</code> if 324 * <code>{@link #min()} <= value <= {@link #max()}</code> 325 */ isValid(int value)326 public boolean isValid(int value) { 327 return min <= value && value <= max; 328 } 329 330 /** 331 * Return the string representation of the <code>value</code> 332 * parameter. 333 * Does not set or examine the value or the argument. 334 * @return the String representation of the 335 * int value. 336 */ stringValueOf(int value)337 public String stringValueOf(int value) { 338 // *** Should this be internationalized???? 339 // *** Even Brian Beck was unsure if an Arabic programmer 340 // *** would expect port numbers in Arabic numerals, 341 // *** so punt for now. 342 return ""+value; 343 } 344 345 /** 346 * Return the value of the argument as a int. Since 347 * the argument may not have been set or may have an invalid 348 * value {@link #isValid(String)} should be called on 349 * {@link #value()} to check its validity. If it is invalid 350 * the int returned by this method is undefined. 351 * @return the value of the argument as a int. 352 */ intValue()353 public int intValue() { 354 if (value() == null) { 355 return 0; 356 } 357 try { 358 return Integer.decode(value()).intValue(); 359 } catch(NumberFormatException exc) { 360 return 0; 361 } 362 } 363 364 /** 365 * The upper bound for the value. 366 * @return the maximum allowed value for this argument. 367 */ max()368 public int max() { 369 return max; 370 } 371 372 /** 373 * The lower bound for the value. 374 * @return the minimum allowed value for this argument. 375 */ min()376 public int min() { 377 return min; 378 } 379 } 380 381 class StringArgumentImpl extends ConnectorImpl.ArgumentImpl 382 implements Connector.StringArgument { 383 private static final long serialVersionUID = 7500484902692107464L; StringArgumentImpl(String name, String label, String description, String value, boolean mustSpecify)384 StringArgumentImpl(String name, String label, String description, 385 String value, boolean mustSpecify) { 386 super(name, label, description, value, mustSpecify); 387 } 388 389 /** 390 * Performs basic sanity check of argument. 391 * @return <code>true</code> always 392 */ isValid(String value)393 public boolean isValid(String value) { 394 return true; 395 } 396 } 397 398 class SelectedArgumentImpl extends ConnectorImpl.ArgumentImpl 399 implements Connector.SelectedArgument { 400 private static final long serialVersionUID = -5689584530908382517L; 401 private final List<String> choices; 402 SelectedArgumentImpl(String name, String label, String description, String value, boolean mustSpecify, List<String> choices)403 SelectedArgumentImpl(String name, String label, String description, 404 String value, 405 boolean mustSpecify, List<String> choices) { 406 super(name, label, description, value, mustSpecify); 407 this.choices = Collections.unmodifiableList(new ArrayList<>(choices)); 408 } 409 410 /** 411 * Return the possible values for the argument 412 * @return {@link List} of {@link String} 413 */ choices()414 public List<String> choices() { 415 return choices; 416 } 417 418 /** 419 * Performs basic sanity check of argument. 420 * @return <code>true</code> if value is one of {@link #choices()}. 421 */ isValid(String value)422 public boolean isValid(String value) { 423 return choices.contains(value); 424 } 425 } 426 } 427