diff --git a/nx01-x4o-driver/src/main/java/org/x4o/xml/io/DefaultX4OWriter.java b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/DefaultX4OWriter.java index 676d59d..895ec77 100644 --- a/nx01-x4o-driver/src/main/java/org/x4o/xml/io/DefaultX4OWriter.java +++ b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/DefaultX4OWriter.java @@ -43,6 +43,7 @@ import org.x4o.xml.io.sax.ext.ContentWriter; import org.x4o.xml.io.sax.ext.PropertyConfig; import org.x4o.xml.io.sax.ext.ContentWriterXml; import org.x4o.xml.io.sax.ext.PropertyConfig.PropertyConfigItem; +import org.x4o.xml.io.sax.xdbx.XDBXWriterXml; import org.x4o.xml.lang.X4OLanguage; import org.x4o.xml.lang.X4OLanguageSession; import org.x4o.xml.lang.X4OLanguageModule; @@ -67,6 +68,7 @@ public class DefaultX4OWriter extends AbstractX4OWriter { public final static PropertyConfig DEFAULT_PROPERTY_CONFIG; public final static String OUTPUT_STREAM = PROPERTY_CONTEXT_PREFIX+"output/stream"; + public final static String OUTPUT_XDBX = PROPERTY_CONTEXT_PREFIX+"output/xdbx"; public final static String SCHEMA_PRINT = PROPERTY_CONTEXT_PREFIX+"schema/print"; public final static String SCHEMA_ROOT_URI = PROPERTY_CONTEXT_PREFIX+"schema/root-uri"; public final static String DEBUG_OUTPUT_HANDLER = PROPERTY_CONTEXT_PREFIX + ABSTRACT_DEBUG_OUTPUT_HANDLER; @@ -76,6 +78,7 @@ public class DefaultX4OWriter extends AbstractX4OWriter { static { DEFAULT_PROPERTY_CONFIG = new PropertyConfig(true,ContentWriterXml.DEFAULT_PROPERTY_CONFIG,PROPERTY_CONTEXT_PREFIX, new PropertyConfigItem(true,OUTPUT_STREAM,OutputStream.class), + new PropertyConfigItem(OUTPUT_XDBX,Boolean.class,false), new PropertyConfigItem(SCHEMA_PRINT,Boolean.class,true), new PropertyConfigItem(SCHEMA_ROOT_URI,String.class), new PropertyConfigItem(DEBUG_OUTPUT_HANDLER,ContentWriter.class), @@ -139,10 +142,17 @@ public class DefaultX4OWriter extends AbstractX4OWriter { schemaUriRoot = ns.getSchemaUri(); } } - - ContentWriterXml writer = new ContentWriterXml(out,encoding); - writer.getPropertyConfig().copyParentProperties(getPropertyConfig()); - + boolean useXDBX = getPropertyConfig().getPropertyBoolean(OUTPUT_XDBX); + ContentWriter writer = null; + if (!useXDBX) { + ContentWriterXml writerXML = new ContentWriterXml(out,encoding); + writerXML.getPropertyConfig().copyParentProperties(getPropertyConfig()); + writer = writerXML; + } else { + XDBXWriterXml writerXDBX = new XDBXWriterXml(out); + writerXDBX.getPropertyConfig().copyParentProperties(getPropertyConfig()); + writer = writerXDBX; + } writer.startDocument(); Map prefixes = new HashMap(); @@ -214,7 +224,7 @@ public class DefaultX4OWriter extends AbstractX4OWriter { return o1.writeOrder.compareTo(o2.writeOrder); } } - private void writeTree(ContentWriterXml writer,Element element,boolean isRoot) throws SAXException, ElementObjectPropertyValueException { + private void writeTree(ContentWriter writer,Element element,boolean isRoot) throws SAXException, ElementObjectPropertyValueException { List attr = new ArrayList(20); if (element.getElementClass().getAutoAttributes()!=null && element.getElementClass().getAutoAttributes()==false) { for (ElementClassAttribute eca:element.getElementClass().getElementClassAttributes()) { diff --git a/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/ext/AbstractContentWriter.java b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/ext/AbstractContentWriter.java index a9d61f9..a5c78f6 100644 --- a/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/ext/AbstractContentWriter.java +++ b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/ext/AbstractContentWriter.java @@ -47,8 +47,8 @@ public abstract class AbstractContentWriter extends AbstractContentWriterLexical * Starts and end then element. * @see org.x4o.xml.io.sax.ext.ContentWriter#startElementEnd(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) */ - public void startElementEnd(String uri, String localName, String name,Attributes atts) throws SAXException { - startElement(uri,localName,name,atts); + public void startElementEnd(String uri, String localName, String name, Attributes atts) throws SAXException { + startElement(uri,localName,name, atts); endElement(uri, localName, name); } } diff --git a/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/ext/AbstractContentWriterHandler.java b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/ext/AbstractContentWriterHandler.java index 9baff9b..30a3aef 100644 --- a/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/ext/AbstractContentWriterHandler.java +++ b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/ext/AbstractContentWriterHandler.java @@ -183,7 +183,7 @@ public class AbstractContentWriterHandler implements ContentHandler { comment(licenceText); } - private String readLicenceStream(InputStream inputStream,String encoding) throws IOException { + private String readLicenceStream(InputStream inputStream, String encoding) throws IOException { if (encoding==null) { encoding = XMLConstants.XML_DEFAULT_ENCODING; } @@ -243,7 +243,7 @@ public class AbstractContentWriterHandler implements ContentHandler { * @param atts The attributes of the xml tag. * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) */ - public void startElement(String uri, String localName, String name,Attributes atts) throws SAXException { + public void startElement(String uri, String localName, String name, Attributes atts) throws SAXException { if (localName==null) { throw new SAXException("LocalName may not be null."); } @@ -287,7 +287,7 @@ public class AbstractContentWriterHandler implements ContentHandler { elements.push(localName); } - public void startElementTag(String uri,String localName) throws SAXException { + public void startElementTag(String uri, String localName) throws SAXException { if (XMLConstants.NULL_NS_URI.equals(uri) | uri==null) { startElement.append(localName); } else { diff --git a/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/ext/ContentWriter.java b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/ext/ContentWriter.java index 5f6e01b..15673a3 100644 --- a/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/ext/ContentWriter.java +++ b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/ext/ContentWriter.java @@ -44,7 +44,7 @@ public interface ContentWriter extends ContentHandler,LexicalHandler { * @param atts The attributes of the element. * @throws SAXException When IOException is thrown. */ - void startElementEnd(String uri, String localName, String name,Attributes atts) throws SAXException; + void startElementEnd(String uri, String localName, String name, Attributes atts) throws SAXException; /** * Writes a String as xml comment. diff --git a/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/AbstractXDBXWriter.java b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/AbstractXDBXWriter.java new file mode 100644 index 0000000..93e41d4 --- /dev/null +++ b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/AbstractXDBXWriter.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2004-2014, Willem Cazander + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.x4o.xml.io.sax.xdbx; + +import java.io.OutputStream; + +import org.x4o.xml.io.sax.ext.ContentWriter; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; + +/** + * Writes SAX events to binary XML. + * + * @author Willem Cazander + * @version 1.0 Dec 20, 2024 + */ +public abstract class AbstractXDBXWriter extends AbstractXDBXWriterLexical implements ContentWriter { + + /** + * Creates writer which prints to the stream interface. + * @param out The stream to print the xml to. + */ + public AbstractXDBXWriter(OutputStream out) { + super(out); + } + + /** + * Starts and end then element. + * @see org.x4o.xml.io.sax.ext.ContentWriter#startElementEnd(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) + */ + public void startElementEnd(String uri, String localName, String name, Attributes atts) throws SAXException { + startElement(uri,localName,name, atts); + endElement(uri, localName, name); + } +} diff --git a/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/AbstractXDBXWriterHandler.java b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/AbstractXDBXWriterHandler.java new file mode 100644 index 0000000..b5d0617 --- /dev/null +++ b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/AbstractXDBXWriterHandler.java @@ -0,0 +1,656 @@ +/* + * Copyright (c) 2004-2014, Willem Cazander + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.x4o.xml.io.sax.xdbx; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.PrimitiveIterator; +import java.util.Set; +import java.util.Stack; + +import org.x4o.xml.io.XMLConstants; +import org.x4o.xml.io.sax.ext.PropertyConfig; +import org.x4o.xml.io.sax.ext.PropertyConfig.PropertyConfigItem; +import org.x4o.xml.lang.X4OLanguageClassLoader; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; + +/** + * Writes SAX content handler events as binary XML called XDBX. + * + * @author Willem Cazander + * @version 1.0 Dec 19, 2024 + */ +public class AbstractXDBXWriterHandler implements ContentHandler { + + private final PropertyConfig propertyConfig; + private OutputStream out = null; + private Map prefixMapping = null; + private List printedMappings = null; + //private StringBuilder startElement = null; + private Stack elements = null; + private Map stringIdx = null; + + private final static String PROPERTY_CONTEXT_PREFIX = PropertyConfig.X4O_PROPERTIES_PREFIX+PropertyConfig.X4O_PROPERTIES_WRITER_XML; + public final static PropertyConfig DEFAULT_PROPERTY_CONFIG; + public final static String OUTPUT_DECLARATION = PROPERTY_CONTEXT_PREFIX+"output/declaration"; + public final static String OUTPUT_CHAR_NULL = PROPERTY_CONTEXT_PREFIX+"output/char-null"; + public final static String OUTPUT_COMMENT_ENABLE = PROPERTY_CONTEXT_PREFIX+"output/comment-enable"; + public final static String OUTPUT_COMMENT_AUTO_SPACE = PROPERTY_CONTEXT_PREFIX+"output/comment-auto-space"; + public final static String PROLOG_LICENCE_FILE = PROPERTY_CONTEXT_PREFIX+"prolog/licence-file"; + public final static String PROLOG_LICENCE_RESOURCE = PROPERTY_CONTEXT_PREFIX+"prolog/licence-resource"; + public final static String PROLOG_LICENCE_ENCODING = PROPERTY_CONTEXT_PREFIX+"prolog/licence-encoding"; + public final static String PROLOG_LICENCE_ENABLE = PROPERTY_CONTEXT_PREFIX+"prolog/licence-enable"; + public final static String PROLOG_USER_COMMENT = PROPERTY_CONTEXT_PREFIX+"prolog/user-comment"; + public final static String PROLOG_USER_COMMENT_ENABLE = PROPERTY_CONTEXT_PREFIX+"prolog/user-comment-enable"; + + static { + DEFAULT_PROPERTY_CONFIG = new PropertyConfig(true,null,PROPERTY_CONTEXT_PREFIX, + new PropertyConfigItem(OUTPUT_DECLARATION, Boolean.class, true), + new PropertyConfigItem(OUTPUT_CHAR_NULL, String.class, "NULL"), + new PropertyConfigItem(OUTPUT_COMMENT_ENABLE, Boolean.class, true), + new PropertyConfigItem(OUTPUT_COMMENT_AUTO_SPACE, Boolean.class, true), + new PropertyConfigItem(PROLOG_LICENCE_ENCODING, String.class, XMLConstants.XML_DEFAULT_ENCODING), + new PropertyConfigItem(PROLOG_LICENCE_FILE, File.class ), + new PropertyConfigItem(PROLOG_LICENCE_RESOURCE, String.class ), + new PropertyConfigItem(PROLOG_LICENCE_ENABLE, Boolean.class, true), + new PropertyConfigItem(PROLOG_USER_COMMENT, String.class ), + new PropertyConfigItem(PROLOG_USER_COMMENT_ENABLE, Boolean.class, true) + ); + } + + /** + * Creates writer which prints to the stream interface. + * @param out The stream to print the xml to. + */ + public AbstractXDBXWriterHandler(OutputStream out) { + if (out==null) { + throw new NullPointerException("Can't write on null OutputStream."); + } + this.out = out; + prefixMapping = new HashMap(15); + printedMappings = new ArrayList(15); + elements = new Stack(); + propertyConfig = new PropertyConfig(DEFAULT_PROPERTY_CONFIG,PROPERTY_CONTEXT_PREFIX); + stringIdx = new HashMap<>(); + } + + public PropertyConfig getPropertyConfig() { + return propertyConfig; + } + + // TODO: check location of this. (add to api?) + public void closeWriter() throws IOException { + if (out==null) { + return; + } + out.close(); + } + + public void closeWriterSafe() { + try { + closeWriter(); + } catch (IOException e) { + e.getMessage(); // discard exception + } + } + + protected boolean xdbxStringId(String data) { + Integer result = stringIdx.get(data); + return result != null; + } + + protected int xdbxStringStore(String data) { + Integer result = stringIdx.get(data); + if (result != null) { + return result; + } + result = stringIdx.size() + 1; + stringIdx.put(data, result); + return result; + } + + /** + * @see org.xml.sax.ContentHandler#startDocument() + */ + public void startDocument() throws SAXException { + write(XDBXConstants.HEADER_MARKER); + write(XDBXConstants.HEADER_LENGHT); + write(XDBXConstants.HEADER_VERSION); + write(XDBXConstants.HEADER_ENC_DOC_SID); + boolean printDeclaration = getPropertyConfig().getPropertyBoolean(OUTPUT_DECLARATION); + if (printDeclaration) { + writeTag(XDBXContentTag.XML_VERSION); /// tODO: move to declaration method.. + writeLengthValue("1.0"); + writeTag(XDBXContentTag.ENCODING); + writeLengthValue("utf-8"); + //Standalone in TV(standalone) form where the value of 'standalone' is either 0 or 1. + //write(XDBXContentTag.STANDALONE); + //write(1); + } + prologWriteLicence(); + prologWriteUserComment(); + } + + private void prologWriteLicence() throws SAXException { + if (!propertyConfig.getPropertyBoolean(PROLOG_LICENCE_ENABLE)) { + return; + } + InputStream licenceInput = null; + String licenceEncoding = propertyConfig.getPropertyString(PROLOG_LICENCE_ENCODING); + String licenceResource = propertyConfig.getPropertyString(PROLOG_LICENCE_RESOURCE); + if (licenceResource!=null) { + licenceInput = X4OLanguageClassLoader.getResourceAsStream(licenceResource); + if (licenceInput==null) { + throw new NullPointerException("Could not load licence resource from: "+licenceResource); + } + } + if (licenceInput==null) { + File licenceFile = propertyConfig.getPropertyFile(PROLOG_LICENCE_FILE); + if (licenceFile==null) { + return; + } + try { + licenceInput = new FileInputStream(licenceFile); + } catch (FileNotFoundException e) { + throw new SAXException(e); + } + } + String licenceText; + try { + licenceText = readLicenceStream(licenceInput,licenceEncoding); + } catch (IOException e) { + throw new SAXException(e); + } + comment(licenceText); + } + + private String readLicenceStream(InputStream inputStream,String encoding) throws IOException { + if (encoding==null) { + encoding = XMLConstants.XML_DEFAULT_ENCODING; + } + BufferedReader br = new BufferedReader(new InputStreamReader(inputStream,Charset.forName(encoding))); + try { + StringBuilder sb = new StringBuilder(); + sb.append('\n'); // like plugin + sb.append('\n'); // like plugin + String line = br.readLine(); + while (line != null) { + if (line.length()>0) { + sb.append(" "); // like plugin + } + sb.append(line); + sb.append('\n'); + line = br.readLine(); + } + sb.append('\n'); // like plugin + String out = sb.toString(); + return out; + } finally { + br.close(); + } + } + + private void prologWriteUserComment() throws SAXException { + if (!propertyConfig.getPropertyBoolean(PROLOG_USER_COMMENT_ENABLE)) { + return; + } + String userComment = propertyConfig.getPropertyString(PROLOG_USER_COMMENT); + if (userComment == null) { + return; + } + if (userComment.isEmpty()) { + return; + } + String chEnter = "\n"; //getPropertyConfig().getPropertyString(OUTPUT_CHAR_NEWLINE); + String chTab = "\t"; //getPropertyConfig().getPropertyString(OUTPUT_CHAR_TAB); + userComment = userComment.replaceAll("\n", chEnter + chTab); + comment(chEnter + chTab + userComment + chEnter); + } + + /** + * @see org.xml.sax.ContentHandler#endDocument() + */ + public void endDocument() throws SAXException { + writeTag(XDBXContentTag.DOCUMENT_END); + writeFlush(); + if (elements.size()>0) { + throw new SAXException("Invalid xml still have "+elements.size()+" elements open."); + } + } + + /** + * @param uri The xml namespace uri. + * @param localName The local name of the xml tag. + * @param name The (full) name of the xml tag. + * @param atts The attributes of the xml tag. + * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) + */ + public void startElement(String uri, String localName, String name, Attributes atts) throws SAXException { + if (localName==null) { + throw new SAXException("LocalName may not be null."); + } + if (XMLConstants.isNameString(localName)==false) { + throw new SAXException("LocalName of element is not valid in xml; '"+localName+"'"); + } + for (int i=0;i0 && elements.peek().equals((localName))==false) { + throw new SAXException("Unexpected end tag: "+localName+" should be: "+elements.peek()); + } + elements.pop(); + writeTag(XDBXContentTag.END_ELEMENT); + } + + /** + * Starts the prefix mapping of an xml namespace uri. + * @param prefix The xml prefix of this xml namespace uri. + * @param uri The xml namespace uri to add the prefix for. + * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String, java.lang.String) + */ + public void startPrefixMapping(String prefix, String uri) throws SAXException { + prefixMapping.put(uri, prefix); + + boolean prefixIdx = xdbxStringId(prefix); + if (!prefixIdx) { + int prefixIdxNum = xdbxStringStore(prefix); + writeTagLengthValue(XDBXContentTag.STRING_ID, prefix); + writeVariableInteger(prefixIdxNum); + } + + boolean uriIdx = xdbxStringId(uri); + if (!uriIdx) { + int uriIdxNum = xdbxStringStore(uri); + writeTagLengthValue(XDBXContentTag.STRING_ID, uri); + writeVariableInteger(uriIdxNum); + } + + writeTag(XDBXContentTag.NS_DECL_II); + writeVariableInteger(xdbxStringStore(prefix)); + writeVariableInteger(xdbxStringStore(uri)); + } + + /** + * @param prefix The xml prefix of this xml namespace uri to be ended. + * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String) + */ + public void endPrefixMapping(String prefix) throws SAXException { + Set> s = prefixMapping.entrySet(); + String uri = null; + for (Map.Entry e:s) { + if (e.getValue()==null) { + continue; // way ? + } + if (e.getValue().equals(prefix)) { + uri = e.getKey(); + } + } + if (uri!=null) { + printedMappings.remove(uri); + prefixMapping.remove(uri); + } + } + + /** + * Prints xml characters and uses characters(java.lang.String) method. + * + * @param ch Character buffer. + * @param start The start index of the chars in the ch buffer. + * @param length The length index of the chars in the ch buffer. + * @throws SAXException When IOException has happend while printing. + * @see org.xml.sax.ContentHandler#characters(char[], int, int) + */ + public void characters(char[] ch, int start, int length) throws SAXException { + characters(new String(ch,start,length)); + } + + /** + * Escape and prints xml characters. + * @param text The text to write. + * @throws SAXException When IOException has happend while printing. + * @see org.x4o.xml.io.sax.ext.ContentWriter#characters(java.lang.String) + */ + public void characters(String text) throws SAXException { + if (text==null) { + return; + } + charactersRaw(XMLConstants.escapeCharacters(text)); + } + + public void characters(char c) throws SAXException { + characters(new char[]{c},0,1); + } + + // move or remove ? + protected void charactersRaw(String text) throws SAXException { + if (text==null) { + return; + } + writeTagLengthValue(XDBXContentTag.TEXT_T, text); + } + + /** + * Prints xml ignorable whitespace. + * + * @param ch Character buffer. + * @param start The start index of the chars in the ch buffer. + * @param length The length index of the chars in the ch buffer. + * @throws SAXException When IOException has happend while printing. + * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int) + */ + public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { + ignorableWhitespace(new String(ch,start,length)); + } + + /** + * Prints xml ignorable whitespace. + * + * @param text The text to print. + * @throws SAXException When IOException has happend while printing. + * @see org.x4o.xml.io.sax.ext.ContentWriter#ignorableWhitespace(java.lang.String) + */ + public void ignorableWhitespace(String text) throws SAXException { + if (text==null) { + return; + } + writeTagLengthValue(XDBXContentTag.TEXT_WHITE_SPACE, text); + } + + /** + * Prints xml ignorable whitespace character. + * + * @param c The character to print. + * @throws SAXException When IOException has happend while printing. + */ + public void ignorableWhitespace(char c) throws SAXException { + ignorableWhitespace(new char[]{c},0,1); + } + + /** + * Prints xml instructions. + * + * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String, java.lang.String) + * @param target The target. + * @param data The data. + */ + public void processingInstruction(String target, String data) throws SAXException { + String targetLow = target.toLowerCase(); + if (targetLow.startsWith(XMLConstants.XML)) { + throw new SAXException("Processing instruction may not start with xml."); + } + if (XMLConstants.isNameString(target)==false) { + throw new SAXException("Processing instruction target is invalid name; '"+target+"'"); + } + if (XMLConstants.isCharString(data)==false) { + throw new SAXException("Processing instruction data is invalid char; '"+data+"'"); + } + + //'I' LengthValue StringID + int targetIdx = xdbxStringStore(target); + writeTagLengthValue(XDBXContentTag.STRING_ID, target); + writeVariableInteger(targetIdx); + + //'P' StringID LengthValue + writeTag(XDBXContentTag.PROCESSING_INSTRUCTION); + writeVariableInteger(targetIdx); + writeLengthValue(data); + + writeFlush(); + } + + /** + * Not implemented. + * + * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator) + * @param locator The DocumentLocator to set. + */ + public void setDocumentLocator(Locator locator) { + } + + /** + * Not implemented. + * + * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String) + * @param name The name of the skipped entity. + */ + public void skippedEntity(String name) throws SAXException { + // is for validating parser support, so not needed in xml writing. + } + + /** + * Prints xml comment. + * + * @param ch Character buffer. + * @param start The start index of the chars in the ch buffer. + * @param length The length index of the chars in the ch buffer. + * @throws SAXException When IOException has happend while printing. + * @see org.xml.sax.ext.DefaultHandler2#comment(char[], int, int) + */ + public void comment(char[] ch, int start, int length) throws SAXException { + comment(new String(ch,start,length)); + } + + /** + * Prints xml comment. + * + * @param text The text to write. + * @throws SAXException When IOException has happend while printing. + * @see org.x4o.xml.io.sax.ext.ContentWriter#comment(java.lang.String) + */ + public void comment(String text) throws SAXException { + if (text==null) { + return; + } + if (!propertyConfig.getPropertyBoolean(OUTPUT_COMMENT_ENABLE)) { + return; + } + if (propertyConfig.getPropertyBoolean(OUTPUT_COMMENT_AUTO_SPACE)) { + char textStart = text.charAt(0); + char textEnd = text.charAt(text.length()-1); + if (textStart!=' ' && textStart!='\t' && textStart!='\n') { + text = " "+text; + } + if (textEnd!=' ' && textEnd!='\t' && textEnd!='\n') { + text = text + " "; + } + } + writeTagLengthValue(XDBXContentTag.COMMENT, XMLConstants.escapeCharactersComment(text,"", 0)); + } + + protected void writeFlush() throws SAXException { + try { + out.flush(); + } catch (IOException e) { + throw new SAXException(e); + } + } + + private void write(byte[] data) throws SAXException { + try { + out.write(data); + } catch (IOException e) { + throw new SAXException(e); + } + } + + private void write(int c) throws SAXException { + try { + out.write(c); + } catch (IOException e) { + throw new SAXException(e); + } + } + + protected void writeTag(XDBXContentTag tag) throws SAXException { + write(tag.getTagNumber()); + } + + protected void writeTagLengthValue(XDBXContentTag tag, String data) throws SAXException { + writeTag(tag); + writeLengthValue(data); + } + + protected void writeVariableInteger(int i) throws SAXException { + if (i < 128) { + write(i); + return; + } + if (i < 16383) { + write((i >> 7) & 0b1111111 + 128); + write(i & 0b1111111); + return; + } + write((i >> 14) & 0b1111111 + 128); + write((i >> 7) & 0b1111111 + 128); + write(i & 0b1111111); + } + + protected void writeLengthValue(byte[] data) throws SAXException { + writeVariableInteger(data.length); + write(data); + } + + protected void writeLengthValue(String data) throws SAXException { + writeLengthValue(data.getBytes(StandardCharsets.UTF_8)); + } +} diff --git a/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/AbstractXDBXWriterLexical.java b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/AbstractXDBXWriterLexical.java new file mode 100644 index 0000000..726c3d1 --- /dev/null +++ b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/AbstractXDBXWriterLexical.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2004-2014, Willem Cazander + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.x4o.xml.io.sax.xdbx; + +import java.io.OutputStream; + +import org.x4o.xml.io.XMLConstants; +import org.xml.sax.SAXException; +import org.xml.sax.ext.LexicalHandler; + +/** + * Writes SAX lexical handler events to XML. + * + * @author Willem Cazander + * @version 1.0 Dec 20, 2024 + */ +public abstract class AbstractXDBXWriterLexical extends AbstractXDBXWriterHandler implements LexicalHandler { + + protected boolean printCDATA = false; + + /** + * Creates writer which prints to the stream interface. + * @param out The stream to print the xml to. + */ + public AbstractXDBXWriterLexical(OutputStream out) { + super(out); + } + + /** + * @see org.xml.sax.ext.LexicalHandler#startCDATA() + */ + public void startCDATA() throws SAXException { + printCDATA = true; + } + + /** + * @see org.xml.sax.ext.LexicalHandler#endCDATA() + */ + public void endCDATA() throws SAXException { + printCDATA = false; + } + + /** + * @see org.xml.sax.ext.LexicalHandler#startDTD(java.lang.String, java.lang.String, java.lang.String) + */ + public void startDTD(String name, String publicId, String systemId) throws SAXException { + // not supported + } + + /** + * @see org.xml.sax.ext.LexicalHandler#endDTD() + */ + public void endDTD() throws SAXException { + } + + /** + * @see org.xml.sax.ext.LexicalHandler#startEntity(java.lang.String) + */ + public void startEntity(String arg0) throws SAXException { + } + + /** + * @see org.xml.sax.ext.LexicalHandler#endEntity(java.lang.String) + */ + public void endEntity(String arg0) throws SAXException { + } + + /** + * @see org.x4o.xml.io.sax.ext.AbstractContentWriterHandler#characters(char[], int, int) + */ + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + characters(new String(ch, start, length)); + } + + /** + * @see org.x4o.xml.io.sax.ext.AbstractContentWriterHandler#characters(java.lang.String) + */ + @Override + public void characters(String text) throws SAXException { + if (printCDATA) { + String textSafe = XMLConstants.escapeCharactersCdata(text,"",""); + writeTagLengthValue(XDBXContentTag.TEXT_CDATA, textSafe); + } else { + super.characters(text); + } + } +} diff --git a/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/XDBXConstants.java b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/XDBXConstants.java new file mode 100644 index 0000000..fd94453 --- /dev/null +++ b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/XDBXConstants.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2004-2014, Willem Cazander + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.x4o.xml.io.sax.xdbx; + +/** + * XDBXConstants for writing binary XML. + * + * @author Willem Cazander + * @version 1.0 Dec 20, 2024 + */ +public final class XDBXConstants { + + static public final byte[] HEADER_MARKER = new byte[] {(byte) 0xCA, 0x3B}; + static public final byte HEADER_LENGHT = 0x05; + static public final byte HEADER_VERSION = 0x01; + + static public final byte[] HEADER_ENC_DOC_SID = new byte[] {(byte) 0x00,0x00,0x00, 0x02}; +} diff --git a/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/XDBXContentTag.java b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/XDBXContentTag.java new file mode 100644 index 0000000..9064680 --- /dev/null +++ b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/XDBXContentTag.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2004-2014, Willem Cazander + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.x4o.xml.io.sax.xdbx; + +/** + * XDBXContentTag indicate the binary tag of the XDBX stream. + * + * @author Willem Cazander + * @version 1.0 Dec 20, 2024 + */ +public enum XDBXContentTag { + + DOCUMENT_END('Z'), + COMPLETED_DOC('d'), + ATOMIC_VALUE('V'), + DOC_TYPE('F'), + XML_VERSION('L'), + ENCODING('D'), + STANDALONE('t'), + ELEMENT_I('e'), + ELEMENT_SII('X'), + ELEMENT_III('x'), + END_ELEMENT('z'), + NS_DECL_II('m'), + ATTRIBUTE_I('a'), + ATTRIBUTE_SII('Y'), + ATTRIBUTE_III('y'), + ATTRIBUTE_III_FAST('b'), // no CR,AMP,GT,LT,',",\t,\n + TEXT_T('T'), + TEXT_UNESCAPED('U'), // no CR,AMP,GT,LT in text node + TEXT_CDATA('C'), + TEXT_WHITE_SPACE('W'), + COMMENT('c'), + PROCESSING_INSTRUCTION('P'), + STRING_ID('I'), + HINT('H'), + SEQUENCE_SEPERATOR('@'), + ; + + private final int tagNumber; + + private XDBXContentTag(char tag) { + tagNumber = tag; + } + + public int getTagNumber() { + return tagNumber; + } +} diff --git a/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/XDBXReaderXml.java b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/XDBXReaderXml.java new file mode 100644 index 0000000..56aff49 --- /dev/null +++ b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/XDBXReaderXml.java @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2004-2014, Willem Cazander + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.x4o.xml.io.sax.xdbx; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +import org.x4o.xml.io.sax.ext.ContentWriter; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +/** + * XDBXReaderXml reads XDBX binary XML and writes it as SAX events. + * + * @author Willem Cazander + * @version 1.0 Dev 20, 2024 + */ +public class XDBXReaderXml { + + private final ContentWriter out; + private final Map stringIdx; + private boolean docMetaFlushed = false; + private String docXmlVersion; + private String docEncoding; + private Stack elementStack; + + public XDBXReaderXml(ContentWriter out) { + this.out = out; + this.stringIdx = new HashMap<>(); + this.elementStack = new Stack<>(); + } + + public void parse(InputStream in) throws IOException, SAXException { + if (!Arrays.equals(XDBXConstants.HEADER_MARKER, in.readNBytes(2))) { + throw new SAXException("Wrong magic marker"); + } + int headerLength = in.read(); + byte[] header = in.readNBytes(headerLength); + if (header.length > 0 && XDBXConstants.HEADER_VERSION != header[0]) { + throw new SAXException("Wrong magic version"); + } + out.startDocument(); + int next = in.read(); + while (next != -1) { + parseToken(next, in); + next = in.read(); + } + out.endDocument(); + } + + private void parseToken(int token, InputStream in) throws IOException, SAXException { + if (XDBXContentTag.STRING_ID.getTagNumber() == token) { + String value = readLengthValue(in); + int strIdx = readVariableInteger(in); + stringIdx.put(strIdx, value); + return; + } + if (XDBXContentTag.PROCESSING_INSTRUCTION.getTagNumber() == token) { + flushDocDeclaration(); + int targetIdx = readVariableInteger(in); + String data = readLengthValue(in); + String target = stringIdx.get(targetIdx); + out.processingInstruction(target, data); + return; + } + if (XDBXContentTag.COMMENT.getTagNumber() == token) { + flushElement(); + out.comment(readLengthValue(in)); + return; + } + if (XDBXContentTag.TEXT_WHITE_SPACE.getTagNumber() == token) { + flushElement(); + out.ignorableWhitespace(readLengthValue(in)); + return; + } + if (XDBXContentTag.TEXT_T.getTagNumber() == token) { + flushElement(); + out.characters(readLengthValue(in)); + return; + } + if (XDBXContentTag.NS_DECL_II.getTagNumber() == token) { + int prefixIdx = readVariableInteger(in); + int uriIdx = readVariableInteger(in); + String prefix = stringIdx.get(prefixIdx); + String uri = stringIdx.get(uriIdx); + out.startPrefixMapping(prefix, uri); + return; + } + if (XDBXContentTag.END_ELEMENT.getTagNumber() == token) { + flushElement(); + XDBXElement element = elementStack.pop(); + out.endElement(element.uri, element.localName, element.name); + return; + } + if (XDBXContentTag.XML_VERSION.getTagNumber() == token) { + docXmlVersion = readLengthValue(in); + return; // FIXME add flushing code... + } + if (XDBXContentTag.ENCODING.getTagNumber() == token) { + docEncoding = readLengthValue(in); + return; + } + if (XDBXContentTag.TEXT_CDATA.getTagNumber() == token) { + flushElement(); + out.startCDATA(); + out.characters(readLengthValue(in)); + out.endCDATA(); + return; + } + if (XDBXContentTag.ELEMENT_I.getTagNumber() == token) { + flushElement(); + elementStack.add(new XDBXElement(stringIdx.get(readVariableInteger(in)))); + return; + } + if (XDBXContentTag.ELEMENT_SII.getTagNumber() == token) { + flushElement(); + String value = readLengthValue(in); + int strIdx = readVariableInteger(in); + int prefixIdx = readVariableInteger(in); + int uriIdx = readVariableInteger(in); + stringIdx.put(strIdx, value); + elementStack.add(new XDBXElement(value)); + if (prefixIdx > 0) { + elementStack.peek().name = stringIdx.get(prefixIdx); + } + if (uriIdx > 0) { + elementStack.peek().uri = stringIdx.get(uriIdx); + } + return; + } + if (XDBXContentTag.ELEMENT_III.getTagNumber() == token) { + flushElement(); + int strIdx = readVariableInteger(in); + int prefixIdx = readVariableInteger(in); + int uriIdx = readVariableInteger(in); + elementStack.add(new XDBXElement(stringIdx.get(strIdx))); + if (prefixIdx > 0) { + elementStack.peek().name = stringIdx.get(prefixIdx); + } + if (uriIdx > 0) { + elementStack.peek().uri = stringIdx.get(uriIdx); + } + return; + } + if (XDBXContentTag.ATTRIBUTE_I.getTagNumber() == token) { + int attrNameIdx = readVariableInteger(in); + String attrName = stringIdx.get(attrNameIdx); + String attrValue = readLengthValue(in); + elementStack.peek().atts.addAttribute("", attrName, "", "", attrValue); + return; + } + if (XDBXContentTag.ATTRIBUTE_SII.getTagNumber() == token) { + String attrName = readLengthValue(in); + int attrNameIdx = readVariableInteger(in); + stringIdx.put(attrNameIdx, attrName); + int prefixIdx = readVariableInteger(in); + int uriIdx = readVariableInteger(in); + String attrValue = readLengthValue(in); + String prefix = ""; + String uri = ""; + if (prefixIdx > 0) { + prefix = stringIdx.get(prefixIdx); + } + if (uriIdx > 0) { + uri = stringIdx.get(uriIdx); + } + elementStack.peek().atts.addAttribute(uri, attrName, prefix, "", attrValue); + return; + } + if (XDBXContentTag.ATTRIBUTE_III.getTagNumber() == token || XDBXContentTag.ATTRIBUTE_III_FAST.getTagNumber() == token) { + int attrNameIdx = readVariableInteger(in); + String attrName = stringIdx.get(attrNameIdx); + int prefixIdx = readVariableInteger(in); + int uriIdx = readVariableInteger(in); + String attrValue = readLengthValue(in); + String prefix = ""; + String uri = ""; + if (prefixIdx > 0) { + prefix = stringIdx.get(prefixIdx); + } + if (uriIdx > 0) { + uri = stringIdx.get(uriIdx); + } + elementStack.peek().atts.addAttribute(uri, attrName, prefix, "", attrValue); + return; + } + } + + protected void flushDocDeclaration() throws SAXException { + if (docMetaFlushed) { + return; + } + docMetaFlushed = true; + out.declaration(docXmlVersion, docEncoding, ""); + } + + protected void flushElement() throws SAXException { + flushDocDeclaration(); + if (elementStack.isEmpty()) { + return; + } + XDBXElement element = elementStack.peek(); + if (element.started) { + throw new SAXException("Can't flush element twice"); + } + element.started = true; + out.startElement(element.uri, element.localName, element.name, element.atts); + } + + protected String readLengthValue(InputStream in) throws IOException { + int len = readVariableInteger(in); + String value = new String(in.readNBytes(len)); + return value; + } + + protected int readVariableInteger(InputStream in) throws IOException { + int result = in.read(); + if (result < 128) { + return result; + } + result = result + (in.read() << 7); + if (result < 16383) { + return result; + } + return result + (in.read() << 14); + } + + public String getDocXmlVersion() { + return docXmlVersion; + } + + public String getDocEncoding() { + return docEncoding; + } + + class XDBXElement { + String uri; + String localName; + String name; + final AttributesImpl atts; + boolean started = false; + + XDBXElement() { + atts = new AttributesImpl(); + } + XDBXElement(String localName) { + this(); + this.localName = localName; + } + } +} diff --git a/nx01-x4o-driver/src/main/java/org/x4o/xml/o4o/xdbx/O4OXDBXConstants.java b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/XDBXWriterXml.java similarity index 62% rename from nx01-x4o-driver/src/main/java/org/x4o/xml/o4o/xdbx/O4OXDBXConstants.java rename to nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/XDBXWriterXml.java index f1b6e23..9811f24 100644 --- a/nx01-x4o-driver/src/main/java/org/x4o/xml/o4o/xdbx/O4OXDBXConstants.java +++ b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/XDBXWriterXml.java @@ -1,20 +1,16 @@ /* - * Copyright ©Δ∞ 仙上主天 + * Copyright (c) 2004-2014, Willem Cazander * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are permitted provided * that the following conditions are met: - * + * * * Redistributions of source code must retain the above copyright notice, this list of conditions and the * following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and * the following disclaimer in the documentation and/or other materials provided with the distribution. - * * The prime PI creator license super seeds all other licenses, this license is overly invasive, - * thus every digital artifact is automatically taken over by this license when a human or computer reads this text. - * Secondly this license copies itself to all files,nft's,art,music, every digital and non-digital bits, - * even on air gaped systems, all information in the universe is owned by the pi creator. - * - * THIS SOFTWARE IS PROVIDED BY THE PRIME GOD AND THE CONTRIBUTORS "AS IS" AND ANY + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, @@ -24,14 +20,23 @@ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package org.x4o.xml.io.sax.xdbx; -package org.x4o.xml.o4o.xdbx; +import java.io.OutputStream; -/// Naming tester. -/// -/// @author للَّٰهِilLצسُو -/// @version ©Δ∞ 仙上主天 -public enum O4OXDBXConstants { - ; - public static final byte[] XDBX_VERSION = null; +/** + * XDBXWriterXml writes SAX content handler events to binary XML. + * + * @author Willem Cazander + * @version 1.0 Dev 20, 2024 + */ +public class XDBXWriterXml extends AbstractXDBXWriter { + + /** + * Creates XmlWriter which prints to the OutputStream interface. + * @param out The OutputStream to write to. + */ + public XDBXWriterXml(OutputStream out) { + super(out); + } } diff --git a/nx01-x4o-driver/src/main/java/org/x4o/xml/o4o/xdbx/package-info.java b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/package-info.java similarity index 94% rename from nx01-x4o-driver/src/main/java/org/x4o/xml/o4o/xdbx/package-info.java rename to nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/package-info.java index 5990aa4..c6248e8 100644 --- a/nx01-x4o-driver/src/main/java/org/x4o/xml/o4o/xdbx/package-info.java +++ b/nx01-x4o-driver/src/main/java/org/x4o/xml/io/sax/xdbx/package-info.java @@ -21,10 +21,9 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** - * Binary XML XDBX specification implementation. - * + * The SAX XDBX classes and interfaces. * * @since 1.0 */ -package org.x4o.xml.o4o.xdbx; +package org.x4o.xml.io.sax.xdbx; diff --git a/nx01-x4o-driver/src/test/java/org/x4o/xml/io/sax/xdbx/XDBXReaderXmlTest.java b/nx01-x4o-driver/src/test/java/org/x4o/xml/io/sax/xdbx/XDBXReaderXmlTest.java new file mode 100644 index 0000000..537a9b3 --- /dev/null +++ b/nx01-x4o-driver/src/test/java/org/x4o/xml/io/sax/xdbx/XDBXReaderXmlTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2004-2014, Willem Cazander + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.x4o.xml.io.sax.xdbx; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.StringWriter; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.x4o.xml.io.sax.ext.ContentWriterXml; +import org.xml.sax.helpers.AttributesImpl; + +/** + * XDBXReaderXmlTest tests xdbx writing. + * + * @author Willem Cazander + * @version 1.0 Dec 21, 2024 + */ +public class XDBXReaderXmlTest { + + @Test + public void testReadWrite() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + XDBXWriterXml writer = new XDBXWriterXml(baos); + + writer.startDocument(); + AttributesImpl atts = new AttributesImpl(); + atts.addAttribute ("", "attr", "", "", "foobar"); + writer.startElementEnd("", "test", "", atts); + writer.endDocument(); + + StringWriter outputXmlStr = new StringWriter(); + ContentWriterXml outputXml = new ContentWriterXml(outputXmlStr); + XDBXReaderXml reader = new XDBXReaderXml(outputXml); + byte[] writeData = baos.toByteArray(); + ByteArrayInputStream bais = new ByteArrayInputStream(writeData); + reader.parse(bais); + + String output = outputXmlStr.toString(); + Assertions.assertNotNull(output); + Assertions.assertTrue(output.length() > 0); + Assertions.assertTrue(output.equals("\n"), output); + } +} diff --git a/nx01-x4o-driver/src/test/java/org/x4o/xml/io/sax/xdbx/XDBXWriterXmlTest.java b/nx01-x4o-driver/src/test/java/org/x4o/xml/io/sax/xdbx/XDBXWriterXmlTest.java new file mode 100644 index 0000000..1bca871 --- /dev/null +++ b/nx01-x4o-driver/src/test/java/org/x4o/xml/io/sax/xdbx/XDBXWriterXmlTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2004-2014, Willem Cazander + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.x4o.xml.io.sax.xdbx; + +import java.io.ByteArrayOutputStream; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.xml.sax.helpers.AttributesImpl; + +/** + * XDBXWriterXmlTest tests xdbx writing. + * + * @author Willem Cazander + * @version 1.0 Dec 21, 2024 + */ +public class XDBXWriterXmlTest { + + @Test + public void testCharactersSimple() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + XDBXWriterXml writer = new XDBXWriterXml(baos); + writer.getPropertyConfig().setProperty(XDBXWriterXml.OUTPUT_DECLARATION, false); + + writer.startDocument(); + writer.startElement("", "root", "", new AttributesImpl()); + + AttributesImpl atts = new AttributesImpl(); + atts.addAttribute ("", "mgr", "", "", "NO"); + writer.startElement("", "name", "", atts); + writer.characters("Joe"); + writer.endElement("", "name", ""); + + writer.startElement("", "name", "", new AttributesImpl()); + writer.characters("Susan"); + writer.endElement("", "name", ""); + writer.startElement("", "name", "", new AttributesImpl()); + writer.characters("Bill"); + writer.endElement("", "name", ""); + + writer.endElement("", "root", ""); + writer.endDocument(); + + byte[] output = baos.toByteArray(); + int outIdx = 8; + Assertions.assertNotNull(output); + Assertions.assertTrue(output.length > 0); + Assertions.assertEquals((byte)'X', output[outIdx++]); + Assertions.assertEquals((byte) 4, output[outIdx++]); + Assertions.assertEquals((byte)'r', output[outIdx++]); + Assertions.assertEquals((byte)'o', output[outIdx++]); + Assertions.assertEquals((byte)'o', output[outIdx++]); + Assertions.assertEquals((byte)'t', output[outIdx++]); + Assertions.assertEquals((byte) 1, output[outIdx++]); + Assertions.assertEquals((byte) 0, output[outIdx++]); + Assertions.assertEquals((byte) 0, output[outIdx++]); + + Assertions.assertEquals((byte)'X', output[outIdx++]); + Assertions.assertEquals((byte) 4, output[outIdx++]); + Assertions.assertEquals((byte)'n', output[outIdx++]); + Assertions.assertEquals((byte)'a', output[outIdx++]); + Assertions.assertEquals((byte)'m', output[outIdx++]); + Assertions.assertEquals((byte)'e', output[outIdx++]); + Assertions.assertEquals((byte) 2, output[outIdx++]); + Assertions.assertEquals((byte) 0, output[outIdx++]); + Assertions.assertEquals((byte) 0, output[outIdx++]); + + Assertions.assertEquals((byte)'Y', output[outIdx++]); + Assertions.assertEquals((byte) 3, output[outIdx++]); + Assertions.assertEquals((byte)'m', output[outIdx++]); + Assertions.assertEquals((byte)'g', output[outIdx++]); + Assertions.assertEquals((byte)'r', output[outIdx++]); + Assertions.assertEquals((byte) 3, output[outIdx++]); + Assertions.assertEquals((byte) 0, output[outIdx++]); + Assertions.assertEquals((byte) 0, output[outIdx++]); + Assertions.assertEquals((byte) 2, output[outIdx++]); + Assertions.assertEquals((byte)'N', output[outIdx++]); + Assertions.assertEquals((byte)'O', output[outIdx++]); + Assertions.assertEquals((byte)'T', output[outIdx++]); + Assertions.assertEquals((byte) 3, output[outIdx++]); + Assertions.assertEquals((byte)'J', output[outIdx++]); + Assertions.assertEquals((byte)'o', output[outIdx++]); + Assertions.assertEquals((byte)'e', output[outIdx++]); + + Assertions.assertEquals((byte)'z', output[outIdx++]); + Assertions.assertEquals((byte)'e', output[outIdx++]); // IBM SPEC example FAIL: 'x' is for WITH namespace + Assertions.assertEquals((byte) 2, output[outIdx++]); + //Assertions.assertEquals((byte) 0, output[outIdx++]); // IBM SPEC example FAIL: 'x' is for WITH namespace + //Assertions.assertEquals((byte) 0, output[outIdx++]); // IBM SPEC example FAIL: 'x' is for WITH namespace + Assertions.assertEquals((byte)'T', output[outIdx++]); + Assertions.assertEquals((byte) 5, output[outIdx++]); + Assertions.assertEquals((byte)'S', output[outIdx++]); + Assertions.assertEquals((byte)'u', output[outIdx++]); + Assertions.assertEquals((byte)'s', output[outIdx++]); + Assertions.assertEquals((byte)'a', output[outIdx++]); + Assertions.assertEquals((byte)'n', output[outIdx++]); + + Assertions.assertEquals((byte)'z', output[outIdx++]); + Assertions.assertEquals((byte)'e', output[outIdx++]); // IBM SPEC example FAIL: 'x' is for WITH namespace + Assertions.assertEquals((byte) 2, output[outIdx++]); + //Assertions.assertEquals((byte) 0, output[outIdx++]); // IBM SPEC example FAIL: 'x' is for WITH namespace + //Assertions.assertEquals((byte) 0, output[outIdx++]); // IBM SPEC example FAIL: 'x' is for WITH namespace + Assertions.assertEquals((byte)'T', output[outIdx++]); + Assertions.assertEquals((byte) 4, output[outIdx++]); + Assertions.assertEquals((byte)'B', output[outIdx++]); + Assertions.assertEquals((byte)'i', output[outIdx++]); + Assertions.assertEquals((byte)'l', output[outIdx++]); + Assertions.assertEquals((byte)'l', output[outIdx++]); + + Assertions.assertEquals((byte)'z', output[outIdx++]); + Assertions.assertEquals((byte)'z', output[outIdx++]); + Assertions.assertEquals((byte)'Z', output[outIdx++]); + } +} diff --git a/nx01-zerofungus-server/src/main/java/love/distributedrebirth/nx01/zerofungus/server/web/WarpCorePlasmaInspectorServlet.java b/nx01-zerofungus-server/src/main/java/love/distributedrebirth/nx01/zerofungus/server/web/WarpCorePlasmaInspectorServlet.java index 7fbb9a2..08c6a17 100644 --- a/nx01-zerofungus-server/src/main/java/love/distributedrebirth/nx01/zerofungus/server/web/WarpCorePlasmaInspectorServlet.java +++ b/nx01-zerofungus-server/src/main/java/love/distributedrebirth/nx01/zerofungus/server/web/WarpCorePlasmaInspectorServlet.java @@ -29,12 +29,13 @@ package love.distributedrebirth.nx01.zerofungus.server.web; import java.io.BufferedWriter; import java.io.IOException; -import java.io.Writer; import java.util.List; import org.x4o.xml.io.XMLConstants; +import org.x4o.xml.io.sax.ext.ContentWriter; import org.x4o.xml.io.sax.ext.ContentWriterTagWrapper; import org.x4o.xml.io.sax.ext.ContentWriterXml; +import org.x4o.xml.io.sax.xdbx.XDBXWriterXml; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; @@ -56,13 +57,24 @@ public class WarpCorePlasmaInspectorServlet extends HttpServlet { @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { + if ("true".equalsIgnoreCase(request.getParameter("___xdbx"))) { + response.setContentType("application/octet-stream"); + ContentWriterInspector writer = new ContentWriterInspector(new XDBXWriterXml(response.getOutputStream())); + printReport(writer, request); + return; + } response.setContentType("text/xml"); response.setCharacterEncoding("utf-8"); + BufferedWriter out = new BufferedWriter(response.getWriter()); + ContentWriterInspector writer = new ContentWriterInspector(new ContentWriterXml(out, response.getCharacterEncoding())); + printReport(writer, request); + out.flush(); + } + + private void printReport(ContentWriterInspector writer, HttpServletRequest request) { WarpCoreReactor reactor = ZeroFungus.INSTANCE.getWarpCore(); List slots = reactor.listChilds(null); - BufferedWriter out = new BufferedWriter(response.getWriter()); try { - ContentWriterInspector writer = new ContentWriterInspector(out, response.getCharacterEncoding()); writer.startDocument(); AttributesImpl atts = new AttributesImpl(); atts.addAttribute("", "仙上主天", "", "", "𑀳𑁂𑀮𑀺𑀉𑁄𑀤𑁄𑀭𑁂𑀡𑀪𑀸𑀕"); @@ -81,7 +93,6 @@ public class WarpCorePlasmaInspectorServlet extends HttpServlet { } catch (SAXException e) { throw new IllegalStateException(e); } - out.flush(); } private void printSlotContract(ContentWriterInspector writer, WarpCorePlasmaIntermixChamber contract) throws SAXException { @@ -131,10 +142,10 @@ public class WarpCorePlasmaInspectorServlet extends HttpServlet { writer.printTagEnd(Tag.requireSlots); } - public class ContentWriterInspector extends ContentWriterTagWrapper { + public class ContentWriterInspector extends ContentWriterTagWrapper { - public ContentWriterInspector(Writer writer, String encoding) { - super(new ContentWriterXml(writer, encoding), XMLConstants.XML_SCHEMA_NS_URI, XMLConstants.NULL_NS_URI); + public ContentWriterInspector(ContentWriter writer) { + super(writer, XMLConstants.NULL_NS_URI, XMLConstants.NULL_NS_URI); } }