001 /*
002 * Copyright (c) 2005 Einar Pehrson <einar@pehrson.nu>.
003 *
004 * This file is part of
005 * CleanSheets - a spreadsheet application for the Java platform.
006 *
007 * CleanSheets is free software; you can redistribute it and/or modify
008 * it under the terms of the GNU General Public License as published by
009 * the Free Software Foundation; either version 2 of the License, or
010 * (at your option) any later version.
011 *
012 * CleanSheets is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015 * GNU General Public License for more details.
016 *
017 * You should have received a copy of the GNU General Public License
018 * along with CleanSheets; if not, write to the Free Software
019 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020 */
021 package csheets.core.formula.lang;
022
023 import java.text.ParseException;
024 import java.util.SortedSet;
025 import java.util.TreeSet;
026 import java.util.regex.Matcher;
027 import java.util.regex.Pattern;
028
029 import csheets.core.Address;
030 import csheets.core.Cell;
031 import csheets.core.Spreadsheet;
032 import csheets.core.Value;
033 import csheets.core.formula.Reference;
034 import csheets.core.formula.util.ExpressionVisitor;
035 import csheets.core.formula.util.ExpressionVisitorException;
036
037 /**
038 * A reference to a cell in a spreadsheet.
039 * @author Einar Pehrson
040 */
041 public class CellReference implements Reference {
042
043 /** The unique version identifier used for serialization */
044 private static final long serialVersionUID = -6600693551615086696L;
045
046 /**
047 * The regular expression pattern used to match cell references:
048 * (\\$??)([a-zA-Z]+)(\\$??)(\\d+)$")
049 */
050 private static final Pattern PATTERN = Pattern.compile(
051 "(\\$??)([a-zA-Z]+)(\\$??)(\\d+)$");
052
053 /** The string used to match the use of absolute references */
054 private static final String ABSOLUTE_OPERATOR = "$";
055
056 /** The cell to which the reference points */
057 private Cell cell;
058
059 /** If the column is denoted with an absolute reference */
060 private boolean columnAbsolute;
061
062 /** If the row is denoted with an absolute reference */
063 private boolean rowAbsolute;
064
065 /**
066 * Creates a new cell reference to the given address.
067 * By default, relative addressing is used.
068 * @param cell the cell to which the reference points
069 */
070 public CellReference(Cell cell) {
071 this(cell, false, false);
072 }
073
074 /**
075 * Creates a new cell reference to the given address, using the given
076 * reference mode.
077 * @param cell the cell to which the reference points
078 * @param columnAbsolute if the column is denoted with an absolute reference
079 * @param rowAbsolute if the column is denoted with an absolute reference
080 */
081 public CellReference(Cell cell, boolean columnAbsolute, boolean rowAbsolute) {
082 this.cell = cell;
083 this.columnAbsolute = columnAbsolute;
084 this.rowAbsolute = rowAbsolute;
085 }
086
087 /**
088 * Creates a new cell reference from a string matching the (@link #PATTERN).
089 * @param spreadsheet the spreadsheet of the cell
090 * @param reference a string representation of the reference
091 * @throws ParseException if the string did not match the pattern
092 */
093 public CellReference(Spreadsheet spreadsheet, String reference) throws ParseException {
094 // Matches the expression
095 Matcher matcher = PATTERN.matcher(reference);
096 if (matcher.matches()) {
097
098 // Parses row and column indices
099 int row = Integer.parseInt(matcher.group(4)) - 1;
100 int column = -1;
101 String columnStr = matcher.group(2).toUpperCase();
102 for (int i = columnStr.length() - 1; i >= 0; i--)
103 column += (columnStr.charAt(i) - Address.LOWEST_CHAR + 1)
104 * Math.pow(Address.HIGHEST_CHAR - Address.LOWEST_CHAR + 1,
105 columnStr.length() - (i + 1));
106
107 // Stores members
108 this.cell = spreadsheet.getCell(new Address(column, row));
109 this.columnAbsolute = matcher.group(1).equals("$");
110 this.rowAbsolute = matcher.group(3).equals("$");
111 } else
112 throw new ParseException(reference, 0);
113 }
114
115 public Value evaluate() {
116 return cell.getValue();
117 }
118
119 public Object accept(ExpressionVisitor visitor) throws ExpressionVisitorException {
120 return visitor.visitReference(this);
121 }
122
123 /**
124 * Returns the cell to which the reference points.
125 * @return the cell to which the reference points
126 */
127 public Cell getCell() {
128 return cell;
129 }
130
131 public SortedSet<Cell> getCells() {
132 SortedSet<Cell> cells = new TreeSet<Cell>();
133 cells.add(cell);
134 return cells;
135 }
136
137 /**
138 * Returns whether the column is denoted with an absolute reference.
139 * @return true if the column is denoted with an absolute reference.
140 */
141 public boolean isColumnAbsolute() {
142 return columnAbsolute;
143 }
144
145 /**
146 * Returns whether the row is denoted with an absolute reference.
147 * @return true if the row is denoted with an absolute reference.
148 */
149 public boolean isRowAbsolute() {
150 return rowAbsolute;
151 }
152
153 /**
154 * Compares the cell reference with the given cell reference for order.
155 * @param reference the reference to be compared
156 * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
157 */
158 public int compareTo(Reference reference) {
159 Cell otherCell = reference.getCells().first();
160 int firstDiff = cell.compareTo(otherCell);
161 if (firstDiff != 0)
162 return firstDiff;
163 else {
164 if (reference instanceof CellReference) {
165 // Handle reference modes?
166 return -1;
167 } else
168 return -1;
169 }
170 }
171
172 /**
173 * Returns a string representation of the address of the cell reference
174 * on the form "B22", composed of the letter of the column and number of
175 * the row that intersect to form the address.
176 * @return a string representation of the address of the cell reference
177 */
178 public String toString() {
179 // Converts column
180 String columnStr = "";
181 for (int tempColumn = cell.getAddress().getColumn();
182 tempColumn >= 0; tempColumn = tempColumn
183 / (Address.HIGHEST_CHAR - Address.LOWEST_CHAR + 1) - 1)
184 columnStr = ((char)((char)(tempColumn % (Address.HIGHEST_CHAR
185 - Address.LOWEST_CHAR + 1)) + Address.LOWEST_CHAR)) + columnStr;
186 if (columnAbsolute)
187 columnStr = ABSOLUTE_OPERATOR + columnStr;
188
189 // Converts row
190 String rowStr = (rowAbsolute ? ABSOLUTE_OPERATOR : "")
191 + (cell.getAddress().getRow() + 1);
192 return columnStr + rowStr;
193 }
194 }