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 
  26 package jdk.nashorn.internal.runtime.options;
  27 
  28 import java.util.Locale;
  29 import java.util.TimeZone;
  30 import 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  */
  37 public 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         default:
 156             break;
 157         }
 158         return this.defaultValue;
 159     }
 160 
 161     /**
 162      * Does this option automatically enable another option, i.e. a dependency.
 163      * @return the dependecy or null if non exists
 164      */
 165     public String getDependency() {
 166         return this.dependency;
 167     }
 168 
 169     /**
 170      * Is this option in conflict with another option so that both can't be enabled
 171      * at the same time
 172      *
 173      * @return the conflicting option or null if none exists
 174      */
 175     public String getConflict() {
 176         return this.conflict;
 177     }
 178 
 179     /**
 180      * Is this option undocumented, i.e. should not show up in the standard help output
 181      *
 182      * @return true if option is undocumented
 183      */
 184     public boolean isUndocumented() {
 185         return this.isUndocumented;
 186     }
 187 
 188     /**
 189      * Get the short version of this option name if one exists, e.g. "-co" for "--compile-only"
 190      *
 191      * @return the short name
 192      */
 193     public String getShortName() {
 194         return this.shortName;
 195     }
 196 
 197     /**
 198      * Get the name of this option, e.g. "--compile-only". A name always exists
 199      *
 200      * @return the name of the option
 201      */
 202     public String getName() {
 203         return this.name;
 204     }
 205 
 206     /**
 207      * Get the description of this option.
 208      *
 209      * @return the description
 210      */
 211     public String getDescription() {
 212         return this.description;
 213     }
 214 
 215     /**
 216      * Is value of this option passed as next argument?
 217      * @return boolean
 218      */
 219     public boolean isValueNextArg() {
 220         return valueNextArg;
 221     }
 222 
 223     private static String strip(final String value, final char start, final char end) {
 224         final int len = value.length();
 225         if (len > 1 && value.charAt(0) == start && value.charAt(len - 1) == end) {
 226             return value.substring(1, len - 1);
 227         }
 228         return null;
 229     }
 230 
 231     private void parse(final String origValue) {
 232         String value = origValue.trim();
 233 
 234         try {
 235             value = OptionTemplate.strip(value, '{', '}');
 236             final QuotedStringTokenizer keyValuePairs = new QuotedStringTokenizer(value, ",");
 237 
 238             while (keyValuePairs.hasMoreTokens()) {
 239                 final String                keyValue = keyValuePairs.nextToken();
 240                 final QuotedStringTokenizer st       = new QuotedStringTokenizer(keyValue, "=");
 241                 final String                keyToken = st.nextToken();
 242                 final String                arg      = st.nextToken();
 243 
 244                 switch (keyToken) {
 245                 case "is_undocumented":
 246                     this.isUndocumented = Boolean.parseBoolean(arg);
 247                     break;
 248                 case "name":
 249                     if (!arg.startsWith("-")) {
 250                         throw new IllegalArgumentException(arg);
 251                     }
 252                     this.name = arg;
 253                     break;
 254                 case "short_name":
 255                     if (!arg.startsWith("-")) {
 256                         throw new IllegalArgumentException(arg);
 257                     }
 258                     this.shortName = arg;
 259                     break;
 260                 case "desc":
 261                     this.description = arg;
 262                     break;
 263                 case "params":
 264                     this.params = arg;
 265                     break;
 266                 case "type":
 267                     this.type = arg.toLowerCase(Locale.ROOT);
 268                     break;
 269                 case "default":
 270                     this.defaultValue = arg;
 271                     break;
 272                 case "dependency":
 273                     this.dependency = arg;
 274                     break;
 275                 case "conflict":
 276                     this.conflict = arg;
 277                     break;
 278                 case "value_next_arg":
 279                     this.valueNextArg = Boolean.parseBoolean(arg);
 280                     break;
 281                 default:
 282                     throw new IllegalArgumentException(keyToken);
 283                 }
 284             }
 285 
 286             // default to boolean if no type is given
 287             if (this.type == null) {
 288                 this.type = "boolean";
 289             }
 290 
 291             if (this.params == null && "boolean".equals(this.type)) {
 292                 this.params = "[true|false]";
 293             }
 294 
 295         } catch (final Exception e) {
 296             throw new IllegalArgumentException(origValue);
 297         }
 298 
 299         if (name == null && shortName == null) {
 300             throw new IllegalArgumentException(origValue);
 301         }
 302     }
 303 
 304     boolean matches(final String key0) {
 305         return key0.equals(this.shortName) || key0.equals(this.name);
 306     }
 307 
 308     private static final int LINE_BREAK = 64;
 309 
 310     @Override
 311     public String toString() {
 312         final StringBuilder sb = new StringBuilder();
 313 
 314         sb.append('\t');
 315 
 316         if (shortName != null) {
 317             sb.append(shortName);
 318             if (name != null) {
 319                 sb.append(", ");
 320             }
 321         }
 322 
 323         if (name != null) {
 324             sb.append(name);
 325         }
 326 
 327         if (description != null) {
 328             final int indent = sb.length();
 329             sb.append(' ');
 330             sb.append('(');
 331             int pos = 0;
 332             for (final char c : description.toCharArray()) {
 333                 sb.append(c);
 334                 pos++;
 335                 if (pos >= LINE_BREAK && Character.isWhitespace(c)) {
 336                     pos = 0;
 337                     sb.append("\n\t");
 338                     for (int i = 0; i < indent; i++) {
 339                         sb.append(' ');
 340                     }
 341                 }
 342             }
 343             sb.append(')');
 344         }
 345 
 346         if (params != null) {
 347             sb.append('\n');
 348             sb.append('\t');
 349             sb.append('\t');
 350             sb.append(Options.getMsg("nashorn.options.param")).append(": ");
 351             sb.append(params);
 352             sb.append("   ");
 353             final Object def = this.getDefaultValue();
 354             if (def != null) {
 355                 sb.append(Options.getMsg("nashorn.options.default")).append(": ");
 356                 sb.append(this.getDefaultValue());
 357             }
 358         }
 359 
 360 
 361         return sb.toString();
 362     }
 363 
 364     @Override
 365     public int compareTo(final OptionTemplate o) {
 366         return this.getKey().compareTo(o.getKey());
 367     }
 368 }
--- EOF ---