OptionTemplate.java revision 1354:a5e202d6eb99
1/*
2 * Copyright (c) 2010, 2013, 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
26package jdk.nashorn.internal.runtime.options;
27
28import java.util.Locale;
29import java.util.TimeZone;
30import jdk.nashorn.internal.runtime.QuotedStringTokenizer;
31
32/**
33 * This describes the valid input for an option, as read from the resource
34 * bundle file. Metainfo such as parameters and description is here as well
35 * for context sensitive help generation.
36 */
37public final class OptionTemplate implements Comparable<OptionTemplate> {
38    /** Resource, e.g. "nashorn" for this option */
39    private final String resource;
40
41    /** Key in the resource bundle */
42    private final String key;
43
44    /** Is this option a help option? */
45    private final boolean isHelp;
46
47    /** Is this option a extended help option? */
48    private final boolean isXHelp;
49
50    /** Name - for example --dump-on-error (usually prefixed with --) */
51    private String name;
52
53    /** Short name - for example -doe (usually prefixed with -) */
54    private String shortName;
55
56    /** Params - a parameter template string */
57    private String params;
58
59    /** Type - e.g. "boolean". */
60    private String type;
61
62    /** Does this option have a default value? */
63    private String defaultValue;
64
65    /** Does this option activate another option when set? */
66    private String dependency;
67
68    /** Does this option conflict with another? */
69    private String conflict;
70
71    /** Is this a documented option that should show up in help? */
72    private boolean isUndocumented;
73
74    /** A longer description of what this option does */
75    private String description;
76
77    /** is the option value specified as next argument? */
78    private boolean valueNextArg;
79
80    OptionTemplate(final String resource, final String key, final String value, final boolean isHelp, final boolean isXHelp) {
81        this.resource = resource;
82        this.key = key;
83        this.isHelp = isHelp;
84        this.isXHelp = isXHelp;
85        parse(value);
86    }
87
88    /**
89     * Is this the special help option, used to generate help for
90     * all the others
91     *
92     * @return true if this is the help option
93     */
94    public boolean isHelp() {
95        return this.isHelp;
96    }
97
98    /**
99     * Is this the special extended help option, used to generate extended help for
100     * all the others
101     *
102     * @return true if this is the extended help option
103     */
104    public boolean isXHelp() {
105        return this.isXHelp;
106    }
107
108    /**
109     * Get the resource name used to prefix this option set, e.g. "nashorn"
110     *
111     * @return the name of the resource
112     */
113    public String getResource() {
114        return this.resource;
115    }
116
117    /**
118     * Get the type of this option
119     *
120     * @return the type of the option
121     */
122    public String getType() {
123        return this.type;
124    }
125
126    /**
127     * Get the key of this option
128     *
129     * @return the key
130     */
131    public String getKey() {
132        return this.key;
133    }
134
135    /**
136     * Get the default value for this option
137     *
138     * @return the default value as a string
139     */
140    public String getDefaultValue() {
141        switch (getType()) {
142        case "boolean":
143            if (this.defaultValue == null) {
144                this.defaultValue = "false";
145            }
146            break;
147        case "integer":
148            if (this.defaultValue == null) {
149                this.defaultValue = "0";
150            }
151            break;
152        case "timezone":
153            this.defaultValue = TimeZone.getDefault().getID();
154            break;
155        case "locale":
156            this.defaultValue = Locale.getDefault().toLanguageTag();
157            break;
158        default:
159            break;
160        }
161        return this.defaultValue;
162    }
163
164    /**
165     * Does this option automatically enable another option, i.e. a dependency.
166     * @return the dependency or null if none exists
167     */
168    public String getDependency() {
169        return this.dependency;
170    }
171
172    /**
173     * Is this option in conflict with another option so that both can't be enabled
174     * at the same time
175     *
176     * @return the conflicting option or null if none exists
177     */
178    public String getConflict() {
179        return this.conflict;
180    }
181
182    /**
183     * Is this option undocumented, i.e. should not show up in the standard help output
184     *
185     * @return true if option is undocumented
186     */
187    public boolean isUndocumented() {
188        return this.isUndocumented;
189    }
190
191    /**
192     * Get the short version of this option name if one exists, e.g. "-co" for "--compile-only"
193     *
194     * @return the short name
195     */
196    public String getShortName() {
197        return this.shortName;
198    }
199
200    /**
201     * Get the name of this option, e.g. "--compile-only". A name always exists
202     *
203     * @return the name of the option
204     */
205    public String getName() {
206        return this.name;
207    }
208
209    /**
210     * Get the description of this option.
211     *
212     * @return the description
213     */
214    public String getDescription() {
215        return this.description;
216    }
217
218    /**
219     * Is value of this option passed as next argument?
220     * @return boolean
221     */
222    public boolean isValueNextArg() {
223        return valueNextArg;
224    }
225
226    private static String strip(final String value, final char start, final char end) {
227        final int len = value.length();
228        if (len > 1 && value.charAt(0) == start && value.charAt(len - 1) == end) {
229            return value.substring(1, len - 1);
230        }
231        return null;
232    }
233
234    private void parse(final String origValue) {
235        String value = origValue.trim();
236
237        try {
238            value = OptionTemplate.strip(value, '{', '}');
239            final QuotedStringTokenizer keyValuePairs = new QuotedStringTokenizer(value, ",");
240
241            while (keyValuePairs.hasMoreTokens()) {
242                final String                keyValue = keyValuePairs.nextToken();
243                final QuotedStringTokenizer st       = new QuotedStringTokenizer(keyValue, "=");
244                final String                keyToken = st.nextToken();
245                final String                arg      = st.nextToken();
246
247                switch (keyToken) {
248                case "is_undocumented":
249                    this.isUndocumented = Boolean.parseBoolean(arg);
250                    break;
251                case "name":
252                    if (!arg.startsWith("-")) {
253                        throw new IllegalArgumentException(arg);
254                    }
255                    this.name = arg;
256                    break;
257                case "short_name":
258                    if (!arg.startsWith("-")) {
259                        throw new IllegalArgumentException(arg);
260                    }
261                    this.shortName = arg;
262                    break;
263                case "desc":
264                    this.description = arg;
265                    break;
266                case "params":
267                    this.params = arg;
268                    break;
269                case "type":
270                    this.type = arg.toLowerCase(Locale.ENGLISH);
271                    break;
272                case "default":
273                    this.defaultValue = arg;
274                    break;
275                case "dependency":
276                    this.dependency = arg;
277                    break;
278                case "conflict":
279                    this.conflict = arg;
280                    break;
281                case "value_next_arg":
282                    this.valueNextArg = Boolean.parseBoolean(arg);
283                    break;
284                default:
285                    throw new IllegalArgumentException(keyToken);
286                }
287            }
288
289            // default to boolean if no type is given
290            if (this.type == null) {
291                this.type = "boolean";
292            }
293
294            if (this.params == null && "boolean".equals(this.type)) {
295                this.params = "[true|false]";
296            }
297
298        } catch (final Exception e) {
299            throw new IllegalArgumentException(origValue);
300        }
301
302        if (name == null && shortName == null) {
303            throw new IllegalArgumentException(origValue);
304        }
305    }
306
307    boolean matches(final String key0) {
308        return key0.equals(this.shortName) || key0.equals(this.name);
309    }
310
311    private static final int LINE_BREAK = 64;
312
313    @Override
314    public String toString() {
315        final StringBuilder sb = new StringBuilder();
316
317        sb.append('\t');
318
319        if (shortName != null) {
320            sb.append(shortName);
321            if (name != null) {
322                sb.append(", ");
323            }
324        }
325
326        if (name != null) {
327            sb.append(name);
328        }
329
330        if (description != null) {
331            final int indent = sb.length();
332            sb.append(' ');
333            sb.append('(');
334            int pos = 0;
335            for (final char c : description.toCharArray()) {
336                sb.append(c);
337                pos++;
338                if (pos >= LINE_BREAK && Character.isWhitespace(c)) {
339                    pos = 0;
340                    sb.append("\n\t");
341                    for (int i = 0; i < indent; i++) {
342                        sb.append(' ');
343                    }
344                }
345            }
346            sb.append(')');
347        }
348
349        if (params != null) {
350            sb.append('\n');
351            sb.append('\t');
352            sb.append('\t');
353            sb.append(Options.getMsg("nashorn.options.param")).append(": ");
354            sb.append(params);
355            sb.append("   ");
356            final Object def = this.getDefaultValue();
357            if (def != null) {
358                sb.append(Options.getMsg("nashorn.options.default")).append(": ");
359                sb.append(this.getDefaultValue());
360            }
361        }
362
363
364        return sb.toString();
365    }
366
367    @Override
368    public int compareTo(final OptionTemplate o) {
369        return this.getKey().compareTo(o.getKey());
370    }
371}
372