1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package de.smartics.exceptions.report.message;
17
18 import java.io.File;
19 import java.lang.reflect.Field;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26
27 import com.sun.javadoc.AnnotationDesc;
28 import com.sun.javadoc.AnnotationTypeDoc;
29 import com.sun.javadoc.ClassDoc;
30 import com.sun.javadoc.Doc;
31 import com.sun.javadoc.FieldDoc;
32 import com.sun.javadoc.AnnotationDesc.ElementValuePair;
33
34 import de.smartics.analysis.javadoc.CollectorContext;
35 import de.smartics.analysis.javadoc.DefaultJavadocCollector;
36 import de.smartics.analysis.javadoc.JavadocCollector;
37 import de.smartics.analysis.javadoc.JavadocException;
38 import de.smartics.analysis.javadoc.filter.AcceptAllFilter;
39 import de.smartics.analysis.javadoc.runtime.RuntimeUtils;
40 import de.smartics.exceptions.i18n.message.MessageParam;
41 import de.smartics.exceptions.i18n.message.MessageParamParser;
42 import de.smartics.exceptions.i18n.message.MessageType;
43 import de.smartics.exceptions.i18n.message.ParentMessageParam;
44 import de.smartics.exceptions.i18n.message.ParseException;
45 import de.smartics.exceptions.i18n.message.UsedBy;
46 import de.smartics.exceptions.i18n.message.MessageParamParser.MessageParamInfo;
47 import de.smartics.report.conf.ProjectConfiguration;
48
49
50
51
52
53
54
55 public final class PlaceHolderHandler
56 {
57
58
59
60
61
62
63
64 private static final Log LOG = LogFactory.getLog(PlaceHolderHandler.class);
65
66
67
68
69
70
71 private final ProjectConfiguration<?> config;
72
73
74
75
76 private final Map<Class<?>, ClassDoc> cache =
77 new HashMap<Class<?>, ClassDoc>();
78
79
80
81
82
83
84
85
86
87
88 public PlaceHolderHandler(final ProjectConfiguration<?> config)
89 {
90 this.config = config;
91 }
92
93
94
95
96
97
98
99
100
101
102
103
104
105 public PlaceHolderInfo readPlaceholderDesc(final ClassLoader classLoader,
106 final FieldDoc fieldDoc)
107 {
108 final AnnotationDesc[] annDescs = fieldDoc.annotations();
109 final String usedByClassName = UsedBy.class.getName();
110 for (AnnotationDesc annDesc : annDescs)
111 {
112 final AnnotationTypeDoc annTypeDoc = annDesc.annotationType();
113 if (usedByClassName.equals(annTypeDoc.qualifiedName()))
114 {
115 final ElementValuePair[] pairs = annDesc.elementValues();
116 for (ElementValuePair pair : pairs)
117 {
118 final String name = pair.element().name();
119 if ("value".equals(name))
120 {
121 final ClassDoc referencedClassDoc =
122 (ClassDoc) pairs[0].value().value();
123 try
124 {
125 final Class<?> referencedClass =
126 (Class<?>) RuntimeUtils.loadClass(classLoader,
127 referencedClassDoc);
128 final PlaceHolderInfo placeHolderInfo =
129 readPlaceholderDesc(referencedClass);
130 return placeHolderInfo;
131 }
132 catch (final Exception e)
133 {
134 if (LOG.isWarnEnabled())
135 {
136 LOG.warn(
137 "Cannot load class '" + referencedClassDoc.qualifiedName()
138 + "' to analyse " + " place holders of field '"
139 + fieldDoc.qualifiedName() + "'.", e);
140 }
141 }
142 }
143 }
144 }
145 }
146 return PlaceHolderInfo.EMPTY;
147 }
148
149 private PlaceHolderInfo readPlaceholderDesc(final Class<?> referencedClass)
150 {
151 final PlaceHolderInfo placeHolderInfo = new PlaceHolderInfo();
152 addParentMessageParam(placeHolderInfo, referencedClass);
153 addMessageParam(placeHolderInfo, referencedClass);
154 return placeHolderInfo;
155 }
156
157 private void addParentMessageParam(final PlaceHolderInfo placeHolderInfo,
158 final Class<?> referencedClass)
159 {
160 final ParentMessageParam messageParam =
161 referencedClass.getAnnotation(ParentMessageParam.class);
162 if (messageParam != null)
163 {
164 addMessageParam(placeHolderInfo, referencedClass, messageParam);
165 }
166 }
167
168 private void addMessageParam(final PlaceHolderInfo placeHolderInfo,
169 final Class<?> referencedClass)
170 {
171 Class<?> currentClass = referencedClass;
172 do
173 {
174 final Field[] fields = currentClass.getDeclaredFields();
175 for (final Field field : fields)
176 {
177 final MessageParam messageParam =
178 field.getAnnotation(MessageParam.class);
179 if (messageParam != null)
180 {
181 addMessageParam(placeHolderInfo, referencedClass, field, messageParam);
182 }
183 }
184
185 currentClass = currentClass.getSuperclass();
186 }
187 while (currentClass != null && currentClass != Exception.class);
188 }
189
190 private void addMessageParam(final PlaceHolderInfo placeHolderInfo,
191 final Class<?> referencedClass, final Field field,
192 final MessageParam messageParam) throws ParseException
193 {
194 final String name = field.getName();
195 addMessageParam(placeHolderInfo, referencedClass, name, messageParam);
196 }
197
198 private void addMessageParam(final PlaceHolderInfo placeHolderInfo,
199 final Class<?> referencedClass, final String name,
200 final MessageParam messageParam) throws ParseException
201 {
202 addMessageParam(placeHolderInfo, referencedClass, name, null,
203 messageParam.value());
204 addMessageParam(placeHolderInfo, referencedClass, name, MessageType.TITLE,
205 messageParam.headerParamIndex());
206 addMessageParam(placeHolderInfo, referencedClass, name,
207 MessageType.SUMMARY, messageParam.summaryParamIndex());
208 addMessageParam(placeHolderInfo, referencedClass, name,
209 MessageType.DETAILS, messageParam.detailsParamIndex());
210 addMessageParam(placeHolderInfo, referencedClass, name,
211 MessageType.IMPLICATIONS_ON_CURRENT_TASK,
212 messageParam.implicationsParamIndex());
213 addMessageParam(placeHolderInfo, referencedClass, name,
214 MessageType.WHAT_TO_DO_NOW, messageParam.todoParamIndex());
215 addMessageParam(placeHolderInfo, referencedClass, name, MessageType.URL,
216 messageParam.urlParamIndex());
217 }
218
219 private void addMessageParam(final PlaceHolderInfo placeHolderInfo,
220 final Class<?> referencedClass, final ParentMessageParam messageParam)
221 throws ParseException
222 {
223 addParentMessageParam(placeHolderInfo, referencedClass, null,
224 messageParam.value());
225 addParentMessageParam(placeHolderInfo, referencedClass, MessageType.TITLE,
226 messageParam.headerParamIndex());
227 addParentMessageParam(placeHolderInfo, referencedClass,
228 MessageType.SUMMARY, messageParam.summaryParamIndex());
229 addParentMessageParam(placeHolderInfo, referencedClass,
230 MessageType.DETAILS, messageParam.detailsParamIndex());
231 addParentMessageParam(placeHolderInfo, referencedClass,
232 MessageType.IMPLICATIONS_ON_CURRENT_TASK,
233 messageParam.implicationsParamIndex());
234 addParentMessageParam(placeHolderInfo, referencedClass,
235 MessageType.WHAT_TO_DO_NOW, messageParam.todoParamIndex());
236 addParentMessageParam(placeHolderInfo, referencedClass, MessageType.URL,
237 messageParam.urlParamIndex());
238 }
239
240 private void addMessageParam(final PlaceHolderInfo placeHolderInfo,
241 final Class<?> referencedClass, final String name,
242 final MessageType messageType, final String value) throws ParseException,
243 NullPointerException
244 {
245 final List<MessageParamInfo> paramInfos = MessageParamParser.parse(value);
246 addInfos(placeHolderInfo, referencedClass, name, messageType, paramInfos);
247 }
248
249 private void addInfos(final PlaceHolderInfo placeHolderInfo,
250 final Class<?> referencedClass, final String name,
251 final MessageType messageType, final List<MessageParamInfo> paramInfos)
252 {
253 for (final MessageParamInfo paramInfo : paramInfos)
254 {
255 final int placeholderIndex = paramInfo.getIndex();
256 final String ognlPath = paramInfo.getOgnlPath();
257
258 final String description = readDescription(referencedClass, name);
259 final PlaceHolderDescId id =
260 new PlaceHolderDescId(placeholderIndex, messageType);
261 final PlaceHolderDesc desc =
262 new PlaceHolderDesc(id, name, ognlPath, description);
263 placeHolderInfo.putPlaceHolderDesc(desc);
264 }
265 }
266
267 private String readDescription(final Class<?> clazz, final String fieldName)
268 {
269 if (clazz != null)
270 {
271 final Class<?> classWithParam = getClassWithParam(clazz, fieldName);
272 if (classWithParam != null)
273 {
274 final ClassDoc classDoc = readClassDoc(classWithParam);
275 if (classDoc != null)
276 {
277 for (FieldDoc fieldDoc : classDoc.fields())
278 {
279 if (fieldName.equals(fieldDoc.name()))
280 {
281 final String description = fieldDoc.commentText();
282 return description;
283 }
284 }
285 }
286 }
287 }
288 return null;
289 }
290
291 private ClassDoc readClassDoc(final Class<?> clazz)
292 {
293 ClassDoc classDoc = cache.get(clazz);
294 if (classDoc == null && !cache.containsKey(clazz))
295 {
296 final String fileRepresentation = calculateSourceFile(clazz);
297 if (fileRepresentation != null)
298 {
299 final Map<String, String> args = new HashMap<String, String>();
300 args.put(fileRepresentation, "javadoc-arg");
301 final CollectorContext context =
302 new CollectorContext(new AcceptAllFilter(), clazz.getClassLoader());
303 final JavadocCollector collector =
304 new DefaultJavadocCollector("placeholder", args, context);
305 try
306 {
307 final List<Doc> docs = collector.collect();
308 classDoc = getClassDoc(clazz, docs);
309 cache.put(clazz, classDoc);
310 }
311 catch (final JavadocException e)
312 {
313 if (LOG.isWarnEnabled())
314 {
315 LOG.warn("Cannot collect Javadoc information for class '"
316 + clazz.getName() + "'.");
317 }
318 cache.put(clazz, null);
319 }
320 }
321 else
322 {
323 if (LOG.isDebugEnabled())
324 {
325 LOG.debug("Cannot find sources for class '" + clazz.getName()
326 + "' in " + config.getSourceRootDirectoryNames());
327 }
328 }
329 }
330 return classDoc;
331 }
332
333 private String calculateSourceFile(final Class<?> clazz)
334 {
335 final String suffix = clazz.getName().replace('.', '/') + ".java";
336 for (String prefix : config.getSourceRootDirectoryNames())
337 {
338 final File file = new File(prefix, suffix);
339 if (file.exists())
340 {
341 return file.getAbsolutePath();
342 }
343 }
344 return null;
345 }
346
347 private ClassDoc getClassDoc(final Class<?> clazz, final List<Doc> docs)
348 {
349 final String qualifiedName = clazz.getName();
350 for (Doc doc : docs)
351 {
352 if (doc.isClass())
353 {
354 final ClassDoc classDoc = (ClassDoc) doc;
355 if (qualifiedName.equals(classDoc.qualifiedName()))
356 {
357 return classDoc;
358 }
359 }
360 }
361 return null;
362 }
363
364 private void addParentMessageParam(final PlaceHolderInfo placeHolderInfo,
365 final Class<?> referencedClass, final MessageType messageType,
366 final String value) throws ParseException, NullPointerException
367 {
368 final Map<String, List<MessageParamInfo>> paramInfoMap =
369 MessageParamParser.parseParentMessageParam(value);
370
371 for (final Map.Entry<String, List<MessageParamInfo>> paramInfoEntry : paramInfoMap
372 .entrySet())
373 {
374 final String name = paramInfoEntry.getKey();
375 final List<MessageParamInfo> paramInfos = paramInfoEntry.getValue();
376
377 addInfos(placeHolderInfo, referencedClass, name, messageType, paramInfos);
378 }
379 }
380
381 private Class<?> getClassWithParam(final Class<?> clazz, final String name)
382 {
383 Class<?> currentClass = clazz;
384 do
385 {
386 try
387 {
388 currentClass.getDeclaredField(name);
389 return currentClass;
390 }
391 catch (final Exception e)
392 {
393
394 }
395 currentClass = currentClass.getSuperclass();
396 }
397 while (currentClass != null && currentClass != Object.class);
398 return null;
399 }
400
401
402
403 }