KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tonbeller > jpivot > xmla > XMLA_QueryAdapter


1 /*
2  * ====================================================================
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-2004 TONBELLER AG.
7  * All Rights Reserved.
8  * You must accept the terms of that agreement to use this software.
9  * ====================================================================
10  *
11  *
12  */

13 package com.tonbeller.jpivot.xmla;
14
15 import java.util.ArrayList JavaDoc;
16 import java.util.Collection JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.List JavaDoc;
19 import java.util.Map JavaDoc;
20
21 import org.apache.log4j.Logger;
22
23 import com.tonbeller.jpivot.olap.mdxparse.CompoundId;
24 import com.tonbeller.jpivot.olap.mdxparse.Exp;
25 import com.tonbeller.jpivot.olap.mdxparse.FunCall;
26 import com.tonbeller.jpivot.olap.mdxparse.Parameter;
27 import com.tonbeller.jpivot.olap.mdxparse.ParsedQuery;
28 import com.tonbeller.jpivot.olap.mdxparse.QueryAxis;
29 import com.tonbeller.jpivot.olap.model.Dimension;
30 import com.tonbeller.jpivot.olap.model.Member;
31 import com.tonbeller.jpivot.olap.model.MemberPropertyMeta;
32 import com.tonbeller.jpivot.olap.model.OlapException;
33 import com.tonbeller.jpivot.olap.navi.MemberProperties;
34 import com.tonbeller.jpivot.olap.query.Quax;
35 import com.tonbeller.jpivot.olap.query.QuaxChangeListener;
36 import com.tonbeller.jpivot.olap.query.QueryAdapter;
37 import com.tonbeller.jpivot.util.StringUtil;
38
39 /**
40  * XMLA Adapter to MDX Query
41  */

