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