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