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 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                                    // Set compressImports to false to decompress imports
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    }