21
21
import java .util .List ;
22
22
import java .util .Map ;
23
23
import java .util .regex .Pattern ;
24
+ import org .apache .commons .lang3 .StringUtils ;
24
25
import org .sonar .check .Rule ;
25
26
import org .sonar .check .RuleProperty ;
26
27
import org .sonar .plugins .php .api .tree .CompilationUnitTree ;
29
30
import org .sonar .plugins .php .api .visitors .PHPVisitorCheck ;
30
31
import org .sonar .plugins .php .api .visitors .PreciseIssue ;
31
32
32
- import static org .sonar .php .checks .utils .RegexUtils .firstOf ;
33
- import static org .sonar .php .checks .utils .RegexUtils .oneOrMore ;
34
- import static org .sonar .php .checks .utils .RegexUtils .optional ;
35
- import static org .sonar .php .checks .utils .RegexUtils .zeroOrMore ;
36
-
37
33
@ Rule (key = "S1192" )
38
34
public class StringLiteralDuplicatedCheck extends PHPVisitorCheck {
39
35
40
36
private static final String MESSAGE = "Define a constant instead of duplicating this literal \" %s\" %s times." ;
41
37
private static final String SECONDARY_MESSAGE = "Duplication." ;
42
38
43
- private static final String ONLY_ALPHANUMERIC_UNDERSCORES_HYPHENS_AND_PERIODS = "^[a-zA-Z_][.\\ -\\ w]+$" ;
44
-
45
- // Single elements
46
- private static final String IDENTIFIER = "[a-zA-Z][a-zA-Z0-9\\ -_:.]*+" ;
47
- private static final String OPT_SPACING = "\\ s*+" ;
48
- private static final String OPT_TEXT_OUTSIDE_OF_TAGS = "[^<>]*+" ;
49
- private static final String DOUBLE_QUOTED_STRING = "\" (?:\\ \\ .|[^\" ])*+\" " ;
50
- private static final String SINGLE_QUOTED_STRING = "'(?:\\ \\ .|[^'])*+'" ;
51
- private static final String NO_QUOTED_STRING = "[a-zA-Z0-9\\ -_:./]++" ;
52
-
53
- // Partial elements matching
54
- private static final String DOUBLE_QUOTED_STRING_PARTIAL_START = "\" (?:\\ \\ .|[^\" ])*+" ;
55
- private static final String SINGLE_QUOTED_STRING_PARTIAL_START = "'(?:\\ \\ .|[^'])*+" ;
56
- private static final String NO_QUOTED_STRING_PARTIAL_START = "[a-zA-Z0-9\\ -_:./]++" ;
57
- private static final String TAG_ATTRIBUTE_PARTIAL_START = OPT_SPACING
58
- + optional ("=" , OPT_SPACING , optional (firstOf (DOUBLE_QUOTED_STRING_PARTIAL_START , SINGLE_QUOTED_STRING_PARTIAL_START , NO_QUOTED_STRING_PARTIAL_START )));
59
-
60
- // Complex regexes
61
- private static final String TAG_ATTRIBUTE = IDENTIFIER + OPT_SPACING + optional ("=" , OPT_SPACING , firstOf (DOUBLE_QUOTED_STRING , SINGLE_QUOTED_STRING , NO_QUOTED_STRING ));
62
- private static final String HTML_TAG_FULL = "</?" + OPT_SPACING + IDENTIFIER + OPT_SPACING + zeroOrMore (TAG_ATTRIBUTE , OPT_SPACING ) + "/?+>" ;
63
- private static final String HTML_TAG_PARTIAL_START = OPT_SPACING + "</?+" + OPT_SPACING
64
- + optional (IDENTIFIER , OPT_SPACING , zeroOrMore (TAG_ATTRIBUTE , OPT_SPACING ), optional (TAG_ATTRIBUTE_PARTIAL_START ));
65
- private static final String HTML_TAG_PARTIAL_END = "[\" ']?+" + OPT_SPACING + zeroOrMore (TAG_ATTRIBUTE , OPT_SPACING ) + "/?+>" ;
66
- private static final String HTML_CONTENT = optional (HTML_TAG_PARTIAL_END ) + oneOrMore (OPT_TEXT_OUTSIDE_OF_TAGS , HTML_TAG_FULL ) + OPT_TEXT_OUTSIDE_OF_TAGS
67
- + optional (HTML_TAG_PARTIAL_START );
68
-
69
- private static final String FULL_ALLOWED_LITERALS_REGEX = firstOf (
70
- HTML_CONTENT ,
71
- OPT_TEXT_OUTSIDE_OF_TAGS + HTML_TAG_PARTIAL_START ,
72
- HTML_TAG_PARTIAL_END + OPT_TEXT_OUTSIDE_OF_TAGS ,
73
- HTML_TAG_PARTIAL_END + OPT_TEXT_OUTSIDE_OF_TAGS + HTML_TAG_PARTIAL_START ,
74
- ONLY_ALPHANUMERIC_UNDERSCORES_HYPHENS_AND_PERIODS );
75
- private static final Pattern ALLOWED_DUPLICATED_LITERALS = Pattern .compile (FULL_ALLOWED_LITERALS_REGEX );
76
-
77
- public static final int THRESHOLD_DEFAULT = 3 ;
78
- public static final int MINIMAL_LITERAL_LENGTH_DEFAULT = 5 ;
39
+ private static final Pattern ALLOWED_DUPLICATED_LITERALS = Pattern .compile ("^[a-zA-Z_][.\\ -\\ w]+$" );
79
40
80
41
private final Map <String , LiteralTree > firstOccurrenceTrees = new HashMap <>();
81
42
private final Map <String , List <LiteralTree >> sameLiteralOccurrences = new HashMap <>();
82
43
44
+ public static final int THRESHOLD_DEFAULT = 3 ;
45
+ public static final int MINIMAL_LITERAL_LENGTH_DEFAULT = 5 ;
46
+
83
47
@ RuleProperty (
84
48
key = "threshold" ,
85
49
defaultValue = "" + THRESHOLD_DEFAULT )
@@ -102,10 +66,9 @@ public void visitCompilationUnit(CompilationUnitTree tree) {
102
66
103
67
private void finish () {
104
68
for (Map .Entry <String , List <LiteralTree >> literalOccurrences : sameLiteralOccurrences .entrySet ()) {
105
- String value = literalOccurrences .getKey ();
106
69
List <LiteralTree > occurrences = literalOccurrences .getValue ();
107
70
108
- if (occurrences .size () >= threshold && ! ALLOWED_DUPLICATED_LITERALS . matcher ( value ). matches () ) {
71
+ if (occurrences .size () >= threshold ) {
109
72
String literal = literalOccurrences .getKey ();
110
73
String message = String .format (MESSAGE , literal , occurrences .size ());
111
74
LiteralTree firstOccurrenceTree = firstOccurrenceTrees .get (literal );
@@ -120,25 +83,22 @@ private void finish() {
120
83
@ Override
121
84
public void visitLiteral (LiteralTree tree ) {
122
85
if (tree .is (Kind .REGULAR_STRING_LITERAL )) {
123
- String literal = tree .value ().replace ("\\ '" , "'" ).replace ("\\ \" " , "\" " );
124
- String value = removeQuotesAndQuotesEscaping (literal );
86
+ String literal = tree .value ();
87
+ String value = StringUtils .substring (literal , 1 , literal .length () - 1 );
88
+
89
+ if (value .length () >= minimalLiteralLength && !ALLOWED_DUPLICATED_LITERALS .matcher (value ).find ()) {
125
90
126
- if (value .length () >= minimalLiteralLength ) {
127
91
if (!sameLiteralOccurrences .containsKey (value )) {
128
92
List <LiteralTree > occurrences = new ArrayList <>();
129
93
occurrences .add (tree );
130
94
sameLiteralOccurrences .put (value , occurrences );
131
95
firstOccurrenceTrees .put (value , tree );
96
+
132
97
} else {
133
98
sameLiteralOccurrences .get (value ).add (tree );
134
99
}
135
100
}
136
101
}
137
102
}
138
103
139
- private static String removeQuotesAndQuotesEscaping (String s ) {
140
- var quote = s .charAt (0 );
141
- return s .substring (1 , s .length () - 1 ).replace ("\\ " + quote , String .valueOf (quote ));
142
- }
143
-
144
104
}
0 commit comments