1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.rendering.internal.macro.footnote

File PutFootnotesMacro.java

 

Coverage histogram

../../../../../../img/srcFileCovDistChart9.png
38% of files have more coverage

Code metrics

16
61
7
1
235
139
17
0.28
8.71
7
2.43

Classes

Class Line # Actions
PutFootnotesMacro 58 61 0% 17 13
0.845238184.5%
 

Contributing tests

This file is covered by 2 tests. .

Source view

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.util.Collections;
23    import java.util.List;
24    import java.util.ListIterator;
25   
26    import javax.inject.Inject;
27    import javax.inject.Named;
28    import javax.inject.Singleton;
29   
30    import org.apache.commons.lang3.StringUtils;
31    import org.xwiki.component.annotation.Component;
32    import org.xwiki.rendering.block.Block;
33    import org.xwiki.rendering.block.FormatBlock;
34    import org.xwiki.rendering.block.LinkBlock;
35    import org.xwiki.rendering.block.ListItemBlock;
36    import org.xwiki.rendering.block.MacroMarkerBlock;
37    import org.xwiki.rendering.block.NumberedListBlock;
38    import org.xwiki.rendering.block.SpaceBlock;
39    import org.xwiki.rendering.block.WordBlock;
40    import org.xwiki.rendering.block.match.ClassBlockMatcher;
41    import org.xwiki.rendering.listener.Format;
42    import org.xwiki.rendering.listener.reference.DocumentResourceReference;
43    import org.xwiki.rendering.macro.AbstractMacro;
44    import org.xwiki.rendering.macro.MacroContentParser;
45    import org.xwiki.rendering.macro.MacroExecutionException;
46    import org.xwiki.rendering.macro.footnote.FootnoteMacroParameters;
47    import org.xwiki.rendering.transformation.MacroTransformationContext;
48   
49    /**
50    * List footnotes at the end of the page.
51    *
52    * @version $Id: 3cc0b501f10606fdb60502d1a49af00ac106f8af $
53    * @since 2.0M2
54    */
55    @Component
56    @Named(PutFootnotesMacro.MACRO_NAME)
57    @Singleton
 
