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

org.osgl.util.StringValueResolver Maven / Gradle / Ivy

The newest version!
package org.osgl.util;

/*-
 * #%L
 * Java Tool
 * %%
 * Copyright (C) 2014 - 2017 OSGL (Open Source General Library)
 * %%
 * 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.
 * #L%
 */

import org.osgl.$;
import org.osgl.exception.NotAppliedException;

import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

/**
 * A String value resolver resolves a {@link String string value} into
 * a certain type of object instance.
 */
public abstract class StringValueResolver extends $.F1 {

    private final Type targetType;

    protected Map attributes = new HashMap();

    public StringValueResolver() {
        targetType = findTargetType();
    }

    protected StringValueResolver(Class targetType) {
        this.targetType = $.requireNotNull(targetType);
    }

    public abstract T resolve(String value);

    public Class targetType() {
        return Generics.classOf(targetType);
    }

    public Type genericTargetType() {
        return targetType;
    }

    private Type findTargetType() {
        return findTargetType(getClass());
    }

    private static Type findTargetType(Class clazz) {
        List typeParams = Generics.typeParamImplementations(clazz, StringValueResolver.class);
        if (typeParams.size() > 0) {
            return typeParams.get(0);
        }
        throw E.unsupport("Cannot identify the target type from %s", clazz);
    }

    @Override
    public final T apply(String s) throws NotAppliedException, $.Break {
        return resolve(s);
    }

    /**
     * Set attribute of this resolver.
     *
     * Note use this method only on new resolver instance instead of shared instance
     *
     * @param key the attribute key
     * @param value the attribute value
     * @return this resolver instance
     */
    public StringValueResolver attribute(String key, Object value) {
        if (null == value) {
            attributes.remove(value);
        } else {
            attributes.put(key, value);
        }
        return this;
    }

    /**
     * Set attributes to this resolver
     *
     * Note use this method only on new resolver instance instead of shared instance
     *
     * @param attributes the attributes map
     * @return this resolver instance
     */
    public StringValueResolver attributes(Map attributes) {
        this.attributes.putAll(attributes);
        return this;
    }

    /**
     * Clear all attributes on this resolver
     * @return this resolver instance
     */
    public StringValueResolver clearAttributes() {
        attributes.clear();
        return this;
    }

    /**
     * Get attribute of this resolver by key specified
     * @param key the attribute key
     * @param  the generic type variable of attribute value
     * @return the attribute value
     */
    protected  V attribute(String key) {
        return (V) attributes.get(key);
    }

    /**
     * Returns an amended copy of this resolver based on the {@link AnnotationAware}
     * provided. This method allows resolver implementation to tune the
     * resolving logic when a certain annotation is provided
     *
     * By default return `this` resolver instance
     *
     * @param beanSpec the bean spec with annotation information
     * @return the amended copy of this resolver
     */
    public StringValueResolver amended(AnnotationAware beanSpec) {
        return this;
    }

    public static  StringValueResolver wrap(final $.Function func, final Class targetType) {
        if (func instanceof StringValueResolver) {
            return (StringValueResolver) func;
        } else {
            return new StringValueResolver(targetType) {
                @Override
                public T resolve(String value) {
                    return func.apply(value);
                }
            };
        }
    }

    // For primary types
    private static final StringValueResolver _boolean = new StringValueResolver() {
        @Override
        public Boolean resolve(String value) {
            if (S.empty(value)) {
                return Boolean.FALSE;
            }
            return Boolean.parseBoolean(value);
        }
    };
    private static final StringValueResolver _Boolean = new StringValueResolver() {
        @Override
        public Boolean resolve(String value) {
            if (S.empty(value)) {
                return null;
            }
            return Boolean.parseBoolean(value);
        }
    };
    private static final Map PREDEFINED_CHARS = new HashMap<>();

    static {
        PREDEFINED_CHARS.put("\\b", '\b');
        PREDEFINED_CHARS.put("\\f", '\f');
        PREDEFINED_CHARS.put("\\n", '\n');
        PREDEFINED_CHARS.put("\\r", '\r');
        PREDEFINED_CHARS.put("\\t", '\t');
        PREDEFINED_CHARS.put("\\", '\"');
        PREDEFINED_CHARS.put("\\'", '\'');
        PREDEFINED_CHARS.put("\\\\", '\\');
    }

