view src/sample/merge/TranslaterImp1.java @ 156:7ebd30e5e385 simulator-2008-8-26

*** empty log message ***
author pin
date Mon, 25 Aug 2008 15:53:03 +0900
parents 0dfb6413a31e
children 1a2269c820df
line wrap: on
line source

package sample.merge;

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

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;
	private LinkedList<REPCommand> undoReplaceList;
	private LinkedList<REPCommand> sentMergedList;
	private int interruptNumber;
	private LinkedList<REPCommand> mergeAgainList;

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

	/**
	 * Translate cmd When the editor send REPCommand.
	 * but now, Only adding cmd to the queue is available.
	 * @param cmd
	 * @return translated command.
	 */
	public REPCommand transSendCmd(REPCommand cmd){
		setCmdState(cmd);
		sentCmds.add(cmd);
		unMergedCmds.push(cmd);
		
		//マージ中にユーザから割り込みがあった場合
		if(sentMergedList.size() > 0){
			mergeAgainList.add(cmd);
		}
		
		return cmd;
	}
	/**
	 * 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;

				//スタック上にあるコマンドを全部undoコマンドにする
		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));
		}
		
		//マージ中のエディタからの割り込み検知に使う
		sentMergedList.addAll(returnCmds);
		
		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);
		
		retCmd.eid = REP.MERGE_EID;
		
		if (cmd.cmd==REP.REPCMD_INSERT) retCmd.cmd=REP.REPCMD_DELETE;
		else if (cmd.cmd==REP.REPCMD_DELETE) retCmd.cmd=REP.REPCMD_INSERT;
		else if (cmd.cmd == REP.REPCMD_REPLACE) retCmd = createUndoReplace(retCmd);
		return retCmd;
	}

	private REPCommand createUndoReplace(REPCommand cmd) {
		for(REPCommand command : undoReplaceList){
			if(command.eid == cmd.eid && command.seq == cmd.seq){
				REPCommand tmp = new REPCommand(command);
				tmp.setCMD(REP.REPCMD_REPLACE);
				return tmp;
			}
		}
		System.out.println(undoReplaceList);
		System.out.println(cmd);
		System.out.println();
		return null;	
	}
	
	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 )
				/* deleteとinsertの場合などはeidによらず、順序を強制する必要があるかも  */
				return -1;

			return 1;
		}
		
	}
	
	private Collection<REPCommand> sortCmds(ArrayList<REPCommand> cmds) {
		TreeSet<REPCommand> sortedCmds1 = new TreeSet<REPCommand>(new REPCommandComparator());
		int top;
		int prevEid=-1;
		int i,j;

		while ( -1 != (top=getPrecedence(cmds, prevEid+1)) ){
		//while ( -1 != (top=getPrecedence(cmds, prevEid)) ){
			REPCommand tmp = cmds.remove(top); 
			sortedCmds1.add(tmp);
			prevEid = tmp.eid;
		}
		
		if(false){
			ArrayList<REPCommand> sortedCmds = new ArrayList<REPCommand>();
		/* 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 sortedCmds1;
	}

	/* 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[];
		assert (cmd.eid != eid);
//		if (cmd.eid==eid){
//			return catchOwnCommand(cmd);
//		}

		for (REPCommand cmd0 : unMergedCmds){
			if (cmd0.eid==cmd.eid) i++;
		}
		
		if ( sentCmds.size()<i ){
			//自分のエディタでされた編集コマンドより他のエディタでの編集コマンドのほうが多かった場合NOPを挿入
			//自分のコマンドがないとマージできないのでNOPを挿入
			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;
	}

	public Stack<REPCommand> getList() {
		// TODO Auto-generated method stub
		return unMergedCmds;
	}

	public LinkedList<REPCommand> getSentCmds() {
		// TODO Auto-generated method stub
		return sentCmds;
	}

	public void setUndoCommand(REPCommand command) {
		// TODO Auto-generated method stub
		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){
				returnCommand.add(command);
			}
		}
		for(REPCommand command : mergeAgainList){
			if(command.eid == eid){
				command.eid = REP.MERGE_EID;
				returnCommand.add(command);
			}
		}
		mergeAgainList.clear();
		sentMergedList = returnCommand;
		return returnCommand;
	}

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