/*
 * $Id: SyncQueue.java,v 1.2 2005/06/26 14:07:18 rampil Exp $
 * Copyright (c) 2004 LOGICAL-PARADOX.ORG
 */
package org.logical_paradox.rss.dsync;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;

/**
 * SyncQueue
 * 同期パッケージ
 * 同期のための情報が格納され，管理されるオブジェクト
 * @author satoshi akabane@logical-paradox.org
 * @version $Revision: 1.2 $
 */
public class SyncQueue {
	/** 同期パッケージの容量(要素数) */
	public static final int SYNCQUEUE_SIZE = 65536;
	/** 同期キューのサイズ */
	private int packageSize;
	/** 同期キュー */
	private Hashtable buf = new Hashtable();

	/**
	 * コンストラクタ
	 */
	public SyncQueue() {
		packageSize = SYNCQUEUE_SIZE;
	}
	/**
	 * コンストラクタ
	 * @param p キューのサイズ
	 */
	public SyncQueue(int p) {
		packageSize = p;
	}

	/**
	 * コマンドを追加する
	 * @param p 同期コマンド
	 * @throws SyncQueueException 同期キューへの格納に失敗
	 */
	public void add(SyncCommand o) throws SyncQueueException {
		if(buf.size() >= packageSize) {
			throw new SyncQueueException();
		}

		// コマンドを格納する処理
		// 検索がしやすいように，宛先別に整理して格納しておく
		Hashtable oSet = (Hashtable)buf.get((String)o.getTo());
		if(oSet == null) {
			oSet = new Hashtable();
		}

		oSet.put(o.getObject().toString(), o);
		buf.put(o.getTo(), oSet);
	}
	/**
	 * 全ての要素を引き出す
	 * @param 登録されている全ての同期コマンド
	 */
	public SyncCommand[] getAll() {
		ArrayList commands = new ArrayList();

		synchronized(buf) {
			Iterator i = buf.values().iterator();
			while(i.hasNext()) {
				Hashtable commandsToAnNode = (Hashtable)i.next();
				commands.addAll(commandsToAnNode.values());
			}
		}

		return (SyncCommand[])commands.toArray(new SyncCommand[0]);
	}
	/**
	 * 自分に宛てたコマンドを引き出す
	 * 引き出したデータは削除される
	 * @param myname 取り出すノードID
	 * @return 指定したノード宛ての同期コマンドのリスト
	 */
	public SyncCommand[] get(String myname) {
		Hashtable oSet = (Hashtable)buf.remove(myname);
		if(oSet == null) {
			// 自分宛てに何もセットされていない
			return new SyncCommand[0];
		}

		Vector dic = new Vector(oSet.values());
		return (SyncCommand[])dic.toArray(new SyncCommand[0]);
	}
	/**
	 * リストに追加する
	 * 追加の際，重複するものを全て削除する<br>
	 * 遅延同期の仕組みでは，各ノードは直下のノード以外管理しない．<br>
	 * 通常あるノードに接続される下位ノードは，100件を超えることがない(マシンパワーの関係で無理っぽい)<br>
	 * また，hashによる検索はなかなか高速であることから，このアルゴリズムで問題ないかと思われる．<br>
	 * <br>
	 * 予想されるリスク<br>
	 * ・下位ノードの数が極端に多くなった場合<br>
	 * ・一度に転送される差分情報の数が，非常に多くなった場合<br>
	 * ・synchronizedブロック<br>
	 * @param o 登録する同期コマンド
	 * @return 受け付けを拒否されたコマンドのリスト
	 */
	public SyncCommand[] add(SyncCommand[] o) {
		ArrayList rejectedCommands = new ArrayList();

		for(int ci = 0; ci < o.length; ci++) {
			synchronized(buf) {
				Iterator i = buf.values().iterator();
				boolean commandDuplicated = false;

				SyncCommand command = null;
				Hashtable commands = null;

				while(i.hasNext()) {
					commands = (Hashtable)i.next();
					command = (SyncCommand)commands.get(o[ci].getObject().toString());
					if(command != null) {
						// あった
						commandDuplicated = true;
						break;
					}
				}

				if(commandDuplicated) {
					switch(o[ci].getType()) {
						case SyncCommand.COMMAND_LOCK:
						case SyncCommand.COMMAND_FORWARD_URL:
							// 対象となるコマンドが既に存在した場合は，削除命令に変換する
							o[ci].reverse();
							String to = o[ci].getTo();
							String from = o[ci].getFrom();
							o[ci].setTo(from);					// もときた方向へ戻す
							rejectedCommands.add(o[ci]);
							break;

						case SyncCommand.COMMAND_UNLOCK:
						case SyncCommand.COMMAND_UNFORWARD_URL:
							// 対象となるコマンドが既に存在していた場合，
							// それを削除して新しいものを追加する
							commands.remove(o[ci].getObject().toString());
							break;
						default:
					}
				} else {
					// 対象となるコマンドがまだ存在していない場合，レコードを作成する
					Hashtable h = (Hashtable)buf.get(o[ci].getTo());
					if(h == null) {
						h = new Hashtable();
					}
					h.put(o[ci].getObject().toString(), o[ci]);
					buf.put(o[ci].getTo(), h);
				}
			}
		}

		return (SyncCommand[])rejectedCommands.toArray(new SyncCommand[0]);
	}

	/**
	 * 指定されたコマンドを削除する
	 * @param command 削除対象の同期コマンド
	 */
	public void remove(SyncCommand command) {
		synchronized(buf) {
			Hashtable oSet = (Hashtable)buf.remove(command.getTo());
			if(oSet != null) {
				// 見つかった場合はリストから削除して格納しなおす
				oSet.remove(command.getObject().toString());
				buf.put(command.getTo(), oSet);
			}
		}
	}
	/**
	 * 指定ノードに対するコマンドを全て削除する
	 * @param nk ノードID
	 */
	public void clear(String nk) {
		synchronized(buf) {
			Hashtable commandsOfTheNode = (Hashtable)buf.get(nk);
			if(commandsOfTheNode != null) {
				commandsOfTheNode.clear();
				buf.remove(commandsOfTheNode);
			}
		}
	}
	/**
	 * 全部削除する
	 */
	public void clear() {
		synchronized(buf) {
			Iterator i = buf.values().iterator();
			while(i.hasNext()) {
				Hashtable h = (Hashtable)i.next();
				h.clear();
			}

			buf.clear();
		}
	}
}

// end of SyncQueue.java