42 public class XMLA_QueryAdapter extends QueryAdapter implements QuaxChangeListener {
43
44   static Logger logger = Logger.getLogger(XMLA_QueryAdapter.class);
45
46   private ParsedQuery parsedQuery;
47   private ParsedQuery cloneQuery;
48   private XMLA_Result result;
49   private String JavaDoc originalMDX;
50   private int nAxes; // number of axes
51

52   /**
53    * Constructor
54    */

55   XMLA_QueryAdapter(XMLA_Model model) {
56     super(model);
57
58     genMDXHierarchize = true; // Result hierarchize cannot be used
59
// genMDXHierarchize = model.isSAP();
60

61     // HHTASK clone ???
62
parsedQuery = model.getPQuery(); //.clone();
63
QueryAxis[] queryAxes = parsedQuery.getAxes();
64
65     // initialize the query axis state objects
66
nAxes = queryAxes.length;
67
68     quaxes = new XMLA_Quax[nAxes];
69     for (int i = 0; i < nAxes; i++) {
70       quaxes[i] = new XMLA_Quax(i, queryAxes[i], model);
71       quaxes[i].addChangeListener(this);
72     }
73
74   }
75
76   /**
77    * implement QuaxChangeListener
78    */

79   public void quaxChanged(Quax quax, Object JavaDoc source, boolean changedByNavi) {
80     useQuax = true;
81     // remove the parameters for this axis from the parsed query
82
Map JavaDoc paraMap = parsedQuery.getParaMap();
83     int iOrdinal = quax.getOrdinal();
84     Collection JavaDoc params = paraMap.values();
85     List JavaDoc removeList = new ArrayList JavaDoc();
86     for (Iterator JavaDoc iter = params.iterator(); iter.hasNext();) {
87       Parameter param = (Parameter) iter.next();
88       int iAxis = param.getIAxis();
89       if (iAxis == iOrdinal) {
90         // the parameter was on the axis for the quax
91
// so it is lost - remove it
92
removeList.add(param.getName().toUpperCase());
93       }
94     }
95     for (Iterator JavaDoc iter = removeList.iterator(); iter.hasNext();) {
96       String JavaDoc objToRemove = (String JavaDoc) iter.next();
97       paraMap.remove(objToRemove);
98     }
99   }
100
101   /**
102    * @return Quaxes array
103    */

104   public Quax[] getQuaxes() {
105     return quaxes;
106   }
107
108   /**
109    * Update the Query Object before Execute.
110    * The current query is build from
111    * - the original query
112    * - adding the drilldown groups
113    * - apply pending swap axes
114    * - apply pending sorts.
115    *
116    * Called from Model.getResult before the query is executed.
117    */

118   protected void onExecute() {
119
120     // if quax is to be used, generate axes from quax
121
if (useQuax) {
122       int iQuaxToSort = -1;
123       if (sortMan != null)
124         iQuaxToSort = sortMan.activeQuaxToSort();
125
126       QueryAxis[] qAxes = parsedQuery.getAxes();
127       for (int i = 0; i < quaxes.length; i++) {
128         boolean doHierarchize = false;
129         if (genMDXHierarchize && quaxes[i].isHierarchizeNeeded() && i != iQuaxToSort) {
130           doHierarchize = true;
131           if (logger.isDebugEnabled())
132             logger.debug("MDX Generation added Hierarchize()");
133         }
134         Exp eSet = (Exp) quaxes[i].genExp(doHierarchize);
135         qAxes[i].setExp(eSet);
136       } // for quaxes
137
} // useQuax
138

139     // DIMENSION PROPERTIES
140
QueryAxis[] qAxes = parsedQuery.getAxes();
141     for (int i = 0; i < quaxes.length; i++) {
142       XMLA_MemberProperties mPropExt = (XMLA_MemberProperties) model
143           .getExtension(MemberProperties.ID);
144       MemberPropertyMeta[] mprops = null;
145       if (mPropExt != null)
146         mprops = mPropExt.getVisibleProperties();
147       if (mprops != null && mprops.length > 0) {
148         List JavaDoc dProps = new ArrayList JavaDoc();
149         PropsLoop: for (int j = 0; j < mprops.length; j++) {
150           String JavaDoc hierUname = mprops[j].getScope();
151           XMLA_Hierarchy hier = ((XMLA_Model) model).lookupHierByUName(hierUname);
152           String JavaDoc dimUname;
153           if (hier != null) {
154             dimUname = hier.getDimUniqueName();
155             // if the dimension is not on the axis - ignore
156
Dimension dim = hier.getDimension();
157             Quax q = findQuax(dim);
158             if ((q == null) || !quaxes[i].equals(q))
159               continue PropsLoop;
160           } else
161             continue PropsLoop;
162           CompoundId cid = new CompoundId(dimUname);
163           String JavaDoc propName = mprops[j].getName();
164           cid.append(StringUtil.bracketsAround(propName));
165           dProps.add(cid);
166         }
167         qAxes[i].setDimProps(dProps);
168       }
169     } // for quaxes
170

171     // generate order function if neccessary
172
if (sortMan != null) {
173       if (!useQuax) {
174         // if Quax is used, the axis exp's are re-generated every time.
175
// if not -
176
// adding a sort to the query must not be permanent.
177
// Therefore, we clone the orig state of the query object and use
178
// the clone furthermore in order to avoid duplicate "Order" functions.
179
if (cloneQuery == null) {
180           if (sortMan.isSortOnQuery())
181             cloneQuery = (ParsedQuery) parsedQuery.clone();
182         } else {
183           // reset to original state
184
if (sortMan.isSortOnQuery())
185             parsedQuery = (ParsedQuery) cloneQuery.clone();
186           else
187             parsedQuery = cloneQuery;
188         }
189       }
190       sortMan.addSortToQuery();
191     }
192
193     
194     // swap axes function if neccessary
195
if (axesSwapped) {
196       swapAxes();
197     }
198
199     // add FONT_SIZE to cell properties, if neccessary
200
// CELL PROPERTIES VALUE, FORMATTED_VALUE, FONT_SIZE
201
Map JavaDoc cmpmap = ((XMLA_Model) model).getCalcMeasurePropMap();
202     if (cmpmap != null && cmpmap.size() > 0) {
203       List JavaDoc cellProps = parsedQuery.getCellProps();
204       CompoundId cid = new CompoundId("FONT_SIZE", false);
205       boolean found = false;
206       for (Iterator JavaDoc iter = cellProps.iterator(); iter.hasNext();) {
207         CompoundId ci = (CompoundId) iter.next();
208         if (ci.toMdx().equalsIgnoreCase("FONT_SIZE")) {
209           found = true;
210           break;
211         }
212       }
213       if (!found)
214         cellProps.add(cid);
215     }
216
217     long t1 = System.currentTimeMillis();
218     
219     String JavaDoc mdx = parsedQuery.toMdx();
220
221     if (logger.isDebugEnabled())
222       logger.debug(mdx);
223
224     long t2 = System.currentTimeMillis();
225     logger.info("monQuery.toString took " + (t2 - t1) + " millisec");
226
227     ((XMLA_Model) model).setCurrentMdx(mdx);
228
229   }
230
231   protected void onExecuteDrill() {
232     long t1 = System.currentTimeMillis();
233     
234     // dsf call toDrillMdx
235
String JavaDoc mdx = parsedQuery.toDrillMdx();
236     
237     if (logger.isDebugEnabled())
238         logger.debug(mdx);
239     
240     long t2 = System.currentTimeMillis();
241     logger.info("monQuery.toString took " + (t2 - t1) + " millisec");
242
243     ((XMLA_Model) model).setCurrentMdx(mdx);
244
245   }
246
247   
248   /**
249    * return the corresponding mdx
250    */

251   String JavaDoc getCurrentMdx() {
252     String JavaDoc mdx = parsedQuery.toMdx();
253     return mdx;
254   }
255
256   /**
257    * @return the XMLA Query object
258    */

259   public ParsedQuery getParsedQuery() {
260     return parsedQuery;
261   }
262
263   /**
264    * create set expression for list of members
265    * @param memList
266    * @return set expression
267    */

268   protected Object JavaDoc createMemberSet(List JavaDoc memList) {
269     Exp[] exps = new Exp[memList.size()];
270     int i = 0;
271     for (Iterator JavaDoc iter = memList.iterator(); iter.hasNext();) {
272       XMLA_Member m = (XMLA_Member) iter.next();
273       exps[i++] = m;
274     }
275     FunCall f = new FunCall("{}", exps, FunCall.TypeBraces);
276     return f;
277   }
278
279   // ***************
280
// Expand Collapse
281
// ***************
282

283   /**
284    * find out, whether a member can be expanded.
285    * this is true, if
286    * - the member is on an axis and
287    * - the member is not yet expanded and
288    * - the member has children
289    * @see com.tonbeller.jpivot.olap.navi.DrillExpand#canExpand(Member)
290    * @param Member to be expanded
291    * @return true if the member can be expanded
292    */

293   public boolean canExpand(Member member) {
294
295     // a calculated member cannot be expanded
296
if (((XMLA_Member) member).isCalculated())
297       return false;
298
299     if (!isDrillable(member, false))
300       return false;
301
302     Dimension dim = member.getLevel().getHierarchy().getDimension();
303     Quax quax = findQuax(dim);
304     return (quax == null) ? false : quax.canExpand(member);
305   }
306
307   /**
308    * @see com.tonbeller.jpivot.olap.navi.DrillExpand#canExpand(Member)
309    * @param position position to be expanded
310    * @param Member to be expanded
311    * @return true if the member can be expanded
312    */

313   public boolean canExpand(Member[] pathMembers) {
314
315     Member m = pathMembers[pathMembers.length - 1];
316     // a calculated member cannot be expanded
317
if (((XMLA_Member) m).isCalculated())
318       return false;
319
320     if (!isDrillable(m, false))
321       return false;
322
323     Dimension dim = m.getLevel().getHierarchy().getDimension();
324     Quax quax = findQuax(dim);
325
326     return (quax == null) ? false : quax.canExpand(pathMembers);
327   }
328
329   /**
330    * first check whether the member is *really* drillable
331    */

332   public void expand(Member member) {
333     XMLA_Member m = (XMLA_Member) member;
334     if (isDrillable(m, true))
335       super.expand(member);
336     else
337       model.fireModelChanged();
338   }
339
340   /**
341    * expand a member in a specific position
342    * first check whether the member is *really* drillable
343    */

344   public void expand(Member[] pathMembers) {
345     XMLA_Member m = (XMLA_Member) pathMembers[pathMembers.length - 1];
346     if (isDrillable(m, true))
347       super.expand(pathMembers);
348     else
349       model.fireModelChanged();
350   }
351
352   /**
353    * @see com.tonbeller.jpivot.olap.navi.DrillExpand#canExpand(Member)
354    * @param Member to be collapsed
355    * @return true if the member can be collapsed
356    */

357   public boolean canCollapse(Member member) {
358     // a calculated member cannot be collapsed
359
if (((XMLA_Member) member).isCalculated())
360       return false;
361     Dimension dim = member.getLevel().getHierarchy().getDimension();
362     Quax quax = findQuax(dim);
363
364     return (quax == null) ? false : quax.canCollapse(member);
365   }
366
367   /**
368    * @see com.tonbeller.jpivot.olap.navi.DrillExpand#canCollapse(Member)
369    * @param position position to be expanded
370    * @return true if the position can be collapsed
371    */

372   public boolean canCollapse(Member[] pathMembers) {
373
374     Member member = pathMembers[pathMembers.length - 1];
375     // a calculated member cannot be collapsed
376
if (((XMLA_Member) member).isCalculated())
377       return false;
378     Dimension dim = member.getLevel().getHierarchy().getDimension();
379     Quax quax = findQuax(dim);
380
381     return (quax == null) ? false : quax.canCollapse(pathMembers);
382   }
383
384   // ************
385
// DrillReplace
386
// ************
387

388   /**
389    * drill down is possible if <code>member</code> has children
390    */

391   public boolean canDrillDown(Member member) {
392
393     if (!isDrillable(member, false))
394       return false;
395     Dimension dim = member.getLevel().getHierarchy().getDimension();
396     Quax quax = findQuax(dim);
397     return (quax == null) ? false : quax.canDrillDown(member);
398   }
399
400   // *********
401
// Swap Axes
402
// *********
403

404   /**
405    * swap axes
406    * update all references to axis number in other objects
407    */

408   void setSwapAxes(boolean swap) {
409     if (parsedQuery.getAxes().length >= 2) {
410       axesSwapped = swap;
411       if (logger.isInfoEnabled()) {
412         logger.info("swapAxes " + axesSwapped);
413       }
414       model.fireModelChanged();
415     }
416   }
417
418   // ********
419
// Internal
420
// ********
421

422   /**
423    * swap axes in parsed query
424    */

425   private void swapAxes() {
426     QueryAxis[] queryAxes = parsedQuery.getAxes();
427     if (queryAxes.length >= 2) {
428       Exp exp = queryAxes[0].getExp();
429       queryAxes[0].setExp(queryAxes[1].getExp());
430       queryAxes[1].setExp(exp);
431     }
432   }
433
434   /**
435    * determine, whether a memebr is drillable
436    * @param member
437    * @return true, if a member is drillable
438    */

439   private boolean isDrillable(Member member, boolean allowComplete) {
440     XMLA_Member m = (XMLA_Member) member;
441     long ccard = m.getChildrenCardinality(); // -1 if not initialized
442
if (ccard >= 0)
443       return (ccard > 0);
444     XMLA_Level level = (XMLA_Level) member.getLevel();
445     XMLA_Model xmod = (XMLA_Model) model;
446     XMLA_Hierarchy hier = (XMLA_Hierarchy) level.getHierarchy();
447     // for performance issues, it is better if we can decide whether a member
448
// is drillable *without* completing it first.
449
if (xmod.isMicrosoft() && hier.getStructure() == XMLA_Hierarchy.STRUCTURE_FULLYBALANCED
450         && level.getChildLevel() != null) {
451       // fully balanced, drillable, if and only if member is on deepest level
452
// does not work with SAP, Hierarchy.structure not supported
453
return true;
454     }
455     if (!allowComplete)
456       return true;
457     try {
458       xmod.completeMember(m);
459     } catch (OlapException e) {
460       logger.error("?", e);
461       return false;
462     }
463     return (m.getChildrenCardinality() > 0);
464   }
465
466 } // End XMLA_QueryAdapter
467
Popular Tags