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