// 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.shared.document;
import com.google.collide.json.shared.JsonStringMap;
import com.google.collide.shared.TaggableLine;
import com.google.collide.shared.util.JsonCollections;
/*
* TODO: prevent leaks by forcing a clear on the tags when a line
* is detached.
*/
/**
* Model for a line of text (including the newline character) in a document.
*
* Lines by design do not know their line numbers since that would require a
* line insertion/deletion to iterate through all of the following lines to
* update the line numbers, thus making insertion/deletion slower operations
* than they have to be. The {@link LineFinder} can help to efficiently resolve
* a line number given the line, or vice versa.
*
* Lines can have tags attached to them by clients of this class.
*/
public class Line implements TaggableLine {
static Line create(Document document, String text) {
return new Line(document, text);
}
private boolean attached;
private final Document document;
private Line nextLine;
private Line previousLine;
// Not final so we can do O(1) clearTags
private JsonStringMap<Object> tags;
private String text;
private Line(Document document, String text) {
this.document = document;
this.text = text;
tags = JsonCollections.createMap();
}
public Document getDocument() {
return document;
}
public Line getNextLine() {
return nextLine;
}
@Override
public Line getPreviousLine() {
return previousLine;
}
/**
* Gets a tag set on this line with {@link #putTag}. This serves as storage
* for arbitrary objects for this line. For example, a document parser may
* store its snapshot here.
*
* It is the client's responsibility to ensure type safety, this method
* blindly casts as a convenience.
*
* @param key the unique identifier for this tag. In order to prevent
* namespace collisions, prefix this with the caller's fully qualified
* class name
*/
@Override
@SuppressWarnings("unchecked")
public <T> T getTag(String key) {
return (T) tags.get(key);
}
@SuppressWarnings("unchecked")
public <T> T removeTag(String key) {
return (T) tags.remove(key);
}
public String getText() {
return text;
}
public boolean hasColumn(int column) {
return column >= 0 && column < text.length();
}
public boolean isAttached() {
return attached;
}
public int length() {
return text.length();
}
/**
* Puts a tag on this line.
*
* @see Line#getTag(String)
*/
@Override
public <T> void putTag(String key, T value) {
tags.put(key, value);
}
/**
* This is not public API and will eventually be hidden in the public
* interface
*/
public void clearTags() {
tags = JsonCollections.createMap();
}
@Override
public String toString() {
String trimmedText = text.trim();
return (trimmedText.length() > 50 ? trimmedText.substring(0, 50) + "..." : trimmedText);
}
@Override
public boolean isFirstLine() {
return getPreviousLine() == null;
}
@Override
public boolean isLastLine() {
return getNextLine() == null;
}
void setAttached(boolean attached) {
this.attached = attached;
}
void setNextLine(Line nextLine) {
this.nextLine = nextLine;
}
void setPreviousLine(Line previousLine) {
this.previousLine = previousLine;
}
void setText(String text) {
this.text = text;
}
}