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 */
018package org.apache.commons.digester.annotations;
019
020import java.lang.annotation.Annotation;
021import java.lang.reflect.AnnotatedElement;
022import java.util.ArrayList;
023import java.util.HashSet;
024import java.util.LinkedHashMap;
025import java.util.List;
026import java.util.Map;
027import java.util.Map.Entry;
028import java.util.Set;
029
030import org.apache.commons.digester.Digester;
031import org.apache.commons.digester.Rule;
032import 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 */
040public 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}