1. Project Clover database Sat Feb 2 2019 06:45:20 CET
  2. Package org.xwiki.rendering.internal.parser.xhtml.wikimodel

File XWikiCommentHandler.java

 

Coverage histogram

../../../../../../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

30
85
9
1
277
173
31
0.36
9.44
9
3.44

Classes

Class Line # Actions
XWikiCommentHandler 59 85 0% 31 2
0.98387198.4%
 

Contributing tests

This file is covered by 332 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.parser.xhtml.wikimodel;
21   
22    import java.util.ArrayDeque;
23    import java.util.Deque;
24   
25    import org.xwiki.component.manager.ComponentLookupException;
26    import org.xwiki.component.manager.ComponentManager;
27    import org.xwiki.rendering.internal.parser.wikimodel.XWikiGeneratorListener;
28    import org.xwiki.rendering.internal.parser.xhtml.XHTMLParser;
29    import org.xwiki.rendering.listener.MetaData;
30    import org.xwiki.rendering.listener.reference.ResourceReference;
31    import org.xwiki.rendering.parser.ResourceReferenceParser;
32    import org.xwiki.rendering.renderer.PrintRenderer;
33    import org.xwiki.rendering.renderer.PrintRendererFactory;
34    import org.xwiki.rendering.renderer.printer.DefaultWikiPrinter;
35    import org.xwiki.rendering.renderer.reference.link.URILabelGenerator;
36    import org.xwiki.rendering.wikimodel.WikiParameter;
37    import org.xwiki.rendering.wikimodel.WikiParameters;
38    import org.xwiki.rendering.wikimodel.WikiReference;
39    import org.xwiki.rendering.wikimodel.xhtml.handler.CommentHandler;
40    import org.xwiki.rendering.wikimodel.xhtml.handler.TagHandler;
41    import org.xwiki.rendering.wikimodel.xhtml.impl.IgnoreElementRule;
42    import org.xwiki.rendering.wikimodel.xhtml.impl.MacroInfo;
43    import org.xwiki.rendering.wikimodel.xhtml.impl.TagStack;
44    import org.xwiki.xml.XMLUtils;
45   
46    import static org.xwiki.rendering.internal.parser.xhtml.wikimodel.XHTMLXWikiGeneratorListener.METADATA_ATTRIBUTE_PREFIX;
47    import static org.xwiki.rendering.wikimodel.xhtml.impl.MacroInfo.MACRO_START;
48    import static org.xwiki.rendering.wikimodel.xhtml.impl.MacroInfo.MACRO_STOP;
49    import static org.xwiki.rendering.wikimodel.xhtml.impl.TagStack.IGNORE_ALL;
50   
51    /**
52    * Handle Link and Macro definitions in comments (we store links in a comment since otherwise there are situations where
53    * it's not possible to reconstruct the original reference from the rendered HTML value and for macros it wouldn't be
54    * possible at all to reconstruct the macro).
55    *
56    * @version $Id: 5476c80b6f0341fdd045fe091630b5948b5bb644 $
57    * @since 1.7M1
58    */
 