    /**
     * Parsing String into char. The rules are:
     *
     * 1. if there value is null or empty length String then return `defval` specified
     * 2. if the length of the String is `1`, then return that one char in the string
     * 3. if the value not starts with '\', then throw `IllegalArgumentException`
     * 4. if the value starts with `\\u` then parse the integer using `16` radix. The check
     *    the range, if it fall into Character range, then return that number, otherwise raise
     *    `IllegalArgumentException`
     * 5. if the value length is 2 then check if it one of {@link #PREDEFINED_CHARS}, if found
     *    then return
     * 6. check if it valid OctalEscape defined in the spec
     *    if pass the check then return that char
     * 7. all other cases throw `IllegalArgumentException`
     *
     * @param value the string value to be resolved
     * @param defVal the default value when string value is `null` or empty
     * @return the char resolved from the string
     */
    private static Character resolveChar(String value, Character defVal) {
        if (null == value) {
            return defVal;
        }
        switch (value.length()) {
            case 0:
                return defVal;
            case 1:
                return value.charAt(0);
            default:
                if (value.startsWith("\\")) {
                    if (value.length() == 2) {
                        Character c = PREDEFINED_CHARS.get(value);
                        if (null != c) {
                            return c;
                        }
                    }
                    try {
                        String s = value.substring(1);
                        if (s.startsWith("u")) {
                            int i = Integer.parseInt(s.substring(1), 16);
                            if (i > Character.MAX_VALUE || i < Character.MIN_VALUE) {
                                throw new IllegalArgumentException("Invalid character: " + value);
                            }
                            return (char) i;
                        } else if (s.length() > 3) {
                            throw new IllegalArgumentException("Invalid character: " + value);
                        } else {
                            if (s.length() == 3) {
                                int i = Integer.parseInt(s.substring(0, 1));
                                if (i > 3) {
                                    throw new IllegalArgumentException("Invalid character: " + value);
                                }
                            }
                            int i = Integer.parseInt(s, 8);
                            return (char) i;
                        }
                    } catch (NumberFormatException e) {
                        throw new IllegalArgumentException("Invalid character: " + value);
                    }
                } else {
                    throw new IllegalArgumentException("Invalid character: " + value);
                }
        }
    }

    /**
     * Returns the char resolver based on {@link #resolveChar(String, Character)} with `\0` as
     * default value
     */
    private static final StringValueResolver _char = new StringValueResolver() {
        @Override
        public Character resolve(String value) {
            return resolveChar(value, '\0');
        }
    };

    /**
     * Returns the char resolver based on {@link #resolveChar(String, Character)} with `null` as
     * default value
     */
    private static final StringValueResolver _Char = new StringValueResolver() {
        @Override
        public Character resolve(String value) {
            return resolveChar(value, null);
        }
    };

    private static final StringValueResolver _charArray = new StringValueResolver() {
        @Override
        public char[] resolve(String value) {
            return null == value ? new char[0] : value.toCharArray();
        }
    };

    private static final StringValueResolver _byte = new StringValueResolver() {
        @Override
        public Byte resolve(String value) {
            if (S.blank(value)) {
                return (byte) 0;
            }
            return Byte.parseByte(value);
        }
    };
    private static final StringValueResolver _Byte = new StringValueResolver() {
        @Override
        public Byte resolve(String value) {
            if (S.blank(value)) {
                return null;
            }
            return Byte.parseByte(value);
        }
    };
    private static final StringValueResolver _short = new StringValueResolver() {
        @Override
        public Short resolve(String value) {
            if (S.blank(value)) {
                return (short) 0;
            }
            return Short.valueOf(value);
        }
    };
    private static final StringValueResolver _Short = new StringValueResolver() {
        @Override
        public Short resolve(String value) {
            if (S.blank(value)) {
                return null;
            }
            return Short.valueOf(value);
        }
    };
    private static final StringValueResolver _Number = new StringValueResolver() {
        @Override
        public Number resolve(String value) {
            if (S.blank(value)) {
                return null;
            }
            if (S.isIntOrLong(value)) {
                long l = Long.parseLong(value);
                if ((l <= Integer.MAX_VALUE) && (l >= Integer.MIN_VALUE)) {
                    return (int)l;
                } else {
                    return l;
                }
            } else {
                return Double.parseDouble(value);
            }
        }
    };

    private static int _int(String s) {
        s = s.trim();
        if (s.contains(".")) {
            float f = Float.valueOf(s);
            return Math.round(f);
        } else if (s.contains("*")) {
            List factors = S.fastSplit(s, "*");
            int n = 1;
            for (String factor : factors) {
                n *= _int(factor);
            }
            return n;
        } else {
            return Integer.valueOf(s);
        }
    }

    private static final StringValueResolver _int = new StringValueResolver() {
        @Override
        public Integer resolve(String value) {
            if (S.blank(value)) {
                return 0;
            }
            return _int(value);
        }
    };
    private static final StringValueResolver _Integer = new StringValueResolver() {
        @Override
        public Integer resolve(String value) {
            if (S.blank(value)) {
                return null;
            }
            return _int(value);
        }
    };
    private static final StringValueResolver _long = new StringValueResolver() {
        @Override
        public Long resolve(String value) {
            if (S.blank(value)) {
                return 0l;
            }
            return _long(value);
        }
    };
    private static final StringValueResolver _Long = new StringValueResolver() {
        @Override
        public Long resolve(String value) {
            if (S.blank(value)) {
                return null;
            }
            return _long(value);
        }
    };

