Clover Coverage Report - XWiki Rendering - Parent POM 4.0-SNAPSHOT (Aggregated)
Coverage timestamp: Mon Mar 12 2012 18:03:13 CET
../../../../../../img/srcFileCovDistChart10.png 0% of files have more coverage
51   201   21   6.38
22   107   0.41   8
8     2.62  
1    
 
  XWikiSyntaxEscapeHandler       Line # 34 51 0% 21 2 97.5% 0.97530866
 
  (311)
 
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.renderer.xwiki20;
21   
22    import java.util.regex.Matcher;
23    import java.util.regex.Pattern;
24   
25    import org.apache.commons.lang3.StringUtils;
26    import org.xwiki.rendering.listener.chaining.BlockStateChainingListener;
27   
28    /**
29    * Escape characters that would be confused for XWiki wiki syntax if they were not escaped.
30    *
31    * @version $Id: cad6f5ca96c988f8e9189b418b7ba127ca6e13f4 $
32    * @since 2.0M3
33    */
 
34    public class XWikiSyntaxEscapeHandler
35    {
36    public static final Pattern STARLISTEND_PATTERN = Pattern.compile("(\\**([:;]*|1+\\.)?\\p{Blank})");
37   
38    private static final Pattern LIST_PATTERN = Pattern
39    .compile("\\p{Blank}*((\\*+[:;]*)|([1*]+\\.[:;]*)|([:;]+))\\p{Blank}+");
40   
41    private static final Pattern QUOTE_PATTERN = Pattern.compile("(\\>+)");
42   
43    private static final Pattern HEADER_PATTERN = Pattern.compile("\\p{Blank}*(=+)");
44   
45    private static final Pattern TABLE_PATTERN = Pattern.compile("\\p{Blank}*(\\||!!)");
46   
47    private static final Pattern DOUBLE_CHARS_PATTERN = Pattern.compile("\\/\\/|\\*\\*|__|--|\\^\\^|,,|##|\\\\\\\\");
48   
49    public static final String ESCAPE_CHAR = "~";
50   
51    private boolean beforeLink = false;
52   
53    private boolean onNewLine = true;
54   
 
55  2457 toggle public void setOnNewLine(boolean onNewLine)
56    {
57  2457 this.onNewLine = onNewLine;
58    }
59   
 
60  720 toggle public boolean isOnNewLine()
61    {
62  720 return this.onNewLine;
63    }
64   
 
65  640 toggle public void escape(StringBuffer accumulatedBuffer, XWikiSyntaxListenerChain listenerChain, boolean escapeLastChar,
66    Pattern escapeFirstIfMatching)
67    {
68  640 BlockStateChainingListener blockStateListener = listenerChain.getBlockStateChainingListener();
69   
70    // Escape tilde symbol (i.e. the escape character).
71    // Note: This needs to be the first replacement since other replacements below also use the tilde symbol
72  640 replaceAll(accumulatedBuffer, ESCAPE_CHAR, ESCAPE_CHAR + ESCAPE_CHAR);
73   
74    // Escape anything that looks like starting of custom parameters
75  640 replaceAll(accumulatedBuffer, "(%", ESCAPE_CHAR + "(%");
76   
77    // When in a paragraph we need to escape symbols that are at beginning of lines and that could be confused
78    // with list items, headers or tables.
79  640 if (blockStateListener.isInLine() && isOnNewLine()) {
80   
81    // Look for list pattern at beginning of line and escape the first character only (it's enough)
82  225 escapeFirstMatchedCharacter(LIST_PATTERN, accumulatedBuffer);
83   
84    // Look for header pattern at beginning of line and escape the first character only (it's enough)
85  225 escapeFirstMatchedCharacter(HEADER_PATTERN, accumulatedBuffer);
86   
87    // Look for table character patterns at beginning of line and escape the first character only (it's enough)
88  225 escapeFirstMatchedCharacter(TABLE_PATTERN, accumulatedBuffer);
89   
90    // Look for quote pattern at beginning of line and escape the first character only (it's enough)
91  225 escapeFirstMatchedCharacter(QUOTE_PATTERN, accumulatedBuffer);
92    }
93   
94    // Escape table characters
95  640 if (blockStateListener.isInTable()) {
96  82 replaceAll(accumulatedBuffer, "|", ESCAPE_CHAR + "|");
97  82 replaceAll(accumulatedBuffer, "!!", ESCAPE_CHAR + "!!");
98    }
99   
100  640 if (escapeFirstIfMatching != null) {
101  6 escapeFirstMatchedCharacter(escapeFirstIfMatching, accumulatedBuffer);
102    }
103   
104    // When in a header we need to escape "=" symbols since otherwise they would be confused for end of section
105    // characters.
106  640 if (blockStateListener.isInHeader()) {
107  52 replaceAll(accumulatedBuffer, "=", ESCAPE_CHAR + "=");
108    }
109   
110    // Escape verbatim "{{{"
111  640 replaceAll(accumulatedBuffer, "{{{", ESCAPE_CHAR + "{" + ESCAPE_CHAR + "{" + ESCAPE_CHAR + "{");
112   
113    // Escape "{{"
114  640 replaceAll(accumulatedBuffer, "{{", ESCAPE_CHAR + "{" + ESCAPE_CHAR + "{");
115   
116    // Escape groups
117  640 replaceAll(accumulatedBuffer, "(((", ESCAPE_CHAR + "(" + ESCAPE_CHAR + "(" + ESCAPE_CHAR + "(");
118  640 replaceAll(accumulatedBuffer, ")))", ESCAPE_CHAR + ")" + ESCAPE_CHAR + ")" + ESCAPE_CHAR + ")");
119   
120    // Escape reserved keywords
121  640 Matcher matcher = DOUBLE_CHARS_PATTERN.matcher(accumulatedBuffer.toString());
122  671 for (int i = 0; matcher.find(); i = i + 2) {
123  31 accumulatedBuffer.replace(matcher.start() + i, matcher.end() + i, ESCAPE_CHAR + matcher.group().charAt(0)
124    + ESCAPE_CHAR + matcher.group().charAt(1));
125    }
126   
127    // Escape ":" in "image:something", "attach:something" and "mailto:something"
128    // Note: even though there are some restriction in the URI specification as to what character is valid after
129    // the ":" character following the scheme we only check for characters greater than the space symbol for
130    // simplicity.
131  640 escapeURI(accumulatedBuffer, "image:");
132  640 escapeURI(accumulatedBuffer, "attach:");
133  640 escapeURI(accumulatedBuffer, "mailto:");
134   
135    // Escape last character if we're told to do so. This is to handle cases such as:
136    // - onWord("hello:") followed by onFormat(ITALIC) which would lead to "hello://" if the ":" wasn't escaped
137    // - onWord("{") followed by onMacro() which would lead to "{{{" if the "{" wasn't escaped
138  640 if (escapeLastChar) {
139  4 accumulatedBuffer.insert(accumulatedBuffer.length() - 1, '~');
140    }
141   
142    // Escape begin link
143  640 replaceAll(accumulatedBuffer, "[[", ESCAPE_CHAR + "[" + ESCAPE_CHAR + "[");
144   
145    // Escape link label
146  640 int linkLevel = getLinkLevel(listenerChain);
147   
148  640 if (linkLevel > 0) {
149    // This need to be done after anything else because link label add another level of escaping (escaped as
150    // link label and then escaped as wiki content).
151  23 String escape = StringUtils.repeat(ESCAPE_CHAR, linkLevel);
152  23 replaceAll(accumulatedBuffer, ESCAPE_CHAR, escape + ESCAPE_CHAR);
153  23 replaceAll(accumulatedBuffer, "]]", escape + "]" + escape + "]");
154  23 replaceAll(accumulatedBuffer, ">>", escape + ">" + escape + ">");
155  23 replaceAll(accumulatedBuffer, "||", escape + "|" + escape + "|");
156    }
157    }
158   
 
159  640 toggle private int getLinkLevel(XWikiSyntaxListenerChain listenerChain)
160    {
161  640 int linkDepth = listenerChain.getBlockStateChainingListener().getLinkDepth();
162   
163  640 if (this.beforeLink) {
164  19 --linkDepth;
165    }
166   
167  640 return linkDepth;
168    }
169   
 
170  156 toggle public void setBeforeLink(boolean beforeLink)
171    {
172  156 this.beforeLink = beforeLink;
173    }
174   
 
175  1920 toggle private void escapeURI(StringBuffer accumulatedBuffer, String match)
176    {
177  1920 int pos = accumulatedBuffer.indexOf(match);
178  1920 if (pos > -1) {
179    // Escape the ":" symbol
180  6 accumulatedBuffer.insert(pos + match.length() - 1, '~');
181    }
182    }
183   
 
184  4788 toggle private void replaceAll(StringBuffer accumulatedBuffer, String match, String replacement)
185    {
186  4788 int pos = -replacement.length();
187  ? while ((pos + replacement.length() < accumulatedBuffer.length())
188    && ((pos = accumulatedBuffer.indexOf(match, pos + replacement.length())) != -1)) {
189  40 accumulatedBuffer.replace(pos, pos + match.length(), replacement);
190    }
191    }
192   
 
193  906 toggle private void escapeFirstMatchedCharacter(Pattern pattern, StringBuffer accumulatedBuffer)
194    {
195  906 Matcher matcher = pattern.matcher(accumulatedBuffer);
196  906 if (matcher.lookingAt()) {
197    // Escape the first character
198  12 accumulatedBuffer.replace(matcher.start(1), matcher.start(1) + 1, ESCAPE_CHAR + matcher.group(1).charAt(0));
199    }
200    }
201    }