/**
* 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;
import java.util.Comparator;
import java.util.List;
/**
* An annotation set which supports setting and clearing annotated regions.
*
* @author danilatos@google.com (Daniel Danilatos)
*/
public interface MutableAnnotationSet<V> extends ReadableAnnotationSet<V> {
// TODO(danilatos): Move/rename these alias interfaces as a top level
// interface once use and semantics have crystallised.
/**
* An annotation set that may be serialised (e.g. via operations).
*/
public interface Persistent extends MutableAnnotationSet<String> {
}
/**
* An annotation set for local book-keeping only.
*/
public interface Local extends MutableAnnotationSet<Object> {
}
/**
* Sets the value for a key over a range. The value may be null, which clears
* the annotation over the range.
*
* Only affects the given range, in contrast with
* {@link #resetAnnotation(int, int, String, Object)}
*
* @param start location of first item in the range
* @param end location of first item beyond the range
* @param key annotation key
* @param value value to set
*/
void setAnnotation(int start, int end, String key, V value);
/**
* Sets the value for a key over a range and clears the value for all
* locations outside the range as an atomic action.
*
* @see #setAnnotation(int, int, String, Object)
*/
void resetAnnotation(int start, int end, String key, V value);
/**
* Immutable class to hold a range with a value
*/
public final class RangedValue<V> {
public final int start;
public final int end;
public final V value;
public RangedValue(int start, int end, V value) {
if (start > end) {
throw new IllegalArgumentException("start must be <= end");
}
this.start = start;
this.end = end;
this.value = value;
}
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object obj) {
if (!(obj instanceof RangedValue) || obj == null) {
return false;
}
RangedValue<V> rv = (RangedValue<V>) obj;
return start == rv.start && end == rv.end && value.equals(rv.value);
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + start;
result = 31 * result + end;
result = 31 * result + value.hashCode();
return result;
}
}
/**
* A special purpose comparator that compares RangedValues only using their
* start and end values. It doesn't use the value, as the value is not
* necessarily Comparable.
*
* Note that the ordering produced by this comparator is not consistent
* with RangedValue<V>.equals(), eg
*
* RangedValue<String>(1, 1, "a") and
* RangedValue<String>(1, 1, "b")
*
* are considered equal.
*/
public class CompareRangedValueByStartThenEnd<V> implements Comparator<RangedValue<V>> {
@Override
public int compare(RangedValue<V> left, RangedValue<V> right) {
int startDelta = left.start - right.start;
if (startDelta != 0) {
return startDelta;
}
// We don't compare on the value - we are only interested in the
// start/end positions
int endDelta = left.end - right.end;
return endDelta;
}
}
/**
* Sets a set of values for a key within a given range and clears the value for all
* other locations inside the range as an atomic action.
*
* The Map of ranges to set must be in order of increasing location and be
* non-overlapping.
*
* NOTE(user) : This function is marked deprecated since it attempts to
* emit a minimal set of mutations to set the requested list of annotations. This
* may harm the semantics of the requested list in the presence of transforms.
* The minimisation of mutations is an optimisation for demo purposes, and you
* should only use this function if you know what you are doing. It will go away
* once we have op combining so that the semantically correct mutations can be
* efficiently sent.
*
* @param rangeStart the beginning of the range
* @param rangeEnd the end of the range
* @param key the key to be setting
* @param values a mapping of a range onto a value
*/
@Deprecated
void resetAnnotationsInRange(int rangeStart, int rangeEnd, String key,
List<RangedValue<V>> values);
}