Clover Coverage Report - XWiki Rendering - Parent POM 4.0-SNAPSHOT (Aggregated)
Coverage timestamp: Mon Mar 12 2012 18:03:13 CET
../../../../../../img/srcFileCovDistChart8.png 68% of files have more coverage
68   257   21   8.5
18   155   0.31   8
8     2.62  
1    
 
  PutFootnotesMacro       Line # 62 68 0% 21 21 77.7% 0.7765958
 
  (2)
 
1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.rendering.internal.macro.footnote;
21   
22    import java.io.StringReader;
23    import java.util.Collections;
24    import java.util.List;
25    import java.util.ListIterator;
26   
27    import javax.inject.Inject;
28    import javax.inject.Named;
29    import javax.inject.Singleton;
30   
31    import org.apache.commons.lang3.StringUtils;
32    import org.xwiki.component.annotation.Component;
33    import org.xwiki.component.manager.ComponentLookupException;
34    import org.xwiki.component.manager.ComponentManager;
35    import org.xwiki.rendering.block.Block;
36    import org.xwiki.rendering.block.FormatBlock;
37    import org.xwiki.rendering.block.LinkBlock;
38    import org.xwiki.rendering.block.ListItemBlock;
39    import org.xwiki.rendering.block.MacroMarkerBlock;
40    import org.xwiki.rendering.block.NumberedListBlock;
41    import org.xwiki.rendering.block.ParagraphBlock;
42    import org.xwiki.rendering.block.SpaceBlock;
43    import org.xwiki.rendering.block.WordBlock;
44    import org.xwiki.rendering.listener.reference.DocumentResourceReference;
45    import org.xwiki.rendering.listener.Format;
46    import org.xwiki.rendering.macro.AbstractMacro;
47    import org.xwiki.rendering.macro.MacroExecutionException;
48    import org.xwiki.rendering.macro.footnote.FootnoteMacroParameters;
49    import org.xwiki.rendering.parser.ParseException;
50    import org.xwiki.rendering.parser.Parser;
51    import org.xwiki.rendering.transformation.MacroTransformationContext;
52   
53    /**
54    * List footnotes at the end of the page.
55    *
56    * @version $Id: 0095c682e45b77bf680f068af862b3f9ef747058 $
57    * @since 2.0M2
58    */
59    @Component
60    @Named(PutFootnotesMacro.MACRO_NAME)
61    @Singleton
 
