1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.checks;
21
22 import java.io.File;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.nio.file.Files;
26 import java.util.HashMap;
27 import java.util.Map;
28 import java.util.Map.Entry;
29 import java.util.Properties;
30 import java.util.regex.Matcher;
31 import java.util.regex.Pattern;
32
33 import com.puppycrawl.tools.checkstyle.StatelessCheck;
34 import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
35 import com.puppycrawl.tools.checkstyle.api.FileText;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73 @StatelessCheck
74 public class UniquePropertiesCheck extends AbstractFileSetCheck {
75
76
77
78
79 public static final String MSG_KEY = "properties.duplicate.property";
80
81
82
83 public static final String MSG_IO_EXCEPTION_KEY = "unable.open.cause";
84
85
86
87
88 private static final Pattern SPACE_PATTERN = Pattern.compile(" ");
89
90
91
92
93 public UniquePropertiesCheck() {
94 setFileExtensions("properties");
95 }
96
97 @Override
98 protected void processFiltered(File file, FileText fileText) {
99 final UniqueProperties properties = new UniqueProperties();
100 try (InputStream inputStream = Files.newInputStream(file.toPath())) {
101 properties.load(inputStream);
102 }
103 catch (IOException ex) {
104 log(1, MSG_IO_EXCEPTION_KEY, file.getPath(),
105 ex.getLocalizedMessage());
106 }
107
108 for (Entry<String, Integer> duplication : properties
109 .getDuplicatedKeys().entrySet()) {
110 final String keyName = duplication.getKey();
111 final int lineNumber = getLineNumber(fileText, keyName);
112
113 log(lineNumber, MSG_KEY, keyName, duplication.getValue() + 1);
114 }
115 }
116
117
118
119
120
121
122
123
124
125
126
127
128 private static int getLineNumber(FileText fileText, String keyName) {
129 final Pattern keyPattern = getKeyPattern(keyName);
130 int lineNumber = 1;
131 final Matcher matcher = keyPattern.matcher("");
132 for (int index = 0; index < fileText.size(); index++) {
133 final String line = fileText.get(index);
134 matcher.reset(line);
135 if (matcher.matches()) {
136 break;
137 }
138 ++lineNumber;
139 }
140
141
142 if (lineNumber > fileText.size() - 1) {
143 lineNumber = 1;
144 }
145 return lineNumber;
146 }
147
148
149
150
151
152
153
154
155 private static Pattern getKeyPattern(String keyName) {
156 final String keyPatternString = "^" + SPACE_PATTERN.matcher(keyName)
157 .replaceAll(Matcher.quoteReplacement("\\\\ ")) + "[\\s:=].*$";
158 return Pattern.compile(keyPatternString);
159 }
160
161
162
163
164
165
166
167
168 private static final class UniqueProperties extends Properties {
169
170
171 private static final long serialVersionUID = 1L;
172
173
174
175
176 private final Map<String, Integer> duplicatedKeys = new HashMap<>();
177
178
179
180
181 @Override
182 public synchronized Object put(Object key, Object value) {
183 final Object oldValue = super.put(key, value);
184 if (oldValue != null && key instanceof String) {
185 final String keyString = (String) key;
186
187 duplicatedKeys.put(keyString,
188 duplicatedKeys.getOrDefault(keyString, 0) + 1);
189 }
190 return oldValue;
191 }
192
193
194
195
196
197
198 public Map<String, Integer> getDuplicatedKeys() {
199 return new HashMap<>(duplicatedKeys);
200 }
201
202 }
203
204 }