summaryrefslogtreecommitdiff
path: root/sources/ch/epfl/lamp/util/Position.java
blob: 486ef7add65c40e310d044b38891a03f6d3f8eee (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
/*     ____ ____  ____ ____  ______                                     *\
**    / __// __ \/ __// __ \/ ____/    SOcos COmpiles Scala             **
**  __\_ \/ /_/ / /__/ /_/ /\_ \       (c) 2002, LAMP/EPFL              **
** /_____/\____/\___/\____/____/                                        **
\*                                                                      */

// $Id$

package ch.epfl.lamp.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;
    }

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

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

    /** 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 source) {
        this(new SourceFile(source, new byte[0]));
    }

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

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

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

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

    /** Returns the file of this position. */
    public SourceFile file() {
        return file;
    }

    /** Returns the line number of this position. */
    public int line() {
        return line;
    }

    /** Returns the column number of this position. */
    public int column() {
        return column;
    }

    /** Returns an int encoding the line and column of this position. */
    public int encodedLineColumn() {
        return encode(line, column);
    }

    /** Returns a string representation of this position. */
    public String toString() {
        StringBuffer buffer = new StringBuffer(file.name());
        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 file.hashCode() ^ encodedLineColumn();
    }

    /** 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 file == that.file && line == that.line && column == that.column;
    }

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