/** * 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 com.google.wave.api.data; import static org.mockito.Mockito.mock; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.wave.api.Attachment; import com.google.wave.api.ElementType; import com.google.wave.api.Range; import com.google.wave.api.Restriction; import junit.framework.TestCase; import org.waveprotocol.wave.model.conversation.Blips; import org.waveprotocol.wave.model.document.Document; import org.waveprotocol.wave.model.document.indexed.IndexedDocument; import org.waveprotocol.wave.model.document.operation.Nindo; 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.Text; import org.waveprotocol.wave.model.document.util.DocProviders; import org.waveprotocol.wave.model.document.util.DocumentImpl; import org.waveprotocol.wave.model.document.util.LineContainers; import org.waveprotocol.wave.model.operation.OperationException; import org.waveprotocol.wave.model.operation.OperationRuntimeException; import org.waveprotocol.wave.model.operation.OperationSequencer; import org.waveprotocol.wave.model.wave.Wavelet; import java.util.Collections; import java.util.List; import java.util.Map; /** * Tests for the DocumentHitIterator related classes. * */ public class DocumentHitIteratorTest extends TestCase { @Override protected void setUp() throws Exception { super.setUp(); Blips.init(); } public void testSingleshot() { Range range = new Range(0, 10); DocumentHitIterator it = new DocumentHitIterator.Singleshot(range); Range first = it.next(); assertEquals(range, first); assertNull(it.next()); } public void testTextMatcherWithoutShift() { ApiView apiView = createApiViewFromXml("1 1 1"); DocumentHitIterator it = new DocumentHitIterator.TextMatcher(apiView, "1", -1); for (int i = 0; i < 3; i++) { Range r = it.next(); assertEquals(i * 2 + 1, r.getStart()); assertEquals(i * 2 + 2, r.getEnd()); } assertNull(it.next()); } public void testTextMatcherDeletingTheFirstChar() { ApiView apiView = createApiViewFromXml("1 1 1"); DocumentHitIterator it = new DocumentHitIterator.TextMatcher(apiView, "1", -1); for (int i = 0; i < 3; i++) { Range r = it.next(); assertEquals(i + 1, r.getStart()); apiView.delete(1, 2); it.shift(1, -1); } assertNull(it.next()); } /** * Pretend we're shifting in the manner of INSERT_AFTER */ public void testTextMatcherShiftInsertAfter() { ApiView apiView = createApiViewFromXml("1 1 1"); DocumentHitIterator it = new DocumentHitIterator.TextMatcher(apiView, "1", -1); for (int i = 0; i < 3; i++) { Range r = it.next(); assertEquals(i * 3 + 1, r.getStart()); apiView.insert(r.getEnd(), "1"); it.shift(r.getEnd(), 1); } assertNull(it.next()); } public void testBasicElementMatcher() { String xml = "text <gadget></gadget> <something/> <gadget/> hello"; ApiView apiView = createApiViewFromXml(xml); DocumentHitIterator it = new DocumentHitIterator.ElementMatcher( apiView, ElementType.GADGET, Collections.<String, String>emptyMap(), -1); List<Integer> hits = Lists.newArrayList(); assertEquals(xml.indexOf("<gadget>") + 1, it.next().getStart()); assertEquals(10, it.next().getStart()); // Elements get counted as 1 assertNull(it.next()); } public void testElementMatcherWithRestrictions() { String xml = "text <image attachment=\"nR_0YFT75\" style=\"full\">" + "<caption>good times</caption></image> hello"; ApiView apiView = createApiViewFromXml(xml); Map<String, String> restrictions = Maps.newHashMap(); assertEquals(1, countHits(new DocumentHitIterator.ElementMatcher( apiView, ElementType.ATTACHMENT, Collections.<String, String>emptyMap(), -1))); Restriction restriction = Attachment.restrictByAttachmentId("nR_0YFT75"); assertEquals(1, countHits(new DocumentHitIterator.ElementMatcher(apiView, ElementType.ATTACHMENT, ImmutableMap.of(restriction.getKey(), restriction.getValue()), -1))); restriction = Attachment.restrictByAttachmentId("nR_0YFT76"); assertEquals(0, countHits(new DocumentHitIterator.ElementMatcher(apiView, ElementType.ATTACHMENT, ImmutableMap.of(restriction.getKey(), restriction.getValue()), -1))); } private int countHits(DocumentHitIterator it) { int res = 0; while (it.next() != null) { res++; } return res; } private static ApiView createApiViewFromXml(String xml) { IndexedDocument<Node, Element, Text> indexedDoc = DocProviders.POJO.parse(LineContainers.debugContainerWrap(xml)); Document doc = new DocumentImpl(createSequencer(indexedDoc), indexedDoc); return new ApiView(doc, mock(Wavelet.class)); } /** * Creates and returns a sequencer which applies incoming ops to the given * document */ private static OperationSequencer<Nindo> createSequencer( final IndexedDocument<Node, Element, Text> document) { return new OperationSequencer<Nindo>() { @Override public void begin() { } @Override public void end() { } @Override public void consume(Nindo op) { try { document.consumeAndReturnInvertible(op); } catch (OperationException oe) { throw new OperationRuntimeException("sequencer consume failed.", oe); } } }; } }