KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mondrian > xmla > Rowset


1 /*
2 // $Id: //open/mondrian/src/main/mondrian/xmla/Rowset.java#25 $
3 // This software is subject to the terms of the Common Public License
4 // Agreement, available at the following URL:
5 // http://www.opensource.org/licenses/cpl.html.
6 // Copyright (C) 2003-2006 Julian Hyde
7 // All Rights Reserved.
8 // You must accept the terms of that agreement to use this software.
9 //
10 // jhyde, May 2, 2003
11 */

12 package mondrian.xmla;
13
14 import java.util.*;
15
16 import mondrian.olap.Util;
17
18 import org.apache.log4j.Logger;
19
20 /**
21  * Base class for an XML for Analysis schema rowset. A concrete derived class
22  * should implement {@link #populate}, calling {@link #addRow} for each row.
23  *
24  * @author jhyde
25  * @see mondrian.xmla.RowsetDefinition
26  * @since May 2, 2003
27  * @version $Id: //open/mondrian/src/main/mondrian/xmla/Rowset.java#25 $
28  */

29 abstract class Rowset implements XmlaConstants {
30     protected static final Logger LOGGER = Logger.getLogger(Rowset.class);
31
32     protected final RowsetDefinition rowsetDefinition;
33     protected final Map<String JavaDoc, List<String JavaDoc>> restrictions;
34     protected final Map<String JavaDoc, String JavaDoc> properties;
35     protected final XmlaRequest request;
36     protected final XmlaHandler handler;
37     private final RowsetDefinition.Column[] restrictedColumns;
38
39     /**
40      * The exceptions thrown in this constructor are not produced during
41      * the execution of an XMLA request and so can be ordinary exceptions and
42      * not XmlaException (which are specifically for generating SOAP Fault
43      * xml).
44      *
45      */

46     Rowset(RowsetDefinition definition, XmlaRequest request, XmlaHandler handler) {
47         this.rowsetDefinition = definition;
48         this.restrictions = request.getRestrictions();
49         this.properties = request.getProperties();
50         this.request = request;
51         this.handler = handler;
52         ArrayList<RowsetDefinition.Column> list =
53             new ArrayList<RowsetDefinition.Column>();
54         for (Map.Entry<String JavaDoc, List<String JavaDoc>> restrictionEntry :
55             restrictions.entrySet())
56         {
57             String JavaDoc restrictedColumn = restrictionEntry.getKey();
58             LOGGER.debug(
59                 "Rowset<init>: restrictedColumn=\"" + restrictedColumn + "\"");
60             final RowsetDefinition.Column column = definition.lookupColumn(
61                 restrictedColumn);
62             if (column == null) {
63                 throw Util.newError("Rowset '" + definition.name() +
64                     "' does not contain column '" + restrictedColumn + "'");
65             }
66             if (!column.restriction) {
67                 throw Util.newError("Rowset '" + definition.name() +
68                     "' column '" + restrictedColumn +
69                     "' does not allow restrictions");
70             }
71             // Check that the value is of the right type.
72
final List<String JavaDoc> requiredValue = restrictionEntry.getValue();
73             if (requiredValue.size() > 1) {
74                 final RowsetDefinition.Type type = column.type;
75                 switch (type) {
76                 case StringArray:
77                 case EnumerationArray:
78                 case StringSometimesArray:
79                     break; // OK
80
default:
81                     throw Util.newError("Rowset '" + definition.name() +
82                         "' column '" + restrictedColumn +
83                         "' can only be restricted on one value at a time");
84                 }
85             }
86             list.add(column);
87         }
88         list = pruneRestrictions(list);
89         this.restrictedColumns =
90             list.toArray(
91                 new RowsetDefinition.Column[list.size()]);
92         for (Map.Entry<String JavaDoc, String JavaDoc> propertyEntry : properties.entrySet()) {
93             String JavaDoc propertyName = propertyEntry.getKey();
94             final PropertyDefinition propertyDef =
95                 Util.lookup(PropertyDefinition.class, propertyName);
96             if (propertyDef == null) {
97                 throw Util.newError("Rowset '" + definition.name() +
98                     "' does not support property '" + propertyName + "'");
99             }
100             final String JavaDoc propertyValue = propertyEntry.getValue();
101             setProperty(propertyDef, propertyValue);
102         }
103     }
104
105     protected ArrayList<RowsetDefinition.Column> pruneRestrictions(
106         ArrayList<RowsetDefinition.Column> list)
107     {
108         return list;
109     }
110
111     /**
112      * Sets a property for this rowset. Called by the constructor for each
113      * supplied property.<p/>
114      *
115      * A derived class should override this method and intercept each
116      * property it supports. Any property it does not support, it should forward
117      * to the base class method, which will probably throw an error.<p/>
118      */

119     protected void setProperty(PropertyDefinition propertyDef, String JavaDoc value) {
120         switch (propertyDef) {
121         case Format:
122             break;
123         case DataSourceInfo:
124             break;
125         case Catalog:
126             break;
127         case LocaleIdentifier:
128             // locale ids:
129
// http://krafft.com/scripts/deluxe-calendar/lcid_chart.htm
130
// 1033 is US English
131
if ((value != null) && (value.equals("1033"))) {
132                 return;
133             }
134             // fall through
135
default:
136             LOGGER.warn("Warning: Rowset '" + rowsetDefinition.name() +
137                     "' does not support property '" + propertyDef.name() +
138                     "' (value is '" + value + "')");
139         }
140     }
141
142     /**
143      * Writes the contents of this rowset as a series of SAX events.
144      */

145     public final void unparse(XmlaResponse response) throws XmlaException
146     {
147         List<Row> rows = new ArrayList<Row>();
148         populate(response, rows);
149         Comparator<Row> comparator = rowsetDefinition.getComparator();
150         if (comparator != null) {
151             Collections.sort(rows, comparator);
152         }
153         for (Row row : rows) {
154             emit(row, response);
155         }
156     }
157
158     /**
159      * Gathers the set of rows which match a given set of the criteria.
160      */

161     public abstract void populate(XmlaResponse response, List<Row> rows) throws XmlaException;
162
163     /**
164      * Adds a {@link Row} to a result, provided that it meets the necessary
165      * criteria. Returns whether the row was added.
166      *
167      * @param row Row
168      * @param rows List of result rows
169      */

170     protected final boolean addRow(Row row, List<Row> rows) throws XmlaException {
171         return rows.add(row);
172     }
173
174     /**
175      * Emits a row for this rowset, reading fields from a
176      * {@link mondrian.xmla.Rowset.Row} object.
177      *
178      * @param row Row
179      * @param response XMLA response writer
180      */

181     protected void emit(Row row, XmlaResponse response) throws XmlaException {
182
183         SaxWriter writer = response.getWriter();
184
185         writer.startElement("row");
186         for (RowsetDefinition.Column column : rowsetDefinition.columnDefinitions) {
187             Object JavaDoc value = row.get(column.name);
188             if (value == null) {
189                 if (!column.nullable) {
190                     throw new XmlaException(
191                         CLIENT_FAULT_FC,
192                         HSB_BAD_NON_NULLABLE_COLUMN_CODE,
193                         HSB_BAD_NON_NULLABLE_COLUMN_FAULT_FS,
194                         Util.newInternal("Value required for column " +
195                             column.name +
196                             " of rowset " +
197                             rowsetDefinition.name()));
198                 }
199             } else if (value instanceof XmlElement[]) {
200                 XmlElement[] elements = (XmlElement[]) value;
201                 for (XmlElement element : elements) {
202                     emitXmlElement(writer, element);
203                 }
204             } else if (value instanceof Object JavaDoc[]) {
205                 Object JavaDoc[] values = (Object JavaDoc[]) value;
206                 for (Object JavaDoc value1 : values) {
207                     writer.startElement(column.name);
208                     writer.characters(value1.toString());
209                     writer.endElement();
210                 }
211             } else if (value instanceof List) {
212                 List values = (List) value;
213                 for (Object JavaDoc value1 : values) {
214                     if (value1 instanceof XmlElement) {
215                         XmlElement xmlElement = (XmlElement) value1;
216                         emitXmlElement(writer, xmlElement);
217                     } else {
218                         writer.startElement(column.name);
219                         writer.characters(value1.toString());
220                         writer.endElement();
221                     }
222                 }
223             } else {
224                 writer.startElement(column.name);
225                 writer.characters(value.toString());
226                 writer.endElement();
227             }
228         }
229         writer.endElement();
230     }
231
232     private void emitXmlElement(SaxWriter writer, XmlElement element) {
233         if (element.attributes == null) {
234             writer.startElement(element.tag);
235         } else {
236             writer.startElement(element.tag, element.attributes);
237         }
238
239         if (element.text == null) {
240             for (XmlElement aChildren : element.children) {
241                 emitXmlElement(writer, aChildren);
242             }
243         } else {
244             writer.characters(element.text);
245         }
246
247         writer.endElement();
248     }
249
250     /**
251      * Emits a row for this rowset, reading field values from an object using
252      * reflection.
253      *
254      */

255     protected void emit(Object JavaDoc row, XmlaResponse response)
256             throws XmlaException {
257         SaxWriter writer = response.getWriter();
258
259         writer.startElement("row");
260         for (RowsetDefinition.Column column : rowsetDefinition.columnDefinitions) {
261             Object JavaDoc value = column.get(row);
262             if (value != null) {
263                 writer.startElement(column.name);
264                 writer.characters(value.toString());
265                 writer.endElement();
266             } else {
267                 writer.startElement(column.name);
268                 writer.endElement();
269             }
270         }
271         writer.endElement();
272     }
273
274     /**
275      * Emits all of the values in an enumeration.
276      */

277     protected <E extends Enum JavaDoc<E>> void emit(
278         Class JavaDoc<E> clazz,
279         XmlaResponse response)
280         throws XmlaException
281     {
282         final E[] valuesSortedByName = clazz.getEnumConstants().clone();
283         Arrays.sort(
284             valuesSortedByName,
285             new Comparator<E>() {
286                 public int compare(E o1, E o2) {
287                     return o1.name().compareTo(o2.name());
288                 }
289             });
290         for (E value : valuesSortedByName) {
291             emit(value, response);
292         }
293     }
294
295     /**
296      * Returns whether a column value matches any restrictions placed on it.
297      * If there are no restrictions, always returns true.
298     protected boolean passesRestriction(RowsetDefinition.Column column,
299             Object value) {
300         final Object requiredValue = restrictions.get(column.name);
301
302         if (requiredValue == null) {
303             return true;
304         } else if (requiredValue instanceof String[]) {
305             return contains((String[]) requiredValue, value.toString());
306         } else {
307             return requiredValue.equals(value);
308         }
309     }
310      */

311
312     /**
313      * Extensions to this abstract class implement a restriction test
314      * for each Rowset's discovery request. If there is no restriction
315      * then the passes method always returns true.
316      * Since whether the restriction is not specified (null), a single
317      * value (String) or an array of values (String[]) is known at
318      * the beginning of a Rowset's populate() method, creating these
319      * just once at the beginning is faster than having to determine
320      * the restriction status each time it is needed.
321      */

322     static abstract class RestrictionTest {
323         public abstract boolean passes(Object JavaDoc value);
324     }
325
326     RestrictionTest getRestrictionTest(RowsetDefinition.Column column) {
327         final List<String JavaDoc> requiredValue = restrictions.get(column.name);
328 /*
329 System.out.println("Rowset.getRestrictionTest: column=" +column.name);
330 System.out.println("Rowset.getRestrictionTest: requiredValue=" +requiredValue);
331 */

332
333         if (requiredValue == null) {
334             return new RestrictionTest() {
335                 public boolean passes(int ival) {
336                     return true;
337                 }
338                 public boolean passes(Object JavaDoc value) {
339                     return true;
340                 }
341             };
342         } else {
343             return new RestrictionTest() {
344                 public boolean passes(Object JavaDoc value) {
345                     return requiredValue.contains(value);
346                 }
347             };
348         }
349     }
350
351     /**
352      * Returns the list of restriction values in the XMLA request.
353      *
354      */

355     private List<String JavaDoc> getRestrictionValue(RowsetDefinition.Column column) {
356         return restrictions.get(column.name);
357     }
358
359     /**
360      * This returns the restriction if its a String or null. This does not
361      * attempt two determine if the restriction is an array of Strings
362      * if all members of the array have the same value (in which case
363      * one could return, again, simply a single String).
364      *
365      */

366     String JavaDoc getRestrictionValueAsString(RowsetDefinition.Column column) {
367         List<String JavaDoc> rval = getRestrictionValue(column);
368         return rval != null && rval.size() == 1 ? rval.get(0) : null;
369     }
370
371     /**
372      * This returns the restriction as an integer (not an Integer) if it
373      * exists and returns -1 otherwise.
374      *
375      */

376     int getRestrictionValueAsInt(RowsetDefinition.Column column) {
377         List<String JavaDoc> rval = getRestrictionValue(column);
378         if (rval != null && rval.size() == 1) {
379             try {
380                 return Integer.parseInt(rval.get(0));
381             } catch (NumberFormatException JavaDoc ex) {
382                 LOGGER.info("Rowset.getRestrictionValue: "+
383                     "bad integer restriction \""+
384                     rval+
385                     "\"");
386                 return -1;
387             }
388         } else {
389             return -1;
390         }
391     }
392
393
394
395     /**
396      * Returns true if there is a restriction for the given column
397      * definition.
398      *
399      */

400     protected boolean isRestricted(RowsetDefinition.Column column) {
401         return (restrictions.get(column.name) != null);
402     }
403
404     /**
405      * A set of name/value pairs, which can be output using
406      * {@link Rowset#addRow}. This uses less memory than simply
407      * using a HashMap and for very big data sets memory is
408      * a concern.
409      */

410     protected class Row {
411         private final ArrayList<String JavaDoc> names;
412         private final ArrayList<Object JavaDoc> values;
413         Row() {
414             this.names = new ArrayList<String JavaDoc>();
415             this.values = new ArrayList<Object JavaDoc>();
416         }
417
418         void set(String JavaDoc name, Object JavaDoc value) {
419             this.names.add(name);
420             this.values.add(value);
421         }
422
423         void set(String JavaDoc name, boolean value) {
424             set(name, value ? "true" : "false");
425         }
426
427         /**
428          * Retrieves the value of a field with a given name, or null if the
429          * field's value is not defined.
430          */

431         public Object JavaDoc get(String JavaDoc name) {
432             int i = this.names.indexOf(name);
433             return (i < 0) ? null : this.values.get(i);
434         }
435     }
436
437     /**
438      * Holder for non-scalar column values of a {@link mondrian.xmla.Rowset.Row}.
439      */

440     protected class XmlElement {
441         private final String JavaDoc tag;
442         private final String JavaDoc[] attributes;
443         private final String JavaDoc text;
444         private final XmlElement[] children;
445
446         XmlElement(String JavaDoc tag, String JavaDoc[] attributes) {
447             this(tag, attributes, null, null);
448         }
449
450         XmlElement(String JavaDoc tag, String JavaDoc[] attributes, String JavaDoc text) {
451             this(tag, attributes, text, null);
452         }
453
454         XmlElement(String JavaDoc tag, String JavaDoc[] attributes, XmlElement[] children) {
455             this(tag, attributes, null, children);
456         }
457
458         private XmlElement(String JavaDoc tag, String JavaDoc[] attributes, String JavaDoc text, XmlElement[] children) {
459             this.tag = tag;
460             this.attributes = attributes;
461             this.text = text;
462             this.children = children;
463         }
464     }
465 }
466
467 // End Rowset.java
468
Popular Tags