1
2
3
4
5
6
7
8
9
10
11 package psiprobe;
12
13 import com.google.common.base.Strings;
14
15 import jakarta.servlet.http.HttpServletRequest;
16 import jakarta.servlet.http.HttpServletResponse;
17
18 import java.io.BufferedInputStream;
19 import java.io.BufferedReader;
20 import java.io.ByteArrayInputStream;
21 import java.io.ByteArrayOutputStream;
22 import java.io.File;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.InputStreamReader;
26 import java.io.OutputStream;
27 import java.io.RandomAccessFile;
28 import java.io.Reader;
29 import java.lang.management.ManagementFactory;
30 import java.nio.charset.Charset;
31 import java.nio.charset.StandardCharsets;
32 import java.nio.file.Files;
33 import java.util.ArrayList;
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.Scanner;
37 import java.util.Set;
38 import java.util.zip.ZipEntry;
39 import java.util.zip.ZipOutputStream;
40
41 import javax.management.MBeanServer;
42 import javax.management.MalformedObjectNameException;
43 import javax.management.ObjectInstance;
44 import javax.management.ObjectName;
45
46 import org.codelibs.jhighlight.renderer.Renderer;
47 import org.codelibs.jhighlight.renderer.XhtmlRendererFactory;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 import psiprobe.tokenizer.StringTokenizer;
52 import psiprobe.tokenizer.Token;
53 import psiprobe.tokenizer.Tokenizer;
54 import psiprobe.tokenizer.TokenizerSymbol;
55
56
57
58
59 public final class Utils {
60
61
62 private static final Logger logger = LoggerFactory.getLogger(Utils.class);
63
64
65
66
67 private Utils() {
68
69 }
70
71
72
73
74
75
76
77
78
79
80
81
82 public static String readFile(File file, String charsetName) throws IOException {
83 try (InputStream fis = Files.newInputStream(file.toPath())) {
84 return readStream(fis, charsetName);
85 }
86 }
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103 public static String readStream(InputStream is, String charsetName) throws IOException {
104 Charset charset;
105 if (Charset.isSupported(charsetName)) {
106 charset = Charset.forName(charsetName);
107 } else {
108
109 charset = Charset.defaultCharset();
110 }
111
112 StringBuilder out = new StringBuilder();
113 try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, charset), 4096)) {
114 String line;
115 while ((line = reader.readLine()) != null) {
116 out.append(line).append('\n');
117 }
118 }
119
120 return out.toString();
121 }
122
123
124
125
126
127
128 public static void delete(File file) {
129 if (file != null && file.exists()) {
130 if (file.isDirectory()) {
131 for (File child : file.listFiles()) {
132 delete(child);
133 }
134 }
135 try {
136 Files.delete(file.toPath());
137 } catch (IOException e) {
138 logger.debug("Cannot delete '{}'", file.getAbsolutePath(), e);
139 }
140 } else {
141 logger.debug("'{}' does not exist", file);
142 }
143 }
144
145
146
147
148
149
150
151
152
153 public static int toInt(String num, int defaultValue) {
154 if (num != null && !num.contains(" ")) {
155 try (Scanner scanner = new Scanner(num)) {
156 if (scanner.hasNextInt()) {
157 return scanner.nextInt();
158 }
159 }
160 }
161 return defaultValue;
162 }
163
164
165
166
167
168
169
170
171
172 public static int toIntHex(String num, int defaultValue) {
173 if (num != null && !num.contains(" ")) {
174 if (num.startsWith("#")) {
175 num = num.substring(1);
176 }
177 try (Scanner scanner = new Scanner(num)) {
178 if (scanner.hasNextInt()) {
179 return scanner.nextInt(16);
180 }
181 }
182 }
183 return defaultValue;
184 }
185
186
187
188
189
190
191
192
193
194 public static long toLong(String num, long defaultValue) {
195 if (num != null && !num.contains(" ")) {
196 try (Scanner scanner = new Scanner(num)) {
197 if (scanner.hasNextLong()) {
198 return scanner.nextLong();
199 }
200 }
201 }
202 return defaultValue;
203 }
204
205
206
207
208
209
210
211
212
213 public static long toLong(Long num, long defaultValue) {
214 return num == null ? defaultValue : num;
215 }
216
217
218
219
220
221
222
223
224
225 public static float toFloat(String num, float defaultValue) {
226 if (num != null && !num.contains(" ")) {
227 try (Scanner scanner = new Scanner(num)) {
228 if (scanner.hasNextFloat()) {
229 return scanner.nextFloat();
230 }
231 }
232 }
233 return defaultValue;
234 }
235
236
237
238
239
240
241
242
243
244
245 public static String getJspEncoding(InputStream is) throws IOException {
246
247 String encoding = null;
248 String contentType = null;
249
250 Tokenizer jspTokenizer = new Tokenizer();
251 jspTokenizer.addSymbol("\n", true);
252 jspTokenizer.addSymbol(" ", true);
253 jspTokenizer.addSymbol("\t", true);
254 jspTokenizer.addSymbol(new TokenizerSymbol("dir", "<%@", "%>", false, false, true, false));
255
256 StringTokenizer directiveTokenizer = new StringTokenizer();
257 directiveTokenizer.addSymbol("\n", true);
258 directiveTokenizer.addSymbol(" ", true);
259 directiveTokenizer.addSymbol("\t", true);
260 directiveTokenizer.addSymbol("=");
261 directiveTokenizer.addSymbol("\"", "\"", false);
262 directiveTokenizer.addSymbol("'", "'", false);
263
264 StringTokenizer contentTypeTokenizer = new StringTokenizer();
265 contentTypeTokenizer.addSymbol(" ", true);
266 contentTypeTokenizer.addSymbol(";", true);
267
268 try (Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
269 jspTokenizer.setReader(reader);
270 while (jspTokenizer.hasMore()) {
271 Token token = jspTokenizer.nextToken();
272 if ("dir".equals(token.getName())) {
273 directiveTokenizer.setString(token.getInnerText());
274 if (directiveTokenizer.hasMore()
275 && "page".equals(directiveTokenizer.nextToken().getText())) {
276 while (directiveTokenizer.hasMore()) {
277 Token directiveToken = directiveTokenizer.nextToken();
278 if ("pageEncoding".equals(directiveToken.getText())) {
279 if (directiveTokenizer.hasMore()
280 && "=".equals(directiveTokenizer.nextToken().getText())
281 && directiveTokenizer.hasMore()) {
282 encoding = directiveTokenizer.nextToken().getInnerText();
283 break;
284 }
285 } else if ("contentType".equals(directiveToken.getText())
286 && directiveTokenizer.hasMore()
287 && "=".equals(directiveTokenizer.nextToken().getText())
288 && directiveTokenizer.hasMore()) {
289 contentType = directiveTokenizer.nextToken().getInnerText();
290 }
291 }
292 }
293 }
294 }
295 }
296
297 if (encoding == null && contentType != null) {
298 contentTypeTokenizer.setString(contentType);
299 while (contentTypeTokenizer.hasMore()) {
300 String token = contentTypeTokenizer.nextToken().getText();
301 if (token.startsWith("charset=")) {
302 encoding = token.substring("charset=".length());
303 break;
304 }
305 }
306 }
307
308 return encoding != null ? encoding : StandardCharsets.UTF_8.name();
309 }
310
311
312
313
314
315
316
317
318
319
320 public static void sendFile(HttpServletRequest request, HttpServletResponse response, File file)
321 throws IOException {
322
323 try (OutputStream out = response.getOutputStream();
324 RandomAccessFile raf = new RandomAccessFile(file, "r")) {
325 long fileSize = raf.length();
326 long rangeStart = 0;
327 long rangeFinish = fileSize - 1;
328
329
330 String range = request.getHeader("Range");
331 if (range != null && range.startsWith("bytes=")) {
332 String pureRange = range.replace("bytes=", "");
333 int rangeSep = pureRange.indexOf('-');
334
335 try {
336 rangeStart = Long.parseLong(pureRange.substring(0, rangeSep));
337 if (rangeStart > fileSize || rangeStart < 0) {
338 rangeStart = 0;
339 }
340 } catch (NumberFormatException e) {
341
342 logger.trace("", e);
343 }
344
345 if (rangeSep < pureRange.length() - 1) {
346 try {
347 rangeFinish = Long.parseLong(pureRange.substring(rangeSep + 1));
348 if (rangeFinish < 0 || rangeFinish >= fileSize) {
349 rangeFinish = fileSize - 1;
350 }
351 } catch (NumberFormatException e) {
352 logger.trace("", e);
353 }
354 }
355 }
356
357
358 response.setContentType("application/x-download");
359 response.setHeader("Content-Disposition", "attachment; filename=" + file.getName());
360 response.setHeader("Accept-Ranges", "bytes");
361 response.setHeader("Content-Length", Long.toString(rangeFinish - rangeStart + 1));
362 response.setHeader("Content-Range",
363 "bytes " + rangeStart + "-" + rangeFinish + "/" + fileSize);
364
365
366 raf.seek(rangeStart);
367
368
369 byte[] buffer = new byte[4096];
370
371 long len;
372 int totalRead = 0;
373 boolean nomore = false;
374 while (true) {
375 len = raf.read(buffer);
376 if (len > 0 && totalRead + len > rangeFinish - rangeStart + 1) {
377
378
379 len = rangeFinish - rangeStart + 1 - totalRead;
380 nomore = true;
381 }
382
383 if (len <= 0) {
384 break;
385 }
386 out.write(buffer, 0, (int) len);
387 totalRead = totalRead + (int) len;
388 if (nomore) {
389 break;
390 }
391 }
392 }
393 }
394
395
396
397
398
399
400
401
402 public static Thread getThreadByName(String name) {
403 if (name != null) {
404
405 ThreadGroup masterGroup = Thread.currentThread().getThreadGroup();
406 while (masterGroup.getParent() != null) {
407 masterGroup = masterGroup.getParent();
408 }
409
410 Thread[] threads = new Thread[masterGroup.activeCount()];
411 masterGroup.enumerate(threads);
412
413 for (Thread thread : threads) {
414 if (thread != null && name.equals(thread.getName())) {
415 return thread;
416 }
417 }
418 }
419 return null;
420 }
421
422
423
424
425
426
427
428
429
430
431
432
433
434 public static String highlightStream(String name, InputStream input, String rendererName,
435 String encoding) throws IOException {
436
437 Renderer jspRenderer = XhtmlRendererFactory.getRenderer(rendererName);
438 if (jspRenderer == null) {
439 return null;
440 }
441
442 ByteArrayOutputStream bos = new ByteArrayOutputStream();
443 jspRenderer.highlight(name, input, bos, encoding, true);
444
445 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
446
447 Tokenizer tokenizer = new Tokenizer(new InputStreamReader(bis, Charset.forName(encoding)));
448 tokenizer.addSymbol(new TokenizerSymbol("EOL", "\n", null, false, false, true, false));
449 tokenizer.addSymbol(new TokenizerSymbol("EOL", "\r\n", null, false, false, true, false));
450
451
452
453
454
455
456 StringBuilder buffer = new StringBuilder();
457 long counter = 0;
458 while (tokenizer.hasMore()) {
459 Token tk = tokenizer.nextToken();
460 if ("EOL".equals(tk.getName())) {
461 counter++;
462 buffer.append(tk.getText());
463 } else if (counter > 0) {
464 buffer.append("<span class=\"codeline\">");
465 buffer.append("<span class=\"linenum\">");
466 buffer.append(leftPad(Long.toString(counter), 6, " ").replace(" ", " "));
467 buffer.append("</span>");
468 buffer.append(tk.getText());
469 buffer.append("</span>");
470 }
471 }
472 return buffer.toString();
473 }
474
475
476
477
478
479
480
481
482
483 public static void sendCompressedFile(HttpServletResponse response, File file)
484 throws IOException {
485 try (ZipOutputStream zip = new ZipOutputStream(response.getOutputStream());
486 InputStream fileInput = new BufferedInputStream(Files.newInputStream(file.toPath()))) {
487
488 String fileName = file.getName();
489
490
491 response.setContentType("application/zip");
492 response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".zip");
493
494 zip.putNextEntry(new ZipEntry(fileName));
495
496
497 byte[] buffer = new byte[4096];
498 long len;
499
500 while ((len = fileInput.read(buffer)) > 0) {
501 zip.write(buffer, 0, (int) len);
502 }
503 zip.closeEntry();
504 }
505 }
506
507
508
509
510
511
512
513
514
515
516 static String leftPad(final String str, final int len, final String fill) {
517 if (str != null && str.length() < len) {
518 return Strings.padStart(str, len, fill.charAt(0));
519 }
520 return str == null ? "" : str;
521 }
522
523
524
525
526
527
528
529
530
531 public static List<String> getNamesForLocale(String baseName, Locale locale) {
532 List<String> result = new ArrayList<>(3);
533 String language = locale.getLanguage();
534 String country = locale.getCountry();
535 String variant = locale.getVariant();
536 StringBuilder temp = new StringBuilder(baseName);
537
538 if (language.length() > 0) {
539 temp.append('_').append(language);
540 result.add(0, temp.toString());
541 }
542
543 if (country.length() > 0) {
544 temp.append('_').append(country);
545 result.add(0, temp.toString());
546 }
547
548 if (variant.length() > 0) {
549 temp.append('_').append(variant);
550 result.add(0, temp.toString());
551 }
552
553 return result;
554 }
555
556
557
558
559
560
561 public static boolean isThreadingEnabled() {
562 try {
563 MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
564 ObjectName objectNameThreading = new ObjectName("java.lang:type=Threading");
565 Set<ObjectInstance> threading = mbeanServer.queryMBeans(objectNameThreading, null);
566 return threading != null && !threading.isEmpty();
567 } catch (MalformedObjectNameException e) {
568 logger.trace("", e);
569 return false;
570 }
571 }
572 }