001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/jsf/tags/sakai_2-4-1/widgets/src/java/org/sakaiproject/jsf/util/RendererUtil.java $
003: * $Id: RendererUtil.java 21053 2007-02-06 18:03:57Z ray@media.berkeley.edu $
004: **********************************************************************************
005: *
006: * Copyright (c) 2003, 2004 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.jsf.util;
021:
022: import java.io.IOException;
023: import java.io.Writer;
024: import java.util.Collection;
025: import java.util.HashMap;
026: import java.util.Iterator;
027: import java.util.List;
028: import java.util.Locale;
029: import java.util.Map;
030: import javax.faces.component.UIComponent;
031: import javax.faces.component.UIForm;
032: import javax.faces.component.UIViewRoot;
033: import javax.faces.context.FacesContext;
034: import javax.faces.context.ResponseWriter;
035: import javax.faces.el.ValueBinding;
036: import javax.faces.model.SelectItem;
037: import javax.servlet.http.HttpServletRequest;
038:
039: /**
040: * Common static utility methods that help in implementing JSF tags.
041: */
042: public class RendererUtil {
043:
044: /** This class is meant for static use only */
045: private RendererUtil() {
046: }
047:
048: /**
049: * Sets component attribute value - if a ValueBinding exists for that
050: * attribute, set through the binding; otherwise, set the value directly on
051: * the component.
052: */
053: public static void setAttribute(FacesContext context,
054: UIComponent component, String name, Object value) {
055: ValueBinding binding = component.getValueBinding(name);
056: if (binding != null) {
057: try {
058: binding.setValue(context, value);
059: } catch (IllegalArgumentException e) {
060: // try setting the value as a String
061: binding.setValue(context, String.valueOf(value));
062: }
063: } else {
064: component.getAttributes().put(name, value);
065: }
066: }
067:
068: /**
069: * Return the attribute value - handles getting the value from a
070: * ValueBinding if necessary. This is necessary because of a difference in
071: * the Sun JSF RI versus the MyFaces RI. The Sun RI
072: * component.getAttributes().get(attrName) will automatically return value
073: * bindings, whereas the MyFaces implmentation requires getting values from
074: * ValueBinding seperately.
075: */
076: public static Object getAttribute(FacesContext context,
077: UIComponent component, String name) {
078: // first check the attributes
079: Object ret = component.getAttributes().get(name);
080: if (ret != null)
081: return ret;
082:
083: // next check the value bindings
084: ValueBinding vb = component.getValueBinding(name);
085: if (vb != null)
086: ret = vb.getValue(context);
087:
088: return ret;
089: }
090:
091: /**
092: * Same as getAttribute, but if not found, we return a default value.
093: */
094: public static Object getDefaultedAttribute(FacesContext context,
095: UIComponent component, String name, Object defaultValue) {
096: Object o = getAttribute(context, component, name);
097: if (o == null)
098: o = defaultValue;
099: return o;
100: }
101:
102: /**
103: * Helper method for recursively encoding a component.
104: *
105: * @param context
106: * the given FacesContext
107: * @param component
108: * the UIComponent to render
109: * @throws IOException
110: */
111: public static void encodeRecursive(FacesContext context,
112: UIComponent component) throws IOException {
113: if (!component.isRendered()) {
114: return;
115: }
116:
117: component.encodeBegin(context);
118:
119: if (component.getRendersChildren()) {
120: component.encodeChildren(context);
121: } else {
122: Iterator iter = component.getChildren().iterator();
123:
124: while (iter.hasNext()) {
125: UIComponent child = (UIComponent) iter.next();
126: encodeRecursive(context, child);
127: }
128: }
129: component.encodeEnd(context);
130: }
131:
132: /**
133: * If renderer supports disabled or readonly attributes use this method to
134: * obtain an early exit from decode method. Good idea to include it anyway,
135: * compnent will continue to work when these properties are added.
136: */
137: public static boolean isDisabledOrReadonly(FacesContext context,
138: UIComponent component) {
139: boolean disabled = false;
140: boolean readOnly = false;
141:
142: Object disabledAttr = getAttribute(context, component,
143: "disabled");
144: if (disabledAttr != null) {
145: disabled = disabledAttr.equals(Boolean.TRUE);
146: }
147:
148: Object readOnlyAttr = getAttribute(context, component,
149: "readonly");
150: if (readOnlyAttr != null) {
151: readOnly = readOnlyAttr.equals(Boolean.TRUE);
152: }
153:
154: return readOnly | disabled;
155: }
156:
157: /**
158: * Write default HTML passthrough attributes
159: */
160: public static void writePassthroughs(FacesContext context,
161: UIComponent component) throws IOException {
162: String[] passthrus = { "ondblclick", "onclick", "onkeydown",
163: "onkeypress", "onkeyup", "onmousedown", "onmousemove",
164: "onmouseout", "onmouseover", "onmouseup" };
165: writePassthroughAttributes(passthrus, true, context, component);
166: }
167:
168: /**
169: * write passthough attributes on the current element
170: */
171: public static void writePassthroughAttributes(String[] passthrus,
172: boolean writeNullAttrs, FacesContext context,
173: UIComponent component) throws IOException {
174: ResponseWriter writer = context.getResponseWriter();
175: for (int i = 0; i < passthrus.length; i++) {
176: String key = passthrus[i];
177: String value = (String) getAttribute(context, component,
178: key);
179: if (writeNullAttrs && value == null)
180: value = "";
181: if (value != null)
182: writer.writeAttribute(key, value, null);
183: }
184: }
185:
186: /**
187: * @param attributeMap
188: * String key/value pairs
189: * @param writer
190: * response writer
191: */
192: public static void writeAttributes(Map attributeMap,
193: ResponseWriter writer) throws IOException {
194: Iterator iter = attributeMap.keySet().iterator();
195: while (iter.hasNext()) {
196: String key = (String) iter.next();
197: String value = (String) attributeMap.get(key);
198: if (value == null)
199: value = "";
200: writer.writeAttribute(key, value, key);
201: }
202:
203: }
204:
205: /**
206: * @param attributeMap
207: * String key/value pairs
208: * @param context
209: * Faces context
210: * @param component
211: * the UIComponent
212: * @throws IOException
213: */
214: public static void writeAttributes(Map attributeMap,
215: FacesContext context) throws IOException {
216: ResponseWriter writer = context.getResponseWriter();
217: writeAttributes(attributeMap, writer);
218: }
219:
220: /**
221: * Renders a script that includes an external JavaScript that gets added to
222: * the document through a document.write() if a gatekeeper value is NOT set.
223: * This effectively makes the script inclusion a per request JavaScript
224: * singleton.
225: *
226: * @param gateKey
227: * for key value pair
228: * @param gateValue
229: * value for key value pair for gatekeeper
230: * @param contextBasePath
231: * the web app with the script
232: * @param scriptPath
233: * the webapp-relative path
234: * @throws IOException
235: */
236: public static void writeSmartExternalScripts(FacesContext context,
237: String gateKey, String gateValue, String contextBasePath,
238: String[] scriptPaths) throws IOException {
239: ResponseWriter writer = context.getResponseWriter();
240: writeSmartExternalScripts(writer, gateKey, gateValue,
241: contextBasePath, scriptPaths);
242: }
243:
244: /**
245: * Renders a script that includes an external JavaScript that gets added to
246: * the document through a document.write() if a gatekeeper value is NOT set.
247: * This effectively makes the script inclusion a per request JavaScript
248: * singleton.
249: *
250: * @param writer
251: * the ResponseWriter
252: * @param gateKey
253: * for key value pair
254: * @param gateValue
255: * value for key value pair for gatekeeper
256: * @param contextBasePath
257: * the web app with the script
258: * @param scriptPath
259: * the webapp-relative path
260: * @throws IOException
261: */
262: public static void writeSmartExternalScripts(ResponseWriter writer,
263: String gateKey, String gateValue, String contextBasePath,
264: String[] scriptPaths) throws IOException {
265: writer.write("<script>");
266: writer.write(" if (typeof window['" + gateKey + "'] == '"
267: + gateValue + "')");
268: writer.write(" {");
269:
270: for (int i = 0; i < scriptPaths.length; i++) {
271: writer.write(" document.write(");
272: writer
273: .write(" \"<\" + \"script type='text/javascript' src='/'\" + "
274: + contextBasePath + " +");
275: writer.write(" \"'" + scriptPaths[i]
276: + "'><\" + \"/script>);");
277: }
278:
279: writer.write(" var " + gateKey + " = '" + gateValue + "';");
280:
281: writer.write(" }");
282: writer.write("</script>");
283: writer.write("");
284: writer.write("");
285: }
286:
287: /**
288: * Get a Map of String key/value pairs from a UIComponent for all attributes
289: * keys in a collection
290: *
291: * @param collection
292: * @param component
293: * @return Map of String key/value pairs from a UIComponent for all keys in
294: * a collection
295: */
296: public static Map mapComponentAttributes(Collection collection,
297: UIComponent component) {
298: Map attributeMap = new HashMap();
299: if (collection == null)
300: return attributeMap;
301: String[] attributeNames = new String[collection.size()];
302: Object[] objs = collection.toArray();
303: for (int i = 0; i < objs.length; i++) {
304: attributeNames[i] = (String) objs[i];
305: }
306: return mapComponentAttributes(attributeNames, component);
307: }
308:
309: /**
310: * Get String key/value pairs from a UIComponent for all attributes keys in
311: * an array
312: *
313: * @param attributeNames
314: * @param component
315: * @return Map of String key/value pairs from a UIComponent for all keys in
316: * a collection
317: */
318: public static Map mapComponentAttributes(String[] attributeNames,
319: UIComponent component) {
320: Map attributeMap = new HashMap();
321: for (int i = 0; i < attributeNames.length; i++) {
322: attributeMap.put(attributeNames[i], (String) component
323: .getAttributes().get(attributeNames[i]));
324: }
325: return attributeMap;
326: }
327:
328: /**
329: * Switch handling utility.
330: *
331: * @param rawSwitch
332: * String input string
333: * @param supportOnOff
334: * boolean can input string be on, off?
335: * @param supportTrueFalse
336: * boolean can input string be true, false?
337: * @param supportYesNo
338: * boolean can input string be yes, no?
339: * @param returnOnOff
340: * boolean output on, off instead of true false
341: * @param returnYesNo
342: * boolean output yes, no instead of true false
343: * @param defaultValue
344: * boolean if unknown, return true or false?
345: * @return String raw swrich sring translated to correct switch value or
346: * default
347: */
348: public static String makeSwitchString(String rawSwitch,
349: boolean supportOnOff, boolean supportTrueFalse,
350: boolean supportYesNo, boolean returnOnOff,
351: boolean returnYesNo, boolean defaultValue) {
352: boolean switchValue = defaultValue;
353:
354: String trueString = "true";
355: String falseString = "false";
356:
357: if (returnOnOff) {
358: trueString = "on";
359: falseString = "off";
360: } else if (returnYesNo) {
361: trueString = "yes";
362: falseString = "no";
363: }
364:
365: if (supportOnOff) {
366: if ("on".equalsIgnoreCase(rawSwitch))
367: switchValue = true;
368: if ("off".equalsIgnoreCase(rawSwitch))
369: switchValue = false;
370: }
371: if (supportTrueFalse) {
372: if ("true".equalsIgnoreCase(rawSwitch))
373: switchValue = true;
374: if ("false".equalsIgnoreCase(rawSwitch))
375: switchValue = false;
376: }
377: if (supportYesNo) {
378: if ("yes".equalsIgnoreCase(rawSwitch))
379: switchValue = true;
380: if ("no".equalsIgnoreCase(rawSwitch))
381: switchValue = false;
382: }
383:
384: if (switchValue) {
385: return trueString;
386: } else {
387: return falseString;
388: }
389: }
390:
391: /**
392: * Given a List of SelectItems render the select options
393: *
394: * @param out
395: * @param items
396: * List of SelectItems
397: * @param selected
398: * seelcted choice
399: * @param clientId
400: * the id
401: * @param styleClass
402: * the optional style class
403: * @param component
404: * the component being rendered
405: * @throws IOException
406: */
407:
408: public static void renderMenu(ResponseWriter out, List items,
409: int selected, String clientId, String styleClass,
410: UIComponent component) throws IOException {
411: // // debug lines
412: // out.writeText("startElement select", null);
413: // if (true) return;
414: out.startElement("select", component);
415: out.writeAttribute("name", clientId, "id");
416: out.writeAttribute("id", clientId, "id");
417: if (styleClass != null) {
418: out.writeAttribute("class", styleClass, "styleClass");
419: }
420:
421: Iterator iter = items.iterator();
422: while (iter.hasNext()) {
423: SelectItem si = (SelectItem) iter.next();
424: Integer value = (Integer) si.getValue();
425: String label = si.getLabel();
426: out.startElement("option", component);
427: out.writeAttribute("value", value, null);
428: if (value.intValue() == selected) {
429: out.writeAttribute("selected", "selected", null);
430: }
431: out.writeText(label, null);
432: }
433: out.endElement("select");
434: }
435:
436: /** Return the form ID of the form containing the given component */
437: public static String getFormId(FacesContext context,
438: UIComponent component) {
439: while (component != null && !(component instanceof UIForm)) {
440: component = component.getParent();
441: }
442: if (component instanceof UIForm)
443: return ((UIForm) component).getId();
444: return null;
445: }
446:
447: /**
448: * @param context FacesContext for the request we are processing
449: * @param writer ResponseWriter to be used
450: * @param key key to use to look up the value in the request
451: * @param path path to the file
452: * @exception IOException if an input/output error occurs while rendering
453: */
454: public static void writeExternalJSDependencies(
455: FacesContext context, ResponseWriter writer, String key,
456: String path) throws IOException {
457: HttpServletRequest req = (HttpServletRequest) context
458: .getExternalContext().getRequest();
459: String jsInclude = (String) req.getAttribute(key);
460:
461: if (jsInclude == null || jsInclude.length() == 0) {
462: // include default stylesheet
463: jsInclude = "<script type=\"text/javascript\" src=\""
464: + path + "\"></script>\n";
465: req.setAttribute(key, jsInclude);
466: writer.write(jsInclude);
467: }
468: }
469:
470: /**
471: * @param context FacesContext for the request we are processing
472: * @param writer ResponseWriter to be used
473: * @param key key to use to look up the value in the request
474: * @param path path to the file
475: * @exception IOException if an input/output error occurs while rendering
476: */
477: public static void writeExternalCSSDependencies(
478: FacesContext context, ResponseWriter writer, String key,
479: String path) throws IOException {
480: HttpServletRequest req = (HttpServletRequest) context
481: .getExternalContext().getRequest();
482: String cssInclude = (String) req.getAttribute(key);
483:
484: if (cssInclude == null || cssInclude.length() == 0) {
485: // include default stylesheet
486: cssInclude = "<link href=\""
487: + path
488: + "\" type=\"text/css\" rel=\"stylesheet\" media=\"all\" />\n";
489: req.setAttribute(key, cssInclude);
490: writer.write(cssInclude);
491: }
492: }
493:
494: public static void writeAttr(Writer inWriter, String inAttr,
495: String inAttrValue) throws IOException {
496: if (inWriter == null || inAttr == null || inAttrValue == null)
497: return;
498:
499: inWriter.write(" ");
500: inWriter.write(inAttr);
501: inWriter.write("=\"");
502: inWriter.write(inAttrValue);
503: inWriter.write("\" ");
504: }
505:
506: }
|