summaryrefslogtreecommitdiff
path: root/sources/scala/tools/util/Position.java
blob: 25d356c1445ce5a228a9d1d8bf0ff209f7b46682 (plain) (blame)
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
/*     ____ ____  ____ ____  ______                                     *\
**    / __// __ \/ __// __ \/ ____/    SOcos COmpiles Scala             **
**  __\_ \/ /_/ / /__/ /_/ /\_ \       (c) 2002, LAMP/EPFL              **
** /_____/\____/\___/\____/____/                                        **
\*                                                                      */

// $Id$

package scala.tools.util;

/**
 * This class represents a position in a source file. Such a position
 * is defined by a source file (mandatory), a line number (optional)
 * and a column number (optional, may be specified only if the line
 * number is defined).
 *
 * Line (Column) numbers range from 0 to Integer.MAX_VALUE. A value of
 * 0 indicates that the line (column) is undefined, 1 represents the
 * first line (column). Negative values are prohibited.
 *
 * The class provides also functions to encode a line number and a
 * column number into one single integer. The encode line (column)
 * numbers range from 0 to LINE_MASK (COLUMN_MASK), where 0 indicates
 * that the line (column) is the undefined and 1 represents the first
 * line (column). Line (Column) numbers greater than LINE_MASK
 * (COLUMN_MASK) are replaced by LINE_MASK (COLUMN_MASK). Furthermore,
 * if the encoded line number is LINE_MASK, the column number is
 * always set to 0.
 *
 * The following properties hold:
 * - the undefined position is 0: encode(0,0) == 0
 * - encodings are non-negative : encode(line,column) >= 0
 * - position order is preserved:
 *   (line1 < line2) || (line1 == line2 && column1 < column2)
 * implies
 *   encode(line1,column1) <= encode(line2,column2)
 */
public class Position {

    //########################################################################
    // Public Constants

    /** Number of bits used to encode the line number */
    public static final int LINE_BITS   = 20;
    /** Number of bits used to encode the column number */
    public static final int COLUMN_BITS = 31 - LINE_BITS; // no negatives => 31

    /** Mask to decode the line number */
    public static final int LINE_MASK   = (1 << LINE_BITS) - 1;
    /** Mask to decode the column number */
    public static final int COLUMN_MASK = (1 << COLUMN_BITS) - 1;

    /** The undefined position */
    public static final int NOPOS       = 0;

    /** The first position in a source file */
    public static final int FIRSTPOS    = encode(1, 1);

    //########################################################################
    // Public Functions

    /** Encodes a position into a single integer. */
    public static int encode(int line, int column) {
        assert line >= 0 : line;
        assert line == 0 ? column == 0 : column >= 0 : line + "," + column;
        if (line >= LINE_MASK) { line = LINE_MASK; column = 0; }
        if (column > COLUMN_MASK) column = COLUMN_MASK;
        return (line << COLUMN_BITS) | column;
    }

    /** Returns the line number of the encoded position. */
    public static int line(int position) {
        return (position >> COLUMN_BITS) & LINE_MASK;
    }

    /** Returns the column number of the encoded position. */
    public static int column(int position) {
        return position & COLUMN_MASK;
    }

    /** Returns a string representation of the encoded position. */
    public static String toString(int position) {
        return line(position) + ":" + column(position);
    }

    //########################################################################
    // Private Fields

    /** The position's source file */
    private final SourceFile source;

    /** The position's line number */
    private final int line;

    /** The position's column number */
    private final int column;

    //########################################################################
    // Public Constructors

    /** Initializes a new instance. */
    public Position(String sourcename) {
        this(new SourceFile(sourcename, new char[0]));
    }

    /** Initializes a new instance. */
    public Position(SourceFile source) {
        this(source, 0, 0);
    }

    /** Initializes a new instance. */
    public Position(SourceFile source, int position) {
        this(source, line(position), column(position));
    }

    /** Initializes a new instance. */
    public Position(SourceFile source, int line, int column) {
        this.source = source;
        this.line = line;
        this.column = column;
        assert source != null;
        assert line >= 0 : line;
        assert line == 0 ? column == 0 : column >= 0 : line + "," + column;
    }

    //########################################################################
    // Public Methods

    /** Returns the underlying abstract file's name. */
    public String getName() {
        return source.getFile().getName();
    }

    /** Returns the underlying abstract file's path position. */
    public String getPath() {
        return source.getFile().getPath();
    }

    /** Returns the underlying abstract file. */
    public AbstractFile getAbstractFile() {
        return source.getFile();
    }

    /** Returns the source file. */
    public SourceFile getSourceFile() {
        return source;
    }

    /** Returns the line content or null if it's unavailable. */
    public String getLineContent() {
        return line == 0 ? null : source.getLine(line);
    }

    /** Returns the line number. */
    public int getLineNumber() {
        return line;
    }

    /** Returns the column number. */
    public int getColumnNumber() {
        return column;
    }

    /** Returns the line and column numbers encoded in an int. */
    public int getLineAndColumnNumber() {
        return encode(line, column);
    }

    /** Returns a string representation of this position. */
    public String toString() {
        StringBuffer buffer = new StringBuffer(source.getFile().getPath());
        if (line > 0) buffer.append(":").append(line);
        if (line > 0 && column > 0) buffer.append(":").append(column);
        return buffer.toString();
    }

    /** Returns the hash code of this position. */
    public int hashCode() {
        return source.hashCode() ^ getLineAndColumnNumber();
    }

    /** Returns true iff the given object represents the same position. */
    public boolean equals(Object object) {
        if (!(object instanceof Position)) return false;
        Position that = (Position)object;
        return this.source == that.source
            && this.line == that.line
            && this.column == that.column;
    }

    //########################################################################
}