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

File IdGenerator.java

 
testGenerateUniqueIdWhenInvalidNonAlphaPrefix: The prefix [a-b] should only contain alphanumerical chara...
testGenerateUniqueIdWhenInvalidEmptyPrefix: The prefix [] should only contain alphanumerical characte...
 

Coverage histogram

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

Code metrics

26
49
7
1
201
93
23
0.47
7
7
3.29

Classes

Class Line # Actions
IdGenerator 35 49 0% 23 10
0.878048887.8%
 

Contributing tests

This file is covered by 169 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.util;
21   
22    import java.util.BitSet;
23    import java.util.HashSet;
24    import java.util.Set;
25   
26    import org.apache.commons.lang3.StringUtils;
27   
28    /**
29    * Stateful generator of id attributes. It's stateful since it remembers the generated ids. Thus a new instance of it
30    * should be used for each document.
31    *
32    * @version $Id: 728a171af6190b6fdebac04b697b130c2f64f501 $
33    * @since 1.6M1
34    */
 
35    public class IdGenerator
36    {
37    /**
38    * Id allowed characters {@link BitSet}.
39    */
40    private static final BitSet ALLOWED = new BitSet(256);
 
41  107 toggle static {
42    // digits
43  1177 for (int i = '0'; i <= '9'; i++) {
44  1070 ALLOWED.set(i);
45    }
46   
47    // alpha
48  2889 for (int i = 'a'; i <= 'z'; i++) {
49  2782 ALLOWED.set(i);
50    }
51  2889 for (int i = 'A'; i <= 'Z'; i++) {
52  2782 ALLOWED.set(i);
53    }
54   
55  107 ALLOWED.set(':');
56  107 ALLOWED.set('_');
57  107 ALLOWED.set('.');
58  107 ALLOWED.set('-');
59    }
60   
61    /**
62    * A table of hex digits.
63    */
64    private static final char[] HEXDIGIT =
65    { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
66   
67    /**
68    * Contains the already generated ids.
69    */
70    private Set<String> generatedIds = new HashSet<String>();
71   
72    /**
73    * Same as {@link #generateUniqueId(String, String)} but with a fixed prefix of "I".
74    *
75    * @param text the text used to generate the unique id
76    * @return the unique id. For example "Hello world" will generate "IHelloworld".
77    */
 
78  7 toggle public String generateUniqueId(String text)
79    {
80    // Note: We always use a prefix (and a prefix with alpha characters) so that the generated id is a valid HTML id
81    // (since HTML id must start with an alpha prefix).
82  7 return generateUniqueId("I", text);
83    }
84   
85    /**
86    * Generate a unique id attribute using the passed text as the seed value. The generated id complies with the XHTML
87    * specification. Extract from <a href="http://www.w3.org/TR/xhtml1/#C_8">XHTML RFC</a>:
88    * <p>
89    * <code> When defining fragment identifiers to be backward-compatible, only strings matching the pattern
90    * [A-Za-z][A-Za-z0-9:_.-]* should be used.</code>
91    * </p>
92    *
93    * @param prefix the prefix of the identifier. Has to match [a-zA-Z].
94    * @param text the text used to generate the unique id
95    * @return the unique id. For example "Hello world" will generate prefix + "Helloworld".
96    */
 
97  1078 toggle public String generateUniqueId(String prefix, String text)
98    {
99    // Verify that the passed prefix contains only alpha characters since the generated id must be a valid HTML id.
100  1076 if (StringUtils.isEmpty(prefix) || !StringUtils.isAlpha(prefix)) {
101  0 Test failure here throw new IllegalArgumentException("The prefix [" + prefix
102    + "] should only contain alphanumerical characters and not be empty.");
103    }
104   
105  1076 String idPrefix = (prefix != null ? prefix : "") + normalizeId(text);
106   
107  1076 int occurence = 0;
108  1076 String id = idPrefix;
109  1195 while (this.generatedIds.contains(id)) {
110  119 occurence++;
111  119 id = idPrefix + "-" + occurence;
112    }
113   
114    // Save the generated id so that the next call to this method will not generate the same id.
115  1076 this.generatedIds.add(id);
116   
117  1076 return id;
118    }
119   
120    /**
121    * Normalize passed string into valid string.
122    * <ul>
123    * <li>Remove white spaces: Clean white space since otherwise they'll get transformed into 20 by the below and thus
124    * for "Hello world" we would get "Hello20world" for the id. It's nicer to get "Helloworld".</li>
125    * <li>Convert all non allowed characters. See {@link #ALLOWED} for allowed characters.</li>
126    * </ul>
127    *
128    * @param stringToNormalize the string to normalize
129    * @return the normalized string
130    */
 
131  1076 toggle private String normalizeId(String stringToNormalize)
132    {
133  1076 int len = stringToNormalize.length();
134  1076 int bufLen = len * 2;
135  1076 if (bufLen < 0) {
136  0 bufLen = Integer.MAX_VALUE;
137    }
138  1076 StringBuffer outBuffer = new StringBuffer(bufLen);
139   
140  20919 for (int x = 0; x < len; x++) {
141  19843 char c = stringToNormalize.charAt(x);
142   
143  19843 if (ALLOWED.get(c)) {
144  17839 outBuffer.append(c);
145  2004 } else if (!Character.isWhitespace(c)) {
146  67 int nibble;
147  67 boolean skip = true;
148   
149  67 nibble = (c >> 12) & 0xF;
150  67 if (nibble != 0) {
151  2 skip = false;
152  2 outBuffer.append(toHex(nibble));
153    }
154   
155  67 nibble = (c >> 8) & 0xF;
156  67 if (!skip || nibble != 0) {
157  2 skip = false;
158  2 outBuffer.append(toHex(nibble));
159    }
160   
161  67 nibble = (c >> 4) & 0xF;
162  67 if (!skip || nibble != 0) {
163  67 outBuffer.append(toHex(nibble));
164    }
165   
166  67 outBuffer.append(toHex(c & 0xF));
167    }
168    }
169   
170  1076 return outBuffer.toString();
171    }
172   
173    /**
174    * Convert a nibble to a hex character.
175    *
176    * @param nibble the nibble to convert.
177    * @return hex character
178    */
 
179  138 toggle private char toHex(int nibble)
180    {
181  138 return HEXDIGIT[(nibble & 0xF)];
182    }
183   
184    /**
185    * Remove the saved previously generated id to make it available again.
186    *
187    * @param id the id to remove from the generated ids.
188    */
 
189  0 toggle public void remove(String id)
190    {
191  0 this.generatedIds.remove(id);
192    }
193   
194    /**
195    * Reset the known generated ids.
196    */
 
197  0 toggle public void reset()
198    {
199  0 this.generatedIds.clear();
200    }
201    }