/** * 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.document.util; import org.waveprotocol.wave.model.document.AnnotationCursor; import org.waveprotocol.wave.model.document.ReadableAnnotationSet; import org.waveprotocol.wave.model.util.CollectionUtils; import org.waveprotocol.wave.model.util.Preconditions; import org.waveprotocol.wave.model.util.ReadableStringSet; import org.waveprotocol.wave.model.util.ReadableStringSet.Proc; import org.waveprotocol.wave.model.util.StringSet; import java.util.NoSuchElementException; import java.util.PriorityQueue; import java.util.Queue; /** * Generic {@link AnnotationCursor}. * * Implemented in terms of an annotation set. * * @param <V> Value parameter of the annotation set * * @author danilatos@google.com (Daniel Danilatos) */ // NOTE(danilatos/ohler): It may at some distant point in the future be worth // having an implementation inside the annotation set impl, for efficiency. public final class GenericAnnotationCursor<V> implements AnnotationCursor { /** * Represents the current location for a key. This class is mutable in its * location. */ private static final class KeyLocation implements Comparable<KeyLocation> { private final String key; private int location; private KeyLocation(String key, int initialLocation) { this.key = key; this.location = initialLocation; } @Override // The PriorityQueue documentation states that the head will have the least // value, so we want a natural ordering on location. public int compareTo(KeyLocation other) { return location - other.location; } } // Invariants. private final ReadableAnnotationSet<V> annotations; private final int end; // State. private final Queue<KeyLocation> locations = new PriorityQueue<KeyLocation>(); int currentLocation; /** * The range values have the same meaning as the annotation set interfaces. * * @param annotations annotation set * @param start start of the range * @param end end of the range * @param keys key set for which to search for changes */ public GenericAnnotationCursor(ReadableAnnotationSet<V> annotations, final int start, int end, ReadableStringSet keys) { Preconditions.checkPositionIndexes(start, end, annotations.size()); Preconditions.checkNotNull(keys, "GenericAnnotationCursor: Key set must not be null"); this.annotations = annotations; this.end = end; keys.each(new Proc() { @Override public void apply(String key) { advance(new KeyLocation(key, start)); } }); this.currentLocation = hasNext() ? start : -1; } @Override public ReadableStringSet nextLocation() { if (!hasNext()) { throw new NoSuchElementException(); } StringSet currentKeys = CollectionUtils.createStringSet(); currentLocation = locations.peek().location; do { KeyLocation keyLocation = locations.remove(); currentKeys.add(keyLocation.key); advance(keyLocation); } while (!locations.isEmpty() && locations.peek().location == currentLocation); return currentKeys; } @Override public int currentLocation() { return currentLocation; } @Override public boolean hasNext() { return !locations.isEmpty(); } /** * Advance the given key location to its next value change and set its * location. If it has one, place it back in the queue. If not (location == * -1), do not place it back in the queue. * * @param keyLocation */ private void advance(KeyLocation keyLocation) { V val = annotations.getAnnotation(keyLocation.location, keyLocation.key); keyLocation.location = annotations.firstAnnotationChange(keyLocation.location, end, keyLocation.key, val); if (keyLocation.location != -1) { locations.add(keyLocation); } } }