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 */ 021package csheets.core.formula.lang; 022 023import java.text.ParseException; 024import java.util.SortedSet; 025import java.util.TreeSet; 026import java.util.regex.Matcher; 027import java.util.regex.Pattern; 028 029import csheets.core.Address; 030import csheets.core.Cell; 031import csheets.core.Spreadsheet; 032import csheets.core.Value; 033import csheets.core.formula.Reference; 034import csheets.core.formula.util.ExpressionVisitor; 035import csheets.core.formula.util.ExpressionVisitorException; 036 037/** 038 * A reference to a cell in a spreadsheet. 039 * @author Einar Pehrson 040 */ 041public 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}