0001: package dinamica;
0002:
0003: import java.text.MessageFormat;
0004: import java.util.ArrayList;
0005: import java.util.HashMap;
0006: import java.util.Iterator;
0007: import java.io.PrintWriter;
0008: import java.net.URLEncoder;
0009: import java.sql.Types;
0010: import javax.servlet.RequestDispatcher;
0011: import javax.servlet.ServletContext;
0012: import javax.servlet.http.*;
0013: import java.util.Locale;
0014: import electric.xml.*;
0015:
0016: /**
0017: * Core-level framework class: Text Generator.
0018: * <br><br>
0019: * Generates text output (html, xml, sql, etc) given
0020: * a template with some special markers that will be replaced
0021: * by data (supplied from recordsets). This class is used by the MVC mechanism of this framework
0022: * and for general purpose text management.
0023: * <br><br>
0024: * This is a very powerfull class, it can generate html forms
0025: * with its values ready to be edited, whole tables with one line
0026: * of code, complete well written, portable SQL statements with one line of code,
0027: * no matter how many fields or the type of each field, the statement will
0028: * be formatted appropiately, without programmer effort.
0029: * <br><br>
0030: * It helps to COMPLETELY separate content from presentation, the
0031: * templates are dynamically loaded and some labels are replaced with
0032: * data provided by recordsets using very simple and effective logic.
0033: * <br><br>
0034: * Markers are represented by:
0035: * <li> Field Markers: ${fld:FieldName}
0036: * <li> Default Values Markers: ${def:DefaultValueName}
0037: * <li> Sequence Markers: ${seq:nextval@SequenceName} - ${seq:currval@SequenceName}. This markers allows cross-database sequence expressions, the generation
0038: * of the actual SEQUENCE SQL expression is determined by context parameters in WEB.XML (sequence-nextval and sequence-currval).
0039: *
0040: * <br><br>
0041: * As you can see, the markers distingish themselves by a prefix which
0042: * denotes its type of marker (fld, def or seq).
0043: * <br><br>
0044: * Field Markers can have an additional attribute (not valid for SQL generation)
0045: * representing the output format, using any valid Java mask for dates and numbers.
0046: * Example: {fld:FieldName@mask} - where "mask" would be the output format mask.<br>
0047: * <br><br>
0048: * Repeating blocks can also be generated and are conmmonly used for
0049: * printing tables or filling HTML SELECT controls (ComboBox/ListBox). Example:
0050: * <pre>
0051: * <repeat>
0052: * <tr>
0053: * <td>${fld:col_1}</td>
0054: * <td>${fld:col_2}</td>
0055: * </tr>
0056: * </repeat>
0057: * </pre>
0058: *
0059: * This can be replaced using one line of code with the data
0060: * values of each record in a recordset, default formats are applied
0061: * unless you specify an output format for a specific field. Default formats
0062: * are configured in WEB.XML, as context parameters (def-format-date and def-format-double).
0063: * <br><br>
0064: * This class also supports two advanced features: Special markers for <b>dynamic labes</b> ${lbl:KeyName} which are replaced
0065: * according to a pre-selected language (ES, EN, IT, etc), this allows multi-language templates, and
0066: * also supports <b>includes</b>, the ability to dynamically include the output of another MVC transaction, which allows
0067: * composition of pages from smaller parts, using a special marker: ${inc:ModuleName}
0068: * <br><br>
0069: * Please consult the How-to documentation to learn all about this class and its
0070: * role inside the framework.
0071: * <br><br>
0072: * Context parameters in WEB.XML used by this class:
0073: * <xmp>
0074: * <context-param>
0075: * <param-name>def-format-date</param-name>
0076: * <param-value>dd-MM-yyyy</param-value>
0077: * <description>default format for dates</description>
0078: * </context-param>
0079: *
0080: * <context-param>
0081: * <param-name>sequence-nextval</param-name>
0082: * <param-value>${seq}.NEXTVAL</param-value>
0083: * <description>SQL expression to obtain the next sequence value - sequence name will be ${seq}</description>
0084: * </context-param>
0085: *
0086: * <context-param>
0087: * <param-name>sequence-currval</param-name>
0088: * <param-value>${seq}.CURRVAL</param-value>
0089: * <description>SQL expression to obtain the current sequence value - sequence name will be ${seq}</description>
0090: * </context-param>
0091: * </xmp>
0092: *
0093: * Creation date: 18/09/2003<br>
0094: * Last Update: 18/09/2003<br>
0095: * (c) 2003 Martin Cordova<br>
0096: * This code is released under the LGPL license<br>
0097: * @author Martin Cordova
0098: */
0099: public class TemplateEngine {
0100:
0101: /** template text */
0102: private String _template = "";
0103:
0104: /** servlet request - used to generate many default values related to the request */
0105: private HttpServletRequest _req = null;
0106:
0107: /** servlet context - used to read configuration from the context */
0108: private ServletContext _ctx = null;
0109:
0110: /** custom defined Locale used for format masks in dates and numbers */
0111: private Locale _locale = null;
0112:
0113: /** callback object that implements the interface IRowEvent */
0114: private IRowEvent _rowEvent = null;
0115:
0116: /** template encoding -if available- */
0117: private String _encoding = null;
0118:
0119: /**
0120: * Set template encoding - for information purporses only
0121: * @param encoding Canonical name of character encoding (ISO-8859-1, etc)
0122: */
0123: public void setEncoding(String encoding) {
0124: _encoding = encoding;
0125: }
0126:
0127: /**
0128: * Set reference to callback object that
0129: * implements the IRowEvent interface
0130: * @param obj Object that implements the IRowEvent interface
0131: */
0132: public void setRowEventObject(IRowEvent obj) {
0133: _rowEvent = obj;
0134: }
0135:
0136: /**
0137: * Set custom LOCALE for formatting
0138: * @param l Locale object
0139: */
0140: public void setLocale(Locale l) throws Throwable {
0141: _locale = l;
0142: replaceLabels();
0143: }
0144:
0145: /**
0146: * Set servlet request reference
0147: * @param req HTTP Servlet Request object
0148: */
0149: public void setRequest(HttpServletRequest req) {
0150: _req = req;
0151: }
0152:
0153: /**
0154: * Servlet oriented constructor
0155: * @param ctx Servlet Context - will be used to read context parameters.
0156: * @param req Servlet Request - will be used to produce default values, like userid or remote_addr.
0157: * @param template Text template with markers
0158: * @throws Throwable
0159: */
0160: public TemplateEngine(ServletContext ctx, HttpServletRequest req,
0161: String template) {
0162: _template = template;
0163: _ctx = ctx;
0164: _req = req;
0165:
0166: //patch june-28-2004 -avoid unnecessary replace calls for repeat sections
0167: try {
0168: replaceDefaultValues();
0169: if (_req != null && _ctx != null) {
0170: replaceSessionAttributes();
0171: replaceRequestAttributes();
0172: }
0173: } catch (Throwable e) {
0174: }
0175:
0176: }
0177:
0178: /**
0179: * Generates SQL using the recordset values from the current record,
0180: * substitutes the field markers with properly formatted values. Saves a lot
0181: * of work with only one line of code. This method is smart enough as to generate
0182: * well formatted values for Strings and portable formats for dates and timestamps,
0183: * using the date/timestamp canonical syntax. Also special characters like single quote (') in
0184: * Strings will be correctly escaped to avoid SQL syntax errors or SQL injection attacks.
0185: * <br><br>
0186: * This object must be created using a SQL template containing markers like ${fld:FieldName}, ${def:date} or ${seq:nextval@SeqName}
0187: * @param rs Recordset containing at least one record, the record position must be valid
0188: * @return Well formatted SQL with all the markers replaced by the corresponding values (only fld, def and seq markers are considered, no format masks allowed)
0189: * @throws Throwable
0190: */
0191: @SuppressWarnings("unchecked")
0192: public String getSql(Recordset rs) throws Throwable {
0193:
0194: try {
0195:
0196: //patch 2007-07-17 replace special marker for security schema ${schema}
0197: //in any SQL template that may contain it
0198: if (_ctx != null) {
0199: if (_template.indexOf("${schema}") > 0) {
0200: String schema = _ctx
0201: .getInitParameter("security-schema");
0202: if (schema == null)
0203: schema = "";
0204: else if (!schema.endsWith(".")
0205: && !schema.equals(""))
0206: schema = schema + ".";
0207: _template = StringUtil.replace(_template,
0208: "${schema}", schema);
0209: }
0210: }
0211: //end patch
0212:
0213: if (rs != null) {
0214: /* get recordset metadata */
0215: HashMap<String, RecordsetField> flds = rs.getFields();
0216:
0217: /* for each field try to replace value */
0218: Iterator i = flds.values().iterator();
0219: while (i.hasNext()) {
0220:
0221: RecordsetField f = (RecordsetField) i.next();
0222: String fname = f.getName();
0223: Object value = rs.getValue(fname);
0224: String marker = "${fld:" + fname + "}";
0225:
0226: if (value == null) {
0227: _template = StringUtil.replace(_template,
0228: marker, "NULL");
0229: } else {
0230: switch (f.getType()) {
0231: case Types.VARCHAR:
0232: case Types.CHAR:
0233: case Types.LONGVARCHAR:
0234: String v = (String) value;
0235: v = StringUtil.replace(v, "'", "''");
0236: _template = StringUtil.replace(_template,
0237: marker, "'" + v + "'");
0238: break;
0239:
0240: case Types.DATE:
0241: java.util.Date d = (java.util.Date) value;
0242: _template = StringUtil.replace(_template,
0243: marker, "{d '"
0244: + StringUtil.formatDate(d,
0245: "yyyy-MM-dd")
0246: + "'}");
0247: break;
0248:
0249: case Types.TIMESTAMP:
0250: java.util.Date d1 = (java.util.Date) value;
0251: _template = StringUtil
0252: .replace(
0253: _template,
0254: marker,
0255: "{ts '"
0256: + StringUtil
0257: .formatDate(
0258: d1,
0259: "yyyy-MM-dd HH:mm:ss:SSS")
0260: + "'}");
0261: break;
0262:
0263: default:
0264: String n = dinamica.StringUtil
0265: .formatNumber(value, "#.######");
0266: n = dinamica.StringUtil
0267: .replace(n, ",", ".");
0268: _template = StringUtil.replace(_template,
0269: marker, n);
0270: break;
0271:
0272: }
0273: }
0274:
0275: }
0276: }
0277:
0278: /* replace default values */
0279: if (_req != null)
0280: replaceDefaultValues();
0281:
0282: /* replace SEQUENCE and request/session markers */
0283: if (_ctx != null && _req != null) {
0284: replaceRequestAttributes();
0285: replaceSessionAttributes();
0286: replaceLabels();
0287:
0288: ArrayList<Marker> seqs = getMarkers("seq");
0289:
0290: /* for each field marker set value */
0291: Iterator<Marker> is = seqs.iterator();
0292: while (is.hasNext()) {
0293: /* get next marker */
0294: Marker m = (Marker) is.next();
0295: String seqType = m.getName(); //sequence mode (nextval|currval)
0296: String seqName = m.getExtraInfo(); //sequence object name
0297: String marker = "${seq:" + seqType + "@" + seqName
0298: + "}";
0299:
0300: /* get sequence configuration from context */
0301: String seqConfigParam = "sequence-" + seqType;
0302: String seqExpr = _ctx
0303: .getInitParameter(seqConfigParam);
0304:
0305: /* throw error if config not found */
0306: if (seqExpr == null || seqExpr.equals("")) {
0307: String args[] = { marker };
0308: String msg = Errors.SEQUENCE_BAD_CONFIGURATION;
0309: msg = MessageFormat
0310: .format(msg, (Object[]) args);
0311: throw new Throwable(msg);
0312: }
0313:
0314: /* replace sequence expression */
0315: String value = "";
0316:
0317: //patch for Resin 3.0.6 - Feb26-2004
0318: if (seqExpr.indexOf("${seq}") < 0)
0319: value = StringUtil.replace(seqExpr, "$[seq]",
0320: seqName);
0321: else
0322: value = StringUtil.replace(seqExpr, "${seq}",
0323: seqName);
0324: //end patch
0325:
0326: _template = StringUtil.replace(_template, marker,
0327: value);
0328:
0329: }
0330: }
0331:
0332: return _template;
0333: } catch (Throwable e) {
0334: String msg = "[TemplateEngine].\n Template:" + _template
0335: + "\n";
0336: String data = "";
0337: if (rs != null) {
0338: data = rs.toString();
0339: System.err.println(msg + data);
0340: }
0341: throw e;
0342: }
0343:
0344: }
0345:
0346: /**
0347: * Replace default values present in the template, default values
0348: * are special markers expressed in the form: ${def:valueName}<br>
0349: * Implemented defaults are:<br>
0350: * <li>${def:user} - HttpServletRequest.getPrincipal().getUserName(). If the user is not authenticated then returns an empty string ""
0351: * <li>${def:date} - yyyy-MM-dd
0352: * <li>${def:time} - HH:mm:ss
0353: * <li>${def:timestamp} - yyyy-MM-dd HH:mm:ss:SSS
0354: * <li>${def:host} - HttpServletRequest.getServerName()
0355: * <li>${def:context} - HttpServletRequest.getContextPath()
0356: * <li>${def:remoteaddr} - HttpServletRequest.getRemoteAddr()
0357: * <li>${def:uri} - HttpServletRequest.getRequestURI()
0358: * <li>${def:dateDMY} - dd-MM-yyyy
0359: * <li>${def:dateMDY} - MM-dd-yyyy
0360: * <li>${def:actionroot} - action parent path
0361: * <li>${def:httpserver} - protocol://hostname:port
0362: * <br>
0363: * All values extracted from HttpServletRequest will be replaced
0364: * by an empty string "" if the request object is null. Use the method
0365: * setRequest() to set the request object or the special constructor
0366: * TemplateEngine(ServletContext ctx, HttpServletRequest req, String t) if you want to set
0367: * the request object.
0368: * <br>
0369: * Default values can be used in every kind of template, SQL, HTML, XML, etc.
0370: */
0371: public void replaceDefaultValues() throws Throwable {
0372:
0373: //patch june-22-2004 don't waste time
0374: if (_template.indexOf("${def:") < 0)
0375: return;
0376:
0377: String markers[] = { "${def:user}", "${def:date}",
0378: "${def:time}", "${def:timestamp}", "${def:host}",
0379: "${def:context}", "${def:remoteaddr}", "${def:uri}",
0380: "${def:dateDMY}", "${def:dateMDY}",
0381: "${def:actionroot}", "${def:httpserver}",
0382: "${def:session}" };
0383:
0384: String values[] = new String[markers.length];
0385:
0386: String userid = null;
0387: if (_req != null)
0388: userid = _req.getRemoteUser();
0389: if (userid == null)
0390: userid = "";
0391:
0392: java.util.Date d = new java.util.Date();
0393: values[0] = userid;
0394: values[1] = StringUtil.formatDate(d, "yyyy-MM-dd");
0395: values[2] = StringUtil.formatDate(d, "HH:mm:ss");
0396: values[3] = StringUtil.formatDate(d, "yyyy-MM-dd HH:mm:ss:SSS");
0397:
0398: if (_req != null)
0399: values[4] = _req.getServerName();
0400: else
0401: values[4] = "";
0402:
0403: if (_req != null)
0404: values[5] = _req.getContextPath();
0405: else
0406: values[5] = "";
0407:
0408: if (_req != null)
0409: values[6] = _req.getRemoteAddr();
0410: else
0411: values[6] = "";
0412:
0413: if (_req != null)
0414: values[7] = _req.getRequestURI();
0415: else
0416: values[7] = "";
0417:
0418: values[8] = StringUtil.formatDate(d, "dd-MM-yyyy");
0419: values[9] = StringUtil.formatDate(d, "MM-dd-yyyy");
0420:
0421: if (_req != null) {
0422: String path = (String) _req
0423: .getAttribute("dinamica.action.path");
0424: path = path.substring(0, path.lastIndexOf("/"));
0425: values[10] = path;
0426: } else
0427: values[10] = "";
0428:
0429: if (_req != null) {
0430: String http = "http://";
0431: if (_req.isSecure())
0432: http = "https://";
0433: http = http + _req.getServerName() + ":"
0434: + _req.getServerPort();
0435: values[11] = http;
0436: } else
0437: values[11] = "";
0438:
0439: if (_req != null) {
0440: values[12] = _req.getSession(true).getId();
0441: } else
0442: values[12] = "";
0443:
0444: for (int i = 0; i < markers.length; i++) {
0445: _template = StringUtil.replace(_template, markers[i],
0446: values[i]);
0447: }
0448:
0449: }
0450:
0451: /**
0452: * Return current state of the internal template
0453: */
0454: public String toString() {
0455: return _template;
0456: }
0457:
0458: /**
0459: * Returns a list of markers of a given type
0460: * @param prefix The type of marker (fld, lbl, inc, seq)
0461: * @return ArrayList containing Marker objects
0462: * @throws Throwable
0463: */
0464: public ArrayList<Marker> getMarkers(String prefix) throws Throwable {
0465:
0466: /* test precondition */
0467: if (prefix.length() != 3) {
0468: String args[] = { prefix };
0469: String msg = Errors.INVALID_PREFIX;
0470: msg = MessageFormat.format(msg, (Object[]) args);
0471: throw new Throwable(msg);
0472: }
0473:
0474: int pos = 0;
0475: ArrayList<Marker> l = new ArrayList<Marker>();
0476:
0477: /* search markers */
0478: while (pos >= 0) {
0479: int pos1 = 0;
0480: int pos2 = 0;
0481: int newPos = 0;
0482:
0483: /* find start of marker */
0484: pos1 = _template.indexOf("${" + prefix + ":", pos);
0485: if (pos1 >= 0) {
0486:
0487: /* find end of marker */
0488: newPos = pos1 + 6;
0489: pos2 = _template.indexOf("}", newPos);
0490:
0491: if (pos2 > 0) {
0492:
0493: /* get marker string */
0494: String fld = _template.substring(newPos, pos2);
0495: Marker m = new Marker(fld, null, pos1, pos2);
0496:
0497: /* search for etra attribute (format or sequence name) */
0498: int pos3 = fld.indexOf("@");
0499: if (pos3 > 0) {
0500:
0501: String name = fld.substring(0, pos3);
0502: String extraInfo = fld.substring(pos3 + 1, fld
0503: .length());
0504:
0505: if ((name.indexOf(" ") >= 0)
0506: || (name.indexOf("\r") >= 0)
0507: || (name.indexOf("\n") >= 0)
0508: || (name.indexOf('\t') >= 0)) {
0509: String args[] = { name };
0510: String msg = Errors.INVALID_MARKER;
0511: msg = MessageFormat.format(msg,
0512: (Object[]) args);
0513: throw new Throwable(msg);
0514: }
0515:
0516: m.setName(name);
0517: m.setExtraInfo(extraInfo);
0518: }
0519:
0520: l.add(m);
0521: } else {
0522: throw new Throwable(Errors.MARKER_UNCLOSED);
0523: }
0524: pos = pos2 + 1;
0525: } else {
0526: pos = -1;
0527: }
0528: }
0529:
0530: return l;
0531:
0532: }
0533:
0534: /**
0535: * Replace all possible field markers with corresponding
0536: * recordset field values from current record. This method
0537: * is mainly used to populate forms
0538: * @param rs Recordset with a valid record position
0539: * @param nullValueExpr The string to represent null values ("" or &nbsp;)
0540: * @throws Throwable
0541: */
0542: public void replace(Recordset rs, String nullValueExpr)
0543: throws Throwable {
0544:
0545: /* parse the template and create list of field markers */
0546: ArrayList<Marker> flds = getMarkers("fld");
0547:
0548: /* call internal replace method */
0549: replace(rs, nullValueExpr, flds);
0550:
0551: }
0552:
0553: /**
0554: * Navigate all the recordset and replace the
0555: * values of each record. This method is used to produce
0556: * tables or fill controls like ComboBoxes or ListBoxes. It is
0557: * suitable for any section of the template that must be repeated
0558: * as many times as records are in the recordset
0559: *
0560: * @param rs Recordset
0561: * @param nullValueExpr The string to represent null values ("" or &nbsp;) - when generating html tables it should be &nbsp;
0562: * @param RepeatSectionTag A custom tag that encloses the repeatable section
0563: * @throws Throwable
0564: */
0565: @SuppressWarnings("unchecked")
0566: public void replace(Recordset rs, String nullValueExpr,
0567: String repeatSectionTag) throws Throwable {
0568:
0569: dinamica.parser.FastTemplateEngine fte = new dinamica.parser.FastTemplateEngine();
0570:
0571: String section = null;
0572: String repeatTemplate = null;
0573: int pos1 = 0;
0574: int pos2 = 0;
0575:
0576: String tagStart = "<" + repeatSectionTag + ">";
0577: String tagEnd = "</" + repeatSectionTag + ">";
0578:
0579: /* find start of repeat section */
0580: pos1 = _template.indexOf(tagStart);
0581: if (pos1 >= 0) {
0582:
0583: /* find end of repeat section */
0584: int newPos = pos1 + tagStart.length();
0585: pos2 = _template.indexOf(tagEnd, newPos);
0586:
0587: if (pos2 > 0) {
0588: /* get section string */
0589: section = _template.substring(pos1, pos2
0590: + tagEnd.length());
0591: repeatTemplate = _template.substring(newPos, pos2);
0592:
0593: /* buffer to contain generated text */
0594: StringBuilder buf = new StringBuilder();
0595:
0596: /* navigate all recordset */
0597: if (rs.getRecordCount() > 0) {
0598:
0599: // fast template engine
0600: fte.setTemplate(repeatTemplate);
0601: ArrayList<dinamica.parser.Marker> markers = fte
0602: .getMarkers();
0603:
0604: // rewind recordset
0605: rs.top();
0606:
0607: /* for each record */
0608: while (rs.next()) {
0609:
0610: setValues(fte, markers, rs, nullValueExpr);
0611:
0612: // get row
0613: String row = fte.toString();
0614:
0615: /* row event available? */
0616: if (_rowEvent != null) {
0617: row = _rowEvent.onNewRow(rs, row);
0618: }
0619:
0620: // append text
0621: buf.append(row);
0622:
0623: }
0624: _template = StringUtil.replace(_template, section,
0625: buf.toString());
0626: } else {
0627: _template = StringUtil.replace(_template, section,
0628: "");
0629: }
0630:
0631: } else {
0632: String args[] = { repeatSectionTag };
0633: String msg = Errors.REPEAT_TAG_NOT_CLOSED;
0634: msg = MessageFormat.format(msg, (Object[]) args);
0635: throw new Throwable(msg);
0636: }
0637:
0638: } else {
0639: String args[] = { repeatSectionTag };
0640: String msg = Errors.REPEAT_TAG_NOT_FOUND;
0641: msg = MessageFormat.format(msg, (Object[]) args);
0642: throw new Throwable(msg);
0643: }
0644:
0645: }
0646:
0647: /**
0648: * Internal method that factorizes most of the code
0649: * that is common to all the overloaders
0650: * @param rs Recordset
0651: * @param nullValueExpr The string to represent null values ("" or &nbsp;) - when generating html tables it should be &nbsp;
0652: * @param markers ArrayList containing the field markers
0653: * @throws Throwable
0654: */
0655: @SuppressWarnings("unchecked")
0656: void replace(Recordset rs, String nullValueExpr, ArrayList markers)
0657: throws Throwable {
0658:
0659: String strValue = null;
0660: Object value = null;
0661: String toReplace = null;
0662:
0663: /* get recordset fields */
0664: HashMap rsFlds = rs.getFields();
0665:
0666: /* parse the template and create list of field markers */
0667: ArrayList flds = markers;
0668:
0669: /* for each field marker set value */
0670: Iterator i = flds.iterator();
0671: while (i.hasNext()) {
0672:
0673: String formatPluginName = null;
0674: IFormatPlugin fmtObj = null;
0675: FormatPluginParser fpp = null;
0676:
0677: /* get next marker */
0678: Marker m = (Marker) i.next();
0679: String fName = m.getName();
0680: String fFormat = m.getExtraInfo();
0681:
0682: /* determine if it is an special field (rowNumber/rowIndex) otherwise if the field exists */
0683: boolean found = true;
0684: boolean fakeField = false;
0685: if (!fName.equals("_rowNumber")
0686: && !fName.equals("_rowIndex")) {
0687: if (!rsFlds.containsKey(fName))
0688: found = false;
0689: } else {
0690: fakeField = true;
0691: }
0692:
0693: /* recordset contains this field? */
0694: if (found) {
0695:
0696: String defDateFmt = null;
0697:
0698: /* read default date format */
0699: if (_ctx != null)
0700: defDateFmt = _ctx
0701: .getInitParameter("def-format-date");
0702:
0703: if (defDateFmt == null || defDateFmt.equals(""))
0704: defDateFmt = "dd-MM-yyyy";
0705:
0706: /* rebuild marker to replace by searched and replaced by corresponding value */
0707: if (fFormat != null) {
0708: toReplace = "${fld:" + fName + "@" + fFormat + "}";
0709:
0710: //PATCH 2005-05-23 - get plugin name if available
0711: if (fFormat.startsWith("class:")) {
0712: formatPluginName = fFormat.substring(6);
0713: fpp = new FormatPluginParser(formatPluginName);
0714: fmtObj = (IFormatPlugin) Thread.currentThread()
0715: .getContextClassLoader().loadClass(
0716: fpp.getName()).newInstance();
0717: }
0718: } else
0719: toReplace = "${fld:" + fName + "}";
0720:
0721: /* get field value */
0722: value = rs.getValue(fName);
0723:
0724: //custom format??
0725: if (fmtObj != null) {
0726: strValue = fmtObj.format(fName, rs, _locale, fpp
0727: .getArgs());
0728: } else {
0729:
0730: /* apply appropiate null representation */
0731: if (value == null) {
0732: strValue = nullValueExpr;
0733: } else {
0734:
0735: /* get field info */
0736: RecordsetField f = null;
0737: if (!fakeField)
0738: f = (RecordsetField) rsFlds.get(fName);
0739: else
0740: f = new RecordsetField(fName, "INTEGER",
0741: Types.INTEGER);
0742:
0743: /* format value according to data type*/
0744: if (f.getType() != Types.DATE
0745: && f.getType() != Types.TIMESTAMP) {
0746: /* format defined? */
0747: if (fFormat == null) {
0748: strValue = String.valueOf(value);
0749: } else {
0750: //is a string data type?
0751: if (f.getType() == Types.VARCHAR
0752: || f.getType() == Types.CHAR
0753: || f.getType() == Types.CLOB
0754: || f.getType() == Types.LONGVARCHAR) {
0755: if (fFormat.equals("xml")) {
0756: //encode special characters for xml/html output
0757: strValue = encodeXML((String) value);
0758: } else if (fFormat.equals("html")) {
0759: //encode special characters for xml/html output
0760: strValue = encodeHTML((String) value);
0761: } else if (fFormat.equals("url")) {
0762: //encode special characters for xml/html output
0763: strValue = URLEncoder
0764: .encode((String) value,
0765: "UTF-8");
0766: } else if (fFormat.equals("js")) {
0767: //encode special characters for xml/html output
0768: strValue = encodeJS((String) value);
0769: } else {
0770: throw new Throwable(
0771: "Invalid format mask for the field:"
0772: + fName);
0773: }
0774:
0775: }
0776: // it is a numeric data type
0777: else {
0778:
0779: if (_locale == null)
0780: strValue = StringUtil
0781: .formatNumber(value,
0782: fFormat);
0783: else
0784: strValue = StringUtil
0785: .formatNumber(value,
0786: fFormat,
0787: _locale);
0788:
0789: }
0790: }
0791: } else {
0792: /* apply default or custom date format? */
0793: if (fFormat == null)
0794: fFormat = defDateFmt;
0795: if (_locale == null)
0796: strValue = StringUtil
0797: .formatDate(
0798: (java.util.Date) value,
0799: fFormat);
0800: else
0801: strValue = StringUtil.formatDate(
0802: (java.util.Date) value,
0803: fFormat, _locale);
0804: }
0805:
0806: }
0807:
0808: }
0809:
0810: /* replace marker with value */
0811: _template = StringUtil.replace(_template, toReplace,
0812: strValue);
0813:
0814: }
0815:
0816: }
0817:
0818: }
0819:
0820: /**
0821: * Replace dynamic labels ${lbl:Name} using the
0822: * session locale, which must be indicated using the method
0823: * setLocale, otherwise the language code
0824: * specified in the context-attribute <b>def-language</b> in WEB.XML will be used
0825: * to select the default locale. If none has been properly configured
0826: * or the label does not exist for the selected Locale, then
0827: * an exception will be triggered. This mechanism uses the label
0828: * configuration file stored in /WEB-INF/labels.xml. Please check
0829: * the documentation to learn the structure of this file.
0830: * Place the appropiate markers and make sure the labels.xml file is properly configured.
0831: * In order to use this method, the TemplateEngine must have access to
0832: * the ServletContext, so use the appropiate constructor. If there is no
0833: * valid reference to the ServletContext, then this method will trigger
0834: * an error
0835: * @throws Throwable
0836: */
0837: public void replaceLabels() throws Throwable {
0838:
0839: if (_ctx == null)
0840: throw new Throwable(
0841: "Servlet Context is null - this method can't work without a ServletContext.");
0842:
0843: //patch june-22-2004 don't waste time
0844: if (_template.indexOf("${lbl:") < 0)
0845: return;
0846:
0847: /* parse the template and create list of label markers */
0848: ArrayList<Marker> flds = getMarkers("lbl");
0849:
0850: /* load labels.xml file */
0851: String xmlData = StringUtil.getResource(_ctx,
0852: "/WEB-INF/labels.xml");
0853:
0854: /* parse document */
0855: Document doc = new Document(xmlData);
0856: Element root = doc.getRoot();
0857:
0858: /* identify locale to be used */
0859: String language = null;
0860: if (_locale == null)
0861: language = _ctx.getInitParameter("def-language");
0862: else
0863: language = _locale.getLanguage();
0864:
0865: if (language == null || language.equals(""))
0866: throw new Throwable(
0867: "Language not defined (Locale or default language may be null)");
0868:
0869: for (int i = 0; i < flds.size(); i++) {
0870: Marker m = (Marker) flds.get(i);
0871: String name = m.getName();
0872: String label = "${" + "lbl:" + name + "}";
0873: _template = StringUtil.replace(_template, label, getLabel(
0874: name, root, language));
0875: }
0876:
0877: }
0878:
0879: /**
0880: * Lookup a dynamic label value given the language code and the label ID
0881: * @param labelName Label ID
0882: * @param root The root element of the labels.xml tree
0883: * @param language The ISO language code (es, en, it, etc.)
0884: * @return The label translation
0885: * @throws Throwable if the label ID or the language code are not valid
0886: */
0887: String getLabel(String labelName, Element root, String language)
0888: throws Throwable {
0889:
0890: /* find label */
0891: Element label = root.getElement(new XPath("label[@id='"
0892: + labelName + "']"));
0893: if (label == null)
0894: throw new Throwable("Label not found: " + labelName);
0895:
0896: /* find translation for language code */
0897: Element translation = label.getElement(new XPath(
0898: "value[@language='" + language + "']"));
0899: if (translation == null)
0900: throw new Throwable(
0901: "Label translation not found for this language code: "
0902: + language);
0903:
0904: return translation.getString();
0905:
0906: }
0907:
0908: /**
0909: * Split template into segments stored into an array.
0910: * A segment may be a printable text or an INCLUDE directive
0911: * to include the content of another resource from the same context
0912: * @return ArrayList containing TemplateSegment objects
0913: * @throws Throwable
0914: */
0915: ArrayList<TemplateSegment> getSegments() throws Throwable {
0916:
0917: ArrayList<TemplateSegment> s = new ArrayList<TemplateSegment>();
0918:
0919: /* get include markers */
0920: ArrayList<Marker> l = getMarkers("inc");
0921:
0922: if (l.size() > 0) {
0923: int lastPos = 0;
0924: for (int i = 0; i < l.size(); i++) {
0925: /* create segment */
0926: Marker m = (Marker) l.get(i);
0927: TemplateSegment seg1 = new TemplateSegment();
0928: seg1.segmentType = "data";
0929: seg1.segmentData = _template.substring(lastPos, m
0930: .getPos1());
0931:
0932: TemplateSegment seg2 = new TemplateSegment();
0933: seg2.segmentType = "inc";
0934: seg2.segmentData = m.getName();
0935:
0936: lastPos = m.getPos2() + 1;
0937:
0938: s.add(seg1);
0939: s.add(seg2);
0940:
0941: }
0942: TemplateSegment seg1 = new TemplateSegment();
0943: seg1.segmentType = "data";
0944: seg1.segmentData = _template.substring(lastPos);
0945: s.add(seg1);
0946:
0947: } else {
0948:
0949: TemplateSegment seg = new TemplateSegment();
0950: seg.segmentType = "data";
0951: seg.segmentData = _template;
0952: s.add(seg);
0953: }
0954:
0955: return s;
0956: }
0957:
0958: /**
0959: * Print template and process any INCLUDE directive
0960: * present into the template; in order to do this the
0961: * class required a reference to the ServletContext, Request
0962: * and Response, otherwise it can't dispatch to include another servlets.
0963: * No writer must be obtained from the Servlet response object
0964: * prior to calling this method, because this method will call getWriter() from
0965: * the passed Response object.<br>
0966: * <br>
0967: * <b>NOTE:</b> default values and dynamic labels will be automatically
0968: * replaced by this method. This is the preferred way to print a template
0969: * to make sure everything is being replaced, the caller is only responsable
0970: * for setting the appropiate response headers.
0971: * @param res HttpServletResponse
0972: * @throws Throwable if the ServletContext reference is null
0973: */
0974: public void print(HttpServletResponse res) throws Throwable {
0975:
0976: if (_ctx == null)
0977: throw new Throwable(
0978: "ServletContext is null - can't print template because the request dispatcher must be obtained from the ServletContext.");
0979:
0980: replaceDefaultValues();
0981: replaceLabels();
0982: replaceRequestAttributes();
0983: replaceSessionAttributes();
0984:
0985: PrintWriter pw = res.getWriter();
0986:
0987: // patch 28-june-2004 - set content length if no includes are used in this template
0988: if (_template.indexOf("${inc:") >= 0) {
0989: ArrayList<TemplateSegment> s = getSegments();
0990: for (int i = 0; i < s.size(); i++) {
0991: TemplateSegment t = (TemplateSegment) s.get(i);
0992: if (t.segmentType.equals("inc")) {
0993: try {
0994: RequestDispatcher rd = _ctx
0995: .getRequestDispatcher(t.segmentData);
0996: rd.include(_req, res);
0997: } catch (Throwable e) {
0998: String msg = "INCLUDE Error (" + t.segmentData
0999: + ") - " + e.getMessage();
1000: throw new Throwable(msg);
1001: }
1002: } else {
1003: pw.print(t.segmentData);
1004: }
1005: }
1006: } else {
1007: //PATCH 2005-02-23 - encoding support
1008: byte body[] = null;
1009: if (_encoding != null)
1010: body = _template.getBytes(_encoding);
1011: else
1012: body = _template.getBytes();
1013:
1014: res.setContentLength(body.length);
1015: pw.print(_template);
1016: }
1017:
1018: }
1019:
1020: /**
1021: * HTML Control utility method.<br>
1022: * Select combobox item for single select combobox. This method
1023: * will insert the word "selected" in the appropiate option element
1024: * from the corresponding select html control.<br>
1025: * <b>NOTE:</b> All html keywords related to the control must be
1026: * in lower case, including attributes and tag names. Name and Value lookup
1027: * is case sensitive!
1028: * @param controlName HTML control name attribute
1029: * @param value Option value to search for
1030: * @throws Throwable if can't find control or its closing tag
1031: */
1032: public void setComboValue(String controlName, String value)
1033: throws Throwable {
1034: int pos1 = 0;
1035: int pos2 = 0;
1036: String combo = "";
1037:
1038: /* define control to find */
1039: String find = "<select name=\"" + controlName + "\"";
1040:
1041: /* find it */
1042: pos1 = _template.indexOf(find);
1043:
1044: /* found? */
1045: if (pos1 >= 0) {
1046: /* extract segment from template */
1047: pos2 = _template.indexOf("</select>", pos1);
1048: if (pos2 > 0) {
1049:
1050: /* extract */
1051: int newpos2 = pos2 + "</select>".length();
1052: combo = _template.substring(pos1, newpos2);
1053:
1054: /* set item=selected if found */
1055: find = "<option value=\"" + value + "\"";
1056: String newItem = find + " selected ";
1057: String temp = StringUtil.replace(combo, find, newItem);
1058:
1059: /* replace into template */
1060: _template = StringUtil.replace(_template, combo, temp);
1061:
1062: } else {
1063: throw new Throwable(
1064: "Can't find closing tag for this HTML control: "
1065: + controlName);
1066: }
1067: } else {
1068: throw new Throwable("HTML control not found: "
1069: + controlName);
1070: }
1071:
1072: }
1073:
1074: /**
1075: * HTML Control utility method.<br>
1076: * Set combobox values for multiple items using a recordset
1077: * to lookup the values from a field and set the corresponding option items
1078: * in the select control. All records from the recordset are used.
1079: * @param controlName Name of the html select control which is also the name
1080: * of the field name to use from the Recordset
1081: * @param rs
1082: * @throws Throwable
1083: */
1084: public void setComboValue(String controlName, Recordset rs)
1085: throws Throwable {
1086: if (rs.getRecordCount() == 0)
1087: return;
1088:
1089: rs.top();
1090: while (rs.next()) {
1091: /* reuse setComboValue method */
1092: String value = String.valueOf(rs.getValue(controlName));
1093: setComboValue(controlName, value);
1094: }
1095: }
1096:
1097: /**
1098: * HTML Control utility method.<br>
1099: * Select RadioButton control from a group of controls
1100: * using a value to match the appropiate control
1101: * <b>NOTE:</b> All html keywords related to the control must be
1102: * in lower case, including attributes and tag names. Name and Value lookup
1103: * is case sensitive!
1104: * @param controlName HTML control name attribute
1105: * @param value Value to search for
1106: * @throws Throwable if can't find control
1107: */
1108: public void setRadioButton(String controlName, String value)
1109: throws Throwable {
1110:
1111: int pos1 = 0;
1112: int pos2 = 0;
1113: String ctrl = "";
1114: int flag = 0;
1115: int pos = 0;
1116:
1117: while (flag >= 0) {
1118:
1119: /* define control to find */
1120: String find = "<input";
1121:
1122: /* find it */
1123: pos1 = _template.indexOf(find, pos);
1124:
1125: /* found? */
1126: if (pos1 >= 0) {
1127: flag = 1;
1128:
1129: /* extract segment from template */
1130: pos2 = _template.indexOf(">", pos1);
1131: if (pos2 > 0) {
1132:
1133: /* extract */
1134: int newpos2 = pos2 + ">".length();
1135: ctrl = _template.substring(pos1, newpos2);
1136:
1137: /* check to see if this is the requested control */
1138: find = "name=\"" + controlName + "\"";
1139: int newpos1 = ctrl.indexOf(find);
1140:
1141: /* found? this is one of the requested controls! */
1142: if (newpos1 >= 0) {
1143:
1144: /* mark this control as "selected" if its value match */
1145: find = "value=\"" + value + "\"";
1146: String newItem = find + " checked ";
1147: String temp = StringUtil.replace(ctrl, find,
1148: newItem);
1149:
1150: /* replace into template */
1151: if (!temp.equals(ctrl)) {
1152: _template = StringUtil.replace(_template,
1153: ctrl, temp);
1154: return;
1155: } else {
1156: ctrl = temp;
1157: }
1158:
1159: }
1160: pos = pos1 + ctrl.length();
1161: } else {
1162: throw new Throwable(
1163: "'input' Tag is not properly closed for this HTML control: "
1164: + controlName);
1165: }
1166: } else {
1167: flag = -1;
1168: }
1169:
1170: }
1171:
1172: }
1173:
1174: /**
1175: * HTML Control utility method.<br>
1176: * Set checkbox values for multiple items using a recordset
1177: * to lookup the values from a field and set the corresponding checkbox items
1178: * in the checkbox control group. All records from the recordset are used.
1179: * @param controlName Name of the html select control which is also the name
1180: * of the field name to use from the Recordset
1181: * @param rs
1182: * @throws Throwable
1183: */
1184: public void setCheckbox(String controlName, Recordset rs)
1185: throws Throwable {
1186: if (rs.getRecordCount() == 0)
1187: return;
1188:
1189: rs.top();
1190: while (rs.next()) {
1191: /* reuse setRadioButton method */
1192: String value = String.valueOf(rs.getValue(controlName));
1193: setRadioButton(controlName, value);
1194: }
1195: }
1196:
1197: /**
1198: * Change template body - if necessary, should be called before replacing any data
1199: * because all previous changes will be lost
1200: * @param string New template body
1201: */
1202: public void setTemplate(String string) {
1203: _template = string;
1204: }
1205:
1206: /**
1207: * Returns a Tag body, including the tag markers.
1208: * This is a utility method that may be used by special
1209: * Output modules, like Master/Detail reports that need
1210: * to extract and later replace subsections of a template.
1211: * @param tagName
1212: * @return
1213: * @throws Throwable if tagName is not present in the template
1214: */
1215: public String getTagBody(String tagName) throws Throwable {
1216:
1217: int pos1 = 0;
1218: int pos2 = 0;
1219:
1220: String tagStart = "<" + tagName + ">";
1221: String tagEnd = "</" + tagName + ">";
1222:
1223: /* find start of repeat section */
1224: pos1 = _template.indexOf(tagStart);
1225: if (pos1 >= 0) {
1226:
1227: /* find end of repeat section */
1228: int newPos = pos1 + tagStart.length();
1229: pos2 = _template.indexOf(tagEnd, newPos);
1230:
1231: if (pos2 > 0) {
1232: /* extract tag body */
1233: return _template
1234: .substring(pos1, pos2 + tagEnd.length());
1235: } else {
1236: String args[] = { tagName };
1237: String msg = Errors.REPEAT_TAG_NOT_CLOSED;
1238: msg = MessageFormat.format(msg, (Object[]) args);
1239: throw new Throwable(msg);
1240: }
1241:
1242: } else {
1243: String args[] = { tagName };
1244: String msg = Errors.REPEAT_TAG_NOT_FOUND;
1245: msg = MessageFormat.format(msg, (Object[]) args);
1246: throw new Throwable(msg);
1247: }
1248:
1249: }
1250:
1251: /**
1252: * Replace field markers representing request attribute values
1253: * like ${req:attributeID}
1254: * @throws Throwable
1255: */
1256: public void replaceRequestAttributes() throws Throwable {
1257: if (_ctx == null)
1258: throw new Throwable(
1259: "Servlet Context is null - this method can't work without a ServletContext.");
1260:
1261: //patch june-22-2004 don't waste time
1262: if (_template.indexOf("${req:") < 0)
1263: return;
1264:
1265: /* parse the template and create list of label markers */
1266: ArrayList<Marker> flds = getMarkers("req");
1267:
1268: for (int i = 0; i < flds.size(); i++) {
1269: Marker m = (Marker) flds.get(i);
1270:
1271: String name = m.getName();
1272:
1273: //PATCH 2005-04-15 - support for XML/URL encoding
1274: String fmt = m.getExtraInfo();
1275: if (fmt == null)
1276: fmt = "";
1277: else
1278: fmt = "@" + fmt;
1279: String label = "${" + "req:" + name + fmt + "}";
1280:
1281: /* PATCH 2004-12-06 - request markers were
1282: * being eliminated if request attribute was null, creating
1283: * problems for custom output modules that set request attributes
1284: */
1285: String value = (String) _req.getAttribute(name);
1286: if (value != null) {
1287: if (!fmt.equals("")) {
1288: if (fmt.equals("@xml"))
1289: value = encodeXML(value);
1290: else if (fmt.equals("@html"))
1291: value = encodeHTML(value);
1292: else if (fmt.equals("@js"))
1293: value = encodeJS(value);
1294: else if (fmt.equals("@url"))
1295: value = URLEncoder.encode(value, "UTF-8");
1296: else
1297: throw new Throwable(
1298: "Invalid encoding directive for request attribute: "
1299: + name);
1300: }
1301: _template = StringUtil.replace(_template, label, value);
1302: }
1303: }
1304: }
1305:
1306: /**
1307: * Replace field markers representing session attribute values
1308: * like ${ses:attributeID}
1309: * @throws Throwable
1310: */
1311: public void replaceSessionAttributes() throws Throwable {
1312:
1313: if (_req == null)
1314: throw new Throwable(
1315: "Request is null - this method can't work without a Request object.");
1316:
1317: //patch june-22-2004 don't waste time
1318: if (_template.indexOf("${ses:") < 0)
1319: return;
1320:
1321: //get session object
1322: HttpSession session = _req.getSession(true);
1323:
1324: /* parse the template and create list of label markers */
1325: ArrayList<Marker> flds = getMarkers("ses");
1326:
1327: for (int i = 0; i < flds.size(); i++) {
1328: Marker m = (Marker) flds.get(i);
1329: String name = m.getName();
1330:
1331: // PATCH 2005-05-25 - test existence of session attribute
1332: Object obj = session.getAttribute(name);
1333:
1334: if (obj == null)
1335: throw new Throwable("Cannot find Session attribute ["
1336: + name + "]; UserID: " + _req.getRemoteUser()
1337: + "; Session isNew: " + session.isNew() + "; ");
1338:
1339: //patch 2005-06-09 - avoid errors if attribute type is not String
1340: String value = String.valueOf(obj);
1341: String label = "${" + "ses:" + name + "}";
1342: _template = StringUtil.replace(_template, label, value);
1343:
1344: }
1345:
1346: }
1347:
1348: /**
1349: * Encode reserved xml characters (&,<,>,',").<br>
1350: * This characters will be replaced by the pre-defined entities.
1351: * @param input String that will be processed
1352: * @return String with all reserved characters replaced
1353: */
1354: public String encodeXML(String input) {
1355: input = StringUtil.replace(input, "&", "&");
1356: input = StringUtil.replace(input, "<", "<");
1357: input = StringUtil.replace(input, ">", ">");
1358: input = StringUtil.replace(input, "'", "'");
1359: input = StringUtil.replace(input, "\"", """);
1360:
1361: return input;
1362: }
1363:
1364: /**
1365: * Encode reserved html characters (&,<,>,',").<br>
1366: * This characters will be replaced by the pre-defined entities.
1367: * @param input String that will be processed
1368: * @return String with all reserved characters replaced
1369: */
1370: public String encodeHTML(String input) {
1371: input = StringUtil.replace(input, "&", "&");
1372: input = StringUtil.replace(input, "<", "<");
1373: input = StringUtil.replace(input, ">", ">");
1374: input = StringUtil.replace(input, "\"", """);
1375:
1376: return input;
1377: }
1378:
1379: /**
1380: * Encode reserved javascript characters (\,").<br>
1381: * This characters will be replaced by the pre-defined entities.
1382: * @param input String that will be processed
1383: * @return String with all reserved characters replaced
1384: */
1385: public String encodeJS(String input) {
1386: input = StringUtil.replace(input, "\\", "\\\\");
1387: input = StringUtil.replace(input, "\"", "\\\"");
1388: input = StringUtil.replace(input, "\r\n", "\\r\\n");
1389:
1390: return input;
1391: }
1392:
1393: /**
1394: * Replace any text into the template
1395: * @param toReplace Text to be replaced
1396: * @param newValue New value
1397: */
1398: public void replace(String toReplace, String newValue) {
1399: _template = StringUtil.replace(_template, toReplace, newValue);
1400: }
1401:
1402: /**
1403: * Set corresponding column values for template markers<br>
1404: * Patch june-28-2004 - Fast parser technique
1405: * @param fte Fast template parser (new fck package)
1406: * @param rs Recordset positioned on a valid record
1407: * @param nullExpression String used to represent a null value
1408: * @throws Throwable
1409: */
1410: @SuppressWarnings("unchecked")
1411: public void setValues(dinamica.parser.FastTemplateEngine fte,
1412: ArrayList<dinamica.parser.Marker> markers, Recordset rs,
1413: String nullExpression) throws Throwable {
1414:
1415: String strValue = null;
1416: Object value = null;
1417:
1418: /* read default date format */
1419: String defDateFmt = null;
1420: if (_ctx != null)
1421: defDateFmt = _ctx.getInitParameter("def-format-date");
1422: if (defDateFmt == null || defDateFmt.equals(""))
1423: defDateFmt = "dd-MM-yyyy";
1424:
1425: /* get recordset fields */
1426: HashMap<String, RecordsetField> rsFlds = rs.getFields();
1427:
1428: /* parse the template and create list of field markers */
1429: ArrayList<dinamica.parser.Marker> flds = markers;
1430:
1431: /* for each field marker set value */
1432: Iterator<dinamica.parser.Marker> i = flds.iterator();
1433: while (i.hasNext()) {
1434:
1435: String formatPluginName = null;
1436: IFormatPlugin fmtObj = null;
1437: FormatPluginParser fpp = null;
1438:
1439: /* get next marker */
1440: dinamica.parser.Marker m = (dinamica.parser.Marker) i
1441: .next();
1442: String fName = m.getColumnName();
1443: String fFormat = m.getFormat();
1444:
1445: /* determine if it is an special field (rowNumber/rowIndex) otherwise if the field exists */
1446: boolean found = true;
1447: boolean fakeField = false;
1448: if (!fName.equals("_rowNumber")
1449: && !fName.equals("_rowIndex")) {
1450: if (!rsFlds.containsKey(fName))
1451: found = false;
1452: } else {
1453: fakeField = true;
1454: }
1455:
1456: /* recordset contains this field? */
1457: if (found) {
1458:
1459: if (fFormat != null) {
1460: //PATCH 2005-05-23 - get plugin name if available
1461: if (fFormat.startsWith("class:")) {
1462: formatPluginName = fFormat.substring(6);
1463: fpp = new FormatPluginParser(formatPluginName);
1464: fmtObj = (IFormatPlugin) Thread.currentThread()
1465: .getContextClassLoader().loadClass(
1466: fpp.getName()).newInstance();
1467: }
1468: }
1469:
1470: /* get field value */
1471: value = rs.getValue(fName);
1472:
1473: if (fmtObj != null) {
1474: strValue = fmtObj.format(fName, rs, _locale, fpp
1475: .getArgs());
1476: } else {
1477: /* apply appropiate null representation */
1478: if (value == null) {
1479: strValue = nullExpression;
1480: } else {
1481:
1482: /* get field info */
1483: RecordsetField f = null;
1484: if (!fakeField)
1485: f = (RecordsetField) rsFlds.get(fName);
1486: else
1487: f = new RecordsetField(fName, "INTEGER",
1488: Types.INTEGER);
1489:
1490: /* format value according to data type*/
1491: if (f.getType() != Types.DATE
1492: && f.getType() != Types.TIMESTAMP) {
1493: /* format defined? */
1494: if (fFormat == null) {
1495: strValue = String.valueOf(value);
1496: } else {
1497: //is a string data type?
1498: if (f.getType() == Types.VARCHAR
1499: || f.getType() == Types.CHAR
1500: || f.getType() == Types.CLOB) {
1501: if (fFormat.equals("xml")) {
1502: //encode special characters for xml/html output
1503: strValue = encodeXML((String) value);
1504: } else if (fFormat.equals("html")) {
1505: //encode special characters for xml/html output
1506: strValue = encodeHTML((String) value);
1507: } else if (fFormat.equals("url")) {
1508: //encode special characters for xml/html output
1509: strValue = URLEncoder
1510: .encode((String) value,
1511: "UTF-8");
1512: } else {
1513: throw new Throwable(
1514: "Invalid format mask for the field:"
1515: + fName);
1516: }
1517:
1518: }
1519: // it is a numeric data type
1520: else {
1521:
1522: if (_locale == null)
1523: strValue = StringUtil
1524: .formatNumber(value,
1525: fFormat);
1526: else
1527: strValue = StringUtil
1528: .formatNumber(value,
1529: fFormat,
1530: _locale);
1531:
1532: }
1533: }
1534: } else {
1535: /* apply default or custom date format? */
1536: if (fFormat == null)
1537: fFormat = defDateFmt;
1538: if (_locale == null)
1539: strValue = StringUtil
1540: .formatDate(
1541: (java.util.Date) value,
1542: fFormat);
1543: else
1544: strValue = StringUtil.formatDate(
1545: (java.util.Date) value,
1546: fFormat, _locale);
1547: }
1548:
1549: }
1550:
1551: }
1552:
1553: // set value
1554: fte.setValue(m.getKey(), strValue);
1555:
1556: }
1557:
1558: }
1559:
1560: }
1561:
1562: /**
1563: * Replace all markers of type ${fld:xxx} with an empty string "".<br>
1564: * Added on Aug-30-2005 to support a new print mode="clear" in config.xml.
1565: * @throws Throwable
1566: */
1567: public void clearFieldMarkers() throws Throwable {
1568:
1569: String toReplace = null;
1570:
1571: /* parse the template and create list of field markers */
1572: ArrayList<Marker> flds = getMarkers("fld");
1573:
1574: /* for each field marker set value */
1575: Iterator<Marker> i = flds.iterator();
1576: while (i.hasNext()) {
1577:
1578: /* get next marker */
1579: Marker m = (Marker) i.next();
1580: String fName = m.getName();
1581: String fFormat = m.getExtraInfo();
1582:
1583: if (fFormat != null)
1584: toReplace = "${fld:" + fName + "@" + fFormat + "}";
1585: else
1586: toReplace = "${fld:" + fName + "}";
1587:
1588: /* replace marker with value */
1589: _template = StringUtil.replace(_template, toReplace, "");
1590:
1591: }
1592:
1593: }
1594:
1595: }
|