// 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.editor.renderer;
import com.google.collide.shared.document.Document;
import com.google.collide.shared.document.Line;
import com.google.collide.shared.document.anchor.Anchor;
import com.google.collide.shared.document.anchor.AnchorManager;
import com.google.collide.shared.document.anchor.AnchorType;
/**
* A line based renderer strategy that uses anchors to define chunk boundaries.
*
* At any given point in the document, the previous anchor of the given type
* contains the information necessary to calculate the style of the current
* chunk.
*/
public class PreviousAnchorRenderer implements LineRenderer {
/**
* Calculate the style to use for the given previous anchor.
*/
public interface AnchorStyleGetter {
public String getStyle(Anchor previousAnchor);
}
/**
* Default style getter which simply uses the anchor's value as the style.
*/
private static class AnchorValueIsStyle implements AnchorStyleGetter {
@Override
public String getStyle(Anchor previousAnchor) {
return previousAnchor.getValue();
}
}
public static final AnchorStyleGetter ANCHOR_VALUE_IS_STYLE = new AnchorValueIsStyle();
/**
* The render anchor is a positional anchor used to search backwards for the
* previous diffchunk.
*/
private static final AnchorType START_SEARCH_ANCHOR_TYPE =
AnchorType.create(PreviousAnchorRenderer.class, "start");
protected final Document document;
private final AnchorType anchorType;
private final AnchorStyleGetter styleGetter;
private int nextChunkLength = 0;
private int linePosition = 0;
private Line line;
private Anchor prevAnchor = null;
public PreviousAnchorRenderer(
Document document, AnchorType anchorType, AnchorStyleGetter styleGetter) {
this.document = document;
this.anchorType = anchorType;
this.styleGetter = styleGetter;
}
@Override
public void renderNextChunk(Target target) {
assert (prevAnchor != null);
assert (nextChunkLength > 0);
String chunkStyle = styleGetter.getStyle(prevAnchor);
target.render(nextChunkLength, chunkStyle);
linePosition += nextChunkLength;
if (linePosition < line.getText().length()) {
calculateNextChunk();
}
}
@Override
public boolean resetToBeginningOfLine(Line line, int lineNumber) {
// Reset the line rendering state
this.line = line;
linePosition = 0;
nextChunkLength = 0;
prevAnchor = null;
calculateNextChunk();
return nextChunkLength > 0;
}
@Override
public boolean shouldLastChunkFillToRight() {
return false;
}
/**
* Using the current rendering state, calculate the length and style for the
* next diff chunk.
*/
private void calculateNextChunk() {
// Get the previous anchor for this line
AnchorManager anchorManager = document.getAnchorManager();
if (prevAnchor == null) {
Anchor startSearchAnchor = document.getAnchorManager().createAnchor(
START_SEARCH_ANCHOR_TYPE, line, AnchorManager.IGNORE_LINE_NUMBER, 1);
prevAnchor = document.getAnchorManager().getPreviousAnchor(startSearchAnchor, anchorType);
document.getAnchorManager().removeAnchor(startSearchAnchor);
if (prevAnchor == null) {
return;
}
} else {
// We have hit an anchor on this line already, get the next one.
prevAnchor = anchorManager.getNextAnchor(prevAnchor, anchorType);
}
Anchor nextAnchor = anchorManager.getNextAnchor(prevAnchor, anchorType);
if (nextAnchor != null && nextAnchor.getLine() == line) {
nextChunkLength = nextAnchor.getColumn() - linePosition;
} else {
nextChunkLength = line.getText().length() - linePosition;
}
assert (nextChunkLength >= 0) : "Got a negative chunk length";
}
}