// 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; /** * Models a text change on the document. * */ public class TextChange { public enum Type { INSERT, DELETE } public static TextChange createDeletion(Line line, int lineNumber, int column, String deletedText) { return new TextChange(Type.DELETE, line, lineNumber, column, line, lineNumber, deletedText); } public static TextChange createInsertion(Line line, int lineNumber, int column, Line lastLine, int lastLineNumber, String text) { return new TextChange(Type.INSERT, line, lineNumber, column, lastLine, lastLineNumber, text); } // This class implements equals and hashCode, make sure to update! private final int column; private final Line lastLine; private final int lastLineNumber; private final Line line; private final int lineNumber; private final String text; private final Type type; public TextChange(Type type, Line line, int lineNumber, int column, Line lastLine, int lastLineNumber, String text) { this.type = type; this.line = line; this.lineNumber = lineNumber; this.column = column; this.lastLine = lastLine; this.lastLineNumber = lastLineNumber; this.text = text; } public int getColumn() { return column; } /** * Returns the last line "touched", meaning the last line that was either * created or mutated. For example, an insertion of "\n" would have * {@link #getLine()} return the line receiving that insertion, and this * method would return the newly inserted line. */ public Line getLastLine() { return lastLine; } /** * Returns the line number corresponding to {@link #getLastLine()}. */ public int getLastLineNumber() { return lastLineNumber; } /** * For insertations, returns the line that received the ending character * of {@link #getText()}; for deletions, returns {@link #getLine()}. */ public Line getEndLine() { if (type == Type.DELETE) { return line; } return text.endsWith("\n") ? lastLine.getPreviousLine() : lastLine; } /** * @return line number corresponding to {@link #getEndLine()}. */ public int getEndLineNumber() { if (type == Type.DELETE) { return lineNumber; } return text.endsWith("\n") ? lastLineNumber - 1 : lastLineNumber; } /** * For insertions, returns the column where the last character of * {@link #getText()} was inserted (on the {@link #getEndLine()}); * for deletions, returns {@link #getColumn()}. */ public int getEndColumn() { if (type == Type.DELETE) { return column; } // Need "- 1" in all returns below since we're returning an inclusive value // If there were no newlines in the inserted text, it's simple if (line == lastLine) { return column + text.length() - 1; } /* * If the last char of text is a newline, then it is endLine's last * character */ if (text.endsWith("\n")) { return getEndLine().getText().length() - 1; } /* * If it is a non-newline character on a multi-line insertion, then find the * length of the insertion text's last line */ int lastLineStartIndexInText = text.lastIndexOf('\n') + 1; return text.length() - lastLineStartIndexInText - 1; } /** * Returns the line that received the text change. */ public Line getLine() { return line; } public int getLineNumber() { return lineNumber; } public String getText() { return text; } public Type getType() { return type; } @Override public String toString() { // Calling type.toString() will prevent it from becoming a simple ordinal switch (type) { case INSERT: return "I(" + column + ", " + text + ")"; case DELETE: return "D(" + column + ", " + text + ")"; default: return "Unknown type (ordinal is " + type.ordinal() + ")"; } } @Override public int hashCode() { int result = 1; result = 31 * result + column; result = 31 * result + lastLine.hashCode(); result = 31 * result + lastLineNumber; result = 31 * result + line.hashCode(); result = 31 * result + lineNumber; result = 31 * result + text.hashCode(); result = 31 * result + type.hashCode(); return result; } @Override public boolean equals(Object otherObj) { if (!(otherObj instanceof TextChange)) { return false; } TextChange o = (TextChange) otherObj; return o.column == column && o.lastLine == lastLine && o.lastLineNumber == lastLineNumber && o.line == line && o.lineNumber == lineNumber && o.text.equals(text) && o.type.equals(type); } }