001: /*
002: * Created on 11 Mar 2007
003: */
004: package uk.org.ponder.rsf.renderer.html;
005:
006: import java.io.InputStream;
007: import java.io.Reader;
008: import java.util.Map;
009:
010: import uk.org.ponder.rsf.components.UIAnchor;
011: import uk.org.ponder.rsf.components.UIBound;
012: import uk.org.ponder.rsf.components.UIBoundBoolean;
013: import uk.org.ponder.rsf.components.UIBoundList;
014: import uk.org.ponder.rsf.components.UIBoundString;
015: import uk.org.ponder.rsf.components.UICommand;
016: import uk.org.ponder.rsf.components.UIComponent;
017: import uk.org.ponder.rsf.components.UIForm;
018: import uk.org.ponder.rsf.components.UILink;
019: import uk.org.ponder.rsf.components.UIOutputMultiline;
020: import uk.org.ponder.rsf.components.UIParameter;
021: import uk.org.ponder.rsf.components.UISelect;
022: import uk.org.ponder.rsf.components.UISelectChoice;
023: import uk.org.ponder.rsf.components.UIVerbatim;
024: import uk.org.ponder.rsf.renderer.ComponentRenderer;
025: import uk.org.ponder.rsf.renderer.RenderUtil;
026: import uk.org.ponder.rsf.renderer.TagRenderContext;
027: import uk.org.ponder.rsf.renderer.scr.StaticRendererCollection;
028: import uk.org.ponder.rsf.request.EarlyRequestParser;
029: import uk.org.ponder.rsf.request.FossilizedConverter;
030: import uk.org.ponder.rsf.template.XMLLump;
031: import uk.org.ponder.rsf.uitype.UITypes;
032: import uk.org.ponder.rsf.view.View;
033: import uk.org.ponder.streamutil.StreamCopyUtil;
034: import uk.org.ponder.streamutil.write.PrintOutputStream;
035: import uk.org.ponder.stringutil.StringList;
036: import uk.org.ponder.util.Constants;
037: import uk.org.ponder.xml.XMLUtil;
038: import uk.org.ponder.xml.XMLWriter;
039:
040: /** The core HTML-dialect specific renderer for a primitive RSF component */
041:
042: public class BasicHTMLComponentRenderer implements ComponentRenderer {
043:
044: private StaticRendererCollection scrc;
045:
046: public void setStaticRenderers(StaticRendererCollection scrc) {
047: this .scrc = scrc;
048: }
049:
050: private void dumpBoundFields(UIBound torender, XMLWriter xmlw) {
051: if (torender != null) {
052: if (torender.fossilizedbinding != null) {
053: RenderUtil.dumpHiddenField(torender.fossilizedbinding,
054: xmlw);
055: }
056: if (torender.fossilizedshaper != null) {
057: RenderUtil.dumpHiddenField(torender.fossilizedshaper,
058: xmlw);
059: }
060: }
061: }
062:
063: public void renderComponent(UIComponent torendero, View view,
064: TagRenderContext trc) {
065: Map attrcopy = trc.attrcopy;
066: XMLWriter xmlw = trc.xmlw;
067: XMLLump[] lumps = trc.uselump.parent.lumps;
068: int lumpindex = trc.uselump.lumpindex;
069: PrintOutputStream pos = trc.pos;
070:
071: // Make an early and general attempt to rewrite side-URLs
072: URLRewriteSCR urlrewriter = (URLRewriteSCR) scrc
073: .getSCR(URLRewriteSCR.NAME);
074: urlrewriter.rewriteURLs(trc.uselump, attrcopy);
075:
076: if (torendero instanceof UIBound) {
077: UIBound torender = (UIBound) torendero;
078:
079: if (torendero.getClass() == UIOutputMultiline.class) {
080: StringList value = ((UIOutputMultiline) torendero)
081: .getValue();
082: if (value == null) {
083: RenderUtil.dumpTillLump(lumps, lumpindex + 1,
084: trc.close.lumpindex + 1, trc.pos);
085: } else {
086: XMLUtil.dumpAttributes(attrcopy, trc.xmlw);
087: trc.pos.print(">");
088: for (int i = 0; i < value.size(); ++i) {
089: if (i != 0) {
090: trc.pos.print("<br/>");
091: }
092: trc.xmlw.write(value.stringAt(i));
093: }
094: trc.closeTag();
095: }
096: } else if (torender.getClass() == UIAnchor.class) {
097: String value = ((UIAnchor) torendero).getValue();
098: if (UITypes.isPlaceholder(value)) {
099: trc.renderUnchanged();
100: } else {
101: attrcopy.put("name", value);
102: trc.replaceAttributes();
103: }
104: }
105: // factor out component-invariant processing of UIBound.
106: else { // non-Anchor, non-Multiline
107: if (torender.willinput) {
108: attrcopy.put("name", torender.submittingname);
109: }
110: if (torendero instanceof UIBoundBoolean) {
111: if (((UIBoundBoolean) torender).getValue()) {
112: attrcopy.put("checked", "yes");
113: } else {
114: attrcopy.remove("checked");
115: }
116: attrcopy.put("value", "true");
117: trc.rewriteLeaf(null);
118: } else if (torendero instanceof UIBoundList) {
119: // Cannot be rendered directly, must be fake
120: trc.renderUnchanged();
121: } else {
122: String value = ((UIBoundString) torender)
123: .getValue();
124: if (trc.uselump.textEquals("<textarea ")) {
125: if (UITypes.isPlaceholder(value)
126: && torender.willinput) {
127: // FORCE a blank value for input components if nothing from
128: // model, if input was intended.
129: value = "";
130: }
131: trc.rewriteLeaf(value);
132: } else if (trc.uselump.textEquals("<input ")) {
133: if (torender.willinput
134: || !UITypes.isPlaceholder(value)) {
135: attrcopy.put("value", value);
136: }
137: trc.rewriteLeaf(null);
138: } else {
139: trc.rewriteLeafOpen(value);
140: }
141: }
142: // unify hidden field processing? ANY parameter children found must
143: // be dumped as hidden fields.
144: }
145: // dump any fossilized binding for this component.
146: dumpBoundFields(torender, xmlw);
147: } // end if UIBound
148:
149: else if (torendero instanceof UISelect) {
150: UISelect select = (UISelect) torendero;
151: if (attrcopy.get("id") != null) {
152: // TODO: This is an irregularity, should probably remove for 0.8
153: attrcopy.put("id", select.selection.getFullID());
154: }
155: boolean ishtmlselect = trc.uselump.textEquals("<select ");
156: if (select.selection instanceof UIBoundList && ishtmlselect) {
157: attrcopy.put("multiple", "true");
158: }
159: if (ishtmlselect) {
160: // The HTML submitted value from a <select> actually corresponds
161: // with the selection member, not the top-level component.
162: if (select.selection.willinput) {
163: attrcopy.put("name",
164: select.selection.submittingname);
165: }
166: }
167: XMLUtil.dumpAttributes(attrcopy, xmlw);
168: if (ishtmlselect) {
169: pos.print(">");
170: String[] values = select.optionlist.getValue();
171: String[] names = select.optionnames == null ? values
172: : select.optionnames.getValue();
173: for (int i = 0; i < names.length; ++i) {
174: pos.print("<option value=\"");
175: String value = values[i];
176: if (value == null)
177: value = Constants.NULL_STRING;
178: xmlw.write(value);
179: if (select.selected.contains(value)) {
180: pos.print("\" selected=\"selected");
181: }
182: pos.print("\">");
183: xmlw.write(names[i]);
184: pos.print("</option>\n");
185: }
186: trc.closeTag();
187: } else {
188: trc.dumpTemplateBody();
189: }
190:
191: dumpBoundFields(select.selection, xmlw);
192: dumpBoundFields(select.optionlist, xmlw);
193: dumpBoundFields(select.optionnames, xmlw);
194: } else if (torendero instanceof UISelectChoice) {
195: UISelectChoice torender = (UISelectChoice) torendero;
196: UISelect parent = (UISelect) view
197: .getComponent(torender.parentFullID);
198: String value = parent.optionlist.getValue()[torender.choiceindex];
199: // currently only peers with "input type="radio"".
200: attrcopy.put("name", parent.selection.submittingname);
201: attrcopy.put("value", value);
202: attrcopy.remove("checked");
203: if (parent.selected.contains(value)) {
204: attrcopy.put("checked", "true");
205: }
206: trc.replaceAttributes();
207: } else if (torendero instanceof UILink) {
208: UILink torender = (UILink) torendero;
209:
210: String attrname = URLRewriteSCR
211: .getLinkAttribute(trc.uselump);
212: if (attrname != null) {
213: String target = torender.target.getValue();
214: if (UITypes.isPlaceholder(target)) {
215: target = (String) attrcopy.get(attrname);
216: } else {
217: String resolved = urlrewriter.resolveURL(
218: trc.uselump.parent, target);
219: if (resolved != null) {
220: target = resolved;
221: }
222: }
223: attrcopy.put(attrname, target);
224: }
225: String value = torender.linktext == null ? null
226: : torender.linktext.getValue();
227: if (value == null) {
228: trc.replaceAttributesOpen();
229: } else {
230: trc.rewriteLeaf(value);
231: }
232: }
233:
234: else if (torendero instanceof UICommand) {
235: UICommand torender = (UICommand) torendero;
236: String value = RenderUtil
237: .makeURLAttributes(torender.parameters);
238: // any desired "attributes" decoded for JUST THIS ACTION must be
239: // secretly bundled as this special attribute.
240: attrcopy
241: .put("name",
242: FossilizedConverter.COMMAND_LINK_PARAMETERS
243: + value);
244: String text = torender.commandtext == null ? null
245: : torender.commandtext.getValue();
246: boolean isbutton = trc.uselump.textEquals("<button ");
247: if (text != null && !isbutton) {
248: attrcopy.put("value", torender.commandtext.getValue());
249: text = null;
250: }
251: trc.rewriteLeaf(text);
252: } else if (torendero instanceof UIForm) {
253: UIForm torender = (UIForm) torendero;
254: attrcopy.put("method", torender.type
255: .equals(EarlyRequestParser.RENDER_REQUEST) ? "get"
256: : "post");
257: // form fixer guarantees that this URL is attribute free.
258: attrcopy.put("action", torender.targetURL);
259:
260: trc.replaceAttributesOpen();
261: for (int i = 0; i < torender.parameters.size(); ++i) {
262: UIParameter param = torender.parameters.parameterAt(i);
263: RenderUtil.dumpHiddenField(param, xmlw);
264: }
265: } else if (torendero instanceof UIVerbatim) {
266: UIVerbatim torender = (UIVerbatim) torendero;
267: String rendered = null;
268: // inefficient implementation for now, upgrade when we write bulk POS
269: // utils.
270: if (torender.markup instanceof InputStream) {
271: rendered = StreamCopyUtil
272: .streamToString((InputStream) torender.markup);
273: } else if (torender.markup instanceof Reader) {
274: rendered = StreamCopyUtil
275: .readerToString((Reader) torender.markup);
276: } else if (torender.markup instanceof UIBoundString) {
277: rendered = ((UIBoundString) (torender.markup))
278: .getValue();
279: } else if (torender.markup != null) {
280: rendered = torender.markup.toString();
281: }
282: if (rendered == null) {
283: trc.renderUnchanged();
284: } else {
285: if (!trc.iselide) {
286: XMLUtil.dumpAttributes(attrcopy, xmlw);
287: pos.print(">");
288: }
289: pos.print(rendered);
290: trc.closeTag();
291: }
292: }
293:
294: }
295: }
|