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