001/* 002 * ModeShape (http://www.modeshape.org) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.modeshape.common.text; 017 018import org.modeshape.common.annotation.Immutable; 019 020/** 021 * A class that represents the position of a particular character in terms of the lines and columns of a character sequence. 022 */ 023@Immutable 024public final class Position { 025 026 /** 027 * The position is used when there is no content. 028 */ 029 public static final Position EMPTY_CONTENT_POSITION = new Position(-1, 1, 0); 030 031 private final int line; 032 private final int column; 033 private final int indexInContent; 034 035 public Position( int indexInContent, 036 int line, 037 int column ) { 038 this.indexInContent = indexInContent < 0 ? -1 : indexInContent; 039 this.line = line; 040 this.column = column; 041 042 assert this.indexInContent >= -1; 043 assert this.line > 0; 044 assert this.column >= 0; 045 // make sure that negative index means an EMPTY_CONTENT_POSITION 046 assert this.indexInContent < 0 ? this.line == 1 && this.column == 0 : true; 047 } 048 049 /** 050 * Get the 0-based index of this position in the content character array. 051 * 052 * @return the index; never negative except for the first position in an empty content. 053 */ 054 public int getIndexInContent() { 055 return indexInContent; 056 } 057 058 /** 059 * Get the 1-based column number of the character. 060 * 061 * @return the column number; always positive 062 */ 063 public int getColumn() { 064 return column; 065 } 066 067 /** 068 * Get the 1-based line number of the character. 069 * 070 * @return the line number; always positive 071 */ 072 public int getLine() { 073 return line; 074 } 075 076 @Override 077 public int hashCode() { 078 return indexInContent; 079 } 080 081 @Override 082 public String toString() { 083 return "" + indexInContent + ':' + line + ':' + column; 084 } 085 086 /** 087 * Return a new position that is the addition of this position and that supplied. 088 * 089 * @param position the position to add to this object; may not be null 090 * @return the combined position 091 */ 092 public Position add( Position position ) { 093 if (this.getIndexInContent() < 0) { 094 return position.getIndexInContent() < 0 ? EMPTY_CONTENT_POSITION : position; 095 } 096 097 if (position.getIndexInContent() < 0) { 098 return this; 099 } 100 101 int index = this.getIndexInContent() + position.getIndexInContent(); 102 int line = position.getLine() + this.getLine() - 1; 103 int column = this.getLine() == 1 ? this.getColumn() + position.getColumn() : this.getColumn(); 104 105 return new Position(index, line, column); 106 } 107}