Source code: com/pjsofts/eurobudget/report/ReportGroup.java
1 /*
2 * ReportGroup.java
3 *
4 * Created on 4 avril 2002, 12:31
5 */
6
7 package com.pjsofts.eurobudget.report;
8
9 import com.pjsofts.eurobudget.EBConstants;
10 import java.util.Iterator;
11 import java.util.List;
12 import java.util.ResourceBundle;
13
14 /**
15 * A group in report.
16 * Sort of tree of ReportGroup (node) or ReportItem (leaf)
17 *
18 * It is a label and a sum (total) plus a list of ReportItems.
19 * Gives also an hint on how to get a graphical representation of it.
20 *
21 * A Group of depth 1 is a simple list of value.
22 * A Group of depth 2 could be seen as an array[series][categories];
23 * Groups with higher depths are not yet managed.
24 * @author pj
25 */
26 public class ReportGroup {
27
28 private static final ResourceBundle i8n = EBConstants.i18n;
29
30
31 /** level in the report 0: main items, 1: first level , ... */
32 //private int level = 0;
33
34 /** Depth = depth of the tree of report, maximum number of report group under this group
35 * depth = 0; means only report items in this group
36 * depth = 1; means it contains at least one group of depth 0;
37 * depth = n+1; means it contains at least one group of depth n;
38 */
39 private int depth = 0;
40
41 /**
42 * maximum number of items in groups included in this one
43 * if the group is seen as a two dimension array [] [] ,
44 * meaning items of included groups are all the same set of labels (name of series)
45 * rows will be group-items of this group (categories) and columns will be items of those included groups (series)
46 */
47 private int maxChildrenSize = 0;
48
49 /** label */
50 private String label = null;
51
52 /** Pourcentage for this group sum, if it is included in another group
53 * Number between 0 and 1
54 */
55 private Number pct = null;
56
57 /** All items included in this group, ReportItems or ReportGroups instances */
58 private List list = null;
59
60 /** totals of all items values included in this group */
61 private java.lang.Double sum = null;
62
63 /** hint on how to make a graphical representation of this group of data
64 * a ReportGroup can't be displayed by all kind of graphics, instead each report group has only a limited
65 * set of possible displayed, that could be known only by the creator of the report group.
66 * (even if we could have an auto shape from data, it couldn't guess all choice, like between a pie and a bar chart).
67 */
68 private int shapeHint = ReportChoice.SHAPE_TEXT;
69 /** high purpose of this report, may define how to use report data and how to display it */
70 private int kind = ReportChoice.KIND_GROUP;
71
72 /**
73 * Creates a new instance of ReportGroup
74 * @param label string
75 * @param list of ReportItems or ReportGroups
76 * @param value for sum, if null Auto compute the sums from list values
77 * and will update all items pct values
78 */
79 public ReportGroup(String label, List list, Number value, int shape, int kind) {
80 this.label = label;
81 this.list = list;
82 this.setShapeHint(shape);
83 this.setKind(kind);
84 double total = 0d;
85 if ( value != null ) {
86 this.sum = new Double(value.doubleValue());
87 } else {
88 double lastItemValue = 0d;
89 for (Iterator it=list.iterator(); it.hasNext() ;) {
90 Object o = it.next();
91 if ( o instanceof ReportItem) {
92 ReportItem item = (ReportItem)o;
93 lastItemValue = item.value.doubleValue();
94 total += lastItemValue;
95 } else if ( o instanceof ReportGroup ) {
96 ReportGroup group = (ReportGroup)o;
97 lastItemValue = group.getSum().doubleValue();
98 total += lastItemValue;
99 } else {
100 throw new IllegalArgumentException(i8n.getString("error_ReportGroup_classcastexception")+o.getClass());
101 }
102 }
103 if ( this.getKind() == ReportChoice.KIND_BALANCE ) {
104 this.sum = new Double(lastItemValue);
105 } else { // GROUP, NOT_DEFINED
106 this.sum = new Double(total);
107 }
108 // rescan all items and update their pct values, also compute the depth
109 if ( this.getKind() != ReportChoice.KIND_BALANCE ) {
110 for (Iterator it=list.iterator(); it.hasNext() ;) {
111 Object o = it.next();
112 if ( o instanceof ReportItem) {
113 ReportItem item = (ReportItem)o;
114 Double pct = new Double(item.value.doubleValue()/total);
115 item.pct = pct;
116 } else if ( o instanceof ReportGroup ) {
117 ReportGroup group = (ReportGroup)o;
118 Double pct = new Double(group.getSum().doubleValue()/total);
119 group.setPct(pct);
120 } else {
121 throw new IllegalArgumentException(i8n.getString("error_ReportGroup_classcastexception")+o.getClass());
122 }
123 }
124 }
125 this.depth = computeDepth();
126 this.maxChildrenSize = computeMaxLength();
127 }
128 }
129
130 /** convenient constructor, defaut shape and kind to not defined */
131 public ReportGroup(String label, List list, Number value) {
132 this(label,list,value, ReportChoice.SHAPE_TEXT, ReportChoice.KIND_GROUP);
133 }
134
135 public String getLabel() {
136 return this.label;
137 }
138
139
140 /** of ReportItem or ReportGroup */
141 public List getList() {
142 return this.list;
143 }
144
145 public Number getSum() {
146 return this.sum;
147 }
148
149 /** @return the depth of this report (how many report group level in this report)*/
150 public int getDepth() {
151 return this.depth;
152 }
153
154 /** @return the maximum items included by direct child
155 * (Get its sense with if depth = 1)
156 */
157 public int getMaxChildrenSize() {
158 return this.maxChildrenSize;
159 }
160
161 public void setPct(Number pct) {
162 this.pct = pct;
163 }
164
165 /** Getter for property pct.
166 * @return Value of property pct.
167 */
168 public java.lang.Number getPct() {
169 return pct;
170 }
171
172 /** @return xml string */
173 public String toXMLString() {
174 StringBuffer sb = new StringBuffer();
175 sb.append("<GROUP>"+"<LABEL>"+label+"</LABEL>"
176 +"<VALUE>"+this.sum+"</VALUE>"+"<PCT>"+pct+"</PCT>");
177 sb.append("<LISTITEMS>");
178 for (Iterator it=list.iterator(); it.hasNext() ;) {
179 Object o = it.next();
180 if ( o instanceof ReportItem) {
181 ReportItem item = (ReportItem)o;
182 sb.append(item.toXMLString());
183 } else if ( o instanceof ReportGroup ) {
184 ReportGroup group = (ReportGroup)o;
185 sb.append(group.toXMLString());
186 } else {
187 throw new IllegalArgumentException(i8n.getString("error_ReportGroup_classcastexception")+o.getClass());
188 }
189 }
190 sb.append("</LISTITEMS>");
191 sb.append("</GROUP>");
192 return sb.toString();
193 }
194
195 /** @return depth of the tree */
196 public int computeDepth() {
197 int newDepth = 0;
198 for (Iterator it = this.list.iterator(); it.hasNext() ;) {
199 Object o = it.next();
200 if ( o instanceof ReportItem) {
201 // do nothing
202 } else if ( o instanceof ReportGroup ) {
203 ReportGroup group = (ReportGroup)o;
204 newDepth = Math.max(newDepth,group.computeDepth()+1);
205 } else {
206 throw new IllegalArgumentException(i8n.getString("error_ReportGroup_classcastexception")+o.getClass());
207 }
208 }
209 return newDepth;
210 }
211
212 /**
213 * @return maximum of leafs that are contained by children of this node
214 */
215 public int computeMaxLength() {
216 int newValue = 0;
217 for (Iterator it=list.iterator(); it.hasNext() ;) {
218 Object o = it.next();
219 if ( o instanceof ReportItem) {
220 // do nothing
221 } else if ( o instanceof ReportGroup ) {
222 ReportGroup group = (ReportGroup)o;
223 newValue = Math.max(newValue,group.getList().size()); //for whole tree: group.computeMaxLength());
224 } else {
225 throw new IllegalArgumentException(i8n.getString("error_ReportGroup_classcastexception")+o.getClass());
226 }
227 }
228 return newValue;
229 }
230
231 /** Setter for property label.
232 * @param label New value of property label.
233 */
234 public void setLabel(java.lang.String label) {
235 this.label = label;
236 }
237
238 /** Getter for property shapeHint.
239 * @return Value of property shapeHint.
240 */
241 public int getShapeHint() {
242 return shapeHint;
243 }
244
245 /** Setter for property shapeHint.
246 * @param shapeHint New value of property shapeHint.
247 */
248 public void setShapeHint(int shapeHint) {
249 this.shapeHint = shapeHint;
250 }
251
252 /** Getter for property kind.
253 * @return Value of property kind.
254 *
255 */
256 public int getKind() {
257 return kind;
258 }
259
260 /** Setter for property kind.
261 * @param kind New value of property kind.
262 *
263 */
264 public void setKind(int kind) {
265 this.kind = kind;
266 }
267
268 }
269