1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.crawler.transformer;
17
18 import java.lang.reflect.Array;
19 import java.net.URLDecoder;
20 import java.util.Arrays;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.LinkedHashMap;
25 import java.util.Map;
26
27 import org.apache.commons.lang3.StringUtils;
28 import org.apache.logging.log4j.Logger;
29 import org.codelibs.core.collection.LruHashMap;
30 import org.codelibs.core.lang.StringUtil;
31 import org.codelibs.fess.Constants;
32 import org.codelibs.fess.crawler.entity.AccessResult;
33 import org.codelibs.fess.crawler.entity.AccessResultData;
34 import org.codelibs.fess.crawler.entity.UrlQueue;
35 import org.codelibs.fess.crawler.util.CrawlingParameterUtil;
36 import org.codelibs.fess.crawler.util.FieldConfigs;
37 import org.codelibs.fess.mylasta.direction.FessConfig;
38 import org.codelibs.fess.util.ComponentUtil;
39
40
41
42
43
44
45 public interface FessTransformer {
46
47
48
49
50
51 Map<String, String> parentEncodingMap = Collections.synchronizedMap(new LruHashMap<>(1000));
52
53
54
55
56
57
58 FessConfig getFessConfig();
59
60
61
62
63
64
65 Logger getLogger();
66
67
68
69
70
71
72
73
74 default String getHost(final String u) {
75 if (StringUtil.isBlank(u)) {
76 return StringUtil.EMPTY;
77 }
78
79 String url = u;
80 final String originalUrl = url;
81
82 int idx = url.indexOf("://");
83 if (idx >= 0) {
84 url = url.substring(idx + 3);
85 }
86
87 idx = url.indexOf('/');
88 if (idx >= 0) {
89 url = url.substring(0, idx);
90 }
91
92 if (url.equals(originalUrl)) {
93 return getFessConfig().getCrawlerDocumentUnknownHostname();
94 }
95
96 return url;
97 }
98
99
100
101
102
103
104
105
106
107 default String getSite(final String u, final String encoding) {
108 if (StringUtil.isBlank(u)) {
109 return StringUtil.EMPTY;
110 }
111
112 String url = u;
113 int idx = url.indexOf("://");
114 if (idx >= 0) {
115 url = url.substring(idx + 3);
116 }
117
118 idx = url.indexOf('?');
119 if (idx >= 0) {
120 url = url.substring(0, idx);
121 }
122
123 if (encoding != null) {
124 String enc;
125 if (StringUtil.isNotBlank(getFessConfig().getCrawlerDocumentSiteEncoding())
126 && (!getFessConfig().isCrawlerDocumentUseSiteEncodingOnEnglish() || "ISO-8859-1".equalsIgnoreCase(encoding)
127 || "US-ASCII".equalsIgnoreCase(encoding))) {
128 enc = getFessConfig().getCrawlerDocumentSiteEncoding();
129 } else {
130 enc = encoding;
131 }
132
133 try {
134 url = URLDecoder.decode(url, enc);
135 } catch (final Exception e) {
136
137 if (getLogger().isDebugEnabled()) {
138 getLogger().debug("Failed to decode URL with encoding {}: {}", enc, url, e);
139 }
140 }
141 }
142
143 return abbreviateSite(url);
144 }
145
146
147
148
149
150
151
152
153
154 default void putResultDataBody(final Map<String, Object> dataMap, final String key, final Object value) {
155 final FessConfig fessConfig = ComponentUtil.getFessConfig();
156 if (fessConfig.getIndexFieldUrl().equals(key) || !dataMap.containsKey(key) || !getFessConfig().isCrawlerDocumentAppendData()) {
157 dataMap.put(key, value);
158 } else {
159 final Object oldValue = dataMap.get(key);
160 final Object[] oldValues;
161 if (oldValue instanceof Object[]) {
162 oldValues = (Object[]) oldValue;
163 } else if (oldValue instanceof Collection<?>) {
164 oldValues = ((Collection<?>) oldValue).toArray();
165 } else {
166 oldValues = new Object[] { oldValue };
167 }
168 if (value.getClass().isArray()) {
169
170 final int newLength = Array.getLength(value);
171 final Object[] values = Arrays.copyOf(oldValues, oldValues.length + newLength);
172 for (int i = 0; i < newLength; i++) {
173 values[oldValues.length + i] = Array.get(value, i);
174 }
175 dataMap.put(key, values);
176 } else {
177 final Object[] values = Arrays.copyOf(oldValues, oldValues.length + 1);
178 values[values.length - 1] = value;
179 dataMap.put(key, values);
180 }
181 }
182 }
183
184
185
186
187
188
189
190
191
192
193
194 default void putResultDataWithTemplate(final Map<String, Object> dataMap, final String key, final Object value, final String template,
195 final String scriptType) {
196 Object target = value;
197 if (template != null) {
198 final Map<String, Object> contextMap = new HashMap<>();
199 contextMap.put("doc", dataMap);
200 final Map<String, Object> paramMap = new HashMap<>(dataMap.size() + 2);
201 paramMap.putAll(dataMap);
202 paramMap.put("value", target);
203 paramMap.put("context", contextMap);
204 target = evaluateValue(scriptType, template, paramMap);
205 }
206 if (key != null && target != null) {
207 putResultDataBody(dataMap, key, target);
208 }
209 }
210
211
212
213
214
215
216
217
218
219 default Object evaluateValue(final String scriptType, final String template, final Map<String, Object> paramMap) {
220 if (StringUtil.isEmpty(template)) {
221 return StringUtil.EMPTY;
222 }
223
224 return ComponentUtil.getScriptEngineFactory().getScriptEngine(scriptType).evaluate(template, paramMap);
225 }
226
227
228
229
230
231
232 default int getMaxSiteLength() {
233 return getFessConfig().getCrawlerDocumentMaxSiteLengthAsInteger();
234 }
235
236
237
238
239
240
241
242 default String abbreviateSite(final String value) {
243 final int maxSiteLength = getMaxSiteLength();
244 if (maxSiteLength > -1) {
245 return StringUtils.abbreviate(value, maxSiteLength);
246 }
247 return value;
248 }
249
250
251
252
253
254
255
256
257
258 default String getFileName(final String url, final String encoding) {
259 if (StringUtil.isBlank(url)) {
260 return StringUtil.EMPTY;
261 }
262
263 int idx = 0;
264 String u = url;
265 if (u.startsWith("https:") || u.startsWith("http:")) {
266 idx = u.lastIndexOf('?');
267 if (idx >= 0) {
268 u = u.substring(0, idx);
269 }
270
271 idx = u.lastIndexOf('#');
272 if (idx >= 0) {
273 u = u.substring(0, idx);
274 }
275 }
276 if (!ComponentUtil.getProtocolHelper().shouldSkipUrlDecode(u)) {
277 u = decodeUrlAsName(u, u.startsWith("file:"));
278 }
279 idx = u.lastIndexOf('/');
280 if (idx >= 0) {
281 if (u.length() > idx + 1) {
282 u = u.substring(idx + 1);
283 } else {
284 u = StringUtil.EMPTY;
285 }
286 }
287 return u;
288 }
289
290
291
292
293
294
295
296
297
298 default String decodeUrlAsName(final String url, final boolean escapePlus) {
299 if (url == null) {
300 return null;
301 }
302
303 final FessConfig fessConfig = getFessConfig();
304 String enc = Constants.UTF_8;
305 if (StringUtil.isBlank(fessConfig.getCrawlerDocumentFileNameEncoding())) {
306 final UrlQueue<?> urlQueue = CrawlingParameterUtil.getUrlQueue();
307 if (urlQueue != null) {
308 final String parentUrl = urlQueue.getParentUrl();
309 if (StringUtil.isNotEmpty(parentUrl)) {
310 final String sessionId = urlQueue.getSessionId();
311 final String pageEnc = getParentEncoding(parentUrl, sessionId);
312 if (pageEnc != null) {
313 enc = pageEnc;
314 } else if (urlQueue.getEncoding() != null) {
315 enc = urlQueue.getEncoding();
316 }
317 }
318 }
319 } else {
320 enc = fessConfig.getCrawlerDocumentFileNameEncoding();
321 }
322
323 final String escapedUrl = escapePlus ? url.replace("+", "%2B") : url;
324 try {
325 return URLDecoder.decode(escapedUrl, enc);
326 } catch (final Exception e) {
327 return url;
328 }
329 }
330
331
332
333
334
335
336
337
338
339 default String getParentEncoding(final String parentUrl, final String sessionId) {
340 final String key = sessionId + ":" + parentUrl;
341 String enc = parentEncodingMap.get(key);
342 if (enc != null) {
343 return enc;
344 }
345
346 final AccessResult<?> accessResult = ComponentUtil.getDataService().getAccessResult(sessionId, parentUrl);
347 if (accessResult != null) {
348 final AccessResultData<?> accessResultData = accessResult.getAccessResultData();
349 if (accessResultData != null && accessResultData.getEncoding() != null) {
350 enc = accessResultData.getEncoding();
351 parentEncodingMap.put(key, enc);
352 return enc;
353 }
354 }
355 return null;
356 }
357
358
359
360
361
362
363
364
365
366 default Map<String, Object> processFieldConfigs(final Map<String, Object> dataMap, final FieldConfigs fieldConfigs) {
367 final Map<String, Object> newDataMap = new LinkedHashMap<>();
368 for (final Map.Entry<String, Object> e : dataMap.entrySet()) {
369 if (fieldConfigs.getConfig(e.getKey()).map(FieldConfigs.Config::isOverwrite).orElse(false)
370 && e.getValue() instanceof final Object[] values && values.length > 0) {
371 newDataMap.put(e.getKey(), values[values.length - 1]);
372 } else {
373 newDataMap.put(e.getKey(), e.getValue());
374 }
375 }
376 return newDataMap;
377 }
378
379 }