001: package jimm.datavision;
002:
003: import jimm.datavision.field.*;
004: import jimm.datavision.source.*;
005: import jimm.datavision.source.sql.*;
006: import jimm.datavision.source.charsep.CharSepSource;
007: import jimm.datavision.source.ncsql.NCDatabase;
008: import jimm.util.I18N;
009: import java.io.*;
010: import java.util.*;
011: import java.awt.Color;
012:
013: import javax.xml.parsers.SAXParserFactory;
014:
015: import org.xml.sax.*;
016: import org.xml.sax.helpers.DefaultHandler;
017:
018: /**
019: * A report reader reads an XML file and creates the innards of a report.
020: *
021: * @author Jim Menard, <a href="mailto:jimm@io.com">jimm@io.com</a>
022: */
023: public class ReportReader extends DefaultHandler {
024:
025: // ================================================================
026: /**
027: * This class is used when we are converting formulas from the old
028: * pre-DTD_VERSION_FORMULA_IDS format to the new format.
029: * <p>
030: * We store a copy of the original eval string because if we were to
031: * retrieve it from the formula by calling
032: * <code>Formula.getEvalString</code>, the formula would attempt to start
033: * observing other formulas within the eval string. This would not work
034: * because those formulas are represented the old way (by name instead of
035: * id) and thus the code that looks for those formulas in order to observe
036: * them fails. We don't have to clone the eval string, just save a
037: * reference to it.
038: */
039: static class FormulaConversion {
040: Formula formula;
041: String expression;
042:
043: FormulaConversion(Formula f, String expr) {
044: formula = f;
045: expression = expr;
046: }
047: }
048:
049: // ================================================================
050:
051: /**
052: * If there is no report element dtd-version attribute, this is the default
053: * value to use. That's because DataVision XML files before version 0.2
054: * didn't include version numbers.
055: */
056: protected static final double DEFAULT_DTD_VERSION = 0.2;
057:
058: /**
059: * This is the DTD version where formula ids were introduced. Versions
060: * before this one require runtime conversion.
061: */
062: protected static final double DTD_VERSION_FORMULA_IDS = 0.2;
063:
064: protected Stack tagNameStack;
065: protected Report report;
066: protected Subreport subreport;
067: protected Parameter parameter;
068: protected Formula formula;
069: protected UserColumn usercol;
070: protected Section section;
071: protected Group group;
072: protected Field field;
073: protected String textData;
074: protected Border border;
075: protected Line line;
076: protected double dtdVersion;
077: protected HashMap formulasToConvert;
078: protected int nextSectionLocation;
079: protected boolean missingColumnSeen;
080: protected boolean inSubreportJoins;
081:
082: /**
083: * Constructor.
084: *
085: * @param report the report we are building
086: */
087: public ReportReader(Report report) {
088: this .report = report;
089: tagNameStack = new Stack();
090: dtdVersion = DEFAULT_DTD_VERSION;
091: }
092:
093: /**
094: * Uses the InputSource to find the XML, reads it, and builds the innards
095: * of the report. To specify a URL, use <code>new
096: * InputSource("http://...")</code>.
097: *
098: * @param in the input source
099: */
100: public void read(InputSource in) throws Exception {
101: removeReportSections();
102: SAXParserFactory.newInstance().newSAXParser().parse(in, this );
103: postParse();
104: }
105:
106: /**
107: * Reads an XML file and builds the innards of the report.
108: *
109: * @param f the XML file
110: */
111: public void read(File f) throws Exception {
112: removeReportSections();
113: SAXParserFactory.newInstance().newSAXParser().parse(f, this );
114: postParse();
115: }
116:
117: /**
118: * Removes the report sections that are created when a report is created.
119: */
120: protected void removeReportSections() {
121: getReport().headers().clear();
122: getReport().pageHeaders().clear();
123: getReport().pageFooters().clear();
124: getReport().footers().clear();
125: getReport().details().clear();
126: }
127:
128: /**
129: * Performed after a parse, we convert old-style formulas if necessary and
130: * ensure that certain report sections are non-empty.
131: */
132: protected void postParse() throws SAXException {
133: convertFormulas();
134:
135: // Headers, footers, and details must have at least one section.
136: ensureNotEmpty(report.headers());
137: ensureNotEmpty(report.pageHeaders());
138: ensureNotEmpty(report.details());
139: ensureNotEmpty(report.footers());
140: ensureNotEmpty(report.pageFooters());
141:
142: for (Iterator iter = report.subreports(); iter.hasNext();)
143: ensureNotEmpty(((Subreport) iter.next()).details());
144: }
145:
146: /**
147: * Ensures that the specified collection of sections is not empty. If we
148: * do create a section, it is marked as suppressed.
149: *
150: * @param area collection of sections
151: * sections
152: */
153: protected void ensureNotEmpty(SectionArea area) {
154: if (area.isEmpty()) {
155: Section s = new Section(report);
156: s.getSuppressionProc().setHidden(true);
157: area.add(s);
158: }
159: }
160:
161: protected Report getReport() {
162: return subreport != null ? subreport : report;
163: }
164:
165: public void startElement(final String namespaceURI,
166: final String localName, final String qName,
167: final Attributes attributes) throws SAXException {
168: String tagName = localName;
169: if (tagName == null || tagName.length() == 0)
170: tagName = qName;
171:
172: String parentTag = tagNameStack.empty() ? null
173: : (String) tagNameStack.peek();
174: tagNameStack.push(new String(tagName));
175:
176: // Get ready to start collecting text
177: if (textData == null || textData.length() > 0)
178: textData = new String();
179:
180: if ("report".equals(tagName))
181: report(attributes);
182: else if ("bean-scripting-framework".equals(tagName))
183: defaultLanguage(attributes);
184: else if ("language".equals(tagName))
185: language(attributes);
186: else if ("database".equals(tagName))
187: database(attributes);
188: else if ("query".equals(tagName))
189: query(attributes);
190: else if ("charsep".equals(tagName))
191: charSepSource(attributes);
192: else if ("nc-database".equals(tagName))
193: ncDatabaseSource(attributes);
194: else if ("column".equals(tagName))
195: column(attributes);
196: else if ("subreport-joins".equals(tagName))
197: inSubreportJoins = true;
198: else if ("join".equals(tagName))
199: join(attributes);
200: else if ("sort".equals(tagName))
201: sort(attributes);
202: else if ("subreport".equals(tagName))
203: subreport(attributes);
204: else if ("parameter".equals(tagName))
205: parameter(attributes);
206: else if ("formula".equals(tagName))
207: formula(parentTag, attributes);
208: else if ("usercol".equals(tagName))
209: usercol(attributes);
210: else if ("headers".equals(tagName))
211: header(parentTag);
212: else if ("footers".equals(tagName))
213: footer(parentTag);
214: else if ("group".equals(tagName))
215: group(attributes);
216: else if ("details".equals(tagName))
217: nextSectionLocation = SectionArea.DETAIL;
218: else if ("section".equals(tagName))
219: section(attributes);
220: else if ("field".equals(tagName))
221: field(attributes);
222: else if ("bounds".equals(tagName))
223: bounds(attributes);
224: else if ("edge".equals(tagName))
225: edge(attributes);
226: else if ("format".equals(tagName))
227: format(attributes);
228: else if ("border".equals(tagName))
229: border(attributes);
230: else if ("line".equals(tagName))
231: line(attributes);
232: else if ("point".equals(tagName))
233: point(attributes);
234: else if ("paper".equals(tagName))
235: paper(attributes);
236: else if ("suppression-proc".equals(tagName))
237: suppressionProc(attributes);
238: }
239:
240: protected void header(String parentTag) {
241: if ("report".equals(parentTag))
242: nextSectionLocation = SectionArea.REPORT_HEADER;
243: else if ("page".equals(parentTag))
244: nextSectionLocation = SectionArea.PAGE_HEADER;
245: else if ("group".equals(parentTag))
246: nextSectionLocation = SectionArea.GROUP_HEADER;
247: }
248:
249: protected void footer(String parentTag) {
250: if ("report".equals(parentTag))
251: nextSectionLocation = SectionArea.REPORT_FOOTER;
252: else if ("page".equals(parentTag))
253: nextSectionLocation = SectionArea.PAGE_FOOTER;
254: else if ("group".equals(parentTag))
255: nextSectionLocation = SectionArea.GROUP_FOOTER;
256: }
257:
258: /**
259: * Handle elements expecting text data.
260: */
261: public void endElement(final String namespaceURI,
262: final String localName, final String qName)
263: throws SAXException {
264: String tagName = localName;
265: if (tagName == null || tagName.length() == 0)
266: tagName = qName;
267:
268: // If we were paranoid, we could compare tagName to the top of the
269: // stack. Let's not.
270: tagNameStack.pop();
271:
272: if ("description".equals(tagName))
273: getReport().setDescription(textData);
274: else if ("subreport".equals(tagName))
275: subreport = null;
276: else if ("subreport-joins".equals(tagName))
277: inSubreportJoins = false;
278: else if ("default".equals(tagName) && parameter != null)
279: parameter.addDefaultValue(textData);
280: else if ("formula".equals(tagName)) {
281: formula.setExpression(textData);
282: // If we need to convert this formula later, save a copy of
283: // this eval string text.
284: if (formulasToConvert != null) {
285: FormulaConversion fc = (FormulaConversion) formulasToConvert
286: .get(formula.getName());
287: if (fc != null)
288: fc.expression = new String(textData);
289: }
290: formula = null;
291: } else if ("usercol".equals(tagName)) {
292: usercol.setExpression(textData);
293: usercol = null;
294: } else if ("text".equals(tagName) && field != null)
295: field.setValue(textData);
296: else if ("where".equals(tagName))
297: getReport().getDataSource().getQuery().setWhereClause(
298: textData);
299: else if ("metadata-url".equals(tagName)) {
300: try {
301: getReport().getDataSource().readMetadataFrom(textData);
302: } catch (Exception e) {
303: throw new SAXException(e);
304: }
305: }
306: }
307:
308: /**
309: * Reads text data. Text data inside a single tag can be broken up into
310: * multiple calls to this method.
311: */
312: public void characters(char ch[], int start, int length) {
313: textData += new String(ch, start, length);
314: }
315:
316: /**
317: * Reads the report tag.
318: */
319: protected void report(Attributes attributes) {
320: String dtdVersionString = attributes.getValue("dtd-version");
321: dtdVersion = (dtdVersionString == null) ? DEFAULT_DTD_VERSION
322: : Double.parseDouble(dtdVersionString);
323:
324: getReport().setName(attributes.getValue("name"));
325: getReport().setTitle(attributes.getValue("title"));
326: getReport().setAuthor(attributes.getValue("author"));
327: }
328:
329: protected void defaultLanguage(Attributes attributes) {
330: String lang = rubyLanguageNameHack(attributes
331: .getValue("default-language"));
332: getReport().getScripting().setDefaultLanguage(lang);
333: }
334:
335: protected void language(Attributes attributes) {
336: String lang = rubyLanguageNameHack(attributes.getValue("name"));
337: getReport().getScripting().addLanguage(lang,
338: attributes.getValue("class"));
339: }
340:
341: protected String rubyLanguageNameHack(String lang) {
342: // Hack: Scripting.java had the default value of "ruby" originally,
343: // and I've changed it to "Ruby".
344: return "ruby".equals(lang) ? "Ruby" : lang;
345: }
346:
347: /**
348: * Reads the database tag and creates the database object. If the report
349: * already has a data source (for example, someone has called
350: * <code>Report.setDataSource</code> or
351: * <code>Report.setDatabaseConnection</code>), then we don't do anything.
352: *
353: * @see Report#hasDataSource
354: * @see Report#setDatabaseConnection
355: */
356: protected void database(Attributes attributes) {
357: if (getReport().hasDataSource())
358: return;
359:
360: try {
361: Database db = new Database(attributes
362: .getValue("driverClassName"), attributes
363: .getValue("connInfo"), getReport(), attributes
364: .getValue("name"), attributes.getValue("username"));
365: getReport().setDataSource(db);
366: } catch (UserCancellationException iae) {
367: // Thrown by dataSource when user cancelled password dialog.
368: // Let the report catch this.
369: throw iae;
370: } catch (Exception e) {
371: ErrorHandler.error(I18N.get("ReportReader.db_err"), e, I18N
372: .get("ReportReader.db_err_title"));
373: }
374: }
375:
376: /**
377: * Reads the query. Nothing to do, since the data source already has an
378: * empty query.
379: */
380: protected void query(Attributes attributes) {
381: }
382:
383: /**
384: * Reads and creates a CharSepSource.
385: */
386: protected void charSepSource(Attributes attributes) {
387: if (getReport().hasDataSource())
388: return;
389:
390: CharSepSource charSepSource = new CharSepSource(getReport());
391: String charString = attributes.getValue("sep-char");
392: if (charString != null)
393: charSepSource.setSepChar(charString.charAt(0));
394:
395: getReport().setDataSource(charSepSource);
396: }
397:
398: /**
399: * Reads and creates an NCDatabase data source.
400: */
401: protected void ncDatabaseSource(Attributes attributes) {
402: if (!getReport().hasDataSource())
403: getReport().setDataSource(new NCDatabase(getReport()));
404: }
405:
406: protected void column(Attributes attributes) {
407: String name = attributes.getValue("name");
408: int type = Column.typeFromString(attributes.getValue("type"));
409: Column col = new Column(name, name, type);
410: col.setDateParseFormat(attributes.getValue("date-format"));
411:
412: getReport().getDataSource().addColumn(col);
413: }
414:
415: protected void join(Attributes attributes) {
416: Column from = findColumn(attributes.getValue("from"));
417: Column to = findColumn(attributes.getValue("to"));
418: if (from != null && to != null) {
419: Join join = new Join(from, attributes.getValue("relation"),
420: to);
421: if (inSubreportJoins)
422: ((Subreport) getReport()).addJoin(join);
423: else
424: getReport().getDataSource().getQuery().addJoin(join);
425: }
426: }
427:
428: protected void sort(Attributes attributes) {
429: String oldColumnIdStr = attributes.getValue("column");
430: Selectable selectable = null;
431: if (oldColumnIdStr != null) { // Old-style column id attribute
432: selectable = findColumn(oldColumnIdStr.trim());
433: if (selectable == null) {
434: group = null;
435: return;
436: }
437: } else {
438: selectable = findSelectable(attributes.getValue(
439: "groupable-id").trim(), attributes.getValue(
440: "groupable-type").trim());
441: }
442:
443: if (selectable != null) {
444: String str = attributes.getValue("order");
445: int val = (str != null && str.length() > 0 && str.charAt(0) == 'd') ? Query.SORT_DESCENDING
446: : Query.SORT_ASCENDING;
447: getReport().getDataSource().getQuery().addSort(selectable,
448: val);
449: }
450: }
451:
452: protected void subreport(Attributes attributes) {
453: subreport = new Subreport(report, new Long(attributes
454: .getValue("id")));
455: // The subreport adds itself to the parent report.
456:
457: removeReportSections(); // Acts on subreport
458:
459: try {
460: Database db = (Database) report.getDataSource();
461: subreport.setDataSource(new SubreportDatabase(db
462: .getConnection(), subreport));
463: } catch (Exception e) {
464: ErrorHandler.error(I18N.get("ReportReader.db_err"), e, I18N
465: .get("ReportReader.db_err_title"));
466: }
467: }
468:
469: protected void parameter(Attributes attributes) throws SAXException {
470: parameter = new Parameter(new Long(attributes.getValue("id")),
471: getReport(), attributes.getValue("type"), attributes
472: .getValue("name"), attributes
473: .getValue("question"), attributes
474: .getValue("arity"));
475: getReport().addParameter(parameter);
476: }
477:
478: /**
479: * Reads a formula. If the XML format is really old, we need to give
480: * each formula an id number and translate its formula text so references
481: * to other formulas use the other formula's id number instead of its name.
482: */
483: protected void formula(String parentTag, Attributes attributes)
484: throws SAXException {
485: String idString = attributes.getValue("id");
486: String name = attributes.getValue("name");
487: Long id = null;
488: if (idString == null) {
489: if (dtdVersion >= DTD_VERSION_FORMULA_IDS) {
490: String str = I18N.get("ReportReader.the_formula")
491: + ' '
492: + name
493: + ' '
494: + I18N
495: .get("ReportReader.formula_missing_id_err");
496: throw new SAXException(str);
497: }
498: // else, we are OK with a null id
499: } else
500: id = new Long(idString);
501:
502: if ("formulas".equals(parentTag)) {
503: formula = new Formula(id, getReport(), name, null);
504: getReport().addFormula(formula);
505: } else if ("suppression-proc".equals(parentTag)) {
506: // We don't use a new formula; we use the one the section has.
507: formula = section.getSuppressionProc().getFormula();
508: } else {
509: formula = new Formula(id, getReport(), name, null);
510: getReport().setStartFormula(formula);
511: }
512:
513: // If the id is null, that means we need to convert this formula
514: // to the current format.
515: if (id == null) {
516: if (formulasToConvert == null)
517: formulasToConvert = new HashMap();
518: FormulaConversion fc = new FormulaConversion(formula, null);
519: formulasToConvert.put(formula.getName(), fc);
520: }
521:
522: String language = attributes.getValue("language");
523: if (language != null)
524: formula.setLanguage(language);
525: }
526:
527: /**
528: * Reads a user column. Value of user column will be read later.
529: */
530: protected void usercol(Attributes attributes) throws SAXException {
531: usercol = new UserColumn(new Long(attributes.getValue("id")),
532: getReport(), attributes.getValue("name"), null);
533: getReport().addUserColumn(usercol);
534: }
535:
536: /**
537: * Revisits each formula and let it convert formula names to
538: * formula id numbers within its eval string.
539: */
540: protected void convertFormulas() throws SAXException {
541: if (formulasToConvert != null) {
542: for (Iterator iter = formulasToConvert.values().iterator(); iter
543: .hasNext();) {
544: FormulaConversion fc = (FormulaConversion) iter.next();
545: Formula f = fc.formula;
546: try {
547: f.setEditableExpression(fc.expression);
548: } catch (IllegalArgumentException iae) {
549: String msg = I18N.get("ReportReader.the_formula")
550: + ' '
551: + f.getName()
552: + ' '
553: + I18N
554: .get("ReportReader.formula_unknown_name");
555: throw new SAXException(msg);
556: }
557: }
558: }
559: }
560:
561: /** Creates a group and adds it to the report. */
562: protected void group(Attributes attributes) {
563: String oldColumnIdStr = attributes.getValue("column");
564: Selectable selectable = null;
565: if (oldColumnIdStr != null) { // Old-style column id attribute
566: selectable = findColumn(oldColumnIdStr.trim());
567: if (selectable == null) {
568: group = null;
569: return;
570: }
571: } else {
572: selectable = findSelectable(attributes.getValue(
573: "groupable-id").trim(), attributes.getValue(
574: "groupable-type").trim());
575: }
576:
577: group = new Group(getReport(), selectable);
578:
579: String sortOrderString = attributes.getValue("sort-order"); // OK if null
580: if (sortOrderString != null)
581: group.setSortOrder(Group
582: .sortOrderStringToInt(sortOrderString.trim()));
583:
584: getReport().groups.add(group);
585: }
586:
587: /** Creates an empty section and adds it to the report. */
588: protected void section(Attributes attributes) {
589: section = new Section(getReport());
590: section.setMinHeight(Double.parseDouble(attributes
591: .getValue("height")));
592:
593: // Handle old XML files that store a "suppressed" attribute.
594: String boolValString = attributes.getValue("suppressed");
595: if ("true".equals(boolValString))
596: section.getSuppressionProc().setHidden(true);
597:
598: boolValString = attributes.getValue("pagebreak");
599: section.setPageBreak("true".equals(boolValString));
600:
601: addSectionToReport();
602: }
603:
604: /**
605: * Adds the last seen section to the report. The value of
606: * <code>nextSectionLocation</code> determines where the section belongs.
607: */
608: protected void addSectionToReport() {
609: switch (nextSectionLocation) {
610: case SectionArea.GROUP_HEADER:
611: if (group != null)
612: group.headers().add(section);
613: break;
614: case SectionArea.GROUP_FOOTER:
615: if (group != null)
616: group.footers().add(section);
617: break;
618: default:
619: getReport().getSectionArea(nextSectionLocation)
620: .add(section);
621: break;
622: }
623: }
624:
625: /**
626: * Reads and creates a field. If the XML format is really old, we need to
627: * convert formula fields by changing their values from the formula name
628: * to the formula id.
629: */
630: protected void field(Attributes attributes) {
631: if (section == null) { // We're reading the report's default field
632: field = report.getDefaultField();
633: return;
634: }
635:
636: String id = attributes.getValue("id");
637: String type = attributes.getValue("type");
638: Object value = attributes.getValue("value");
639: String visibleString = attributes.getValue("visible");
640:
641: boolean visible = true;
642: if (visibleString != null) {
643: visibleString = visibleString.trim().toLowerCase();
644: if (visibleString.length() > 0)
645: visible = "true".equals(visibleString);
646: }
647:
648: // If this is a dataSource column, make sure the column exists.
649: if ("column".equals(type)
650: && findColumn(value.toString()) == null) {
651: field = null;
652: return;
653: }
654:
655: // If we are converting formulas without id numbers, change our value
656: // (a pointer to the formula itself) from the formula's name to the
657: // formula's newly-created id.
658: if ("formula".equals(type) && formulasToConvert != null
659: && value != null) {
660: Formula f = ((FormulaConversion) formulasToConvert
661: .get(value)).formula;
662: if (f != null)
663: value = f.getId();
664: }
665:
666: field = Field.create(new Long(id), getReport(), section, type,
667: value, visible);
668: if (field instanceof AggregateField && group != null)
669: ((AggregateField) field).setGroup(group);
670:
671: section.addField(field);
672: }
673:
674: /** Reads and sets the current field's bounds rectangle. */
675: protected void bounds(Attributes attributes) {
676: if (field != null)
677: field.getBounds().setBounds(
678: Double.parseDouble(attributes.getValue("x")),
679: Double.parseDouble(attributes.getValue("y")),
680: Double.parseDouble(attributes.getValue("width")),
681: Double.parseDouble(attributes.getValue("height")));
682: }
683:
684: /** * Reads and creates the current field's format. */
685: protected void format(Attributes attributes) {
686: if (field == null)
687: return;
688:
689: Format format = field.getFormat();
690: String val;
691:
692: if ((val = attributes.getValue("font")) != null)
693: format.setFontFamilyName(val);
694: if ((val = attributes.getValue("size")) != null)
695: format.setSize(Double.parseDouble(val));
696: if ((val = attributes.getValue("bold")) != null)
697: format.setBold("true".equals(val));
698: if ((val = attributes.getValue("italic")) != null)
699: format.setItalic("true".equals(val));
700: if ((val = attributes.getValue("underline")) != null)
701: format.setUnderline("true".equals(val));
702: if ((val = attributes.getValue("wrap")) != null)
703: format.setWrap("true".equals(val));
704: if ((val = attributes.getValue("align")) != null)
705: format.setAlign(Format.alignFromString(val));
706: if ((val = attributes.getValue("color")) != null)
707: format.setColor(parseColor(val));
708: if ((val = attributes.getValue("format")) != null)
709: format.setFormat(val);
710: }
711:
712: /** Parses color string and returns a <code>java.awt.Color</code>. */
713: protected Color parseColor(String val) {
714: StringTokenizer tok = new StringTokenizer(val, ";");
715: int r = Integer.parseInt(tok.nextToken().trim());
716: int g = Integer.parseInt(tok.nextToken().trim());
717: int b = Integer.parseInt(tok.nextToken().trim());
718: int a = Integer.parseInt(tok.nextToken().trim());
719: return new Color(r, g, b, a);
720: }
721:
722: /** Reads and creates a new field border. */
723: protected void border(Attributes attributes) {
724: if (field != null) {
725: border = new Border(field);
726: field.setBorder(border);
727:
728: String val = attributes.getValue("color");
729: if (val != null)
730: border.setColor(parseColor(val));
731: }
732: }
733:
734: /** Reads and creates a new border edge. */
735: protected void edge(Attributes attributes) {
736: if (field == null)
737: return;
738:
739: String val = attributes.getValue("style");
740: int style = BorderEdge.styleFromString(val);
741:
742: val = attributes.getValue("thickness");
743: double thickness = (val == null) ? BorderEdge.DEFAULT_THICKNESS
744: : Double.parseDouble(val);
745:
746: val = attributes.getValue("number");
747: int num = (val == null) ? BorderEdge.DEFAULT_NUMBER : Integer
748: .parseInt(val);
749:
750: BorderEdge edge = new BorderEdge(style, thickness, num);
751:
752: String loc = attributes.getValue("location");
753: if ("top".equals(loc))
754: border.setTop(edge);
755: else if ("bottom".equals(loc))
756: border.setBottom(edge);
757: else if ("left".equals(loc))
758: border.setLeft(edge);
759: else if ("right".equals(loc))
760: border.setRight(edge);
761: }
762:
763: /** Reads and creates a new line. */
764: protected void line(Attributes attributes) {
765: String val = attributes.getValue("thickness");
766: double thickness = (val == null) ? 1.0 : Double
767: .parseDouble(val);
768: Color color = null;
769: if ((val = attributes.getValue("color")) != null)
770: color = parseColor(val);
771:
772: val = attributes.getValue("visible");
773: boolean visible = true;
774: if (val != null) {
775: val = val.trim().toLowerCase();
776: if (val.length() > 0)
777: visible = "true".equals(val);
778: }
779:
780: section.lines.add(line = new Line(getReport(), section,
781: thickness, color, visible));
782: }
783:
784: /** Reads a line's point and adds it to the current line. */
785: protected void point(Attributes attributes) {
786: line.addEndPoint(Double.parseDouble(attributes.getValue("x")),
787: Double.parseDouble(attributes.getValue("y")));
788: }
789:
790: /** Reads paper size name and orientation. */
791: protected void paper(Attributes attributes) {
792: int orientation = PaperFormat.PORTRAIT; // Default value
793: String orientationStr = attributes.getValue("orientation");
794: if (orientationStr != null
795: && "landscape".equals(orientationStr.toLowerCase()))
796: orientation = PaperFormat.LANDSCAPE;
797:
798: PaperFormat pf = PaperFormat.get(orientation, attributes
799: .getValue("name"));
800: if (pf != null)
801: getReport().setPaperFormat(pf);
802: }
803:
804: /** Reads suppression proc. */
805: protected void suppressionProc(Attributes attributes) {
806: String state = attributes.getValue("hide");
807: if (state != null && state.length() > 0) {
808: SuppressionProc sp = section.getSuppressionProc();
809: state = state.trim().toLowerCase();
810: if ("true".equals(state))
811: sp.setHidden(true);
812: }
813: }
814:
815: /**
816: * Returns the column identified by its name. The first time we can not find
817: * a column, we report an error to the user.
818: *
819: * @param fullName a column name
820: * @return a dataSource column
821: */
822: protected Column findColumn(String fullName) {
823: Column col = getReport().findColumn(fullName);
824: if (col == null && !missingColumnSeen) {
825: missingColumnSeen = true;
826: ErrorHandler.error(I18N.get("ReportReader.the_column")
827: + ' ' + fullName + ' '
828: + I18N.get("ReportReader.column_unknown"));
829: }
830: return col;
831: }
832:
833: /**
834: * Returns the selectable identified by its id and type. The first time we can
835: * not find one, we report an error to the user.
836: *
837: * @param idStr an id string
838: * @param typeStr a type string ("column", "usercol")
839: * @return a dataSource column
840: */
841: protected Selectable findSelectable(String idStr, String typeStr) {
842: Selectable g = getReport().findSelectable(idStr, typeStr);
843: if (g == null && !missingColumnSeen) {
844: missingColumnSeen = true;
845: ErrorHandler.error(I18N.get("ReportReader.the_column")
846: + ' ' + idStr + " (" + typeStr + ") "
847: + I18N.get("ReportReader.column_unknown"));
848: }
849: return g;
850: }
851:
852: }
|