/**
 * License Agreement.
 *
 *  JBoss RichFaces - Ajax4jsf Component Library
 *
 * Copyright (C) 2007  Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

package org.richfaces.component.xml;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.digester.Digester;
import org.apache.commons.digester.RulesBase;
import org.richfaces.model.TreeNode;
import org.richfaces.model.TreeNodeImpl;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * @author Nick Belaevski - nbelaevski@exadel.com
 * created 16.11.2006
 * 
 */
public class XmlTreeDataBuilder {
	
	private final static class Rule extends org.apache.commons.digester.Rule {
		private int level = -1;
		private List idsList = new ArrayList();
		private List treeNodesList = new ArrayList();
		private List exclusionSets = new ArrayList();
		private TreeNode treeNode = new TreeNodeImpl(); //add empty node to serve as root
		
		public void begin(String namespace, String name, Attributes attributes)
				throws Exception {
			super.begin(namespace, name, attributes);

			level++;
			
			XmlNodeData xmlNodeData = new XmlNodeData();
			xmlNodeData.setName(name);
			xmlNodeData.setNamespace(namespace);

			String id = null;
			
			if (attributes != null) {
				int length = attributes.getLength();
				for (int i = 0; i < length; i++) {
					xmlNodeData.setAttribute(attributes.getQName(i),
							attributes.getValue(i));
					
				}
				
				id = attributes.getValue("id");
			}
			
			if (exclusionSets.size() == level) {
				exclusionSets.add(null);
			}

			if (id == null || id.length() == 0) {
				int currentId = 0;

				if (idsList.size() <= level) {
					for (int i = idsList.size(); i <= level; i++) {
						idsList.add(null);
					}
				} else {
					Integer integer = (Integer) idsList.get(level);
					currentId = integer.intValue() + 1;
				}

				Set exclusions = (Set) exclusionSets.get(level);
				
				while (exclusions != null && exclusions.contains(Integer.toString(currentId))) {
					currentId++;
				}
				
				idsList.set(level, new Integer(currentId));

				id = Integer.toString(currentId);
			} else {
				Set exclusions = (Set) exclusionSets.get(level);
				if (exclusions == null) {
					exclusions = new HashSet();
				
					exclusionSets.set(level, exclusions);
				}
				
				exclusions.add(id);
			}
			
			TreeNode node = new TreeNodeImpl();
			node.setData(xmlNodeData);
		
			this.treeNode.addChild(id, node);
			this.treeNodesList.add(this.treeNode);
			this.treeNode = node;
		}
		
		public void body(String namespace, String name, String text)
				throws Exception {
			super.body(namespace, name, text);
			
			if (text != null) {
				((XmlNodeData) this.treeNode.getData()).setText(text.trim());
			}
		}
		
		public void end(String namespace, String name) throws Exception {
			super.end(namespace, name);
			
			level--;
			
			if (idsList.size() - 1 > level + 1) {
				//idsList grew larger than we really need
				idsList.remove(idsList.size() - 1);
			}

			if (exclusionSets.size() - 1 > level + 1) {
				//the same condition as above
				exclusionSets.remove(exclusionSets.size() - 1);
			}

			this.treeNode = (TreeNode) this.treeNodesList.remove(this.treeNodesList.size() - 1);
		}
	}
	
	public static TreeNode build(InputSource inputSource) throws SAXException, IOException {
		Digester digester = new Digester(); 
		Rule rule = new Rule();
		final List rulesList = new ArrayList(1);
		rulesList.add(rule);
		
		RulesBase rulesBase = new RulesBase() {
			protected List lookup(String namespace, String name) {
				return rulesList;
			}
		};
		digester.setRules(rulesBase);
		digester.setNamespaceAware(true);
		digester.parse(inputSource);
		
		return rule.treeNode;
	}
}
