All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.shaded.nonapi.io.github.classgraph.json.JSONUtils Maven / Gradle / Ivy

The newest version!
/*
 * This file is part of ClassGraph.
 *
 * Author: Luke Hutchison
 *
 * Hosted at: https://github.com/classgraph/classgraph
 *
 * --
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2019 Luke Hutchison
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without
 * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
 * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
 * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
 * OR OTHER DEALINGS IN THE SOFTWARE.
 */
package com.hazelcast.shaded.nonapi.io.github.classgraph.json;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.concurrent.Callable;

import com.hazelcast.shaded.nonapi.io.github.classgraph.reflection.ReflectionUtils;

/** Utils for Java serialization and deserialization. */
public final class JSONUtils {
    private static Method isAccessibleMethod;
    private static Method setAccessibleMethod;
    private static Method trySetAccessibleMethod;

    static {
        // Find deprecated methods isAccessible/setAccessible, to remove compile-time warnings
        // TODO Switch to using  MethodHandles once this is fixed:
        // https://github.com/mojohaus/animal-sniffer/issues/67
        try {
            isAccessibleMethod = AccessibleObject.class.getDeclaredMethod("isAccessible");
        } catch (final Throwable t) {
            // Ignore
        }
        try {
            setAccessibleMethod = AccessibleObject.class.getDeclaredMethod("setAccessible", boolean.class);
        } catch (final Throwable t) {
            // Ignore
        }
        try {
            trySetAccessibleMethod = AccessibleObject.class.getDeclaredMethod("trySetAccessible");
        } catch (final Throwable t) {
            // Ignore
        }
    }

    private static boolean isAccessible(final AccessibleObject obj) {
        if (isAccessibleMethod != null) {
            // JDK 7/8: use isAccessible (deprecated in JDK 9+)
            try {
                if ((Boolean) isAccessibleMethod.invoke(obj)) {
                    return true;
                }
            } catch (final Throwable e) {
                // Ignore
            }
        }
        return false;
    }

    private static boolean tryMakeAccessible(final AccessibleObject obj) {
        if (setAccessibleMethod != null) {
            try {
                setAccessibleMethod.invoke(obj, true);
                return true;
            } catch (final Throwable e) {
                // Ignore
            }
        }
        if (trySetAccessibleMethod != null) {
            try {
                if ((Boolean) trySetAccessibleMethod.invoke(obj)) {
                    return true;
                }
            } catch (final Throwable e) {
                // Ignore
            }
        }
        return false;
    }

    public static boolean makeAccessible(final AccessibleObject obj, final ReflectionUtils reflectionUtils) {
        // This reflection code is duplicated from StandardReflectionDriver, because calling
        // ReflectionUtils.reflectionDriver.makeAccessible(obj) does not work when called from here
        // (private fields can't be accessed from outside this package even after calling setAccessible(true))
        if (isAccessible(obj) || tryMakeAccessible(obj)) {
            return true;
        }
        try {
            return reflectionUtils.doPrivileged(new Callable() {
                @Override
                public Boolean call() throws Exception {
                    return tryMakeAccessible(obj);
                }
            });
        } catch (final Throwable t) {
            return false;
        }
    }

    // -------------------------------------------------------------------------------------------------------------

    /**
     * JSON object key name for objects that are linked to from more than one object. Key name is only used if the
     * class that a JSON object was serialized from does not have its own id field annotated with {@link Id}.
     */
    static final String ID_KEY = "__ID";

    /** JSON object reference id prefix. */
    static final String ID_PREFIX = "[#";

    /** JSON object reference id suffix. */
    static final String ID_SUFFIX = "]";

    /** JSON character-to-string escaping replacements -- see http://www.json.org/ under "string". */
    private static final String[] JSON_CHAR_REPLACEMENTS = new String[256];

    static {
        for (int c = 0; c < 256; c++) {
            if (c == 32) {
                c = 127;
            }
            final int nibble1 = c >> 4;
            final char hexDigit1 = nibble1 <= 9 ? (char) ('0' + nibble1) : (char) ('A' + nibble1 - 10);
            final int nibble0 = c & 0xf;
            final char hexDigit0 = nibble0 <= 9 ? (char) ('0' + nibble0) : (char) ('A' + nibble0 - 10);
            JSON_CHAR_REPLACEMENTS[c] = "\\u00" + hexDigit1 + "" + hexDigit0;
        }
        JSON_CHAR_REPLACEMENTS['"'] = "\\\"";
        JSON_CHAR_REPLACEMENTS['\\'] = "\\\\";
        JSON_CHAR_REPLACEMENTS['\n'] = "\\n";
        JSON_CHAR_REPLACEMENTS['\r'] = "\\r";
        JSON_CHAR_REPLACEMENTS['\t'] = "\\t";
        JSON_CHAR_REPLACEMENTS['\b'] = "\\b";
        JSON_CHAR_REPLACEMENTS['\f'] = "\\f";
    }

