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 }