org.jolokia.converter.object.StringToObjectConverter.java Source code

Java tutorial

Introduction

Here is the source code for org.jolokia.converter.object.StringToObjectConverter.java

Source

package org.jolokia.converter.object;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;

import javax.management.ObjectName;

import org.jolokia.util.*;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.ParseException;

/*
 * Copyright 2009-2013 Roland Huss
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Converter from a string representation to its Java object form
 * @author roland
 * @since Jun 11, 2009
 */
public class StringToObjectConverter {

    private static final Map<String, Parser> PARSER_MAP = new HashMap<String, Parser>();
    private static final Map<String, Class> TYPE_SIGNATURE_MAP = new HashMap<String, Class>();

    static {
        PARSER_MAP.put(Byte.class.getName(), new ByteParser());
        PARSER_MAP.put("byte", new ByteParser());
        PARSER_MAP.put(Integer.class.getName(), new IntParser());
        PARSER_MAP.put("int", new IntParser());
        PARSER_MAP.put(Long.class.getName(), new LongParser());
        PARSER_MAP.put("long", new LongParser());
        PARSER_MAP.put(Short.class.getName(), new ShortParser());
        PARSER_MAP.put("short", new ShortParser());
        PARSER_MAP.put(Double.class.getName(), new DoubleParser());
        PARSER_MAP.put("double", new DoubleParser());
        PARSER_MAP.put(Float.class.getName(), new FloatParser());
        PARSER_MAP.put("float", new FloatParser());
        PARSER_MAP.put(BigDecimal.class.getName(), new BigDecimalParser());
        PARSER_MAP.put(BigInteger.class.getName(), new BigIntegerParser());

        PARSER_MAP.put(Boolean.class.getName(), new BooleanParser());
        PARSER_MAP.put("boolean", new BooleanParser());
        PARSER_MAP.put("char", new CharParser());
        PARSER_MAP.put(Character.class.getName(), new CharParser());
        PARSER_MAP.put(String.class.getName(), new StringParser());
        PARSER_MAP.put(Date.class.getName(), new DateParser());
        PARSER_MAP.put(ObjectName.class.getName(), new ObjectNameParser());
        PARSER_MAP.put(URL.class.getName(), new URLParser());

        JSONParser jsonExtractor = new JSONParser();
        for (Class type : new Class[] { Map.class, List.class, JSONObject.class, JSONArray.class }) {
            PARSER_MAP.put(type.getName(), jsonExtractor);
        }

        TYPE_SIGNATURE_MAP.put("Z", boolean.class);
        TYPE_SIGNATURE_MAP.put("B", byte.class);
        TYPE_SIGNATURE_MAP.put("C", char.class);
        TYPE_SIGNATURE_MAP.put("S", short.class);
        TYPE_SIGNATURE_MAP.put("I", int.class);
        TYPE_SIGNATURE_MAP.put("J", long.class);
        TYPE_SIGNATURE_MAP.put("F", float.class);
        TYPE_SIGNATURE_MAP.put("D", double.class);
    }

    /**
     * Prepare a value from a either a given object or its string representation.
     * If the value is already assignable to the given class name it is returned directly.
     *
     * @param pExpectedClassName type name of the expected type
     * @param pValue value to either take directly or to convert from its string representation.
     * @return the prepared / converted object
     */
    public Object prepareValue(String pExpectedClassName, Object pValue) {
        if (pValue == null) {
            return null;
        } else {
            Class expectedClass = ClassUtil.classForName(pExpectedClassName);
            Object param = null;
            if (expectedClass != null) {
                param = prepareValue(expectedClass, pValue);
            }
            if (param == null) {
                // Ok, we try to convert it from a string
                // If expectedClass is null, it is probably a native type, so we
                // let happen the string conversion
                // later on (e.g. conversion of pArgument.toString()) which will throw
                // an exception at this point if conversion can not be done

                return convertFromString(pExpectedClassName, pValue.toString());
            }
            return param;
        }
    }

