1 /* 2 * Copyright 2011-2013 smartics, Kronseder & Reiner GmbH 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package de.smartics.util.source; 17 18 import java.io.Serializable; 19 20 /** 21 * Provides information about a source code location. 22 * 23 * @todo Serial: check validity of lines and columns when read from stream. 24 */ 25 public class SourceCodeLocation implements Serializable 26 { 27 // ********************************* Fields ********************************* 28 29 // --- constants ------------------------------------------------------------ 30 31 /** 32 * The class version identifier. 33 * <p> 34 * The value of this constant is {@value}. 35 * </p> 36 */ 37 private static final long serialVersionUID = 1L; 38 39 /** 40 * Defines an unknown location. The fields must not be accessed. 41 */ 42 public static final SourceCodeLocation UNKNOWN_LOCATION = 43 new SourceCodeLocation(Position.INVALID_POSITION, 44 Position.INVALID_POSITION) 45 { 46 private static final long serialVersionUID = 1L; 47 48 @Override 49 public String toString() 50 { 51 return "INVALID"; 52 } 53 }; 54 55 // --- members -------------------------------------------------------------- 56 57 /** 58 * The start position of the source code location. 59 * 60 * @serial 61 */ 62 private final Position start; 63 64 /** 65 * The end position of the source code location. 66 * 67 * @serial 68 */ 69 private final Position end; 70 71 // ****************************** Initializer ******************************* 72 73 // ****************************** Constructors ****************************** 74 75 /** 76 * Default constructor. 77 * 78 * @param start the start position of the source code location. 79 * @param end the end position of the source code location. 80 * @throws IllegalArgumentException if <code>start</code> or <code>end</code> 81 * are <code>null</code> or <code>start</code> position is greater 82 * than the <code>end</code> position. 83 */ 84 public SourceCodeLocation(final Position start, final Position end) 85 throws IllegalArgumentException 86 { 87 checkArguments(start, end); 88 this.start = start; 89 this.end = end; 90 } 91 92 // ****************************** Inner Classes ***************************** 93 94 /** 95 * Defines a position in the source code. 96 * 97 * @todo Serial: check validity of lines and columns when read from stream. 98 */ 99 public static class Position implements Serializable, Comparable<Position> 100 { 101 // ******************************** Fields ******************************** 102 103 // --- constants ---------------------------------------------------------- 104 105 /** 106 * The class version identifier. 107 * <p> 108 * The value of this constant is {@value}. 109 * </p> 110 */ 111 private static final long serialVersionUID = 1L; 112 113 /** 114 * An invalid position. 115 */ 116 private static final Position INVALID_POSITION = new Position() // NOPMD 117 { 118 private static final long serialVersionUID = 1L; 119 120 @Override 121 public String toString() 122 { 123 return "INVALID"; 124 } 125 }; 126 127 // --- members ------------------------------------------------------------ 128 /** 129 * The line number of the position. The lines start by <code>1</code>. 130 * 131 * @serial 132 */ 133 private final long line; 134 135 /** 136 * The column number of the position. The columns start by <code>1</code>. 137 * 138 * @serial 139 */ 140 private final long column; 141 142 // ***************************** Initializer ****************************** 143 144 // ***************************** Constructors ***************************** 145 146 /** 147 * Invalid instance constructor. 148 */ 149 private Position() 150 { 151 this.line = 0; 152 this.column = 0; 153 } 154 155 /** 156 * Default constructor. 157 * 158 * @param line the line number of the position. 159 * @param column the column number of the position. 160 * @throws IllegalArgumentException if <code>line</code> or 161 * <code>column</code> is smaller than <code>1</code>. 162 */ 163 public Position(final long line, final long column) 164 throws IllegalArgumentException 165 { 166 checkArguments(line, column); 167 this.line = line; 168 this.column = column; 169 } 170 171 // ***************************** Inner Classes **************************** 172 173 // ******************************** Methods ******************************* 174 175 // --- init --------------------------------------------------------------- 176 177 private static void checkArguments(final long line, final long column) 178 throws IllegalArgumentException 179 { 180 if (line < 1L) 181 { 182 throw new IllegalArgumentException( 183 "The line number is required to be within [1, MAX_LONG], but is: " 184 + line); 185 } 186 if (column < 1L) 187 { 188 throw new IllegalArgumentException( 189 "The column number is required to be within [1, MAX_LONG], but is: " 190 + column); 191 } 192 } 193 194 // --- get&set ------------------------------------------------------------ 195 196 /** 197 * Returns the line number of the position. The lines start by 198 * <code>1</code>. 199 * 200 * @return the line number of the position. 201 */ 202 public long getLine() 203 { 204 return line; 205 } 206 207 /** 208 * Returns the column number of the position. The columns start by 209 * <code>1</code>. 210 * 211 * @return the column number of the position. 212 */ 213 public long getColumn() 214 { 215 return column; 216 } 217 218 // --- business ----------------------------------------------------------- 219 220 // --- object basics ------------------------------------------------------ 221 222 /** 223 * {@inheritDoc} 224 */ 225 @Override 226 public int compareTo(final Position other) 227 { 228 if (line == other.line) 229 { 230 final long columnCompare = column - other.column; 231 if (columnCompare < 0) 232 { 233 return -1; 234 } 235 else if (columnCompare > 0) 236 { 237 return 1; 238 } 239 else 240 { 241 return 0; 242 } 243 } 244 else 245 { 246 final long lineCompare = line - other.line; 247 if (lineCompare < 0) 248 { 249 return -1; 250 } 251 else 252 { 253 return 1; 254 } 255 } 256 } 257 258 /** 259 * Returns the hash code of the object. 260 * 261 * @return the hash code. 262 */ 263 @Override 264 public int hashCode() 265 { 266 int result = 17; 267 result = 37 * result + (int) (line ^ (line >>> 32)); 268 result = 37 * result + (int) (column ^ (column >>> 32)); 269 return result; 270 } 271 272 /** 273 * Returns <code>true</code> if the given object is semantically equal to 274 * the given object, <code>false</code> otherwise. 275 * 276 * @param object the instance to compare to. 277 * @return <code>true</code> if the given object is semantically equal to 278 * the given object, <code>false</code> otherwise. 279 */ 280 @Override 281 public boolean equals(final Object object) 282 { 283 if (this == object) 284 { 285 return true; 286 } 287 else if (object == null || getClass() != object.getClass()) 288 { 289 return false; 290 } 291 292 final SourceCodeLocation.Position other = 293 (SourceCodeLocation.Position) object; 294 295 return (line == other.line && column == other.column); 296 } 297 298 /** 299 * Delegates call to {@link java.lang.StringBuilder#toString()}. 300 * 301 * @return the result of the call to 302 * {@link java.lang.StringBuilder#toString()}. 303 * @see java.lang.StringBuilder#toString() 304 */ 305 @Override 306 public String toString() 307 { 308 final StringBuilder buffer = new StringBuilder(); 309 buffer.append(line).append(':').append(column); 310 return buffer.toString(); 311 } 312 } 313 314 // ********************************* Methods ******************************** 315 316 // --- init ----------------------------------------------------------------- 317 318 private static void checkArguments(final Position start, final Position end) 319 throws IllegalArgumentException 320 { 321 if (start == null) 322 { 323 throw new IllegalArgumentException( 324 "The start position of the location is required."); 325 } 326 if (end == null) 327 { 328 throw new IllegalArgumentException( 329 "The end position of the location is required."); 330 } 331 if (start.compareTo(end) > 0) 332 { 333 throw new IllegalArgumentException( 334 "The start position '" + start 335 + " must not be after the end position " + end + "'."); 336 } 337 } 338 339 // --- get&set -------------------------------------------------------------- 340 341 /** 342 * Returns the start position of the source code location. 343 * 344 * @return the start position of the source code location. 345 */ 346 public Position getStart() 347 { 348 return start; 349 } 350 351 /** 352 * Returns the end position of the source code location. 353 * 354 * @return the end position of the source code location. 355 */ 356 public Position getEnd() 357 { 358 return end; 359 } 360 361 // --- business ------------------------------------------------------------- 362 363 /** 364 * Checks if the location is known. 365 * 366 * @return <code>true</code> if it is a known location with valid content, 367 * <code>false</code> if it is an unknown location identified by 368 * {@link #UNKNOWN_LOCATION}. 369 */ 370 public boolean isKnown() 371 { 372 return this != UNKNOWN_LOCATION; 373 } 374 375 // --- object basics -------------------------------------------------------- 376 377 /** 378 * Returns the string representation of the object. 379 * 380 * @return the string representation of the object. 381 */ 382 @Override 383 public String toString() 384 { 385 final StringBuilder buffer = new StringBuilder(); 386 387 buffer.append(start).append('-').append(end); 388 389 return buffer.toString(); 390 } 391 }