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