/* * Copyright 2000-2009 JetBrains s.r.o. * * 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.intellij.openapi.editor.impl.event; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.util.diff.Diff; import com.intellij.util.diff.FilesTooBigForDiffException; import org.jetbrains.annotations.NotNull; public class DocumentEventImpl extends DocumentEvent { private final int myOffset; private final CharSequence myOldString; private final int myOldLength; private final CharSequence myNewString; private final int myNewLength; private final long myOldTimeStamp; private final boolean myIsWholeDocReplaced; private Diff.Change myChange; private static final Diff.Change TOO_BIG_FILE = new Diff.Change(0, 0, 0, 0, null); private final int myInitialStartOffset; private final int myInitialOldLength; public DocumentEventImpl(@NotNull Document document, int offset, @NotNull CharSequence oldString, @NotNull CharSequence newString, long oldTimeStamp, boolean wholeTextReplaced) { this(document, offset, oldString, newString, oldTimeStamp, wholeTextReplaced, offset, oldString.length()); } public DocumentEventImpl(@NotNull Document document, int offset, @NotNull CharSequence oldString, @NotNull CharSequence newString, long oldTimeStamp, boolean wholeTextReplaced, int initialStartOffset, int initialOldLength) { super(document); myOffset = offset; myOldString = oldString; myOldLength = oldString.length(); myNewString = newString; myNewLength = newString.length(); myInitialStartOffset = initialStartOffset; myInitialOldLength = initialOldLength; myOldTimeStamp = oldTimeStamp; myIsWholeDocReplaced = getDocument().getTextLength() != 0 && wholeTextReplaced; assert initialStartOffset >= 0 : initialStartOffset; assert initialOldLength >= 0 : initialOldLength; assert initialStartOffset+initialOldLength <= document.getTextLength() : "initialStartOffset = " + initialStartOffset + "; initialOldLength = " + initialOldLength+";document.getTextLength() = " + document.getTextLength(); } @Override public int getOffset() { return myOffset; } @Override public int getOldLength() { return myOldLength; } @Override public int getNewLength() { return myNewLength; } @NotNull @Override public CharSequence getOldFragment() { return myOldString; } @NotNull @Override public CharSequence getNewFragment() { return myNewString; } @Override @NotNull public Document getDocument() { return (Document)getSource(); } /** * @return initial start offset as requested in {@link Document#replaceString(int, int, CharSequence)} call, before common prefix and * suffix were removed from the changed range. */ public int getInitialStartOffset() { return myInitialStartOffset; } /** * @return initial "old fragment" length (endOffset - startOffset) as requested in {@link Document#replaceString(int, int, CharSequence)} call, before common prefix and * suffix were removed from the changed range. */ public int getInitialOldLength() { return myInitialOldLength; } @Override public long getOldTimeStamp() { return myOldTimeStamp; } @SuppressWarnings("HardCodedStringLiteral") public String toString() { return "DocumentEventImpl[myOffset=" + myOffset + ", myOldLength=" + myOldLength + ", myNewLength=" + myNewLength + ", myOldString='" + myOldString + "', myNewString='" + myNewString + "']" + (isWholeTextReplaced() ? " Whole." : "."); } @Override public boolean isWholeTextReplaced() { return myIsWholeDocReplaced; } public int translateLineViaDiff(int line) throws FilesTooBigForDiffException { Diff.Change change = reBuildDiffIfNeeded(); if (change == null) return line; int startLine = getDocument().getLineNumber(getOffset()); line -= startLine; int newLine = line; while (change != null) { if (line < change.line0) break; if (line >= change.line0 + change.deleted) { newLine += change.inserted - change.deleted; } else { int delta = Math.min(change.inserted, line - change.line0); newLine = change.line1 + delta; break; } change = change.link; } return newLine + startLine; } public int translateLineViaDiffStrict(int line) throws FilesTooBigForDiffException { Diff.Change change = reBuildDiffIfNeeded(); if (change == null) return line; int startLine = getDocument().getLineNumber(getOffset()); if (line < startLine) return line; int translatedRelative = Diff.translateLine(change, line - startLine); return translatedRelative < 0 ? -1 : translatedRelative + startLine; } // line numbers in Diff.Change are relative to change start private Diff.Change reBuildDiffIfNeeded() throws FilesTooBigForDiffException { if (myChange == TOO_BIG_FILE) throw new FilesTooBigForDiffException(0); if (myChange == null) { try { myChange = Diff.buildChanges(myOldString, myNewString); } catch (FilesTooBigForDiffException e) { myChange = TOO_BIG_FILE; throw e; } } return myChange; } }