001    /**
002     * Copyright (c) 2000-present 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.ArrayUtil;
020    import com.liferay.portal.kernel.util.CharPool;
021    import com.liferay.portal.kernel.util.ListUtil;
022    import com.liferay.portal.kernel.util.StringBundler;
023    import com.liferay.portal.kernel.util.StringPool;
024    import com.liferay.portal.kernel.util.StringUtil;
025    import com.liferay.portal.kernel.util.Tuple;
026    import com.liferay.portal.kernel.util.Validator;
027    
028    import com.thoughtworks.qdox.JavaDocBuilder;
029    import com.thoughtworks.qdox.model.JavaMethod;
030    
031    import java.io.File;
032    
033    import java.util.ArrayList;
034    import java.util.Iterator;
035    import java.util.List;
036    import java.util.Set;
037    import java.util.TreeSet;
038    import java.util.regex.Matcher;
039    import java.util.regex.Pattern;
040    
041    /**
042     * @author Hugo Huijser
043     */
044    public class JavaClass {
045    
046            public JavaClass(
047                            String name, String packagePath, File file, String fileName,
048                            String absolutePath, String content, int lineCount, String indent,
049                            JavaClass outerClass,
050                            List<String> javaTermAccessLevelModifierExclusions)
051                    throws Exception {
052    
053                    _name = name;
054                    _packagePath = packagePath;
055                    _file = file;
056                    _fileName = fileName;
057                    _absolutePath = absolutePath;
058                    _content = content;
059                    _lineCount = lineCount;
060                    _indent = indent;
061                    _outerClass = outerClass;
062                    _javaTermAccessLevelModifierExclusions =
063                            javaTermAccessLevelModifierExclusions;
064    
065                    _javaTerms = getJavaTerms();
066            }
067    
068            public String formatJavaTerms(
069                            Set<String> annotationsExclusions, Set<String> immutableFieldTypes,
070                            List<String> checkJavaFieldTypesExclusions,
071                            List<String> javaTermSortExclusions,
072                            List<String> testAnnotationsExclusions)
073                    throws Exception {
074    
075                    if ((_javaTerms == null) || _javaTerms.isEmpty()) {
076                            return _content;
077                    }
078    
079                    String originalContent = _content;
080    
081                    JavaTerm previousJavaTerm = null;
082    
083                    Iterator<JavaTerm> itr = _javaTerms.iterator();
084    
085                    while (itr.hasNext()) {
086                            JavaTerm javaTerm = itr.next();
087    
088                            if (javaTerm.isConstructor()) {
089                                    checkConstructor(javaTerm);
090                            }
091    
092                            checkUnusedParameters(javaTerm);
093    
094                            if (!BaseSourceProcessor.isExcluded(
095                                            checkJavaFieldTypesExclusions, _absolutePath)) {
096    
097                                    checkJavaFieldType(
098                                            javaTerm, annotationsExclusions, immutableFieldTypes);
099                            }
100    
101                            if (!originalContent.equals(_content)) {
102                                    return _content;
103                            }
104    
105                            sortJavaTerms(previousJavaTerm, javaTerm, javaTermSortExclusions);
106                            fixTabsAndIncorrectEmptyLines(javaTerm);
107                            formatAnnotations(javaTerm, testAnnotationsExclusions);
108    
109                            if (!originalContent.equals(_content)) {
110                                    return _content;
111                            }
112    
113                            previousJavaTerm = javaTerm;
114                    }
115    
116                    for (JavaClass innerClass : _innerClasses) {
117                            String innerClassContent = innerClass.getContent();
118    
119                            String newInnerClassContent = innerClass.formatJavaTerms(
120                                    annotationsExclusions, immutableFieldTypes,
121                                    checkJavaFieldTypesExclusions, javaTermSortExclusions,
122                                    testAnnotationsExclusions);
123    
124                            if (!innerClassContent.equals(newInnerClassContent)) {
125                                    _content = StringUtil.replace(
126                                            _content, innerClassContent, newInnerClassContent);
127    
128                                    return _content;
129                            }
130                    }
131    
132                    fixJavaTermsDividers(_javaTerms, javaTermSortExclusions);
133    
134                    return _content;
135            }
136    
137            public String getContent() {
138                    return _content;
139            }
140    
141            protected Set<JavaTerm> addStaticBlocks(
142                    Set<JavaTerm> javaTerms, List<JavaTerm> staticBlocks) {
143    
144                    Set<JavaTerm> newJavaTerms = new TreeSet<JavaTerm>(
145                            new JavaTermComparator());
146    
147                    Iterator<JavaTerm> javaTermsIterator = javaTerms.iterator();
148    
149                    while (javaTermsIterator.hasNext()) {
150                            JavaTerm javaTerm = javaTermsIterator.next();
151    
152                            if (!javaTerm.isStatic() || !javaTerm.isVariable()) {
153                                    newJavaTerms.add(javaTerm);
154    
155                                    continue;
156                            }
157    
158                            Iterator<JavaTerm> staticBlocksIterator = staticBlocks.iterator();
159    
160                            while (staticBlocksIterator.hasNext()) {
161                                    JavaTerm staticBlock = staticBlocksIterator.next();
162    
163                                    String staticBlockContent = staticBlock.getContent();
164    
165                                    if (staticBlockContent.contains(javaTerm.getName())) {
166                                            staticBlock.setType(javaTerm.getType() + 1);
167    
168                                            newJavaTerms.add(staticBlock);
169    
170                                            staticBlocksIterator.remove();
171                                    }
172                            }
173    
174                            newJavaTerms.add(javaTerm);
175                    }
176    
177                    if (!staticBlocks.isEmpty()) {
178                            newJavaTerms.addAll(staticBlocks);
179                    }
180    
181                    return newJavaTerms;
182            }
183    
184            protected void checkAnnotationForMethod(
185                    JavaTerm javaTerm, String annotation, String requiredMethodNameRegex,
186                    int requiredMethodType, String fileName) {
187    
188                    String methodContent = javaTerm.getContent();
189                    String methodName = javaTerm.getName();
190    
191                    Pattern pattern = Pattern.compile(requiredMethodNameRegex);
192    
193                    Matcher matcher = pattern.matcher(methodName);
194    
195                    if (methodContent.contains(
196                                    _indent + StringPool.AT + annotation + "\n") ||
197                            methodContent.contains(
198                                    _indent + StringPool.AT + annotation +
199                                            StringPool.OPEN_PARENTHESIS)) {
200    
201                            if (!matcher.find()) {
202                                    BaseSourceProcessor.processErrorMessage(
203                                            fileName,
204                                            "LPS-36303: Incorrect method name: " + methodName + " " +
205                                                    fileName);
206                            }
207                            else if (javaTerm.getType() != requiredMethodType) {
208                                    BaseSourceProcessor.processErrorMessage(
209                                            fileName,
210                                            "LPS-36303: Incorrect method type for " + methodName + " " +
211                                                    fileName);
212                            }
213                    }
214                    else if (matcher.find() &&
215                                     !methodContent.contains(_indent + "@Override")) {
216    
217                            BaseSourceProcessor.processErrorMessage(
218                                    fileName,
219                                    "Annotation @" + annotation + " required for " + methodName +
220                                            " " + fileName);
221                    }
222            }
223    
224            protected void checkConstructor(JavaTerm javaTerm) throws Exception {
225                    String javaTermContent = javaTerm.getContent();
226    
227                    if (javaTermContent.contains(StringPool.TAB + "super();")) {
228                            String newJavaTermContent = StringUtil.replace(
229                                    javaTermContent, StringPool.TAB + "super();", StringPool.BLANK);
230    
231                            _content = StringUtil.replace(
232                                    _content, javaTermContent, newJavaTermContent);
233    
234                            return;
235                    }
236    
237                    if (!ListUtil.isEmpty(javaTerm.getParameterTypes())) {
238                            checkConstructorParameterOrder(javaTerm);
239    
240                            return;
241                    }
242    
243                    if ((_packagePath == null) || (_constructorCount > 1) ||
244                            !javaTermContent.contains("{\n" + _indent + "}\n")) {
245    
246                            return;
247                    }
248    
249                    String accessModifier = getAccessModifier();
250    
251                    if ((javaTerm.isPrivate() &&
252                             !accessModifier.equals(_ACCESS_MODIFIER_PRIVATE)) ||
253                            (javaTerm.isProtected() &&
254                             !accessModifier.equals(_ACCESS_MODIFIER_PRIVATE) &&
255                             !accessModifier.equals(_ACCESS_MODIFIER_PROTECTED))) {
256    
257                            return;
258                    }
259    
260                    Pattern pattern = Pattern.compile("class " + _name + "[ \t\n]+extends");
261    
262                    Matcher matcher = pattern.matcher(_content);
263    
264                    if (!matcher.find()) {
265                            return;
266                    }
267    
268                    JavaDocBuilder javaDocBuilder = new JavaDocBuilder();
269    
270                    javaDocBuilder.addSource(_file);
271    
272                    com.thoughtworks.qdox.model.JavaClass javaClass =
273                            javaDocBuilder.getClassByName(
274                                    _packagePath + StringPool.PERIOD + _name);
275    
276                    com.thoughtworks.qdox.model.JavaClass superJavaClass =
277                            javaClass.getSuperJavaClass();
278    
279                    JavaMethod superJavaClassConstructor =
280                            superJavaClass.getMethodBySignature(superJavaClass.getName(), null);
281    
282                    if ((superJavaClassConstructor != null) &&
283                            ArrayUtil.isEmpty(superJavaClassConstructor.getExceptions())) {
284    
285                            _content = StringUtil.replace(
286                                    _content, javaTermContent, StringPool.BLANK);
287                    }
288            }
289    
290            protected void checkConstructorParameterOrder(JavaTerm javaTerm) {
291                    int previousPos = -1;
292    
293                    for (String parameterName : javaTerm.getParameterNames()) {
294                            Pattern pattern = Pattern.compile(
295                                    "\\{\n([\\s\\S]*?)(_" + parameterName + " =[ \t\n]+" +
296                                            parameterName + ";)");
297    
298                            Matcher matcher = pattern.matcher(javaTerm.getContent());
299    
300                            if (!matcher.find()) {
301                                    continue;
302                            }
303    
304                            String beforeParameter = matcher.group(1);
305    
306                            if (beforeParameter.contains(parameterName + " =")) {
307                                    continue;
308                            }
309    
310                            int pos = matcher.start(2);
311    
312                            if (previousPos > pos) {
313                                    BaseSourceProcessor.processErrorMessage(
314                                            _fileName,
315                                            "Constructor parameter order " + parameterName + ": " +
316                                                    _fileName);
317    
318                                    return;
319                            }
320    
321                            previousPos = pos;
322                    }
323            }
324    
325            protected void checkFinalableFieldType(
326                            JavaTerm javaTerm, Set<String> annotationsExclusions,
327                            boolean isStatic)
328                    throws Exception {
329    
330                    String javaTermContent = javaTerm.getContent();
331    
332                    for (String annotation : annotationsExclusions) {
333                            if (javaTermContent.contains(
334                                            _indent + StringPool.AT + annotation)) {
335    
336                                    return;
337                            }
338                    }
339    
340                    StringBundler sb = new StringBundler(4);
341    
342                    sb.append("(\\b|\\.)");
343                    sb.append(javaTerm.getName());
344                    sb.append(" (=)|(\\+\\+)|(--)|(\\+=)|(-=)|(\\*=)|(/=)|(%=)");
345                    sb.append("|(\\|=)|(&=)|(^=) ");
346    
347                    Pattern pattern = Pattern.compile(sb.toString());
348    
349                    if (!isFinalableField(javaTerm, _name, pattern, true)) {
350                            return;
351                    }
352    
353                    String newJavaTermContent = null;
354    
355                    if (isStatic) {
356                            newJavaTermContent = StringUtil.replaceFirst(
357                                    javaTermContent, "private static ", "private static final ");
358                    }
359                    else {
360                            newJavaTermContent = StringUtil.replaceFirst(
361                                    javaTermContent, "private ", "private final ");
362                    }
363    
364                    _content = StringUtil.replace(
365                            _content, javaTermContent, newJavaTermContent);
366            }
367    
368            protected void checkImmutableFieldType(JavaTerm javaTerm) {
369                    String oldName = javaTerm.getName();
370    
371                    if (oldName.equals("serialVersionUID")) {
372                            return;
373                    }
374    
375                    Matcher matcher = _camelCasePattern.matcher(oldName);
376    
377                    String newName = matcher.replaceAll("$1_$2");
378    
379                    newName = StringUtil.toUpperCase(newName);
380    
381                    if (newName.charAt(0) != CharPool.UNDERLINE) {
382                            newName = StringPool.UNDERLINE.concat(newName);
383                    }
384    
385                    _content = _content.replaceAll(
386                            "(?<=[\\W&&[^.\"]])(" + oldName + ")\\b", newName);
387            }
388    
389            protected void checkJavaFieldType(
390                            JavaTerm javaTerm, Set<String> annotationsExclusions,
391                            Set<String> immutableFieldTypes)
392                    throws Exception {
393    
394                    if (!BaseSourceProcessor.portalSource || !javaTerm.isVariable()) {
395                            return;
396                    }
397    
398                    Pattern pattern = Pattern.compile(
399                            "\t(private |protected |public )(static )?(final)?([\\s\\S]*?)" +
400                                    javaTerm.getName());
401    
402                    Matcher matcher = pattern.matcher(javaTerm.getContent());
403    
404                    if (!matcher.find()) {
405                            return;
406                    }
407    
408                    boolean isFinal = Validator.isNotNull(matcher.group(3));
409                    boolean isStatic = Validator.isNotNull(matcher.group(2));
410                    String javaFieldType = StringUtil.trim(matcher.group(4));
411    
412                    if (isFinal && isStatic && javaFieldType.startsWith("Map<")) {
413                            checkMutableFieldType(javaTerm);
414                    }
415    
416                    if (!javaTerm.isPrivate()) {
417                            return;
418                    }
419    
420                    if (isFinal) {
421                            if (immutableFieldTypes.contains(javaFieldType)) {
422                                    if (isStatic) {
423                                            checkImmutableFieldType(javaTerm);
424                                    }
425                                    else {
426                                            checkStaticableFieldType(javaTerm);
427                                    }
428                            }
429                    }
430                    else {
431                            checkFinalableFieldType(javaTerm, annotationsExclusions, isStatic);
432                    }
433            }
434    
435            protected void checkMutableFieldType(JavaTerm javaTerm) {
436                    String oldName = javaTerm.getName();
437    
438                    String newName = oldName;
439    
440                    if (newName.charAt(0) != CharPool.UNDERLINE) {
441                            newName = StringPool.UNDERLINE.concat(newName);
442                    }
443    
444                    if (StringUtil.isUpperCase(newName)) {
445                            StringBundler sb = new StringBundler(newName.length());
446    
447                            for (int i = 0; i < newName.length(); i++) {
448                                    char c = newName.charAt(i);
449    
450                                    if (i > 1) {
451                                            if (c == CharPool.UNDERLINE) {
452                                                    continue;
453                                            }
454    
455                                            if (newName.charAt(i - 1) == CharPool.UNDERLINE) {
456                                                    sb.append(c);
457    
458                                                    continue;
459                                            }
460                                    }
461    
462                                    sb.append(Character.toLowerCase(c));
463                            }
464    
465                            newName = sb.toString();
466                    }
467    
468    
469                    if (!newName.equals(oldName)) {
470                            _content = _content.replaceAll(
471                                    "(?<=[\\W&&[^.\"]])(" + oldName + ")\\b", newName);
472                    }
473            }
474    
475            protected void checkStaticableFieldType(JavaTerm javaTerm) {
476                    String javaTermContent = javaTerm.getContent();
477    
478                    if (!javaTermContent.contains(StringPool.EQUAL)) {
479                            return;
480                    }
481    
482                    String newJavaTermContent = StringUtil.replaceFirst(
483                            javaTermContent, "private final", "private static final");
484    
485                    _content = StringUtil.replace(
486                            _content, javaTermContent, newJavaTermContent);
487            }
488    
489            protected void checkTestAnnotations(JavaTerm javaTerm) {
490                    int methodType = javaTerm.getType();
491    
492                    if ((methodType != JavaTerm.TYPE_METHOD_PUBLIC) &&
493                            (methodType != JavaTerm.TYPE_METHOD_PUBLIC_STATIC)) {
494    
495                            return;
496                    }
497    
498                    checkAnnotationForMethod(
499                            javaTerm, "After", "^.*tearDown\\z", JavaTerm.TYPE_METHOD_PUBLIC,
500                            _fileName);
501                    checkAnnotationForMethod(
502                            javaTerm, "AfterClass", "^.*tearDownClass\\z",
503                            JavaTerm.TYPE_METHOD_PUBLIC_STATIC, _fileName);
504                    checkAnnotationForMethod(
505                            javaTerm, "Before", "^.*setUp\\z", JavaTerm.TYPE_METHOD_PUBLIC,
506                            _fileName);
507                    checkAnnotationForMethod(
508                            javaTerm, "BeforeClass", "^.*setUpClass\\z",
509                            JavaTerm.TYPE_METHOD_PUBLIC_STATIC, _fileName);
510                    checkAnnotationForMethod(
511                            javaTerm, "Test", "^.*test", JavaTerm.TYPE_METHOD_PUBLIC,
512                            _fileName);
513            }
514    
515            protected void checkUnusedParameters(JavaTerm javaTerm) {
516                    if (!javaTerm.isPrivate() || !javaTerm.isMethod()) {
517                            return;
518                    }
519    
520                    for (String parameterName : javaTerm.getParameterNames()) {
521                            if (StringUtil.count(javaTerm.getContent(), parameterName) == 1) {
522                                    BaseSourceProcessor.processErrorMessage(
523                                            _fileName,
524                                            "Unused parameter " + parameterName + ": " + _fileName +
525                                                    " " + javaTerm.getLineCount());
526                            }
527                    }
528            }
529    
530            protected void fixJavaTermsDividers(
531                    Set<JavaTerm> javaTerms, List<String> javaTermSortExclusions) {
532    
533                    JavaTerm previousJavaTerm = null;
534    
535                    Iterator<JavaTerm> itr = javaTerms.iterator();
536    
537                    while (itr.hasNext()) {
538                            JavaTerm javaTerm = itr.next();
539    
540                            if (previousJavaTerm == null) {
541                                    previousJavaTerm = javaTerm;
542    
543                                    continue;
544                            }
545    
546                            String javaTermContent = javaTerm.getContent();
547    
548                            if (javaTermContent.startsWith(_indent + "//")) {
549                                    previousJavaTerm = javaTerm;
550    
551                                    continue;
552                            }
553    
554                            String previousJavaTermContent = previousJavaTerm.getContent();
555    
556                            if (previousJavaTermContent.startsWith(_indent + "//")) {
557                                    previousJavaTerm = javaTerm;
558    
559                                    continue;
560                            }
561    
562                            String javaTermName = javaTerm.getName();
563    
564                            if (BaseSourceProcessor.isExcluded(
565                                            javaTermSortExclusions, _absolutePath,
566                                            javaTerm.getLineCount(), javaTermName)) {
567    
568                                    previousJavaTerm = javaTerm;
569    
570                                    continue;
571                            }
572    
573                            String previousJavaTermName = previousJavaTerm.getName();
574    
575                            boolean requiresEmptyLine = false;
576    
577                            if (previousJavaTerm.getType() != javaTerm.getType()) {
578                                    requiresEmptyLine = true;
579                            }
580                            else if (!javaTerm.isVariable()) {
581                                    requiresEmptyLine = true;
582                            }
583                            else if ((StringUtil.isUpperCase(javaTermName) &&
584                                              !StringUtil.isLowerCase(javaTermName)) ||
585                                             (StringUtil.isUpperCase(previousJavaTermName) &&
586                                              !StringUtil.isLowerCase(previousJavaTermName))) {
587    
588                                    requiresEmptyLine = true;
589                            }
590                            else if (hasAnnotationCommentOrJavadoc(javaTermContent) ||
591                                             hasAnnotationCommentOrJavadoc(previousJavaTermContent)) {
592    
593                                    requiresEmptyLine = true;
594                            }
595                            else if ((previousJavaTerm.getType() ==
596                                                    JavaTerm.TYPE_VARIABLE_PRIVATE_STATIC) &&
597                                             (previousJavaTermName.equals("_instance") ||
598                                              previousJavaTermName.equals("_log") ||
599                                              previousJavaTermName.equals("_logger"))) {
600    
601                                    requiresEmptyLine = true;
602                            }
603                            else if (previousJavaTermContent.contains("\n\n\t") ||
604                                             javaTermContent.contains("\n\n\t")) {
605    
606                                    requiresEmptyLine = true;
607                            }
608    
609                            if (requiresEmptyLine) {
610                                    if (!_content.contains("\n\n" + javaTermContent)) {
611                                            _content = StringUtil.replace(
612                                                    _content, "\n" + javaTermContent,
613                                                    "\n\n" + javaTermContent);
614    
615                                            return;
616                                    }
617                            }
618                            else if (_content.contains("\n\n" + javaTermContent)) {
619                                    _content = StringUtil.replace(
620                                            _content, "\n\n" + javaTermContent, "\n" + javaTermContent);
621    
622                                    return;
623                            }
624    
625                            previousJavaTerm = javaTerm;
626                    }
627    
628                    String lastJavaTermContent = previousJavaTerm.getContent();
629    
630                    if (!lastJavaTermContent.endsWith("\n\n")) {
631                            int x = _content.lastIndexOf(CharPool.CLOSE_CURLY_BRACE);
632    
633                            _content = StringUtil.insert(
634                                    _content, "\n", x - _indent.length() + 1);
635                    }
636            }
637    
638            protected String fixLeadingTabs(
639                    String content, String line, int expectedTabCount) {
640    
641                    int leadingTabCount = JavaSourceProcessor.getLeadingTabCount(line);
642    
643                    String newLine = line;
644    
645                    while (leadingTabCount != expectedTabCount) {
646                            if (leadingTabCount > expectedTabCount) {
647                                    newLine = StringUtil.replaceFirst(
648                                            newLine, StringPool.TAB, StringPool.BLANK);
649    
650                                    leadingTabCount--;
651                            }
652                            else {
653                                    newLine = StringPool.TAB + newLine;
654    
655                                    leadingTabCount++;
656                            }
657                    }
658    
659                    return StringUtil.replace(content, line, newLine);
660            }
661    
662            protected void fixTabsAndIncorrectEmptyLines(JavaTerm javaTerm) {
663                    if (!javaTerm.isConstructor() && !javaTerm.isMethod()) {
664                            return;
665                    }
666    
667                    String javaTermContent = "\n" + javaTerm.getContent();
668    
669                    Pattern methodNameAndParametersPattern = Pattern.compile(
670                            "\n" + _indent + "(private |protected |public ).*?(\\{|;)\n",
671                            Pattern.DOTALL);
672    
673                    Matcher matcher = methodNameAndParametersPattern.matcher(
674                            javaTermContent);
675    
676                    if (!matcher.find()) {
677                            return;
678                    }
679    
680                    String methodNameAndParameters = matcher.group();
681    
682                    String[] lines = StringUtil.splitLines(methodNameAndParameters);
683    
684                    if (lines.length == 1) {
685                            if (methodNameAndParameters.endsWith("{\n") &&
686                                    javaTermContent.contains(methodNameAndParameters + "\n") &&
687                                    !javaTermContent.contains(
688                                            methodNameAndParameters + "\n" + _indent + StringPool.TAB +
689                                                    "/*") &&
690                                    !javaTermContent.contains(
691                                            methodNameAndParameters + "\n" + _indent + StringPool.TAB +
692                                                    "// ")) {
693    
694                                    String trimmedJavaTermContent = StringUtil.trimTrailing(
695                                            javaTermContent);
696    
697                                    if (!trimmedJavaTermContent.endsWith(
698                                                    "\n\n" + _indent + StringPool.CLOSE_CURLY_BRACE)) {
699    
700                                            _content = StringUtil.replace(
701                                                    _content, methodNameAndParameters + "\n",
702                                                    methodNameAndParameters);
703                                    }
704                            }
705    
706                            return;
707                    }
708    
709                    if (methodNameAndParameters.endsWith("{\n") &&
710                            !javaTermContent.contains(methodNameAndParameters + "\n") &&
711                            !javaTermContent.contains(
712                                    methodNameAndParameters + _indent +
713                                            StringPool.CLOSE_CURLY_BRACE)) {
714    
715                            _content = StringUtil.replace(
716                                    _content, methodNameAndParameters,
717                                    methodNameAndParameters + "\n");
718                    }
719    
720                    boolean throwsException = methodNameAndParameters.contains(
721                            _indent + "throws ");
722    
723                    String newMethodNameAndParameters = methodNameAndParameters;
724    
725                    int expectedTabCount = -1;
726    
727                    for (int i = 0; i < lines.length; i++) {
728                            String line = lines[i];
729    
730                            if (line.contains(_indent + "throws ")) {
731                                    newMethodNameAndParameters = fixLeadingTabs(
732                                            newMethodNameAndParameters, line, _indent.length() + 1);
733    
734                                    break;
735                            }
736    
737                            if (expectedTabCount == -1) {
738                                    if (line.endsWith(StringPool.OPEN_PARENTHESIS)) {
739                                            expectedTabCount =
740                                                    Math.max(
741                                                            JavaSourceProcessor.getLeadingTabCount(line),
742                                                            _indent.length()) +
743                                                                    1;
744    
745                                            if (throwsException &&
746                                                    (expectedTabCount == (_indent.length() + 1))) {
747    
748                                                    expectedTabCount += 1;
749                                            }
750                                    }
751                            }
752                            else {
753                                    String previousLine = lines[i - 1];
754    
755                                    if (previousLine.endsWith(StringPool.COMMA) ||
756                                            previousLine.endsWith(StringPool.OPEN_PARENTHESIS)) {
757    
758                                            newMethodNameAndParameters = fixLeadingTabs(
759                                                    newMethodNameAndParameters, line, expectedTabCount);
760                                    }
761                                    else {
762                                            newMethodNameAndParameters = fixLeadingTabs(
763                                                    newMethodNameAndParameters, line,
764                                                    JavaSourceProcessor.getLeadingTabCount(previousLine) +
765                                                            1);
766                                    }
767                            }
768                    }
769    
770                    _content = StringUtil.replace(
771                            _content, methodNameAndParameters, newMethodNameAndParameters);
772            }
773    
774            protected void formatAnnotations(
775                            JavaTerm javaTerm, List<String> testAnnotationsExclusions)
776                    throws Exception {
777    
778                    if ((_indent.length() == 1) &&
779                            !BaseSourceProcessor.isExcluded(
780                                    testAnnotationsExclusions, _absolutePath) &&
781                            _fileName.endsWith("Test.java")) {
782    
783                            checkTestAnnotations(javaTerm);
784                    }
785    
786                    String javaTermContent = javaTerm.getContent();
787    
788                    String newJavaTermContent = JavaSourceProcessor.sortAnnotations(
789                            javaTermContent, _indent);
790    
791                    if (!javaTermContent.equals(newJavaTermContent)) {
792                            _content = _content.replace(javaTermContent, newJavaTermContent);
793                    }
794            }
795    
796            protected String getAccessModifier() {
797                    Matcher matcher = _classPattern.matcher(_content);
798    
799                    if (matcher.find()) {
800                            String accessModifier = matcher.group(1);
801    
802                            if (accessModifier.equals(_ACCESS_MODIFIER_PRIVATE) ||
803                                    accessModifier.equals(_ACCESS_MODIFIER_PROTECTED) ||
804                                    accessModifier.equals(_ACCESS_MODIFIER_PUBLIC)) {
805    
806                                    return accessModifier;
807                            }
808                    }
809    
810                    return _ACCESS_MODIFIER_UNKNOWN;
811            }
812    
813            protected String getClassName(String line) {
814                    int pos = line.indexOf(" extends ");
815    
816                    if (pos == -1) {
817                            pos = line.indexOf(" implements ");
818                    }
819    
820                    if (pos == -1) {
821                            pos = line.indexOf(StringPool.OPEN_CURLY_BRACE);
822                    }
823    
824                    if (pos != -1) {
825                            line = line.substring(0, pos);
826                    }
827    
828                    pos = line.indexOf(StringPool.LESS_THAN);
829    
830                    if (pos != -1) {
831                            line = line.substring(0, pos);
832                    }
833    
834                    line = line.trim();
835    
836                    pos = line.lastIndexOf(StringPool.SPACE);
837    
838                    return line.substring(pos + 1);
839            }
840    
841            protected String getConstructorOrMethodName(String line, int pos) {
842                    line = line.substring(0, pos);
843    
844                    int x = line.lastIndexOf(StringPool.SPACE);
845    
846                    return line.substring(x + 1);
847            }
848    
849            protected JavaTerm getJavaTerm(
850                            String name, int type, int lineCount, int startPos, int endPos)
851                    throws Exception {
852    
853                    String javaTermContent = _content.substring(startPos, endPos);
854    
855                    if (Validator.isNull(name) || !isValidJavaTerm(javaTermContent)) {
856                            return null;
857                    }
858    
859                    JavaTerm javaTerm = new JavaTerm(
860                            name, type, javaTermContent, lineCount);
861    
862                    if (javaTerm.isConstructor()) {
863                            _constructorCount++;
864                    }
865    
866                    if (!javaTerm.isClass()) {
867                            return javaTerm;
868                    }
869    
870                    JavaClass innerClass = new JavaClass(
871                            name, _packagePath, _file, _fileName, _absolutePath,
872                            javaTermContent, lineCount, _indent + StringPool.TAB, this,
873                            _javaTermAccessLevelModifierExclusions);
874    
875                    _innerClasses.add(innerClass);
876    
877                    return javaTerm;
878            }
879    
880            protected Set<JavaTerm> getJavaTerms() throws Exception {
881                    if (_javaTerms != null) {
882                            return _javaTerms;
883                    }
884    
885                    Set<JavaTerm> javaTerms = new TreeSet<JavaTerm>(
886                            new JavaTermComparator(false));
887                    List<JavaTerm> staticBlocks = new ArrayList<JavaTerm>();
888    
889                    UnsyncBufferedReader unsyncBufferedReader = new UnsyncBufferedReader(
890                            new UnsyncStringReader(_content));
891    
892                    int index = 0;
893                    int lineCount = _lineCount - 1;
894    
895                    String line = null;
896    
897                    String javaTermName = null;
898                    int javaTermLineCount = -1;
899                    int javaTermStartPosition = -1;
900                    int javaTermType = -1;
901    
902                    int lastCommentOrAnnotationPos = -1;
903    
904                    while ((line = unsyncBufferedReader.readLine()) != null) {
905                            lineCount++;
906    
907                            if (JavaSourceProcessor.getLeadingTabCount(line) !=
908                                            _indent.length()) {
909    
910                                    index = index + line.length() + 1;
911    
912                                    continue;
913                            }
914    
915                            if (line.startsWith(_indent + "private ") ||
916                                    line.equals(_indent + "private") ||
917                                    line.startsWith(_indent + "protected ") ||
918                                    line.equals(_indent + "protected") ||
919                                    line.startsWith(_indent + "public ") ||
920                                    line.equals(_indent + "public") ||
921                                    line.equals(_indent + "static {")) {
922    
923                                    Tuple tuple = getJavaTermTuple(line, _content, index);
924    
925                                    if (tuple == null) {
926                                            return null;
927                                    }
928    
929                                    int javaTermEndPosition = 0;
930    
931                                    if (lastCommentOrAnnotationPos == -1) {
932                                            javaTermEndPosition = index;
933                                    }
934                                    else {
935                                            javaTermEndPosition = lastCommentOrAnnotationPos;
936                                    }
937    
938                                    if ((javaTermStartPosition != -1) &&
939                                            (javaTermEndPosition < _content.length())) {
940    
941                                            JavaTerm javaTerm = getJavaTerm(
942                                                    javaTermName, javaTermType, javaTermLineCount,
943                                                    javaTermStartPosition, javaTermEndPosition);
944    
945                                            if (javaTerm == null) {
946                                                    return null;
947                                            }
948    
949                                            if (javaTermType == JavaTerm.TYPE_STATIC_BLOCK) {
950                                                    staticBlocks.add(javaTerm);
951                                            }
952                                            else {
953                                                    javaTerms.add(javaTerm);
954                                            }
955                                    }
956    
957                                    javaTermLineCount = lineCount;
958                                    javaTermName = (String)tuple.getObject(0);
959                                    javaTermStartPosition = javaTermEndPosition;
960                                    javaTermType = (Integer)tuple.getObject(1);
961    
962                                    lastCommentOrAnnotationPos = -1;
963                            }
964                            else if (hasAnnotationCommentOrJavadoc(line)) {
965                                    if (lastCommentOrAnnotationPos == -1) {
966                                            lastCommentOrAnnotationPos = index;
967                                    }
968                            }
969                            else if (!line.startsWith(_indent + StringPool.CLOSE_CURLY_BRACE) &&
970                                             !line.startsWith(_indent + StringPool.CLOSE_PARENTHESIS) &&
971                                             !line.startsWith(_indent + "extends") &&
972                                             !line.startsWith(_indent + "implements") &&
973                                             !BaseSourceProcessor.isExcluded(
974                                                     _javaTermAccessLevelModifierExclusions, _absolutePath,
975                                                     lineCount)) {
976    
977                                    Matcher matcher = _classPattern.matcher(_content);
978    
979                                    if (matcher.find()) {
980                                            String insideClass = _content.substring(matcher.end());
981    
982                                            if (insideClass.contains(line)) {
983                                                    BaseSourceProcessor.processErrorMessage(
984                                                            _fileName,
985                                                            "Missing access level modifier: " + _fileName +
986                                                                    " " + lineCount);
987                                            }
988                                    }
989                            }
990    
991                            index = index + line.length() + 1;
992                    }
993    
994                    if (javaTermStartPosition != -1) {
995                            int javaTermEndPosition =
996                                    _content.lastIndexOf(StringPool.CLOSE_CURLY_BRACE) -
997                                            _indent.length() + 1;
998    
999                            JavaTerm javaTerm = getJavaTerm(
1000                                    javaTermName, javaTermType, javaTermLineCount,
1001                                    javaTermStartPosition, javaTermEndPosition);
1002    
1003                            if (javaTerm == null) {
1004                                    return null;
1005                            }
1006    
1007                            if (javaTermType == JavaTerm.TYPE_STATIC_BLOCK) {
1008                                    staticBlocks.add(javaTerm);
1009                            }
1010                            else {
1011                                    javaTerms.add(javaTerm);
1012                            }
1013                    }
1014    
1015                    _javaTerms = addStaticBlocks(javaTerms, staticBlocks);
1016    
1017                    return _javaTerms;
1018            }
1019    
1020            protected Tuple getJavaTermTuple(String line, String accessModifier) {
1021                    if (!line.startsWith(_indent + accessModifier + StringPool.SPACE)) {
1022                            return null;
1023                    }
1024    
1025                    int x = line.indexOf(StringPool.EQUAL);
1026                    int y = line.indexOf(StringPool.OPEN_PARENTHESIS);
1027    
1028                    if (line.startsWith(_indent + accessModifier + " static ")) {
1029                            if (line.contains(" class ") || line.contains(" enum ")) {
1030                                    return getJavaTermTuple(
1031                                            getClassName(line), accessModifier,
1032                                            JavaTerm.TYPE_CLASS_PRIVATE_STATIC,
1033                                            JavaTerm.TYPE_CLASS_PROTECTED_STATIC,
1034                                            JavaTerm.TYPE_CLASS_PUBLIC_STATIC);
1035                            }
1036    
1037                            if (((x > 0) && ((y == -1) || (y > x))) ||
1038                                    (line.endsWith(StringPool.SEMICOLON) && (y == -1))) {
1039    
1040                                    return getJavaTermTuple(
1041                                            getVariableName(line), accessModifier,
1042                                            JavaTerm.TYPE_VARIABLE_PRIVATE_STATIC,
1043                                            JavaTerm.TYPE_VARIABLE_PROTECTED_STATIC,
1044                                            JavaTerm.TYPE_VARIABLE_PUBLIC_STATIC);
1045                            }
1046    
1047                            if (y != -1) {
1048                                    return getJavaTermTuple(
1049                                            getConstructorOrMethodName(line, y), accessModifier,
1050                                            JavaTerm.TYPE_METHOD_PRIVATE_STATIC,
1051                                            JavaTerm.TYPE_METHOD_PROTECTED_STATIC,
1052                                            JavaTerm.TYPE_METHOD_PUBLIC_STATIC);
1053                            }
1054    
1055                            return null;
1056                    }
1057    
1058                    if (line.contains(" @interface ") || line.contains(" class ") ||
1059                            line.contains(" enum ") || line.contains(" interface ")) {
1060    
1061                            return getJavaTermTuple(
1062                                    getClassName(line), accessModifier, JavaTerm.TYPE_CLASS_PRIVATE,
1063                                    JavaTerm.TYPE_CLASS_PROTECTED, JavaTerm.TYPE_CLASS_PUBLIC);
1064                    }
1065    
1066                    if (((x > 0) && ((y == -1) || (y > x))) ||
1067                            (line.endsWith(StringPool.SEMICOLON) && (y == -1))) {
1068    
1069                            return getJavaTermTuple(
1070                                    getVariableName(line), accessModifier,
1071                                    JavaTerm.TYPE_VARIABLE_PRIVATE,
1072                                    JavaTerm.TYPE_VARIABLE_PROTECTED,
1073                                    JavaTerm.TYPE_VARIABLE_PUBLIC);
1074                    }
1075    
1076                    if (y != -1) {
1077                            int spaceCount = StringUtil.count(
1078                                    line.substring(0, y), StringPool.SPACE);
1079    
1080                            if (spaceCount == 1) {
1081                                    return getJavaTermTuple(
1082                                            getConstructorOrMethodName(line, y), accessModifier,
1083                                            JavaTerm.TYPE_CONSTRUCTOR_PRIVATE,
1084                                            JavaTerm.TYPE_CONSTRUCTOR_PROTECTED,
1085                                            JavaTerm.TYPE_CONSTRUCTOR_PUBLIC);
1086                            }
1087    
1088                            if (spaceCount > 1) {
1089                                    return getJavaTermTuple(
1090                                            getConstructorOrMethodName(line, y), accessModifier,
1091                                            JavaTerm.TYPE_METHOD_PRIVATE,
1092                                            JavaTerm.TYPE_METHOD_PROTECTED,
1093                                            JavaTerm.TYPE_METHOD_PUBLIC);
1094                            }
1095                    }
1096    
1097                    return null;
1098            }
1099    
1100            protected Tuple getJavaTermTuple(String line, String content, int index) {
1101                    int posStartNextLine = index;
1102    
1103                    while (!line.endsWith(StringPool.OPEN_CURLY_BRACE) &&
1104                               !line.endsWith(StringPool.SEMICOLON)) {
1105    
1106                            posStartNextLine =
1107                                    content.indexOf(StringPool.NEW_LINE, posStartNextLine) + 1;
1108    
1109                            int posEndNextline = content.indexOf(
1110                                    StringPool.NEW_LINE, posStartNextLine);
1111    
1112                            String nextLine = content.substring(
1113                                    posStartNextLine, posEndNextline);
1114    
1115                            nextLine = StringUtil.trimLeading(nextLine);
1116    
1117                            if (line.endsWith(StringPool.OPEN_PARENTHESIS)) {
1118                                    line += nextLine;
1119                            }
1120                            else {
1121                                    line += StringPool.SPACE + nextLine;
1122                            }
1123                    }
1124    
1125                    line = StringUtil.replace(line, " synchronized " , StringPool.SPACE);
1126    
1127                    for (String accessModifier : _ACCESS_MODIFIERS) {
1128                            Tuple tuple = getJavaTermTuple(line, accessModifier);
1129    
1130                            if (tuple != null) {
1131                                    return tuple;
1132                            }
1133                    }
1134    
1135                    if (line.startsWith(_indent + "static {")) {
1136                            return new Tuple("static", JavaTerm.TYPE_STATIC_BLOCK);
1137                    }
1138    
1139                    return null;
1140            }
1141    
1142            protected Tuple getJavaTermTuple(
1143                    String javaTermName, String accessModifier, int privateJavaTermType,
1144                    int protectedJavaTermType, int publicJavaTermType) {
1145    
1146                    if (accessModifier.equals(_ACCESS_MODIFIER_PRIVATE)) {
1147                            return new Tuple(javaTermName, privateJavaTermType);
1148                    }
1149    
1150                    if (accessModifier.equals(_ACCESS_MODIFIER_PROTECTED)) {
1151                            return new Tuple(javaTermName, protectedJavaTermType);
1152                    }
1153    
1154                    return new Tuple(javaTermName, publicJavaTermType);
1155            }
1156    
1157            protected String getVariableName(String line) {
1158                    int x = line.indexOf(StringPool.EQUAL);
1159                    int y = line.lastIndexOf(StringPool.SPACE);
1160    
1161                    if (x != -1) {
1162                            line = line.substring(0, x);
1163                            line = StringUtil.trim(line);
1164    
1165                            y = line.lastIndexOf(StringPool.SPACE);
1166    
1167                            return line.substring(y + 1);
1168                    }
1169    
1170                    if (line.endsWith(StringPool.SEMICOLON)) {
1171                            return line.substring(y + 1, line.length() - 1);
1172                    }
1173    
1174                    return StringPool.BLANK;
1175            }
1176    
1177            protected boolean hasAnnotationCommentOrJavadoc(String s) {
1178                    if (s.startsWith(_indent + StringPool.AT) ||
1179                            s.startsWith(_indent + StringPool.SLASH) ||
1180                            s.startsWith(_indent + " *")) {
1181    
1182                            return true;
1183                    }
1184                    else {
1185                            return false;
1186                    }
1187            }
1188    
1189            protected boolean isFinalableField(
1190                    JavaTerm javaTerm, String javaTermClassName, Pattern pattern,
1191                    boolean checkOuterClass) {
1192    
1193                    if (checkOuterClass && (_outerClass != null)) {
1194                            return _outerClass.isFinalableField(
1195                                    javaTerm, javaTermClassName, pattern, true);
1196                    }
1197    
1198                    for (JavaTerm curJavaTerm : _javaTerms) {
1199                            if (!curJavaTerm.isMethod() &&
1200                                    (!curJavaTerm.isConstructor() ||
1201                                     javaTermClassName.equals(_name))) {
1202    
1203                                    continue;
1204                            }
1205    
1206                            Matcher matcher = pattern.matcher(curJavaTerm.getContent());
1207    
1208                            if (matcher.find()) {
1209                                    return false;
1210                            }
1211                    }
1212    
1213                    for (JavaClass innerClass : _innerClasses) {
1214                            if (!innerClass.isFinalableField(
1215                                            javaTerm, javaTermClassName, pattern, false)) {
1216    
1217                                    return false;
1218                            }
1219                    }
1220    
1221                    return true;
1222            }
1223    
1224            protected boolean isValidJavaTerm(String content) {
1225                    if (content.startsWith(_indent + "static {")) {
1226                            return true;
1227                    }
1228    
1229                    while (!content.startsWith(_indent + "private") &&
1230                               !content.startsWith(_indent + "protected") &&
1231                               !content.startsWith(_indent + "public")) {
1232    
1233                            content = content.substring(content.indexOf("\n") + 1);
1234                    }
1235    
1236                    int indentLinesCount =
1237                            StringUtil.count(content, "\n" + _indent) -
1238                                    StringUtil.count(content, "\n" + _indent + StringPool.TAB);
1239    
1240                    content = StringUtil.trim(content);
1241    
1242                    if (content.endsWith(StringPool.CLOSE_CURLY_BRACE) &&
1243                            ((indentLinesCount == 1) ||
1244                             (((indentLinesCount == 2) || (indentLinesCount == 3)) &&
1245                              content.contains("\n" + _indent + "static {")))) {
1246    
1247                            return true;
1248                    }
1249                    else if ((content.endsWith("};") && (indentLinesCount == 1)) ||
1250                                     (content.endsWith(StringPool.SEMICOLON) &&
1251                                      (indentLinesCount == 0))) {
1252    
1253                            return true;
1254                    }
1255    
1256                    return false;
1257            }
1258    
1259            protected void sortJavaTerms(
1260                    JavaTerm previousJavaTerm, JavaTerm javaTerm,
1261                    List<String> javaTermSortExclusions) {
1262    
1263                    if (previousJavaTerm == null) {
1264                            return;
1265                    }
1266    
1267                    String javaTermName = javaTerm.getName();
1268    
1269                    if (BaseSourceProcessor.isExcluded(
1270                                    javaTermSortExclusions, _absolutePath, -1, javaTermName)) {
1271    
1272                            return;
1273                    }
1274    
1275                    if (previousJavaTerm.getLineCount() <= javaTerm.getLineCount()) {
1276                            return;
1277                    }
1278    
1279                    String previousJavaTermName = previousJavaTerm.getName();
1280    
1281                    String javaTermNameLowerCase = StringUtil.toLowerCase(javaTermName);
1282                    String previousJavaTermNameLowerCase = StringUtil.toLowerCase(
1283                            previousJavaTermName);
1284    
1285                    if (_fileName.contains("persistence") &&
1286                            ((previousJavaTermName.startsWith("doCount") &&
1287                              javaTermName.startsWith("doCount")) ||
1288                             (previousJavaTermName.startsWith("doFind") &&
1289                              javaTermName.startsWith("doFind")) ||
1290                             (previousJavaTermNameLowerCase.startsWith("count") &&
1291                              javaTermNameLowerCase.startsWith("count")) ||
1292                             (previousJavaTermNameLowerCase.startsWith("filter") &&
1293                              javaTermNameLowerCase.startsWith("filter")) ||
1294                             (previousJavaTermNameLowerCase.startsWith("find") &&
1295                              javaTermNameLowerCase.startsWith("find")) ||
1296                             (previousJavaTermNameLowerCase.startsWith("join") &&
1297                              javaTermNameLowerCase.startsWith("join")))) {
1298                    }
1299                    else {
1300                            _content = StringUtil.replaceFirst(
1301                                    _content, "\n" + javaTerm.getContent(),
1302                                    "\n" + previousJavaTerm.getContent());
1303                            _content = StringUtil.replaceLast(
1304                                    _content, "\n" + previousJavaTerm.getContent(),
1305                                    "\n" + javaTerm.getContent());
1306                    }
1307            }
1308    
1309            private static final String _ACCESS_MODIFIER_PRIVATE = "private";
1310    
1311            private static final String _ACCESS_MODIFIER_PROTECTED = "protected";
1312    
1313            private static final String _ACCESS_MODIFIER_PUBLIC = "public";
1314    
1315            private static final String _ACCESS_MODIFIER_UNKNOWN = "unknown";
1316    
1317            private static final String[] _ACCESS_MODIFIERS = {
1318                    _ACCESS_MODIFIER_PRIVATE, _ACCESS_MODIFIER_PROTECTED,
1319                    _ACCESS_MODIFIER_PUBLIC
1320            };
1321    
1322            private String _absolutePath;
1323            private Pattern _camelCasePattern = Pattern.compile("([a-z])([A-Z0-9])");
1324            private Pattern _classPattern = Pattern.compile(
1325                    "(private |protected |public )(static )*class ([\\s\\S]*?) \\{\n");
1326            private int _constructorCount = 0;
1327            private String _content;
1328            private File _file;
1329            private String _fileName;
1330            private String _indent;
1331            private List<JavaClass> _innerClasses = new ArrayList<JavaClass>();
1332            private List<String> _javaTermAccessLevelModifierExclusions;
1333            private Set<JavaTerm> _javaTerms;
1334            private int _lineCount;
1335            private String _name;
1336            private JavaClass _outerClass;
1337            private String _packagePath;
1338    
1339    }