PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/src/uk/org/ponder/rsf/renderer/html/BasicHTMLComponentRenderer.java

https://github.com/rsf/RSFUtil
Java | 321 lines | 281 code | 19 blank | 21 comment | 98 complexity | 0bed7450a0b7eec8eaacd2f4b6feee2c MD5 | raw file
  1. /*
  2. * Created on 11 Mar 2007
  3. */
  4. package uk.org.ponder.rsf.renderer.html;
  5. import java.io.InputStream;
  6. import java.io.Reader;
  7. import java.util.Map;
  8. import uk.org.ponder.rsf.components.UIAnchor;
  9. import uk.org.ponder.rsf.components.UIBound;
  10. import uk.org.ponder.rsf.components.UIBoundBoolean;
  11. import uk.org.ponder.rsf.components.UIBoundList;
  12. import uk.org.ponder.rsf.components.UIBoundString;
  13. import uk.org.ponder.rsf.components.UICommand;
  14. import uk.org.ponder.rsf.components.UIComponent;
  15. import uk.org.ponder.rsf.components.UIForm;
  16. import uk.org.ponder.rsf.components.UILink;
  17. import uk.org.ponder.rsf.components.UIOutputMultiline;
  18. import uk.org.ponder.rsf.components.UIParameter;
  19. import uk.org.ponder.rsf.components.UISelect;
  20. import uk.org.ponder.rsf.components.UISelectChoice;
  21. import uk.org.ponder.rsf.components.UIVerbatim;
  22. import uk.org.ponder.rsf.renderer.ComponentRenderer;
  23. import uk.org.ponder.rsf.renderer.RenderUtil;
  24. import uk.org.ponder.rsf.renderer.TagRenderContext;
  25. import uk.org.ponder.rsf.renderer.scr.StaticRendererCollection;
  26. import uk.org.ponder.rsf.request.EarlyRequestParser;
  27. import uk.org.ponder.rsf.request.FossilizedConverter;
  28. import uk.org.ponder.rsf.template.XMLLump;
  29. import uk.org.ponder.rsf.uitype.UITypes;
  30. import uk.org.ponder.rsf.view.View;
  31. import uk.org.ponder.streamutil.StreamCopyUtil;
  32. import uk.org.ponder.streamutil.write.PrintOutputStream;
  33. import uk.org.ponder.stringutil.StringList;
  34. import uk.org.ponder.util.Constants;
  35. import uk.org.ponder.xml.XMLUtil;
  36. import uk.org.ponder.xml.XMLWriter;
  37. /** The core HTML-dialect specific renderer for a primitive RSF component */
  38. public class BasicHTMLComponentRenderer implements ComponentRenderer {
  39. private StaticRendererCollection scrc;
  40. public void setStaticRenderers(StaticRendererCollection scrc) {
  41. this.scrc = scrc;
  42. }
  43. private void dumpBoundFields(UIBound torender, XMLWriter xmlw) {
  44. if (torender != null) {
  45. if (torender.fossilizedbinding != null) {
  46. RenderUtil.dumpHiddenField(torender.fossilizedbinding, xmlw);
  47. }
  48. if (torender.fossilizedshaper != null) {
  49. RenderUtil.dumpHiddenField(torender.fossilizedshaper, xmlw);
  50. }
  51. }
  52. }
  53. public void renderComponent(UIComponent torendero, View view,
  54. TagRenderContext trc) {
  55. Map attrcopy = trc.attrcopy;
  56. XMLWriter xmlw = trc.xmlw;
  57. XMLLump[] lumps = trc.uselump.parent.lumps;
  58. int lumpindex = trc.uselump.lumpindex;
  59. PrintOutputStream pos = trc.pos;
  60. String tagname = trc.uselump.getTag();
  61. // Make an early and general attempt to rewrite side-URLs
  62. URLRewriteSCR urlrewriter = (URLRewriteSCR) scrc.getSCR(URLRewriteSCR.NAME);
  63. urlrewriter.rewriteURLs(trc.uselump, attrcopy);
  64. if (torendero instanceof UIBound) {
  65. UIBound torender = (UIBound) torendero;
  66. if (torendero.getClass() == UIOutputMultiline.class) {
  67. StringList value = ((UIOutputMultiline) torendero).getValue();
  68. if (value == null) {
  69. RenderUtil.dumpTillLump(lumps, lumpindex + 1,
  70. trc.close.lumpindex + 1, trc.pos);
  71. }
  72. else {
  73. XMLUtil.dumpAttributes(attrcopy, trc.xmlw);
  74. trc.pos.print(">");
  75. for (int i = 0; i < value.size(); ++i) {
  76. if (i != 0) {
  77. trc.pos.print("<br/>");
  78. }
  79. trc.xmlw.write(value.stringAt(i));
  80. }
  81. trc.closeTag();
  82. }
  83. }
  84. else if (torender.getClass() == UIAnchor.class) {
  85. String value = ((UIAnchor) torendero).getValue();
  86. if (UITypes.isPlaceholder(value)) {
  87. trc.renderUnchanged();
  88. }
  89. else {
  90. attrcopy.put("name", value);
  91. trc.replaceAttributes();
  92. }
  93. }
  94. // factor out component-invariant processing of UIBound.
  95. else { // non-Anchor, non-Multiline
  96. if (torender.willinput) {
  97. attrcopy.put("name", torender.submittingname);
  98. }
  99. if (torendero instanceof UIBoundBoolean) {
  100. if (((UIBoundBoolean) torender).getValue()) {
  101. attrcopy.put("checked", "checked");
  102. }
  103. else {
  104. attrcopy.remove("checked");
  105. }
  106. attrcopy.put("value", "true");
  107. trc.rewriteLeaf(null);
  108. }
  109. else if (torendero instanceof UIBoundList) {
  110. // Cannot be rendered directly, must be fake
  111. trc.renderUnchanged();
  112. }
  113. else {
  114. String value = ((UIBoundString) torender).getValue();
  115. if ("textarea".equals(tagname)) {
  116. if (UITypes.isPlaceholder(value) && torender.willinput) {
  117. // FORCE a blank value for input components if nothing from
  118. // model, if input was intended.
  119. value = "";
  120. }
  121. trc.rewriteLeaf(value);
  122. }
  123. else if ("input".equals(tagname)) {
  124. if (torender.willinput || !UITypes.isPlaceholder(value)) {
  125. attrcopy.put("value", value);
  126. }
  127. trc.rewriteLeaf(null);
  128. }
  129. else {
  130. trc.rewriteLeafOpen(value);
  131. }
  132. }
  133. // unify hidden field processing? ANY parameter children found must
  134. // be dumped as hidden fields.
  135. }
  136. // dump any fossilized binding for this component.
  137. dumpBoundFields(torender, xmlw);
  138. } // end if UIBound
  139. else if (torendero instanceof UISelect) {
  140. UISelect select = (UISelect) torendero;
  141. if (attrcopy.get("id") != null) {
  142. // TODO: This is an irregularity, should probably remove for 0.8
  143. attrcopy.put("id", select.selection.getFullID());
  144. }
  145. boolean ishtmlselect = trc.uselump.isTag("select");
  146. if (select.selection instanceof UIBoundList && ishtmlselect) {
  147. attrcopy.put("multiple", "multiple");
  148. }
  149. if (ishtmlselect) {
  150. // The HTML submitted value from a <select> actually corresponds
  151. // with the selection member, not the top-level component.
  152. if (select.selection.willinput) {
  153. attrcopy.put("name", select.selection.submittingname);
  154. }
  155. }
  156. XMLUtil.dumpAttributes(attrcopy, xmlw);
  157. if (ishtmlselect) {
  158. pos.print(">\n");
  159. String[] values = select.optionlist.getValue();
  160. String[] names = select.optionnames == null ? values
  161. : select.optionnames.getValue();
  162. String[] groups = select.groupnames == null? null : select.groupnames.getValue();
  163. String lastgroup = "";
  164. for (int i = 0; i < values.length; ++i) {
  165. String thisgroup = groups == null? null: i < groups.length? groups[i] : null;
  166. if (thisgroup == null) thisgroup = "";
  167. boolean change = !thisgroup.equals(lastgroup);
  168. if (change) {
  169. if (!lastgroup.equals("")) {
  170. pos.println("</optgroup>");
  171. }
  172. if (!thisgroup.equals("")) {
  173. pos.println("<optgroup label=\"" + thisgroup + "\">");
  174. }
  175. }
  176. pos.print("<option value=\"");
  177. String value = values[i];
  178. if (value == null)
  179. value = Constants.NULL_STRING;
  180. xmlw.write(value);
  181. if (select.selected.contains(value)) {
  182. pos.print("\" selected=\"selected");
  183. }
  184. pos.print("\">");
  185. xmlw.write(names[i]);
  186. pos.print("</option>\n");
  187. lastgroup = thisgroup;
  188. }
  189. if (!lastgroup.equals("")) {
  190. pos.println("</optgroup>");
  191. }
  192. trc.closeTag();
  193. }
  194. else {
  195. trc.dumpTemplateBody();
  196. }
  197. dumpBoundFields(select.selection, xmlw);
  198. dumpBoundFields(select.optionlist, xmlw);
  199. dumpBoundFields(select.optionnames, xmlw);
  200. }
  201. else if (torendero instanceof UISelectChoice) {
  202. UISelectChoice torender = (UISelectChoice) torendero;
  203. UISelect parent = (UISelect) view.getComponent(torender.parentFullID);
  204. String value = parent.optionlist.getValue()[torender.choiceindex];
  205. // peers with <input type="radio"/> or <input type="checkbox"/>
  206. attrcopy.put("name", parent.selection.submittingname);
  207. attrcopy.put("value", value);
  208. attrcopy.remove("checked");
  209. if (parent.selected.contains(value)) {
  210. attrcopy.put("checked", "checked");
  211. }
  212. trc.replaceAttributes();
  213. }
  214. else if (torendero instanceof UILink) {
  215. UILink torender = (UILink) torendero;
  216. String attrname = URLRewriteSCR.getLinkAttribute(trc.uselump);
  217. if (attrname != null) {
  218. String target = torender.target.getValue();
  219. if (UITypes.isPlaceholder(target)) {
  220. target = (String) attrcopy.get(attrname);
  221. }
  222. else {
  223. String resolved = urlrewriter.resolveURL(trc.uselump.parent, target);
  224. if (resolved != null) {
  225. target = resolved;
  226. }
  227. }
  228. attrcopy.put(attrname, target);
  229. }
  230. String value = torender.linktext == null ? null
  231. : torender.linktext.getValue();
  232. if (value == null) {
  233. trc.replaceAttributesOpen();
  234. }
  235. else {
  236. trc.rewriteLeaf(value);
  237. }
  238. }
  239. else if (torendero instanceof UICommand) {
  240. UICommand torender = (UICommand) torendero;
  241. String value = RenderUtil.makeURLAttributes(torender.parameters);
  242. // any desired "attributes" decoded for JUST THIS ACTION must be
  243. // secretly bundled as this special attribute.
  244. attrcopy.put("name", FossilizedConverter.COMMAND_LINK_PARAMETERS + value);
  245. String text = torender.commandtext == null ? null
  246. : torender.commandtext.getValue();
  247. boolean isbutton = "button".equals(tagname);
  248. if (text != null && !isbutton) {
  249. attrcopy.put("value", torender.commandtext.getValue());
  250. text = null;
  251. }
  252. trc.rewriteLeaf(text);
  253. }
  254. else if (torendero instanceof UIForm) {
  255. if (!"form".equals(tagname)) {
  256. throw new IllegalArgumentException(
  257. "UIForm peered with unrecognised tag " + tagname
  258. + " (only <form> is supported for HTML)");
  259. }
  260. UIForm torender = (UIForm) torendero;
  261. attrcopy.put("method",
  262. torender.type.equals(EarlyRequestParser.RENDER_REQUEST)?
  263. "get" : "post");
  264. // form fixer guarantees that this URL is attribute free.
  265. attrcopy.put("action", torender.targetURL);
  266. trc.replaceAttributesOpen();
  267. for (int i = 0; i < torender.parameters.size(); ++i) {
  268. UIParameter param = torender.parameters.parameterAt(i);
  269. RenderUtil.dumpHiddenField(param, xmlw);
  270. }
  271. }
  272. else if (torendero instanceof UIVerbatim) {
  273. UIVerbatim torender = (UIVerbatim) torendero;
  274. String rendered = null;
  275. // inefficient implementation for now, upgrade when we write bulk POS
  276. // utils.
  277. if (torender.markup instanceof InputStream) {
  278. rendered = StreamCopyUtil.streamToString((InputStream) torender.markup);
  279. }
  280. else if (torender.markup instanceof Reader) {
  281. rendered = StreamCopyUtil.readerToString((Reader) torender.markup);
  282. }
  283. else if (torender.markup instanceof UIBoundString) {
  284. rendered = ((UIBoundString)(torender.markup)).getValue();
  285. }
  286. else if (torender.markup != null) {
  287. rendered = torender.markup.toString();
  288. }
  289. if (rendered == null) {
  290. trc.renderUnchanged();
  291. }
  292. else {
  293. if (!trc.iselide) {
  294. XMLUtil.dumpAttributes(attrcopy, xmlw);
  295. pos.print(">");
  296. }
  297. pos.print(rendered);
  298. trc.closeTag();
  299. }
  300. }
  301. }
  302. }