/**
* 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.operation.impl;
import java.util.ArrayList;
import java.util.List;
import org.waveprotocol.wave.model.document.operation.AnnotationBoundaryMap;
import org.waveprotocol.wave.model.document.operation.util.ImmutableUpdateMap;
import org.waveprotocol.wave.model.util.Preconditions;
public class AnnotationsUpdateImpl
extends ImmutableUpdateMap<AnnotationsUpdateImpl, AnnotationsUpdate>
implements AnnotationsUpdate {
public static final AnnotationsUpdateImpl EMPTY_MAP = new AnnotationsUpdateImpl();
public AnnotationsUpdateImpl() {}
private AnnotationsUpdateImpl(List<AttributeUpdate> updates) {
super(updates);
}
@Override
protected AnnotationsUpdateImpl createFromList(List<AttributeUpdate> updates) {
return new AnnotationsUpdateImpl(updates);
}
/**
* A string that is larger (according to compareTo) than any valid annotation key.
*/
private static final String MAX_STRING = "\uFFFF";
@Override
public AnnotationsUpdateImpl composeWith(AnnotationBoundaryMap map) {
List<AttributeUpdate> newUpdates = new ArrayList<AttributeUpdate>();
int existingIndex = 0;
int changeIndex = 0;
int endIndex = 0;
while (existingIndex < updates.size()
|| changeIndex < map.changeSize()
|| endIndex < map.endSize()) {
String existingKey = existingIndex < updates.size() ? updates.get(existingIndex).name
: MAX_STRING;
String changeKey = changeIndex < map.changeSize() ? map.getChangeKey(changeIndex)
: MAX_STRING;
String endKey = endIndex < map.endSize() ? map.getEndKey(endIndex) : MAX_STRING;
// cases:
// existingKey < endKey && existingKey < changeKey: keep, advance existing
// existingKey < endKey && existingKey = changeKey: replace, advance existing & change
// existingKey < endKey && existingKey > changeKey: add change, advance change
// existingKey = endKey && existingKey < changeKey: remove, advance existing & end
// existingKey = endKey && existingKey = changeKey: error (key in both change and end)
// existingKey = endKey && existingKey > changeKey: remove, add change, advance all 3
// existingKey > endKey: error (attempt to end key that is not part of the update)
int existingVsEnd = existingKey.compareTo(endKey);
int existingVsChange = existingKey.compareTo(changeKey);
if (existingVsEnd < 0) {
if (existingVsChange < 0) {
newUpdates.add(updates.get(existingIndex));
existingIndex++;
} else if (existingVsChange == 0) {
newUpdates.add(new AttributeUpdate(changeKey,
map.getOldValue(changeIndex),
map.getNewValue(changeIndex)));
existingIndex++;
changeIndex++;
} else if (existingVsChange > 0) {
newUpdates.add(new AttributeUpdate(changeKey,
map.getOldValue(changeIndex),
map.getNewValue(changeIndex)));
changeIndex++;
} else {
assert false;
}
} else if (existingVsEnd == 0) {
if (existingVsChange < 0) {
existingIndex++;
endIndex++;
} else if (existingVsChange == 0) {
Preconditions.illegalArgument("AnnotationBoundaryMap with key both in change and end: "
+ changeKey);
} else if (existingVsChange > 0) {
newUpdates.add(new AttributeUpdate(changeKey,
map.getOldValue(changeIndex),
map.getNewValue(changeIndex)));
existingIndex++;
endIndex++;
changeIndex++;
} else {
assert false;
}
} else if (existingVsEnd > 0) {
Preconditions.illegalArgument("Attempt to end key that is not part of the update: "
+ endKey);
} else {
assert false;
}
}
return createFromList(newUpdates);
}
public boolean containsKey(String key) {
Preconditions.checkNotNull(key, "Null key");
// TODO: Use Arrays.binarySearch(a, key, c).
for (AttributeUpdate u : updates) {
if (key.equals(u.name)) {
return true;
}
}
return false;
}
}