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.concurrent.atomic.AtomicInteger;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33
34 import com.puppycrawl.tools.checkstyle.StatelessCheck;
35 import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck;
36 import com.puppycrawl.tools.checkstyle.api.FileText;
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
74 @StatelessCheck
75 public class UniquePropertiesCheck extends AbstractFileSetCheck {
76
77
78
79
80 public static final String MSG_KEY = "properties.duplicate.property";
81
82
83
84 public static final String MSG_IO_EXCEPTION_KEY = "unable.open.cause";
85
86
87
88
89 private static final Pattern SPACE_PATTERN = Pattern.compile(" ");
90
91
92
93
94 public UniquePropertiesCheck() {
95 setFileExtensions("properties");
96 }
97
98 @Override
99 protected void processFiltered(File file, FileText fileText) {
100 final UniqueProperties properties = new UniqueProperties();
101 try (InputStream inputStream = Files.newInputStream(file.toPath())) {
102 properties.load(inputStream);
103 }
104 catch (IOException ex) {
105 log(1, MSG_IO_EXCEPTION_KEY, file.getPath(),
106 ex.getLocalizedMessage());
107 }
108
109 for (Entry<String, AtomicInteger> duplication : properties
110 .getDuplicatedKeys().entrySet()) {
111 final String keyName = duplication.getKey();
112 final int lineNumber = getLineNumber(fileText, keyName);
113
114 log(lineNumber, MSG_KEY, keyName, duplication.getValue().get() + 1);
115 }
116 }
117
118
119
120
121
122
123
124
125
126
127
128
129 private static int getLineNumber(FileText fileText, String keyName) {
130 final Pattern keyPattern = getKeyPattern(keyName);
131 int lineNumber = 1;
132 final Matcher matcher = keyPattern.matcher("");
133 for (int index = 0; index < fileText.size(); index++) {
134 final String line = fileText.get(index);
135 matcher.reset(line);
136 if (matcher.matches()) {
137 break;
138 }
139 ++lineNumber;
140 }
141
142
143 if (lineNumber > fileText.size() - 1) {
144 lineNumber = 1;
145 }
146 return lineNumber;
147 }
148
149
150
151
152
153
154
155
156 private static Pattern getKeyPattern(String keyName) {
157 final String keyPatternString = "^" + SPACE_PATTERN.matcher(keyName)
158 .replaceAll(Matcher.quoteReplacement("\\\\ ")) + "[\\s:=].*$";
159 return Pattern.compile(keyPatternString);
160 }
161
162
163
164
165
166
167
168
169 private static final class UniqueProperties extends Properties {
170
171
172 private static final long serialVersionUID = 1L;
173
174
175
176
177 private final Map<String, AtomicInteger> duplicatedKeys = new HashMap<>();
178
179
180
181
182 @Override
183 public synchronized Object put(Object key, Object value) {
184 final Object oldValue = super.put(key, value);
185 if (oldValue != null && key instanceof String) {
186 final String keyString = (String) key;
187
188 duplicatedKeys.computeIfAbsent(keyString, empty -> new AtomicInteger(0))
189 .incrementAndGet();
190 }
191 return oldValue;
192 }
193
194
195
196
197
198
199 public Map<String, AtomicInteger> getDuplicatedKeys() {
200 return new HashMap<>(duplicatedKeys);
201 }
202
203 }
204
205 }