/
Location.java
372 lines (323 loc) · 11.3 KB
/
Location.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
package net.sf.eclipsefp.haskell.buildwrapper.types;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import net.sf.eclipsefp.haskell.buildwrapper.BWFacade;
import net.sf.eclipsefp.haskell.buildwrapper.BuildWrapperPlugin;
import net.sf.eclipsefp.haskell.buildwrapper.util.BWText;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DefaultLineTracker;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ILineTracker;
import org.eclipse.jface.text.IRegion;
import org.eclipse.ui.texteditor.MarkerUtilities;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* A span of text within a file, in line:column format.
* Lines are one-based, columns are zero-based.
* The start is inclusive; the end is exclusive.
*
* @author Thomas ten Cate
*/
public class Location {
private String fileName;
private String otherName;
private int startLine, startColumn, endLine, endColumn;
public Location(JSONObject json) throws JSONException {
this(null,json);
}
public Location(IFile f, JSONArray json) throws JSONException {
this(f!=null?f.getLocation().toOSString():"",json);
}
public Location(String fn, JSONArray json) throws JSONException {
startLine=json.getInt(0);
startColumn=json.getInt(1)-1; // we're zero based, Haskell code 1 based
if (json.length()>3){
endLine=json.getInt(2);
endColumn=json.getInt(3)-1;// we're zero based, Haskell code 1 based
} else if (json.length()>2){
endLine=startLine;
endColumn=json.getInt(2)-1;// we're zero based, Haskell code 1 based
} else {
endLine=startLine;
endColumn=startColumn+1;
}
if (endColumn==-1 && endLine>startLine){
endLine--;
}
this.fileName = fn;
}
public Location(IFile f, JSONObject json) throws JSONException {
this.fileName = json.optString("file");
this.otherName = json.optString("other");
if ( f != null
&& (this.fileName == null || this.fileName.length() == 0)
&& (this.otherName == null || this.otherName.length() == 0) ) {
// Default the file name to the Java file resource
this.fileName = f.getLocation().toOSString();
}
if (json.optString("no-location").length() == 0) {
JSONArray region = json.getJSONArray("region");
startLine = region.getInt(0);
startColumn = region.getInt(1);
if (startColumn<0){
startColumn=0;
}
endLine = region.getInt(2);
endColumn = region.getInt(3);
}
}
/**
* clone
* @param l
*/
public Location(Location l) {
this.fileName = l.getFileName();
this.startLine = l.getStartLine();
this.startColumn = l.getStartColumn();
this.endLine = l.getEndLine();
this.endColumn = l.getEndColumn();
}
public Location(String fileName, int startLine, int startColumn, int endLine, int endColumn) {
this.fileName = fileName;
this.startLine = startLine;
this.startColumn = startColumn;
int mv=0;
if (startColumn<0){
mv=0-startColumn;
this.startColumn=0;
}
this.endLine = endLine;
this.endColumn = endColumn+mv;
}
public Location(String fileName, IDocument document, IRegion region) throws BadLocationException {
this.fileName = fileName;
int startOffset = region.getOffset();
int endOffset = startOffset + region.getLength();
int docLine=document.getLineOfOffset(startOffset);
this.startLine =docLine+1 ;
this.startColumn = startOffset - document.getLineOffset(docLine);
docLine=document.getLineOfOffset(endOffset);
this.endLine = docLine+1;
this.endColumn = endOffset - document.getLineOffset(docLine);
}
/**
* Create a location from a buildwrapper location, expanding empty spans when possible.
*/
public Location(IProject project, String fileName, int startLin, int startCol, int endLin, int endCol) {
this.fileName = fileName;
startLine = startLin;
startColumn = startCol - 1; // Buildwrapper columns start at 1
endLine = endLin;
endColumn = endCol - 1; // Buildwrapper columns start at 1
if (endLine > startLine) { // IMarker does not support multi-line spans, so we reduce it to an empty span
endLine = startLine;
endColumn = startColumn;
}
if (startLine==endLine && startColumn==endColumn) { // span is empty
// If the span is empty, we try to extend it to extend it to a single character span.
ILineTracker lineTracker = getLineTracker(project.getLocation().toOSString() +"/"+ BWFacade.DIST_FOLDER+"/"+fileName);
if (lineTracker != null) {
try {
if (startLine>lineTracker.getNumberOfLines()){
startLine=lineTracker.getNumberOfLines();
}
if (endLine>lineTracker.getNumberOfLines()){
endLine=lineTracker.getNumberOfLines();
}
//System.err.println("Initial span: "+startLine+":"+startColumn+" to "+endLine+":"+endColumn);
String delimiter = lineTracker.getLineDelimiter(startLine-1); // apparently this can return null
int lineLength = lineTracker.getLineLength(startLine-1 /*LineTracker is 0 based*/ )
- (delimiter == null ? 0 : delimiter.length()); // subtract the delimiter length
if (startColumn < lineLength) { // not past the last character, so we can extend to the right.
endColumn += 1;
} else {
if (startColumn > 0) { // past last character, but there are characters to the left.
startColumn -= 1;
}
// else, we have startColumn == lineLength == 0, so the line is empty and we cannot extend the span.
}
//System.err.println("Fixed span: "+startLine+":"+startColumn+" to "+endLine+":"+endColumn);
} catch (BadLocationException e){
BuildWrapperPlugin.logError(BWText.process_parse_note_error, e);
}
} else {
//System.err.println("LineTracker is null for file "+fileName);
}
}
}
/**
* Create a LineTracker for the file at filePath, for easy querying line lengths.
* If any exception occurs, we simply return null.
* Note that line numbers are 0 based, and the returned length includes the separator.
*/
private static ILineTracker getLineTracker(String filePath) {
ILineTracker lineTracker;
InputStream input = null;
try {
lineTracker = new DefaultLineTracker();
input = new FileInputStream( filePath );
byte[] contents = new byte[input.available()];
input.read(contents);
String stringContents = new String(contents);
lineTracker.set(stringContents);
}
catch(Exception e) { // CoreException or IOException
lineTracker = null;
}
finally {
if (input != null)
try {
input.close();
}
catch (IOException e) {
lineTracker = null;
}
}
return lineTracker;
}
/**
* Returns the offset within the given document
* that the start of this {@link Location} object represents.
*/
public int getStartOffset(IDocument document) throws BadLocationException {
return document.getLineOffset(startLine-1) + startColumn;
}
/**
* Returns the offset within the given document
* that the end of this {@link Location} object represents.
*/
public int getEndOffset(IDocument document) throws BadLocationException {
return document.getLineOffset(endLine-1) + endColumn;
}
public int getLength(IDocument document) throws BadLocationException {
return getEndOffset(document) - getStartOffset(document);
}
public String getContents(IDocument document) throws BadLocationException {
int st=getStartOffset(document);
return document.get(st,getEndOffset(document)-st);
}
public String getFileName() {
return fileName;
}
public String getOtherName() {
return otherName;
}
public int getStartLine() {
return startLine;
}
public int getStartColumn() {
return startColumn;
}
public int getEndLine() {
return endLine;
}
public int getEndColumn() {
return endColumn;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Location)) {
return false;
}
Location other = (Location)obj;
return
fileName.equals(other.fileName) &&
startLine == other.startLine && startColumn == other.startColumn &&
endLine == other.endLine && endColumn == other.endColumn;
}
@Override
public int hashCode() {
return fileName.hashCode()<<16+startLine<<8+startColumn;
}
@Override
public String toString() {
return String.format("%d:%d-%d:%d", startLine, startColumn, endLine, endColumn);
}
public IFile getIFile(IProject p){
String pl=p.getLocation().toOSString();
String loc=getFileName();
if (loc!=null && loc.startsWith(pl)){
return p.getFile(loc.substring(pl.length()));
} else if (!new File(loc).isAbsolute()) {
return p.getFile(loc);
}
return null;
}
public Map<Object,Object> getMarkerProperties(IDocument d){
int line= Math.min(getStartLine(),d.getNumberOfLines());
final Map<Object,Object> attributes=new HashMap<Object,Object>();
//MarkerUtilities.setLineNumber(attributes, line);
//if (getStartLine()==getEndLine()){
MarkerUtilities.setLineNumber(attributes, line);
//}
try {
int offset=d.getLineOffset(line-1);
int start=getStartColumn();
MarkerUtilities.setCharStart(attributes, offset+start);
offset=d.getLineOffset(Math.min(getEndLine(),d.getNumberOfLines())-1);
int end=offset+getEndColumn();
if (end>=d.getLength()){
end=d.getLength()-1;
}
MarkerUtilities.setCharEnd(attributes, end);
} catch (BadLocationException ble){
// ignore
}
//}
return attributes;
}
public Map<Object,Object> getMarkerProperties(int maxLines){
int line= Math.min(getStartLine(),maxLines);
final Map<Object,Object> attributes=new HashMap<Object,Object>();
MarkerUtilities.setLineNumber(attributes, line);
// if (getStartLine()==getEndLine()){
// int start=getStartColumn();
// int end=getEndColumn();
// // if we have startColumn==endColumn we could take end+1
// // BUT if end goes over the document size, or start is zero, or if Eclipse feels like it, the marker is not shown on the document
// // so it's better to just show the line without more info
// if (end>start){
// MarkerUtilities.setCharStart(attributes, start);
// // exclusive
// MarkerUtilities.setCharEnd(attributes, end-1);
// }
// }
return attributes;
}
public void setStartLine(int startLine) {
this.startLine = startLine;
}
public void setStartColumn(int startColumn) {
this.startColumn = startColumn;
}
public void setEndLine(int endLine) {
this.endLine = endLine;
}
public void setEndColumn(int endColumn) {
this.endColumn = endColumn;
}
public boolean contains(int line,int column){
if( startLine<=line && endLine>=line){
if (startLine==line){
if (startColumn>column){
return false;
}
}
if (endLine==line){
if (endColumn<column){
return false;
}
}
return true;
}
return false;
}
}