Package org.waveprotocol.wave.model.document.indexed

Source Code of org.waveprotocol.wave.model.document.indexed.ObservableIndexedDocumentTest

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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.document.indexed;


import junit.framework.TestCase;

import org.waveprotocol.wave.model.document.MutableDocument;
import org.waveprotocol.wave.model.document.MutableDocumentImpl;
import org.waveprotocol.wave.model.document.ReadableDocument;
import org.waveprotocol.wave.model.document.indexed.DocumentEvent.AttributesModified;
import org.waveprotocol.wave.model.document.indexed.DocumentEvent.ContentDeleted;
import org.waveprotocol.wave.model.document.indexed.DocumentEvent.ContentInserted;
import org.waveprotocol.wave.model.document.indexed.DocumentEvent.TextDeleted;
import org.waveprotocol.wave.model.document.indexed.DocumentEvent.TextInserted;
import org.waveprotocol.wave.model.document.indexed.DocumentHandler.EventBundle;
import org.waveprotocol.wave.model.document.operation.Attributes;
import org.waveprotocol.wave.model.document.operation.Nindo;
import org.waveprotocol.wave.model.document.operation.Nindo.Builder;
import org.waveprotocol.wave.model.document.operation.automaton.DocumentSchema;
import org.waveprotocol.wave.model.document.operation.impl.AttributesImpl;
import org.waveprotocol.wave.model.document.raw.impl.Element;
import org.waveprotocol.wave.model.document.raw.impl.Node;
import org.waveprotocol.wave.model.document.raw.impl.RawDocumentImpl;
import org.waveprotocol.wave.model.document.raw.impl.Text;
import org.waveprotocol.wave.model.document.util.DocHelper;
import org.waveprotocol.wave.model.document.util.DocProviders;
import org.waveprotocol.wave.model.document.util.Point;
import org.waveprotocol.wave.model.operation.OperationException;
import org.waveprotocol.wave.model.operation.OperationRuntimeException;
import org.waveprotocol.wave.model.operation.OperationSequencer;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Basic tests for observable indexed doc
*
* @author danilatos@google.com (Daniel Danilatos)
*/

public class ObservableIndexedDocumentTest extends TestCase {
  EventBundle<Node, Element, Text> events, events2;

  IndexedDocument<Node, Element, Text> indexed;
  IndexedDocument<Node, Element, Text> indexedCopy;
  MutableDocument<Node, Element, Text> doc;
  Element root;

  /** Empty attributes map. */
  private static final Map<String, String> NA = new HashMap<String, String>();

  DocumentHandler<Node, Element, Text> handler = new DocumentHandler<Node, Element, Text>() {
    @Override
    public void onDocumentEvents(EventBundle<Node, Element, Text> eventBundle) {
      events = eventBundle;
    }
  };

  DocumentHandler<Node, Element, Text> handler2 = new DocumentHandler<Node, Element, Text>() {
    @Override
    public void onDocumentEvents(EventBundle<Node, Element, Text> eventBundle) {
      events2 = eventBundle;
    }
  };

  public void testDeletes() throws OperationException {
    for (int i = 0; i < 2; i++) {
      testNindoConsume = i == 0;
      doTestDeletes();
    }
  }

  @SuppressWarnings("unchecked")
  public void doTestDeletes() {
    String a = "<p>abc<b>def</b></p>";
    String b = "<p><b>deH</b>abG</p>";
    String txt = "alksjdflk";

    create(a);

    doc.deleteRange(l(0), l(doc.size()));
    checkEvents(cd(0, 10, dl(st("p", NA), tt("abc"), st("b", NA), tt("def"), et("b"), et("p"))));

    create(a + b);
    List<Element> deletions = new ArrayList<Element>();
    deletions.addAll(
        elementsOuter(doc, DocHelper.getFirstChildElement(doc, doc.getDocumentElement())));
    deletions.addAll(
        elementsOuter(doc, DocHelper.getLastChildElement(doc, doc.getDocumentElement())));

    doc.deleteRange(l(0), l(doc.size()));
    checkEvents(
        cd(0, 10, dl(st("p", NA), tt("abc"), st("b", NA), tt("def"), et("b"), et("p"))),
        cd(0, 10, dl(st("p", NA), st("b", NA), tt("deH"), et("b"), tt("abG"), et("p")))
    );
    checkDeletions(deletions);
    deletions.clear();

    create(txt);
    doc.deleteRange(l(0), l(doc.size()));
    checkEvents(td(0, txt));

    create(a + txt + b);
    deletions.addAll(
        elementsOuter(doc, DocHelper.getFirstChildElement(doc, doc.getDocumentElement())));
    deletions.addAll(
        elementsOuter(doc, DocHelper.getLastChildElement(doc, doc.getDocumentElement())));
    doc.deleteRange(l(0), l(doc.size()));
    checkEvents(
        cd(0, 10, dl(st("p", NA), tt("abc"), st("b", NA), tt("def"), et("b"), et("p"))),
        td(0, txt),
        cd(0, 10, dl(st("p", NA), st("b", NA), tt("deH"), et("b"), tt("abG"), et("p")))
    );
    checkDeletions(deletions);
    deletions.clear();
  }

