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 }