    // Extract a type version of the method above. This might be useful later
    // on, e.g. when setting enums should be supported for certain
    // use cases
    private Object prepareValue(Class expectedClass, Object pValue) {
        if (pValue == null) {
            return null;
        }
        if (Enum.class.isAssignableFrom(expectedClass)) {
            return Enum.valueOf(expectedClass, pValue.toString());
        } else {
            return prepareForDirectUsage(expectedClass, pValue);
        }
    }

    /**
     * For GET requests, where operation arguments and values to write are given in
     * string representation as part of the URL, certain special tags are used to indicate
     * special values:
     *
     * <ul>
     *    <li><code>[null]</code> for indicating a null value</li>
     *    <li><code>""</code> for indicating an empty string</li>
     * </ul>
     *
     * This method converts these tags to the proper value. If not a tag, the original
     * value is returned.
     *
     * If you need this tag values in the original semantics, please use POST requests.
     *
     * @param pValue the string value to check for a tag
     * @return the converted value or the original one if no tag has been found.
     */
    public static String convertSpecialStringTags(String pValue) {
        if ("[null]".equals(pValue)) {
            // Null marker for get requests
            return null;
        } else if ("\"\"".equals(pValue)) {
            // Special string value for an empty String
            return "";
        } else {
            return pValue;
        }
    }

    // ======================================================================================================

    // Check whether an argument can be used directly 
    // or the argument could be used in a public constructor
    // or whether it needs some sort of conversion, 
    // Returns null if a string conversion should happen
    private Object prepareForDirectUsage(Class expectedClass, Object pArgument) {
        Class givenClass = pArgument.getClass();
        if (expectedClass.isArray() && List.class.isAssignableFrom(givenClass)) {
            return convertListToArray(expectedClass, (List) pArgument);
        } else {
            return expectedClass.isAssignableFrom(givenClass) ? pArgument : null;
        }
    }

    private Object convertByConstructor(String pType, String pValue) {
        Class<?> expectedClass = ClassUtil.classForName(pType);
        if (expectedClass != null) {
            for (Constructor<?> constructor : expectedClass.getConstructors()) {
                // only support only 1 constructor parameter
                if (constructor.getParameterTypes().length == 1
                        && constructor.getParameterTypes()[0].isAssignableFrom(String.class)) {
                    try {
                        return constructor.newInstance(pValue);
                    } catch (Exception ignore) {
                    }
                }
            }
        }

        return null;
    }

    /**
     * Deserialize a string representation to an object for a given type
     *
     * @param pType type to convert to
     * @param pValue the value to convert from
     * @return the converted value
     */
    public Object convertFromString(String pType, String pValue) {
        String value = convertSpecialStringTags(pValue);

        if (value == null) {
            return null;
        }
        if (pType.startsWith("[") && pType.length() >= 2) {
            return convertToArray(pType, value);
        }

        Parser parser = PARSER_MAP.get(pType);
        if (parser != null) {
            return parser.extract(value);
        }

        Object cValue = convertByConstructor(pType, pValue);
        if (cValue != null) {
            return cValue;
        }

        throw new IllegalArgumentException(
                "Cannot convert string " + value + " to type " + pType + " because no converter could be found");
    }

    // Convert an array
    private Object convertToArray(String pType, String pValue) {
        // It's an array
        String t = pType.substring(1, 2);
        Class valueType;
        if (t.equals("L")) {
            // It's an object-type
            String oType = pType.substring(2, pType.length() - 1).replace('/', '.');
            valueType = ClassUtil.classForName(oType);
            if (valueType == null) {
                throw new IllegalArgumentException("No class of type " + oType + "found");
            }
        } else {
            valueType = TYPE_SIGNATURE_MAP.get(t);
            if (valueType == null) {
                throw new IllegalArgumentException("Cannot convert to unknown array type " + t);
            }
        }
        String[] values = EscapeUtil.splitAsArray(pValue, EscapeUtil.PATH_ESCAPE, ",");
        Object ret = Array.newInstance(valueType, values.length);
        int i = 0;
        for (String value : values) {
            Array.set(ret, i++,
                    value.equals("[null]") ? null : convertFromString(valueType.getCanonicalName(), value));
        }
        return ret;
    }

