/*******************************************************************************
* Copyright 2013 Naver Business Platform Corp.
*
* Licensed 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.handmark.pulltorefresh.configuration.xml;
import java.io.IOException;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import com.handmark.pulltorefresh.configuration.xml.XmlPullParserWrapper.DocumentState;
import com.handmark.pulltorefresh.library.internal.Assert;
/**
*
* {@code XmlPullNode}-based Parser using XmlPullParser <br />
* When you override this class, you must define parse strategies, and return the root node (in {@link #generateRootNode()} of {@code XmlPullNode}s containing each parse strategy,
* and return the result (in {@link #getResult()} for which provide that to client after parse.
* @author Wonjun Kim
* @param <R> Result type
*/
abstract class XmlPullNodeParser<R> {
private final XmlPullParserWrapper parser;
private final XmlPullNode rootNode;
/**
* this flag prevents duplicate parse.
*/
private boolean isParsed = false;
/**
* @param parser which has not been read yet. <br /> throw {@code NullPointerException} if {@code parser} is null
*/
public XmlPullNodeParser(XmlPullParserWrapper parser) {
Assert.notNull(parser, "XmlPullParser");
this.parser = parser;
this.rootNode = generateRootNode();
}
/**
* @return entry point for {@code XmlPullNode}-based parse
*/
protected abstract XmlPullNode generateRootNode();
/**
* Parse the data and return {@code <R>} type result <br />
* even if you call this methods several times, parsing happens once and no more duplicate parse
* @return parsed data
* @throws XmlPullParserException
* @throws IOException
*/
public final R parse() throws XmlPullParserException, IOException {
// avoid duplicate parse
if ( isParsed == true ) {
return getResult();
}
// First, Find the root node.
DocumentState documentState;
String rootName = rootNode.getName();
documentState = parser.nextStartTagByName(rootName);
if ( documentState.END.equals(documentState)) {
throw new XmlPullParserException(rootName + " tag has not found.");
}
// deeply parsing
parseInternal(rootNode);
isParsed = true;
return getResult();
}
/**
* Return a result after parse <br />Be called from {@link #parse()} method
* @return result value to return after parsing is done
*/
protected abstract R getResult();
/**
* Call {@link XmlPullNode.XmlPullNodeCallback#process(XmlPullParser)} and parse child nodes of current node
*
* @param currentNode target of which call a callback and parse children
* @throws XmlPullParserException
* @throws IOException
*/
private void parseInternal(XmlPullNode currentNode) throws XmlPullParserException, IOException {
// NOTE : too many permissions to node
currentNode.getCallback().process(parser);
while ( true ) {
// if document is end during finding the end tag of this current node, it throws the exception below.
if ( parser.isEndDocument() ) {
throw new XmlPullParserException("XML document is invalid.");
}
// if the end tag is found, parsing this scope is being ended.
if ( parser.isEndTag() && parser.matchCurrentTagName(currentNode.getName())) {
break;
}
// get next tag
parser.next();
// when a child node is found, deeply parsing
if ( parser.isStartTag() ) {
String currentTagName = parser.getName();
XmlPullNode childNode = currentNode.getChild(currentTagName);
if ( childNode != null ) {
// recursively!
parseInternal(childNode);
}
}
}
}
}