Skip to content

Commit 58cda7e

Browse files
committed
feat: QualifiedNameLookup extensions
- allow one object to be initialized from another - make QualifiedNameSegmentTreeLookup externalizable
1 parent b6c678c commit 58cda7e

File tree

5 files changed

+143
-7
lines changed

5 files changed

+143
-7
lines changed

com.avaloq.tools.ddk.xtext.test/META-INF/MANIFEST.MF

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,21 @@ Require-Bundle: com.avaloq.tools.ddk.xtext,
2727
org.eclipse.pde.core,
2828
org.eclipse.xtext.xbase.lib,
2929
org.eclipse.emf.ecore
30-
Import-Package: org.slf4j, org.apache.log4j,
31-
com.avaloq.tools.ddk.check.runtime.test.core,
30+
Import-Package: com.avaloq.tools.ddk.check.runtime.test.core,
3231
com.avaloq.tools.ddk.check.test.core,
3332
com.avaloq.tools.ddk.check.test.runtime.tests,
3433
com.avaloq.tools.ddk.check.ui.test,
3534
com.avaloq.tools.ddk.checkcfg.test,
3635
com.avaloq.tools.ddk.checkcfg.ui.test,
36+
com.avaloq.tools.ddk.sample.helloworld.test,
3737
com.avaloq.tools.ddk.typesystem.test,
3838
com.avaloq.tools.ddk.xtext.generator.test.generator,
3939
com.avaloq.tools.ddk.xtext.generator.util,
4040
com.avaloq.tools.ddk.xtext.test.export,
4141
com.avaloq.tools.ddk.xtext.test.format,
4242
com.avaloq.tools.ddk.xtext.ui.test,
43-
com.avaloq.tools.ddk.sample.helloworld.test
43+
org.apache.log4j,
44+
org.slf4j
4445
Export-Package: com.avaloq.tools.ddk.xtext,
4546
com.avaloq.tools.ddk.xtext.formatter,
4647
com.avaloq.tools.ddk.xtext.formatter.services,

com.avaloq.tools.ddk.xtext.test/src/com/avaloq/tools/ddk/xtext/naming/QualifiedNameSegmentTreeLookupTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,14 @@
1313
import static org.junit.Assert.assertEquals;
1414
import static org.junit.Assert.assertNull;
1515

16+
import java.io.ByteArrayInputStream;
17+
import java.io.ByteArrayOutputStream;
18+
import java.io.IOException;
19+
import java.io.ObjectInputStream;
20+
import java.io.ObjectOutputStream;
1621
import java.util.Collection;
1722
import java.util.Collections;
23+
import java.util.List;
1824

1925
import org.eclipse.emf.common.util.URI;
2026
import org.eclipse.xtext.naming.QualifiedName;
@@ -144,6 +150,30 @@ public void testOutOfOrderInsertion() {
144150
assertContentEquals(value2, lookup.get(name2));
145151
}
146152

153+
@Test
154+
public void testLoadStore() throws IOException, ClassNotFoundException {
155+
List<QualifiedName> nameList = List.of(name("foo"), name("foo.bar"), name("bar"));
156+
for (QualifiedName qn : nameList) {
157+
lookup.put(qn, uri(qn));
158+
}
159+
ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream();
160+
ObjectOutputStream objectOutStream = new ObjectOutputStream(byteOutStream);
161+
objectOutStream.writeObject(lookup);
162+
byte[] data = byteOutStream.toByteArray();
163+
164+
ObjectInputStream inStream = new ObjectInputStream(new ByteArrayInputStream(data));
165+
Object readBack = inStream.readObject();
166+
167+
assertEquals(readBack.getClass(), lookup.getClass());
168+
169+
@SuppressWarnings("unchecked")
170+
QualifiedNameLookup<URI> readBackLookup = (QualifiedNameLookup<URI>) readBack;
171+
172+
for (QualifiedName qn : nameList) {
173+
assertEquals(lookup.get(qn), readBackLookup.get(qn));
174+
}
175+
}
176+
147177
private QualifiedName name(final String str) {
148178
return QualifiedNames.safeQualifiedName(str);
149179
}

