//package xml;

import java.io.IOException;
import java.io.File;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

import org.apache.commons.digester3.Digester;
import org.apache.commons.digester3.ObjectCreateRule;
import org.xml.sax.SAXException;

public class GetRiverLength {

	public static void renderRiver(River start, String indent) {
		System.out.println(indent+"-"+start.toString()+" TLength: "+start.getTransitiveLength());
		for (River r : start.sources) {
			renderRiver(r, " "+indent);
		}
	}
	
	
	public static class RiverMap extends HashMap<String, River> {
		public void addRiver(River r) {
			this.put(r.getRiverId(), r);
		}
		
		public void reverseConnectRivers() {
			for (River r : values()) {
				for(String dest_id : r.to) {
					River d = get(dest_id);
					if(d != null)
						d.addSource(r);
				}
			}
			
		}
	}
	
	public static class River {
		String riverId = null;
		int length;
		final Set<River> sources;
		final Set<String> to;
		
		public River() {
			this.sources = new HashSet<River>();
			this.to = new HashSet<String>();
		}

		public void setLength(int length) {
			this.length = length;
		}
		
		public void setRiverId(String river_id) {
			this.riverId = river_id;
		}
		
		public String getRiverId() {
			return riverId;
		}
		
		public int getLength() {
			return length;
		}
		
		public int getTransitiveLength() {
			int t_length = length;
			for (River r : sources) {
				t_length += r.getTransitiveLength();
			}
			return t_length;
		}
		
		public void addSource(River r) {
			sources.add(r);
		}
		
		public void addDestination(String water) {
			to.add(water);
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result
					+ ((riverId == null) ? 0 : riverId.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			River other = (River) obj;
			if (riverId == null) {
				if (other.riverId != null)
					return false;
			} else if (!riverId.equals(other.riverId))
				return false;
			return true;
		}

		@Override
		public String toString() {
			return riverId+"("+length+")";
		}
	};

	public static void main(String[] args) {

         	File res = new File("mondial.xml");

		System.out.println("Parsing: "+res);
		
		Digester digester = new Digester();
		digester.push(new RiverMap());

		digester.addRule("", new ObjectCreateRule(River.class){
			public void end(String namespace, String name) throws Exception {
				
			};
		} );
		
		digester.addObjectCreate("mondial/river", River.class);
		digester.addSetProperties("mondial/river", "id", "riverId");
		digester.addBeanPropertySetter("mondial/river/length");
		digester.addCallMethod("mondial/river/to", "addDestination", 1);
		digester.addCallParam("mondial/river/to", 0, "water");
		digester.addSetNext("mondial/river", "addRiver");
		
		try {
			
			digester.setValidating(false);
			final RiverMap river_map = digester.parse(res);
			river_map.reverseConnectRivers();
			renderRiver(river_map.get("river-Zaire"), "");
			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		}

	}

}
