1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package com.puppycrawl.tools.checkstyle;
20
21 import java.io.FileInputStream;
22 import java.io.FileNotFoundException;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.ByteArrayOutputStream;
26 import java.io.ObjectOutputStream;
27 import java.io.OutputStream;
28 import java.io.Serializable;
29 import java.util.Properties;
30 import java.security.MessageDigest;
31
32
33 import com.puppycrawl.tools.checkstyle.api.Configuration;
34 import com.puppycrawl.tools.checkstyle.api.Utils;
35
36
37
38
39
40
41
42
43
44
45
46
47
48 final class PropertyCacheFile
49 {
50
51
52
53
54
55
56 private static final String CONFIG_HASH_KEY = "configuration*?";
57
58
59 private final String detailsFile;
60
61 private final Properties details = new Properties();
62
63
64
65
66
67
68
69 PropertyCacheFile(Configuration currentConfig, String fileName)
70 {
71 boolean setInActive = true;
72 if (fileName != null) {
73 FileInputStream inStream = null;
74
75
76 final String currentConfigHash = getConfigHashCode(currentConfig);
77 try {
78 inStream = new FileInputStream(fileName);
79 details.load(inStream);
80 final String cachedConfigHash =
81 details.getProperty(CONFIG_HASH_KEY);
82 setInActive = false;
83 if (cachedConfigHash == null
84 || !cachedConfigHash.equals(currentConfigHash))
85 {
86
87 details.clear();
88 details.put(CONFIG_HASH_KEY, currentConfigHash);
89 }
90 }
91 catch (final FileNotFoundException e) {
92
93 setInActive = false;
94
95 details.put(CONFIG_HASH_KEY, currentConfigHash);
96 }
97 catch (final IOException e) {
98 Utils.getExceptionLogger()
99 .debug("Unable to open cache file, ignoring.", e);
100 }
101 finally {
102 Utils.closeQuietly(inStream);
103 }
104 }
105 detailsFile = setInActive ? null : fileName;
106 }
107
108
109 void destroy()
110 {
111 if (detailsFile != null) {
112 FileOutputStream out = null;
113 try {
114 out = new FileOutputStream(detailsFile);
115 details.store(out, null);
116 }
117 catch (final IOException e) {
118 Utils.getExceptionLogger()
119 .debug("Unable to save cache file.", e);
120 }
121 finally {
122 this.flushAndCloseOutStream(out);
123 }
124 }
125 }
126
127
128
129
130
131 private void flushAndCloseOutStream(OutputStream stream)
132 {
133 if (stream != null) {
134 try {
135 stream.flush();
136 }
137 catch (final IOException ex) {
138 Utils.getExceptionLogger()
139 .debug("Unable to flush output stream.", ex);
140 }
141 finally {
142 Utils.closeQuietly(stream);
143 }
144 }
145 }
146
147
148
149
150
151
152 boolean alreadyChecked(String fileName, long timestamp)
153 {
154 final String lastChecked = details.getProperty(fileName);
155 return lastChecked != null
156 && lastChecked.equals(Long.toString(timestamp));
157 }
158
159
160
161
162
163
164 void checkedOk(String fileName, long timestamp)
165 {
166 details.put(fileName, Long.toString(timestamp));
167 }
168
169
170
171
172
173
174
175 private String getConfigHashCode(Serializable configuration)
176 {
177 try {
178
179
180 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
181 ObjectOutputStream oos = null;
182 try {
183 oos = new ObjectOutputStream(baos);
184 oos.writeObject(configuration);
185 }
186 finally {
187 this.flushAndCloseOutStream(oos);
188 }
189
190
191
192
193
194 final MessageDigest md = MessageDigest.getInstance("SHA");
195 md.update(baos.toByteArray());
196
197 return hexEncode(md.digest());
198 }
199 catch (final Exception ex) {
200 Utils.getExceptionLogger()
201 .debug("Unable to calculate hashcode.", ex);
202 return "ALWAYS FRESH: " + System.currentTimeMillis();
203 }
204 }
205
206
207 private static final char[] HEX_CHARS = {
208 '0', '1', '2', '3', '4', '5', '6', '7',
209 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
210 };
211
212
213 private static final int MASK_0X0F = 0x0F;
214
215
216 private static final int SHIFT_4 = 4;
217
218
219
220
221
222
223 private static String hexEncode(byte[] byteArray)
224 {
225 final StringBuilder buf = new StringBuilder(2 * byteArray.length);
226 for (final byte b : byteArray) {
227 final int low = b & MASK_0X0F;
228 final int high = b >> SHIFT_4 & MASK_0X0F;
229 buf.append(HEX_CHARS[high]);
230 buf.append(HEX_CHARS[low]);
231 }
232 return buf.toString();
233 }
234 }