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.StringReader;
026import java.util.Iterator;
027import java.util.List;
028import java.util.ListIterator;
029import java.util.Vector;
030
031/**
032 * This class represents an Assertion.
033 * @author Fredrik Johansson
034 * @author Peter Palotas
035 */
036public class USAssertion extends Assertion {
037
038        /** The unique version identifier used for serialization */
039        private static final long serialVersionUID = -7911803174007268562L;
040
041        /** The original assertion string as entered by the user. */
042        protected String assertion;
043
044        /** A list of warnings or inconsitencies in the current assertion */
045        private List<AssertionWarning> warnings = new Vector<AssertionWarning>();
046
047
048        /**
049         * Constructs an Assertion object
050         * @param assertion is a string representation of an assertion
051     * @throws AssertionException is thrown if a syntactic or semantic error occurs
052     */
053        public USAssertion(String assertion) throws AssertionException {
054
055                List<Interval> orIntervals = new Vector<Interval>();
056                List<Interval> exceptIntervals = new Vector<Interval>();
057
058                AssertionLexer lexer = new AssertionLexer(new StringReader(assertion));
059                AssertionParser parser = new AssertionParser(lexer);
060
061                try {
062                        parser.assertion(this, orIntervals, exceptIntervals);
063                } catch (antlr.MismatchedCharException mce) {
064                        throw new AssertionException(mce);
065                } catch (antlr.MismatchedTokenException mte) {
066                        throw new AssertionException(mte);
067                } catch (antlr.NoViableAltException nvae) {
068                        throw new AssertionException(nvae);
069                } catch (antlr.NoViableAltForCharException nvafce) {
070                        throw new AssertionException(nvafce);
071                } catch (antlr.SemanticException se) {
072                        throw new AssertionException(se);
073                } catch (antlr.RecognitionException re) {
074                        throw new AssertionException(re);
075                } catch (antlr.TokenStreamException tse) {
076                        throw new AssertionException(tse);
077                }
078
079                this.assertion = assertion;
080
081                // To do: Make the inconsistency checks more efficient and add more conditions to check for.
082
083                // Check the consistency of the assertion, and create warnings for
084                // suspected inconsistencies.
085
086                // Check for intersections between or-intervals
087                for (ListIterator<Interval> it1 = orIntervals.listIterator(); it1.hasNext(); ) {
088                        Interval i1 = it1.next();
089                        for (ListIterator<Interval> it2 = orIntervals.listIterator(it1.nextIndex()); it2.hasNext(); ) {
090                                Interval i2 = it2.next();
091                                if (i1.intersects(i2)) {
092                                        // Check if either interval completely encloses the other.
093                                        if (i1.encloses(i2))
094                                                warnings.add(new AssertionWarning(AssertionWarning.Type.ENCLOSING, i2, i1));
095                                        else if (i2.encloses(i1))
096                                                warnings.add(new AssertionWarning(AssertionWarning.Type.ENCLOSING, i1, i2));
097                                        else
098                                                warnings.add(new AssertionWarning(AssertionWarning.Type.INTERSECTING, i1, i2));
099                                }
100                        }
101                }
102
103                // Check for intersections between exclusion intervals
104                for (ListIterator<Interval> it1 = exceptIntervals.listIterator(); it1.hasNext(); ) {
105                        Interval i1 = it1.next();
106                        for (ListIterator<Interval> it2 = exceptIntervals.listIterator(it1.nextIndex()); it2.hasNext(); ) {
107                                Interval i2 = it2.next();
108                                if (i1.intersects(i2)) {
109                                        // Check if either interval completely encloses the other.
110                                        if (i1.encloses(i2))
111                                                warnings.add(new AssertionWarning(AssertionWarning.Type.ENCLOSING, i2, i1));
112                                        else if (i2.encloses(i1))
113                                                warnings.add(new AssertionWarning(AssertionWarning.Type.ENCLOSING, i1, i2));
114                                        else
115                                                warnings.add(new AssertionWarning(AssertionWarning.Type.INTERSECTING, i1, i2));
116                                }
117                        }
118                }
119
120                // Check for complete exclusions of intervals and exclusions of nothing.
121                for (ListIterator<Interval> it1 = exceptIntervals.listIterator(); it1.hasNext(); ) {
122                        Interval i1 = it1.next();
123                        for (ListIterator<Interval> it2 = orIntervals.listIterator(); it2.hasNext(); ) {
124                                Interval i2 = it2.next();
125                                if (i1.encloses(i2))
126                                        warnings.add(new AssertionWarning(AssertionWarning.Type.EXCLUDING, i1, i2));
127                        }
128                }
129
130                // Build the MultiInterval
131                for (Interval io : orIntervals) {
132                        intervals.include(io);
133                }
134
135                for (Interval ie : exceptIntervals) {
136                        intervals.exclude(ie);
137                }
138
139                // Special case if only "integer" was specified as an assertion
140                if (isInteger && orIntervals.isEmpty() && exceptIntervals.isEmpty()) {
141                        intervals.include(new Interval(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, false, false));
142                }
143        }
144
145        /**
146         * Indicates wether the assertion is consistent or wether it may contain
147         * some inconsistencies.  If there are inconsistecies the details can be
148         * retrieved by calling <code>getWarnings()</code>.
149         * @return <code>true</code> if there are no inconsitencies, <code>false</code> otherwise.
150         */
151        public boolean isConsistent() {
152                return warnings.isEmpty();
153        }
154
155        /**
156         * Retrieves the warnings associated with this assertion describing possible
157         * inconsitencies in the assertion.
158         * @return a <code>List</code> of <code>AssertionWarning</code> objects describing any
159         * warnings/inconsistencies in the assertion represented by this object. This <code>List</code>
160         * will be non-empty if <code>isConsistent()</code> returns <code>false</code>, and empty otherwise.
161         */
162        public List<AssertionWarning> getWarnings() {
163                return warnings;
164        }
165
166        /**
167         * @return the String representation of the assertion as specified in the
168         *         constructor.
169         */
170        public String toString() {
171                return assertion;
172        }
173
174
175
176        /** Used for "pretty printing" an assertion. The assertion string
177                is re-constructed from the parsed data.
178                @return a pretty printed version of the assertion.
179        */
180        public String prettyString()
181        {
182                return super.toString();
183        }
184
185        /** Used to print all warnings that were generated while parsing the assertion.
186                Used only for debugging purposes.
187                @deprecated printWarnings
188        */
189        public void printWarnings()
190        {
191                List w = getWarnings();
192                Iterator i = w.iterator();
193                System.out.println("\nWarnings for: " + toString());
194                for ( ; i.hasNext() ;)
195                {
196                        AssertionWarning aw = (AssertionWarning)i.next();
197                        System.out.println(aw.toString());
198                }
199        }
200}