001package ball.beans;
002/*-
003 * ##########################################################################
004 * Utilities
005 * %%
006 * Copyright (C) 2008 - 2022 Allen D. Ball
007 * %%
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *      http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 * ##########################################################################
020 */
021import ball.annotation.ConstantValueMustConvertTo;
022import java.lang.reflect.Method;
023import java.util.Collections;
024import java.util.EnumMap;
025import java.util.Map;
026import java.util.regex.Matcher;
027import java.util.regex.Pattern;
028
029import static java.beans.Introspector.decapitalize;
030
031/**
032 * Bean property method {@link Enum} type.
033 *
034 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball}
035 */
036public enum PropertyMethodEnum {
037    GET, IS, SET;
038
039    private static final Map<PropertyMethodEnum,Method> MAP = Collections.unmodifiableMap(new MethodPrototypeMap());
040
041    @ConstantValueMustConvertTo(value = Pattern.class, method = "compile")
042    private static final String PROPERTY_REGEX = "([\\p{Upper}][\\p{Alnum}]*)";
043
044    private Pattern pattern = null;
045
046    /**
047     * Method to get the prototype return type {@link Class} for this method
048     * type.
049     *
050     * @return  The return type {@link Class}.
051     */
052    public Class<?> getReturnType() { return MAP.get(this).getReturnType(); }
053
054    /**
055     * Method to get the prototype parameter types ({@link Class}es) for
056     * this method type.
057     *
058     * @return  The parameter types array (of {@link Class}es).
059     */
060    public Class<?>[] getParameterTypes() { return MAP.get(this).getParameterTypes();
061    }
062
063    /**
064     * Method to get the property name from the argument method name.
065     *
066     * @param   method          The candidate getter/setter method name.
067     *
068     * @return  The property name if method name matches the pattern;
069     *          {@code null} if the argument is {@code null} or doesn't
070     *          match.
071     */
072    public String getPropertyName(String method) {
073        String name = null;
074
075        if (method != null) {
076            Matcher matcher = pattern().matcher(method);
077
078            if (matcher.matches()) {
079                name = decapitalize(matcher.group(1));
080            }
081        }
082
083        return name;
084    }
085
086    private Pattern pattern() {
087        if (pattern == null) {
088            pattern = Pattern.compile(Pattern.quote(name().toLowerCase()) + PROPERTY_REGEX);
089        }
090
091        return pattern;
092    }
093
094    /**
095     * Static method to get a property name from a {@link Method} name.
096     * (This method does not check return type or parameter types.)
097     *
098     * @param   method          The {@link Method}.
099     *
100     * @return  The property name if method name matches the pattern;
101     *          {@code null} if the argument is {@code null} or the name
102     *          doesn't match.
103     */
104    public static String getPropertyName(Method method) {
105        String name = null;
106
107        if (method != null) {
108            for (PropertyMethodEnum methodEnum : values()) {
109                name = methodEnum.getPropertyName(method.getName());
110
111                if (name != null) {
112                    break;
113                }
114            }
115        }
116
117        return name;
118    }
119
120    private static class MethodPrototypeMap extends EnumMap<PropertyMethodEnum,Method> {
121        private static final long serialVersionUID = 6408568606272721794L;
122
123        public MethodPrototypeMap() {
124            super(PropertyMethodEnum.class);
125
126            for (Method method : Prototypes.class.getDeclaredMethods()) {
127                put(PropertyMethodEnum.valueOf(method.getName()), method);
128            }
129        }
130
131        public interface Prototypes<T> {
132            public T GET();
133            public boolean IS();
134            public void SET(T value);
135        }
136    }
137}