com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/naming/QualifiedNameLookup.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,16 @@ public interface QualifiedNameLookup<T> extends ICache<QualifiedName, T> {
9393
*/
9494
void clear();
9595

96+
/**
97+
* Initializes this QualifiedNameLookup from source.
98+
* Performs a shallow copy, i.e. values are shared between the objects after the operation.
99+
* Only allowed if this object is empty.
100+
*
101+
* @param source
102+
* QualifiedNameLookup to initialize from
103+
* @throws IllegalStateException
104+
* if this object is not empty.
105+
*/
106+
void initializeFrom(QualifiedNameLookup<T> source);
107+
96108
}

com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/naming/QualifiedNameSegmentTreeLookup.java

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,18 @@
1010
*******************************************************************************/
1111
package com.avaloq.tools.ddk.xtext.naming;
1212

13+
import java.io.Externalizable;
14+
import java.io.IOException;
15+
import java.io.ObjectInput;
16+
import java.io.ObjectOutput;
1317
import java.lang.ref.WeakReference;
1418
import java.util.ArrayList;
1519
import java.util.Arrays;
1620
import java.util.Collection;
1721
import java.util.List;
1822
import java.util.Set;
1923

24+
import org.eclipse.emf.common.util.URI;
2025
import org.eclipse.xtext.naming.QualifiedName;
2126

2227
import com.avaloq.tools.ddk.annotations.SuppressFBWarnings;
@@ -43,22 +48,28 @@
4348
* @param <T>
4449
* value element type
4550
*/
46-
public class QualifiedNameSegmentTreeLookup<T> implements QualifiedNameLookup<T> {
51+
public class QualifiedNameSegmentTreeLookup<T> implements QualifiedNameLookup<T>, Externalizable {
52+
private static final long serialVersionUID = 1L;
4753

4854
/**
4955
* A node in the tree representing a single segment of a qualified name.
5056
*/
5157
// CHECKSTYLE:CHECK-OFF ParameterAssignmentCheck
5258
// CHECKSTYLE:CHECK-OFF FinalParametersCheck
5359
// CHECKSTYLE:CHECK-OFF VisibilityModifier
54-
private static class SegmentNode {
60+
private static class SegmentNode implements Externalizable {
61+
private static final long serialVersionUID = 1L;
5562

5663
protected static final int DEFAULT_CHILD_CAPACITY = 4;
5764

58-
private final String segment;
65+
private String segment;
5966
protected Object[] values;
6067
protected List<SegmentNode> children;
6168

69+
public SegmentNode() {
70+
// required by externalization
71+
}
72+
6273
/**
6374
* Creates a new node for the given qualified name segment.
6475
*
@@ -306,12 +317,58 @@ protected int binarySearch(final List<SegmentNode> list, final String key) {
306317
public String toString() {
307318
return "Node(" + segment + ")"; //$NON-NLS-1$ //$NON-NLS-2$
308319
}
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+
}
309361
}
310362

311363
/**
312364
* Subclass which implements value sharing between nodes and child nodes to reduce the memory footprint.
313365
*/
314366
private static class ValueSharingSegmentNode extends SegmentNode {
367+
private static final long serialVersionUID = 1L;
368+
369+
public ValueSharingSegmentNode() {
370+
// required by externalization
371+
}
315372

316373
ValueSharingSegmentNode(final String segment) {
317374
super(segment);
@@ -384,7 +441,7 @@ private abstract static class Visitor {
384441
public abstract void visit(SegmentNode node);
385442
}
386443

387-
private final SegmentNode root;
444+
private SegmentNode root;
388445

389446
private long size;
390447
private long hits;
@@ -417,6 +474,10 @@ public static <K, V> QualifiedNameLookup<V> createNameLookupCache(final String n
417474
return cache;
418475
}
419476

477+
public QualifiedNameSegmentTreeLookup() {
478+
// required by externalisation
479+
}
480+
420481
public QualifiedNameSegmentTreeLookup(final Class<T> elementType, final boolean shareValues) { // NOPMD
421482
root = shareValues ? new ValueSharingSegmentNode("") : new SegmentNode(""); //$NON-NLS-1$ //$NON-NLS-2$
422483
init();
@@ -518,4 +579,31 @@ public CacheStatistics getStatistics() {
518579
return new CacheStatistics(size, hits, misses);
519580
}
520581

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+
521609
}

com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/naming/TreeSetLookup.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,9 @@ public CacheStatistics getStatistics() {
114114
return new CacheStatistics(lookupMap.size(), hits, misses);
115115
}
116116

117+
@Override
118+
public void initializeFrom(final QualifiedNameLookup<T> source) {
119+
throw new UnsupportedOperationException();
120+
}
121+
117122
}

0 commit comments

Comments
 (0)