();
+ int i = 0;
+ Quoting type;
+ while (i < identifier.length()) {
+ if (identifier.charAt(i) != '&' && identifier.charAt(i) != '[') {
+ throw new RuntimeException("invalid member '" + identifier + "'");
+ }
+
+ if (identifier.charAt(i) == '&') {
+ i++;
+ type = Quoting.KEY;
+ } else {
+ type = Quoting.QUOTED;
+ }
+
+ if (identifier.charAt(i) != '[') {
+ throw new RuntimeException("invalid member '" + identifier + "'");
+ }
+
+ int j = getEndIndex(identifier, i + 1);
+ if (j == -1) {
+ throw new RuntimeException("invalid member '" + identifier + "'");
+ }
+
+ list.add(
+ new Segment(
+ null,
+ replace(identifier.substring(i + 1, j), "]]", "]"),
+ type));
+
+ i = j + 2;
+ }
+ return list;
+ }
+
+ /**
+ * Returns the end of the current segment.
+ *
+ * @param s Identifier string
+ * @param i Start of identifier segment
+ * @return End of segment
+ */
+ private static int getEndIndex(String s, int i) {
+ while (i < s.length()) {
+ char ch = s.charAt(i);
+ if (ch == ']') {
+ if (i + 1 < s.length() && s.charAt(i + 1) == ']') { // found ]] => skip
+ i += 2;
+ } else {
+ return i;
+ }
+ } else {
+ i++;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns a string with every occurrence of a seek string replaced with
+ * another.
+ *
+ * @param s String to act on
+ * @param find String to find
+ * @param replace String to replace it with
+ * @return The modified string
+ */
+ private static String replace(
+ String s,
+ String find,
+ String replace)
+ {
+ // let's be optimistic
+ int found = s.indexOf(find);
+ if (found == -1) {
+ return s;
+ }
+ StringBuilder sb = new StringBuilder(s.length() + 20);
+ int start = 0;
+ char[] chars = s.toCharArray();
+ final int step = find.length();
+ if (step == 0) {
+ // Special case where find is "".
+ sb.append(s);
+ replace(sb, 0, find, replace);
+ } else {
+ for (;;) {
+ sb.append(chars, start, found-start);
+ if (found == s.length()) {
+ break;
+ }
+ sb.append(replace);
+ start = found + step;
+ found = s.indexOf(find, start);
+ if (found == -1) {
+ found = s.length();
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Replaces all occurrences of a string in a buffer with another.
+ *
+ * @param buf String buffer to act on
+ * @param start Ordinal within find
to start searching
+ * @param find String to find
+ * @param replace String to replace it with
+ * @return The string buffer
+ */
+ private static StringBuilder replace(
+ StringBuilder buf,
+ int start,
+ String find,
+ String replace)
+ {
+ // Search and replace from the end towards the start, to avoid O(n ^ 2)
+ // copying if the string occurs very commonly.
+ int findLength = find.length();
+ if (findLength == 0) {
+ // Special case where the seek string is empty.
+ for (int j = buf.length(); j >= 0; --j) {
+ buf.insert(j, replace);
+ }
+ return buf;
+ }
+ int k = buf.length();
+ while (k > 0) {
+ int i = buf.lastIndexOf(find, k);
+ if (i < start) {
+ break;
+ }
+ buf.replace(i, i + find.length(), replace);
+ // Step back far enough to ensure that the beginning of the section
+ // we just replaced does not cause a match.
+ k = i - findLength;
+ }
+ return buf;
+ }
+
/**
* Component in a compound identifier. It is described by its name and how
* the name is quoted.
@@ -179,6 +351,22 @@ public Segment(String name) {
this(null, name, Quoting.QUOTED);
}
+ /**
+ * Returns a string representation of this Segment.
+ *
+ * For example, "[Foo]", "&[123]", "Abc".
+ *
+ * @return String representation of this Segment
+ */
+ public String toString() {
+ switch (quoting) {
+ case UNQUOTED: //return name; Disabled to pass old tests...
+ case QUOTED: return "[" + name + "]";
+ case KEY: return "&[" + name + "]";
+ default: return "UNKNOWN:" + name;
+ }
+ }
+
/**
* Returns the region of the source code which this Segment was created
* from, if it was created by parsing.
diff --git a/src/org/olap4j/metadata/Cube.java b/src/org/olap4j/metadata/Cube.java
index c661ec2..75e76ad 100644
--- a/src/org/olap4j/metadata/Cube.java
+++ b/src/org/olap4j/metadata/Cube.java
@@ -9,6 +9,8 @@
*/
package org.olap4j.metadata;
+import org.olap4j.OlapException;
+
import java.util.*;
/**
@@ -18,6 +20,8 @@
* {@link Dimension}s and a list of {@link Measure}s. It may also have one or
* more {@link NamedSet}s.
*
+ * @see org.olap4j.metadata.Cube#getMeasures()
+ *
* @author jhyde
* @version $Id$
* @since Aug 22, 2006
@@ -42,6 +46,18 @@ public interface Cube extends MetadataElement {
*/
NamedList getDimensions();
+ /**
+ * Returns a list of {@link Hierarchy} objects in this Cube.
+ *
+ * The caller should assume that the list is immutable;
+ * if the caller modifies the list, behavior is undefined.
+ *
+ * @see org.olap4j.OlapDatabaseMetaData#getHierarchies(String, String, String, String, String)
+ *
+ * @return list of Dimensions
+ */
+ NamedList getHierarchies();
+
/**
* Returns a list of {@link Measure} objects in this Cube.
*
@@ -102,9 +118,12 @@ public interface Cube extends MetadataElement {
* dimension.
*
* @param nameParts Components of the fully-qualified member name
+ *
* @return member with the given name, or null if not found
+ *
+ * @throws OlapException if error occurs
*/
- Member lookupMember(String... nameParts);
+ Member lookupMember(String... nameParts) throws OlapException;
/**
* Finds a collection of members in the current Cube related to a given
@@ -114,23 +133,26 @@ public interface Cube extends MetadataElement {
* name as for {@link #lookupMember(String[])}, then applies the set of
* tree-operations to find related members.
*
- * The returned collection is sorted in hierarchical order. If no member
- * is found with the given name, the collection is empty.
+ *
The returned collection is sorted by level number then by member
+ * ordinal. If no member is found with the given name, the collection is
+ * empty.
*
*
For example,
*
- *
+ *
* lookupMembers(
* EnumSet.of(TreeOp.ANCESTORS, TreeOp.CHILDREN),
* "Time", "1997", "Q2")
- *
+ *
*
* returns
*
- *
- * [Time].[1997], [Time].[1997].[Q2].[4],
- * [Time].[1997].[Q2].[5], [Time].[1997].[Q2].[6]
- *
+ *
+ * [Time].[1997]
+ * [Time].[1997].[Q2].[4]
+ * [Time].[1997].[Q2].[5]
+ * [Time].[1997].[Q2].[6]
+ *
*
* The fully-qualified name starts with the name of the dimension,
* followed by the name of a root member, and continues with the name of
@@ -151,10 +173,12 @@ public interface Cube extends MetadataElement {
*
* @return collection of members related to the given member, or empty
* set if the member is not found
+ *
+ * @throws OlapException if error occurs
*/
List lookupMembers(
Set treeOps,
- String... nameParts);
+ String... nameParts) throws OlapException;
}
// End Cube.java
diff --git a/src/org/olap4j/metadata/Datatype.java b/src/org/olap4j/metadata/Datatype.java
new file mode 100644
index 0000000..575ac50
--- /dev/null
+++ b/src/org/olap4j/metadata/Datatype.java
@@ -0,0 +1,98 @@
+/*
+// $Id: Property.java 40 2007-11-18 01:13:26Z jhyde $
+// 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.
+// Copyright (C) 2006-2007 Julian Hyde
+// All Rights Reserved.
+// You must accept the terms of that agreement to use this software.
+*/
+package org.olap4j.metadata;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Enumeration of the allowable data types of a Property or Measure.
+ *
+ * The values derive from the OLE DB specification, specifically a
+ * subset of the OLE DB Types Indicators returned by SQL Server.
+ *
+ * @author jhyde
+ * @version $Id: Property.java 40 2007-11-18 01:13:26Z jhyde $
+ * @since Aug 23, 2006
+ */
+public enum Datatype {
+ /*
+ * The following values exactly match VARENUM
+ * in Automation and may be used in VARIANT.
+ */
+ INTEGER(3, "DBTYPE_I4", "A four-byte, signed integer: INTEGER"),
+
+ DOUBLE(5, "DBTYPE_R8", "A double-precision floating-point value: Double"),
+
+ CURRENCY(6, "DBTYPE_CY", "A currency value: LARGE_INTEGER, Currency is a fixed-point number with four digits to the right of the decimal point. It is stored in an eight-byte signed integer, scaled by 10,000."),
+
+ BOOLEAN(11, "DBTYPE_BOOL", "A Boolean value stored in the same way as in Automation: VARIANT_BOOL; 0 means false and ~0 (bitwise, the value is not 0; that is, all bits are set to 1) means true."),
+
+ /**
+ * Used by SQL Server for value.
+ */
+ VARIANT(12, "DBTYPE_VARIANT", "An Automation VARIANT"),
+
+ /**
+ * Used by SQL Server for font size.
+ */
+ UNSIGNED_SHORT(18, "DBTYPE_UI2", "A two-byte, unsigned integer"),
+
+ /**
+ * Used by SQL Server for colors, font flags and cell ordinal.
+ */
+ UNSIGNED_INTEGER(19, "DBTYPE_UI4", "A four-byte, unsigned integer"),
+
+ /*
+ * The following values exactly match VARENUM
+ * in Automation but cannot be used in VARIANT.
+ */
+ LARGE_INTEGER(20, "DBTYPE_I8", "An eight-byte, signed integer: LARGE_INTEGER"),
+
+ /*
+ * The following values are not in VARENUM in OLE.
+ */
+ STRING(130, "DBTYPE_WSTR", "A null-terminated Unicode character string: wchar_t[length]; If DBTYPE_WSTR is used by itself, the number of bytes allocated for the string, including the null-termination character, is specified by cbMaxLen in the DBBINDING structure. If DBTYPE_WSTR is combined with DBTYPE_BYREF, the number of bytes allocated for the string, including the null-termination character, is at least the length of the string plus two. In either case, the actual length of the string is determined from the bound length value. The maximum length of the string is the number of allocated bytes divided by sizeof(wchar_t) and truncated to the nearest integer.");
+
+ private final int xmlaOrdinal;
+
+ private static final Map xmlaMap =
+ new HashMap();
+
+ static {
+ for (Datatype datatype : values()) {
+ xmlaMap.put(datatype.xmlaOrdinal, datatype);
+ }
+ }
+
+ Datatype(
+ int xmlaOrdinal,
+ String dbTypeIndicator,
+ String description)
+ {
+ this.xmlaOrdinal = xmlaOrdinal;
+ }
+
+ /**
+ * Looks up a Datatype by its XMLA ordinal.
+ *
+ * @param xmlaOrdinal Ordinal of a Datatype according to the XMLA
+ * specification.
+ *
+ * @return Datatype with the given ordinal, or null if there is no
+ * such Datatype
+ */
+ public static Datatype forXmlaOrdinal(int xmlaOrdinal) {
+ return xmlaMap.get(xmlaOrdinal);
+ }
+
+}
+
+// End Datatype.java
diff --git a/src/org/olap4j/metadata/Hierarchy.java b/src/org/olap4j/metadata/Hierarchy.java
index dbc24bf..bd1734b 100644
--- a/src/org/olap4j/metadata/Hierarchy.java
+++ b/src/org/olap4j/metadata/Hierarchy.java
@@ -9,6 +9,8 @@
*/
package org.olap4j.metadata;
+import org.olap4j.OlapException;
+
/**
* An organization of the set of {@link Member}s in a {@link Dimension} and
* their positions relative to one another.
@@ -70,9 +72,15 @@ public interface Hierarchy extends MetadataElement {
* The caller should assume that the list is immutable;
* if the caller modifies the list, behavior is undefined.
*
+ * The result is similar to calling
+ * getLevels().get(0).getMembers()
. The contents will be the
+ * same, but this method returns a link {@link NamedList} rather than a
+ * mere {@link java.util.List} because the members of the root level are
+ * known to have unique names.
+ *
* @return root members of this hierarchy
*/
- NamedList getRootMembers();
+ NamedList getRootMembers() throws OlapException;
}
// End Hierarchy.java
diff --git a/src/org/olap4j/metadata/Level.java b/src/org/olap4j/metadata/Level.java
index da5fa5b..b83e5b1 100644
--- a/src/org/olap4j/metadata/Level.java
+++ b/src/org/olap4j/metadata/Level.java
@@ -9,7 +9,9 @@
*/
package org.olap4j.metadata;
-import java.util.List;
+import org.olap4j.OlapException;
+
+import java.util.*;
/**
* Group of {@link Member} objects in a {@link Hierarchy},
@@ -81,7 +83,14 @@ public interface Level extends MetadataElement {
*
* @return List of members in this Level
*/
- List getMembers();
+ List getMembers() throws OlapException;
+
+ /**
+ * Returns the number of members in this Level.
+ *
+ * @return number of members
+ */
+ int getCardinality();
/**
* Enumeration of the types of a {@link Level}.
@@ -199,6 +208,15 @@ public enum Type {
private final int xmlaOrdinal;
+ private static final Map xmlaMap =
+ new HashMap();
+
+ static {
+ for (Type type : values()) {
+ xmlaMap.put(type.xmlaOrdinal, type);
+ }
+ }
+
private Type(int code) {
this.xmlaOrdinal = code;
}
@@ -214,7 +232,20 @@ private Type(int code) {
public int xmlaOrdinal() {
return xmlaOrdinal;
}
-
+
+ /**
+ * Looks up a Type by its XMLA ordinal.
+ *
+ * @param xmlaOrdinal Ordinal of a level Type according to XMLA
+ * specification.
+ *
+ * @return Type with the given ordinal, or null if there is no such
+ * Type
+ */
+ public static Type forXmlaOrdinal(int xmlaOrdinal) {
+ return xmlaMap.get(xmlaOrdinal);
+ }
+
/**
* Returns whether this is a time-related level
* ({@link #TimeYears}, {@link #TimeQuarters}, {@link #TimeMonths},
diff --git a/src/org/olap4j/metadata/Measure.java b/src/org/olap4j/metadata/Measure.java
index 1f1d87c..c1951f4 100644
--- a/src/org/olap4j/metadata/Measure.java
+++ b/src/org/olap4j/metadata/Measure.java
@@ -9,6 +9,8 @@
*/
package org.olap4j.metadata;
+import java.util.*;
+
/**
* Data value of primary interest to the user browsing the cube.
*
@@ -20,6 +22,26 @@
* @since Oct 13, 2006
*/
public interface Measure extends Member {
+ /**
+ * Returns the Aggregator of this Measure.
+ *
+ * @return Aggregator
+ */
+ Aggregator getAggregator();
+
+ /**
+ * Returns the data type of this Measure.
+ *
+ * @return data type
+ */
+ Datatype getDatatype();
+
+ /**
+ * Returns whether this Measure is visible.
+ *
+ * @return whether this Measure is visible
+ */
+ boolean isVisible();
/**
* Enumeration of the aggregate functions which can be used to derive a
@@ -78,6 +100,21 @@ enum Aggregator {
private final int xmlaOrdinal;
+ private static final Map xmlaMap =
+ new HashMap();
+
+ static {
+ for (Aggregator aggregator : values()) {
+ xmlaMap.put(aggregator.xmlaOrdinal, aggregator);
+ }
+ }
+
+ /**
+ * Creates an Aggregator.
+ *
+ * @param xmlaOrdinal Ordinal of the aggregator in the XMLA
+ * specification
+ */
private Aggregator(int xmlaOrdinal) {
this.xmlaOrdinal = xmlaOrdinal;
}
@@ -93,6 +130,19 @@ private Aggregator(int xmlaOrdinal) {
public final int xmlaOrdinal() {
return xmlaOrdinal;
}
+
+ /**
+ * Looks up an Aggregator by its XMLA ordinal.
+ *
+ * @param xmlaOrdinal Ordinal of an Aggregator according to the XMLA
+ * specification.
+ *
+ * @return Aggregator with the given ordinal, or null if there is no
+ * such Aggregator
+ */
+ public static Aggregator forXmlaOrdinal(int xmlaOrdinal) {
+ return xmlaMap.get(xmlaOrdinal);
+ }
}
}
diff --git a/src/org/olap4j/metadata/Member.java b/src/org/olap4j/metadata/Member.java
index cfb220c..8dc2b0b 100644
--- a/src/org/olap4j/metadata/Member.java
+++ b/src/org/olap4j/metadata/Member.java
@@ -38,7 +38,17 @@ public interface Member extends MetadataElement {
*
* @return children of this member
*/
- NamedList extends Member> getChildMembers();
+ NamedList extends Member> getChildMembers() throws OlapException;
+
+ /**
+ * Returns the number of children this Member has.
+ *
+ * This method has the same effect as
+ * getChildMembers().size()
, but is typically less expensive.
+ *
+ * @return number of children
+ */
+ int getChildMemberCount();
/**
* Returns the parent of this Member, or null if it has no parent.
@@ -169,7 +179,7 @@ private Type(int ordinal) {
ParseTreeNode getExpression();
/**
- * Returns array of all members, which are ancestor to this
.
+ * Returns array of all members which are ancestor to this
.
*
* @return ancestor Members
*/
diff --git a/src/org/olap4j/metadata/MetadataElement.java b/src/org/olap4j/metadata/MetadataElement.java
index 3b9f240..e251bb4 100644
--- a/src/org/olap4j/metadata/MetadataElement.java
+++ b/src/org/olap4j/metadata/MetadataElement.java
@@ -36,8 +36,10 @@ public interface MetadataElement {
/**
* Returns the caption of this element in the given locale.
*
- * If locale
is null or if no caption has been defined for the
- * element in that locale, returns the caption in base locale.
+ *
If locale
is null or if no caption has been defined for
+ * the element in that locale, returns the caption in base locale.
+ *
+ * This method may return the empty string, but never returns null.
*
* @param locale Locale
* @return Caption of this element in the given locale, or the base locale;
@@ -48,8 +50,11 @@ public interface MetadataElement {
/**
* Returns the description of this element in the given locale.
*
- * If locale
is null or if no description has been defined for
- * the element in that locale, returns the description in base locale.
+ * If locale
is null or if no description has been defined
+ * for the element in that locale, returns the description in base
+ * locale.
+ *
+ * This method may return the empty string, but never returns null.
*
* @param locale Locale
* @return description of this element in the given locale, or the base
diff --git a/src/org/olap4j/metadata/Property.java b/src/org/olap4j/metadata/Property.java
index fc7a0af..a7fd48a 100644
--- a/src/org/olap4j/metadata/Property.java
+++ b/src/org/olap4j/metadata/Property.java
@@ -9,7 +9,7 @@
*/
package org.olap4j.metadata;
-import java.util.Locale;
+import java.util.*;
/**
* Definition of a property of a {@link Member} or
@@ -28,29 +28,113 @@ public interface Property extends MetadataElement {
Datatype getDatatype();
/**
- * Returns the scope of this property.
+ * Returns a set of flags which describe the type of this Property.
*
- * @return scope of this Property
+ * @return type of this Property
*/
- Scope getScope();
+ Set getType();
/**
- * Enumeration of the scope of a Property: whether it belongs to a member
- * or a cell.
+ * Returns the content type of this Property.
+ *
+ * @return content type
*/
- enum Scope {
- MEMBER,
- CELL
- }
+ ContentType getContentType();
/**
- * Enumeration of the allowable data types of a Property.
+ * Enumeration of aspects of the type of a Property. In particular, whether
+ * it belongs to a member or a cell.
+ *
+ * The values are as specified by XMLA for the PROPERTY_TYPE attribute
+ * of the MDSCHEMA_PROPERTIES data set.
+ * For example, XMLA specifies that the value 9 (0x1 | 0x8) means that a
+ * property belongs to a member and is a binary large object (BLOB).
+ * In this case, {@link Property#getType} will return the {@link Set}
+ * {{@link #MEMBER}, {@link #BLOB}}.
*/
- enum Datatype {
- TYPE_STRING,
- TYPE_OTHER,
- TYPE_NUMERIC,
- TYPE_BOOLEAN
+ enum TypeFlag {
+ /**
+ * Identifies a property of a member. This property can be used in the
+ * DIMENSION PROPERTIES clause of the SELECT statement.
+ */
+ MEMBER(1),
+
+ /**
+ * Identifies a property of a cell. This property can be used in the
+ * CELL PROPERTIES clause that occurs at the end of the SELECT
+ * statement.
+ */
+ CELL(2),
+
+ /**
+ * Identifies an internal property.
+ */
+ SYSTEM(4),
+
+ /**
+ * Identifies a property which contains a binary large object (blob).
+ */
+ BLOB(8);
+
+ public final int xmlaOrdinal;
+ private static final Map xmlaMap =
+ new HashMap();
+
+ static {
+ for (TypeFlag typeFlag : values()) {
+ xmlaMap.put(typeFlag.xmlaOrdinal, typeFlag);
+ }
+ }
+
+ private static final Set CELL_TYPE_FLAG =
+ Collections.unmodifiableSet(EnumSet.of(TypeFlag.CELL));
+ private static final Set MEMBER_TYPE_FLAG =
+ Collections.unmodifiableSet(EnumSet.of(TypeFlag.MEMBER));
+
+ private TypeFlag(int xmlaOrdinal) {
+ this.xmlaOrdinal = xmlaOrdinal;
+ }
+
+ /**
+ * Looks up a TypeFlag by its XMLA ordinal.
+ *
+ * @param xmlaOrdinal Ordinal of a TypeFlag according to the XMLA
+ * specification.
+ *
+ * @return TypeFlag with the given ordinal, or null if there is no
+ * such TypeFlag
+ */
+ public static TypeFlag forXmlaOrdinal(int xmlaOrdinal) {
+ return xmlaMap.get(xmlaOrdinal);
+ }
+
+ /**
+ * Creates a set of TypeFlag values by parsing a mask.
+ *
+ * For example, forMask(9)
returns the set
+ * {{@link #MEMBER}, {@link #BLOB}} because 9 = MEMBER (1) | BLOB (8).
+ *
+ * @param xmlaOrdinalMask Bit mask
+ * @return Set of TypeFlag values
+ */
+ public static Set forMask(int xmlaOrdinalMask) {
+ switch (xmlaOrdinalMask) {
+ // Optimize common cases {MEMBER} and {CELL}.
+ case 1:
+ return MEMBER_TYPE_FLAG;
+ case 2:
+ return CELL_TYPE_FLAG;
+ default:
+ Set type =
+ EnumSet.noneOf(TypeFlag.class);
+ for (TypeFlag typeFlag : values()) {
+ if ((xmlaOrdinalMask & typeFlag.xmlaOrdinal) != 0) {
+ type.add(typeFlag);
+ }
+ }
+ return type;
+ }
+ }
}
/**
@@ -83,110 +167,110 @@ enum StandardMemberProperty implements Property {
* Definition of the property which
* holds the name of the current catalog.
*/
- CATALOG_NAME(Datatype.TYPE_STRING, 10, false, "Optional. The name of the catalog to which this member belongs. NULL if the provider does not support catalogs."),
+ CATALOG_NAME(Datatype.STRING, 10, false, "Optional. The name of the catalog to which this member belongs. NULL if the provider does not support catalogs."),
/**
* Definition of the property which
* holds the name of the current schema.
*/
- SCHEMA_NAME(Datatype.TYPE_STRING, 11, false, "Optional. The name of the schema to which this member belongs. NULL if the provider does not support schemas."),
+ SCHEMA_NAME(Datatype.STRING, 11, false, "Optional. The name of the schema to which this member belongs. NULL if the provider does not support schemas."),
/**
* Definition of the property which
* holds the name of the current cube.
*/
- CUBE_NAME(Datatype.TYPE_STRING, 12, false, "Required. Name of the cube to which this member belongs."),
+ CUBE_NAME(Datatype.STRING, 12, false, "Required. Name of the cube to which this member belongs."),
/**
* Definition of the property which
* holds the unique name of the current dimension.
*/
- DIMENSION_UNIQUE_NAME(Datatype.TYPE_STRING, 13, false, "Required. Unique name of the dimension to which this member belongs. For providers that generate unique names by qualification, each component of this name is delimited."),
+ DIMENSION_UNIQUE_NAME(Datatype.STRING, 13, false, "Required. Unique name of the dimension to which this member belongs. For providers that generate unique names by qualification, each component of this name is delimited."),
/**
* Definition of the property which
* holds the unique name of the current hierarchy.
*/
- HIERARCHY_UNIQUE_NAME(Datatype.TYPE_STRING, 14, false, "Required. Unique name of the hierarchy. If the member belongs to more than one hierarchy, there is one row for each hierarchy to which it belongs. For providers that generate unique names by qualification, each component of this name is delimited."),
+ HIERARCHY_UNIQUE_NAME(Datatype.STRING, 14, false, "Required. Unique name of the hierarchy. If the member belongs to more than one hierarchy, there is one row for each hierarchy to which it belongs. For providers that generate unique names by qualification, each component of this name is delimited."),
/**
* Definition of the property which
* holds the unique name of the current level.
*/
- LEVEL_UNIQUE_NAME(Datatype.TYPE_STRING, 15, false, "Required. Unique name of the level to which the member belongs. For providers that generate unique names by qualification, each component of this name is delimited."),
+ LEVEL_UNIQUE_NAME(Datatype.STRING, 15, false, "Required. Unique name of the level to which the member belongs. For providers that generate unique names by qualification, each component of this name is delimited."),
/**
* Definition of the property which
* holds the ordinal of the current level.
*/
- LEVEL_NUMBER(Datatype.TYPE_STRING, 16, false, "Required. The distance of the member from the root of the hierarchy. The root level is zero."),
+ LEVEL_NUMBER(Datatype.UNSIGNED_INTEGER, 16, false, "Required. The distance of the member from the root of the hierarchy. The root level is zero."),
/**
* Definition of the property which
* holds the ordinal of the current member.
*/
- MEMBER_ORDINAL(Datatype.TYPE_NUMERIC, 17, false, "Required. Ordinal number of the member. Sort rank of the member when members of this dimension are sorted in their natural sort order. If providers do not have the concept of natural ordering, this should be the rank when sorted by MEMBER_NAME."),
+ MEMBER_ORDINAL(Datatype.UNSIGNED_INTEGER, 17, false, "Required. Ordinal number of the member. Sort rank of the member when members of this dimension are sorted in their natural sort order. If providers do not have the concept of natural ordering, this should be the rank when sorted by MEMBER_NAME."),
/**
* Definition of the property which
* holds the name of the current member.
*/
- MEMBER_NAME(Datatype.TYPE_STRING, 18, false, "Required. Name of the member."),
+ MEMBER_NAME(Datatype.STRING, 18, false, "Required. Name of the member."),
/**
* Definition of the property which
* holds the unique name of the current member.
*/
- MEMBER_UNIQUE_NAME(Datatype.TYPE_STRING, 19, false, "Required. Unique name of the member. For providers that generate unique names by qualification, each component of this name is delimited."),
+ MEMBER_UNIQUE_NAME(Datatype.STRING, 19, false, "Required. Unique name of the member. For providers that generate unique names by qualification, each component of this name is delimited."),
/**
* Definition of the property which
* holds the type of the member.
*/
- MEMBER_TYPE(Datatype.TYPE_STRING, 20, false, "Required. Type of the member. Can be one of the following values: MDMEMBER_Datatype.TYPE_REGULAR, MDMEMBER_Datatype.TYPE_ALL, MDMEMBER_Datatype.TYPE_FORMULA, MDMEMBER_Datatype.TYPE_MEASURE, MDMEMBER_Datatype.TYPE_UNKNOWN. MDMEMBER_Datatype.TYPE_FORMULA takes precedence over MDMEMBER_Datatype.TYPE_MEASURE. Therefore, if there is a formula (calculated) member on the Measures dimension, it is listed as MDMEMBER_Datatype.TYPE_FORMULA."),
+ MEMBER_TYPE(Datatype.STRING, 20, false, "Required. Type of the member. Can be one of the following values: MDMEMBER_Datatype.TYPE_REGULAR, MDMEMBER_Datatype.TYPE_ALL, MDMEMBER_Datatype.TYPE_FORMULA, MDMEMBER_Datatype.TYPE_MEASURE, MDMEMBER_Datatype.TYPE_UNKNOWN. MDMEMBER_Datatype.TYPE_FORMULA takes precedence over MDMEMBER_Datatype.TYPE_MEASURE. Therefore, if there is a formula (calculated) member on the Measures dimension, it is listed as MDMEMBER_Datatype.TYPE_FORMULA."),
/**
* Definition of the property which
* holds the GUID of the member
*/
- MEMBER_GUID(Datatype.TYPE_STRING, 21, false, "Optional. Member GUID. NULL if no GUID exists."),
+ MEMBER_GUID(Datatype.STRING, 21, false, "Optional. Member GUID. NULL if no GUID exists."),
/**
* Definition of the property which
* holds the label or caption associated with the member, or the
* member's name if no caption is defined.
*/
- MEMBER_CAPTION(Datatype.TYPE_STRING, 22, false, "Required. A label or caption associated with the member. Used primarily for display purposes. If a caption does not exist, MEMBER_NAME is returned."),
+ MEMBER_CAPTION(Datatype.STRING, 22, false, "Required. A label or caption associated with the member. Used primarily for display purposes. If a caption does not exist, MEMBER_NAME is returned."),
/**
* Definition of the property which holds the
* number of children this member has.
*/
- CHILDREN_CARDINALITY(Datatype.TYPE_NUMERIC, 23, false, "Required. Number of children that the member has. This can be an estimate, so consumers should not rely on this to be the exact count. Providers should return the best estimate possible."),
+ CHILDREN_CARDINALITY(Datatype.UNSIGNED_INTEGER, 23, false, "Required. Number of children that the member has. This can be an estimate, so consumers should not rely on this to be the exact count. Providers should return the best estimate possible."),
/**
* Definition of the property which holds the
* distance from the root of the hierarchy of this member's parent.
*/
- PARENT_LEVEL(Datatype.TYPE_NUMERIC, 24, false, "Required. The distance of the member's parent from the root level of the hierarchy. The root level is zero."),
+ PARENT_LEVEL(Datatype.UNSIGNED_INTEGER, 24, false, "Required. The distance of the member's parent from the root level of the hierarchy. The root level is zero."),
/**
* Definition of the property which holds the
* Name of the current catalog.
*/
- PARENT_UNIQUE_NAME(Datatype.TYPE_STRING, 25, false, "Required. Unique name of the member's parent. NULL is returned for any members at the root level. For providers that generate unique names by qualification, each component of this name is delimited."),
+ PARENT_UNIQUE_NAME(Datatype.STRING, 25, false, "Required. Unique name of the member's parent. NULL is returned for any members at the root level. For providers that generate unique names by qualification, each component of this name is delimited."),
/**
* Definition of the property which holds the
* number of parents that this member has. Generally 1, or 0 for root members.
*/
- PARENT_COUNT(Datatype.TYPE_NUMERIC, 26, false, "Required. Number of parents that this member has."),
+ PARENT_COUNT(Datatype.UNSIGNED_INTEGER, 26, false, "Required. Number of parents that this member has."),
/**
* Definition of the property which holds the
* description of this member.
*/
- DESCRIPTION(Datatype.TYPE_STRING, 27, false, "Optional. A human-readable description of the member."),
+ DESCRIPTION(Datatype.STRING, 27, false, "Optional. A human-readable description of the member."),
/**
* Definition of the internal property which holds the
@@ -194,7 +278,7 @@ enum StandardMemberProperty implements Property {
* (especially a measure or calculated member) in a user interface such as
* JPivot.
*/
- $visible(Datatype.TYPE_BOOLEAN, 28, true, null),
+ $visible(Datatype.BOOLEAN, 28, true, null),
/**
* Definition of the property which
@@ -203,7 +287,7 @@ enum StandardMemberProperty implements Property {
* Caution: Level depth of members in parent-child hierarchy isn't from their levels.
* It's calculated from the underlying data dynamically.
*/
- DEPTH(Datatype.TYPE_NUMERIC, 43, true, "The level depth of a member"),
+ DEPTH(Datatype.UNSIGNED_INTEGER, 43, true, "The level depth of a member"),
/**
* Definition of the property which
@@ -211,14 +295,14 @@ enum StandardMemberProperty implements Property {
*
*
Caution: This property's value is calculated based on a specified MDX query, so its value is dynamic at runtime.
*/
- DISPLAY_INFO(Datatype.TYPE_NUMERIC, 44, false, "Display instruction of a member for XML/A"),
+ DISPLAY_INFO(Datatype.UNSIGNED_INTEGER, 44, false, "Display instruction of a member for XML/A"),
/**
* Definition of the property which
* holds the value of a cell. Is usually numeric (since most measures are
* numeric) but is occasionally another type.
*/
- VALUE(Datatype.TYPE_NUMERIC, 41, false, "The unformatted value of the cell.");
+ VALUE(Datatype.VARIANT, 41, false, "The unformatted value of the cell.");
private final Datatype type;
private final String description;
@@ -256,8 +340,12 @@ public Datatype getDatatype() {
return type;
}
- public Scope getScope() {
- return Scope.MEMBER;
+ public Set getType() {
+ return TypeFlag.forMask(TypeFlag.MEMBER.xmlaOrdinal);
+ }
+
+ public ContentType getContentType() {
+ return ContentType.REGULAR;
}
public boolean isInternal() {
@@ -285,47 +373,47 @@ public boolean isInternal() {
*
*/
enum StandardCellProperty implements Property {
- BACK_COLOR(Datatype.TYPE_STRING, 30, false, "The background color for displaying the VALUE or FORMATTED_VALUE property. For more information, see FORE_COLOR and BACK_COLOR Contents."),
+ BACK_COLOR(Datatype.STRING, 30, false, "The background color for displaying the VALUE or FORMATTED_VALUE property. For more information, see FORE_COLOR and BACK_COLOR Contents."),
- CELL_EVALUATION_LIST(Datatype.TYPE_STRING, 31, false, "The semicolon-delimited list of evaluated formulas applicable to the cell, in order from lowest to highest solve order. For more information about solve order, see Understanding Pass Order and Solve Order"),
+ CELL_EVALUATION_LIST(Datatype.STRING, 31, false, "The semicolon-delimited list of evaluated formulas applicable to the cell, in order from lowest to highest solve order. For more information about solve order, see Understanding Pass Order and Solve Order"),
- CELL_ORDINAL(Datatype.TYPE_NUMERIC, 32, false, "The ordinal number of the cell in the dataset."),
+ CELL_ORDINAL(Datatype.UNSIGNED_INTEGER, 32, false, "The ordinal number of the cell in the dataset."),
- FORE_COLOR(Datatype.TYPE_STRING, 33, false, "The foreground color for displaying the VALUE or FORMATTED_VALUE property. For more information, see FORE_COLOR and BACK_COLOR Contents."),
+ FORE_COLOR(Datatype.STRING, 33, false, "The foreground color for displaying the VALUE or FORMATTED_VALUE property. For more information, see FORE_COLOR and BACK_COLOR Contents."),
- FONT_NAME(Datatype.TYPE_STRING, 34, false, "The font to be used to display the VALUE or FORMATTED_VALUE property."),
+ FONT_NAME(Datatype.STRING, 34, false, "The font to be used to display the VALUE or FORMATTED_VALUE property."),
- FONT_SIZE(Datatype.TYPE_STRING, 35, false, "Font size to be used to display the VALUE or FORMATTED_VALUE property."),
+ FONT_SIZE(Datatype.STRING, 35, false, "Font size to be used to display the VALUE or FORMATTED_VALUE property."),
- FONT_FLAGS(Datatype.TYPE_NUMERIC, 36, false, "The bitmask detailing effects on the font. The value is the result of a bitwise OR operation of one or more of the following constants: MDFF_BOLD = 1, MDFF_ITALIC = 2, MDFF_UNDERLINE = 4, MDFF_STRIKEOUT = 8. For example, the value 5 represents the combination of bold (MDFF_BOLD) and underline (MDFF_UNDERLINE) font effects."),
+ FONT_FLAGS(Datatype.UNSIGNED_INTEGER, 36, false, "The bitmask detailing effects on the font. The value is the result of a bitwise OR operation of one or more of the following constants: MDFF_BOLD = 1, MDFF_ITALIC = 2, MDFF_UNDERLINE = 4, MDFF_STRIKEOUT = 8. For example, the value 5 represents the combination of bold (MDFF_BOLD) and underline (MDFF_UNDERLINE) font effects."),
/**
* Definition of the property which
* holds the formatted value of a cell.
*/
- FORMATTED_VALUE(Datatype.TYPE_STRING, 37, false, "The character string that represents a formatted display of the VALUE property."),
+ FORMATTED_VALUE(Datatype.STRING, 37, false, "The character string that represents a formatted display of the VALUE property."),
/**
* Definition of the property which
* holds the format string used to format cell values.
*/
- FORMAT_STRING(Datatype.TYPE_STRING, 38, false, "The format string used to create the FORMATTED_VALUE property value. For more information, see FORMAT_STRING Contents."),
+ FORMAT_STRING(Datatype.STRING, 38, false, "The format string used to create the FORMATTED_VALUE property value. For more information, see FORMAT_STRING Contents."),
- NON_EMPTY_BEHAVIOR(Datatype.TYPE_STRING, 39, false, "The measure used to determine the behavior of calculated members when resolving empty cells."),
+ NON_EMPTY_BEHAVIOR(Datatype.STRING, 39, false, "The measure used to determine the behavior of calculated members when resolving empty cells."),
/**
* Definition of the property which
* determines the solve order of a calculated member with respect to other
* calculated members.
*/
- SOLVE_ORDER(Datatype.TYPE_NUMERIC, 40, false, "The solve order of the cell."),
+ SOLVE_ORDER(Datatype.INTEGER, 40, false, "The solve order of the cell."),
/**
* Definition of the property which
* holds the value of a cell. Is usually numeric (since most measures are
* numeric) but is occasionally another type.
*/
- VALUE(Datatype.TYPE_NUMERIC, 41, false, "The unformatted value of the cell."),
+ VALUE(Datatype.VARIANT, 41, false, "The unformatted value of the cell."),
/**
* Definition of the property which
@@ -335,7 +423,7 @@ enum StandardCellProperty implements Property {
* is not specified, the datatype is "Numeric" by default, except measures
* whose aggregator is "Count", whose datatype is "Integer".
*/
- DATATYPE(Datatype.TYPE_STRING, 42, false, "The datatype of the cell.");
+ DATATYPE(Datatype.STRING, 42, false, "The datatype of the cell.");
/**
* The datatype of the property.
@@ -360,8 +448,8 @@ public Datatype getDatatype() {
return type;
}
- public Scope getScope() {
- return Scope.CELL;
+ public Set getType() {
+ return TypeFlag.forMask(TypeFlag.CELL.xmlaOrdinal);
}
public String getName() {
@@ -383,6 +471,10 @@ public String getDescription(Locale locale) {
public boolean isInternal() {
return internal;
}
+
+ public ContentType getContentType() {
+ return ContentType.REGULAR;
+ }
}
/**
@@ -462,6 +554,15 @@ enum ContentType {
private final int xmlaOrdinal;
+ private static final Map xmlaMap =
+ new HashMap();
+
+ static {
+ for (ContentType contentType : values()) {
+ xmlaMap.put(contentType.xmlaOrdinal, contentType);
+ }
+ }
+
/**
* Returns the ordinal code as specified by XMLA.
*
@@ -477,6 +578,19 @@ public int xmlaOrdinal() {
private ContentType(int xmlaOrdinal) {
this.xmlaOrdinal = xmlaOrdinal;
}
+
+ /**
+ * Looks up a ContentType by its XMLA ordinal.
+ *
+ * @param xmlaOrdinal Ordinal of a ContentType according to the XMLA
+ * specification.
+ *
+ * @return ContentType with the given ordinal, or null if there is no
+ * such ContentType
+ */
+ public static ContentType forXmlaOrdinal(int xmlaOrdinal) {
+ return xmlaMap.get(xmlaOrdinal);
+ }
}
}
diff --git a/testsrc/org/olap4j/ConnectionTest.java b/testsrc/org/olap4j/ConnectionTest.java
index 130a72c..311de01 100644
--- a/testsrc/org/olap4j/ConnectionTest.java
+++ b/testsrc/org/olap4j/ConnectionTest.java
@@ -553,14 +553,12 @@ private void checkAxisMetaData(CellSetAxisMetaData cellSetAxisMetaData) {
assertEquals(2, hierarchies.size());
assertEquals("Store", hierarchies.get(0).getName());
assertEquals("Gender", hierarchies.get(1).getName());
- final NamedList properties =
+ final List properties =
cellSetAxisMetaData.getProperties();
assertEquals(3, properties.size());
assertEquals("MEMBER_ORDINAL", properties.get(0).getName());
assertEquals("MEMBER_UNIQUE_NAME", properties.get(1).getName());
assertEquals("DISPLAY_INFO", properties.get(2).getName());
- assertNotNull(properties.get("DISPLAY_INFO"));
- assertNull(properties.get("FOO_BAR"));
}
public void testCellSet() throws SQLException {
@@ -1117,8 +1115,8 @@ public void testMetadata() throws Exception {
assertEquals("MEMBER_CAPTION", property.getName());
assertEquals("MEMBER_CAPTION", property.getUniqueName());
- assertEquals(Property.Scope.MEMBER, property.getScope());
- assertEquals(Property.Datatype.TYPE_STRING, property.getDatatype());
+ assertEquals(EnumSet.of(Property.TypeFlag.MEMBER), property.getType());
+ assertEquals(Datatype.STRING, property.getDatatype());
}
/**
diff --git a/testsrc/org/olap4j/OlapTest.java b/testsrc/org/olap4j/OlapTest.java
index 43d16bf..15174f6 100644
--- a/testsrc/org/olap4j/OlapTest.java
+++ b/testsrc/org/olap4j/OlapTest.java
@@ -208,8 +208,7 @@ public static void listLevels(Hierarchy hierarchy) {
}
}
- public static void listMembers(Level level) {
-
+ public static void listMembers(Level level) throws OlapException {
List members = level.getMembers();
for (Member member : members) {
System.out.println("member name=" + member.getName());