1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package io.wcm.handler.url.suffix.impl;
21
22 import java.util.HashMap;
23 import java.util.Map;
24
25 import org.apache.commons.lang3.StringUtils;
26 import org.apache.commons.lang3.text.translate.CharSequenceTranslator;
27 import org.apache.commons.lang3.text.translate.EntityArrays;
28 import org.apache.commons.lang3.text.translate.LookupTranslator;
29 import org.apache.sling.api.resource.Resource;
30
31
32
33
34 @SuppressWarnings("deprecation")
35 public final class UrlSuffixUtil {
36
37
38
39
40 public static final char SUFFIX_PART_DELIMITER = '/';
41
42
43
44
45 public static final char KEY_VALUE_DELIMITER = '=';
46
47
48
49
50 public static final String ESCAPE_DELIMITER = "~";
51
52
53
54
55 public static final String ESCAPED_SLASH = ESCAPE_DELIMITER + hexCode('/');
56
57
58
59
60 private static final String[][] SPECIAL_CHARS_MAPPING = {
61
62 {
63 Character.toString(SUFFIX_PART_DELIMITER), ESCAPE_DELIMITER + hexCode(SUFFIX_PART_DELIMITER)
64 },
65 {
66 Character.toString(KEY_VALUE_DELIMITER), ESCAPE_DELIMITER + hexCode(KEY_VALUE_DELIMITER)
67 },
68
69
70 {
71 Character.toString('.'), ESCAPE_DELIMITER + hexCode('.')
72 },
73
74 {
75 Character.toString('%'), ESCAPE_DELIMITER + hexCode('%')
76 },
77
78 {
79 Character.toString('/'), ESCAPE_DELIMITER + hexCode('/')
80 },
81
82 {
83 Character.toString(':'), ESCAPE_DELIMITER + hexCode(':')
84 },
85
86 {
87 Character.toString(' '), ESCAPE_DELIMITER + hexCode(' ')
88 }
89 };
90
91
92
93
94 private static final CharSequenceTranslator ESCAPE_SPECIAL_CHARS = new LookupTranslator(SPECIAL_CHARS_MAPPING);
95
96
97
98
99 private static final CharSequenceTranslator UNESCAPE_SPECIAL_CHARS = new LookupTranslator(EntityArrays.invert(SPECIAL_CHARS_MAPPING));
100
101 private UrlSuffixUtil() {
102
103 }
104
105
106
107
108
109
110 public static String hexCode(char c) {
111 return Integer.toString(c, 16).toUpperCase();
112 }
113
114
115
116
117
118
119 public static String encodeResourcePathPart(String relativePath) {
120 return ESCAPE_SPECIAL_CHARS.translate(relativePath);
121 }
122
123
124
125
126
127
128 public static String decodeResourcePathPart(String suffixPart) {
129 return UNESCAPE_SPECIAL_CHARS.translate(suffixPart);
130 }
131
132
133
134
135
136
137 public static String encodeKeyValuePart(String string) {
138 return ESCAPE_SPECIAL_CHARS.translate(string);
139 }
140
141
142
143
144
145
146 public static String decodeValue(String suffixPart) {
147
148 String value = StringUtils.substringAfter(suffixPart, Character.toString(KEY_VALUE_DELIMITER));
149 return UNESCAPE_SPECIAL_CHARS.translate(value);
150 }
151
152
153
154
155
156
157 public static String decodeKey(String suffixPart) {
158
159 String key = StringUtils.substringBefore(suffixPart, Character.toString(KEY_VALUE_DELIMITER));
160 return UNESCAPE_SPECIAL_CHARS.translate(key);
161 }
162
163
164
165
166
167
168 public static String[] splitSuffix(String suffix) {
169 String theSuffix = suffix;
170
171 String[] parts;
172 if (StringUtils.isBlank(theSuffix)) {
173
174 parts = new String[0];
175 }
176 else {
177
178 if (theSuffix.startsWith(ESCAPED_SLASH)) {
179 theSuffix = theSuffix.substring(ESCAPED_SLASH.length());
180 }
181
182
183 theSuffix = StringUtils.substringBeforeLast(theSuffix, ".");
184
185
186 parts = StringUtils.split(theSuffix, SUFFIX_PART_DELIMITER);
187 }
188
189 return parts;
190 }
191
192
193
194
195
196
197
198 public static Map<String, Object> keyValuePairAsMap(String key, Object value) {
199 Map<String, Object> paramMap = new HashMap<>();
200 paramMap.put(key, value);
201 return paramMap;
202 }
203
204
205
206
207
208
209 public static String getRelativePath(Resource resource, Resource baseResource) {
210 if (baseResource == null) {
211 throw new IllegalArgumentException("the base resource for constructing relative path must not be null");
212 }
213 if (resource == null) {
214 throw new IllegalArgumentException("the resource for constructing relative path must not be null");
215 }
216 String absolutePath = resource.getPath();
217 String basePath = baseResource.getPath();
218
219 if (absolutePath.equals(basePath)) {
220
221 return ".";
222 }
223
224
225 if (!absolutePath.startsWith(basePath + "/")) {
226 throw new IllegalArgumentException("the resource " + resource + " is not a descendent of the base resource " + baseResource);
227 }
228
229
230 return StringUtils.substringAfter(absolutePath, basePath + "/");
231 }
232
233 }