/*
 * Copyright 2006 Takahiro Nakamura.
 *
 * 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 woolpack.xml;

import java.util.ArrayList;
import java.util.List;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import woolpack.fn.Fn;

/**
 * {@link Node#getChildNodes()}を利用する単純な{@link NodeFindable}です。
 * @author nakamura
 *
 */
public class NodeFindableChildNodesImpl implements NodeFindable {
	private Fn<Node, Boolean> nodeChecker;
	private boolean deepFlag;
	
	/**
	 * @param nodeChecker ノード評価の委譲先。
	 * @param deepFlag ノードが見付かった場合にその子ノードも検索するなら true。
	 */
	public NodeFindableChildNodesImpl(
			final Fn<Node, Boolean> nodeChecker,
			final boolean deepFlag) {
		this.nodeChecker = nodeChecker;
		this.deepFlag = deepFlag;
	}

	public NodeList evaluateList(final Object node) {
		final List<Node> list = new ArrayList<Node>();
		findNode(list, (Node) node, false);
		return new NodeList() {
			public int getLength() {
				return list.size();
			}
			public Node item(final int i) {
				return list.get(i);
			}
		};
	}

	public Node evaluateOne(final Object node) {
		final List<Node> list = new ArrayList<Node>(1);
		try {
			findNode(list, (Node) node, true);
		} catch (final IllegalStateException e) {
			if ("node found.".equals(e.getMessage())) {
				return list.get(0);
			} else {
				throw e;
			}
		}
		return null;
	}

	private void findNode(final List<Node> list, final Node base, final boolean oneFlag) {
		if (nodeChecker.exec(base)) {
			list.add(base);
			if (oneFlag) {
				throw new IllegalStateException("node found.");
			}
			if (!deepFlag) {
				return;
			}
		}
		final NodeList nodeList = base.getChildNodes();
		for (int i = 0; i < nodeList.getLength(); i++) {
			findNode(list, nodeList.item(i), oneFlag);
		}
	}

	public boolean isDeepFlag() {
		return deepFlag;
	}
	public void setDeepFlag(final boolean deepFlag) {
		this.deepFlag = deepFlag;
	}
	public Fn<Node, Boolean> getNodeChecker() {
		return nodeChecker;
	}
	public void setNodeChecker(final Fn<Node, Boolean> nodeChecker) {
		this.nodeChecker = nodeChecker;
	}
}
