Skip to content

Commit 46c7e46

Browse files
committed
add tests + fix XMLWriter
fix README.md links for core.loaders.maven
1 parent a3e2018 commit 46c7e46

File tree

5 files changed

+166
-41
lines changed

5 files changed

+166
-41
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ is recommended for better performances on disk operations (detection of physical
2626
- core [![Maven Central](https://img.shields.io/maven-central/v/net.lecousin/core.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22net.lecousin%22%20AND%20a%3A%22core%22)
2727
[![Javadoc](https://img.shields.io/badge/javadoc-0.16.1-brightgreen.svg)](https://www.javadoc.io/doc/net.lecousin/core/0.16.1)
2828

29-
- core.loaders.maven [![Maven Central](https://img.shields.io/maven-central/v/net.lecousin/core.loaders.maven.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22net.lecousin%22%20AND%20a%3A%22core.loaders.maven%22)
30-
[![Javadoc](https://img.shields.io/badge/javadoc-0.16.1-brightgreen.svg)](https://www.javadoc.io/doc/net.lecousin/core.loaders.maven/0.16.1)
29+
- core.loaders.maven [![Maven Central](https://img.shields.io/maven-central/v/net.lecousin/core.loaders.maven.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22net.lecousin.core.loaders%22%20AND%20a%3A%22maven%22)
30+
[![Javadoc](https://img.shields.io/badge/javadoc-0.16.1-brightgreen.svg)](https://www.javadoc.io/doc/net.lecousin.core.loaders/maven/0.16.1)
3131

3232
- core.javaee [![Maven Central](https://img.shields.io/maven-central/v/net.lecousin/core.javaee.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22net.lecousin%22%20AND%20a%3A%22core.javaee%22)
3333
[![Javadoc](https://img.shields.io/badge/javadoc-0.16.1-brightgreen.svg)](https://www.javadoc.io/doc/net.lecousin/core.javaee/0.16.1)

net.lecousin.core/src/main/java/net/lecousin/framework/io/text/BufferedWritableCharacterStream.java

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -267,22 +267,18 @@ public IAsync<IOException> writeAsync(char[] c, int off, int len) {
267267
}
268268

269269
private void writeAsync(char[] c, int off, int len, Async<IOException> result) {
270-
new Task.Cpu<Void, NoException>("BufferedWritableCharacterStream.writeAsync", output.getPriority()) {
271-
@Override
272-
public Void run() {
273-
int l = len > buffer.length - pos ? buffer.length - pos : len;
274-
System.arraycopy(c, off, buffer, pos, l);
275-
pos += l;
276-
if (l == len) {
277-
if (pos == buffer.length)
278-
flushBufferAsync().onDone(result);
279-
else
280-
result.unblock();
281-
return null;
282-
}
283-
flushBufferAsync().onDone(() -> writeAsync(c, off + l, len - l, result), result);
284-
return null;
285-
}
286-
}.start();
270+
int l = len > buffer.length - pos ? buffer.length - pos : len;
271+
System.arraycopy(c, off, buffer, pos, l);
272+
pos += l;
273+
if (l == len) {
274+
if (pos == buffer.length)
275+
flushBufferAsync().onDone(result);
276+
else
277+
result.unblock();
278+
return;
279+
}
280+
flushBufferAsync().thenStart(new Task.Cpu.FromRunnable(
281+
"BufferedWritableCharacterStream.writeAsync", output.getPriority(),
282+
() -> writeAsync(c, off + l, len - l, result)), result);
287283
}
288284
}

net.lecousin.core/src/main/java/net/lecousin/framework/xml/XMLWriter.java

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,10 @@ public static String escape(CharSequence s) {
9292
private static final char[] ATTRIBUTE_EQUALS = new char[] { '=', '"' };
9393
private static final char[] CLOSE_EMPTY_TAG = new char[] { '/', '>' };
9494
private static final char[] START_CLOSE = new char[] { '<', '/' };
95-
private static final char[] START_CDATA = new char[] { '[', 'C', 'D', 'A', 'T', 'A', '[' };
96-
private static final char[] END_CDATA = new char[] { ']', ']' };
97-
private static final char[] START_COMMENT = new char[] { '<', '!', '-', '-', ' ' };
98-
private static final char[] END_COMMENT = new char[] { ' ', '-', '-', '>' };
95+
private static final char[] START_CDATA = new char[] { '<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[' };
96+
private static final char[] END_CDATA = new char[] { ']', ']', '>' };
97+
private static final char[] START_COMMENT = new char[] { '<', '!', '-', '-' };
98+
private static final char[] END_COMMENT = new char[] { '-', '-', '>' };
9999

100100
private static final char[] PRETTY_END_TAG = new char[] { '>', '\n' };
101101

@@ -105,7 +105,7 @@ public static String escape(CharSequence s) {
105105
* Start the document with the XML processing instruction if needed, and opening the root element.
106106
* @param rootNamespaceURI namespace of the root element
107107
* @param rootLocalName name of the root element
108-
* @param namespaces mapping from namespace URI and prefix, prefix may be empty for default namespace
108+
* @param namespaces mapping from namespace URI to prefix, prefix may be empty for default namespace
109109
*/
110110
public IAsync<IOException> start(String rootNamespaceURI, String rootLocalName, Map<String, String> namespaces) {
111111
if (includeXMLDeclaration) {
@@ -292,7 +292,8 @@ public IAsync<IOException> addCData(CharSequence data) {
292292
if (ctx.open)
293293
endOfAttributes(ctx);
294294
if (pretty) {
295-
writer.write('\n');
295+
if (lastNodeType != Node.ELEMENT_NODE && lastNodeType != Node.COMMENT_NODE && lastNodeType != Node.CDATA_SECTION_NODE)
296+
writer.write('\n');
296297
indent();
297298
}
298299
writer.write(START_CDATA);
@@ -310,7 +311,8 @@ public IAsync<IOException> addComment(CharSequence comment) {
310311
if (ctx != null && ctx.open)
311312
endOfAttributes(ctx);
312313
if (pretty) {
313-
writer.write('\n');
314+
if (lastNodeType != Node.ELEMENT_NODE && lastNodeType != Node.COMMENT_NODE && lastNodeType != Node.CDATA_SECTION_NODE)
315+
writer.write('\n');
314316
indent();
315317
}
316318
writer.write(START_COMMENT);
@@ -322,6 +324,8 @@ public IAsync<IOException> addComment(CharSequence comment) {
322324
return writer.write('\n');
323325
}
324326

327+
private static final String DOM_TASK_DESCRIPTION = "Write DOM";
328+
325329
/** Write the given DOM element. */
326330
public IAsync<IOException> write(Element element) {
327331
String name = element.getLocalName();
@@ -333,13 +337,48 @@ public IAsync<IOException> write(Element element) {
333337
namespaces = new HashMap<>(5);
334338
namespaces.put(uri, prefix);
335339
}
336-
openElement(uri, name, namespaces);
340+
IAsync<IOException> open = openElement(uri, name, namespaces);
341+
if (open.isDone())
342+
return writeAttributes(element);
343+
Async<IOException> sp = new Async<>();
344+
open.thenStart(new Task.Cpu.FromRunnable(DOM_TASK_DESCRIPTION, output.getPriority(), () ->
345+
writeAttributes(element).onDone(sp)
346+
), sp);
347+
return sp;
348+
}
349+
350+
private IAsync<IOException> writeAttributes(Element element) {
337351
NamedNodeMap attrs = element.getAttributes();
338-
if (attrs != null)
339-
for (int i = 0; i < attrs.getLength(); ++i) {
340-
Node a = attrs.item(i);
341-
addAttribute(a.getNodeName(), a.getNodeValue());
352+
if (attrs != null && attrs.getLength() > 0)
353+
return writeAttribute(element, attrs, 0);
354+
return writeChildren(element);
355+
}
356+
357+
private IAsync<IOException> writeAttribute(Element element, NamedNodeMap attrs, int attrIndex) {
358+
do {
359+
Node a = attrs.item(attrIndex);
360+
IAsync<IOException> sp = addAttribute(a.getNodeName(), a.getNodeValue());
361+
if (sp.isDone()) {
362+
if (sp.hasError()) return sp;
363+
attrIndex++;
364+
if (attrIndex == attrs.getLength())
365+
return writeChildren(element);
366+
continue;
342367
}
368+
Async<IOException> result = new Async<>();
369+
int nextIndex = attrIndex + 1;
370+
sp.thenStart(new Task.Cpu.FromRunnable(DOM_TASK_DESCRIPTION, output.getPriority(), () -> {
371+
if (nextIndex == attrs.getLength()) {
372+
writeChildren(element).onDone(result);
373+
return;
374+
}
375+
writeAttribute(element, attrs, nextIndex).onDone(result);
376+
}), result);
377+
return result;
378+
} while (true);
379+
}
380+
381+
private IAsync<IOException> writeChildren(Element element) {
343382
NodeList children = element.getChildNodes();
344383
if (children.getLength() == 0)
345384
return closeElement();
@@ -349,7 +388,7 @@ public IAsync<IOException> write(Element element) {
349388
return writeChild(children, 0);
350389
}
351390
Async<IOException> sp = new Async<>();
352-
open.thenStart(new Task.Cpu.FromRunnable("Write DOM", output.getPriority(), () ->
391+
open.thenStart(new Task.Cpu.FromRunnable(DOM_TASK_DESCRIPTION, output.getPriority(), () ->
353392
writeChild(children, 0).onDone(sp)
354393
), sp);
355394
return sp;
@@ -372,14 +411,14 @@ else if (child instanceof Text)
372411
if (sp.isDone()) {
373412
if (sp.hasError()) return sp;
374413
childIndex++;
375-
if (childIndex == children.getLength()) return sp;
414+
if (childIndex == children.getLength()) return closeElement();
376415
continue;
377416
}
378417
Async<IOException> result = new Async<>();
379418
int nextIndex = childIndex + 1;
380-
sp.thenStart(new Task.Cpu.FromRunnable("Write DOM", output.getPriority(), () -> {
419+
sp.thenStart(new Task.Cpu.FromRunnable(DOM_TASK_DESCRIPTION, output.getPriority(), () -> {
381420
if (nextIndex == children.getLength()) {
382-
result.unblock();
421+
closeElement().onDone(result);
383422
return;
384423
}
385424
writeChild(children, nextIndex).onDone(result);

net.lecousin.core/src/test/java/net/lecousin/framework/core/tests/concurrent/async/TestJoinPoint.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,8 @@ public void testWithTimeout() {
127127
MutableInteger timedout = new MutableInteger(0);
128128
jp.timeout(1, timedout::inc);
129129
new Task.Cpu.FromRunnable("test", Task.PRIORITY_NORMAL, () -> {
130-
try { Thread.sleep(1000); }
131-
catch (InterruptedException e) {}
132130
as.unblockSuccess(Integer.valueOf(11));
133-
}).start();
131+
}).executeIn(5000).start();
134132
jp.start();
135133
jp.block(10000);
136134
Assert.assertTrue(jp.isDone());
@@ -145,10 +143,8 @@ public void testListenTime() {
145143
MutableInteger timedout = new MutableInteger(0);
146144
jp.listenTime(1, timedout::inc);
147145
new Task.Cpu.FromRunnable("test", Task.PRIORITY_NORMAL, () -> {
148-
try { Thread.sleep(1000); }
149-
catch (InterruptedException e) {}
150146
as.unblockSuccess(Integer.valueOf(11));
151-
}).start();
147+
}).executeIn(2500).start();
152148
jp.start();
153149
jp.block(10000);
154150
Assert.assertTrue(jp.isDone());

net.lecousin.core/src/test/java/net/lecousin/framework/core/tests/xml/TestXMLWriter.java

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,27 @@
11
package net.lecousin.framework.core.tests.xml;
22

33
import java.io.IOException;
4+
import java.io.StringReader;
45
import java.nio.charset.StandardCharsets;
6+
import java.util.HashMap;
7+
import java.util.LinkedHashMap;
8+
import java.util.Map;
59

10+
import javax.xml.parsers.DocumentBuilderFactory;
11+
12+
import net.lecousin.framework.concurrent.Task;
613
import net.lecousin.framework.core.test.LCCoreAbstractTest;
14+
import net.lecousin.framework.io.IO.Seekable.SeekType;
15+
import net.lecousin.framework.io.IOUtil;
716
import net.lecousin.framework.io.buffering.MemoryIO;
17+
import net.lecousin.framework.io.text.BufferedWritableCharacterStream;
818
import net.lecousin.framework.xml.XMLWriter;
919

20+
import org.junit.Assert;
1021
import org.junit.Test;
22+
import org.w3c.dom.Document;
23+
import org.w3c.dom.Element;
24+
import org.xml.sax.InputSource;
1125

1226
public class TestXMLWriter extends LCCoreAbstractTest {
1327

@@ -56,4 +70,84 @@ public void testWriteAfterClose() throws Exception {
5670
io.close();
5771
}
5872

73+
@Test
74+
public void testBasic() throws Exception {
75+
MemoryIO io = new MemoryIO(4096, "test");
76+
XMLWriter writer = new XMLWriter(io, null, true, true);
77+
Map<String, String> namespaces = new LinkedHashMap<>();
78+
namespaces.put("http://test1", "");
79+
namespaces.put("http://test2", "t2");
80+
namespaces.put("http://test3", "t3");
81+
writer.start("http://test2", "root", namespaces);
82+
Map<String, String> namespaces2 = new LinkedHashMap<>();
83+
namespaces2.put("http://test4", "t4");
84+
writer.openElement(null, "element1", namespaces2);
85+
writer.addAttribute("a1", "v1");
86+
writer.openElement("http://test2", "sub1", null);
87+
writer.addText("my text");
88+
writer.closeElement();
89+
writer.openElement("http://test3", "sub2", null);
90+
writer.addComment(" my comment ");
91+
writer.closeElement();
92+
writer.openElement("http://test4", "sub3", null);
93+
writer.addCData("my cdata");
94+
writer.closeElement();
95+
writer.closeElement();
96+
writer.end().blockThrow(0);
97+
io.seekSync(SeekType.FROM_BEGINNING, 0);
98+
String xml = IOUtil.readFullyAsString(io, StandardCharsets.UTF_8, Task.PRIORITY_NORMAL).blockResult(0).asString();
99+
io.close();
100+
Assert.assertEquals("<?xml version=\"1.1\" encoding=\"UTF-8\"?>\n" +
101+
"<t2:root xmlns=\"http://test1\" xmlns:t2=\"http://test2\" xmlns:t3=\"http://test3\">\n" +
102+
"\t<element1 xmlns:t4=\"http://test4\" a1=\"v1\">\n" +
103+
"\t\t<t2:sub1>\n" +
104+
"\t\t\tmy text\n" +
105+
"\t\t</t2:sub1>\n" +
106+
"\t\t<t3:sub2>\n" +
107+
"\t\t\t<!-- my comment -->\n" +
108+
"\t\t</t3:sub2>\n" +
109+
"\t\t<t4:sub3>\n" +
110+
"\t\t\t<![CDATA[my cdata]]>\n" +
111+
"\t\t</t4:sub3>\n" +
112+
"\t</element1>\n" +
113+
"</t2:root>\n",
114+
xml);
115+
}
116+
117+
@Test
118+
public void testFromDOM() throws Exception {
119+
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
120+
String xmlSource = "<root><element a1=\"v1\"><sub1><!-- comment --></sub1><sub2>text</sub2></element></root>";
121+
factory.setValidating(false);
122+
factory.setExpandEntityReferences(false);
123+
factory.setNamespaceAware(true);
124+
Document doc = factory.newDocumentBuilder().parse(new InputSource(new StringReader(xmlSource)));
125+
126+
// with normal buffer
127+
MemoryIO io = new MemoryIO(4096, "test");
128+
XMLWriter writer = new XMLWriter(io, null, false, false);
129+
Element root = doc.getDocumentElement();
130+
Map<String, String> namespaces = new HashMap<>();
131+
writer.start(null, root.getLocalName(), namespaces);
132+
writer.write((Element)root.getChildNodes().item(0)).blockThrow(0);
133+
writer.end().blockThrow(0);
134+
io.seekSync(SeekType.FROM_BEGINNING, 0);
135+
String xml = IOUtil.readFullyAsString(io, StandardCharsets.UTF_8, Task.PRIORITY_NORMAL).blockResult(0).asString();
136+
io.close();
137+
Assert.assertEquals(xmlSource, xml);
138+
139+
// with small buffer
140+
io = new MemoryIO(2, "test");
141+
writer = new XMLWriter(new BufferedWritableCharacterStream(io, StandardCharsets.UTF_8, 2), false, false);
142+
root = doc.getDocumentElement();
143+
namespaces = new HashMap<>();
144+
writer.start(null, root.getLocalName(), namespaces);
145+
writer.write((Element)root.getChildNodes().item(0)).blockThrow(0);
146+
writer.end().blockThrow(0);
147+
io.seekSync(SeekType.FROM_BEGINNING, 0);
148+
xml = IOUtil.readFullyAsString(io, StandardCharsets.UTF_8, Task.PRIORITY_NORMAL).blockResult(0).asString();
149+
io.close();
150+
Assert.assertEquals(xmlSource, xml);
151+
}
152+
59153
}

0 commit comments

Comments
 (0)