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 
defaultArguments()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 
addStringArgument(String name, String label, String description, String defaultValue, boolean mustSpecify)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 
addBooleanArgument(String name, String label, String description, boolean defaultValue, boolean mustSpecify)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 
addIntegerArgument(String name, String label, String description, String defaultValue, boolean mustSpecify, int min, int max)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 
addSelectedArgument(String name, String label, String description, String defaultValue, boolean mustSpecify, List<String> list)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 
argument(String name, Map<String, ? extends Argument> arguments)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 
getString(String key)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 
toString()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 
ArgumentImpl(String name, String label, String description, String value, boolean mustSpecify)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 
isValid(String value)164         public abstract boolean isValid(String value);
165 
name()166         public String name() {
167             return name;
168         }
169 
label()170         public String label() {
171             return label;
172         }
173 
description()174         public String description() {
175             return description;
176         }
177 
value()178         public String value() {
179             return value;
180         }
181 
setValue(String value)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 
mustSpecify()189         public boolean mustSpecify() {
190             return mustSpecify;
191         }
192 
equals(Object obj)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 
hashCode()205         public int hashCode() {
206             return description().hashCode();
207         }
208 
clone()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 
toString()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;
BooleanArgumentImpl(String name, String label, String description, boolean value, boolean mustSpecify)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          */
setValue(boolean value)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          */
isValid(String value)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          */
stringValueOf(boolean value)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          */
booleanValue()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 
IntegerArgumentImpl(String name, String label, String description, String value, boolean mustSpecify, int min, int max)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          */
setValue(int value)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          */
isValid(String value)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          */
isValid(int value)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          */
stringValueOf(int value)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          */
intValue()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          */
max()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          */
min()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;
StringArgumentImpl(String name, String label, String description, String value, boolean mustSpecify)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          */
isValid(String value)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 
SelectedArgumentImpl(String name, String label, String description, String value, boolean mustSpecify, List<String> choices)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          */
choices()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          */
isValid(String value)421         public boolean isValid(String value) {
422             return choices.contains(value);
423         }
424     }
425 }
426