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}