/** * Copyright 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.waveprotocol.wave.model.operation.testing; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.waveprotocol.wave.model.operation.Domain; import org.waveprotocol.wave.model.operation.OperationException; import org.waveprotocol.wave.model.operation.OperationPair; import org.waveprotocol.wave.model.operation.TransformException; import org.waveprotocol.wave.model.operation.testing.StringDomain.StringOp.Component; import org.waveprotocol.wave.model.operation.testing.StringDomain.StringOp.Delete; import org.waveprotocol.wave.model.operation.testing.StringDomain.StringOp.Insert; import org.waveprotocol.wave.model.operation.testing.StringDomain.StringOp.Skip; public class StringDomain implements Domain<StringDomain.Data, StringDomain.StringOp> { public static final class Data { private final StringBuilder value = new StringBuilder(); @Override public String toString() { return value.toString(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((value == null) ? 0 : value.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Data other = (Data) obj; return value.toString().equals(other.value.toString()); } } public static class StringOp { final List<Component> components; public StringOp(List<Component> components) { this.components = new ArrayList<Component>(components); } public StringOp(Component[] ... componentGroups) { this.components = new ArrayList<Component>(); for (Component[] group : componentGroups) { components.addAll(Arrays.asList(group)); } } void apply(Data d) throws OperationException { int location = 0; for (Component c : components) { location += c.apply(location, d); } } static abstract class Component { abstract int apply(int location, Data d) throws OperationException; } static class Insert extends Component { final char inserted; public Insert(char inserted) { this.inserted = inserted; } @Override int apply(int location, Data d) throws OperationException { d.value.insert(location, inserted); return 1; } @Override public String toString() { return inserted + ""; } } static class Delete extends Component { final char deleted; public Delete(char deleted) { this.deleted = deleted; } @Override int apply(int location, Data d) throws OperationException { if (location >= d.value.length()) { throw new OperationException("Delete at end"); } d.value.deleteCharAt(location); return 0; } @Override public String toString() { return ((char) (deleted - 'A' + 'a')) + ""; } } static class Skip extends Component { static final Skip INSTANCE = new Skip(); @Override int apply(int location, Data d) throws OperationException { if (location >= d.value.length()) { throw new OperationException("Skip past end"); } return 1; } @Override public String toString() { return "-"; } } @Override public String toString() { StringBuilder b = new StringBuilder(); for (Component c : components) { b.append(c.toString()); } return b.toString(); } } private StringOp.Insert[] i(String text) { StringOp.Insert[] group = new StringOp.Insert[text.length()]; for (int i = 0; i < text.length(); i++) { group[i] = new StringOp.Insert(text.charAt(i)); } return group; } private StringOp.Delete[] d(String text) { StringOp.Delete[] group = new StringOp.Delete[text.length()]; for (int i = 0; i < text.length(); i++) { group[i] = new StringOp.Delete(text.charAt(i)); } return group; } private StringOp.Skip[] s(int distance) { StringOp.Skip[] group = new StringOp.Skip[distance]; for (int i = 0; i < distance; i++) { group[i] = StringOp.Skip.INSTANCE; } return group; } @Override public void apply(StringOp op, Data state) throws OperationException { op.apply(state); } @Override public StringOp asOperation(Data state) { return new StringOp(i(state.value.toString())); } @Override public StringOp compose(StringOp f, StringOp g) throws OperationException { Iterator<Component> after = f.components.iterator(); Iterator<Component> before = g.components.iterator(); //System.err.println("XXXX"); List<Component> list = new ArrayList<Component>(); Component beforeComponent = null; Component afterComponent = null; boolean advanceBefore = true; boolean advanceAfter = true; while ((!advanceBefore || before.hasNext()) && (!advanceAfter || after.hasNext())) { if (advanceBefore) beforeComponent = before.next(); if (advanceAfter) afterComponent = after.next(); advanceBefore = false; advanceAfter = false; if (beforeComponent instanceof Skip) { if (afterComponent instanceof Skip) { //System.err.println("SS " + beforeComponent + " " + afterComponent); list.add(Skip.INSTANCE); advanceBefore = true; advanceAfter = true; } else if (afterComponent instanceof Insert) { //System.err.println("SI " + beforeComponent + " " + afterComponent); list.add(afterComponent); advanceAfter = true; } else { //System.err.println("SD " + beforeComponent + " " + afterComponent); assert afterComponent instanceof Delete; list.add(afterComponent); advanceBefore = true; advanceAfter = true; } } else if (beforeComponent instanceof Insert) { if (afterComponent instanceof Skip) { //System.err.println("IS " + beforeComponent + " " + afterComponent); list.add(beforeComponent); advanceBefore = true; advanceAfter = true; } else if (afterComponent instanceof Insert) { //System.err.println("II " + beforeComponent + " " + afterComponent); list.add(afterComponent); advanceAfter = true; } else { //System.err.println("ID " + beforeComponent + " " + afterComponent); assert afterComponent instanceof Delete; advanceBefore = true; advanceAfter = true; } } else { assert beforeComponent instanceof Delete; if (afterComponent instanceof Skip) { //System.err.println("DS " + beforeComponent + " " + afterComponent); list.add(beforeComponent); advanceBefore = true; } else if (afterComponent instanceof Insert) { //System.err.println("DI " + beforeComponent + " " + afterComponent); list.add(afterComponent); advanceAfter = true; } else { assert afterComponent instanceof Delete; //System.err.println("DD " + beforeComponent + " " + afterComponent); list.add(beforeComponent); advanceBefore = true; } } } if (!advanceBefore && beforeComponent != null) { list.add(beforeComponent); } while (before.hasNext()) { list.add(before.next()); //System.err.println("Finally before: " + list.get(list.size() - 1)); } if (!advanceAfter && afterComponent != null) { list.add(afterComponent); } while (after.hasNext()) { list.add(after.next()); //System.err.println("Finally after: " + list.get(list.size() - 1)); } StringOp op = new StringOp(list); //System.err.println("Composed " + f + " of " + g + " = " + op); return op; } @Override public OperationPair<StringOp> transform(StringOp clientOp, StringOp serverOp) throws TransformException { Iterator<Component> client = clientOp.components.iterator(); Iterator<Component> server = serverOp.components.iterator(); //System.err.println("======================="); List<Component> clientList = new ArrayList<Component>(); List<Component> serverList = new ArrayList<Component>(); Component clientComponent = null; Component serverComponent = null; boolean advanceClient = true; boolean advanceServer = true; while ((!advanceServer || server.hasNext()) && (!advanceClient || client.hasNext())) { if (advanceClient) clientComponent = client.next(); if (advanceServer) serverComponent = server.next(); advanceClient = false; advanceServer = false; if (serverComponent instanceof Insert) { //System.err.println("I? " + serverComponent + " " + clientComponent); serverList.add(serverComponent); clientList.add(Skip.INSTANCE); advanceServer = true; } else if (serverComponent instanceof Skip) { if (clientComponent instanceof Skip) { //System.err.println("SS " + serverComponent + " " + clientComponent); serverList.add(Skip.INSTANCE); clientList.add(Skip.INSTANCE); advanceServer = true; advanceClient = true; } else if (clientComponent instanceof Insert) { //System.err.println("SI " + serverComponent + " " + clientComponent); serverList.add(Skip.INSTANCE); clientList.add(clientComponent); advanceClient = true; } else { //System.err.println("SD " + serverComponent + " " + clientComponent); assert clientComponent instanceof Delete; clientList.add(clientComponent); advanceClient = true; advanceServer = true; } } else { assert serverComponent instanceof Delete; if (clientComponent instanceof Skip) { //System.err.println("DS " + serverComponent + " " + clientComponent); serverList.add(serverComponent); advanceServer = true; advanceClient = true; } else if (clientComponent instanceof Insert) { //System.err.println("DI " + serverComponent + " " + clientComponent); serverList.add(Skip.INSTANCE); clientList.add(clientComponent); advanceClient = true; } else { //System.err.println("DD " + serverComponent + " " + clientComponent); assert clientComponent instanceof Delete; advanceClient = true; advanceServer = true; } } } if (!advanceClient && clientComponent != null) { clientList.add(clientComponent); //System.err.println("Client: " + clientList.get(clientList.size() - 1)); } while (client.hasNext()) { clientList.add(client.next()); //System.err.println("Client: " + clientList.get(clientList.size() - 1)); } if (!advanceServer && serverComponent != null) { serverList.add(serverComponent); //System.err.println("Server: " + serverList.get(serverList.size() - 1)); } while (server.hasNext()) { serverList.add(server.next()); //System.err.println("Server: " + serverList.get(serverList.size() - 1)); } StringOp clientOp2 = new StringOp(clientList); StringOp serverOp2 = new StringOp(serverList); return new OperationPair<StringOp>(clientOp2, serverOp2); } @Override public boolean equivalent(Data state1, Data state2) { return state1.equals(state2); } @Override public Data initialState() { return new Data(); } @Override public StringOp invert(StringOp operation) { // TODO Auto-generated method stub //return null; throw new UnsupportedOperationException("invert"); } }