com.davidbracewell.io.structured.xml.XMLReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mango Show documentation
Show all versions of mango Show documentation
A set of utilities and tools to speed up and ease programming in Java.
/*
* (c) 2005 David B. Bracewell
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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 com.davidbracewell.io.structured.xml;
import com.davidbracewell.conversion.Val;
import com.davidbracewell.io.Resources;
import com.davidbracewell.io.resource.Resource;
import com.davidbracewell.io.structured.ElementType;
import com.davidbracewell.io.structured.StructuredReader;
import com.davidbracewell.string.StringUtils;
import com.davidbracewell.tuple.Tuple2;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import lombok.NonNull;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import java.io.IOException;
import java.io.Reader;
import java.util.NoSuchElementException;
import java.util.Stack;
/**
* An implementation of a StructuredReader that reads xml.
*
* @author David B. Bracewell
*/
public class XMLReader extends StructuredReader {
private final String documentTag;
private final XMLEventReader reader;
private final Stack> stack;
private ElementType documentType;
private String readerText;
/**
* Creates an XMLReader
*
* @param resource The resource to read
* @throws IOException Something went wrong reading
*/
public XMLReader(Resource resource) throws IOException {
this("document", resource);
}
/**
* Creates an XMLReader
*
* @param resource The resource to read
* @throws IOException Something went wrong reading
*/
public XMLReader(Reader resource) throws IOException {
this("document", Resources.fromReader(resource));
}
/**
* Creates an XMLReader
*
* @param documentTag The document tag
* @param resource The resource to read
* @throws IOException Something went wrong reading
*/
public XMLReader(String documentTag, @NonNull Resource resource) throws IOException {
try {
Preconditions.checkArgument(!Strings.isNullOrEmpty(documentTag));
this.documentTag = documentTag;
this.reader = XMLInputFactory.newFactory().createXMLEventReader(resource.inputStream(), "UTF-8");
this.stack = new Stack<>();
} catch (Exception e) {
throw new IOException(e);
}
}
private XMLEvent next() throws IOException {
peek(); // move to the next real event
try {
return reader.nextEvent();
} catch (XMLStreamException e) {
throw new IOException(e);
} catch (NoSuchElementException e) {
return null;
}
}
private XMLReader validate(XMLEvent event, ElementType expectedElement, Tuple2 expectedTopOfStack) throws IOException {
if (event == null) {
throw new IOException("Parsing error event was null");
}
ElementType element = xmlEventToStructuredElement(event);
if (expectedElement != null && element != expectedElement) {
throw new IOException("Parsing error: expected (" + expectedElement + ") found (" + element + ")");
}
if (expectedTopOfStack != null && (stack.isEmpty() || !stack.peek().equals(expectedTopOfStack))) {
throw new IOException("Parsing error: expected (" + expectedTopOfStack + ") found (" + stack.peek() + ")");
}
return this;
}
@Override
public XMLReader beginDocument() throws IOException {
try {
XMLEvent event = next();
if (event == null) {
throw new IOException();
}
if (event.isStartDocument()) {
event = reader.nextTag();
}
if (!event.isStartElement() || !((StartElement) event).getName().toString().equals(documentTag)) {
throw new IOException("document tag does not match : expected (<" + documentTag + ">)");
}
documentType = xmlEventToStructuredElement(event);
stack.push(Tuple2.of(documentTag, ElementType.BEGIN_DOCUMENT));
} catch (XMLStreamException e) {
throw new IOException(e);
}
return this;
}
@Override
public void endDocument() throws IOException {
XMLEvent event = next();
validate(event, ElementType.END_DOCUMENT, Tuple2.of(documentTag, ElementType.BEGIN_DOCUMENT));
}
private ElementType xmlEventToStructuredElement(XMLEvent event) {
if (event == null) {
return ElementType.END_DOCUMENT;
}
if (event.isStartDocument()) {
return ElementType.BEGIN_DOCUMENT;
}
if (event.isEndDocument()) {
return ElementType.END_DOCUMENT;
}
if (event.isStartElement()) {
StartElement element = (StartElement) event;
if (element.getName().getLocalPart().equals(documentTag)) {
return ElementType.BEGIN_DOCUMENT;
}
QName elementType = new QName("", "type");
if (element.getAttributeByName(elementType) == null) {
return ElementType.NAME;
}
String typeName = element.getAttributeByName(elementType).getValue();
if (typeName.equalsIgnoreCase("array") || element.getName().toString().equals("array")) {
return ElementType.BEGIN_ARRAY;
}
if (typeName.equalsIgnoreCase("object") || element.getName().toString().equals("object")) {
return ElementType.BEGIN_OBJECT;
}
if (typeName.equalsIgnoreCase("value") || element.getName().toString().equals("value")) {
return ElementType.VALUE;
}
return ElementType.NAME;
}
if (event.isEndElement()) {
EndElement element = (EndElement) event;
String elementName = element.getName().getLocalPart();
Tuple2 top = stack.peek();
if (top.equals(Tuple2.of(elementName, ElementType.BEGIN_DOCUMENT))) {
return ElementType.END_DOCUMENT;
}
if (top.equals(Tuple2.of(elementName, ElementType.BEGIN_ARRAY))) {
return ElementType.END_ARRAY;
}
if (top.equals(Tuple2.of(elementName, ElementType.BEGIN_OBJECT))) {
return ElementType.END_OBJECT;
}
return ElementType.END_KEY_VALUE;
}
return ElementType.OTHER;
}
public ElementType peek() throws IOException {
if (!StringUtils.isNullOrBlank(readerText)) {
return ElementType.END_KEY_VALUE;
}
while (true) {
try {
XMLEvent event = reader.peek();
ElementType element = xmlEventToStructuredElement(event);
if (element != ElementType.OTHER) {
return element;
}
reader.nextEvent();
} catch (XMLStreamException e) {
throw new IOException(e);
}
}
}
@Override
public String beginObject() throws IOException {
XMLEvent event = next();
validate(event, ElementType.BEGIN_OBJECT, null);
String name = ((StartElement) event).getName().getLocalPart();
stack.push(Tuple2.of(name, ElementType.BEGIN_OBJECT));
return name;
}
@Override
public StructuredReader endObject() throws IOException {
XMLEvent event = next();
validate(event, ElementType.END_OBJECT, Tuple2.of(((EndElement) event).getName().getLocalPart(), ElementType.BEGIN_OBJECT));
stack.pop();
return this;
}
@Override
public ElementType getDocumentType() {
return documentType;
}
@Override
public String beginArray() throws IOException {
XMLEvent event = next();
validate(event, ElementType.BEGIN_ARRAY, null);
String name = ((StartElement) event).getName().getLocalPart();
stack.push(Tuple2.of(name, ElementType.BEGIN_ARRAY));
return name;
}
@Override
public StructuredReader endArray() throws IOException {
XMLEvent event = next();
validate(event, ElementType.END_ARRAY, Tuple2.of(((EndElement) event).getName().getLocalPart(), ElementType.BEGIN_ARRAY));
stack.pop();
return this;
}
@Override
public boolean hasNext() throws IOException {
return reader.hasNext();
}
private String handleNullables(String text) {
if (text == null || text.equals("null")) {
return null;
}
return text;
}
@Override
public Tuple2 nextKeyValue() throws IOException {
XMLEvent event = next();
validate(event, ElementType.NAME, null);
String key = ((StartElement) event).getName().getLocalPart();
try {
this.readerText = reader.getElementText();
} catch (XMLStreamException e) {
}
return Tuple2.of(key, nextValue());
}
@Override
public Tuple2 nextKeyValue(Class clazz) throws IOException {
XMLEvent event = next();
validate(event, ElementType.NAME, null);
String key = ((StartElement) event).getName().getLocalPart();
return Tuple2.of(key, nextValue(clazz));
}
public ElementType skip() throws IOException {
ElementType element = peek();
switch (element) {
case BEGIN_ARRAY:
beginArray();
while (peek() != ElementType.END_ARRAY) {
skip();
}
endArray();
break;
case BEGIN_OBJECT:
beginObject();
while (peek() != ElementType.END_OBJECT) {
skip();
}
endObject();
break;
default:
next();
}
return element;
}
@Override
protected Val nextSimpleValue() throws IOException {
Val v;
if (!StringUtils.isNullOrBlank(readerText)) {
v = Val.of(readerText);
readerText = null;
} else if (peek() == ElementType.VALUE) {
next();
try {
return Val.of(reader.getElementText());
} catch (XMLStreamException e) {
throw new IOException(e);
}
} else {
throw new IOException("Error");
}
return v;
}
@Override
public void close() throws IOException {
try {
reader.close();
} catch (XMLStreamException e) {
throw new IOException(e);
}
}
}// END OF XMLReader
© 2015 - 2025 Weber Informatics LLC | Privacy Policy