001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
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    /**
038     * @author Hugo Huijser
039     */
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 doFormat() throws Exception {
229                    String copyright = getCopyright();
230                    String oldCopyright = getOldCopyright();
231    
232                    String[] excludes = new String[] {
233                            "**\\portal\\aui\\**", "**\\bin\\**", "**\\null.jsp", "**\\tmp\\**",
234                            "**\\tools\\**"
235                    };
236                    String[] includes = new String[] {
237                            "**\\*.jsp", "**\\*.jspf", "**\\*.vm"
238                    };
239    
240                    List<String> fileNames = getFileNames(excludes, includes);
241    
242                    for (String fileName : fileNames) {
243                            File file = new File(BASEDIR + fileName);
244    
245                            fileName = StringUtil.replace(
246                                    fileName, StringPool.BACK_SLASH, StringPool.SLASH);
247    
248                            String content = fileUtil.read(file);
249    
250                            _jspContents.put(fileName, content);
251                    }
252    
253                    boolean stripJSPImports = true;
254    
255                    for (String fileName : fileNames) {
256                            File file = new File(BASEDIR + fileName);
257    
258                            fileName = StringUtil.replace(
259                                    fileName, StringPool.BACK_SLASH, StringPool.SLASH);
260    
261                            String content = fileUtil.read(file);
262    
263                            String oldContent = content;
264                            String newContent = StringPool.BLANK;
265    
266                            for (;;) {
267                                    newContent = formatJSP(fileName, oldContent);
268    
269                                    if (oldContent.equals(newContent)) {
270                                            break;
271                                    }
272    
273                                    oldContent = newContent;
274                            }
275    
276                            newContent = StringUtil.replace(
277                                    newContent,
278                                    new String[] {
279                                            "<br/>", "\"/>", "\" >", "@page import", "\"%>", ")%>",
280                                            "else{", "for(", "function (", "if(", "javascript: ",
281                                            "while(", "){\n", "\n\n\n"
282                                    },
283                                    new String[] {
284                                            "<br />", "\" />", "\">", "@ page import", "\" %>", ") %>",
285                                            "else {", "for (", "function(", "if (", "javascript:",
286                                            "while (", ") {\n", "\n\n"
287                                    });
288    
289                            newContent = fixCompatClassImports(fileName, newContent);
290    
291                            if (stripJSPImports) {
292                                    try {
293                                            newContent = stripJSPImports(fileName, newContent);
294                                    }
295                                    catch (RuntimeException re) {
296                                            stripJSPImports = false;
297                                    }
298                            }
299    
300                            newContent = fixCopyright(
301                                    newContent, copyright, oldCopyright, file, fileName);
302    
303                            newContent = StringUtil.replace(
304                                    newContent,
305                                    new String[] {
306                                            "alert('<%= LanguageUtil.",
307                                            "alert(\"<%= LanguageUtil.", "confirm('<%= LanguageUtil.",
308                                            "confirm(\"<%= LanguageUtil."
309                                    },
310                                    new String[] {
311                                            "alert('<%= UnicodeLanguageUtil.",
312                                            "alert(\"<%= UnicodeLanguageUtil.",
313                                            "confirm('<%= UnicodeLanguageUtil.",
314                                            "confirm(\"<%= UnicodeLanguageUtil."
315                                    });
316    
317                            if (newContent.contains("    ")) {
318                                    if (!fileName.matches(".*template.*\\.vm$")) {
319                                            processErrorMessage(fileName, "tab: " + fileName);
320                                    }
321                            }
322    
323                            if (fileName.endsWith("init.jsp")) {
324                                    int x = newContent.indexOf("<%@ page import=");
325    
326                                    int y = newContent.lastIndexOf("<%@ page import=");
327    
328                                    y = newContent.indexOf("%>", y);
329    
330                                    if ((x != -1) && (y != -1) && (y > x)) {
331    
332                                            // Set compressImports to false to decompress imports
333    
334                                            boolean compressImports = true;
335    
336                                            if (compressImports) {
337                                                    String imports = newContent.substring(x, y);
338    
339                                                    imports = StringUtil.replace(
340                                                            imports, new String[] {"%>\r\n<%@ ", "%>\n<%@ "},
341                                                            new String[] {"%><%@\r\n", "%><%@\n"});
342    
343                                                    newContent =
344                                                            newContent.substring(0, x) + imports +
345                                                                    newContent.substring(y);
346                                            }
347                                    }
348                            }
349    
350                            newContent = fixSessionKey(fileName, newContent, sessionKeyPattern);
351                            newContent = fixSessionKey(
352                                    fileName, newContent, taglibSessionKeyPattern);
353    
354                            checkLanguageKeys(fileName, newContent, languageKeyPattern);
355                            checkLanguageKeys(fileName, newContent, _taglibLanguageKeyPattern);
356                            checkXSS(fileName, newContent);
357    
358                            if ((newContent != null) && !content.equals(newContent)) {
359                                    fileUtil.write(file, newContent);
360    
361                                    sourceFormatterHelper.printError(fileName, file);
362                            }
363                    }
364            }
365    
366            protected String formatJSP(String fileName, String content)
367                    throws IOException {
368    
369                    StringBundler sb = new StringBundler();
370    
371                    UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
372                            new UnsyncStringReader(content));
373    
374                    int lineCount = 0;
375    
376                    String line = null;
377    
378                    String previousLine = StringPool.BLANK;
379    
380                    String currentAttributeAndValue = null;
381                    String previousAttribute = null;
382                    String previousAttributeAndValue = null;
383    
384                    boolean readAttributes = false;
385    
386                    String currentException = null;
387                    String previousException = null;
388    
389                    boolean hasUnsortedExceptions = false;
390    
391                    boolean javaSource = false;
392    
393                    while ((line = unsyncBufferedReader.readLine()) != null) {
394                            lineCount++;
395    
396                            if (!fileName.contains("jsonw") ||
397                                    !fileName.endsWith("action.jsp")) {
398    
399                                    line = trimLine(line, false);
400                            }
401    
402                            if (line.contains("<aui:button ") &&
403                                    line.contains("type=\"button\"")) {
404    
405                                    processErrorMessage(
406                                            fileName, "aui:button " + fileName + " " + lineCount);
407                            }
408    
409                            String trimmedLine = StringUtil.trimLeading(line);
410                            String trimmedPreviousLine = StringUtil.trimLeading(previousLine);
411    
412                            if (trimmedLine.equals("<%") || trimmedLine.equals("<%!")) {
413                                    javaSource = true;
414                            }
415                            else if (trimmedLine.equals("%>")) {
416                                    javaSource = false;
417                            }
418    
419                            if (javaSource && portalSource &&
420                                    hasUnusedVariable(fileName, trimmedLine)) {
421    
422                                    processErrorMessage(
423                                            fileName, "Unused variable: " + fileName + " " + lineCount);
424                            }
425    
426                            if (!trimmedLine.equals("%>") && line.contains("%>") &&
427                                    !line.contains("--%>") && !line.contains(" %>")) {
428    
429                                    line = StringUtil.replace(line, "%>", " %>");
430                            }
431    
432                            if (line.contains("<%=") && !line.contains("<%= ")) {
433                                    line = StringUtil.replace(line, "<%=", "<%= ");
434                            }
435    
436                            if (trimmedPreviousLine.equals("%>") && Validator.isNotNull(line) &&
437                                    !trimmedLine.equals("-->")) {
438    
439                                    sb.append("\n");
440                            }
441                            else if (Validator.isNotNull(previousLine) &&
442                                             !trimmedPreviousLine.equals("<!--") &&
443                                             trimmedLine.equals("<%")) {
444    
445                                    sb.append("\n");
446                            }
447                            else if (trimmedPreviousLine.equals("<%") &&
448                                             Validator.isNull(line)) {
449    
450                                    continue;
451                            }
452                            else if (trimmedPreviousLine.equals("<%") &&
453                                             trimmedLine.startsWith("//")) {
454    
455                                    sb.append("\n");
456                            }
457                            else if (Validator.isNull(previousLine) &&
458                                             trimmedLine.equals("%>") && (sb.index() > 2)) {
459    
460                                    String lineBeforePreviousLine = sb.stringAt(sb.index() - 3);
461    
462                                    if (!lineBeforePreviousLine.startsWith("//")) {
463                                            sb.setIndex(sb.index() - 1);
464                                    }
465                            }
466    
467                            if ((trimmedLine.startsWith("if (") ||
468                                     trimmedLine.startsWith("else if (") ||
469                                     trimmedLine.startsWith("while (")) &&
470                                    trimmedLine.endsWith(") {")) {
471    
472                                    checkIfClauseParentheses(trimmedLine, fileName, lineCount);
473                            }
474    
475                            if (readAttributes) {
476                                    if (!trimmedLine.startsWith(StringPool.FORWARD_SLASH) &&
477                                            !trimmedLine.startsWith(StringPool.GREATER_THAN)) {
478    
479                                            int pos = trimmedLine.indexOf(StringPool.EQUAL);
480    
481                                            if (pos != -1) {
482                                                    String attribute = trimmedLine.substring(0, pos);
483    
484                                                    if (!trimmedLine.endsWith(StringPool.QUOTE) &&
485                                                            !trimmedLine.endsWith(StringPool.APOSTROPHE)) {
486    
487                                                            processErrorMessage(
488                                                                    fileName,
489                                                                    "attribute: " + fileName + " " + lineCount);
490    
491                                                            readAttributes = false;
492                                                    }
493                                                    else if (trimmedLine.endsWith(StringPool.APOSTROPHE) &&
494                                                                     !trimmedLine.contains(StringPool.QUOTE)) {
495    
496                                                            line = StringUtil.replace(
497                                                                    line, StringPool.APOSTROPHE, StringPool.QUOTE);
498    
499                                                            readAttributes = false;
500                                                    }
501                                                    else if (Validator.isNotNull(previousAttribute)) {
502                                                            if (!isJSPAttributName(attribute)) {
503                                                                    processErrorMessage(
504                                                                            fileName,
505                                                                            "attribute: " + fileName + " " + lineCount);
506    
507                                                                    readAttributes = false;
508                                                            }
509                                                            else if (Validator.isNull(
510                                                                                    previousAttributeAndValue) &&
511                                                                             (previousAttribute.compareTo(
512                                                                                     attribute) > 0)) {
513    
514                                                                    previousAttributeAndValue = previousLine;
515                                                                    currentAttributeAndValue = line;
516                                                            }
517                                                    }
518    
519                                                    if (!readAttributes) {
520                                                            previousAttribute = null;
521                                                            previousAttributeAndValue = null;
522                                                    }
523                                                    else {
524                                                            previousAttribute = attribute;
525                                                    }
526                                            }
527                                    }
528                                    else {
529                                            previousAttribute = null;
530    
531                                            readAttributes = false;
532                                    }
533                            }
534    
535                            if (!hasUnsortedExceptions) {
536                                    int i = line.indexOf("<liferay-ui:error exception=\"<%=");
537    
538                                    if (i != -1) {
539                                            currentException = line.substring(i + 33);
540    
541                                            if (Validator.isNotNull(previousException) &&
542                                                    (previousException.compareTo(currentException) > 0)) {
543    
544                                                    hasUnsortedExceptions = true;
545                                            }
546                                    }
547    
548                                    if (!hasUnsortedExceptions) {
549                                            previousException = currentException;
550                                            currentException = null;
551                                    }
552                            }
553    
554                            if (trimmedLine.startsWith(StringPool.LESS_THAN) &&
555                                    !trimmedLine.startsWith("<%") &&
556                                    !trimmedLine.startsWith("<!")) {
557    
558                                    if (!trimmedLine.contains(StringPool.GREATER_THAN) &&
559                                            !trimmedLine.contains(StringPool.SPACE)) {
560    
561                                            readAttributes = true;
562                                    }
563                                    else {
564                                            line = sortJSPAttributes(fileName, line, lineCount);
565                                    }
566                            }
567    
568                            if (!trimmedLine.contains(StringPool.DOUBLE_SLASH) &&
569                                    !trimmedLine.startsWith(StringPool.STAR)) {
570    
571                                    while (trimmedLine.contains(StringPool.TAB)) {
572                                            line = StringUtil.replaceLast(
573                                                    line, StringPool.TAB, StringPool.SPACE);
574    
575                                            trimmedLine = StringUtil.replaceLast(
576                                                    trimmedLine, StringPool.TAB, StringPool.SPACE);
577                                    }
578    
579                                    while (trimmedLine.contains(StringPool.DOUBLE_SPACE) &&
580                                               !trimmedLine.contains(
581                                                       StringPool.QUOTE + StringPool.DOUBLE_SPACE) &&
582                                               !fileName.endsWith(".vm")) {
583    
584                                            line = StringUtil.replaceLast(
585                                                    line, StringPool.DOUBLE_SPACE, StringPool.SPACE);
586    
587                                            trimmedLine = StringUtil.replaceLast(
588                                                    trimmedLine, StringPool.DOUBLE_SPACE, StringPool.SPACE);
589                                    }
590                            }
591    
592                            if (!fileName.endsWith("/touch.jsp")) {
593                                    int x = line.indexOf("<%@ include file");
594    
595                                    if (x != -1) {
596                                            x = line.indexOf(StringPool.QUOTE, x);
597    
598                                            int y = line.indexOf(StringPool.QUOTE, x + 1);
599    
600                                            if (y != -1) {
601                                                    String includeFileName = line.substring(x + 1, y);
602    
603                                                    Matcher matcher = _jspIncludeFilePattern.matcher(
604                                                            includeFileName);
605    
606                                                    if (!matcher.find()) {
607                                                            processErrorMessage(
608                                                                    fileName,
609                                                                    "include: " + fileName + " " + lineCount);
610                                                    }
611                                            }
612                                    }
613                            }
614    
615                            line = replacePrimitiveWrapperInstantiation(
616                                    fileName, line, lineCount);
617    
618                            previousLine = line;
619    
620                            sb.append(line);
621                            sb.append("\n");
622                    }
623    
624                    unsyncBufferedReader.close();
625    
626                    content = sb.toString();
627    
628                    if (content.endsWith("\n")) {
629                            content = content.substring(0, content.length() - 1);
630                    }
631    
632                    content = formatTaglibQuotes(fileName, content, StringPool.QUOTE);
633                    content = formatTaglibQuotes(fileName, content, StringPool.APOSTROPHE);
634    
635                    if (Validator.isNotNull(previousAttributeAndValue)) {
636                            content = StringUtil.replaceFirst(
637                                    content,
638                                    previousAttributeAndValue + "\n" + currentAttributeAndValue,
639                                    currentAttributeAndValue + "\n" + previousAttributeAndValue);
640                    }
641    
642                    if (hasUnsortedExceptions) {
643                            if ((StringUtil.count(content, currentException) > 1) ||
644                                    (StringUtil.count(content, previousException) > 1)) {
645    
646                                    processErrorMessage(
647                                            fileName, "unsorted exceptions: " + fileName);
648                            }
649                            else {
650                                    content = StringUtil.replaceFirst(
651                                            content, previousException, currentException);
652    
653                                    content = StringUtil.replaceLast(
654                                            content, currentException, previousException);
655                            }
656                    }
657    
658                    return content;
659            }
660    
661            protected String formatTaglibQuotes(
662                    String fileName, String content, String quoteType) {
663    
664                    String quoteFix = StringPool.APOSTROPHE;
665    
666                    if (quoteFix.equals(quoteType)) {
667                            quoteFix = StringPool.QUOTE;
668                    }
669    
670                    Pattern pattern = Pattern.compile(getTaglibRegex(quoteType));
671    
672                    Matcher matcher = pattern.matcher(content);
673    
674                    while (matcher.find()) {
675                            int x = content.indexOf(quoteType + "<%=", matcher.start());
676                            int y = content.indexOf("%>" + quoteType, x);
677    
678                            while ((x != -1) && (y != -1)) {
679                                    String result = content.substring(x + 1, y + 2);
680    
681                                    if (result.contains(quoteType)) {
682                                            int lineCount = 1;
683    
684                                            char[] contentCharArray = content.toCharArray();
685    
686                                            for (int i = 0; i < x; i++) {
687                                                    if (contentCharArray[i] == CharPool.NEW_LINE) {
688                                                            lineCount++;
689                                                    }
690                                            }
691    
692                                            if (!result.contains(quoteFix)) {
693                                                    StringBundler sb = new StringBundler(5);
694    
695                                                    sb.append(content.substring(0, x));
696                                                    sb.append(quoteFix);
697                                                    sb.append(result);
698                                                    sb.append(quoteFix);
699                                                    sb.append(content.substring(y + 3, content.length()));
700    
701                                                    content = sb.toString();
702                                            }
703                                            else {
704                                                    processErrorMessage(
705                                                            fileName, "taglib: " + fileName + " " + lineCount);
706                                            }
707                                    }
708    
709                                    x = content.indexOf(quoteType + "<%=", y);
710    
711                                    if (x > matcher.end()) {
712                                            break;
713                                    }
714    
715                                    y = content.indexOf("%>" + quoteType, x);
716                            }
717                    }
718    
719                    return content;
720            }
721    
722            protected List<String> getJSPDuplicateImports(
723                    String fileName, String content, List<String> importLines) {
724    
725                    List<String> duplicateImports = new ArrayList<String>();
726    
727                    for (String importLine : importLines) {
728                            int x = content.indexOf("<%@ include file=");
729    
730                            if (x == -1) {
731                                    continue;
732                            }
733    
734                            int y = content.indexOf("<%@ page import=");
735    
736                            if (y == -1) {
737                                    continue;
738                            }
739    
740                            if ((x < y) && isJSPDuplicateImport(fileName, importLine, false)) {
741                                    duplicateImports.add(importLine);
742                            }
743                    }
744    
745                    return duplicateImports;
746            }
747    
748            protected String getTaglibRegex(String quoteType) {
749                    StringBuilder sb = new StringBuilder();
750    
751                    sb.append("<(");
752    
753                    for (int i = 0; i < _TAG_LIBRARIES.length; i++) {
754                            sb.append(_TAG_LIBRARIES[i]);
755                            sb.append(StringPool.PIPE);
756                    }
757    
758                    sb.deleteCharAt(sb.length() - 1);
759                    sb.append("):([^>]|%>)*");
760                    sb.append(quoteType);
761                    sb.append("<%=.*");
762                    sb.append(quoteType);
763                    sb.append(".*%>");
764                    sb.append(quoteType);
765                    sb.append("([^>]|%>)*>");
766    
767                    return sb.toString();
768            }
769    
770            protected String getVariableName(String line) {
771                    if (!line.endsWith(";") || line.startsWith("//")) {
772                            return null;
773                    }
774    
775                    String variableName = null;
776    
777                    int x = line.indexOf(" = ");
778    
779                    if (x == -1) {
780                            int y = line.lastIndexOf(" ");
781    
782                            if (y != -1) {
783                                    variableName = line.substring(y + 1, line.length() - 1);
784                            }
785                    }
786                    else {
787                            line = line.substring(0, x);
788    
789                            int y = line.lastIndexOf(" ");
790    
791                            if (y != -1) {
792                                    variableName = line.substring(y + 1);
793                            }
794                    }
795    
796                    if (Validator.isVariableName(variableName)) {
797                            return variableName;
798                    }
799    
800                    return null;
801            }
802    
803            protected boolean hasUnusedVariable(String fileName, String line) {
804                    if (line.contains(": ")) {
805                            return false;
806                    }
807    
808                    String variableName = getVariableName(line);
809    
810                    if (Validator.isNull(variableName) || variableName.equals("false") ||
811                            variableName.equals("true")) {
812    
813                            return false;
814                    }
815    
816                    Set<String> includeFileNames = new HashSet<String>();
817    
818                    includeFileNames.add(fileName);
819    
820                    Set<String> checkedFileNames = new HashSet<String>();
821    
822                    return !isClassOrVariableRequired(
823                            fileName, variableName, includeFileNames, checkedFileNames);
824            }
825    
826            protected boolean isClassOrVariableRequired(
827                    String fileName, String name, Set<String> includeFileNames,
828                    Set<String> checkedFileNames) {
829    
830                    if (checkedFileNames.contains(fileName)) {
831                            return false;
832                    }
833    
834                    checkedFileNames.add(fileName);
835    
836                    String content = _jspContents.get(fileName);
837    
838                    if (Validator.isNull(content)) {
839                            return false;
840                    }
841    
842                    Pattern pattern = Pattern.compile(
843                            "[^A-Za-z0-9_]" + name + "[^A-Za-z0-9_]");
844    
845                    Matcher matcher = pattern.matcher(content);
846    
847                    if (matcher.find() &&
848                            ((checkedFileNames.size() > 1) || matcher.find())) {
849    
850                            return true;
851                    }
852    
853                    addJSPIncludeFileNames(fileName, includeFileNames);
854    
855                    String docrootPath = fileName.substring(
856                            0, fileName.indexOf("docroot") + 7);
857    
858                    fileName = fileName.replaceFirst(docrootPath, StringPool.BLANK);
859    
860                    if (fileName.endsWith("init.jsp") ||
861                            fileName.contains("init-ext.jsp")) {
862    
863                            addJSPReferenceFileNames(fileName, includeFileNames);
864                    }
865    
866                    String[] includeFileNamesArray = includeFileNames.toArray(
867                            new String[includeFileNames.size()]);
868    
869                    for (String includeFileName : includeFileNamesArray) {
870                            if (!checkedFileNames.contains(includeFileName) &&
871                                    isClassOrVariableRequired(
872                                            includeFileName, name, includeFileNames,
873                                            checkedFileNames)) {
874    
875                                    return true;
876                            }
877                    }
878    
879                    return false;
880            }
881    
882            protected boolean isJSPAttributName(String attributeName) {
883                    if (Validator.isNull(attributeName)) {
884                            return false;
885                    }
886    
887                    Matcher matcher = _jspAttributeNamePattern.matcher(attributeName);
888    
889                    return matcher.matches();
890            }
891    
892            protected boolean isJSPDuplicateImport(
893                    String fileName, String importLine, boolean checkFile) {
894    
895                    String content = _jspContents.get(fileName);
896    
897                    if (Validator.isNull(content)) {
898                            return false;
899                    }
900    
901                    int x = importLine.indexOf("page");
902    
903                    if (x == -1) {
904                            return false;
905                    }
906    
907                    if (checkFile && content.contains(importLine.substring(x))) {
908                            return true;
909                    }
910    
911                    int y = content.indexOf("<%@ include file=");
912    
913                    if (y == -1) {
914                            return false;
915                    }
916    
917                    y = content.indexOf(StringPool.QUOTE, y);
918    
919                    if (y == -1) {
920                            return false;
921                    }
922    
923                    int z = content.indexOf(StringPool.QUOTE, y + 1);
924    
925                    if (z == -1) {
926                            return false;
927                    }
928    
929                    String includeFileName = content.substring(y + 1, z);
930    
931                    String docrootPath = fileName.substring(
932                            0, fileName.indexOf("docroot") + 7);
933    
934                    includeFileName = docrootPath + includeFileName;
935    
936                    return isJSPDuplicateImport(includeFileName, importLine, true);
937            }
938    
939            protected String sortJSPAttributes(
940                    String fileName, String line, int lineCount) {
941    
942                    String s = line;
943    
944                    int x = s.indexOf(StringPool.SPACE);
945    
946                    if (x == -1) {
947                            return line;
948                    }
949    
950                    s = s.substring(x + 1);
951    
952                    String previousAttribute = null;
953                    String previousAttributeAndValue = null;
954    
955                    boolean wrongOrder = false;
956    
957                    for (x = 0;;) {
958                            x = s.indexOf(StringPool.EQUAL);
959    
960                            if ((x == -1) || (s.length() <= (x + 1))) {
961                                    return line;
962                            }
963    
964                            String attribute = s.substring(0, x);
965    
966                            if (!isJSPAttributName(attribute)) {
967                                    return line;
968                            }
969    
970                            if (Validator.isNotNull(previousAttribute) &&
971                                    (previousAttribute.compareTo(attribute) > 0)) {
972    
973                                    wrongOrder = true;
974                            }
975    
976                            s = s.substring(x + 1);
977    
978                            char delimeter = s.charAt(0);
979    
980                            if ((delimeter != CharPool.APOSTROPHE) &&
981                                    (delimeter != CharPool.QUOTE)) {
982    
983                                    processErrorMessage(
984                                            fileName, "delimeter: " + fileName + " " + lineCount);
985    
986                                    return line;
987                            }
988    
989                            s = s.substring(1);
990    
991                            String value = null;
992    
993                            int y = -1;
994    
995                            for (;;) {
996                                    y = s.indexOf(delimeter, y + 1);
997    
998                                    if ((y == -1) || (s.length() <= (y + 1))) {
999                                            return line;
1000                                    }
1001    
1002                                    value = s.substring(0, y);
1003    
1004                                    if (value.startsWith("<%")) {
1005                                            int endJavaCodeSignCount = StringUtil.count(value, "%>");
1006                                            int startJavaCodeSignCount = StringUtil.count(value, "<%");
1007    
1008                                            if (endJavaCodeSignCount == startJavaCodeSignCount) {
1009                                                    break;
1010                                            }
1011                                    }
1012                                    else {
1013                                            int greaterThanCount = StringUtil.count(
1014                                                    value, StringPool.GREATER_THAN);
1015                                            int lessThanCount = StringUtil.count(
1016                                                    value, StringPool.LESS_THAN);
1017    
1018                                            if (greaterThanCount == lessThanCount) {
1019                                                    break;
1020                                            }
1021                                    }
1022                            }
1023    
1024                            if ((delimeter == CharPool.APOSTROPHE) &&
1025                                    !value.contains(StringPool.QUOTE)) {
1026    
1027                                    return StringUtil.replace(
1028                                            line, StringPool.APOSTROPHE + value + StringPool.APOSTROPHE,
1029                                            StringPool.QUOTE + value + StringPool.QUOTE);
1030                            }
1031    
1032                            StringBundler sb = new StringBundler(5);
1033    
1034                            sb.append(attribute);
1035                            sb.append(StringPool.EQUAL);
1036                            sb.append(delimeter);
1037                            sb.append(value);
1038                            sb.append(delimeter);
1039    
1040                            String currentAttributeAndValue = sb.toString();
1041    
1042                            if (wrongOrder) {
1043                                    if ((StringUtil.count(line, currentAttributeAndValue) == 1) &&
1044                                            (StringUtil.count(line, previousAttributeAndValue) == 1)) {
1045    
1046                                            line = StringUtil.replaceFirst(
1047                                                    line, previousAttributeAndValue,
1048                                                    currentAttributeAndValue);
1049    
1050                                            line = StringUtil.replaceLast(
1051                                                    line, currentAttributeAndValue,
1052                                                    previousAttributeAndValue);
1053                                    }
1054    
1055                                    return line;
1056                            }
1057    
1058                            s = s.substring(y + 1);
1059    
1060                            s = StringUtil.trimLeading(s);
1061    
1062                            previousAttribute = attribute;
1063                            previousAttributeAndValue = currentAttributeAndValue;
1064                    }
1065            }
1066    
1067            protected String stripJSPImports(String fileName, String content)
1068                    throws IOException {
1069    
1070                    fileName = fileName.replace(
1071                            CharPool.BACK_SLASH, CharPool.FORWARD_SLASH);
1072    
1073                    if (!fileName.contains("docroot") ||
1074                            fileName.endsWith("init-ext.jsp")) {
1075    
1076                            return content;
1077                    }
1078    
1079                    Matcher matcher = _jspImportPattern.matcher(content);
1080    
1081                    if (!matcher.find()) {
1082                            return content;
1083                    }
1084    
1085                    String imports = matcher.group();
1086    
1087                    imports = StringUtil.replace(
1088                            imports, new String[] {"%><%@\r\n", "%><%@\n"},
1089                            new String[] {"%>\r\n<%@ ", "%>\n<%@ "});
1090    
1091                    if (!fileName.endsWith("html/common/init.jsp") &&
1092                            !fileName.endsWith("html/portal/init.jsp")) {
1093    
1094                            List<String> importLines = new ArrayList<String>();
1095    
1096                            UnsyncBufferedReader unsyncBufferedReader =
1097                                    new UnsyncBufferedReader(new UnsyncStringReader(imports));
1098    
1099                            String line = null;
1100    
1101                            while ((line = unsyncBufferedReader.readLine()) != null) {
1102                                    if (line.contains("import=")) {
1103                                            importLines.add(line);
1104                                    }
1105                            }
1106    
1107                            List<String> unneededImports = getJSPDuplicateImports(
1108                                    fileName, content, importLines);
1109    
1110                            addJSPUnusedImports(fileName, importLines, unneededImports);
1111    
1112                            for (String unneededImport : unneededImports) {
1113                                    imports = StringUtil.replace(
1114                                            imports, unneededImport, StringPool.BLANK);
1115                            }
1116                    }
1117    
1118                    imports = formatImports(imports, 17);
1119    
1120                    String beforeImports = content.substring(0, matcher.start());
1121    
1122                    if (Validator.isNull(imports)) {
1123                            beforeImports = StringUtil.replaceLast(
1124                                    beforeImports, "\n", StringPool.BLANK);
1125                    }
1126    
1127                    String afterImports = content.substring(matcher.end());
1128    
1129                    if (Validator.isNull(afterImports)) {
1130                            imports = StringUtil.replaceLast(imports, "\n", StringPool.BLANK);
1131    
1132                            content = beforeImports + imports;
1133    
1134                            return content;
1135                    }
1136    
1137                    content = beforeImports + imports + "\n" + afterImports;
1138    
1139                    return content;
1140            }
1141    
1142            private static final String[] _TAG_LIBRARIES = new String[] {
1143                    "aui", "c", "html", "jsp", "liferay-portlet", "liferay-security",
1144                    "liferay-theme", "liferay-ui", "liferay-util", "portlet", "struts",
1145                    "tiles"
1146            };
1147    
1148            private Pattern _jspAttributeNamePattern = Pattern.compile(
1149                    "[a-z]+[-_a-zA-Z0-9]*");
1150            private Map<String, String> _jspContents = new HashMap<String, String>();
1151            private Pattern _jspImportPattern = Pattern.compile(
1152                    "(<.*\n*page.import=\".*>\n*)+", Pattern.MULTILINE);
1153            private Pattern _jspIncludeFilePattern = Pattern.compile("/.*[.]jsp[f]?");
1154            private Pattern _taglibLanguageKeyPattern = Pattern.compile(
1155                    "(?:confirmation|label|(?:M|m)essage|message key|names|title)=\"[^A-Z" +
1156                            "<=%\\[\\s]+\"");
1157            private Pattern _xssPattern = Pattern.compile(
1158                    "\\s+([^\\s]+)\\s*=\\s*(Bean)?ParamUtil\\.getString\\(");
1159    
1160    }