    /** Lookup table for fast indenting. */
    private static final String[] INDENT_LEVELS = new String[17];
    static {
        final StringBuilder buf = new StringBuilder();
        for (int i = 0; i < INDENT_LEVELS.length; i++) {
            INDENT_LEVELS[i] = buf.toString();
            buf.append(' ');
        }
    }

    // -------------------------------------------------------------------------------------------------------------

    /**
     * Constructor.
     */
    private JSONUtils() {
        // Cannot be constructed
    }

    /**
     * Escape a string to be surrounded in double quotes in JSON.
     *
     * @param unsafeStr
     *            the unsafe str
     * @param buf
     *            the buf
     */
    static void escapeJSONString(final String unsafeStr, final StringBuilder buf) {
        if (unsafeStr == null) {
            return;
        }
        // Fast path
        boolean needsEscaping = false;
        for (int i = 0, n = unsafeStr.length(); i < n; i++) {
            final char c = unsafeStr.charAt(i);
            if (c > 0xff || JSON_CHAR_REPLACEMENTS[c] != null) {
                needsEscaping = true;
                break;
            }
        }
        if (!needsEscaping) {
            buf.append(unsafeStr);
            return;
        }
        // Slow path
        for (int i = 0, n = unsafeStr.length(); i < n; i++) {
            final char c = unsafeStr.charAt(i);
            if (c > 0xff) {
                buf.append("\\u");
                final int nibble3 = ((c) & 0xf000) >> 12;
                buf.append(nibble3 <= 9 ? (char) ('0' + nibble3) : (char) ('A' + nibble3 - 10));
                final int nibble2 = ((c) & 0xf00) >> 8;
                buf.append(nibble2 <= 9 ? (char) ('0' + nibble2) : (char) ('A' + nibble2 - 10));
                final int nibble1 = ((c) & 0xf0) >> 4;
                buf.append(nibble1 <= 9 ? (char) ('0' + nibble1) : (char) ('A' + nibble1 - 10));
                final int nibble0 = ((c) & 0xf);
                buf.append(nibble0 <= 9 ? (char) ('0' + nibble0) : (char) ('A' + nibble0 - 10));
            } else {
                final String replacement = JSON_CHAR_REPLACEMENTS[c];
                if (replacement == null) {
                    buf.append(c);
                } else {
                    buf.append(replacement);
                }
            }
        }
    }

    /**
     * Escape a string to be surrounded in double quotes in JSON.
     * 
     * @param unsafeStr
     *            The string to escape.
     * @return The escaped string.
     */
    public static String escapeJSONString(final String unsafeStr) {
        final StringBuilder buf = new StringBuilder(unsafeStr.length() * 2);
        escapeJSONString(unsafeStr, buf);
        return buf.toString();
    }

    // -------------------------------------------------------------------------------------------------------------

    /**
     * Indent (depth * indentWidth) spaces.
     *
     * @param depth
     *            the depth
     * @param indentWidth
     *            the indent width
     * @param buf
     *            the buf
     */
    static void indent(final int depth, final int indentWidth, final StringBuilder buf) {
        final int maxIndent = INDENT_LEVELS.length - 1;
        for (int d = depth * indentWidth; d > 0;) {
            final int n = Math.min(d, maxIndent);
            buf.append(INDENT_LEVELS[n]);
            d -= n;
        }
    }

    // -------------------------------------------------------------------------------------------------------------

