001 /* 002 // $Id: //open/mondrian/src/main/mondrian/olap/Id.java#30 $ 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/src/main/mondrian/olap/Id.java#30 $ 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: 163 //return name; Disabled to pass old tests... 164 case QUOTED: 165 return "[" + name + "]"; 166 case KEY: 167 return "&[" + name + "]"; 168 default: 169 return "UNKNOWN:" + name; 170 } 171 } 172 173 /** 174 * Appends this segment to a StringBuffer 175 * 176 * @param buf StringBuffer 177 */ 178 public void toString(StringBuilder buf) { 179 switch (quoting) { 180 case UNQUOTED: 181 buf.append(name); 182 return; 183 case QUOTED: 184 Util.quoteMdxIdentifier(name, buf); 185 return; 186 case KEY: 187 buf.append('&'); 188 Util.quoteMdxIdentifier(name, buf); 189 return; 190 default: 191 throw Util.unexpected(quoting); 192 } 193 } 194 195 public boolean equals(final Object o) { 196 if (o instanceof Segment) { 197 Segment that = (Segment) o; 198 return that.name.equals(this.name) && 199 that.quoting == this.quoting; 200 } else { 201 return false; 202 } 203 } 204 205 public int hashCode() { 206 return name.hashCode(); 207 } 208 209 /** 210 * Converts an array of names to a list of segments. 211 * 212 * @param nameParts Array of names 213 * @return List of segments 214 */ 215 public static List<Segment> toList(String... nameParts) { 216 final List<Segment> segments = 217 new ArrayList<Segment>(nameParts.length); 218 for (String namePart : nameParts) { 219 segments.add(new Segment(namePart, Id.Quoting.QUOTED)); 220 } 221 return segments; 222 } 223 224 /** 225 * Returns whether this segment matches a given name according to 226 * the rules of case-sensitivity and quoting. 227 * 228 * @param name Name to match 229 * @return Whether matches 230 */ 231 public boolean matches(String name) { 232 switch (quoting) { 233 case UNQUOTED: 234 return Util.equalName(this.name, name); 235 case QUOTED: 236 return this.name.equals(name); 237 default: 238 return false; 239 } 240 } 241 } 242 243 public enum Quoting { 244 245 /** 246 * Unquoted identifier, for example "Measures". 247 */ 248 UNQUOTED, 249 250 /** 251 * Quoted identifier, for example "[Measures]". 252 */ 253 QUOTED, 254 255 /** 256 * Identifier quoted with an ampersand to indicate a key value, for 257 * example the second segment in "[Employees].&[89]". 258 */ 259 KEY 260 } 261 } 262 263 // End Id.java