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     @SuppressWarnings("serial") // JDK implementation class
 148     abstract class ArgumentImpl implements Connector.Argument, Cloneable, Serializable {
 149         private String name;
 150         private String label;
 151         private String description;
 152         private String value;
 153         private boolean mustSpecify;
 154 
 155         ArgumentImpl(String name, String label, String description,
 156                      String value,
 157                      boolean mustSpecify) {
 158             this.name = name;
 159             this.label = label;
 160             this.description = description;
 161             this.value = value;
 162             this.mustSpecify = mustSpecify;
 163         }
 164 
 165         public abstract boolean isValid(String value);
 166 
 167         public String name() {
 168             return name;
 169         }
 170 
 171         public String label() {
 172             return label;
 173         }
 174 
 175         public String description() {
 176             return description;
 177         }
 178 
 179         public String value() {
 180             return value;
 181         }
 182 
 183         public void setValue(String value) {
 184             if (value == null) {
 185                 throw new NullPointerException("Can't set null value");
 186             }
 187             this.value = value;
 188         }
 189 
 190         public boolean mustSpecify() {
 191             return mustSpecify;
 192         }
 193 
 194         public boolean equals(Object obj) {
 195             if ((obj != null) && (obj instanceof Connector.Argument)) {
 196                 Connector.Argument other = (Connector.Argument)obj;
 197                 return (name().equals(other.name())) &&
 198                        (description().equals(other.description())) &&
 199                        (mustSpecify() == other.mustSpecify()) &&
 200                        (value().equals(other.value()));
 201             } else {
 202                 return false;
 203             }
 204         }
 205 
 206         public int hashCode() {
 207             return description().hashCode();
 208         }
 209 
 210         public Object clone() {
 211             try {
 212                 return super.clone();
 213             } catch (CloneNotSupportedException e) {
 214                 // Object should always support clone
 215                 throw new InternalException();
 216             }
 217         }
 218 
 219         public String toString() {
 220             return name() + "=" + value();
 221         }
 222     }
 223 
 224     class BooleanArgumentImpl extends ConnectorImpl.ArgumentImpl
 225                               implements Connector.BooleanArgument {
 226         private static final long serialVersionUID = 1624542968639361316L;
 227         BooleanArgumentImpl(String name, String label, String description,
 228                             boolean value,
 229                             boolean mustSpecify) {
 230             super(name, label, description, null, mustSpecify);
 231             if(trueString == null) {
 232                 trueString = getString("true");
 233                 falseString = getString("false");
 234             }
 235             setValue(value);
 236         }
 237 
 238         /**
 239          * Sets the value of the argument.
 240          */
 241         public void setValue(boolean value) {
 242             setValue(stringValueOf(value));
 243         }
 244 
 245         /**
 246          * Performs basic sanity check of argument.
 247          * @return <code>true</code> if value is a string
 248          * representation of a boolean value.
 249          * @see #stringValueOf(boolean)
 250          */
 251         public boolean isValid(String value) {
 252             return value.equals(trueString) || value.equals(falseString);
 253         }
 254 
 255         /**
 256          * Return the string representation of the <code>value</code>
 257          * parameter.
 258          * Does not set or examine the value or the argument.
 259          * @return the localized String representation of the
 260          * boolean value.
 261          */
 262         public String stringValueOf(boolean value) {
 263             return value? trueString : falseString;
 264         }
 265 
 266         /**
 267          * Return the value of the argument as a boolean.  Since
 268          * the argument may not have been set or may have an invalid
 269          * value {@link #isValid(String)} should be called on
 270          * {@link #value()} to check its validity.  If it is invalid
 271          * the boolean returned by this method is undefined.
 272          * @return the value of the argument as a boolean.
 273          */
 274         public boolean booleanValue() {
 275             return value().equals(trueString);
 276         }
 277     }
 278 
 279     class IntegerArgumentImpl extends ConnectorImpl.ArgumentImpl
 280                               implements Connector.IntegerArgument {
 281         private static final long serialVersionUID = 763286081923797770L;
 282         private final int min;
 283         private final int max;
 284 
 285         IntegerArgumentImpl(String name, String label, String description,
 286                             String value,
 287                             boolean mustSpecify, int min, int max) {
 288             super(name, label, description, value, mustSpecify);
 289             this.min = min;
 290             this.max = max;
 291         }
 292 
 293         /**
 294          * Sets the value of the argument.
 295          * The value should be checked with {@link #isValid(int)}
 296          * before setting it; invalid values will throw an exception
 297          * when the connection is established - for example,
 298          * on {@link LaunchingConnector#launch}
 299          */
 300         public void setValue(int value) {
 301             setValue(stringValueOf(value));
 302         }
 303 
 304         /**
 305          * Performs basic sanity check of argument.
 306          * @return <code>true</code> if value represents an int that is
 307          * <code>{@link #min()} &lt;= value &lt;= {@link #max()}</code>
 308          */
 309         public boolean isValid(String value) {
 310             if (value == null) {
 311                 return false;
 312             }
 313             try {
 314                 return isValid(Integer.decode(value).intValue());
 315             } catch(NumberFormatException exc) {
 316                 return false;
 317             }
 318         }
 319 
 320         /**
 321          * Performs basic sanity check of argument.
 322          * @return <code>true</code> if
 323          * <code>{@link #min()} &lt;= value  &lt;= {@link #max()}</code>
 324          */
 325         public boolean isValid(int value) {
 326             return min <= value && value <= max;
 327         }
 328 
 329         /**
 330          * Return the string representation of the <code>value</code>
 331          * parameter.
 332          * Does not set or examine the value or the argument.
 333          * @return the String representation of the
 334          * int value.
 335          */
 336         public String stringValueOf(int value) {
 337             // *** Should this be internationalized????
 338             // *** Even Brian Beck was unsure if an Arabic programmer
 339             // *** would expect port numbers in Arabic numerals,
 340             // *** so punt for now.
 341             return ""+value;
 342         }
 343 
 344         /**
 345          * Return the value of the argument as a int.  Since
 346          * the argument may not have been set or may have an invalid
 347          * value {@link #isValid(String)} should be called on
 348          * {@link #value()} to check its validity.  If it is invalid
 349          * the int returned by this method is undefined.
 350          * @return the value of the argument as a int.
 351          */
 352         public int intValue() {
 353             if (value() == null) {
 354                 return 0;
 355             }
 356             try {
 357                 return Integer.decode(value()).intValue();
 358             } catch(NumberFormatException exc) {
 359                 return 0;
 360             }
 361         }
 362 
 363         /**
 364          * The upper bound for the value.
 365          * @return the maximum allowed value for this argument.
 366          */
 367         public int max() {
 368             return max;
 369         }
 370 
 371         /**
 372          * The lower bound for the value.
 373          * @return the minimum allowed value for this argument.
 374          */
 375         public int min() {
 376             return min;
 377         }
 378     }
 379 
 380     class StringArgumentImpl extends ConnectorImpl.ArgumentImpl
 381                               implements Connector.StringArgument {
 382         private static final long serialVersionUID = 7500484902692107464L;
 383         StringArgumentImpl(String name, String label, String description,
 384                            String value,
 385                            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          */
 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 
 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<String>(choices));
 408         }
 409 
 410         /**
 411          * Return the possible values for the argument
 412          * @return {@link List} of {@link String}
 413          */
 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          */
 422         public boolean isValid(String value) {
 423             return choices.contains(value);
 424         }
 425     }
 426 }