changeset 113:522c6bd9b11b

Merge test using JAVApathfinder
author kent
date Sun, 23 Dec 2007 16:14:06 +0900
parents 65ba9545fa49
children c59b0886061c
files src/pathfinder/mergetest/ChannelSimulator.java src/pathfinder/mergetest/EditorSimulator.java src/pathfinder/mergetest/EditorSimulatorAsync.java src/pathfinder/mergetest/NetworkSimulator.java src/pathfinder/mergetest/NetworkSimulatorwithSeMa.java src/pathfinder/mergetest/NetworkSimulatorwithoutSeMa.java src/pathfinder/mergetest/SeMaSimulator.java src/pathfinder/mergetest/TestMerger.java src/pathfinder/mergetest/Text.java
diffstat 9 files changed, 722 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pathfinder/mergetest/ChannelSimulator.java	Sun Dec 23 16:14:06 2007 +0900
@@ -0,0 +1,73 @@
+package pathfinder.mergetest;
+
+import java.util.concurrent.BlockingQueue;
+
+public class ChannelSimulator<P> {
+	private BlockingQueue<P> qread;
+	private BlockingQueue<P> qwrite;
+	private NetworkSimulator<P> ns;
+
+	public ChannelSimulator(NetworkSimulator<P> _ns, BlockingQueue<P> _a, BlockingQueue<P> _b){
+		ns = _ns;
+		qread = _a;
+		qwrite = _b;
+	}
+
+	/* read from Queue.  */
+	public P read(){
+		try {
+			return qread.take();
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+	/* write to Queue.  */
+	public boolean write(P p){
+		try {
+			synchronized (ns){
+				qwrite.put(p);
+				ns.notifyAll();
+			}
+			return true;
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+			return false;
+		}
+	}
+	/* polling read queue. write queue can be written any time.  */
+	public P poll() {
+		return qread.poll();
+	}
+
+	
+	/* accessor methods.  */
+	public BlockingQueue<P> getReadQ(){
+		return qread;
+	}
+	public BlockingQueue<P> getWriteQ(){
+		return qwrite;
+	}
+	public void setReadQ(BlockingQueue<P> bq){
+		qread = bq;
+	}
+	public void setWriteQ(BlockingQueue<P> bq){
+		qwrite = bq;
+	}
+
+	/* return state of the Queue(debug)  */
+	public boolean readQisEmpty() {
+		return qread.isEmpty();
+	}
+	public boolean writeQisEmpty() {
+		return qwrite.isEmpty();
+	}
+	
+
+	/* for Session Manager.  */
+	public ChannelSimulator<P> getServerChannel() {
+		return new ChannelSimulator<P>(ns, qwrite, qread);
+	}
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pathfinder/mergetest/EditorSimulator.java	Sun Dec 23 16:14:06 2007 +0900
@@ -0,0 +1,121 @@
+package pathfinder.mergetest;
+
+import java.util.Queue;
+
+import remoteeditor.command.REPCommand;
+import remoteeditor.network.REP;
+import sample.merge.TranslaterImp1;
+
+public class EditorSimulator extends Thread{
+	private int eid;
+	private int seq;
+	private boolean isOwner;
+	private NetworkSimulator<REPCommand> ns;
+	private ChannelSimulator<REPCommand> cs;
+	private Queue<REPCommand> CmdList;
+	private TranslaterImp1 translater;
+	private Text text;
+	private boolean running=true;
+
+	public EditorSimulator(int _eid, NetworkSimulator<REPCommand> _ns, Queue<REPCommand> q, String _name) {
+		super(_name);
+		eid = _eid;
+		ns = _ns;
+		CmdList = q;
+		translater = new TranslaterImp1(_eid);
+		text = new Text();
+		cs = ns.connect();
+	}
+
+	public void setOwner(boolean f){
+		isOwner = f;
+	}
+	synchronized public void finish(){
+		running = false;
+	}
+
+	public void run(){
+		System.out.println("Editor"+eid+" start.");
+
+		// Send All Command that is included CmdList.
+		sendAllCommand();
+
+		// MainLoop, 
+		while(running){
+			REPCommand cmd = cs.read();
+			REPCommand[] cmds;
+
+			//終了条件
+			if (cmd.eid==eid && cmd.cmd==REP.SMCMD_QUIT){
+				System.out.println("\tEditor"+eid+" catch QUIT command emited by itself.");
+				translater.transReceiveCmd(cmd);
+				running=false; break;
+			}
+			System.out.println("\tEditor"+eid+" catch command from "+cmd.eid+" NO."+cmd.seq);
+			
+			if (cmd.eid==eid){
+				cmds = translater.catchOwnCommand(cmd);
+				for (int i=0; i<cmds.length; i++){
+					cmd = cmds[i];
+					System.out.println("\t\tEditor"+eid+" edit text. ");
+					text.edit(cmd);
+				}
+			} else {
+				cmds = translater.transReceiveCmd(cmd);
+				for (int i=0; i<cmds.length; i++){
+					cmd = cmds[i];
+					System.out.println("\t\tEditor"+eid+" edit text and pass Cmd. ");
+					text.edit(cmd);
+					cs.write(new REPCommand(cmd));
+				}
+			}
+		}
+
+		System.out.println("Editor"+eid+" finish.");
+	}
+
+	private void sendOneCommand() {
+		REPCommand[] cmds;
+		REPCommand cmd = CmdList.poll();
+		cmd.eid = eid;
+		cmds = translater.transSendCmd(cmd);
+		cmd.setString("this is inserted or replaced by Editor"+cmd.eid+":"+cmd.seq);
+
+		if (isOwner) cmd.setThroughMaster(true);
+		for (int i=0; i<cmds.length; i++){
+			text.edit(cmds[i]);
+			cs.write(new REPCommand(cmds[i]));
+		}
+	}
+	private void sendAllCommand() {
+		REPCommand[] cmds;
+		for (REPCommand cmd: CmdList){
+			cmd.seq = seq;
+			cmd.eid = eid;
+			cmds = translater.transSendCmd(cmd);
+			cmd.setString("this is inserted or replaced by Editor"+cmd.eid+":"+cmd.seq);
+			//if (isOwner) cmd.setThroughMaster(true);
+			for (int i=0; i<cmds.length; i++){
+				text.edit(cmds[i]);
+				cs.write(new REPCommand(cmds[i]));
+			}
+		}
+
+		// Send Quit Command
+		cmds = translater.transSendCmd( new REPCommand(REP.SMCMD_QUIT, 0, eid, seq++, 0, 0, "QUIT by Editor"+eid));
+		for (int i=0; i<cmds.length; i++){
+			text.edit(cmds[i]);
+			cs.write(new REPCommand(cmds[i]));
+		}
+
+	}
+/*
+	private boolean checkQuit(REPCommand cmd) {
+		// 最初に全部のコマンドを送信するから、自分のQUITが来るのは最後
+		return (cmd.eid==eid && cmd.cmd==REP.SMCMD_QUIT);
+	}
+*/
+	public Text getText(){
+		return text;
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pathfinder/mergetest/EditorSimulatorAsync.java	Sun Dec 23 16:14:06 2007 +0900
@@ -0,0 +1,150 @@
+package pathfinder.mergetest;
+
+import java.util.Queue;
+
+import remoteeditor.command.REPCommand;
+import remoteeditor.network.REP;
+import sample.merge.TranslaterImp1;
+
+public class EditorSimulatorAsync extends EditorSimulator{
+	private int eid;
+	private boolean isOwner;
+	private NetworkSimulator<REPCommand> ns;
+	private ChannelSimulator<REPCommand> cs;
+	private TranslaterImp1 translater;
+	private Text text;
+	private boolean running=true;
+	private User user;
+
+	public EditorSimulatorAsync(int _eid, NetworkSimulator<REPCommand> _ns, Queue<REPCommand> q, String _name) {
+		super(_eid, _ns, q, _name);
+		eid = _eid;
+		ns = _ns;
+		cs = ns.connect();
+		translater = new TranslaterImp1(_eid);
+		text = new Text();
+		user = new User(translater, text, q, cs);
+	}
+
+	public void run(){
+		System.out.println("Editor"+eid+" start.");
+		user.start();
+
+		// MainLoop, 
+		while(running){
+			REPCommand cmd = cs.read();
+			REPCommand[] cmds;
+
+			//終了条件
+			if (cmd.eid==eid && cmd.cmd==REP.SMCMD_QUIT){
+				System.out.println("\tEditor"+eid+" catch QUIT command emited by itself.");
+				translater.transReceiveCmd(cmd);
+				running=false; break;
+			}
+			System.out.println("\tEditor"+eid+" catch command from "+cmd.eid+" NO."+cmd.seq);
+			
+			if (cmd.eid==eid){
+				cmds = translater.catchOwnCommand(cmd);
+				for (int i=0; i<cmds.length; i++){
+					cmd = cmds[i];
+					System.out.println("\t\tEditor"+eid+" edit text. ");
+					text.edit(cmd);
+				}
+			} else {
+				cmds = translater.transReceiveCmd(cmd);
+				for (int i=0; i<cmds.length; i++){
+					cmd = cmds[i];
+					System.out.println("\t\tEditor"+eid+" edit text and pass Cmd. ");
+					text.edit(cmd);
+					cs.write(cmd);
+				}
+			}
+		}
+
+		try { user.join();
+		} catch (InterruptedException e) { e.printStackTrace(); }
+		
+		System.out.println("Editor"+eid+" finish.");
+	}
+
+/*
+	private boolean checkQuit(REPCommand cmd) {
+		// 最初に全部のコマンドを送信するから、自分のQUITが来るのは最後
+		return (cmd.eid==eid && cmd.cmd==REP.SMCMD_QUIT);
+	}
+*/
+	public Text getText(){
+		return text;
+	}
+	
+	
+	
+	public class User extends Thread{
+		private Queue<REPCommand> CmdList;
+		private TranslaterImp1 translater;
+		private Text text;
+		private ChannelSimulator<REPCommand> cs;
+
+		public User(TranslaterImp1 _translater, Text _text, Queue<REPCommand> q, ChannelSimulator<REPCommand> _cs){
+			translater = _translater;
+			text = _text;
+			CmdList = q;
+			cs = _cs;
+		}
+		
+		public void run(){
+			System.out.println("Editor"+eid+"'s writer thread start.");
+			while ( !CmdList.isEmpty() ){
+				sendOneCommand();
+			}
+			sendQuitCommand();
+			System.out.println("Editor"+eid+"'s writer thread finish.");
+		}
+
+		private void sendOneCommand() {
+			REPCommand[] cmds;
+			REPCommand cmd = CmdList.poll();
+			cmd.eid = eid;
+			cmds = translater.transSendCmd(cmd);
+			cmd.setString("this is inserted or replaced by Editor"+cmd.eid+":"+cmd.seq);
+
+			if (isOwner) cmd.setThroughMaster(true);
+			for (int i=0; i<cmds.length; i++){
+				text.edit(cmds[i]);
+				cs.write(cmds[i]);
+			}
+		}
+
+		private void sendQuitCommand(){
+			REPCommand[] cmds;
+			cmds = translater.transSendCmd( new REPCommand(REP.SMCMD_QUIT, 0, eid, 0, 0, 0, "QUIT by Editor"+eid));
+			for (int i=0; i<cmds.length; i++){
+				text.edit(cmds[i]);
+				cs.write(cmds[i]);
+			}
+		}
+/*
+		private void sendAllCommand() {
+			REPCommand[] cmds;
+			for (REPCommand cmd: CmdList){
+				cmd.seq = seq;
+				cmd.eid = eid;
+				cmds = translater.transSendCmd(cmd);
+				cmd.setString("this is inserted or replaced by Editor"+cmd.eid+":"+cmd.seq);
+				//if (isOwner) cmd.setThroughMaster(true);
+				for (int i=0; i<cmds.length; i++){
+					text.edit(cmds[i]);
+					cs.write(cmds[i]);
+				}
+			}
+
+		}
+*/
+	}
+	
+	
+	
+	
+	
+	
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pathfinder/mergetest/NetworkSimulator.java	Sun Dec 23 16:14:06 2007 +0900
@@ -0,0 +1,14 @@
+package pathfinder.mergetest;
+
+
+public interface NetworkSimulator<P> {
+
+	/**
+	 * Request to connect.
+	 * Client editor use this method to connect SeMa. 
+	 * @param cs
+	 */
+	public ChannelSimulator<P> connect();
+	public ChannelSimulator<P> accept();
+	public boolean checkAllCS();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pathfinder/mergetest/NetworkSimulatorwithSeMa.java	Sun Dec 23 16:14:06 2007 +0900
@@ -0,0 +1,55 @@
+package pathfinder.mergetest;
+
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import remoteeditor.command.REPCommand;
+
+public class NetworkSimulatorwithSeMa<P> implements NetworkSimulator<P> {
+
+	/** Waiting connectionRequest to be accepted by SessionManager. */
+	private Queue<ChannelSimulator<P>> acceptList;
+	/** Established connection */
+	private Queue<ChannelSimulator<P>> connectedList;
+	
+	//private SeMaSimulator<REPCommand> sema;
+	
+	public NetworkSimulatorwithSeMa(){
+		acceptList = new LinkedList<ChannelSimulator<P>>();
+		connectedList = new LinkedList<ChannelSimulator<P>>();
+	}
+
+	/**
+	 * Establish connection. It's called by SeMa.
+	 * @return
+	 */
+	public ChannelSimulator<P> accept(){
+		ChannelSimulator<P> cs = acceptList.poll();
+		if (cs==null) return null;
+
+		connectedList.offer(cs);
+		return cs.getServerChannel();
+	}
+
+	/**
+	 * Request to connect.
+	 * Client editor use this method to connect SeMa. 
+	 * @param cs
+	 */
+	public ChannelSimulator<P> connect(){
+		BlockingQueue<P> rq = new LinkedBlockingQueue<P>();
+		BlockingQueue<P> wq = new LinkedBlockingQueue<P>();
+		ChannelSimulator<P> cs = new ChannelSimulator<P>(this, rq, wq);
+		acceptList.offer(cs);
+		return cs;
+	}
+		
+	public boolean checkAllCS(){
+		for(ChannelSimulator<P> cs: connectedList){
+			if(!cs.readQisEmpty()) return false;
+		}
+		return true;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pathfinder/mergetest/NetworkSimulatorwithoutSeMa.java	Sun Dec 23 16:14:06 2007 +0900
@@ -0,0 +1,55 @@
+package pathfinder.mergetest;
+
+import java.util.LinkedList;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+public class NetworkSimulatorwithoutSeMa<P> implements NetworkSimulator<P> {
+	/** Established connection */
+	private LinkedList<ChannelSimulator<P>> connectedList;
+
+	public NetworkSimulatorwithoutSeMa(){
+		connectedList = new LinkedList<ChannelSimulator<P>>();
+	}
+
+	/**
+	 * Request to connect.
+	 * Client editor use this method to connect SeMa. 
+	 * @param cs
+	 */
+	public ChannelSimulator<P> connect(){
+		ChannelSimulator<P> cs;
+		if (connectedList.isEmpty()){
+			BlockingQueue<P> q = new LinkedBlockingQueue<P>();
+			cs = new ChannelSimulator<P>(this, q, q);
+		}else{
+			BlockingQueue<P> rq = connectedList.getLast().getWriteQ();
+			BlockingQueue<P> wq = new LinkedBlockingQueue<P>();
+			connectedList.getFirst().setReadQ(wq);
+			
+/*			ChannelSimulator<P> lastcs = connectedList.getLast();
+			BlockingQueue<P> rq = lastcs.getWriteQ();
+
+			BlockingQueue<P> wq = new LinkedBlockingQueue<P>();
+			ChannelSimulator<P> firstcs = connectedList.getFirst();
+			firstcs.setReadQ(wq);
+*/
+			cs = new ChannelSimulator<P>(this, rq, wq);
+		}
+
+		connectedList.addLast(cs);
+		return cs;
+	}
+	
+	public ChannelSimulator<P> accept(){
+		return null;
+	}
+
+	public boolean checkAllCS(){
+		for(ChannelSimulator<P> cs: connectedList){
+			if(!cs.readQisEmpty()) return false;
+		}
+		return true;
+	}
+	
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pathfinder/mergetest/SeMaSimulator.java	Sun Dec 23 16:14:06 2007 +0900
@@ -0,0 +1,92 @@
+package pathfinder.mergetest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SeMaSimulator<P> extends Thread {
+	private int MAX_PACKET;
+	private int MAX_CLIENT;
+	private boolean running=true;
+	private NetworkSimulator<P> ns;
+	private List<ChannelSimulator<P>> csList;
+
+	public SeMaSimulator(NetworkSimulator<P> _ns, int max_client, int max_packet){
+		ns = _ns;
+		MAX_CLIENT = max_client;
+		MAX_PACKET = max_packet;
+		csList = new ArrayList<ChannelSimulator<P>>();
+	}
+	public SeMaSimulator(NetworkSimulator<P> _ns, int max_client){
+		this(_ns, max_client, 0);
+	}
+	public SeMaSimulator(NetworkSimulator<P> _ns){
+		this(_ns, 2);
+	}
+
+	synchronized public void finish(){
+		synchronized(ns){
+			running = false;
+			ns.notify();
+		}
+	}
+
+	/**
+	 * Check whether the NetworkSimulator hold waiting connections.
+	 */
+	private void checkAccept(){
+		ChannelSimulator<P> cs;
+		while((cs=ns.accept())!=null){
+			csList.add(cs);
+		}
+	}
+
+	public void run(){
+		int i=0;
+		int count=0;
+		P packet;
+
+		while(csList.size()<MAX_CLIENT){ checkAccept(); Thread.yield(); }
+		System.out.println("SessionManager start.");
+
+		/* Main Loop */
+		ChannelSimulator<P> cs = csList.get(i);
+		while(running
+				&& (MAX_PACKET==0 || count<MAX_PACKET)){
+			synchronized(ns){
+				int prev_i=i;
+				while((packet=cs.poll())==null && running){
+					i = (i+1)%csList.size();   // i++
+					cs = csList.get(i);        // 次のChennelをゲット
+					if(i==prev_i) try { ns.wait(); } catch (InterruptedException e) { e.printStackTrace(); }
+				}
+			}
+			if(!running) break;
+
+			System.out.println("SeMa pass packet to "+i+":>> "+packet.toString());
+			i = (i+1)%csList.size();   // i++
+			cs = csList.get(i);        // 次のChennelをゲット
+
+			if ( !cs.write(packet) ){
+				System.err.println("Session Manager failed to write.");
+			}
+			count++;
+		}
+/*
+		ChannelSimulator<P> cs = csList.get(i);
+		while(running
+				&& MAX_PACKET==0 || count<MAX_PACKET){
+			packet=cs.poll();          // [i]からread
+			//if(packet!=null) System.out.println("SeMa catch packet to "+i+":>> "+packet.toString());
+			i = (i+1)%csList.size();   // i++
+			cs = csList.get(i);        // 次のChennelをゲット
+			if (packet!=null) {
+				System.out.println("SeMa pass packet to "+i+":>> "+packet.toString());
+				cs.write(packet);      // readできていたならそれを書き込む
+				count++;
+			}
+			//if (i==0) checkAccept();   //全部回ったらaccept待ちをチェック
+		}
+*/
+		System.out.println("SessionManager finish.");
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pathfinder/mergetest/TestMerger.java	Sun Dec 23 16:14:06 2007 +0900
@@ -0,0 +1,102 @@
+package pathfinder.mergetest;
+
+import java.util.LinkedList;
+import remoteeditor.command.REPCommand;
+import remoteeditor.network.REP;
+
+public class TestMerger {
+	static public int cmdNO[] = { REP.REPCMD_INSERT, REP.REPCMD_REPLACE, REP.REPCMD_DELETE };
+	private NetworkSimulator<REPCommand> ns=null;
+	private LinkedList<EditorSimulator> editors;
+	private SeMaSimulator<REPCommand> sema;
+
+	public TestMerger(){
+		editors = new LinkedList<EditorSimulator>();
+	}
+
+	public static void main(String[] args){
+		TestMerger tm;
+		int i = (args.length>0) ? Integer.parseInt(args[0]) : 2;
+		System.out.println("number of Editor = "+i);
+		int j = (args.length>1) ? Integer.parseInt(args[1]) : 3;
+		System.out.println("number of Packet = "+i);
+		tm = new TestMerger();
+
+		tm.init(false, i, j);
+		tm.startTest();
+
+		tm.printAllTexts();
+		//if (!tm.checkCS())
+		//	System.out.println("Error!! :some ChannelSimulator still have packet!");
+		if (!tm.checkEquality())
+			System.out.println("Error!! :all Editor's text is NOT mutch!");
+		assert tm.checkEquality();
+	}
+
+	private void startTest() {
+		for (EditorSimulator ee: editors){
+			ee.start();
+		}
+		if (sema!=null) sema.start();
+
+		for (EditorSimulator ee: editors){
+			try {
+				ee.join();
+			} catch (InterruptedException e) {
+				e.printStackTrace();
+			}
+		}
+		if (sema!=null) sema.finish();
+	}
+
+	private void init(boolean sm, int ne, int np){
+		if (sm){
+			ns = new NetworkSimulatorwithSeMa<REPCommand>();
+			sema = new SeMaSimulator<REPCommand>(ns, ne);
+		} else {
+			ns = new NetworkSimulatorwithoutSeMa<REPCommand>();
+			sema = null;
+		}
+
+		for (int i=0; i<ne; i++){
+			LinkedList<REPCommand> cmds = new LinkedList<REPCommand>();
+			// 各エディタが送信するコマンド列を生成
+
+			for (int j=0; j<np; j++){
+				String str = "created by Editor"+i+":"+j;
+				REPCommand cmd = new REPCommand(REP.REPCMD_INSERT,
+					0, i, j,
+					10, //Verify.random(text.size()-1), //size-1?
+					str.length(), str);
+				cmds.add( cmd);
+			}
+
+			EditorSimulator ee = new EditorSimulator(i, ns, cmds, "Editor"+i);
+			if(i==0) ee.setOwner(true);
+			editors.add(ee);
+		}
+	}
+
+	private void printAllTexts(){
+		for(EditorSimulator ee: editors){
+			System.out.println("--"+ee.getName()+"------------------------");
+			ee.getText().printAllText();
+		}
+	}
+/*
+	private boolean checkCS(){
+		return ns.checkAllCS();
+	}
+*/
+	private boolean checkEquality(){
+		/*
+		Text ee0 = editors.remove().getText();
+		return editors.remove().getText().equals(ee0);
+		*/
+		Text text0 = editors.element().getText();
+		for(EditorSimulator ee: editors){
+			if (!text0.equals(ee.getText())) return false;
+		}
+		return true;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pathfinder/mergetest/Text.java	Sun Dec 23 16:14:06 2007 +0900
@@ -0,0 +1,60 @@
+package pathfinder.mergetest;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+
+import remoteeditor.command.REPCommand;
+import remoteeditor.network.REP;
+
+public class Text {
+	static private String[] text0 = {
+		"aaa", "bbb", "ccc", "ddd", "eee",
+		"fff", "ggg", "hhh", "iii", "jjj",
+		"kkk", "lll", "mmm", "nnn", "ooo",
+		"ppp", "qqq", "rrr", "sss", "ttt",
+		"uuu", "vvv", "www", "xxx", "yyy", "zzz"
+	};
+	LinkedList<String> strList;
+
+	public Text(){
+		this(Text.text0);
+	}
+	public Text(String[] _strings){
+		strList = new LinkedList<String>(Arrays.asList(_strings));
+	}
+
+	synchronized public void insert(int i, String str){
+		assert 0<i && i<strList.size();
+		strList.add(i, str);
+	}
+	synchronized public void delete(int i){
+		assert 0<i && i<strList.size();
+		strList.remove(i);
+	}
+	synchronized public void replace(int i, String str){
+		assert 0<i && i<strList.size();
+		strList.set(i, str);
+	}
+	public String get(int i){
+		assert 0<i && i<strList.size();
+		return strList.get(i);
+	}
+	public void edit(REPCommand cmd){
+		if (cmd.cmd==REP.REPCMD_INSERT)        insert(cmd.lineno, cmd.string);
+		else if (cmd.cmd==REP.REPCMD_REPLACE)  replace(cmd.lineno, cmd.string);
+		else if (cmd.cmd==REP.REPCMD_DELETE)   delete(cmd.lineno);
+		//else assert false;
+	}
+
+	public int size(){
+		return strList.size();
+	}
+	public void printAllText(){
+		for( String str: strList){
+			System.out.println(str);
+		}
+	}
+	public boolean equals(Text _target){
+		return strList.equals(_target.strList);
+	}
+}