/* // $Id: //guest/julian_hyde/mondrian/src/main/mondrian/olap/Util.java#1 $ // This software is subject to the terms of the Common Public License // Agreement, available at the following URL: // http://www.opensource.org/licenses/cpl.html. // (C) Copyright 2001-2002 Kana Software, Inc. and others. // All Rights Reserved. // You must accept the terms of that agreement to use this software. // // jhyde, 6 August, 2001 */ package mondrian.olap; import mondrian.resource.ChainableThrowable; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.math.BigDecimal; import java.util.Arrays; import java.util.Comparator; import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; import java.util.StringTokenizer; import java.util.Vector; /** * todo: * * @author jhyde * @since 6 August, 2001 * @version $Id: //guest/julian_hyde/mondrian/src/main/mondrian/olap/Util.java#1 $ **/ public class Util extends mondrian.xom.XOMUtil { public static final Object nullValue = new NullCellValue(); private static Hashtable threadRes = new Hashtable(); /** encodes string for MDX (escapes ] as ]] inside a name) */ public static String mdxEncodeString(String st) { String retString = new String(); for (int i = 0; i < st.length(); i++) { if (st.charAt(i) == ']' && (i+1) < st.length() && st.charAt(i+1) != '.') retString += "]"; //escaping character retString += st.charAt(i); } return retString; } /** Return quoted */ public static String quoteForMdx(String val) { String s0 = replace(val, "\"", "\"\""); return "\"" + s0 + "\""; } /** * Return string quoted in [...]. For example, "San Francisco" becomes * "[San Francisco]". todo: "a [bracketed] string" should become "[a * [bracketed]] string]", but does not at present. */ public static String quoteMdxIdentifier(String id) { return "[" + id + "]"; } /** * Return identifiers quoted in [...].[...]. For example, {"Store", "USA", * "California"} becomes "[Store].[USA].[California]". **/ public static String quoteMdxIdentifier(String[] ids) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < ids.length; i++) { if (i > 0) { sb.append("."); } sb.append(quoteMdxIdentifier(ids[i])); } return sb.toString(); } /** Does not modify the original string */ public static String replace(String s,String find,String replace) { // let's be optimistic int found = s.indexOf(find); if (found == -1) { return s; } StringBuffer sb = new StringBuffer(s.length()); int start = 0; for (;;) { for (; start < found; start++) { sb.append(s.charAt(start)); } if (found == s.length()) { break; } sb.append(replace); start += find.length(); found = s.indexOf(find,start); if (found == -1) { found = s.length(); } } return sb.toString(); } public static String[] explode(String s) { Vector vector = new Vector(); int i = 0; while (i < s.length()) { if (s.charAt(i) != '[') { throw getRes().newMdxInvalidMember(s); } // s may contain extra ']' characters, so look for a ']' followed // by a '.' (still not perfect... really ought to scan, ignoring // escaped ']]' sequences) int j = s.indexOf("].", i); if (j == -1) { j = s.lastIndexOf("]"); } if (j <= i) { throw getRes().newMdxInvalidMember(s); } String sub = s.substring(i + 1, j); vector.addElement(sub); if (j + 1 < s.length()) if (s.charAt(j+1) != '.') { throw getRes().newMdxInvalidMember(s); } i = j + 2; } String[] names = new String[vector.size()]; vector.copyInto(names); return names; } public static String implode(String[] names) { if (names.length == 0) { return ""; } StringBuffer sb = new StringBuffer("["); for (int i = 0; i < names.length; i++) { if (i > 0) { sb.append("].["); } sb.append(names[i]); } sb.append("]"); return sb.toString(); } public static String makeFqName(String name) { return quoteMdxIdentifier(name); } public static String makeFqName(OlapElement parent, String name) { if (parent == null) { return Util.quoteMdxIdentifier(name); } else { return parent.getUniqueName() + "." + quoteMdxIdentifier(name); } } public static String makeFqName(String parentUniqueName, String name) { if (parentUniqueName == null) { return quoteMdxIdentifier(name); } else { return parentUniqueName + "." + quoteMdxIdentifier(name); } } /** * Resolves a name such as '[Products].[Product Department].[Produce]' by * parsing out the components ('Products', etc.) and resolving them one at * a time. */ public static OlapElement lookupCompound( NameResolver st, String s, OlapElement mdxElement) { return lookupCompound(st, explode(s), mdxElement); } public static OlapElement lookupCompound( NameResolver st, String[] names, OlapElement mdxElement) { for (int i = 0; mdxElement != null && i < names.length; i++) { String sub = names[i]; mdxElement = st.lookupChild(mdxElement, sub, false); } return mdxElement; } /** * As {@link #lookupCompound(NameResolver,String,OlapElement)}, but with * the option to fail instead of returning null. */ public static OlapElement lookupCompound( NameResolver st, String s, OlapElement mdxElement, boolean failIfNotFound) { OlapElement e = lookupCompound(st, s, mdxElement); if (e == null && failIfNotFound) { throw getRes().newMdxChildObjectNotFound( s, mdxElement.getQualifiedName()); } return e; } public static Member lookupMemberCompound( NameResolver st, String[] names, boolean failIfNotFound) { OlapElement mdxElem = lookupCompound(st, names, st.getCube()); if (mdxElem instanceof Member) { return (Member) mdxElem; } else if (failIfNotFound) { String s = implode(names); throw getRes().newMdxCantFindMember(s); } return null; } public static Member lookupMember( NameResolver st, String s, boolean failIfNotFound) { Member member = st.lookupMemberFromCache(s); if (member != null) { return member; } OlapElement mdxElem = lookupCompound( st, s, st.getCube(), failIfNotFound); if (mdxElem instanceof Member) { return (Member) mdxElem; } else if (failIfNotFound) { throw getRes().newMdxCantFindMember(s); } return null; } static Vector toVector(Object[] array) { Vector vector = new Vector(); return addArray(vector, array); } static Vector addArray(Vector vector, Object[] array) { for (int i = 0; i < array.length; i++) { vector.addElement(array[i]); } return vector; } static Hashtable toHashtable(Vector vector) { Hashtable hashtable = new Hashtable(); for (int i = 0, count = vector.size(); i < count; i++) { Object o = vector.elementAt(i); hashtable.put(o, o); } return hashtable; } static Vector addMembers(Vector vector, Hierarchy hierarchy) { Level[] levels = hierarchy.getLevels(); for (int i = 0; i < levels.length; i++) { addMembers(vector, levels[i]); } return vector; } static Vector addMembers(Vector vector, Level level) { Member[] members = level.getMembers(); return addArray(vector, members); } static Object getArg(Evaluator evaluator, Exp[] args, int index) { return getArg(evaluator, args, index, null); } static Object getArg( Evaluator evaluator, Exp[] args, int index, Object defaultValue) { if (index >= args.length) { return defaultValue; } Exp exp = args[index]; return exp.evaluate(evaluator); } static String getStringArg( Evaluator evaluator, Exp[] args, int index, String defaultValue) { return (String) getArg(evaluator, args, index, defaultValue); } static int getIntArg(Evaluator evaluator, Exp[] args, int index) { Object o = args[index].evaluateScalar(evaluator); if (o instanceof Integer) { return ((Integer) o).intValue(); } else { // we need to handle String("5.0") String s = o.toString(); double d = Double.valueOf(s).doubleValue(); return (int) d; } } static BigDecimal getDecimalArg(Evaluator evaluator, Exp[] args, int index) { Object o = args[index].evaluateScalar(evaluator); if (o instanceof Double) { return new BigDecimal(((Double) o).doubleValue()); } else { return (BigDecimal) o; } } static Double getDoubleArg(Evaluator evaluator, Exp[] args, int index) { Object o = args[index].evaluateScalar(evaluator); if (o instanceof BigDecimal) { return new Double(((BigDecimal) o).doubleValue()); } else if (o instanceof Util.ErrorCellValue) { return new Double(Double.NaN); } else if (o instanceof Util.NullCellValue) { return new Double(0); } else { return (Double) o; } } static Member getMemberArg( Evaluator evaluator, Exp[] args, int index, boolean fail) { if (index >= args.length) { if (fail) { throw getRes().newInternal("missing member argument"); } else { return null; } } Exp arg = args[index]; Object o = arg.evaluate(evaluator); if (o instanceof Member) { return (Member) o; } else if (o instanceof Hierarchy) { return evaluator.getContext( ((Hierarchy) o).getDimension()); } else if (o instanceof Dimension) { return evaluator.getContext((Dimension) o); } else { throw getRes().newInternal("expecting a level, got " + o); } } static Level getLevelArg(Exp[] args, int index, boolean fail) { if (index >= args.length) { if (fail) { throw getRes().newInternal("missing level argument"); } else { return null; } } Util.assert(index < args.length); return (Level) args[index]; } static Hierarchy getHierarchyArg( Evaluator evaluator, Exp[] args, int index, boolean fail) { if (index >= args.length) { if (fail) { throw getRes().newInternal("missing hierarchy argument"); } else { return null; } } Exp arg = args[index]; Object o = arg.evaluate(evaluator); if (o instanceof Member) { return ((Member) o).getHierarchy(); } else if (o instanceof Level) { return ((Level) o).getHierarchy(); } else if (o instanceof Hierarchy) { return (Hierarchy) o; } else if (o instanceof Dimension) { return ((Dimension) o).getHierarchies()[0]; } else { throw getRes().newInternal("expecting a hierarchy, got " + o); } } static HashSet toHashSet(Vector v) { HashSet set = new HashSet(); Enumeration e = v.elements(); while (e.hasMoreElements()) { set.add(e.nextElement()); } return set; } /** * Returns whether <code>m0</code> is an ancestor of <code>m1</code>. * * @param strict if true, a member is not an ancestor of itself **/ static boolean isAncestorOf(Member m0, Member m1, boolean strict) { if (strict) { if (m1 == null) { return false; } m1 = m1.getParentMember(); } while (m1 != null) { if (m1 == m0) { return true; } m1 = m1.getParentMember(); } return false; } static Hashtable evaluateMembers( Evaluator evaluator, ExpBase exp, Vector members) { Member[] constantTuple = exp.isConstantTuple(); if (constantTuple == null) { return _evaluateMembers(evaluator.push(new Member[0]), exp, members); } else { // exp is constant -- add it to the context before the loop, rather // than at every step return evaluateMembers(evaluator.push(constantTuple), members); } } private static Hashtable _evaluateMembers( Evaluator evaluator, ExpBase exp, Vector members) { Hashtable mapMemberToValue = new Hashtable(); for (int i = 0, count = members.size(); i < count; i++) { Member member = (Member) members.elementAt(i); evaluator.setContext(member); Object o = exp.evaluate(evaluator); Object result; if (o instanceof Member) { evaluator.setContext((Member) o); result = evaluator.evaluateCurrent(); } else if (o instanceof Member[]) { evaluator.setContext((Member[]) o); result = evaluator.evaluateCurrent(); } else { result = o; } mapMemberToValue.put(member, result); } return mapMemberToValue; } static Hashtable evaluateMembers(Evaluator evaluator, Vector members) { Hashtable mapMemberToValue = new Hashtable(); for (int i = 0, count = members.size(); i < count; i++) { Member member = (Member) members.elementAt(i); evaluator.setContext(member); Object result = evaluator.evaluateCurrent(); mapMemberToValue.put(member, result); } return mapMemberToValue; } static void sort( Evaluator evaluator, Vector members, ExpBase exp, boolean desc, boolean brk) { Hashtable mapMemberToValue = evaluateMembers(evaluator, exp, members); Comparator comparator = new MemberComparator( mapMemberToValue, desc, brk); sort(comparator, members); } static void sort(Comparator comparator, Vector vector) { Object[] objects = new Object[vector.size()]; vector.copyInto(objects); Arrays.sort(objects, comparator); for (int i = 0; i < vector.size(); i++) { vector.setElementAt(objects[i], i); } } static Object sum(Evaluator evaluator, Vector members, ExpBase exp) { // todo: treat constant exps as evaluateMembers() does double sum = 0; int valueCount = 0, errorCount = 0; for (int i = 0, count = members.size(); i < count; i++) { Member member = (Member) members.elementAt(i); evaluator.setContext(member); Object o = exp.evaluateScalar(evaluator); if (o == null || o == Util.nullValue) { } else if (o instanceof Util.ErrorCellValue) { // Carry on summing, so that if we are running in a // BatchingCellReader, we find out all the dependent cells we // need errorCount++; } else if (o instanceof BigDecimal) { valueCount++; sum += ((BigDecimal) o).doubleValue(); } else { valueCount++; sum += ((Double) o).doubleValue(); } } return errorCount > 0 ? ((CubeBase) evaluator.getCube()).getErrCellValue() : valueCount == 0 ? Util.nullValue : new Double(sum); } static int sign(double d) { return d == 0 ? 0 : d < 0 ? -1 : 1; } static Vector periodsToDate( Evaluator evaluator, Level level, Member member) { if (member == null) { member = evaluator.getContext( level.getHierarchy().getDimension()); } Member[] members = level.getPeriodsToDate(member); return toVector(members); } static class MemberComparator implements Comparator { Hashtable mapMemberToValue; boolean desc; boolean brk; MemberComparator( Hashtable mapMemberToValue, boolean desc, boolean brk) { this.mapMemberToValue = mapMemberToValue; this.desc = desc; this.brk = brk; } // implement Comparator public int compare(Object o, Object p) { Member member0 = (Member) o, member1 = (Member) p; int c = compareInternal(member0, member1); return desc ? -c : c; } private int compareInternal(Member member0, Member member1) { int c; if (!brk) { c = member0.compareHierarchically(member1); if (c != 0) { return c; } } Object value0 = mapMemberToValue.get(member0), value1 = mapMemberToValue.get(member1); if (value0 == value1) { return 0; } else if (value0 == Util.nullValue) { return 1; // null == +infinity } else if (value1 == Util.nullValue) { return -1; // null == +infinity } else if (value0 instanceof String) { return ((String) value0).compareTo((String) value1); } else if (value0 instanceof Double) { return sign(((Double) value0).doubleValue() - ((Double) value1).doubleValue()); } else if (value0 instanceof BigDecimal) { return ((BigDecimal) value0).compareTo((BigDecimal) value1); } else { throw getRes().newInternal("cannot compare " + value0); } } }; static class NullCellValue { public String toString() { return "#NULL"; } }; static class ErrorCellValue { public String toString() { return "#ERR"; } }; public static void assert(boolean b) { if (!b) { throw getRes().newInternal("assert failed"); } } public static void assert(boolean b, String message) { if (!b) { throw getRes().newInternal("assert failed: " + message); } } public static Error newInternal(String message) { return new Error(message); } public static void setThreadRes(MondrianResource resource) { if (resource == null) { threadRes.remove(Thread.currentThread()); } else { threadRes.put(Thread.currentThread(), resource); } } public static MondrianResource getRes() { MondrianResource resource = (MondrianResource) threadRes.get( Thread.currentThread()); if (resource == null) { String fileName = "file:///d:\\Main\\common\\java\\Resources\\MdxResource_en.xml"; try { resource = new MondrianResource( new java.net.URL(fileName), java.util.Locale.ENGLISH); } catch (IOException e) { throw new Error( "could not load resource file '" + fileName + "': " + getErrorMessage(e)); } } return resource; } /** * Converts an error into an array of strings, the most recent error first. * * @param e the error; may be null. Errors are chained if the error * implmements {@link ChainableError}. **/ public static String[] convertStackToString(Throwable e) { Vector v = new Vector(); while (e != null) { String sMsg = getErrorMessage(e); v.addElement(sMsg); if (e instanceof ChainableThrowable) { e = ((ChainableThrowable) e).getNextThrowable(); } else { e = null; } } String[] msgs = new String[v.size()]; v.copyInto(msgs); return msgs; } /** * @see #getErrorMessage(Throwable,boolean) **/ public static String getErrorMessage(Throwable err) { boolean prependClassName = !(err instanceof java.sql.SQLException || err.getClass() == java.lang.Exception.class); return getErrorMessage(err, prependClassName); } /** * Constructs the message associated with an arbitrary Java error, making * up one based on the stack trace if there is none. * * @param err the error * @param prependClassName should the error be preceded by the * class name of the Java exception? defaults to false, unless the error * is derived from {@link java.sql.Exception} or is exactly a {@link * java.lang.Exception} */ public static String getErrorMessage( Throwable err, boolean prependClassName) { String errMsg = err.getMessage(); if ((errMsg == null) || (err instanceof RuntimeException)) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); err.printStackTrace(pw); return sw.toString(); } else { if (prependClassName) { return err.getClass().getName() + ": " + errMsg; } else { return errMsg; } } } /** * <code>PropertyList</code> is an order-preserving list of key-value * pairs. Lookup is case-insensitive, but the case of keys is preserved. **/ public static class PropertyList { Vector v = new Vector(); public String get(String key) { for (int i = 0, n = v.size(); i < n; i++) { String[] pair = (String[]) v.elementAt(i); if (pair[0].equalsIgnoreCase(key)) { return pair[1]; } } return null; } public String put(String key, String value) { for (int i = 0, n = v.size(); i < n; i++) { String[] pair = (String[]) v.elementAt(i); if (pair[0].equalsIgnoreCase(key)) { String old = pair[1]; pair[1] = value; return old; } } v.addElement(new String[] {key, value}); return null; } public String toString() { StringBuffer sb = new StringBuffer(); for (int i = 0, n = v.size(); i < n; i++) { String[] pair = (String[]) v.elementAt(i); if (i++ > 0) { sb.append("; "); } sb.append(pair[0]); sb.append("="); sb.append(pair[1]); } return sb.toString(); } }; /** * Converts an OLE DB connect string such as "Provider=MSOLAP; * DataSource=LOCALHOST;" into a {@link PropertyList} containing (key, * value) pairs {("Provider","MSOLAP"), ("DataSource", "LOCALHOST")}. * * <p>Syntax Notes (quotes are not implemented)<ul> * * <li> Values may be delimited by single or double quotes, (for example, * name='value' or name="value"). Either single or double quotes may be * used within a connection string by using the other delimiter, for * example, name="value's" or name='value"s',but not name='value's' or * name=""value"". The value type is irrelevant. * * <li> All blank characters, except those placed within a value or within * quotes, are ignored. * * <li> Keyword value pairs must be separated by a semicolon (;). If a * semicolon is part of a value, it also must be delimited by quotes. * * <li> Names are not case sensitive. If a given name occurs more than once * in the connection string, the value associated with the last occurence * is used. * * <li> No escape sequences are supported. * </ul> **/ public static PropertyList parseConnectString(String s) { PropertyList properties = new PropertyList(); StringTokenizer st = new StringTokenizer(s, ";"); while (st.hasMoreTokens()) { String pair = st.nextToken(); // e.g. "Provider=MSOLAP" String key, value; int eq = pair.indexOf("="); if (eq < 0) { key = pair; value = null; } else { key = pair.substring(0, eq); value = pair.substring(eq + 1); } key = replace(key, " ", ""); // e.g. "Data Source" -> "DataSource" properties.put(key, value); } return properties; } } // End Util.java
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#5 | 1893 | Julian Hyde | mondrian: Integrate //guest/paul_dymecki/mondrian/...@1892 to //guest/julian_hyde/mondrian/... | ||
#4 | 1603 | Julian Hyde |
mondrian: Add Andreas' taglib; Rename 'mondrian.rolap.Util.assert' to 'assertTrue', because 'assert' is a keyword in jdk 1.4; Fix 'NON EMPTY'; JUnit framework for tests; Add Oracle dataset. |
||
#3 | 1576 | Julian Hyde |
mondrian: fix dataset (add column customer.ordinal); create dataset for oracle; get queries working on oracle; get format strings working; refactor out new packages mondrian.rolap.agg and mondrian.rolap.sql. |
||
#2 | 1499 | Julian Hyde |
Mondrian: Re-organize functions and type-checking Add mondrian.olap.fun package |
||
#1 | 1453 | Julian Hyde | mondrian: first source check-in |