Skip to content

Commit 855922a

Browse files
authored
Merge pull request #4578 from evolvedbinary/feature/preserve-xml-declaration
Preserve XML Declaration
2 parents 118e6dd + 92e157a commit 855922a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+810
-121
lines changed

exist-core/pom.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@
271271
<groupId>org.exist-db.thirdparty.xerces</groupId>
272272
<artifactId>xercesImpl</artifactId>
273273
<version>2.12.2</version>
274-
<classifier>xml-schema-1.1</classifier>
274+
<classifier>jdk14-xml-schema-1.1</classifier>
275275
<exclusions>
276276
<exclusion> <!-- conflicts with Java 17's javax.xml module -->
277277
<groupId>xml-apis</groupId>
@@ -679,6 +679,7 @@
679679
<!--
680680
Already under LGPL 2.1, but with a different Copyright
681681
-->
682+
<exclude>src/main/java/org/exist/dom/persistent/XMLDeclarationImpl.java</exclude>
682683
<exclude>src/main/java/org/exist/resolver/ResolverFactory.java</exclude>
683684
<exclude>src/main/java/org/exist/resolver/XercesXmlResolverAdapter.java</exclude>
684685
<exclude>src/main/java/org/exist/util/UTF8.java</exclude>
@@ -831,6 +832,7 @@ The original license statement is also included below.]]></preamble>
831832
-->
832833
<header>${project.parent.relativePath}/FDB-backport-LGPL-21-ONLY-license.template.txt</header>
833834
<includes>
835+
<include>src/main/java/org/exist/dom/persistent/XMLDeclarationImpl.java</include>
834836
<include>src/main/java/org/exist/resolver/ResolverFactory.java</include>
835837
<include>src/main/java/org/exist/resolver/XercesXmlResolverAdapter.java</include>
836838
<include>src/test/java/org/exist/storage/MoveCollectionTest.java</include>

exist-core/src/main/java/org/exist/Indexer.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.apache.logging.log4j.LogManager;
2727
import org.apache.logging.log4j.Logger;
2828
import org.exist.collections.CollectionConfiguration;
29+
import org.exist.dom.QName;
2930
import org.exist.dom.persistent.AttrImpl;
3031
import org.exist.dom.persistent.CDATASectionImpl;
3132
import org.exist.dom.persistent.CommentImpl;
@@ -34,9 +35,9 @@
3435
import org.exist.dom.persistent.ElementImpl;
3536
import org.exist.dom.persistent.NodeHandle;
3637
import org.exist.dom.persistent.ProcessingInstructionImpl;
37-
import org.exist.dom.QName;
3838
import org.exist.dom.persistent.StoredNode;
3939
import org.exist.dom.persistent.TextImpl;
40+
import org.exist.dom.persistent.XMLDeclarationImpl;
4041
import org.exist.indexing.StreamListener;
4142
import org.exist.indexing.StreamListener.ReindexMode;
4243
import org.exist.storage.DBBroker;
@@ -62,6 +63,8 @@
6263
import org.xml.sax.SAXParseException;
6364
import org.xml.sax.ext.LexicalHandler;
6465

