diff --git a/NOTICE.txt b/NOTICE.txt index 9b71e46658..2ea45edfa2 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -195,3 +195,11 @@ can be obtained at: * HOMEPAGE: * http://logging.apache.org/log4j/ +This product optionally depends on 'Aalto XML', an ultra-high performance +non-blocking XML processor, which can be obtained at: + + * LICENSE: + * license/LICENSE.aalto-xml.txt (Apache License 2.0) + * HOMEPAGE: + * http://wiki.fasterxml.com/AaltoHome + diff --git a/codec-xml/pom.xml b/codec-xml/pom.xml new file mode 100644 index 0000000000..f3938758cc --- /dev/null +++ b/codec-xml/pom.xml @@ -0,0 +1,48 @@ + + + + + 4.0.0 + + io.netty + netty-parent + 4.1.0.Beta4-SNAPSHOT + + + netty-codec-xml + jar + + Netty/Codec/XML + + + + ${project.groupId} + netty-codec + ${project.version} + + + ${project.groupId} + netty-handler + ${project.version} + + + com.fasterxml + aalto-xml + + + + diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlAttribute.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlAttribute.java new file mode 100644 index 0000000000..d40ce138c3 --- /dev/null +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlAttribute.java @@ -0,0 +1,94 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.xml; + + +/** + * XML attributes, it is part of {@link XmlElement} + */ +public class XmlAttribute { + + private final String type; + private final String name; + private final String prefix; + private final String namespace; + private final String value; + + public XmlAttribute(String type, String name, String prefix, String namespace, String value) { + this.type = type; + this.name = name; + this.prefix = prefix; + this.namespace = namespace; + this.value = value; + } + + public String type() { + return type; + } + + public String name() { + return name; + } + + public String prefix() { + return prefix; + } + + public String namespace() { + return namespace; + } + + public String value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + + XmlAttribute that = (XmlAttribute) o; + + if (!name.equals(that.name)) { return false; } + if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) { return false; } + if (prefix != null ? !prefix.equals(that.prefix) : that.prefix != null) { return false; } + if (type != null ? !type.equals(that.type) : that.type != null) { return false; } + if (value != null ? !value.equals(that.value) : that.value != null) { return false; } + + return true; + } + + @Override + public int hashCode() { + int result = type != null ? type.hashCode() : 0; + result = 31 * result + name.hashCode(); + result = 31 * result + (prefix != null ? prefix.hashCode() : 0); + result = 31 * result + (namespace != null ? namespace.hashCode() : 0); + result = 31 * result + (value != null ? value.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "XmlAttribute{" + + "type='" + type + '\'' + + ", name='" + name + '\'' + + ", prefix='" + prefix + '\'' + + ", namespace='" + namespace + '\'' + + ", value='" + value + '\'' + + '}'; + } +} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlCdata.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlCdata.java new file mode 100644 index 0000000000..e1e96a8572 --- /dev/null +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlCdata.java @@ -0,0 +1,27 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.xml; + +/** + * XML CDATA ... + */ +public class XmlCdata extends XmlContent { + + public XmlCdata(String data) { + super(data); + } + +} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlCharacters.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlCharacters.java new file mode 100644 index 0000000000..f29a997b79 --- /dev/null +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlCharacters.java @@ -0,0 +1,27 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.xml; + +/** + * XML characters, e.g. <test>characters</test>, but not CDATA. + */ +public class XmlCharacters extends XmlContent { + + public XmlCharacters(String data) { + super(data); + } + +} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlComment.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlComment.java new file mode 100644 index 0000000000..d01da0d404 --- /dev/null +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlComment.java @@ -0,0 +1,27 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.xml; + +/** + * XML Comment + */ +public class XmlComment extends XmlContent { + + public XmlComment(String data) { + super(data); + } + +} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlContent.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlContent.java new file mode 100644 index 0000000000..a47df3312c --- /dev/null +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlContent.java @@ -0,0 +1,56 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.xml; + +/** + * XML Content is base class for XML CDATA, Comments, Characters and Space + */ +public abstract class XmlContent { + + private final String data; + + protected XmlContent(String data) { + this.data = data; + } + + public String data() { + return data; + } + + @Override + public boolean equals(Object o) { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + + XmlContent that = (XmlContent) o; + + if (data != null ? !data.equals(that.data) : that.data != null) { return false; } + + return true; + } + + @Override + public int hashCode() { + return data != null ? data.hashCode() : 0; + } + + @Override + public String toString() { + return "XmlContent{" + + "data='" + data + '\'' + + '}'; + } +} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDTD.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDTD.java new file mode 100644 index 0000000000..754539b243 --- /dev/null +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDTD.java @@ -0,0 +1,57 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.xml; + +/** + * DTD (Document Type Definition) + */ +public class XmlDTD { + + private final String text; + + public XmlDTD(String text) { + this.text = text; + } + + public String text() { + return text; + } + + @Override + public boolean equals(Object o) { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + + XmlDTD xmlDTD = (XmlDTD) o; + + if (text != null ? !text.equals(xmlDTD.text) : xmlDTD.text != null) { return false; } + + return true; + } + + @Override + public int hashCode() { + return text != null ? text.hashCode() : 0; + } + + @Override + public String toString() { + return "XmlDTD{" + + "text='" + text + '\'' + + '}'; + } + +} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDecoder.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDecoder.java new file mode 100644 index 0000000000..a5add5de0d --- /dev/null +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDecoder.java @@ -0,0 +1,116 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.xml; + +import com.fasterxml.aalto.AsyncInputFeeder; +import com.fasterxml.aalto.AsyncXMLInputFactory; +import com.fasterxml.aalto.AsyncXMLStreamReader; +import com.fasterxml.aalto.stax.InputFactoryImpl; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; + +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import java.util.List; + +/** + * Async XML decoder based on Aalto XML parser. + * + * Parses the incoming data into one of XML messages defined in this package. + */ + +public class XmlDecoder extends ByteToMessageDecoder { + + private static final AsyncXMLInputFactory XML_INPUT_FACTORY = new InputFactoryImpl(); + private static final XmlDocumentEnd XML_DOCUMENT_END = XmlDocumentEnd.INSTANCE; + + private final AsyncXMLStreamReader streamReader = XML_INPUT_FACTORY.createAsyncXMLStreamReader(); + private final AsyncInputFeeder streamFeeder = streamReader.getInputFeeder(); + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + byte[] buffer = new byte[in.readableBytes()]; + in.readBytes(buffer); + try { + streamFeeder.feedInput(buffer, 0, buffer.length); + } catch (XMLStreamException exception) { + in.skipBytes(in.readableBytes()); + throw exception; + } + + while (!streamFeeder.needMoreInput()) { + int type = streamReader.next(); + switch (type) { + case XMLStreamConstants.START_DOCUMENT: + out.add(new XmlDocumentStart(streamReader.getEncoding(), streamReader.getVersion(), + streamReader.isStandalone(), streamReader.getCharacterEncodingScheme())); + break; + case XMLStreamConstants.END_DOCUMENT: + out.add(XML_DOCUMENT_END); + break; + case XMLStreamConstants.START_ELEMENT: + XmlElementStart elementStart = new XmlElementStart(streamReader.getLocalName(), + streamReader.getName().getNamespaceURI(), streamReader.getPrefix()); + for (int x = 0; x < streamReader.getAttributeCount(); x++) { + XmlAttribute attribute = new XmlAttribute(streamReader.getAttributeType(x), + streamReader.getAttributeLocalName(x), streamReader.getAttributePrefix(x), + streamReader.getAttributeNamespace(x), streamReader.getAttributeValue(x)); + elementStart.attributes().add(attribute); + } + for (int x = 0; x < streamReader.getNamespaceCount(); x++) { + XmlNamespace namespace = new XmlNamespace(streamReader.getNamespacePrefix(x), + streamReader.getNamespaceURI(x)); + elementStart.namespaces().add(namespace); + } + out.add(elementStart); + break; + case XMLStreamConstants.END_ELEMENT: + XmlElementEnd elementEnd = new XmlElementEnd(streamReader.getLocalName(), + streamReader.getName().getNamespaceURI(), streamReader.getPrefix()); + for (int x = 0; x < streamReader.getNamespaceCount(); x++) { + XmlNamespace namespace = new XmlNamespace(streamReader.getNamespacePrefix(x), + streamReader.getNamespaceURI(x)); + elementEnd.namespaces().add(namespace); + } + out.add(elementEnd); + break; + case XMLStreamConstants.PROCESSING_INSTRUCTION: + out.add(new XmlProcessingInstruction(streamReader.getPIData(), streamReader.getPITarget())); + break; + case XMLStreamConstants.CHARACTERS: + out.add(new XmlCharacters(streamReader.getText())); + break; + case XMLStreamConstants.COMMENT: + out.add(new XmlComment(streamReader.getText())); + break; + case XMLStreamConstants.SPACE: + out.add(new XmlSpace(streamReader.getText())); + break; + case XMLStreamConstants.ENTITY_REFERENCE: + out.add(new XmlEntityReference(streamReader.getLocalName(), streamReader.getText())); + break; + case XMLStreamConstants.DTD: + out.add(new XmlDTD(streamReader.getText())); + break; + case XMLStreamConstants.CDATA: + out.add(new XmlCdata(streamReader.getText())); + break; + } + } + } + +} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDocumentEnd.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDocumentEnd.java new file mode 100644 index 0000000000..6e7394517b --- /dev/null +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDocumentEnd.java @@ -0,0 +1,28 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.xml; + +/** + * End of XML document + */ +public final class XmlDocumentEnd { + + public static final XmlDocumentEnd INSTANCE = new XmlDocumentEnd(); + + private XmlDocumentEnd() { + } + +} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDocumentStart.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDocumentStart.java new file mode 100644 index 0000000000..311a1f5702 --- /dev/null +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlDocumentStart.java @@ -0,0 +1,90 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.xml; + +/** + * Beginning of the XML document ... i.e. XML header + */ +public class XmlDocumentStart { + + private final String encoding; + private final String version; + private final boolean standalone; + private final String encodingScheme; + + public XmlDocumentStart(String encoding, String version, boolean standalone, String encodingScheme) { + this.encoding = encoding; + this.version = version; + this.standalone = standalone; + this.encodingScheme = encodingScheme; + } + + /** Return defined or guessed XML encoding **/ + public String encoding() { + return encoding; + } + + /** Return defined XML version or null **/ + public String version() { + return version; + } + + /** Return standalonity of the document **/ + public boolean standalone() { + return standalone; + } + + /** Return defined encoding or null **/ + public String encodingScheme() { + return encodingScheme; + } + + @Override + public boolean equals(Object o) { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + + XmlDocumentStart that = (XmlDocumentStart) o; + + if (standalone != that.standalone) { return false; } + if (encoding != null ? !encoding.equals(that.encoding) : that.encoding != null) { return false; } + if (encodingScheme != null ? !encodingScheme.equals(that.encodingScheme) : that.encodingScheme != null) { + return false; + } + if (version != null ? !version.equals(that.version) : that.version != null) { return false; } + + return true; + } + + @Override + public int hashCode() { + int result = encoding != null ? encoding.hashCode() : 0; + result = 31 * result + (version != null ? version.hashCode() : 0); + result = 31 * result + (standalone ? 1 : 0); + result = 31 * result + (encodingScheme != null ? encodingScheme.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "XmlDocumentStart{" + + "encoding='" + encoding + '\'' + + ", version='" + version + '\'' + + ", standalone=" + standalone + + ", encodingScheme='" + encodingScheme + '\'' + + '}'; + } +} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElement.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElement.java new file mode 100644 index 0000000000..885e814a09 --- /dev/null +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElement.java @@ -0,0 +1,87 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.xml; + +import java.util.LinkedList; +import java.util.List; + +/** + * Generic XML element in document, {@link XmlElementStart} represents open element and provides access to attributes, + * {@link XmlElementEnd} represents closing element. + */ +public abstract class XmlElement { + + private final String name; + private final String namespace; + private final String prefix; + + private final List namespaces = new LinkedList(); + + protected XmlElement(String name, String namespace, String prefix) { + this.name = name; + this.namespace = namespace; + this.prefix = prefix; + } + + public String name() { + return name; + } + + public String namespace() { + return namespace; + } + + public String prefix() { + return prefix; + } + + public List namespaces() { + return namespaces; + } + + @Override + public boolean equals(Object o) { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + + XmlElement that = (XmlElement) o; + + if (!name.equals(that.name)) { return false; } + if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) { return false; } + if (namespaces != null ? !namespaces.equals(that.namespaces) : that.namespaces != null) { return false; } + if (prefix != null ? !prefix.equals(that.prefix) : that.prefix != null) { return false; } + + return true; + } + + @Override + public int hashCode() { + int result = name.hashCode(); + result = 31 * result + (namespace != null ? namespace.hashCode() : 0); + result = 31 * result + (prefix != null ? prefix.hashCode() : 0); + result = 31 * result + (namespaces != null ? namespaces.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return ", name='" + name + '\'' + + ", namespace='" + namespace + '\'' + + ", prefix='" + prefix + '\'' + + ", namespaces=" + namespaces; + } + +} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElementEnd.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElementEnd.java new file mode 100644 index 0000000000..67dd384e65 --- /dev/null +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElementEnd.java @@ -0,0 +1,34 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.xml; + +/** + * Specific {@link XmlElement} representing end of element. + */ +public class XmlElementEnd extends XmlElement { + + public XmlElementEnd(String name, String namespace, String prefix) { + super(name, namespace, prefix); + } + + @Override + public String toString() { + return "XmlElementStart{" + + super.toString() + + "} "; + } + +} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElementStart.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElementStart.java new file mode 100644 index 0000000000..17d603eb4c --- /dev/null +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlElementStart.java @@ -0,0 +1,64 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.xml; + +import java.util.LinkedList; +import java.util.List; + +/** + * Specific {@link XmlElement} representing beginning of element. + */ +public class XmlElementStart extends XmlElement { + + private final List attributes = new LinkedList(); + + public XmlElementStart(String name, String namespace, String prefix) { + super(name, namespace, prefix); + } + + public List attributes() { + return attributes; + } + + @Override + public boolean equals(Object o) { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + if (!super.equals(o)) { return false; } + + XmlElementStart that = (XmlElementStart) o; + + if (attributes != null ? !attributes.equals(that.attributes) : that.attributes != null) { return false; } + + return true; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (attributes != null ? attributes.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "XmlElementStart{" + + "attributes=" + attributes + + super.toString() + + "} "; + } + +} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlEntityReference.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlEntityReference.java new file mode 100644 index 0000000000..ba3cba9ada --- /dev/null +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlEntityReference.java @@ -0,0 +1,66 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.xml; + +/** + * XML entity reference ... {@code &#nnnn;} + */ +public class XmlEntityReference { + + private final String name; + private final String text; + + public XmlEntityReference(String name, String text) { + this.name = name; + this.text = text; + } + + public String name() { + return name; + } + + public String text() { + return text; + } + + @Override + public boolean equals(Object o) { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + + XmlEntityReference that = (XmlEntityReference) o; + + if (name != null ? !name.equals(that.name) : that.name != null) { return false; } + if (text != null ? !text.equals(that.text) : that.text != null) { return false; } + + return true; + } + + @Override + public int hashCode() { + int result = name != null ? name.hashCode() : 0; + result = 31 * result + (text != null ? text.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "XmlEntityReference{" + + "name='" + name + '\'' + + ", text='" + text + '\'' + + '}'; + } +} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlNamespace.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlNamespace.java new file mode 100644 index 0000000000..9cbb86fb42 --- /dev/null +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlNamespace.java @@ -0,0 +1,67 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.xml; + +/** + * XML namespace is part of XML element. + */ +public class XmlNamespace { + + private final String prefix; + private final String uri; + + public XmlNamespace(String prefix, String uri) { + this.prefix = prefix; + this.uri = uri; + } + + public String prefix() { + return prefix; + } + + public String uri() { + return uri; + } + + @Override + public boolean equals(Object o) { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + + XmlNamespace that = (XmlNamespace) o; + + if (prefix != null ? !prefix.equals(that.prefix) : that.prefix != null) { return false; } + if (uri != null ? !uri.equals(that.uri) : that.uri != null) { return false; } + + return true; + } + + @Override + public int hashCode() { + int result = prefix != null ? prefix.hashCode() : 0; + result = 31 * result + (uri != null ? uri.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "XmlNamespace{" + + "prefix='" + prefix + '\'' + + ", uri='" + uri + '\'' + + '}'; + } + +} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlProcessingInstruction.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlProcessingInstruction.java new file mode 100644 index 0000000000..27dc4deee0 --- /dev/null +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlProcessingInstruction.java @@ -0,0 +1,67 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.xml; + +/** + * XML processing instruction + */ +public class XmlProcessingInstruction { + + private final String data; + private final String target; + + public XmlProcessingInstruction(String data, String target) { + this.data = data; + this.target = target; + } + + public String data() { + return data; + } + + public String target() { + return target; + } + + @Override + public boolean equals(Object o) { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + + XmlProcessingInstruction that = (XmlProcessingInstruction) o; + + if (data != null ? !data.equals(that.data) : that.data != null) { return false; } + if (target != null ? !target.equals(that.target) : that.target != null) { return false; } + + return true; + } + + @Override + public int hashCode() { + int result = data != null ? data.hashCode() : 0; + result = 31 * result + (target != null ? target.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "XmlProcessingInstruction{" + + "data='" + data + '\'' + + ", target='" + target + '\'' + + '}'; + } + +} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlSpace.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlSpace.java new file mode 100644 index 0000000000..3944b442b2 --- /dev/null +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/XmlSpace.java @@ -0,0 +1,27 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.xml; + +/** + * White space characters + */ +public class XmlSpace extends XmlContent { + + public XmlSpace(String data) { + super(data); + } + +} diff --git a/codec-xml/src/main/java/io/netty/handler/codec/xml/package-info.java b/codec-xml/src/main/java/io/netty/handler/codec/xml/package-info.java new file mode 100644 index 0000000000..f5d681360a --- /dev/null +++ b/codec-xml/src/main/java/io/netty/handler/codec/xml/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +/** + * XML codec provides asynchronous and non-blocking XML parser based on the + * Aalto XML parser. + */ +package io.netty.handler.codec.xml; diff --git a/codec-xml/src/test/java/io/netty/handler/codec/xml/XmlDecoderTest.java b/codec-xml/src/test/java/io/netty/handler/codec/xml/XmlDecoderTest.java new file mode 100644 index 0000000000..1d623d605b --- /dev/null +++ b/codec-xml/src/test/java/io/netty/handler/codec/xml/XmlDecoderTest.java @@ -0,0 +1,299 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.xml; + +import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.util.CharsetUtil; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.core.IsNull.nullValue; + +/** + * Verifies the basic functionality of the {@link XmlDecoder}. + * XML borrowed from + * Woodstox : Validate against XML Schema + */ +public class XmlDecoderTest { + + private static final String XML1 = "" + + "" + + "" + + "" + + "" + + "±1\n" + + "Alba ]]>" + + " " + + "100"; + + private static final String XML3 = ""; + + private static final String XML4 = ""; + + private EmbeddedChannel channel; + + @Before + public void setup() throws Exception { + channel = new EmbeddedChannel(new XmlDecoder()); + } + + @After + public void teardown() throws Exception { + channel.finish(); + } + + /** + * This test feeds basic XML and verifies the resulting messages + */ + @Test + public void shouldDecodeRequestWithSimpleXml() { + Object temp; + + write(XML1); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlDocumentStart.class)); + assertThat(((XmlDocumentStart) temp).version(), is("1.0")); + assertThat(((XmlDocumentStart) temp).encoding(), is("UTF-8")); + assertThat(((XmlDocumentStart) temp).standalone(), is(false)); + assertThat(((XmlDocumentStart) temp).encodingScheme(), is(nullValue())); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlDTD.class)); + assertThat(((XmlDTD) temp).text(), is("employee.dtd")); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlProcessingInstruction.class)); + assertThat(((XmlProcessingInstruction) temp).target(), is("xml-stylesheet")); + assertThat(((XmlProcessingInstruction) temp).data(), is("type=\"text/css\" href=\"netty.css\"")); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlProcessingInstruction.class)); + assertThat(((XmlProcessingInstruction) temp).target(), is("xml-test")); + assertThat(((XmlProcessingInstruction) temp).data(), is("")); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlElementStart.class)); + assertThat(((XmlElementStart) temp).name(), is("employee")); + assertThat(((XmlElementStart) temp).prefix(), is("")); + assertThat(((XmlElementStart) temp).namespace(), is("")); + assertThat(((XmlElementStart) temp).attributes().size(), is(0)); + assertThat(((XmlElementStart) temp).namespaces().size(), is(1)); + assertThat(((XmlElementStart) temp).namespaces().get(0).prefix(), is("nettya")); + assertThat(((XmlElementStart) temp).namespaces().get(0).uri(), is("http://netty.io/netty/a")); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlElementStart.class)); + assertThat(((XmlElementStart) temp).name(), is("id")); + assertThat(((XmlElementStart) temp).prefix(), is("nettya")); + assertThat(((XmlElementStart) temp).namespace(), is("http://netty.io/netty/a")); + assertThat(((XmlElementStart) temp).attributes().size(), is(0)); + assertThat(((XmlElementStart) temp).namespaces().size(), is(0)); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlEntityReference.class)); + assertThat(((XmlEntityReference) temp).name(), is("plusmn")); + assertThat(((XmlEntityReference) temp).text(), is("")); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlCharacters.class)); + assertThat(((XmlCharacters) temp).data(), is("1")); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlElementEnd.class)); + assertThat(((XmlElementEnd) temp).name(), is("id")); + assertThat(((XmlElementEnd) temp).prefix(), is("nettya")); + assertThat(((XmlElementEnd) temp).namespace(), is("http://netty.io/netty/a")); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlCharacters.class)); + assertThat(((XmlCharacters) temp).data(), is("\n")); + + temp = channel.readInbound(); + assertThat(temp, nullValue()); + + write(XML2); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlElementStart.class)); + assertThat(((XmlElementStart) temp).name(), is("name")); + assertThat(((XmlElementStart) temp).prefix(), is("")); + assertThat(((XmlElementStart) temp).namespace(), is("")); + assertThat(((XmlElementStart) temp).attributes().size(), is(1)); + assertThat(((XmlElementStart) temp).attributes().get(0).name(), is("type")); + assertThat(((XmlElementStart) temp).attributes().get(0).value(), is("given")); + assertThat(((XmlElementStart) temp).attributes().get(0).prefix(), is("")); + assertThat(((XmlElementStart) temp).attributes().get(0).namespace(), is("")); + assertThat(((XmlElementStart) temp).namespaces().size(), is(0)); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlCharacters.class)); + assertThat(((XmlCharacters) temp).data(), is("Alba")); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlElementEnd.class)); + assertThat(((XmlElementEnd) temp).name(), is("name")); + assertThat(((XmlElementEnd) temp).prefix(), is("")); + assertThat(((XmlElementEnd) temp).namespace(), is("")); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlCdata.class)); + assertThat(((XmlCdata) temp).data(), is(" ")); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlCharacters.class)); + assertThat(((XmlCharacters) temp).data(), is(" ")); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlComment.class)); + assertThat(((XmlComment) temp).data(), is(" namespaced ")); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlElementStart.class)); + assertThat(((XmlElementStart) temp).name(), is("salary")); + assertThat(((XmlElementStart) temp).prefix(), is("nettyb")); + assertThat(((XmlElementStart) temp).namespace(), is("http://netty.io/netty/b")); + assertThat(((XmlElementStart) temp).attributes().size(), is(1)); + assertThat(((XmlElementStart) temp).attributes().get(0).name(), is("period")); + assertThat(((XmlElementStart) temp).attributes().get(0).value(), is("weekly")); + assertThat(((XmlElementStart) temp).attributes().get(0).prefix(), is("nettyb")); + assertThat(((XmlElementStart) temp).attributes().get(0).namespace(), is("http://netty.io/netty/b")); + assertThat(((XmlElementStart) temp).namespaces().size(), is(1)); + assertThat(((XmlElementStart) temp).namespaces().get(0).prefix(), is("nettyb")); + assertThat(((XmlElementStart) temp).namespaces().get(0).uri(), is("http://netty.io/netty/b")); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlCharacters.class)); + assertThat(((XmlCharacters) temp).data(), is("100")); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlElementEnd.class)); + assertThat(((XmlElementEnd) temp).name(), is("salary")); + assertThat(((XmlElementEnd) temp).prefix(), is("nettyb")); + assertThat(((XmlElementEnd) temp).namespace(), is("http://netty.io/netty/b")); + assertThat(((XmlElementEnd) temp).namespaces().size(), is(1)); + assertThat(((XmlElementEnd) temp).namespaces().get(0).prefix(), is("nettyb")); + assertThat(((XmlElementEnd) temp).namespaces().get(0).uri(), is("http://netty.io/netty/b")); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlElementStart.class)); + assertThat(((XmlElementStart) temp).name(), is("last")); + assertThat(((XmlElementStart) temp).prefix(), is("")); + assertThat(((XmlElementStart) temp).namespace(), is("")); + assertThat(((XmlElementStart) temp).attributes().size(), is(0)); + assertThat(((XmlElementStart) temp).namespaces().size(), is(0)); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlElementEnd.class)); + assertThat(((XmlElementEnd) temp).name(), is("last")); + assertThat(((XmlElementEnd) temp).prefix(), is("")); + assertThat(((XmlElementEnd) temp).namespace(), is("")); + assertThat(((XmlElementEnd) temp).namespaces().size(), is(0)); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlElementEnd.class)); + assertThat(((XmlElementEnd) temp).name(), is("employee")); + assertThat(((XmlElementEnd) temp).prefix(), is("")); + assertThat(((XmlElementEnd) temp).namespace(), is("")); + assertThat(((XmlElementEnd) temp).namespaces().size(), is(1)); + assertThat(((XmlElementEnd) temp).namespaces().get(0).prefix(), is("nettya")); + assertThat(((XmlElementEnd) temp).namespaces().get(0).uri(), is("http://netty.io/netty/a")); + + temp = channel.readInbound(); + assertThat(temp, nullValue()); + } + + /** + * This test checks for different attributes in XML header + */ + @Test + public void shouldDecodeXmlHeader() { + Object temp; + + write(XML3); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlDocumentStart.class)); + assertThat(((XmlDocumentStart) temp).version(), is("1.1")); + assertThat(((XmlDocumentStart) temp).encoding(), is("UTF-8")); + assertThat(((XmlDocumentStart) temp).standalone(), is(true)); + assertThat(((XmlDocumentStart) temp).encodingScheme(), is("UTF-8")); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlElementStart.class)); + assertThat(((XmlElementStart) temp).name(), is("netty")); + assertThat(((XmlElementStart) temp).prefix(), is("")); + assertThat(((XmlElementStart) temp).namespace(), is("")); + assertThat(((XmlElementStart) temp).attributes().size(), is(0)); + assertThat(((XmlElementStart) temp).namespaces().size(), is(0)); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlElementEnd.class)); + assertThat(((XmlElementEnd) temp).name(), is("netty")); + assertThat(((XmlElementEnd) temp).prefix(), is("")); + assertThat(((XmlElementEnd) temp).namespace(), is("")); + assertThat(((XmlElementEnd) temp).namespaces().size(), is(0)); + + temp = channel.readInbound(); + assertThat(temp, nullValue()); + } + + /** + * This test checks for no XML header + */ + @Test + public void shouldDecodeWithoutHeader() { + Object temp; + + write(XML4); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlDocumentStart.class)); + assertThat(((XmlDocumentStart) temp).version(), is(nullValue())); + assertThat(((XmlDocumentStart) temp).encoding(), is("UTF-8")); + assertThat(((XmlDocumentStart) temp).standalone(), is(false)); + assertThat(((XmlDocumentStart) temp).encodingScheme(), is(nullValue())); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlElementStart.class)); + assertThat(((XmlElementStart) temp).name(), is("netty")); + assertThat(((XmlElementStart) temp).prefix(), is("")); + assertThat(((XmlElementStart) temp).namespace(), is("")); + assertThat(((XmlElementStart) temp).attributes().size(), is(0)); + assertThat(((XmlElementStart) temp).namespaces().size(), is(0)); + + temp = channel.readInbound(); + assertThat(temp, instanceOf(XmlElementEnd.class)); + assertThat(((XmlElementEnd) temp).name(), is("netty")); + assertThat(((XmlElementEnd) temp).prefix(), is("")); + assertThat(((XmlElementEnd) temp).namespace(), is("")); + assertThat(((XmlElementEnd) temp).namespaces().size(), is(0)); + + temp = channel.readInbound(); + assertThat(temp, nullValue()); + } + + private void write(String content) { + assertThat(channel.writeInbound(Unpooled.copiedBuffer(content, CharsetUtil.UTF_8)), is(true)); + } + +} diff --git a/license/LICENSE.aalto-xml.txt b/license/LICENSE.aalto-xml.txt new file mode 100644 index 0000000000..a94403f6e4 --- /dev/null +++ b/license/LICENSE.aalto-xml.txt @@ -0,0 +1,13 @@ +This copy of Aalto XML processor is licensed under the +Apache (Software) License, version 2.0 ("the License"). +See the License for details about distribution rights, and the +specific rights regarding derivate works. + +You may obtain a copy of the License at: + +http://www.apache.org/licenses/ + +A copy is also included with both the the downloadable source code package +and jar that contains class bytecodes, as file "ASL 2.0". In both cases, +that file should be located next to this file: in source distribution +the location should be "release-notes/asl"; and in jar "META-INF/" \ No newline at end of file diff --git a/pom.xml b/pom.xml index 2914e0c112..f85ad1a0cd 100644 --- a/pom.xml +++ b/pom.xml @@ -500,6 +500,7 @@ codec-mqtt codec-socks codec-stomp + codec-xml resolver resolver-dns transport @@ -593,6 +594,12 @@ true + + com.fasterxml + aalto-xml + 0.9.9 + + com.jcraft jzlib