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) 2003-2005 Julian Hyde
008// Copyright (C) 2005-2011 Pentaho
009// All Rights Reserved.
010*/
011package mondrian.xmla;
012
013import mondrian.olap.*;
014import mondrian.util.Composite;
015
016import org.olap4j.OlapConnection;
017import org.olap4j.OlapException;
018import org.olap4j.impl.ArrayNamedListImpl;
019import org.olap4j.impl.Olap4jUtil;
020import org.olap4j.mdx.IdentifierNode;
021import org.olap4j.mdx.IdentifierSegment;
022import org.olap4j.metadata.*;
023import org.olap4j.metadata.Cube;
024import org.olap4j.metadata.Dimension;
025import org.olap4j.metadata.Hierarchy;
026import org.olap4j.metadata.Level;
027import org.olap4j.metadata.Member;
028import org.olap4j.metadata.Member.TreeOp;
029import org.olap4j.metadata.NamedSet;
030import org.olap4j.metadata.Property;
031import org.olap4j.metadata.Schema;
032import org.olap4j.metadata.XmlaConstants;
033
034import java.lang.reflect.*;
035import java.sql.SQLException;
036import java.text.Format;
037import java.text.SimpleDateFormat;
038import java.util.*;
039
040import static mondrian.olap.Util.filter;
041import static mondrian.xmla.XmlaConstants.*;
042import static mondrian.xmla.XmlaHandler.getExtra;
043
044/**
045 * <code>RowsetDefinition</code> defines a rowset, including the columns it
046 * should contain.
047 *
048 * <p>See "XML for Analysis Rowsets", page 38 of the XML for Analysis
049 * Specification, version 1.1.
050 *
051 * @author jhyde
052 */
053public enum RowsetDefinition {
054    /**
055     * Returns a list of XML for Analysis data sources
056     * available on the server or Web Service. (For an
057     * example of how these may be published, see
058     * "XML for Analysis Implementation Walkthrough"
059     * in the XML for Analysis specification.)
060     *
061     *  http://msdn2.microsoft.com/en-us/library/ms126129(SQL.90).aspx
062     *
063     *
064     * restrictions
065     *
066     * Not supported
067     */
068    DISCOVER_DATASOURCES(
069        0,
070        "Returns a list of XML for Analysis data sources available on the "
071        + "server or Web Service.",
072        new Column[] {
073            DiscoverDatasourcesRowset.DataSourceName,
074            DiscoverDatasourcesRowset.DataSourceDescription,
075            DiscoverDatasourcesRowset.URL,
076            DiscoverDatasourcesRowset.DataSourceInfo,
077            DiscoverDatasourcesRowset.ProviderName,
078            DiscoverDatasourcesRowset.ProviderType,
079            DiscoverDatasourcesRowset.AuthenticationMode,
080        },
081        // XMLA does not specify a sort order, but olap4j does.
082        new Column[] {
083            DiscoverDatasourcesRowset.DataSourceName,
084        })
085    {
086        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
087            return new DiscoverDatasourcesRowset(request, handler);
088        }
089    },
090
091    /**
092     * Note that SQL Server also returns the data-mining columns.
093     *
094     *
095     * restrictions
096     *
097     * Not supported
098     */
099    DISCOVER_SCHEMA_ROWSETS(
100        2,
101        "Returns the names, values, and other information of all supported "
102        + "RequestType enumeration values.",
103        new Column[] {
104            DiscoverSchemaRowsetsRowset.SchemaName,
105            DiscoverSchemaRowsetsRowset.SchemaGuid,
106            DiscoverSchemaRowsetsRowset.Restrictions,
107            DiscoverSchemaRowsetsRowset.Description,
108        },
109        null /* not sorted */)
110    {
111        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
112            return new DiscoverSchemaRowsetsRowset(request, handler);
113        }
114        protected void writeRowsetXmlSchemaRowDef(SaxWriter writer) {
115            writer.startElement(
116                "xsd:complexType",
117                "name", "row");
118            writer.startElement("xsd:sequence");
119            for (Column column : columnDefinitions) {
120                final String name =
121                    XmlaUtil.ElementNameEncoder.INSTANCE.encode(column.name);
122
123                if (column == DiscoverSchemaRowsetsRowset.Restrictions) {
124                    writer.startElement(
125                        "xsd:element",
126                        "sql:field", column.name,
127                        "name", name,
128                        "minOccurs", 0,
129                        "maxOccurs", "unbounded");
130                    writer.startElement("xsd:complexType");
131                    writer.startElement("xsd:sequence");
132                    writer.element(
133                        "xsd:element",
134                        "name", "Name",
135                        "type", "xsd:string",
136                        "sql:field", "Name");
137                    writer.element(
138                        "xsd:element",
139                        "name", "Type",
140                        "type", "xsd:string",
141                        "sql:field", "Type");
142
143                    writer.endElement(); // xsd:sequence
144                    writer.endElement(); // xsd:complexType
145                    writer.endElement(); // xsd:element
146
147                } else {
148                    final String xsdType = column.type.columnType;
149
150                    Object[] attrs;
151                    if (column.nullable) {
152                        if (column.unbounded) {
153                            attrs = new Object[]{
154                                "sql:field", column.name,
155                                "name", name,
156                                "type", xsdType,
157                                "minOccurs", 0,
158                                "maxOccurs", "unbounded"
159                            };
160                        } else {
161                            attrs = new Object[]{
162                                "sql:field", column.name,
163                                "name", name,
164                                "type", xsdType,
165                                "minOccurs", 0
166                            };
167                        }
168                    } else {
169                        if (column.unbounded) {
170                            attrs = new Object[]{
171                                "sql:field", column.name,
172                                "name", name,
173                                "type", xsdType,
174                                "maxOccurs", "unbounded"
175                            };
176                        } else {
177                            attrs = new Object[]{
178                                "sql:field", column.name,
179                                "name", name,
180                                "type", xsdType
181                            };
182                        }
183                    }
184                    writer.element("xsd:element", attrs);
185                }
186            }
187            writer.endElement(); // xsd:sequence
188            writer.endElement(); // xsd:complexType
189        }
190    },
191
192    /**
193     *
194     *
195     *
196     * restrictions
197     *
198     * Not supported
199     */
200    DISCOVER_ENUMERATORS(
201        3,
202        "Returns a list of names, data types, and enumeration values for "
203        + "enumerators supported by the provider of a specific data source.",
204        new Column[] {
205            DiscoverEnumeratorsRowset.EnumName,
206            DiscoverEnumeratorsRowset.EnumDescription,
207            DiscoverEnumeratorsRowset.EnumType,
208            DiscoverEnumeratorsRowset.ElementName,
209            DiscoverEnumeratorsRowset.ElementDescription,
210            DiscoverEnumeratorsRowset.ElementValue,
211        },
212        null /* not sorted */)
213    {
214        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
215            return new DiscoverEnumeratorsRowset(request, handler);
216        }
217    },
218
219    /**
220     *
221     *
222     *
223     * restrictions
224     *
225     * Not supported
226     */
227    DISCOVER_PROPERTIES(
228        1,
229        "Returns a list of information and values about the requested "
230        + "properties that are supported by the specified data source "
231        + "provider.",
232        new Column[] {
233            DiscoverPropertiesRowset.PropertyName,
234            DiscoverPropertiesRowset.PropertyDescription,
235            DiscoverPropertiesRowset.PropertyType,
236            DiscoverPropertiesRowset.PropertyAccessType,
237            DiscoverPropertiesRowset.IsRequired,
238            DiscoverPropertiesRowset.Value,
239        },
240        null /* not sorted */)
241    {
242        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
243            return new DiscoverPropertiesRowset(request, handler);
244        }
245    },
246
247    /**
248     *
249     *
250     *
251     * restrictions
252     *
253     * Not supported
254     */
255    DISCOVER_KEYWORDS(
256        4,
257        "Returns an XML list of keywords reserved by the provider.",
258        new Column[] {
259            DiscoverKeywordsRowset.Keyword,
260        },
261        null /* not sorted */)
262    {
263        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
264            return new DiscoverKeywordsRowset(request, handler);
265        }
266    },
267
268    /**
269     *
270     *
271     *
272     * restrictions
273     *
274     * Not supported
275     */
276    DISCOVER_LITERALS(
277        5,
278        "Returns information about literals supported by the provider.",
279        new Column[] {
280            DiscoverLiteralsRowset.LiteralName,
281            DiscoverLiteralsRowset.LiteralValue,
282            DiscoverLiteralsRowset.LiteralInvalidChars,
283            DiscoverLiteralsRowset.LiteralInvalidStartingChars,
284            DiscoverLiteralsRowset.LiteralMaxLength,
285        },
286        null /* not sorted */)
287    {
288        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
289            return new DiscoverLiteralsRowset(request, handler);
290        }
291    },
292
293    /**
294     *
295     *
296     *
297     * restrictions
298     *
299     * Not supported
300     */
301    DBSCHEMA_CATALOGS(
302        6,
303        "Identifies the physical attributes associated with catalogs "
304        + "accessible from the provider.",
305        new Column[] {
306            DbschemaCatalogsRowset.CatalogName,
307            DbschemaCatalogsRowset.Description,
308            DbschemaCatalogsRowset.Roles,
309            DbschemaCatalogsRowset.DateModified,
310        },
311        new Column[] {
312            DbschemaCatalogsRowset.CatalogName,
313        })
314    {
315        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
316            return new DbschemaCatalogsRowset(request, handler);
317        }
318    },
319
320    /**
321     *
322     *
323     *
324     * restrictions
325     *
326     * Not supported
327     *    COLUMN_OLAP_TYPE
328     */
329    DBSCHEMA_COLUMNS(
330        7, null,
331        new Column[] {
332            DbschemaColumnsRowset.TableCatalog,
333            DbschemaColumnsRowset.TableSchema,
334            DbschemaColumnsRowset.TableName,
335            DbschemaColumnsRowset.ColumnName,
336            DbschemaColumnsRowset.OrdinalPosition,
337            DbschemaColumnsRowset.ColumnHasDefault,
338            DbschemaColumnsRowset.ColumnFlags,
339            DbschemaColumnsRowset.IsNullable,
340            DbschemaColumnsRowset.DataType,
341            DbschemaColumnsRowset.CharacterMaximumLength,
342            DbschemaColumnsRowset.CharacterOctetLength,
343            DbschemaColumnsRowset.NumericPrecision,
344            DbschemaColumnsRowset.NumericScale,
345        },
346        new Column[] {
347            DbschemaColumnsRowset.TableCatalog,
348            DbschemaColumnsRowset.TableSchema,
349            DbschemaColumnsRowset.TableName,
350        })
351    {
352        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
353            return new DbschemaColumnsRowset(request, handler);
354        }
355    },
356
357    /**
358     *
359     *
360     *
361     * restrictions
362     *
363     * Not supported
364     */
365    DBSCHEMA_PROVIDER_TYPES(
366        8, null,
367        new Column[] {
368            DbschemaProviderTypesRowset.TypeName,
369            DbschemaProviderTypesRowset.DataType,
370            DbschemaProviderTypesRowset.ColumnSize,
371            DbschemaProviderTypesRowset.LiteralPrefix,
372            DbschemaProviderTypesRowset.LiteralSuffix,
373            DbschemaProviderTypesRowset.IsNullable,
374            DbschemaProviderTypesRowset.CaseSensitive,
375            DbschemaProviderTypesRowset.Searchable,
376            DbschemaProviderTypesRowset.UnsignedAttribute,
377            DbschemaProviderTypesRowset.FixedPrecScale,
378            DbschemaProviderTypesRowset.AutoUniqueValue,
379            DbschemaProviderTypesRowset.IsLong,
380            DbschemaProviderTypesRowset.BestMatch,
381        },
382        new Column[] {
383            DbschemaProviderTypesRowset.DataType,
384        })
385    {
386        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
387            return new DbschemaProviderTypesRowset(request, handler);
388        }
389    },
390
391    DBSCHEMA_SCHEMATA(
392        8, null,
393        new Column[] {
394            DbschemaSchemataRowset.CatalogName,
395            DbschemaSchemataRowset.SchemaName,
396            DbschemaSchemataRowset.SchemaOwner,
397        },
398        new Column[] {
399            DbschemaSchemataRowset.CatalogName,
400            DbschemaSchemataRowset.SchemaName,
401            DbschemaSchemataRowset.SchemaOwner,
402        })
403    {
404        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
405            return new DbschemaSchemataRowset(request, handler);
406        }
407    },
408
409    /**
410     * http://msdn2.microsoft.com/en-us/library/ms126299(SQL.90).aspx
411     *
412     * restrictions:
413     *   TABLE_CATALOG Optional
414     *   TABLE_SCHEMA Optional
415     *   TABLE_NAME Optional
416     *   TABLE_TYPE Optional
417     *   TABLE_OLAP_TYPE Optional
418     *
419     * Not supported
420     */
421    DBSCHEMA_TABLES(
422        9, null,
423        new Column[] {
424            DbschemaTablesRowset.TableCatalog,
425            DbschemaTablesRowset.TableSchema,
426            DbschemaTablesRowset.TableName,
427            DbschemaTablesRowset.TableType,
428            DbschemaTablesRowset.TableGuid,
429            DbschemaTablesRowset.Description,
430            DbschemaTablesRowset.TablePropId,
431            DbschemaTablesRowset.DateCreated,
432            DbschemaTablesRowset.DateModified,
433            //TableOlapType,
434        },
435        new Column[] {
436            DbschemaTablesRowset.TableType,
437            DbschemaTablesRowset.TableCatalog,
438            DbschemaTablesRowset.TableSchema,
439            DbschemaTablesRowset.TableName,
440        })
441    {
442        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
443            return new DbschemaTablesRowset(request, handler);
444        }
445    },
446
447    /**
448     * http://msdn.microsoft.com/library/en-us/oledb/htm/
449     * oledbtables_info_rowset.asp
450     *
451     *
452     * restrictions
453     *
454     * Not supported
455     */
456    DBSCHEMA_TABLES_INFO(
457        10, null,
458        new Column[] {
459            DbschemaTablesInfoRowset.TableCatalog,
460            DbschemaTablesInfoRowset.TableSchema,
461            DbschemaTablesInfoRowset.TableName,
462            DbschemaTablesInfoRowset.TableType,
463            DbschemaTablesInfoRowset.TableGuid,
464            DbschemaTablesInfoRowset.Bookmarks,
465            DbschemaTablesInfoRowset.BookmarkType,
466            DbschemaTablesInfoRowset.BookmarkDataType,
467            DbschemaTablesInfoRowset.BookmarkMaximumLength,
468            DbschemaTablesInfoRowset.BookmarkInformation,
469            DbschemaTablesInfoRowset.TableVersion,
470            DbschemaTablesInfoRowset.Cardinality,
471            DbschemaTablesInfoRowset.Description,
472            DbschemaTablesInfoRowset.TablePropId,
473        },
474        null /* cannot find doc -- presume unsorted */)
475    {
476        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
477            return new DbschemaTablesInfoRowset(request, handler);
478        }
479    },
480
481    /**
482     * http://msdn2.microsoft.com/en-us/library/ms126032(SQL.90).aspx
483     *
484     * restrictions
485     *   CATALOG_NAME Optional
486     *   SCHEMA_NAME Optional
487     *   CUBE_NAME Mandatory
488     *   ACTION_NAME Optional
489     *   ACTION_TYPE Optional
490     *   COORDINATE Mandatory
491     *   COORDINATE_TYPE Mandatory
492     *   INVOCATION
493     *      (Optional) The INVOCATION restriction column defaults to the
494     *      value of MDACTION_INVOCATION_INTERACTIVE. To retrieve all
495     *      actions, use the MDACTION_INVOCATION_ALL value in the
496     *      INVOCATION restriction column.
497     *   CUBE_SOURCE
498     *      (Optional) A bitmap with one of the following valid values:
499     *
500     *      1 CUBE
501     *      2 DIMENSION
502     *
503     *      Default restriction is a value of 1.
504     *
505     * Not supported
506     */
507    MDSCHEMA_ACTIONS(
508        11, null, new Column[] {
509            MdschemaActionsRowset.CatalogName,
510            MdschemaActionsRowset.SchemaName,
511            MdschemaActionsRowset.CubeName,
512            MdschemaActionsRowset.ActionName,
513            MdschemaActionsRowset.Coordinate,
514            MdschemaActionsRowset.CoordinateType,
515        }, new Column[] {
516            // Spec says sort on CATALOG_NAME, SCHEMA_NAME, CUBE_NAME,
517            // ACTION_NAME.
518            MdschemaActionsRowset.CatalogName,
519            MdschemaActionsRowset.SchemaName,
520            MdschemaActionsRowset.CubeName,
521            MdschemaActionsRowset.ActionName,
522        })
523    {
524        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
525            return new MdschemaActionsRowset(request, handler);
526        }
527    },
528
529    /**
530     * http://msdn2.microsoft.com/en-us/library/ms126271(SQL.90).aspx
531     *
532     * restrictions
533     *   CATALOG_NAME Optional.
534     *   SCHEMA_NAME Optional.
535     *   CUBE_NAME Optional.
536     *   CUBE_TYPE
537     *      (Optional) A bitmap with one of these valid values:
538     *      1 CUBE
539     *      2 DIMENSION
540     *     Default restriction is a value of 1.
541     *   BASE_CUBE_NAME Optional.
542     *
543     * Not supported
544     *   CREATED_ON
545     *   LAST_SCHEMA_UPDATE
546     *   SCHEMA_UPDATED_BY
547     *   LAST_DATA_UPDATE
548     *   DATA_UPDATED_BY
549     *   ANNOTATIONS
550     */
551    MDSCHEMA_CUBES(
552        12, null,
553        new Column[] {
554            MdschemaCubesRowset.CatalogName,
555            MdschemaCubesRowset.SchemaName,
556            MdschemaCubesRowset.CubeName,
557            MdschemaCubesRowset.CubeType,
558            MdschemaCubesRowset.CubeGuid,
559            MdschemaCubesRowset.CreatedOn,
560            MdschemaCubesRowset.LastSchemaUpdate,
561            MdschemaCubesRowset.SchemaUpdatedBy,
562            MdschemaCubesRowset.LastDataUpdate,
563            MdschemaCubesRowset.DataUpdatedBy,
564            MdschemaCubesRowset.IsDrillthroughEnabled,
565            MdschemaCubesRowset.IsWriteEnabled,
566            MdschemaCubesRowset.IsLinkable,
567            MdschemaCubesRowset.IsSqlEnabled,
568            MdschemaCubesRowset.CubeCaption,
569            MdschemaCubesRowset.Description,
570            MdschemaCubesRowset.Dimensions,
571            MdschemaCubesRowset.Sets,
572            MdschemaCubesRowset.Measures
573        },
574        new Column[] {
575            MdschemaCubesRowset.CatalogName,
576            MdschemaCubesRowset.SchemaName,
577            MdschemaCubesRowset.CubeName,
578        })
579    {
580        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
581            return new MdschemaCubesRowset(request, handler);
582        }
583    },
584
585    /**
586     * http://msdn2.microsoft.com/en-us/library/ms126180(SQL.90).aspx
587     * http://msdn2.microsoft.com/en-us/library/ms126180.aspx
588     *
589     * restrictions
590     *    CATALOG_NAME Optional.
591     *    SCHEMA_NAME Optional.
592     *    CUBE_NAME Optional.
593     *    DIMENSION_NAME Optional.
594     *    DIMENSION_UNIQUE_NAME Optional.
595     *    CUBE_SOURCE (Optional) A bitmap with one of the following valid
596     *    values:
597     *      1 CUBE
598     *      2 DIMENSION
599     *    Default restriction is a value of 1.
600     *
601     *    DIMENSION_VISIBILITY (Optional) A bitmap with one of the following
602     *    valid values:
603     *      1 Visible
604     *      2 Not visible
605     *    Default restriction is a value of 1.
606     */
607    MDSCHEMA_DIMENSIONS(
608        13, null,
609        new Column[] {
610            MdschemaDimensionsRowset.CatalogName,
611            MdschemaDimensionsRowset.SchemaName,
612            MdschemaDimensionsRowset.CubeName,
613            MdschemaDimensionsRowset.DimensionName,
614            MdschemaDimensionsRowset.DimensionUniqueName,
615            MdschemaDimensionsRowset.DimensionGuid,
616            MdschemaDimensionsRowset.DimensionCaption,
617            MdschemaDimensionsRowset.DimensionOrdinal,
618            MdschemaDimensionsRowset.DimensionType,
619            MdschemaDimensionsRowset.DimensionCardinality,
620            MdschemaDimensionsRowset.DefaultHierarchy,
621            MdschemaDimensionsRowset.Description,
622            MdschemaDimensionsRowset.IsVirtual,
623            MdschemaDimensionsRowset.IsReadWrite,
624            MdschemaDimensionsRowset.DimensionUniqueSettings,
625            MdschemaDimensionsRowset.DimensionMasterUniqueName,
626            MdschemaDimensionsRowset.DimensionIsVisible,
627            MdschemaDimensionsRowset.Hierarchies,
628        },
629        new Column[] {
630            MdschemaDimensionsRowset.CatalogName,
631            MdschemaDimensionsRowset.SchemaName,
632            MdschemaDimensionsRowset.CubeName,
633            MdschemaDimensionsRowset.DimensionName,
634        })
635    {
636        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
637            return new MdschemaDimensionsRowset(request, handler);
638        }
639    },
640
641    /**
642     * http://msdn2.microsoft.com/en-us/library/ms126257(SQL.90).aspx
643     *
644     * restrictions
645     *   LIBRARY_NAME Optional.
646     *   INTERFACE_NAME Optional.
647     *   FUNCTION_NAME Optional.
648     *   ORIGIN Optional.
649     *
650     * Not supported
651     *  DLL_NAME
652     *    Optional
653     *  HELP_FILE
654     *    Optional
655     *  HELP_CONTEXT
656     *    Optional
657     *    - SQL Server xml schema says that this must be present
658     *  OBJECT
659     *    Optional
660     *  CAPTION The display caption for the function.
661     */
662    MDSCHEMA_FUNCTIONS(
663        14, null,
664        new Column[] {
665            MdschemaFunctionsRowset.FunctionName,
666            MdschemaFunctionsRowset.Description,
667            MdschemaFunctionsRowset.ParameterList,
668            MdschemaFunctionsRowset.ReturnType,
669            MdschemaFunctionsRowset.Origin,
670            MdschemaFunctionsRowset.InterfaceName,
671            MdschemaFunctionsRowset.LibraryName,
672            MdschemaFunctionsRowset.Caption,
673        },
674        new Column[] {
675            MdschemaFunctionsRowset.LibraryName,
676            MdschemaFunctionsRowset.InterfaceName,
677            MdschemaFunctionsRowset.FunctionName,
678            MdschemaFunctionsRowset.Origin,
679        })
680    {
681        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
682            return new MdschemaFunctionsRowset(request, handler);
683        }
684    },
685
686    /**
687     * http://msdn2.microsoft.com/en-us/library/ms126062(SQL.90).aspx
688     *
689     * restrictions
690     *    CATALOG_NAME Optional.
691     *    SCHEMA_NAME Optional.
692     *    CUBE_NAME Optional.
693     *    DIMENSION_UNIQUE_NAME Optional.
694     *    HIERARCHY_NAME Optional.
695     *    HIERARCHY_UNIQUE_NAME Optional.
696     *    HIERARCHY_ORIGIN
697     *       (Optional) A default restriction is in effect
698     *       on MD_USER_DEFINED and MD_SYSTEM_ENABLED.
699     *    CUBE_SOURCE
700     *      (Optional) A bitmap with one of the following valid values:
701     *      1 CUBE
702     *      2 DIMENSION
703     *      Default restriction is a value of 1.
704     *    HIERARCHY_VISIBILITY
705     *      (Optional) A bitmap with one of the following valid values:
706     *      1 Visible
707     *      2 Not visible
708     *      Default restriction is a value of 1.
709     *
710     * Not supported
711     *  HIERARCHY_ORIGIN
712     *  HIERARCHY_DISPLAY_FOLDER
713     *  INSTANCE_SELECTION
714     */
715    MDSCHEMA_HIERARCHIES(
716        15, null,
717        new Column[] {
718            MdschemaHierarchiesRowset.CatalogName,
719            MdschemaHierarchiesRowset.SchemaName,
720            MdschemaHierarchiesRowset.CubeName,
721            MdschemaHierarchiesRowset.DimensionUniqueName,
722            MdschemaHierarchiesRowset.HierarchyName,
723            MdschemaHierarchiesRowset.HierarchyUniqueName,
724            MdschemaHierarchiesRowset.HierarchyGuid,
725            MdschemaHierarchiesRowset.HierarchyCaption,
726            MdschemaHierarchiesRowset.DimensionType,
727            MdschemaHierarchiesRowset.HierarchyCardinality,
728            MdschemaHierarchiesRowset.DefaultMember,
729            MdschemaHierarchiesRowset.AllMember,
730            MdschemaHierarchiesRowset.Description,
731            MdschemaHierarchiesRowset.Structure,
732            MdschemaHierarchiesRowset.IsVirtual,
733            MdschemaHierarchiesRowset.IsReadWrite,
734            MdschemaHierarchiesRowset.DimensionUniqueSettings,
735            MdschemaHierarchiesRowset.DimensionIsVisible,
736            MdschemaHierarchiesRowset.HierarchyIsVisible,
737            MdschemaHierarchiesRowset.HierarchyOrdinal,
738            MdschemaHierarchiesRowset.DimensionIsShared,
739            MdschemaHierarchiesRowset.ParentChild,
740            MdschemaHierarchiesRowset.Levels,
741        },
742        new Column[] {
743            MdschemaHierarchiesRowset.CatalogName,
744            MdschemaHierarchiesRowset.SchemaName,
745            MdschemaHierarchiesRowset.CubeName,
746            MdschemaHierarchiesRowset.DimensionUniqueName,
747            MdschemaHierarchiesRowset.HierarchyName,
748        })
749    {
750        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
751            return new MdschemaHierarchiesRowset(request, handler);
752        }
753    },
754
755    /**
756     * http://msdn2.microsoft.com/en-us/library/ms126038(SQL.90).aspx
757     *
758     * restriction
759     *   CATALOG_NAME Optional.
760     *   SCHEMA_NAME Optional.
761     *   CUBE_NAME Optional.
762     *   DIMENSION_UNIQUE_NAME Optional.
763     *   HIERARCHY_UNIQUE_NAME Optional.
764     *   LEVEL_NAME Optional.
765     *   LEVEL_UNIQUE_NAME Optional.
766     *   LEVEL_ORIGIN
767     *       (Optional) A default restriction is in effect
768     *       on MD_USER_DEFINED and MD_SYSTEM_ENABLED
769     *   CUBE_SOURCE
770     *       (Optional) A bitmap with one of the following valid values:
771     *       1 CUBE
772     *       2 DIMENSION
773     *       Default restriction is a value of 1.
774     *   LEVEL_VISIBILITY
775     *       (Optional) A bitmap with one of the following values:
776     *       1 Visible
777     *       2 Not visible
778     *       Default restriction is a value of 1.
779     *
780     * Not supported
781     *  CUSTOM_ROLLUP_SETTINGS
782     *  LEVEL_UNIQUE_SETTINGS
783     *  LEVEL_ORDERING_PROPERTY
784     *  LEVEL_DBTYPE
785     *  LEVEL_MASTER_UNIQUE_NAME
786     *  LEVEL_NAME_SQL_COLUMN_NAME Customers:(All)!NAME
787     *  LEVEL_KEY_SQL_COLUMN_NAME Customers:(All)!KEY
788     *  LEVEL_UNIQUE_NAME_SQL_COLUMN_NAME Customers:(All)!UNIQUE_NAME
789     *  LEVEL_ATTRIBUTE_HIERARCHY_NAME
790     *  LEVEL_KEY_CARDINALITY
791     *  LEVEL_ORIGIN
792     *
793     */
794    MDSCHEMA_LEVELS(
795        16, null,
796        new Column[] {
797            MdschemaLevelsRowset.CatalogName,
798            MdschemaLevelsRowset.SchemaName,
799            MdschemaLevelsRowset.CubeName,
800            MdschemaLevelsRowset.DimensionUniqueName,
801            MdschemaLevelsRowset.HierarchyUniqueName,
802            MdschemaLevelsRowset.LevelName,
803            MdschemaLevelsRowset.LevelUniqueName,
804            MdschemaLevelsRowset.LevelGuid,
805            MdschemaLevelsRowset.LevelCaption,
806            MdschemaLevelsRowset.LevelNumber,
807            MdschemaLevelsRowset.LevelCardinality,
808            MdschemaLevelsRowset.LevelType,
809            MdschemaLevelsRowset.CustomRollupSettings,
810            MdschemaLevelsRowset.LevelUniqueSettings,
811            MdschemaLevelsRowset.LevelIsVisible,
812            MdschemaLevelsRowset.Description,
813        },
814        new Column[] {
815            MdschemaLevelsRowset.CatalogName,
816            MdschemaLevelsRowset.SchemaName,
817            MdschemaLevelsRowset.CubeName,
818            MdschemaLevelsRowset.DimensionUniqueName,
819            MdschemaLevelsRowset.HierarchyUniqueName,
820            MdschemaLevelsRowset.LevelNumber,
821        })
822    {
823        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
824            return new MdschemaLevelsRowset(request, handler);
825        }
826    },
827
828    /**
829     * http://msdn2.microsoft.com/en-us/library/ms126250(SQL.90).aspx
830     *
831     * restrictions
832     *   CATALOG_NAME Optional.
833     *   SCHEMA_NAME Optional.
834     *   CUBE_NAME Optional.
835     *   MEASURE_NAME Optional.
836     *   MEASURE_UNIQUE_NAME Optional.
837     *   CUBE_SOURCE
838     *     (Optional) A bitmap with one of the following valid values:
839     *     1 CUBE
840     *     2 DIMENSION
841     *     Default restriction is a value of 1.
842     *   MEASURE_VISIBILITY
843     *     (Optional) A bitmap with one of the following valid values:
844     *     1 Visible
845     *     2 Not Visible
846     *     Default restriction is a value of 1.
847     *
848     * Not supported
849     *  MEASURE_GUID
850     *  NUMERIC_PRECISION
851     *  NUMERIC_SCALE
852     *  MEASURE_UNITS
853     *  EXPRESSION
854     *  MEASURE_NAME_SQL_COLUMN_NAME
855     *  MEASURE_UNQUALIFIED_CAPTION
856     *  MEASUREGROUP_NAME
857     *  MEASURE_DISPLAY_FOLDER
858     *  DEFAULT_FORMAT_STRING
859     */
860    MDSCHEMA_MEASURES(
861        17, null,
862        new Column[] {
863            MdschemaMeasuresRowset.CatalogName,
864            MdschemaMeasuresRowset.SchemaName,
865            MdschemaMeasuresRowset.CubeName,
866            MdschemaMeasuresRowset.MeasureName,
867            MdschemaMeasuresRowset.MeasureUniqueName,
868            MdschemaMeasuresRowset.MeasureCaption,
869            MdschemaMeasuresRowset.MeasureGuid,
870            MdschemaMeasuresRowset.MeasureAggregator,
871            MdschemaMeasuresRowset.DataType,
872            MdschemaMeasuresRowset.MeasureIsVisible,
873            MdschemaMeasuresRowset.LevelsList,
874            MdschemaMeasuresRowset.Description,
875            MdschemaMeasuresRowset.FormatString,
876        },
877        new Column[] {
878            MdschemaMeasuresRowset.CatalogName,
879            MdschemaMeasuresRowset.SchemaName,
880            MdschemaMeasuresRowset.CubeName,
881            MdschemaMeasuresRowset.MeasureName,
882        })
883    {
884        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
885            return new MdschemaMeasuresRowset(request, handler);
886        }
887    },
888
889    /**
890     *
891     * http://msdn2.microsoft.com/es-es/library/ms126046.aspx
892     *
893     *
894     * restrictions
895     *   CATALOG_NAME Optional.
896     *   SCHEMA_NAME Optional.
897     *   CUBE_NAME Optional.
898     *   DIMENSION_UNIQUE_NAME Optional.
899     *   HIERARCHY_UNIQUE_NAME Optional.
900     *   LEVEL_UNIQUE_NAME Optional.
901     *   LEVEL_NUMBER Optional.
902     *   MEMBER_NAME Optional.
903     *   MEMBER_UNIQUE_NAME Optional.
904     *   MEMBER_CAPTION Optional.
905     *   MEMBER_TYPE Optional.
906     *   TREE_OP (Optional) Only applies to a single member:
907     *      MDTREEOP_ANCESTORS (0x20) returns all of the ancestors.
908     *      MDTREEOP_CHILDREN (0x01) returns only the immediate children.
909     *      MDTREEOP_SIBLINGS (0x02) returns members on the same level.
910     *      MDTREEOP_PARENT (0x04) returns only the immediate parent.
911     *      MDTREEOP_SELF (0x08) returns itself in the list of
912     *                 returned rows.
913     *      MDTREEOP_DESCENDANTS (0x10) returns all of the descendants.
914     *   CUBE_SOURCE (Optional) A bitmap with one of the
915     *      following valid values:
916     *        1 CUBE
917     *        2 DIMENSION
918     *      Default restriction is a value of 1.
919     *
920     * Not supported
921     */
922    MDSCHEMA_MEMBERS(
923        18, null,
924        new Column[] {
925            MdschemaMembersRowset.CatalogName,
926            MdschemaMembersRowset.SchemaName,
927            MdschemaMembersRowset.CubeName,
928            MdschemaMembersRowset.DimensionUniqueName,
929            MdschemaMembersRowset.HierarchyUniqueName,
930            MdschemaMembersRowset.LevelUniqueName,
931            MdschemaMembersRowset.LevelNumber,
932            MdschemaMembersRowset.MemberOrdinal,
933            MdschemaMembersRowset.MemberName,
934            MdschemaMembersRowset.MemberUniqueName,
935            MdschemaMembersRowset.MemberType,
936            MdschemaMembersRowset.MemberGuid,
937            MdschemaMembersRowset.MemberCaption,
938            MdschemaMembersRowset.ChildrenCardinality,
939            MdschemaMembersRowset.ParentLevel,
940            MdschemaMembersRowset.ParentUniqueName,
941            MdschemaMembersRowset.ParentCount,
942            MdschemaMembersRowset.TreeOp_,
943            MdschemaMembersRowset.Depth,
944        },
945        new Column[] {
946            MdschemaMembersRowset.CatalogName,
947            MdschemaMembersRowset.SchemaName,
948            MdschemaMembersRowset.CubeName,
949            MdschemaMembersRowset.DimensionUniqueName,
950            MdschemaMembersRowset.HierarchyUniqueName,
951            MdschemaMembersRowset.LevelUniqueName,
952            MdschemaMembersRowset.LevelNumber,
953            MdschemaMembersRowset.MemberOrdinal,
954        })
955    {
956        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
957            return new MdschemaMembersRowset(request, handler);
958        }
959    },
960
961    /**
962     * http://msdn2.microsoft.com/en-us/library/ms126309(SQL.90).aspx
963     *
964     * restrictions
965     *    CATALOG_NAME Mandatory
966     *    SCHEMA_NAME Optional
967     *    CUBE_NAME Optional
968     *    DIMENSION_UNIQUE_NAME Optional
969     *    HIERARCHY_UNIQUE_NAME Optional
970     *    LEVEL_UNIQUE_NAME Optional
971     *
972     *    MEMBER_UNIQUE_NAME Optional
973     *    PROPERTY_NAME Optional
974     *    PROPERTY_TYPE Optional
975     *    PROPERTY_CONTENT_TYPE
976     *       (Optional) A default restriction is in place on MDPROP_MEMBER
977     *       OR MDPROP_CELL.
978     *    PROPERTY_ORIGIN
979     *       (Optional) A default restriction is in place on MD_USER_DEFINED
980     *       OR MD_SYSTEM_ENABLED
981     *    CUBE_SOURCE
982     *       (Optional) A bitmap with one of the following valid values:
983     *       1 CUBE
984     *       2 DIMENSION
985     *       Default restriction is a value of 1.
986     *    PROPERTY_VISIBILITY
987     *       (Optional) A bitmap with one of the following valid values:
988     *       1 Visible
989     *       2 Not visible
990     *       Default restriction is a value of 1.
991     *
992     * Not supported
993     *    PROPERTY_ORIGIN
994     *    CUBE_SOURCE
995     *    PROPERTY_VISIBILITY
996     *    CHARACTER_MAXIMUM_LENGTH
997     *    CHARACTER_OCTET_LENGTH
998     *    NUMERIC_PRECISION
999     *    NUMERIC_SCALE
1000     *    DESCRIPTION
1001     *    SQL_COLUMN_NAME
1002     *    LANGUAGE
1003     *    PROPERTY_ATTRIBUTE_HIERARCHY_NAME
1004     *    PROPERTY_CARDINALITY
1005     *    MIME_TYPE
1006     *    PROPERTY_IS_VISIBLE
1007     */
1008    MDSCHEMA_PROPERTIES(
1009        19, null,
1010        new Column[] {
1011            MdschemaPropertiesRowset.CatalogName,
1012            MdschemaPropertiesRowset.SchemaName,
1013            MdschemaPropertiesRowset.CubeName,
1014            MdschemaPropertiesRowset.DimensionUniqueName,
1015            MdschemaPropertiesRowset.HierarchyUniqueName,
1016            MdschemaPropertiesRowset.LevelUniqueName,
1017            MdschemaPropertiesRowset.MemberUniqueName,
1018            MdschemaPropertiesRowset.PropertyName,
1019            MdschemaPropertiesRowset.PropertyCaption,
1020            MdschemaPropertiesRowset.PropertyType,
1021            MdschemaPropertiesRowset.DataType,
1022            MdschemaPropertiesRowset.PropertyContentType,
1023            MdschemaPropertiesRowset.Description
1024        },
1025        null /* not sorted */)
1026    {
1027        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
1028            return new MdschemaPropertiesRowset(request, handler);
1029        }
1030    },
1031
1032    /**
1033     * http://msdn2.microsoft.com/en-us/library/ms126290(SQL.90).aspx
1034     *
1035     * restrictions
1036     *    CATALOG_NAME Optional.
1037     *    SCHEMA_NAME Optional.
1038     *    CUBE_NAME Optional.
1039     *    SET_NAME Optional.
1040     *    SCOPE Optional.
1041     *    HIERARCHY_UNIQUE_NAME Optional.
1042     *    CUBE_SOURCE Optional.
1043     *        Note: Only one hierarchy can be included, and only those named
1044     *        sets whose hierarchies exactly match the restriction are
1045     *        returned.
1046     *
1047     * Not supported
1048     *    EXPRESSION
1049     *    DIMENSIONS
1050     *    SET_DISPLAY_FOLDER
1051     */
1052    MDSCHEMA_SETS(
1053        20, null,
1054        new Column[] {
1055            MdschemaSetsRowset.CatalogName,
1056            MdschemaSetsRowset.SchemaName,
1057            MdschemaSetsRowset.CubeName,
1058            MdschemaSetsRowset.SetName,
1059            MdschemaSetsRowset.Scope,
1060        },
1061        new Column[] {
1062            MdschemaSetsRowset.CatalogName,
1063            MdschemaSetsRowset.SchemaName,
1064            MdschemaSetsRowset.CubeName,
1065        })
1066    {
1067        public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
1068            return new MdschemaSetsRowset(request, handler);
1069        }
1070    };
1071
1072    transient final Column[] columnDefinitions;
1073    transient final Column[] sortColumnDefinitions;
1074
1075    /**
1076     * Date the schema was last modified.
1077     *
1078     * <p>TODO: currently schema grammar does not support modify date
1079     * so we return just some date for now.
1080     */
1081    private static final String dateModified = "2005-01-25T17:35:32";
1082    private final String description;
1083
1084    static final String UUID_PATTERN =
1085        "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-"
1086        + "[0-9a-fA-F]{12}";
1087
1088    /**
1089     * Creates a rowset definition.
1090     *
1091     * @param ordinal Rowset ordinal, per OLE DB for OLAP
1092     * @param description Description
1093     * @param columnDefinitions List of column definitions
1094     * @param sortColumnDefinitions List of column definitions to sort on,
1095     */
1096    RowsetDefinition(
1097        int ordinal,
1098        String description,
1099        Column[] columnDefinitions,
1100        Column[] sortColumnDefinitions)
1101    {
1102        Util.discard(ordinal);
1103        this.description = description;
1104        this.columnDefinitions = columnDefinitions;
1105        this.sortColumnDefinitions = sortColumnDefinitions;
1106    }
1107
1108    public abstract Rowset getRowset(XmlaRequest request, XmlaHandler handler);
1109
1110    public Column lookupColumn(String name) {
1111        for (Column columnDefinition : columnDefinitions) {
1112            if (columnDefinition.name.equals(name)) {
1113                return columnDefinition;
1114            }
1115        }
1116        return null;
1117    }
1118
1119    /**
1120     * Returns a comparator with which to sort rows of this rowset definition.
1121     * The sort order is defined by the {@link #sortColumnDefinitions} field.
1122     * If the rowset is not sorted, returns null.
1123     */
1124    Comparator<Rowset.Row> getComparator() {
1125        if (sortColumnDefinitions == null) {
1126            return null;
1127        }
1128        return new Comparator<Rowset.Row>() {
1129            public int compare(Rowset.Row row1, Rowset.Row row2) {
1130                // A faster implementation is welcome.
1131                for (Column sortColumn : sortColumnDefinitions) {
1132                    Comparable val1 = (Comparable) row1.get(sortColumn.name);
1133                    Comparable val2 = (Comparable) row2.get(sortColumn.name);
1134                    if ((val1 == null) && (val2 == null)) {
1135                        // columns can be optional, compare next column
1136                        continue;
1137                    } else if (val1 == null) {
1138                        return -1;
1139                    } else if (val2 == null) {
1140                        return 1;
1141                    } else if (val1 instanceof String
1142                       && val2 instanceof String)
1143                    {
1144                        int v =
1145                            ((String) val1).compareToIgnoreCase((String) val2);
1146                        // if equal (= 0), compare next column
1147                        if (v != 0) {
1148                            return v;
1149                        }
1150                    } else {
1151                        int v = val1.compareTo(val2);
1152                        // if equal (= 0), compare next column
1153                        if (v != 0) {
1154                            return v;
1155                        }
1156                    }
1157                }
1158                return 0;
1159            }
1160        };
1161    }
1162
1163    /**
1164     * Generates an XML schema description to the writer.
1165     * This is broken into top, Row definition and bottom so that on a
1166     * case by case basis a RowsetDefinition can redefine the Row
1167     * definition output. The default assumes a flat set of elements, but
1168     * for example, SchemaRowsets has a element with child elements.
1169     *
1170     * @param writer SAX writer
1171     * @see XmlaHandler#writeDatasetXmlSchema(SaxWriter, mondrian.xmla.XmlaHandler.SetType)
1172     */
1173    void writeRowsetXmlSchema(SaxWriter writer) {
1174        writeRowsetXmlSchemaTop(writer);
1175        writeRowsetXmlSchemaRowDef(writer);
1176        writeRowsetXmlSchemaBottom(writer);
1177    }
1178
1179    protected void writeRowsetXmlSchemaTop(SaxWriter writer) {
1180        writer.startElement(
1181            "xsd:schema",
1182            "xmlns:xsd", NS_XSD,
1183            "xmlns", NS_XMLA_ROWSET,
1184            "xmlns:xsi", NS_XSI,
1185            "xmlns:sql", "urn:schemas-microsoft-com:xml-sql",
1186            "targetNamespace", NS_XMLA_ROWSET,
1187            "elementFormDefault", "qualified");
1188
1189        writer.startElement(
1190            "xsd:element",
1191            "name", "root");
1192        writer.startElement("xsd:complexType");
1193        writer.startElement("xsd:sequence");
1194        writer.element(
1195            "xsd:element",
1196            "name", "row",
1197            "type", "row",
1198            "minOccurs", 0,
1199            "maxOccurs", "unbounded");
1200        writer.endElement(); // xsd:sequence
1201        writer.endElement(); // xsd:complexType
1202        writer.endElement(); // xsd:element
1203
1204        // MS SQL includes this in its schema section even thought
1205        // its not need for most queries.
1206        writer.startElement(
1207            "xsd:simpleType",
1208            "name", "uuid");
1209        writer.startElement(
1210            "xsd:restriction",
1211            "base", "xsd:string");
1212        writer.element(
1213            "xsd:pattern",
1214            "value", UUID_PATTERN);
1215
1216        writer.endElement(); // xsd:restriction
1217        writer.endElement(); // xsd:simpleType
1218    }
1219
1220    protected void writeRowsetXmlSchemaRowDef(SaxWriter writer) {
1221        writer.startElement(
1222            "xsd:complexType",
1223            "name", "row");
1224        writer.startElement("xsd:sequence");
1225        for (Column column : columnDefinitions) {
1226            final String name =
1227                XmlaUtil.ElementNameEncoder.INSTANCE.encode(column.name);
1228            final String xsdType = column.type.columnType;
1229
1230            Object[] attrs;
1231            if (column.nullable) {
1232                if (column.unbounded) {
1233                    attrs = new Object[]{
1234                        "sql:field", column.name,
1235                        "name", name,
1236                        "type", xsdType,
1237                        "minOccurs", 0,
1238                        "maxOccurs", "unbounded"
1239                    };
1240                } else {
1241                    attrs = new Object[]{
1242                        "sql:field", column.name,
1243                        "name", name,
1244                        "type", xsdType,
1245                        "minOccurs", 0
1246                    };
1247                }
1248            } else {
1249                if (column.unbounded) {
1250                    attrs = new Object[]{
1251                        "sql:field", column.name,
1252                        "name", name,
1253                        "type", xsdType,
1254                        "maxOccurs", "unbounded"
1255                    };
1256                } else {
1257                    attrs = new Object[]{
1258                        "sql:field", column.name,
1259                        "name", name,
1260                        "type", xsdType
1261                    };
1262                }
1263            }
1264            writer.element("xsd:element", attrs);
1265        }
1266        writer.endElement(); // xsd:sequence
1267        writer.endElement(); // xsd:complexType
1268    }
1269
1270    protected void writeRowsetXmlSchemaBottom(SaxWriter writer) {
1271        writer.endElement(); // xsd:schema
1272    }
1273
1274    enum Type {
1275        String("xsd:string"),
1276        StringArray("xsd:string"),
1277        Array("xsd:string"),
1278        Enumeration("xsd:string"),
1279        EnumerationArray("xsd:string"),
1280        EnumString("xsd:string"),
1281        Boolean("xsd:boolean"),
1282        StringSometimesArray("xsd:string"),
1283        Integer("xsd:int"),
1284        UnsignedInteger("xsd:unsignedInt"),
1285        DateTime("xsd:dateTime"),
1286        Rowset(null),
1287        Short("xsd:short"),
1288        UUID("uuid"),
1289        UnsignedShort("xsd:unsignedShort"),
1290        Long("xsd:long"),
1291        UnsignedLong("xsd:unsignedLong");
1292
1293        public final String columnType;
1294
1295        Type(String columnType) {
1296            this.columnType = columnType;
1297        }
1298
1299        boolean isEnum() {
1300            return this == Enumeration
1301               || this == EnumerationArray
1302               || this == EnumString;
1303        }
1304
1305        String getName() {
1306            return this == String ? "string" : name();
1307        }
1308    }
1309
1310    private static XmlaConstants.DBType getDBTypeFromProperty(Property prop) {
1311        switch (prop.getDatatype()) {
1312        case STRING:
1313            return XmlaConstants.DBType.WSTR;
1314        case INTEGER:
1315        case UNSIGNED_INTEGER:
1316        case DOUBLE:
1317            return XmlaConstants.DBType.R8;
1318        case BOOLEAN:
1319            return XmlaConstants.DBType.BOOL;
1320        default:
1321            // TODO: what type is it really, its not a string
1322            return XmlaConstants.DBType.WSTR;
1323        }
1324    }
1325
1326    static class Column {
1327
1328        /**
1329         * This is used as the true value for the restriction parameter.
1330         */
1331        static final boolean RESTRICTION = true;
1332        /**
1333         * This is used as the false value for the restriction parameter.
1334         */
1335        static final boolean NOT_RESTRICTION = false;
1336
1337        /**
1338         * This is used as the false value for the nullable parameter.
1339         */
1340        static final boolean REQUIRED = false;
1341        /**
1342         * This is used as the true value for the nullable parameter.
1343         */
1344        static final boolean OPTIONAL = true;
1345
1346        /**
1347         * This is used as the false value for the unbounded parameter.
1348         */
1349        static final boolean ONE_MAX = false;
1350        /**
1351         * This is used as the true value for the unbounded parameter.
1352         */
1353        static final boolean UNBOUNDED = true;
1354
1355        final String name;
1356        final Type type;
1357        final Enumeration enumeration;
1358        final String description;
1359        final boolean restriction;
1360        final boolean nullable;
1361        final boolean unbounded;
1362
1363        /**
1364         * Creates a column.
1365         *
1366         * @param name Name of column
1367         * @param type A {@link mondrian.xmla.RowsetDefinition.Type} value
1368         * @param enumeratedType Must be specified for enumeration or array
1369         *                       of enumerations
1370         * @param description Description of column
1371         * @param restriction Whether column can be used as a filter on its
1372         *     rowset
1373         * @param nullable Whether column can contain null values
1374         * @pre type != null
1375         * @pre (type == Type.Enumeration
1376         *  || type == Type.EnumerationArray
1377         *  || type == Type.EnumString)
1378         *  == (enumeratedType != null)
1379         * @pre description == null || description.indexOf('\r') == -1
1380         */
1381        Column(
1382            String name,
1383            Type type,
1384            Enumeration enumeratedType,
1385            boolean restriction,
1386            boolean nullable,
1387            String description)
1388        {
1389            this(
1390                name, type, enumeratedType,
1391                restriction, nullable, ONE_MAX, description);
1392        }
1393
1394        Column(
1395            String name,
1396            Type type,
1397            Enumeration enumeratedType,
1398            boolean restriction,
1399            boolean nullable,
1400            boolean unbounded,
1401            String description)
1402        {
1403            assert type != null;
1404            assert (type == Type.Enumeration
1405                    || type == Type.EnumerationArray
1406                    || type == Type.EnumString)
1407                   == (enumeratedType != null);
1408            // Line endings must be UNIX style (LF) not Windows style (LF+CR).
1409            // Thus the client will receive the same XML, regardless
1410            // of the server O/S.
1411            assert description == null || description.indexOf('\r') == -1;
1412            this.name = name;
1413            this.type = type;
1414            this.enumeration = enumeratedType;
1415            this.description = description;
1416            this.restriction = restriction;
1417            this.nullable = nullable;
1418            this.unbounded = unbounded;
1419        }
1420
1421        /**
1422         * Retrieves a value of this column from a row. The base implementation
1423         * uses reflection to call an accessor method; a derived class may
1424         * provide a different implementation.
1425         *
1426         * @param row Row
1427         */
1428        protected Object get(Object row) {
1429            return getFromAccessor(row);
1430        }
1431
1432        /**
1433         * Retrieves the value of this column "MyColumn" from a field called
1434         * "myColumn".
1435         *
1436         * @param row Current row
1437         * @return Value of given this property of the given row
1438         */
1439        protected final Object getFromField(Object row) {
1440            try {
1441                String javaFieldName =
1442                    name.substring(0, 1).toLowerCase()
1443                    + name.substring(1);
1444                Field field = row.getClass().getField(javaFieldName);
1445                return field.get(row);
1446            } catch (NoSuchFieldException e) {
1447                throw Util.newInternal(
1448                    e, "Error while accessing rowset column " + name);
1449            } catch (SecurityException e) {
1450                throw Util.newInternal(
1451                    e, "Error while accessing rowset column " + name);
1452            } catch (IllegalAccessException e) {
1453                throw Util.newInternal(
1454                    e, "Error while accessing rowset column " + name);
1455            }
1456        }
1457
1458        /**
1459         * Retrieves the value of this column "MyColumn" by calling a method
1460         * called "getMyColumn()".
1461         *
1462         * @param row Current row
1463         * @return Value of given this property of the given row
1464         */
1465        protected final Object getFromAccessor(Object row) {
1466            try {
1467                String javaMethodName = "get" + name;
1468                Method method = row.getClass().getMethod(javaMethodName);
1469                return method.invoke(row);
1470            } catch (SecurityException e) {
1471                throw Util.newInternal(
1472                    e, "Error while accessing rowset column " + name);
1473            } catch (IllegalAccessException e) {
1474                throw Util.newInternal(
1475                    e, "Error while accessing rowset column " + name);
1476            } catch (NoSuchMethodException e) {
1477                throw Util.newInternal(
1478                    e, "Error while accessing rowset column " + name);
1479            } catch (InvocationTargetException e) {
1480                throw Util.newInternal(
1481                    e, "Error while accessing rowset column " + name);
1482            }
1483        }
1484
1485        public String getColumnType() {
1486            if (type.isEnum()) {
1487                return enumeration.type.columnType;
1488            }
1489            return type.columnType;
1490        }
1491    }
1492
1493    // -------------------------------------------------------------------------
1494    // From this point on, just rowset classess.
1495
1496    static class DiscoverDatasourcesRowset extends Rowset {
1497        private static final Column DataSourceName =
1498            new Column(
1499                "DataSourceName",
1500                Type.String,
1501                null,
1502                Column.RESTRICTION,
1503                Column.REQUIRED,
1504                "The name of the data source, such as FoodMart 2000.");
1505        private static final Column DataSourceDescription =
1506            new Column(
1507                "DataSourceDescription",
1508                Type.String,
1509                null,
1510                Column.NOT_RESTRICTION,
1511                Column.OPTIONAL,
1512                "A description of the data source, as entered by the "
1513                + "publisher.");
1514        private static final Column URL =
1515            new Column(
1516                "URL",
1517                Type.String,
1518                null,
1519                Column.RESTRICTION,
1520                Column.OPTIONAL,
1521                "The unique path that shows where to invoke the XML for "
1522                + "Analysis methods for that data source.");
1523        private static final Column DataSourceInfo =
1524            new Column(
1525                "DataSourceInfo",
1526                Type.String,
1527                null,
1528                Column.NOT_RESTRICTION,
1529                Column.OPTIONAL,
1530                "A string containing any additional information required to "
1531                + "connect to the data source. This can include the Initial "
1532                + "Catalog property or other information for the provider.\n"
1533                + "Example: \"Provider=MSOLAP;Data Source=Local;\"");
1534        private static final Column ProviderName =
1535            new Column(
1536                "ProviderName",
1537                Type.String,
1538                null,
1539                Column.RESTRICTION,
1540                Column.OPTIONAL,
1541                "The name of the provider behind the data source.\n"
1542                + "Example: \"MSDASQL\"");
1543        private static final Column ProviderType =
1544            new Column(
1545                "ProviderType",
1546                Type.EnumerationArray,
1547                Enumeration.PROVIDER_TYPE,
1548                Column.RESTRICTION,
1549                Column.REQUIRED,
1550                Column.UNBOUNDED,
1551                "The types of data supported by the provider. May include one "
1552                + "or more of the following types. Example follows this "
1553                + "table.\n"
1554                + "TDP: tabular data provider.\n"
1555                + "MDP: multidimensional data provider.\n"
1556                + "DMP: data mining provider. A DMP provider implements the "
1557                + "OLE DB for Data Mining specification.");
1558        private static final Column AuthenticationMode =
1559            new Column(
1560                "AuthenticationMode",
1561                Type.EnumString,
1562                Enumeration.AUTHENTICATION_MODE,
1563                Column.RESTRICTION,
1564                Column.REQUIRED,
1565                "Specification of what type of security mode the data source "
1566                + "uses. Values can be one of the following:\n"
1567                + "Unauthenticated: no user ID or password needs to be sent.\n"
1568                + "Authenticated: User ID and Password must be included in the "
1569                + "information required for the connection.\n"
1570                + "Integrated: the data source uses the underlying security to "
1571                + "determine authorization, such as Integrated Security "
1572                + "provided by Microsoft Internet Information Services (IIS).");
1573
1574        public DiscoverDatasourcesRowset(
1575            XmlaRequest request, XmlaHandler handler)
1576        {
1577            super(DISCOVER_DATASOURCES, request, handler);
1578        }
1579
1580        private static final Column[] columns = {
1581            DataSourceName,
1582            DataSourceDescription,
1583            URL,
1584            DataSourceInfo,
1585            ProviderName,
1586            ProviderType,
1587            AuthenticationMode,
1588        };
1589
1590        public void populateImpl(
1591            XmlaResponse response, OlapConnection connection, List<Row> rows)
1592            throws XmlaException, SQLException
1593        {
1594            if (needConnection()) {
1595                final XmlaHandler.XmlaExtra extra = getExtra(connection);
1596                for (Map<String, Object> ds : extra.getDataSources(connection))
1597                {
1598                    Row row = new Row();
1599                    for (Column column : columns) {
1600                        row.set(column.name, ds.get(column.name));
1601                    }
1602                    addRow(row, rows);
1603                }
1604            } else {
1605                // using pre-configured discover datasources response
1606                Row row = new Row();
1607                Map<String, Object> map =
1608                    this.handler.connectionFactory
1609                        .getPreConfiguredDiscoverDatasourcesResponse();
1610                for (Column column : columns) {
1611                    row.set(column.name, map.get(column.name));
1612                }
1613                addRow(row, rows);
1614            }
1615        }
1616
1617        @Override
1618        protected boolean needConnection() {
1619            // If the olap connection factory has a pre configured response,
1620            // we don't need to connect to find metadata. This is good.
1621            return this.handler.connectionFactory
1622                       .getPreConfiguredDiscoverDatasourcesResponse() == null;
1623        }
1624
1625        protected void setProperty(
1626            PropertyDefinition propertyDef,
1627            String value)
1628        {
1629            switch (propertyDef) {
1630            case Content:
1631                break;
1632            default:
1633                super.setProperty(propertyDef, value);
1634            }
1635        }
1636    }
1637
1638    static class DiscoverSchemaRowsetsRowset extends Rowset {
1639        private static final Column SchemaName =
1640            new Column(
1641                "SchemaName",
1642                Type.StringArray,
1643                null,
1644                Column.RESTRICTION,
1645                Column.REQUIRED,
1646                "The name of the schema/request. This returns the values in "
1647                + "the RequestTypes enumeration, plus any additional types "
1648                + "supported by the provider. The provider defines rowset "
1649                + "structures for the additional types");
1650        private static final Column SchemaGuid =
1651            new Column(
1652                "SchemaGuid",
1653                Type.UUID,
1654                null,
1655                Column.NOT_RESTRICTION,
1656                Column.OPTIONAL,
1657                "The GUID of the schema.");
1658        private static final Column Restrictions =
1659            new Column(
1660                "Restrictions",
1661                Type.Array,
1662                null,
1663                Column.NOT_RESTRICTION,
1664                Column.REQUIRED,
1665                "An array of the restrictions suppoted by provider. An example "
1666                + "follows this table.");
1667        private static final Column Description =
1668            new Column(
1669                "Description",
1670                Type.String,
1671                null,
1672                Column.NOT_RESTRICTION,
1673                Column.REQUIRED,
1674                "A localizable description of the schema");
1675
1676        public DiscoverSchemaRowsetsRowset(
1677            XmlaRequest request, XmlaHandler handler)
1678        {
1679            super(DISCOVER_SCHEMA_ROWSETS, request, handler);
1680        }
1681
1682        public void populateImpl(
1683            XmlaResponse response, OlapConnection connection, List<Row> rows)
1684            throws XmlaException
1685        {
1686            RowsetDefinition[] rowsetDefinitions =
1687                RowsetDefinition.class.getEnumConstants().clone();
1688            Arrays.sort(
1689                rowsetDefinitions,
1690                new Comparator<RowsetDefinition>() {
1691                    public int compare(
1692                        RowsetDefinition o1,
1693                        RowsetDefinition o2)
1694                    {
1695                        return o1.name().compareTo(o2.name());
1696                    }
1697                });
1698            for (RowsetDefinition rowsetDefinition : rowsetDefinitions) {
1699                Row row = new Row();
1700                row.set(SchemaName.name, rowsetDefinition.name());
1701
1702                // TODO: If we have a SchemaGuid output here
1703                //row.set(SchemaGuid.name, "");
1704
1705                row.set(Restrictions.name, getRestrictions(rowsetDefinition));
1706
1707                String desc = rowsetDefinition.getDescription();
1708                row.set(Description.name, (desc == null) ? "" : desc);
1709                addRow(row, rows);
1710            }
1711        }
1712
1713        private List<XmlElement> getRestrictions(
1714            RowsetDefinition rowsetDefinition)
1715        {
1716            List<XmlElement> restrictionList = new ArrayList<XmlElement>();
1717            final Column[] columns = rowsetDefinition.columnDefinitions;
1718            for (Column column : columns) {
1719                if (column.restriction) {
1720                    restrictionList.add(
1721                        new XmlElement(
1722                            Restrictions.name,
1723                            null,
1724                            new XmlElement[]{
1725                                new XmlElement("Name", null, column.name),
1726                                new XmlElement(
1727                                    "Type",
1728                                    null,
1729                                    column.getColumnType())}));
1730                }
1731            }
1732            return restrictionList;
1733        }
1734
1735        protected void setProperty(
1736            PropertyDefinition propertyDef, String value)
1737        {
1738            switch (propertyDef) {
1739            case Content:
1740                break;
1741            default:
1742                super.setProperty(propertyDef, value);
1743            }
1744        }
1745    }
1746
1747    public String getDescription() {
1748        return description;
1749    }
1750
1751    static class DiscoverPropertiesRowset extends Rowset {
1752        private final Util.Functor1<Boolean, PropertyDefinition> propNameCond;
1753
1754        DiscoverPropertiesRowset(XmlaRequest request, XmlaHandler handler) {
1755            super(DISCOVER_PROPERTIES, request, handler);
1756            propNameCond = makeCondition(PROPDEF_NAME_GETTER, PropertyName);
1757        }
1758
1759        private static final Column PropertyName =
1760            new Column(
1761                "PropertyName",
1762                Type.StringSometimesArray,
1763                null,
1764                Column.RESTRICTION,
1765                Column.REQUIRED,
1766                "The name of the property.");
1767        private static final Column PropertyDescription =
1768            new Column(
1769                "PropertyDescription",
1770                Type.String,
1771                null,
1772                Column.NOT_RESTRICTION,
1773                Column.REQUIRED,
1774                "A localizable text description of the property.");
1775        private static final Column PropertyType =
1776            new Column(
1777                "PropertyType",
1778                Type.String,
1779                null,
1780                Column.NOT_RESTRICTION,
1781                Column.REQUIRED,
1782                "The XML data type of the property.");
1783        private static final Column PropertyAccessType =
1784            new Column(
1785                "PropertyAccessType",
1786                Type.EnumString,
1787                Enumeration.ACCESS,
1788                Column.NOT_RESTRICTION,
1789                Column.REQUIRED,
1790                "Access for the property. The value can be Read, Write, or "
1791                + "ReadWrite.");
1792        private static final Column IsRequired =
1793            new Column(
1794                "IsRequired",
1795                Type.Boolean,
1796                null,
1797                Column.NOT_RESTRICTION,
1798                Column.REQUIRED,
1799                "True if a property is required, false if it is not required.");
1800        private static final Column Value =
1801            new Column(
1802                "Value",
1803                Type.String,
1804                null,
1805                Column.NOT_RESTRICTION,
1806                Column.REQUIRED,
1807                "The current value of the property.");
1808
1809        protected boolean needConnection() {
1810            return false;
1811        }
1812
1813        public void populateImpl(
1814            XmlaResponse response, OlapConnection connection, List<Row> rows)
1815            throws XmlaException
1816        {
1817            for (PropertyDefinition propertyDefinition
1818                : PropertyDefinition.class.getEnumConstants())
1819            {
1820                if (!propNameCond.apply(propertyDefinition)) {
1821                    continue;
1822                }
1823                Row row = new Row();
1824                row.set(PropertyName.name, propertyDefinition.name());
1825                row.set(
1826                    PropertyDescription.name, propertyDefinition.description);
1827                row.set(PropertyType.name, propertyDefinition.type.getName());
1828                row.set(PropertyAccessType.name, propertyDefinition.access);
1829                row.set(IsRequired.name, false);
1830                row.set(Value.name, propertyDefinition.value);
1831                addRow(row, rows);
1832            }
1833        }
1834
1835        protected void setProperty(
1836            PropertyDefinition propertyDef, String value)
1837        {
1838            switch (propertyDef) {
1839            case Content:
1840                break;
1841            default:
1842                super.setProperty(propertyDef, value);
1843            }
1844        }
1845    }
1846
1847    static class DiscoverEnumeratorsRowset extends Rowset {
1848        DiscoverEnumeratorsRowset(XmlaRequest request, XmlaHandler handler) {
1849            super(DISCOVER_ENUMERATORS, request, handler);
1850        }
1851
1852        private static final Column EnumName =
1853            new Column(
1854                "EnumName",
1855                Type.StringArray,
1856                null,
1857                Column.RESTRICTION,
1858                Column.REQUIRED,
1859                "The name of the enumerator that contains a set of values.");
1860        private static final Column EnumDescription =
1861            new Column(
1862                "EnumDescription",
1863                Type.String,
1864                null,
1865                Column.NOT_RESTRICTION,
1866                Column.OPTIONAL,
1867                "A localizable description of the enumerator.");
1868        private static final Column EnumType =
1869            new Column(
1870                "EnumType",
1871                Type.String,
1872                null,
1873                Column.NOT_RESTRICTION,
1874                Column.REQUIRED,
1875                "The data type of the Enum values.");
1876        private static final Column ElementName =
1877            new Column(
1878                "ElementName",
1879                Type.String,
1880                null,
1881                Column.NOT_RESTRICTION,
1882                Column.REQUIRED,
1883                "The name of one of the value elements in the enumerator set.\n"
1884                + "Example: TDP");
1885        private static final Column ElementDescription =
1886            new Column(
1887                "ElementDescription",
1888                Type.String,
1889                null,
1890                Column.NOT_RESTRICTION,
1891                Column.OPTIONAL,
1892                "A localizable description of the element (optional).");
1893        private static final Column ElementValue =
1894            new Column(
1895                "ElementValue",
1896                Type.String,
1897                null,
1898                Column.NOT_RESTRICTION,
1899                Column.OPTIONAL,
1900                "The value of the element.\n" + "Example: 01");
1901
1902        public void populateImpl(
1903            XmlaResponse response, OlapConnection connection, List<Row> rows)
1904            throws XmlaException
1905        {
1906            List<Enumeration> enumerators = getEnumerators();
1907            for (Enumeration enumerator : enumerators) {
1908                final List<? extends Enum> values = enumerator.getValues();
1909                for (Enum<?> value : values) {
1910                    Row row = new Row();
1911                    row.set(EnumName.name, enumerator.name);
1912                    row.set(EnumDescription.name, enumerator.description);
1913
1914                    // Note: SQL Server always has EnumType string
1915                    // Need type of element of array, not the array
1916                    // it self.
1917                    row.set(EnumType.name, "string");
1918
1919                    final String name =
1920                        (value instanceof XmlaConstant)
1921                            ? ((XmlaConstant) value).xmlaName()
1922                            : value.name();
1923                    row.set(ElementName.name, name);
1924
1925                    final String description =
1926                     (value instanceof XmlaConstant)
1927                        ? ((XmlaConstant) value).getDescription()
1928                         : (value instanceof XmlaConstants.EnumWithDesc)
1929                        ? ((XmlaConstants.EnumWithDesc) value).getDescription()
1930                             : null;
1931                    if (description != null) {
1932                        row.set(
1933                            ElementDescription.name,
1934                            description);
1935                    }
1936
1937                    switch (enumerator.type) {
1938                    case String:
1939                    case StringArray:
1940                        // these don't have ordinals
1941                        break;
1942                    default:
1943                        final int ordinal =
1944                            (value instanceof XmlaConstant
1945                             && ((XmlaConstant) value).xmlaOrdinal() != -1)
1946                                ? ((XmlaConstant) value).xmlaOrdinal()
1947                                : value.ordinal();
1948                        row.set(ElementValue.name, ordinal);
1949                        break;
1950                    }
1951                    addRow(row, rows);
1952                }
1953            }
1954        }
1955
1956        private static List<Enumeration> getEnumerators() {
1957            // Build a set because we need to eliminate duplicates.
1958            SortedSet<Enumeration> enumeratorSet = new TreeSet<Enumeration>(
1959                new Comparator<Enumeration>() {
1960                    public int compare(Enumeration o1, Enumeration o2) {
1961                        return o1.name.compareTo(o2.name);
1962                    }
1963                }
1964            );
1965            for (RowsetDefinition rowsetDefinition
1966                : RowsetDefinition.class.getEnumConstants())
1967            {
1968                for (Column column : rowsetDefinition.columnDefinitions) {
1969                    if (column.enumeration != null) {
1970                        enumeratorSet.add(column.enumeration);
1971                    }
1972                }
1973            }
1974            return new ArrayList<Enumeration>(enumeratorSet);
1975        }
1976
1977        protected void setProperty(
1978            PropertyDefinition propertyDef, String value)
1979        {
1980            switch (propertyDef) {
1981            case Content:
1982                break;
1983            default:
1984                super.setProperty(propertyDef, value);
1985            }
1986        }
1987    }
1988
1989    static class DiscoverKeywordsRowset extends Rowset {
1990        DiscoverKeywordsRowset(XmlaRequest request, XmlaHandler handler) {
1991            super(DISCOVER_KEYWORDS, request, handler);
1992        }
1993
1994        private static final Column Keyword =
1995            new Column(
1996                "Keyword",
1997                Type.StringSometimesArray,
1998                null,
1999                Column.RESTRICTION,
2000                Column.REQUIRED,
2001                "A list of all the keywords reserved by a provider.\n"
2002                + "Example: AND");
2003
2004        public void populateImpl(
2005            XmlaResponse response, OlapConnection connection, List<Row> rows)
2006            throws XmlaException
2007        {
2008            MondrianServer mondrianServer = MondrianServer.forId(null);
2009            for (String keyword : mondrianServer.getKeywords()) {
2010                Row row = new Row();
2011                row.set(Keyword.name, keyword);
2012                addRow(row, rows);
2013            }
2014        }
2015
2016        protected void setProperty(
2017            PropertyDefinition propertyDef,
2018            String value)
2019        {
2020            switch (propertyDef) {
2021            case Content:
2022                break;
2023            default:
2024                super.setProperty(propertyDef, value);
2025            }
2026        }
2027    }
2028
2029    static class DiscoverLiteralsRowset extends Rowset {
2030        DiscoverLiteralsRowset(XmlaRequest request, XmlaHandler handler) {
2031            super(DISCOVER_LITERALS, request, handler);
2032        }
2033
2034        private static final Column LiteralName = new Column(
2035            "LiteralName",
2036            Type.StringSometimesArray,
2037            null,
2038            Column.RESTRICTION,
2039            Column.REQUIRED,
2040            "The name of the literal described in the row.\n"
2041            + "Example: DBLITERAL_LIKE_PERCENT");
2042
2043        private static final Column LiteralValue = new Column(
2044            "LiteralValue",
2045            Type.String,
2046            null,
2047            Column.NOT_RESTRICTION,
2048            Column.OPTIONAL,
2049            "Contains the actual literal value.\n"
2050            + "Example, if LiteralName is DBLITERAL_LIKE_PERCENT and the "
2051            + "percent character (%) is used to match zero or more characters "
2052            + "in a LIKE clause, this column's value would be \"%\".");
2053
2054        private static final Column LiteralInvalidChars = new Column(
2055            "LiteralInvalidChars",
2056            Type.String,
2057            null,
2058            Column.NOT_RESTRICTION,
2059            Column.OPTIONAL,
2060            "The characters, in the literal, that are not valid.\n"
2061            + "For example, if table names can contain anything other than a "
2062            + "numeric character, this string would be \"0123456789\".");
2063
2064        private static final Column LiteralInvalidStartingChars = new Column(
2065            "LiteralInvalidStartingChars",
2066            Type.String,
2067            null,
2068            Column.NOT_RESTRICTION,
2069            Column.OPTIONAL,
2070            "The characters that are not valid as the first character of the "
2071            + "literal. If the literal can start with any valid character, "
2072            + "this is null.");
2073
2074        private static final Column LiteralMaxLength = new Column(
2075            "LiteralMaxLength",
2076            Type.Integer,
2077            null,
2078            Column.NOT_RESTRICTION,
2079            Column.OPTIONAL,
2080            "The maximum number of characters in the literal. If there is no "
2081            + "maximum or the maximum is unknown, the value is ?1.");
2082
2083        public void populateImpl(
2084            XmlaResponse response, OlapConnection connection, List<Row> rows)
2085            throws XmlaException
2086        {
2087            populate(
2088                XmlaConstants.Literal.class,
2089                rows,
2090                new Comparator<XmlaConstants.Literal>() {
2091                public int compare(
2092                    XmlaConstants.Literal o1,
2093                    XmlaConstants.Literal o2)
2094                {
2095                    return o1.name().compareTo(o2.name());
2096                }
2097            });
2098        }
2099
2100        protected void setProperty(
2101            PropertyDefinition propertyDef,
2102            String value)
2103        {
2104            switch (propertyDef) {
2105            case Content:
2106                break;
2107            default:
2108                super.setProperty(propertyDef, value);
2109            }
2110        }
2111    }
2112
2113    static class DbschemaCatalogsRowset extends Rowset {
2114        private final Util.Functor1<Boolean, Catalog> catalogNameCond;
2115
2116        DbschemaCatalogsRowset(XmlaRequest request, XmlaHandler handler) {
2117            super(DBSCHEMA_CATALOGS, request, handler);
2118            catalogNameCond = makeCondition(CATALOG_NAME_GETTER, CatalogName);
2119        }
2120
2121        private static final Column CatalogName =
2122            new Column(
2123                "CATALOG_NAME",
2124                Type.String,
2125                null,
2126                Column.RESTRICTION,
2127                Column.REQUIRED,
2128                "Catalog name. Cannot be NULL.");
2129        private static final Column Description =
2130            new Column(
2131                "DESCRIPTION",
2132                Type.String,
2133                null,
2134                Column.NOT_RESTRICTION,
2135                Column.REQUIRED,
2136                "Human-readable description of the catalog.");
2137        private static final Column Roles =
2138            new Column(
2139                "ROLES",
2140                Type.String,
2141                null,
2142                Column.NOT_RESTRICTION,
2143                Column.REQUIRED,
2144                "A comma delimited list of roles to which the current user "
2145                + "belongs. An asterisk (*) is included as a role if the "
2146                + "current user is a server or database administrator. "
2147                + "Username is appended to ROLES if one of the roles uses "
2148                + "dynamic security.");
2149        private static final Column DateModified =
2150            new Column(
2151                "DATE_MODIFIED",
2152                Type.DateTime,
2153                null,
2154                Column.NOT_RESTRICTION,
2155                Column.OPTIONAL,
2156                "The date that the catalog was last modified.");
2157
2158        public void populateImpl(
2159            XmlaResponse response, OlapConnection connection, List<Row> rows)
2160            throws XmlaException, SQLException
2161        {
2162            for (Catalog catalog
2163                : catIter(connection, catNameCond(), catalogNameCond))
2164            {
2165                for (Schema schema : catalog.getSchemas()) {
2166                    Row row = new Row();
2167                    row.set(CatalogName.name, catalog.getName());
2168
2169                    // TODO: currently schema grammar does not support a
2170                    // description
2171                    row.set(Description.name, "No description available");
2172
2173                    // get Role names
2174                    StringBuilder buf = new StringBuilder(100);
2175                    List<String> roleNames =
2176                        getExtra(connection).getSchemaRoleNames(schema);
2177                    serialize(buf, roleNames);
2178                    row.set(Roles.name, buf.toString());
2179
2180                    // TODO: currently schema grammar does not support modify
2181                    // date so we return just some date for now.
2182                    if (false) {
2183                        row.set(DateModified.name, dateModified);
2184                    }
2185                    addRow(row, rows);
2186                }
2187            }
2188        }
2189
2190        protected void setProperty(
2191            PropertyDefinition propertyDef, String value)
2192        {
2193            switch (propertyDef) {
2194            case Content:
2195                break;
2196            default:
2197                super.setProperty(propertyDef, value);
2198            }
2199        }
2200    }
2201
2202    static class DbschemaColumnsRowset extends Rowset {
2203        private final Util.Functor1<Boolean, Catalog> tableCatalogCond;
2204        private final Util.Functor1<Boolean, Cube> tableNameCond;
2205        private final Util.Functor1<Boolean, String> columnNameCond;
2206
2207        DbschemaColumnsRowset(XmlaRequest request, XmlaHandler handler) {
2208            super(DBSCHEMA_COLUMNS, request, handler);
2209            tableCatalogCond = makeCondition(CATALOG_NAME_GETTER, TableCatalog);
2210            tableNameCond = makeCondition(ELEMENT_NAME_GETTER, TableName);
2211            columnNameCond = makeCondition(ColumnName);
2212        }
2213
2214        private static final Column TableCatalog =
2215            new Column(
2216                "TABLE_CATALOG",
2217                Type.String,
2218                null,
2219                Column.RESTRICTION,
2220                Column.REQUIRED,
2221                "The name of the Database.");
2222        private static final Column TableSchema =
2223            new Column(
2224                "TABLE_SCHEMA",
2225                Type.String,
2226                null,
2227                Column.RESTRICTION,
2228                Column.OPTIONAL,
2229                null);
2230        private static final Column TableName =
2231            new Column(
2232                "TABLE_NAME",
2233                Type.String,
2234                null,
2235                Column.RESTRICTION,
2236                Column.REQUIRED,
2237                "The name of the cube.");
2238        private static final Column ColumnName =
2239            new Column(
2240                "COLUMN_NAME",
2241                Type.String,
2242                null,
2243                Column.RESTRICTION,
2244                Column.REQUIRED,
2245                "The name of the attribute hierarchy or measure.");
2246        private static final Column OrdinalPosition =
2247            new Column(
2248                "ORDINAL_POSITION",
2249                Type.UnsignedInteger,
2250                null,
2251                Column.NOT_RESTRICTION,
2252                Column.REQUIRED,
2253                "The position of the column, beginning with 1.");
2254        private static final Column ColumnHasDefault =
2255            new Column(
2256                "COLUMN_HAS_DEFAULT",
2257                Type.Boolean,
2258                null,
2259                Column.NOT_RESTRICTION,
2260                Column.OPTIONAL,
2261                "Not supported.");
2262        /*
2263         *  A bitmask indicating the information stored in
2264         *      DBCOLUMNFLAGS in OLE DB.
2265         *  1 = Bookmark
2266         *  2 = Fixed length
2267         *  4 = Nullable
2268         *  8 = Row versioning
2269         *  16 = Updateable column
2270         *
2271         * And, of course, MS SQL Server sometimes has the value of 80!!
2272        */
2273        private static final Column ColumnFlags =
2274            new Column(
2275                "COLUMN_FLAGS",
2276                Type.UnsignedInteger,
2277                null,
2278                Column.NOT_RESTRICTION,
2279                Column.REQUIRED,
2280                "A DBCOLUMNFLAGS bitmask indicating column properties.");
2281        private static final Column IsNullable =
2282            new Column(
2283                "IS_NULLABLE",
2284                Type.Boolean,
2285                null,
2286                Column.NOT_RESTRICTION,
2287                Column.REQUIRED,
2288                "Always returns false.");
2289        private static final Column DataType =
2290            new Column(
2291                "DATA_TYPE",
2292                Type.UnsignedShort,
2293                null,
2294                Column.NOT_RESTRICTION,
2295                Column.REQUIRED,
2296                "The data type of the column. Returns a string for dimension "
2297                + "columns and a variant for measures.");
2298        private static final Column CharacterMaximumLength =
2299            new Column(
2300                "CHARACTER_MAXIMUM_LENGTH",
2301                Type.UnsignedInteger,
2302                null,
2303                Column.NOT_RESTRICTION,
2304                Column.OPTIONAL,
2305                "The maximum possible length of a value within the column.");
2306        private static final Column CharacterOctetLength =
2307            new Column(
2308                "CHARACTER_OCTET_LENGTH",
2309                Type.UnsignedInteger,
2310                null,
2311                Column.NOT_RESTRICTION,
2312                Column.OPTIONAL,
2313                "The maximum possible length of a value within the column, in "
2314                + "bytes, for character or binary columns.");
2315        private static final Column NumericPrecision =
2316            new Column(
2317                "NUMERIC_PRECISION",
2318                Type.UnsignedShort,
2319                null,
2320                Column.NOT_RESTRICTION,
2321                Column.OPTIONAL,
2322                "The maximum precision of the column for numeric data types "
2323                + "other than DBTYPE_VARNUMERIC.");
2324        private static final Column NumericScale =
2325            new Column(
2326                "NUMERIC_SCALE",
2327                Type.Short,
2328                null,
2329                Column.NOT_RESTRICTION,
2330                Column.OPTIONAL,
2331                "The number of digits to the right of the decimal point for "
2332                + "DBTYPE_DECIMAL, DBTYPE_NUMERIC, DBTYPE_VARNUMERIC. "
2333                + "Otherwise, this is NULL.");
2334
2335        public void populateImpl(
2336            XmlaResponse response,
2337            OlapConnection connection,
2338            List<Row> rows)
2339            throws XmlaException, OlapException
2340        {
2341            for (Catalog catalog
2342                : catIter(connection, catNameCond(), tableCatalogCond))
2343            {
2344                // By definition, mondrian catalogs have only one
2345                // schema. It is safe to use get(0)
2346                final Schema schema = catalog.getSchemas().get(0);
2347                final boolean emitInvisibleMembers =
2348                    XmlaUtil.shouldEmitInvisibleMembers(request);
2349                int ordinalPosition = 1;
2350                Row row;
2351
2352                for (Cube cube : filter(sortedCubes(schema), tableNameCond)) {
2353                    for (Dimension dimension : cube.getDimensions()) {
2354                        for (Hierarchy hierarchy : dimension.getHierarchies()) {
2355                            ordinalPosition =
2356                                populateHierarchy(
2357                                    cube, hierarchy,
2358                                    ordinalPosition, rows);
2359                        }
2360                    }
2361
2362                    List<Measure> rms = cube.getMeasures();
2363                    for (int k = 1; k < rms.size(); k++) {
2364                        Measure member = rms.get(k);
2365
2366                        // null == true for regular cubes
2367                        // virtual cubes do not set the visible property
2368                        // on its measures so it might be null.
2369                        Boolean visible = (Boolean)
2370                            member.getPropertyValue(
2371                                Property.StandardMemberProperty.$visible);
2372                        if (visible == null) {
2373                            visible = true;
2374                        }
2375                        if (!emitInvisibleMembers && !visible) {
2376                            continue;
2377                        }
2378
2379                        String memberName = member.getName();
2380                        final String columnName = "Measures:" + memberName;
2381                        if (!columnNameCond.apply(columnName)) {
2382                            continue;
2383                        }
2384
2385                        row = new Row();
2386                        row.set(TableCatalog.name, catalog.getName());
2387                        row.set(TableName.name, cube.getName());
2388                        row.set(ColumnName.name, columnName);
2389                        row.set(OrdinalPosition.name, ordinalPosition++);
2390                        row.set(ColumnHasDefault.name, false);
2391                        row.set(ColumnFlags.name, 0);
2392                        row.set(IsNullable.name, false);
2393                        // TODO: here is where one tries to determine the
2394                        // type of the column - since these are all
2395                        // Measures, aggregate Measures??, maybe they
2396                        // are all numeric? (or currency)
2397                        row.set(
2398                            DataType.name,
2399                            XmlaConstants.DBType.R8.xmlaOrdinal());
2400                        // TODO: 16/255 seems to be what MS SQL Server
2401                        // always returns.
2402                        row.set(NumericPrecision.name, 16);
2403                        row.set(NumericScale.name, 255);
2404                        addRow(row, rows);
2405                    }
2406                }
2407            }
2408        }
2409
2410        private int populateHierarchy(
2411            Cube cube,
2412            Hierarchy hierarchy,
2413            int ordinalPosition,
2414            List<Row> rows)
2415        {
2416            String schemaName = cube.getSchema().getName();
2417            String cubeName = cube.getName();
2418            String hierarchyName = hierarchy.getName();
2419
2420            if (hierarchy.hasAll()) {
2421                Row row = new Row();
2422                row.set(TableCatalog.name, schemaName);
2423                row.set(TableName.name, cubeName);
2424                row.set(ColumnName.name, hierarchyName + ":(All)!NAME");
2425                row.set(OrdinalPosition.name, ordinalPosition++);
2426                row.set(ColumnHasDefault.name, false);
2427                row.set(ColumnFlags.name, 0);
2428                row.set(IsNullable.name, false);
2429                // names are always WSTR
2430                row.set(DataType.name, XmlaConstants.DBType.WSTR.xmlaOrdinal());
2431                row.set(CharacterMaximumLength.name, 0);
2432                row.set(CharacterOctetLength.name, 0);
2433                addRow(row, rows);
2434
2435                row = new Row();
2436                row.set(TableCatalog.name, schemaName);
2437                row.set(TableName.name, cubeName);
2438                row.set(ColumnName.name, hierarchyName + ":(All)!UNIQUE_NAME");
2439                row.set(OrdinalPosition.name, ordinalPosition++);
2440                row.set(ColumnHasDefault.name, false);
2441                row.set(ColumnFlags.name, 0);
2442                row.set(IsNullable.name, false);
2443                // names are always WSTR
2444                row.set(DataType.name, XmlaConstants.DBType.WSTR.xmlaOrdinal());
2445                row.set(CharacterMaximumLength.name, 0);
2446                row.set(CharacterOctetLength.name, 0);
2447                addRow(row, rows);
2448
2449                if (false) {
2450                    // TODO: SQLServer outputs this hasall KEY column name -
2451                    // don't know what it's for
2452                    row = new Row();
2453                    row.set(TableCatalog.name, schemaName);
2454                    row.set(TableName.name, cubeName);
2455                    row.set(ColumnName.name, hierarchyName + ":(All)!KEY");
2456                    row.set(OrdinalPosition.name, ordinalPosition++);
2457                    row.set(ColumnHasDefault.name, false);
2458                    row.set(ColumnFlags.name, 0);
2459                    row.set(IsNullable.name, false);
2460                    // names are always BOOL
2461                    row.set(
2462                        DataType.name, XmlaConstants.DBType.BOOL.xmlaOrdinal());
2463                    row.set(NumericPrecision.name, 255);
2464                    row.set(NumericScale.name, 255);
2465                    addRow(row, rows);
2466                }
2467            }
2468
2469            for (Level level : hierarchy.getLevels()) {
2470                ordinalPosition =
2471                    populateLevel(
2472                        cube, hierarchy, level, ordinalPosition, rows);
2473            }
2474            return ordinalPosition;
2475        }
2476
2477        private int populateLevel(
2478            Cube cube,
2479            Hierarchy hierarchy,
2480            Level level,
2481            int ordinalPosition,
2482            List<Row> rows)
2483        {
2484            String schemaName = cube.getSchema().getName();
2485            String cubeName = cube.getName();
2486            String hierarchyName = hierarchy.getName();
2487            String levelName = level.getName();
2488
2489            Row row = new Row();
2490            row.set(TableCatalog.name, schemaName);
2491            row.set(TableName.name, cubeName);
2492            row.set(
2493                ColumnName.name,
2494                hierarchyName + ':' + levelName + "!NAME");
2495            row.set(OrdinalPosition.name, ordinalPosition++);
2496            row.set(ColumnHasDefault.name, false);
2497            row.set(ColumnFlags.name, 0);
2498            row.set(IsNullable.name, false);
2499            // names are always WSTR
2500            row.set(DataType.name, XmlaConstants.DBType.WSTR.xmlaOrdinal());
2501            row.set(CharacterMaximumLength.name, 0);
2502            row.set(CharacterOctetLength.name, 0);
2503            addRow(row, rows);
2504
2505            row = new Row();
2506            row.set(TableCatalog.name, schemaName);
2507            row.set(TableName.name, cubeName);
2508            row.set(
2509                ColumnName.name,
2510                hierarchyName + ':' + levelName + "!UNIQUE_NAME");
2511            row.set(OrdinalPosition.name, ordinalPosition++);
2512            row.set(ColumnHasDefault.name, false);
2513            row.set(ColumnFlags.name, 0);
2514            row.set(IsNullable.name, false);
2515            // names are always WSTR
2516            row.set(DataType.name, XmlaConstants.DBType.WSTR.xmlaOrdinal());
2517            row.set(CharacterMaximumLength.name, 0);
2518            row.set(CharacterOctetLength.name, 0);
2519            addRow(row, rows);
2520
2521/*
2522TODO: see above
2523            row = new Row();
2524            row.set(TableCatalog.name, schemaName);
2525            row.set(TableName.name, cubeName);
2526            row.set(ColumnName.name,
2527                hierarchyName + ":" + levelName + "!KEY");
2528            row.set(OrdinalPosition.name, ordinalPosition++);
2529            row.set(ColumnHasDefault.name, false);
2530            row.set(ColumnFlags.name, 0);
2531            row.set(IsNullable.name, false);
2532            // names are always BOOL
2533            row.set(DataType.name, DBType.BOOL.ordinal());
2534            row.set(NumericPrecision.name, 255);
2535            row.set(NumericScale.name, 255);
2536            addRow(row, rows);
2537*/
2538            NamedList<Property> props = level.getProperties();
2539            for (Property prop : props) {
2540                String propName = prop.getName();
2541
2542                row = new Row();
2543                row.set(TableCatalog.name, schemaName);
2544                row.set(TableName.name, cubeName);
2545                row.set(
2546                    ColumnName.name,
2547                    hierarchyName + ':' + levelName + '!' + propName);
2548                row.set(OrdinalPosition.name, ordinalPosition++);
2549                row.set(ColumnHasDefault.name, false);
2550                row.set(ColumnFlags.name, 0);
2551                row.set(IsNullable.name, false);
2552
2553                XmlaConstants.DBType dbType = getDBTypeFromProperty(prop);
2554                row.set(DataType.name, dbType.xmlaOrdinal());
2555
2556                switch (prop.getDatatype()) {
2557                case STRING:
2558                    row.set(CharacterMaximumLength.name, 0);
2559                    row.set(CharacterOctetLength.name, 0);
2560                    break;
2561                case INTEGER:
2562                case UNSIGNED_INTEGER:
2563                case DOUBLE:
2564                    // TODO: 16/255 seems to be what MS SQL Server
2565                    // always returns.
2566                    row.set(NumericPrecision.name, 16);
2567                    row.set(NumericScale.name, 255);
2568                    break;
2569                case BOOLEAN:
2570                    row.set(NumericPrecision.name, 255);
2571                    row.set(NumericScale.name, 255);
2572                    break;
2573                default:
2574                    // TODO: what type is it really, its
2575                    // not a string
2576                    row.set(CharacterMaximumLength.name, 0);
2577                    row.set(CharacterOctetLength.name, 0);
2578                    break;
2579                }
2580                addRow(row, rows);
2581            }
2582            return ordinalPosition;
2583        }
2584
2585        protected void setProperty(
2586            PropertyDefinition propertyDef, String value)
2587        {
2588            switch (propertyDef) {
2589            case Content:
2590                break;
2591            default:
2592                super.setProperty(propertyDef, value);
2593            }
2594        }
2595    }
2596
2597    static class DbschemaProviderTypesRowset extends Rowset {
2598        private final Util.Functor1<Boolean, Integer> dataTypeCond;
2599
2600        DbschemaProviderTypesRowset(XmlaRequest request, XmlaHandler handler) {
2601            super(DBSCHEMA_PROVIDER_TYPES, request, handler);
2602            dataTypeCond = makeCondition(DataType);
2603        }
2604
2605        /*
2606        DATA_TYPE DBTYPE_UI2
2607        BEST_MATCH DBTYPE_BOOL
2608        Column(String name, Type type, Enumeration enumeratedType,
2609        boolean restriction, boolean nullable, String description)
2610        */
2611        /*
2612         * These are the columns returned by SQL Server.
2613         */
2614        private static final Column TypeName =
2615            new Column(
2616                "TYPE_NAME",
2617                Type.String,
2618                null,
2619                Column.NOT_RESTRICTION,
2620                Column.REQUIRED,
2621                "The provider-specific data type name.");
2622        private static final Column DataType =
2623            new Column(
2624                "DATA_TYPE",
2625                Type.UnsignedShort,
2626                null,
2627                Column.RESTRICTION,
2628                Column.REQUIRED,
2629                "The indicator of the data type.");
2630        private static final Column ColumnSize =
2631            new Column(
2632                "COLUMN_SIZE",
2633                Type.UnsignedInteger,
2634                null,
2635                Column.NOT_RESTRICTION,
2636                Column.REQUIRED,
2637                "The length of a non-numeric column. If the data type is "
2638                + "numeric, this is the upper bound on the maximum precision "
2639                + "of the data type.");
2640        private static final Column LiteralPrefix =
2641            new Column(
2642                "LITERAL_PREFIX",
2643                Type.String,
2644                null,
2645                Column.NOT_RESTRICTION,
2646                Column.OPTIONAL,
2647                "The character or characters used to prefix a literal of this "
2648                + "type in a text command.");
2649        private static final Column LiteralSuffix =
2650            new Column(
2651                "LITERAL_SUFFIX",
2652                Type.String,
2653                null,
2654                Column.NOT_RESTRICTION,
2655                Column.OPTIONAL,
2656                "The character or characters used to suffix a literal of this "
2657                + "type in a text command.");
2658        private static final Column IsNullable =
2659            new Column(
2660                "IS_NULLABLE",
2661                Type.Boolean,
2662                null,
2663                Column.NOT_RESTRICTION,
2664                Column.OPTIONAL,
2665                "A Boolean that indicates whether the data type is nullable. "
2666                + "NULL-- indicates that it is not known whether the data type "
2667                + "is nullable.");
2668        private static final Column CaseSensitive =
2669            new Column(
2670                "CASE_SENSITIVE",
2671                Type.Boolean,
2672                null,
2673                Column.NOT_RESTRICTION,
2674                Column.OPTIONAL,
2675                "A Boolean that indicates whether the data type is a "
2676                + "characters type and case-sensitive.");
2677        private static final Column Searchable =
2678            new Column(
2679                "SEARCHABLE",
2680                Type.UnsignedInteger,
2681                null,
2682                Column.NOT_RESTRICTION,
2683                Column.OPTIONAL,
2684                "An integer indicating how the data type can be used in "
2685                + "searches if the provider supports ICommandText; otherwise, "
2686                + "NULL.");
2687        private static final Column UnsignedAttribute =
2688            new Column(
2689                "UNSIGNED_ATTRIBUTE",
2690                Type.Boolean,
2691                null,
2692                Column.NOT_RESTRICTION,
2693                Column.OPTIONAL,
2694                "A Boolean that indicates whether the data type is unsigned.");
2695        private static final Column FixedPrecScale =
2696            new Column(
2697                "FIXED_PREC_SCALE",
2698                Type.Boolean,
2699                null,
2700                Column.NOT_RESTRICTION,
2701                Column.OPTIONAL,
2702                "A Boolean that indicates whether the data type has a fixed "
2703                + "precision and scale.");
2704        private static final Column AutoUniqueValue =
2705            new Column(
2706                "AUTO_UNIQUE_VALUE",
2707                Type.Boolean,
2708                null,
2709                Column.NOT_RESTRICTION,
2710                Column.OPTIONAL,
2711                "A Boolean that indicates whether the data type is "
2712                + "autoincrementing.");
2713        private static final Column IsLong =
2714            new Column(
2715                "IS_LONG",
2716                Type.Boolean,
2717                null,
2718                Column.NOT_RESTRICTION,
2719                Column.OPTIONAL,
2720                "A Boolean that indicates whether the data type is a binary "
2721                + "large object (BLOB) and has very long data.");
2722        private static final Column BestMatch =
2723            new Column(
2724                "BEST_MATCH",
2725                Type.Boolean,
2726                null,
2727                Column.RESTRICTION,
2728                Column.OPTIONAL,
2729                "A Boolean that indicates whether the data type is a best "
2730                + "match.");
2731
2732        @Override
2733        protected boolean needConnection() {
2734            return false;
2735        }
2736
2737        public void populateImpl(
2738            XmlaResponse response,
2739            OlapConnection connection,
2740            List<Row> rows)
2741            throws XmlaException
2742        {
2743            // Identifies the (base) data types supported by the data provider.
2744            Row row;
2745
2746            // i4
2747            Integer dt = XmlaConstants.DBType.I4.xmlaOrdinal();
2748            if (dataTypeCond.apply(dt)) {
2749                row = new Row();
2750                row.set(TypeName.name, XmlaConstants.DBType.I4.userName);
2751                row.set(DataType.name, dt);
2752                row.set(ColumnSize.name, 8);
2753                row.set(IsNullable.name, true);
2754                row.set(Searchable.name, null);
2755                row.set(UnsignedAttribute.name, false);
2756                row.set(FixedPrecScale.name, false);
2757                row.set(AutoUniqueValue.name, false);
2758                row.set(IsLong.name, false);
2759                row.set(BestMatch.name, true);
2760                addRow(row, rows);
2761            }
2762
2763            // R8
2764            dt = XmlaConstants.DBType.R8.xmlaOrdinal();
2765            if (dataTypeCond.apply(dt)) {
2766                row = new Row();
2767                row.set(TypeName.name, XmlaConstants.DBType.R8.userName);
2768                row.set(DataType.name, dt);
2769                row.set(ColumnSize.name, 16);
2770                row.set(IsNullable.name, true);
2771                row.set(Searchable.name, null);
2772                row.set(UnsignedAttribute.name, false);
2773                row.set(FixedPrecScale.name, false);
2774                row.set(AutoUniqueValue.name, false);
2775                row.set(IsLong.name, false);
2776                row.set(BestMatch.name, true);
2777                addRow(row, rows);
2778            }
2779
2780            // CY
2781            dt = XmlaConstants.DBType.CY.xmlaOrdinal();
2782            if (dataTypeCond.apply(dt)) {
2783                row = new Row();
2784                row.set(TypeName.name, XmlaConstants.DBType.CY.userName);
2785                row.set(DataType.name, dt);
2786                row.set(ColumnSize.name, 8);
2787                row.set(IsNullable.name, true);
2788                row.set(Searchable.name, null);
2789                row.set(UnsignedAttribute.name, false);
2790                row.set(FixedPrecScale.name, false);
2791                row.set(AutoUniqueValue.name, false);
2792                row.set(IsLong.name, false);
2793                row.set(BestMatch.name, true);
2794                addRow(row, rows);
2795            }
2796
2797            // BOOL
2798            dt = XmlaConstants.DBType.BOOL.xmlaOrdinal();
2799            if (dataTypeCond.apply(dt)) {
2800                row = new Row();
2801                row.set(TypeName.name, XmlaConstants.DBType.BOOL.userName);
2802                row.set(DataType.name, dt);
2803                row.set(ColumnSize.name, 1);
2804                row.set(IsNullable.name, true);
2805                row.set(Searchable.name, null);
2806                row.set(UnsignedAttribute.name, false);
2807                row.set(FixedPrecScale.name, false);
2808                row.set(AutoUniqueValue.name, false);
2809                row.set(IsLong.name, false);
2810                row.set(BestMatch.name, true);
2811                addRow(row, rows);
2812            }
2813
2814            // I8
2815            dt = XmlaConstants.DBType.I8.xmlaOrdinal();
2816            if (dataTypeCond.apply(dt)) {
2817                row = new Row();
2818                row.set(TypeName.name, XmlaConstants.DBType.I8.userName);
2819                row.set(DataType.name, dt);
2820                row.set(ColumnSize.name, 16);
2821                row.set(IsNullable.name, true);
2822                row.set(Searchable.name, null);
2823                row.set(UnsignedAttribute.name, false);
2824                row.set(FixedPrecScale.name, false);
2825                row.set(AutoUniqueValue.name, false);
2826                row.set(IsLong.name, false);
2827                row.set(BestMatch.name, true);
2828                addRow(row, rows);
2829            }
2830
2831            // WSTR
2832            dt = XmlaConstants.DBType.WSTR.xmlaOrdinal();
2833            if (dataTypeCond.apply(dt)) {
2834                row = new Row();
2835                row.set(TypeName.name, XmlaConstants.DBType.WSTR.userName);
2836                row.set(DataType.name, dt);
2837                // how big are the string columns in the db
2838                row.set(ColumnSize.name, 255);
2839                row.set(LiteralPrefix.name, "\"");
2840                row.set(LiteralSuffix.name, "\"");
2841                row.set(IsNullable.name, true);
2842                row.set(CaseSensitive.name, false);
2843                row.set(Searchable.name, null);
2844                row.set(FixedPrecScale.name, false);
2845                row.set(AutoUniqueValue.name, false);
2846                row.set(IsLong.name, false);
2847                row.set(BestMatch.name, true);
2848                addRow(row, rows);
2849            }
2850        }
2851
2852        protected void setProperty(
2853            PropertyDefinition propertyDef, String value)
2854        {
2855            switch (propertyDef) {
2856            case Content:
2857                break;
2858            default:
2859                super.setProperty(propertyDef, value);
2860            }
2861        }
2862    }
2863
2864    static class DbschemaSchemataRowset extends Rowset {
2865        private final Util.Functor1<Boolean, Catalog> catalogNameCond;
2866
2867        DbschemaSchemataRowset(XmlaRequest request, XmlaHandler handler) {
2868            super(DBSCHEMA_SCHEMATA, request, handler);
2869            catalogNameCond = makeCondition(CATALOG_NAME_GETTER, CatalogName);
2870        }
2871
2872        /*
2873         * These are the columns returned by SQL Server.
2874         */
2875        private static final Column CatalogName =
2876            new Column(
2877                "CATALOG_NAME",
2878                Type.String,
2879                null,
2880                Column.RESTRICTION,
2881                Column.REQUIRED,
2882                "The provider-specific data type name.");
2883        private static final Column SchemaName =
2884            new Column(
2885                "SCHEMA_NAME",
2886                Type.String,
2887                null,
2888                Column.RESTRICTION,
2889                Column.REQUIRED,
2890                "The indicator of the data type.");
2891        private static final Column SchemaOwner =
2892            new Column(
2893                "SCHEMA_OWNER",
2894                Type.String,
2895                null,
2896                Column.RESTRICTION,
2897                Column.REQUIRED,
2898                "The length of a non-numeric column. If the data type is "
2899                + "numeric, this is the upper bound on the maximum precision "
2900                + "of the data type.");
2901
2902        public void populateImpl(
2903            XmlaResponse response,
2904            OlapConnection connection,
2905            List<Row> rows)
2906            throws XmlaException, OlapException
2907        {
2908            for (Catalog catalog
2909                : catIter(connection, catalogNameCond, catNameCond()))
2910            {
2911                for (Schema schema : catalog.getSchemas()) {
2912                    Row row = new Row();
2913                    row.set(CatalogName.name, catalog.getName());
2914                    row.set(SchemaName.name, schema.getName());
2915                    row.set(SchemaOwner.name, "");
2916                    addRow(row, rows);
2917                }
2918            }
2919        }
2920
2921        protected void setProperty(
2922            PropertyDefinition propertyDef, String value)
2923        {
2924            switch (propertyDef) {
2925            case Content:
2926                break;
2927            default:
2928                super.setProperty(propertyDef, value);
2929            }
2930        }
2931    }
2932
2933    static class DbschemaTablesRowset extends Rowset {
2934        private final Util.Functor1<Boolean, Catalog> tableCatalogCond;
2935        private final Util.Functor1<Boolean, Cube> tableNameCond;
2936        private final Util.Functor1<Boolean, String> tableTypeCond;
2937
2938        DbschemaTablesRowset(XmlaRequest request, XmlaHandler handler) {
2939            super(DBSCHEMA_TABLES, request, handler);
2940            tableCatalogCond = makeCondition(CATALOG_NAME_GETTER, TableCatalog);
2941            tableNameCond = makeCondition(ELEMENT_NAME_GETTER, TableName);
2942            tableTypeCond = makeCondition(TableType);
2943        }
2944
2945        private static final Column TableCatalog =
2946            new Column(
2947                "TABLE_CATALOG",
2948                Type.String,
2949                null,
2950                Column.RESTRICTION,
2951                Column.REQUIRED,
2952                "The name of the catalog to which this object belongs.");
2953        private static final Column TableSchema =
2954            new Column(
2955                "TABLE_SCHEMA",
2956                Type.String,
2957                null,
2958                Column.RESTRICTION,
2959                Column.OPTIONAL,
2960                "The name of the cube to which this object belongs.");
2961        private static final Column TableName =
2962            new Column(
2963                "TABLE_NAME",
2964                Type.String,
2965                null,
2966                Column.RESTRICTION,
2967                Column.REQUIRED,
2968                "The name of the object, if TABLE_TYPE is TABLE.");
2969        private static final Column TableType =
2970            new Column(
2971                "TABLE_TYPE",
2972                Type.String,
2973                null,
2974                Column.RESTRICTION,
2975                Column.REQUIRED,
2976                "The type of the table. TABLE indicates the object is a "
2977                + "measure group. SYSTEM TABLE indicates the object is a "
2978                + "dimension.");
2979
2980        private static final Column TableGuid =
2981            new Column(
2982                "TABLE_GUID",
2983                Type.UUID,
2984                null,
2985                Column.NOT_RESTRICTION,
2986                Column.OPTIONAL,
2987                "Not supported.");
2988        private static final Column Description =
2989            new Column(
2990                "DESCRIPTION",
2991                Type.String,
2992                null,
2993                Column.NOT_RESTRICTION,
2994                Column.OPTIONAL,
2995                "A human-readable description of the object.");
2996        private static final Column TablePropId =
2997            new Column(
2998                "TABLE_PROPID",
2999                Type.UnsignedInteger,
3000                null,
3001                Column.NOT_RESTRICTION,
3002                Column.OPTIONAL,
3003                "Not supported.");
3004        private static final Column DateCreated =
3005            new Column(
3006                "DATE_CREATED",
3007                Type.DateTime,
3008                null,
3009                Column.NOT_RESTRICTION,
3010                Column.OPTIONAL,
3011                "Not supported.");
3012        private static final Column DateModified =
3013            new Column(
3014                "DATE_MODIFIED",
3015                Type.DateTime,
3016                null,
3017                Column.NOT_RESTRICTION,
3018                Column.OPTIONAL,
3019                "The date the object was last modified.");
3020
3021        /*
3022        private static final Column TableOlapType =
3023            new Column(
3024                "TABLE_OLAP_TYPE",
3025                Type.String,
3026                null,
3027                Column.RESTRICTION,
3028                Column.OPTIONAL,
3029                "The OLAP type of the object.  MEASURE_GROUP indicates the "
3030                + "object is a measure group.  CUBE_DIMENSION indicated the "
3031                + "object is a dimension.");
3032        */
3033
3034        public void populateImpl(
3035            XmlaResponse response,
3036            OlapConnection connection,
3037            List<Row> rows)
3038            throws XmlaException, OlapException
3039        {
3040            for (Catalog catalog
3041                : catIter(connection, catNameCond(), tableCatalogCond))
3042            {
3043                // By definition, mondrian catalogs have only one
3044                // schema. It is safe to use get(0)
3045                final Schema schema = catalog.getSchemas().get(0);
3046                Row row;
3047                for (Cube cube : filter(sortedCubes(schema), tableNameCond)) {
3048                    String desc = cube.getDescription();
3049                    if (desc == null) {
3050                        //TODO: currently this is always null
3051                        desc =
3052                            catalog.getName() + " - "
3053                            + cube.getName() + " Cube";
3054                    }
3055
3056                    if (tableTypeCond.apply("TABLE")) {
3057                        row = new Row();
3058                        row.set(TableCatalog.name, catalog.getName());
3059                        row.set(TableName.name, cube.getName());
3060                        row.set(TableType.name, "TABLE");
3061                        row.set(Description.name, desc);
3062                        if (false) {
3063                            row.set(DateModified.name, dateModified);
3064                        }
3065                        addRow(row, rows);
3066                    }
3067
3068
3069                    if (tableTypeCond.apply("SYSTEM TABLE")) {
3070                        for (Dimension dimension : cube.getDimensions()) {
3071                            if (dimension.getDimensionType()
3072                                == Dimension.Type.MEASURE)
3073                            {
3074                                continue;
3075                            }
3076                            for (Hierarchy hierarchy
3077                                : dimension.getHierarchies())
3078                            {
3079                                populateHierarchy(
3080                                    cube, hierarchy, rows);
3081                            }
3082                        }
3083                    }
3084                }
3085            }
3086        }
3087
3088        private void populateHierarchy(
3089            Cube cube, Hierarchy hierarchy, List<Row> rows)
3090        {
3091/*
3092            String schemaName = cube.getSchema().getName();
3093            String cubeName = cube.getName();
3094            String hierarchyName = hierarchy.getName();
3095
3096            String desc = hierarchy.getDescription();
3097            if (desc == null) {
3098                //TODO: currently this is always null
3099                desc = schemaName +
3100                    " - " +
3101                    cubeName +
3102                    " Cube - " +
3103                    hierarchyName +
3104                    " Hierarchy";
3105            }
3106
3107            if (hierarchy.hasAll()) {
3108                String tableName = cubeName +
3109                    ':' + hierarchyName + ':' + "(All)";
3110
3111                Row row = new Row();
3112                row.set(TableCatalog.name, schemaName);
3113                row.set(TableName.name, tableName);
3114                row.set(TableType.name, "SYSTEM TABLE");
3115                row.set(Description.name, desc);
3116                row.set(DateModified.name, dateModified);
3117                addRow(row, rows);
3118            }
3119*/
3120            for (Level level : hierarchy.getLevels()) {
3121                populateLevel(cube, hierarchy, level, rows);
3122            }
3123        }
3124
3125        private void populateLevel(
3126            Cube cube,
3127            Hierarchy hierarchy,
3128            Level level,
3129            List<Row> rows)
3130        {
3131            String schemaName = cube.getSchema().getName();
3132            String cubeName = cube.getName();
3133            String hierarchyName = getHierarchyName(hierarchy);
3134            String levelName = level.getName();
3135
3136            String tableName =
3137                cubeName + ':' + hierarchyName + ':' + levelName;
3138
3139            String desc = level.getDescription();
3140            if (desc == null) {
3141                //TODO: currently this is always null
3142                desc =
3143                    schemaName + " - "
3144                    + cubeName + " Cube - "
3145                    + hierarchyName + " Hierarchy - "
3146                    + levelName + " Level";
3147            }
3148
3149            Row row = new Row();
3150            row.set(TableCatalog.name, schemaName);
3151            row.set(TableName.name, tableName);
3152            row.set(TableType.name, "SYSTEM TABLE");
3153            row.set(Description.name, desc);
3154            if (false) {
3155                row.set(DateModified.name, dateModified);
3156            }
3157            addRow(row, rows);
3158        }
3159
3160        protected void setProperty(
3161            PropertyDefinition propertyDef, String value)
3162        {
3163            switch (propertyDef) {
3164            case Content:
3165                break;
3166            default:
3167                super.setProperty(propertyDef, value);
3168            }
3169        }
3170    }
3171
3172    // TODO: Is this needed????
3173    static class DbschemaTablesInfoRowset extends Rowset {
3174        DbschemaTablesInfoRowset(XmlaRequest request, XmlaHandler handler) {
3175            super(DBSCHEMA_TABLES_INFO, request, handler);
3176        }
3177
3178        private static final Column TableCatalog =
3179            new Column(
3180                "TABLE_CATALOG",
3181                Type.String,
3182                null,
3183                Column.RESTRICTION,
3184                Column.OPTIONAL,
3185                "Catalog name. NULL if the provider does not support "
3186                + "catalogs.");
3187        private static final Column TableSchema =
3188            new Column(
3189                "TABLE_SCHEMA",
3190                Type.String,
3191                null,
3192                Column.RESTRICTION,
3193                Column.OPTIONAL,
3194                "Unqualified schema name. NULL if the provider does not "
3195                + "support schemas.");
3196        private static final Column TableName =
3197            new Column(
3198                "TABLE_NAME",
3199                Type.String,
3200                null,
3201                Column.RESTRICTION,
3202                Column.REQUIRED,
3203                "Table name.");
3204        private static final Column TableType =
3205            new Column(
3206                "TABLE_TYPE",
3207                Type.String,
3208                null,
3209                Column.RESTRICTION,
3210                Column.REQUIRED,
3211                "Table type. One of the following or a provider-specific "
3212                + "value: ALIAS, TABLE, SYNONYM, SYSTEM TABLE, VIEW, GLOBAL "
3213                + "TEMPORARY, LOCAL TEMPORARY, EXTERNAL TABLE, SYSTEM VIEW");
3214        private static final Column TableGuid =
3215            new Column(
3216                "TABLE_GUID",
3217                Type.UUID,
3218                null,
3219                Column.NOT_RESTRICTION,
3220                Column.OPTIONAL,
3221                "GUID that uniquely identifies the table. Providers that do "
3222                + "not use GUIDs to identify tables should return NULL in this "
3223                + "column.");
3224
3225        private static final Column Bookmarks =
3226            new Column(
3227                "BOOKMARKS",
3228                Type.Boolean,
3229                null,
3230                Column.NOT_RESTRICTION,
3231                Column.REQUIRED,
3232                "Whether this table supports bookmarks. Allways is false.");
3233        private static final Column BookmarkType =
3234            new Column(
3235                "BOOKMARK_TYPE",
3236                Type.Integer,
3237                null,
3238                Column.NOT_RESTRICTION,
3239                Column.OPTIONAL,
3240                "Default bookmark type supported on this table.");
3241        private static final Column BookmarkDataType =
3242            new Column(
3243                "BOOKMARK_DATATYPE",
3244                Type.UnsignedShort,
3245                null,
3246                Column.NOT_RESTRICTION,
3247                Column.OPTIONAL,
3248                "The indicator of the bookmark's native data type.");
3249        private static final Column BookmarkMaximumLength =
3250            new Column(
3251                "BOOKMARK_MAXIMUM_LENGTH",
3252                Type.UnsignedInteger,
3253                null,
3254                Column.NOT_RESTRICTION,
3255                Column.OPTIONAL,
3256                "Maximum length of the bookmark in bytes.");
3257        private static final Column BookmarkInformation =
3258            new Column(
3259                "BOOKMARK_INFORMATION",
3260                Type.UnsignedInteger,
3261                null,
3262                Column.NOT_RESTRICTION,
3263                Column.OPTIONAL,
3264                "A bitmask specifying additional information about bookmarks "
3265                + "over the rowset. ");
3266        private static final Column TableVersion =
3267            new Column(
3268                "TABLE_VERSION",
3269                Type.Long,
3270                null,
3271                Column.NOT_RESTRICTION,
3272                Column.OPTIONAL,
3273                "Version number for this table or NULL if the provider does "
3274                + "not support returning table version information.");
3275        private static final Column Cardinality =
3276            new Column(
3277                "CARDINALITY",
3278                Type.UnsignedLong,
3279                null,
3280                Column.NOT_RESTRICTION,
3281                Column.REQUIRED,
3282                "Cardinality (number of rows) of the table.");
3283        private static final Column Description =
3284            new Column(
3285                "DESCRIPTION",
3286                Type.String,
3287                null,
3288                Column.NOT_RESTRICTION,
3289                Column.OPTIONAL,
3290                "Human-readable description of the table.");
3291        private static final Column TablePropId =
3292            new Column(
3293                "TABLE_PROPID",
3294                Type.UnsignedInteger,
3295                null,
3296                Column.NOT_RESTRICTION,
3297                Column.OPTIONAL,
3298                "Property ID of the table. Return null.");
3299
3300        public void populateImpl(
3301            XmlaResponse response,
3302            OlapConnection connection,
3303            List<Row> rows)
3304            throws XmlaException, OlapException
3305        {
3306            for (Catalog catalog : catIter(connection, catNameCond())) {
3307                // By definition, mondrian catalogs have only one
3308                // schema. It is safe to use get(0)
3309                final Schema schema = catalog.getSchemas().get(0);
3310                //TODO: Is this cubes or tables? SQL Server returns what
3311                // in foodmart are cube names for TABLE_NAME
3312                for (Cube cube : sortedCubes(schema)) {
3313                    String cubeName = cube.getName();
3314                    String desc = cube.getDescription();
3315                    if (desc == null) {
3316                        //TODO: currently this is always null
3317                        desc = catalog.getName() + " - " + cubeName + " Cube";
3318                    }
3319                    //TODO: SQL Server returns 1000000 for all tables
3320                    int cardinality = 1000000;
3321                    String version = "null";
3322
3323                    Row row = new Row();
3324                    row.set(TableCatalog.name, catalog.getName());
3325                    row.set(TableName.name, cubeName);
3326                    row.set(TableType.name, "TABLE");
3327                    row.set(Bookmarks.name, false);
3328                    row.set(TableVersion.name, version);
3329                    row.set(Cardinality.name, cardinality);
3330                    row.set(Description.name, desc);
3331                    addRow(row, rows);
3332                }
3333            }
3334        }
3335
3336        protected void setProperty(
3337            PropertyDefinition propertyDef,
3338            String value)
3339        {
3340            switch (propertyDef) {
3341            case Content:
3342                break;
3343            default:
3344                super.setProperty(propertyDef, value);
3345            }
3346        }
3347    }
3348
3349    static class MdschemaActionsRowset extends Rowset {
3350        MdschemaActionsRowset(XmlaRequest request, XmlaHandler handler) {
3351            super(MDSCHEMA_ACTIONS, request, handler);
3352        }
3353
3354        private static final Column CatalogName =
3355            new Column(
3356                "CATALOG_NAME",
3357                Type.String,
3358                null,
3359                Column.RESTRICTION,
3360                Column.OPTIONAL,
3361                "The name of the catalog to which this action belongs.");
3362        private static final Column SchemaName =
3363            new Column(
3364                "SCHEMA_NAME",
3365                Type.String,
3366                null,
3367                Column.RESTRICTION,
3368                Column.OPTIONAL,
3369                "The name of the schema to which this action belongs.");
3370        private static final Column CubeName =
3371            new Column(
3372                "CUBE_NAME",
3373                Type.String,
3374                null,
3375                Column.RESTRICTION,
3376                Column.REQUIRED,
3377                "The name of the cube to which this action belongs.");
3378        private static final Column ActionName =
3379            new Column(
3380                "ACTION_NAME",
3381                Type.String,
3382                null,
3383                Column.RESTRICTION,
3384                Column.REQUIRED,
3385                "The name of the action.");
3386        private static final Column Coordinate =
3387            new Column(
3388                "COORDINATE",
3389                Type.String,
3390                null,
3391                Column.RESTRICTION,
3392                Column.REQUIRED,
3393                null);
3394        private static final Column CoordinateType =
3395            new Column(
3396                "COORDINATE_TYPE",
3397                Type.Integer,
3398                null,
3399                Column.RESTRICTION,
3400                Column.REQUIRED,
3401                null);
3402        /*
3403            TODO: optional columns
3404        ACTION_TYPE
3405        INVOCATION
3406        CUBE_SOURCE
3407    */
3408
3409        public void populateImpl(
3410            XmlaResponse response,
3411            OlapConnection connection,
3412            List<Row> rows)
3413            throws XmlaException
3414        {
3415            // mondrian doesn't support actions. It's not an error to ask for
3416            // them, there just aren't any
3417        }
3418    }
3419
3420    public static class MdschemaCubesRowset extends Rowset {
3421        private final Util.Functor1<Boolean, Catalog> catalogNameCond;
3422        private final Util.Functor1<Boolean, Schema> schemaNameCond;
3423        private final Util.Functor1<Boolean, Cube> cubeNameCond;
3424
3425        MdschemaCubesRowset(XmlaRequest request, XmlaHandler handler) {
3426            super(MDSCHEMA_CUBES, request, handler);
3427            catalogNameCond = makeCondition(CATALOG_NAME_GETTER, CatalogName);
3428            schemaNameCond = makeCondition(SCHEMA_NAME_GETTER, SchemaName);
3429            cubeNameCond = makeCondition(ELEMENT_NAME_GETTER, CubeName);
3430        }
3431
3432        public static final String MD_CUBTYPE_CUBE = "CUBE";
3433        public static final String MD_CUBTYPE_VIRTUAL_CUBE = "VIRTUAL CUBE";
3434
3435        private static final Column CatalogName =
3436            new Column(
3437                "CATALOG_NAME",
3438                Type.String,
3439                null,
3440                Column.RESTRICTION,
3441                Column.OPTIONAL,
3442                "The name of the catalog to which this cube belongs.");
3443        private static final Column SchemaName =
3444            new Column(
3445                "SCHEMA_NAME",
3446                Type.String,
3447                null,
3448                Column.RESTRICTION,
3449                Column.OPTIONAL,
3450                "The name of the schema to which this cube belongs.");
3451        private static final Column CubeName =
3452            new Column(
3453                "CUBE_NAME",
3454                Type.String,
3455                null,
3456                Column.RESTRICTION,
3457                Column.REQUIRED,
3458                "Name of the cube.");
3459        private static final Column CubeType =
3460            new Column(
3461                "CUBE_TYPE",
3462                Type.String,
3463                null,
3464                Column.RESTRICTION,
3465                Column.REQUIRED,
3466                "Cube type.");
3467        private static final Column CubeGuid =
3468            new Column(
3469                "CUBE_GUID",
3470                Type.UUID,
3471                null,
3472                Column.NOT_RESTRICTION,
3473                Column.OPTIONAL,
3474                "Cube type.");
3475        private static final Column CreatedOn =
3476            new Column(
3477                "CREATED_ON",
3478                Type.DateTime,
3479                null,
3480                Column.NOT_RESTRICTION,
3481                Column.OPTIONAL,
3482                "Date and time of cube creation.");
3483        private static final Column LastSchemaUpdate =
3484            new Column(
3485                "LAST_SCHEMA_UPDATE",
3486                Type.DateTime,
3487                null,
3488                Column.NOT_RESTRICTION,
3489                Column.OPTIONAL,
3490                "Date and time of last schema update.");
3491        private static final Column SchemaUpdatedBy =
3492            new Column(
3493                "SCHEMA_UPDATED_BY",
3494                Type.String,
3495                null,
3496                Column.NOT_RESTRICTION,
3497                Column.OPTIONAL,
3498                "User ID of the person who last updated the schema.");
3499        private static final Column LastDataUpdate =
3500            new Column(
3501                "LAST_DATA_UPDATE",
3502                Type.DateTime,
3503                null,
3504                Column.NOT_RESTRICTION,
3505                Column.OPTIONAL,
3506                "Date and time of last data update.");
3507        private static final Column DataUpdatedBy =
3508            new Column(
3509                "DATA_UPDATED_BY",
3510                Type.String,
3511                null,
3512                Column.NOT_RESTRICTION,
3513                Column.OPTIONAL,
3514                "User ID of the person who last updated the data.");
3515        private static final Column IsDrillthroughEnabled =
3516            new Column(
3517                "IS_DRILLTHROUGH_ENABLED",
3518                Type.Boolean,
3519                null,
3520                Column.NOT_RESTRICTION,
3521                Column.REQUIRED,
3522                "Describes whether DRILLTHROUGH can be performed on the "
3523                + "members of a cube");
3524        private static final Column IsWriteEnabled =
3525            new Column(
3526                "IS_WRITE_ENABLED",
3527                Type.Boolean,
3528                null,
3529                Column.NOT_RESTRICTION,
3530                Column.REQUIRED,
3531                "Describes whether a cube is write-enabled");
3532        private static final Column IsLinkable =
3533            new Column(
3534                "IS_LINKABLE",
3535                Type.Boolean,
3536                null,
3537                Column.NOT_RESTRICTION,
3538                Column.REQUIRED,
3539                "Describes whether a cube can be used in a linked cube");
3540        private static final Column IsSqlEnabled =
3541            new Column(
3542                "IS_SQL_ENABLED",
3543                Type.Boolean,
3544                null,
3545                Column.NOT_RESTRICTION,
3546                Column.REQUIRED,
3547                "Describes whether or not SQL can be used on the cube");
3548        private static final Column CubeCaption =
3549            new Column(
3550                "CUBE_CAPTION",
3551                Type.String,
3552                null,
3553                Column.NOT_RESTRICTION,
3554                Column.OPTIONAL,
3555                "The caption of the cube.");
3556        private static final Column Description =
3557            new Column(
3558                "DESCRIPTION",
3559                Type.String,
3560                null,
3561                Column.NOT_RESTRICTION,
3562                Column.OPTIONAL,
3563                "A user-friendly description of the dimension.");
3564        private static final Column Dimensions =
3565            new Column(
3566                "DIMENSIONS",
3567                Type.Rowset,
3568                null,
3569                Column.NOT_RESTRICTION,
3570                Column.OPTIONAL,
3571                "Dimensions in this cube.");
3572        private static final Column Sets =
3573            new Column(
3574                "SETS",
3575                Type.Rowset,
3576                null,
3577                Column.NOT_RESTRICTION,
3578                Column.OPTIONAL,
3579                "Sets in this cube.");
3580        private static final Column Measures =
3581            new Column(
3582                "MEASURES",
3583                Type.Rowset,
3584                null,
3585                Column.NOT_RESTRICTION,
3586                Column.OPTIONAL,
3587                "Measures in this cube.");
3588
3589        public void populateImpl(
3590            XmlaResponse response,
3591            OlapConnection connection,
3592            List<Row> rows)
3593            throws XmlaException, SQLException
3594        {
3595            for (Catalog catalog
3596                : catIter(connection, catNameCond(), catalogNameCond))
3597            {
3598                for (Schema schema
3599                    : filter(catalog.getSchemas(), schemaNameCond))
3600                {
3601                    for (Cube cube : filter(sortedCubes(schema), cubeNameCond))
3602                    {
3603                        String desc = cube.getDescription();
3604                        if (desc == null) {
3605                            desc =
3606                                catalog.getName() + " Schema - "
3607                                + cube.getName() + " Cube";
3608                        }
3609
3610                        Row row = new Row();
3611                        row.set(CatalogName.name, catalog.getName());
3612                        row.set(SchemaName.name, schema.getName());
3613                        row.set(CubeName.name, cube.getName());
3614                        final XmlaHandler.XmlaExtra extra =
3615                            getExtra(connection);
3616                        row.set(CubeType.name, extra.getCubeType(cube));
3617                        //row.set(CubeGuid.name, "");
3618                        //row.set(CreatedOn.name, "");
3619                        //row.set(LastSchemaUpdate.name, "");
3620                        //row.set(SchemaUpdatedBy.name, "");
3621                        //row.set(LastDataUpdate.name, "");
3622                        //row.set(DataUpdatedBy.name, "");
3623                        row.set(IsDrillthroughEnabled.name, true);
3624                        row.set(IsWriteEnabled.name, false);
3625                        row.set(IsLinkable.name, false);
3626                        row.set(IsSqlEnabled.name, false);
3627                        row.set(CubeCaption.name, cube.getCaption());
3628                        row.set(Description.name, desc);
3629                        Format formatter =
3630                            new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
3631                        String formattedDate =
3632                            formatter.format(
3633                                extra.getSchemaLoadDate(schema));
3634                        row.set(LastSchemaUpdate.name, formattedDate);
3635                        if (deep) {
3636                            row.set(
3637                                Dimensions.name,
3638                                new MdschemaDimensionsRowset(
3639                                    wrapRequest(
3640                                        request,
3641                                        Olap4jUtil.mapOf(
3642                                            MdschemaDimensionsRowset
3643                                                .CatalogName,
3644                                            catalog.getName(),
3645                                            MdschemaDimensionsRowset.SchemaName,
3646                                            schema.getName(),
3647                                            MdschemaDimensionsRowset.CubeName,
3648                                            cube.getName())),
3649                                    handler));
3650                            row.set(
3651                                Sets.name,
3652                                new MdschemaSetsRowset(
3653                                    wrapRequest(
3654                                        request,
3655                                        Olap4jUtil.mapOf(
3656                                            MdschemaSetsRowset.CatalogName,
3657                                            catalog.getName(),
3658                                            MdschemaSetsRowset.SchemaName,
3659                                            schema.getName(),
3660                                            MdschemaSetsRowset.CubeName,
3661                                            cube.getName())),
3662                                    handler));
3663                            row.set(
3664                                Measures.name,
3665                                new MdschemaMeasuresRowset(
3666                                    wrapRequest(
3667                                        request,
3668                                        Olap4jUtil.mapOf(
3669                                            MdschemaMeasuresRowset.CatalogName,
3670                                            catalog.getName(),
3671                                            MdschemaMeasuresRowset.SchemaName,
3672                                            schema.getName(),
3673                                            MdschemaMeasuresRowset.CubeName,
3674                                            cube.getName())),
3675                                    handler));
3676                        }
3677                        addRow(row, rows);
3678                    }
3679                }
3680            }
3681        }
3682
3683        protected void setProperty(
3684            PropertyDefinition propertyDef,
3685            String value)
3686        {
3687            switch (propertyDef) {
3688            case Content:
3689                break;
3690            default:
3691                super.setProperty(propertyDef, value);
3692            }
3693        }
3694    }
3695
3696    static class MdschemaDimensionsRowset extends Rowset {
3697        private final Util.Functor1<Boolean, Catalog> catalogNameCond;
3698        private final Util.Functor1<Boolean, Schema> schemaNameCond;
3699        private final Util.Functor1<Boolean, Cube> cubeNameCond;
3700        private final Util.Functor1<Boolean, Dimension> dimensionUnameCond;
3701        private final Util.Functor1<Boolean, Dimension> dimensionNameCond;
3702
3703        MdschemaDimensionsRowset(XmlaRequest request, XmlaHandler handler) {
3704            super(MDSCHEMA_DIMENSIONS, request, handler);
3705            catalogNameCond = makeCondition(CATALOG_NAME_GETTER, CatalogName);
3706            schemaNameCond = makeCondition(SCHEMA_NAME_GETTER, SchemaName);
3707            cubeNameCond = makeCondition(ELEMENT_NAME_GETTER, CubeName);
3708            dimensionUnameCond =
3709                makeCondition(ELEMENT_UNAME_GETTER, DimensionUniqueName);
3710            dimensionNameCond =
3711                makeCondition(ELEMENT_NAME_GETTER, DimensionName);
3712        }
3713
3714        public static final int MD_DIMTYPE_OTHER = 3;
3715        public static final int MD_DIMTYPE_MEASURE = 2;
3716        public static final int MD_DIMTYPE_TIME = 1;
3717
3718        private static final Column CatalogName =
3719            new Column(
3720                "CATALOG_NAME",
3721                Type.String,
3722                null,
3723                Column.RESTRICTION,
3724                Column.OPTIONAL,
3725                "The name of the database.");
3726        private static final Column SchemaName =
3727            new Column(
3728                "SCHEMA_NAME",
3729                Type.String,
3730                null,
3731                Column.RESTRICTION,
3732                Column.OPTIONAL,
3733                "Not supported.");
3734        private static final Column CubeName =
3735            new Column(
3736                "CUBE_NAME",
3737                Type.String,
3738                null,
3739                Column.RESTRICTION,
3740                Column.REQUIRED,
3741                "The name of the cube.");
3742        private static final Column DimensionName =
3743            new Column(
3744                "DIMENSION_NAME",
3745                Type.String,
3746                null,
3747                Column.RESTRICTION,
3748                Column.REQUIRED,
3749                "The name of the dimension.");
3750        private static final Column DimensionUniqueName =
3751            new Column(
3752                "DIMENSION_UNIQUE_NAME",
3753                Type.String,
3754                null,
3755                Column.RESTRICTION,
3756                Column.REQUIRED,
3757                "The unique name of the dimension.");
3758        private static final Column DimensionGuid =
3759            new Column(
3760                "DIMENSION_GUID",
3761                Type.UUID,
3762                null,
3763                Column.NOT_RESTRICTION,
3764                Column.OPTIONAL,
3765                "Not supported.");
3766        private static final Column DimensionCaption =
3767            new Column(
3768                "DIMENSION_CAPTION",
3769                Type.String,
3770                null,
3771                Column.NOT_RESTRICTION,
3772                Column.REQUIRED,
3773                "The caption of the dimension.");
3774        private static final Column DimensionOrdinal =
3775            new Column(
3776                "DIMENSION_ORDINAL",
3777                Type.UnsignedInteger,
3778                null,
3779                Column.NOT_RESTRICTION,
3780                Column.REQUIRED,
3781                "The position of the dimension within the cube.");
3782        /*
3783         * SQL Server returns values:
3784         *   MD_DIMTYPE_TIME (1)
3785         *   MD_DIMTYPE_MEASURE (2)
3786         *   MD_DIMTYPE_OTHER (3)
3787         */
3788        private static final Column DimensionType =
3789            new Column(
3790                "DIMENSION_TYPE",
3791                Type.Short,
3792                null,
3793                Column.NOT_RESTRICTION,
3794                Column.REQUIRED,
3795                "The type of the dimension.");
3796        private static final Column DimensionCardinality =
3797            new Column(
3798                "DIMENSION_CARDINALITY",
3799                Type.UnsignedInteger,
3800                null,
3801                Column.NOT_RESTRICTION,
3802                Column.REQUIRED,
3803                "The number of members in the key attribute.");
3804        private static final Column DefaultHierarchy =
3805            new Column(
3806                "DEFAULT_HIERARCHY",
3807                Type.String,
3808                null,
3809                Column.NOT_RESTRICTION,
3810                Column.REQUIRED,
3811                "A hierarchy from the dimension. Preserved for backwards "
3812                + "compatibility.");
3813        private static final Column Description =
3814            new Column(
3815                "DESCRIPTION",
3816                Type.String,
3817                null,
3818                Column.NOT_RESTRICTION,
3819                Column.OPTIONAL,
3820                "A user-friendly description of the dimension.");
3821        private static final Column IsVirtual =
3822            new Column(
3823                "IS_VIRTUAL",
3824                Type.Boolean,
3825                null,
3826                Column.NOT_RESTRICTION,
3827                Column.OPTIONAL,
3828                "Always FALSE.");
3829        private static final Column IsReadWrite =
3830            new Column(
3831                "IS_READWRITE",
3832                Type.Boolean,
3833                null,
3834                Column.NOT_RESTRICTION,
3835                Column.OPTIONAL,
3836                "A Boolean that indicates whether the dimension is "
3837                + "write-enabled.");
3838        /*
3839         * SQL Server returns values: 0 or 1
3840         */
3841        private static final Column DimensionUniqueSettings =
3842            new Column(
3843                "DIMENSION_UNIQUE_SETTINGS",
3844                Type.Integer,
3845                null,
3846                Column.NOT_RESTRICTION,
3847                Column.OPTIONAL,
3848                "A bitmap that specifies which columns contain unique values "
3849                + "if the dimension contains only members with unique names.");
3850        private static final Column DimensionMasterUniqueName =
3851            new Column(
3852                "DIMENSION_MASTER_UNIQUE_NAME",
3853                Type.String,
3854                null,
3855                Column.NOT_RESTRICTION,
3856                Column.OPTIONAL,
3857                "Always NULL.");
3858        private static final Column DimensionIsVisible =
3859            new Column(
3860                "DIMENSION_IS_VISIBLE",
3861                Type.Boolean,
3862                null,
3863                Column.NOT_RESTRICTION,
3864                Column.OPTIONAL,
3865                "Always TRUE.");
3866        private static final Column Hierarchies =
3867            new Column(
3868                "HIERARCHIES",
3869                Type.Rowset,
3870                null,
3871                Column.NOT_RESTRICTION,
3872                Column.OPTIONAL,
3873                "Hierarchies in this dimension.");
3874
3875        public void populateImpl(
3876            XmlaResponse response,
3877            OlapConnection connection,
3878            List<Row> rows)
3879            throws XmlaException, SQLException
3880        {
3881            for (Catalog catalog
3882                : catIter(connection, catNameCond(), catalogNameCond))
3883            {
3884                populateCatalog(connection, catalog, rows);
3885            }
3886        }
3887
3888        protected void populateCatalog(
3889            OlapConnection connection,
3890            Catalog catalog,
3891            List<Row> rows)
3892            throws XmlaException, SQLException
3893        {
3894            for (Schema schema : filter(catalog.getSchemas(), schemaNameCond)) {
3895                for (Cube cube : filteredCubes(schema, cubeNameCond)) {
3896                    populateCube(connection, catalog, cube, rows);
3897                }
3898            }
3899        }
3900
3901        protected void populateCube(
3902            OlapConnection connection,
3903            Catalog catalog,
3904            Cube cube,
3905            List<Row> rows)
3906            throws XmlaException, SQLException
3907        {
3908            for (Dimension dimension
3909                : filter(
3910                    cube.getDimensions(),
3911                    dimensionNameCond,
3912                    dimensionUnameCond))
3913            {
3914                populateDimension(
3915                    connection, catalog, cube, dimension, rows);
3916            }
3917        }
3918
3919        protected void populateDimension(
3920            OlapConnection connection,
3921            Catalog catalog,
3922            Cube cube,
3923            Dimension dimension,
3924            List<Row> rows)
3925            throws XmlaException, SQLException
3926        {
3927            String desc = dimension.getDescription();
3928            if (desc == null) {
3929                desc =
3930                    cube.getName() + " Cube - "
3931                    + dimension.getName() + " Dimension";
3932            }
3933
3934            Row row = new Row();
3935            row.set(CatalogName.name, catalog.getName());
3936            row.set(SchemaName.name, cube.getSchema().getName());
3937            row.set(CubeName.name, cube.getName());
3938            row.set(DimensionName.name, dimension.getName());
3939            row.set(DimensionUniqueName.name, dimension.getUniqueName());
3940            row.set(DimensionCaption.name, dimension.getCaption());
3941            row.set(
3942                DimensionOrdinal.name, cube.getDimensions().indexOf(dimension));
3943            row.set(DimensionType.name, getDimensionType(dimension));
3944
3945            //Is this the number of primaryKey members there are??
3946            // According to microsoft this is:
3947            //    "The number of members in the key attribute."
3948            // There may be a better way of doing this but
3949            // this is what I came up with. Note that I need to
3950            // add '1' to the number inorder for it to match
3951            // match what microsoft SQL Server is producing.
3952            // The '1' might have to do with whether or not the
3953            // hierarchy has a 'all' member or not - don't know yet.
3954            // large data set total for Orders cube 0m42.923s
3955            Hierarchy firstHierarchy = dimension.getHierarchies().get(0);
3956            NamedList<Level> levels = firstHierarchy.getLevels();
3957            Level lastLevel = levels.get(levels.size() - 1);
3958
3959            /*
3960            if override config setting is set
3961                if approxRowCount has a value
3962                    use it
3963            else
3964                                    do default
3965            */
3966
3967            // Added by TWI to returned cached row numbers
3968
3969            int n = getExtra(connection).getLevelCardinality(lastLevel);
3970            row.set(DimensionCardinality.name, n + 1);
3971
3972            // TODO: I think that this is just the dimension name
3973            row.set(DefaultHierarchy.name, dimension.getUniqueName());
3974            row.set(Description.name, desc);
3975            row.set(IsVirtual.name, false);
3976            // SQL Server always returns false
3977            row.set(IsReadWrite.name, false);
3978            // TODO: don't know what to do here
3979            // Are these the levels with uniqueMembers == true?
3980            // How are they mapped to specific column numbers?
3981            row.set(DimensionUniqueSettings.name, 0);
3982            row.set(DimensionIsVisible.name, dimension.isVisible());
3983            if (deep) {
3984                row.set(
3985                    Hierarchies.name,
3986                    new MdschemaHierarchiesRowset(
3987                        wrapRequest(
3988                            request,
3989                            Olap4jUtil.mapOf(
3990                                MdschemaHierarchiesRowset.CatalogName,
3991                                catalog.getName(),
3992                                MdschemaHierarchiesRowset.SchemaName,
3993                                cube.getSchema().getName(),
3994                                MdschemaHierarchiesRowset.CubeName,
3995                                cube.getName(),
3996                                MdschemaHierarchiesRowset.DimensionUniqueName,
3997                                dimension.getUniqueName())),
3998                        handler));
3999            }
4000
4001            addRow(row, rows);
4002        }
4003
4004        protected void setProperty(
4005            PropertyDefinition propertyDef, String value)
4006        {
4007            switch (propertyDef) {
4008            case Content:
4009                break;
4010            default:
4011                super.setProperty(propertyDef, value);
4012            }
4013        }
4014    }
4015
4016    static int getDimensionType(Dimension dim) throws OlapException {
4017        switch (dim.getDimensionType()) {
4018        case MEASURE:
4019            return MdschemaDimensionsRowset.MD_DIMTYPE_MEASURE;
4020        case TIME:
4021            return MdschemaDimensionsRowset.MD_DIMTYPE_TIME;
4022        default:
4023            return MdschemaDimensionsRowset.MD_DIMTYPE_OTHER;
4024        }
4025    }
4026
4027    public static class MdschemaFunctionsRowset extends Rowset {
4028        /**
4029         * http://www.csidata.com/custserv/onlinehelp/VBSdocs/vbs57.htm
4030         */
4031        public enum VarType {
4032            Empty("Uninitialized (default)"),
4033            Null("Contains no valid data"),
4034            Integer("Integer subtype"),
4035            Long("Long subtype"),
4036            Single("Single subtype"),
4037            Double("Double subtype"),
4038            Currency("Currency subtype"),
4039            Date("Date subtype"),
4040            String("String subtype"),
4041            Object("Object subtype"),
4042            Error("Error subtype"),
4043            Boolean("Boolean subtype"),
4044            Variant("Variant subtype"),
4045            DataObject("DataObject subtype"),
4046            Decimal("Decimal subtype"),
4047            Byte("Byte subtype"),
4048            Array("Array subtype");
4049
4050            public static VarType forCategory(int category) {
4051                switch (category) {
4052                case Category.Unknown:
4053                    // expression == unknown ???
4054                    // case Category.Expression:
4055                    return Empty;
4056                case Category.Array:
4057                    return Array;
4058                case Category.Dimension:
4059                case Category.Hierarchy:
4060                case Category.Level:
4061                case Category.Member:
4062                case Category.Set:
4063                case Category.Tuple:
4064                case Category.Cube:
4065                case Category.Value:
4066                    return Variant;
4067                case Category.Logical:
4068                    return Boolean;
4069                case Category.Numeric:
4070                    return Double;
4071                case Category.String:
4072                case Category.Symbol:
4073                case Category.Constant:
4074                    return String;
4075                case Category.DateTime:
4076                    return Date;
4077                case Category.Integer:
4078                case Category.Mask:
4079                    return Integer;
4080                }
4081                // NOTE: this should never happen
4082                return Empty;
4083            }
4084
4085            VarType(String description) {
4086                Util.discard(description);
4087            }
4088        }
4089
4090        private final Util.Functor1<Boolean, String> functionNameCond;
4091
4092        MdschemaFunctionsRowset(XmlaRequest request, XmlaHandler handler) {
4093            super(MDSCHEMA_FUNCTIONS, request, handler);
4094            functionNameCond = makeCondition(FunctionName);
4095        }
4096
4097        private static final Column FunctionName =
4098            new Column(
4099                "FUNCTION_NAME",
4100                Type.String,
4101                null,
4102                Column.RESTRICTION,
4103                Column.REQUIRED,
4104                "The name of the function.");
4105        private static final Column Description =
4106            new Column(
4107                "DESCRIPTION",
4108                Type.String,
4109                null,
4110                Column.NOT_RESTRICTION,
4111                Column.OPTIONAL,
4112                "A description of the function.");
4113        private static final Column ParameterList =
4114            new Column(
4115                "PARAMETER_LIST",
4116                Type.String,
4117                null,
4118                Column.NOT_RESTRICTION,
4119                Column.OPTIONAL,
4120                "A comma delimited list of parameters.");
4121        private static final Column ReturnType =
4122            new Column(
4123                "RETURN_TYPE",
4124                Type.Integer,
4125                null,
4126                Column.NOT_RESTRICTION,
4127                Column.REQUIRED,
4128                "The VARTYPE of the return data type of the function.");
4129        private static final Column Origin =
4130            new Column(
4131                "ORIGIN",
4132                Type.Integer,
4133                null,
4134                Column.RESTRICTION,
4135                Column.REQUIRED,
4136                "The origin of the function:  1 for MDX functions.  2 for "
4137                + "user-defined functions.");
4138        private static final Column InterfaceName =
4139            new Column(
4140                "INTERFACE_NAME",
4141                Type.String,
4142                null,
4143                Column.RESTRICTION,
4144                Column.REQUIRED,
4145                "The name of the interface for user-defined functions");
4146        private static final Column LibraryName =
4147            new Column(
4148                "LIBRARY_NAME",
4149                Type.String,
4150                null,
4151                Column.RESTRICTION,
4152                Column.OPTIONAL,
4153                "The name of the type library for user-defined functions. "
4154                + "NULL for MDX functions.");
4155        private static final Column Caption =
4156            new Column(
4157                "CAPTION",
4158                Type.String,
4159                null,
4160                Column.NOT_RESTRICTION,
4161                Column.OPTIONAL,
4162                "The display caption for the function.");
4163
4164        public void populateImpl(
4165            XmlaResponse response,
4166            OlapConnection connection,
4167            List<Row> rows)
4168            throws XmlaException, SQLException
4169        {
4170            final XmlaHandler.XmlaExtra extra = getExtra(connection);
4171            for (Catalog catalog : catIter(connection, catNameCond())) {
4172                // By definition, mondrian catalogs have only one
4173                // schema. It is safe to use get(0)
4174                final Schema schema = catalog.getSchemas().get(0);
4175                List<XmlaHandler.XmlaExtra.FunctionDefinition> funDefs =
4176                    new ArrayList<XmlaHandler.XmlaExtra.FunctionDefinition>();
4177
4178                // olap4j does not support describing functions. Call an
4179                // auxiliary method.
4180                extra.getSchemaFunctionList(
4181                    funDefs,
4182                    schema,
4183                    functionNameCond);
4184                for (XmlaHandler.XmlaExtra.FunctionDefinition funDef : funDefs)
4185                {
4186                    Row row = new Row();
4187                    row.set(FunctionName.name, funDef.functionName);
4188                    row.set(Description.name, funDef.description);
4189                    row.set(ParameterList.name, funDef.parameterList);
4190                    row.set(ReturnType.name, funDef.returnType);
4191                    row.set(Origin.name, funDef.origin);
4192                    //row.set(LibraryName.name, "");
4193                    row.set(InterfaceName.name, funDef.interfaceName);
4194                    row.set(Caption.name, funDef.caption);
4195                    addRow(row, rows);
4196                }
4197            }
4198        }
4199
4200        protected void setProperty(
4201            PropertyDefinition propertyDef,
4202            String value)
4203        {
4204            switch (propertyDef) {
4205            case Content:
4206                break;
4207            default:
4208                super.setProperty(propertyDef, value);
4209            }
4210        }
4211    }
4212
4213    static class MdschemaHierarchiesRowset extends Rowset {
4214        private final Util.Functor1<Boolean, Catalog> catalogCond;
4215        private final Util.Functor1<Boolean, Schema> schemaNameCond;
4216        private final Util.Functor1<Boolean, Cube> cubeNameCond;
4217        private final Util.Functor1<Boolean, Dimension> dimensionUnameCond;
4218        private final Util.Functor1<Boolean, Hierarchy> hierarchyUnameCond;
4219        private final Util.Functor1<Boolean, Hierarchy> hierarchyNameCond;
4220
4221        MdschemaHierarchiesRowset(XmlaRequest request, XmlaHandler handler) {
4222            super(MDSCHEMA_HIERARCHIES, request, handler);
4223            catalogCond = makeCondition(CATALOG_NAME_GETTER, CatalogName);
4224            schemaNameCond = makeCondition(SCHEMA_NAME_GETTER, SchemaName);
4225            cubeNameCond = makeCondition(ELEMENT_NAME_GETTER, CubeName);
4226            dimensionUnameCond =
4227                makeCondition(ELEMENT_UNAME_GETTER, DimensionUniqueName);
4228            hierarchyUnameCond =
4229                makeCondition(ELEMENT_UNAME_GETTER, HierarchyUniqueName);
4230            hierarchyNameCond =
4231                makeCondition(ELEMENT_NAME_GETTER, HierarchyName);
4232        }
4233
4234        private static final Column CatalogName =
4235            new Column(
4236                "CATALOG_NAME",
4237                Type.String,
4238                null,
4239                Column.RESTRICTION,
4240                Column.OPTIONAL,
4241                "The name of the catalog to which this hierarchy belongs.");
4242        private static final Column SchemaName =
4243            new Column(
4244                "SCHEMA_NAME",
4245                Type.String,
4246                null,
4247                Column.RESTRICTION,
4248                Column.OPTIONAL,
4249                "Not supported");
4250        private static final Column CubeName =
4251            new Column(
4252                "CUBE_NAME",
4253                Type.String,
4254                null,
4255                Column.RESTRICTION,
4256                Column.REQUIRED,
4257                "The name of the cube to which this hierarchy belongs.");
4258        private static final Column DimensionUniqueName =
4259            new Column(
4260                "DIMENSION_UNIQUE_NAME",
4261                Type.String,
4262                null,
4263                Column.RESTRICTION,
4264                Column.REQUIRED,
4265                "The unique name of the dimension to which this hierarchy "
4266                + "belongs.");
4267        private static final Column HierarchyName =
4268            new Column(
4269                "HIERARCHY_NAME",
4270                Type.String,
4271                null,
4272                Column.RESTRICTION,
4273                Column.REQUIRED,
4274                "The name of the hierarchy. Blank if there is only a single "
4275                + "hierarchy in the dimension.");
4276        private static final Column HierarchyUniqueName =
4277            new Column(
4278                "HIERARCHY_UNIQUE_NAME",
4279                Type.String,
4280                null,
4281                Column.RESTRICTION,
4282                Column.REQUIRED,
4283                "The unique name of the hierarchy.");
4284
4285        private static final Column HierarchyGuid =
4286            new Column(
4287                "HIERARCHY_GUID",
4288                Type.UUID,
4289                null,
4290                Column.NOT_RESTRICTION,
4291                Column.OPTIONAL,
4292                "Hierarchy GUID.");
4293
4294        private static final Column HierarchyCaption =
4295            new Column(
4296                "HIERARCHY_CAPTION",
4297                Type.String,
4298                null,
4299                Column.NOT_RESTRICTION,
4300                Column.REQUIRED,
4301                "A label or a caption associated with the hierarchy.");
4302        private static final Column DimensionType =
4303            new Column(
4304                "DIMENSION_TYPE",
4305                Type.Short,
4306                null,
4307                Column.NOT_RESTRICTION,
4308                Column.REQUIRED,
4309                "The type of the dimension.");
4310        private static final Column HierarchyCardinality =
4311            new Column(
4312                "HIERARCHY_CARDINALITY",
4313                Type.UnsignedInteger,
4314                null,
4315                Column.NOT_RESTRICTION,
4316                Column.REQUIRED,
4317                "The number of members in the hierarchy.");
4318        private static final Column DefaultMember =
4319            new Column(
4320                "DEFAULT_MEMBER",
4321                Type.String,
4322                null,
4323                Column.NOT_RESTRICTION,
4324                Column.OPTIONAL,
4325                "The default member for this hierarchy.");
4326        private static final Column AllMember =
4327            new Column(
4328                "ALL_MEMBER",
4329                Type.String,
4330                null,
4331                Column.NOT_RESTRICTION,
4332                Column.OPTIONAL,
4333                "The member at the highest level of rollup in the hierarchy.");
4334        private static final Column Description =
4335            new Column(
4336                "DESCRIPTION",
4337                Type.String,
4338                null,
4339                Column.NOT_RESTRICTION,
4340                Column.OPTIONAL,
4341                "A human-readable description of the hierarchy. NULL if no "
4342                + "description exists.");
4343        private static final Column Structure =
4344            new Column(
4345                "STRUCTURE",
4346                Type.Short,
4347                null,
4348                Column.NOT_RESTRICTION,
4349                Column.REQUIRED,
4350                "The structure of the hierarchy.");
4351        private static final Column IsVirtual =
4352            new Column(
4353                "IS_VIRTUAL",
4354                Type.Boolean,
4355                null,
4356                Column.NOT_RESTRICTION,
4357                Column.REQUIRED,
4358                "Always returns False.");
4359        private static final Column IsReadWrite =
4360            new Column(
4361                "IS_READWRITE",
4362                Type.Boolean,
4363                null,
4364                Column.NOT_RESTRICTION,
4365                Column.REQUIRED,
4366                "A Boolean that indicates whether the Write Back to dimension "
4367                + "column is enabled.");
4368        private static final Column DimensionUniqueSettings =
4369            new Column(
4370                "DIMENSION_UNIQUE_SETTINGS",
4371                Type.Integer,
4372                null,
4373                Column.NOT_RESTRICTION,
4374                Column.REQUIRED,
4375                "Always returns MDDIMENSIONS_MEMBER_KEY_UNIQUE (1).");
4376        private static final Column DimensionIsVisible =
4377            new Column(
4378                "DIMENSION_IS_VISIBLE",
4379                Type.Boolean,
4380                null,
4381                Column.NOT_RESTRICTION,
4382                Column.REQUIRED,
4383                "A Boolean that indicates whether the parent dimension is visible.");
4384        private static final Column HierarchyIsVisible =
4385            new Column(
4386                "HIERARCHY_IS_VISIBLE",
4387                Type.Boolean,
4388                null,
4389                Column.NOT_RESTRICTION,
4390                Column.REQUIRED,
4391                "A Boolean that indicates whether the hieararchy is visible.");
4392        private static final Column HierarchyOrdinal =
4393            new Column(
4394                "HIERARCHY_ORDINAL",
4395                Type.UnsignedInteger,
4396                null,
4397                Column.NOT_RESTRICTION,
4398                Column.REQUIRED,
4399                "The ordinal number of the hierarchy across all hierarchies of "
4400                + "the cube.");
4401        private static final Column DimensionIsShared =
4402            new Column(
4403                "DIMENSION_IS_SHARED",
4404                Type.Boolean,
4405                null,
4406                Column.NOT_RESTRICTION,
4407                Column.REQUIRED,
4408                "Always returns true.");
4409        private static final Column Levels =
4410            new Column(
4411                "LEVELS",
4412                Type.Rowset,
4413                null,
4414                Column.NOT_RESTRICTION,
4415                Column.OPTIONAL,
4416                "Levels in this hierarchy.");
4417
4418
4419        /*
4420         * NOTE: This is non-standard, where did it come from?
4421         */
4422        private static final Column ParentChild =
4423            new Column(
4424                "PARENT_CHILD",
4425                Type.Boolean,
4426                null,
4427                Column.NOT_RESTRICTION,
4428                Column.OPTIONAL,
4429                "Is hierarchy a parent.");
4430
4431        public void populateImpl(
4432            XmlaResponse response,
4433            OlapConnection connection,
4434            List<Row> rows)
4435            throws XmlaException, SQLException
4436        {
4437            for (Catalog catalog
4438                : catIter(connection, catNameCond(), catalogCond))
4439            {
4440                populateCatalog(connection, catalog, rows);
4441            }
4442        }
4443
4444        protected void populateCatalog(
4445            OlapConnection connection,
4446            Catalog catalog,
4447            List<Row> rows)
4448            throws XmlaException, SQLException
4449        {
4450            for (Schema schema : filter(catalog.getSchemas(), schemaNameCond)) {
4451                for (Cube cube : filteredCubes(schema, cubeNameCond)) {
4452                    populateCube(connection, catalog, cube, rows);
4453                }
4454            }
4455        }
4456
4457        protected void populateCube(
4458            OlapConnection connection,
4459            Catalog catalog,
4460            Cube cube,
4461            List<Row> rows)
4462            throws XmlaException, SQLException
4463        {
4464            int ordinal = 0;
4465            for (Dimension dimension : cube.getDimensions()) {
4466                // Must increment ordinal for all dimensions but
4467                // only output some of them.
4468                boolean genOutput = dimensionUnameCond.apply(dimension);
4469                if (genOutput) {
4470                    populateDimension(
4471                        connection, catalog, cube, dimension, ordinal, rows);
4472                }
4473                ordinal += dimension.getHierarchies().size();
4474            }
4475        }
4476
4477        protected void populateDimension(
4478            OlapConnection connection,
4479            Catalog catalog,
4480            Cube cube,
4481            Dimension dimension,
4482            int ordinal,
4483            List<Row> rows)
4484            throws XmlaException, SQLException
4485        {
4486            final NamedList<Hierarchy> hierarchies = dimension.getHierarchies();
4487            for (Hierarchy hierarchy
4488                : filter(hierarchies, hierarchyNameCond, hierarchyUnameCond))
4489            {
4490                populateHierarchy(
4491                    connection,
4492                    catalog,
4493                    cube,
4494                    dimension,
4495                    hierarchy,
4496                    ordinal + hierarchies.indexOf(hierarchy),
4497                    rows);
4498            }
4499        }
4500
4501        protected void populateHierarchy(
4502            OlapConnection connection,
4503            Catalog catalog,
4504            Cube cube,
4505            Dimension dimension,
4506            Hierarchy hierarchy,
4507            int ordinal,
4508            List<Row> rows)
4509            throws XmlaException, SQLException
4510        {
4511            final XmlaHandler.XmlaExtra extra = getExtra(connection);
4512            String desc = hierarchy.getDescription();
4513            if (desc == null) {
4514                desc =
4515                    cube.getName() + " Cube - "
4516                    + getHierarchyName(hierarchy) + " Hierarchy";
4517            }
4518
4519            Row row = new Row();
4520            row.set(CatalogName.name, catalog.getName());
4521            row.set(SchemaName.name, cube.getSchema().getName());
4522            row.set(CubeName.name, cube.getName());
4523            row.set(DimensionUniqueName.name, dimension.getUniqueName());
4524            row.set(HierarchyName.name, hierarchy.getName());
4525            row.set(HierarchyUniqueName.name, hierarchy.getUniqueName());
4526            //row.set(HierarchyGuid.name, "");
4527
4528            row.set(HierarchyCaption.name, hierarchy.getCaption());
4529            row.set(DimensionType.name, getDimensionType(dimension));
4530            // The number of members in the hierarchy. Because
4531            // of the presence of multiple hierarchies, this number
4532            // might not be the same as DIMENSION_CARDINALITY. This
4533            // value can be an approximation of the real
4534            // cardinality. Consumers should not assume that this
4535            // value is accurate.
4536            int cardinality = extra.getHierarchyCardinality(hierarchy);
4537            row.set(HierarchyCardinality.name, cardinality);
4538
4539            row.set(
4540                DefaultMember.name,
4541                hierarchy.getDefaultMember().getUniqueName());
4542            if (hierarchy.hasAll()) {
4543                row.set(
4544                    AllMember.name,
4545                    hierarchy.getRootMembers().get(0).getUniqueName());
4546            }
4547            row.set(Description.name, desc);
4548
4549            //TODO: only support:
4550            // MD_STRUCTURE_FULLYBALANCED (0)
4551            // MD_STRUCTURE_RAGGEDBALANCED (1)
4552            row.set(Structure.name, extra.getHierarchyStructure(hierarchy));
4553
4554            row.set(IsVirtual.name, false);
4555            row.set(IsReadWrite.name, false);
4556
4557            // NOTE that SQL Server returns '0' not '1'.
4558            row.set(DimensionUniqueSettings.name, 0);
4559
4560            row.set(DimensionIsVisible.name, dimension.isVisible());
4561            row.set(HierarchyIsVisible.name, hierarchy.isVisible());
4562
4563            row.set(HierarchyOrdinal.name, ordinal);
4564
4565            // always true
4566            row.set(DimensionIsShared.name, true);
4567
4568            row.set(ParentChild.name, extra.isHierarchyParentChild(hierarchy));
4569            if (deep) {
4570                row.set(
4571                    Levels.name,
4572                    new MdschemaLevelsRowset(
4573                        wrapRequest(
4574                            request,
4575                            Olap4jUtil.mapOf(
4576                                MdschemaLevelsRowset.CatalogName,
4577                                catalog.getName(),
4578                                MdschemaLevelsRowset.SchemaName,
4579                                cube.getSchema().getName(),
4580                                MdschemaLevelsRowset.CubeName,
4581                                cube.getName(),
4582                                MdschemaLevelsRowset.DimensionUniqueName,
4583                                dimension.getUniqueName(),
4584                                MdschemaLevelsRowset.HierarchyUniqueName,
4585                                hierarchy.getUniqueName())),
4586                        handler));
4587            }
4588            addRow(row, rows);
4589        }
4590
4591        protected void setProperty(
4592            PropertyDefinition propertyDef,
4593            String value)
4594        {
4595            switch (propertyDef) {
4596            case Content:
4597                break;
4598            default:
4599                super.setProperty(propertyDef, value);
4600            }
4601        }
4602    }
4603
4604    static class MdschemaLevelsRowset extends Rowset {
4605        private final Util.Functor1<Boolean, Catalog> catalogCond;
4606        private final Util.Functor1<Boolean, Schema> schemaNameCond;
4607        private final Util.Functor1<Boolean, Cube> cubeNameCond;
4608        private final Util.Functor1<Boolean, Dimension> dimensionUnameCond;
4609        private final Util.Functor1<Boolean, Hierarchy> hierarchyUnameCond;
4610        private final Util.Functor1<Boolean, Level> levelUnameCond;
4611        private final Util.Functor1<Boolean, Level> levelNameCond;
4612
4613        MdschemaLevelsRowset(XmlaRequest request, XmlaHandler handler) {
4614            super(MDSCHEMA_LEVELS, request, handler);
4615            catalogCond = makeCondition(CATALOG_NAME_GETTER, CatalogName);
4616            schemaNameCond = makeCondition(SCHEMA_NAME_GETTER, SchemaName);
4617            cubeNameCond = makeCondition(ELEMENT_NAME_GETTER, CubeName);
4618            dimensionUnameCond =
4619                makeCondition(ELEMENT_UNAME_GETTER, DimensionUniqueName);
4620            hierarchyUnameCond =
4621                makeCondition(ELEMENT_UNAME_GETTER, HierarchyUniqueName);
4622            levelUnameCond =
4623                makeCondition(ELEMENT_UNAME_GETTER, LevelUniqueName);
4624            levelNameCond = makeCondition(ELEMENT_NAME_GETTER, LevelName);
4625        }
4626
4627        public static final int MDLEVEL_TYPE_UNKNOWN = 0x0000;
4628        public static final int MDLEVEL_TYPE_REGULAR = 0x0000;
4629        public static final int MDLEVEL_TYPE_ALL = 0x0001;
4630        public static final int MDLEVEL_TYPE_CALCULATED = 0x0002;
4631        public static final int MDLEVEL_TYPE_TIME = 0x0004;
4632        public static final int MDLEVEL_TYPE_RESERVED1 = 0x0008;
4633        public static final int MDLEVEL_TYPE_TIME_YEARS = 0x0014;
4634        public static final int MDLEVEL_TYPE_TIME_HALF_YEAR = 0x0024;
4635        public static final int MDLEVEL_TYPE_TIME_QUARTERS = 0x0044;
4636        public static final int MDLEVEL_TYPE_TIME_MONTHS = 0x0084;
4637        public static final int MDLEVEL_TYPE_TIME_WEEKS = 0x0104;
4638        public static final int MDLEVEL_TYPE_TIME_DAYS = 0x0204;
4639        public static final int MDLEVEL_TYPE_TIME_HOURS = 0x0304;
4640        public static final int MDLEVEL_TYPE_TIME_MINUTES = 0x0404;
4641        public static final int MDLEVEL_TYPE_TIME_SECONDS = 0x0804;
4642        public static final int MDLEVEL_TYPE_TIME_UNDEFINED = 0x1004;
4643
4644        private static final Column CatalogName =
4645            new Column(
4646                "CATALOG_NAME",
4647                Type.String,
4648                null,
4649                Column.RESTRICTION,
4650                Column.OPTIONAL,
4651                "The name of the catalog to which this level belongs.");
4652        private static final Column SchemaName =
4653            new Column(
4654                "SCHEMA_NAME",
4655                Type.String,
4656                null,
4657                Column.RESTRICTION,
4658                Column.OPTIONAL,
4659                "The name of the schema to which this level belongs.");
4660        private static final Column CubeName =
4661            new Column(
4662                "CUBE_NAME",
4663                Type.String,
4664                null,
4665                Column.RESTRICTION,
4666                Column.REQUIRED,
4667                "The name of the cube to which this level belongs.");
4668        private static final Column DimensionUniqueName =
4669            new Column(
4670                "DIMENSION_UNIQUE_NAME",
4671                Type.String,
4672                null,
4673                Column.RESTRICTION,
4674                Column.REQUIRED,
4675                "The unique name of the dimension to which this level "
4676                + "belongs.");
4677        private static final Column HierarchyUniqueName =
4678            new Column(
4679                "HIERARCHY_UNIQUE_NAME",
4680                Type.String,
4681                null,
4682                Column.RESTRICTION,
4683                Column.REQUIRED,
4684                "The unique name of the hierarchy.");
4685        private static final Column LevelName =
4686            new Column(
4687                "LEVEL_NAME",
4688                Type.String,
4689                null,
4690                Column.RESTRICTION,
4691                Column.REQUIRED,
4692                "The name of the level.");
4693        private static final Column LevelUniqueName =
4694            new Column(
4695                "LEVEL_UNIQUE_NAME",
4696                Type.String,
4697                null,
4698                Column.RESTRICTION,
4699                Column.REQUIRED,
4700                "The properly escaped unique name of the level.");
4701        private static final Column LevelGuid =
4702            new Column(
4703                "LEVEL_GUID",
4704                Type.UUID,
4705                null,
4706                Column.NOT_RESTRICTION,
4707                Column.OPTIONAL,
4708                "Level GUID.");
4709        private static final Column LevelCaption =
4710            new Column(
4711                "LEVEL_CAPTION",
4712                Type.String,
4713                null,
4714                Column.NOT_RESTRICTION,
4715                Column.REQUIRED,
4716                "A label or caption associated with the hierarchy.");
4717        private static final Column LevelNumber =
4718            new Column(
4719                "LEVEL_NUMBER",
4720                Type.UnsignedInteger,
4721                null,
4722                Column.NOT_RESTRICTION,
4723                Column.REQUIRED,
4724                "The distance of the level from the root of the hierarchy. "
4725                + "Root level is zero (0).");
4726        private static final Column LevelCardinality =
4727            new Column(
4728                "LEVEL_CARDINALITY",
4729                Type.UnsignedInteger,
4730                null,
4731                Column.NOT_RESTRICTION,
4732                Column.REQUIRED,
4733                "The number of members in the level. This value can be an "
4734                + "approximation of the real cardinality.");
4735        private static final Column LevelType =
4736            new Column(
4737                "LEVEL_TYPE",
4738                Type.Integer,
4739                null,
4740                Column.NOT_RESTRICTION,
4741                Column.REQUIRED,
4742                "Type of the level");
4743        private static final Column CustomRollupSettings =
4744            new Column(
4745                "CUSTOM_ROLLUP_SETTINGS",
4746                Type.Integer,
4747                null,
4748                Column.NOT_RESTRICTION,
4749                Column.REQUIRED,
4750                "A bitmap that specifies the custom rollup options.");
4751        private static final Column LevelUniqueSettings =
4752            new Column(
4753                "LEVEL_UNIQUE_SETTINGS",
4754                Type.Integer,
4755                null,
4756                Column.NOT_RESTRICTION,
4757                Column.REQUIRED,
4758                "A bitmap that specifies which columns contain unique values, "
4759                + "if the level only has members with unique names or keys.");
4760        private static final Column LevelIsVisible =
4761            new Column(
4762                "LEVEL_IS_VISIBLE",
4763                Type.Boolean,
4764                null,
4765                Column.NOT_RESTRICTION,
4766                Column.REQUIRED,
4767                "A Boolean that indicates whether the level is visible.");
4768        private static final Column Description =
4769            new Column(
4770                "DESCRIPTION",
4771                Type.String,
4772                null,
4773                Column.NOT_RESTRICTION,
4774                Column.OPTIONAL,
4775                "A human-readable description of the level. NULL if no "
4776                + "description exists.");
4777
4778        public void populateImpl(
4779            XmlaResponse response,
4780            OlapConnection connection,
4781            List<Row> rows)
4782            throws XmlaException, SQLException
4783        {
4784            for (Catalog catalog
4785                : catIter(connection, catNameCond(), catalogCond))
4786            {
4787                populateCatalog(connection, catalog, rows);
4788            }
4789        }
4790
4791        protected void populateCatalog(
4792            OlapConnection connection,
4793            Catalog catalog,
4794            List<Row> rows)
4795            throws XmlaException, SQLException
4796        {
4797            for (Schema schema : filter(catalog.getSchemas(), schemaNameCond)) {
4798                for (Cube cube : filteredCubes(schema, cubeNameCond)) {
4799                    populateCube(connection, catalog, cube, rows);
4800                }
4801            }
4802        }
4803
4804        protected void populateCube(
4805            OlapConnection connection,
4806            Catalog catalog,
4807            Cube cube,
4808            List<Row> rows)
4809            throws XmlaException, SQLException
4810        {
4811            for (Dimension dimension
4812                : filter(cube.getDimensions(), dimensionUnameCond))
4813            {
4814                populateDimension(
4815                    connection, catalog, cube, dimension, rows);
4816            }
4817        }
4818
4819        protected void populateDimension(
4820            OlapConnection connection,
4821            Catalog catalog,
4822            Cube cube,
4823            Dimension dimension,
4824            List<Row> rows)
4825            throws XmlaException, SQLException
4826        {
4827            for (Hierarchy hierarchy
4828                : filter(dimension.getHierarchies(), hierarchyUnameCond))
4829            {
4830                populateHierarchy(
4831                    connection, catalog, cube, hierarchy, rows);
4832            }
4833        }
4834
4835        protected void populateHierarchy(
4836            OlapConnection connection,
4837            Catalog catalog,
4838            Cube cube,
4839            Hierarchy hierarchy,
4840            List<Row> rows)
4841            throws XmlaException, SQLException
4842        {
4843            for (Level level
4844                : filter(hierarchy.getLevels(), levelUnameCond, levelNameCond))
4845            {
4846                outputLevel(
4847                    connection, catalog, cube, hierarchy, level, rows);
4848            }
4849        }
4850
4851        /**
4852         * Outputs a level.
4853         *
4854         * @param catalog Catalog name
4855         * @param cube Cube definition
4856         * @param hierarchy Hierarchy
4857         * @param level Level
4858         * @param rows List of rows to output to
4859         * @return whether the level is visible
4860         * @throws XmlaException If error occurs
4861         */
4862        protected boolean outputLevel(
4863            OlapConnection connection,
4864            Catalog catalog,
4865            Cube cube,
4866            Hierarchy hierarchy,
4867            Level level,
4868            List<Row> rows)
4869            throws XmlaException, SQLException
4870        {
4871            final XmlaHandler.XmlaExtra extra = getExtra(connection);
4872            String desc = level.getDescription();
4873            if (desc == null) {
4874                desc =
4875                    cube.getName() + " Cube - "
4876                    + getHierarchyName(hierarchy) + " Hierarchy - "
4877                    + level.getName() + " Level";
4878            }
4879
4880            Row row = new Row();
4881            row.set(CatalogName.name, catalog.getName());
4882            row.set(SchemaName.name, cube.getSchema().getName());
4883            row.set(CubeName.name, cube.getName());
4884            row.set(
4885                DimensionUniqueName.name,
4886                hierarchy.getDimension().getUniqueName());
4887            row.set(HierarchyUniqueName.name, hierarchy.getUniqueName());
4888            row.set(LevelName.name, level.getName());
4889            row.set(LevelUniqueName.name, level.getUniqueName());
4890            //row.set(LevelGuid.name, "");
4891            row.set(LevelCaption.name, level.getCaption());
4892            // see notes on this #getDepth()
4893            row.set(LevelNumber.name, level.getDepth());
4894
4895            // Get level cardinality
4896            // According to microsoft this is:
4897            //   "The number of members in the level."
4898            int n = extra.getLevelCardinality(level);
4899            row.set(LevelCardinality.name, n);
4900
4901            row.set(LevelType.name, getLevelType(level));
4902
4903            // TODO: most of the time this is correct
4904            row.set(CustomRollupSettings.name, 0);
4905
4906            int uniqueSettings = 0;
4907            if (level.getLevelType() == Level.Type.ALL) {
4908                uniqueSettings |= 2;
4909            }
4910            if (extra.isLevelUnique(level)) {
4911                uniqueSettings |= 1;
4912            }
4913            row.set(LevelUniqueSettings.name, uniqueSettings);
4914            row.set(LevelIsVisible.name, level.isVisible());
4915            row.set(Description.name, desc);
4916            addRow(row, rows);
4917            return true;
4918        }
4919
4920        private int getLevelType(Level lev) {
4921            int ret = 0;
4922
4923            switch (lev.getLevelType()) {
4924            case ALL:
4925                ret |= MDLEVEL_TYPE_ALL;
4926                break;
4927            case REGULAR:
4928                ret |= MDLEVEL_TYPE_REGULAR;
4929                break;
4930            case TIME_YEARS:
4931                ret |= MDLEVEL_TYPE_TIME_YEARS;
4932                break;
4933            case TIME_HALF_YEAR:
4934                ret |= MDLEVEL_TYPE_TIME_HALF_YEAR;
4935                break;
4936            case TIME_QUARTERS:
4937                ret |= MDLEVEL_TYPE_TIME_QUARTERS;
4938                break;
4939            case TIME_MONTHS:
4940                ret |= MDLEVEL_TYPE_TIME_MONTHS;
4941                break;
4942            case TIME_WEEKS:
4943                ret |= MDLEVEL_TYPE_TIME_WEEKS;
4944                break;
4945            case TIME_DAYS:
4946                ret |= MDLEVEL_TYPE_TIME_DAYS;
4947                break;
4948            case TIME_HOURS:
4949                ret |= MDLEVEL_TYPE_TIME_HOURS;
4950                break;
4951            case TIME_MINUTES:
4952                ret |= MDLEVEL_TYPE_TIME_MINUTES;
4953                break;
4954            case TIME_SECONDS:
4955                ret |= MDLEVEL_TYPE_TIME_SECONDS;
4956                break;
4957            case TIME_UNDEFINED:
4958                ret |= MDLEVEL_TYPE_TIME_UNDEFINED;
4959                break;
4960            default:
4961                ret |= MDLEVEL_TYPE_UNKNOWN;
4962            }
4963
4964            return ret;
4965        }
4966
4967        protected void setProperty(
4968            PropertyDefinition propertyDef, String value)
4969        {
4970            switch (propertyDef) {
4971            case Content:
4972                break;
4973            default:
4974                super.setProperty(propertyDef, value);
4975            }
4976        }
4977    }
4978
4979
4980    public static class MdschemaMeasuresRowset extends Rowset {
4981        public static final int MDMEASURE_AGGR_UNKNOWN = 0;
4982        public static final int MDMEASURE_AGGR_SUM = 1;
4983        public static final int MDMEASURE_AGGR_COUNT = 2;
4984        public static final int MDMEASURE_AGGR_MIN = 3;
4985        public static final int MDMEASURE_AGGR_MAX = 4;
4986        public static final int MDMEASURE_AGGR_AVG = 5;
4987        public static final int MDMEASURE_AGGR_VAR = 6;
4988        public static final int MDMEASURE_AGGR_STD = 7;
4989        public static final int MDMEASURE_AGGR_CALCULATED = 127;
4990
4991        private final Util.Functor1<Boolean, Catalog> catalogCond;
4992        private final Util.Functor1<Boolean, Schema> schemaNameCond;
4993        private final Util.Functor1<Boolean, Cube> cubeNameCond;
4994        private final Util.Functor1<Boolean, Measure> measureUnameCond;
4995        private final Util.Functor1<Boolean, Measure> measureNameCond;
4996
4997        MdschemaMeasuresRowset(XmlaRequest request, XmlaHandler handler) {
4998            super(MDSCHEMA_MEASURES, request, handler);
4999            catalogCond = makeCondition(CATALOG_NAME_GETTER, CatalogName);
5000            schemaNameCond = makeCondition(SCHEMA_NAME_GETTER, SchemaName);
5001            cubeNameCond = makeCondition(ELEMENT_NAME_GETTER, CubeName);
5002            measureNameCond = makeCondition(ELEMENT_NAME_GETTER, MeasureName);
5003            measureUnameCond =
5004                makeCondition(ELEMENT_UNAME_GETTER, MeasureUniqueName);
5005        }
5006
5007        private static final Column CatalogName =
5008            new Column(
5009                "CATALOG_NAME",
5010                Type.String,
5011                null,
5012                Column.RESTRICTION,
5013                Column.OPTIONAL,
5014                "The name of the catalog to which this measure belongs.");
5015        private static final Column SchemaName =
5016            new Column(
5017                "SCHEMA_NAME",
5018                Type.String,
5019                null,
5020                Column.RESTRICTION,
5021                Column.OPTIONAL,
5022                "The name of the schema to which this measure belongs.");
5023        private static final Column CubeName =
5024            new Column(
5025                "CUBE_NAME",
5026                Type.String,
5027                null,
5028                Column.RESTRICTION,
5029                Column.REQUIRED,
5030                "The name of the cube to which this measure belongs.");
5031        private static final Column MeasureName =
5032            new Column(
5033                "MEASURE_NAME",
5034                Type.String,
5035                null,
5036                Column.RESTRICTION,
5037                Column.REQUIRED,
5038                "The name of the measure.");
5039        private static final Column MeasureUniqueName =
5040            new Column(
5041                "MEASURE_UNIQUE_NAME",
5042                Type.String,
5043                null,
5044                Column.RESTRICTION,
5045                Column.REQUIRED,
5046                "The Unique name of the measure.");
5047        private static final Column MeasureCaption =
5048            new Column(
5049                "MEASURE_CAPTION",
5050                Type.String,
5051                null,
5052                Column.NOT_RESTRICTION,
5053                Column.REQUIRED,
5054                "A label or caption associated with the measure.");
5055        private static final Column MeasureGuid =
5056            new Column(
5057                "MEASURE_GUID",
5058                Type.UUID,
5059                null,
5060                Column.NOT_RESTRICTION,
5061                Column.OPTIONAL,
5062                "Measure GUID.");
5063        private static final Column MeasureAggregator =
5064            new Column(
5065                "MEASURE_AGGREGATOR",
5066                Type.Integer,
5067                null,
5068                Column.NOT_RESTRICTION,
5069                Column.REQUIRED,
5070                "How a measure was derived.");
5071        private static final Column DataType =
5072            new Column(
5073                "DATA_TYPE",
5074                Type.UnsignedShort,
5075                null,
5076                Column.NOT_RESTRICTION,
5077                Column.REQUIRED,
5078                "Data type of the measure.");
5079        private static final Column MeasureIsVisible =
5080            new Column(
5081                "MEASURE_IS_VISIBLE",
5082                Type.Boolean,
5083                null,
5084                Column.NOT_RESTRICTION,
5085                Column.REQUIRED,
5086                "A Boolean that always returns True. If the measure is not "
5087                + "visible, it will not be included in the schema rowset.");
5088        private static final Column LevelsList =
5089            new Column(
5090                "LEVELS_LIST",
5091                Type.String,
5092                null,
5093                Column.NOT_RESTRICTION,
5094                Column.OPTIONAL,
5095                "A string that always returns NULL. EXCEPT that SQL Server "
5096                + "returns non-null values!!!");
5097        private static final Column Description =
5098            new Column(
5099                "DESCRIPTION",
5100                Type.String,
5101                null,
5102                Column.NOT_RESTRICTION,
5103                Column.OPTIONAL,
5104                "A human-readable description of the measure.");
5105        private static final Column FormatString =
5106            new Column(
5107                "DEFAULT_FORMAT_STRING",
5108                Type.String,
5109                null,
5110                Column.NOT_RESTRICTION,
5111                Column.OPTIONAL,
5112                "The default format string for the measure.");
5113
5114        public void populateImpl(
5115            XmlaResponse response,
5116            OlapConnection connection,
5117            List<Row> rows)
5118            throws XmlaException, SQLException
5119        {
5120            for (Catalog catalog
5121                : catIter(connection, catNameCond(), catalogCond))
5122            {
5123                populateCatalog(connection, catalog, rows);
5124            }
5125        }
5126
5127        protected void populateCatalog(
5128            OlapConnection connection,
5129            Catalog catalog,
5130            List<Row> rows)
5131            throws XmlaException, SQLException
5132        {
5133            // SQL Server actually includes the LEVELS_LIST row
5134            StringBuilder buf = new StringBuilder(100);
5135
5136            for (Schema schema : filter(catalog.getSchemas(), schemaNameCond)) {
5137                for (Cube cube : filteredCubes(schema, cubeNameCond)) {
5138                    buf.setLength(0);
5139
5140                    int j = 0;
5141                    for (Dimension dimension : cube.getDimensions()) {
5142                        if (dimension.getDimensionType()
5143                            == Dimension.Type.MEASURE)
5144                        {
5145                            continue;
5146                        }
5147                        for (Hierarchy hierarchy : dimension.getHierarchies()) {
5148                            NamedList<Level> levels = hierarchy.getLevels();
5149                            Level lastLevel = levels.get(levels.size() - 1);
5150                            if (j++ > 0) {
5151                                buf.append(',');
5152                            }
5153                            buf.append(lastLevel.getUniqueName());
5154                        }
5155                    }
5156                    String levelListStr = buf.toString();
5157
5158                    List<Member> calcMembers = new ArrayList<Member>();
5159                    for (Measure measure
5160                        : filter(
5161                            cube.getMeasures(),
5162                            measureNameCond,
5163                            measureUnameCond))
5164                    {
5165                        if (measure.isCalculated()) {
5166                            // Output calculated measures after stored
5167                            // measures.
5168                            calcMembers.add(measure);
5169                        } else {
5170                            populateMember(
5171                                connection, catalog,
5172                                measure, cube, levelListStr, rows);
5173                        }
5174                    }
5175
5176                    for (Member member : calcMembers) {
5177                        populateMember(
5178                            connection, catalog, member, cube, null, rows);
5179                    }
5180                }
5181            }
5182        }
5183
5184        private void populateMember(
5185            OlapConnection connection,
5186            Catalog catalog,
5187            Member member,
5188            Cube cube,
5189            String levelListStr,
5190            List<Row> rows)
5191            throws SQLException
5192        {
5193            Boolean visible =
5194                (Boolean) member.getPropertyValue(
5195                    Property.StandardMemberProperty.$visible);
5196            if (visible == null) {
5197                visible = true;
5198            }
5199            if (!visible && !XmlaUtil.shouldEmitInvisibleMembers(request)) {
5200                return;
5201            }
5202
5203            //TODO: currently this is always null
5204            String desc = member.getDescription();
5205            if (desc == null) {
5206                desc =
5207                    cube.getName() + " Cube - "
5208                    + member.getName() + " Member";
5209            }
5210            final String formatString =
5211                (String) member.getPropertyValue(
5212                    Property.StandardCellProperty.FORMAT_STRING);
5213
5214            Row row = new Row();
5215            row.set(CatalogName.name, catalog.getName());
5216            row.set(SchemaName.name, cube.getSchema().getName());
5217            row.set(CubeName.name, cube.getName());
5218            row.set(MeasureName.name, member.getName());
5219            row.set(MeasureUniqueName.name, member.getUniqueName());
5220            row.set(MeasureCaption.name, member.getCaption());
5221            //row.set(MeasureGuid.name, "");
5222
5223            final XmlaHandler.XmlaExtra extra = getExtra(connection);
5224            row.set(MeasureAggregator.name, extra.getMeasureAggregator(member));
5225
5226            // DATA_TYPE DBType best guess is string
5227            XmlaConstants.DBType dbType = XmlaConstants.DBType.WSTR;
5228            String datatype = (String)
5229                member.getPropertyValue(Property.StandardCellProperty.DATATYPE);
5230            if (datatype != null) {
5231                if (datatype.equals("Integer")) {
5232                    dbType = XmlaConstants.DBType.I4;
5233                } else if (datatype.equals("Numeric")) {
5234                    dbType = XmlaConstants.DBType.R8;
5235                } else {
5236                    dbType = XmlaConstants.DBType.WSTR;
5237                }
5238            }
5239            row.set(DataType.name, dbType.xmlaOrdinal());
5240            row.set(MeasureIsVisible.name, visible);
5241
5242            if (levelListStr != null) {
5243                row.set(LevelsList.name, levelListStr);
5244            }
5245
5246            row.set(Description.name, desc);
5247            row.set(FormatString.name, formatString);
5248            addRow(row, rows);
5249        }
5250
5251        protected void setProperty(
5252            PropertyDefinition propertyDef, String value)
5253        {
5254            switch (propertyDef) {
5255            case Content:
5256                break;
5257            default:
5258                super.setProperty(propertyDef, value);
5259            }
5260        }
5261    }
5262
5263    static class MdschemaMembersRowset extends Rowset {
5264        private final Util.Functor1<Boolean, Catalog> catalogCond;
5265        private final Util.Functor1<Boolean, Schema> schemaNameCond;
5266        private final Util.Functor1<Boolean, Cube> cubeNameCond;
5267        private final Util.Functor1<Boolean, Dimension> dimensionUnameCond;
5268        private final Util.Functor1<Boolean, Hierarchy> hierarchyUnameCond;
5269        private final Util.Functor1<Boolean, Member> memberNameCond;
5270        private final Util.Functor1<Boolean, Member> memberUnameCond;
5271        private final Util.Functor1<Boolean, Member> memberTypeCond;
5272
5273        MdschemaMembersRowset(XmlaRequest request, XmlaHandler handler) {
5274            super(MDSCHEMA_MEMBERS, request, handler);
5275            catalogCond = makeCondition(CATALOG_NAME_GETTER, CatalogName);
5276            schemaNameCond = makeCondition(SCHEMA_NAME_GETTER, SchemaName);
5277            cubeNameCond = makeCondition(ELEMENT_NAME_GETTER, CubeName);
5278            dimensionUnameCond =
5279                makeCondition(ELEMENT_UNAME_GETTER, DimensionUniqueName);
5280            hierarchyUnameCond =
5281                makeCondition(ELEMENT_UNAME_GETTER, HierarchyUniqueName);
5282            memberNameCond = makeCondition(ELEMENT_NAME_GETTER, MemberName);
5283            memberUnameCond =
5284                makeCondition(ELEMENT_UNAME_GETTER, MemberUniqueName);
5285            memberTypeCond = makeCondition(MEMBER_TYPE_GETTER, MemberType);
5286        }
5287
5288        private static final Column CatalogName =
5289            new Column(
5290                "CATALOG_NAME",
5291                Type.String,
5292                null,
5293                Column.RESTRICTION,
5294                Column.OPTIONAL,
5295                "The name of the catalog to which this member belongs.");
5296        private static final Column SchemaName =
5297            new Column(
5298                "SCHEMA_NAME",
5299                Type.String,
5300                null,
5301                Column.RESTRICTION,
5302                Column.OPTIONAL,
5303                "The name of the schema to which this member belongs.");
5304        private static final Column CubeName =
5305            new Column(
5306                "CUBE_NAME",
5307                Type.String,
5308                null,
5309                Column.RESTRICTION,
5310                Column.REQUIRED,
5311                "Name of the cube to which this member belongs.");
5312        private static final Column DimensionUniqueName =
5313            new Column(
5314                "DIMENSION_UNIQUE_NAME",
5315                Type.String,
5316                null,
5317                Column.RESTRICTION,
5318                Column.REQUIRED,
5319                "Unique name of the dimension to which this member belongs.");
5320        private static final Column HierarchyUniqueName =
5321            new Column(
5322                "HIERARCHY_UNIQUE_NAME",
5323                Type.String,
5324                null,
5325                Column.RESTRICTION,
5326                Column.REQUIRED,
5327                "Unique name of the hierarchy. If the member belongs to more "
5328                + "than one hierarchy, there is one row for each hierarchy to "
5329                + "which it belongs.");
5330        private static final Column LevelUniqueName =
5331            new Column(
5332                "LEVEL_UNIQUE_NAME",
5333                Type.String,
5334                null,
5335                Column.RESTRICTION,
5336                Column.REQUIRED,
5337                " Unique name of the level to which the member belongs.");
5338        private static final Column LevelNumber =
5339            new Column(
5340                "LEVEL_NUMBER",
5341                Type.UnsignedInteger,
5342                null,
5343                Column.RESTRICTION,
5344                Column.REQUIRED,
5345                "The distance of the member from the root of the hierarchy.");
5346        private static final Column MemberOrdinal =
5347            new Column(
5348                "MEMBER_ORDINAL",
5349                Type.UnsignedInteger,
5350                null,
5351                Column.NOT_RESTRICTION,
5352                Column.REQUIRED,
5353                "Ordinal number of the member. Sort rank of the member when "
5354                + "members of this dimension are sorted in their natural sort "
5355                + "order. If providers do not have the concept of natural "
5356                + "ordering, this should be the rank when sorted by "
5357                + "MEMBER_NAME.");
5358        private static final Column MemberName =
5359            new Column(
5360                "MEMBER_NAME",
5361                Type.String,
5362                null,
5363                Column.RESTRICTION,
5364                Column.REQUIRED,
5365                "Name of the member.");
5366        private static final Column MemberUniqueName =
5367            new Column(
5368                "MEMBER_UNIQUE_NAME",
5369                Type.StringSometimesArray,
5370                null,
5371                Column.RESTRICTION,
5372                Column.REQUIRED,
5373                " Unique name of the member.");
5374        private static final Column MemberType =
5375            new Column(
5376                "MEMBER_TYPE",
5377                Type.Integer,
5378                null,
5379                Column.RESTRICTION,
5380                Column.REQUIRED,
5381                "Type of the member.");
5382        private static final Column MemberGuid =
5383            new Column(
5384                "MEMBER_GUID",
5385                Type.UUID,
5386                null,
5387                Column.NOT_RESTRICTION,
5388                Column.OPTIONAL,
5389                "Memeber GUID.");
5390        private static final Column MemberCaption =
5391            new Column(
5392                "MEMBER_CAPTION",
5393                Type.String,
5394                null,
5395                Column.RESTRICTION,
5396                Column.REQUIRED,
5397                "A label or caption associated with the member.");
5398        private static final Column ChildrenCardinality =
5399            new Column(
5400                "CHILDREN_CARDINALITY",
5401                Type.UnsignedInteger,
5402                null,
5403                Column.NOT_RESTRICTION,
5404                Column.REQUIRED,
5405                "Number of children that the member has.");
5406        private static final Column ParentLevel =
5407            new Column(
5408                "PARENT_LEVEL",
5409                Type.UnsignedInteger,
5410                null,
5411                Column.NOT_RESTRICTION,
5412                Column.REQUIRED,
5413                "The distance of the member's parent from the root level of "
5414                + "the hierarchy.");
5415        private static final Column ParentUniqueName =
5416            new Column(
5417                "PARENT_UNIQUE_NAME",
5418                Type.String,
5419                null,
5420                Column.NOT_RESTRICTION,
5421                Column.OPTIONAL,
5422                "Unique name of the member's parent.");
5423        private static final Column ParentCount =
5424            new Column(
5425                "PARENT_COUNT",
5426                Type.UnsignedInteger,
5427                null,
5428                Column.NOT_RESTRICTION,
5429                Column.REQUIRED,
5430                "Number of parents that this member has.");
5431        private static final Column TreeOp_ =
5432            new Column(
5433                "TREE_OP",
5434                Type.Enumeration,
5435                Enumeration.TREE_OP,
5436                Column.RESTRICTION,
5437                Column.OPTIONAL,
5438                "Tree Operation");
5439        /* Mondrian specified member properties. */
5440        private static final Column Depth =
5441            new Column(
5442                "DEPTH",
5443                Type.Integer,
5444                null,
5445                Column.NOT_RESTRICTION,
5446                Column.OPTIONAL,
5447                "depth");
5448
5449        public void populateImpl(
5450            XmlaResponse response,
5451            OlapConnection connection,
5452            List<Row> rows)
5453            throws XmlaException, SQLException
5454        {
5455            for (Catalog catalog
5456                : catIter(connection, catNameCond(), catalogCond))
5457            {
5458                populateCatalog(connection, catalog, rows);
5459            }
5460        }
5461
5462        protected void populateCatalog(
5463            OlapConnection connection,
5464            Catalog catalog,
5465            List<Row> rows)
5466            throws XmlaException, SQLException
5467        {
5468            for (Schema schema : filter(catalog.getSchemas(), schemaNameCond)) {
5469                for (Cube cube : filteredCubes(schema, cubeNameCond)) {
5470                    if (isRestricted(MemberUniqueName)) {
5471                        // NOTE: it is believed that if MEMBER_UNIQUE_NAME is
5472                        // a restriction, then none of the remaining possible
5473                        // restrictions other than TREE_OP are relevant
5474                        // (or allowed??).
5475                        outputUniqueMemberName(
5476                            connection, catalog, cube, rows);
5477                    } else {
5478                        populateCube(connection, catalog, cube, rows);
5479                    }
5480                }
5481            }
5482        }
5483
5484        protected void populateCube(
5485            OlapConnection connection,
5486            Catalog catalog,
5487            Cube cube,
5488            List<Row> rows)
5489            throws XmlaException, SQLException
5490        {
5491            if (isRestricted(LevelUniqueName)) {
5492                // Note: If the LEVEL_UNIQUE_NAME has been specified, then
5493                // the dimension and hierarchy are specified implicitly.
5494                String levelUniqueName =
5495                    getRestrictionValueAsString(LevelUniqueName);
5496                if (levelUniqueName == null) {
5497                    // The query specified two or more unique names
5498                    // which means that nothing will match.
5499                    return;
5500                }
5501
5502                Level level = lookupLevel(cube, levelUniqueName);
5503                if (level != null) {
5504                    // Get members of this level, without access control, but
5505                    // including calculated members.
5506                    List<Member> members = level.getMembers();
5507                    outputMembers(connection, members, catalog, cube, rows);
5508                }
5509            } else {
5510                for (Dimension dimension
5511                    : filter(cube.getDimensions(), dimensionUnameCond))
5512                {
5513                    populateDimension(
5514                        connection, catalog, cube, dimension, rows);
5515                }
5516            }
5517        }
5518
5519        protected void populateDimension(
5520            OlapConnection connection,
5521            Catalog catalog,
5522            Cube cube,
5523            Dimension dimension,
5524            List<Row> rows)
5525            throws XmlaException, SQLException
5526        {
5527            for (Hierarchy hierarchy
5528                : filter(dimension.getHierarchies(), hierarchyUnameCond))
5529            {
5530                populateHierarchy(
5531                    connection, catalog, cube, hierarchy, rows);
5532            }
5533        }
5534
5535        protected void populateHierarchy(
5536            OlapConnection connection,
5537            Catalog catalog,
5538            Cube cube,
5539            Hierarchy hierarchy,
5540            List<Row> rows)
5541            throws XmlaException, SQLException
5542        {
5543            if (isRestricted(LevelNumber)) {
5544                int levelNumber = getRestrictionValueAsInt(LevelNumber);
5545                if (levelNumber == -1) {
5546                    LOGGER.warn(
5547                        "RowsetDefinition.populateHierarchy: "
5548                        + "LevelNumber invalid");
5549                    return;
5550                }
5551                NamedList<Level> levels = hierarchy.getLevels();
5552                if (levelNumber >= levels.size()) {
5553                    LOGGER.warn(
5554                        "RowsetDefinition.populateHierarchy: "
5555                        + "LevelNumber ("
5556                        + levelNumber
5557                        + ") is greater than number of levels ("
5558                        + levels.size()
5559                        + ") for hierarchy \""
5560                        + hierarchy.getUniqueName()
5561                        + "\"");
5562                    return;
5563                }
5564
5565                Level level = levels.get(levelNumber);
5566                List<Member> members = level.getMembers();
5567                outputMembers(connection, members, catalog, cube, rows);
5568            } else {
5569                // At this point we get ALL of the members associated with
5570                // the Hierarchy (rather than getting them one at a time).
5571                // The value returned is not used at this point but they are
5572                // now cached in the SchemaReader.
5573                for (Level level : hierarchy.getLevels()) {
5574                    outputMembers(
5575                        connection, level.getMembers(),
5576                        catalog, cube, rows);
5577                }
5578            }
5579        }
5580
5581        /**
5582         * Returns whether a value contains all of the bits in a mask.
5583         */
5584        private static boolean mask(int value, int mask) {
5585            return (value & mask) == mask;
5586        }
5587
5588        /**
5589         * Adds a member to a result list and, depending upon the
5590         * <code>treeOp</code> parameter, other relatives of the member. This
5591         * method recursively invokes itself to walk up, down, or across the
5592         * hierarchy.
5593         */
5594        private void populateMember(
5595            OlapConnection connection,
5596            Catalog catalog,
5597            Cube cube,
5598            Member member,
5599            int treeOp,
5600            List<Row> rows)
5601            throws SQLException
5602        {
5603            // Visit node itself.
5604            if (mask(treeOp, TreeOp.SELF.xmlaOrdinal())) {
5605                outputMember(connection, member, catalog, cube, rows);
5606            }
5607            // Visit node's siblings (not including itself).
5608            if (mask(treeOp, TreeOp.SIBLINGS.xmlaOrdinal())) {
5609                final List<Member> siblings;
5610                final Member parent = member.getParentMember();
5611                if (parent == null) {
5612                    siblings = member.getHierarchy().getRootMembers();
5613                } else {
5614                    siblings = Olap4jUtil.cast(parent.getChildMembers());
5615                }
5616                for (Member sibling : siblings) {
5617                    if (sibling.equals(member)) {
5618                        continue;
5619                    }
5620                    populateMember(
5621                        connection, catalog,
5622                        cube, sibling,
5623                        TreeOp.SELF.xmlaOrdinal(), rows);
5624                }
5625            }
5626            // Visit node's descendants or its immediate children, but not both.
5627            if (mask(treeOp, TreeOp.DESCENDANTS.xmlaOrdinal())) {
5628                for (Member child : member.getChildMembers()) {
5629                    populateMember(
5630                        connection, catalog,
5631                        cube, child,
5632                        TreeOp.SELF.xmlaOrdinal() |
5633                        TreeOp.DESCENDANTS.xmlaOrdinal(),
5634                        rows);
5635                }
5636            } else if (mask(
5637                    treeOp, TreeOp.CHILDREN.xmlaOrdinal()))
5638            {
5639                for (Member child : member.getChildMembers()) {
5640                    populateMember(
5641                        connection, catalog,
5642                        cube, child,
5643                        TreeOp.SELF.xmlaOrdinal(), rows);
5644                }
5645            }
5646            // Visit node's ancestors or its immediate parent, but not both.
5647            if (mask(treeOp, TreeOp.ANCESTORS.xmlaOrdinal())) {
5648                final Member parent = member.getParentMember();
5649                if (parent != null) {
5650                    populateMember(
5651                        connection, catalog,
5652                        cube, parent,
5653                        TreeOp.SELF.xmlaOrdinal() |
5654                        TreeOp.ANCESTORS.xmlaOrdinal(), rows);
5655                }
5656            } else if (mask(treeOp, TreeOp.PARENT.xmlaOrdinal())) {
5657                final Member parent = member.getParentMember();
5658                if (parent != null) {
5659                    populateMember(
5660                        connection, catalog,
5661                        cube, parent,
5662                        TreeOp.SELF.xmlaOrdinal(), rows);
5663                }
5664            }
5665        }
5666
5667        protected ArrayList<Column> pruneRestrictions(ArrayList<Column> list) {
5668            // If they've restricted TreeOp, we don't want to literally filter
5669            // the result on TreeOp (because it's not an output column) or
5670            // on MemberUniqueName (because TreeOp will have caused us to
5671            // generate other members than the one asked for).
5672            if (list.contains(TreeOp_)) {
5673                list.remove(TreeOp_);
5674                list.remove(MemberUniqueName);
5675            }
5676            return list;
5677        }
5678
5679        private void outputMembers(
5680            OlapConnection connection,
5681            List<Member> members,
5682            final Catalog catalog,
5683            Cube cube,
5684            List<Row> rows)
5685            throws SQLException
5686        {
5687            for (Member member : members) {
5688                outputMember(connection, member, catalog, cube, rows);
5689            }
5690        }
5691
5692        private void outputUniqueMemberName(
5693            final OlapConnection connection,
5694            final Catalog catalog,
5695            Cube cube,
5696            List<Row> rows)
5697            throws SQLException
5698        {
5699            final Object unameRestrictions =
5700                restrictions.get(MemberUniqueName.name);
5701            List<String> list;
5702            if (unameRestrictions instanceof String) {
5703                list = Collections.singletonList((String) unameRestrictions);
5704            } else {
5705                list = (List<String>) unameRestrictions;
5706            }
5707            for (String memberUniqueName : list) {
5708                final IdentifierNode identifierNode =
5709                    IdentifierNode.parseIdentifier(memberUniqueName);
5710                Member member =
5711                    cube.lookupMember(identifierNode.getSegmentList());
5712                if (member == null) {
5713                    return;
5714                }
5715                if (isRestricted(TreeOp_)) {
5716                    int treeOp = getRestrictionValueAsInt(TreeOp_);
5717                    if (treeOp == -1) {
5718                        return;
5719                    }
5720                    populateMember(
5721                        connection, catalog,
5722                        cube, member, treeOp, rows);
5723                } else {
5724                    outputMember(connection, member, catalog, cube, rows);
5725                }
5726            }
5727        }
5728
5729        private void outputMember(
5730            OlapConnection connection,
5731            Member member,
5732            final Catalog catalog,
5733            Cube cube,
5734            List<Row> rows)
5735            throws SQLException
5736        {
5737            if (!memberNameCond.apply(member)) {
5738                return;
5739            }
5740            if (!memberTypeCond.apply(member)) {
5741                return;
5742            }
5743
5744            getExtra(connection).checkMemberOrdinal(member);
5745
5746            // Check whether the member is visible, otherwise do not dump.
5747            Boolean visible =
5748                (Boolean) member.getPropertyValue(
5749                    Property.StandardMemberProperty.$visible);
5750            if (visible == null) {
5751                visible = true;
5752            }
5753            if (!visible && !XmlaUtil.shouldEmitInvisibleMembers(request)) {
5754                return;
5755            }
5756
5757            final Level level = member.getLevel();
5758            final Hierarchy hierarchy = level.getHierarchy();
5759            final Dimension dimension = hierarchy.getDimension();
5760
5761            int adjustedLevelDepth = level.getDepth();
5762
5763            Row row = new Row();
5764            row.set(CatalogName.name, catalog.getName());
5765            row.set(SchemaName.name, cube.getSchema().getName());
5766            row.set(CubeName.name, cube.getName());
5767            row.set(DimensionUniqueName.name, dimension.getUniqueName());
5768            row.set(HierarchyUniqueName.name, hierarchy.getUniqueName());
5769            row.set(LevelUniqueName.name, level.getUniqueName());
5770            row.set(LevelNumber.name, adjustedLevelDepth);
5771            row.set(MemberOrdinal.name, member.getOrdinal());
5772            row.set(MemberName.name, member.getName());
5773            row.set(MemberUniqueName.name, member.getUniqueName());
5774            row.set(MemberType.name, member.getMemberType().ordinal());
5775            //row.set(MemberGuid.name, "");
5776            row.set(MemberCaption.name, member.getCaption());
5777            row.set(
5778                ChildrenCardinality.name,
5779                member.getPropertyValue(
5780                    Property.StandardMemberProperty.CHILDREN_CARDINALITY));
5781            row.set(ChildrenCardinality.name, 100);
5782
5783            if (adjustedLevelDepth == 0) {
5784                row.set(ParentLevel.name, 0);
5785            } else {
5786                row.set(ParentLevel.name, adjustedLevelDepth - 1);
5787                final Member parentMember = member.getParentMember();
5788                if (parentMember != null) {
5789                    row.set(
5790                        ParentUniqueName.name, parentMember.getUniqueName());
5791                }
5792            }
5793
5794            row.set(ParentCount.name, member.getParentMember() == null ? 0 : 1);
5795
5796            row.set(Depth.name, member.getDepth());
5797            addRow(row, rows);
5798        }
5799
5800        protected void setProperty(
5801            PropertyDefinition propertyDef,
5802            String value)
5803        {
5804            switch (propertyDef) {
5805            case Content:
5806                break;
5807            default:
5808                super.setProperty(propertyDef, value);
5809            }
5810        }
5811    }
5812
5813    static class MdschemaSetsRowset extends Rowset {
5814        private final Util.Functor1<Boolean, Catalog> catalogCond;
5815        private final Util.Functor1<Boolean, Schema> schemaNameCond;
5816        private final Util.Functor1<Boolean, Cube> cubeNameCond;
5817        private final Util.Functor1<Boolean, NamedSet> setUnameCond;
5818        private static final String GLOBAL_SCOPE = "1";
5819
5820        MdschemaSetsRowset(XmlaRequest request, XmlaHandler handler) {
5821            super(MDSCHEMA_SETS, request, handler);
5822            catalogCond = makeCondition(CATALOG_NAME_GETTER, CatalogName);
5823            schemaNameCond = makeCondition(SCHEMA_NAME_GETTER, SchemaName);
5824            cubeNameCond = makeCondition(ELEMENT_NAME_GETTER, CubeName);
5825            setUnameCond = makeCondition(ELEMENT_UNAME_GETTER, SetName);
5826        }
5827
5828        private static final Column CatalogName =
5829            new Column(
5830                "CATALOG_NAME",
5831                Type.String,
5832                null,
5833                true,
5834                true,
5835                null);
5836        private static final Column SchemaName =
5837            new Column(
5838                "SCHEMA_NAME",
5839                Type.String,
5840                null,
5841                true,
5842                true,
5843                null);
5844        private static final Column CubeName =
5845            new Column(
5846                "CUBE_NAME",
5847                Type.String,
5848                null,
5849                true,
5850                false,
5851                null);
5852        private static final Column SetName =
5853            new Column(
5854                "SET_NAME",
5855                Type.String,
5856                null,
5857                true,
5858                false,
5859                null);
5860        private static final Column SetCaption =
5861            new Column(
5862                "SET_CAPTION",
5863                Type.String,
5864                null,
5865                true,
5866                true,
5867                null);
5868        private static final Column Scope =
5869            new Column(
5870                "SCOPE",
5871                Type.Integer,
5872                null,
5873                true,
5874                false,
5875                null);
5876        private static final Column Description =
5877            new Column(
5878                "DESCRIPTION",
5879                Type.String,
5880                null,
5881                false,
5882                true,
5883                "A human-readable description of the measure.");
5884
5885        public void populateImpl(
5886            XmlaResponse response,
5887            OlapConnection connection,
5888            List<Row> rows)
5889            throws XmlaException, OlapException
5890        {
5891            for (Catalog catalog
5892                : catIter(connection, catNameCond(), catalogCond))
5893            {
5894                processCatalog(connection, catalog, rows);
5895            }
5896        }
5897
5898        private void processCatalog(
5899            OlapConnection connection,
5900            Catalog catalog,
5901            List<Row> rows)
5902            throws OlapException
5903        {
5904            for (Schema schema : filter(catalog.getSchemas(), schemaNameCond)) {
5905                for (Cube cube : filter(sortedCubes(schema), cubeNameCond)) {
5906                    populateNamedSets(cube, catalog, rows);
5907                }
5908            }
5909        }
5910
5911        private void populateNamedSets(
5912            Cube cube,
5913            Catalog catalog,
5914            List<Row> rows)
5915        {
5916            for (NamedSet namedSet : filter(cube.getSets(), setUnameCond)) {
5917                Row row = new Row();
5918                row.set(CatalogName.name, catalog.getName());
5919                row.set(SchemaName.name, cube.getSchema().getName());
5920                row.set(CubeName.name, cube.getName());
5921                row.set(SetName.name, namedSet.getUniqueName());
5922                row.set(Scope.name, GLOBAL_SCOPE);
5923                row.set(Description.name, namedSet.getDescription());
5924                addRow(row, rows);
5925            }
5926        }
5927    }
5928
5929    static class MdschemaPropertiesRowset extends Rowset {
5930        private final Util.Functor1<Boolean, Catalog> catalogCond;
5931        private final Util.Functor1<Boolean, Schema> schemaNameCond;
5932        private final Util.Functor1<Boolean, Cube> cubeNameCond;
5933        private final Util.Functor1<Boolean, Dimension> dimensionUnameCond;
5934        private final Util.Functor1<Boolean, Hierarchy> hierarchyUnameCond;
5935        private final Util.Functor1<Boolean, Property> propertyNameCond;
5936
5937        MdschemaPropertiesRowset(XmlaRequest request, XmlaHandler handler) {
5938            super(MDSCHEMA_PROPERTIES, request, handler);
5939            catalogCond = makeCondition(CATALOG_NAME_GETTER, CatalogName);
5940            schemaNameCond = makeCondition(SCHEMA_NAME_GETTER, SchemaName);
5941            cubeNameCond = makeCondition(ELEMENT_NAME_GETTER, CubeName);
5942            dimensionUnameCond =
5943                makeCondition(ELEMENT_UNAME_GETTER, DimensionUniqueName);
5944            hierarchyUnameCond =
5945                makeCondition(ELEMENT_UNAME_GETTER, HierarchyUniqueName);
5946            propertyNameCond = makeCondition(ELEMENT_NAME_GETTER, PropertyName);
5947        }
5948
5949        private static final Column CatalogName =
5950            new Column(
5951                "CATALOG_NAME",
5952                Type.String,
5953                null,
5954                Column.RESTRICTION,
5955                Column.OPTIONAL,
5956                "The name of the database.");
5957        private static final Column SchemaName =
5958            new Column(
5959                "SCHEMA_NAME",
5960                Type.String,
5961                null,
5962                Column.RESTRICTION,
5963                Column.OPTIONAL,
5964                "The name of the schema to which this property belongs.");
5965        private static final Column CubeName =
5966            new Column(
5967                "CUBE_NAME",
5968                Type.String,
5969                null,
5970                Column.RESTRICTION,
5971                Column.OPTIONAL,
5972                "The name of the cube.");
5973        private static final Column DimensionUniqueName =
5974            new Column(
5975                "DIMENSION_UNIQUE_NAME",
5976                Type.String,
5977                null,
5978                Column.RESTRICTION,
5979                Column.OPTIONAL,
5980                "The unique name of the dimension.");
5981        private static final Column HierarchyUniqueName =
5982            new Column(
5983                "HIERARCHY_UNIQUE_NAME",
5984                Type.String,
5985                null,
5986                Column.RESTRICTION,
5987                Column.OPTIONAL,
5988                "The unique name of the hierarchy.");
5989        private static final Column LevelUniqueName =
5990            new Column(
5991                "LEVEL_UNIQUE_NAME",
5992                Type.String,
5993                null,
5994                Column.RESTRICTION,
5995                Column.OPTIONAL,
5996                "The unique name of the level to which this property belongs.");
5997        // According to MS this should not be nullable
5998        private static final Column MemberUniqueName =
5999            new Column(
6000                "MEMBER_UNIQUE_NAME",
6001                Type.String,
6002                null,
6003                Column.RESTRICTION,
6004                Column.OPTIONAL,
6005                "The unique name of the member to which the property belongs.");
6006        private static final Column PropertyName =
6007            new Column(
6008                "PROPERTY_NAME",
6009                Type.String,
6010                null,
6011                Column.RESTRICTION,
6012                Column.REQUIRED,
6013                "Name of the property.");
6014        private static final Column PropertyType =
6015            new Column(
6016                "PROPERTY_TYPE",
6017                Type.Short,
6018                null,
6019                Column.RESTRICTION,
6020                Column.REQUIRED,
6021                "A bitmap that specifies the type of the property");
6022        private static final Column PropertyCaption =
6023            new Column(
6024                "PROPERTY_CAPTION",
6025                Type.String,
6026                null,
6027                Column.NOT_RESTRICTION,
6028                Column.REQUIRED,
6029                "A label or caption associated with the property, used "
6030                + "primarily for display purposes.");
6031        private static final Column DataType =
6032            new Column(
6033                "DATA_TYPE",
6034                Type.UnsignedShort,
6035                null,
6036                Column.NOT_RESTRICTION,
6037                Column.REQUIRED,
6038                "Data type of the property.");
6039        private static final Column PropertyContentType =
6040            new Column(
6041                "PROPERTY_CONTENT_TYPE",
6042                Type.Short,
6043                null,
6044                Column.RESTRICTION,
6045                Column.OPTIONAL,
6046                "The type of the property.");
6047        private static final Column Description =
6048            new Column(
6049                "DESCRIPTION",
6050                Type.String,
6051                null,
6052                Column.NOT_RESTRICTION,
6053                Column.OPTIONAL,
6054                "A human-readable description of the measure.");
6055
6056        protected boolean needConnection() {
6057            return false;
6058        }
6059
6060        public void populateImpl(
6061            XmlaResponse response,
6062            OlapConnection connection,
6063            List<Row> rows)
6064            throws XmlaException, SQLException
6065        {
6066            // Default PROPERTY_TYPE is MDPROP_MEMBER.
6067            @SuppressWarnings({"unchecked"})
6068            final List<String> list =
6069                (List<String>) restrictions.get(PropertyType.name);
6070            Set<Property.TypeFlag> typeFlags;
6071            if (list == null) {
6072                typeFlags =
6073                    Olap4jUtil.enumSetOf(
6074                        Property.TypeFlag.MEMBER);
6075            } else {
6076                typeFlags =
6077                    Property.TypeFlag.getDictionary().forMask(
6078                        Integer.valueOf(list.get(0)));
6079            }
6080
6081            for (Property.TypeFlag typeFlag : typeFlags) {
6082                switch (typeFlag) {
6083                case MEMBER:
6084                    populateMember(rows);
6085                    break;
6086                case CELL:
6087                    populateCell(rows);
6088                    break;
6089                case SYSTEM:
6090                case BLOB:
6091                default:
6092                    break;
6093                }
6094            }
6095        }
6096
6097        private void populateCell(List<Row> rows) {
6098            for (Property.StandardCellProperty property
6099                : Property.StandardCellProperty.values())
6100            {
6101                Row row = new Row();
6102                row.set(
6103                    PropertyType.name,
6104                    Property.TypeFlag.getDictionary()
6105                        .toMask(
6106                            property.getType()));
6107                row.set(PropertyName.name, property.name());
6108                row.set(PropertyCaption.name, property.getCaption());
6109                row.set(DataType.name, property.getDatatype().xmlaOrdinal());
6110                addRow(row, rows);
6111            }
6112        }
6113
6114        private void populateMember(List<Row> rows) throws SQLException {
6115            OlapConnection connection =
6116                handler.getConnection(
6117                    request,
6118                    Collections.<String, String>emptyMap());
6119            for (Catalog catalog
6120                : catIter(connection, catNameCond(), catalogCond))
6121            {
6122                populateCatalog(catalog, rows);
6123            }
6124        }
6125
6126        protected void populateCatalog(
6127            Catalog catalog,
6128            List<Row> rows)
6129            throws XmlaException, SQLException
6130        {
6131            for (Schema schema : filter(catalog.getSchemas(), schemaNameCond)) {
6132                for (Cube cube : filteredCubes(schema, cubeNameCond)) {
6133                    populateCube(catalog, cube, rows);
6134                }
6135            }
6136        }
6137
6138        protected void populateCube(
6139            Catalog catalog,
6140            Cube cube,
6141            List<Row> rows)
6142            throws XmlaException, SQLException
6143        {
6144            if (cube instanceof SharedDimensionHolderCube) {
6145                return;
6146            }
6147            if (isRestricted(LevelUniqueName)) {
6148                // Note: If the LEVEL_UNIQUE_NAME has been specified, then
6149                // the dimension and hierarchy are specified implicitly.
6150                String levelUniqueName =
6151                    getRestrictionValueAsString(LevelUniqueName);
6152                if (levelUniqueName == null) {
6153                    // The query specified two or more unique names
6154                    // which means that nothing will match.
6155                    return;
6156                }
6157                Level level = lookupLevel(cube, levelUniqueName);
6158                if (level == null) {
6159                    return;
6160                }
6161                populateLevel(
6162                    catalog, cube, level, rows);
6163            } else {
6164                for (Dimension dimension
6165                    : filter(cube.getDimensions(), dimensionUnameCond))
6166                {
6167                    populateDimension(
6168                        catalog, cube, dimension, rows);
6169                }
6170            }
6171        }
6172
6173        private void populateDimension(
6174            Catalog catalog,
6175            Cube cube,
6176            Dimension dimension,
6177            List<Row> rows)
6178            throws SQLException
6179        {
6180            for (Hierarchy hierarchy
6181                : filter(dimension.getHierarchies(), hierarchyUnameCond))
6182            {
6183                populateHierarchy(
6184                    catalog, cube, hierarchy, rows);
6185            }
6186        }
6187
6188        private void populateHierarchy(
6189            Catalog catalog,
6190            Cube cube,
6191            Hierarchy hierarchy,
6192            List<Row> rows)
6193            throws SQLException
6194        {
6195            for (Level level : hierarchy.getLevels()) {
6196                populateLevel(catalog, cube, level, rows);
6197            }
6198        }
6199
6200        private void populateLevel(
6201            Catalog catalog,
6202            Cube cube,
6203            Level level,
6204            List<Row> rows)
6205            throws SQLException
6206        {
6207            final XmlaHandler.XmlaExtra extra =
6208                getExtra(catalog.getMetaData().getConnection());
6209            for (Property property
6210                : filter(extra.getLevelProperties(level), propertyNameCond))
6211            {
6212                if (extra.isPropertyInternal(property)) {
6213                    continue;
6214                }
6215                outputProperty(
6216                    property, catalog, cube, level, rows);
6217            }
6218        }
6219
6220        private void outputProperty(
6221            Property property,
6222            Catalog catalog,
6223            Cube cube,
6224            Level level,
6225            List<Row> rows)
6226        {
6227            Hierarchy hierarchy = level.getHierarchy();
6228            Dimension dimension = hierarchy.getDimension();
6229
6230            String propertyName = property.getName();
6231
6232            Row row = new Row();
6233            row.set(CatalogName.name, catalog.getName());
6234            row.set(SchemaName.name, cube.getSchema().getName());
6235            row.set(CubeName.name, cube.getName());
6236            row.set(DimensionUniqueName.name, dimension.getUniqueName());
6237            row.set(HierarchyUniqueName.name, hierarchy.getUniqueName());
6238            row.set(LevelUniqueName.name, level.getUniqueName());
6239            //TODO: what is the correct value here
6240            //row.set(MemberUniqueName.name, "");
6241
6242            row.set(PropertyName.name, propertyName);
6243            // Only member properties now
6244            row.set(
6245                PropertyType.name,
6246                Property.TypeFlag.MEMBER.xmlaOrdinal());
6247            row.set(
6248                PropertyContentType.name,
6249                Property.ContentType.REGULAR.xmlaOrdinal());
6250            row.set(PropertyCaption.name, property.getCaption());
6251            XmlaConstants.DBType dbType = getDBTypeFromProperty(property);
6252            row.set(DataType.name, dbType.xmlaOrdinal());
6253
6254            String desc =
6255                cube.getName() + " Cube - "
6256                + getHierarchyName(hierarchy) + " Hierarchy - "
6257                + level.getName() + " Level - "
6258                + property.getName() + " Property";
6259            row.set(Description.name, desc);
6260
6261            addRow(row, rows);
6262        }
6263
6264        protected void setProperty(
6265            PropertyDefinition propertyDef,
6266            String value)
6267        {
6268            switch (propertyDef) {
6269            case Content:
6270                break;
6271            default:
6272                super.setProperty(propertyDef, value);
6273            }
6274        }
6275    }
6276
6277    public static final Util.Functor1<String, Catalog> CATALOG_NAME_GETTER =
6278        new Util.Functor1<String, Catalog>() {
6279            public String apply(Catalog catalog) {
6280                return catalog.getName();
6281            }
6282        };
6283
6284    public static final Util.Functor1<String, Schema> SCHEMA_NAME_GETTER =
6285        new Util.Functor1<String, Schema>() {
6286            public String apply(Schema schema) {
6287                return schema.getName();
6288            }
6289        };
6290
6291    public static final Util.Functor1<String, MetadataElement>
6292        ELEMENT_NAME_GETTER =
6293        new Util.Functor1<String, MetadataElement>() {
6294            public String apply(MetadataElement element) {
6295                return element.getName();
6296            }
6297        };
6298
6299    public static final Util.Functor1<String, MetadataElement>
6300        ELEMENT_UNAME_GETTER =
6301        new Util.Functor1<String, MetadataElement>() {
6302            public String apply(MetadataElement element) {
6303                return element.getUniqueName();
6304            }
6305        };
6306
6307    public static final Util.Functor1<Member.Type, Member>
6308        MEMBER_TYPE_GETTER =
6309        new Util.Functor1<Member.Type, Member>() {
6310            public Member.Type apply(Member member) {
6311                return member.getMemberType();
6312            }
6313        };
6314
6315    public static final Util.Functor1<String, PropertyDefinition>
6316        PROPDEF_NAME_GETTER =
6317        new Util.Functor1<String, PropertyDefinition>() {
6318            public String apply(PropertyDefinition property) {
6319                return property.name();
6320            }
6321        };
6322
6323    static void serialize(StringBuilder buf, Collection<String> strings) {
6324        int k = 0;
6325        for (String name : Util.sort(strings)) {
6326            if (k++ > 0) {
6327                buf.append(',');
6328            }
6329            buf.append(name);
6330        }
6331    }
6332
6333    private static Level lookupLevel(Cube cube, String levelUniqueName) {
6334        for (Dimension dimension : cube.getDimensions()) {
6335            for (Hierarchy hierarchy : dimension.getHierarchies()) {
6336                for (Level level : hierarchy.getLevels()) {
6337                    if (level.getUniqueName().equals(levelUniqueName)) {
6338                        return level;
6339                    }
6340                }
6341            }
6342        }
6343        return null;
6344    }
6345
6346    static Iterable<Cube> sortedCubes(Schema schema) throws OlapException {
6347        return Util.sort(
6348            schema.getCubes(),
6349            new Comparator<Cube>() {
6350                public int compare(Cube o1, Cube o2) {
6351                    return o1.getName().compareTo(o2.getName());
6352                }
6353            }
6354        );
6355    }
6356
6357    static Iterable<Cube> filteredCubes(
6358        final Schema schema,
6359        Util.Functor1<Boolean, Cube> cubeNameCond)
6360        throws OlapException
6361    {
6362        final Iterable<Cube> iterable =
6363            filter(sortedCubes(schema), cubeNameCond);
6364        if (!cubeNameCond.apply(new SharedDimensionHolderCube(schema))) {
6365            return iterable;
6366        }
6367        return Composite.of(
6368            Collections.singletonList(
6369                new SharedDimensionHolderCube(schema)),
6370            iterable);
6371    }
6372
6373    private static String getHierarchyName(Hierarchy hierarchy) {
6374        String hierarchyName = hierarchy.getName();
6375        if (MondrianProperties.instance().SsasCompatibleNaming.get()
6376            && !hierarchyName.equals(hierarchy.getDimension().getName()))
6377        {
6378            hierarchyName =
6379                hierarchy.getDimension().getName() + "." + hierarchyName;
6380        }
6381        return hierarchyName;
6382    }
6383
6384    private static XmlaRequest wrapRequest(
6385        XmlaRequest request, Map<Column, String> map)
6386    {
6387        final Map<String, Object> restrictionsMap =
6388            new HashMap<String, Object>(request.getRestrictions());
6389        for (Map.Entry<Column, String> entry : map.entrySet()) {
6390            restrictionsMap.put(
6391                entry.getKey().name,
6392                Collections.singletonList(entry.getValue()));
6393        }
6394
6395        return new DelegatingXmlaRequest(request) {
6396            @Override
6397            public Map<String, Object> getRestrictions() {
6398                return restrictionsMap;
6399            }
6400        };
6401    }
6402
6403    /**
6404     * Returns an iterator over the catalogs in a connection, setting the
6405     * connection's catalog to each successful catalog in turn.
6406     *
6407     * @param connection Connection
6408     * @param conds Zero or more conditions to be applied to catalogs
6409     * @return Iterator over catalogs
6410     */
6411    private static Iterable<Catalog> catIter(
6412        final OlapConnection connection,
6413        final Util.Functor1<Boolean, Catalog>... conds)
6414    {
6415        return new Iterable<Catalog>() {
6416            public Iterator<Catalog> iterator() {
6417                try {
6418                    return new Iterator<Catalog>() {
6419                        final Iterator<Catalog> catalogIter =
6420                            Util.filter(
6421                                connection.getOlapCatalogs(),
6422                                conds).iterator();
6423
6424                        public boolean hasNext() {
6425                            return catalogIter.hasNext();
6426                        }
6427
6428                        public Catalog next() {
6429                            Catalog catalog = catalogIter.next();
6430                            try {
6431                                connection.setCatalog(catalog.getName());
6432                            } catch (SQLException e) {
6433                                throw new RuntimeException(e);
6434                            }
6435                            return catalog;
6436                        }
6437
6438                        public void remove() {
6439                            throw new UnsupportedOperationException();
6440                        }
6441                    };
6442                } catch (OlapException e) {
6443                    throw new RuntimeException(
6444                        "Failed to obtain a list of catalogs form the connection object.",
6445                        e);
6446                }
6447            }
6448        };
6449    }
6450
6451    private static class DelegatingXmlaRequest implements XmlaRequest {
6452        protected final XmlaRequest request;
6453
6454        public DelegatingXmlaRequest(XmlaRequest request) {
6455            this.request = request;
6456        }
6457
6458        public XmlaConstants.Method getMethod() {
6459            return request.getMethod();
6460        }
6461
6462        public Map<String, String> getProperties() {
6463            return request.getProperties();
6464        }
6465
6466        public Map<String, Object> getRestrictions() {
6467            return request.getRestrictions();
6468        }
6469
6470        public String getStatement() {
6471            return request.getStatement();
6472        }
6473
6474        public String getRoleName() {
6475            return request.getRoleName();
6476        }
6477
6478        public String getRequestType() {
6479            return request.getRequestType();
6480        }
6481
6482        public boolean isDrillThrough() {
6483            return request.isDrillThrough();
6484        }
6485
6486        public String getUsername() {
6487            return request.getUsername();
6488        }
6489
6490        public String getPassword() {
6491            return request.getPassword();
6492        }
6493
6494        public String getSessionId() {
6495            return request.getSessionId();
6496        }
6497    }
6498
6499    /**
6500     * Dummy implementation of {@link Cube} that holds all shared dimensions
6501     * in a given schema. Less error-prone than requiring all generator code
6502     * to cope with a null Cube.
6503     */
6504    private static class SharedDimensionHolderCube implements Cube {
6505        private final Schema schema;
6506
6507        public SharedDimensionHolderCube(Schema schema) {
6508            this.schema = schema;
6509        }
6510
6511        public Schema getSchema() {
6512            return schema;
6513        }
6514
6515        public NamedList<Dimension> getDimensions() {
6516            try {
6517                return schema.getSharedDimensions();
6518            } catch (OlapException e) {
6519                throw new RuntimeException(e);
6520            }
6521        }
6522
6523        public NamedList<Hierarchy> getHierarchies() {
6524            final NamedList<Hierarchy> hierarchyList =
6525                new ArrayNamedListImpl<Hierarchy>() {
6526                    public String getName(Object hierarchy) {
6527                        return ((Hierarchy)hierarchy).getName();
6528                    }
6529                };
6530            for (Dimension dimension : getDimensions()) {
6531                hierarchyList.addAll(dimension.getHierarchies());
6532            }
6533            return hierarchyList;
6534        }
6535
6536        public List<Measure> getMeasures() {
6537            return Collections.emptyList();
6538        }
6539
6540        public NamedList<NamedSet> getSets() {
6541            throw new UnsupportedOperationException();
6542        }
6543
6544        public Collection<Locale> getSupportedLocales() {
6545            throw new UnsupportedOperationException();
6546        }
6547
6548        public Member lookupMember(List<IdentifierSegment> identifierSegments)
6549            throws org.olap4j.OlapException
6550        {
6551            throw new UnsupportedOperationException();
6552        }
6553
6554        public List<Member> lookupMembers(
6555            Set<Member.TreeOp> treeOps,
6556            List<IdentifierSegment> identifierSegments)
6557            throws org.olap4j.OlapException
6558        {
6559            throw new UnsupportedOperationException();
6560        }
6561
6562        public boolean isDrillThroughEnabled() {
6563            return false;
6564        }
6565
6566        public String getName() {
6567            return "";
6568        }
6569
6570        public String getUniqueName() {
6571            return "";
6572        }
6573
6574        public String getCaption() {
6575            return "";
6576        }
6577
6578        public String getDescription() {
6579            return "";
6580        }
6581
6582        public boolean isVisible() {
6583            return false;
6584        }
6585    }
6586}
6587
6588// End RowsetDefinition.java