// Copyright 2012 Google Inc. All Rights Reserved.
//
// 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.collide.client.util.collections;
import com.google.collide.json.shared.JsonArray;
import com.google.collide.json.shared.JsonStringMap;
import com.google.common.base.Preconditions;
import javax.annotation.Nonnull;
/**
* Implementation of sorted multiset of strings based
* on {@link SkipListStringSet}.
*
* <p>This collection allows storing Strings so that <ul>
* <li>adding / removing item is performed in {@code O(1)} if there is no equal
* item in collection, otherwise {@code O(lg N)}
* <li>searching item is performed in {@code O(lg N)}
* <li>search finds earliest (in sorting order) item greater or equal to given
* <li>fetching next item is performed in {@code O(1)}
* <li>every unique item appears only once when iterating
* <li>iterator returns items in the sorted order
* </ul>
*
* <p>Actually this object is composed of skip list (for sorted search result)
* and map (for better performance).
*
* <p>{@link #remove} will fail in situation then item counter should become
* less than zero.
*
* <p>{@link #search} is delegated to {@link SkipListStringSet} instance.
*
* <p>{@code null} items should not be passed to add / remove / search.
*
*/
public final class SkipListStringBag implements StringMultiset {
private JsonStringMap<Counter> itemCounters;
private SkipListStringSet itemsSet;
public SkipListStringBag() {
clear();
}
@Override
public final void addAll(@Nonnull JsonArray<String> items) {
// TODO: Check if iterate is faster.
for (int i = 0, l = items.size(); i < l; i++) {
add(items.get(i));
}
}
@Override
public final void add(@Nonnull String item) {
Counter counter = itemCounters.get(item);
if (counter == null) {
counter = new Counter();
itemCounters.put(item, counter);
itemsSet.add(item);
} else {
counter.increment();
}
}
@Override
public final void removeAll(@Nonnull JsonArray<String> items) {
// TODO: Check if iterate is faster.
for (int i = 0, l = items.size(); i < l; i++) {
remove(items.get(i));
}
}
@Override
public final void remove(@Nonnull String item) {
Counter counter = itemCounters.get(item);
// TODO: Remove this precondition.
Preconditions.checkNotNull(counter, "trying to remove absent item: %s", item);
if (counter.decrement()) {
itemCounters.remove(item);
itemsSet.remove(item);
}
}
@Override
public boolean contains(@Nonnull String item) {
return itemCounters.containsKey(item);
}
public final Iterable<String> search(@Nonnull String item) {
return itemsSet.search(item);
}
@Override
public void clear() {
itemCounters = ClientStringMap.create();
itemsSet = SkipListStringSet.create();
}
}