View Javadoc

1   /*
2    * Copyright 2007-2013 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.maven.exceptions;
17  
18  import java.util.ResourceBundle;
19  
20  import org.apache.commons.lang.StringUtils;
21  import org.apache.maven.doxia.sink.Sink;
22  import org.apache.maven.doxia.sink.SinkEventAttributeSet;
23  import org.apache.maven.doxia.sink.SinkEventAttributes;
24  
25  import com.thoughtworks.qdox.model.JavaClass;
26  
27  import de.smartics.exceptions.core.Code;
28  import de.smartics.exceptions.i18n.message.MessageType;
29  import de.smartics.exceptions.report.data.ExceptionCodeReportItem;
30  import de.smartics.exceptions.report.data.ProjectConfiguration;
31  import de.smartics.exceptions.report.data.StoredExceptionCodesReport;
32  import de.smartics.exceptions.report.generator.AbstractOutputReportGenerator;
33  import de.smartics.exceptions.report.generator.StringFunction;
34  import de.smartics.exceptions.report.message.PlaceHolderDesc;
35  import de.smartics.exceptions.report.message.PlaceHolderInfo;
36  import de.smartics.exceptions.report.renderer.JavadocRenderer;
37  import de.smartics.exceptions.report.renderer.html.HtmlRendererFactory;
38  import de.smartics.exceptions.report.utils.JavadocUtils;
39  import de.smartics.maven.exceptions.util.TextCoder;
40  
41  /**
42   * Simple generator that generates with a Maven sink.
43   */
44  public abstract class AbstractSinkReportGenerator extends
45      AbstractOutputReportGenerator<Sink>
46  {
47    // ********************************* Fields *********************************
48  
49    // --- constants ------------------------------------------------------------
50  
51    // --- members --------------------------------------------------------------
52  
53    // ****************************** Initializer *******************************
54  
55    // ****************************** Constructors ******************************
56  
57    /**
58     * Default constructor.
59     */
60    protected AbstractSinkReportGenerator()
61    {
62      super(new JavadocRenderer(new HtmlRendererFactory()));
63    }
64  
65    // ****************************** Inner Classes *****************************
66  
67    // ********************************* Methods ********************************
68  
69    // --- init -----------------------------------------------------------------
70  
71    // --- get&set --------------------------------------------------------------
72  
73    // --- business -------------------------------------------------------------
74  
75    @Override
76    protected void writeReportElementInfo(final Sink output,
77        final ProjectConfiguration<Sink> config,
78        final ExceptionCodeReportItem item) throws Exception
79    {
80      final Sink sink = output;
81  
82      sink.tableRow();
83  
84      final Code instance = item.getCode();
85  
86      sink.tableCell();
87      final String codeId = instance.getDisplayId();
88      sink.anchor(codeId);
89      sink.text(codeId);
90      sink.anchor_();
91      sink.tableCell_();
92  
93      sink.tableCell();
94      final String itemName = item.getJavadoc().getName();
95      final String itemId =
96          item.getTypeJavadoc().getFullyQualifiedName() + '_' + itemName;
97      sink.anchor(itemId);
98      sink.text(itemName);
99      sink.anchor_();
100     sink.tableCell_();
101 
102     sink.tableCell();
103 
104     final String javadoc = this.renderer.render(item.getJavadoc());
105     if (StringUtils.isNotBlank(javadoc))
106     {
107       final TextCoder coder = new TextCoder(config);
108       sink.rawText(coder.run(javadoc));
109     }
110     else
111     {
112       sink.rawText("&nbsp;");
113     }
114 
115     // TODO: BUG 681
116     // renderPlaceholderInfo(config, item, sink);
117 
118     sink.tableCell_();
119 
120     sink.tableRow_();
121   }
122 
123   protected void renderPlaceholderInfo(final ProjectConfiguration<Sink> config,
124       final ExceptionCodeReportItem item, final Sink sink)
125   {
126     final PlaceHolderInfo info = item.getPlaceHolderInfo();
127     if (!info.isEmpty())
128     {
129       sink.table();
130       final ResourceBundle bundle = config.getBundle();
131       final TextCoder coder = new TextCoder(config);
132 
133       sink.tableRow();
134       renderHeaderCell(sink, bundle, "report.placeholder.message");
135       renderHeaderCell(sink, bundle, "report.placeholder.index");
136       renderHeaderCell(sink, bundle, "report.placeholder.path");
137       renderHeaderCell(sink, bundle, "report.placeholder.param");
138       renderHeaderCell(sink, bundle, "report.placeholder.desc");
139       sink.tableRow_();
140 
141       for (final MessageType type : MessageType.values())
142       {
143         for (final PlaceHolderDesc desc : info.getPlaceHolderDescs(type))
144         {
145           final MessageType currentType = desc.getPlaceHolderMessageType();
146           if (currentType != null || type == MessageType.SUMMARY)
147           {
148             sink.tableRow();
149 
150             sink.tableCell();
151             if (currentType == null)
152             {
153               sink.text(bundle.getString("report.placeholder.value.all"));
154             }
155             else
156             {
157               sink.text(String.valueOf(type));
158             }
159             sink.tableCell_();
160 
161             sink.tableCell();
162             sink.text(String.valueOf(desc.getPlaceHolderIndex()));
163             sink.tableCell_();
164 
165             sink.tableCell();
166             final String path = desc.getOgnlPath();
167             if (StringUtils.isNotBlank(path))
168             {
169               sink.text(String.valueOf(path));
170             }
171             else
172             {
173               sink.text("-");
174             }
175             sink.tableCell_();
176 
177             sink.tableCell();
178             sink.text(String.valueOf(desc.getParamName()));
179             sink.tableCell_();
180 
181             sink.tableCell();
182             final String description = desc.getDescription();
183             sink.text(description != null ? coder.run(description) : "");
184             sink.tableCell_();
185 
186             sink.tableRow_();
187           }
188         }
189       }
190       sink.table_();
191     }
192   }
193 
194   private void renderHeaderCell(final Sink sink, final ResourceBundle bundle,
195       final String key)
196   {
197     sink.tableHeaderCell();
198     sink.text(bundle.getString(key));
199     sink.tableHeaderCell_();
200   }
201 
202   @Override
203   protected void writeContent(final Sink sink,
204       final ProjectConfiguration<Sink> config,
205       final StoredExceptionCodesReport report) throws Exception // NOPMD
206   {
207     final ResourceBundle bundle = config.getBundle();
208     final String title = bundle.getString("report.name");
209     sink.head();
210     sink.title();
211     sink.text(title);
212     sink.title_();
213     sink.head_();
214 
215     sink.body();
216     sink.section1();
217 
218     sink.sectionTitle1();
219     sink.text(title);
220     sink.sectionTitle1_();
221 
222     sink.text(bundle.getString("report.description"));
223     sink.lineBreak();
224 
225     if (!report.isEmpty())
226     {
227       write(sink, config, report);
228     }
229     else
230     {
231       sink.lineBreak();
232       sink.lineBreak();
233       sink.text(bundle.getString("report.noElementsFound"));
234     }
235 
236     sink.section1_();
237     sink.body_();
238     // sink.flush();
239     // sink.close();
240   }
241 
242   @Override
243   protected void writeInfoFooter(final Sink sink,
244       final ProjectConfiguration<Sink> config) throws Exception // NOPMD
245   {
246     sink.table_();
247     sink.section2_();
248   }
249 
250   protected void writeInfoSubFooter(final Sink sink,
251       final ProjectConfiguration<Sink> config) throws Exception // NOPMD
252   {
253     sink.section3_();
254   }
255 
256   /**
257    * Writes the header and introduction text on a section 2.
258    * <p>
259    * If no <code>headLine</code> is given, the qualified class name is written
260    * as section title instead.
261    * </p>
262    *
263    * @param output the output to write to.
264    * @param config not used in this implementation.
265    * @param headLine the text for the section header. May be <code>null</code>.
266    * @param classDoc the type information to render if <code>headLine</code> is
267    *          <code>null</code> as section header and its comment to render as
268    *          introduction text.
269    * @throws Exception on any problem encountered.
270    */
271   @Override
272   protected void writeInfoHeader(final Sink output,
273       final ProjectConfiguration<Sink> config, final String headLine,
274       final ExceptionCodeReportItem item) throws Exception // NOPMD
275   {
276     final Sink sink = output;
277     final Code code = item.getCode();
278     final String componentId = code.getComponentId();
279     final JavaClass classDoc = item.getTypeJavadoc();
280     final String className = classDoc.getFullyQualifiedName();
281 
282     sink.section2();
283     sink.sectionTitle2();
284     final String title = getCodeTitle(headLine, item);
285     sink.text(title);
286     sink.sectionTitle2_();
287 
288     final String classComment = classDoc.getComment();
289     if (classComment != null)
290     {
291       final TextCoder coder = new TextCoder(config);
292       final String normalizedComment = normalize(coder, classComment);
293       sink.rawText(normalizedComment);
294     }
295 
296     if (StringUtils.isNotBlank(componentId))
297     {
298       sink.paragraph();
299       sink.monospaced();
300       final String relDir = config.getJavadocRelativeDir();
301       if (relDir != null)
302       {
303 
304         final String path = JavadocUtils.expandJavadocUrl(relDir, className);
305         sink.link(path);
306         sink.text(className);
307         sink.link_();
308       }
309       else
310       {
311         sink.text(className);
312       }
313 
314       sink.monospaced_();
315       sink.paragraph_();
316     }
317   }
318 
319   private static String normalize(final TextCoder coder,
320       final String classComment)
321   {
322     // Check if the first paragraph is embedded in a paragraph element.
323     // Usually it is not. In that cases we should add it.
324     final String trimmed = coder.run(classComment.trim());
325     if (!trimmed.startsWith("<"))
326     {
327       // TODO: We should check if the element is a block element or not.
328       final int index = trimmed.indexOf("<p");
329       if (index != -1)
330       {
331         return "<p>" + trimmed.substring(0, index) + "</p>"
332                + trimmed.substring(index);
333       }
334       else
335       {
336         return "<p>" + trimmed + "</p>\n";
337       }
338     }
339 
340     return trimmed;
341   }
342 
343   @Override
344   protected void writeInfoSubHeader(final Sink output,
345       final ProjectConfiguration<Sink> config, final String subHeader)
346   {
347     final Sink sink = output;
348     sink.section3();
349     sink.sectionTitle3();
350     sink.text(subHeader);
351     sink.sectionTitle3_();
352   }
353 
354   /**
355    * Writes the table header.
356    *
357    * @param output the output to write to.
358    * @param config the project configuration to control the output.
359    */
360   @Override
361   protected void writeTableHeader(final Sink sink,
362       final ProjectConfiguration<Sink> config)
363   {
364     sink.table();
365     sink.tableRow();
366 
367     final ResourceBundle bundle = config.getBundle();
368     final SinkEventAttributes attsFirstColumn = new SinkEventAttributeSet();
369     attsFirstColumn.addAttribute(SinkEventAttributes.WIDTH, 100);
370     sink.tableHeaderCell(attsFirstColumn);
371     final String codeLabel =
372         StringFunction.getMessage(bundle,
373             "report.exceptioncodes.item.reportElement", "Code");
374     sink.text(codeLabel);
375     sink.tableHeaderCell_();
376 
377     final SinkEventAttributes attsSecondColumn = new SinkEventAttributeSet();
378     attsSecondColumn.addAttribute(SinkEventAttributes.WIDTH, 150);
379     sink.tableHeaderCell(attsSecondColumn);
380     final String propertyNameLabel =
381         StringFunction.getMessage(bundle,
382             "report.exceptioncodes.item.propertyName", "Property Name");
383     sink.text(propertyNameLabel);
384     sink.tableHeaderCell_();
385 
386     sink.tableHeaderCell();
387     final String descriptionLabel =
388         StringFunction.getMessage(bundle,
389             "report.exceptioncodes.item.description", "Description");
390     sink.text(descriptionLabel);
391     sink.tableHeaderCell_();
392 
393     sink.tableRow_();
394   }
395 
396   @Override
397   protected void writeFooter(final Sink output,
398       final ProjectConfiguration<Sink> config)
399   {
400   }
401 
402   // --- object basics --------------------------------------------------------
403 
404 }