001 /*
002 // $Id: //open/mondrian-release/3.0/src/main/mondrian/olap/Id.java#2 $
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) 1998-2002 Kana Software, Inc.
007 // Copyright (C) 2001-2007 Julian Hyde and others
008 // All Rights Reserved.
009 // You must accept the terms of that agreement to use this software.
010 //
011 // jhyde, 21 January, 1999
012 */
013
014 package mondrian.olap;
015 import mondrian.olap.type.Type;
016 import mondrian.mdx.MdxVisitor;
017
018 import java.io.PrintWriter;
019 import java.util.List;
020 import java.util.ArrayList;
021 import java.util.Collections;
022
023 /**
024 * Multi-part identifier.
025 *
026 * @version $Id: //open/mondrian-release/3.0/src/main/mondrian/olap/Id.java#2 $
027 */
028 public class Id
029 extends ExpBase
030 implements Cloneable {
031
032 private final List<Segment> segments;
033
034 /**
035 * Creates an identifier containing a single part.
036 *
037 * @param segment Segment, consisting of a name and quoting style
038 */
039 public Id(Segment segment) {
040 segments = Collections.singletonList(segment);
041 }
042
043 public Id(List<Segment> segments) {
044 this.segments = segments;
045 }
046
047 public Id clone() {
048 // This is immutable, so no need to clone.
049 return this;
050 }
051
052 public int getCategory() {
053 return Category.Unknown;
054 }
055
056 public Type getType() {
057 // Can't give the type until we have resolved.
058 throw new UnsupportedOperationException();
059 }
060
061 public String toString() {
062 final StringBuilder buf = new StringBuilder();
063 Util.quoteMdxIdentifier(segments, buf);
064 return buf.toString();
065 }
066
067 public String[] toStringArray() {
068 String[] names = new String[segments.size()];
069 int k = 0;
070 for (Segment segment : segments) {
071 names[k++] = segment.name;
072 }
073 return names;
074 }
075
076 public List<Segment> getSegments() {
077 return Collections.unmodifiableList(this.segments);
078 }
079
080 public Id.Segment getElement(int i) {
081 return segments.get(i);
082 }
083
084 /**
085 * Returns a new Identifier consisting of this one with another segment
086 * appended. Does not modify this Identifier.
087 *
088 * @param segment Name of segment
089 * @return New identifier
090 */
091 public Id append(Segment segment) {
092 List<Segment> newSegments = new ArrayList<Segment>(segments);
093 newSegments.add(segment);
094 return new Id(newSegments);
095 }
096
097 public Exp accept(Validator validator) {
098 if (segments.size() == 1) {
099 final Segment s = segments.get(0);
100 if (s.quoting == Quoting.UNQUOTED &&
101 validator.getFunTable().isReserved(s.name)) {
102 return Literal.createSymbol(s.name.toUpperCase());
103 }
104 }
105 final Exp element =
106 Util.lookup(
107 validator.getQuery(), segments, true);
108
109 if (element == null) {
110 return null;
111 }
112 return element.accept(validator);
113 }
114
115 public Object accept(MdxVisitor visitor) {
116 return visitor.visit(this);
117 }
118
119 public void unparse(PrintWriter pw) {
120 int k = 0;
121 for (Segment s : segments) {
122 if (k++ > 0) {
123 pw.print(".");
124 }
125 switch (s.quoting) {
126 case UNQUOTED:
127 pw.print(s.name);
128 break;
129 case KEY:
130 pw.print("&[" + Util.mdxEncodeString(s.name) + "]");
131 break;
132 case QUOTED:
133 pw.print("[" + Util.mdxEncodeString(s.name) + "]");
134 break;
135 }
136 }
137 }
138
139 /**
140 * Component in a compound identifier. It is described by its name and how
141 * the name is quoted.
142 *
143 * <p>For example, the identifier
144 * <code>[Store].USA.[New Mexico].&[45]</code> has four segments:<ul>
145 * <li>"Store", {@link mondrian.olap.Id.Quoting#QUOTED}</li>
146 * <li>"USA", {@link mondrian.olap.Id.Quoting#UNQUOTED}</li>
147 * <li>"New Mexico", {@link mondrian.olap.Id.Quoting#QUOTED}</li>
148 * <li>"45", {@link mondrian.olap.Id.Quoting#KEY}</li>
149 * </ul>
150 */
151 public static class Segment {
152 public final String name;
153 public final Quoting quoting;
154
155 public Segment(String name, Quoting quoting) {
156 this.name = name;
157 this.quoting = quoting;
158 }
159
160 public String toString() {
161 switch (quoting) {
162 case UNQUOTED: //return name; Disabled to pass old tests...
163 case QUOTED: return "[" + name + "]";
164 case KEY: return "&[" + name + "]";
165 default: return "UNKNOWN:" + name;
166 }
167 }
168
169 /**
170 * Appends this segment to a StringBuffer
171 *
172 * @param buf StringBuffer
173 */
174 public void toString(StringBuilder buf) {
175 switch (quoting) {
176 case UNQUOTED:
177 buf.append(name);
178 return;
179 case QUOTED:
180 Util.quoteMdxIdentifier(name, buf);
181 return;
182 case KEY:
183 buf.append('&');
184 Util.quoteMdxIdentifier(name, buf);
185 return;
186 default:
187 throw Util.unexpected(quoting);
188 }
189 }
190
191 public boolean equals(final Object o) {
192 if (o instanceof Segment) {
193 Segment that = (Segment) o;
194 return that.name.equals(this.name) &&
195 that.quoting == this.quoting;
196 } else {
197 return false;
198 }
199 }
200
201 public int hashCode() {
202 return name.hashCode();
203 }
204
205 /**
206 * Converts an array of names to a list of segments.
207 *
208 * @param nameParts Array of names
209 * @return List of segments
210 */
211 public static List<Segment> toList(String... nameParts) {
212 final List<Segment> segments =
213 new ArrayList<Segment>(nameParts.length);
214 for (String namePart : nameParts) {
215 segments.add(new Segment(namePart, Id.Quoting.QUOTED));
216 }
217 return segments;
218 }
219
220 /**
221 * Returns whether this segment matches a given name according to
222 * the rules of case-sensitivity and quoting.
223 *
224 * @param name Name to match
225 * @return Whether matches
226 */
227 public boolean matches(String name) {
228 switch (quoting) {
229 case UNQUOTED:
230 return Util.equalName(this.name, name);
231 case QUOTED:
232 return this.name.equals(name);
233 default:
234 return false;
235 }
236 }
237 }
238
239 public enum Quoting {
240
241 /**
242 * Unquoted identifier, for example "Measures".
243 */
244 UNQUOTED,
245
246 /**
247 * Quoted identifier, for example "[Measures]".
248 */
249 QUOTED,
250
251 /**
252 * Identifier quoted with an ampersand to indicate a key value, for
253 * example the second segment in "[Employees].&[89]".
254 */
255 KEY
256 }
257 }
258
259 // End Id.java