OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
182 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.utils.doml;
15
 
16
import java.io.BufferedOutputStream;
17
import java.io.BufferedWriter;
18
import java.io.IOException;
19
import java.math.BigDecimal;
20
import java.text.DecimalFormat;
21
import java.text.DecimalFormatSymbols;
22
import java.text.ParseException;
23
import java.text.SimpleDateFormat;
24
import java.time.Instant;
25
import java.util.ArrayList;
26
import java.util.Collections;
27
import java.util.Date;
28
import java.util.List;
29
import java.util.Locale;
30
import java.util.TimeZone;
31
 
32
public class Element {
33
    private String name;
34
 
35
    private List<String> attributes;
36
    private List<Element> children;
37
 
38
    private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT = ThreadLocal.withInitial(() -> {
39
        final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
40
        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
41
        return dateFormat;
42
    });
43
 
44
    private static final ThreadLocal<DecimalFormat> DOUBLE_FORMAT = ThreadLocal.withInitial(() -> {
45
        final DecimalFormat f = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
46
        f.setMaximumFractionDigits(340); // 340 = DecimalFormat.DOUBLE_FRACTION_DIGITS
47
        return f;
48
    });
49
 
50
    private static final ThreadLocal<DecimalFormat> FLOAT_FORMAT = ThreadLocal.withInitial(() -> {
51
        final DecimalFormat f = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
52
        f.setMaximumFractionDigits(170);
53
        return f;
54
    });
55
 
56
    /**
57
     * Element names are case-sensitive
58
     *
59
     * Element names can contain letters, digits,colons, hyphens, underscores, and periods.
60
     *
61
     * Element names cannot contain spaces
62
     */
63
 
64
    public Element(String name) {
65
        if (name == null) {
66
            throw new IllegalArgumentException("null name");
67
        }
68
        this.name = name;
69
    }
70
 
71
    public Element(String name, int expectedAttributesCount) {
72
        if (name == null) {
73
            throw new IllegalArgumentException("null name");
74
        }
75
        this.name = name;
76
        if (expectedAttributesCount > 0) {
77
            this.attributes = new ArrayList<>(expectedAttributesCount * 2);
78
        }
79
    }
80
 
81
    public Element(String name, int expectedAttributesCount, int expecteChildrenCount) {
82
        if (name == null) {
83
            throw new IllegalArgumentException("null name");
84
        }
85
        this.name = name;
86
        if (expectedAttributesCount > 0) {
87
            this.attributes = new ArrayList<>(expectedAttributesCount * 2);
88
        }
89
        if (expecteChildrenCount > 0) {
90
            this.children = new ArrayList<>(expecteChildrenCount);
91
        }
92
    }
93
 
94
    public String getName() {
95
        return this.name;
96
    }
97
 
98
    public void setName(String name) {
99
        if (name == null) {
100
            throw new IllegalArgumentException("null name");
101
        }
102
        this.name = name;
103
    }
104
 
105
    public int getAttributeCount() {
106
        if (this.attributes == null)
107
            return 0;
108
        return this.attributes.size() / 2;
109
    }
110
 
111
    // String
112
 
113
    public String getAttribute(String name) {
114
        if (name == null) {
115
            throw new IllegalArgumentException("null name");
116
        }
117
        if (this.attributes == null) {
118
            return null;
119
        }
120
        final int size = this.attributes.size();
121
        for (int i = 0; i < size; i += 2) {
122
            if (this.attributes.get(i).equals(name)) {
123
                return this.attributes.get(i + 1);
124
            }
125
        }
126
        return null;
127
    }
128
 
129
    public String getAttribute(String name, String defaultValue) {
130
        final String v = getAttribute(name);
131
        if (v == null) {
132
            return defaultValue;
133
        }
134
        return v;
135
    }
136
 
137
    public void setAttribute(String name, String value) {
138
        if (name == null) {
139
            throw new IllegalArgumentException("null name");
140
        }
141
        if (value == null) {
142
            throw new IllegalArgumentException("null value for attribute " + name);
143
        }
144
        if (this.attributes == null) {
145
            this.attributes = new ArrayList<>();
146
        } else {
147
            final int size = this.attributes.size();
148
            for (int i = 0; i < size; i += 2) {
149
                if (this.attributes.get(i).equals(name)) {
150
                    this.attributes.set(i + 1, value);
151
                    return;
152
                }
153
            }
154
        }
155
        this.attributes.add(name);
156
        this.attributes.add(value);
157
    }
158
 
159
    // Integer
160
 
161
    public Integer getAttributeAsInteger(String name) {
162
        final String v = getAttribute(name);
163
        if (v == null) {
164
            return null;
165
        }
166
        return Integer.parseInt(v);
167
    }
168
 
169
    public int getAttributeAsInteger(String name, int defaultValue) {
170
        final String v = getAttribute(name);
171
        if (v == null) {
172
            return defaultValue;
173
        }
174
        return Integer.parseInt(v);
175
    }
176
 
177
    public void setAttribute(String name, int value) {
178
        setAttribute(name, String.valueOf(value));
179
    }
180
 
181
    // Long
182
 
183
    public Long getAttributeAsLong(String name) {
184
        final String v = getAttribute(name);
185
        if (v == null) {
186
            return null;
187
        }
188
        return Long.parseLong(v);
189
    }
190
 
191
    public long getAttributeAsLong(String name, long defaultValue) {
192
        final String v = getAttribute(name);
193
        if (v == null) {
194
            return defaultValue;
195
        }
196
        return Long.parseLong(v);
197
    }
198
 
199
    public void setAttribute(String name, long value) {
200
        setAttribute(name, String.valueOf(value));
201
    }
202
 
203
    // Float
204
 
205
    public Float getAttributeAsFloat(String name) {
206
        final String v = getAttribute(name);
207
        if (v == null) {
208
            return null;
209
        }
210
        return parseFloat(v);
211
    }
212
 
213
    public float getAttributeAsFloat(String name, float defaultValue) {
214
        final String v = getAttribute(name);
215
        if (v == null) {
216
            return defaultValue;
217
        }
218
        return parseFloat(v);
219
    }
220
 
221
    public void setAttribute(String name, float value) {
222
        setAttribute(name, floatToString(value));
223
    }
224
 
225
    // Double
226
 
227
    public Double getAttributeAsDouble(String name) {
228
        final String v = getAttribute(name);
229
        if (v == null) {
230
            return null;
231
        }
232
        return parseDouble(name);
233
    }
234
 
235
    public double getAttributeAsLong(String name, double defaultValue) {
236
        final String v = getAttribute(name);
237
        if (v == null) {
238
            return defaultValue;
239
        }
240
        return parseDouble(name);
241
    }
242
 
243
    public void setAttribute(String name, double value) {
244
        setAttribute(name, doubleToString(value));
245
    }
246
 
247
    public static String doubleToString(final double d) {
248
        if (StrictMath.rint(d) == d && Double.isFinite(d)) {
249
            return Long.toString((long) d);
250
        } else {
251
            return DOUBLE_FORMAT.get().format(d);
252
        }
253
    }
254
 
255
    private static double parseDouble(String v) {
256
        boolean neg = false;
257
        int start = 0;
258
        if (v.charAt(0) == '-') {
259
            neg = true;
260
            start = 1;
261
        }
262
        double result = 0;
263
        int length = v.length();
264
        int pointIndex = length;
265
        for (int i = start; i < length; i++) {
266
            char c = v.charAt(i);
267
            if (c == '.') {
268
                pointIndex = i;
269
                break;
270
            }
271
        }
272
        for (int i = start; i < pointIndex; i++) {
273
            result = 10 * result + (v.charAt(i) - '0');
274
        }
275
        int n = 0;
276
        int div = 1;
277
        for (int i = pointIndex + 1; i < length; i++) {
278
            n = 10 * n + (v.charAt(i) - '0');
279
            div = div * 10;
280
        }
281
 
282
        result += ((double) n) / div;
283
        if (neg) {
284
            result = (result * -1f);
285
        }
286
        return result;
287
    }
288
 
289
    public static String floatToString(final double d) {
290
        if (StrictMath.rint(d) == d && Double.isFinite(d)) {
291
            return Long.toString((long) d);
292
        } else {
293
            return FLOAT_FORMAT.get().format(d);
294
 
295
        }
296
    }
297
 
298
    private static float parseFloat(String v) {
299
        boolean neg = false;
300
        int start = 0;
301
        if (v.charAt(0) == '-') {
302
            neg = true;
303
            start = 1;
304
        }
305
        float result = 0;
306
        int length = v.length();
307
        int pointIndex = length;
308
        for (int i = start; i < length; i++) {
309
            char c = v.charAt(i);
310
            if (c == '.') {
311
                pointIndex = i;
312
                break;
313
            }
314
        }
315
        for (int i = start; i < pointIndex; i++) {
316
            result = 10 * result + (v.charAt(i) - '0');
317
        }
318
        int n = 0;
319
        int div = 1;
320
        for (int i = pointIndex + 1; i < length; i++) {
321
            n = 10 * n + (v.charAt(i) - '0');
322
            div = div * 10;
323
        }
324
 
325
        result += ((float) n) / div;
326
        if (neg) {
327
            result = (result * -1f);
328
        }
329
        return result;
330
    }
331
    // BigDecimal
332
 
333
    public BigDecimal getAttributeAsBigDecimal(String name) {
334
        final String v = getAttribute(name);
335
        if (v == null) {
336
            return null;
337
        }
338
        return new BigDecimal(v);
339
    }
340
 
341
    public BigDecimal getAttributeAsBigDecimal(String name, BigDecimal defaultValue) {
342
        final String v = getAttribute(name);
343
        if (v == null) {
344
            return defaultValue;
345
        }
346
        return new BigDecimal(v);
347
    }
348
 
349
    public void setAttribute(String name, BigDecimal value) {
350
        setAttribute(name, String.valueOf(value));
351
    }
352
 
353
    // Boolean
354
 
355
    public Boolean getAttributeAsBoolean(String name) {
356
        final String v = getAttribute(name);
357
        if (v == null) {
358
            return null;
359
        }
360
        if (name.equals("true")) {
361
            return Boolean.TRUE;
362
        }
363
        return Boolean.FALSE;
364
    }
365
 
366
    public boolean getAttributeAsBoolean(String name, boolean defaultValue) {
367
        final String v = getAttribute(name);
368
        if (v == null) {
369
            return defaultValue;
370
        }
371
        return name.equals("true");
372
    }
373
 
374
    public void setAttribute(String name, boolean value) {
375
        setAttribute(name, value ? "true" : "false");
376
    }
377
 
378
    // Date
379
 
380
    public Date getAttributeAsDate(String name) {
381
        final String v = getAttribute(name);
382
        if (v == null)
383
            return null;
384
        try {
385
 
386
            return DATE_FORMAT.get().parse(v);
387
        } catch (ParseException e) {
388
            throw new IllegalStateException("cannot parse date : '" + v + "' : " + e);
389
        }
390
    }
391
 
392
    public Date getAttributeAsDate(String name, Date defaultValue) {
393
        final Date v = getAttributeAsDate(name);
394
        if (v == null)
395
            return defaultValue;
396
        return v;
397
    }
398
 
399
    public void setAttribute(String name, Date value) {
400
        setAttribute(name, value.toInstant().toString());
401
    }
402
 
403
    // Instant
404
 
405
    public Instant getAttributeAsInstant(String name) {
406
        String v = getAttribute(name);
407
        if (v == null)
408
            return null;
409
        return Instant.parse(v);
410
 
411
    }
412
 
413
    public void setAttribute(String name, Instant value) {
414
        setAttribute(name, value.toString());
415
    }
416
 
417
    public void addChild(Element child) {
418
        if (child == null) {
419
            throw new IllegalArgumentException("null child");
420
        }
421
        if (this.children == null) {
422
            this.children = new ArrayList<>();
423
        }
424
        this.children.add(child);
425
    }
426
 
427
    public void addChildren(List<Element> children) {
428
        if (children == null) {
429
            throw new IllegalArgumentException("null children");
430
        }
431
        if (this.children == null) {
432
            this.children = new ArrayList<>(children.size() * 2);
433
        }
434
        this.children.addAll(children);
435
    }
436
 
437
    /**
438
     * This returns the first child element within this element with the given name. If no elements
439
     * exist for the specified name and namespace, null is returned.
440
     *
441
     * @param name of child element to match
442
     * @return the first matching child element, or null if not found
443
     */
444
    public Element getChild(String name) {
445
        if (name == null) {
446
            throw new IllegalArgumentException("null name");
447
        }
448
        if (this.children == null) {
449
            return null;
450
        }
451
        for (Element e : this.children) {
452
            if (e.getName().equals(name)) {
453
                return e;
454
            }
455
        }
456
        return null;
457
    }
458
 
459
    public List<Element> getChildren(String name) {
460
        if (name == null) {
461
            throw new IllegalArgumentException("null name");
462
        }
463
        if (this.children == null) {
464
            return Collections.emptyList();
465
        }
466
        final List<Element> result = new ArrayList<>();
467
        for (Element e : this.children) {
468
            if (e.getName().equals(name)) {
469
                result.add(e);
470
            }
471
        }
472
        return result;
473
    }
474
 
475
    /**
476
     * This returns a <code>List</code> of all the child elements nested directly (one level deep)
477
     * within this element, as <code>Element</code> objects. If this target element has no nested
478
     * elements, an empty List is returned. The returned list is unmodifiable.
479
     *
480
     * No recursion is performed.
481
     *
482
     * @return list of child <code>XXMLElement</code> objects for this element
483
     */
484
    public List<Element> getChildren() {
485
        if (this.children == null) {
486
            return Collections.emptyList();
487
        }
488
        return Collections.unmodifiableList(children);
489
    }
490
 
491
    /**
492
     * <p>
493
     * This removes the first child element (one level deep) with the given name. Returns true if a
494
     * child was removed.
495
     * </p>
496
     *
497
     * @param name of child element to remove
498
     * @return whether deletion occurred
499
     */
500
    public boolean removeChild(final String name) {
501
        if (name == null) {
502
            throw new IllegalArgumentException("null name");
503
        }
504
        if (this.children == null) {
505
            return false;
506
        }
507
        final int size = this.children.size();
508
        for (int i = 0; i < size; i++) {
509
            if (this.children.get(i).getName().equals(name)) {
510
                this.children.remove(i);
511
                return true;
512
            }
513
        }
514
        return false;
515
    }
516
 
517
    public int getChildrenCount() {
518
        if (this.children == null) {
519
            return 0;
520
        }
521
        return this.children.size();
522
    }
523
 
524
    void appendAttributes(BufferedWriter writer) throws IOException {
525
        if (this.attributes == null) {
526
            return;
527
        }
528
        final int size = this.attributes.size();
529
        for (int i = 0; i < size; i += 2) {
530
            writer.append(' ');
531
            writer.append(this.attributes.get(i));
532
            writer.append('=');
533
            writer.append('\"');
534
            writer.append(escapeAttributeValue(this.attributes.get(i + 1)));
535
            writer.append('\"');
536
        }
537
    }
538
 
539
    void writeAttributes(BufferedOutputStream out) throws IOException {
540
        final int size = this.attributes.size();
541
        for (int i = 0; i < size; i += 2) {
542
            Document.writeUTF(out, this.attributes.get(i));
543
            Document.writeUTF(out, this.attributes.get(i + 1));
544
        }
545
    }
546
 
547
    private String escapeAttributeValue(String string) {
548
        StringBuilder b = new StringBuilder(string.length());
549
        int length = string.length();
550
        for (int i = 0; i < length; i++) {
551
            char c = string.charAt(i);
552
            if (c == '\n') {
553
                b.append('\\');
554
                b.append('n');
555
            } else if (c == '\r') {
556
                b.append('\\');
557
                b.append('r');
558
            } else if (c == '\t') {
559
                b.append('\\');
560
                b.append('t');
561
            } else if (c == '\\') {
562
                b.append('\\');
563
                b.append('\\');
564
            } else if (c == '"') {
565
                b.append('\\');
566
                b.append('"');
567
            } else {
568
                b.append(c);
569
            }
570
        }
571
        return b.toString();
572
    }
573
 
574
    void addAttributeNoCheck(String name, String value) {
575
        this.attributes.add(name);
576
        this.attributes.add(value);
577
    }
578
 
579
    void addChildNoCheck(Element child) {
580
        if (child == null) {
581
            throw new IllegalArgumentException("null child");
582
        }
583
        this.children.add(child);
584
    }
585
 
586
}