66+
import javax.annotation.Nullable;
67+
6568
/**
6669
* Parses a given input document via SAX, stores it to the database and handles
6770
* index-creation.
@@ -542,6 +545,12 @@ public void startDocument() {
542545
nodeFactoryInstanceCnt = 1;
543546
}
544547

548+
@Override
549+
public void declaration(@Nullable final String version, @Nullable final String encoding, @Nullable final String standalone) throws SAXException {
550+
final XMLDeclarationImpl xmlDecl = new XMLDeclarationImpl(version, encoding, standalone);
551+
document.setXmlDeclaration(xmlDecl);
552+
}
553+
545554
final boolean hasNormAttribute(final Attributes attributes) {
546555
for (int i = 0; i < attributes.getLength(); i++) {
547556
if(attributes.getLocalName(i).equals("norm")) {

exist-core/src/main/java/org/exist/client/InteractiveClient.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@
8989
import se.softhouse.jargo.ArgumentException;
9090

9191
import static java.nio.charset.StandardCharsets.UTF_8;
92+
import static javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION;
93+
import static org.exist.storage.serializers.EXistOutputKeys.OMIT_ORIGINAL_XML_DECLARATION;
9294
import static org.exist.storage.serializers.EXistOutputKeys.OUTPUT_DOCTYPE;
9395
import static org.xmldb.api.base.ResourceType.XML_RESOURCE;
9496

@@ -143,6 +145,8 @@ public class InteractiveClient {
143145
DEFAULT_PROPERTIES.setProperty(USER, USER_DEFAULT);
144146
DEFAULT_PROPERTIES.setProperty(EDITOR, EDIT_CMD);
145147
DEFAULT_PROPERTIES.setProperty(INDENT, "true");
148+
DEFAULT_PROPERTIES.setProperty(OMIT_XML_DECLARATION, "no");
149+
DEFAULT_PROPERTIES.setProperty(OMIT_ORIGINAL_XML_DECLARATION, "no");
146150
DEFAULT_PROPERTIES.setProperty(OUTPUT_DOCTYPE, "true");
147151
DEFAULT_PROPERTIES.setProperty(ENCODING, ENCODING_DEFAULT.name());
148152
DEFAULT_PROPERTIES.setProperty(COLORS, "false");

exist-core/src/main/java/org/exist/collections/triggers/DocumentTriggers.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
import org.xml.sax.SAXParseException;
4141
import org.xml.sax.ext.LexicalHandler;
4242

43+
import javax.annotation.Nullable;
44+
4345
/**
4446
* @author <a href="mailto:[email protected]">Dmitriy Shabanov</a>
4547
*
@@ -144,6 +146,11 @@ public void startDocument() throws SAXException {
144146
contentHandler.startDocument();
145147
}
146148

149+
@Override
150+
public void declaration(@Nullable final String version, @Nullable final String encoding, @Nullable final String standalone) throws SAXException {
151+
contentHandler.declaration(version, encoding, standalone);
152+
}
153+
147154
@Override
148155
public void endDocument() throws SAXException {
149156
contentHandler.endDocument();

exist-core/src/main/java/org/exist/collections/triggers/SAXTrigger.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
import org.xml.sax.SAXParseException;
3737
import org.xml.sax.ext.LexicalHandler;
3838

39+
import javax.annotation.Nullable;
40+
3941
/**
4042
* Abstract default implementation of a Trigger. This implementation just
4143
* forwards all SAX events to the output content handler.
@@ -89,6 +91,13 @@ public void startDocument() throws SAXException {
8991
nextContentHandler.startDocument();
9092
}
9193

94+
@Override
95+
public void declaration(@Nullable final String version, @Nullable final String encoding, @Nullable final String standalone) throws SAXException {
96+
if (nextContentHandler != null) {
97+
nextContentHandler.declaration(version, encoding, standalone);
98+
}
99+
}
100+
92101
@Override
93102
public void endDocument() throws SAXException {
94103
if (nextContentHandler != null)

exist-core/src/main/java/org/exist/dom/memtree/DocumentBuilderReceiver.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ public void endDocument() throws SAXException {
122122
builder.endDocument();
123123
}
124124

125+
@Override
126+
public void declaration(final String version, final String encoding, final String standalone) throws SAXException {
127+
// NOTE(AR) in-memory documents do not support XML Declaration
128+
}
129+
125130
@Override
126131
public void startPrefixMapping(final String prefix, final String namespaceURI) throws SAXException {
127132
if(prefix == null || prefix.length() == 0) {

exist-core/src/main/java/org/exist/dom/persistent/DocumentImpl.java

Lines changed: 96 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ public class DocumentImpl extends NodeImpl<DocumentImpl> implements Resource, Do
7777

7878
//public static final byte DOCUMENT_NODE_SIGNATURE = 0x0F;
7979

80+
public static final byte NO_XMLDECL = 0;
81+
public static final byte HAS_XMLDECL = 1;
82+
8083
public static final byte NO_DOCTYPE = 0;
8184
public static final byte HAS_DOCTYPE = 1;
8285

@@ -140,6 +143,11 @@ public class DocumentImpl extends NodeImpl<DocumentImpl> implements Resource, Do
140143
*/
141144
protected int userLock = 0;
142145

146+
/**
147+
* The document's XML Declaration - if specified.
148+
*/
149+
@Nullable private XMLDeclarationImpl xmlDecl = null;
150+
143151
/**
144152
* The document's doctype declaration - if specified.
145153
*/
@@ -203,7 +211,7 @@ public DocumentImpl(final Expression expression, final BrokerPool pool, @Nullabl
203211
final XmldbURI fileURI) {
204212
this(expression, pool, collection, docId, fileURI,
205213
PermissionFactory.getDefaultResourcePermission(pool.getSecurityManager()), 0, null,
206-
System.currentTimeMillis(), null, null, null);
214+
System.currentTimeMillis(), null, null, null, null);
207215
}
208216

