001
014
015 package com.liferay.portal.tools.sourceformatter;
016
017 import com.liferay.portal.kernel.io.unsync.UnsyncBufferedReader;
018 import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
019 import com.liferay.portal.kernel.util.CharPool;
020 import com.liferay.portal.kernel.util.GetterUtil;
021 import com.liferay.portal.kernel.util.StringBundler;
022 import com.liferay.portal.kernel.util.StringPool;
023 import com.liferay.portal.kernel.util.StringUtil;
024 import com.liferay.portal.kernel.util.Validator;
025
026 import java.io.File;
027 import java.io.IOException;
028
029 import java.util.ArrayList;
030 import java.util.HashMap;
031 import java.util.HashSet;
032 import java.util.List;
033 import java.util.Map;
034 import java.util.Set;
035 import java.util.regex.Matcher;
036 import java.util.regex.Pattern;
037
038
041 public class JSPSourceProcessor extends BaseSourceProcessor {
042
043 protected void addImportCounts(String content) {
044 Matcher matcher = _importsPattern.matcher(content);
045
046 while (matcher.find()) {
047 String importName = matcher.group(1);
048
049 int count = 0;
050
051 if (_importCountMap.containsKey(importName)) {
052 count = _importCountMap.get(importName);
053 }
054 else {
055 int pos = importName.lastIndexOf(StringPool.PERIOD);
056
057 String importClassName = importName.substring(pos + 1);
058
059 if (_importClassNames.contains(importClassName)) {
060 _duplicateImportClassNames.add(importClassName);
061 }
062 else {
063 _importClassNames.add(importClassName);
064 }
065 }
066
067 _importCountMap.put(importName, count + 1);
068 }
069 }
070
071 protected void addJSPIncludeFileNames(String fileName) {
072 String content = _jspContents.get(fileName);
073
074 if (Validator.isNull(content)) {
075 return;
076 }
077
078 for (int x = 0;;) {
079 x = content.indexOf("<%@ include file=", x);
080
081 if (x == -1) {
082 break;
083 }
084
085 x = content.indexOf(StringPool.QUOTE, x);
086
087 if (x == -1) {
088 break;
089 }
090
091 int y = content.indexOf(StringPool.QUOTE, x + 1);
092
093 if (y == -1) {
094 break;
095 }
096
097 String includeFileName = content.substring(x + 1, y);
098
099 Matcher matcher = _jspIncludeFilePattern.matcher(includeFileName);
100
101 if (!matcher.find()) {
102 throw new RuntimeException(
103 "Invalid include " + includeFileName);
104 }
105
106 includeFileName = buildFullPathIncludeFileName(
107 fileName, includeFileName);
108
109 if ((includeFileName.endsWith("jsp") ||
110 includeFileName.endsWith("jspf")) &&
111 !includeFileName.endsWith("html/common/init.jsp") &&
112 !includeFileName.endsWith("html/portlet/init.jsp") &&
113 !includeFileName.endsWith("html/taglib/init.jsp") &&
114 !_includeFileNames.contains(includeFileName)) {
115
116 _includeFileNames.add(includeFileName);
117 }
118
119 x = y;
120 }
121 }
122
123 protected void addJSPReferenceFileNames(String fileName) {
124 for (Map.Entry<String, String> entry : _jspContents.entrySet()) {
125 String referenceFileName = entry.getKey();
126
127 if (_includeFileNames.contains(referenceFileName)) {
128 continue;
129 }
130
131 String sharedPath = fileName.substring(
132 0, StringUtil.startsWithWeight(referenceFileName, fileName));
133
134 if (Validator.isNull(sharedPath) ||
135 !sharedPath.contains(StringPool.SLASH)) {
136
137 continue;
138 }
139
140 if (!sharedPath.endsWith(StringPool.SLASH)) {
141 sharedPath = sharedPath.substring(
142 0, sharedPath.lastIndexOf(CharPool.SLASH) + 1);
143 }
144
145 String content = null;
146
147 for (int x = -1;;) {
148 x = sharedPath.indexOf(CharPool.SLASH, x + 1);
149
150 if (x == -1) {
151 break;
152 }
153
154 if (content == null) {
155 content = entry.getValue();
156 }
157
158 if (content.contains(
159 "<%@ include file=\"" + fileName.substring(x))) {
160
161 _includeFileNames.add(referenceFileName);
162
163 break;
164 }
165 }
166 }
167 }
168
169 protected void addJSPUnusedImports(
170 String fileName, List<String> importLines,
171 List<String> unneededImports) {
172
173 for (String importLine : importLines) {
174 int x = importLine.indexOf(StringPool.QUOTE);
175 int y = importLine.indexOf(StringPool.QUOTE, x + 1);
176
177 if ((x == -1) || (y == -1)) {
178 continue;
179 }
180
181 String className = importLine.substring(x + 1, y);
182
183 className = className.substring(
184 className.lastIndexOf(StringPool.PERIOD) + 1);
185
186 String regex = "[^A-Za-z0-9_\"]" + className + "[^A-Za-z0-9_\"]";
187
188 if (hasUnusedJSPTerm(fileName, regex, "class")) {
189 unneededImports.add(importLine);
190 }
191 }
192 }
193
194 protected String buildFullPathIncludeFileName(
195 String fileName, String includeFileName) {
196
197 String topLevelDirName = null;
198
199 int x = includeFileName.indexOf(CharPool.SLASH, 1);
200
201 if (x != -1) {
202 topLevelDirName = includeFileName.substring(1, x);
203 }
204
205 String path = fileName;
206
207 while (true) {
208 int y = path.lastIndexOf(CharPool.SLASH);
209
210 if (y == -1) {
211 return StringPool.BLANK;
212 }
213
214 if (Validator.isNull(topLevelDirName) ||
215 path.equals(topLevelDirName) ||
216 path.endsWith(StringPool.SLASH + topLevelDirName)) {
217
218 String fullPathIncludeFileName =
219 path.substring(0, y) + includeFileName;
220
221 if (_jspContents.containsKey(fullPathIncludeFileName) &&
222 !fullPathIncludeFileName.equals(fileName)) {
223
224 return fullPathIncludeFileName;
225 }
226 }
227
228 path = path.substring(0, y);
229 }
230 }
231
232 protected boolean checkTaglibVulnerability(
233 String jspContent, String vulnerability) {
234
235 int pos1 = -1;
236
237 do {
238 pos1 = jspContent.indexOf(vulnerability, pos1 + 1);
239
240 if (pos1 != -1) {
241 int pos2 = jspContent.lastIndexOf(CharPool.LESS_THAN, pos1);
242
243 while ((pos2 > 0) &&
244 (jspContent.charAt(pos2 + 1) == CharPool.PERCENT)) {
245
246 pos2 = jspContent.lastIndexOf(CharPool.LESS_THAN, pos2 - 1);
247 }
248
249 String tagContent = jspContent.substring(pos2, pos1);
250
251 if (!tagContent.startsWith("<aui:") &&
252 !tagContent.startsWith("<liferay-portlet:") &&
253 !tagContent.startsWith("<liferay-util:") &&
254 !tagContent.startsWith("<portlet:")) {
255
256 return true;
257 }
258 }
259 }
260 while (pos1 != -1);
261
262 return false;
263 }
264
265 protected void checkXSS(String fileName, String jspContent) {
266 Matcher matcher = _xssPattern.matcher(jspContent);
267
268 while (matcher.find()) {
269 boolean xssVulnerable = false;
270
271 String jspVariable = matcher.group(1);
272
273 String anchorVulnerability = " href=\"<%= " + jspVariable + " %>";
274
275 if (checkTaglibVulnerability(jspContent, anchorVulnerability)) {
276 xssVulnerable = true;
277 }
278
279 String inputVulnerability = " value=\"<%= " + jspVariable + " %>";
280
281 if (checkTaglibVulnerability(jspContent, inputVulnerability)) {
282 xssVulnerable = true;
283 }
284
285 String inlineStringVulnerability1 = "'<%= " + jspVariable + " %>";
286
287 if (jspContent.contains(inlineStringVulnerability1)) {
288 xssVulnerable = true;
289 }
290
291 String inlineStringVulnerability2 = "(\"<%= " + jspVariable + " %>";
292
293 if (jspContent.contains(inlineStringVulnerability2)) {
294 xssVulnerable = true;
295 }
296
297 String inlineStringVulnerability3 = " \"<%= " + jspVariable + " %>";
298
299 if (jspContent.contains(inlineStringVulnerability3)) {
300 xssVulnerable = true;
301 }
302
303 String documentIdVulnerability = ".<%= " + jspVariable + " %>";
304
305 if (jspContent.contains(documentIdVulnerability)) {
306 xssVulnerable = true;
307 }
308
309 if (xssVulnerable) {
310 processErrorMessage(
311 fileName, "(xss): " + fileName + " (" + jspVariable + ")");
312 }
313 }
314 }
315
316 @Override
317 protected String doFormat(
318 File file, String fileName, String absolutePath, String content)
319 throws Exception {
320
321 String newContent = formatJSP(fileName, absolutePath, content);
322
323 newContent = StringUtil.replace(
324 newContent,
325 new String[] {
326 "<br/>", "\"/>", "\" >", "@page import", "\"%>", ")%>", "else{",
327 "for(", "function (", "if(", "javascript: ", "while(", "){\n",
328 ";;\n", "\n\n\n"
329 },
330 new String[] {
331 "<br />", "\" />", "\">", "@ page import", "\" %>", ") %>",
332 "else {", "for (", "function(", "if (", "javascript:",
333 "while (", ") {\n", ";\n", "\n\n"
334 });
335
336 newContent = fixCompatClassImports(absolutePath, newContent);
337
338 if (_stripJSPImports && !_jspContents.isEmpty()) {
339 try {
340 newContent = stripJSPImports(fileName, newContent);
341 }
342 catch (RuntimeException re) {
343 _stripJSPImports = false;
344 }
345 }
346
347 if (portalSource &&
348 content.contains("page import=") &&
349 !fileName.contains("init.jsp") &&
350 !fileName.contains("init-ext.jsp") &&
351 !fileName.contains("/taglib/aui/") &&
352 !fileName.endsWith("touch.jsp") &&
353 (fileName.endsWith(".jspf") || content.contains("include file="))) {
354
355 processErrorMessage(
356 fileName, "move imports to init.jsp: " + fileName);
357 }
358
359 newContent = fixCopyright(newContent, absolutePath, fileName);
360
361 newContent = StringUtil.replace(
362 newContent,
363 new String[] {
364 "alert('<%= LanguageUtil.", "alert(\"<%= LanguageUtil.",
365 "confirm('<%= LanguageUtil.", "confirm(\"<%= LanguageUtil."
366 },
367 new String[] {
368 "alert('<%= UnicodeLanguageUtil.",
369 "alert(\"<%= UnicodeLanguageUtil.",
370 "confirm('<%= UnicodeLanguageUtil.",
371 "confirm(\"<%= UnicodeLanguageUtil."
372 });
373
374 if (newContent.contains(" ")) {
375 if (!fileName.matches(".*template.*\\.vm$")) {
376 processErrorMessage(fileName, "tab: " + fileName);
377 }
378 }
379
380 if (fileName.endsWith("init.jsp") || fileName.endsWith("init.jspf")) {
381 int x = newContent.indexOf("<%@ page import=");
382
383 int y = newContent.lastIndexOf("<%@ page import=");
384
385 y = newContent.indexOf("%>", y);
386
387 if ((x != -1) && (y != -1) && (y > x)) {
388
389
390
391 boolean compressImports = true;
392
393 if (compressImports) {
394 String imports = newContent.substring(x, y);
395
396 imports = StringUtil.replace(
397 imports, new String[] {"%>\r\n<%@ ", "%>\n<%@ "},
398 new String[] {"%><%@\r\n", "%><%@\n"});
399
400 newContent =
401 newContent.substring(0, x) + imports +
402 newContent.substring(y);
403 }
404 }
405 }
406
407 newContent = fixSessionKey(fileName, newContent, sessionKeyPattern);
408 newContent = fixSessionKey(
409 fileName, newContent, taglibSessionKeyPattern);
410
411 checkLanguageKeys(fileName, newContent, languageKeyPattern);
412 checkLanguageKeys(fileName, newContent, _taglibLanguageKeyPattern1);
413 checkLanguageKeys(fileName, newContent, _taglibLanguageKeyPattern2);
414 checkLanguageKeys(fileName, newContent, _taglibLanguageKeyPattern3);
415
416 checkXSS(fileName, newContent);
417
418
419
420 newContent = fixIncorrectParameterTypeForLanguageUtil(
421 newContent, true, fileName);
422
423 Matcher matcher = _javaClassPattern.matcher(newContent);
424
425 if (matcher.find()) {
426 String javaClassContent = matcher.group();
427
428 javaClassContent = javaClassContent.substring(1);
429
430 String beforeJavaClass = newContent.substring(
431 0, matcher.start() + 1);
432
433 int javaClassLineCount =
434 StringUtil.count(beforeJavaClass, "\n") + 1;
435
436 newContent = formatJavaTerms(
437 fileName, absolutePath, newContent, javaClassContent,
438 javaClassLineCount, null, null, null);
439 }
440
441 if (!content.equals(newContent)) {
442 _jspContents.put(fileName, newContent);
443 }
444
445 return newContent;
446 }
447
448 @Override
449 protected void format() throws Exception {
450 _moveFrequentlyUsedImportsToCommonInit = GetterUtil.getBoolean(
451 getProperty("move.frequently.used.imports.to.common.init"));
452 _unusedVariablesExclusions = getPropertyList(
453 "jsp.unused.variables.excludes.files");
454
455 String[] excludes = new String[] {"**\\null.jsp", "**\\tools\\**"};
456 String[] includes = new String[] {
457 "**\\*.jsp", "**\\*.jspf", "**\\*.vm"
458 };
459
460 List<String> fileNames = getFileNames(excludes, includes);
461
462 Pattern pattern = Pattern.compile(
463 "\\s*@\\s*include\\s*file=['\"](.*)['\"]");
464
465 for (String fileName : fileNames) {
466 File file = new File(BASEDIR + fileName);
467
468 fileName = StringUtil.replace(
469 fileName, StringPool.BACK_SLASH, StringPool.SLASH);
470
471 String absolutePath = getAbsolutePath(file);
472
473 String content = fileUtil.read(file);
474
475 Matcher matcher = pattern.matcher(content);
476
477 String newContent = content;
478
479 while (matcher.find()) {
480 newContent = StringUtil.replaceFirst(
481 newContent, matcher.group(),
482 "@ include file=\"" + matcher.group(1) + "\"",
483 matcher.start());
484 }
485
486 processFormattedFile(file, fileName, content, newContent);
487
488 if (portalSource &&
489 _moveFrequentlyUsedImportsToCommonInit &&
490 fileName.endsWith("/init.jsp") &&
491 !absolutePath.contains("/modules/") &&
492 !fileName.endsWith("/common/init.jsp")) {
493
494 addImportCounts(content);
495 }
496
497 _jspContents.put(fileName, newContent);
498 }
499
500 if (portalSource && _moveFrequentlyUsedImportsToCommonInit) {
501 moveFrequentlyUsedImportsToCommonInit(4);
502 }
503
504 for (String fileName : fileNames) {
505 format(fileName);
506 }
507 }
508
509 protected String formatJSP(
510 String fileName, String absolutePath, String content)
511 throws IOException {
512
513 StringBundler sb = new StringBundler();
514
515 String currentAttributeAndValue = null;
516 String previousAttribute = null;
517 String previousAttributeAndValue = null;
518
519 String currentException = null;
520 String previousException = null;
521
522 boolean hasUnsortedExceptions = false;
523
524 try (UnsyncBufferedReader unsyncBufferedReader =
525 new UnsyncBufferedReader(new UnsyncStringReader(content))) {
526
527 _checkedForIncludesFileNames = new HashSet<String>();
528 _includeFileNames = new HashSet<String>();
529
530 int lineCount = 0;
531
532 String line = null;
533
534 String previousLine = StringPool.BLANK;
535
536 boolean readAttributes = false;
537
538 boolean javaSource = false;
539
540 while ((line = unsyncBufferedReader.readLine()) != null) {
541 lineCount++;
542
543 if (portalSource && hasUnusedTaglib(fileName, line)) {
544 continue;
545 }
546
547 if (!fileName.contains("jsonw") ||
548 !fileName.endsWith("action.jsp")) {
549
550 line = trimLine(line, false);
551 }
552
553 if (line.contains("<aui:button ") &&
554 line.contains("type=\"button\"")) {
555
556 processErrorMessage(
557 fileName, "aui:button " + fileName + " " + lineCount);
558 }
559
560 if (line.contains("debugger.")) {
561 processErrorMessage(
562 fileName, "debugger " + fileName + " " + lineCount);
563 }
564
565 String trimmedLine = StringUtil.trimLeading(line);
566 String trimmedPreviousLine = StringUtil.trimLeading(
567 previousLine);
568
569 checkStringBundler(trimmedLine, fileName, lineCount);
570
571 checkEmptyCollection(trimmedLine, fileName, lineCount);
572
573 if (trimmedLine.equals("<%") || trimmedLine.equals("<%!")) {
574 javaSource = true;
575 }
576 else if (trimmedLine.equals("%>")) {
577 javaSource = false;
578 }
579
580 if (javaSource || trimmedLine.contains("<%= ")) {
581 checkInefficientStringMethods(
582 line, fileName, absolutePath, lineCount);
583 }
584
585 if (javaSource && portalSource &&
586 !isExcluded(
587 _unusedVariablesExclusions, absolutePath, lineCount) &&
588 !_jspContents.isEmpty() &&
589 hasUnusedVariable(fileName, trimmedLine)) {
590
591 continue;
592 }
593
594
595
596 if (line.contains(".sendRedirect(") &&
597 !fileName.endsWith("_jsp.jsp")) {
598
599 processErrorMessage(
600 fileName,
601 "Do not use sendRedirect in jsp: " + fileName + " " +
602 lineCount);
603 }
604
605 if (!trimmedLine.equals("%>") && line.contains("%>") &&
606 !line.contains("--%>") && !line.contains(" %>")) {
607
608 line = StringUtil.replace(line, "%>", " %>");
609 }
610
611 if (line.contains("<%=") && !line.contains("<%= ")) {
612 line = StringUtil.replace(line, "<%=", "<%= ");
613 }
614
615 if (trimmedPreviousLine.equals("%>") &&
616 Validator.isNotNull(line) && !trimmedLine.equals("-->")) {
617
618 sb.append("\n");
619 }
620 else if (Validator.isNotNull(previousLine) &&
621 !trimmedPreviousLine.equals("<!--") &&
622 trimmedLine.equals("<%")) {
623
624 sb.append("\n");
625 }
626 else if (trimmedPreviousLine.equals("<%") &&
627 Validator.isNull(line)) {
628
629 continue;
630 }
631 else if (trimmedPreviousLine.equals("<%") &&
632 trimmedLine.startsWith("
633
634 sb.append("\n");
635 }
636 else if (Validator.isNull(previousLine) &&
637 trimmedLine.equals("%>") && (sb.index() > 2)) {
638
639 String lineBeforePreviousLine = sb.stringAt(sb.index() - 3);
640
641 if (!lineBeforePreviousLine.startsWith("
642 sb.setIndex(sb.index() - 1);
643 }
644 }
645
646 if ((trimmedLine.startsWith("if (") ||
647 trimmedLine.startsWith("else if (") ||
648 trimmedLine.startsWith("while (")) &&
649 trimmedLine.endsWith(") {")) {
650
651 checkIfClauseParentheses(trimmedLine, fileName, lineCount);
652 }
653
654 if (readAttributes) {
655 if (!trimmedLine.startsWith(StringPool.FORWARD_SLASH) &&
656 !trimmedLine.startsWith(StringPool.GREATER_THAN)) {
657
658 int pos = trimmedLine.indexOf(StringPool.EQUAL);
659
660 if (pos != -1) {
661 String attribute = trimmedLine.substring(0, pos);
662
663 if (!trimmedLine.endsWith(StringPool.APOSTROPHE) &&
664 !trimmedLine.endsWith(
665 StringPool.GREATER_THAN) &&
666 !trimmedLine.endsWith(StringPool.QUOTE)) {
667
668 processErrorMessage(
669 fileName,
670 "attribute: " + fileName + " " + lineCount);
671
672 readAttributes = false;
673 }
674 else if (trimmedLine.endsWith(
675 StringPool.APOSTROPHE) &&
676 !trimmedLine.contains(StringPool.QUOTE)) {
677
678 line = StringUtil.replace(
679 line, StringPool.APOSTROPHE,
680 StringPool.QUOTE);
681
682 readAttributes = false;
683 }
684 else if (Validator.isNotNull(previousAttribute)) {
685 if (!isAttributName(attribute) &&
686 !attribute.startsWith(
687 StringPool.LESS_THAN)) {
688
689 processErrorMessage(
690 fileName,
691 "attribute: " + fileName + " " +
692 lineCount);
693
694 readAttributes = false;
695 }
696 else if (Validator.isNull(
697 previousAttributeAndValue) &&
698 (previousAttribute.compareTo(
699 attribute) > 0)) {
700
701 previousAttributeAndValue = previousLine;
702 currentAttributeAndValue = line;
703 }
704 }
705
706 if (!readAttributes) {
707 previousAttribute = null;
708 previousAttributeAndValue = null;
709 }
710 else {
711 previousAttribute = attribute;
712 }
713 }
714 }
715 else {
716 previousAttribute = null;
717
718 readAttributes = false;
719 }
720 }
721
722 if (!hasUnsortedExceptions) {
723 int x = line.indexOf("<liferay-ui:error exception=\"<%=");
724
725 if (x != -1) {
726 int y = line.indexOf(".class %>", x);
727
728 if (y != -1) {
729 currentException = line.substring(x, y);
730
731 if (Validator.isNotNull(previousException) &&
732 (previousException.compareTo(currentException) >
733 0)) {
734
735 currentException = line;
736 previousException = previousLine;
737
738 hasUnsortedExceptions = true;
739 }
740 }
741 }
742
743 if (!hasUnsortedExceptions) {
744 previousException = currentException;
745 currentException = null;
746 }
747 }
748
749 if (trimmedLine.startsWith(StringPool.LESS_THAN) &&
750 !trimmedLine.startsWith("<%") &&
751 !trimmedLine.startsWith("<!")) {
752
753 if (!trimmedLine.contains(StringPool.GREATER_THAN) &&
754 !trimmedLine.contains(StringPool.SPACE)) {
755
756 readAttributes = true;
757 }
758 else {
759 line = sortAttributes(fileName, line, lineCount, true);
760 }
761 }
762
763 if (!trimmedLine.contains(StringPool.DOUBLE_SLASH) &&
764 !trimmedLine.startsWith(StringPool.STAR)) {
765
766 while (trimmedLine.contains(StringPool.TAB)) {
767 line = StringUtil.replaceLast(
768 line, StringPool.TAB, StringPool.SPACE);
769
770 trimmedLine = StringUtil.replaceLast(
771 trimmedLine, StringPool.TAB, StringPool.SPACE);
772 }
773
774 while (trimmedLine.contains(StringPool.DOUBLE_SPACE) &&
775 !trimmedLine.contains(
776 StringPool.QUOTE + StringPool.DOUBLE_SPACE) &&
777 !fileName.endsWith(".vm")) {
778
779 line = StringUtil.replaceLast(
780 line, StringPool.DOUBLE_SPACE, StringPool.SPACE);
781
782 trimmedLine = StringUtil.replaceLast(
783 trimmedLine, StringPool.DOUBLE_SPACE,
784 StringPool.SPACE);
785 }
786 }
787
788 if (!fileName.endsWith("/touch.jsp")) {
789 int x = line.indexOf("<%@ include file");
790
791 if (x != -1) {
792 x = line.indexOf(StringPool.QUOTE, x);
793
794 int y = line.indexOf(StringPool.QUOTE, x + 1);
795
796 if (y != -1) {
797 String includeFileName = line.substring(x + 1, y);
798
799 Matcher matcher = _jspIncludeFilePattern.matcher(
800 includeFileName);
801
802 if (!matcher.find()) {
803 processErrorMessage(
804 fileName,
805 "include: " + fileName + " " + lineCount);
806 }
807 }
808 }
809 }
810
811 line = replacePrimitiveWrapperInstantiation(
812 fileName, line, lineCount);
813
814 previousLine = line;
815
816 sb.append(line);
817 sb.append("\n");
818 }
819 }
820
821 content = sb.toString();
822
823 if (content.endsWith("\n")) {
824 content = content.substring(0, content.length() - 1);
825 }
826
827 content = formatTaglibQuotes(fileName, content, StringPool.QUOTE);
828 content = formatTaglibQuotes(fileName, content, StringPool.APOSTROPHE);
829
830 if (Validator.isNotNull(previousAttributeAndValue)) {
831 content = StringUtil.replaceFirst(
832 content,
833 previousAttributeAndValue + "\n" + currentAttributeAndValue,
834 currentAttributeAndValue + "\n" + previousAttributeAndValue);
835 }
836
837 if (hasUnsortedExceptions) {
838 if ((StringUtil.count(content, currentException) > 1) ||
839 (StringUtil.count(content, previousException) > 1)) {
840
841 processErrorMessage(
842 fileName, "unsorted exceptions: " + fileName);
843 }
844 else {
845 content = StringUtil.replaceFirst(
846 content, previousException, currentException);
847
848 content = StringUtil.replaceLast(
849 content, currentException, previousException);
850 }
851 }
852
853 return content;
854 }
855
856 protected String formatTaglibQuotes(
857 String fileName, String content, String quoteType) {
858
859 String quoteFix = StringPool.APOSTROPHE;
860
861 if (quoteFix.equals(quoteType)) {
862 quoteFix = StringPool.QUOTE;
863 }
864
865 Pattern pattern = Pattern.compile(getTaglibRegex(quoteType));
866
867 Matcher matcher = pattern.matcher(content);
868
869 while (matcher.find()) {
870 int x = content.indexOf(quoteType + "<%=", matcher.start());
871 int y = content.indexOf("%>" + quoteType, x);
872
873 while ((x != -1) && (y != -1)) {
874 String beforeResult = content.substring(matcher.start(), x);
875
876 if (beforeResult.contains(" />\"")) {
877 break;
878 }
879
880 String result = content.substring(x + 1, y + 2);
881
882 if (result.contains(quoteType)) {
883 int lineCount = 1;
884
885 char[] contentCharArray = content.toCharArray();
886
887 for (int i = 0; i < x; i++) {
888 if (contentCharArray[i] == CharPool.NEW_LINE) {
889 lineCount++;
890 }
891 }
892
893 if (!result.contains(quoteFix)) {
894 StringBundler sb = new StringBundler(5);
895
896 sb.append(content.substring(0, x));
897 sb.append(quoteFix);
898 sb.append(result);
899 sb.append(quoteFix);
900 sb.append(content.substring(y + 3, content.length()));
901
902 content = sb.toString();
903 }
904 else {
905 processErrorMessage(
906 fileName, "taglib: " + fileName + " " + lineCount);
907 }
908 }
909
910 x = content.indexOf(quoteType + "<%=", y);
911
912 if (x > matcher.end()) {
913 break;
914 }
915
916 y = content.indexOf("%>" + quoteType, x);
917 }
918 }
919
920 return content;
921 }
922
923 protected List<String> getJSPDuplicateImports(
924 String fileName, String content, List<String> importLines) {
925
926 List<String> duplicateImports = new ArrayList<String>();
927
928 for (String importLine : importLines) {
929 int x = content.indexOf("<%@ include file=");
930
931 if (x == -1) {
932 continue;
933 }
934
935 int y = content.indexOf("<%@ page import=");
936
937 if (y == -1) {
938 continue;
939 }
940
941 if ((x < y) && isJSPDuplicateImport(fileName, importLine, false)) {
942 duplicateImports.add(importLine);
943 }
944 }
945
946 return duplicateImports;
947 }
948
949 protected String getTaglibRegex(String quoteType) {
950 StringBuilder sb = new StringBuilder();
951
952 sb.append("<(");
953
954 for (int i = 0; i < _TAG_LIBRARIES.length; i++) {
955 sb.append(_TAG_LIBRARIES[i]);
956 sb.append(StringPool.PIPE);
957 }
958
959 sb.deleteCharAt(sb.length() - 1);
960 sb.append("):([^>]|%>)*");
961 sb.append(quoteType);
962 sb.append("<%=.*");
963 sb.append(quoteType);
964 sb.append(".*%>");
965 sb.append(quoteType);
966 sb.append("([^>]|%>)*>");
967
968 return sb.toString();
969 }
970
971 protected String getVariableName(String line) {
972 if (!line.endsWith(";") || line.startsWith("
973 return null;
974 }
975
976 String variableName = null;
977
978 int x = line.indexOf(" = ");
979
980 if (x == -1) {
981 int y = line.lastIndexOf(" ");
982
983 if (y != -1) {
984 variableName = line.substring(y + 1, line.length() - 1);
985 }
986 }
987 else {
988 line = line.substring(0, x);
989
990 int y = line.lastIndexOf(" ");
991
992 if (y != -1) {
993 variableName = line.substring(y + 1);
994 }
995 }
996
997 if (Validator.isVariableName(variableName)) {
998 return variableName;
999 }
1000
1001 return null;
1002 }
1003
1004 protected boolean hasUnusedJSPTerm(
1005 String fileName, String regex, String type) {
1006
1007 _includeFileNames.add(fileName);
1008
1009 Set<String> checkedForUnusedJSPTerm = new HashSet<String>();
1010
1011 return !isJSPTermRequired(
1012 fileName, regex, type, checkedForUnusedJSPTerm);
1013 }
1014
1015 protected boolean hasUnusedTaglib(String fileName, String line) {
1016 if (!line.startsWith("<%@ taglib uri=")) {
1017 return false;
1018 }
1019
1020 int x = line.indexOf(" prefix=");
1021
1022 if (x == -1) {
1023 return false;
1024 }
1025
1026 x = line.indexOf(StringPool.QUOTE, x);
1027
1028 int y = line.indexOf(StringPool.QUOTE, x + 1);
1029
1030 if ((x == -1) || (y == -1)) {
1031 return false;
1032 }
1033
1034 String taglibPrefix = line.substring(x + 1, y);
1035
1036 String regex = StringPool.LESS_THAN + taglibPrefix + StringPool.COLON;
1037
1038 return hasUnusedJSPTerm(fileName, regex, "taglib");
1039 }
1040
1041 protected boolean hasUnusedVariable(String fileName, String line) {
1042 if (line.contains(": ")) {
1043 return false;
1044 }
1045
1046 String variableName = getVariableName(line);
1047
1048 if (Validator.isNull(variableName) || variableName.equals("false") ||
1049 variableName.equals("true")) {
1050
1051 return false;
1052 }
1053
1054 String regex = "[^A-Za-z0-9_\"]" + variableName + "[^A-Za-z0-9_\"]";
1055
1056 return hasUnusedJSPTerm(fileName, regex, "variable");
1057 }
1058
1059 protected boolean isJSPDuplicateImport(
1060 String fileName, String importLine, boolean checkFile) {
1061
1062 String content = _jspContents.get(fileName);
1063
1064 if (Validator.isNull(content)) {
1065 return false;
1066 }
1067
1068 int x = importLine.indexOf("page");
1069
1070 if (x == -1) {
1071 return false;
1072 }
1073
1074 if (checkFile && content.contains(importLine.substring(x))) {
1075 return true;
1076 }
1077
1078 int y = content.indexOf("<%@ include file=");
1079
1080 if (y == -1) {
1081 return false;
1082 }
1083
1084 y = content.indexOf(StringPool.QUOTE, y);
1085
1086 if (y == -1) {
1087 return false;
1088 }
1089
1090 int z = content.indexOf(StringPool.QUOTE, y + 1);
1091
1092 if (z == -1) {
1093 return false;
1094 }
1095
1096 String includeFileName = content.substring(y + 1, z);
1097
1098 includeFileName = buildFullPathIncludeFileName(
1099 fileName, includeFileName);
1100
1101 return isJSPDuplicateImport(includeFileName, importLine, true);
1102 }
1103
1104 protected boolean isJSPTermRequired(
1105 String fileName, String regex, String type,
1106 Set<String> checkedForUnusedJSPTerm) {
1107
1108 if (checkedForUnusedJSPTerm.contains(fileName)) {
1109 return false;
1110 }
1111
1112 checkedForUnusedJSPTerm.add(fileName);
1113
1114 String content = _jspContents.get(fileName);
1115
1116 if (Validator.isNull(content)) {
1117 return false;
1118 }
1119
1120 Pattern pattern = Pattern.compile(regex);
1121
1122 Matcher matcher = pattern.matcher(content);
1123
1124 if (matcher.find() &&
1125 (!type.equals("variable") || (checkedForUnusedJSPTerm.size() > 1) ||
1126 matcher.find())) {
1127
1128 return true;
1129 }
1130
1131 if (!_checkedForIncludesFileNames.contains(fileName)) {
1132 addJSPIncludeFileNames(fileName);
1133
1134 if (fileName.endsWith("init.jsp") ||
1135 fileName.endsWith("init.jspf") ||
1136 fileName.contains("init-ext.jsp")) {
1137
1138 addJSPReferenceFileNames(fileName);
1139 }
1140 }
1141
1142 _checkedForIncludesFileNames.add(fileName);
1143
1144 String[] includeFileNamesArray = _includeFileNames.toArray(
1145 new String[_includeFileNames.size()]);
1146
1147 for (String includeFileName : includeFileNamesArray) {
1148 if (!checkedForUnusedJSPTerm.contains(includeFileName) &&
1149 isJSPTermRequired(
1150 includeFileName, regex, type, checkedForUnusedJSPTerm)) {
1151
1152 return true;
1153 }
1154 }
1155
1156 return false;
1157 }
1158
1159 protected void moveFrequentlyUsedImportsToCommonInit(int minCount)
1160 throws IOException {
1161
1162 if (_importCountMap.isEmpty()) {
1163 return;
1164 }
1165
1166 String commonInitFileName = "portal-web/docroot/html/common/init.jsp";
1167
1168 File commonInitFile = null;
1169 String commonInitFileContent = null;
1170
1171 int x = -1;
1172
1173 for (Map.Entry<String, Integer> importCount :
1174 _importCountMap.entrySet()) {
1175
1176 Integer count = importCount.getValue();
1177
1178 if (count < minCount) {
1179 continue;
1180 }
1181
1182 String importName = importCount.getKey();
1183
1184 int y = importName.lastIndexOf(StringPool.PERIOD);
1185
1186 String importClassName = importName.substring(y + 1);
1187
1188 if (_duplicateImportClassNames.contains(importClassName)) {
1189 continue;
1190 }
1191
1192 if (commonInitFileContent == null) {
1193 commonInitFile = new File(commonInitFileName);
1194
1195 commonInitFileContent = fileUtil.read(commonInitFile);
1196
1197 x = commonInitFileContent.indexOf("<%@ page import");
1198 }
1199
1200 commonInitFileContent = StringUtil.insert(
1201 commonInitFileContent,
1202 "<%@ page import=\"" + importName + "\" %>\n", x);
1203 }
1204
1205 if (commonInitFileContent != null) {
1206 fileUtil.write(commonInitFile, commonInitFileContent);
1207
1208 _jspContents.put(commonInitFileName, commonInitFileContent);
1209 }
1210 }
1211
1212 protected String stripJSPImports(String fileName, String content)
1213 throws IOException {
1214
1215 fileName = fileName.replace(
1216 CharPool.BACK_SLASH, CharPool.FORWARD_SLASH);
1217
1218 if (fileName.endsWith("init-ext.jsp")) {
1219 return content;
1220 }
1221
1222 Matcher matcher = _jspImportPattern.matcher(content);
1223
1224 if (!matcher.find()) {
1225 return content;
1226 }
1227
1228 String imports = matcher.group();
1229
1230 imports = StringUtil.replace(
1231 imports, new String[] {"%><%@\r\n", "%><%@\n"},
1232 new String[] {"%>\r\n<%@ ", "%>\n<%@ "});
1233
1234 List<String> importLines = new ArrayList<String>();
1235
1236 UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
1237 new UnsyncStringReader(imports));
1238
1239 String line = null;
1240
1241 while ((line = unsyncBufferedReader.readLine()) != null) {
1242 if (line.contains("import=")) {
1243 importLines.add(line);
1244 }
1245 }
1246
1247 List<String> unneededImports = getJSPDuplicateImports(
1248 fileName, content, importLines);
1249
1250 addJSPUnusedImports(fileName, importLines, unneededImports);
1251
1252 for (String unneededImport : unneededImports) {
1253 imports = StringUtil.replace(
1254 imports, unneededImport, StringPool.BLANK);
1255 }
1256
1257 ImportsFormatter importsFormatter = new JSPImportsFormatter();
1258
1259 imports = importsFormatter.format(imports);
1260
1261 String beforeImports = content.substring(0, matcher.start());
1262
1263 if (Validator.isNull(imports)) {
1264 beforeImports = StringUtil.replaceLast(
1265 beforeImports, "\n", StringPool.BLANK);
1266 }
1267
1268 String afterImports = content.substring(matcher.end());
1269
1270 if (Validator.isNull(afterImports)) {
1271 imports = StringUtil.replaceLast(imports, "\n", StringPool.BLANK);
1272
1273 content = beforeImports + imports;
1274
1275 return content;
1276 }
1277
1278 content = beforeImports + imports + "\n" + afterImports;
1279
1280 return content;
1281 }
1282
1283 private static final String[] _TAG_LIBRARIES = new String[] {
1284 "aui", "c", "html", "jsp", "liferay-portlet", "liferay-security",
1285 "liferay-theme", "liferay-ui", "liferay-util", "portlet", "struts",
1286 "tiles"
1287 };
1288
1289 private Set<String> _checkedForIncludesFileNames = new HashSet<String>();
1290 private List<String> _duplicateImportClassNames = new ArrayList<String>();
1291 private List<String> _importClassNames = new ArrayList<String>();
1292 private Map<String, Integer> _importCountMap =
1293 new HashMap<String, Integer>();
1294 private Pattern _importsPattern = Pattern.compile("page import=\"(.+)\"");
1295 private Set<String> _includeFileNames = new HashSet<String>();
1296 private Pattern _javaClassPattern = Pattern.compile(
1297 "\n(private|protected|public).* class ([\\s\\S]*?)\n\\}\n");
1298 private Map<String, String> _jspContents = new HashMap<String, String>();
1299 private Pattern _jspImportPattern = Pattern.compile(
1300 "(<.*\n*page.import=\".*>\n*)+", Pattern.MULTILINE);
1301 private Pattern _jspIncludeFilePattern = Pattern.compile("/.*[.]jsp[f]?");
1302 private boolean _moveFrequentlyUsedImportsToCommonInit;
1303 private boolean _stripJSPImports = true;
1304 private Pattern _taglibLanguageKeyPattern1 = Pattern.compile(
1305 "(?:confirmation|label|(?:M|m)essage|message key|names|title)=\"[^A-Z" +
1306 "<=%\\[\\s]+\"");
1307 private Pattern _taglibLanguageKeyPattern2 = Pattern.compile(
1308 "(aui:)(?:input|select|field-wrapper) (?!.*label=(?:'|\").+(?:'|\").*" +
1309 "name=\"[^<=%\\[\\s]+\")(?!.*name=\"[^<=%\\[\\s]+\".*title=" +
1310 "(?:'|\").+(?:'|\"))(?!.*name=\"[^<=%\\[\\s]+\".*type=\"" +
1311 "hidden\").*name=\"([^<=%\\[\\s]+)\"");
1312 private Pattern _taglibLanguageKeyPattern3 = Pattern.compile(
1313 "(liferay-ui:)(?:input-resource) .*id=\"([^<=%\\[\\s]+)\"(?!.*title=" +
1314 "(?:'|\").+(?:'|\"))");
1315 private List<String> _unusedVariablesExclusions;
1316 private Pattern _xssPattern = Pattern.compile(
1317 "\\s+([^\\s]+)\\s*=\\s*(Bean)?ParamUtil\\.getString\\(");
1318
1319 }