All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.amazonaws.transform.StaxUnmarshallerContext Maven / Gradle / Ivy

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();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy