001 /*
002 * Copyright (c) 2005 Peter Palotas, Fredrik Johansson, Einar Pehrson,
003 * Sebastian Kekkonen, Lars Magnus Lång, 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 */
023 package csheets.ext.assertion;
024
025 import java.io.IOException;
026 import java.util.ArrayList;
027 import java.util.List;
028
029 import csheets.core.Cell;
030 import csheets.core.Value;
031 import 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 */
039 public 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 }