4ee1dc2e7324b4477e94bea84d62b1dab603de81
[idea/community.git] / platform / core-impl / src / com / intellij / openapi / editor / impl / event / DocumentEventImpl.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.openapi.editor.impl.event;
17
18 import com.intellij.openapi.editor.Document;
19 import com.intellij.openapi.editor.event.DocumentEvent;
20 import com.intellij.util.diff.Diff;
21 import com.intellij.util.diff.FilesTooBigForDiffException;
22 import org.jetbrains.annotations.NotNull;
23
24 public class DocumentEventImpl extends DocumentEvent {
25   private final int myOffset;
26   private final CharSequence myOldString;
27   private final int myOldLength;
28   private final CharSequence myNewString;
29   private final int myNewLength;
30
31   private final long myOldTimeStamp;
32   private final boolean myIsWholeDocReplaced;
33   private Diff.Change myChange;
34   private static final Diff.Change TOO_BIG_FILE = new Diff.Change(0, 0, 0, 0, null) {
35   };
36
37   private final int myInitialStartOffset;
38   private final int myInitialOldLength;
39
40   public DocumentEventImpl(@NotNull Document document,
41                            int offset,
42                            @NotNull CharSequence oldString,
43                            @NotNull CharSequence newString,
44                            long oldTimeStamp,
45                            boolean wholeTextReplaced) {
46     this(document, offset, oldString, newString, oldTimeStamp, wholeTextReplaced, offset, oldString.length());
47   }
48   public DocumentEventImpl(@NotNull Document document,
49                            int offset,
50                            @NotNull CharSequence oldString,
51                            @NotNull CharSequence newString,
52                            long oldTimeStamp,
53                            boolean wholeTextReplaced,
54                            int initialStartOffset,
55                            int initialOldLength) {
56     super(document);
57     myOffset = offset;
58
59     myOldString = oldString;
60     myOldLength = oldString.length();
61
62     myNewString = newString;
63     myNewLength = newString.length();
64
65     myInitialStartOffset = initialStartOffset;
66     myInitialOldLength = initialOldLength;
67
68     myOldTimeStamp = oldTimeStamp;
69
70     myIsWholeDocReplaced = getDocument().getTextLength() != 0 && wholeTextReplaced;
71   }
72
73   @Override
74   public int getOffset() {
75     return myOffset;
76   }
77
78   @Override
79   public int getOldLength() {
80     return myOldLength;
81   }
82
83   @Override
84   public int getNewLength() {
85     return myNewLength;
86   }
87
88   @NotNull
89   @Override
90   public CharSequence getOldFragment() {
91     return myOldString;
92   }
93
94   @NotNull
95   @Override
96   public CharSequence getNewFragment() {
97     return myNewString;
98   }
99
100   @Override
101   @NotNull
102   public Document getDocument() {
103     return (Document)getSource();
104   }
105
106   /**
107    * @return initial start offset as requested in {@link Document#replaceString(int, int, CharSequence)} call, before common prefix and
108    * suffix were removed from the changed range.
109    */
110   public int getInitialStartOffset() {
111     return myInitialStartOffset;
112   }
113
114   /**
115    * @return initial "old fragment" length (endOffset - startOffset) as requested in {@link Document#replaceString(int, int, CharSequence)} call, before common prefix and
116    * suffix were removed from the changed range.
117    */
118   public int getInitialOldLength() {
119     return myInitialOldLength;
120   }
121
122   @Override
123   public long getOldTimeStamp() {
124     return myOldTimeStamp;
125   }
126
127   @SuppressWarnings("HardCodedStringLiteral")
128   public String toString() {
129     return "DocumentEventImpl[myOffset=" + myOffset + ", myOldLength=" + myOldLength + ", myNewLength=" + myNewLength +
130            ", myOldString='" + myOldString + "', myNewString='" + myNewString + "']" + (isWholeTextReplaced() ? " Whole." : ".");
131   }
132
133   @Override
134   public boolean isWholeTextReplaced() {
135     return myIsWholeDocReplaced;
136   }
137
138   public int translateLineViaDiff(int line) throws FilesTooBigForDiffException {
139     Diff.Change change = reBuildDiffIfNeeded();
140     if (change == null) return line;
141
142     int startLine = getDocument().getLineNumber(getOffset());
143     line -= startLine;
144     int newLine = line;
145
146     while (change != null) {
147       if (line < change.line0) break;
148       if (line >= change.line0 + change.deleted) {
149         newLine += change.inserted - change.deleted;
150       }
151       else {
152         int delta = Math.min(change.inserted, line - change.line0);
153         newLine = change.line1 + delta;
154         break;
155       }
156
157       change = change.link;
158     }
159
160     return newLine + startLine;
161   }
162
163   public int translateLineViaDiffStrict(int line) throws FilesTooBigForDiffException {
164     Diff.Change change = reBuildDiffIfNeeded();
165     if (change == null) return line;
166     int startLine = getDocument().getLineNumber(getOffset());
167     if (line < startLine) return line;
168     int translatedRelative = Diff.translateLine(change, line - startLine);
169     return translatedRelative < 0 ? -1 : translatedRelative + startLine;
170   }
171
172   // line numbers in Diff.Change are relative to change start
173   private Diff.Change reBuildDiffIfNeeded() throws FilesTooBigForDiffException {
174     if (myChange == TOO_BIG_FILE) throw new FilesTooBigForDiffException(0);
175     if (myChange == null) {
176       try {
177         myChange = Diff.buildChanges(myOldString, myNewString);
178       }
179       catch (FilesTooBigForDiffException e) {
180         myChange = TOO_BIG_FILE;
181         throw e;
182       }
183     }
184     return myChange;
185   }
186
187
188 }