/** * 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; import org.waveprotocol.wave.model.util.CollectionUtils; import org.waveprotocol.wave.model.util.Pair; import org.waveprotocol.wave.model.util.ReadableStringMap.ProcV; import org.waveprotocol.wave.model.util.ReadableStringSet.Proc; import org.waveprotocol.wave.model.util.StringMap; import org.waveprotocol.wave.model.util.StringSet; import java.util.Arrays; import java.util.Comparator; /** * A more convenient builder than AnnotationBoundaryMapImpl.builder(). * * @author ohler@google.com (Christian Ohler) */ // TODO(ohler): merge with AnnotationBoundaryMapImpl.Builder public final class AnnotationBoundaryMapBuilder { private final StringSet endKeys = CollectionUtils.createStringSet(); private final StringMap<Pair<String, String>> changes = CollectionUtils.createStringMap(); public AnnotationBoundaryMapBuilder() {} /** * Adds an end of the given key to the boundary map under construction. This * overrides any previous calls to change() with that key. */ public AnnotationBoundaryMapBuilder end(String key) { changes.remove(key); endKeys.add(key); return this; } /** * Adds a change of the given key from oldValue to newValue to the boundary * map under construction. This overrides any previous calls to change() * or end() with the same key. */ public AnnotationBoundaryMapBuilder change(String key, String oldValue, String newValue) { endKeys.remove(key); changes.put(key, Pair.of(oldValue, newValue)); return this; } private static class Triplet { final String key; final String oldValue; final String newValue; public Triplet(String key, String oldValue, String newValue) { assert key != null; this.key = key; this.oldValue = oldValue; this.newValue = newValue; } } private static Comparator<Triplet> TRIPLET_COMPARATOR = new Comparator<Triplet>() { @Override public int compare(Triplet a, Triplet b) { return a.key.compareTo(b.key); } }; private static class AnnotationBoundaryMapImpl implements AnnotationBoundaryMap { final String[] ends; final Triplet[] changes; public AnnotationBoundaryMapImpl(String[] ends, Triplet[] changes) { this.ends = ends; this.changes = changes; } @Override public int changeSize() { return changes.length; } @Override public int endSize() { return ends.length; } @Override public String getChangeKey(int i) { return changes[i].key; } @Override public String getEndKey(int i) { return ends[i]; } @Override public String getNewValue(int i) { return changes[i].newValue; } @Override public String getOldValue(int i) { return changes[i].oldValue; } } /** * Returns an AnnotationBoundaryMap that corresponds to this builder's state. * * Behaviour is undefined if this builder is used after calling this method. */ public AnnotationBoundaryMap build() { final String[] ends = new String[endKeys.countEntries()]; final Triplet[] changes = new Triplet[this.changes.countEntries()]; endKeys.each(new Proc() { int i = 0; @Override public void apply(String key) { ends[i] = key; i++; }}); this.changes.each(new ProcV<Pair<String, String>> () { int i = 0; @Override public void apply(String key, Pair<String, String> value) { changes[i] = new Triplet(key, value.first, value.second); i++; }}); Arrays.sort(ends); Arrays.sort(changes, TRIPLET_COMPARATOR); return new AnnotationBoundaryMapImpl(ends, changes); } }