View Javadoc

1   /*
2    * Copyright 2012-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.properties.utils;
17  
18  import java.util.Arrays;
19  import java.util.HashSet;
20  import java.util.Set;
21  import java.util.regex.Matcher;
22  import java.util.regex.Pattern;
23  
24  import org.apache.commons.lang.StringUtils;
25  
26  /**
27   * Utilities to normalize Javadoc comments.
28   */
29  public final class JavadocCommentHelper
30  {
31    // ********************************* Fields *********************************
32  
33    // --- constants ------------------------------------------------------------
34  
35    /**
36     * The pattern matches inline Javadoc tags. In group 1 is the text that is to
37     * be used and surrounded with {@link #prefix1} and {@link #suffix1}, if there
38     * is no match for group 2. If group 2 is present, the text is to be used
39     * without prefix/suffix 2.
40     */
41    private static final Pattern INLINE_PATTERN = Pattern.compile(
42        "\\{@[\\w]+\\s*([#\\.\\w]+(\\([^)]*\\))?)\\s*([^}]+)?\\}",
43        Pattern.MULTILINE);
44  
45    /**
46     * The elements that are of type block.
47     */
48    private static final Set<String> BLOCK_ELEMENTS = new HashSet<String>(
49        Arrays.asList(new String[] { "address", "blockquote", "center", "del",
50                                    "dir", "div", "dl", "fieldset", "form", "h1",
51                                    "h2", "h3", "h4", "h5", "h5", "hr", "ins",
52                                    "isindex", "menu", "noframes", "noscript",
53                                    "ol", "p", "pre", "table", "ul" }));
54  
55    // --- members --------------------------------------------------------------
56  
57    /**
58     * The prefix for group 1 match.
59     */
60    private final String prefix1;
61  
62    /**
63     * The suffix for group 1 match.
64     */
65    private final String suffix1;
66  
67    /**
68     * The prefix for group 2 match.
69     */
70    private final String prefix2;
71  
72    /**
73     * The suffix for group 2 match.
74     */
75    private final String suffix2;
76  
77    // ****************************** Initializer *******************************
78  
79    // ****************************** Constructors ******************************
80  
81    /**
82     * Default constructor.
83     *
84     * @param prefix1 the prefix for group 1 match.
85     * @param suffix1 the suffix for group 1 match.
86     * @param prefix2 the prefix for group 2 match.
87     * @param suffix2 the suffix for group 2 match.
88     */
89    public JavadocCommentHelper(final String prefix1, final String suffix1,
90        final String prefix2, final String suffix2)
91    {
92      this.prefix1 = prefix1;
93      this.suffix1 = suffix1;
94      this.prefix2 = prefix2;
95      this.suffix2 = suffix2;
96    }
97  
98    // ****************************** Inner Classes *****************************
99  
100   // ********************************* Methods ********************************
101 
102   // --- init -----------------------------------------------------------------
103 
104   // --- create ---------------------------------------------------------------
105 
106   /**
107    * Creates a default helper for normalizing in an HTML context.
108    *
109    * @return the HTML version of the helper.
110    */
111   public static JavadocCommentHelper createHtml()
112   {
113     return new JavadocCommentHelper("<tt>", "</tt>", "<i>", "</i>");
114   }
115 
116   /**
117    * Creates a default helper for normalizing in text context.
118    *
119    * @return the text version of the helper.
120    */
121   public static JavadocCommentHelper createText()
122   {
123     return new JavadocCommentHelper("'", "'", "", "");
124   }
125 
126   // --- get&set --------------------------------------------------------------
127 
128   // --- business -------------------------------------------------------------
129 
130   /**
131    * Usually the first paragraph in a Javadoc comment is not tagged as a
132    * paragraph to reduce the markup. This helper probes if the block tag is
133    * present and if not adds it.
134    *
135    * @param fragment the javadoc fragment to check.
136    * @return the cleaned fragment.
137    */
138   public String expandFirstBlock(final String fragment)
139   {
140     if (StringUtils.isBlank(fragment))
141     {
142       return fragment;
143     }
144 
145     final String norm = fragment.trim();
146     if (isStartingWithTag(norm))
147     {
148       final String elementName = calcElementName(norm);
149       if (isBlockElement(elementName))
150       {
151         final int index = norm.indexOf("</" + elementName);
152         return createBlock(norm, index);
153       }
154     }
155     else
156     {
157       final int index = calcIndexOfFirstBlockElement(norm);
158       return createBlock(norm, index);
159     }
160 
161     return fragment;
162   }
163 
164   private static int calcIndexOfFirstBlockElement(final String fragment)
165   {
166     int index = Integer.MAX_VALUE;
167     for (final String elementName : BLOCK_ELEMENTS)
168     {
169       final int currentIndex = fragment.indexOf('<' + elementName);
170       if (currentIndex != -1)
171       {
172         index = Math.min(currentIndex, index);
173       }
174     }
175 
176     return index != Integer.MAX_VALUE ? index : -1;
177   }
178 
179   private static String createBlock(final String fragment, final int index)
180   {
181     if (index != -1)
182     {
183       final String firstBlock = fragment.substring(0, index);
184       final String rest = fragment.substring(index);
185       return "<p>" + firstBlock + "</p>\n" + rest;
186     }
187     else
188     {
189       return "<p>" + fragment + "</p>";
190     }
191   }
192 
193   private static boolean isStartingWithTag(final String fragment)
194   {
195     return fragment.startsWith("<");
196   }
197 
198   private static String calcElementName(final String fragment)
199   {
200     final StringBuilder buffer = new StringBuilder();
201     final int end = fragment.length();
202     int i = 1;
203     while (i < end)
204     {
205       final char ch = fragment.charAt(i++);
206       if (Character.isLetterOrDigit(ch))
207       {
208         buffer.append(ch);
209       }
210     }
211 
212     final String name = buffer.toString();
213     return name;
214   }
215 
216   private static boolean isBlockElement(final String elementName)
217   {
218     return BLOCK_ELEMENTS.contains(elementName);
219   }
220 
221   /**
222    * Replaces Javadoc inline tags.
223    *
224    * @param fragment the fragment with inline tags.
225    * @return the normalized fragment.
226    */
227   public String replaceJavadocInlines(final String fragment)
228   {
229     if (StringUtils.isBlank(fragment))
230     {
231       return fragment;
232     }
233 
234     final StringBuffer buffer = new StringBuffer(fragment.length());
235     final Matcher matcher = INLINE_PATTERN.matcher(fragment);
236 
237     while (matcher.find())
238     {
239       final String group2 = matcher.group(3);
240       if (group2 != null)
241       {
242         final String replacement = prefix2 + group2 + suffix2;
243         matcher.appendReplacement(buffer, replacement);
244       }
245       else
246       {
247         String group1 = matcher.group(1);
248         if (group1.length() > 1 && group1.charAt(0) == '#')
249         {
250           group1 = group1.substring(1);
251         }
252         final String replacement = prefix1 + group1 + suffix1;
253         matcher.appendReplacement(buffer, replacement);
254       }
255     }
256     matcher.appendTail(buffer);
257 
258     return buffer.toString();
259   }
260 
261   // --- object basics --------------------------------------------------------
262 
263 }