  private <E> List<E> elements(ReadableDocument<? super E, E, ?> doc) {
    return elementsInner(doc, doc.getDocumentElement());
  }

  private <E> List<E> elementsInner(ReadableDocument<? super E, E, ?> doc, E e) {
    List<E> els = new ArrayList<E>();
    buildElements(doc, e, els);
    return els;
  }

  private <E> List<E> elementsOuter(ReadableDocument<? super E, E, ?> doc, E e) {
    List<E> els = new ArrayList<E>();
    els.add(e);
    buildElements(doc, e, els);
    return els;
  }

  private <E> void buildElements(ReadableDocument<? super E, E, ?> doc, E e, List<E> els) {
    E child = DocHelper.getFirstChildElement(doc, e);
    while (child != null) {
      els.add(child);
      buildElements(doc, child, els);
      child = DocHelper.getNextSiblingElement(doc, child);
    }
  }

  public void testAttributes() throws OperationException {
    for (int i = 0; i < 2; i++) {
      testNindoConsume = i == 0;
      doTestAttributes();
    }
  }

  @SuppressWarnings("unchecked")
  public void doTestAttributes() {
    String a = "<p>abc<b>def</b></p>";

    create(a);
    Element elem = (Element) doc.getDocumentElement().getFirstChild();

    Attributes attrs = attrs("x", "1", "y", "2", "z", "3");
    doc.setElementAttributes(elem, attrs);
    checkEvents(am(elem, pairs("x", null, "y", null, "z", null), attrs));

    Attributes updates = attrs("w", "4", "x", "1", "y", "5");
    doc.updateElementAttributes(elem, updates);
    // x omitted because no change.
    checkEvents(am(elem, pairs("w", null, "y", "2"), attrs("w", "4", "y", "5")));

    Map<String, String> updates2 = pairs("w", null);
    doc.updateElementAttributes(elem, updates2);
    // x omitted because no change.
    checkEvents(am(elem, pairs("w", "4"), pairs("w", null)));

    Attributes sets = attrs("x", "1", "y", "6", "v", "7");
    doc.setElementAttributes(elem, sets);
    // x omitted because no change.
    checkEvents(am(elem,
        pairs("y", "5", "z", "3", "v", null),
        pairs("y", "6", "z", null, "v", "7")));

    // tests lack of concurrent modification exception:
    Attributes sets2 = attrs("x", "1", "y", "6", "v", "7");
    doc.setElementAttributes(elem, sets2);
    checkEvents(am(elem, pairs(), pairs()));
  }

  public void testInserts() throws OperationException {
    for (int i = 0; i < 2; i++) {
      testNindoConsume = i == 0;
      doTestInserts();
    }
  }

  @SuppressWarnings("unchecked")
  public void doTestInserts() throws OperationException {
    String a = "<p>abc<b>def</b></p>";

    create(a);

    Element firstEl = (Element) doc.getFirstChild(doc.getDocumentElement());

    Builder b = at(0);
    Attributes attrs = attrs("x", "1", "y", "2");
    b.replaceAttributes(attrs);
    b.elementStart("x", attrs("a", "1"));
    b.characters("hello");
    b.elementStart("y", attrs("b", "2", "c", "3"));
    b.characters("yeah");
    b.elementEnd();
    b.elementEnd();
    String moreText = "more text";
    b.characters(moreText);
    consumeNindo(b.build());

    checkEvents(
        am(firstEl, pairs("x", null, "y", null), attrs),
        ci(doc.asElement(firstEl.getFirstChild())),
        ti(14, moreText));
  }

  public void testAdjacentContentInsertsAndDeletes1() throws OperationException {
    testNindoConsume = true;
    doTestAdjacentContentInsertsAndDeletes();
  }

  public void testAdjacentContentInsertsAndDeletes2() throws OperationException {
    testNindoConsume = false;
    doTestAdjacentContentInsertsAndDeletes();
  }

  @SuppressWarnings("unchecked")
  public void doTestAdjacentContentInsertsAndDeletes() throws OperationException {
    String a1 = "<zz><p x=\"y\">abc<b a=\"b\">def</b></p></zz>";
    String a = a1 + "<p>blah</p>";

    create(a);

    // GWT doesn't define subList.
    Element top = (Element) doc.getDocumentElement().getFirstChild();
    Node n1 = doc.getFirstChild(top);
    Node n2 = doc.getLastChild(top);
    List<Element> all = elementsInner(doc, top);
    List<Element> els = Arrays.asList(all.get(0), all.get(1));

    Builder b = at(0);
    Attributes attrs = attrs("x", "1", "y", "2");
    b.replaceAttributes(attrs);
    b.elementStart("x", attrs("a", "1"));
    b.characters("hello");
    b.elementStart("y", attrs("b", "2", "c", "3"));
    b.characters("yeah");
    b.elementEnd();
    b.elementEnd();
    String moreText = "more text";
    b.characters(moreText);
    b.deleteElementStart();
    b.deleteCharacters(3);
    b.deleteElementStart();
    b.deleteCharacters(3);
    b.deleteElementEnd();
    b.deleteElementEnd();
    String moreText2 = "more";
    b.characters(moreText2);
    consumeNindo(b.build());

    checkEvents(
        am(top, pairs("x", null, "y", null), attrs),
        ci(doc.asElement(top.getFirstChild())),
        ti(14, moreText),
        cd(23, 10, dl(st("p", attrs("x", "y")), tt("abc"), st("b", attrs("a", "b")),
            tt("def"), et("b"), et("p"))),
        ti(14 + moreText.length(), moreText2));
    checkDeletions(els);
  }

