KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mondrian > xmla > XmlaHandler


1 /*
2 // This software is subject to the terms of the Common Public License
3 // Agreement, available at the following URL:
4 // http://www.opensource.org/licenses/cpl.html.
5 // Copyright (C) 2003-2007 Julian Hyde
6 // All Rights Reserved.
7 // You must accept the terms of that agreement to use this software.
8 */

9 package mondrian.xmla;
10
11 import mondrian.calc.ExpCompiler.ResultStyle;
12 import mondrian.olap.*;
13 import mondrian.olap.Connection;
14 import mondrian.olap.DriverManager;
15 import mondrian.rolap.*;
16 import mondrian.rolap.agg.CellRequest;
17 import mondrian.spi.CatalogLocator;
18 import mondrian.xmla.impl.DefaultSaxWriter;
19
20 import org.apache.log4j.Logger;
21 import org.xml.sax.SAXException JavaDoc;
22
23 import javax.sql.DataSource JavaDoc;
24 import java.math.BigDecimal JavaDoc;
25 import java.sql.*;
26 import java.util.*;
27 import java.io.StringWriter JavaDoc;
28 import java.io.PrintWriter JavaDoc;
29
30
31 /**
32  * An <code>XmlaHandler</code> responds to XML for Analysis (XML/A) requests.
33  *
34  * @author jhyde, Gang Chen
35  * @version $Id: //open/mondrian/src/main/mondrian/xmla/XmlaHandler.java#37 $
36  * @since 27 April, 2003
37  */

