001package ball.util; 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.MatcherGroup; 022import ball.annotation.PatternRegex; 023import java.lang.annotation.AnnotationFormatError; 024import java.lang.reflect.Field; 025import java.lang.reflect.Method; 026import java.util.List; 027import java.util.regex.Matcher; 028import java.util.regex.Pattern; 029import org.apache.commons.lang3.reflect.FieldUtils; 030import org.apache.commons.lang3.reflect.MethodUtils; 031 032import static ball.util.Converter.convertTo; 033 034/** 035 * Interface providing default methods for beans classes annotated with 036 * {@link PatternRegex} and whose methods are annotated with 037 * {@link MatcherGroup}. 038 * 039 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 040 */ 041public interface PatternMatcherBean { 042 043 /** 044 * Method to initialize fields and methods annotated with 045 * {@link MatcherGroup} with the results of parsing the {@code sequence} 046 * argument. 047 * 048 * @param sequence The {@link CharSequence} to parse. 049 * 050 * @throws IllegalArgumentException 051 * If the {@link CharSequence} does not match 052 * the {@link PatternRegex#value()}. 053 */ 054 default void initialize(CharSequence sequence) { 055 Matcher matcher = matcher(sequence); 056 057 if (! matcher.matches()) { 058 throw new IllegalArgumentException("\"" + String.valueOf(sequence) 059 + "\" does not match " + matcher.pattern().pattern()); 060 } 061 062 try { 063 List<Field> fields = FieldUtils.getFieldsListWithAnnotation(getClass(), MatcherGroup.class); 064 065 for (Field field : fields) { 066 MatcherGroup group = field.getAnnotation(MatcherGroup.class); 067 String string = matcher.group(group.value()); 068 Object value = (string != null) ? convertTo(string, field.getType()) : null; 069 070 FieldUtils.writeField(field, this, value, true); 071 } 072 073 List<Method> methods = 074 MethodUtils.getMethodsListWithAnnotation(getClass(), MatcherGroup.class, true, true); 075 076 for (Method method : methods) { 077 MatcherGroup group = method.getAnnotation(MatcherGroup.class); 078 Object value = convertTo(matcher.group(group.value()), method.getParameterTypes()[0]); 079 080 MethodUtils.invokeMethod(this, true, 081 method.getName(), new Object[] { value }, method.getParameterTypes()); 082 } 083 } catch (IllegalAccessException exception) { 084 exception.printStackTrace(System.err); 085 } catch (RuntimeException exception) { 086 throw exception; 087 } catch (Exception exception) { 088 throw new IllegalArgumentException(String.valueOf(matcher), exception); 089 } 090 } 091 092 /** 093 * Method to get the {@link Matcher} for the argument input. 094 * 095 * @param sequence The {@link CharSequence} to parse. 096 * 097 * @return The {@link Matcher} for the {@link #pattern()} applied to 098 * the {@link CharSequence}. 099 */ 100 default Matcher matcher(CharSequence sequence) { 101 return pattern().matcher(sequence); 102 } 103 104 /** 105 * Method to get the compiled {@link Pattern} for this annotated bean. 106 * 107 * @return The {@link Pattern}. 108 */ 109 default Pattern pattern() { 110 Pattern pattern = null; 111 PatternRegex annotation = getClass().getAnnotation(PatternRegex.class); 112 113 try { 114 pattern = Pattern.compile(annotation.value()); 115 } catch (Exception exception) { 116 throw new AnnotationFormatError(annotation.toString(), exception); 117 } 118 119 return pattern; 120 } 121}