001package ball.util;
002/*-
003 * ##########################################################################
004 * Utilities
005 * $Id: PatternMatcherBean.java 5285 2020-02-05 04:23:21Z ball $
006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/ball-util/trunk/src/main/java/ball/util/PatternMatcherBean.java $
007 * %%
008 * Copyright (C) 2008 - 2020 Allen D. Ball
009 * %%
010 * Licensed under the Apache License, Version 2.0 (the "License");
011 * you may not use this file except in compliance with the License.
012 * You may obtain a copy of the License at
013 *
014 *      http://www.apache.org/licenses/LICENSE-2.0
015 *
016 * Unless required by applicable law or agreed to in writing, software
017 * distributed under the License is distributed on an "AS IS" BASIS,
018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
019 * See the License for the specific language governing permissions and
020 * limitations under the License.
021 * ##########################################################################
022 */
023import ball.annotation.MatcherGroup;
024import ball.annotation.PatternRegex;
025import java.lang.annotation.AnnotationFormatError;
026import java.lang.reflect.Field;
027import java.lang.reflect.Method;
028import java.util.List;
029import java.util.regex.Matcher;
030import java.util.regex.Pattern;
031import org.apache.commons.lang3.reflect.FieldUtils;
032import org.apache.commons.lang3.reflect.MethodUtils;
033
034import static ball.util.Converter.convertTo;
035
036/**
037 * Interface providing default methods for beans classes annotated with
038 * {@link PatternRegex} and whose methods are annotated with
039 * {@link MatcherGroup}.
040 *
041 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball}
042 * @version $Revision: 5285 $
043 */
044public interface PatternMatcherBean {
045
046    /**
047     * Method to initialize fields and methods annotated with
048     * {@link MatcherGroup} with the results of parsing the {@code sequence}
049     * argument.
050     *
051     * @param   sequence        The {@link CharSequence} to parse.
052     *
053     * @throws  IllegalArgumentException
054     *                          If the {@link CharSequence} does not match
055     *                          the {@link PatternRegex#value()}.
056     */
057    default void initialize(CharSequence sequence) {
058        Matcher matcher = matcher(sequence);
059
060        if (! matcher.matches()) {
061            throw new IllegalArgumentException("\"" + String.valueOf(sequence)
062                                               + "\" does not match "
063                                               + matcher.pattern().pattern());
064        }
065
066        try {
067            List<Field> fields =
068                FieldUtils.getFieldsListWithAnnotation(getClass(),
069                                                       MatcherGroup.class);
070
071            for (Field field : fields) {
072                MatcherGroup group = field.getAnnotation(MatcherGroup.class);
073                String string = matcher.group(group.value());
074                Object value =
075                    (string != null)
076                        ? convertTo(string, field.getType())
077                        : null;
078
079                FieldUtils.writeField(field, this, value, true);
080            }
081
082            List<Method> methods =
083                MethodUtils.getMethodsListWithAnnotation(getClass(),
084                                                         MatcherGroup.class,
085                                                         true, true);
086
087            for (Method method : methods) {
088                MatcherGroup group = method.getAnnotation(MatcherGroup.class);
089                Object value =
090                    convertTo(matcher.group(group.value()),
091                              method.getParameterTypes()[0]);
092
093                MethodUtils.invokeMethod(this, true,
094                                         method.getName(),
095                                         new Object[] { value },
096                                         method.getParameterTypes());
097            }
098        } catch (IllegalAccessException exception) {
099            exception.printStackTrace(System.err);
100        } catch (RuntimeException exception) {
101            throw exception;
102        } catch (Exception exception) {
103            throw new IllegalArgumentException(String.valueOf(matcher),
104                                               exception);
105        }
106    }
107
108    /**
109     * Method to get the {@link Matcher} for the argument input.
110     *
111     * @param   sequence        The {@link CharSequence} to parse.
112     *
113     * @return  The {@link Matcher} for the {@link #pattern()} applied to
114     *          the {@link CharSequence}.
115     */
116    default Matcher matcher(CharSequence sequence) {
117        return pattern().matcher(sequence);
118    }
119
120    /**
121     * Method to get the compiled {@link Pattern} for this annotated bean.
122     *
123     * @return  The {@link Pattern}.
124     */
125    default Pattern pattern() {
126        Pattern pattern = null;
127        PatternRegex annotation = getClass().getAnnotation(PatternRegex.class);
128
129        try {
130            pattern = Pattern.compile(annotation.value());
131        } catch (Exception exception) {
132            throw new AnnotationFormatError(annotation.toString(), exception);
133        }
134
135        return pattern;
136    }
137}