Skip to content

Commit 0392935

Browse files
committed
[bugfix] Fix an issue with confusion between wrapping and typing in the serializer
Closes eXist-db/exist#5910
1 parent fab571c commit 0392935

File tree

6 files changed

+279
-3
lines changed

6 files changed

+279
-3
lines changed

exist-core/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,7 @@
757757
<include>src/main/java/org/exist/storage/io/VariableByteFilterInputStream.java</include>
758758
<include>src/main/java/org/exist/storage/io/VariableByteFilterOutputStream.java</include>
759759
<include>src/main/java/org/exist/storage/io/VariableByteOutput.java</include>
760+
<include>src/test/java/org/exist/storage/serializers/NativeSerializerTest.java</include>
760761
<include>src/main/java/org/exist/util/ByteOrderMark.java</include>
761762
<include>src/main/java/org/exist/util/JREUtil.java</include>
762763
<include>src/main/java/org/exist/util/OSUtil.java</include>
@@ -1079,6 +1080,7 @@
10791080
<include>src/main/java/org/exist/xquery/NamedFunctionReference.java</include>
10801081
<include>src/main/java/org/exist/xquery/Optimizer.java</include>
10811082
<include>src/main/java/org/exist/xquery/PerformanceStatsImpl.java</include>
1083+
<include>src/test/java/org/exist/xquery/RestBinariesTest.java</include>
10821084
<include>src/main/java/org/exist/xquery/TryCatchExpression.java</include>
10831085
<include>src/main/java/org/exist/xquery/UserDefinedFunction.java</include>
10841086
<include>src/test/java/org/exist/xquery/WindowClauseTest.java</include>
@@ -1566,6 +1568,7 @@
15661568
<exclude>src/main/java/org/exist/storage/lock/ManagedLockGroupDocumentLock.java</exclude>
15671569
<exclude>src/main/java/org/exist/storage/lock/ManagedSingleLockDocumentLock.java</exclude>
15681570
<exclude>src/main/java/org/exist/storage/recovery/RecoveryManager.java</exclude>
1571+
<exclude>src/test/java/org/exist/storage/serializers/NativeSerializerTest.java</exclude>
15691572
<exclude>src/main/java/org/exist/storage/serializers/Serializer.java</exclude>
15701573
<exclude>src/test/resources-filtered/org/exist/storage/statistics/conf.xml</exclude>
15711574
<exclude>src/main/java/org/exist/storage/sync/SyncTask.java</exclude>
@@ -1687,6 +1690,7 @@
16871690
<exclude>src/main/java/org/exist/xquery/NameTest.java</exclude>
16881691
<exclude>src/main/java/org/exist/xquery/Optimizer.java</exclude>
16891692
<exclude>src/main/java/org/exist/xquery/PerformanceStatsImpl.java</exclude>
1693+
<exclude>src/test/java/org/exist/xquery/RestBinariesTest.java</exclude>
16901694
<exclude>src/main/java/org/exist/xquery/TryCatchExpression.java</exclude>
16911695
<exclude>src/main/java/org/exist/xquery/UserDefinedFunction.java</exclude>
16921696
<exclude>src/test/java/org/exist/xquery/WatchdogTest.java</exclude>

