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*/
020package org.olap4j.mdx;
021
022import org.olap4j.impl.Olap4jUtil;
023import org.olap4j.type.*;
024
025import java.io.PrintWriter;
026import 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 */
040public 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