001/* 002 * Copyright (c) 2005 Peter Palotas, Fredrik Johansson, Einar Pehrson, 003 * Sebastian Kekkonen, Lars Magnus Lang, Malin Johansson and Sofia Nilsson 004 * 005 * This file is part of 006 * CleanSheets Extension for Assertions 007 * 008 * CleanSheets Extension for Assertions is free software; you can 009 * redistribute it and/or modify it under the terms of the GNU General Public 010 * License as published by the Free Software Foundation; either version 2 of 011 * the License, or (at your option) any later version. 012 * 013 * CleanSheets Extension for Assertions is distributed in the hope that 014 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied 015 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 016 * See the GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with CleanSheets Extension for Assertions; if not, write to the 020 * Free Software Foundation, Inc., 59 Temple Place, Suite 330, 021 * Boston, MA 02111-1307 USA 022 */ 023package csheets.ext.assertion; 024 025import java.io.IOException; 026import java.util.ArrayList; 027import java.util.List; 028 029import csheets.core.Cell; 030import csheets.core.Value; 031import csheets.ext.CellExtension; 032 033/** 034 * An extension of a cell in a spreadsheet, with support for assertions. 035 * @author Peter Palotas 036 * @author Fredrik Johansson 037 * @author Einar Pehrson 038 */ 039public class AssertableCell extends CellExtension { 040 041 /** The unique version identifier used for serialization */ 042 private static final long serialVersionUID = 4956240183977127091L; 043 044 /** The cell's user-specified assertion */ 045 private USAssertion usAssertion; 046 047 /** The cell's system-generated assertion */ 048 private SGAssertion sgAssertion; 049 050 /** The listeners registered to receive events from the assertable cell */ 051 private transient List<AssertableCellListener> listeners 052 = new ArrayList<AssertableCellListener>(); 053 054 /** 055 * A flag that, when set, indicates that the system-generated assertion is 056 * <code>null</code> because of a mathematical error. 057 */ 058 private boolean mathError; 059 060 /** 061 * A short description of a possible existing math-error occuring when 062 * generating the system-generated assertion. 063 */ 064 private String mathErrorMsg; 065 066 /** 067 * Creates a assertable cell extension for the given cell. 068 * @param cell the cell to extend 069 */ 070 AssertableCell(Cell cell) { 071 super(cell, AssertionExtension.NAME); 072 } 073 074 075/* 076 * DATA UPDATES 077 */ 078 079 080 public void contentChanged(Cell cell) { 081 if (getFormula() != null && !getFormula().getReferences().isEmpty()) 082 generateAssertion(); 083 else { 084 sgAssertion = null; 085 for (Cell depCell : getDependents()) 086 ((AssertableCell)depCell).generateAssertion(); 087 } 088 } 089 090 091/* 092 * ASSERTIONS ACCESSORS 093 */ 094 095 096 /** 097 * Get the cell's user supplied assertion. 098 * @return The user supplied assertion for the cell or <code>null</code> if no user 099 supplied assertion exists. 100 */ 101 public USAssertion getUSAssertion() { 102 return usAssertion; 103 } 104 105 /** Get the cell's system generated assertion. 106 @return The system generated assertion for the cell or <code>null</code> if no 107 system generated assertion exist. */ 108 public SGAssertion getSGAssertion() { 109 return sgAssertion; 110 } 111 112 /** Get the most significant assertion of the cell. This is currently the 113 System Generated Assertion if one exists, and the user supplied assertion otherwise. 114 @return the most significant assertion if one exists, or <code>null</code> otherwise. */ 115 public Assertion getPriorityAssertion() { 116 if (isSGAsserted()) 117 return getSGAssertion(); 118 119 if (isUSAsserted()) 120 return getUSAssertion(); 121 122 return null; 123 } 124 125 /** 126 * Returns whether the cell has an assertion. 127 * @return true if the cell has an assertion 128 */ 129 public boolean isAsserted() { 130 return isUSAsserted() || isSGAsserted(); 131 } 132 133 /** Checks if the cell has a user supplied assertion associated with it. 134 @return <code>true</code> if the cell has a user supplied assertion associated with it, <code>false</code> otherwise. */ 135 public boolean isUSAsserted() { 136 return usAssertion != null; 137 } 138 139 /** Checks if the cell has a system generated assertion associated with it. 140 @return <code>true</code> if the cell has a system generated assertion associated with it, <code>false</code> otherwise. */ 141 public boolean isSGAsserted() { 142 return sgAssertion != null; 143 } 144 145 146/* 147 * ASSERTION MODIFIERS 148 */ 149 150 151 /** 152 * Sets the user-specified assertion for the cell. 153 * @param assertion the user-specified assertion 154 */ 155 public void setUSAssertion(USAssertion assertion) { 156 this.usAssertion = assertion; 157 // Notifies listeners 158 fireAssertionsChanged(); 159 160 // Notifies all depending SGAssertions if this cell has no SGAssertion 161 if (sgAssertion == null) 162 for (Cell cell : getDependents()) 163 ((AssertableCell)cell).generateAssertion(); 164 } 165 166 /** 167 * Invoked to indicate that the content of the cell in the spreadsheet was 168 * modified and that assertions that depend on that data must be updated. 169 */ 170 public void generateAssertion() { 171 if (getFormula() != null && !getFormula().hasCircularReference()) { 172 // For debugging purposes only 173 // System.out.println("Update SGA for cell: " + this); 174 175 try { 176 sgAssertion = new SGAssertion(this); 177 mathError = false; 178 mathErrorMsg = null; 179 } catch (AssertionArithmeticException e) { 180 // For debugging purposes only 181 //e.printStackTrace(); 182 183 sgAssertion = null; 184 mathError = false; 185 mathErrorMsg = null; 186 } catch (MathException e) { 187 // Division by zero error 188 mathErrorMsg = e.getMessage(); 189 mathError = true; 190 sgAssertion = null; 191 } 192 193 for (Cell cell : getDependents()) 194 ((AssertableCell)cell).generateAssertion(); 195 196 // Notifies listeners 197 fireAssertionsChanged(); 198 } 199 } 200 201 202/* 203 * ASSERTING 204 */ 205 206 207 /** Asserts the current value of the cell using the user supplied assertion associated with the cell. 208 @return the result of the assertion. If no assertion exists, <code>Assertion.Result.OK</code> is returned. 209 @see USAssertion#validate(Value) */ 210 public Assertion.Result assertUS() { 211 return assertUS(getValue()); 212 } 213 214 /** Asserts the specified value using the user supplied assertion associated with the cell. 215 @return the result of the assertion. If no assertion exists, <code>Assertion.Result.OK</code> is returned. 216 @see USAssertion#validate(Value) */ 217 public Assertion.Result assertUS(Value value) { 218 if (usAssertion != null) 219 return usAssertion.validate(value); 220 221 // If no assertion exist, the value is okay. 222 return Assertion.Result.OK; 223 } 224 225 /** Asserts the current value of the cell using the system generated assertion associated with the cell. 226 @return the result of the assertion. If no assertion exists, <code>Assertion.Result.OK</code> is returned. 227 @see SGAssertion#validate(Value) */ 228 public Assertion.Result assertSG() { 229 return assertSG(getValue()); 230 } 231 232 /** Asserts the specified value using the system generated assertion associated with the cell. 233 @return the result of the assertion. If no assertion exists, <code>Assertion.Result.OK</code> is returned. 234 @see SGAssertion#validate(Value) */ 235 public Assertion.Result assertSG(Value value) { 236 if (sgAssertion != null) 237 return sgAssertion.validate(value); 238 239 // If no assertion exist, the value is okay. 240 return Assertion.Result.OK; 241 } 242 243 /** Asserts the specified value using any (or both) assertion(s) associated with the cell. 244 The system generated assertion (if available) will be run first and upon error its return 245 code will be returned. If the value was successfully asserted using the system generated 246 assertion then the value will be asserted using the user supplied assertion, and its 247 return code will be returned. If no assertion is available in the cell <code>Assertion.Result.OK</code> 248 will be returned. 249 @return the success status of the assertion. 250 @see USAssertion#validate(Value) */ 251 public Assertion.Result assertAny(Value value) { 252 Assertion.Result res = Assertion.Result.OK; 253 254 if (isSGAsserted()) { 255 res = assertSG(value); 256 } 257 258 if (res == Assertion.Result.OK && isUSAsserted()) { 259 res = assertUS(value); 260 } 261 262 return res; 263 } 264 265 /** Asserts the current value of the cell using any (or both) assertion(s) associated with the cell. 266 The system generated assertion (if available) will be run first and upon error its return 267 code will be returned. If the value was successfully asserted using the system generated 268 assertion then the value will be asserted using the user supplied assertion, and its 269 return code will be returned. If no assertion is available in the cell <code>Assertion.Result.OK</code> 270 will be returned. 271 @return the success status of the assertion. 272 @see USAssertion#validate(Value) */ 273 public Assertion.Result assertAny() { 274 return assertAny(getValue()); 275 } 276 277 /** Checks wether the assertions associated with the cell agree with each other and 278 that no division by zero can occur using values within this assertion. 279 @return 280 <ul> 281 <li><code>Assertion.ComparisonResult.OK</code> if both the SGA and USA are specified for the cell and are equal <i>or</i> 282 if none or only one of them is specified, AND the system generated assertion did not fail to 283 generate because it would include a division by zero error. 284 <li><code>Assertion.ComparisonResult.NON_EQUAL</code> if the SGA and USA are both specified and do not agree with each other. 285 <li><code>Assertion.ComparisonResult.DIV_BY_ZERO</code> if the SGA failed to be generated because it would lead to 286 a division by zero error, either because of errenous assertions in precedents or an errenous 287 formula in the cell. 288 </ul> 289 */ 290 public Assertion.ComparisonResult assertAssertions() { 291 292 if (!isSGAsserted() && mathError) { 293 Assertion.ComparisonResult errorResult = Assertion.ComparisonResult.ILLEGAL_INTERVAL; 294 errorResult.setErrorMsg(mathErrorMsg); 295 return errorResult; 296 } 297 298 if (isSGAsserted() && isUSAsserted()) 299 return sgAssertion.equals(usAssertion) ? Assertion.ComparisonResult.OK : Assertion.ComparisonResult.NON_EQUAL; 300 301 return Assertion.ComparisonResult.OK; 302 } 303 304 /** Checks wether there are any errors or inconsitencies in the cell related 305 to assertions. 306 @return <code>false</code> if there are no errors that can be found in the cell using 307 assertion related functions, <code>true</code> otherwise. */ 308 public boolean hasAssertionError() { 309 return !((assertAssertions() == Assertion.ComparisonResult.OK) && (assertAny() == Assertion.Result.OK)); 310 } 311 312 313/* 314 * EVENT LISTENING SUPPORT 315 */ 316 317 318 /** 319 * Registers the given listener on the cell. 320 * @param listener the listener to be added 321 */ 322 public void addAssertableCellListener(AssertableCellListener listener) { 323 listeners.add(listener); 324 } 325 326 /** 327 * Removes the given listener from the cell. 328 * @param listener the listener to be removed 329 */ 330 public void removeAssertableCellListener(AssertableCellListener listener) { 331 listeners.remove(listener); 332 } 333 334 /** 335 * Notifies all registered listeners that the cell's assertions changed. 336 */ 337 protected void fireAssertionsChanged() { 338 for (AssertableCellListener listener : listeners) 339 listener.assertionsChanged(this); 340 } 341 342 /** 343 * Customizes serialization, by recreating the listener list. 344 * @param stream the object input stream from which the object is to be read 345 * @throws IOException If any of the usual Input/Output related exceptions occur 346 * @throws ClassNotFoundException If the class of a serialized object cannot be found. 347 */ 348 private void readObject(java.io.ObjectInputStream stream) 349 throws java.io.IOException, ClassNotFoundException { 350 stream.defaultReadObject(); 351 listeners = new ArrayList<AssertableCellListener>(); 352 } 353}