view src/sample/merge/TranslaterImp1.java @ 114:c59b0886061c

translater
author kent
date Sun, 23 Dec 2007 16:15:15 +0900
parents 2e649cd44078
children a9fdbcbe351f
line wrap: on
line source

package sample.merge;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Stack;

import remoteeditor.command.REPCommand;
import remoteeditor.network.REP;

public class TranslaterImp1 implements Translater{
	//List <REPCommand> userList;
	//List <REPCommand> tokenList;
	private LinkedList<REPCommand> sentCmds;
	//private LinkedList<REPCommand> unMergedCmds;
	private Stack<REPCommand> unMergedCmds;
	public int eid;
	private int seq;

	public TranslaterImp1(int _eid){
		eid = _eid;
		sentCmds = new LinkedList<REPCommand>();
		unMergedCmds = new Stack<REPCommand>();
	}

	/**
	 * Translate cmd When the editor send REPCommand.
	 * but now, Only adding cmd to the queue is available.
	 * @param cmd
	 * @return translated command.
	 */
	synchronized public REPCommand[] transSendCmd(REPCommand cmd){
		REPCommand[] cmds = new REPCommand[1];
		setCmdState(cmd);
		cmds[0] = cmd;
		sentCmds.add(cmd);
		unMergedCmds.push(cmd);
		return cmds;
	}
	/**
	 * Dequeue command cmd that was returned.
	 * @param cmd
	 */
	synchronized public REPCommand[] catchOwnCommand(REPCommand cmd){
		ArrayList<REPCommand> returnCmds = new ArrayList<REPCommand>();
		ArrayList<REPCommand> cmds = new ArrayList<REPCommand>();
		// ringである以上、戻ってきたコマンドは確実にキューsentCmdsの先頭にある事を期待している
		REPCommand tmp = sentCmds.poll();
		assert tmp.seq==cmd.seq;
		assert cmd.eid==eid;

		/* スタックから全部取り出してマージする まだマージできないやつはまたスタックへ戻す  */
		/* スタックから全部取り出す  */
		while ( !unMergedCmds.isEmpty() ){
			REPCommand cmd0 = unMergedCmds.pop();
			returnCmds.add( createUndo(cmd0) );
			cmds.add(cmd0);
		}

		/* 必要な分だけソートして返却用のリストに追加  */
		//if (cmds.size()==0) return null;
		returnCmds.addAll( sortCmds(cmds) );

		/* 残ったコマンドも再び実行させるが、まだマージされてないのでunMergedにも入れる  */
		for(int i=0; i<cmds.size(); i++){
			returnCmds.add( cmds.get(i));
			unMergedCmds.push( cmds.get(i));
		}
		//returnCmds.addAll( cmds);
		//unMergedCmds.addAll( cmds);
		return returnCmds.toArray(new REPCommand[0]);
	}

	private REPCommand createUndo(REPCommand cmd){
		//REPCommand retCmd = cmd.clone();
		String str = new String(cmd.string);
		REPCommand retCmd = new REPCommand(cmd.cmd, cmd.sid, cmd.eid, cmd.seq, cmd.lineno, str.length(), str);

		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;
	}

	private ArrayList<REPCommand> sortCmds(ArrayList<REPCommand> cmds) {
		ArrayList<REPCommand> sortedCmds = new ArrayList<REPCommand>();
		int top;
		int prevEid=0;
		int i,j;

		/*  
		 * EID,SEQの低い順に、各EID毎に1個だけ選択しsortedCmdsに格納
		 * cmdsにはまだデータが残る     
		 */
		while ( -1 != (top=getPrecedence(cmds, prevEid+1)) ){
			REPCommand tmp = cmds.remove(top); 
			sortedCmds.add(tmp);
			prevEid = tmp.eid;
		}

		/* lineno の大きい順にソート  */
		for (i=0; i<sortedCmds.size(); i++){
			int k=i;
			for (j=i+1; j<sortedCmds.size(); j++){
				if ( sortedCmds.get(k).lineno > sortedCmds.get(j).lineno ) continue;
				else if ( sortedCmds.get(k).lineno < sortedCmds.get(j).lineno 
						|| sortedCmds.get(k).eid > sortedCmds.get(j).eid )
					   /* deleteとinsertの場合などはeidによらず、順序を強制する必要があるかも  */
					k=j;
			}
			REPCommand tmp = sortedCmds.get(i);
			sortedCmds.set(i, sortedCmds.get(k));
			sortedCmds.set(k, tmp);
		}
		return sortedCmds;
	}

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

	/**
	 * Translate Command cmd that was received from SeMa.
	 * @param cmd the command to be translated.
	 * @return translated commannd.
	 */
	synchronized public REPCommand[] transReceiveCmd(REPCommand cmd){
		int i=0;
		REPCommand cmds[];
		if (cmd.eid==eid){
			return catchOwnCommand(cmd);
		}

		for (REPCommand cmd0 : unMergedCmds){
			if (cmd0.eid==cmd.eid) i++;
		}

		if ( sentCmds.size()<i ){
			cmds = new REPCommand[2];
			String str = "NO OPERATION";
			cmds[0] = setCmdState( new REPCommand(REP.REPCMD_NOP, 0, eid, 0, 0, str.length(), str) );
			cmds[1] = cmd;
			//unMergedCmds.push(cmds[0]);
			unMergedCmds.push(cmd);
			sentCmds.add(cmds[0]);
			return cmds;
		}

		unMergedCmds.push(cmd); /* but.. */
		cmds = new REPCommand[1];
		cmds[0] = cmd;
		return cmds;
	}

	private REPCommand setCmdState(REPCommand cmd){
		cmd.seq = seq++;
		cmd.eid = eid;
		return cmd;
	}
	public void setEid(int _eid){
		eid = _eid;
	}
}