exist-core/src/main/java/org/exist/storage/serializers/Serializer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,7 +1205,7 @@ private void itemToSAX(final Item item, final boolean typed, final boolean wrap)
12051205
serializeToReceiver(node, false);
12061206
}
12071207
} else {
1208-
if (wrap) {
1208+
if (typed) {
12091209
final AttrList attrs = new AttrList();
12101210
attrs.addAttribute(ATTR_TYPE_QNAME, Type.getTypeName(item.getType()));
12111211
receiver.startElement(ELEM_VALUE_QNAME, attrs);
@@ -1215,7 +1215,7 @@ private void itemToSAX(final Item item, final boolean typed, final boolean wrap)
12151215
} catch (final XPathException e) {
12161216
throw new SAXException(e.getMessage(), e);
12171217
}
1218-
if (wrap) {
1218+
if (typed) {
12191219
receiver.endElement(ELEM_VALUE_QNAME);
12201220
}
12211221
}
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
/*
2+
* Elemental
3+
* Copyright (C) 2024, Evolved Binary Ltd
4+
*
5+
6+
* https://www.evolvedbinary.com | https://www.elemental.xyz
7+
*
8+
* This library is free software; you can redistribute it and/or
9+
* modify it under the terms of the GNU Lesser General Public
10+
* License as published by the Free Software Foundation; version 2.1.
11+
*
12+
* This library is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
* Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public
18+
* License along with this library; if not, write to the Free Software
19+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20+
*/
21+
package org.exist.storage.serializers;
22+
23+
import org.easymock.Capture;
24+
import org.exist.dom.memtree.MemTreeBuilder;
25+
import org.exist.util.Configuration;
26+
import org.exist.util.serializer.SAXSerializer;
27+
import org.exist.xquery.XPathException;
28+
import org.exist.xquery.value.*;
29+
import org.junit.jupiter.params.ParameterizedTest;
30+
import org.junit.jupiter.params.provider.CsvSource;
31+
import org.xml.sax.SAXException;
32+
import org.xmlunit.matchers.CompareMatcher;
33+
34+
import java.io.IOException;
35+
import java.io.StringWriter;
36+
import java.io.Writer;
37+
import java.util.ArrayList;
38+
import java.util.List;
39+
40+
import static org.easymock.EasyMock.*;
41+
import static org.exist.Namespaces.EXIST_NS;
42+
import static org.exist.Namespaces.EXIST_NS_PREFIX;
43+
import static org.hamcrest.MatcherAssert.assertThat;
44+
import static org.junit.jupiter.api.Assertions.assertEquals;
45+
46+
public class NativeSerializerTest {
47+
48+
private static final int DEFAULT_START = 1;
49+
private static final long DEFAULT_COMPILATION_TIME = 0;
50+
private static final long DEFAULT_EXECUTION_TIME = 0;
51+
52+
public enum Wrapped {
53+
NOT_WRAPPED,
54+
WRAPPED;
55+
}
56+
57+
public enum Typed {
58+
NOT_TYPED,
59+
TYPED;
60+
}
61+
62+
@ParameterizedTest
63+
@CsvSource({"NOT_WRAPPED,NOT_TYPED", "WRAPPED,NOT_TYPED", "NOT_WRAPPED,TYPED", "WRAPPED,TYPED"})
64+
public void serializeInteger(final Wrapped wrapped, final Typed typed) throws SAXException, XPathException, IOException {
65+
final Sequence sequence = new ValueSequence();
66+
sequence.add(new IntegerValue(123));
67+
sequence.add(new IntegerValue(456));
68+
69+
assertSerialize(sequence, wrapped == Wrapped.WRAPPED, typed == Typed.TYPED);
70+
}
71+
72+
@ParameterizedTest
73+
@CsvSource({"NOT_WRAPPED,NOT_TYPED", "WRAPPED,NOT_TYPED", "NOT_WRAPPED,TYPED", "WRAPPED,TYPED"})
74+
public void serializeText(final Wrapped wrapped, final Typed typed) throws SAXException, XPathException, IOException {
75+
final MemTreeBuilder builder = new MemTreeBuilder();
76+
builder.startDocument();
77+
final int text1Id = builder.characters("hello");
78+
builder.comment("separator");
79+
final int text2Id = builder.characters("world");
80+
builder.endDocument();
81+
82+
final Sequence sequence = new ValueSequence();
83+
sequence.add(builder.getDocument().getNode(text1Id));
84+
sequence.add(builder.getDocument().getNode(text2Id));
85+
86+
assertSerialize(sequence, wrapped == Wrapped.WRAPPED, typed == Typed.TYPED);
87+
}
88+
89+
@ParameterizedTest
90+
@CsvSource({"NOT_WRAPPED,NOT_TYPED", "WRAPPED,NOT_TYPED", "NOT_WRAPPED,TYPED", "WRAPPED,TYPED"})
91+
public void serializeMixed(final Wrapped wrapped, final Typed typed) throws SAXException, XPathException, IOException {
92+
final MemTreeBuilder builder = new MemTreeBuilder();
93+
builder.startDocument();
94+
final int text1Id = builder.characters("hello");
95+
builder.comment("separator");
96+
final int text2Id = builder.characters("world");
97+
builder.endDocument();
98+
99+
final Sequence sequence = new ValueSequence();
100+
sequence.add(new IntegerValue(123));
101+
sequence.add(builder.getDocument().getNode(text1Id));
102+
sequence.add(new StringValue("Some string"));
103+
sequence.add(builder.getDocument().getNode(text2Id));
104+
sequence.add(new StringValue("Another string"));
105+
106+
assertSerialize(sequence, wrapped == Wrapped.WRAPPED, typed == Typed.TYPED);
107+
}
108+
109+
private void assertSerialize(final Sequence sequence, final boolean wrapped, final boolean typed) throws IOException, SAXException, XPathException {
110+
final String expected;
111+
if (wrapped && typed) {
112+
final List<String> items = type(sequence, false);
113+
expected = wrap(items);
114+
} else if (wrapped) {
115+
final List<String> items = toStrings(sequence);
116+
expected = wrap(items);
117+
} else if (typed) {
118+
final List<String> items = type(sequence, true);
119+
expected = join(items, "");
120+
} else {
121+
expected = join(sequence, "");
122+
}
123+
124+
final String serialized = serialize(sequence, wrapped, typed);
125+
126+
if (wrapped) {
127+
assertThat(serialized, CompareMatcher.isIdenticalTo(expected));
128+
} else if (typed) {
129+
assertThat(wrapInElement(serialized), CompareMatcher.isIdenticalTo(wrapInElement(expected)));
130+
} else {
131+
assertEquals(expected, serialized);
132+
}
133+
}
134+
135+
private static String join(final List<String> strings, final String separator) {
136+
return String.join(separator, strings);
137+
}
138+
139+
private static String join(final Sequence sequence, final String separator) throws XPathException {
140+
return join(toStrings(sequence), separator);
141+
}
142+
143+
private static List<String> toStrings(final Sequence sequence) throws XPathException {
144+
final List<String> strings = new ArrayList<>(sequence.getItemCount());
145+
for (final SequenceIterator it = sequence.iterate(); it.hasNext();) {
146+
final Item item = it.nextItem();
147+
strings.add(item.getStringValue());
148+
}
149+
return strings;
150+
}
151+
152+
private static String wrapInElement(final String string) {
153+
return "<stub-element>" + string + "</stub-element>";
154+
}
155+
156+
private static List<String> type(final Sequence sequence, final boolean explicitNamespace) throws XPathException {
157+
final String namespace;
158+
if (explicitNamespace) {
159+
namespace = " xmlns:" + EXIST_NS_PREFIX + "=\"" + EXIST_NS + "\"";
160+
} else {
161+
namespace = "";
162+
}
163+
164+
final List<String> typed = new ArrayList<>(sequence.getItemCount());
165+
for (final SequenceIterator it = sequence.iterate(); it.hasNext();) {
166+
final Item item = it.nextItem();
167+
if (item.getType() == Type.TEXT) {
168+
typed.add("<exist:text" + namespace + ">" + item.getStringValue() + "</exist:text>");
169+
} else {
170+
typed.add("<exist:value" + namespace + " exist:type=\"" + Type.getTypeName(item.getType()) + "\">" + item.getStringValue() + "</exist:value>");
171+
}
172+
}
173+
return typed;
174+
}
175+
176+
private static String wrap(final List<String> items) {
177+
final StringBuilder builder = new StringBuilder()
178+
.append("<exist:result xmlns:exist=\"http://exist.sourceforge.net/NS/exist\" exist:hits=\"")
179+
.append(items.size())
180+
.append("\" exist:start=\"")
181+
.append(DEFAULT_START)
182+
.append("\" exist:count=\"")
183+
.append(items.size())
184+
.append("\" exist:compilation-time=\"")
185+
.append(DEFAULT_COMPILATION_TIME)
186+
.append("\" exist:execution-time=\"")
187+
.append(DEFAULT_EXECUTION_TIME)
188+
.append("\">");
189+
190+
for (final String item : items) {
191+
builder.append(item);
192+
}
193+
194+
return builder.append("</exist:result>").toString();
195+
}
196+
197+
private String serialize(final Sequence sequence, final boolean wrapped, final boolean typed) throws IOException, SAXException {
198+
final int count = sequence.getItemCount();
199+
200+
final Configuration mockConfiguration = mock(Configuration.class);
201+
expect(mockConfiguration.getProperty(anyString())).andReturn(null).anyTimes();
202+
final Capture<Object> capturePropertyDefault = newCapture();
203+
expect(mockConfiguration.getProperty(anyString(), capture(capturePropertyDefault))).andAnswer(() -> capturePropertyDefault.getValue());
204+
205+
replay(mockConfiguration);
206+
207+
final Serializer serializer = new NativeSerializer(null, mockConfiguration);
208+
try (final Writer writer = new StringWriter()) {
209+
final SAXSerializer saxSerializer = new SAXSerializer(writer, null);
210+
serializer.setSAXHandlers(saxSerializer, saxSerializer);
211+
serializer.toSAX(sequence, DEFAULT_START, count, wrapped, typed, DEFAULT_COMPILATION_TIME, DEFAULT_EXECUTION_TIME);
212+
return writer.toString();
213+
214+
} finally {
215+
verify(mockConfiguration);
216+
}
217+
}
218+
}

exist-core/src/test/java/org/exist/xquery/RestBinariesTest.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,28 @@
11
/*
2+
* Elemental
3+
* Copyright (C) 2024, Evolved Binary Ltd
4+
*
5+
6+
* https://www.evolvedbinary.com | https://www.elemental.xyz
7+
*
8+
* This library is free software; you can redistribute it and/or
9+
* modify it under the terms of the GNU Lesser General Public
10+
* License as published by the Free Software Foundation; version 2.1.
11+
*
12+
* This library is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
* Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public
18+
* License along with this library; if not, write to the Free Software
19+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20+
*
21+
* NOTE: Parts of this file contain code from 'The eXist-db Authors'.
22+
* The original license header is included below.
23+
*
24+
* =====================================================================
25+
*
226
* eXist-db Open Source Native XML Database
327
* Copyright (C) 2001 The eXist-db Authors
428
*
@@ -19,7 +43,6 @@
1943
* License along with this library; if not, write to the Free Software
2044
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2145
*/
22-
2346
package org.exist.xquery;
2447

2548
import org.apache.commons.codec.binary.Base64;
@@ -32,6 +55,7 @@
3255
import org.apache.http.entity.ContentType;
3356
import org.exist.http.jaxb.Query;
3457
import org.exist.http.jaxb.Result;
58+
import org.exist.http.jaxb.YesNo;
3559
import org.exist.test.ExistWebServer;
3660
import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
3761
import org.exist.xmldb.XmldbURI;
@@ -51,6 +75,7 @@
5175
import static org.apache.http.HttpStatus.SC_OK;
5276
import static org.exist.TestUtils.ADMIN_DB_PWD;
5377
import static org.exist.TestUtils.ADMIN_DB_USER;
78+
import static org.exist.http.jaxb.YesNo.YES;
5479
import static org.junit.Assert.assertArrayEquals;
5580

5681
/**
@@ -156,6 +181,7 @@ protected QueryResultAccessor<Result, Exception> executeXQuery(final String xque
156181
private HttpResponse postXquery(final String xquery) throws JAXBException, IOException {
157182
final Query query = new Query();
158183
query.setText(xquery);
184+
query.setTyped(YES);
159185

160186
final JAXBContext jaxbContext = JAXBContext.newInstance("org.exist.http.jaxb");
161187
final Marshaller marshaller = jaxbContext.createMarshaller();

extensions/modules/file/pom.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@
218218
<include>src/test/resources/standalone-webapp/WEB-INF/web.xml</include>
219219
<include>src/main/java/org/exist/xquery/modules/file/FileModuleHelper.java</include>
220220
<include>src/main/java/org/exist/xquery/modules/file/FileReadUnicode.java</include>
221+
<include>src/test/java/org/exist/xquery/modules/file/RestBinariesTest.java</include>
221222
<include>src/main/java/org/exist/xquery/modules/file/Sync.java</include>
222223
</includes>
223224
</licenseSet>
@@ -234,6 +235,7 @@
234235
<exclude>src/test/resources/standalone-webapp/WEB-INF/web.xml</exclude>
235236
<exclude>src/main/java/org/exist/xquery/modules/file/FileModuleHelper.java</exclude>
236237
<exclude>src/main/java/org/exist/xquery/modules/file/FileReadUnicode.java</exclude>
238+
<exclude>src/test/java/org/exist/xquery/modules/file/RestBinariesTest.java</exclude>
237239
<exclude>src/main/java/org/exist/xquery/modules/file/Sync.java</exclude>
238240
</excludes>
239241
</licenseSet>

extensions/modules/file/src/test/java/org/exist/xquery/modules/file/RestBinariesTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,28 @@
11
/*
2+
* Elemental
3+
* Copyright (C) 2024, Evolved Binary Ltd
4+
*
5+
6+
* https://www.evolvedbinary.com | https://www.elemental.xyz
7+
*
8+
* This library is free software; you can redistribute it and/or
9+
* modify it under the terms of the GNU Lesser General Public
10+
* License as published by the Free Software Foundation; version 2.1.
11+
*
12+
* This library is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
* Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public
18+
* License along with this library; if not, write to the Free Software
19+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20+
*
21+
* NOTE: Parts of this file contain code from 'The eXist-db Authors'.
22+
* The original license header is included below.
23+
*
24+
* =====================================================================
25+
*
226
* eXist-db Open Source Native XML Database
327
* Copyright (C) 2001 The eXist-db Authors
428
*
@@ -51,6 +75,7 @@
5175
import static org.apache.http.HttpStatus.SC_OK;
5276
import static org.exist.TestUtils.ADMIN_DB_PWD;
5377
import static org.exist.TestUtils.ADMIN_DB_USER;
78+
import static org.exist.http.jaxb.YesNo.YES;
5479
import static org.junit.Assert.assertArrayEquals;
5580

5681
/**
@@ -163,6 +188,7 @@ protected QueryResultAccessor<Result, Exception> executeXQuery(final String xque
163188
private HttpResponse postXquery(final String xquery) throws JAXBException, IOException {
164189
final Query query = new Query();
165190
query.setText(xquery);
191+
query.setTyped(YES);
166192

167193
final JAXBContext jaxbContext = JAXBContext.newInstance("org.exist.http.jaxb");
168194
final Marshaller marshaller = jaxbContext.createMarshaller();

0 commit comments

Comments
 (0)