view rep/translator/TranslatorImpl.java @ 325:be0831086e63

*** empty log message ***
author kono
date Sat, 11 Oct 2008 17:43:51 +0900
parents e235998427a6
children b1a6acf0b8a9
line wrap: on
line source

package rep.translator;

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

import rep.Editor;
import rep.Forwarder;
import rep.REPCommand;
import rep.REP;

public class TranslatorImpl implements Translator{
	public int eid;
	/*
	 * queue が5つもいるって、あまりに馬鹿げてる。
	 */
	private LinkedList<REPCommand> sentCmds;
	private Stack<REPCommand> unMergedCmds;
	private LinkedList<REPCommand> undoReplaceList;
	public LinkedList<REPCommand> sentMergedList;
	private LinkedList<REPCommand> mergeAgainList;
	boolean merge_mode = false;

	public TranslatorImpl(int _eid){
		eid = _eid;
		sentCmds = new LinkedList<REPCommand>();
		unMergedCmds = new Stack<REPCommand>();
		undoReplaceList = 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){
		sentCmds.add(cmd);
		unMergedCmds.push(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(Editor editor){
		LinkedList<REPCommand> output = new LinkedList<REPCommand>();
		LinkedList<REPCommand> cmds = new LinkedList<REPCommand>();
				//スタック上にあるコマンドを全部undoコマンドにする
		while ( !unMergedCmds.isEmpty() ){
			REPCommand cmd0 = unMergedCmds.pop();
			output.add( createUndo(cmd0) );
			cmds.add(cmd0);
		}

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

		/* 残ったコマンドも再び実行させるが、まだマージされてないのでunMergedにも入れる  */
		output.addAll(cmds);
		for(REPCommand c: cmds) {
			output.add(c);
			unMergedCmds.push(c);
		}
		return editor.optimizedSend(output);
	}

	public REPCommand prev() {
		return sentCmds.poll();
	}

	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(Forwarder nextEditor,REPCommand cmd){
		assert (cmd.eid != eid);
		// nop command の挿入は Editor 側で行って、こちら側ではやらない
		unMergedCmds.push(cmd);
		nextEditor.send(cmd);
	}

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

	public Stack<REPCommand> getList() {
		return unMergedCmds;
	}

	public LinkedList<REPCommand> getSentCmds() {
		return sentCmds;
	}

	public void setUndoCommand(REPCommand command) {
		undoReplaceList.add(command);
	}

	public boolean checkMergeConflict(REPCommand command) {
		// sentMergedList.remove();
		
		if(mergeAgainList.size() > 0){
			mergeAgainList.add(command);
			if(sentMergedList.size() == 0){
				return true;
			}
		}

		return false;
	}

	public LinkedList<REPCommand> getMergeAgain() {
		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();
		sentMergedList = returnCommand;
		return returnCommand;
	}

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

	public boolean isMerging() {
		return merge_mode;
	}

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

	public void mergeAck() {

	}
	
	public void endMerge() {
		if(isFinished())
			merge_mode = false;
	}



}