view rep/TranslatorImpl.java @ 382:4b87f89b3afd

REP Session Manager (Java version) new structure
author one@firefly.cr.ie.u-ryukyu.ac.jp
date Mon, 10 Nov 2008 22:07:45 +0900
parents
children
line wrap: on
line source

package rep;

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

import rep.handler.REPNode;
import rep.optimizers.REPCommandOptimizer;

public class TranslatorImpl implements Translator{
	public int eid;
	
	public REPCommandOptimizer optimizer;
	private LinkedList<REPCommand> unMergedCmds;
	public LinkedList<REPCommand> sentMergedList;
	private LinkedList<REPCommand> mergeAgainList;
	boolean merge_mode = false;

	public TranslatorImpl(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){
		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);
		for(REPCommand c: cmds) {
			output.add(c);
			unMergedCmds.add(c);
		}
		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);
		}				
		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);
		// nop command の挿入は Editor 側で行って、こちら側ではやらない
		unMergedCmds.add(cmd);
		if(cmd.cmd==REP.REPCMD_DELETE) {
			// delete のundo用の文字列は、外に出す意味はない
			cmd = new REPCommand(cmd);
			cmd.string=null;
		}
		nextEditor.send(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);
			}
		}
		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() {
	}



}