    /**
     * Get a field value, appropriately handling primitive-typed fields.
     *
     * @param containingObj
     *            the containing object
     * @param field
     *            the field
     * @return the field value
     * @throws IllegalArgumentException
     *             if the specified object is not an instance of the class or interface declaring the underlying
     *             field
     * @throws IllegalAccessException
     *             if the field cannot be read
     */
    static Object getFieldValue(final Object containingObj, final Field field)
            throws IllegalArgumentException, IllegalAccessException {
        // return ReflectionUtils.getFieldVal(true, containingObj, field.getName());
        final Class fieldType = field.getType();
        if (fieldType == Integer.TYPE) {
            return field.getInt(containingObj);
        } else if (fieldType == Long.TYPE) {
            return field.getLong(containingObj);
        } else if (fieldType == Short.TYPE) {
            return field.getShort(containingObj);
        } else if (fieldType == Double.TYPE) {
            return field.getDouble(containingObj);
        } else if (fieldType == Float.TYPE) {
            return field.getFloat(containingObj);
        } else if (fieldType == Boolean.TYPE) {
            return field.getBoolean(containingObj);
        } else if (fieldType == Byte.TYPE) {
            return field.getByte(containingObj);
        } else if (fieldType == Character.TYPE) {
            return field.getChar(containingObj);
        } else {
            return field.get(containingObj);
        }
    }

    // -------------------------------------------------------------------------------------------------------------

    /**
     * Return true for classes that can be equal to a basic value type (types that can be converted directly to and
     * from string representation).
     *
     * @param cls
     *            the class
     * @return true, if the class is a basic value type
     */
    static boolean isBasicValueType(final Class cls) {
        return cls == String.class //
                || cls == Integer.class || cls == Integer.TYPE //
                || cls == Long.class || cls == Long.TYPE //
                || cls == Short.class || cls == Short.TYPE //
                || cls == Float.class || cls == Float.TYPE //
                || cls == Double.class || cls == Double.TYPE //
                || cls == Byte.class || cls == Byte.TYPE //
                || cls == Character.class || cls == Character.TYPE //
                || cls == Boolean.class || cls == Boolean.TYPE //
                || cls.isEnum() //
                || cls == Class.class;
    }

    /**
     * Return true for types that can be converted directly to and from string representation.
     *
     * @param type
     *            the type
     * @return true, if the type is a basic value type
     */
    static boolean isBasicValueType(final Type type) {
        if (type instanceof Class) {
            return isBasicValueType((Class) type);
        } else if (type instanceof ParameterizedType) {
            return isBasicValueType(((ParameterizedType) type).getRawType());
        } else {
            return false;
        }
    }

    /**
     * Return true for objects that can be converted directly to and from string representation.
     *
     * @param obj
     *            the object
     * @return true, if the object is null or of basic value type
     */
    static boolean isBasicValueType(final Object obj) {
        return obj == null || obj instanceof String || obj instanceof Integer || obj instanceof Boolean
                || obj instanceof Long || obj instanceof Float || obj instanceof Double || obj instanceof Short
                || obj instanceof Byte || obj instanceof Character || obj.getClass().isEnum()
                || obj instanceof Class;
    }

    /**
     * Return true for objects that are collections or arrays (i.e. objects that are convertible to a JSON array).
     *
     * @param obj
     *            the object
     * @return true, if the object is a collection or array
     */
    static boolean isCollectionOrArray(final Object obj) {
        final Class cls = obj.getClass();
        return Collection.class.isAssignableFrom(cls) || cls.isArray();
    }

    // -------------------------------------------------------------------------------------------------------------

    /**
     * Get the raw type from a Type.
     *
     * @param type
     *            the type
     * @return the raw type
     * @throws IllegalArgumentException
     *             if passed a TypeVariable or anything other than a {@code Class} reference or
     *             {@link ParameterizedType}.
     */
    static Class getRawType(final Type type) {
        if (type instanceof Class) {
            return (Class) type;
        } else if (type instanceof ParameterizedType) {
            return (Class) ((ParameterizedType) type).getRawType();
        } else {
            throw new IllegalArgumentException("Illegal type: " + type);
        }
    }

    /**
     * Check if a field is serializable. Don't serialize transient, final, synthetic, or inaccessible fields.
     * 
     * 

* N.B. Tries to set field to accessible, which will require an "opens" declarations from modules that want to * allow this introspection. * * @param field * the field * @param onlySerializePublicFields * if true, only serialize public fields * @return true if the field is serializable */ static boolean fieldIsSerializable(final Field field, final boolean onlySerializePublicFields, final ReflectionUtils reflectionUtils) { final int modifiers = field.getModifiers(); if ((!onlySerializePublicFields || Modifier.isPublic(modifiers)) && !Modifier.isTransient(modifiers) && !Modifier.isFinal(modifiers) && ((modifiers & 0x1000 /* synthetic */) == 0)) { return makeAccessible(field, reflectionUtils); } return false; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy