1
2
3
4
5
6
7
8 package net.sf.saxon.option.jdom2;
9
10 import net.sf.saxon.event.PipelineConfiguration;
11 import net.sf.saxon.event.ReceiverOption;
12 import net.sf.saxon.lib.NamespaceConstant;
13 import net.sf.saxon.om.*;
14 import net.sf.saxon.s9api.Location;
15 import net.sf.saxon.trans.XPathException;
16 import net.sf.saxon.tree.util.FastStringBuffer;
17 import net.sf.saxon.type.SchemaType;
18 import net.sf.saxon.value.Whitespace;
19 import org.jdom2.*;
20
21 import java.util.Stack;
22
23
24
25
26
27 public class JDOM2Writer extends net.sf.saxon.event.Builder {
28
29 private Document document;
30 private Stack<Parent> ancestors = new Stack<>();
31 private boolean implicitDocumentNode = false;
32 private FastStringBuffer textBuffer = new FastStringBuffer(FastStringBuffer.C256);
33 private Stack<NamespaceMap> nsStack = new Stack<>();
34
35
36
37
38
39
40
41 public JDOM2Writer(PipelineConfiguration pipe) {
42 super(pipe);
43 nsStack.push(NamespaceMap.emptyMap());
44 }
45
46
47
48
49
50
51
52
53
54 @Override
55 public void setUnparsedEntity(String name, String systemID, String publicID) throws XPathException {
56
57 }
58
59
60
61
62
63 @Override
64 public void open() {
65 }
66
67
68
69
70
71 @Override
72 public void close() {
73 }
74
75
76
77
78
79
80 @Override
81 public void startDocument(int properties) throws XPathException {
82 document = new Document();
83 document.setBaseURI(systemId);
84 ancestors.push(document);
85 textBuffer.setLength(0);
86 }
87
88
89
90
91
92 @Override
93 public void endDocument() throws XPathException {
94 ancestors.pop();
95 }
96
97
98
99
100
101 @Override
102 public void startElement(NodeName elemName, SchemaType type,
103 AttributeMap attributes, NamespaceMap namespaces,
104 Location location, int properties) throws XPathException {
105 flush();
106 String local = elemName.getLocalPart();
107 String uri = elemName.getURI();
108 String prefix = elemName.getPrefix();
109 Element element;
110 if (ancestors.isEmpty()) {
111 startDocument(ReceiverOption.NONE);
112 implicitDocumentNode = true;
113 }
114 element = new Element(local, prefix, uri);
115 if (ancestors.size() == 1) {
116 document.setRootElement(element);
117 } else {
118 ancestors.peek().addContent(element);
119 }
120 ancestors.push(element);
121
122 NamespaceMap parentMap = nsStack.peek();
123 if (namespaces != parentMap) {
124 NamespaceBinding[] declarations = namespaces.getDifferences(parentMap, false);
125 for (NamespaceBinding ns : declarations) {
126 namespace(element, ns);
127 }
128 }
129 nsStack.push(namespaces);
130
131 for (AttributeInfo att : attributes) {
132 NodeName nameCode = att.getNodeName();
133 String attlocal = nameCode.getLocalPart();
134 String atturi = nameCode.getURI();
135 String attprefix = nameCode.getPrefix();
136 String value = att.getValue();
137 Namespace ns = attprefix.isEmpty() ?
138 Namespace.getNamespace(atturi) :
139 Namespace.getNamespace(attprefix, atturi);
140 boolean isXmlId = uri.equals(NamespaceConstant.XML) && attlocal.equals("id");
141 if (isXmlId) {
142 value = Whitespace.trim(value);
143 }
144 Attribute attr = new Attribute(attlocal, value, ns);
145 if (isXmlId) {
146 attr.setAttributeType(AttributeType.ID);
147 }
148 element.getAttributes().add(attr);
149 }
150 }
151
152 private void namespace(Element element, NamespaceBinding namespaceBinding) throws XPathException {
153 String prefix = namespaceBinding.getPrefix();
154 String uri = namespaceBinding.getURI();
155 if (uri.isEmpty() && prefix.length() != 0) {
156
157 return;
158 }
159 Namespace ns = prefix.isEmpty() ?
160 Namespace.getNamespace(uri) :
161 Namespace.getNamespace(prefix, uri);
162 element.addNamespaceDeclaration(ns);
163 }
164
165
166
167
168
169 @Override
170 public void endElement() throws XPathException {
171 flush();
172 ancestors.pop();
173 nsStack.pop();
174 Object parent = ancestors.peek();
175 if (parent == document && implicitDocumentNode) {
176 endDocument();
177 }
178 }
179
180
181
182
183
184 @Override
185 public void characters(CharSequence chars, Location locationId, int properties) throws XPathException {
186 textBuffer.cat(chars);
187 }
188
189 private void flush() {
190 if (textBuffer.length() != 0) {
191 Text text = new Text(textBuffer.toString());
192 ancestors.peek().addContent(text);
193 textBuffer.setLength(0);
194 }
195 }
196
197
198
199
200
201
202 @Override
203 public void processingInstruction(String target, CharSequence data, Location locationId, int properties)
204 throws XPathException {
205 flush();
206 ProcessingInstruction pi = new ProcessingInstruction(target, data.toString());
207 ancestors.peek().addContent(pi);
208 }
209
210
211
212
213
214 @Override
215 public void comment(CharSequence chars, Location locationId, int properties) throws XPathException {
216 flush();
217 Comment comment = new Comment(chars.toString());
218 ancestors.peek().addContent(comment);
219 }
220
221
222
223
224
225
226
227
228
229 @Override
230 public boolean usesTypeAnnotations() {
231 return false;
232 }
233
234
235
236
237
238
239
240 public Document getDocument() {
241 return document;
242 }
243
244
245
246
247
248
249
250 @Override
251 public NodeInfo getCurrentRoot() {
252 return new JDOM2DocumentWrapper(document, config).getRootNode();
253 }
254 }
255
256
257