001/* 002// This software is subject to the terms of the Eclipse Public License v1.0 003// Agreement, available at the following URL: 004// http://www.eclipse.org/legal/epl-v10.html. 005// You must accept the terms of that agreement to use this software. 006// 007// Copyright (C) 1998-2005 Julian Hyde 008// Copyright (C) 2005-2012 Pentaho and others 009// All Rights Reserved. 010*/ 011package mondrian.olap; 012 013import mondrian.mdx.MdxVisitor; 014import mondrian.olap.type.Type; 015 016import org.olap4j.impl.UnmodifiableArrayList; 017 018import java.io.PrintWriter; 019import java.util.*; 020 021/** 022 * Multi-part identifier. 023 * 024 * @author jhyde, 21 January, 1999 025 */ 026public class Id 027 extends ExpBase 028 implements Cloneable 029{ 030 031 private final List<Segment> segments; 032 033 /** 034 * Creates an identifier containing a single part. 035 * 036 * @param segment Segment, consisting of a name and quoting style 037 */ 038 public Id(Segment segment) { 039 segments = Collections.singletonList(segment); 040 } 041 042 public Id(List<Segment> segments) { 043 this.segments = segments; 044 if (segments.size() <= 0) { 045 throw new IllegalArgumentException(); 046 } 047 } 048 049 public Id clone() { 050 // This is immutable, so no need to clone. 051 return this; 052 } 053 054 public int getCategory() { 055 return Category.Unknown; 056 } 057 058 public Type getType() { 059 // Can't give the type until we have resolved. 060 throw new UnsupportedOperationException(); 061 } 062 063 public String toString() { 064 final StringBuilder buf = new StringBuilder(); 065 Util.quoteMdxIdentifier(segments, buf); 066 return buf.toString(); 067 } 068 069 public String[] toStringArray() { 070 String[] names = new String[segments.size()]; 071 int k = 0; 072 for (Segment segment : segments) { 073 names[k++] = ((NameSegment) segment).getName(); 074 } 075 return names; 076 } 077 078 public List<Segment> getSegments() { 079 return Collections.unmodifiableList(this.segments); 080 } 081 082 public Id.Segment getElement(int i) { 083 return segments.get(i); 084 } 085 086 /** 087 * Returns a new Identifier consisting of this one with another segment 088 * appended. Does not modify this Identifier. 089 * 090 * @param segment Name of segment 091 * @return New identifier 092 */ 093 public Id append(Segment segment) { 094 List<Segment> newSegments = new ArrayList<Segment>(segments); 095 newSegments.add(segment); 096 return new Id(newSegments); 097 } 098 099 public Exp accept(Validator validator) { 100 if (segments.size() == 1) { 101 final Segment s = segments.get(0); 102 if (s.quoting == Quoting.UNQUOTED) { 103 NameSegment nameSegment = (NameSegment) s; 104 if (validator.getFunTable().isReserved(nameSegment.getName())) { 105 return Literal.createSymbol( 106 nameSegment.getName().toUpperCase()); 107 } 108 } 109 } 110 final Exp element = 111 Util.lookup( 112 validator.getQuery(), 113 validator.getSchemaReader().withLocus(), 114 segments, 115 true); 116 117 if (element == null) { 118 return null; 119 } 120 return element.accept(validator); 121 } 122 123 public Object accept(MdxVisitor visitor) { 124 return visitor.visit(this); 125 } 126 127 public void unparse(PrintWriter pw) { 128 pw.print(toString()); 129 } 130 131 /** 132 * Component in a compound identifier. It is described by its name and how 133 * the name is quoted. 134 * 135 * <p>For example, the identifier 136 * <code>[Store].USA.[New Mexico].&[45]</code> has four segments:<ul> 137 * <li>"Store", {@link mondrian.olap.Id.Quoting#QUOTED}</li> 138 * <li>"USA", {@link mondrian.olap.Id.Quoting#UNQUOTED}</li> 139 * <li>"New Mexico", {@link mondrian.olap.Id.Quoting#QUOTED}</li> 140 * <li>"45", {@link mondrian.olap.Id.Quoting#KEY}</li> 141 * </ul> 142 */ 143 public static abstract class Segment { 144 public final Quoting quoting; 145 146 protected Segment(Quoting quoting) { 147 this.quoting = quoting; 148 } 149 150 public String toString() { 151 final StringBuilder buf = new StringBuilder(); 152 toString(buf); 153 return buf.toString(); 154 } 155 156 public Quoting getQuoting() { 157 return quoting; 158 } 159 160 public abstract List<NameSegment> getKeyParts(); 161 162 /** 163 * Converts an array of names to a list of segments. 164 * 165 * @param nameParts Array of names 166 * @return List of segments 167 */ 168 public static List<Segment> toList(String... nameParts) { 169 final List<Segment> segments = 170 new ArrayList<Segment>(nameParts.length); 171 for (String namePart : nameParts) { 172 segments.add(new NameSegment(namePart)); 173 } 174 return segments; 175 } 176 177 /** 178 * Returns whether this segment matches a given name according to 179 * the rules of case-sensitivity and quoting. 180 * 181 * @param name Name to match 182 * @return Whether matches 183 */ 184 public abstract boolean matches(String name); 185 186 /** 187 * Appends this segment to a StringBuilder. 188 * 189 * @param buf String builder to write to 190 */ 191 public abstract void toString(StringBuilder buf); 192 } 193 194 /** 195 * Component in a compound identifier that describes the name of an object. 196 * Optionally, the name is quoted in brackets. 197 * 198 * @see KeySegment 199 */ 200 public static class NameSegment extends Segment { 201 public final String name; 202 203 /** 204 * Creates a name segment with the given quoting. 205 * 206 * @param name Name 207 * @param quoting Quoting style 208 */ 209 public NameSegment(String name, Quoting quoting) { 210 super(quoting); 211 this.name = name; 212 if (name == null) { 213 throw new NullPointerException(); 214 } 215 if (!(quoting == Quoting.QUOTED || quoting == Quoting.UNQUOTED)) { 216 throw new IllegalArgumentException(); 217 } 218 } 219 220 /** 221 * Creates a quoted name segment. 222 * 223 * @param name Name 224 */ 225 public NameSegment(String name) { 226 this(name, Quoting.QUOTED); 227 } 228 229 public boolean equals(final Object o) { 230 if (this == o) { 231 return true; 232 } 233 if (!(o instanceof NameSegment)) { 234 return false; 235 } 236 NameSegment that = (NameSegment) o; 237 return that.name.equals(this.name); 238 } 239 240 public int hashCode() { 241 return name.hashCode(); 242 } 243 244 public String getName() { 245 return name; 246 } 247 248 public List<NameSegment> getKeyParts() { 249 return null; 250 } 251 252 public void toString(StringBuilder buf) { 253 switch (quoting) { 254 case UNQUOTED: 255 buf.append(name); 256 return; 257 case QUOTED: 258 Util.quoteMdxIdentifier(name, buf); 259 return; 260 default: 261 throw Util.unexpected(quoting); 262 } 263 } 264 265 public boolean matches(String name) { 266 switch (quoting) { 267 case UNQUOTED: 268 return Util.equalName(this.name, name); 269 case QUOTED: 270 return Util.equalName(this.name, name); 271 default: 272 return false; 273 } 274 } 275 } 276 277 /** 278 * Identifier segment representing a key, possibly composite. 279 */ 280 public static class KeySegment extends Segment { 281 public final List<NameSegment> subSegmentList; 282 283 /** 284 * Creates a KeySegment with one or more sub-segments. 285 * 286 * @param subSegments Array of sub-segments 287 */ 288 public KeySegment(NameSegment... subSegments) { 289 super(Quoting.KEY); 290 if (subSegments.length < 1) { 291 throw new IllegalArgumentException(); 292 } 293 this.subSegmentList = UnmodifiableArrayList.asCopyOf(subSegments); 294 } 295 296 /** 297 * Creates a KeySegment a list of sub-segments. 298 * 299 * @param subSegmentList List of sub-segments 300 */ 301 public KeySegment(List<NameSegment> subSegmentList) { 302 super(Quoting.KEY); 303 if (subSegmentList.size() < 1) { 304 throw new IllegalArgumentException(); 305 } 306 this.subSegmentList = 307 new UnmodifiableArrayList<NameSegment>( 308 subSegmentList.toArray( 309 new NameSegment[subSegmentList.size()])); 310 } 311 312 public boolean equals(final Object o) { 313 if (this == o) { 314 return true; 315 } 316 if (!(o instanceof KeySegment)) { 317 return false; 318 } 319 KeySegment that = (KeySegment) o; 320 return this.subSegmentList.equals(that.subSegmentList); 321 } 322 323 public int hashCode() { 324 return subSegmentList.hashCode(); 325 } 326 327 public void toString(StringBuilder buf) { 328 for (NameSegment segment : subSegmentList) { 329 buf.append('&'); 330 segment.toString(buf); 331 } 332 } 333 334 public List<NameSegment> getKeyParts() { 335 return subSegmentList; 336 } 337 338 public boolean matches(String name) { 339 return false; 340 } 341 } 342 343 public enum Quoting { 344 345 /** 346 * Unquoted identifier, for example "Measures". 347 */ 348 UNQUOTED, 349 350 /** 351 * Quoted identifier, for example "[Measures]". 352 */ 353 QUOTED, 354 355 /** 356 * Identifier quoted with an ampersand to indicate a key value, for 357 * example the second segment in "[Employees].&[89]". 358 */ 359 KEY 360 } 361} 362 363// End Id.java