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) 2005-2005 Julian Hyde
008// Copyright (C) 2005-2011 Pentaho
009// All Rights Reserved.
010*/
011package mondrian.olap.type;
012
013import mondrian.olap.*;
014import mondrian.resource.MondrianResource;
015
016import java.util.*;
017
018/**
019 * Tuple type.
020 *
021 * @author jhyde
022 * @since Feb 17, 2005
023 */
024public class TupleType implements Type {
025    public final Type[] elementTypes;
026    private final String digest;
027
028    /**
029     * Creates a type representing a tuple whose fields are the given types.
030     *
031     * @param elementTypes Array of types of the members in this tuple
032     */
033    public TupleType(Type[] elementTypes) {
034        assert elementTypes != null;
035        this.elementTypes = elementTypes.clone();
036
037        final StringBuilder buf = new StringBuilder();
038        buf.append("TupleType<");
039        int k = 0;
040        for (Type elementType : elementTypes) {
041            if (k++ > 0) {
042                buf.append(", ");
043            }
044            buf.append(elementType);
045        }
046        buf.append(">");
047        digest = buf.toString();
048    }
049
050    public String toString() {
051        return digest;
052    }
053
054    public boolean equals(Object obj) {
055        if (obj instanceof TupleType) {
056            TupleType that = (TupleType) obj;
057            return Arrays.equals(this.elementTypes, that.elementTypes);
058        } else {
059            return false;
060        }
061    }
062
063    public int hashCode() {
064        return digest.hashCode();
065    }
066
067    public boolean usesDimension(Dimension dimension, boolean definitely) {
068        for (Type elementType : elementTypes) {
069            if (elementType.usesDimension(dimension, definitely)) {
070                return true;
071            }
072        }
073        return false;
074    }
075
076    public boolean usesHierarchy(Hierarchy hierarchy, boolean definitely) {
077        for (Type elementType : elementTypes) {
078            if (elementType.usesHierarchy(hierarchy, definitely)) {
079                return true;
080            }
081        }
082        return false;
083    }
084
085    public List<Hierarchy> getHierarchies() {
086        final List<Hierarchy> hierarchies =
087            new ArrayList<Hierarchy>(elementTypes.length);
088        for (Type elementType : elementTypes) {
089            hierarchies.add(elementType.getHierarchy());
090        }
091        return hierarchies;
092    }
093
094    public int getArity() {
095        return elementTypes.length;
096    }
097
098    public Dimension getDimension() {
099        throw new UnsupportedOperationException();
100    }
101
102    public Hierarchy getHierarchy() {
103        throw new UnsupportedOperationException();
104    }
105
106    public Level getLevel() {
107        throw new UnsupportedOperationException();
108    }
109
110    public Type getValueType() {
111        for (Type elementType : elementTypes) {
112            if (elementType instanceof MemberType) {
113                MemberType memberType = (MemberType) elementType;
114                Dimension dimension = memberType.getDimension();
115                if (dimension != null && dimension.isMeasures()) {
116                    return memberType.getValueType();
117                }
118            }
119        }
120        return new ScalarType();
121    }
122
123    public Type computeCommonType(Type type, int[] conversionCount) {
124        if (type instanceof ScalarType) {
125            return getValueType().computeCommonType(type, conversionCount);
126        }
127        if (type instanceof MemberType) {
128            return commonTupleType(
129                new TupleType(new Type[]{type}),
130                conversionCount);
131        }
132        if (!(type instanceof TupleType)) {
133            return null;
134        }
135        return commonTupleType(type, conversionCount);
136    }
137
138    public boolean isInstance(Object value) {
139        if (!(value instanceof Object[])) {
140            return false;
141        }
142        Object[] objects = (Object[]) value;
143        if (objects.length != elementTypes.length) {
144            return false;
145        }
146        for (int i = 0; i < objects.length; i++) {
147            if (!elementTypes[i].isInstance(objects[i])) {
148                return false;
149            }
150        }
151        return true;
152    }
153
154    private Type commonTupleType(Type type, int[] conversionCount) {
155        TupleType that = (TupleType) type;
156
157        if (this.elementTypes.length < that.elementTypes.length) {
158            return createCommonTupleType(that, conversionCount);
159        }
160        return that.createCommonTupleType(this, conversionCount);
161    }
162
163    private Type createCommonTupleType(TupleType that, int[] conversionCount) {
164        final List<Type> elementTypes = new ArrayList<Type>();
165        for (int i = 0; i < this.elementTypes.length; i++) {
166            Type commonType = this.elementTypes[i].computeCommonType(
167                that.elementTypes[i], conversionCount);
168            elementTypes.add(commonType);
169            if (commonType == null) {
170                return null;
171            }
172        }
173        if (elementTypes.size() < that.elementTypes.length) {
174            for (int i = elementTypes.size();
175                i < that.elementTypes.length; i++)
176            {
177                elementTypes.add(new ScalarType());
178            }
179        }
180        return new TupleType(
181            elementTypes.toArray(new Type[elementTypes.size()]));
182    }
183
184    /**
185     * Checks that there are no duplicate dimensions in a list of member types.
186     * If so, the member types will form a valid tuple type.
187     * If not, throws {@link mondrian.olap.MondrianException}.
188     *
189     * @param memberTypes Array of member types
190     */
191    public static void checkHierarchies(MemberType[] memberTypes) {
192        for (int i = 0; i < memberTypes.length; i++) {
193            MemberType memberType = memberTypes[i];
194            for (int j = 0; j < i; j++) {
195                MemberType member1 = memberTypes[j];
196                final Hierarchy hierarchy = memberType.getHierarchy();
197                final Hierarchy hierarchy1 = member1.getHierarchy();
198                if (hierarchy != null && hierarchy == hierarchy1) {
199                    throw MondrianResource.instance().DupHierarchiesInTuple.ex(
200                        hierarchy.getUniqueName());
201                }
202            }
203        }
204    }
205}
206
207// End TupleType.java