001: package jimm.datavision;
002:
003: import jimm.datavision.field.Field;
004: import jimm.datavision.field.FormulaField;
005: import jimm.util.XMLWriter;
006: import java.util.*;
007:
008: /**
009: * A section of a report contains a group of fields and suppression
010: * information. Sections include page headers and footers, report headers
011: * and footers, group headers and footers, and details. Sections contain
012: * elements (fields and lines).
013: *
014: * @see Report
015: * @see Field
016: * @see Line
017: * @author Jim Menard, <a href="mailto:jimm@io.com">jimm@io.com</a>
018: */
019: public class Section extends Observable implements Writeable, Observer {
020:
021: protected static final double DEFAULT_HEIGHT = 20;
022:
023: protected Report report;
024: protected SectionArea area;
025: protected double minHeight;
026: protected ArrayList fields;
027: protected ArrayList lines;
028: protected SuppressionProc suppressionProc;
029: protected boolean pageBreak;
030:
031: /**
032: * Constructor.
033: *
034: * @param r the report containing this section
035: */
036: public Section(Report r) {
037: report = r;
038: minHeight = DEFAULT_HEIGHT;
039: fields = new ArrayList();
040: lines = new ArrayList();
041: suppressionProc = new SuppressionProc(report);
042: }
043:
044: public void update(Observable o, Object arg) {
045: setChanged();
046: notifyObservers(arg);
047: }
048:
049: /**
050: * Returns the report containing this section.
051: */
052: public Report getReport() {
053: return report;
054: }
055:
056: /**
057: * Returns the min height of this section.
058: *
059: * @return the min height
060: */
061: public double getMinHeight() {
062: return minHeight;
063: }
064:
065: /**
066: * Sets the min height.
067: *
068: * @param newMinHeight the new min height
069: */
070: public void setMinHeight(double newMinHeight) {
071: if (minHeight != newMinHeight) {
072: minHeight = newMinHeight;
073: setChanged();
074: notifyObservers();
075: }
076: }
077:
078: /**
079: * Returns the height of this section: not the minimum height defined
080: * when the report was designed but rather the height necessary to
081: * output this section. The height returned is the maximum of
082: * <var>minHeight</var> and the highest y coordinate of any field.
083: *
084: * @return the height this section will need to be output
085: */
086: public double getOutputHeight() {
087: double h = minHeight;
088: for (Iterator iter = fields.iterator(); iter.hasNext();) {
089: Field f = (Field) iter.next();
090: double y = f.getBounds().y + f.getOutputHeight();
091: if (y > h)
092: h = y;
093: }
094: return h;
095: }
096:
097: /**
098: * Returns the width of this section.
099: *
100: * @return the width
101: */
102: public double getWidth() {
103: return report.getPaperFormat().getWidth();
104: }
105:
106: /**
107: * Returns the area this section is contained within.
108: *
109: * @return the area this section is contained within
110: */
111: public SectionArea getArea() {
112: return area;
113: }
114:
115: /**
116: * Sets the area this section is contained within and notifies any observers
117: * of the change.
118: *
119: * @param area a section area
120: */
121: public void setArea(SectionArea area) {
122: if (area != this .area) {
123: this .area = area;
124: setChanged();
125: notifyObservers();
126: }
127: }
128:
129: /**
130: * Returns the name of this section.
131: *
132: * @return the section name
133: */
134: public String getName() {
135: return (area == null) ? null : area.getName();
136: }
137:
138: /**
139: * Given an id, returns the field within this section that has that id.
140: * If no field with the specified id exists, returns <code>null</code>.
141: *
142: * @return a field, or <code>null</code> if no field with the specified
143: * id exists in this section
144: */
145: public Field findField(Object id) {
146: Long lid;
147: if (id instanceof String)
148: lid = new Long((String) id);
149: else
150: lid = (Long) id;
151:
152: for (Iterator iter = fields.iterator(); iter.hasNext();) {
153: Field f = (Field) iter.next();
154: if (f.getId().equals(lid))
155: return f;
156: }
157: return null;
158: }
159:
160: /**
161: * Adds a field to this section.
162: *
163: * @param f a field
164: */
165: public void addField(Field f) {
166: fields.add(f);
167: f.addObserver(this );
168: f.setSection(this );
169: setChanged();
170: notifyObservers();
171: }
172:
173: /**
174: * Removes a field from this section.
175: *
176: * @param f field
177: */
178: public void removeField(Field f) {
179: if (fields.contains(f)) {
180: fields.remove(f);
181: f.deleteObserver(this );
182: f.setSection(null);
183: setChanged();
184: notifyObservers();
185: }
186: }
187:
188: /**
189: * Returns an iterator over all fields in this section.
190: *
191: * @return an iterator
192: */
193: public Iterator fields() {
194: return fields.iterator();
195: }
196:
197: /**
198: * Returns an array of this section's fields sorted by <var>comp</var>.
199: *
200: * @param comp the comparator to use for sorting
201: * @return an array of fields sorted by <var>comp</var>
202: */
203: public Object[] fieldsSortedBy(Comparator comp) {
204: Object[] sorted = fields.toArray();
205: Arrays.sort(sorted, comp);
206: return sorted;
207: }
208:
209: /**
210: * Returns the number of fields in this section.
211: *
212: * @return the number of fields in this section
213: */
214: public int numFields() {
215: return fields.size();
216: }
217:
218: /**
219: * Adds a line to this section.
220: *
221: * @param l a line
222: */
223: public void addLine(Line l) {
224: lines.add(l);
225: setChanged();
226: notifyObservers();
227: }
228:
229: /**
230: * Removes a line from this section.
231: *
232: * @param f line
233: */
234: public void removeLine(Line f) {
235: lines.remove(f);
236: setChanged();
237: notifyObservers();
238: }
239:
240: /**
241: * Returns an iterator over all lines in this section.
242: *
243: * @return an iterator
244: */
245: public Iterator lines() {
246: return lines.iterator();
247: }
248:
249: public boolean isHidden() {
250: return suppressionProc.isHidden();
251: }
252:
253: /**
254: * Returns <code>true</code> if this is a report detail section.
255: *
256: * @return <code>true</code> if this is a report detail section
257: */
258: public boolean isDetail() {
259: return area.isDetail();
260: }
261:
262: /**
263: * Returns the boolean page break flag.
264: *
265: * @return <code>true</code> if we should start a new page before this
266: * section.
267: */
268: public boolean hasPageBreak() {
269: return pageBreak;
270: }
271:
272: /**
273: * Sets the page break flag.
274: *
275: * @param flag new value
276: */
277: public void setPageBreak(boolean flag) {
278: pageBreak = flag;
279: }
280:
281: /**
282: * Returns the supression proc this section uses
283: *
284: * @return a suppression proc
285: */
286: public SuppressionProc getSuppressionProc() {
287: return suppressionProc;
288: }
289:
290: /**
291: * Returns <code>true</code> if the specified field is inside this section.
292: *
293: * @param f a field
294: * @return <code>true</code> if the field is within this section
295: */
296: public boolean contains(Field f) {
297: return fields.contains(f);
298: }
299:
300: /**
301: * Returns <code>true</code> if the specified field exists within this
302: * section either directly (as a field) or indirectly (as a formula used
303: * by an aggregate, user column, or formula or by the suppression proc).
304: *
305: * @param f a field
306: * @return <code>true</code> if the specified field is referenced within
307: * this section
308: */
309: public boolean containsReferenceTo(Field f) {
310: for (Iterator iter = fields(); iter.hasNext();) {
311: Field field = (Field) iter.next();
312: if (field == f || field.refersTo(f))
313: return true;
314: }
315: return suppressionProc.refersTo(f);
316: }
317:
318: /**
319: * Returns <code>true</code> if the specified formula exists within this
320: * section either directly (as a formula field) or indirectly (as a formula
321: * used by an aggregate, user column, or formula or by the suppression proc).
322: *
323: * @param f a formula
324: * @return <code>true</code> if the specified formula is referenced within
325: * this section
326: */
327: public boolean containsReferenceTo(Formula f) {
328: for (Iterator iter = fields(); iter.hasNext();) {
329: Field field = (Field) iter.next();
330: if (field.refersTo(f))
331: return true;
332: }
333: return suppressionProc.refersTo(f);
334: }
335:
336: /**
337: * Returns <code>true</code> if the specified user column exists within this
338: * section either directly (as a user column field) or indirectly (as a user
339: * column used by an aggregate, a formula, or by the suppression proc).
340: *
341: * @param uc a user column
342: * @return <code>true</code> if the specified formula is referenced within
343: * this section
344: */
345: public boolean containsReferenceTo(UserColumn uc) {
346: for (Iterator iter = fields(); iter.hasNext();) {
347: Field field = (Field) iter.next();
348: if (field.refersTo(uc))
349: return true;
350: }
351: return suppressionProc.refersTo(uc);
352: }
353:
354: /**
355: * Returns <code>true</code> if the specified parameter exists within this
356: * section either directly (as a parameter field) or indirectly (as a parameter
357: * used by an aggregate, a formula, or by the suppression proc).
358: *
359: * @param p a parameter
360: * @return <code>true</code> if the specified formula is referenced within
361: * this section
362: */
363: public boolean containsReferenceTo(Parameter p) {
364: for (Iterator iter = fields(); iter.hasNext();) {
365: Field field = (Field) iter.next();
366: if (field.refersTo(p))
367: return true;
368: }
369: return suppressionProc.refersTo(p);
370: }
371:
372: /**
373: * Forces all of the formulas used in this section to be evaluated.
374: * See the comment for <code>LayoutEngine.groupHeaders</code> for why
375: * this method is necessary.
376: *
377: * @see jimm.datavision.layout.LayoutEngine#groupHeaders
378: */
379: public void evaluateFormulas() {
380: for (Iterator iter = fields(); iter.hasNext();) {
381: Field field = (Field) iter.next();
382: if (field instanceof FormulaField)
383: field.getValue();
384: }
385: }
386:
387: /**
388: * Return <code>true</code> if this section should be printed, given this
389: * particular row of data and our supressed state and suppression proc.
390: *
391: * @return <code>true</code> if the data should be displayed
392: */
393: public boolean isVisibleForCurrentRow() {
394: return !suppressionProc.suppress();
395: }
396:
397: /**
398: * Writes this section and all it contains as an XML tag.
399: *
400: * @param out a writer that knows how to write XML
401: */
402: public void writeXML(XMLWriter out) {
403: out.startElement("section");
404: out.attr("height", minHeight);
405: if (pageBreak)
406: out.attr("pagebreak", pageBreak);
407:
408: ListWriter.writeList(out, fields);
409: ListWriter.writeList(out, lines);
410: suppressionProc.writeXML(out);
411:
412: out.endElement();
413: }
414:
415: }
|