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