comparison graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java @ 16130:6f7d3f3703d3

Truffle/Source: - LineLocation and LineBreakpoint no longer implement Comparable - TextMap now internal to the Source factory
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Mon, 16 Jun 2014 20:52:06 -0700
parents 74e142bd2b12
children 7109baa7b9eb
comparison
equal deleted inserted replaced
16113:a3a9d703c078 16130:6f7d3f3703d3
192 } 192 }
193 193
194 return builder.toString(); 194 return builder.toString();
195 } 195 }
196 196
197 protected Source() { 197 Source() {
198 } 198 }
199 199
200 protected TextMap textMap = null; 200 private TextMap textMap = null;
201 201
202 protected abstract void reset(); 202 protected abstract void reset();
203 203
204 /** 204 /**
205 * Returns the name of this resource holding a guest language program. An example would be the 205 * Returns the name of this resource holding a guest language program. An example would be the
264 } 264 }
265 265
266 /** 266 /**
267 * Given a 0-based character offset, return the 1-based number of the line that includes the 267 * Given a 0-based character offset, return the 1-based number of the line that includes the
268 * position. 268 * position.
269 */ 269 *
270 public final int getLineNumber(int offset) { 270 * @throws IllegalArgumentException if the offset is outside the text contents
271 */
272 public final int getLineNumber(int offset) throws IllegalArgumentException {
271 return checkTextMap().offsetToLine(offset); 273 return checkTextMap().offsetToLine(offset);
272 } 274 }
273 275
274 /** 276 /**
277 * Given a 0-based character offset, return the 1-based number of the column at the position.
278 *
279 * @throws IllegalArgumentException if the offset is outside the text contents
280 */
281 public final int getColumnNumber(int offset) throws IllegalArgumentException {
282 return checkTextMap().offsetToCol(offset);
283 }
284
285 /**
275 * Given a 1-based line number, return the 0-based offset of the first character in the line. 286 * Given a 1-based line number, return the 0-based offset of the first character in the line.
276 */ 287 *
277 public final int getLineStartOffset(int lineNumber) { 288 * @throws IllegalArgumentException if there is no such line in the text
289 */
290 public final int getLineStartOffset(int lineNumber) throws IllegalArgumentException {
278 return checkTextMap().lineStartOffset(lineNumber); 291 return checkTextMap().lineStartOffset(lineNumber);
279 } 292 }
280 293
281 /** 294 /**
282 * The number of characters (not counting a possible terminating newline) in a (1-based) 295 * The number of characters (not counting a possible terminating newline) in a (1-based)
283 * numbered line. 296 * numbered line.
284 */ 297 *
285 public final int getLineLength(int lineNumber) { 298 * @throws IllegalArgumentException if there is no such line in the text
299 */
300 public final int getLineLength(int lineNumber) throws IllegalArgumentException {
286 return checkTextMap().lineLength(lineNumber); 301 return checkTextMap().lineLength(lineNumber);
287 } 302 }
288 303
289 /** 304 /**
290 * Creates a representation of a contiguous region of text in the source. 305 * Creates a representation of a contiguous region of text in the source.
807 return false; 822 return false;
808 } 823 }
809 return source.equals(other.source); 824 return source.equals(other.source);
810 } 825 }
811 826
812 @Override 827 }
813 public int compareTo(Object o) { 828
814 final LineLocationImpl other = (LineLocationImpl) o; 829 /**
815 final int nameOrder = source.getName().compareTo(other.source.getName()); 830 * A utility for converting between coordinate systems in a string of text interspersed with
816 if (nameOrder != 0) { 831 * newline characters. The coordinate systems are:
817 return nameOrder; 832 * <ul>
818 } 833 * <li>0-based character offset from the beginning of the text, where newline characters count
819 return Integer.compare(line, other.line); 834 * as a single character and the first character in the text occupies position 0.</li>
820 } 835 * <li>1-based position in the 2D space of lines and columns, in which the first position in the
821 836 * text is at (1,1).</li>
822 } 837 * </ul>
838 * <p>
839 * This utility is based on positions occupied by characters, not text stream positions as in a
840 * text editor. The distinction shows up in editors where you can put the cursor just past the
841 * last character in a buffer; this is necessary, among other reasons, so that you can put the
842 * edit cursor in a new (empty) buffer. For the purposes of this utility, however, there are no
843 * character positions in an empty text string and there are no lines in an empty text string.
844 * <p>
845 * A newline character designates the end of a line and occupies a column position.
846 * <p>
847 * If the text ends with a character other than a newline, then the characters following the
848 * final newline character count as a line, even though not newline-terminated.
849 * <p>
850 * <strong>Limitations:</strong>
851 * <ul>
852 * <li>Does not handle multiple character encodings correctly.</li>
853 * <li>Treats tabs as occupying 1 column.</li>
854 * <li>Does not handle multiple-character line termination sequences correctly.</li>
855 * </ul>
856 */
857 private static final class TextMap {
858
859 // 0-based offsets of newline characters in the text, with sentinel
860 private final int[] nlOffsets;
861
862 // The number of characters in the text, including newlines (which count as 1).
863 private final int textLength;
864
865 // Is the final text character a newline?
866 final boolean finalNL;
867
868 /**
869 * Constructs map permitting translation between 0-based character offsets and 1-based
870 * lines/columns.
871 */
872 public TextMap(String text) {
873 this.textLength = text.length();
874 final ArrayList<Integer> lines = new ArrayList<>();
875 lines.add(0);
876 int offset = 0;
877
878 while (offset < text.length()) {
879 final int nlIndex = text.indexOf('\n', offset);
880 if (nlIndex >= 0) {
881 offset = nlIndex + 1;
882 lines.add(offset);
883 } else {
884 break;
885 }
886 }
887 lines.add(Integer.MAX_VALUE);
888
889 nlOffsets = new int[lines.size()];
890 for (int line = 0; line < lines.size(); line++) {
891 nlOffsets[line] = lines.get(line);
892 }
893
894 finalNL = textLength > 0 && (textLength == nlOffsets[nlOffsets.length - 2]);
895 }
896
897 /**
898 * Converts 0-based character offset to 1-based number of the line containing the character.
899 *
900 * @throws IllegalArgumentException if the offset is outside the string.
901 */
902 public int offsetToLine(int offset) throws IllegalArgumentException {
903 if (offset < 0 || offset >= textLength) {
904 throw new IllegalArgumentException("offset out of bounds");
905 }
906 int line = 1;
907 while (offset >= nlOffsets[line]) {
908 line++;
909 }
910 return line;
911 }
912
913 /**
914 * Converts 0-based character offset to 1-based number of the column occupied by the
915 * character.
916 * <p>
917 * Tabs are not expanded; they occupy 1 column.
918 *
919 * @throws IllegalArgumentException if the offset is outside the string.
920 */
921 public int offsetToCol(int offset) throws IllegalArgumentException {
922 return 1 + offset - nlOffsets[offsetToLine(offset) - 1];
923 }
924
925 /**
926 * The number of lines in the text; if characters appear after the final newline, then they
927 * also count as a line, even though not newline-terminated.
928 */
929 public int lineCount() {
930 if (textLength == 0) {
931 return 0;
932 }
933 return finalNL ? nlOffsets.length - 2 : nlOffsets.length - 1;
934 }
935
936 /**
937 * Converts 1-based line number to the 0-based offset of the line's first character; this
938 * would be the offset of a newline if the line is empty.
939 *
940 * @throws IllegalArgumentException if there is no such line in the text.
941 */
942 public int lineStartOffset(int line) throws IllegalArgumentException {
943 if (textLength == 0 || lineOutOfRange(line)) {
944 throw new IllegalArgumentException("line out of bounds");
945 }
946 return nlOffsets[line - 1];
947 }
948
949 /**
950 * Gets the number of characters in a line, identified by 1-based line number;
951 * <em>does not</em> include the final newline, if any.
952 *
953 * @throws IllegalArgumentException if there is no such line in the text.
954 */
955 public int lineLength(int line) throws IllegalArgumentException {
956 if (textLength == 0 || lineOutOfRange(line)) {
957 throw new IllegalArgumentException("line out of bounds");
958 }
959 if (line == nlOffsets.length - 1 && !finalNL) {
960 return textLength - nlOffsets[line - 1];
961 }
962 return (nlOffsets[line] - nlOffsets[line - 1]) - 1;
963
964 }
965
966 /**
967 * Is the line number out of range.
968 */
969 private boolean lineOutOfRange(int line) {
970 return line <= 0 || line >= nlOffsets.length || (line == nlOffsets.length - 1 && finalNL);
971 }
972
973 }
974
823 } 975 }