Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/jdaemon/era/xml/AbstractXMLCube.java


1   /*
2    * AbstractXMLCube.java
3    *
4    *  Copyright (C) 2002 Jan Stanger
5    *  This program is distributed under the terms of the Lesser GNU General Public
6    *  License (v2 or later) per the included COPYING.txt file, or see www.fsf.org.
7    *
8    * Created in June 2002
9    */
10  
11  package org.jdaemon.era.xml;
12  
13  import org.jdaemon.era.*;
14  import org.jdaemon.util.*;
15  
16  //Standard Java imports
17  import java.io.*;
18  import java.text.*;
19  import java.util.*;
20  
21  //XML related imports
22  import javax.xml.parsers.*;
23  import org.w3c.dom.*;
24  
25  //Log4J
26  import org.apache.log4j.Logger;
27  
28  import org.apache.xml.serialize.*;
29  
30  /**
31   * Abstract implementation of XMLCube. Extends AbstractCube (for completeness -- 
32   * even though it does not reuse much -- may have to revise this class or AbstractCube). 
33   * Note that AbstractCube more closely matches the requirements of the JavaCube than the XMLCube. 
34   *
35   * @author Jan Stanger, modified by Nimish Shah
36   */
37  public abstract class AbstractXMLCube extends AbstractCube implements XMLCube {
38  
39      /** Logger */
40      private static Logger log = Logger.getLogger("org.jdaemon.era.xml.AbstractXMLCube");
41      
42      /** XML DOM tree */
43      protected Document xmlDoc;
44      protected String xmlString;
45      
46      /** Descriptor containing meta data about Cube */
47      protected CubeDescriptor descriptor;    
48      
49      public AbstractXMLCube() {}    
50      
51      /**
52       * Create new cube containing a subset of the values in this Cube 
53       */
54      public Cube constrain(Filter filter) {
55          return new FilteredXMLCube(this, filter);
56      }
57      
58      /**
59       * Create new cube containing a subset of the values in this Cube 
60       */
61      public Cube constrain(String attribute, int operator, Object operand) {
62          return new FilteredXMLCube(this, getFilter(attribute, operator, operand));
63      }
64      
65      /**
66       * Obtain a filter object for use in a constrained cube (e.g. FilteredXMLCube)
67       *
68       * @param attribute Attribute name to filter by
69       * @param contraintType Type of constraint (see XMLFilters)
70       * @param value Value to filter by
71       * @return StandardFilter object representing the filtering criteria
72       */
73      public XMLFilter getFilter(String attribute, int constraintType, Object value) {
74          return new XMLFilters().getFilter(attribute, constraintType, value);
75      }    
76  
77      /**
78       * Generic method for adding data from an existing Cube (of whatever flavor)
79       * to this XMLCube in the form of an XML DOM tree. It makes heavy use of
80       * the meta data stored in the Descriptor of that Cube.
81       *
82       * @param results Query results (as Cube)
83       */
84      public void addAll(Cube results) {
85          log.debug("addAll: 'results' (input Cube) instance of: " + results.getClass().getName());
86          
87          DecimalFormat decFmt = (DecimalFormat)DecimalFormat.getInstance();
88          decFmt.applyPattern("############0.00000"); //Chopping off decimals that are too long.
89          
90          //Retrieving Descriptor from Cube
91          descriptor = results.getDescriptor();
92          
93          log.debug("addAll: Descriptor: " + descriptor);
94      
95          try {
96              //Creating empty in-memory XML document
97              xmlDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
98          }
99          catch (ParserConfigurationException e) {
100             //Decided not to throw this exception because it is VERY unlikely that this error
101             //will ever occur, therefore it shouldn't be required for callers to catch this
102             //exception. Instead, CubeException is only instantiated for error logging purposes.
103             new CubeException(this, "addAll(Cube)", "Exception during instantiation of new Document using the DocumentBuilderFactory", e);
104             //Since we 'swallow' this exception, we at least want to see the complete error stack
105             e.printStackTrace();
106         }
107         
108         //Creating root node and adding it to XML document
109         Node root = addXMLNode(xmlDoc, xmlDoc, "DataSet", "Root");
110 
111         //Add initial meta data section
112         Node metaData = addXMLNode(xmlDoc, root, "ResultSetMetaData");
113 
114         //Iterators & temp variables for looping through all rows of query result
115         Iterator it = results.iterator();
116         Object currRow;
117         String currAttrNm;
118         Object currAttrVal;
119         Iterator attributeIt;
120 
121         //Temp variables for meta-data while loop
122         String currDType = "";
123         HashMap currElemAttribs;
124         
125   log.debug("addAll: Check if Iterator is null: " + it);
126 
127         log.debug("addAll: Check if Descriptor is null: " + descriptor);
128 
129         //Get all attribute names from Descriptor
130         attributeIt = descriptor.getAttributeNames();
131 
132         log.debug("addAll: Check if attributeIt is null : " + attributeIt);
133 
134         boolean firstTime = true;
135         
136         while (it.hasNext()) {      
137             //Retrieving next row
138             currRow = it.next();
139             
140             /** 
141              * Create meta data section by analyzing the first data object's fields.
142              * We might want to consider storing this type of meta data in the descriptor 
143              * so that this 'dynamic analysis' is not needed.
144              */            
145 
146             if (firstTime) {
147             
148                 //Loop through all attributes (columns) of first row and add to XML DOM
149                 while (attributeIt.hasNext()) {
150                     currAttrNm = (String)attributeIt.next();
151                     currAttrVal = results.getAttribute(currAttrNm, currRow);
152 
153                     //if (currAttrVal != null) log.debug("addAll: meta-data: currAttrNm value: " + currAttrNm + "; currAttrVal value: " + currAttrVal + " instanceof: " + currAttrVal.getClass().getName());
154 
155                     //Dynamically determining XML data type for meta data section. Only done once for each column.
156                     if (currAttrVal instanceof Number) {
157                         currDType = "number";
158                     }
159                     else {
160                         currDType = "text";
161                     }
162 
163                     //Creating map of attribute names and values to be added to node
164                     currElemAttribs = new HashMap();
165                     currElemAttribs.put("name", currAttrNm);
166                     currElemAttribs.put("dtype", currDType);
167                     //Adding node to metadata section
168                     addXMLNode(xmlDoc, metaData, "ColumnMetaData", currElemAttribs);
169                 }
170              
171                 firstTime = false;
172                 log.debug("addAll: Finished meta-data generation");                
173             }
174 
175        
176             /** 
177              * Now dealing with actual content
178              */        
179             
180             //Create new DataRow node. 
181             HashMap typeDetail = new HashMap();
182             typeDetail.put("type", "detail");
183             Node dataRow = addXMLNode(xmlDoc, root, "DataRow", typeDetail);
184             
185             //Get all attribute names from Descriptor
186             attributeIt = descriptor.getAttributeNames();
187             //Loop through all attributes (columns) of current row and add to XML DOM
188             while (attributeIt.hasNext()) {
189                 currAttrNm = (String)attributeIt.next();
190                 currAttrVal = results.getAttribute(currAttrNm, currRow);
191                 if (currAttrVal != null) {
192                     if (currAttrVal instanceof Number) {
193                         currAttrVal = decFmt.format(currAttrVal); //Cut down decimals of numeric values to avoid confusion for the rendering XSL
194                     }
195                     else {
196                         currAttrVal = currAttrVal.toString();
197                     }
198                 }
199                 else currAttrVal = "";
200                 
201                 //log.debug("addAll: content creation: currAttrNm value: " + currAttrNm + "; currAttrVal value: " + currAttrVal);
202 
203                 addXMLNode(xmlDoc, dataRow, "column", currAttrNm, (String)currAttrVal);
204             }
205 
206         }
207         
208         log.debug("addAll: After addall loop");
209         
210         // By Nimish: Convert the XML DOM into a string representation so that it can be refered in a more memory efficient way.
211         ByteArrayOutputStream arrayOutputStream = null;
212         try {
213             XMLSerializer serializer = new XMLSerializer();
214             arrayOutputStream = new ByteArrayOutputStream();
215             serializer.setOutputByteStream(arrayOutputStream);
216             serializer.serialize(xmlDoc);
217             xmlString = arrayOutputStream.toString();
218             log.info("String length " + xmlString.length());
219         }
220         catch (Exception e) {
221             e.printStackTrace();
222             new CubeException(this, "addAll(Cube)", "Exception converting the XML DOM to String representation", e);            
223         }
224         finally{
225             try
226                 {arrayOutputStream.close();}
227             catch (Exception e){}
228             
229             arrayOutputStream = null;
230             xmlDoc = null;
231             results = null;
232         }
233         
234 /*
235         //Test output
236         try {            
237             XMLSerializer serializer = new XMLSerializer();
238             
239             //Setting the out put stream as System.out for the XML to be printed on the console. can give FileOutputStream for creating a file
240             File addAll = new File("/tmp/addALL.xml");
241             FileOutputStream fos = new FileOutputStream(addAll);
242             //serializer.setOutputByteStream(System.out);
243             serializer.setOutputByteStream(fos);
244             serializer.serialize(xmlDoc);
245         }
246         catch (Exception e) {
247             System.out.println("Exception in addALL " + e);
248             e.printStackTrace();
249         }
250 */
251         
252         log.debug("addAll: Finished!");
253     }
254     
255     /**
256      * Retrieve handle on internal XML document representation. Needed for getXML() method implementation
257      */
258     public Document getXmlDoc() {
259         return this.xmlDoc;
260     }
261 
262     /**
263      * Retrieve handle on internal XML String representation. Needed for getXML() method implementation
264      */
265     public String getXmlString() {
266         return this.xmlString;
267     }
268 
269     
270     /*********** Internal helper methods ****************/
271     
272     
273     /**
274      * Helper method for adding elements to parent nodes
275      *
276      * @param xmlDoc XML DOM tree
277      * @param parent Parent node to attach this text node to
278      * @param elementName Element to be added
279      * @param attributes Map of attribute names mapped to attribute values
280      * @param value Value for text node
281      */
282     protected Node addXMLNode(Document xmlDoc, Node parent, String elementName, Map attributes, String value) {
283         Element node = xmlDoc.createElement(elementName);
284         if (attributes != null) {
285             Iterator it = attributes.keySet().iterator();
286             String currKey;
287             String currVal;
288             while (it.hasNext()) {
289                 currKey = (String)it.next();
290                 currVal = (String)attributes.get(currKey);
291                 node.setAttribute(currKey, currVal);
292             }
293         }
294         if ((value != null) && (!value.equals(""))) {
295             
296             //Removing forbidden characters (i.e. '<' and '>')
297             int forbiddenStringIndex;
298             while ((forbiddenStringIndex = value.indexOf("<")) != -1) {
299                 value = value.substring(0, forbiddenStringIndex) + "&lt;" + value.substring(forbiddenStringIndex+1);
300             }
301             while ((forbiddenStringIndex = value.indexOf(">")) != -1) {
302                 value = value.substring(0, forbiddenStringIndex) + "&gt;" + value.substring(forbiddenStringIndex+1);
303             }
304             
305             node.appendChild(xmlDoc.createTextNode(value));
306         }
307         parent.appendChild(node);
308         
309         return node;
310     }
311 
312     /**
313      * Helper method for adding elements to parent nodes
314      *
315      * @param xmlDoc XML DOM tree
316      * @param parent Parent node to attach this text node to
317      * @param elementName Element to be added
318      * @param attributes Map of attribute names mapped to attribute values
319      */    
320     protected Node addXMLNode(Document xmlDoc, Node parent, String elementName, Map attributes) {
321         return addXMLNode(xmlDoc, parent, elementName, attributes, null);
322     }
323 
324     /**
325      * Helper method for adding elements to parent nodes
326      *
327      * @param xmlDoc XML DOM tree
328      * @param parent Parent node to attach this text node to
329      * @param elementName Element to be added
330      */        
331     protected Node addXMLNode(Document xmlDoc, Node parent, String elementName) {
332         return addXMLNode(xmlDoc, parent, elementName, new String(), new String());
333     }
334     
335     /**
336      * Helper method for adding elements to parent nodes
337      *
338      * @param xmlDoc XML DOM tree
339      * @param parent Parent node to attach this text node to
340      * @param elementName Element to be added
341      * @param nameAttribute Value for name attribute of element to be added
342      * @param value Value for text node*
343      */           
344     protected Node addXMLNode(Document xmlDoc, Node parent, String elementName, String nameAttribute, String value) {
345         HashMap attributes = new HashMap();
346         if ((nameAttribute != null) && (!nameAttribute.equals(""))) {
347             attributes.put("name", nameAttribute);
348         }
349         return addXMLNode(xmlDoc, parent, elementName, attributes, value);
350     }     
351 
352     /**
353      * Helper method for adding elements to parent nodes
354      *
355      * @param xmlDoc XML DOM tree
356      * @param parent Parent node to attach this text node to
357      * @param elementName Element to be added
358      * @param nameAttribute Value for name attribute of element to be added
359      */       
360     protected Node addXMLNode(Document xmlDoc, Node parent, String elementName, String nameAttribute) {
361         return addXMLNode(xmlDoc, parent, elementName, nameAttribute, "");
362     }
363     
364     /*
365     public int size() {
366         NodeList list = xmlDoc.getElementsByTagName("DataRow");
367         return list.getLength();
368     }
369     */
370     
371     public int size() {
372         return xmlString.length();
373     }
374 
375     public CubeDescriptor getDescriptor() {
376         return descriptor;
377     }     
378     
379     
380     /*********** Unsupported methods ***********/
381     
382     
383     /**
384      * Not currently supported by XML cubes
385      */
386     public Object getMax(String dimension) {
387         if (true) throw new UnsupportedCubeOperationException(this, "getMax()");
388         return null; //dummy return to satisfy compiler
389     }
390     
391     /**
392      * Not currently supported by XML cubes
393      */
394     public Object getMin(String dimension) {
395         if (true) throw new UnsupportedCubeOperationException(this, "getMin()");
396         return null; //dummy return to satisfy compiler
397     }
398     
399     /**
400      * Not currently supported by XML cubes
401      */
402     public double getSum(String attribute) {
403         if (true) throw new UnsupportedCubeOperationException(this, "getSum()");
404         return -1; //dummy return to satisfy compiler
405     }    
406     
407     /**
408      * Not currently supported by XML cubes
409      */
410     public Iterator groupBy(String attribute) {
411         if (true) throw new UnsupportedCubeOperationException(this, "groupBy()");
412         return null; //dummy return to satisfy compiler
413     }
414     
415     /**
416      * Not currently supported by XML cubes
417      */
418     public Object getAttribute(String attribute, Object object) {
419         if (true) throw new UnsupportedCubeOperationException(this, "getAttribute()");
420         return null; //dummy return to satisfy compiler
421     }    
422     
423     /**
424      * Not currently supported by XML cubes
425      */
426     public SortedSet getAttributeSet(String attribute) {
427         if (true) throw new UnsupportedCubeOperationException(this, "getAttributeSet()");
428         return null; //dummy return to satisfy compiler
429     }
430     
431     /**
432      * Not currently supported by XML cubes
433      */    
434     public Iterator iterator() {
435         throw new UnsupportedCubeOperationException(this, "iterator()");
436     }
437 
438     /**
439      * Not currently supported by XML cubes
440      */    
441     public boolean add(Object object) {
442         if (true) throw new UnsupportedCubeOperationException(this, "add()");
443         return false; //dummy return to satisfy compiler
444     }    
445 
446 }