62    public class PutFootnotesMacro extends AbstractMacro<FootnoteMacroParameters>
63    {
64    /** The name of this macro. */
65    public static final String MACRO_NAME = "putFootnotes";
66   
67    /** The description of the macro. */
68    private static final String DESCRIPTION =
69    "Displays the footnotes defined so far."
70    + " If missing, all footnotes are displayed by default at the end of the page.";
71   
72    /** ID attribute name. */
73    private static final String ID_ATTRIBUTE_NAME = "id";
74   
75    /** CSS Class attribute name. */
76    private static final String CLASS_ATTRIBUTE_NAME = "class";
77   
78    /** Prefix for the ID of the reference link to the footnote. */
79    private static final String FOOTNOTE_ID_PREFIX = "x_footnote_";
80   
81    /** Prefix for the ID of the footnote. */
82    private static final String FOOTNOTE_REFERENCE_ID_PREFIX = "x_footnote_ref_";
83   
84    /** Used to get the current syntax parser. */
85    @Inject
86    private ComponentManager componentManager;
87   
88    /**
89    * Create and initialize the descriptor of the macro.
90    */
 
91  2 toggle public PutFootnotesMacro()
92    {
93  2 super("Put Footnote", DESCRIPTION, FootnoteMacroParameters.class);
94  2 setDefaultCategory(DEFAULT_CATEGORY_CONTENT);
95    }
96   
 
97  0 toggle @Override
98    public boolean supportsInlineMode()
99    {
100  0 return false;
101    }
102   
 
103  2 toggle @Override
104    public List<Block> execute(FootnoteMacroParameters parameters, String content, MacroTransformationContext context)
105    throws MacroExecutionException
106    {
107  2 List<Block> result = Collections.emptyList();
108   
109    // Get the list of footnotes in the document
110  2 Block root = context.getXDOM();
111  2 List<MacroMarkerBlock> footnotes = root.getChildrenByType(MacroMarkerBlock.class, true);
112  4 for (ListIterator<MacroMarkerBlock> it = footnotes.listIterator(); it.hasNext();) {
113  2 MacroMarkerBlock macro = it.next();
114  2 if (FootnoteMacro.MACRO_NAME.equals(macro.getId())) {
115  2 continue;
116  0 } else if (PutFootnotesMacro.MACRO_NAME.equals(macro.getId())) {
117  0 macro.getParent().replaceChild(Collections.<Block> emptyList(), macro);
118    }
119  0 it.remove();
120    }
121  2 if (footnotes.size() <= 0) {
122  0 return result;
123    }
124   
125  2 NumberedListBlock container = new NumberedListBlock(Collections.<Block> emptyList());
126  2 container.setParameter(CLASS_ATTRIBUTE_NAME, "footnotes");
127  2 Block footnoteResult;
128   
129  2 int counter = 1;
130  2 for (MacroMarkerBlock footnote : footnotes) {
131  2 footnoteResult = processFootnote(footnote, counter, context);
132  2 if (footnoteResult != null) {
133  2 container.addChild(footnoteResult);
134  2 counter++;
135    }
136    }
137   
138  2 return Collections.<Block> singletonList(container);
139    }
140   
141    /**
142    * Processes a {{footnote}} macro, by generating a footnote element to insert in the footnote list and a reference
143    * to it, which is placed instead of the macro call.
144    *
145    * @param footnoteMacro the {{footnote}} macro element
146    * @param counter the current footnote counter
147    * @param context the execution context of the macro
148    * @return the footnote element which should be inserted in the footnote list
149    * @throws MacroExecutionException if the footnote content cannot be further processed
150    */
 
151  2 toggle private ListItemBlock processFootnote(MacroMarkerBlock footnoteMacro, int counter,
152    MacroTransformationContext context) throws MacroExecutionException
153    {
154  2 String content = footnoteMacro.getContent();
155  2 if (StringUtils.isBlank(content)) {
156  0 content = " ";
157    }
158    // Construct the footnote and reference blocks
159  2 Block referenceBlock = createFootnoteReferenceBlock(counter);
160  2 ListItemBlock footnoteBlock = createFootnoteBlock(content, counter, context);
161    // Insert the footnote and the reference in the document.
162  2 if (referenceBlock != null && footnoteBlock != null) {
163  2 addFootnoteRef(footnoteMacro, referenceBlock);
164  2 return footnoteBlock;
165    }
166  0 return null;
167    }
168   
169    /**
170    * Add a footnote to the list of document footnotes. If such a list doesn't exist yet, create it and append it to
171    * the end of the document.
172    *
173    * @param footnoteMacro the {{footnote}} macro being processed
174    * @param footnoteRef the generated block corresponding to the footnote to be inserted
175    */
 
176  2 toggle private void addFootnoteRef(MacroMarkerBlock footnoteMacro, Block footnoteRef)
177    {
178  2 for (ListIterator<Block> it = footnoteMacro.getChildren().listIterator(); it.hasNext();) {
179  0 Block b = it.next();
180  0 it.remove();
181    // b.replace(Collections.<Block> emptyList());
182    }
183  2 footnoteMacro.addChild(footnoteRef);
184    }
185   
186    /**
187    * Generate the footnote reference (link) that should be inserted at the location of the macro, and should point to
188    * the actual footnote at the end of the document.
189    *
190    * @param counter the current footnote counter
191    * @return the generated reference element, displayed as {@code (superscript(link(footnote index)))}
192    */
 
193  2 toggle private Block createFootnoteReferenceBlock(int counter)
194    {
195  2 Block result = new WordBlock(counter + "");
196  2 DocumentResourceReference reference = new DocumentResourceReference(null);
197  2 reference.setAnchor(FOOTNOTE_ID_PREFIX + counter);
198  2 result = new LinkBlock(Collections.singletonList(result), reference, false);
199  2 result = new FormatBlock(Collections.singletonList(result), Format.SUPERSCRIPT);
200  2 ((FormatBlock) result).setParameter(ID_ATTRIBUTE_NAME, FOOTNOTE_REFERENCE_ID_PREFIX + counter);
201  2 ((FormatBlock) result).setParameter(CLASS_ATTRIBUTE_NAME, "footnoteRef");
202  2 return result;
203    }
204   
205    /**
206    * Generate the footnote block, a numbered list item containing a backlink to the footnote's reference, and the
207    * actual footnote text, parsed into XDOM.
208    *
209    * @param content the string representation of the actual footnote text; the content of the macro
210    * @param counter the current footnote counter
211    * @param context the macro transformation context, used for obtaining the correct parser for parsing the content
212    * @return the generated footnote block
213    * @throws MacroExecutionException if parsing the content fails
214    */
 
215  2 toggle private ListItemBlock createFootnoteBlock(String content, int counter, MacroTransformationContext context)
216    throws MacroExecutionException
217    {
218  2 Parser parser = getSyntaxParser(context);
219  2 List<Block> parsedContent = Collections.emptyList();
220  2 try {
221  2 parsedContent = parser.parse(new StringReader(content)).getChildren();
222  2 if (parsedContent.size() > 0 && parsedContent.get(0) instanceof ParagraphBlock) {
223  2 Block firstBlock = parsedContent.remove(0);
224  2 parsedContent.addAll(0, firstBlock.getChildren());
225    }
226    } catch (ParseException e) {
227  0 parsedContent = Collections.<Block> singletonList(new WordBlock(content));
228    }
229  2 Block result = new WordBlock("^");
230  2 DocumentResourceReference reference = new DocumentResourceReference(null);
231  2 reference.setAnchor(FOOTNOTE_REFERENCE_ID_PREFIX + counter);
232  2 result = new LinkBlock(Collections.singletonList(result), reference, false);
233  2 ((LinkBlock) result).setParameter(ID_ATTRIBUTE_NAME, FOOTNOTE_ID_PREFIX + counter);
234  2 ((LinkBlock) result).setParameter(CLASS_ATTRIBUTE_NAME, "footnoteBackRef");
235  2 result = new ListItemBlock(Collections.singletonList(result));
236  2 result.addChild(new SpaceBlock());
237  2 result.addChildren(parsedContent);
238  2 ((ListItemBlock) result).setParameter(CLASS_ATTRIBUTE_NAME, FootnoteMacro.MACRO_NAME);
239  2 return (ListItemBlock) result;
240    }
241   
242    /**
243    * Get the parser of the current wiki syntax.
244    *
245    * @param context the context of the macro transformation.
246    * @return the parser of the current wiki syntax.
247    * @throws MacroExecutionException Failed to find source parser.
248    */
 
249  2 toggle protected Parser getSyntaxParser(MacroTransformationContext context) throws MacroExecutionException
250    {
251  2 try {
252  2 return this.componentManager.lookup(Parser.class, context.getSyntax().toIdString());
253    } catch (ComponentLookupException e) {
254  0 throw new MacroExecutionException("Failed to find source parser", e);
255    }
256    }
257    }