209217
/**
@@ -225,7 +233,7 @@ public DocumentImpl(final int docId, final DocumentImpl prevDoc) {
225233
*/
226234
public DocumentImpl(final Expression expression, final int docId, final DocumentImpl prevDoc) {
227235
this(expression, prevDoc.pool, prevDoc.collection, docId, prevDoc.fileURI, prevDoc.permissions.copy(), 0, null,
228-
System.currentTimeMillis(), null, null, null);
236+
System.currentTimeMillis(), null, null, null, null);
229237
}
230238

231239
/**
@@ -242,13 +250,40 @@ public DocumentImpl(final Expression expression, final int docId, final Document
242250
* @param lastModified the last modified time of the document, or null to use the {@code created} time
243251
* @param mimeType the media type of the document, or null for application/xml
244252
* @param docType the document type, or null
253+
*
254+
* @deprecated Use {@link DocumentImpl(BrokerPool, Collection, int, XmldbURI, Permission, int, long[], long, Long, String, XMLDeclarationImpl, DocumentType)}
255+
*/
256+
@Deprecated
257+
public DocumentImpl(final BrokerPool pool, @Nullable final Collection collection,
258+
final int docId, final XmldbURI fileURI, final Permission permissions,
259+
final int children, @Nullable final long[] childAddress,
260+
final long created, @Nullable final Long lastModified, @Nullable final String mimeType,
261+
@Nullable final DocumentType docType) {
262+
this(null, pool, collection, docId, fileURI, permissions, children, childAddress, created, lastModified, mimeType, null, docType);
263+
}
264+
265+
/**
266+
* Creates a new persistent Document instance to replace an existing document instance.
267+
*
268+
* @param pool The broker pool
269+
* @param collection The Collection which holds this document
270+
* @param docId the id of the document
271+
* @param fileURI The name of the document
272+
* @param permissions the permissions of the document
273+
* @param children the number of children that the document has
274+
* @param childAddress the addresses of the child nodes
275+
* @param created the created time of the document
276+
* @param lastModified the last modified time of the document, or null to use the {@code created} time
277+
* @param mimeType the media type of the document, or null for application/xml
278+
* @param xmlDecl the XML Declaration, or null
279+
* @param docType the document type, or null
245280
*/
246281
public DocumentImpl(final BrokerPool pool, @Nullable final Collection collection,
247282
final int docId, final XmldbURI fileURI, final Permission permissions,
248283
final int children, @Nullable final long[] childAddress,
249284
final long created, @Nullable final Long lastModified, @Nullable final String mimeType,
250-
@Nullable final DocumentType docType) {
251-
this(null, pool, collection, docId, fileURI, permissions, children, childAddress, created, lastModified, mimeType, docType);
285+
@Nullable final XMLDeclarationImpl xmlDecl, @Nullable final DocumentType docType) {
286+
this(null, pool, collection, docId, fileURI, permissions, children, childAddress, created, lastModified, mimeType, xmlDecl, docType);
252287
}
253288

