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) 2002-2005 Julian Hyde 008// Copyright (C) 2005-2009 Pentaho 009// All Rights Reserved. 010*/ 011package mondrian.olap.fun; 012 013import mondrian.olap.*; 014import mondrian.util.Pair; 015 016import java.util.*; 017 018/** 019 * Abstract implementation of {@link FunTable}. 020 * 021 * <p>The derived class must implement 022 * {@link #defineFunctions(mondrian.olap.FunTable.Builder)} to define 023 * each function which will be recognized by this table. This method is called 024 * from the constructor, after which point, no further functions can be added. 025 */ 026public abstract class FunTableImpl implements FunTable { 027 /** 028 * Maps the upper-case name of a function plus its 029 * {@link mondrian.olap.Syntax} to an array of 030 * {@link Resolver} objects for that name. 031 */ 032 private Map<Pair<String, Syntax>, List<Resolver>> mapNameToResolvers; 033 private Set<String> reservedWordSet; 034 private List<String> reservedWordList; 035 private Set<String> propertyWords; 036 private List<FunInfo> funInfoList; 037 038 /** 039 * Creates a FunTableImpl. 040 */ 041 protected FunTableImpl() { 042 } 043 044 /** 045 * Initializes the function table. 046 */ 047 public final void init() { 048 final BuilderImpl builder = new BuilderImpl(); 049 defineFunctions(builder); 050 builder.organizeFunctions(); 051 052 // Copy information out of builder into this. 053 this.funInfoList = Collections.unmodifiableList(builder.funInfoList); 054 this.mapNameToResolvers = 055 Collections.unmodifiableMap(builder.mapNameToResolvers); 056 this.reservedWordSet = builder.reservedWords; 057 final String[] reservedWords = 058 builder.reservedWords.toArray( 059 new String[builder.reservedWords.size()]); 060 Arrays.sort(reservedWords); 061 this.reservedWordList = 062 Collections.unmodifiableList(Arrays.asList(reservedWords)); 063 this.propertyWords = Collections.unmodifiableSet(builder.propertyWords); 064 } 065 066 /** 067 * Creates a key to look up an operator in the resolver map. The key 068 * consists of the uppercase function name and the syntax. 069 * 070 * @param name Function/operator name 071 * @param syntax Syntax 072 * @return Key 073 */ 074 private static Pair<String, Syntax> makeResolverKey( 075 String name, 076 Syntax syntax) 077 { 078 return new Pair<String, Syntax>(name.toUpperCase(), syntax); 079 } 080 081 public List<String> getReservedWords() { 082 return reservedWordList; 083 } 084 085 public boolean isReserved(String s) { 086 return reservedWordSet.contains(s.toUpperCase()); 087 } 088 089 public List<Resolver> getResolvers() { 090 final List<Resolver> list = new ArrayList<Resolver>(); 091 for (List<Resolver> resolvers : mapNameToResolvers.values()) { 092 list.addAll(resolvers); 093 } 094 return list; 095 } 096 097 public boolean isProperty(String s) { 098 return propertyWords.contains(s.toUpperCase()); 099 } 100 101 public List<FunInfo> getFunInfoList() { 102 return funInfoList; 103 } 104 105 public List<Resolver> getResolvers(String name, Syntax syntax) { 106 Pair<String, Syntax> key = makeResolverKey(name, syntax); 107 List<Resolver> resolvers = mapNameToResolvers.get(key); 108 if (resolvers == null) { 109 resolvers = Collections.emptyList(); 110 } 111 return resolvers; 112 } 113 114 /** 115 * Implementation of {@link mondrian.olap.FunTable.Builder}. 116 * Functions are added to lists each time {@link #define(Resolver)} is 117 * called, then {@link #organizeFunctions()} sorts and indexes the map. 118 */ 119 private class BuilderImpl implements Builder { 120 private final List<Resolver> resolverList = new ArrayList<Resolver>(); 121 private final List<FunInfo> funInfoList = new ArrayList<FunInfo>(); 122 private final Map<Pair<String, Syntax>, List<Resolver>> 123 mapNameToResolvers = 124 new HashMap<Pair<String, Syntax>, List<Resolver>>(); 125 private final Set<String> reservedWords = new HashSet<String>(); 126 private final Set<String> propertyWords = new HashSet<String>(); 127 128 public void define(FunDef funDef) { 129 define(new SimpleResolver(funDef)); 130 } 131 132 public void define(Resolver resolver) { 133 funInfoList.add(FunInfo.make(resolver)); 134 if (resolver.getSyntax() == Syntax.Property) { 135 propertyWords.add(resolver.getName().toUpperCase()); 136 } 137 resolverList.add(resolver); 138 final String[] reservedWords = resolver.getReservedWords(); 139 for (String reservedWord : reservedWords) { 140 defineReserved(reservedWord); 141 } 142 } 143 144 public void define(FunInfo funInfo) { 145 funInfoList.add(funInfo); 146 } 147 148 public void defineReserved(String s) { 149 reservedWords.add(s.toUpperCase()); 150 } 151 152 /** 153 * Indexes the collection of functions. 154 */ 155 protected void organizeFunctions() { 156 Collections.sort(funInfoList); 157 158 // Map upper-case function names to resolvers. 159 final List<List<Resolver>> nonSingletonResolverLists = 160 new ArrayList<List<Resolver>>(); 161 for (Resolver resolver : resolverList) { 162 Pair<String, Syntax> key = 163 makeResolverKey( 164 resolver.getName(), 165 resolver.getSyntax()); 166 List<Resolver> list = mapNameToResolvers.get(key); 167 if (list == null) { 168 list = new ArrayList<Resolver>(); 169 mapNameToResolvers.put(key, list); 170 } 171 list.add(resolver); 172 if (list.size() == 2) { 173 nonSingletonResolverLists.add(list); 174 } 175 } 176 177 // Sort lists by signature (skipping singleton lists) 178 final Comparator<Resolver> comparator = 179 new Comparator<Resolver>() { 180 public int compare(Resolver o1, Resolver o2) { 181 return o1.getSignature().compareTo(o2.getSignature()); 182 } 183 }; 184 for (List<Resolver> resolverList : nonSingletonResolverLists) { 185 Collections.sort(resolverList, comparator); 186 } 187 } 188 } 189} 190 191// End FunTableImpl.java