/* * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package vm.share.options; import java.lang.reflect.Array; import java.util.HashMap; import java.util.Map; import nsk.share.TestBug; /** * A utility class used to parse arguments of various primitive types. */ public class PrimitiveParser { /** * Checks if the parser can handle passed type ("primitive/wrapper" or one-dim array of those). * Note that one-dim arrays of primitives/strings/wrappers are also supported. * @param type the type to parse against * @return true, if can parse. */ public static boolean canHandle(Class type) { if(type.isArray()) { Class compType = type.getComponentType(); if(compType.isArray()) return false; // Cannot handle multidimensional arrays return canHandle(compType); } type = convertPrimitiveTypeToWrapper(type); return parsers.containsKey(type); } /** * A simple helper method. * @param type the type to check for * @return the parser to use, null if there is none. */ private static PParser getParser(Class type) { return parsers.get(convertPrimitiveTypeToWrapper(type)); } /** * The main API method of this class. * @param param the parameter to parse * @param type parameter type to parse against * @return returns the object of a given type. * @throws vm.share.options.PrimitiveParser.ParserException */ public static Object parse(String param, Class type) throws ParserException { if(type.isArray()) { Class compType = type.getComponentType(); if(compType.isArray()) throw new ParserException("Cannot handle multidimensional arrays"); if(!canHandle(compType)) throw new ParserException("Unable to parse unknown array component type " + compType); String[] params = param.split(","); Object arr = Array.newInstance(compType, params.length); for (int i = 0; i < params.length; i++) { String par = params[i].trim(); Array.set(arr, i, parse(par, compType)); } return arr; } else { if(!canHandle(type)) throw new ParserException("Unable to parse unknown type " + type); return getParser(type).parse(param); } } // I'm not sure, if generics are of any use here... static private abstract class PParser { abstract T parse(String param) throws ParserException; // Class getClassKey() // { // return PrimitiveParser.(Class) T.getClass(); // } } /** * Converts primitive types to corresponding wrapper classes. * We could register int.class, boolean.class etc in the hashtable instead. * (Or Integer.TYPE, etc.) * @param type to convert to wrapper * @return wrapper class or type if it is not primitive */ public static Class convertPrimitiveTypeToWrapper(Class type) { if(!type.isPrimitive()) return type; Object arr = Array.newInstance(type, 1); Object v = Array.get(arr, 0); return v.getClass(); } //"kind of state" machine stuff private static Map, PParser> parsers; static { parsers = new HashMap, PrimitiveParser.PParser>(16); parsers.put(Integer.class, new PParser() { @Override Integer parse(String param) throws ParserException { if ( param.startsWith("0x") ) return Integer.parseInt(param.substring(2)); else return Integer.valueOf(param); } }); parsers.put(Boolean.class, new PParser() { @Override Boolean parse(String param) throws ParserException { //special behavior for options if(param == null) return true; if(param.trim().length()==0) return true; return Boolean.valueOf(param); } }); parsers.put(String.class, new PParser() { @Override String parse(String param) throws ParserException { if(param == null) throw new ParserException(" Got null value string."); return param; } }); parsers.put(Character.class, new PParser() { @Override Character parse(String param) throws ParserException { if(param.length()!=1) throw new TestBug("Found Character type option of length != 1"); return Character.valueOf(param.charAt(0)); } }); parsers.put(Byte.class, new PParser() { @Override Byte parse(String param) throws ParserException { if ( param.startsWith("0x") ) return Byte.parseByte(param.substring(2)); else return Byte.valueOf(param); } }); parsers.put(Short.class, new PParser() { @Override Short parse(String param) throws ParserException { if ( param.startsWith("0x") ) return Short.parseShort(param.substring(2)); else return Short.valueOf(param); } }); parsers.put(Long.class, new PParser() { @Override Long parse(String param) throws ParserException { if ( param.startsWith("0x") ) return Long.parseLong(param.substring(2)); else return Long.valueOf(param); } }); parsers.put(Float.class, new PParser() { @Override Float parse(String param) throws ParserException { return Float.valueOf(param); } }); parsers.put(Double.class, new PParser() { @Override Double parse(String param) throws ParserException { return Double.valueOf(param); } }); } /* Discussion * 1. It was proposed to use instead of the convertPrimitive the following * * private static Map, Class> wrapperClasses = new HashMap, Class>(); * * so we could do if(type.isPrimitive()) type = wrapperClasses.get(type); static { wrapperClasses.put(boolean.class, Boolean.class); wrapperClasses.put(short.class, Short.class); wrapperClasses.put(int.class, Integer.class); wrapperClasses.put(Long.Type, Long.class); // we can do it this way! wrapperClasses.put(float.class, Float.class); wrapperClasses.put(double.class, Double.class); } * The alternative is to register PParsers with corresponding Primitive type too. * * Also canHandle() could use return wrapperClasses.keySet().contains(type) || wrapperClasses.entrySet().contains(type); * 2. Parsing can be implemented via reflection return type.getMethod("valueOf", new Class[]{String.class}).invoke(null, string); * I don't like using reflection as it prevents optimisation, * also now Strings and Characters are handled in a nice fashion. * * As for convertToPrimitive trick both ways are good, * but current looks more generic though tricky */ //// some test, should it be commented out? // public static void main(String[] args) // { // try // { // String str = "0"; // Object o = null; // str = "0"; // o = parse(str, String.class); // System.out.println("value:" + str + " type: " + o.getClass() + " value:#" + o + "#"); // // str = "0"; // o = parse(str, int.class); // System.out.println("value:" + str + " type: " + o.getClass() + " value:#" + o + "#"); // str = "0"; // o = parse(str, Integer.class); // System.out.println("value:" + str + " type: " + o.getClass() + " value:#" + o + "#"); // // str = "0,1,2"; // o = parse(str, int[].class); // System.out.println("value:" + str + " type: " + o.getClass() + " value:#" + (int[]) o + "#"); // System.out.println("DATA:" + java.util.Arrays.toString((int[])o)); // // System.out.println("DATA:" + java.util.Arrays.deepToString( (int[]) o)); // // str = "0"; // o = parse(str, byte.class); // System.out.println("value:" + str + " type: " + o.getClass() + " value:#" + o + "#"); // // str = "0"; // o = parse(str, HashMap.class); // System.out.println("value:" + str + " type: " + o.getClass() + " value:#" + o + "#"); //// String str = "0"; Object o = parsePrimitiveString(help_option, type); System.out.println("value:" + str + " type: " + o.getClass() + " value:#" + o + "#"); // } catch (ParserException ex) // { // System.out.println("" +ex); // } //// String str = "0"; Object o = parsePrimitiveString(help_option, type); System.out.println("value:" + str + " type: " + o.getClass() + " value:#" + o + "#"); // // } }