|
10 | 10 | *******************************************************************************/ |
11 | 11 | package com.avaloq.tools.ddk.xtext.naming; |
12 | 12 |
|
| 13 | +import java.io.Externalizable; |
| 14 | +import java.io.IOException; |
| 15 | +import java.io.ObjectInput; |
| 16 | +import java.io.ObjectOutput; |
13 | 17 | import java.lang.ref.WeakReference; |
14 | 18 | import java.util.ArrayList; |
15 | 19 | import java.util.Arrays; |
16 | 20 | import java.util.Collection; |
17 | 21 | import java.util.List; |
18 | 22 | import java.util.Set; |
19 | 23 |
|
| 24 | +import org.eclipse.emf.common.util.URI; |
20 | 25 | import org.eclipse.xtext.naming.QualifiedName; |
21 | 26 |
|
22 | 27 | import com.avaloq.tools.ddk.annotations.SuppressFBWarnings; |
|
43 | 48 | * @param <T> |
44 | 49 | * value element type |
45 | 50 | */ |
46 | | -public class QualifiedNameSegmentTreeLookup<T> implements QualifiedNameLookup<T> { |
| 51 | +public class QualifiedNameSegmentTreeLookup<T> implements QualifiedNameLookup<T>, Externalizable { |
| 52 | + private static final long serialVersionUID = 1L; |
47 | 53 |
|
48 | 54 | /** |
49 | 55 | * A node in the tree representing a single segment of a qualified name. |
50 | 56 | */ |
51 | 57 | // CHECKSTYLE:CHECK-OFF ParameterAssignmentCheck |
52 | 58 | // CHECKSTYLE:CHECK-OFF FinalParametersCheck |
53 | 59 | // CHECKSTYLE:CHECK-OFF VisibilityModifier |
54 | | - private static class SegmentNode { |
| 60 | + private static class SegmentNode implements Externalizable { |
| 61 | + private static final long serialVersionUID = 1L; |
55 | 62 |
|
56 | 63 | protected static final int DEFAULT_CHILD_CAPACITY = 4; |
57 | 64 |
|
58 | | - private final String segment; |
| 65 | + private String segment; |
59 | 66 | protected Object[] values; |
60 | 67 | protected List<SegmentNode> children; |
61 | 68 |
|
| 69 | + public SegmentNode() { |
| 70 | + // required by externalization |
| 71 | + } |
| 72 | + |
62 | 73 | /** |
63 | 74 | * Creates a new node for the given qualified name segment. |
64 | 75 | * |
@@ -306,12 +317,58 @@ protected int binarySearch(final List<SegmentNode> list, final String key) { |
306 | 317 | public String toString() { |
307 | 318 | return "Node(" + segment + ")"; //$NON-NLS-1$ //$NON-NLS-2$ |
308 | 319 | } |
| 320 | + |
| 321 | + @Override |
| 322 | + public void writeExternal(final ObjectOutput out) throws IOException { |
| 323 | + out.writeObject(segment); |
| 324 | + if (values == null) { |
| 325 | + out.writeInt(0); |
| 326 | + } else { |
| 327 | + out.writeInt(values.length); |
| 328 | + for (Object value : values) { |
| 329 | + if (value instanceof URI uri) { |
| 330 | + out.writeBoolean(true); |
| 331 | + out.writeObject(uri.toString()); |
| 332 | + } else { |
| 333 | + out.writeBoolean(false); |
| 334 | + out.writeObject(value); |
| 335 | + } |
| 336 | + } |
| 337 | + } |
| 338 | + out.writeObject(children); |
| 339 | + } |
| 340 | + |
| 341 | + @SuppressWarnings("unchecked") |
| 342 | + @Override |
| 343 | + public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException { |
| 344 | + segment = (String) in.readObject(); |
| 345 | + int valueLength = in.readInt(); |
| 346 | + if (valueLength > 0) { |
| 347 | + values = new Object[valueLength]; |
| 348 | + for (int i = 0; i < valueLength; i++) { |
| 349 | + Boolean convertToURI = in.readBoolean(); |
| 350 | + Object readBack = in.readObject(); |
| 351 | + if (convertToURI) { // do not test with instanceof: if convertToURI is true but the object read is not a String |
| 352 | + // then we want the CCE because something went wrong |
| 353 | + values[i] = URI.createURI((String) readBack); |
| 354 | + } else { |
| 355 | + values[i] = readBack; |
| 356 | + } |
| 357 | + } |
| 358 | + } |
| 359 | + children = (List<SegmentNode>) in.readObject(); |
| 360 | + } |
309 | 361 | } |
310 | 362 |
|
311 | 363 | /** |
312 | 364 | * Subclass which implements value sharing between nodes and child nodes to reduce the memory footprint. |
313 | 365 | */ |
314 | 366 | private static class ValueSharingSegmentNode extends SegmentNode { |
| 367 | + private static final long serialVersionUID = 1L; |
| 368 | + |
| 369 | + public ValueSharingSegmentNode() { |
| 370 | + // required by externalization |
| 371 | + } |
315 | 372 |
|
316 | 373 | ValueSharingSegmentNode(final String segment) { |
317 | 374 | super(segment); |
@@ -384,7 +441,7 @@ private abstract static class Visitor { |
384 | 441 | public abstract void visit(SegmentNode node); |
385 | 442 | } |
386 | 443 |
|
387 | | - private final SegmentNode root; |
| 444 | + private SegmentNode root; |
388 | 445 |
|
389 | 446 | private long size; |
390 | 447 | private long hits; |
@@ -417,6 +474,10 @@ public static <K, V> QualifiedNameLookup<V> createNameLookupCache(final String n |
417 | 474 | return cache; |
418 | 475 | } |
419 | 476 |
|
| 477 | + public QualifiedNameSegmentTreeLookup() { |
| 478 | + // required by externalisation |
| 479 | + } |
| 480 | + |
420 | 481 | public QualifiedNameSegmentTreeLookup(final Class<T> elementType, final boolean shareValues) { // NOPMD |
421 | 482 | root = shareValues ? new ValueSharingSegmentNode("") : new SegmentNode(""); //$NON-NLS-1$ //$NON-NLS-2$ |
422 | 483 | init(); |
@@ -518,4 +579,31 @@ public CacheStatistics getStatistics() { |
518 | 579 | return new CacheStatistics(size, hits, misses); |
519 | 580 | } |
520 | 581 |
|
| 582 | + @Override |
| 583 | + public void writeExternal(final ObjectOutput out) throws IOException { |
| 584 | + out.writeLong(size); |
| 585 | + out.writeObject(root); |
| 586 | + } |
| 587 | + |
| 588 | + @Override |
| 589 | + @SuppressFBWarnings("AT_NONATOMIC_64BIT_PRIMITIVE") |
| 590 | + public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException { |
| 591 | + size = in.readLong(); |
| 592 | + root = (SegmentNode) in.readObject(); |
| 593 | + } |
| 594 | + |
| 595 | + @Override |
| 596 | + @SuppressFBWarnings("AT_NONATOMIC_64BIT_PRIMITIVE") |
| 597 | + public void initializeFrom(final QualifiedNameLookup<T> source) { |
| 598 | + if (size > 0) { |
| 599 | + throw new IllegalStateException("must not initialize non-empty object"); //$NON-NLS-1$ |
| 600 | + } |
| 601 | + if (!(source instanceof QualifiedNameSegmentTreeLookup<T> other)) { |
| 602 | + throw new UnsupportedOperationException("cannot copy from " + source.getClass()); //$NON-NLS-1$ |
| 603 | + } else { |
| 604 | + this.size = other.size; |
| 605 | + this.root = other.root; |
| 606 | + } |
| 607 | + } |
| 608 | + |
521 | 609 | } |
0 commit comments