    // Convert a list to an array of the given type
    private Object convertListToArray(Class pType, List pList) {
        Class valueType = pType.getComponentType();
        Object ret = Array.newInstance(valueType, pList.size());
        int i = 0;
        for (Object value : pList) {
            if (value == null) {
                if (!valueType.isPrimitive()) {
                    Array.set(ret, i++, null);
                } else {
                    throw new IllegalArgumentException(
                            "Cannot use a null value in an array of type " + valueType.getSimpleName());
                }
            } else {
                if (valueType.isAssignableFrom(value.getClass())) {
                    // Can be set directly
                    Array.set(ret, i++, value);
                } else {
                    // Try to convert from string
                    Array.set(ret, i++, convertFromString(valueType.getCanonicalName(), value.toString()));
                }
            }
        }
        return ret;
    }

    // ===========================================================================
    // Extractor interface
    private interface Parser {
        /**
         * Extract a particular string value
         * @param pValue value to extract
         * @return the extracted value
         */
        Object extract(String pValue);
    }

    private static class StringParser implements Parser {
        /** {@inheritDoc} */
        public Object extract(String pValue) {
            return pValue;
        }
    }

    private static class IntParser implements Parser {
        /** {@inheritDoc} */
        public Object extract(String pValue) {
            return Integer.parseInt(pValue);
        }
    }

    private static class LongParser implements Parser {
        /** {@inheritDoc} */
        public Object extract(String pValue) {
            return Long.parseLong(pValue);
        }
    }

    private static class BooleanParser implements Parser {
        /** {@inheritDoc} */
        public Object extract(String pValue) {
            return Boolean.parseBoolean(pValue);
        }
    }

    private static class DoubleParser implements Parser {
        /** {@inheritDoc} */
        public Object extract(String pValue) {
            return Double.parseDouble(pValue);
        }
    }

    private static class FloatParser implements Parser {
        /** {@inheritDoc} */
        public Object extract(String pValue) {
            return Float.parseFloat(pValue);
        }
    }

    private static class ByteParser implements Parser {
        /** {@inheritDoc} */
        public Object extract(String pValue) {
            return Byte.parseByte(pValue);
        }
    }

    private static class CharParser implements Parser {
        /** {@inheritDoc} */
        public Object extract(String pValue) {
            return pValue.charAt(0);
        }
    }

    private static class ShortParser implements Parser {
        /** {@inheritDoc} */
        public Object extract(String pValue) {
            return Short.parseShort(pValue);
        }
    }

    private static class BigDecimalParser implements Parser {
        /** {@inheritDoc} */
        public Object extract(String pValue) {
            return new BigDecimal(pValue);
        }
    }

    private static class BigIntegerParser implements Parser {
        /** {@inheritDoc} */
        public Object extract(String pValue) {
            return new BigInteger(pValue);
        }
    }

    private static class DateParser implements Parser {

        /** {@inheritDoc} */
        public Object extract(String pValue) {
            long time;
            try {
                time = Long.parseLong(pValue);
                return new Date(time);
            } catch (NumberFormatException exp) {
                return DateUtil.fromISO8601(pValue);
            }
        }
    }

    private static class JSONParser implements Parser {

        /** {@inheritDoc} */
        public Object extract(String pValue) {
            try {
                return new org.json.simple.parser.JSONParser().parse(pValue);
            } catch (ParseException e) {
                throw new IllegalArgumentException("Cannot parse JSON " + pValue + ": " + e, e);
            }
        }
    }

    private static class ObjectNameParser implements Parser {

        /** {@inheritDoc} */
        public Object extract(String pValue) {
            try {
                return new javax.management.ObjectName(pValue);
            } catch (javax.management.MalformedObjectNameException e) {
                throw new IllegalArgumentException("Cannot parse ObjectName " + pValue + ": " + e, e);
            }
        }
    }

    private static class URLParser implements Parser {

        /** {@inheritDoc} */
        public Object extract(String pValue) {
            try {
                return new URL(pValue);
            } catch (MalformedURLException e) {
                throw new IllegalArgumentException("Cannot parse URL " + pValue + ": " + e, e);
            }
        }
    }
}