001 /*
002 * Copyright (c) 2007 Mozilla Foundation
003 *
004 * Permission is hereby granted, free of charge, to any person obtaining a
005 * copy of this software and associated documentation files (the "Software"),
006 * to deal in the Software without restriction, including without limitation
007 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
008 * and/or sell copies of the Software, and to permit persons to whom the
009 * Software is furnished to do so, subject to the following conditions:
010 *
011 * The above copyright notice and this permission notice shall be included in
012 * all copies or substantial portions of the Software.
013 *
014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
015 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
016 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
017 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
018 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
019 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
020 * DEALINGS IN THE SOFTWARE.
021 */
022
023 package nu.validator.source;
024
025 /**
026 * Immutable source location with zero-based indexes.
027 * Cannot point to a line break.
028 *
029 * @version $Id$
030 * @author hsivonen
031 */
032 public final class Location implements Comparable<Location>, Cloneable {
033
034 private final SourceCode owner;
035 private final int line;
036 private final int column;
037
038 /**
039 * @param line
040 * @param column
041 */
042 Location(final SourceCode owner, int line, int column) {
043 this.owner = owner;
044 if (line < 0) {
045 line = 0;
046 column = 0;
047 } else if (column < 0) {
048 line--;
049 if (line < 0) {
050 line = 0;
051 column = 0;
052 } else {
053 column = owner.getLine(line).getBufferLength();
054 }
055 }
056 this.line = line;
057 this.column = column;
058 }
059
060 public int compareTo(Location o) {
061 if (this.line < o.line) {
062 return -1;
063 } else if (this.line > o.line) {
064 return 1;
065 } else {
066 if (this.column < o.column) {
067 return -1;
068 } else if (this.column > o.column) {
069 return 1;
070 } else {
071 return 0;
072 }
073 }
074 }
075
076 /**
077 * @see java.lang.Object#equals(java.lang.Object)
078 */
079 @Override
080 public boolean equals(Object obj) {
081 if (obj instanceof Location) {
082 Location loc = (Location) obj;
083 return this.line == loc.line && this.column == loc.column;
084 } else {
085 return false;
086 }
087 }
088
089 /**
090 * Returns the column.
091 *
092 * @return the column
093 */
094 public int getColumn() {
095 return column;
096 }
097
098 /**
099 * Returns the line.
100 *
101 * @return the line
102 */
103 public int getLine() {
104 return line;
105 }
106
107 /**
108 *
109 * @see java.lang.Object#hashCode()
110 */
111 @Override
112 public int hashCode() {
113 return (line << 16) + column;
114 }
115
116 Location next() {
117 return step(1);
118 }
119
120 Location prev() {
121 return step(-1);
122 }
123
124 Location step(int offset) {
125 int newLine = line;
126 int newColumn = column;
127 if (offset > 0) {
128 for (int i = 0; i < offset; i++) {
129 if (newLine == owner.getNumberOfLines()) {
130 break;
131 }
132 newColumn++;
133 Line sourceLine = owner.getLine(newLine);
134 if (newColumn > sourceLine.getBufferLength()) {
135 newLine++;
136 newColumn = 0;
137 }
138 }
139 return new Location(owner, newLine, newColumn);
140 } if (offset < 0) {
141 offset = -offset;
142 for (int i = 0; i < offset; i++) {
143 if (newLine == 0 && newColumn == 0) {
144 break;
145 }
146 newColumn--;
147 if (newColumn == -1) {
148 newLine--;
149 newColumn = owner.getLine(newLine).getBufferLength();
150 }
151 }
152 return new Location(owner, newLine, newColumn);
153 } else {
154 return this;
155 }
156 }
157
158 /**
159 * @see java.lang.Object#toString()
160 */
161 @Override
162 public String toString() {
163 return line + ", " + column;
164 }
165 }