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.StringReader;
026    import java.util.Iterator;
027    import java.util.List;
028    import java.util.ListIterator;
029    import java.util.Vector;
030    
031    /**
032     * This class represents an Assertion.
033     * @author Fredrik Johansson
034     * @author Peter Palotas
035     */
036    public 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    }