view rep/handler/Translator.java @ 421:f8916a96a373

(no commit message)
author one
date Sat, 06 Jun 2009 14:42:40 +0900
parents 795ef563f2a0
children 1acc3dfde5d3
line wrap: on
line source

package rep.handler;

import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeSet;

import rep.REP;
import rep.REPCommand;
import rep.SessionManager;
import rep.channel.REPLogger;
import rep.optimizers.REPCommandOptimizer;

public class Translator {
	public int eid;
	
	public REPCommandOptimizer optimizer;
	private LinkedList<REPCommand> unMergedCmds;
	public LinkedList<REPCommand> sentMergedList;
	private LinkedList<REPCommand> mergeAgainList;
	public REPLogger logger = SessionManager.logger;
	boolean merge_mode = false;

	public Translator(int _eid,REPCommandOptimizer opt){
		eid = _eid;
		optimizer = opt;
		unMergedCmds = new LinkedList<REPCommand>();
		mergeAgainList = new LinkedList<REPCommand>();
		sentMergedList = new LinkedList<REPCommand>();
	}

	/**
	 * New command from an editor
	 * The command is sent to the next editor
	 * @param cmd
	 * @return translated command.
	 */
	public REPCommand transSendCmd(REPCommand cmd){
		assert(cmd.eid==eid);
		unMergedCmds.add(cmd);
		
		//マージ中にユーザから割り込みがあった場合
		if(isMerging()){
			mergeAgainList.add(cmd);
		}
		
		return cmd;
	}
	/**
	 * My command is returned from the session ring, and START_MERGE_ACK
	 * is returned. At this
	 * stage my writeQueue is empty, our editor is waiting for me.
	 * Start merge process.
	 * @param cmd
	 */
	public boolean catchOwnCommand(REPNode editor){
		logger.writeLog("beforeMarge:"+unMergedCmds);
		LinkedList<REPCommand> output = new LinkedList<REPCommand>();
		LinkedList<REPCommand> cmds = new LinkedList<REPCommand>();
				//スタック上にあるコマンドを全部undoコマンドにする
		while ( !unMergedCmds.isEmpty() ){
			REPCommand cmd0 = unMergedCmds.removeLast();
			output.add( createUndo(cmd0) );
			cmds.add(cmd0);
		}

		/* 必要な分だけソートして返却用のリストに追加  */
		output.addAll( sortCmds(cmds) );

		/* 残ったコマンドも再び実行させるが、まだマージされてないのでunMergedにも入れる  */
		output.addAll(cmds);
		unMergedCmds.addAll(cmds);
		logger.writeLog("outputMarge:"+output);
		logger.writeLog("afterMarge:"+unMergedCmds);
		return optimizedSend(editor,output);
	}

	/**
	 * Sent optimized merged command list
	 * @param editor 
	 * @param output
	 * @return if any sent commands output 
	 */
	public boolean optimizedSend(REPNode editor, LinkedList<REPCommand> output) {
		List<REPCommand> output1 = optimizer.optimize(output);
		if (output1.size()==0) {
			merge_mode = false;
			return false;
		}
		for(REPCommand c:output1) {
			REPCommand m = new REPCommand(c);
			m.setEID(REP.MERGE_EID.id);
			m.setSEQID(editor.seq());
			sentMergedList.add(m);
			editor.send(m);
		}				
		merge_mode = true;
		return true;
	}
	
	private REPCommand createUndo(REPCommand cmd){
		REPCommand retCmd = new REPCommand(cmd);
		if (cmd.cmd==REP.REPCMD_INSERT) retCmd.cmd=REP.REPCMD_DELETE;
		else if (cmd.cmd==REP.REPCMD_DELETE) retCmd.cmd=REP.REPCMD_INSERT;
		return retCmd;
	}

	class REPCommandComparator implements Comparator<REPCommand>{

		public int compare(REPCommand o1, REPCommand o2) {
			
			if ( o2.lineno > o1.lineno ) return 1;
			else if ( o2.lineno < o1.lineno 
					|| o2.eid > o1.eid )
				return -1;

			return 1;
		}
		
	}
	
	private Collection<REPCommand> sortCmds(LinkedList<REPCommand> cmds) {
		TreeSet<REPCommand> sortedCmds1 = new TreeSet<REPCommand>(new REPCommandComparator());
		int top;
		int prevEid=-1;
		while ( -1 != (top=getPrecedence(cmds, prevEid+1)) ){
			REPCommand tmp = cmds.remove(top); 
			sortedCmds1.add(tmp);
			prevEid = tmp.eid;
		}
		
		return sortedCmds1;
	}

	/* search cmd. ordering by  min EID that is lower lowEid and min SEQ.  */
	private int getPrecedence(LinkedList<REPCommand> cmds, int lowEid) {
		int cEid, cSeq;
		cEid=cSeq=Integer.MAX_VALUE;
		int ret=-1;
		for (int i=0; i<cmds.size(); i++){
			REPCommand c = cmds.get(i);
			if ( c.eid<lowEid ) continue;
			else if ( c.eid>cEid ) continue;
			else if ( c.eid==cEid ) {
				if ( c.seq>cSeq ) continue;
				cSeq=c.seq;
				ret = i;
			} else { /* tmp.eid<cEid */
				cEid = c.eid;
				cSeq = c.seq;
				ret = i;
			}
		}
		return ret;
	}

	/**
	 * Translate Command cmd that was received from SeMa.
	 * @param cmd the command to be translated.
	 * @return translated commannd.
	 */
	public void transReceiveCmd(REPNode nextEditor,REPCommand cmd){
		assert (cmd.eid != eid);
		unMergedCmds.add(cmd);
	}

	public void setEid(int _eid){
		eid = _eid;
	}

	public boolean checkMergeConflict(REPCommand command) {
		REPCommand prev = sentMergedList.remove();
		assert (prev.seq==command.seq);
		
		if(mergeAgainList.size() > 0){
			mergeAgainList.add(command);
			return true;
		}
		if(sentMergedList.size()==0) {
			merge_mode=false;
		}
		return false;
	}

	public void getMergeAgain(REPNode editor) {
		LinkedList<REPCommand> returnCommand = new LinkedList<REPCommand>();
		for(int i = 0; i < mergeAgainList.size(); i++){
			//eid = REP.MEGE_EID
			returnCommand.add(createUndo(mergeAgainList.get(mergeAgainList.size() - i -1)));
		}
		for(REPCommand command : mergeAgainList){
			if(command.eid == REP.MERGE_EID.id){
				returnCommand.add(command);
			}
		}
		for(REPCommand command : mergeAgainList){
			if(command.eid == eid){
				command.eid = REP.MERGE_EID.id;
				returnCommand.add(command);
			}
		}
//		int count = 0;
//		for(REPCommand command: returnCommand) {
//			switch(command.cmd) {
//			case REPCMD_INSERT: count++; break;
//			case REPCMD_DELETE: count--; break;
//			default: assert false;
//			}
//		}
		logger.writeLog("MergeAgain ret="+returnCommand.size());
//				+" increment="+count);
		mergeAgainList.clear();
		optimizedSend(editor, returnCommand);
	}

	public boolean isFinished() {
		if(unMergedCmds.size() > 0) return false;
		if(sentMergedList.size() > 0) return false;
		return true;
	}

	public boolean isMerging() {
		return merge_mode;
	}

	public void startMerge(REPCommand cmd) {
		merge_mode = true;
	}

	public void mergeAck() {
	}



}