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

Quick Search    Search Deep

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


1   /*
2    * GenericTotal.java
3    *
4    *  Copyright (C) 2002 Nimish Shah
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 August 2002
9    */
10  
11  package org.jdaemon.era.xml;
12  
13  import org.apache.xerces.parsers.*;
14  import org.apache.xml.serialize.*;
15  import org.xml.sax.*;
16  import org.w3c.dom.*;
17  
18  import java.io.*;
19  import java.text.*;
20  import java.util.*;
21  
22  import org.jdaemon.era.CubeDescriptor;
23  import org.jdaemon.util.AttributeList;
24  
25  //Log4J
26  import org.apache.log4j.Logger;
27  
28  /**
29   * This is a helper class used for inserting totals after groups breaks. This class is used by XMLCube for totaling non numeric fields for
30   * each report. The group breaks and columns which need to be totaled are passed in as an argument. The class accepts a Document object for XML
31   * and arrays for group list and total columns. The first group should be the first element in the groupby arrray
32   *
33   * @author Nimish Shah
34   */
35  public class GenericTotal {
36      
37      /** Logger */
38      private static Logger log = Logger.getLogger("--GenericTotal--");
39      
40      /**
41       * The format object used to convert the exponent value representation into a normal number.
42       */
43      DecimalFormat format = new DecimalFormat("########0.00");
44      
45      Set[] totalonLevelDependent;
46      
47      /**
48       * This is a no argument constructor for the class
49       */
50      public GenericTotal() {}
51      
52      /**
53       * This method can be used to test this class from command prompt. The XML file needs to be argument passed to this class. The
54       * groupby and total columns are hardcoded.
55       */
56      
57      public static void main(String[] args) {
58          try {            
59              long startTime = System.currentTimeMillis();
60              
61              String[] groupby = {"AccountTypeNarrative", "AccountNumber", "LocalCCY"};
62              String[] totalafter = {"true", "false", "true"};
63              String[] totalon = {"MarketValueBase", "MarketValueLocal"};
64              GenericTotal genericTotal = new GenericTotal();
65              
66              //Convert the xml file in to input source
67              InputSource source = new InputSource(args[0]);
68              //create a new DOM Parser
69              DOMParser parser = new DOMParser();
70              //Parse the xml file
71              parser.parse(source);
72              //get the document object
73              Document doc = parser.getDocument();
74              
75              doc = genericTotal.insertTotals(doc, groupby, totalafter, totalon, null);
76              
77              log.debug("Total time from main method is " + (System.currentTimeMillis()- startTime));
78          }
79          catch (Exception e) {
80              e.printStackTrace();
81          }
82      }
83      
84      /**
85       * This method is called on the reuqest of client. This method accepts the request from client and calls
86       * different methods and processes the request. This method delegates the request to handleRequest,
87       * which internally delegates it to the corresponding handler as per the Source value. The referece of
88       * handler class is obtained from the Action mapping class. After getting the processing done from
89       * hander class. The request is dispatched to the corresponding url for action value (display type).
90       * The url is obtained from the Action Mapping class
91       * @param   Document doc          -- The document which needs to be processd for adding total rows.
92       * @param   String[] groupby        -- The array of group break columns
93       * @param  String[] totalon        -- The array of columns which need to be totaled on.
94       * @param   Map      attributeMetadataMap -- Map containing meta data of Cub
95       */
96      
97      public Document insertTotals(Document doc, String[] groupby, String[] totalafter, String[] totalon, Map attributeMetadataMap /*, CubeDescriptor descriptor*/) {
98  
99          //Removing '_Abs' or '_Ms' from groupbreak name to prevent wrong totals label. This is not final but will have 
100         //to suffice until we have put the meta data into the Cube that would link hidden and display columns to one another.
101         for (int i=0; i < groupby.length; i++) {
102             String currElement = groupby[i];
103             
104             log.debug("insertTotals: currElement is: " + currElement);        
105 
106             if (currElement.endsWith("_Abs") || currElement.endsWith("_Ms")) {
107                 int index = currElement.indexOf("_Abs");
108                 if (index == -1) index = currElement.indexOf("_Ms");
109                 groupby[i] = currElement.substring(0, index);
110             }        
111             
112             log.debug("insertTotals: modified currElement is: " + groupby[i]);
113         }
114         
115         /**
116          * Setting up the arrays for holding the current and previous values of group break columns. Also defining as array
117          * to hold the totals for different columns of different group breaks.
118          */
119         int groupbreaks = groupby.length;
120         int nooftotals = totalon.length;
121 
122         if (groupbreaks == 0 || nooftotals == 0)
123             {return doc;}
124         
125         String[] tmp_groupby = {"none", "none", "none"};
126         String[] tmp_totalafter = {"false", "false", "false"};
127         
128         log.debug("Total no of groups breaks are : " + groupbreaks);
129         
130         if (groupbreaks >= 3) {
131             for (int z=0;z < 3 ;z++ ) {
132                 tmp_groupby[z] = groupby[z];
133                 tmp_totalafter[z] = totalafter[z];
134                 log.debug(z + " : Show total " + totalafter[z] + " for Group break " + groupby[z]);
135             }
136             groupbreaks = 3;
137         }
138         else {
139             for (int z=0; z < groupbreaks;  z++) {
140                 tmp_groupby[z] = groupby[z];
141                 tmp_totalafter[z] = totalafter[z];
142                 log.debug(z + " : Show total " + totalafter[z] + " for Group break " + groupby[z]);
143             }
144         }
145         
146         
147         
148         // Creating new totalonMasterSet based on original totalon array values.
149         // BUT: All columns that should not be totaled because the field they
150         // are dependent on is not in the groupby array are not being added to
151         // that totalonMasterSet set.
152         
153         HashSet totalonMasterSet = new HashSet();
154         
155         //Looping through all totalon columns
156         for (int z = 0; z < totalon.length; z++) {
157             boolean keyFound = false;
158             
159             //Safety check in case the links map is null (for cubes that did not create it)
160             if (attributeMetadataMap != null) {
161                 
162                 log.debug("insertTotals: attributeMetadataMap is not null");
163                 
164                 //Looping through all keys of links map to see if the current columns is contained
165                 //in that key's values
166                 Set keySet = attributeMetadataMap.keySet();
167                 Iterator it = keySet.iterator();
168                 while (it.hasNext()) {
169                     String currKey = (String)it.next();
170                     
171                     log.debug("insertTotals: Looping through attributeMetadataMap: currKey is " + currKey);
172 
173                     Object attrListObj = attributeMetadataMap.get(currKey);
174                     if (attrListObj == null) {
175                         log.debug("insertTotals:attrListObj is null");
176                         continue;
177                     }
178                     Object totalDepends = ((AttributeList)attrListObj).get("total-dependency");
179                     if (totalDepends == null) {
180                         log.debug("insertTotals: totalDepends is null");
181                         continue;
182                     }
183                     
184                     log.debug("insertTotals: totalDepends is " + totalDepends);
185                     
186                     String[] currLinks = (String[])totalDepends;
187                     Arrays.sort(currLinks);
188                     int result = Arrays.binarySearch(currLinks, totalon[z]);
189                     if (result >= 0) { //=found key, now check if key in list of groupby
190                         keyFound = true;
191                         for (int j = 0; j < tmp_groupby.length; j++) {
192                             if (tmp_groupby[j].equals(currKey)) {
193                                 log.debug("insertTotals: Adding to totalonMasterSet: " + totalon[z]);
194                                 //Add column if one of the groupbys equals the current key
195                                 totalonMasterSet.add(totalon[z]);
196                                 break;
197                             }
198                         }
199                     }
200                 }
201             }
202             
203             if (!keyFound) {
204                 totalonMasterSet.add(totalon[z]);
205                 log.debug("insertTotals: (else) Adding " + totalon[z] + " to totalonMasterSet");
206             }
207             
208         }
209                 
210         
211         // Now creating Set that holds the column sets for all group levels
212         
213         this.totalonLevelDependent = new HashSet[tmp_groupby.length];
214         
215         String[] currLinks = null;
216         
217         //For every group level, figure out what column totals to display
218         for (int k = tmp_groupby.length-1; k >= 0; k--) {
219             log.debug("insertTotals: (Iteration " + k + ") In loop for totalonMasterSet");
220             
221             //If currLinks is not null (due to previous iteration), remove the contained
222             //columns from the set of totaled columns
223             if (currLinks != null) {
224                 for (int h = 0; h < currLinks.length; h++) {
225                     //For next level, remove the dependent columns
226                     totalonMasterSet.remove(currLinks[h]);
227                     log.debug("insertTotals: (Iteration " + k + ") Removing column from master list: " + currLinks[h]);
228                 }
229             }
230             //Define the set of level-k totaling columns as clone of the 'totalonMasterSet'. The master set
231             //may be further reduced in the next iteration for the next sorting level.
232             totalonLevelDependent[k] = (Set)totalonMasterSet.clone();
233             
234             log.debug("insertTotals: (Iteration " + k + ") After cloning of totalonMasterSet");
235             
236             //Retrieve dependent columns for next sorting level (if any)
237             if (attributeMetadataMap != null) {
238                 Object attrListObj = attributeMetadataMap.get(tmp_groupby[k]);
239                 if (attrListObj == null) {
240                     log.debug("insertTotals: (Iteration " + k + ") attrListObj (2) is null");
241                 }
242                 else {
243                     log.debug("insertTotals: (Iteration " + k + ") attrListObj (2) is NOT null");
244                     Object totalDepends = ((org.jdaemon.util.AttributeList)attrListObj).get("total-dependency");
245                     if (totalDepends != null) {
246                         log.debug("insertTotals: (Iteration " + k + ") totalDepends (2) is null");
247                         currLinks = (String[])totalDepends;
248                     }
249                 }
250             }
251             
252             log.debug("insertTotals: At end of loop for totalonMasterSet");
253         }
254         
255         log.debug("insertTotals: After loop for totalonMasterSet");
256 
257         
258         
259         // Now doing the actual totaling and adding of total rows to XML
260         // document structure 
261         
262         String[] prevGroup = new String[groupbreaks];
263         String[] currGroup = new String[groupbreaks];
264         double[][] totals = new double[groupbreaks][nooftotals];
265         
266         //Creating an array to hold current value of the columns which need to be totaled
267         double[] curvalue1 = new double[nooftotals];
268         
269         try {
270             //get the total no DataRow's present in the XML document
271             
272             NodeList nodeList = doc.getElementsByTagName("DataRow");
273             int nodelength = nodeList.getLength();
274             
275             //Logic for Totals need to be processed only if there is atleast one DataRow present in the XML
276             if (nodelength > 0) {
277                 //Get root node DataSet from the document
278                 Node dataSet = doc.getFirstChild();
279                 
280                 //Take the pointer to node before DataRow. This will be text node of ResultSetMetaData
281                 Node node = dataSet.getFirstChild();
282                 node = node.getNextSibling();
283                 node = node.getNextSibling();
284                 
285                 //Traverse the XML document for all the DataRow's
286                 for (int i = 0; i < nodelength; i++) {
287                     node = node.getNextSibling();
288                     
289                     log.debug("insertTotals: After getNextSibling, node is: " + node);
290                     
291                     //get the current values for the group break columns
292                     for (int j=0; j<groupbreaks; j++) {
293                         if (tmp_groupby[j].equals("none") || !(tmp_totalafter[j].equals("true")) ) {
294                             currGroup[j] = "none";
295                         }
296                         else {
297                             NodeList childNodeList = ((Element)node).getElementsByTagName("column");
298                             int colCount = childNodeList.getLength();
299                             for (int z=0; z < colCount; z++){
300                                 Attr attribute = ((Element)childNodeList.item(z)).getAttributeNode("name");
301                                 if (attribute.getValue().equals(tmp_groupby[j])) {
302                                     Node child = childNodeList.item(z).getFirstChild();
303                                     if (child == null)
304                                        {currGroup[j] = "";}
305                                     else
306                                        {currGroup[j] = child.getNodeValue().trim();}
307                                     break;
308                                 }
309                             }
310                         }
311                     }
312                     
313                     //get the current values for the columns which need to be totaled
314                     for (int j=0; j<nooftotals; j++) {
315                         NodeList childNodeList = ((Element)node).getElementsByTagName("column");
316                         int colCount = childNodeList.getLength();
317                         for (int z=0; z < colCount; z++){
318                             Attr attribute = ((Element)childNodeList.item(z)).getAttributeNode("name");
319                             if (attribute.getValue().equals(totalon[j])) {
320                                 Node child = childNodeList.item(z).getFirstChild();
321                                 curvalue1[j] = Double.valueOf(child.getNodeValue().trim()).doubleValue();
322                                 break;
323                             }
324                         }
325                     }
326                     
327                     /**
328                      * Check for the first group level if the previous group break value is same as current then keep adding the
329                      * data for total else insert a totals column in output XML Document.
330                      */
331                     if (prevGroup[0] == null || prevGroup[0].equals(currGroup[0])) {
332                         
333                         for (int z=0; z<nooftotals; z++)
334                         {totals[0][z] = totals[0][z] + curvalue1[z];}
335                         
336                         /**
337                          * Check for the second group level if the previous group break value is same as current then keep adding the
338                          * data for total else insert a totals column in output XML Document.
339                          */
340                         if (prevGroup[1] == null  || prevGroup[1].equals(currGroup[1])) {
341                             for (int z=0; z<nooftotals; z++)
342                             {totals[1][z] = totals[1][z] + curvalue1[z];}
343                             
344                             /**
345                              * Check for the first group level if the previous group break value is same as current then keep adding the
346                              * data for total else insert a totals column in output XML Document.
347                              */
348                             if (prevGroup[2] == null || prevGroup[2].equals(currGroup[2])) {
349                                 for (int z=0; z<nooftotals; z++)
350                                 {totals[2][z] = totals[2][z] + curvalue1[z];}
351                             }
352                             else {
353                                 //Insert total rows for third group break
354                                 if (tmp_totalafter[2].equals("true"))
355                                 {insertTotalsRow(doc, dataSet, node, totalon, totals[2], "2", prevGroup[2]);}
356                                 else
357                                 {insertTotalsRow(doc, dataSet, node, totalon, totals[2], "2", "none");}
358                                 
359                                 //reset the total to current value
360                                 for (int z=0; z<nooftotals; z++)
361                                 {totals[2][z] = curvalue1[z];}
362                             }
363                         }
364                         else {
365                             //Insert total rows for second and third group break
366                             if (tmp_totalafter[2].equals("true"))
367                             {insertTotalsRow(doc, dataSet, node, totalon, totals[2], "2", prevGroup[2]);}
368                             else
369                             {insertTotalsRow(doc, dataSet, node, totalon, totals[2], "2", "none");}
370                             if (tmp_totalafter[1].equals("true"))
371                             {insertTotalsRow(doc, dataSet, node, totalon, totals[1], "1", prevGroup[1]);}
372                             else
373                             {insertTotalsRow(doc, dataSet, node, totalon, totals[1], "1", "none");}
374                             
375                             //reset the totals to current value
376                             for (int z=0; z<nooftotals; z++)
377                             {totals[1][z] = curvalue1[z];}
378                             for (int z=0; z<nooftotals; z++)
379                             {totals[2][z] = curvalue1[z];}
380                         }
381                     }
382                     else {
383                         //Insert total rows for first, second and third group break
384                         if (tmp_totalafter[2].equals("true"))
385                         {insertTotalsRow(doc, dataSet, node, totalon, totals[2], "2", prevGroup[2]);}
386                         else
387                         {insertTotalsRow(doc, dataSet, node, totalon, totals[2], "2", "none");}
388                         if (tmp_totalafter[1].equals("true"))
389                         {insertTotalsRow(doc, dataSet, node, totalon, totals[1], "1", prevGroup[1]);}
390                         else
391                         {insertTotalsRow(doc, dataSet, node, totalon, totals[1], "1", "none");}
392                         if (tmp_totalafter[0].equals("true"))
393                         {insertTotalsRow(doc, dataSet, node, totalon, totals[0], "0", prevGroup[0]);}
394                         else
395                         {insertTotalsRow(doc, dataSet, node, totalon, totals[0], "0", "none");}
396                         
397                         //reset the totals to current value
398                         for (int z=0; z<nooftotals; z++)
399                         {totals[0][z] = curvalue1[z];}
400                         for (int z=0; z<nooftotals; z++)
401                         {totals[1][z] = curvalue1[z];}
402                         for (int z=0; z<nooftotals; z++)
403                         {totals[2][z] = curvalue1[z];}
404                     }
405                     
406                     //assigning the current values for group break columns as previous values before starting next row
407                     for (int j =0; j < groupbreaks; j++)
408                     {prevGroup[j] = currGroup[j];}
409                     
410                     node = node.getNextSibling();
411                 }
412                 
413                 //Insert total rows after last dataRow.
414                 if (tmp_totalafter[2].equals("true"))
415                 {insertTotalsRow(doc, dataSet, node, totalon, totals[2], "2", prevGroup[2]);}
416                 else
417                 {insertTotalsRow(doc, dataSet, node, totalon, totals[2], "2", "none");}
418                 if (tmp_totalafter[1].equals("true"))
419                 {insertTotalsRow(doc, dataSet, node, totalon, totals[1], "1", prevGroup[1]);}
420                 else
421                 {insertTotalsRow(doc, dataSet, node, totalon, totals[1], "1", "none");}
422                 if (tmp_totalafter[0].equals("true"))
423                 {insertTotalsRow(doc, dataSet, node, totalon, totals[0], "0", prevGroup[0]);}
424                 else
425                 {insertTotalsRow(doc, dataSet, node, totalon, totals[0], "0", "none");}
426                 /**
427                 File xmloutput = new File("/tmp/xmloutput.xml");
428                 FileOutputStream fos = new FileOutputStream(xmloutput);
429                 XMLSerializer serializer = new XMLSerializer();
430                 serializer.setOutputByteStream(fos);
431                 serializer.serialize(doc);
432                 fos.close();
433                  */
434             }
435         } catch (Exception e) {
436             log.error("There is an exception in GenericTotal class " + e);
437             // StringWriter will contain text of stack trace
438             StringWriter stringWriter = new StringWriter();
439             // Need to encapsulate the StringWriter into a Printwriter object to fill up with stack trace
440             PrintWriter printWriter = new PrintWriter(stringWriter);
441             // Get the stack trace and fill the PrintWriter
442             e.printStackTrace(printWriter);
443             // StringBuffer to hold stack trace
444             StringBuffer error = stringWriter.getBuffer();
445             // Close writers
446             printWriter.close();
447             //Print error
448             log.error(error.toString());
449             e.printStackTrace();
450         }
451         return doc;
452     }
453     
454     /**
455      * This method is private to this class. It is internally used to append the totals row inside the XML Document. All the
456      * data required to create the DataRow is passed as an argument.
457      * param Document doc    -- The XML document which is being processed
458      * param Node rootNode    -- The root node of the document which is getting processed
459      * param Node refNode    -- The Element before which the totals row needs to be inserted
460      * param String[] colname  -- The column names for which the total is begin added
461      * param String[] data    -- The total data
462      * param String grouplevel  -- The grouping level for which the total is inserted.
463      */
464     private void insertTotalsRow(Document doc, Node rootNode, Node refNode, String[] colname, double[] data, String grouplevel, String groupbreak) {
465         Element dataRow = doc.createElement("DataRow");
466         dataRow.setAttribute("type", "total");
467         
468         Element groupLevel = doc.createElement("grouplevel");
469         groupLevel.appendChild(doc.createTextNode(grouplevel));
470         dataRow.appendChild(groupLevel);
471 
472         Element groupBreak = doc.createElement("groupbreak");
473         groupBreak.appendChild(doc.createTextNode(groupbreak));
474         dataRow.appendChild(groupBreak);
475         
476         Set totalonLevelDependent = this.totalonLevelDependent[Integer.parseInt(grouplevel)];
477         
478         for (int z=0; z<colname.length; z++) {
479             String currCol = colname[z];            
480         
481             log.debug("insertTotalsRows: Inserting total for column " + currCol + ": " + data[z]);
482             
483             if (!totalonLevelDependent.contains(currCol))
484                 continue;
485             
486             Element total = doc.createElement("total");
487             
488             Element colName = doc.createElement("colname");
489             colName.appendChild(doc.createTextNode(currCol));
490             total.appendChild(colName);
491             
492             Element dataTotal = doc.createElement("data");
493             dataTotal.appendChild(doc.createTextNode(format.format(data[z])));
494             total.appendChild(dataTotal);
495             
496             dataRow.appendChild(total);
497         }
498         rootNode.insertBefore(dataRow, refNode);        
499     }
500     
501 }