001/* 002// This software is subject to the terms of the Eclipse Public License v1.0 003// Agreement, available at the following URL: 004// http://www.eclipse.org/legal/epl-v10.html. 005// You must accept the terms of that agreement to use this software. 006// 007// Copyright (C) 2002-2005 Julian Hyde 008// Copyright (C) 2005-2009 Pentaho and others 009// All Rights Reserved. 010*/ 011package mondrian.web.taglib; 012 013import mondrian.olap.*; 014 015import org.apache.log4j.Logger; 016 017import org.w3c.dom.*; 018 019import java.io.*; 020import java.util.List; 021import javax.xml.parsers.*; 022import javax.xml.transform.Templates; 023import javax.xml.transform.TransformerFactory; 024import javax.xml.transform.dom.DOMSource; 025import javax.xml.transform.stream.StreamResult; 026import javax.xml.transform.stream.StreamSource; 027 028/** 029 * Transforms a mondrian result into a DOM (Document Object Model). 030 * 031 * @author Andreas Voss, 22 March, 2002 032 */ 033public class DomBuilder { 034 private static final Logger LOGGER = Logger.getLogger(DomBuilder.class); 035 036 Document factory; 037 Result result; 038 int dimCount; 039 040 protected DomBuilder(Document factory, Result result) { 041 this.factory = factory; 042 this.result = result; 043 } 044 045 public static Document build(Result result) 046 throws ParserConfigurationException 047 { 048 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 049 dbf.setValidating(false); 050 dbf.setExpandEntityReferences(true); 051 DocumentBuilder db = dbf.newDocumentBuilder(); 052 Document doc = db.newDocument(); 053 Element table = build(doc, result); 054 doc.appendChild(table); 055 // debug(doc); 056 return doc; 057 } 058 059 public static Element build(Document factory, Result result) { 060 return new DomBuilder(factory, result).build(); 061 } 062 063 private Element build() { 064 dimCount = result.getAxes().length; 065 Element mdxtable = factory.createElement("mdxtable"); 066 Element query = elem("query", mdxtable); 067 cdata(Util.unparse(result.getQuery()), query); 068 Element head = elem("head", mdxtable); 069 Element body = elem("body", mdxtable); 070 switch (dimCount) { 071 case 0: 072 buildRows0Dim(body); 073 break; 074 case 1: 075 buildColumns(head, result.getAxes()[0]); 076 buildRows1Dim(body); 077 break; 078 case 2: 079 buildColumns(head, result.getAxes()[0]); 080 buildRows2Dim(body, result.getAxes()[1]); 081 break; 082 default: 083 throw new IllegalArgumentException( 084 "DomBuilder requires 0, 1 or 2 dimensional result"); 085 } 086 Element slicers = elem("slicers", mdxtable); 087 buildSlicer(slicers); 088 return mdxtable; 089 } 090 091 abstract class AxisBuilder { 092 Member[] prevMembers; 093 Element[] prevElems; 094 int [] prevSpan; 095 096 Element parent; 097 List<Position> positions; 098 int levels; 099 100 AxisBuilder(Element parent, Axis axis) { 101 this.parent = parent; 102 103 positions = axis.getPositions(); 104 levels = positions.get(0).size(); 105 prevMembers = new Member[levels]; 106 prevElems = new Element[levels]; 107 prevSpan = new int[levels]; 108 } 109 110 abstract int getRowCount(); 111 abstract Element build(int rowIndex); 112 } 113 114 class RowBuilder extends AxisBuilder { 115 RowBuilder(Element parent, Axis axis) { 116 super(parent, axis); 117 } 118 119 Element build(int rowIndex) { 120 boolean even = (rowIndex % 2 != 0); // counting starts at row 1 121 Element row = elem("row", parent); 122 build(row, positions.get(rowIndex), even); 123 return row; 124 } 125 126 int getRowCount() { 127 return positions.size(); 128 } 129 130 private void build( 131 Element row, 132 List<Member> currentMembers, 133 boolean even) 134 { 135 for (int i = 0; i < levels; i++) { 136 Member currentMember = currentMembers.get(i); 137 Member prevMember = prevMembers[i]; 138 if (prevMember == null || !prevMember.equals(currentMember)) { 139 Element currentElem = 140 createMemberElem("row-heading", row, currentMember); 141 if (even) { 142 currentElem.setAttribute("style", "even"); 143 } else { 144 currentElem.setAttribute("style", "odd"); 145 } 146 prevMembers[i] = currentMember; 147 prevElems[i] = currentElem; 148 prevSpan[i] = 1; 149 for (int j = i + 1; j < levels; j++) { 150 prevMembers[j] = null; 151 } 152 } else { 153 Element prevElem = prevElems[i]; 154 prevElem.setAttribute("style", "span"); 155 prevSpan[i] += 1; 156 prevElem.setAttribute( 157 "rowspan", Integer.toString(prevSpan[i])); 158 } 159 } 160 } 161 } 162 163 164 class ColumnBuilder extends AxisBuilder { 165 ColumnBuilder(Element parent, Axis axis) { 166 super(parent, axis); 167 } 168 169 int getRowCount() { 170 return levels; 171 } 172 173 Element build(int rowIndex) { 174 Element row = elem("row", parent); 175 if (dimCount > 1 && rowIndex == 0) { 176 buildCornerElement(row); 177 } 178 build(row, rowIndex); 179 return row; 180 } 181 182 private void build(Element row, int rowIndex) { 183 for (int i = 0; i < levels; i++) { 184 prevMembers[i] = null; 185 } 186 for (int i = 0; i < positions.size(); i++) { 187 Position position = positions.get(i); 188 //Member[] currentMembers = positions.get(i).getMembers(); 189 190 for (int j = 0; j < rowIndex - 1; j++) { 191 Member currentMember = position.get(j); 192 if (prevMembers[j] == null 193 || !prevMembers[j].equals(currentMember)) 194 { 195 prevMembers[j] = currentMember; 196 for (int k = j + 1; k < levels; k++) { 197 prevMembers[j] = null; 198 } 199 } 200 } 201 202 Member currentMember = position.get(rowIndex); 203 Member prevMember = prevMembers[rowIndex]; 204 if (prevMember == null || !prevMember.equals(currentMember)) { 205 Element currentElem = 206 createMemberElem("column-heading", row, currentMember); 207 prevMembers[rowIndex] = currentMember; 208 prevElems[rowIndex] = currentElem; 209 prevSpan[rowIndex] = 1; 210 for (int j = rowIndex + 1; j < levels; j++) { 211 prevMembers[j] = null; 212 } 213 } else { 214 Element prevElem = prevElems[rowIndex]; 215 prevElem.setAttribute("style", "span"); 216 prevSpan[rowIndex] += 1; 217 prevElem.setAttribute( 218 "colspan", Integer.toString(prevSpan[rowIndex])); 219 } 220 } 221 } 222 223 void buildCornerElement(Element row) { 224 Element corner = elem("corner", row); 225 corner.setAttribute( 226 "rowspan", 227 Integer.toString( 228 result.getAxes()[0].getPositions().get(0).size())); 229 corner.setAttribute( 230 "colspan", 231 Integer.toString( 232 result.getAxes()[1].getPositions().get(0).size())); 233 } 234 } 235 236 237 private void buildRows2Dim(Element parent, Axis axis) { 238 RowBuilder rb = new RowBuilder(parent, axis); 239 final int N = rb.getRowCount(); 240 int[] cellIndex = new int[2]; 241 for (int i = 0; i < N; i++) { 242 Element row = rb.build(i); 243 boolean even = (i % 2 != 0); // counting starts at row 1 244 cellIndex[1] = i; 245 buildCells(row, cellIndex, even); 246 } 247 } 248 249 private void buildRows1Dim(Element parent) { 250 int[] cellIndex = new int[1]; 251 Element row = elem("row", parent); 252 buildCells(row, cellIndex, false); 253 } 254 255 private void buildColumns(Element parent, Axis axis) { 256 ColumnBuilder cb = new ColumnBuilder(parent, axis); 257 final int N = cb.getRowCount(); 258 for (int i = 0; i < N; i++) { 259 Element row = cb.build(i); 260 } 261 } 262 263 264 private void buildCells(Element row, int[] cellIndex, boolean even) { 265 int columns = result.getAxes()[0].getPositions().size(); 266 for (int i = 0; i < columns; i++) { 267 cellIndex[0] = i; 268 Cell cell = result.getCell(cellIndex); 269 buildCell(cell, row, even); 270 } 271 } 272 273 private void buildCell(Cell cell, Element row, boolean even) { 274 Element cellElem = elem("cell", row); 275 String s = cell.getFormattedValue(); 276 if (s == null || s.length() == 0 || s.equals("(null)")) { 277 s = "\u00a0"; // 278 } 279 cellElem.setAttribute("value", s); 280 cellElem.setAttribute("style", even ? "even" : "odd"); 281 } 282 283 private void buildRows0Dim(Element parent) { 284 int[] cellIndex = new int[0]; 285 Element row = elem("row", parent); 286 Cell cell = result.getCell(cellIndex); 287 buildCell(cell, row, false); 288 } 289 290 private void buildSlicer(Element parent) { 291 List<Position> positions = result.getSlicerAxis().getPositions(); 292 for (int i = 0; i < positions.size(); i++) { 293 Position position = positions.get(i); 294 if (position.size() > 0) { 295 Element el = elem("position", parent); 296 for (int j = 0; j < position.size(); j++) { 297 createMemberElem("member", el, position.get(j)); 298 } 299 } 300 } 301 } 302 303 private Element createMemberElem(String name, Element parent, Member m) { 304 Element e = elem(name, parent); 305 e.setAttribute("caption", m.getCaption()); 306 e.setAttribute("depth", Integer.toString(m.getLevel().getDepth())); 307 //e.setAttribute("name", m.getName()); 308 //e.setAttribute("qname", m.getQualifiedName()); 309 e.setAttribute("uname", m.getUniqueName()); 310 e.setAttribute("colspan", "1"); 311 e.setAttribute("rowspan", "1"); 312 313 // add properties to dom tree 314 addMemberProperties(m, e); 315 316 return e; 317 } 318 319 private void addMemberProperties(Member m, Element e) { 320 Property[] props = m.getLevel().getProperties(); 321 if (props != null) { 322 for (int i = 0; i < props.length; i++) { 323 String propName = props[i].getName(); 324 String propValue = "" + m.getPropertyValue(propName); 325 Element propElem = elem("property", e); 326 propElem.setAttribute("name", propName); 327 propElem.setAttribute("value", propValue); 328 } 329 } 330 } 331 332 private Element elem(String name, Element parent) { 333 Element elem = factory.createElement(name); 334 parent.appendChild(elem); 335 return elem; 336 } 337 338 private Object cdata(String content, Element parent) { 339 CDATASection section = factory.createCDATASection(content); 340 parent.appendChild(section); 341 return section; 342 } 343 344 private static final String PRETTY_PRINTER = 345 "" 346 + "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n" 347 + "<xsl:output method=\"xml\" indent=\"yes\"/>\n" 348 + "<xsl:template match=\"*|@*\">\n" 349 + " <xsl:copy>\n" 350 + " <xsl:apply-templates select=\"*|@*\"/>\n" 351 + " </xsl:copy>\n" 352 + "</xsl:template>\n" 353 + "</xsl:stylesheet>\n"; 354 355 public static void debug(Document doc) { 356 try { 357 TransformerFactory tf = TransformerFactory.newInstance(); 358 StringReader input = new StringReader(PRETTY_PRINTER); 359 Templates templates = tf.newTemplates(new StreamSource(input)); 360 OutputStream result = new ByteArrayOutputStream(); 361 templates.newTransformer().transform( 362 new DOMSource(doc), 363 new StreamResult(result)); 364 LOGGER.debug(result.toString()); 365 } catch (Exception e) { 366 e.printStackTrace(); 367 } 368 } 369 370} 371 372// End DomBuilder.java