/**
* Copyright 2010 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.Preconditions;
import java.util.Map;
/**
* Computes the cost of documents.
*
* The cost is a metric that, roughly speaking, counts the number of components
* and how big each comopnent is. We could call it "size" but IndexedDocument
* has a method size() already (it returns the length).
*
* @author ohler@google.com (Christian Ohler)
*/
public class DocumentCostFunction {
private final int objectOverhead;
/**
* The objectOverhead is a parameter that affects how expensive elements and
* annotation boundaries are relative to characters.
*/
public static DocumentCostFunction withObjectOverhead(int objectOverhead) {
return new DocumentCostFunction(objectOverhead);
}
private DocumentCostFunction(int objectOverhead) {
Preconditions.checkArgument(objectOverhead >= 0, "Negative objectOverhead: %s", objectOverhead);
this.objectOverhead = objectOverhead;
}
// In every loop, we add objectOverhead for every item, in addition to the
// cost of the item itself. This makes sure that even empty strings and other
// empty stuff have a nonzero cost, and it is uniform and easy to predict.
public int computeCost(String s) {
return s.codePointCount(0, s.length());
}
public int computeCostNullable(String s) {
return s == null ? 0 : computeCost(s);
}
public int computeCost(Attributes attrs) {
int accu = 0;
for (Map.Entry<String, String> attr : attrs.entrySet()) {
accu += objectOverhead + computeCost(attr.getKey()) + computeCost(attr.getValue());
}
return accu;
}
public int computeCost(AnnotationBoundaryMap map) {
int accu = 0;
for (int i = 0; i < map.changeSize(); i++) {
accu += objectOverhead + computeCost(map.getChangeKey(i))
+ computeCostNullable(map.getOldValue(i))
+ computeCostNullable(map.getNewValue(i));
}
for (int i = 0; i < map.endSize(); i++) {
accu += objectOverhead + computeCost(map.getEndKey(i));
}
return accu;
}
/**
* Computes the cost of the given document.
*/
public int computeCost(DocInitialization doc) {
final int[] accu = { 0 };
doc.apply(new DocInitializationCursor() {
@Override
public void annotationBoundary(AnnotationBoundaryMap map) {
accu[0] += objectOverhead + computeCost(map);
}
@Override
public void characters(String chars) {
accu[0] += objectOverhead + computeCost(chars);
}
@Override
public void elementStart(String type, Attributes attrs) {
accu[0] += objectOverhead + computeCost(type) + computeCost(attrs);
}
@Override
public void elementEnd() {
accu[0] += objectOverhead;
}
});
return accu[0];
}
}