58    public class PutFootnotesMacro extends AbstractMacro<FootnoteMacroParameters>
59    {
60    /** The name of this macro. */
61    public static final String MACRO_NAME = "putFootnotes";
62   
63    /** The description of the macro. */
64    private static final String DESCRIPTION =
65    "Displays the footnotes defined so far."
66    + " If missing, all footnotes are displayed by default at the end of the page.";
67   
68    /** ID attribute name. */
69    private static final String ID_ATTRIBUTE_NAME = "id";
70   
71    /** CSS Class attribute name. */
72    private static final String CLASS_ATTRIBUTE_NAME = "class";
73   
74    /** Prefix for the ID of the reference link to the footnote. */
75    private static final String FOOTNOTE_ID_PREFIX = "x_footnote_";
76   
77    /** Prefix for the ID of the footnote. */
78    private static final String FOOTNOTE_REFERENCE_ID_PREFIX = "x_footnote_ref_";
79   
80    /**
81    * Used to parse the content of the macro.
82    */
83    @Inject
84    private MacroContentParser contentParser;
85   
86    /**
87    * Create and initialize the descriptor of the macro.
88    */
 
89  3 toggle public PutFootnotesMacro()
90    {
91  3 super("Put Footnote", DESCRIPTION, FootnoteMacroParameters.class);
92  3 setDefaultCategory(DEFAULT_CATEGORY_CONTENT);
93    }
94   
 
95  0 toggle @Override
96    public boolean supportsInlineMode()
97    {
98  0 return false;
99    }
100   
 
101  3 toggle @Override
102    public List<Block> execute(FootnoteMacroParameters parameters, String content, MacroTransformationContext context)
103    throws MacroExecutionException
104    {
105  3 List<Block> result = Collections.emptyList();
106   
107    // Get the list of footnotes in the document
108  3 Block root = context.getXDOM();
109  3 List<MacroMarkerBlock> footnotes =
110    root.getBlocks(new ClassBlockMatcher(MacroMarkerBlock.class), Block.Axes.DESCENDANT);
111  29 for (ListIterator<MacroMarkerBlock> it = footnotes.listIterator(); it.hasNext();) {
112  26 MacroMarkerBlock macro = it.next();
113  26 if (FootnoteMacro.MACRO_NAME.equals(macro.getId())) {
114  2 continue;
115  24 } else if (PutFootnotesMacro.MACRO_NAME.equals(macro.getId())) {
116  0 macro.getParent().replaceChild(Collections.<Block>emptyList(), macro);
117    }
118  24 it.remove();
119    }
120  3 if (footnotes.size() <= 0) {
121  1 return result;
122    }
123   
124  2 NumberedListBlock container = new NumberedListBlock(Collections.<Block>emptyList());
125  2 container.setParameter(CLASS_ATTRIBUTE_NAME, "footnotes");
126  2 Block footnoteResult;
127   
128  2 int counter = 1;
129  2 for (MacroMarkerBlock footnote : footnotes) {
130  2 footnoteResult = processFootnote(footnote, counter, context);
131  2 if (footnoteResult != null) {
132  2 container.addChild(footnoteResult);
133  2 counter++;
134    }
135    }
136   
137  2 return Collections.<Block>singletonList(container);
138    }
139   
140    /**
141    * Processes a {{footnote}} macro, by generating a footnote element to insert in the footnote list and a reference
142    * to it, which is placed instead of the macro call.
143    *
144    * @param footnoteMacro the {{footnote}} macro element
145    * @param counter the current footnote counter
146    * @param context the execution context of the macro
147    * @return the footnote element which should be inserted in the footnote list
148    * @throws MacroExecutionException if the footnote content cannot be further processed
149    */
 
150  2 toggle private ListItemBlock processFootnote(MacroMarkerBlock footnoteMacro, int counter,
151    MacroTransformationContext context) throws MacroExecutionException
152    {
153  2 String content = footnoteMacro.getContent();
154  2 if (StringUtils.isBlank(content)) {
155  0 content = " ";
156    }
157    // Construct the footnote and reference blocks
158  2 Block referenceBlock = createFootnoteReferenceBlock(counter);
159  2 ListItemBlock footnoteBlock = createFootnoteBlock(content, counter, context);
160    // Insert the footnote and the reference in the document.
161  2 if (referenceBlock != null && footnoteBlock != null) {
162  2 addFootnoteRef(footnoteMacro, referenceBlock);
163  2 return footnoteBlock;
164    }
165  0 return null;
166    }
167   
168    /**
169    * Add a footnote to the list of document footnotes. If such a list doesn't exist yet, create it and append it to
170    * the end of the document.
171    *
172    * @param footnoteMacro the {{footnote}} macro being processed
173    * @param footnoteRef the generated block corresponding to the footnote to be inserted
174    */
 
175  2 toggle private void addFootnoteRef(MacroMarkerBlock footnoteMacro, Block footnoteRef)
176    {
177  2 for (ListIterator<Block> it = footnoteMacro.getChildren().listIterator(); it.hasNext();) {
178  0 Block b = it.next();
179  0 it.remove();
180    // b.replace(Collections.<Block> emptyList());
181    }
182  2 footnoteMacro.addChild(footnoteRef);
183    }
184   
185    /**
186    * Generate the footnote reference (link) that should be inserted at the location of the macro, and should point to
187    * the actual footnote at the end of the document.
188    *
189    * @param counter the current footnote counter
190    * @return the generated reference element, displayed as {@code (superscript(link(footnote index)))}
191    */
 
192  2 toggle private Block createFootnoteReferenceBlock(int counter)
193    {
194  2 Block result = new WordBlock(counter + "");
195  2 DocumentResourceReference reference = new DocumentResourceReference(null);
196  2 reference.setAnchor(FOOTNOTE_ID_PREFIX + counter);
197  2 result = new LinkBlock(Collections.singletonList(result), reference, false);
198  2 result = new FormatBlock(Collections.singletonList(result), Format.SUPERSCRIPT);
199  2 result.setParameter(ID_ATTRIBUTE_NAME, FOOTNOTE_REFERENCE_ID_PREFIX + counter);
200  2 result.setParameter(CLASS_ATTRIBUTE_NAME, "footnoteRef");
201  2 return result;
202    }
203   
204    /**
205    * Generate the footnote block, a numbered list item containing a backlink to the footnote's reference, and the
206    * actual footnote text, parsed into XDOM.
207    *
208    * @param content the string representation of the actual footnote text; the content of the macro
209    * @param counter the current footnote counter
210    * @param context the macro transformation context, used for obtaining the correct parser for parsing the content
211    * @return the generated footnote block
212    * @throws MacroExecutionException if parsing the content fails
213    */
 
214  2 toggle private ListItemBlock createFootnoteBlock(String content, int counter, MacroTransformationContext context)
215    throws MacroExecutionException
216    {
217  2 List<Block> parsedContent;
218  2 try {
219  2 parsedContent = this.contentParser.parse(content, context, false, true).getChildren();
220    } catch (MacroExecutionException e) {
221  0 parsedContent = Collections.<Block>singletonList(new WordBlock(content));
222    }
223  2 Block result = new WordBlock("^");
224  2 DocumentResourceReference reference = new DocumentResourceReference(null);
225  2 reference.setAnchor(FOOTNOTE_REFERENCE_ID_PREFIX + counter);
226  2 result = new LinkBlock(Collections.singletonList(result), reference, false);
227  2 result.setParameter(ID_ATTRIBUTE_NAME, FOOTNOTE_ID_PREFIX + counter);
228  2 result.setParameter(CLASS_ATTRIBUTE_NAME, "footnoteBackRef");
229  2 result = new ListItemBlock(Collections.singletonList(result));
230  2 result.addChild(new SpaceBlock());
231  2 result.addChildren(parsedContent);
232  2 result.setParameter(CLASS_ATTRIBUTE_NAME, FootnoteMacro.MACRO_NAME);
233  2 return (ListItemBlock) result;
234    }
235    }