1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.taglib;
17
18 import java.io.File;
19 import java.math.RoundingMode;
20 import java.nio.file.Files;
21 import java.nio.file.Path;
22 import java.nio.file.Paths;
23 import java.text.DecimalFormat;
24 import java.text.ParseException;
25 import java.text.SimpleDateFormat;
26 import java.time.LocalDateTime;
27 import java.time.format.DateTimeFormatter;
28 import java.util.Base64;
29 import java.util.Date;
30 import java.util.Enumeration;
31 import java.util.List;
32 import java.util.Locale;
33 import java.util.Map;
34 import java.util.concurrent.ExecutionException;
35 import java.util.concurrent.TimeUnit;
36 import java.util.stream.Collectors;
37
38 import javax.servlet.http.HttpServletRequest;
39
40 import org.apache.commons.text.StringEscapeUtils;
41 import org.codelibs.core.lang.StringUtil;
42 import org.codelibs.fess.Constants;
43 import org.codelibs.fess.entity.FacetQueryView;
44 import org.codelibs.fess.helper.ViewHelper;
45 import org.codelibs.fess.util.ComponentUtil;
46 import org.lastaflute.di.util.LdiURLUtil;
47 import org.lastaflute.web.util.LaRequestUtil;
48 import org.lastaflute.web.util.LaResponseUtil;
49 import org.lastaflute.web.util.LaServletContextUtil;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53 import com.google.common.cache.CacheBuilder;
54 import com.google.common.cache.CacheLoader;
55 import com.google.common.cache.LoadingCache;
56
57 public class FessFunctions {
58 private static final Logger logger = LoggerFactory.getLogger(FessFunctions.class);
59
60 private static final String GEO_PREFIX = "geo.";
61
62 private static final String FACET_PREFIX = "facet.";
63
64 private static LoadingCache<String, Long> resourceHashCache = CacheBuilder.newBuilder().maximumSize(1000)
65 .expireAfterWrite(10, TimeUnit.MINUTES).build(new CacheLoader<String, Long>() {
66 @Override
67 public Long load(final String key) throws Exception {
68 try {
69 final Path path = Paths.get(LaServletContextUtil.getServletContext().getRealPath(key));
70 if (Files.isRegularFile(path)) {
71 return Files.getLastModifiedTime(path).toMillis();
72 }
73 } catch (final Exception e) {
74 logger.debug("Failed to access " + key, e);
75 }
76 return 0L;
77 }
78 });
79
80 protected FessFunctions() {
81
82 }
83
84 public static Boolean labelExists(final String value) {
85 @SuppressWarnings("unchecked")
86 final Map<String, String> labelValueMap = (Map<String, String>) LaRequestUtil.getRequest().getAttribute(Constants.LABEL_VALUE_MAP);
87 if (labelValueMap != null) {
88 return labelValueMap.get(value) != null;
89 }
90 return false;
91 }
92
93 public static String label(final String value) {
94 @SuppressWarnings("unchecked")
95 final Map<String, String> labelValueMap = (Map<String, String>) LaRequestUtil.getRequest().getAttribute(Constants.LABEL_VALUE_MAP);
96 if (labelValueMap != null) {
97 final String name = labelValueMap.get(value);
98 if (name != null) {
99 return name;
100 }
101 }
102 return value;
103 }
104
105 public static Date date(final Long value) {
106 if (value == null) {
107 return null;
108 }
109 return new Date(value.longValue());
110 }
111
112 public static Date parseDate(final String value) {
113 return parseDate(value, Constants.ISO_DATETIME_FORMAT);
114 }
115
116 public static Date parseDate(final String value, final String format) {
117 if (value == null) {
118 return null;
119 }
120 try {
121 final SimpleDateFormat sdf = new SimpleDateFormat(format);
122 sdf.setTimeZone(Constants.TIMEZONE_UTC);
123 return sdf.parse(value);
124 } catch (final ParseException e) {
125 return null;
126 }
127 }
128
129 public static String formatDate(final Date date) {
130 final SimpleDateFormat sdf = new SimpleDateFormat(Constants.ISO_DATETIME_FORMAT);
131 sdf.setTimeZone(Constants.TIMEZONE_UTC);
132 return sdf.format(date);
133 }
134
135 public static String formatDate(final LocalDateTime date) {
136 return date.format(DateTimeFormatter.ofPattern(Constants.ISO_DATETIME_FORMAT, Locale.ROOT));
137 }
138
139 public static String formatNumber(final long value) {
140 int ratio = 1;
141 String unit = "";
142 String format = "0.#";
143 if (value < 1024) {
144 format = "0";
145 } else if (value < (1024 * 1024)) {
146 ratio = 1024;
147 unit = "K";
148 } else if (value < (1024 * 1024 * 1024)) {
149 ratio = 1024 * 1024;
150 unit = "M";
151 } else {
152 ratio = 1024 * 1024 * 1024;
153 unit = "G";
154 }
155 final DecimalFormat df = new DecimalFormat(format + unit);
156 df.setRoundingMode(RoundingMode.HALF_UP);
157 return df.format((double) value / ratio);
158 }
159
160 public static String pagingQuery(final String query) {
161 final HttpServletRequest request = LaRequestUtil.getRequest();
162 @SuppressWarnings("unchecked")
163 final List<String> pagingQueryList = (List<String>) request.getAttribute(Constants.PAGING_QUERY_LIST);
164 if (pagingQueryList != null) {
165 final String prefix;
166 if (query != null) {
167 prefix = "ex_q=" + query.split(":")[0] + "%3A";
168 } else {
169 prefix = null;
170 }
171 return pagingQueryList.stream().filter(s -> prefix == null || !s.startsWith(prefix))
172 .collect(Collectors.joining("&", "&", StringUtil.EMPTY));
173 }
174 return StringUtil.EMPTY;
175 }
176
177 public static String facetQuery() {
178 return createQuery(Constants.FACET_QUERY, FACET_PREFIX);
179 }
180
181 public static String geoQuery() {
182 return createQuery(Constants.GEO_QUERY, GEO_PREFIX);
183 }
184
185 public static String facetForm() {
186 return createForm(Constants.FACET_FORM, FACET_PREFIX);
187 }
188
189 public static String geoForm() {
190 return createForm(Constants.GEO_FORM, GEO_PREFIX);
191 }
192
193 public static List<FacetQueryView> facetQueryViewList() {
194 final ViewHelper viewHelper = ComponentUtil.getViewHelper();
195 return viewHelper.getFacetQueryViewList();
196 }
197
198 private static String createQuery(final String key, final String prefix) {
199 final HttpServletRequest request = LaRequestUtil.getRequest();
200 String query = (String) request.getAttribute(key);
201 if (query == null) {
202 final StringBuilder buf = new StringBuilder(100);
203 final Enumeration<String> names = request.getParameterNames();
204 while (names.hasMoreElements()) {
205 final String name = names.nextElement();
206 if (name.startsWith(prefix)) {
207 final String[] values = request.getParameterValues(name);
208 if (values != null) {
209 for (final String value : values) {
210 buf.append('&');
211 buf.append(LdiURLUtil.encode(name, Constants.UTF_8));
212 buf.append('=');
213 buf.append(LdiURLUtil.encode(value, Constants.UTF_8));
214 }
215 }
216 }
217 }
218 query = buf.toString();
219 request.setAttribute(key, query);
220 }
221 return query;
222 }
223
224 private static String createForm(final String key, final String prefix) {
225 final HttpServletRequest request = LaRequestUtil.getRequest();
226 String query = (String) request.getAttribute(key);
227 if (query == null) {
228 final StringBuilder buf = new StringBuilder(100);
229 final Enumeration<String> names = request.getParameterNames();
230 while (names.hasMoreElements()) {
231 final String name = names.nextElement();
232 if (name.startsWith(prefix)) {
233 final String[] values = request.getParameterValues(name);
234 if (values != null) {
235 for (final String value : values) {
236 buf.append("<input type=\"hidden\" name=\"");
237 buf.append(StringEscapeUtils.escapeHtml4(name));
238 buf.append("\" value=\"");
239 buf.append(StringEscapeUtils.escapeHtml4(value));
240 buf.append("\"/>");
241 }
242 }
243 }
244 }
245 query = buf.toString();
246 request.setAttribute(key, query);
247 }
248 return query;
249 }
250
251 public static String base64(final String value) {
252 if (value == null) {
253 return StringUtil.EMPTY;
254 }
255 return Base64.getUrlEncoder().encodeToString(value.getBytes(Constants.CHARSET_UTF_8));
256 }
257
258 public static boolean fileExists(final String path) {
259 final File file = new File(LaServletContextUtil.getServletContext().getRealPath(path));
260 return file.exists();
261 }
262
263 public static String url(final String input) {
264 if (input == null) {
265 final String msg = "The argument 'input' should not be null.";
266 throw new IllegalArgumentException(msg);
267 }
268 if (!input.startsWith("/")) {
269 final String msg = "The argument 'input' should start with slash '/': " + input;
270 throw new IllegalArgumentException(msg);
271 }
272 final String contextPath = LaRequestUtil.getRequest().getContextPath();
273 final StringBuilder sb = new StringBuilder();
274 if (contextPath.length() > 1) {
275 sb.append(contextPath);
276 }
277 sb.append(input);
278 if (input.indexOf('?') == -1) {
279 try {
280 final Long value = resourceHashCache.get(input);
281 if (value.longValue() > 0) {
282 sb.append("?t=").append(value.toString());
283 }
284 } catch (final ExecutionException e) {
285 logger.debug("Failed to access " + input, e);
286 }
287 }
288 return LaResponseUtil.getResponse().encodeURL(sb.toString());
289 }
290
291 public static String sdh(final String input) {
292 if (StringUtil.isBlank(input)) {
293 return input;
294 }
295 return ComponentUtil.getDocumentHelper().encodeSimilarDocHash(input);
296 }
297 }