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()} &lt;= value &lt;= {@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()} &lt;= value  &lt;= {@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