/*
 * 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()}を利用して DOM ノードを検索する{@link Fn}を生成するユーティリティです。
 * @author nakamura
 *
 */
public final class NodeFindUtils {
	private NodeFindUtils() {
	}
	
	/**
	 * エレメントを全てたどってノードを検索する関数を生成します。
	 * @param <E>
	 * @param nodeChecker 検索条件。
	 * @return 関数。
	 */
	public static <E extends Exception> Fn<Node, Node, E> one(
			final Fn<Node, Boolean, ? extends E> nodeChecker) {
		return new Fn<Node, Node, E>() {
			public Node exec(final Node c) throws E {
				if (nodeChecker.exec(c)) {
					return c;
				}
				final NodeList nodeList = c.getChildNodes();
				for (int i = 0; i < nodeList.getLength(); i++) {
					final Node result = exec(nodeList.item(i));
					if (result != null) {
						return result;
					}
				}
				return null;
			}
		};
	}
	
	/**
	 * エレメントを全てたどってノードリストを検索する関数を生成します。
	 * @param <E>
	 * @param nodeChecker 検索条件。
	 * @param deepFlag ノードが見付かった場合、継続してその子ノードを検索するならtrue。
	 * @return 関数。
	 */
	public static <E extends Exception> Fn<Node, NodeList, E> list(
			final Fn<Node, Boolean, ? extends E> nodeChecker,
			final boolean deepFlag) {
		return new Fn<Node, NodeList, E>() {
			public NodeList exec(final Node c) throws E {
				final List<Node> list = new ArrayList<Node>();
				findNode(list, c);
				return new NodeList() {
					public int getLength() {
						return list.size();
					}
					public Node item(final int i) {
						return list.get(i);
					}
				};
			}

			private void findNode(final List<Node> list, final Node base) throws E {
				if (nodeChecker.exec(base)) {
					list.add(base);
					if (!deepFlag) {
						return;
					}
				}
				final NodeList nodeList = base.getChildNodes();
				for (int i = 0; i < nodeList.getLength(); i++) {
					findNode(list, nodeList.item(i));
				}
			}
		};
	}
}
