1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.bcel.classfile;
18
19 import java.io.DataInput;
20 import java.io.DataOutputStream;
21 import java.io.IOException;
22 import java.util.HashMap;
23 import java.util.LinkedHashMap;
24 import java.util.Map;
25 import java.util.Objects;
26
27 import org.apache.bcel.Const;
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public final class ConstantUtf8 extends Constant {
59
60 private static final class Cache {
61
62 private static final boolean BCEL_STATISTICS = Boolean.getBoolean(SYS_PROP_STATISTICS);
63 private static final int MAX_ENTRIES = Integer.getInteger(SYS_PROP_CACHE_MAX_ENTRIES, 0).intValue();
64 private static final int INITIAL_CAPACITY = (int) (MAX_ENTRIES / 0.75);
65
66 private static final HashMap<String, ConstantUtf8> CACHE = new LinkedHashMap<String, ConstantUtf8>(INITIAL_CAPACITY, 0.75f, true) {
67
68 private static final long serialVersionUID = -8506975356158971766L;
69
70 @Override
71 protected boolean removeEldestEntry(final Map.Entry<String, ConstantUtf8> eldest) {
72 return size() > MAX_ENTRIES;
73 }
74 };
75
76
77 private static final int MAX_ENTRY_SIZE = Integer.getInteger(SYS_PROP_CACHE_MAX_ENTRY_SIZE, 200).intValue();
78
79 static boolean isEnabled() {
80 return MAX_ENTRIES > 0 && MAX_ENTRY_SIZE > 0;
81 }
82
83 }
84
85
86 private static volatile int considered;
87 private static volatile int created;
88 private static volatile int hits;
89 private static volatile int skipped;
90
91 private static final String SYS_PROP_CACHE_MAX_ENTRIES = "bcel.maxcached";
92 private static final String SYS_PROP_CACHE_MAX_ENTRY_SIZE = "bcel.maxcached.size";
93 private static final String SYS_PROP_STATISTICS = "bcel.statistics";
94
95 static {
96 if (Cache.BCEL_STATISTICS) {
97 Runtime.getRuntime().addShutdownHook(new Thread(ConstantUtf8::printStats));
98 }
99 }
100
101
102
103
104
105
106 public static synchronized void clearCache() {
107 Cache.CACHE.clear();
108 }
109
110
111 static synchronized void clearStats() {
112 hits = considered = skipped = created = 0;
113 }
114
115
116 private static void countCreated() {
117 created++;
118 }
119
120
121
122
123
124
125
126
127
128
129
130 public static ConstantUtf8 getCachedInstance(final String value) {
131 if (value.length() > Cache.MAX_ENTRY_SIZE) {
132 skipped++;
133 return new ConstantUtf8(value);
134 }
135 considered++;
136 synchronized (ConstantUtf8.class) {
137 ConstantUtf8 result = Cache.CACHE.get(value);
138 if (result != null) {
139 hits++;
140 return result;
141 }
142 result = new ConstantUtf8(value);
143 Cache.CACHE.put(value, result);
144 return result;
145 }
146 }
147
148
149
150
151
152
153
154
155
156
157
158
159 public static ConstantUtf8 getInstance(final DataInput dataInput) throws IOException {
160 return getInstance(dataInput.readUTF());
161 }
162
163
164
165
166
167
168
169
170
171
172
173 public static ConstantUtf8 getInstance(final String value) {
174 return Cache.isEnabled() ? getCachedInstance(value) : new ConstantUtf8(value);
175 }
176
177
178 static void printStats() {
179 final String prefix = "[Apache Commons BCEL]";
180 System.err.printf("%s Cache hit %,d/%,d, %d skipped.%n", prefix, hits, considered, skipped);
181 System.err.printf("%s Total of %,d ConstantUtf8 objects created.%n", prefix, created);
182 System.err.printf("%s Configuration: %s=%,d, %s=%,d.%n", prefix, SYS_PROP_CACHE_MAX_ENTRIES, Cache.MAX_ENTRIES, SYS_PROP_CACHE_MAX_ENTRY_SIZE,
183 Cache.MAX_ENTRY_SIZE);
184 }
185
186 private final String value;
187
188
189
190
191
192
193 public ConstantUtf8(final ConstantUtf8 constantUtf8) {
194 this(constantUtf8.getBytes());
195 }
196
197
198
199
200
201
202
203 ConstantUtf8(final DataInput dataInput) throws IOException {
204 super(Const.CONSTANT_Utf8);
205 value = dataInput.readUTF();
206 countCreated();
207 }
208
209
210
211
212 public ConstantUtf8(final String value) {
213 super(Const.CONSTANT_Utf8);
214 this.value = Objects.requireNonNull(value, "value");
215 countCreated();
216 }
217
218
219
220
221
222
223
224 @Override
225 public void accept(final Visitor v) {
226 v.visitConstantUtf8(this);
227 }
228
229
230
231
232
233
234
235 @Override
236 public void dump(final DataOutputStream file) throws IOException {
237 file.writeByte(super.getTag());
238 file.writeUTF(value);
239 }
240
241
242
243
244 public String getBytes() {
245 return value;
246 }
247
248
249
250
251
252 @java.lang.Deprecated
253 public void setBytes(final String bytes) {
254 throw new UnsupportedOperationException();
255 }
256
257
258
259
260 @Override
261 public String toString() {
262 return super.toString() + "(\"" + Utility.replace(value, "\n", "\\n") + "\")";
263 }
264 }