com.amazonaws.transform.StaxUnmarshallerContext Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aws-sdk-android Show documentation
Show all versions of aws-sdk-android Show documentation
Gradle project for AWS SDK Android
The newest version!
/*
* Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file 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.amazonaws.transform;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* Contains the unmarshalling state for the parsing of an XML response. The
* unmarshallers are stateless so that they can be reused, so this class holds
* the state while different unmarshallers work together to parse an XML
* response. It also tracks the current position and element depth of the
* document being parsed and provides utilties for accessing the next XML event
* from the parser, reading element text, handling attribute XML events, etc.
*/
public class StaxUnmarshallerContext {
private int currentEventType;
private final XmlPullParser xpp;
public final Deque stack = new LinkedList();
private String stackString = "";
private Map metadata = new HashMap();
private List metadataExpressions = new ArrayList();
private final Map headers;
/**
* Constructs a new unmarshaller context using the specified source of XML
* events.
*
* @param xpp
* The source of XML events for this unmarshalling context.
*/
public StaxUnmarshallerContext(XmlPullParser xpp) {
this(xpp, null);
}
/**
* Constructs a new unmarshaller context using the specified source of XML
* events, and a set of response headers.
*
* @param xpp
* The source of XML events for this unmarshalling context.
* @param headers
* The set of response headers associated with this unmarshaller
* context.
*/
public StaxUnmarshallerContext(XmlPullParser xpp, Map headers) {
this.xpp = xpp;
this.headers = headers;
}
/**
* Returns the value of the header with the specified name from the
* response, or null if not present.
*
* @param header
* The name of the header to lookup.
*
* @return The value of the header with the specified name from the
* response, or null if not present.
*/
public String getHeader(String header) {
if (headers == null) return null;
return headers.get(header);
}
/**
* Returns the text contents of the current element being parsed.
*
* @return The text contents of the current element being parsed.
* @throws IOException
* @throws XmlPullParserException
*/
public String readText() throws XmlPullParserException, IOException {
String s = xpp.nextText();
// Warning: Prior to API level 14, the pull parser returned by
// android.util.Xml did not always advance to the END_TAG event when
// this method was called. Work around by using manually advancing after
// calls to nextText():
if (xpp.getEventType() != XmlPullParser.END_TAG) {
xpp.next();
}
currentEventType = xpp.getEventType();
updateContext();
return s;
}
/**
* Returns the element depth of the parser's current position in the XML
* document being parsed.
*
* @return The element depth of the parser's current position in the XML
* document being parsed.
*/
public int getCurrentDepth() {
return stack.size();
}
/**
* Tests the specified expression against the current position in the XML
* document being parsed.
*
* @param expression
* The psuedo-xpath expression to test.
* @return True if the expression matches the current document position,
* otherwise false.
*/
public boolean testExpression(String expression) {
return testExpression(expression, getCurrentDepth());
}
/**
* Tests the specified expression against the current position in the XML
* document being parsed, and restricts the expression to matching at the
* specified stack depth.
*
* @param expression
* The psuedo-xpath expression to test.
* @param startingStackDepth
* The depth in the stack representing where the expression must
* start matching in order for this method to return true.
*
* @return True if the specified expression matches the current position in
* the XML document, starting from the specified depth.
*/
public boolean testExpression(String expression, int startingStackDepth) {
if (expression.equals(".")) return true;
int index = -1;
while ((index = expression.indexOf("/", index + 1)) > -1) {
// Don't consider attributes a new depth level
if (expression.charAt(index + 1) != '@') {
startingStackDepth++;
}
}
return (getCurrentDepth() == startingStackDepth
&& stackString.endsWith("/" + expression));
}
/**
* Returns true if this unmarshaller context is at the very beginning of a
* source document (i.e. no data has been parsed from the document yet).
*
* @return true if this unmarshaller context is at the very beginning of a
* source document (i.e. no data has been parsed from the document
* yet).
*/
public boolean isStartOfDocument() {
return currentEventType == XmlPullParser.START_DOCUMENT;
}
/**
* Returns the next XML event for the document being parsed. It's one of
* XmlPullParser.START_DOCUMENT,XmlPullParser.END_DOCUMENT,
* XmlPullParser.START_TAG, XmlPullParser.END_TAG.
*
* @return The next XML event for the document being parsed.
* @throws IOException
* @throws XmlPullParserException
*/
public int nextEvent() throws XmlPullParserException, IOException {
currentEventType = xpp.next();
// skip text node
if (currentEventType == XmlPullParser.TEXT) {
currentEventType = xpp.next();
}
updateContext();
// look for meta data
if (currentEventType == XmlPullParser.START_TAG) {
for (MetadataExpression metadataExpression : metadataExpressions) {
if (testExpression(metadataExpression.expression,
metadataExpression.targetDepth)) {
metadata.put(metadataExpression.key, readText());
break;
}
}
}
return currentEventType;
}
/**
* Returns any metadata collected through metadata expressions while this
* context was reading the XML events from the XML document.
*
* @return A map of any metadata collected through metadata expressions
* while this context was reading the XML document.
*/
public Map getMetadata() {
return metadata;
}
/**
* Registers an expression, which if matched, will cause the data for the
* matching element to be stored in the metadata map under the specified
* key.
*
* @param expression
* The expression an element must match in order for it's data to
* be pulled out and stored in the metadata map.
* @param targetDepth
* The depth in the XML document where the expression match must
* start.
* @param storageKey
* The key under which to store the matching element's data.
*/
public void registerMetadataExpression(String expression, int targetDepth, String storageKey) {
metadataExpressions.add(new MetadataExpression(expression, targetDepth, storageKey));
}
/*
* Private Interface
*/
/**
* Simple container for the details of a metadata expression this
* unmarshaller context is looking for.
*/
private static class MetadataExpression {
public String expression;
public int targetDepth;
public String key;
public MetadataExpression(String expression, int targetDepth, String key) {
this.expression = expression;
this.targetDepth = targetDepth;
this.key = key;
}
}
private void updateContext() {
if (currentEventType == XmlPullParser.START_TAG) {
stackString += "/" + xpp.getName();
stack.push(stackString);
} else if (currentEventType == XmlPullParser.END_TAG) {
stack.pop();
stackString = stack.isEmpty() ? "" : stack.peek();
}
}
}