254289
/**
@@ -266,12 +301,40 @@ public DocumentImpl(final BrokerPool pool, @Nullable final Collection collection
266301
* @param lastModified the last modified time of the document, or null to use the {@code created} time
267302
* @param mimeType the media type of the document, or null for application/xml
268303
* @param docType the document type, or null
304+
*
305+
* @deprecated Use {@link DocumentImpl(Expression, BrokerPool, Collection, int ,XmldbURI, Permission, int, long[], long, Long, String, XMLDeclarationImpl, DocumentType)}
269306
*/
307+
@Deprecated
270308
public DocumentImpl(final Expression expression, final BrokerPool pool, @Nullable final Collection collection,
271309
final int docId, final XmldbURI fileURI, final Permission permissions,
272310
final int children, @Nullable final long[] childAddress,
273311
final long created, @Nullable final Long lastModified, @Nullable final String mimeType,
274312
@Nullable final DocumentType docType) {
313+
this(expression, pool, collection, docId, fileURI, permissions, children, childAddress, created, lastModified, mimeType, null, docType);
314+
}
315+
316+
/**
317+
* Creates a new persistent Document instance to replace an existing document instance.
318+
*
319+
* @param expression the expression from which the document instance derives
320+
* @param pool The broker pool
321+
* @param collection The Collection which holds this document
322+
* @param docId the id of the document
323+
* @param fileURI The name of the document
324+
* @param permissions the permissions of the document
325+
* @param children the number of children that the document has
326+
* @param childAddress the addresses of the child nodes
327+
* @param created the created time of the document
328+
* @param lastModified the last modified time of the document, or null to use the {@code created} time
329+
* @param mimeType the media type of the document, or null for application/xml
330+
* @param xmlDecl the XML Declaration, or null
331+
* @param docType the document type, or null
332+
*/
333+
public DocumentImpl(final Expression expression, final BrokerPool pool, @Nullable final Collection collection,
334+
final int docId, final XmldbURI fileURI, final Permission permissions,
335+
final int children, @Nullable final long[] childAddress,
336+
final long created, @Nullable final Long lastModified, @Nullable final String mimeType,
337+
@Nullable final XMLDeclarationImpl xmlDecl, @Nullable final DocumentType docType) {
275338
super(expression);
276339
this.pool = pool;
277340

@@ -286,6 +349,7 @@ public DocumentImpl(final Expression expression, final BrokerPool pool, @Nullabl
286349
this.created = created;
287350
this.lastModified = lastModified == null ? created : lastModified;
288351
this.mimeType = mimeType == null ? MimeType.XML_TYPE.getName() : mimeType;
352+
this.xmlDecl = xmlDecl;
289353
this.docType = docType;
290354

291355
//inherit the group to the resource if current collection is setGid
@@ -790,6 +854,12 @@ void writeDocumentAttributes(final SymbolTable symbolTable, final VariableByteOu
790854
ostream.writeInt(symbolTable.getMimeTypeId(mimeType));
791855
ostream.writeInt(pageCount);
792856
ostream.writeInt(userLock);
857+
if (xmlDecl != null) {
858+
ostream.writeByte(HAS_XMLDECL);
859+
xmlDecl.write(ostream);
860+
} else {
861+
ostream.writeByte(NO_XMLDECL);
862+
}
793863
if (docType != null) {
794864
ostream.writeByte(HAS_DOCTYPE);
795865
((DocumentTypeImpl) docType).write(ostream);
@@ -833,6 +903,12 @@ public static DocumentImpl read(final BrokerPool pool, final VariableByteInput i
833903
final String mimeType = pool.getSymbols().getMimeType(mimeTypeSymbolsIndex);
834904
final int pageCount = istream.readInt();
835905
final int userLock = istream.readInt();
906+
final XMLDeclarationImpl xmlDecl;
907+
if (istream.readByte() == HAS_XMLDECL) {
908+
xmlDecl = XMLDeclarationImpl.read(istream);
909+
} else {
910+
xmlDecl = null;
911+
}
836912
final DocumentTypeImpl docType;
837913
if (istream.readByte() == HAS_DOCTYPE) {
838914
docType = DocumentTypeImpl.read(istream);
@@ -846,7 +922,7 @@ public static DocumentImpl read(final BrokerPool pool, final VariableByteInput i
846922
lockToken = null;
847923
}
848924

849-
final DocumentImpl doc = new DocumentImpl(null, pool, null, docId, fileURI, permissions, children, childAddress, created, lastModified, mimeType, docType);
925+
final DocumentImpl doc = new DocumentImpl(null, pool, null, docId, fileURI, permissions, children, childAddress, created, lastModified, mimeType, xmlDecl, docType);
850926
doc.pageCount = pageCount;
851927
doc.userLock = userLock;
852928
doc.lockToken = lockToken;
@@ -1027,6 +1103,21 @@ protected NodeList findElementsByTagName(final NodeHandle root, final QName qnam
10271103
return NodeSet.EMPTY_SET;
10281104
}
10291105

1106+
/**
1107+
* The method <code>setXmlDeclaration</code>
1108+
*
1109+
* @param xmlDecl a <code>XMLDeclarationImpl</code> value
1110+
*/
1111+
@EnsureContainerLocked(mode=WRITE_LOCK)
1112+
public void setXmlDeclaration(final XMLDeclarationImpl xmlDecl) {
1113+
this.xmlDecl = xmlDecl;
1114+
}
1115+
1116+
@EnsureContainerLocked(mode=READ_LOCK)
1117+
public @Nullable XMLDeclarationImpl getXmlDeclaration() {
1118+
return xmlDecl;
1119+
}
1120+
10301121
/************************************************
10311122
*
10321123
* NodeImpl methods

0 commit comments

Comments
 (0)