/*******************************************************************************
* 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 java.util.Map;
import java.util.TreeMap;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import com.handmark.pulltorefresh.library.internal.Assert;
/**
* {@code XmlPullNode} is used for parsing xml as a node tree.<br />
* If you implement {@link XmlPullNode#XmlPullNodeCallback} and pass that on to this class,<br /> {@link XmlPullNodeParser} calls {@link XmlPullNode#XmlPullNodeCallback#process(XmlPullParser)} when it has found this current node during parse.<br />
* When it has found a child node of current node' children, and if the child node has been added in current node as child XmlPullNode, {@link XmlPullNodeParser} continues parsing at the child.
* @author Wonjun Kim
*
*/
class XmlPullNode {
/**
* Unlimited repeat value <br />
* If you add a child (by calling {@link #addChildNode(XmlPullNode, int)} with setting repeat limit and the limit is {@code INFINITE}, the limit becomes meaningless.<br />
*/
public static final int INFINITE = -1;
/**
* Map which store children<br />
* child's node name can have upper case or lower. all is the same whether that is upper or lower.
*/
private final Map<String, XmlPullNodeContainer> children = new TreeMap<String, XmlPullNodeContainer>(
String.CASE_INSENSITIVE_ORDER);
/**
* Current node's name
*/
private final String tagName;
/**
* Callback <br /> {@code XmlPullNodeCallback.process(XmlPullParser)} method is called when this node has found in xml.
*/
private final XmlPullNodeCallback callback;
/**
* Default callback. This doesn't any kind of action.
*/
private static final XmlPullNodeCallback nullCallback = new XmlPullNodeCallback() {
@Override
public void process(XmlPullParser parser) {
// do nothing
}
};
/**
* Default Constructor (only set a node name)
* @param tagName Node name. this must not be null
*/
public XmlPullNode(String tagName) {
this(tagName, null);
}
/**
* Constructor with {@code tagName} and {@code callback}
* @param tagName Node name. this must not be null
* @param callback
*/
public XmlPullNode(String tagName, XmlPullNodeCallback callback) {
Assert.notNull(tagName, "Tag Name");
this.tagName = tagName;
this.callback = (callback == null) ? nullCallback : callback;
}
/**
* @return Current node name
*/
public String getName() {
return tagName;
}
/**
* Add a child node into this node without repeat limit<br />
* NOTE: When it has found a child node of current node' children, and if the child node has been added in current node as child XmlPullNode, {@link XmlPullNodeParser} continues parsing at the child.
* @param node Child node to add
* @return true if it is successful to add
*/
public boolean addChildNode(XmlPullNode node) {
return addChildNode(node, INFINITE);
}
/**
* Add a child node into this node with repeat limit<br />
* NOTE: When it has found a child node of current node' children, and if the child node has been added in current node as child XmlPullNode, {@link XmlPullNodeParser} continues parsing at the child.
* @param node Child node to add
* @param repeatLimit Repeat limit. if a child repeats over the limit, an error occurs during parse.
* @return true if it is successful to add
*/
public boolean addChildNode(XmlPullNode node, int repeatLimit) {
XmlPullNodeContainer pullNodeContainer = children.get(node.getName());
if (pullNodeContainer != null) {
return false;
}
children.put(node.getName(),
new XmlPullNodeContainer(node, repeatLimit));
return true;
}
/**
* Get a child of this node's chidren
* @param tagName Child node name to find
* @return Child node if the child has been found or null
* @throws XmlPullParserException
*/
public XmlPullNode getChild(String tagName) throws XmlPullParserException {
XmlPullNodeContainer pullNodeContainer = children.get(tagName);
if (pullNodeContainer == null) {
return null;
}
return pullNodeContainer.takeXmlPullNode();
}
/**
* @return Callback instance
*/
public XmlPullNodeCallback getCallback() {
return callback;
}
/**
* Callback to process some action for {@code XmlPullNode}. <br />
*
* NOTE: If you implement {@link XmlPullNode#XmlPullNodeCallback} and pass that on to {@code XmlPullNode} class,<br /> {@link XmlPullNodeParser} calls {@link XmlPullNode#XmlPullNodeCallback#process(XmlPullParser)} when it has found this the node during parse.
* @author Wonjun Kim
*
*/
public static interface XmlPullNodeCallback {
/**
* @param parser {@code XmlPullParser} instance which are parsing xml. WARNING: Must carefully use {@code parser}. using {@code parser} can affect to cause an parsing error. Because {@code parser} can easily go to some wrong position during usage.
* @throws XmlPullParserException
* @throws IOException
*/
void process(XmlPullParser parser) throws XmlPullParserException, IOException;
}
/**
* {@XmlPullNodeContainer} is a container for saving {@code repeatLimit}. And check the limit to occur an error. <br />
* NOTE : Justly, this Class is not thread-safe. :)
* @author Wonjun Kim
*
*/
private static class XmlPullNodeContainer {
/**
* Real {@code XmlPullnode} instance
*/
private XmlPullNode node;
/**
* Repetition limit value decreasing one by which a node occurs at a time
*/
private int repeatLimit;
/**
* Default Constructor
* @param node Infinite repeat {@code XmlPullNode} instance
*/
public XmlPullNodeContainer(XmlPullNode node) {
this(node, INFINITE);
}
/**
* Constructor with {@code node} and {@code repeatLimit}
* @param node Finite repeat (only if you set positive {@code repeatLimit}) {@code XmlPullNode} instance
* @param repeatLimit Repeat limit value. This must be positive.
*/
public XmlPullNodeContainer(XmlPullNode node, int repeatLimit) {
Assert.notNull(node, "XmlPullNode");
this.node = node;
this.repeatLimit = repeatLimit;
}
/**
* Return {@code XmlPullNode} instance or throw a limit error
* @return {@code XmlPullNode} instance if the node repeats under the limit
* @throws XmlPullParserException if a number of repetitions reaches the limit
*/
public XmlPullNode takeXmlPullNode() throws XmlPullParserException {
if (repeatLimit > 0) {
decreaseRepeatLimit();
return node;
}
// throw an error
if (repeatLimit == 0) {
String tagName = node.getName();
throw new XmlPullParserException("Tag '" + tagName
+ "' should not have more " + repeatLimit + " nodes.");
}
// if infinite repeats,
return node;
}
/**
* one step forward to the limit error!
*/
private void decreaseRepeatLimit() {
--repeatLimit;
}
}
}