  private Attributes attrs(String ... strs) {
    return new AttributesImpl(pairs(strs));
  }

  private Map<String, String> pairs(String ... strs) {
    assert strs.length % 2 == 0;
    Map<String, String> map = new HashMap<String, String>();
    for (int i = 0; i < strs.length; i += 2) {
      map.put(strs[i], strs[i + 1]);
    }
    return map;
  }

  private DocumentEvent<Node, Element, Text> ti(int loc, String text) {
    return new TextInserted<Node, Element, Text>(loc, text);
  }

  private DocumentEvent<Node, Element, Text> ci(Element e) {
    return new ContentInserted<Node, Element, Text>(e);
  }

  private DocumentEvent<Node, Element, Text> am(Element e,
      Map<String, String> oldVals, Map<String, String> newVals) {
    return new AttributesModified<Node, Element, Text>(e, oldVals, newVals);
  }

  private DocumentEvent<Node, Element, Text> td(int loc, String text) {
    return new TextDeleted<Node, Element, Text>(loc, text);
  }

  private static List<ContentDeleted.Token> dl(ContentDeleted.Token... tokens) {
    List<ContentDeleted.Token> tokenList = new ArrayList<ContentDeleted.Token>();
    for (ContentDeleted.Token token : tokens) {
      tokenList.add(token);
    }
    return tokenList;
  }

  private static ContentDeleted.Token tt(String text) {
    return ContentDeleted.Token.textToken(text);
  }

  private static ContentDeleted.Token st(String tagName, Map<String, String> attributes) {
    return ContentDeleted.Token.elementStartToken(tagName, attributes);
  }

  private static ContentDeleted.Token et(String tagName) {
    return ContentDeleted.Token.elementEndToken(tagName);
  }

  private DocumentEvent<Node, Element, Text> cd(int loc, int size,
      List<ContentDeleted.Token> tokens) {
    return new ContentDeleted<Node, Element, Text>(loc, size, tokens, null);
  }

  private void checkDeletions(Collection<Element> expected) {
    for (Element expectedDeletion : expected) {
      assertTrue(getEvents().wasDeleted(expectedDeletion));
    }
    for (Element realDeletion : getEvents().getDeletedElements()) {
      assertTrue(expected.contains(realDeletion));
    }
  }

  private void checkEvents(Object ... expectedEvents) {
    assertEquals(flatten(Arrays.asList(expectedEvents)), getEvents().getEventComponents());
  }

  private List<Object> flatten(List<?> objects) {
    List<Object> list = new ArrayList<Object>();
    for (Object o : objects) {
      if (o instanceof List) {
        list.addAll(flatten((List<?>) o));
      } else {
        list.add(o);
      }
    }
    return list;
  }

  private Point<Node> l(int location) {
    if (location < 0) {
      location = doc.size() + location;
    }
    return doc.locate(location);
  }

  private void consumeNindo(Nindo op) throws OperationException {
    indexedCopy.consume(indexed.consumeAndReturnInvertible(op));
  }

  boolean testNindoConsume;

  private void create(String xml) {

    RawDocumentImpl raw = DocProviders.ROJO.parse("<d>" + xml + "</d>");
    RawDocumentImpl raw2 = DocProviders.ROJO.parse("<d>" + xml + "</d>");

    indexed = new ObservableIndexedDocument<Node, Element, Text, Void>(handler, raw, null,
        DocumentSchema.NO_SCHEMA_CONSTRAINTS);
    indexedCopy = new ObservableIndexedDocument<Node, Element, Text, Void>(
        handler2, raw2, null, DocumentSchema.NO_SCHEMA_CONSTRAINTS);

    doc = new MutableDocumentImpl<Node, Element, Text>(
        new OperationSequencer<Nindo>() {
          @Override
          public void begin() {
          }

          @Override
          public void consume(Nindo op) {
            try {
              consumeNindo(op);
            } catch (OperationException e) {
              throw new OperationRuntimeException("Bug!", e);
            }
          }

          @Override
          public void end() {
          }
        }, testNindoConsume ? indexed : indexedCopy);

    root = doc.getDocumentElement();
  }

  EventBundle<Node, Element, Text> getEvents() {
    return testNindoConsume ? events : events2;
  }

  private Builder at(int location) {
    Builder b = new Builder();
    if (location > 0) {
      b.skip(location);
    }
    return b;
  }
}
TOP

Related Classes of org.waveprotocol.wave.model.document.indexed.ObservableIndexedDocumentTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.