001: /*
002: * Created on Jul 27, 2005
003: */
004: package uk.org.ponder.rsf.renderer;
005:
006: import java.util.Collections;
007: import java.util.Iterator;
008: import java.util.Map;
009:
010: import uk.org.ponder.arrayutil.ArrayUtil;
011: import uk.org.ponder.rsf.components.ParameterList;
012: import uk.org.ponder.rsf.components.UIBasicListMember;
013: import uk.org.ponder.rsf.components.UIBoundList;
014: import uk.org.ponder.rsf.components.UIBoundString;
015: import uk.org.ponder.rsf.components.UIComponent;
016: import uk.org.ponder.rsf.components.UIParameter;
017: import uk.org.ponder.rsf.components.UISelect;
018: import uk.org.ponder.rsf.renderer.scr.BasicSCR;
019: import uk.org.ponder.rsf.renderer.scr.CollectingSCR;
020: import uk.org.ponder.rsf.renderer.scr.StaticComponentRenderer;
021: import uk.org.ponder.rsf.request.FossilizedConverter;
022: import uk.org.ponder.rsf.template.XMLLump;
023: import uk.org.ponder.rsf.template.XMLLumpComparator;
024: import uk.org.ponder.rsf.template.XMLLumpList;
025: import uk.org.ponder.rsf.template.XMLLumpMMap;
026: import uk.org.ponder.rsf.view.View;
027: import uk.org.ponder.streamutil.write.PrintOutputStream;
028: import uk.org.ponder.stringutil.CharWrap;
029: import uk.org.ponder.stringutil.URLEncoder;
030: import uk.org.ponder.stringutil.URLUtil;
031: import uk.org.ponder.util.Logger;
032: import uk.org.ponder.xml.XMLUtil;
033: import uk.org.ponder.xml.XMLWriter;
034:
035: /**
036: * @author Antranig Basman (antranig@caret.cam.ac.uk)
037: *
038: */
039: public class RenderUtil {
040:
041: public static int dumpTillLump(XMLLump[] lumps, int start,
042: int limit, PrintOutputStream target) {
043: // for (; start < limit; ++ start) {
044: // target.print(lumps[start].text);
045: // }
046: target.write(lumps[start].parent.buffer, lumps[start].start,
047: lumps[limit].start - lumps[start].start);
048: return limit;
049: }
050:
051: /**
052: * Dump from template to output until either we reduce below
053: * <code>basedepth</code> recursion level, or we hit an rsf:id, or end of
054: * file. Return the lump index we reached. This has two uses, firstly from the
055: * base of the main scanning loop, and secondly from the "glue" scanning. The
056: * main scanning loop runs until we reduce BELOW RECURSION LEVEL OF PARENT,
057: * i.e. we output its closing tag and then return. The glue loop requires that
058: * we DO NOT OUTPUT THE CLOSING TAG OF PARENT because we may have some number
059: * of repetitive components still to render.
060: */
061: public static int dumpScan(XMLLump[] lumps, int renderindex,
062: int basedepth, PrintOutputStream target,
063: boolean closeparent, boolean insideleaf) {
064: int start = lumps[renderindex].start;
065: char[] buffer = lumps[renderindex].parent.buffer;
066: while (true) {
067: if (renderindex == lumps.length)
068: break;
069: XMLLump lump = lumps[renderindex];
070: if (lump.nestingdepth < basedepth)
071: break;
072: if (lump.rsfID != null) {
073: if (!insideleaf)
074: break;
075: if (insideleaf
076: && lump.nestingdepth > basedepth
077: + (closeparent ? 0 : 1)) {
078: Logger.log
079: .warn("Error in component tree - leaf component found to contain further components - at "
080: + lump.toString());
081: } else
082: break;
083: }
084: // target.print(lump.text);
085: ++renderindex;
086: }
087: // ASSUMPTIONS: close tags are ONE LUMP
088: if (!closeparent
089: && (renderindex == lumps.length || lumps[renderindex].rsfID == null))
090: --renderindex;
091: int limit = (renderindex == lumps.length ? buffer.length
092: : lumps[renderindex].start);
093:
094: target.write(buffer, start, limit - start);
095: return renderindex;
096: }
097:
098: public static void dumpHiddenField(UIParameter todump,
099: XMLWriter xmlw) {
100: xmlw.writeRaw("<input type=\"hidden\" ");
101: XMLUtil.dumpAttribute(todump.virtual ? "id" : "name",
102: todump.name, xmlw);
103: XMLUtil.dumpAttribute("value", todump.value, xmlw);
104: xmlw.writeRaw(" />\n");
105: }
106:
107: public static String appendAttributes(String baseurl,
108: String attributes) {
109: // Replace a leading & by ? in the attributes, if there are no
110: // existing attributes in the URL
111: // TODO: hop into the URL before any anchors
112: if (baseurl.indexOf('?') == -1 && attributes.length() > 0) {
113: attributes = "?" + attributes.substring(1);
114: }
115: return baseurl + attributes;
116: }
117:
118: public static String makeURLAttributes(ParameterList params) {
119: CharWrap togo = new CharWrap();
120: for (int i = 0; i < params.size(); ++i) {
121: UIParameter param = params.parameterAt(i);
122: togo.append("&").append(URLEncoder.encode(param.name))
123: .append("=").append(URLEncoder.encode(param.value));
124: }
125: return togo.toString();
126: }
127:
128: /**
129: * "Unpacks" the supplied command link "name" (as encoded using the
130: * HTMLRenderSystem for submission controls) by treating it as a section of
131: * URL attribute stanzas. The key/value pairs encoded in it will be added to
132: * the supplied (modifiable) map.
133: */
134: public static void unpackCommandLink(String longvalue,
135: Map requestparams) {
136: String[] split = longvalue.split("[&=]");
137: // start at 1 since string will begin with &
138: if ((split.length % 2) == 0) {
139: Logger.log
140: .warn("Erroneous submission - odd number of parameters/values in "
141: + longvalue);
142: return;
143: }
144: for (int i = 1; i < split.length; i += 2) {
145: String key = URLUtil.decodeURL(split[i]);
146: String value = URLUtil.decodeURL(split[i + 1]);
147: Logger.log.info("Unpacked command link key " + key
148: + " value " + value);
149: String[] existing = (String[]) requestparams.get(key);
150: if (existing == null) {
151: requestparams.put(key, new String[] { value });
152: } else {
153: String[] fused = (String[]) ArrayUtil.append(existing,
154: value);
155: requestparams.put(key, fused);
156: }
157: }
158:
159: }
160:
161: public static UIComponent resolveListMember(View view,
162: UIBasicListMember torendero) {
163: UIComponent parent = view.getComponent(torendero.parentFullID);
164: UIBoundList boundlist = parent instanceof UISelect ? ((UISelect) parent).optionnames
165: : (UIBoundList) parent;
166: String[] valuelist = boundlist.getValue();
167: // Reference off the end of an array is not an error - it may be being dynamically expanded
168: String value = torendero.choiceindex < valuelist.length ? valuelist[torendero.choiceindex]
169: : "";
170: String submittingname = boundlist.submittingname;
171: UIBoundString togo = new UIBoundString();
172: togo.setValue(value);
173: if (torendero.willinput) {
174: togo.submittingname = submittingname;
175: togo.willinput = true;
176: }
177: return togo;
178: }
179:
180: public static String findCommandParams(Map requestparams) {
181: for (Iterator parit = requestparams.keySet().iterator(); parit
182: .hasNext();) {
183: String key = (String) parit.next();
184: if (key
185: .startsWith(FossilizedConverter.COMMAND_LINK_PARAMETERS))
186: return key;
187: }
188: return null;
189: }
190:
191: public static int renderSCR(StaticComponentRenderer scr,
192: XMLLump lump, XMLWriter xmlw, XMLLumpMMap collecteds) {
193: if (scr instanceof BasicSCR) {
194: return ((BasicSCR) scr).render(lump, xmlw);
195: } else {
196: CollectingSCR collector = (CollectingSCR) scr;
197: String[] tocollect = collector.getCollectingNames();
198: XMLLumpList collected = new XMLLumpList();
199: for (int i = 0; i < tocollect.length; ++i) {
200: XMLLumpList this collect = collecteds
201: .headsForID(tocollect[i]);
202: if (this collect != null) {
203: collected.addAll(this collect);
204: }
205: }
206: return collector.render(lump, collected, xmlw);
207: }
208: }
209:
210: public static boolean isFirstSCR(XMLLump lump, String scrname) {
211: XMLLump parent = lump.getDownHolder();
212: String lookname = XMLLump.SCR_PREFIX + scrname;
213: XMLLumpList sames = new XMLLumpList();
214: sames.addAll(parent.downmap.headsForID(lookname));
215: Collections.sort(sames, XMLLumpComparator.instance());
216: return sames.get(0) == lump;
217: }
218:
219: }
|