59    public class XWikiCommentHandler extends CommentHandler implements XWikiWikiModelHandler
60    {
61    private XHTMLParser parser;
62   
63    private PrintRendererFactory xwikiSyntaxPrintRendererFactory;
64   
65    private ComponentManager componentManager;
66   
67    private ResourceReferenceParser xhtmlMarkerResourceReferenceParser;
68   
69    /**
70    * We're using a stack so that we can have nested comment handling. For example when we have a link to an image we
71    * need nested comment support.
72    */
73    private Deque<String> commentContentStack = new ArrayDeque<String>();
74   
75    /**
76    * @since 2.5RC1
77    * @todo Remove the need to pass a Parser when WikiModel implements support for wiki syntax in links. See
78    * http://code.google.com/p/wikimodel/issues/detail?id=87
79    */
 
80  334 toggle public XWikiCommentHandler(ComponentManager componentManager, XHTMLParser parser,
81    PrintRendererFactory xwikiSyntaxPrintRendererFactory,
82    ResourceReferenceParser xhtmlMarkerResourceReferenceParser)
83    {
84  334 this.componentManager = componentManager;
85  334 this.parser = parser;
86  334 this.xwikiSyntaxPrintRendererFactory = xwikiSyntaxPrintRendererFactory;
87  334 this.xhtmlMarkerResourceReferenceParser = xhtmlMarkerResourceReferenceParser;
88    }
89   
 
90  291 toggle @Override
91    public void onComment(String content, TagStack stack)
92    {
93    // if ignoreElements is true it means we are inside a macro or another block we don't want to parse content
94  291 boolean ignoreElements = stack.shouldIgnoreElements();
95   
96    // If the comment starts with "startwikilink" then we need to gather all XHTML tags inside
97    // the A tag, till we get a "stopwikilink" comment.
98    // Same for "startimage" and "stopimage".
99  291 if (!ignoreElements && content.startsWith("startwikilink:")) {
100  57 handleLinkCommentStart(XMLUtils.unescapeXMLComment(content), stack);
101  234 } else if (!ignoreElements && content.startsWith("stopwikilink")) {
102  57 handleLinkCommentStop(stack);
103  177 } else if (!ignoreElements && content.startsWith("startimage:")) {
104  23 handleImageCommentStart(XMLUtils.unescapeXMLComment(content), stack);
105  154 } else if (!ignoreElements && content.startsWith("stopimage")) {
106  23 handleImageCommentStop(stack);
107  131 } else if (content.startsWith(MACRO_START)) {
108  55 this.handleMacroCommentStart(XMLUtils.unescapeXMLComment(content), stack);
109  76 } else if (content.startsWith(MACRO_STOP)) {
110  57 this.handleMacroCommentStop(stack);
111    } else {
112  19 super.onComment(content, stack);
113    }
114    }
115   
 
116  55 toggle private void handleMacroCommentStart(String content, TagStack stack)
117    {
118  55 boolean shouldIgnoreAll;
119   
120    // true if we are already in a non generated content block
121  55 boolean inNonGeneratedContent = stack.getStackParameter(NON_GENERATED_CONTENT_STACK) != null
122    && (Boolean) stack.getStackParameter(NON_GENERATED_CONTENT_STACK);
123   
124    // if we are in a macro but not in a non generated content, we should ignore all
125  55 if (stack.getStackParameter(MACRO_INFO) != null && !inNonGeneratedContent) {
126  1 shouldIgnoreAll = true;
127    } else {
128  54 shouldIgnoreAll = false;
129    }
130   
131  55 MacroInfo macroInfo = new MacroInfo(content);
132  55 stack.pushStackParameter(MACRO_INFO, macroInfo);
133   
134    // we ignore all elements
135  55 if (shouldIgnoreAll) {
136  1 stack.setIgnoreElements();
137   
138    // we ignore elements until we get a non generated content: then the rule will be deactivated
139    // see IgnoreElementRule
140    } else {
141  54 stack.pushIgnoreElementRule(new IgnoreElementRule(tagContext -> {
142  300 WikiParameters wikiParameters = tagContext.getParams();
143  300 if (wikiParameters != null) {
144  300 return wikiParameters.getParameter(METADATA_ATTRIBUTE_PREFIX + MetaData.NON_GENERATED_CONTENT) != null;
145    }
146  0 return false;
147    }, true));
148    }
149    }
150   
 
151  57 toggle private void handleMacroCommentStop(TagStack stack)
152    {
153  57 if (stack.getStackParameter(MACRO_INFO) != null) {
154  55 MacroInfo macroInfo = (MacroInfo) stack.popStackParameter(MACRO_INFO);
155  55 IgnoreElementRule ignoreElementRule = stack.popIgnoreElementRule();
156   
157    // if we were ignoring all we don't want to output the macro
158  55 if (!ignoreElementRule.equals(IGNORE_ALL)) {
159  54 if (stack.isInsideBlockElement()) {
160  16 stack.getScannerContext().onMacroInline(macroInfo.getName(), macroInfo.getParameters(),
161    macroInfo.getContent());
162    } else {
163  38 TagHandler.sendEmptyLines(stack);
164  38 stack.getScannerContext().onMacroBlock(macroInfo.getName(), macroInfo.getParameters(),
165    macroInfo.getContent());
166    }
167    }
168    }
169    }
170   
 
171  57 toggle private void handleLinkCommentStart(String content, TagStack stack)
172    {
173    // Since wikimodel does not support wiki syntax in link labels we need to pass the link label "as is" (as it
174    // originally appears in the parsed source) and handle it specially in DefaultXWikiGeneratorListener, with the
175    // parser passed as the first parameter in the DefaultXWikiGeneratorListener constructor.
176    // Since we cannot get this label as it originally appeared in the HTML source ( we are doing a SAX-like
177    // parsing), we should render the XDOM as HTML to get an HTML label.
178    // Since any syntax would do it, as long as this renderer matches the corresponding
179    // DefaultXWikiGeneratorListener
180    // parser, we use an xwiki 2.1 renderer for it is less complex (no context needed to render xwiki 2.1, no url
181    // resolution needed, no reference validity tests).
182    // see DefaultXWikiGeneratorListener#DefaultXWikiGeneratorListener(Parser, ResourceReferenceParser, ImageParser)
183    // see WikiModelXHTMLParser#getLinkLabelParser()
184    // see http://code.google.com/p/wikimodel/issues/detail?id=87
185    // TODO: remove this workaround when wiki syntax in link labels will be supported by wikimodel
186  57 DefaultWikiPrinter printer = new DefaultWikiPrinter();
187   
188  57 PrintRenderer linkLabelRenderer = this.xwikiSyntaxPrintRendererFactory.createRenderer(printer);
189    // Make sure to flush whatever the renderer implementation
190  57 linkLabelRenderer.beginDocument(MetaData.EMPTY);
191   
192  57 XWikiGeneratorListener xwikiListener = this.parser.createXWikiGeneratorListener(linkLabelRenderer, null);
193   
194  57 stack.pushStackParameter(LINK_LISTENER, xwikiListener);
195   
196  57 stack.pushStackParameter(IS_IN_LINK, Boolean.TRUE);
197  57 stack.pushStackParameter(IS_FREE_STANDING_LINK, Boolean.FALSE);
198  57 stack.pushStackParameter(LINK_PARAMETERS, WikiParameters.EMPTY);
199   
200  57 this.commentContentStack.push(content.substring("startwikilink:".length()));
201    }
202   
 
203  57 toggle private void handleLinkCommentStop(TagStack stack)
204    {
205  57 XWikiGeneratorListener xwikiListener =
206    (XWikiGeneratorListener) stack.popStackParameter(LINK_LISTENER);
207  57 PrintRenderer linkLabelRenderer = (PrintRenderer) xwikiListener.getListener();
208   
209    // Make sure to flush whatever the renderer implementation
210  57 linkLabelRenderer.endDocument(MetaData.EMPTY);
211   
212  57 boolean isFreeStandingLink = (Boolean) stack.getStackParameter(IS_FREE_STANDING_LINK);
213   
214  57 ResourceReference linkReference = this.xhtmlMarkerResourceReferenceParser.parse(this.commentContentStack.pop());
215  57 WikiParameters linkParams = WikiParameters.EMPTY;
216  57 String label = null;
217  57 if (!isFreeStandingLink) {
218  44 label = linkLabelRenderer.getPrinter().toString();
219   
220    // Add the Link reference parameters to the link parameters.
221  44 linkParams = (WikiParameters) stack.getStackParameter(LINK_PARAMETERS);
222    }
223   
224  57 WikiReference wikiReference = new XWikiWikiReference(linkReference, label, linkParams, isFreeStandingLink);
225  57 stack.getScannerContext().onReference(wikiReference);
226   
227  57 stack.popStackParameter(IS_IN_LINK);
228  57 stack.popStackParameter(IS_FREE_STANDING_LINK);
229  57 stack.popStackParameter(LINK_PARAMETERS);
230    }
231   
 
232  23 toggle private void handleImageCommentStart(String content, TagStack stack)
233    {
234  23 stack.setStackParameter(IS_IN_IMAGE, Boolean.TRUE);
235  23 this.commentContentStack.push(content.substring("startimage:".length()));
236    }
237   
 
238  23 toggle private void handleImageCommentStop(TagStack stack)
239    {
240  23 boolean isFreeStandingImage = (Boolean) stack.getStackParameter(IS_FREE_STANDING_IMAGE);
241   
242  23 ResourceReference imageReference =
243    this.xhtmlMarkerResourceReferenceParser.parse(this.commentContentStack.pop());
244   
245  23 WikiParameters imageParams = WikiParameters.EMPTY;
246  23 if (!isFreeStandingImage) {
247    // Remove the ALT attribute if the content has the same value as the original image location
248    // This is because the XHTML renderer automatically adds an ALT attribute since it is mandatory
249    // in the XHTML specifications.
250  10 imageParams = (WikiParameters) stack.getStackParameter(IMAGE_PARAMETERS);
251  10 WikiParameter alt = imageParams.getParameter("alt");
252  10 if (alt != null && alt.getValue().equals(computeAltAttributeValue(imageReference))) {
253  8 imageParams = imageParams.remove("alt");
254    }
255    }
256   
257  23 WikiReference reference = new XWikiWikiReference(imageReference, null, imageParams, isFreeStandingImage);
258  23 stack.getScannerContext().onImage(reference);
259   
260  23 stack.setStackParameter(IS_IN_IMAGE, Boolean.FALSE);
261  23 stack.setStackParameter(IS_FREE_STANDING_IMAGE, Boolean.FALSE);
262  23 stack.setStackParameter(IMAGE_PARAMETERS, WikiParameters.EMPTY);
263    }
264   
 
265  8 toggle private String computeAltAttributeValue(ResourceReference reference)
266    {
267  8 String label;
268  8 try {
269  8 URILabelGenerator uriLabelGenerator =
270    this.componentManager.getInstance(URILabelGenerator.class, reference.getType().getScheme());
271  5 label = uriLabelGenerator.generateLabel(reference);
272    } catch (ComponentLookupException e) {
273  3 label = reference.getReference();
274    }
275  8 return label;
276    }
277    }