    private static long _long(String s) {
        s = s.trim();
        if (s.contains(".")) {
            double d = Double.valueOf(s);
            return Math.round(d);
        } else if (s.contains("*")) {
            List factors = S.fastSplit(s, "*");
            long n = 1l;
            for (String factor : factors) {
                n *= _long(factor);
            }
            return n;
        } else {
            return Long.valueOf(s);
        }
    }
    private static final StringValueResolver _float = new StringValueResolver() {
        @Override
        public Float resolve(String value) {
            if (S.blank(value)) {
                return 0f;
            }
            float n = _float(value);
            if (Float.isInfinite(n) || Float.isNaN(n)) {
                throw new IllegalArgumentException("float value out of scope: " + value);
            }
            return n;
        }
    };
    private static final StringValueResolver _Float = new StringValueResolver() {
        @Override
        public Float resolve(String value) {
            if (S.blank(value)) {
                return null;
            }
            float n = _float(value);
            if (Float.isInfinite(n) || Float.isNaN(n)) {
                throw new IllegalArgumentException("float value out of scope: " + value);
            }
            return n;
        }
    };

    private static float _float(String s) {
        s = s.trim();
        if (s.contains("*")) {
            List factors = S.fastSplit(s, "*");
            float n = 1f;
            for (String factor : factors) {
                n *= _float(factor);
            }
            return n;
        } else {
            return Float.valueOf(s);
        }
    }
    private static final StringValueResolver _double = new StringValueResolver() {
        @Override
        public Double resolve(String value) {
            if (S.blank(value)) {
                return 0d;
            }
            double n = _double(value);
            if (Double.isInfinite(n) || Double.isNaN(n)) {
                throw new IllegalArgumentException("double value out of scope: " + value);
            }
            return n;
        }
    };
    private static final StringValueResolver _Double = new StringValueResolver() {
        @Override
        public Double resolve(String value) {
            if (S.blank(value)) {
                return null;
            }
            double n = _double(value);
            if (Double.isInfinite(n) || Double.isNaN(n)) {
                throw new IllegalArgumentException("double value out of scope: " + value);
            }
            return n;
        }
    };

    private static double _double(String s) {
        s = s.trim();
        if (s.contains("*")) {
            List factors = S.fastSplit(s, "*");
            double n = 1d;
            for (String factor : factors) {
                n *= _double(factor);
            }
            return n;
        } else {
            return Double.valueOf(s);
        }
    }
    private static final StringValueResolver _String = wrap($.F.identity(), String.class);
    private static final StringValueResolver _Str = new StringValueResolver() {
        @Override
        public Str resolve(String value) {
            if (null == value) {
                return null;
            }
            return S.str(value);
        }
    };
    private static final StringValueResolver _FastStr = new StringValueResolver() {
        @Override
        public FastStr resolve(String value) {
            if (null == value) {
                return null;
            }
            return FastStr.of(value);
        }
    };
    private static final StringValueResolver _Locale = new StringValueResolver() {
        @Override
        public Locale resolve(String value) {
            return Locale.forLanguageTag(value);
        }
    };

    private static Map predefined = C.newMap(
            boolean.class, _boolean,
            Boolean.class, _Boolean,
            char.class, _char,
            Character.class, _Char,
            byte.class, _byte,
            Byte.class, _Byte,
            short.class, _short,
            Short.class, _Short,
            Number.class, _Number,
            int.class, _int,
            Integer.class, _Integer,
            long.class, _long,
            Long.class, _Long,
            float.class, _float,
            Float.class, _Float,
            double.class, _double,
            Double.class, _Double,
            String.class, _String,
            Locale.class, _Locale,
            Str.class, _Str,
            FastStr.class, _FastStr,
            char[].class, _charArray,
            BigInteger.class, new BigIntegerValueObjectCodec(),
            BigDecimal.class, new BigDecimalValueObjectCodec(),
            Keyword.class, new KeywordValueObjectCodec()
    );

    public static  void addPredefinedResolver(Class type, StringValueResolver resolver) {
        predefined.put(type, resolver);
    }

    public static Map predefined() {
        return C.Map(predefined);
    }

    @SuppressWarnings("unchecked")
    public static  StringValueResolver predefined(Class type) {
        return predefined.get(type);
    }

    public static void main(String[] args) {
        System.out.println(_float.targetType());
        System.out.println(_String.targetType());
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy