1 package com.trendmicro.grid.acl.metadata;
2
3 import net.sf.tinyjee.util.Hex;
4
5 import javax.xml.bind.annotation.*;
6 import java.io.Externalizable;
7 import java.io.IOException;
8 import java.io.ObjectInput;
9 import java.io.ObjectOutput;
10 import java.lang.reflect.Array;
11 import java.util.Arrays;
12 import java.util.Date;
13
14
15
16
17
18
19
20
21
22
23
24 @XmlAccessorType(XmlAccessType.PROPERTY)
25 @XmlAccessorOrder(value = XmlAccessOrder.UNDEFINED)
26 @XmlType(name = "meta", namespace = Metadata.NS, propOrder = {
27 "name", "value", "stringValues", "booleanValues", "numericValues", "dateValues", "binaryValue"})
28 public class Meta implements Cloneable, Externalizable {
29
30 private static final long serialVersionUID = -1817738693919240310L;
31 private static final byte serialVersion = 1;
32
33 private static final boolean TRIM_STRING_VALUES =
34 Boolean.parseBoolean(System.getProperty("gacl.meta.trim.values", Boolean.TRUE.toString()));
35 private static final boolean TRIM_NAMES =
36 Boolean.parseBoolean(System.getProperty("gacl.meta.trim.names", Boolean.TRUE.toString()));
37 private static final boolean PRINT_BINARY_AS_HEX_STRING =
38 Boolean.parseBoolean(System.getProperty("gacl.meta.print.binary.as.hex.string", Boolean.FALSE.toString()));
39
40 String name;
41
42 Object value;
43 DataType valueType;
44
45 transient int hashCode;
46
47
48
49
50 public Meta() {
51 }
52
53
54
55
56
57
58 public Meta(String name) {
59 if (name == null)
60 throw new IllegalArgumentException("Name cannot be set to 'null'");
61 setName(name);
62 }
63
64 @XmlID
65 @XmlAttribute(required = true)
66 public String getName() {
67 return name == null ? "" : name;
68 }
69
70 public void setName(String name) {
71 if (this.name == null || this.name.isEmpty() || this.name.equals(name)) {
72 this.name = name != null && TRIM_NAMES ? name.trim() : name;
73 } else {
74 throw new IllegalStateException("It's illegal to change the name of a named Meta element. " +
75 "'" + this.name + "' cannot be changed to '" + name + "'.");
76 }
77 }
78
79
80
81
82
83
84 @XmlTransient
85 public Object getAsAnyValue() {
86 return value;
87 }
88
89
90
91
92
93
94 @XmlTransient
95 public DataType getDataType() {
96 return valueType;
97 }
98
99 @XmlAttribute
100 public String getValue() {
101 return valueType == DataType.STRING && value instanceof String ? (String) value : null;
102 }
103
104 public void setValue(String value) {
105 this.value = value != null && TRIM_STRING_VALUES ? value.trim() : value;
106 valueType = DataType.STRING;
107 hashCode = 0;
108 }
109
110
111
112
113
114
115
116 public Meta value(String value) {
117 setValue(value);
118 return this;
119 }
120
121 @XmlElement(name = "v", namespace = Metadata.NS)
122 public StringValue[] getStringValues() {
123 String[] v = getValues();
124 if (v == null)
125 return null;
126 StringValue[] values = new StringValue[v.length];
127 for (int i = 0; i < v.length; i++)
128 values[i] = new StringValue(v[i]);
129
130 return values;
131 }
132
133 public void setStringValues(StringValue[] values) {
134 if (values == null)
135 setValues();
136 else {
137 String[] v = new String[values.length];
138 for (int i = 0; i < v.length; i++)
139 v[i] = values[i].getContent();
140 setValues(v);
141 }
142 }
143
144 @XmlTransient
145 public String[] getValues() {
146 return valueType == DataType.STRING && value instanceof String[] ? (String[]) value : null;
147 }
148
149 public void setValues(String... values) {
150 value = values;
151
152 if (TRIM_STRING_VALUES) {
153 for (int i = 0; i < values.length; i++) {
154 if (values[i] != null)
155 values[i] = values[i].trim();
156 }
157 }
158
159 valueType = DataType.STRING;
160 hashCode = 0;
161 }
162
163
164
165
166
167
168
169 public Meta value(String... values) {
170 setValues(values);
171 return this;
172 }
173
174 @XmlList
175 @XmlAttribute
176 public boolean[] getBooleanValues() {
177 return valueType == DataType.BOOLEAN ? (boolean[]) value : null;
178 }
179
180 public void setBooleanValues(boolean... booleanValues) {
181 value = booleanValues;
182 valueType = DataType.BOOLEAN;
183 hashCode = 0;
184 }
185
186
187
188
189
190
191
192 public Meta value(boolean... booleanValues) {
193 setBooleanValues(booleanValues);
194 return this;
195 }
196
197 @XmlList
198 @XmlAttribute
199 public double[] getNumericValues() {
200 return valueType == DataType.NUMERIC ? (double[]) value : null;
201 }
202
203 public void setNumericValues(double... numericValues) {
204 value = numericValues;
205 valueType = DataType.NUMERIC;
206 hashCode = 0;
207 }
208
209
210
211
212
213
214
215 public Meta value(double... numericValues) {
216 setNumericValues(numericValues);
217 return this;
218 }
219
220 @XmlList
221 @XmlAttribute
222 public Date[] getDateValues() {
223 return valueType == DataType.DATE ? (Date[]) value : null;
224 }
225
226 public void setDateValues(Date... dateValues) {
227 value = dateValues;
228 valueType = DataType.DATE;
229 hashCode = 0;
230 }
231
232
233
234
235
236
237
238 public Meta value(Date... dateValues) {
239 setDateValues(dateValues);
240 return this;
241 }
242
243 @XmlAttribute
244 public byte[] getBinaryValue() {
245 return valueType == DataType.BINARY ? (byte[]) value : null;
246 }
247
248 public void setBinaryValue(byte[] binaryValue) {
249 value = binaryValue;
250 valueType = DataType.BINARY;
251 hashCode = 0;
252 }
253
254
255
256
257
258
259
260 public Meta value(byte[] binaryValue) {
261 setBinaryValue(binaryValue);
262 return this;
263 }
264
265
266
267
268 @Override
269 public String toString() {
270 Object value = this.value;
271 if (value != null && value.getClass().isArray()) {
272 if (value instanceof byte[]) {
273 value = PRINT_BINARY_AS_HEX_STRING ? Hex.encode((byte[]) value) : "<<binary>>";
274 } else {
275 StringBuilder b = new StringBuilder();
276 for (int i = 0, len = Array.getLength(value); i < len; i++)
277 b.append(i > 0 ? ", " : "").append(Array.get(value, i));
278 value = b.toString();
279 }
280 }
281
282 return "Meta{" +
283 "name='" + name + '\'' +
284 ", type='" + getDataType() + '\'' +
285 ", value='" + value + '\'' +
286 '}';
287 }
288
289
290
291
292 @Override
293 public Meta clone() {
294 try {
295 return (Meta) super.clone();
296 } catch (CloneNotSupportedException e) {
297 throw new RuntimeException(e);
298 }
299 }
300
301
302
303
304 @Override
305 public boolean equals(Object o) {
306 if (this == o) return true;
307 if (!(o instanceof Meta)) return false;
308
309 Meta meta = (Meta) o;
310
311 if (valueType != meta.valueType) return false;
312 if (name != null ? !name.equals(meta.name) : meta.name != null) return false;
313
314 if (value != null) {
315 switch (valueType) {
316 case STRING:
317 case DATE:
318 if (value instanceof Object[])
319 return Arrays.equals((Object[]) value, (Object[]) meta.value);
320 return value.equals(meta.value);
321 case BOOLEAN:
322 return Arrays.equals((boolean[]) value, (boolean[]) meta.value);
323 case NUMERIC:
324 return Arrays.equals((double[]) value, (double[]) meta.value);
325 case BINARY:
326 return Arrays.equals((byte[]) value, (byte[]) meta.value);
327 }
328 return false;
329 } else
330 return meta.value == null;
331 }
332
333
334
335
336 @Override
337 public int hashCode() {
338 if (hashCode == 0) {
339 int result = 0;
340 if (value != null) {
341 switch (valueType) {
342 case STRING:
343 case DATE:
344 if (value instanceof Object[])
345 result = Arrays.hashCode((Object[]) value);
346 else
347 result = value.hashCode();
348 break;
349 case BOOLEAN:
350 result = Arrays.hashCode((boolean[]) value);
351 break;
352 case NUMERIC:
353 result = Arrays.hashCode((double[]) value);
354 break;
355 case BINARY:
356 result = Arrays.hashCode((byte[]) value);
357 break;
358 }
359 }
360
361 result = 31 * result + (valueType != null ? valueType.hashCode() : 0);
362 result = 31 * result + (name != null ? name.hashCode() : 0);
363 hashCode = result;
364 }
365 return hashCode;
366 }
367
368
369
370
371 @Override
372 public void writeExternal(ObjectOutput out) throws IOException {
373 out.writeByte(serialVersion);
374 out.writeUTF(name == null ? "" : name);
375 out.writeInt(valueType.ordinal());
376 out.writeObject(value);
377 }
378
379
380
381
382 @Override
383 public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
384 switch (in.readByte()) {
385 case 1:
386 name = in.readUTF();
387 valueType = DataType.values()[in.readInt()];
388 value = in.readObject();
389 break;
390 default:
391 throw new IOException("Stream is corrupted or was written with a newer unsupported version.");
392 }
393 }
394 }