38 public class XmlaHandler implements XmlaConstants {
39     private static final Logger LOGGER = Logger.getLogger(XmlaHandler.class);
40
41     private final Map<String JavaDoc, DataSourcesConfig.DataSource> dataSourcesMap;
42     private final List<String JavaDoc> drillThruColumnNames = new ArrayList<String JavaDoc>();
43     private final CatalogLocator catalogLocator;
44     private final String JavaDoc prefix;
45
46     private static final int ROW_SET = 1;
47     private static final int MD_DATA_SET = 2;
48
49     private static final String JavaDoc ROW_SET_XML_SCHEMA = computeXsd(ROW_SET);
50     private static final String JavaDoc EMPTY_ROW_SET_XML_SCHEMA =
51                                     computeEmptyXsd(ROW_SET);
52
53     private static final String JavaDoc MD_DATA_SET_XML_SCHEMA = computeXsd(MD_DATA_SET);
54     private static final String JavaDoc EMPTY_MD_DATA_SET_XML_SCHEMA =
55                                     computeEmptyXsd(MD_DATA_SET);
56
57     private static final String JavaDoc NS_XML_SQL = "urn:schemas-microsoft-com:xml-sql";
58
59     private static String JavaDoc computeXsd(int settype) {
60         final StringWriter JavaDoc sw = new StringWriter JavaDoc();
61         SaxWriter writer = new DefaultSaxWriter(new PrintWriter JavaDoc(sw), 3);
62         writeDatasetXmlSchema(writer, settype);
63         writer.flush();
64         return sw.toString();
65     }
66
67     private static String JavaDoc computeEmptyXsd(int settype) {
68         final StringWriter JavaDoc sw = new StringWriter JavaDoc();
69         SaxWriter writer = new DefaultSaxWriter(new PrintWriter JavaDoc(sw), 3);
70         writeEmptyDatasetXmlSchema(writer, settype);
71         writer.flush();
72         return sw.toString();
73     }
74
75     private static interface QueryResult {
76         public void unparse(SaxWriter res) throws SAXException JavaDoc;
77     }
78
79     /**
80      * Creates an <code>XmlaHandler</code>.
81      *
82      * @param dataSources Data sources
83      * @param catalogLocator Catalog locator
84      * @param prefix XML Namespace. Typical value is "xmla", but a value of
85      * "cxmla" works around an Internet Explorer 7 bug
86      */

87     public XmlaHandler(
88         DataSourcesConfig.DataSources dataSources,
89         CatalogLocator catalogLocator,
90         String JavaDoc prefix)
91     {
92         this.catalogLocator = catalogLocator;
93         assert prefix != null;
94         this.prefix = prefix;
95         Map<String JavaDoc, DataSourcesConfig.DataSource> map =
96             new HashMap<String JavaDoc, DataSourcesConfig.DataSource>();
97         if (dataSources != null) {
98             for (DataSourcesConfig.DataSource ds : dataSources.dataSources) {
99                 if (map.containsKey(ds.getDataSourceName())) {
100                     // This is not an XmlaException
101
throw Util.newError(
102                         "duplicated data source name '" +
103                             ds.getDataSourceName() + "'");
104                 }
105                 // Set parent pointers.
106
for (DataSourcesConfig.Catalog catalog : ds.catalogs.catalogs) {
107                     catalog.setDataSource(ds);
108                 }
109                 map.put(ds.getDataSourceName(), ds);
110             }
111         }
112         dataSourcesMap = Collections.unmodifiableMap(map);
113     }
114
115     public Map<String JavaDoc, DataSourcesConfig.DataSource> getDataSourceEntries() {
116         return dataSourcesMap;
117     }
118
119     /**
120      * Processes a request.
121      *
122      * @param request XML request, for example, "<SOAP-ENV:Envelope ...>".
123      * @param response Destination for response
124      */

125     public void process(XmlaRequest request, XmlaResponse response)
126             throws XmlaException {
127         int method = request.getMethod();
128         long start = System.currentTimeMillis();
129
130         switch (method) {
131         case METHOD_DISCOVER:
132             discover(request, response);
133             break;
134         case METHOD_EXECUTE:
135             execute(request, response);
136             break;
137         default:
138             throw new XmlaException(
139                 CLIENT_FAULT_FC,
140                 HSB_BAD_METHOD_CODE,
141                 HSB_BAD_METHOD_FAULT_FS,
142                 new IllegalArgumentException JavaDoc(
143                     "Unsupported XML/A method: " +method));
144         }
145         if (LOGGER.isDebugEnabled()) {
146             long end = System.currentTimeMillis();
147             LOGGER.debug("XmlaHandler.process: time = " +(end-start));
148             LOGGER.debug("XmlaHandler.process: " +Util.printMemory());
149         }
150     }
151
152     private void checkFormat(XmlaRequest request) throws XmlaException {
153         // Check response's rowset format in request
154
final Map<String JavaDoc, String JavaDoc> properties = request.getProperties();
155         if (request.isDrillThrough()) {
156             final String JavaDoc formatName =
157                 properties.get(PropertyDefinition.Format.name());
158             Enumeration.Format format =
159                 valueOf(
160                     Enumeration.Format.class,
161                     formatName,
162                     null);
163             if (format != Enumeration.Format.Tabular) {
164                 throw new XmlaException(
165                     CLIENT_FAULT_FC,
166                     HSB_DRILL_THROUGH_FORMAT_CODE,
167                     HSB_DRILL_THROUGH_FORMAT_FAULT_FS,
168                     new UnsupportedOperationException JavaDoc(
169                         "<Format>: only 'Tabular' allowed when drilling through"));
170             }
171         } else {
172             final String JavaDoc formatName =
173                 properties.get(PropertyDefinition.Format.name());
174             if (formatName != null) {
175                 Enumeration.Format format = valueOf(
176                     Enumeration.Format.class, formatName, null);
177                 if (format != Enumeration.Format.Multidimensional &&
178                     format != Enumeration.Format.Tabular) {
179                     throw new UnsupportedOperationException JavaDoc(
180                         "<Format>: only 'Multidimensional', 'Tabular' currently supported");
181                 }
182             }
183             final String JavaDoc axisFormatName =
184                 properties.get(PropertyDefinition.AxisFormat.name());
185             if (axisFormatName != null) {
186                 Enumeration.AxisFormat axisFormat = valueOf(
187                     Enumeration.AxisFormat.class, axisFormatName, null);
188
189                 if (axisFormat != Enumeration.AxisFormat.TupleFormat) {
190                     throw new UnsupportedOperationException JavaDoc(
191                         "<AxisFormat>: only 'TupleFormat' currently supported");
192                 }
193             }
194         }
195     }
196
197     private void execute(XmlaRequest request, XmlaResponse response)
198             throws XmlaException {
199
200         final Map<String JavaDoc, String JavaDoc> properties = request.getProperties();
201         final String JavaDoc contentName =
202             properties.get(PropertyDefinition.Content.name());
203         // default value is SchemaData
204
Enumeration.Content content =
205             valueOf(Enumeration.Content.class, contentName, CONTENT_DEFAULT);
206
207         // Handle execute
208
QueryResult result;
209         if (request.isDrillThrough()) {
210             String JavaDoc tabFields =
211                 properties.get(PropertyDefinition.TableFields.name());
212             if (tabFields != null && tabFields.length() > 0) {
213                 // Presence of TABLE_FIELDS property initiates advanced
214
// drill-through.
215
result = executeColumnQuery(request);
216             } else {
217                 result = executeDrillThroughQuery(request);
218             }
219         } else {
220             result = executeQuery(request);
221         }
222
223         SaxWriter writer = response.getWriter();
224         writer.startDocument();
225
226         writer.startElement(prefix + ":ExecuteResponse", new String JavaDoc[] {
227             "xmlns:" + prefix, NS_XMLA});
228         writer.startElement(prefix + ":return");
229         boolean rowset =
230             request.isDrillThrough() ||
231                 Enumeration.Format.Tabular.name().equals(
232                     request.getProperties().get(
233                         PropertyDefinition.Format.name()));
234         writer.startElement("root", new String JavaDoc[] {
235             "xmlns",
236             result == null ? NS_XMLA_EMPTY :
237                 rowset ? NS_XMLA_ROWSET :
238                 NS_XMLA_MDDATASET,
239             "xmlns:xsi", NS_XSI,
240             "xmlns:xsd", NS_XSD,
241             "xmlns:EX", NS_XMLA_EX,
242         });
243
244         if ((content == Enumeration.Content.Schema)
245                 || (content == Enumeration.Content.SchemaData)) {
246             if (result != null) {
247                 if (result instanceof MDDataSet_Tabular) {
248                     MDDataSet_Tabular tabResult = (MDDataSet_Tabular) result;
249                     tabResult.metadata(writer);
250                 } else if (rowset) {
251                     writer.verbatim(ROW_SET_XML_SCHEMA);
252                 } else {
253                     writer.verbatim(MD_DATA_SET_XML_SCHEMA);
254                 }
255             } else {
256                 if (rowset) {
257                     writer.verbatim(EMPTY_ROW_SET_XML_SCHEMA);
258                 } else {
259                     writer.verbatim(EMPTY_MD_DATA_SET_XML_SCHEMA);
260                 }
261             }
262         }
263
264         try {
265             if ((content == Enumeration.Content.Data)
266                     || (content == Enumeration.Content.SchemaData)) {
267                 if (result != null) {
268                     result.unparse(writer);
269                 }
270             }
271         } catch (XmlaException xex) {
272             throw xex;
273         } catch (Throwable JavaDoc t) {
274             throw new XmlaException(
275                 SERVER_FAULT_FC,
276                 HSB_EXECUTE_UNPARSE_CODE,
277                 HSB_EXECUTE_UNPARSE_FAULT_FS,
278                 t);
279         } finally {
280             writer.endElement(); // root
281
writer.endElement(); // return
282
writer.endElement(); // ExecuteResponse
283
}
284
285         writer.endDocument();
286     }
287
288     /**
289      * Computes the XML Schema for a dataset.
290      *
291      * @param writer SAX writer
292      * @see RowsetDefinition#writeRowsetXmlSchema(SaxWriter)
293      */

294     static void writeDatasetXmlSchema(SaxWriter writer, int settype) {
295         String JavaDoc setNsXmla = (settype == ROW_SET)
296                 ? XmlaConstants.NS_XMLA_ROWSET
297                 : XmlaConstants.NS_XMLA_MDDATASET;
298
299         writer.startElement("xsd:schema", new String JavaDoc[] {
300             "xmlns:xsd", XmlaConstants.NS_XSD,
301             "targetNamespace", setNsXmla,
302             "xmlns", setNsXmla,
303             "xmlns:xsi", XmlaConstants.NS_XSI,
304             "xmlns:sql", NS_XML_SQL,
305             "elementFormDefault", "qualified"
306         });
307
308         // MemberType
309

310         writer.startElement("xsd:complexType", new String JavaDoc[] {
311             "name", "MemberType"
312         });
313         writer.startElement("xsd:sequence");
314         writer.element("xsd:element", new String JavaDoc[] {
315             "name", "UName",
316             "type", "xsd:string",
317         });
318         writer.element("xsd:element", new String JavaDoc[] {
319             "name", "Caption",
320             "type", "xsd:string",
321         });
322         writer.element("xsd:element", new String JavaDoc[] {
323             "name", "LName",
324             "type", "xsd:string",
325         });
326         writer.element("xsd:element", new String JavaDoc[] {
327             "name", "LNum",
328             "type", "xsd:unsignedInt",
329         });
330         writer.element("xsd:element", new String JavaDoc[] {
331             "name", "DisplayInfo",
332             "type", "xsd:unsignedInt",
333         });
334         writer.startElement("xsd:sequence", new String JavaDoc[] {
335             "maxOccurs", "unbounded",
336             "minOccurs", "0",
337         });
338         writer.element("xsd:any", new String JavaDoc[] {
339             "processContents", "lax",
340             "maxOccurs", "unbounded",
341         });
342         writer.endElement(); // xsd:sequence
343
writer.endElement(); // xsd:sequence
344
writer.element("xsd:attribute", new String JavaDoc[] {
345             "name", "Hierarchy",
346             "type", "xsd:string",
347         });
348         writer.endElement(); // xsd:complexType name="MemberType"
349

350         // PropType
351

352         writer.startElement("xsd:complexType", new String JavaDoc[] {
353             "name", "PropType",
354         });
355         writer.element("xsd:attribute", new String JavaDoc[] {
356             "name", "name",
357             "type", "xsd:string",
358         });
359         writer.endElement(); // xsd:complexType name="PropType"
360

361         // TupleType
362

363         writer.startElement("xsd:complexType", new String JavaDoc[] {
364             "name", "TupleType"
365         });
366         writer.startElement("xsd:sequence", new String JavaDoc[] {
367             "maxOccurs", "unbounded"
368         });
369         writer.element("xsd:element", new String JavaDoc[] {
370             "name", "Member",
371             "type", "MemberType",
372         });
373         writer.endElement(); // xsd:sequence
374
writer.endElement(); // xsd:complexType name="TupleType"
375

376         // MembersType
377

378         writer.startElement("xsd:complexType", new String JavaDoc[] {
379             "name", "MembersType"
380         });
381         writer.startElement("xsd:sequence", new String JavaDoc[] {
382             "maxOccurs", "unbounded",
383         });
384         writer.element("xsd:element", new String JavaDoc[] {
385             "name", "Member",
386             "type", "MemberType",
387         });
388         writer.endElement(); // xsd:sequence
389
writer.element("xsd:attribute", new String JavaDoc[] {
390             "name", "Hierarchy",
391             "type", "xsd:string",
392         });
393         writer.endElement(); // xsd:complexType
394

395         // TuplesType
396

397         writer.startElement("xsd:complexType", new String JavaDoc[] {
398             "name", "TuplesType"
399         });
400         writer.startElement("xsd:sequence", new String JavaDoc[] {
401             "maxOccurs", "unbounded",
402         });
403         writer.element("xsd:element", new String JavaDoc[] {
404             "name", "Tuple",
405             "type", "TupleType",
406         });
407         writer.endElement(); // xsd:sequence
408
writer.endElement(); // xsd:complexType
409

410         // CrossProductType
411

412         writer.startElement("xsd:complexType", new String JavaDoc[] {
413             "name", "CrossProductType",
414         });
415         writer.startElement("xsd:sequence");
416         writer.startElement("xsd:choice", new String JavaDoc[] {
417             "minOccurs", "0",
418             "maxOccurs", "unbounded",
419         });
420         writer.element("xsd:element", new String JavaDoc[] {
421             "name", "Members",
422             "type", "MembersType"
423         });
424         writer.element("xsd:element", new String JavaDoc[] {
425             "name", "Tuples",
426             "type", "TuplesType"
427         });
428         writer.endElement(); // xsd:choice
429
writer.endElement(); // xsd:sequence
430
writer.element("xsd:attribute", new String JavaDoc[] {
431             "name", "Size",
432             "type", "xsd:unsignedInt"
433         });
434         writer.endElement(); // xsd:complexType
435

436         // OlapInfo
437

438         writer.startElement("xsd:complexType", new String JavaDoc[] {
439             "name", "OlapInfo",
440         });
441         writer.startElement("xsd:sequence");
442
443         { // <CubeInfo>
444
writer.startElement("xsd:element", new String JavaDoc[] {
445                 "name", "CubeInfo"
446             });
447             writer.startElement("xsd:complexType");
448             writer.startElement("xsd:sequence");
449
450             { // <Cube>
451
writer.startElement("xsd:element", new String JavaDoc[] {
452                     "name", "Cube",
453                     "maxOccurs", "unbounded"
454                 });
455                 writer.startElement("xsd:complexType");
456                 writer.startElement("xsd:sequence");
457
458                 writer.element("xsd:element", new String JavaDoc[] {
459                     "name", "CubeName",
460                     "type", "xsd:string"
461                 });
462
463                 writer.endElement(); // xsd:sequence
464
writer.endElement(); // xsd:complexType
465
writer.endElement(); // xsd:element name=Cube
466
}
467
468             writer.endElement(); // xsd:sequence
469
writer.endElement(); // xsd:complexType
470
writer.endElement(); // xsd:element name=CubeInfo
471
}
472         { // <AxesInfo>
473
writer.startElement("xsd:element", new String JavaDoc[] {
474                 "name", "AxesInfo"
475             });
476             writer.startElement("xsd:complexType");
477             writer.startElement("xsd:sequence");
478             { // <AxisInfo>
479
writer.startElement("xsd:element", new String JavaDoc[] {
480                     "name", "AxisInfo",
481                     "maxOccurs", "unbounded"
482                 });
483                 writer.startElement("xsd:complexType");
484                 writer.startElement("xsd:sequence");
485
486                 { // <HierarchyInfo>
487
writer.startElement("xsd:element", new String JavaDoc[] {
488                         "name", "HierarchyInfo",
489                         "minOccurs", "0",
490                         "maxOccurs", "unbounded"
491                     });
492                     writer.startElement("xsd:complexType");
493                     writer.startElement("xsd:sequence");
494                     writer.startElement("xsd:sequence", new String JavaDoc[] {
495                         "maxOccurs", "unbounded"
496                     });
497                     writer.element("xsd:element", new String JavaDoc[] {
498                         "name", "UName",
499                         "type", "PropType"
500                     });
501                     writer.element("xsd:element", new String JavaDoc[] {
502                         "name", "Caption",
503                         "type", "PropType"
504                     });
505                     writer.element("xsd:element", new String JavaDoc[] {
506                         "name", "LName",
507                         "type", "PropType"
508                     });
509                     writer.element("xsd:element", new String JavaDoc[] {
510                         "name", "LNum",
511                         "type", "PropType"
512                     });
513                     writer.element("xsd:element", new String JavaDoc[] {
514                         "name", "DisplayInfo",
515                         "type", "PropType",
516                         "minOccurs", "0",
517                         "maxOccurs", "unbounded"
518                     });
519                     if (false) writer.element("xsd:element", new String JavaDoc[] {
520                         "name", "PARENT_MEMBER_NAME",
521                         "type", "PropType",
522                         "minOccurs", "0",
523                         "maxOccurs", "unbounded"
524                     });
525                     writer.endElement(); // xsd:sequence
526

527                     // This is the Depth element for JPivot??
528
writer.startElement("xsd:sequence");
529                     writer.element("xsd:any", new String JavaDoc[] {
530                         "processContents", "lax",
531                          "minOccurs", "0",
532                         "maxOccurs", "unbounded"
533                     });
534                     writer.endElement(); // xsd:sequence
535

536                     writer.endElement(); // xsd:sequence
537
writer.endElement(); // xsd:complexType
538
writer.element("xsd:attribute", new String JavaDoc[] {
539                         "name", "name",
540                         "type", "xsd:string",
541                         "use", "required"
542                     });
543                     writer.endElement(); // xsd:element name=HierarchyInfo
544
}
545                 writer.endElement(); // xsd:sequence
546
writer.element("xsd:attribute", new String JavaDoc[] {
547                     "name", "name",
548                     "type", "xsd:string"
549                 });
550                 writer.endElement(); // xsd:complexType
551
writer.endElement(); // xsd:element name=AxisInfo
552
}
553             writer.endElement(); // xsd:sequence
554
writer.endElement(); // xsd:complexType
555
writer.endElement(); // xsd:element name=AxesInfo
556
}
557
558         // CellInfo
559

560         { // <CellInfo>
561
writer.startElement("xsd:element", new String JavaDoc[] {
562                 "name", "CellInfo"
563             });
564             writer.startElement("xsd:complexType");
565             writer.startElement("xsd:sequence");
566             writer.startElement("xsd:sequence", new String JavaDoc[] {
567                 "minOccurs", "0",
568                 "maxOccurs", "unbounded"
569             });
570             writer.startElement("xsd:choice");
571             writer.element("xsd:element", new String JavaDoc[] {
572                 "name", "Value",
573                 "type", "PropType"
574             });
575             writer.element("xsd:element", new String JavaDoc[] {
576                 "name", "FmtValue",
577                 "type", "PropType"
578             });
579             writer.element("xsd:element", new String JavaDoc[] {
580                 "name", "BackColor",
581                 "type", "PropType"
582             });
583             writer.element("xsd:element", new String JavaDoc[] {
584                 "name", "ForeColor",
585                 "type", "PropType"
586             });
587             writer.element("xsd:element", new String JavaDoc[] {
588                 "name", "FontName",
589                 "type", "PropType"
590             });
591             writer.element("xsd:element", new String JavaDoc[] {
592                 "name", "FontSize",
593                 "type", "PropType"
594             });
595             writer.element("xsd:element", new String JavaDoc[] {
596                 "name", "FontFlags",
597                 "type", "PropType"
598             });
599             writer.element("xsd:element", new String JavaDoc[] {
600                 "name", "FormatString",
601                 "type", "PropType"
602             });
603             writer.element("xsd:element", new String JavaDoc[] {
604                 "name", "NonEmptyBehavior",
605                 "type", "PropType"
606             });
607             writer.element("xsd:element", new String JavaDoc[] {
608                 "name", "SolveOrder",
609                 "type", "PropType"
610             });
611             writer.element("xsd:element", new String JavaDoc[] {
612                 "name", "Updateable",
613                 "type", "PropType"
614             });
615             writer.element("xsd:element", new String JavaDoc[] {
616                 "name", "Visible",
617                 "type", "PropType"
618             });
619             writer.element("xsd:element", new String JavaDoc[] {
620                 "name", "Expression",
621                 "type", "PropType"
622             });
623             writer.endElement(); // xsd:choice
624
writer.endElement(); // xsd:sequence
625
writer.startElement("xsd:sequence", new String JavaDoc[] {
626                 "maxOccurs", "unbounded",
627                 "minOccurs", "0"
628             });
629             writer.element("xsd:any", new String JavaDoc[] {
630                 "processContents", "lax",
631                 "maxOccurs", "unbounded"
632             });
633             writer.endElement(); // xsd:sequence
634
writer.endElement(); // xsd:sequence
635
writer.endElement(); // xsd:complexType
636
writer.endElement(); // xsd:element name=CellInfo
637
}
638
639         writer.endElement(); // xsd:sequence
640
writer.endElement(); // xsd:complexType
641

642         // Axes
643

644         writer.startElement("xsd:complexType", new String JavaDoc[] {
645             "name", "Axes"
646         });
647         writer.startElement("xsd:sequence", new String JavaDoc[] {
648             "maxOccurs", "unbounded"
649         });
650         { // <Axis>
651
writer.startElement("xsd:element", new String JavaDoc[] {
652                 "name", "Axis"
653             });
654             writer.startElement("xsd:complexType");
655             writer.startElement("xsd:choice", new String JavaDoc[] {
656                 "minOccurs", "0",
657                 "maxOccurs", "unbounded"
658             });
659             writer.element("xsd:element", new String JavaDoc[] {
660                 "name", "CrossProduct",
661                 "type", "CrossProductType"
662             });
663             writer.element("xsd:element", new String JavaDoc[] {
664                 "name", "Tuples",
665                 "type", "TuplesType"
666             });
667             writer.element("xsd:element", new String JavaDoc[] {
668                 "name", "Members",
669                 "type", "MembersType"
670             });
671             writer.endElement(); // xsd:choice
672
writer.element("xsd:attribute", new String JavaDoc[] {
673                 "name", "name",
674                 "type", "xsd:string"
675             });
676             writer.endElement(); // xsd:complexType
677
}
678         writer.endElement(); // xsd:element
679
writer.endElement(); // xsd:sequence
680
writer.endElement(); // xsd:complexType
681

682         // CellData
683

684         writer.startElement("xsd:complexType", new String JavaDoc[] {
685             "name", "CellData"
686         });
687         writer.startElement("xsd:sequence");
688         { // <Cell>
689
writer.startElement("xsd:element", new String JavaDoc[] {
690                 "name", "Cell",
691                 "minOccurs", "0",
692                 "maxOccurs", "unbounded"
693             });
694             writer.startElement("xsd:complexType");
695             writer.startElement("xsd:sequence", new String JavaDoc[] {
696                 "maxOccurs", "unbounded"
697             });
698             writer.startElement("xsd:choice");
699             writer.element("xsd:element", new String JavaDoc[] {
700                 "name", "Value"
701             });
702             writer.element("xsd:element", new String JavaDoc[] {
703                 "name", "FmtValue",
704                 "type", "xsd:string"
705             });
706             writer.element("xsd:element", new String JavaDoc[] {
707                 "name", "BackColor",
708                 "type", "xsd:unsignedInt"
709             });
710             writer.element("xsd:element", new String JavaDoc[] {
711                 "name", "ForeColor",
712                 "type", "xsd:unsignedInt"
713             });
714             writer.element("xsd:element", new String JavaDoc[] {
715                 "name", "FontName",
716                 "type", "xsd:string"
717             });
718             writer.element("xsd:element", new String JavaDoc[] {
719                 "name", "FontSize",
720                 "type", "xsd:unsignedShort"
721             });
722             writer.element("xsd:element", new String JavaDoc[] {
723                 "name", "FontFlags",
724                 "type", "xsd:unsignedInt"
725             });
726             writer.element("xsd:element", new String JavaDoc[] {
727                 "name", "FormatString",
728                 "type", "xsd:string"
729             });
730             writer.element("xsd:element", new String JavaDoc[] {
731                 "name", "NonEmptyBehavior",
732                 "type", "xsd:unsignedShort"
733             });
734             writer.element("xsd:element", new String JavaDoc[] {
735                 "name", "SolveOrder",
736                 "type", "xsd:unsignedInt"
737             });
738             writer.element("xsd:element", new String JavaDoc[] {
739                 "name", "Updateable",
740                 "type", "xsd:unsignedInt"
741             });
742             writer.element("xsd:element", new String JavaDoc[] {
743                 "name", "Visible",
744                 "type", "xsd:unsignedInt"
745             });
746             writer.element("xsd:element", new String JavaDoc[] {
747                 "name", "Expression",
748                 "type", "xsd:string"
749             });
750             writer.endElement(); // xsd:choice
751
writer.endElement(); // xsd:sequence
752
writer.element("xsd:attribute", new String JavaDoc[] {
753                 "name", "CellOrdinal",
754                 "type", "xsd:unsignedInt",
755                 "use", "required"
756             });
757             writer.endElement(); // xsd:complexType
758
writer.endElement(); // xsd:element name=Cell
759
}
760         writer.endElement(); // xsd:sequence
761
writer.endElement(); // xsd:complexType
762

763         { // <root>
764
writer.startElement("xsd:element", new String JavaDoc[] {
765                 "name", "root"
766             });
767             writer.startElement("xsd:complexType");
768             writer.startElement("xsd:sequence", new String JavaDoc[] {
769                 "maxOccurs", "unbounded"
770             });
771             writer.element("xsd:element", new String JavaDoc[] {
772                 "name", "OlapInfo",
773                 "type", "OlapInfo"
774             });
775             writer.element("xsd:element", new String JavaDoc[] {
776                 "name", "Axes",
777                 "type", "Axes"
778             });
779             writer.element("xsd:element", new String JavaDoc[] {
780                 "name", "CellData",
781                 "type", "CellData"
782             });
783             writer.endElement(); // xsd:sequence
784
writer.endElement(); // xsd:complexType
785
writer.endElement(); // xsd:element name=root
786
}
787
788         writer.endElement(); // xsd:schema
789
}
790
791     static void writeEmptyDatasetXmlSchema(SaxWriter writer, int settype) {
792         String JavaDoc setNsXmla = XmlaConstants.NS_XMLA_ROWSET;
793         writer.startElement("xsd:schema", new String JavaDoc[] {
794             "xmlns:xsd", XmlaConstants.NS_XSD,
795             "targetNamespace", setNsXmla,
796             "xmlns", setNsXmla,
797             "xmlns:xsi", XmlaConstants.NS_XSI,
798             "xmlns:sql", NS_XML_SQL,
799             "elementFormDefault", "qualified"
800         });
801
802         writer.element("xsd:element", new String JavaDoc[] {
803             "name", "root"
804         });
805
806         writer.endElement(); // xsd:schema
807
}
808
809     private QueryResult executeDrillThroughQuery(XmlaRequest request)
810             throws XmlaException {
811
812         checkFormat(request);
813
814         DataSourcesConfig.DataSource ds = getDataSource(request);
815         DataSourcesConfig.Catalog dsCatalog = getCatalog(request, ds);
816         String JavaDoc role = request.getRole();
817         final Map<String JavaDoc, String JavaDoc> properties = request.getProperties();
818
819         final RolapConnection connection =
820             (RolapConnection) getConnection(dsCatalog, role);
821
822         final String JavaDoc statement = request.getStatement();
823         final Query query = connection.parseQuery(statement);
824         query.setResultStyle(ResultStyle.LIST);
825         final Result result = connection.execute(query);
826         Cell dtCell = result.getCell(new int[] {0, 0});
827
828         if (!dtCell.canDrillThrough()) {
829             throw new XmlaException(
830                 SERVER_FAULT_FC,
831                 HSB_DRILL_THROUGH_NOT_ALLOWED_CODE,
832                 HSB_DRILL_THROUGH_NOT_ALLOWED_FAULT_FS,
833                 Util.newError("Cannot do DrillThrough operation on the cell"));
834         }
835
836         String JavaDoc dtSql = dtCell.getDrillThroughSQL(true);
837         java.sql.Connection JavaDoc sqlConn = null;
838         ResultSet rs = null;
839
840         try {
841             final String JavaDoc advancedFlag =
842                 properties.get(PropertyDefinition.AdvancedFlag.name());
843             if ("true".equals(advancedFlag)) {
844                 final Position position = result.getAxes()[0].getPositions().get(0);
845                 Member[] members = position.toArray(new Member[position.size()]);
846
847                 CellRequest cellRequest =
848                     RolapAggregationManager.makeRequest(members, false, false);
849                 List<MondrianDef.Relation> relationList =
850                     new ArrayList<MondrianDef.Relation>();
851                 final RolapStar.Table factTable =
852                     cellRequest.getMeasure().getStar().getFactTable();
853                 MondrianDef.Relation relation = factTable.getRelation();
854                 relationList.add(relation);
855
856                 for (RolapStar.Table table : factTable.getChildren()) {
857                     relationList.add(table.getRelation());
858                 }
859                 List<String JavaDoc> truncatedTableList = new ArrayList<String JavaDoc>();
860                 sqlConn = connection.getDataSource().getConnection();
861                 Statement stmt = sqlConn.createStatement();
862                 List<List<String JavaDoc>> fields = new ArrayList<List<String JavaDoc>>();
863
864                 Map<String JavaDoc, List<String JavaDoc>> tableFieldMap =
865                     new HashMap<String JavaDoc, List<String JavaDoc>>();
866                 for (MondrianDef.Relation relation1 : relationList) {
867                     final String JavaDoc tableName = relation1.toString();
868                     List<String JavaDoc> fieldNameList = new ArrayList<String JavaDoc>();
869                     // FIXME: Quote table name
870
dtSql = "SELECT * FROM " + tableName + " WHERE 1=2";
871                     rs = stmt.executeQuery(dtSql);
872                     ResultSetMetaData rsMeta = rs.getMetaData();
873                     for (int j = 1; j <= rsMeta.getColumnCount(); j++) {
874                         String JavaDoc colName = rsMeta.getColumnName(j);
875                         boolean colNameExists = false;
876                         for (List<String JavaDoc> prvField : fields) {
877                             if (prvField.contains(colName)) {
878                                 colNameExists = true;
879                                 break;
880                             }
881                         }
882                         if (!colNameExists) {
883                             fieldNameList.add(rsMeta.getColumnName(j));
884                         }
885                     }
886                     fields.add(fieldNameList);
887                     String JavaDoc truncatedTableName =
888                         tableName.substring(tableName.lastIndexOf(".") + 1);
889                     truncatedTableList.add(truncatedTableName);
890                     tableFieldMap.put(truncatedTableName, fieldNameList);
891                 }
892                 return new TabularRowSet(tableFieldMap, truncatedTableList);
893             } else {
894                 int count = -1;
895                 if (MondrianProperties.instance().EnableTotalCount.booleanValue()) {
896                     count = dtCell.getDrillThroughCount();
897                 }
898
899                 sqlConn = connection.getDataSource().getConnection();
900                 if (LOGGER.isDebugEnabled()) {
901                     LOGGER.debug("drill through sql: " + dtSql);
902                 }
903                 int resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE;
904                 int resultSetConcurrency = ResultSet.CONCUR_READ_ONLY;
905                 if (!sqlConn.getMetaData().supportsResultSetConcurrency(
906                     ResultSet.TYPE_SCROLL_INSENSITIVE,
907                     ResultSet.CONCUR_READ_ONLY)) {
908                     // downgrade to non-scroll cursor, since we can
909
// fake absolute() via forward fetch
910
resultSetType = ResultSet.TYPE_FORWARD_ONLY;
911                 }
912                 SqlStatement stmt =
913                     RolapUtil.executeQuery(
914                         connection.getDataSource(), dtSql, -1,
915                         "XmlaHandler.executeDrillThroughQuery",
916                         "Error in drill through",
917                         resultSetType, resultSetConcurrency);
918                 return new TabularRowSet(
919                     stmt, request.drillThroughMaxRows(),
920                     request.drillThroughFirstRowset(), count,
921                     resultSetType);
922             }
923         } catch (XmlaException xex) {
924             throw xex;
925         } catch (SQLException sqle) {
926             throw new XmlaException(
927                 SERVER_FAULT_FC,
928                 HSB_DRILL_THROUGH_SQL_CODE,
929                 HSB_DRILL_THROUGH_SQL_FAULT_FS,
930                 Util.newError(sqle, "Error in drill through"));
931         } catch (RuntimeException JavaDoc e) {
932             throw new XmlaException(
933                 SERVER_FAULT_FC,
934                 HSB_DRILL_THROUGH_SQL_CODE,
935                 HSB_DRILL_THROUGH_SQL_FAULT_FS,
936                 e);
937         } finally {
938             try {
939                 if (rs != null) rs.close();
940             } catch (SQLException ignored) {
941             }
942             try {
943                 if (sqlConn != null && !sqlConn.isClosed()) sqlConn.close();
944             } catch (SQLException ignored) {
945             }
946         }
947     }
948
949     static class TabularRowSet implements QueryResult {
950         private final String JavaDoc[] headers;
951         private final List<Object JavaDoc[]> rows;
952         private int totalCount;
953
954         /**
955          * Creates a TabularRowSet based upon a SQL statement result.
956          *
957          * <p>Closes the SqlStatement when it is done.
958          *
959          * @param stmt SqlStatement
960          * @param maxRows Maximum row count
961          * @param firstRowset Ordinal of row to skip to (1-based), or 0 to
962          * start from beginning
963          * @param totalCount Total number of rows. If >= 0, writes the
964          * "totalCount" attribute.
965          * @param resultSetType Type of ResultSet, for example
966          * {@link ResultSet#TYPE_FORWARD_ONLY}.
967          */

968         public TabularRowSet(
969             SqlStatement stmt,
970             int maxRows,
971             int firstRowset,
972             int totalCount,
973             int resultSetType)
974         {
975             this.totalCount = totalCount;
976             ResultSet rs = stmt.getResultSet();
977             try {
978                 ResultSetMetaData md = rs.getMetaData();
979                 int columnCount = md.getColumnCount();
980
981                 // populate header
982
headers = new String JavaDoc[columnCount];
983                 for (int i = 0; i < columnCount; i++) {
984                     headers[i] = md.getColumnName(i + 1);
985                 }
986
987                 // skip to first rowset specified in request
988
int firstRow = (firstRowset <= 0 ? 1 : firstRowset);
989                 if (resultSetType == ResultSet.TYPE_FORWARD_ONLY) {
990                     for (int i = 0; i < firstRow; ++i) {
991                         if (!rs.next()) {
992                             break;
993                         }
994                     }
995                 } else {
996                     rs.absolute(firstRow);
997                 }
998
999                 // populate data
1000
rows = new ArrayList<Object JavaDoc[]>();
1001                maxRows = (maxRows <= 0 ? Integer.MAX_VALUE : maxRows);
1002                do {
1003                    Object JavaDoc[] row = new Object JavaDoc[columnCount];
1004                    for (int i = 0; i < columnCount; i++) {
1005                        row[i] = rs.getObject(i + 1);
1006                    }
1007                    rows.add(row);
1008                } while (rs.next() && --maxRows > 0);
1009            } catch (SQLException e) {
1010                throw stmt.handle(e);
1011            } finally {
1012                stmt.close();
1013            }
1014        }
1015
1016        /**
1017         * Alternate constructor for advanced drill-through.
1018         *
1019         * @param tableFieldMap Map from table name to a list of the names of
1020         * the fields in the table
1021         * @param tableList List of table names
1022         */

1023        public TabularRowSet(
1024            Map<String JavaDoc, List<String JavaDoc>> tableFieldMap, List<String JavaDoc> tableList)
1025        {
1026            List<String JavaDoc> headerList = new ArrayList<String JavaDoc>();
1027            for (String JavaDoc tableName : tableList) {
1028                List<String JavaDoc> fieldNames = tableFieldMap.get(tableName);
1029                for (String JavaDoc fieldName : fieldNames) {
1030                    headerList.add(tableName + "." + fieldName);
1031                }
1032            }
1033            headers = headerList.toArray(new String JavaDoc[headerList.size()]);
1034            rows = new ArrayList<Object JavaDoc[]>();
1035            Object JavaDoc[] row = new Object JavaDoc[headers.length];
1036            for (int k = 0; k < row.length; k++) {
1037                row[k] = k;
1038            }
1039            rows.add(row);
1040        }
1041
1042        public void unparse(SaxWriter writer) throws SAXException JavaDoc {
1043            String JavaDoc[] encodedHeader = new String JavaDoc[headers.length];
1044            for (int i = 0; i < headers.length; i++) {
1045                // replace invalid XML element name, like " ", with "_x0020_" in
1046
// column headers, otherwise will generate a badly-formatted xml
1047
// doc.
1048
encodedHeader[i] = XmlaUtil.encodeElementName(headers[i]);
1049            }
1050
1051            // write total count row if enabled
1052
if (totalCount >= 0) {
1053                String JavaDoc countStr = Integer.toString(totalCount);
1054                writer.startElement("row");
1055                for (String JavaDoc anEncodedHeader : encodedHeader) {
1056                    writer.startElement(anEncodedHeader);
1057                    writer.characters(countStr);
1058                    writer.endElement();
1059                }
1060                writer.endElement(); // row
1061
}
1062
1063            for (Object JavaDoc[] row : rows) {
1064                writer.startElement("row");
1065                for (int i = 0; i < row.length; i++) {
1066                    writer.startElement(encodedHeader[i]);
1067                    Object JavaDoc value = row[i];
1068                    if (value == null) {
1069                        writer.characters("null");
1070                    } else {
1071                        String JavaDoc valueString = value.toString();
1072                        if (value instanceof Number JavaDoc) {
1073                            valueString =
1074                                XmlaUtil.normalizeNumericString(valueString);
1075                        }
1076                        writer.characters(valueString);
1077                    }
1078                    writer.endElement();
1079                }
1080                writer.endElement(); // row
1081
}
1082        }
1083
1084        public TabularRowSet(ResultSet rs) throws SQLException {
1085            ResultSetMetaData md = rs.getMetaData();
1086            int columnCount = md.getColumnCount();
1087
1088            // populate header
1089
headers = new String JavaDoc[columnCount];
1090            for (int i = 0; i < columnCount; i++) {
1091                headers[i] = md.getColumnName(i + 1);
1092            }
1093
1094            // populate data
1095
rows = new ArrayList<Object JavaDoc[]>();
1096            while (rs.next()) {
1097                Object JavaDoc[] row = new Object JavaDoc[columnCount];
1098                for (int i = 0; i < columnCount; i++) {
1099                    row[i] = rs.getObject(i + 1);
1100                }
1101                rows.add(row);
1102            }
1103        }
1104    }
1105
1106    private QueryResult executeQuery(XmlaRequest request)
1107            throws XmlaException {
1108        final String JavaDoc statement = request.getStatement();
1109
1110        if (LOGGER.isDebugEnabled()) {
1111            LOGGER.debug("mdx: \"" + statement + "\"");
1112        }
1113
1114        if ((statement == null) || (statement.length() == 0)) {
1115            return null;
1116        } else {
1117            checkFormat(request);
1118
1119            DataSourcesConfig.DataSource ds = getDataSource(request);
1120            DataSourcesConfig.Catalog dsCatalog = getCatalog(request, ds);
1121            String JavaDoc role = request.getRole();
1122
1123            final Connection connection = getConnection(dsCatalog, role);
1124
1125            final Query query;
1126            try {
1127                query = connection.parseQuery(statement);
1128                query.setResultStyle(ResultStyle.LIST);
1129            } catch (XmlaException ex) {
1130                throw ex;
1131            } catch (Exception JavaDoc ex) {
1132                throw new XmlaException(
1133                    CLIENT_FAULT_FC,
1134                    HSB_PARSE_QUERY_CODE,
1135                    HSB_PARSE_QUERY_FAULT_FS,
1136                    ex);
1137            }
1138            final Result result;
1139            try {
1140                result = connection.execute(query);
1141            } catch (XmlaException ex) {
1142                throw ex;
1143            } catch (Exception JavaDoc ex) {
1144                throw new XmlaException(
1145                    SERVER_FAULT_FC,
1146                    HSB_EXECUTE_QUERY_CODE,
1147                    HSB_EXECUTE_QUERY_FAULT_FS,
1148                    ex);
1149            }
1150
1151            final String JavaDoc formatName = request.getProperties().get(
1152                    PropertyDefinition.Format.name());
1153            Enumeration.Format format = valueOf(Enumeration.Format.class, formatName,
1154                null);
1155
1156            if (format == Enumeration.Format.Multidimensional) {
1157                return new MDDataSet_Multidimensional(result);
1158            } else {
1159                return new MDDataSet_Tabular(result);
1160            }
1161        }
1162    }
1163
1164    /**
1165     * Deduces the XML datatype from the declared datatype
1166     * of the measure, if present. (It comes from the
1167     * "datatype" attribute of the "Measure" element.) If
1168     * not present, use the value type to guess.
1169     *
1170     * <p>The value type depends upon the RDBMS and the JDBC
1171     * driver, so it tends to produce inconsistent results
1172     * between platforms.
1173     *
1174     * @param cell Cell
1175     * @param value Value of the cell
1176     * @return XSD data type (e.g. "xsd:int", "xsd:double", "xsd:string")
1177     */

1178    protected static String JavaDoc deduceValueType(Cell cell, final Object JavaDoc value) {
1179        String JavaDoc datatype = (String JavaDoc)
1180                cell.getPropertyValue(Property.DATATYPE.getName());
1181        if (datatype != null) {
1182            if (datatype.equals("Integer")) {
1183                return "xsd:int";
1184            } else if (datatype.equals("Numeric")) {
1185                return "xsd:double";
1186            } else {
1187                return "xsd:string";
1188            }
1189        } else if (value instanceof Integer JavaDoc || value instanceof Long JavaDoc) {
1190            return "xsd:int";
1191        } else if (value instanceof Double JavaDoc || value instanceof BigDecimal JavaDoc) {
1192            return "xsd:double";
1193        } else {
1194            return "xsd:string";
1195        }
1196    }
1197    protected static String JavaDoc deduceValueType(Evaluator evaluator,
1198                                            final Object JavaDoc value) {
1199        String JavaDoc datatype = (String JavaDoc)
1200                evaluator.getProperty(Property.DATATYPE.getName(), null);
1201        if (datatype != null) {
1202            if (datatype.equals("Integer")) {
1203                return "xsd:int";
1204            } else if (datatype.equals("Numeric")) {
1205                return "xsd:double";
1206            } else {
1207                return "xsd:string";
1208            }
1209        } else if (value instanceof Integer JavaDoc || value instanceof Long JavaDoc) {
1210            return "xsd:int";
1211        } else if (value instanceof Double JavaDoc || value instanceof BigDecimal JavaDoc) {
1212            return "xsd:double";
1213        } else {
1214            return "xsd:string";
1215        }
1216    }
1217
1218    static abstract class MDDataSet implements QueryResult {
1219        protected final Result result;
1220
1221        protected static final String JavaDoc[] cellProps = new String JavaDoc[] {
1222            "Value",
1223            "FmtValue",
1224            "FormatString"};
1225
1226        protected static final String JavaDoc[] cellPropLongs = new String JavaDoc[] {
1227            Property.VALUE.name,
1228            Property.FORMATTED_VALUE.name,
1229            Property.FORMAT_STRING.name};
1230
1231        protected static final String JavaDoc[] defaultProps = new String JavaDoc[] {
1232            "UName",
1233            "Caption",
1234            "LName",
1235            "LNum",
1236            "DisplayInfo",
1237            // Not in spec nor generated by SQL Server
1238
// "Depth"
1239
};
1240        protected static final Map<String JavaDoc, String JavaDoc> longPropNames = new HashMap<String JavaDoc, String JavaDoc>();
1241
1242        static {
1243            longPropNames.put("UName", Property.MEMBER_UNIQUE_NAME.name);
1244            longPropNames.put("Caption", Property.MEMBER_CAPTION.name);
1245            longPropNames.put("LName", Property.LEVEL_UNIQUE_NAME.name);
1246            longPropNames.put("LNum", Property.LEVEL_NUMBER.name);
1247            longPropNames.put("DisplayInfo", Property.DISPLAY_INFO.name);
1248        }
1249
1250        protected MDDataSet(Result result) {
1251            this.result = result;
1252        }
1253    }
1254
1255    static class MDDataSet_Multidimensional extends MDDataSet {
1256        private Hierarchy[] slicerAxisHierarchies;
1257
1258        protected MDDataSet_Multidimensional(Result result) {
1259            super(result);
1260        }
1261
1262        public void unparse(SaxWriter writer) throws SAXException JavaDoc {
1263            olapInfo(writer);
1264            axes(writer);
1265            cellData(writer);
1266        }
1267
1268        private void olapInfo(SaxWriter writer) {
1269            // What are all of the cube's hierachies
1270
Cube cube = result.getQuery().getCube();
1271            List<Hierarchy> hierarchyList = new ArrayList<Hierarchy>();
1272            Dimension[] dimensions = cube.getDimensions();
1273            for (Dimension dimension : dimensions) {
1274                Hierarchy[] hierarchies = dimension.getHierarchies();
1275                for (Hierarchy hierarchy : hierarchies) {
1276                    hierarchyList.add(hierarchy);
1277                }
1278            }
1279
1280            writer.startElement("OlapInfo");
1281            writer.startElement("CubeInfo");
1282            writer.startElement("Cube");
1283            writer.startElement("CubeName");
1284            writer.characters(result.getQuery().getCube().getName());
1285            writer.endElement();
1286            writer.endElement();
1287            writer.endElement(); // CubeInfo
1288

1289            // create AxesInfo for axes
1290
// -----------
1291
writer.startElement("AxesInfo");
1292            final Axis[] axes = result.getAxes();
1293            final QueryAxis[] queryAxes = result.getQuery().getAxes();
1294            //axisInfo(writer, result.getSlicerAxis(), "SlicerAxis");
1295
List<Hierarchy> axisHierarchyList = new ArrayList<Hierarchy>();
1296            for (int i = 0; i < axes.length; i++) {
1297                Hierarchy[] hiers =
1298                        axisInfo(writer, axes[i], queryAxes[i], "Axis" + i);
1299                for (Hierarchy hier : hiers) {
1300                    axisHierarchyList.add(hier);
1301                }
1302
1303            }
1304            // Remove all seen axes.
1305
// What this does is for each hierarchy in one of the
1306
// axes, find its Dimension and then all of that Dimension's
1307
// Hierarchies (most of the time you are back to the original
1308
// Hierarchy) and then for each Hierarchy remove it from the
1309
// list of all Hierarchies.
1310
// NOTE: Don't know if this is correct!!
1311
for (Hierarchy hier1 : axisHierarchyList) {
1312                Dimension dim = hier1.getDimension();
1313                Hierarchy[] hiers = dim.getHierarchies();
1314                for (Hierarchy h : hiers) {
1315                    String JavaDoc uniqueName = h.getUniqueName();
1316                    for (Iterator<Hierarchy> it2 = hierarchyList.iterator(); it2
1317                        .hasNext();) {
1318                        Hierarchy hier2 = it2.next();
1319                        if (uniqueName.equals(hier2.getUniqueName())) {
1320                            it2.remove();
1321                            break;
1322                        }
1323                    }
1324                }
1325            }
1326
1327            ///////////////////////////////////////////////
1328
// create AxesInfo for slicer axes
1329
//
1330
Hierarchy[] hierarchies =
1331                hierarchyList.toArray(new Hierarchy[hierarchyList.size()]);
1332            writer.startElement("AxisInfo",
1333                    new String JavaDoc[] { "name", "SlicerAxis"});
1334            final QueryAxis slicerAxis = result.getQuery().getSlicerAxis();
1335            writeHierarchyInfo(writer, hierarchies, getProps(slicerAxis));
1336            writer.endElement(); // AxisInfo
1337
slicerAxisHierarchies = hierarchies;
1338            //
1339
///////////////////////////////////////////////
1340

1341
1342            writer.endElement(); // AxesInfo
1343
// -----------
1344
writer.startElement("CellInfo");
1345            writer.element("Value", new String JavaDoc[] {
1346                "name", "VALUE"});
1347            writer.element("FmtValue", new String JavaDoc[] {
1348                "name", "FORMATTED_VALUE"});
1349            writer.element("FormatString", new String JavaDoc[] {
1350                "name", "FORMAT_STRING"});
1351            writer.endElement(); // CellInfo
1352
// -----------
1353
writer.endElement(); // OlapInfo
1354
}
1355
1356        private Hierarchy[] axisInfo(
1357                SaxWriter writer,
1358                Axis axis,
1359                QueryAxis queryAxis,
1360                String JavaDoc axisName) {
1361
1362            writer.startElement("AxisInfo", new String JavaDoc[] { "name", axisName});
1363
1364            Hierarchy[] hierarchies;
1365            Iterator<Position> it = axis.getPositions().iterator();
1366            if (it.hasNext()) {
1367                final Position position = it.next();
1368                List<Hierarchy> l = new ArrayList<Hierarchy>();
1369                for (Member member: position) {
1370                    l.add(member.getHierarchy());
1371                }
1372                hierarchies = l.toArray(new Hierarchy[l.size()]);
1373            } else {
1374                hierarchies = new Hierarchy[0];
1375                //final QueryAxis queryAxis = this.result.getQuery().axes[i];
1376
// TODO:
1377
}
1378            String JavaDoc[] props = getProps(queryAxis);
1379            writeHierarchyInfo(writer, hierarchies, props);
1380
1381            writer.endElement(); // AxisInfo
1382

1383            return hierarchies;
1384        }
1385
1386
1387        private void writeHierarchyInfo(
1388                SaxWriter writer,
1389                Hierarchy[] hierarchies,
1390                String JavaDoc[] props) {
1391
1392            for (Hierarchy hierarchy : hierarchies) {
1393                writer.startElement(
1394                    "HierarchyInfo", new String JavaDoc[]{
1395                    "name", hierarchy.getName()
1396                });
1397                for (final String JavaDoc prop : props) {
1398                    String JavaDoc longPropName = longPropNames.get(prop);
1399                    if (longPropName == null) {
1400                        longPropName = prop;
1401                    }
1402                    writer.element(
1403                        prop, new String JavaDoc[]{
1404                        "name",
1405                        hierarchy.getUniqueName() + "." +
1406                            Util.quoteMdxIdentifier(longPropName),
1407                    });
1408                }
1409                writer.endElement(); // HierarchyInfo
1410
}
1411        }
1412
1413        private void axes(SaxWriter writer) {
1414            writer.startElement("Axes");
1415            //axis(writer, result.getSlicerAxis(), "SlicerAxis");
1416
final Axis[] axes = result.getAxes();
1417            final QueryAxis[] queryAxes = result.getQuery().getAxes();
1418            for (int i = 0; i < axes.length; i++) {
1419                final String JavaDoc[] props = getProps(queryAxes[i]);
1420                axis(writer, axes[i], props, "Axis" + i);
1421            }
1422
1423            ////////////////////////////////////////////
1424
// now generate SlicerAxis information
1425
//
1426
Hierarchy[] hierarchies = slicerAxisHierarchies;
1427            writer.startElement("Axis", new String JavaDoc[] { "name", "SlicerAxis"});
1428            writer.startElement("Tuples");
1429            writer.startElement("Tuple");
1430
1431            Map<String JavaDoc, Integer JavaDoc> memberMap = new HashMap<String JavaDoc, Integer JavaDoc>();
1432            Member positionMember;
1433            Axis slicerAxis = result.getSlicerAxis();
1434            if (slicerAxis.getPositions() != null &&
1435                slicerAxis.getPositions().size() > 0) {
1436                final Position pos0 = slicerAxis.getPositions().get(0);
1437                int i = 0;
1438                for (Member member : pos0) {
1439                    memberMap.put(member.getHierarchy().getName(), i++);
1440                }
1441            }
1442
1443            final QueryAxis slicerQueryAxis = result.getQuery().getSlicerAxis();
1444            final List<Member> slicerMembers =
1445                result.getSlicerAxis().getPositions().get(0);
1446            for (Hierarchy hierarchy : hierarchies) {
1447                // Find which member is on the slicer. If it's not explicitly
1448
// there, use the default member.
1449
Member member = hierarchy.getDefaultMember();
1450                final Integer JavaDoc indexPosition =
1451                    memberMap.get(hierarchy.getName());
1452                if (indexPosition != null) {
1453                    positionMember =
1454                        slicerAxis.getPositions().get(0).get(indexPosition);
1455                } else {
1456                    positionMember = null;
1457                }
1458                for (Member slicerMember : slicerMembers) {
1459                    if (slicerMember.getHierarchy().equals(hierarchy)) {
1460                        member = slicerMember;
1461                        break;
1462                    }
1463                }
1464
1465                if (member != null) {
1466                    if (positionMember != null) {
1467                        writeMember(
1468                            writer, positionMember, null,
1469                            slicerAxis.getPositions().get(0), indexPosition,
1470                            getProps(slicerQueryAxis));
1471                    } else {
1472                        slicerAxis(writer, member, getProps(slicerQueryAxis));
1473                    }
1474                } else {
1475                    LOGGER.warn(
1476                        "Can not create SlicerAxis: " +
1477                            "null default member for Hierarchy " +
1478                            hierarchy.getUniqueName());
1479                }
1480            }
1481
1482            //
1483
////////////////////////////////////////////
1484

1485            writer.endElement(); // Tuple
1486
writer.endElement(); // Tuples
1487
writer.endElement(); // Axis
1488

1489
1490
1491            writer.endElement(); // Axes
1492
}
1493
1494        private String JavaDoc[] getProps(QueryAxis queryAxis) {
1495            if (queryAxis == null) {
1496                return defaultProps;
1497            }
1498            Id[] dimensionProperties = queryAxis.getDimensionProperties();
1499            if (dimensionProperties.length == 0) {
1500                return defaultProps;
1501            }
1502            String JavaDoc[] props = new String JavaDoc[defaultProps.length + dimensionProperties.length];
1503            System.arraycopy(defaultProps, 0, props, 0, defaultProps.length);
1504            for (int i = 0; i < dimensionProperties.length; i++) {
1505                props[defaultProps.length + i] =
1506                        dimensionProperties[i].toStringArray()[0];
1507            }
1508            return props;
1509        }
1510
1511        private void axis(SaxWriter writer, Axis axis, String JavaDoc[] props, String JavaDoc axisName) {
1512            writer.startElement("Axis", new String JavaDoc[] { "name", axisName});
1513            writer.startElement("Tuples");
1514
1515            List<Position> positions = axis.getPositions();
1516            Iterator<Position> pit = positions.iterator();
1517            Position prevPosition = null;
1518            Position position = pit.hasNext() ? pit.next() : null;
1519            Position nextPosition = pit.hasNext() ? pit.next() : null;
1520            while (position != null) {
1521                writer.startElement("Tuple");
1522                int k = 0;
1523                for (Member member: position) {
1524                    writeMember(
1525                        writer, member, prevPosition, nextPosition, k++, props);
1526                }
1527                writer.endElement(); // Tuple
1528
prevPosition = position;
1529                position = nextPosition;
1530                nextPosition = pit.hasNext() ? pit.next() : null;
1531            }
1532            writer.endElement(); // Tuples
1533
writer.endElement(); // Axis
1534
}
1535
1536        private void writeMember(
1537            SaxWriter writer,
1538            Member member,
1539            Position prevPosition,
1540            Position nextPosition,
1541            int k,
1542            String JavaDoc[] props)
1543        {
1544            writer.startElement("Member", new String JavaDoc[] {
1545                "Hierarchy", member.getHierarchy().getName()});
1546            for (String JavaDoc prop : props) {
1547                Object JavaDoc value;
1548                String JavaDoc propLong = longPropNames.get(prop);
1549                if (propLong == null) {
1550                    propLong = prop;
1551                }
1552                if (propLong.equals(Property.DISPLAY_INFO.name)) {
1553                    Integer JavaDoc childrenCard = (Integer JavaDoc) member
1554                      .getPropertyValue(Property.CHILDREN_CARDINALITY.name);
1555                    value = calculateDisplayInfo(prevPosition,
1556                                nextPosition,
1557                                member, k, childrenCard);
1558                } else if (propLong.equals(Property.DEPTH.name)) {
1559                    value = member.getDepth();
1560                } else {
1561                    value = member.getPropertyValue(propLong);
1562                }
1563                if (value != null) {
1564                    writer.startElement(prop); // Properties
1565
writer.characters(value.toString());
1566                    writer.endElement(); // Properties
1567
}
1568            }
1569            writer.endElement(); // Member
1570
}
1571
1572        private void slicerAxis(
1573                SaxWriter writer, Member member, String JavaDoc[] props) {
1574            writer.startElement("Member", new String JavaDoc[] {
1575                "Hierarchy", member.getHierarchy().getName()});
1576            for (String JavaDoc prop : props) {
1577                Object JavaDoc value;
1578                String JavaDoc propLong = longPropNames.get(prop);
1579                if (propLong == null) {
1580                    propLong = prop;
1581                }
1582                if (propLong.equals(Property.DISPLAY_INFO.name)) {
1583                    Integer JavaDoc childrenCard =
1584                        (Integer JavaDoc) member
1585                            .getPropertyValue(Property.CHILDREN_CARDINALITY.name);
1586                    // NOTE: don't know if this is correct for
1587
// SlicerAxis
1588
int displayInfo = 0xffff & childrenCard;
1589/*
1590                    int displayInfo =
1591                        calculateDisplayInfo((j == 0 ? null : positions[j - 1]),
1592                          (j + 1 == positions.length ? null : positions[j + 1]),
1593                          member, k, childrenCard.intValue());
1594*/

1595                    value = displayInfo;
1596                } else if (propLong.equals(Property.DEPTH.name)) {
1597                    value = member.getDepth();
1598                } else {
1599                    value = member.getPropertyValue(propLong);
1600                }
1601                if (value != null) {
1602                    writer.startElement(prop); // Properties
1603
writer.characters(value.toString());
1604                    writer.endElement(); // Properties
1605
}
1606            }
1607            writer.endElement(); // Member
1608
}
1609
1610        private int calculateDisplayInfo(
1611            Position prevPosition, Position nextPosition,
1612            Member currentMember, int memberOrdinal, int childrenCount)
1613        {
1614            int displayInfo = 0xffff & childrenCount;
1615
1616            if (nextPosition != null) {
1617                String JavaDoc currentUName = currentMember.getUniqueName();
1618                Member nextMember = nextPosition.get(memberOrdinal);
1619                String JavaDoc nextParentUName = nextMember.getParentUniqueName();
1620                if (currentUName.equals(nextParentUName)) {
1621                    displayInfo |= 0x10000;
1622                }
1623            }
1624            if (prevPosition != null) {
1625                String JavaDoc currentParentUName = currentMember.getParentUniqueName();
1626                Member prevMember = prevPosition.get(memberOrdinal);
1627                String JavaDoc prevParentUName = prevMember.getParentUniqueName();
1628                if (currentParentUName != null &&
1629                    currentParentUName.equals(prevParentUName)) {
1630                    displayInfo |= 0x20000;
1631                }
1632            }
1633            return displayInfo;
1634        }
1635
1636        private void cellData(SaxWriter writer) {
1637            writer.startElement("CellData");
1638            final int axisCount = result.getAxes().length;
1639            int[] pos = new int[axisCount];
1640            int[] cellOrdinal = new int[] {0};
1641
1642            Evaluator evaluator = RolapUtil.createEvaluator(result.getQuery());
1643            int axisOrdinal = axisCount-1;
1644            recurse(writer, pos, axisOrdinal, evaluator, cellOrdinal);
1645
1646            writer.endElement(); // CellData
1647
}
1648        private void recurse(SaxWriter writer, int[] pos,
1649                int axisOrdinal, Evaluator evaluator, int[] cellOrdinal) {
1650            if (axisOrdinal < 0) {
1651                emitCell(writer, pos, evaluator, cellOrdinal[0]++);
1652
1653            } else {
1654                Axis axis = result.getAxes()[axisOrdinal];
1655                List<Position> positions = axis.getPositions();
1656                int i = 0;
1657                for (Position position: positions) {
1658                    pos[axisOrdinal] = i;
1659                    evaluator.setContext(position);
1660                    recurse(writer, pos, axisOrdinal - 1, evaluator, cellOrdinal);
1661                    i++;
1662                }
1663            }
1664        }
1665        private void emitCell(SaxWriter writer, int[] pos,
1666                            Evaluator evaluator, int ordinal) {
1667            Cell cell = result.getCell(pos);
1668            if (cell.isNull()) {
1669                // Ignore null cell like MS AS
1670
return;
1671            }
1672
1673            writer.startElement("Cell", new String JavaDoc[] {
1674                "CellOrdinal", Integer.toString(ordinal)});
1675            for (int i = 0; i < cellProps.length; i++) {
1676                String JavaDoc cellPropLong = cellPropLongs[i];
1677                final Object JavaDoc value = cell.getPropertyValue(cellPropLong);
1678
1679                if (value != null) {
1680                    if (cellPropLong.equals(Property.VALUE.name)) {
1681                        String JavaDoc valueType = deduceValueType(evaluator, value);
1682                        writer.startElement(cellProps[i], new String JavaDoc[] {"xsi:type", valueType});
1683                    } else {
1684                        writer.startElement(cellProps[i]);
1685                    }
1686
1687                    String JavaDoc valueString = value.toString();
1688
1689                    if (cellPropLong.equals(Property.VALUE.name) &&
1690                            value instanceof Number JavaDoc) {
1691                        valueString = XmlaUtil.normalizeNumericString(valueString);
1692                    }
1693
1694                    writer.characters(valueString);
1695                    writer.endElement();
1696                }
1697            }
1698            writer.endElement(); // Cell
1699
}
1700    }
1701
1702    static abstract class ColumnHandler {
1703        protected final String JavaDoc name;
1704        protected final String JavaDoc encodedName;
1705
1706        protected ColumnHandler(String JavaDoc name) {
1707            this.name = name;
1708            this.encodedName = XmlaUtil.encodeElementName(this.name);
1709        }
1710
1711        abstract void write(SaxWriter writer, Cell cell, Member[] members);
1712        abstract void metadata(SaxWriter writer);
1713    }
1714
1715
1716    /**
1717     * Callback to handle one column, representing the combination of a
1718     * level and a property (e.g. [Store].[Store State].[MEMBER_UNIQUE_NAME])
1719     * in a flattened dataset.
1720     */

1721    static class CellColumnHandler extends ColumnHandler {
1722
1723        CellColumnHandler(String JavaDoc name) {
1724            super(name);
1725        }
1726
1727        public void metadata(SaxWriter writer) {
1728            writer.element("xsd:element", new String JavaDoc[] {
1729                "minOccurs", "0",
1730                "name", encodedName,
1731                "sql:field", name,
1732            });
1733        }
1734
1735        public void write(
1736                SaxWriter writer, Cell cell, Member[] members) {
1737            if (cell.isNull()) {
1738                return;
1739            }
1740            Object JavaDoc value = cell.getValue();
1741            String JavaDoc valueString = value.toString();
1742            String JavaDoc valueType = deduceValueType(cell, value);
1743
1744            writer.startElement(encodedName, new String JavaDoc[] {
1745                "xsi:type", valueType});
1746            if (value instanceof Number JavaDoc) {
1747                valueString = XmlaUtil.normalizeNumericString(valueString);
1748            }
1749            writer.characters(valueString);
1750            writer.endElement();
1751        }
1752    }
1753
1754    /**
1755     * Callback to handle one column, representing the combination of a
1756     * level and a property (e.g. [Store].[Store State].[MEMBER_UNIQUE_NAME])
1757     * in a flattened dataset.
1758     */

1759    static class MemberColumnHandler extends ColumnHandler {
1760        private final String JavaDoc property;
1761        private final Level level;
1762        private final int memberOrdinal;
1763
1764        public MemberColumnHandler(
1765                String JavaDoc property, Level level, int memberOrdinal) {
1766            super(level.getUniqueName() + "." +
1767                    Util.quoteMdxIdentifier(property));
1768            this.property = property;
1769            this.level = level;
1770            this.memberOrdinal = memberOrdinal;
1771        }
1772
1773        public void metadata(SaxWriter writer) {
1774            writer.element("xsd:element", new String JavaDoc[] {
1775                "minOccurs", "0",
1776                "name", encodedName,
1777                "sql:field", name,
1778                "type", "xsd:string",
1779            });
1780        }
1781
1782        public void write(
1783                SaxWriter writer, Cell cell, Member[] members) {
1784            Member member = members[memberOrdinal];
1785            final int depth = level.getDepth();
1786            if (member.getDepth() < depth) {
1787                // This column deals with a level below the current member.
1788
// There is no value to write.
1789
return;
1790            }
1791            while (member.getDepth() > depth) {
1792                member = member.getParentMember();
1793            }
1794            final Object JavaDoc propertyValue = member.getPropertyValue(property);
1795            if (propertyValue == null) {
1796                return;
1797            }
1798
1799            writer.startElement(encodedName);
1800            writer.characters(propertyValue.toString());
1801            writer.endElement();
1802        }
1803    }
1804
1805    static class MDDataSet_Tabular extends MDDataSet {
1806        private final boolean empty;
1807        private final int[] pos;
1808        private final int axisCount;
1809        private int cellOrdinal;
1810
1811        private static final Id[] MemberCaptionIdArray = {
1812            new Id(new Id.Segment(Property.MEMBER_CAPTION.name, Id.Quoting.QUOTED))
1813        };
1814        private final Member[] members;
1815        private final ColumnHandler[] columnHandlers;
1816
1817        public MDDataSet_Tabular(Result result) {
1818            super(result);
1819            final Axis[] axes = result.getAxes();
1820            axisCount = axes.length;
1821            pos = new int[axisCount];
1822
1823            // Count dimensions, and deduce list of levels which appear on
1824
// non-COLUMNS axes.
1825
boolean empty = false;
1826            int dimensionCount = 0;
1827            for (int i = axes.length - 1; i > 0; i--) {
1828                Axis axis = axes[i];
1829                if (axis.getPositions().size() == 0) {
1830                    // If any axis is empty, the whole data set is empty.
1831
empty = true;
1832                    continue;
1833                }
1834                dimensionCount += axis.getPositions().get(0).size();
1835            }
1836            this.empty = empty;
1837
1838            // Build a list of the lowest level used on each non-COLUMNS axis.
1839
Level[] levels = new Level[dimensionCount];
1840            List<ColumnHandler> columnHandlerList = new ArrayList<ColumnHandler>();
1841            int memberOrdinal = 0;
1842            if (!empty) {
1843                for (int i = axes.length - 1; i > 0; i--) {
1844                    final Axis axis = axes[i];
1845                    final QueryAxis queryAxis = result.getQuery().getAxes()[i];
1846                    final int z0 = memberOrdinal; // save ordinal so can rewind
1847
final List<Position> positions = axis.getPositions();
1848                    int jj = 0;
1849                    for (Position position: positions) {
1850                        memberOrdinal = z0; // rewind to start
1851
//final Member[] members = position.members;
1852
for (Member member : position) {
1853                            if (jj == 0 ||
1854                                member.getLevel().getDepth() >
1855                                    levels[memberOrdinal].getDepth()) {
1856                                levels[memberOrdinal] = member.getLevel();
1857                            }
1858                        }
1859                        ++memberOrdinal;
1860                        jj++;
1861                    }
1862
1863                    // Now we know the lowest levels on this axis, add
1864
// properties.
1865
Id[] dimProps = queryAxis.getDimensionProperties();
1866                    if (dimProps.length == 0) {
1867                        dimProps = MemberCaptionIdArray;
1868                    }
1869                    for (int j = z0; j < memberOrdinal; j++) {
1870                        Level level = levels[j];
1871                        for (int k = 0; k <= level.getDepth(); k++) {
1872                            final Level level2 =
1873                                    level.getHierarchy().getLevels()[k];
1874                            if (level2.isAll()) {
1875                                continue;
1876                            }
1877                            for (Id dimProp : dimProps) {
1878                                columnHandlerList.add(
1879                                    new MemberColumnHandler(
1880                                        dimProp.toStringArray()[0],
1881                                        level2,
1882                                        j));
1883                            }
1884                        }
1885                    }
1886                }
1887            }
1888            this.members = new Member[memberOrdinal];
1889
1890            // Deduce the list of column headings.
1891
Axis columnsAxis = axes[0];
1892            for (Position position : columnsAxis.getPositions()) {
1893                String JavaDoc name = null;
1894                int j = 0;
1895                for (Member member : position) {
1896                    if (j == 0) {
1897                        name = member.getUniqueName();
1898                    } else {
1899                        name = name + "." + member.getUniqueName();
1900                    }
1901                    j++;
1902                }
1903                columnHandlerList.add(
1904                    new CellColumnHandler(name));
1905            }
1906
1907            this.columnHandlers =
1908                columnHandlerList.toArray(
1909                    new ColumnHandler[columnHandlerList.size()]);
1910        }
1911
1912        public void metadata(SaxWriter writer) {
1913            if (empty) {
1914                return;
1915            }
1916            writer.startElement("xsd:schema", new String JavaDoc[] {
1917                "xmlns:xsd", XmlaConstants.NS_XSD,
1918                "targetNamespace", NS_XMLA_ROWSET,
1919                "xmlns", NS_XMLA_ROWSET,
1920                "xmlns:xsi", XmlaConstants.NS_XSI,
1921                "xmlns:sql", NS_XML_SQL,
1922                "elementFormDefault", "qualified"
1923            });
1924
1925            { // <root>
1926
writer.startElement("xsd:element", new String JavaDoc[] {
1927                    "name", "root"
1928                });
1929                writer.startElement("xsd:complexType");
1930                writer.startElement("xsd:sequence");
1931                writer.element("xsd:element", new String JavaDoc[] {
1932                    "maxOccurs", "unbounded",
1933                    "minOccurs", "0",
1934                    "name", "row",
1935                    "type", "row",
1936                });
1937                writer.endElement(); // xsd:sequence
1938
writer.endElement(); // xsd:complexType
1939
writer.endElement(); // xsd:element name=root
1940
}
1941
1942            { // xsd:simpleType name="uuid"
1943
writer.startElement("xsd:simpleType", new String JavaDoc[] {
1944                    "name", "uuid",
1945                });
1946                writer.startElement("xsd:restriction", new String JavaDoc[] {
1947                    "base", "xsd:string",
1948                });
1949                writer.element("xsd:pattern", new String JavaDoc[] {
1950                    "value", "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"
1951                });
1952                writer.endElement(); // xsd:restriction
1953
writer.endElement(); // xsd:simpleType
1954
}
1955
1956            { // xsd:complexType name="row"
1957
writer.startElement("xsd:complexType", new String JavaDoc[] {
1958                    "name", "row",
1959                });
1960                writer.startElement("xsd:sequence");
1961                for (ColumnHandler columnHandler : columnHandlers) {
1962                    columnHandler.metadata(writer);
1963                }
1964                writer.endElement(); // xsd:sequence
1965
writer.endElement(); // xsd:complexType
1966
}
1967            writer.endElement(); // xsd:schema
1968
}
1969
1970        public void unparse(SaxWriter writer) throws SAXException JavaDoc {
1971            if (empty) {
1972                return;
1973            }
1974            cellData(writer);
1975        }
1976
1977        private void cellData(SaxWriter writer) throws SAXException JavaDoc {
1978            cellOrdinal = 0;
1979            if (axisCount == 0) { // For MDX like: SELECT FROM Sales
1980
emitCell(writer, result.getCell(pos));
1981            } else {
1982                recurse(writer, axisCount - 1, 0);
1983            }
1984        }
1985
1986        private void recurse(
1987                SaxWriter writer,
1988                int axis,
1989                final int headerOrdinal) throws SAXException JavaDoc {
1990            final List<Position> positions = result.getAxes()[axis].getPositions();
1991            int i = 0;
1992            for (Position position: positions) {
1993                pos[axis] = i;
1994                if (axis == 0) {
1995                    final Cell cell = result.getCell(pos);
1996                    emitCell(writer, cell);
1997                } else {
1998                    // Populate headers and values with levels.
1999
int ho = headerOrdinal;
2000                    for (Member member : position) {
2001                        members[ho++] = member;
2002                    }
2003
2004                    recurse(writer, axis - 1, ho);
2005                }
2006                i++;
2007            }
2008        }
2009
2010        private void emitCell(SaxWriter writer, Cell cell) {
2011            ++cellOrdinal;
2012            Util.discard(cellOrdinal);
2013
2014            // Ignore empty cells.
2015
final Object JavaDoc cellValue = cell.getValue();
2016            if (cellValue == null) {
2017                return;
2018            }
2019
2020            writer.startElement("row");
2021            for (ColumnHandler columnHandler : columnHandlers) {
2022                columnHandler.write(writer, cell, members);
2023            }
2024            writer.endElement();
2025        }
2026    }
2027
2028    private void discover(XmlaRequest request, XmlaResponse response)
2029            throws XmlaException {
2030
2031        final RowsetDefinition rowsetDefinition =
2032            RowsetDefinition.valueOf(request.getRequestType());
2033        Rowset rowset = rowsetDefinition.getRowset(request, this);
2034
2035        final String JavaDoc formatName =
2036            request.getProperties().get(PropertyDefinition.Format.name());
2037        Enumeration.Format format =
2038            valueOf(
2039                Enumeration.Format.class,
2040                formatName,
2041                Enumeration.Format.Tabular);
2042        if (format != Enumeration.Format.Tabular) {
2043            throw new XmlaException(
2044                CLIENT_FAULT_FC,
2045                HSB_DISCOVER_FORMAT_CODE,
2046                HSB_DISCOVER_FORMAT_FAULT_FS,
2047                new UnsupportedOperationException JavaDoc("<Format>: only 'Tabular' allowed in Discover method type"));
2048        }
2049        final String JavaDoc contentName =
2050            request.getProperties().get(PropertyDefinition.Content.name());
2051        // default value is SchemaData
2052
Enumeration.Content content =
2053            valueOf(Enumeration.Content.class, contentName, CONTENT_DEFAULT);
2054
2055        SaxWriter writer = response.getWriter();
2056        writer.startDocument();
2057
2058        writer.startElement(prefix + ":DiscoverResponse", new String JavaDoc[] {
2059            "xmlns:" + prefix, NS_XMLA});
2060        writer.startElement(prefix + ":return");
2061        writer.startElement("root", new String JavaDoc[] {
2062            "xmlns", NS_XMLA_ROWSET,
2063            "xmlns:xsi", NS_XSI,
2064            "xmlns:xsd", NS_XSD,
2065            "xmlns:EX", NS_XMLA_EX
2066        });
2067
2068        if ((content == Enumeration.Content.Schema)
2069                || (content == Enumeration.Content.SchemaData)) {
2070            rowset.rowsetDefinition.writeRowsetXmlSchema(writer);
2071        }
2072
2073        try {
2074            if ((content == Enumeration.Content.Data)
2075                    || (content == Enumeration.Content.SchemaData)) {
2076                rowset.unparse(response);
2077            }
2078        } catch (XmlaException xex) {
2079            throw xex;
2080        } catch (Throwable JavaDoc t) {
2081            throw new XmlaException(
2082                SERVER_FAULT_FC,
2083                HSB_DISCOVER_UNPARSE_CODE,
2084                HSB_DISCOVER_UNPARSE_FAULT_FS,
2085                t);
2086
2087        } finally {
2088            // keep the tags balanced, even if there's an error
2089
writer.endElement();
2090            writer.endElement();
2091            writer.endElement();
2092        }
2093
2094        writer.endDocument();
2095    }
2096
2097    /**
2098     * Returns enum constant of the specified enum type with the given name.
2099     *
2100     * @param enumType Enumerated type
2101     * @param name Name of constant
2102     * @param defaultValue Default value if constant is not found
2103     * @return Value, or null if name is null or value does not exist
2104     */

2105    private <E extends Enum JavaDoc<E>> E valueOf(
2106        Class JavaDoc<E> enumType,
2107        String JavaDoc name, E defaultValue)
2108    {
2109        if (name == null) {
2110            return defaultValue;
2111        } else {
2112            try {
2113                return Enum.valueOf(enumType, name);
2114            } catch (IllegalArgumentException JavaDoc e) {
2115                return defaultValue;
2116            }
2117        }
2118    }
2119
2120    /**
2121     * Gets a Connection given a catalog (and implicitly the catalog's data
2122     * source) and a user role.
2123     *
2124     * @param catalog Catalog
2125     * @param role User role
2126     * @return Connection
2127     * @throws XmlaException If error occurs
2128     */

2129    protected Connection getConnection(
2130            DataSourcesConfig.Catalog catalog,
2131            String JavaDoc role)
2132            throws XmlaException {
2133        DataSourcesConfig.DataSource ds = catalog.getDataSource();
2134
2135        Util.PropertyList connectProperties =
2136            Util.parseConnectString(catalog.getDataSourceInfo());
2137
2138        String JavaDoc catalogUrl = catalogLocator.locate(catalog.definition);
2139
2140        if (LOGGER.isDebugEnabled()) {
2141            if (catalogUrl == null) {
2142                LOGGER.debug("XmlaHandler.getConnection: catalogUrl is null");
2143            } else {
2144                LOGGER.debug("XmlaHandler.getConnection: catalogUrl=" + catalogUrl);
2145            }
2146        }
2147
2148        connectProperties.put("catalog", catalogUrl);
2149
2150        // Checking access
2151
if (!DataSourcesConfig.DataSource.AUTH_MODE_UNAUTHENTICATED
2152            .equalsIgnoreCase(
2153                ds.getAuthenticationMode()) &&
2154            null == role)
2155        {
2156            throw new XmlaException(
2157                CLIENT_FAULT_FC,
2158                HSB_ACCESS_DENIED_CODE,
2159                HSB_ACCESS_DENIED_FAULT_FS,
2160                new SecurityException JavaDoc(
2161                    "Access denied for data source needing authentication"));
2162        }
2163
2164        connectProperties.put("role", role);
2165        RolapConnection conn =
2166            (RolapConnection) DriverManager.getConnection(
2167                connectProperties, null, false);
2168
2169if (LOGGER.isDebugEnabled()) {
2170if (conn == null) {
2171LOGGER.debug("XmlaHandler.getConnection: returning connection null");
2172} else {
2173LOGGER.debug("XmlaHandler.getConnection: returning connection not null");
2174}
2175}
2176        return conn;
2177    }
2178
2179    /**
2180     * Returns the DataSource associated with the request property or null if
2181     * one was not specified.
2182     *
2183     * @param request Request
2184     * @return DataSource for this request
2185     * @throws XmlaException If error occurs
2186     */

2187    public DataSourcesConfig.DataSource getDataSource(XmlaRequest request)
2188        throws XmlaException
2189    {
2190        Map<String JavaDoc, String JavaDoc> properties = request.getProperties();
2191        final String JavaDoc dataSourceInfo =
2192            properties.get(PropertyDefinition.DataSourceInfo.name());
2193        if (!dataSourcesMap.containsKey(dataSourceInfo)) {
2194            throw new XmlaException(
2195                CLIENT_FAULT_FC,
2196                HSB_CONNECTION_DATA_SOURCE_CODE,
2197                HSB_CONNECTION_DATA_SOURCE_FAULT_FS,
2198                Util.newError("no data source is configured with name '" +
2199                    dataSourceInfo + "'"));
2200        }
2201        if (LOGGER.isDebugEnabled()) {
2202            LOGGER.debug("XmlaHandler.getDataSource: dataSourceInfo=" +
2203                dataSourceInfo);
2204        }
2205
2206        final DataSourcesConfig.DataSource ds =
2207            dataSourcesMap.get(dataSourceInfo);
2208        if (LOGGER.isDebugEnabled()) {
2209            if (ds == null) {
2210                // TODO: this if a failure situation
2211
LOGGER.debug("XmlaHandler.getDataSource: ds is null");
2212            } else {
2213                LOGGER.debug("XmlaHandler.getDataSource: ds.dataSourceInfo=" +
2214                    ds.getDataSourceInfo());
2215            }
2216        }
2217        return ds;
2218    }
2219
2220    /**
2221     * Get the DataSourcesConfig.Catalog with the given catalog name from the
2222     * DataSource's catalogs if there is a match and otherwise return null.
2223     *
2224     * @param ds DataSource
2225     * @param catalogName Catalog name
2226     * @return DataSourcesConfig.Catalog or null
2227     */

2228    public DataSourcesConfig.Catalog getCatalog(
2229        DataSourcesConfig.DataSource ds,
2230        String JavaDoc catalogName)
2231    {
2232        DataSourcesConfig.Catalog[] catalogs = ds.catalogs.catalogs;
2233        if (catalogName == null) {
2234            // if there is no catalog name - its optional and there is
2235
// only one, then return it.
2236
if (catalogs.length == 1) {
2237                return catalogs[0];
2238            }
2239        } else {
2240            for (DataSourcesConfig.Catalog dsCatalog : catalogs) {
2241                if (catalogName.equals(dsCatalog.name)) {
2242                    return dsCatalog;
2243                }
2244            }
2245        }
2246        return null;
2247    }
2248
2249    /**
2250     * Get array of DataSourcesConfig.Catalog returning only one entry if the
2251     * catalog was specified as a property in the request or all catalogs
2252     * associated with the Datasource if there was no catalog property.
2253     *
2254     * @param request Request
2255     * @param ds DataSource
2256     * @return Array of DataSourcesConfig.Catalog
2257     */

2258    public DataSourcesConfig.Catalog[] getCatalogs(
2259            XmlaRequest request,
2260            DataSourcesConfig.DataSource ds) {
2261
2262        Map<String JavaDoc, String JavaDoc> properties = request.getProperties();
2263        final String JavaDoc catalogName =
2264            properties.get(PropertyDefinition.Catalog.name());
2265        if (catalogName != null) {
2266            DataSourcesConfig.Catalog dsCatalog = getCatalog(ds, catalogName);
2267            return new DataSourcesConfig.Catalog[] { dsCatalog };
2268        } else {
2269            // no catalog specified in Properties so return them all
2270
return ds.catalogs.catalogs;
2271        }
2272    }
2273
2274    /**
2275     * Returns the DataSourcesConfig.Catalog associated with the
2276     * catalog name that is part of the request properties or
2277     * null if there is no catalog with that name.
2278     *
2279     * @param request Request
2280     * @param ds DataSource
2281     * @return DataSourcesConfig Catalog or null
2282     * @throws XmlaException If error occurs
2283     */

2284    public DataSourcesConfig.Catalog getCatalog(
2285        XmlaRequest request,
2286        DataSourcesConfig.DataSource ds)
2287        throws XmlaException
2288    {
2289        Map<String JavaDoc, String JavaDoc> properties = request.getProperties();
2290        final String JavaDoc catalogName =
2291            properties.get(PropertyDefinition.Catalog.name());
2292        DataSourcesConfig.Catalog dsCatalog = getCatalog(ds, catalogName);
2293        if ((dsCatalog == null) && (catalogName == null)) {
2294            throw new XmlaException(
2295                CLIENT_FAULT_FC,
2296                HSB_CONNECTION_DATA_SOURCE_CODE,
2297                HSB_CONNECTION_DATA_SOURCE_FAULT_FS,
2298                Util.newError("no catalog named '" + catalogName + "'"));
2299        }
2300        return dsCatalog;
2301    }
2302
2303    private TabularRowSet executeColumnQuery(XmlaRequest request)
2304        throws XmlaException
2305    {
2306        checkFormat(request);
2307
2308        DataSourcesConfig.DataSource ds = getDataSource(request);
2309        DataSourcesConfig.Catalog dsCatalog = getCatalog(request, ds);
2310        String JavaDoc role = request.getRole();
2311
2312        final Connection connection = getConnection(dsCatalog, role);
2313        final String JavaDoc statement = request.getStatement();
2314        final Query query = connection.parseQuery(statement);
2315        query.setResultStyle(ResultStyle.LIST);
2316        final Result result = connection.execute(query);
2317        Cell dtCell = result.getCell(new int[] {0, 0});
2318
2319        if (!dtCell.canDrillThrough()) {
2320            throw new XmlaException(
2321                SERVER_FAULT_FC,
2322                HSB_DRILL_THROUGH_NOT_ALLOWED_CODE,
2323                HSB_DRILL_THROUGH_NOT_ALLOWED_FAULT_FS,
2324                Util.newError("Cannot do DillThrough operation on the cell"));
2325        }
2326
2327        final Map<String JavaDoc, String JavaDoc> properties = request.getProperties();
2328        String JavaDoc dtSql = dtCell.getDrillThroughSQL(true);
2329
2330        int index = dtSql.indexOf("from");
2331        String JavaDoc whereClause = " " + dtSql.substring(index);
2332        final String JavaDoc fieldNames = properties.get(PropertyDefinition.TableFields.name());
2333        StringTokenizer st = new StringTokenizer(fieldNames, ",");
2334        drillThruColumnNames.clear();
2335        while (st.hasMoreTokens()) {
2336            drillThruColumnNames.add(st.nextToken());
2337        }
2338
2339        // Create Select Clause
2340
StringBuilder JavaDoc buf = new StringBuilder JavaDoc("select ");
2341        int k = -1;
2342        for (String JavaDoc drillThruColumnName : drillThruColumnNames) {
2343            if (++k > 0) {
2344                buf.append(",");
2345            }
2346            buf.append(drillThruColumnName);
2347        }
2348        buf.append(' ');
2349        buf.append(whereClause);
2350        dtSql = buf.toString();
2351
2352        DataSource JavaDoc dataSource = ((RolapConnection) connection).getDataSource();
2353        try {
2354            int count = -1;
2355            if (MondrianProperties.instance().EnableTotalCount.booleanValue()) {
2356                String JavaDoc temp = dtSql.toUpperCase();
2357                int fromOff = temp.indexOf("FROM");
2358                buf.setLength(0);
2359                buf.append("select count(*) ");
2360                buf.append(dtSql.substring(fromOff));
2361
2362                String JavaDoc countSql = buf.toString();
2363
2364                if (LOGGER.isDebugEnabled()) {
2365                    LOGGER.debug("Advanced drill through counting sql: " + countSql);
2366                }
2367                SqlStatement smt =
2368                    RolapUtil.executeQuery(
2369                        dataSource, countSql, -1,
2370                        "XmlaHandler.executeColumnQuery",
2371                        "Advanced drill-through",
2372                        ResultSet.TYPE_SCROLL_INSENSITIVE,
2373                        ResultSet.CONCUR_READ_ONLY);
2374
2375                try {
2376                    ResultSet rs = smt.getResultSet();
2377                    if (rs.next()) {
2378                        count = rs.getInt(1);
2379                        ++smt.rowCount;
2380                    }
2381                } catch (SQLException e) {
2382                    smt.handle(e);
2383                } finally {
2384                    smt.close();
2385                }
2386            }
2387
2388            if (LOGGER.isDebugEnabled()) {
2389                LOGGER.debug("Advanced drill through sql: " + dtSql);
2390            }
2391            SqlStatement stmt =
2392                RolapUtil.executeQuery(
2393                    dataSource, dtSql, -1, "XmlaHandler.executeColumnQuery",
2394                    "Advanced drill-through",
2395                    ResultSet.TYPE_SCROLL_INSENSITIVE,
2396                    ResultSet.CONCUR_READ_ONLY);
2397
2398            return new TabularRowSet(
2399                stmt, request.drillThroughMaxRows(),
2400                request.drillThroughFirstRowset(), count,
2401                ResultSet.TYPE_FORWARD_ONLY);
2402
2403        } catch (XmlaException xex) {
2404            throw xex;
2405        } catch (RuntimeException JavaDoc rte) {
2406            throw new XmlaException(
2407                SERVER_FAULT_FC,
2408                HSB_DRILL_THROUGH_SQL_CODE,
2409                HSB_DRILL_THROUGH_SQL_FAULT_FS,
2410                rte);
2411        }
2412    }
2413}
2414
2415// End XmlaHandler.java
2416
Popular Tags