001: /**
002: * ===========================================
003: * JFreeReport : a free Java reporting library
004: * ===========================================
005: *
006: * Project Info: http://reporting.pentaho.org/
007: *
008: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
009: *
010: * This library is free software; you can redistribute it and/or modify it under the terms
011: * of the GNU Lesser General Public License as published by the Free Software Foundation;
012: * either version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015: * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016: * See the GNU Lesser General Public License for more details.
017: *
018: * You should have received a copy of the GNU Lesser General Public License along with this
019: * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020: * Boston, MA 02111-1307, USA.
021: *
022: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023: * in the United States and other countries.]
024: *
025: * ------------
026: * Group.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report;
030:
031: import java.io.Serializable;
032: import java.util.Arrays;
033: import java.util.Collections;
034: import java.util.Iterator;
035: import java.util.List;
036: import java.util.TreeSet;
037:
038: /**
039: * A report group. Reports can contain any number of (nested) groups. The order of the
040: * fields is not important. If the group does not contain any fields, the group spans the
041: * whole report from the first to the last row (such a group is called the default group).
042: * <p/>
043: * The group's field list should not be modified after the group was added to the group
044: * list, or the results are undefined.
045: * <p/>
046: * Groups of the same GroupList must have a subgroup relation. The designated child group
047: * must contain all fields of the direct parent plus at least one new field. There is no
048: * requirement, that the referenced field actually exists, if it doesn't, null is assumed
049: * as field value.
050: * <p/>
051: * It is recommended that the name of the group is unique within the report. The name will
052: * not be used internally to identify the group, but most functions depend on a
053: * recognizable group name to identify the group to be processed.
054: *
055: * @author David Gilbert
056: * @author Thomas Morgner
057: * @see GroupList
058: */
059: public class Group implements Serializable, Cloneable, Comparable {
060: /**
061: * A unique identifier for long term persistance.
062: */
063: private static final long serialVersionUID = 8309478419800349694L;
064:
065: /**
066: * The name of the group.
067: */
068: private String name;
069:
070: /**
071: * The fields that define the group (can be empty).
072: */
073: private TreeSet fields;
074:
075: /**
076: * Cached fields.
077: */
078: private transient String[] fieldsCached;
079:
080: /**
081: * The group header (optional).
082: */
083: private GroupHeader header;
084:
085: /**
086: * The group footer (optional).
087: */
088: private GroupFooter footer;
089:
090: /**
091: * The internal constant to mark anonymous group names.
092: */
093: public static final String ANONYMOUS_GROUP_PREFIX = "anonymousGroup@";
094:
095: /**
096: * The report definition, to which this group is assigned.
097: */
098: private ReportDefinition reportDefinition;
099:
100: /**
101: * Constructs a group with no fields, and an empty header and footer.
102: */
103: public Group() {
104: this .name = Group.ANONYMOUS_GROUP_PREFIX
105: + System.identityHashCode(this );
106: this .fields = new TreeSet();
107: this .footer = new GroupFooter();
108: this .header = new GroupHeader();
109: }
110:
111: /**
112: * Defines the name for this group. The name must not be null.
113: *
114: * @param name the group name (null not permitted).
115: */
116: public void setName(final String name) {
117: if (name == null) {
118: throw new NullPointerException("Name must not be null");
119: }
120:
121: this .name = name;
122: }
123:
124: /**
125: * Returns the name of the group. This will never be null.
126: *
127: * @return the group name.
128: */
129: public String getName() {
130: return this .name;
131: }
132:
133: /**
134: * Returns the group header. <P> The group header is a report band that contains
135: * elements that should be printed at the start of a group.
136: *
137: * @return the group header.
138: */
139: public GroupHeader getHeader() {
140: return header;
141: }
142:
143: /**
144: * Sets the header for the group.
145: *
146: * @param header the header (null not permitted).
147: * @throws NullPointerException if the given header is null
148: */
149: public void setHeader(final GroupHeader header) {
150: if (header == null) {
151: throw new NullPointerException("Header must not be null");
152: }
153: this .header.setReportDefinition(null);
154: this .header = header;
155: this .header.setReportDefinition(reportDefinition);
156: }
157:
158: /**
159: * Returns the group footer.
160: *
161: * @return the footer.
162: */
163: public GroupFooter getFooter() {
164: return footer;
165: }
166:
167: /**
168: * Sets the footer for the group.
169: *
170: * @param footer the footer (null not permitted).
171: * @throws NullPointerException if the given footer is null.
172: */
173: public void setFooter(final GroupFooter footer) {
174: if (footer == null) {
175: throw new NullPointerException(
176: "The footer must not be null");
177: }
178: this .footer.setReportDefinition(null);
179: this .footer = footer;
180: this .footer.setReportDefinition(reportDefinition);
181: }
182:
183: /**
184: * Sets the fields for this group. The given list must contain Strings defining the
185: * needed fields from the DataRow. Don't reference Function-Fields here, functions are
186: * not supported in th groupfield definition.
187: *
188: * @param c the list containing strings.
189: * @throws NullPointerException if the given list is null or the list contains
190: * null-values.
191: */
192: public void setFields(final List c) {
193: if (c == null) {
194: throw new NullPointerException();
195: }
196: fields.clear();
197: fieldsCached = null;
198: final Iterator it = c.iterator();
199: while (it.hasNext()) {
200: final String field = (String) it.next();
201: addField(field);
202: }
203: }
204:
205: /**
206: * Adds a field to the group. The field names must correspond to the column names in
207: * the report's TableModel.
208: *
209: * @param name the field name (null not permitted).
210: * @throws NullPointerException if the name is null
211: */
212: public void addField(final String name) {
213: if (name == null) {
214: throw new NullPointerException(
215: "Group.addField(...): name is null.");
216: }
217: fields.add(name);
218: fieldsCached = null;
219: }
220:
221: /**
222: * Returns the list of fields for this group.
223: *
224: * @return a list (unmodifiable) of fields for the group.
225: */
226: public List getFields() {
227: if (fieldsCached == null) {
228: fieldsCached = (String[]) fields.toArray(new String[fields
229: .size()]);
230: }
231: return Collections
232: .unmodifiableList(Arrays.asList(fieldsCached));
233: }
234:
235: /**
236: * Returns the group fields as array. The array must not be modified.
237: *
238: * @return the fields as string array.
239: */
240: public String[] getFieldsArray() {
241: if (fieldsCached == null) {
242: fieldsCached = (String[]) fields.toArray(new String[fields
243: .size()]);
244: }
245: return fieldsCached;
246: }
247:
248: /**
249: * Clones this Element.
250: *
251: * @return a clone of this element.
252: *
253: * @throws CloneNotSupportedException should never be thrown.
254: */
255: public Object clone() throws CloneNotSupportedException {
256: final Group g = (Group) super .clone();
257: g.fields = new TreeSet(fields);
258: g.fieldsCached = fieldsCached;
259: g.footer = (GroupFooter) footer.clone();
260: g.header = (GroupHeader) header.clone();
261: return g;
262: }
263:
264: /**
265: * Compares two objects (required to be instances of the Group class). The group's field
266: * lists are compared, order of the fields does not matter.
267: *
268: * @param o the to be compared object.
269: * @return an integer indicating the relative ordering of the two groups.
270: */
271: public int compareTo(final Object o) {
272: final Group g = (Group) o;
273:
274: /** Remove all element, which are in both lists, they are equal */
275: if (fields.size() == g.fields.size()) {
276: // both lists contain the same elements.
277: if (fields.containsAll(g.fields)) {
278: return 0;
279: } else {
280: // groups with the same number of -, but different fields, are not compareable.
281: throw new IllegalArgumentException(
282: "These groups are not comparable, as they don't have any subgroup relation. "
283: + " Groups of the same GroupList must have a subgroup relation. The designated "
284: + " child group must contain all fields of the direct parent plus at least one "
285: + " new field.");
286: }
287: }
288:
289: if (fields.containsAll(g.fields)) {
290: // c2 contains all elements of c1, so c1 is subgroup of c2
291: return 1;
292: }
293: if (g.fields.containsAll(fields)) {
294: // c1 contains all elements of c2, so c2 is subgroup of c1
295: return -1;
296: }
297: // not compareable, invalid groups
298: // return 0;
299: throw new IllegalArgumentException(
300: "These groups are not comparable, as they don't have any subgroup relation. "
301: + " Groups of the same GroupList must have a subgroup relation. The designated "
302: + " child group must contain all fields of the direct parent plus at least one "
303: + " new field.");
304: }
305:
306: /**
307: * Checks, whether the group is equal. A group is considered equal to another group, if
308: * it defines the same fields as the other group.
309: *
310: * @param obj the object to be checked
311: * @return true, if the object is a group instance with the same fields, false
312: * otherwise.
313: */
314: public boolean equals(final Object obj) {
315: if (this == obj) {
316: return true;
317: }
318: if (!(obj instanceof Group)) {
319: return false;
320: }
321:
322: final Group group = (Group) obj;
323:
324: if (!fields.equals(group.fields)) {
325: return false;
326: }
327:
328: return true;
329: }
330:
331: /**
332: * Computes a hashcode for this group.
333: *
334: * @return the hashcode.
335: */
336: public int hashCode() {
337: final String[] fields = getFieldsArray();
338:
339: int hashCode = 0;
340: final int length = fields.length;
341: for (int i = 0; i < length; i++) {
342: hashCode = 29 * hashCode + fields[i].hashCode();
343: }
344: return hashCode;
345: }
346:
347: /**
348: * Returns a string representation of the group (useful for debugging).
349: *
350: * @return A string.
351: */
352: public String toString() {
353: final StringBuffer b = new StringBuffer();
354: b.append("Group={Name='");
355: b.append(getName());
356: b.append("', fields=");
357: b.append(fields);
358: b.append("} ");
359: return b.toString();
360: }
361:
362: /**
363: * Assigns the report definition to the group and all bands in that group.
364: *
365: * @param reportDefinition the report definition (maybe null).
366: */
367: public void setReportDefinition(
368: final ReportDefinition reportDefinition) {
369: this .reportDefinition = reportDefinition;
370: this .header.setReportDefinition(reportDefinition);
371: this .footer.setReportDefinition(reportDefinition);
372: }
373:
374: /**
375: * Returns the assigned report definition of the group.
376: *
377: * @return the report definition (maybe null).
378: */
379: public ReportDefinition getReportDefinition() {
380: return reportDefinition;
381: }
382: }
|