001    /* $Id: FromAnnotationsRuleSet.java 992746 2010-09-05 09:31:53Z simonetripodi $
002     *
003     * Licensed to the Apache Software Foundation (ASF) under one or more
004     * contributor license agreements.  See the NOTICE file distributed with
005     * this work for additional information regarding copyright ownership.
006     * The ASF licenses this file to You under the Apache License, Version 2.0
007     * (the "License"); you may not use this file except in compliance with
008     * the License.  You may obtain a copy of the License at
009     *
010     *      http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    package org.apache.commons.digester.annotations;
019    
020    import java.lang.annotation.Annotation;
021    import java.lang.reflect.AnnotatedElement;
022    import java.util.ArrayList;
023    import java.util.HashSet;
024    import java.util.LinkedHashMap;
025    import java.util.List;
026    import java.util.Map;
027    import java.util.Map.Entry;
028    import java.util.Set;
029    
030    import org.apache.commons.digester.Digester;
031    import org.apache.commons.digester.Rule;
032    import org.apache.commons.digester.RuleSet;
033    
034    /**
035     * A {@link RuleSet} implementation that's able to inject {@link Rule}s created
036     * with the annotations analysis.
037     *
038     * @since 2.1
039     */
040    public final class FromAnnotationsRuleSet implements RuleSet {
041    
042        /**
043         * The data structure that stores the patterns/{@link AnnotationRuleProvider}
044         * pairs.
045         */
046        private final Map<String, List<AnnotationRuleProvider<Annotation, AnnotatedElement, Rule>>> rules =
047            new LinkedHashMap<String, List<AnnotationRuleProvider<Annotation, AnnotatedElement, Rule>>>();
048    
049        /**
050         * Maintains all the classes that this RuleSet produces mapping for.
051         */
052        private final Set<Class<?>> mappedClasses = new HashSet<Class<?>>();
053    
054        private final DigesterLoader digesterLoader;
055    
056        /**
057         * The namespace URI.
058         */
059        private volatile String namespaceURI;
060    
061        /**
062         * Created a new {@code FromAnnotationsRuleSet} instance.
063         *
064         * @param digesterLoader the parent DigesterLoader.
065         */
066        protected FromAnnotationsRuleSet(DigesterLoader digesterLoader) {
067            this.digesterLoader = digesterLoader;
068        }
069    
070        /**
071         * {@inheritDoc}
072         */
073        public void addRuleInstances(Digester digester) {
074            String pattern;
075            Rule rule;
076            for (Entry<String, List<AnnotationRuleProvider<Annotation, AnnotatedElement, Rule>>> entry :
077                    this.rules.entrySet()) {
078                pattern = entry.getKey();
079                for (AnnotationRuleProvider<Annotation, AnnotatedElement, Rule> provider : entry.getValue()) {
080                    rule = provider.get();
081                    if (this.namespaceURI != null) {
082                        rule.setNamespaceURI(this.namespaceURI);
083                    }
084                    digester.addRule(pattern, rule);
085                }
086            }
087        }
088    
089        /**
090         * Analyzes the target class and adds the {@link AnnotationRuleProvider}s to
091         * this {@link FromAnnotationsRuleSet}.
092         *
093         * @param target the class has to be analyzed.
094         */
095        public void addRules(Class<?> target) {
096            this.digesterLoader.addRulesTo(target, this);
097        }
098    
099        /**
100         * Builds and register an {@link AnnotationRuleProvider} for a specific
101         * pattern.
102         *
103         * @param <T> the {@link AnnotationRuleProvider} type.
104         * @param pattern the pattern has to be associated to the rule provider.
105         * @param klass the {@link AnnotationRuleProvider} type has to be instantiated.
106         * @param annotation the current visited annotation.
107         * @param element the current visited element.
108         */
109        public <A extends Annotation, E extends AnnotatedElement, R extends Rule, T extends AnnotationRuleProvider<A, E, R>>
110            void addRuleProvider(String pattern,
111                Class<T> klass,
112                A annotation,
113                E element) {
114    
115            T annotationRuleProvider =
116                this.digesterLoader.getAnnotationRuleProviderFactory().newInstance(klass);
117            annotationRuleProvider.init(annotation, element);
118            this.addRuleProvider(pattern, annotationRuleProvider);
119        }
120    
121        /**
122         * Register an {@link AnnotationRuleProvider} for a specific pattern.
123         *
124         * @param pattern the pattern has to be associated to the rule provider.
125         * @param ruleProvider the provider that builds the digester rule.
126         */
127        @SuppressWarnings("unchecked")
128        public void addRuleProvider(String pattern,
129                AnnotationRuleProvider<? extends Annotation, ? extends AnnotatedElement, ? extends Rule> ruleProvider) {
130            List<AnnotationRuleProvider<Annotation, AnnotatedElement, Rule>> rules;
131    
132            if (this.rules.containsKey(pattern)) {
133                rules = this.rules.get(pattern);
134            } else {
135                rules = new ArrayList<AnnotationRuleProvider<Annotation, AnnotatedElement, Rule>>();
136                this.rules.put(pattern, rules);
137            }
138    
139            rules.add((AnnotationRuleProvider<Annotation, AnnotatedElement, Rule>) ruleProvider);
140        }
141    
142        /**
143         * Retrieves a specific instance of the {@link AnnotationRuleProvider} for
144         * the input pattern.
145         *
146         * @param <T> the {@link AnnotationRuleProvider} type
147         * @param pattern the input pattern
148         * @param providerClass the {@link AnnotationRuleProvider} class
149         * @return an {@link AnnotationRuleProvider} for the input pattern if found,
150         *         null otherwise.
151         */
152        public <T extends AnnotationRuleProvider<? extends Annotation, ? extends AnnotatedElement, ? extends Rule>>
153                T getProvider(String pattern, Class<T> providerClass) {
154    
155            if (!this.rules.containsKey(pattern)) {
156                return null;
157            }
158    
159            for (AnnotationRuleProvider<Annotation, AnnotatedElement, Rule> rule : this.rules.get(pattern)) {
160                if (providerClass.isInstance(rule)) {
161                    return providerClass.cast(rule);
162                }
163            }
164    
165            return null;
166        }
167    
168        /**
169         * Add created {@link AnnotationRuleProvider}s created in another analysis
170         * session.
171         *
172         * @param ruleSet the {@code RuleSet} created in another analysis session.
173         */
174        public void addRulesProviderFrom(final FromAnnotationsRuleSet ruleSet) {
175            this.rules.putAll(ruleSet.getRules());
176        }
177    
178        /**
179         * Checks if this RuleSet builds Digester mapping rules for the input type.
180         *
181         * @param clazz the input type.
182         * @return true, if this RuleSet builds Digester mapping rules for the input
183         *         type, false otherwise.
184         */
185        protected boolean mapsClass(Class<?> clazz) {
186            return this.mappedClasses.contains(clazz);
187        }
188    
189        /**
190         * Remember that this RuleSet is able to build Digester mapping rules for
191         * the input type.
192         *
193         * @param clazz the input type.
194         */
195        protected void addMappedClass(Class<?> clazz) {
196            this.mappedClasses.add(clazz);
197        }
198    
199        /**
200         * Returns the data structure  the patterns/{@link AnnotationRuleProvider}
201         * pairs.
202         *
203         * @return the data structure  the patterns/{@link AnnotationRuleProvider}
204         *         pairs.
205         */
206        private Map<String, List<AnnotationRuleProvider<Annotation, AnnotatedElement, Rule>>> getRules() {
207            return this.rules;
208        }
209    
210        /**
211         * {@inheritDoc}
212         */
213        public String getNamespaceURI() {
214            return this.namespaceURI;
215        }
216    
217        /**
218         * Sets the namespace URI that will be applied to all Rule instances
219         * created from this RuleSet.
220         *
221         * @param namespaceURI the namespace URI that will be applied to all Rule
222         * instances created from this RuleSet.
223         */
224        public void setNamespaceURI(String namespaceURI) {
225            this.namespaceURI = namespaceURI;
226        }
227    
228        /**
229         * {@inheritDoc}
230         */
231        @Override
232        public String toString() {
233            return "{ mappedClasses="
234                + this.mappedClasses
235                + ", rules="
236                + this.rules.toString()
237                + ", namespaceURI="
238                + this.namespaceURI
239                + " }";
240        }
241    
242    }