/**
 * URLPool
 * 未探索のURLを格納しておくオブジェクト
 */
package org.logical_paradox.rss.http;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;


public class URLPool {
	private Hashtable domain = null;		// ドメイン名を管理するテーブル


	/**
	 * デフォルトコンストラクタ
	 */
	public URLPool() {
		domain = new Hashtable();
	}

	/**
	 * 未処理URLを追加する
	 */
	public void add( String url ) throws MalformedURLException {
		// URLからドメイン名の部分を抽出する
		String dn = getDomainName( url );
		if( dn == null || dn.trim().length() == 0 ) {
			throw new MalformedURLException();
		}

		// 該当するドメイン名のURLセットを取得する
		HashSet s = (HashSet)domain.get( dn );
		if( s == null ) {
			s = new HashSet();
			domain.put( dn, s );
		}

		// 未処理URLを追加する
		// HashSetなので重複は上書きされる．従って判定は必要ない
		s.add( url );

		synchronized( domain ) {
			domain.notifyAll();
		}
	}

	/**
	 * 未処理URLをまとめて追加する
	 */
	public void add( String[] urls ) throws MalformedURLException {
		for( int i = 0; i < urls.length; i++ ) {
			if( urls[i] != null ) {
				add( urls[i] );
			}
		}

		synchronized( domain ) {
			domain.notifyAll();
		}
	}

	/**
	 * 未処理URLをまとめて追加する
	 */
	public void add( Iterator i ) throws MalformedURLException {
		while( i.hasNext() ) {
			add( (String)i.next() );
		}
		synchronized( domain ) {
			domain.notifyAll();
		}
	}

	/**
	 * 指定範囲のドメイン名を返す
	 */
	public String[] domains( int idx, int len ) {
		String[] d = domains();
		Vector p = new Vector();

		// 指定範囲のドメイン名だけフェッチ
		for( int i = 0; i < len && i < d.length; i++ ) {
			p.addElement( d[i] );
		}

		// 文字列の配列にして返す
		return (String[])p.toArray( new String[0] );
	}

	/**
	 * ドメイン名一覧を返す
	 */
	public String[] domains() {
		Set keys = null;
		synchronized( domain ) {
			keys = domain.keySet();
		}

		if( keys == null ) {
			return null;
		}
		return (String[])keys.toArray( new String[0] );
	}

	/**
	 * 指定ドメインのURLを返す
	 * 指定ドメインのURLが存在しない場合はnullを返す
	 */
	public String[] urls( String dn ) {
		HashSet set = (HashSet)domain.get( dn );
		if( set == null ) {
			return null;
		}

		return (String[])set.toArray( new String[0] );
	}

	/**
	 * 指定ドメインのイテレータを返す
	 * 指定ドメインのURLが存在しない場合はnullを返す
	 */
	public Iterator getURLIterator( String dn ) {
		HashSet set = (HashSet)domain.get( dn );
		if( set == null ) {
			return null;
		}

		return set.iterator();
	}

	/**
	 * URLからドメイン名の部分だけを取得する
	 * この場合，ポート番号やプロトコルは関係ないものとする
	 * ただしサブドメインは関係があるものとする(別のサーバーである可能性があるため)
	 */
	protected String getDomainName( String url ) throws MalformedURLException {
		URL u = new URL( url );
		String siteName = new String( u.getHost() );

		return siteName;
	}

	/**
	 * 指定URLをプールから削除する
	 */
	public void remove( String url ) throws MalformedURLException {
		String dn = getDomainName( url );
		if( dn == null || dn.trim().length() == 0 ) {
			throw new MalformedURLException();
		}

		HashSet set = (HashSet)domain.get( dn );
		if( set == null ) {
			return;
		}

		// 削除する
		set.remove( url );

		if( set.size() == 0 ) {
			// 未処理URLを1つも保有しないドメインになってしまった場合は
			// そのドメインを削除する
			domain.remove( dn );
		}
	}

	/**
	 * 指定ドメインを削除する
	 */
	public void removeDomain( String domainname ) {
		synchronized( domain ) {
			HashSet domainSet = (HashSet)domain.remove( domainname );
			if( domainSet != null ) {
				domainSet.clear();
			}
		}
	}

	/**
	 * このプールのサイズを返す
	 */
	public int size() {
		int remain = 0;

		synchronized( domain ) {
			Enumeration e = domain.elements();
			while( e.hasMoreElements() ) {
				HashSet s = (HashSet)e.nextElement();
				remain += s.size();
			}
		}

		return remain;
	}

	/*
	 * このプールにデータが格納されているかどうかを返す
	 */
	public boolean isEmpty() {
		boolean rc = false;
		synchronized( domain ) {
			rc = domain.isEmpty();
		}

		return rc;
	}

	/*
	 * このオブジェクトが空の間，スレッドの待ち合わせをする
	 */
	public void waitUntilEmpty() {
		synchronized( domain ) {
			while( domain.isEmpty() == true ) {
				try {
					domain.wait();
				} catch( InterruptedException ie ) {
					// 待ち合わせが強制的に終了した．別にそれならそれで結構
				}
			}
		}
	}
}

// end of URLPool.java
