001    /*
002    // $Id: OlapException.java 125 2008-11-02 07:43:12Z jhyde $
003    // This software is subject to the terms of the Common Public License
004    // Agreement, available at the following URL:
005    // http://www.opensource.org/licenses/cpl.html.
006    // Copyright (C) 2006-2008 Julian Hyde
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    package org.olap4j;
011    
012    import java.sql.SQLException;
013    
014    /**
015     * <p>An exception describing an error accessing an OLAP database.</p>
016     *
017     * <p>Since olap4j extends JDBC, it is natural that <code>OlapException</code>
018     * should extend JDBC's {@link SQLException}. The implementation by an olap4j
019     * driver of a JDBC method which is declared to throw a SQLException may, if the
020     * driver chooses, throw instead an OlapException.</p>
021     *
022     * <p>OlapException provides some additional information to help an OLAP client
023     * identify the location of the error. This information is
024     *
025     * @author jhyde
026     * @version $Id: OlapException.java 125 2008-11-02 07:43:12Z jhyde $
027     * @since Oct 23, 2006
028     */
029    public class OlapException extends SQLException {
030        private Region region;
031        private Object context;
032    
033        /**
034         * Constructs a fully-specified <code>SQLException</code> object.
035         *
036         * @param reason a description of the exception
037         * @param sqlState an XOPEN or SQL 99 code identifying the exception
038         * @param vendorCode a database vendor-specific exception code
039         */
040        public OlapException(String reason, String sqlState, int vendorCode) {
041            super(reason, sqlState, vendorCode);
042        }
043    
044        /**
045         * Constructs an <code>SQLException</code> object with the given reason and
046         * SQLState; the <code>vendorCode</code> field defaults to 0.
047         *
048         * @param reason a description of the exception
049         * @param sqlState an XOPEN or SQL 99 code identifying the exception
050         */
051        public OlapException(String reason, String sqlState) {
052            super(reason, sqlState);
053        }
054    
055        /**
056         * Constructs an <code>SQLException</code> object with a reason;
057         * the <code>sqlState</code> field defaults to <code>null</code>, and
058         * the <code>vendorCode</code> field defaults to 0.
059         *
060         * @param reason a description of the exception
061         */
062        public OlapException(String reason) {
063            super(reason);
064        }
065    
066        /**
067         * Constructs an <code>SQLException</code> object;
068         * the <code>reason</code> field defaults to null,
069         * the <code>sqlState</code> field defaults to <code>null</code>, and
070         * the <code>vendorCode</code> field defaults to 0.
071         */
072        public OlapException() {
073            super();
074        }
075    
076        /**
077         * Constructs an <code>OlapException</code> object with a given
078         * <code>reason</code> and <code>cause</code>.
079         *
080         * @param  reason the detail message (which is saved for later retrieval
081         *         by the {@link #getMessage()} method).
082         * @param  cause the cause (which is saved for later retrieval by the
083         *         {@link #getCause()} method).  (A <tt>null</tt> value is
084         *         permitted, and indicates that the cause is nonexistent or
085         *         unknown.)
086         */
087        public OlapException(String reason, Throwable cause) {
088            // Cannot call super(reason, cause) because
089            // SQLException(String, Throwable) only exists from JDK 1.6.
090            super(reason);
091            initCause(cause);
092        }
093    
094        /**
095         * Sets the textual region where the exception occurred.
096         *
097         * @param region Textual region
098         */
099        public void setRegion(Region region) {
100            this.region = region;
101        }
102    
103        /**
104         * Returns the textual region where the exception occurred, or null if no
105         * region can be identified.
106         *
107         * @return Region where the exception occurred
108         */
109        public Region getRegion() {
110            return region;
111        }
112    
113        /**
114         * Sets the context where the exception occurred.
115         *
116         * @param context Context where the exception occurred
117         * @throws IllegalArgumentException If context is not a {@link Cell}
118         *   or a {@link Position}
119         */
120        public void setContext(Object context) {
121            if (!(context instanceof Cell) &&
122                !(context instanceof Position)) {
123                throw new IllegalArgumentException(
124                    "expected Cell or Position");
125            }
126            this.context = context;
127        }
128    
129        /**
130         * Returns the context where the exception occurred.
131         * Typically a {@link Cell} or a {@link Position}, or null.
132         *
133         * @return context where the exception occurred, or null
134         */
135        public Object getContext() {
136            return context;
137        }
138    
139        /**
140         * Description of the position of a syntax or validation error in the source
141         * MDX string.
142         *
143         * <p>Row and column positions are 1-based and inclusive. For example,
144         * in</p>
145         *
146         * <blockquote>
147         * <pre>
148         * SELECT { [Measures].MEMBERS } ON COLUMNS,
149         *    { } ON ROWS
150         * FROM [Sales]
151         * </pre>
152         * </blockquote>
153         *
154         * <p>the <code>SELECT</code> keyword occupies positions (1, 1) through
155         * (1, 6), and would have a <code>Region(startLine=1, startColumn=1,
156         * endColumn=1, endLine=6)</code>.</p>
157         */
158        public static final class Region {
159            public final int startLine;
160            public final int startColumn;
161            public final int endLine;
162            public final int endColumn;
163    
164            protected Region(
165                int startLine,
166                int startColumn,
167                int endLine,
168                int endColumn)
169            {
170                this.startLine = startLine;
171                this.startColumn = startColumn;
172                this.endColumn = endLine;
173                this.endLine = endColumn;
174            }
175    
176            public String toString() {
177                if (startLine == endColumn && startColumn == endLine) {
178                    return "line " + startLine + ", column " + startColumn;
179                } else {
180                    return "line " + startLine + ", column " + startColumn +
181                        " through line " + endLine + ", column " + endColumn;
182                }
183            }
184        }
185    }
186    
187    // End OlapException.java