1 /*
   2  * Copyright (c) 2015, 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 package jdk.tools.jimage;
  26 
  27 import java.io.PrintWriter;
  28 import java.text.MessageFormat;
  29 import java.util.ArrayList;
  30 import java.util.List;
  31 import java.util.Locale;
  32 import java.util.MissingResourceException;
  33 import java.util.ResourceBundle;
  34 
  35 /**
  36  *
  37  * JImage tools shared helper.
  38  */
  39 public final class TaskHelper {
  40 
  41     public class BadArgs extends Exception {
  42 
  43         static final long serialVersionUID = 8765093759964640721L;
  44 
  45         private BadArgs(String key, Object... args) {
  46             super(bundleHelper.getMessage(key, args));
  47             this.key = key;
  48             this.args = args;
  49         }
  50 
  51         public BadArgs showUsage(boolean b) {
  52             showUsage = b;
  53             return this;
  54         }
  55         public final String key;
  56         public final Object[] args;
  57         public boolean showUsage;
  58     }
  59 
  60     public static abstract class Option<T> {
  61 
  62         final boolean hasArg;
  63         final String[] aliases;
  64 
  65         public Option(boolean hasArg, String... aliases) {
  66             this.hasArg = hasArg;
  67             this.aliases = aliases;
  68         }
  69 
  70         public boolean isHidden() {
  71             return false;
  72         }
  73 
  74         public boolean matches(String opt) {
  75             for (String a : aliases) {
  76                 if (a.equals(opt)) {
  77                     return true;
  78                 } else if (opt.startsWith("--") && hasArg && opt.startsWith(a + "=")) {
  79                     return true;
  80                 }
  81             }
  82             return false;
  83         }
  84 
  85         public boolean ignoreRest() {
  86             return false;
  87         }
  88 
  89         protected abstract void process(T task, String opt, String arg) throws BadArgs;
  90     }
  91 
  92     public static abstract class HiddenOption<T> extends Option<T> {
  93 
  94         public HiddenOption(boolean hasArg, String... aliases) {
  95             super(hasArg, aliases);
  96         }
  97 
  98         @Override
  99         public boolean isHidden() {
 100             return true;
 101         }
 102     }
 103 
 104     private class ResourceBundleHelper {
 105 
 106         private final ResourceBundle bundle;
 107 
 108         ResourceBundleHelper(String path) {
 109             Locale locale = Locale.getDefault();
 110             try {
 111                 bundle = ResourceBundle.getBundle(path, locale);
 112             } catch (MissingResourceException e) {
 113                 throw new InternalError("Cannot find resource bundle for locale " + locale);
 114             }
 115         }
 116 
 117         String getMessage(String key, Object... args) {
 118             String val = bundle.getString(key);
 119             return MessageFormat.format(val, args);
 120         }
 121 
 122     }
 123 
 124     public class OptionsHelper<T> {
 125 
 126         private final List<Option<T>> options;
 127 
 128         OptionsHelper(List<Option<T>> options) {
 129             this.options = options;
 130         }
 131 
 132         public List<String> handleOptions(T task, String[] args) throws BadArgs {
 133             List<String> rest = new ArrayList<>();
 134             // process options
 135             for (int i = 0; i < args.length; i++) {
 136                 if (args[i].charAt(0) == '-') {
 137                     String name = args[i];
 138                     Option<T> option = getOption(name);
 139                     if (option == null) {
 140                         throw new BadArgs("err.unknown.option", name).showUsage(true);
 141                     }
 142                     String param = null;
 143                     if (option.hasArg) {
 144                         if (name.startsWith("--") && name.indexOf('=') > 0) {
 145                             param = name.substring(name.indexOf('=') + 1, name.length());
 146                         } else if (i + 1 < args.length) {
 147                             param = args[++i];
 148                         }
 149                         if (param == null || param.isEmpty() || param.charAt(0) == '-') {
 150                             throw new BadArgs("err.missing.arg", name).showUsage(true);
 151                         }
 152                     }
 153                     option.process(task, name, param);
 154                     if (option.ignoreRest()) {
 155                         i = args.length;
 156                     }
 157                 } else {
 158                     rest.add(args[i]);
 159                 }
 160             }
 161             return rest;
 162         }
 163 
 164         private Option<T> getOption(String name) throws BadArgs {
 165             for (Option<T> o : options) {
 166                 if (o.matches(name)) {
 167                     return o;
 168                 }
 169             }
 170             return null;
 171         }
 172 
 173         public void showHelp(String progName, String pluginsHeader) {
 174             log.println(bundleHelper.getMessage("main.usage", progName));
 175             for (Option<?> o : options) {
 176                 String name = o.aliases[0].substring(1); // there must always be at least one name
 177                 name = name.charAt(0) == '-' ? name.substring(1) : name;
 178                 if (o.isHidden() || name.equals("h")) {
 179                     continue;
 180                 }
 181                 log.println(bundleHelper.getMessage("main.opt." + name));
 182             }
 183         }
 184     }
 185 
 186     private PrintWriter log;
 187     private final ResourceBundleHelper bundleHelper;
 188 
 189     public TaskHelper(String path) {
 190         this.bundleHelper = new ResourceBundleHelper(path);
 191     }
 192 
 193     public <T> OptionsHelper<T> newOptionsHelper(Class<T> clazz, Option<?>[] options) {
 194         List<Option<T>> optionsList = new ArrayList<>();
 195         for (Option<?> o : options) {
 196             @SuppressWarnings("unchecked")
 197             Option<T> opt = (Option<T>) o;
 198             optionsList.add(opt);
 199         }
 200         return new OptionsHelper<>(optionsList);
 201     }
 202 
 203     public BadArgs newBadArgs(String key, Object... args) {
 204         return new BadArgs(key, args);
 205     }
 206 
 207     public String getMessage(String key, Object... args) {
 208         return bundleHelper.getMessage(key, args);
 209     }
 210 
 211     public void setLog(PrintWriter log) {
 212         this.log = log;
 213     }
 214 
 215     public void reportError(String key, Object... args) {
 216         log.println(bundleHelper.getMessage("error.prefix") + " " + bundleHelper.getMessage(key, args));
 217     }
 218 
 219     public void warning(String key, Object... args) {
 220         log.println(bundleHelper.getMessage("warn.prefix") + " " + bundleHelper.getMessage(key, args));
 221     }
 222 
 223     public void showVersion(boolean full) {
 224         log.println(version(full ? "full" : "release"));
 225     }
 226 
 227     public String version(String key) {
 228         return System.getProperty("java.version");
 229     }
 230 
 231 }