001    /*
002    // $Id: LiteralNode.java 482 2012-01-05 23:27:27Z jhyde $
003    //
004    // Licensed to Julian Hyde under one or more contributor license
005    // agreements. See the NOTICE file distributed with this work for
006    // additional information regarding copyright ownership.
007    //
008    // Julian Hyde licenses this file to you under the Apache License,
009    // Version 2.0 (the "License"); you may not use this file except in
010    // compliance with the License. You may obtain a copy of the License at:
011    //
012    // http://www.apache.org/licenses/LICENSE-2.0
013    //
014    // Unless required by applicable law or agreed to in writing, software
015    // distributed under the License is distributed on an "AS IS" BASIS,
016    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017    // See the License for the specific language governing permissions and
018    // limitations under the License.
019    */
020    package org.olap4j.mdx;
021    
022    import org.olap4j.impl.Olap4jUtil;
023    import org.olap4j.type.*;
024    
025    import java.io.PrintWriter;
026    import java.math.BigDecimal;
027    
028    /**
029     * Represents a constant value, such as a string or number, in a parse tree.
030     *
031     * <p>Symbols, such as the <code>ASC</code> keyword in
032     * <code>Order([Store].Members, [Measures].[Unit Sales], ASC)</code>, are
033     * also represented as Literals.
034     *
035     * <p>A LiteralNode is immutable.
036     *
037     * @version $Id: LiteralNode.java 482 2012-01-05 23:27:27Z jhyde $
038     * @author jhyde
039     */
040    public class LiteralNode implements ParseTreeNode {
041    
042        // Data members.
043    
044        private final Object value;
045        private final Type type;
046        private final ParseRegion region;
047    
048        /**
049         * Private constructor.
050         *
051         * <p>Use the creation methods {@link #createString} etc.
052         *
053         * @param region Region of source code
054         * @param type Type of this literal; must not be null
055         * @param value Value of this literal, must be null only if this is the
056         *   null literal
057         */
058        private LiteralNode(
059            ParseRegion region,
060            Type type,
061            Object value)
062        {
063            assert type != null;
064            assert (type instanceof NullType) == (value == null);
065            assert (type instanceof StringType || type instanceof SymbolType)
066                   == (value instanceof String);
067            assert (type instanceof NumericType) == (value instanceof BigDecimal);
068            this.region = region;
069            this.type = type;
070            this.value = value;
071        }
072    
073        /**
074         * Creates a literal with the NULL value.
075         *
076         * @param region Region of source code
077         * @return literal representing the NULL value
078         */
079        public static LiteralNode createNull(ParseRegion region) {
080            return new LiteralNode(region, new NullType(), null);
081        }
082    
083        /**
084         * Creates a string literal.
085         *
086         * @param region Region of source code
087         * @param value String value
088         *
089         * @return literal representing the string value
090         *
091         * @see #createSymbol
092         */
093        public static LiteralNode createString(
094            ParseRegion region,
095            String value)
096        {
097            if (value == null) {
098                throw new IllegalArgumentException("value must not be null");
099            }
100            return new LiteralNode(region, new StringType(), value);
101        }
102    
103        /**
104         * Creates a symbol literal.
105         *
106         * @param region Region of source code
107         * @param value Name of symbol
108         *
109         * @return literal representing the symbol value
110         *
111         * @see #createString
112         */
113        public static LiteralNode createSymbol(
114            ParseRegion region,
115            String value)
116        {
117            if (value == null) {
118                throw new IllegalArgumentException("value must not be null");
119            }
120            return new LiteralNode(region, new SymbolType(), value);
121        }
122    
123        /**
124         * Creates a numeric literal.
125         *
126         * @param region Region of source code
127         * @param value Value of literal; must not be null
128         * @param approximate Whether the literal is approximate
129         *
130         * @return literal representing the integer value
131         */
132        public static LiteralNode createNumeric(
133            ParseRegion region,
134            BigDecimal value,
135            boolean approximate)
136        {
137            if (value == null) {
138                throw new IllegalArgumentException("value must not be null");
139            }
140            Olap4jUtil.discard(approximate); // reserved for future use
141            return new LiteralNode(region, new NumericType(), value);
142        }
143    
144        public <T> T accept(ParseTreeVisitor<T> visitor) {
145            return visitor.visit(this);
146        }
147    
148        public Type getType() {
149            return type;
150        }
151    
152        public ParseRegion getRegion() {
153            return region;
154        }
155    
156        /**
157         * Returns the value of this literal.
158         *
159         * <p>Value is always of type {@link String} (if the literal is a string or
160         * a symbol), of type {@link java.math.BigDecimal} (if the literal is
161         * numeric), or null (if the literal is of null type).
162         *
163         * @return value
164         */
165        public Object getValue() {
166            return value;
167        }
168    
169        public void unparse(ParseTreeWriter writer) {
170            PrintWriter pw = writer.getPrintWriter();
171            if (value == null) {
172                pw.print("NULL");
173            } else if (type instanceof SymbolType) {
174                pw.print(value);
175            } else if (type instanceof NumericType) {
176                pw.print(value);
177            } else if (type instanceof StringType) {
178                pw.print(MdxUtil.quoteForMdx((String) value));
179            } else {
180                throw new AssertionError("unexpected literal type " + type);
181            }
182        }
183    
184        public LiteralNode deepCopy() {
185            // No need to copy: literal nodes are immutable.
186            return this;
187        }
188    
189    }
190    
191    // End LiteralNode.java