001
014
015 package com.liferay.portal.tools.sourceformatter;
016
017 import com.liferay.portal.kernel.io.unsync.UnsyncBufferedReader;
018 import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
019 import com.liferay.portal.kernel.util.ArrayUtil;
020 import com.liferay.portal.kernel.util.CharPool;
021 import com.liferay.portal.kernel.util.GetterUtil;
022 import com.liferay.portal.kernel.util.ListUtil;
023 import com.liferay.portal.kernel.util.ReflectionUtil;
024 import com.liferay.portal.kernel.util.ReleaseInfo;
025 import com.liferay.portal.kernel.util.SetUtil;
026 import com.liferay.portal.kernel.util.StringBundler;
027 import com.liferay.portal.kernel.util.StringPool;
028 import com.liferay.portal.kernel.util.StringUtil;
029 import com.liferay.portal.kernel.util.TextFormatter;
030 import com.liferay.portal.kernel.util.Validator;
031 import com.liferay.portal.util.FileImpl;
032 import com.liferay.portal.xml.SAXReaderImpl;
033
034 import java.io.File;
035 import java.io.FileInputStream;
036 import java.io.FileNotFoundException;
037 import java.io.IOException;
038 import java.io.InputStream;
039
040 import java.util.ArrayList;
041 import java.util.Enumeration;
042 import java.util.HashMap;
043 import java.util.List;
044 import java.util.Map;
045 import java.util.Properties;
046 import java.util.Set;
047 import java.util.regex.Matcher;
048 import java.util.regex.Pattern;
049
050 import org.apache.tools.ant.DirectoryScanner;
051
052
058 public abstract class BaseSourceProcessor implements SourceProcessor {
059
060 public BaseSourceProcessor() {
061 portalSource = _isPortalSource();
062
063 try {
064 _properties = _getProperties();
065 }
066 catch (Exception e) {
067 ReflectionUtil.throwException(e);
068 }
069 }
070
071 @Override
072 public void format(
073 boolean useProperties, boolean printErrors, boolean autoFix)
074 throws Exception {
075
076 _init(useProperties, printErrors, autoFix);
077
078 format();
079
080 sourceFormatterHelper.close();
081 }
082
083 @Override
084 public String format(
085 String fileName, boolean useProperties, boolean printErrors,
086 boolean autoFix)
087 throws Exception {
088
089 try {
090 _init(useProperties, printErrors, autoFix);
091
092 return format(fileName);
093 }
094 finally {
095 sourceFormatterHelper.close();
096 }
097 }
098
099 @Override
100 public List<String> getErrorMessages() {
101 List<String> errorMessages = new ArrayList<String>();
102
103 for (Map.Entry<String, List<String>> entry :
104 _errorMessagesMap.entrySet()) {
105
106 errorMessages.addAll(entry.getValue());
107 }
108
109 return errorMessages;
110 }
111
112 @Override
113 public SourceMismatchException getFirstSourceMismatchException() {
114 return _firstSourceMismatchException;
115 }
116
117 protected static boolean isExcluded(
118 List<String> exclusions, String absolutePath) {
119
120 return isExcluded(exclusions, absolutePath, -1);
121 }
122
123 protected static boolean isExcluded(
124 List<String> exclusions, String absolutePath, int lineCount) {
125
126 return isExcluded(exclusions, absolutePath, lineCount, null);
127 }
128
129 protected static boolean isExcluded(
130 List<String> exclusions, String absolutePath, int lineCount,
131 String javaTermName) {
132
133 if (ListUtil.isEmpty(exclusions)) {
134 return false;
135 }
136
137 String absolutePathWithJavaTermName = null;
138
139 if (Validator.isNotNull(javaTermName)) {
140 absolutePathWithJavaTermName =
141 absolutePath + StringPool.AT + javaTermName;
142 }
143
144 String absolutePathWithLineCount = null;
145
146 if (lineCount > 0) {
147 absolutePathWithLineCount =
148 absolutePath + StringPool.AT + lineCount;
149 }
150
151 for (String exclusion : exclusions) {
152 if (absolutePath.endsWith(exclusion) ||
153 ((absolutePathWithJavaTermName != null) &&
154 absolutePathWithJavaTermName.endsWith(exclusion)) ||
155 ((absolutePathWithLineCount != null) &&
156 absolutePathWithLineCount.endsWith(exclusion))) {
157
158 return true;
159 }
160 }
161
162 return false;
163 }
164
165 protected static void processErrorMessage(String fileName, String message) {
166 List<String> errorMessages = _errorMessagesMap.get(fileName);
167
168 if (errorMessages == null) {
169 errorMessages = new ArrayList<String>();
170 }
171
172 errorMessages.add(message);
173
174 _errorMessagesMap.put(fileName, errorMessages);
175 }
176
177 protected void checkEmptyCollection(
178 String line, String fileName, int lineCount) {
179
180
181
182 Matcher matcher = emptyCollectionPattern.matcher(line);
183
184 if (matcher.find()) {
185 String collectionType = TextFormatter.format(
186 matcher.group(1), TextFormatter.J);
187
188 processErrorMessage(
189 fileName,
190 "Use Collections.empty" + collectionType + "(): " + fileName +
191 " " + lineCount);
192 }
193 }
194
195 protected void checkIfClauseParentheses(
196 String ifClause, String fileName, int lineCount) {
197
198 int quoteCount = StringUtil.count(ifClause, StringPool.QUOTE);
199
200 if ((quoteCount % 2) == 1) {
201 return;
202 }
203
204 ifClause = stripQuotes(ifClause, CharPool.QUOTE);
205
206 ifClause = stripQuotes(ifClause, CharPool.APOSTROPHE);
207
208 if (ifClause.contains(StringPool.DOUBLE_SLASH) ||
209 ifClause.contains("")) {
210
211 return;
212 }
213
214 if (hasRedundantParentheses(ifClause, "||", "&&") ||
215 hasRedundantParentheses(ifClause, "&&", "||")) {
216
217 processErrorMessage(
218 fileName,
219 "redundant parentheses: " + fileName + " " + lineCount);
220 }
221
222 ifClause = stripRedundantParentheses(ifClause);
223
224 int level = 0;
225 int max = StringUtil.count(ifClause, StringPool.OPEN_PARENTHESIS);
226 int previousParenthesisPos = -1;
227
228 int[] levels = new int[max];
229
230 for (int i = 0; i < ifClause.length(); i++) {
231 char c = ifClause.charAt(i);
232
233 if ((c == CharPool.OPEN_PARENTHESIS) ||
234 (c == CharPool.CLOSE_PARENTHESIS)) {
235
236 if (previousParenthesisPos != -1) {
237 String s = ifClause.substring(
238 previousParenthesisPos + 1, i);
239
240 if (hasMissingParentheses(s)) {
241 processErrorMessage(
242 fileName,
243 "missing parentheses: " + fileName + " " +
244 lineCount);
245 }
246 }
247
248 previousParenthesisPos = i;
249
250 if (c == CharPool.OPEN_PARENTHESIS) {
251 levels[level] = i;
252
253 level += 1;
254 }
255 else {
256 int posOpenParenthesis = levels[level - 1];
257
258 if (level > 1) {
259 char nextChar = ifClause.charAt(i + 1);
260 char previousChar = ifClause.charAt(
261 posOpenParenthesis - 1);
262
263 if (!Character.isLetterOrDigit(nextChar) &&
264 (nextChar != CharPool.PERIOD) &&
265 !Character.isLetterOrDigit(previousChar)) {
266
267 String s = ifClause.substring(
268 posOpenParenthesis + 1, i);
269
270 if (hasRedundantParentheses(s)) {
271 processErrorMessage(
272 fileName,
273 "redundant parentheses: " + fileName + " " +
274 lineCount);
275 }
276 }
277
278 if ((previousChar == CharPool.OPEN_PARENTHESIS) &&
279 (nextChar == CharPool.CLOSE_PARENTHESIS)) {
280
281 processErrorMessage(
282 fileName,
283 "redundant parentheses: " + fileName + " " +
284 lineCount);
285 }
286 }
287
288 level -= 1;
289 }
290 }
291 }
292 }
293
294 protected void checkInefficientStringMethods(
295 String line, String fileName, String absolutePath, int lineCount) {
296
297 if (isRunsOutsidePortal(absolutePath) ||
298 fileName.endsWith("GetterUtil.java")) {
299
300 return;
301 }
302
303 String methodName = "toLowerCase";
304
305 int pos = line.indexOf(".toLowerCase()");
306
307 if (pos == -1) {
308 methodName = "toUpperCase";
309
310 pos = line.indexOf(".toUpperCase()");
311 }
312
313 if ((pos == -1) && !line.contains("StringUtil.equalsIgnoreCase(")) {
314 methodName = "equalsIgnoreCase";
315
316 pos = line.indexOf(".equalsIgnoreCase(");
317 }
318
319 if (pos != -1) {
320 processErrorMessage(
321 fileName,
322 "Use StringUtil." + methodName + ": " + fileName + " " +
323 lineCount);
324 }
325 }
326
327 protected void checkLanguageKeys(
328 String fileName, String content, Pattern pattern)
329 throws IOException {
330
331 String fileExtension = fileUtil.getExtension(fileName);
332
333 if (!portalSource || fileExtension.equals("vm")) {
334 return;
335 }
336
337 if (_portalLanguageProperties == null) {
338 _portalLanguageProperties = new Properties();
339
340 ClassLoader classLoader =
341 BaseSourceProcessor.class.getClassLoader();
342
343 InputStream inputStream = classLoader.getResourceAsStream(
344 "content/Language.properties");
345
346 _portalLanguageProperties.load(inputStream);
347 }
348
349 Matcher matcher = pattern.matcher(content);
350
351 while (matcher.find()) {
352 String[] languageKeys = getLanguageKeys(matcher);
353
354 for (String languageKey : languageKeys) {
355 if (Validator.isNumber(languageKey) ||
356 languageKey.endsWith(StringPool.DASH) ||
357 languageKey.endsWith(StringPool.OPEN_BRACKET) ||
358 languageKey.endsWith(StringPool.PERIOD) ||
359 languageKey.endsWith(StringPool.UNDERLINE) ||
360 languageKey.startsWith(StringPool.DASH) ||
361 languageKey.startsWith(StringPool.OPEN_BRACKET) ||
362 languageKey.startsWith(StringPool.OPEN_CURLY_BRACE) ||
363 languageKey.startsWith(StringPool.PERIOD) ||
364 languageKey.startsWith(StringPool.UNDERLINE) ||
365 _portalLanguageProperties.containsKey(languageKey)) {
366
367 continue;
368 }
369
370 Properties languageProperties = getLanguageProperties(fileName);
371
372 if ((languageProperties == null) ||
373 !languageProperties.containsKey(languageKey)) {
374
375 processErrorMessage(
376 fileName,
377 "missing language key: " + languageKey +
378 StringPool.SPACE + fileName);
379 }
380 }
381 }
382 }
383
384 protected void checkStringBundler(
385 String line, String fileName, int lineCount) {
386
387 if ((!line.startsWith("sb.append(") && !line.contains("SB.append(")) ||
388 !line.endsWith(");")) {
389
390 return;
391 }
392
393 int pos = line.indexOf(".append(");
394
395 line = line.substring(pos + 8, line.length() - 2);
396
397 line = stripQuotes(line, CharPool.QUOTE);
398
399 if (!line.contains(" + ")) {
400 return;
401 }
402
403 String[] lineParts = StringUtil.split(line, " + ");
404
405 for (String linePart : lineParts) {
406 int closeParenthesesCount = StringUtil.count(
407 linePart, StringPool.CLOSE_PARENTHESIS);
408 int openParenthesesCount = StringUtil.count(
409 linePart, StringPool.OPEN_PARENTHESIS);
410
411 if (closeParenthesesCount != openParenthesesCount) {
412 return;
413 }
414
415 if (Validator.isNumber(linePart)) {
416 return;
417 }
418 }
419
420 processErrorMessage(fileName, "plus: " + fileName + " " + lineCount);
421 }
422
423 protected abstract String doFormat(
424 File file, String fileName, String absolutePath, String content)
425 throws Exception;
426
427 protected String fixCompatClassImports(String absolutePath, String content)
428 throws IOException {
429
430 if (portalSource || !_usePortalCompatImport ||
431 absolutePath.contains("/ext-") ||
432 absolutePath.contains("/portal-compat-shared/")) {
433
434 return content;
435 }
436
437 Map<String, String> compatClassNamesMap = getCompatClassNamesMap();
438
439 String newContent = content;
440
441 for (Map.Entry<String, String> entry : compatClassNamesMap.entrySet()) {
442 String compatClassName = entry.getKey();
443 String extendedClassName = entry.getValue();
444
445 Pattern pattern = Pattern.compile(extendedClassName + "\\W");
446
447 while (true) {
448 Matcher matcher = pattern.matcher(newContent);
449
450 if (!matcher.find()) {
451 break;
452 }
453
454 newContent =
455 newContent.substring(0, matcher.start()) + compatClassName +
456 newContent.substring(matcher.end() - 1);
457 }
458 }
459
460 return newContent;
461 }
462
463 protected String fixCopyright(
464 String content, String absolutePath, String fileName)
465 throws IOException {
466
467 if (_copyright == null) {
468 _copyright = getContent("copyright.txt", 4);
469 }
470
471 String copyright = _copyright;
472
473 if (fileName.endsWith(".vm") || Validator.isNull(copyright)) {
474 return content;
475 }
476
477 if (_oldCopyright == null) {
478 _oldCopyright = getContent("old-copyright.txt", 4);
479 }
480
481 if (Validator.isNotNull(_oldCopyright) &&
482 content.contains(_oldCopyright)) {
483
484 content = StringUtil.replace(content, _oldCopyright, copyright);
485
486 processErrorMessage(fileName, "old (c): " + fileName);
487 }
488
489 if (!content.contains(copyright)) {
490 String customCopyright = getCustomCopyright(absolutePath);
491
492 if (Validator.isNotNull(customCopyright)) {
493 copyright = customCopyright;
494 }
495
496 if (!content.contains(copyright)) {
497 processErrorMessage(fileName, "(c): " + fileName);
498 }
499 }
500
501 if (fileName.endsWith(".jsp") || fileName.endsWith(".jspf")) {
502 content = StringUtil.replace(
503 content, "<%\n" + copyright + "\n%>",
504 "<%--\n" + copyright + "\n--%>");
505 }
506
507 int x = content.indexOf("* Copyright (c) 2000-");
508
509 if (x == -1) {
510 return content;
511 }
512
513 int y = content.indexOf("Liferay", x);
514
515 String contentCopyrightYear = content.substring(x, y);
516
517 x = copyright.indexOf("* Copyright (c) 2000-");
518
519 if (x == -1) {
520 return content;
521 }
522
523 y = copyright.indexOf("Liferay", x);
524
525 String copyrightYear = copyright.substring(x, y);
526
527 return StringUtil.replace(content, contentCopyrightYear, copyrightYear);
528 }
529
530 protected String fixIncorrectParameterTypeForLanguageUtil(
531 String content, boolean autoFix, String fileName) {
532
533 if (portalSource) {
534 return content;
535 }
536
537 String expectedParameter = getProperty(
538 "languageutil.expected.parameter");
539 String incorrectParameter = getProperty(
540 "languageutil.incorrect.parameter");
541
542 if (!content.contains(
543 "LanguageUtil.format(" + incorrectParameter + ", ") &&
544 !content.contains(
545 "LanguageUtil.get(" + incorrectParameter + ", ")) {
546
547 return content;
548 }
549
550 if (autoFix) {
551 content = StringUtil.replace(
552 content,
553 new String[] {
554 "LanguageUtil.format(" + incorrectParameter + ", ",
555 "LanguageUtil.get(" + incorrectParameter + ", "
556 },
557 new String[] {
558 "LanguageUtil.format(" + expectedParameter + ", ",
559 "LanguageUtil.get(" + expectedParameter + ", "
560 });
561 }
562 else {
563 processErrorMessage(
564 fileName,
565 "(Unicode)LanguageUtil.format/get methods require " +
566 expectedParameter + " parameter instead of " +
567 incorrectParameter + " " + fileName);
568 }
569
570 return content;
571 }
572
573 protected String fixSessionKey(
574 String fileName, String content, Pattern pattern) {
575
576 Matcher matcher = pattern.matcher(content);
577
578 if (!matcher.find()) {
579 return content;
580 }
581
582 String newContent = content;
583
584 do {
585 String match = matcher.group();
586
587 String s = null;
588
589 if (pattern.equals(sessionKeyPattern)) {
590 s = StringPool.COMMA;
591 }
592 else if (pattern.equals(taglibSessionKeyPattern)) {
593 s = "key=";
594 }
595
596 int x = match.indexOf(s);
597
598 if (x == -1) {
599 continue;
600 }
601
602 x = x + s.length();
603
604 String substring = match.substring(x).trim();
605
606 String quote = StringPool.BLANK;
607
608 if (substring.startsWith(StringPool.APOSTROPHE)) {
609 quote = StringPool.APOSTROPHE;
610 }
611 else if (substring.startsWith(StringPool.QUOTE)) {
612 quote = StringPool.QUOTE;
613 }
614 else {
615 continue;
616 }
617
618 int y = match.indexOf(quote, x);
619 int z = match.indexOf(quote, y + 1);
620
621 if ((y == -1) || (z == -1)) {
622 continue;
623 }
624
625 String prefix = match.substring(0, y + 1);
626 String suffix = match.substring(z);
627 String oldKey = match.substring(y + 1, z);
628
629 boolean alphaNumericKey = true;
630
631 for (char c : oldKey.toCharArray()) {
632 if (!Validator.isChar(c) && !Validator.isDigit(c) &&
633 (c != CharPool.DASH) && (c != CharPool.UNDERLINE)) {
634
635 alphaNumericKey = false;
636 }
637 }
638
639 if (!alphaNumericKey) {
640 continue;
641 }
642
643 String newKey = TextFormatter.format(oldKey, TextFormatter.O);
644
645 newKey = TextFormatter.format(newKey, TextFormatter.M);
646
647 if (newKey.equals(oldKey)) {
648 continue;
649 }
650
651 String oldSub = prefix.concat(oldKey).concat(suffix);
652 String newSub = prefix.concat(newKey).concat(suffix);
653
654 newContent = StringUtil.replaceFirst(newContent, oldSub, newSub);
655 }
656 while (matcher.find());
657
658 return newContent;
659 }
660
661 protected abstract void format() throws Exception;
662
663 protected String format(
664 File file, String fileName, String absolutePath, String content)
665 throws Exception {
666
667 _errorMessagesMap.remove(fileName);
668
669 String newContent = doFormat(file, fileName, absolutePath, content);
670
671 newContent = StringUtil.replace(
672 newContent, StringPool.RETURN, StringPool.BLANK);
673
674 if (content.equals(newContent)) {
675 return content;
676 }
677
678 return format(file, fileName, absolutePath, newContent);
679 }
680
681 protected String format(String fileName) throws Exception {
682 File file = new File(BASEDIR + fileName);
683
684 fileName = StringUtil.replace(
685 fileName, StringPool.BACK_SLASH, StringPool.SLASH);
686
687 String absolutePath = getAbsolutePath(file);
688
689 String content = fileUtil.read(file);
690
691 String newContent = format(file, fileName, absolutePath, content);
692
693 processFormattedFile(file, fileName, content, newContent);
694
695 return newContent;
696 }
697
698 protected String formatJavaTerms(
699 String javaClassName, String packagePath, File file,
700 String fileName, String absolutePath, String content,
701 String javaClassContent, int javaClassLineCount,
702 List<String> checkJavaFieldTypesExclusions,
703 List<String> javaTermAccessLevelModifierExclusions,
704 List<String> javaTermSortExclusions,
705 List<String> testAnnotationsExclusions)
706 throws Exception {
707
708 JavaClass javaClass = new JavaClass(
709 javaClassName, packagePath, file, fileName, absolutePath,
710 javaClassContent, javaClassLineCount, StringPool.TAB, null,
711 javaTermAccessLevelModifierExclusions);
712
713 String newJavaClassContent = javaClass.formatJavaTerms(
714 getAnnotationsExclusions(), getImmutableFieldTypes(),
715 checkJavaFieldTypesExclusions, javaTermSortExclusions,
716 testAnnotationsExclusions);
717
718 if (!javaClassContent.equals(newJavaClassContent)) {
719 return StringUtil.replaceFirst(
720 content, javaClassContent, newJavaClassContent);
721 }
722
723 return content;
724 }
725
726 protected String formatTagAttributeType(
727 String line, String tag, String attributeAndValue)
728 throws Exception {
729
730 return line;
731 }
732
733 protected String getAbsolutePath(File file) {
734 String absolutePath = fileUtil.getAbsolutePath(file);
735
736 return StringUtil.replace(absolutePath, "/./", StringPool.SLASH);
737 }
738
739 protected Set<String> getAnnotationsExclusions() {
740 if (_annotationsExclusions != null) {
741 return _annotationsExclusions;
742 }
743
744 _annotationsExclusions = SetUtil.fromArray(
745 new String[] {
746 "ArquillianResource", "BeanReference", "Inject", "Mock",
747 "SuppressWarnings"
748 });
749
750 return _annotationsExclusions;
751 }
752
753 protected Map<String, String> getCompatClassNamesMap() throws IOException {
754 if (_compatClassNamesMap != null) {
755 return _compatClassNamesMap;
756 }
757
758 Map<String, String> compatClassNamesMap = new HashMap<String, String>();
759
760 String[] includes = new String[] {
761 "**\\portal-compat-shared\\src\\com\\liferay\\compat\\**\\*.java"
762 };
763
764 String basedir = BASEDIR;
765
766 List<String> fileNames = new ArrayList<String>();
767
768 for (int i = 0; i < 3; i++) {
769 fileNames = getFileNames(basedir, new String[0], includes);
770
771 if (!fileNames.isEmpty()) {
772 break;
773 }
774
775 basedir = "../" + basedir;
776 }
777
778 for (String fileName : fileNames) {
779 if (!fileName.startsWith("shared")) {
780 break;
781 }
782
783 File file = new File(basedir + fileName);
784
785 String content = fileUtil.read(file);
786
787 fileName = StringUtil.replace(
788 fileName, StringPool.BACK_SLASH, StringPool.SLASH);
789
790 fileName = StringUtil.replace(
791 fileName, StringPool.SLASH, StringPool.PERIOD);
792
793 int pos = fileName.indexOf("com.");
794
795 String compatClassName = fileName.substring(pos);
796
797 compatClassName = compatClassName.substring(
798 0, compatClassName.length() - 5);
799
800 String extendedClassName = StringUtil.replace(
801 compatClassName, "compat.", StringPool.BLANK);
802
803 if (content.contains("extends " + extendedClassName)) {
804 compatClassNamesMap.put(compatClassName, extendedClassName);
805 }
806 }
807
808 _compatClassNamesMap = compatClassNamesMap;
809
810 return _compatClassNamesMap;
811 }
812
813 protected String getContent(String fileName, int level) throws IOException {
814 File file = getFile(fileName, level);
815
816 if (file != null) {
817 String content = fileUtil.read(file);
818
819 if (Validator.isNotNull(content)) {
820 return content;
821 }
822 }
823
824 return StringPool.BLANK;
825 }
826
827 protected String getCustomCopyright(String absolutePath)
828 throws IOException {
829
830 for (int x = absolutePath.length();;) {
831 x = absolutePath.lastIndexOf(StringPool.SLASH, x);
832
833 if (x == -1) {
834 break;
835 }
836
837 String copyright = fileUtil.read(
838 absolutePath.substring(0, x + 1) + "copyright.txt");
839
840 if (Validator.isNotNull(copyright)) {
841 return copyright;
842 }
843
844 x = x - 1;
845 }
846
847 return null;
848 }
849
850 protected File getFile(String fileName, int level) {
851 for (int i = 0; i < level; i++) {
852 if (fileUtil.exists(fileName)) {
853 return new File(fileName);
854 }
855
856 fileName = "../" + fileName;
857 }
858
859 return null;
860 }
861
862 protected List<String> getFileNames(
863 String basedir, String[] excludes, String[] includes) {
864
865 DirectoryScanner directoryScanner = new DirectoryScanner();
866
867 directoryScanner.setBasedir(basedir);
868
869 if (_excludes != null) {
870 excludes = ArrayUtil.append(excludes, _excludes);
871 }
872
873 directoryScanner.setExcludes(excludes);
874
875 directoryScanner.setIncludes(includes);
876
877 return sourceFormatterHelper.scanForFiles(directoryScanner);
878 }
879
880 protected List<String> getFileNames(String[] excludes, String[] includes) {
881 return getFileNames(BASEDIR, excludes, includes);
882 }
883
884 protected Set<String> getImmutableFieldTypes() {
885 if (_immutableFieldTypes != null) {
886 return _immutableFieldTypes;
887 }
888
889 Set<String> immutableFieldTypes = SetUtil.fromArray(
890 new String[] {
891 "boolean", "byte", "char", "double", "float", "int", "long",
892 "short", "Boolean", "Byte", "Character", "Class", "Double",
893 "Float", "Int", "Long", "Number", "Short", "String",
894 });
895
896 immutableFieldTypes.addAll(getPropertyList("immutable.field.types"));
897
898 _immutableFieldTypes = immutableFieldTypes;
899
900 return _immutableFieldTypes;
901 }
902
903 protected String[] getLanguageKeys(Matcher matcher) {
904 int groupCount = matcher.groupCount();
905
906 if (groupCount == 1) {
907 String languageKey = matcher.group(1);
908
909 if (Validator.isNotNull(languageKey)) {
910 return new String[] {languageKey};
911 }
912 }
913 else if (groupCount == 2) {
914 String languageKey = matcher.group(2);
915
916 languageKey = TextFormatter.format(languageKey, TextFormatter.P);
917
918 return new String[] {languageKey};
919 }
920
921 StringBundler sb = new StringBundler();
922
923 String match = matcher.group();
924
925 int count = 0;
926
927 for (int i = 0; i < match.length(); i++) {
928 char c = match.charAt(i);
929
930 switch (c) {
931 case CharPool.CLOSE_PARENTHESIS:
932 if (count <= 1) {
933 return new String[0];
934 }
935
936 count--;
937
938 break;
939
940 case CharPool.OPEN_PARENTHESIS:
941 count++;
942
943 break;
944
945 case CharPool.QUOTE:
946 if (count > 1) {
947 break;
948 }
949
950 while (i < match.length()) {
951 i++;
952
953 if (match.charAt(i) == CharPool.QUOTE) {
954 String languageKey = sb.toString();
955
956 if (match.startsWith("names")) {
957 return StringUtil.split(languageKey);
958 }
959 else {
960 return new String[] {languageKey};
961 }
962 }
963
964 sb.append(match.charAt(i));
965 }
966 }
967 }
968
969 return new String[0];
970 }
971
972 protected Properties getLanguageProperties(String fileName) {
973 StringBundler sb = new StringBundler(4);
974
975 int pos = fileName.indexOf("/docroot/");
976
977 sb.append(BASEDIR);
978
979 if (pos != -1) {
980 sb.append(fileName.substring(0, pos + 9));
981 sb.append("WEB-INF/src/");
982 }
983 else {
984 pos = fileName.indexOf("/src/");
985
986 if (pos == -1) {
987 return null;
988 }
989
990 sb.append(fileName.substring(0, pos + 5));
991 }
992
993 sb.append("content/Language.properties");
994
995 try {
996 Properties properties = new Properties();
997
998 InputStream inputStream = new FileInputStream(sb.toString());
999
1000 properties.load(inputStream);
1001
1002 return properties;
1003 }
1004 catch (Exception e) {
1005 }
1006
1007 return null;
1008 }
1009
1010 protected String getMainReleaseVersion() {
1011 if (_mainReleaseVersion != null) {
1012 return _mainReleaseVersion;
1013 }
1014
1015 String releaseVersion = ReleaseInfo.getVersion();
1016
1017 int pos = releaseVersion.lastIndexOf(StringPool.PERIOD);
1018
1019 _mainReleaseVersion = releaseVersion.substring(0, pos) + ".0";
1020
1021 return _mainReleaseVersion;
1022 }
1023
1024 protected String getProperty(String key) {
1025 return _properties.getProperty(key);
1026 }
1027
1028 protected List<String> getPropertyList(String key) {
1029 return ListUtil.fromString(
1030 GetterUtil.getString(getProperty(key)), StringPool.COMMA);
1031 }
1032
1033 protected boolean hasMissingParentheses(String s) {
1034 if (Validator.isNull(s)) {
1035 return false;
1036 }
1037
1038 boolean containsAndOperator = s.contains("&&");
1039 boolean containsOrOperator = s.contains("||");
1040
1041 if (containsAndOperator && containsOrOperator) {
1042 return true;
1043 }
1044
1045 boolean containsCompareOperator =
1046 (s.contains(" == ") || s.contains(" != ") || s.contains(" < ") ||
1047 s.contains(" > ") || s.contains(" =< ") || s.contains(" => ") ||
1048 s.contains(" <= ") || s.contains(" >= "));
1049 boolean containsMathOperator =
1050 (s.contains(" = ") || s.contains(" - ") || s.contains(" + ") ||
1051 s.contains(" & ") || s.contains(" % ") || s.contains(" * ") ||
1052 s.contains(" / "));
1053
1054 if (containsCompareOperator &&
1055 (containsAndOperator || containsOrOperator ||
1056 (containsMathOperator && !s.contains(StringPool.OPEN_BRACKET)))) {
1057
1058 return true;
1059 }
1060
1061 return false;
1062 }
1063
1064 protected boolean hasRedundantParentheses(String s) {
1065 if (!s.contains("&&") && !s.contains("||")) {
1066 for (int x = 0;;) {
1067 x = s.indexOf(StringPool.CLOSE_PARENTHESIS);
1068
1069 if (x == -1) {
1070 break;
1071 }
1072
1073 int y = s.substring(0, x).lastIndexOf(
1074 StringPool.OPEN_PARENTHESIS);
1075
1076 if (y == -1) {
1077 break;
1078 }
1079
1080 s = s.substring(0, y) + s.substring(x + 1);
1081 }
1082 }
1083
1084 if (Validator.isNotNull(s) && !s.contains(StringPool.SPACE)) {
1085 return true;
1086 }
1087 else {
1088 return false;
1089 }
1090 }
1091
1092 protected boolean hasRedundantParentheses(
1093 String s, String operator1, String operator2) {
1094
1095 String[] parts = StringUtil.split(s, operator1);
1096
1097 if (parts.length < 3) {
1098 return false;
1099 }
1100
1101 for (int i = 1; i < (parts.length - 1); i++) {
1102 String part = parts[i];
1103
1104 if (part.contains(operator2) || part.contains("!(")) {
1105 continue;
1106 }
1107
1108 int closeParenthesesCount = StringUtil.count(
1109 part, StringPool.CLOSE_PARENTHESIS);
1110 int openParenthesesCount = StringUtil.count(
1111 part, StringPool.OPEN_PARENTHESIS);
1112
1113 if (Math.abs(closeParenthesesCount - openParenthesesCount) == 1) {
1114 return true;
1115 }
1116 }
1117
1118 return false;
1119 }
1120
1121 protected boolean isAttributName(String attributeName) {
1122 if (Validator.isNull(attributeName)) {
1123 return false;
1124 }
1125
1126 Matcher matcher = attributeNamePattern.matcher(attributeName);
1127
1128 return matcher.matches();
1129 }
1130
1131 protected boolean isRunsOutsidePortal(String absolutePath) {
1132 if (_runOutsidePortalExclusions == null) {
1133 _runOutsidePortalExclusions = getPropertyList(
1134 "run.outside.portal.excludes");
1135 }
1136
1137 for (String runOutsidePortalExclusions : _runOutsidePortalExclusions) {
1138 if (absolutePath.contains(runOutsidePortalExclusions)) {
1139 return true;
1140 }
1141 }
1142
1143 return false;
1144 }
1145
1146 protected void processFormattedFile(
1147 File file, String fileName, String content, String newContent)
1148 throws IOException {
1149
1150 if (_printErrors) {
1151 List<String> errorMessages = _errorMessagesMap.get(fileName);
1152
1153 if (errorMessages != null) {
1154 for (String errorMessage : errorMessages) {
1155 sourceFormatterHelper.printError(fileName, errorMessage);
1156 }
1157 }
1158 }
1159
1160 if (content.equals(newContent)) {
1161 return;
1162 }
1163
1164 if (_autoFix) {
1165 fileUtil.write(file, newContent);
1166 }
1167 else if (_firstSourceMismatchException == null) {
1168 _firstSourceMismatchException = new SourceMismatchException(
1169 fileName, content, newContent);
1170 }
1171
1172 if (_printErrors) {
1173 sourceFormatterHelper.printError(fileName, file);
1174 }
1175 }
1176
1177 protected String replacePrimitiveWrapperInstantiation(
1178 String fileName, String line, int lineCount) {
1179
1180 if (true) {
1181 return line;
1182 }
1183
1184 String newLine = StringUtil.replace(
1185 line,
1186 new String[] {
1187 "new Boolean(", "new Byte(", "new Character(", "new Integer(",
1188 "new Long(", "new Short("
1189 },
1190 new String[] {
1191 "Boolean.valueOf(", "Byte.valueOf(", "Character.valueOf(",
1192 "Integer.valueOf(", "Long.valueOf(", "Short.valueOf("
1193 });
1194
1195 if (!line.equals(newLine)) {
1196 processErrorMessage(
1197 fileName, "> new Primitive(: " + fileName + " " + lineCount);
1198 }
1199
1200 return newLine;
1201 }
1202
1203 protected String sortAttributes(
1204 String fileName, String line, int lineCount,
1205 boolean allowApostropheDelimeter)
1206 throws Exception {
1207
1208 String s = line;
1209
1210 int x = s.indexOf(StringPool.LESS_THAN);
1211 int y = s.indexOf(StringPool.SPACE);
1212
1213 if ((x == -1) || (x >= y)) {
1214 return line;
1215 }
1216
1217 String tag = s.substring(x + 1, y);
1218
1219 s = s.substring(y + 1);
1220
1221 String previousAttribute = null;
1222 String previousAttributeAndValue = null;
1223
1224 boolean wrongOrder = false;
1225
1226 for (x = 0;;) {
1227 x = s.indexOf(StringPool.EQUAL);
1228
1229 if ((x == -1) || (s.length() <= (x + 1))) {
1230 return line;
1231 }
1232
1233 String attribute = s.substring(0, x);
1234
1235 if (!isAttributName(attribute)) {
1236 return line;
1237 }
1238
1239 if (Validator.isNotNull(previousAttribute) &&
1240 (previousAttribute.compareTo(attribute) > 0)) {
1241
1242 wrongOrder = true;
1243 }
1244
1245 s = s.substring(x + 1);
1246
1247 char delimeter = s.charAt(0);
1248
1249 if ((delimeter != CharPool.APOSTROPHE) &&
1250 (delimeter != CharPool.QUOTE)) {
1251
1252 if (delimeter != CharPool.AMPERSAND) {
1253 processErrorMessage(
1254 fileName, "delimeter: " + fileName + " " + lineCount);
1255 }
1256
1257 return line;
1258 }
1259
1260 s = s.substring(1);
1261
1262 String value = null;
1263
1264 y = -1;
1265
1266 while (true) {
1267 y = s.indexOf(delimeter, y + 1);
1268
1269 if ((y == -1) || (s.length() <= (y + 1))) {
1270 return line;
1271 }
1272
1273 value = s.substring(0, y);
1274
1275 if (value.startsWith("<%")) {
1276 int endJavaCodeSignCount = StringUtil.count(value, "%>");
1277 int startJavaCodeSignCount = StringUtil.count(value, "<%");
1278
1279 if (endJavaCodeSignCount == startJavaCodeSignCount) {
1280 break;
1281 }
1282 }
1283 else {
1284 int greaterThanCount = StringUtil.count(
1285 value, StringPool.GREATER_THAN);
1286 int lessThanCount = StringUtil.count(
1287 value, StringPool.LESS_THAN);
1288
1289 if (greaterThanCount == lessThanCount) {
1290 break;
1291 }
1292 }
1293 }
1294
1295 if (delimeter == CharPool.APOSTROPHE) {
1296 if (!value.contains(StringPool.QUOTE)) {
1297 line = StringUtil.replace(
1298 line,
1299 StringPool.APOSTROPHE + value + StringPool.APOSTROPHE,
1300 StringPool.QUOTE + value + StringPool.QUOTE);
1301
1302 return sortAttributes(
1303 fileName, line, lineCount, allowApostropheDelimeter);
1304 }
1305 else if (!allowApostropheDelimeter) {
1306 String newValue = StringUtil.replace(
1307 value, StringPool.QUOTE, """);
1308
1309 line = StringUtil.replace(
1310 line,
1311 StringPool.APOSTROPHE + value + StringPool.APOSTROPHE,
1312 StringPool.QUOTE + newValue + StringPool.QUOTE);
1313
1314 return sortAttributes(
1315 fileName, line, lineCount, allowApostropheDelimeter);
1316 }
1317 }
1318
1319 StringBundler sb = new StringBundler(5);
1320
1321 sb.append(attribute);
1322 sb.append(StringPool.EQUAL);
1323 sb.append(delimeter);
1324 sb.append(value);
1325 sb.append(delimeter);
1326
1327 String currentAttributeAndValue = sb.toString();
1328
1329 String newLine = formatTagAttributeType(
1330 line, tag, currentAttributeAndValue);
1331
1332 if (!newLine.equals(line)) {
1333 return sortAttributes(
1334 fileName, newLine, lineCount, allowApostropheDelimeter);
1335 }
1336
1337 if (wrongOrder) {
1338 if ((StringUtil.count(line, currentAttributeAndValue) == 1) &&
1339 (StringUtil.count(line, previousAttributeAndValue) == 1)) {
1340
1341 line = StringUtil.replaceFirst(
1342 line, previousAttributeAndValue,
1343 currentAttributeAndValue);
1344
1345 line = StringUtil.replaceLast(
1346 line, currentAttributeAndValue,
1347 previousAttributeAndValue);
1348
1349 return sortAttributes(
1350 fileName, line, lineCount, allowApostropheDelimeter);
1351 }
1352
1353 return line;
1354 }
1355
1356 s = s.substring(y + 1);
1357
1358 if (s.startsWith(StringPool.GREATER_THAN)) {
1359 x = s.indexOf(StringPool.SPACE);
1360
1361 if (x == -1) {
1362 return line;
1363 }
1364
1365 s = s.substring(x + 1);
1366
1367 previousAttribute = null;
1368 previousAttributeAndValue = null;
1369 }
1370 else {
1371 s = StringUtil.trimLeading(s);
1372
1373 previousAttribute = attribute;
1374 previousAttributeAndValue = currentAttributeAndValue;
1375 }
1376 }
1377 }
1378
1379 protected String stripLine(
1380 String s, char startDelimeter, char endDelimeter) {
1381
1382 boolean insideDelimeters = false;
1383 int level = 0;
1384
1385 StringBundler sb = new StringBundler();
1386
1387 for (int i = 0; i < s.length(); i++) {
1388 char c = s.charAt(i);
1389
1390 if (insideDelimeters) {
1391 if (c == endDelimeter) {
1392 if (level > 0) {
1393 level -= 1;
1394 }
1395 else {
1396 if ((c > 1) &&
1397 (s.charAt(i - 1) == CharPool.BACK_SLASH) &&
1398 (s.charAt(i - 2) != CharPool.BACK_SLASH)) {
1399
1400 continue;
1401 }
1402
1403 insideDelimeters = false;
1404 }
1405 }
1406 else if (c == startDelimeter) {
1407 level += 1;
1408 }
1409 }
1410 else if (c == startDelimeter) {
1411 insideDelimeters = true;
1412 }
1413 else {
1414 sb.append(c);
1415 }
1416 }
1417
1418 return sb.toString();
1419 }
1420
1421 protected String stripQuotes(String s, char delimeter) {
1422 return stripLine(s, delimeter, delimeter);
1423 }
1424
1425 protected String stripRedundantParentheses(String s) {
1426 for (int x = 0;;) {
1427 x = s.indexOf(StringPool.OPEN_PARENTHESIS, x + 1);
1428 int y = s.indexOf(StringPool.CLOSE_PARENTHESIS, x);
1429
1430 if ((x == -1) || (y == -1)) {
1431 return s;
1432 }
1433
1434 String linePart = s.substring(x + 1, y);
1435
1436 linePart = StringUtil.replace(
1437 linePart, StringPool.COMMA, StringPool.BLANK);
1438
1439 if (Validator.isAlphanumericName(linePart) ||
1440 Validator.isNull(linePart)) {
1441
1442 s = s.substring(0, x) + s.substring(y + 1);
1443 }
1444 }
1445 }
1446
1447 protected String trimContent(String content, boolean allowLeadingSpaces)
1448 throws IOException {
1449
1450 StringBundler sb = new StringBundler();
1451
1452 try (UnsyncBufferedReader unsyncBufferedReader =
1453 new UnsyncBufferedReader(new UnsyncStringReader(content))) {
1454
1455 String line = null;
1456
1457 while ((line = unsyncBufferedReader.readLine()) != null) {
1458 sb.append(trimLine(line, allowLeadingSpaces));
1459 sb.append("\n");
1460 }
1461 }
1462
1463 content = sb.toString();
1464
1465 if (content.endsWith("\n")) {
1466 content = content.substring(0, content.length() - 1);
1467 }
1468
1469 return content;
1470 }
1471
1472 protected String trimLine(String line, boolean allowLeadingSpaces) {
1473 if (line.trim().length() == 0) {
1474 return StringPool.BLANK;
1475 }
1476
1477 line = StringUtil.trimTrailing(line);
1478
1479 if (allowLeadingSpaces || !line.startsWith(StringPool.SPACE) ||
1480 line.startsWith(" *")) {
1481
1482 return line;
1483 }
1484
1485 if (!line.startsWith(StringPool.FOUR_SPACES)) {
1486 while (line.startsWith(StringPool.SPACE)) {
1487 line = StringUtil.replaceFirst(
1488 line, StringPool.SPACE, StringPool.BLANK);
1489 }
1490 }
1491 else {
1492 int pos = 0;
1493
1494 String temp = line;
1495
1496 while (temp.startsWith(StringPool.FOUR_SPACES)) {
1497 line = StringUtil.replaceFirst(
1498 line, StringPool.FOUR_SPACES, StringPool.TAB);
1499
1500 pos++;
1501
1502 temp = line.substring(pos);
1503 }
1504 }
1505
1506 return line;
1507 }
1508
1509 protected static final String BASEDIR = "./";
1510
1511 protected static Pattern attributeNamePattern = Pattern.compile(
1512 "[a-z]+[-_a-zA-Z0-9]*");
1513 protected static Pattern emptyCollectionPattern = Pattern.compile(
1514 "Collections\\.EMPTY_(LIST|MAP|SET)");
1515 protected static FileImpl fileUtil = FileImpl.getInstance();
1516 protected static Pattern languageKeyPattern = Pattern.compile(
1517 "LanguageUtil.(?:get|format)\\([^;%]+|Liferay.Language.get\\('([^']+)");
1518 protected static boolean portalSource;
1519 protected static SAXReaderImpl saxReaderUtil = SAXReaderImpl.getInstance();
1520 protected static Pattern sessionKeyPattern = Pattern.compile(
1521 "SessionErrors.(?:add|contains|get)\\([^;%&|!]+|".concat(
1522 "SessionMessages.(?:add|contains|get)\\([^;%&|!]+"),
1523 Pattern.MULTILINE);
1524 protected static SourceFormatterHelper sourceFormatterHelper;
1525 protected static Pattern taglibSessionKeyPattern = Pattern.compile(
1526 "<liferay-ui:error [^>]+>|<liferay-ui:success [^>]+>",
1527 Pattern.MULTILINE);
1528
1529 private String[] _getExcludes() {
1530 List<String> excludesList = ListUtil.fromString(
1531 GetterUtil.getString(
1532 System.getProperty("source.formatter.excludes")));
1533
1534 excludesList.addAll(getPropertyList("source.formatter.excludes"));
1535
1536 String[] includes = new String[] {"**\\source_formatter.ignore"};
1537
1538 List<String> ignoreFileNames = getFileNames(new String[0], includes);
1539
1540 for (String ignoreFileName : ignoreFileNames) {
1541 excludesList.add(
1542 ignoreFileName.substring(0, ignoreFileName.length() - 23) +
1543 "**");
1544 }
1545
1546 return excludesList.toArray(new String[excludesList.size()]);
1547 }
1548
1549 private Properties _getProperties() throws Exception {
1550 String fileName = "source-formatter.properties";
1551
1552 Properties properties = new Properties();
1553
1554 List<Properties> propertiesList = new ArrayList<Properties>();
1555
1556 int level = 2;
1557
1558 if (portalSource) {
1559 level = 3;
1560 }
1561
1562 for (int i = 0; i <= level; i++) {
1563 try {
1564 InputStream inputStream = new FileInputStream(fileName);
1565
1566 Properties props = new Properties();
1567
1568 props.load(inputStream);
1569
1570 propertiesList.add(props);
1571 }
1572 catch (FileNotFoundException fnfe) {
1573 }
1574
1575 fileName = "../" + fileName;
1576 }
1577
1578 if (propertiesList.isEmpty()) {
1579 return properties;
1580 }
1581
1582 properties = propertiesList.get(0);
1583
1584 if (propertiesList.size() == 1) {
1585 return properties;
1586 }
1587
1588 for (int i = 1; i < propertiesList.size(); i++) {
1589 Properties props = propertiesList.get(i);
1590
1591 Enumeration<String> enu =
1592 (Enumeration<String>)props.propertyNames();
1593
1594 while (enu.hasMoreElements()) {
1595 String key = enu.nextElement();
1596
1597 String value = props.getProperty(key);
1598
1599 if (Validator.isNull(value)) {
1600 continue;
1601 }
1602
1603 if (key.contains("excludes")) {
1604 String existingValue = properties.getProperty(key);
1605
1606 if (Validator.isNotNull(existingValue)) {
1607 value = existingValue + StringPool.COMMA + value;
1608 }
1609
1610 properties.put(key, value);
1611 }
1612 else if (!properties.containsKey(key)) {
1613 properties.put(key, value);
1614 }
1615 }
1616 }
1617
1618 return properties;
1619 }
1620
1621 private void _init(
1622 boolean useProperties, boolean printErrors, boolean autoFix)
1623 throws Exception {
1624
1625 _errorMessagesMap = new HashMap<String, List<String>>();
1626
1627 sourceFormatterHelper = new SourceFormatterHelper(useProperties);
1628
1629 sourceFormatterHelper.init();
1630
1631 _autoFix = autoFix;
1632
1633 _excludes = _getExcludes();
1634
1635 _printErrors = printErrors;
1636
1637 _usePortalCompatImport = GetterUtil.getBoolean(
1638 getProperty("use.portal.compat.import"));
1639 }
1640
1641 private boolean _isPortalSource() {
1642 if (getFile("portal-impl", 4) != null) {
1643 return true;
1644 }
1645 else {
1646 return false;
1647 }
1648 }
1649
1650 private static Map<String, List<String>> _errorMessagesMap =
1651 new HashMap<String, List<String>>();
1652 private static boolean _printErrors;
1653
1654 private Set<String> _annotationsExclusions;
1655 private boolean _autoFix;
1656 private Map<String, String> _compatClassNamesMap;
1657 private String _copyright;
1658 private String[] _excludes;
1659 private SourceMismatchException _firstSourceMismatchException;
1660 private Set<String> _immutableFieldTypes;
1661 private String _mainReleaseVersion;
1662 private String _oldCopyright;
1663 private Properties _portalLanguageProperties;
1664 private Properties _properties;
1665 private List<String> _runOutsidePortalExclusions;
1666 private boolean _usePortalCompatImport;
1667
1668 }