001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package com.sun.rave.web.ui.util;
042:
043: import java.io.IOException;
044: import java.io.PrintWriter;
045: import java.io.Writer;
046: import java.util.Iterator;
047: import java.util.Map;
048: import java.util.HashMap;
049: import java.text.MessageFormat;
050:
051: import javax.servlet.RequestDispatcher;
052: import javax.servlet.ServletContext;
053: import javax.servlet.ServletOutputStream;
054: import javax.servlet.ServletRequest;
055: import javax.servlet.ServletResponse;
056: import javax.servlet.http.HttpServletResponseWrapper;
057: import javax.servlet.http.HttpServletResponse;
058:
059: import javax.faces.component.ActionSource;
060: import javax.faces.component.UIComponent;
061: import javax.faces.context.FacesContext;
062: import javax.faces.context.ResponseWriter;
063:
064: import com.sun.rave.web.ui.component.Body;
065: import com.sun.rave.web.ui.component.Icon;
066: import com.sun.rave.web.ui.theme.Theme;
067: import com.sun.rave.web.ui.theme.ThemeImages;
068: import com.sun.rave.web.ui.theme.ThemeStyles;
069: import java.util.ArrayList;
070: import javax.faces.component.UIParameter;
071:
072: /**
073: *
074: * @author avk
075: */
076: public class RenderingUtilities {
077:
078: /** Creates a new instance of RenderingUtilities. */
079: public RenderingUtilities() {
080: }
081:
082: /**
083: * Render a component.
084: * @param component The component to render
085: * @param context The FacesContext of the request
086: *
087: */
088: public static void renderComponent(UIComponent component,
089: FacesContext context) throws IOException {
090:
091: if (!component.isRendered()) {
092: return;
093: }
094:
095: // this is a workaround a jsf bug in tables where it caches the
096: // client id. We are forcing the caching not to happen.
097: // this could be a potential source of performance issues if
098: // it turns out the jsf folks really wanted this
099:
100: String id = component.getId();
101: if (id != null) {
102: component.setId(id);
103: }
104: component.encodeBegin(context);
105: if (component.getRendersChildren()) {
106: component.encodeChildren(context);
107: } else {
108: Iterator kids = component.getChildren().iterator();
109: while (kids.hasNext()) {
110: UIComponent kid = (UIComponent) kids.next();
111: renderComponent(kid, context);
112: }
113: }
114: component.encodeEnd(context);
115: }
116:
117: /**
118: * This method goes through an array of possible attribute names,
119: * evaluates if they have been set on the component, and writes
120: * them out using the specified writer.
121: * @param component The component being rendered
122: * @param writer The writer to use to write the attributes
123: * @param possibleAttributes String attributes that are treated as
124: * passthrough for this component
125: */
126: public static void writeStringAttributes(UIComponent component,
127: ResponseWriter writer, String[] possibleAttributes)
128: throws IOException {
129:
130: // Get the rest of the component attributes and display them
131: Map attributes = component.getAttributes();
132:
133: int numNames = possibleAttributes.length;
134: String attributeName = null;
135: Object attributeValue;
136:
137: for (int counter = 0; counter < numNames; counter++) {
138: attributeName = possibleAttributes[counter];
139: attributeValue = attributes.get(attributeName);
140: if (attributeValue != null) {
141: writer.writeAttribute(attributeName.toLowerCase(),
142: String.valueOf(attributeValue), attributeName);
143: }
144: }
145: }
146:
147: /**
148: * Add any attributes on the specified list directly to the specified
149: * ResponseWriter for which the specified UIComponent has a non-null String
150: * value. This method may be used to "pass through" commonly used attribute
151: * name/value pairs with a minimum of code. Attribute names are converted to
152: * lower case in the rendered output. Any name/value pairs in the extraHtml
153: * String shall take precedence over attribute values.
154: *
155: * @param context FacesContext for the current request.
156: * @param component EditableValueHolder component whose submitted value is
157: * to be stored.
158: * @param writer ResponseWriter to which the element start should be rendered.
159: * @param names List of attribute names to be passed through.
160: * @param extraHtml Extra name/value pairs to be rendered.
161: *
162: * @exception IOException if an input/output error occurs
163: */
164: public static void writeStringAttributes(UIComponent component,
165: ResponseWriter writer, String names[], String extraHtml)
166: throws IOException {
167: if (component == null || names == null) {
168: return;
169: }
170: Map attributes = component.getAttributes();
171: Object value;
172: for (int i = 0; i < names.length; i++) {
173: // Special case for names matching "valign" instead of "align".
174: if (extraHtml == null
175: || extraHtml.indexOf(names[i] + "=") != 0
176: && extraHtml.indexOf(" " + names[i] + "=") == -1) {
177: value = attributes.get(names[i]);
178: if (value != null) {
179: if (value instanceof String) {
180: writer.writeAttribute(names[i].toLowerCase(),
181: (String) value, names[i]);
182: } else {
183: writer.writeAttribute(names[i].toLowerCase(),
184: value.toString(), names[i]);
185: }
186: }
187: }
188: }
189: // Render extra HTML attributes.
190: renderExtraHtmlAttributes(writer, extraHtml);
191: }
192:
193: /**
194: * This method will output a hidden field for use with Params and components
195: * that need to submit a value through a hidden field.
196: * Note: The name of the hidden field will be written as is. For Params
197: * no encoding inside the form is done. This is intentional.
198: * @param writer The writer to use to write the attributes
199: * @param id The identifier of the hidden field.
200: * passthrough for this component
201: */
202: public static void renderHiddenField(UIComponent component,
203: ResponseWriter writer, String id, String value)
204: throws IOException {
205: if (id == null) {
206: // TODO: when we figure out our exception string strategy, fix this
207: throw new IllegalArgumentException(
208: "An f:param tag had a null name attribute");
209: }
210:
211: writer.startElement("input", component); //NOI18N
212: writer.writeAttribute("id", id, null); //NOI18N
213: writer.writeAttribute("name", id, null); //NOI18N
214: if (value != null) {
215: writer.writeAttribute("value", value, null); //NOI18N
216: }
217: writer.writeAttribute("type", "hidden", null); //NOI18N
218: writer.endElement("input"); //NOI18N
219: }
220:
221: /**
222: * <p>Return a space-separated list of CSS style classes to render for
223: * this component, or <code>null</code> for none.</p>
224: *
225: * @param component <code>UIComponent</code> for which to calculate classes
226: * @param styles Additional styles specified by the renderer
227: */
228: public static String getStyleClasses(FacesContext context,
229: UIComponent component, String styles) {
230: String styleClass = (String) component.getAttributes().get(
231: "styleClass");
232:
233: boolean componentNotVisible = !isVisible(component);
234:
235: if (componentNotVisible) {
236: String hiddenStyleClass = ThemeUtilities.getTheme(context)
237: .getStyleClass(ThemeStyles.HIDDEN);
238: if (styleClass != null) {
239: styleClass += " " + hiddenStyleClass;
240: } else {
241: styleClass = hiddenStyleClass;
242: }
243: }
244:
245: if (styleClass != null) {
246: if (styles != null) {
247: return styleClass + " " + styles;
248: } else {
249: return styleClass;
250: }
251: } else {
252: if (styles != null) {
253: return styles;
254: } else {
255: return null;
256: }
257: }
258: }
259:
260: /**
261: *
262: */
263: public static void renderStyleClass(FacesContext context,
264: ResponseWriter writer, UIComponent component,
265: String extraStyles) throws IOException {
266: String classes = getStyleClasses(context, component,
267: extraStyles);
268: if (classes != null) {
269: writer.writeAttribute("class", classes, "styleClass");
270: }
271: }
272:
273: /**
274: * Helper method to render style classes when name/value pairs are given
275: * via an extraHtml String. This method will append the given style to the
276: * class name/value pair found in the extraHtml String. The class name/value
277: * is removed from the returned extraHtml String so that developers may
278: * invoke the writeStringAttributes method without rendering the style
279: * class, again.
280: *
281: * @param context FacesContext for the current request.
282: * @param component The UIComponent component to be rendered.
283: * @param writer ResponseWriter to which the element start should be rendered.
284: * @param style The style to append to the component's styleClass property.
285: * @param extraHtml Extra name/value pairs to be rendered.
286: */
287: public static String renderStyleClass(FacesContext context,
288: ResponseWriter writer, UIComponent component,
289: String styleClass, String extraHtml) throws IOException {
290: if (styleClass != null) {
291: int first = -1;
292: if (extraHtml != null
293: && (first = extraHtml.indexOf("class=")) != -1) {
294: try {
295: // Concat given class value with styleClass attribute.
296: int quote = first + 6; // Quote char index.
297: char ch = extraHtml.charAt(quote); // Get quote char.
298: int last = extraHtml.indexOf(ch, quote + 1); // Last index.
299: String s = extraHtml.substring(first, last + 1); // Get name/value pair
300: extraHtml = extraHtml.replaceAll(s, ""); // Remove substring.
301: s = s.substring(7, s.length() - 1); // Remove quote chars.
302: styleClass = s + " " + styleClass; // Append styleClass.
303: } catch (IndexOutOfBoundsException e) {
304: }
305: }
306: renderStyleClass(context, writer, component, styleClass);
307: }
308: return extraHtml;
309: }
310:
311: /**
312: *
313: */
314: public static String getJavascriptId(FacesContext context,
315: UIComponent component) {
316: String client = component.getClientId(context);
317: return client.replace(':', '_');
318: }
319:
320: /**
321: *
322: */
323: public static boolean isPortlet(FacesContext context) {
324: if (context.getExternalContext().getContext() instanceof ServletContext) {
325: return false;
326: }
327: return true;
328: }
329:
330: /**
331: * Get the client ID of the last component to have focus.
332: */
333: public static String getLastClientID(FacesContext context) {
334: return (String) context.getExternalContext().getRequestMap()
335: .get(Body.FOCUS_PARAM);
336: }
337:
338: /**
339: * Set the client ID of the last component to have focus.
340: */
341: public static void setLastClientID(FacesContext context,
342: String clientId) {
343: context.getExternalContext().getRequestMap().put(
344: Body.FOCUS_PARAM, clientId);
345: }
346:
347: /**
348: * Helper function to render a transparent spacer image.
349: *
350: *
351: * @param writer The current ResponseWriter
352: * @param component The uicomponent
353: * @param height The value to use for the image height attribute
354: * @param width The value to use for the image width attribute
355: */
356: public static void renderSpacer(ResponseWriter writer,
357: UIComponent component, String dotSrc, int height, int width)
358: throws IOException {
359:
360: if (height == 0 && width == 0) {
361: return;
362: }
363: writer.startElement("img", component);
364: writer.writeAttribute("src", dotSrc, null); // NOI18N
365: writer.writeAttribute("alt", "", null); // NOI18N
366: writer.writeAttribute("border", "0", null); // NOI18N
367: writer.writeAttribute("height", new Integer(height), null); // NOI18N
368: writer.writeAttribute("width", new Integer(width), null); // NOI18N
369: writer.endElement("img"); // NOI18N
370:
371: }
372:
373: /**
374: * Helper function to render a transparent spacer image.
375: *
376: *
377: * @param writer The current ResponseWriter
378: * @param component The uicomponent
379: * @param height The value to use for the image height attribute
380: * @param width The value to use for the image width attribute
381: */
382: public static void renderSpacer(FacesContext context,
383: ResponseWriter writer, UIComponent component, int height,
384: int width) throws IOException {
385:
386: if (height == 0 && width == 0) {
387: return;
388: }
389: Theme theme = ThemeUtilities.getTheme(context);
390: String dotSrc = theme.getIcon(ThemeImages.DOT).getUrl();
391: renderSpacer(writer, component, dotSrc, height, width);
392: }
393:
394: /**
395: * Helper function to render theme scripts
396: *
397: *
398: * @param context containing theme
399: * @param writer The current ResponseWriter
400: * @param component The uicomponent
401: */
402: public static void renderJavaScript(UIComponent component,
403: Theme theme, FacesContext context, ResponseWriter writer)
404:
405: throws IOException {
406:
407: String javascripts[] = theme.getGlobalJSFiles();
408: for (int i = 0; i < javascripts.length; i++) {
409: writer.startElement("script", component); // NOI18N
410: writer.writeAttribute("type", "text/javascript", null); // NOI18N
411: writer.writeURIAttribute("src", javascripts[i], null); // NOI18N
412: writer.endElement("script"); // NOI18N
413: writer.write("\n"); // NOI18N
414: }
415: }
416:
417: /**
418: * Helper function to render theme stylesheet link(s)
419: *
420: *
421: * @param context containing theme
422: * @param writer The current ResponseWriter
423: * @param component The uicomponent
424: */
425: public static void renderStyleSheetLink(UIComponent component,
426: Theme theme, FacesContext context, ResponseWriter writer)
427: throws IOException {
428:
429: //Master.
430: String master = theme.getPathToMasterStylesheet();
431:
432: if (master != null) {
433: writer.startElement("link", component); //NOI18N
434: writer.writeAttribute("rel", "stylesheet", null); //NOI18N
435: writer.writeAttribute("type", "text/css", null); //NOI18N
436: writer.writeURIAttribute("href", master, null); //NOI18N
437: writer.endElement("link"); //NOI18N
438: writer.write("\n"); //NOI18N
439: }
440:
441: //browser specific stuff.
442: String browserSS = theme.getPathToStylesheet(context);
443: if (browserSS != null) {
444: writer.startElement("link", component); //NOI18N
445: writer.writeAttribute("rel", "stylesheet", null); //NOI18N
446: writer.writeAttribute("type", "text/css", null); //NOI18N
447: writer.writeURIAttribute("href", browserSS, null); //NOI18N
448: writer.endElement("link"); //NOI18N
449: writer.write("\n"); //NOI18N
450: }
451:
452: String stylesheets[] = theme.getGlobalStylesheets();
453: for (int i = 0; i < stylesheets.length; i++) {
454: writer.startElement("link", component); //NOI18N
455: writer.writeAttribute("rel", "stylesheet", null); //NOI18N
456: writer.writeAttribute("type", "text/css", null); //NOI18N
457: writer.writeURIAttribute("href", stylesheets[i], null); //NOI18N
458: writer.endElement("link"); //NOI18N
459: writer.write("\n"); //NOI18N
460: }
461: }
462:
463: /**
464: * Helper function to render theme stylesheet definitions inline
465: *
466: *
467: * @param context containing theme
468: * @param writer The current ResponseWriter
469: * @param component The uicomponent
470: */
471: public static void renderStyleSheetInline(UIComponent component,
472: Theme theme, FacesContext context, ResponseWriter writer)
473: throws IOException {
474:
475: writer.startElement("style", component); //NOI18N
476: writer.writeAttribute("type", "text/css", null); //NOI18N
477: writer.write("\n"); //NOI18N
478: String master = theme.getPathToMasterStylesheet();
479:
480: if (master != null) {
481: writer.write("@import(\""); //NOI18N
482: writer.write(master); //NOI18N
483: writer.write("\");");
484: writer.write("\n"); //NOI18N
485: }
486:
487: //browser specific stuff.
488: String browserSS = theme.getPathToStylesheet(context);
489: if (browserSS != null) {
490: writer.write("@import(\""); //NOI18N
491: writer.write(browserSS); //NOI18N
492: writer.write("\");");
493: writer.write("\n"); //NOI18N
494: }
495:
496: String stylesheets[] = theme.getGlobalStylesheets();
497: for (int i = 0; i < stylesheets.length; i++) {
498: writer.write("@import(\""); //NOI18N
499: writer.write(stylesheets[i]); //NOI18N
500: writer.write("\");");
501: writer.write("\n"); //NOI18N
502: }
503: writer.endElement("style"); //NOI18N
504: }
505:
506: /**
507: * Perform a <code>RequestDispatcher.include</code> of the specified URI
508: * <code>jspURI</code>.
509: * <p>
510: * The path identifed by <code>jspURI</code> must begin with
511: * a <code><f:subview></code> tag. The URI must not have
512: * as part of its path the FacesServlet mapping. For example if the
513: * FacesServlet mapping maps to <code>/faces/*</code> then
514: * <code>jspURI</code> must not have <code>/faces/</code> as part of
515: * its path.
516: * </p>
517: * <p>
518: * If <code>jspUIR</code> is a relative path then the
519: * request context path is prepended to it.
520: * </p>
521: * @param context the <code>FacesContext</code> for this request
522: * @param writer the <code>ResponseWrite</code> destination for the
523: * rendered output
524: * @param jspURI the URI identifying a JSP page to be included.
525: * @throws IOException if response can't be written or <code>jspURI</code>
526: * cannot be included. Real cause is chained.
527: */
528: public static void includeJsp(FacesContext context,
529: ResponseWriter writer, String jspURI) throws IOException {
530:
531: class ResponseWrapper extends HttpServletResponseWrapper {
532: private PrintWriter printWriter;
533:
534: public ResponseWrapper(HttpServletResponse response,
535: Writer writer) {
536: super ((HttpServletResponse) response);
537: this .printWriter = new PrintWriter(writer);
538: }
539:
540: public PrintWriter getWriter() {
541: return printWriter;
542: }
543:
544: public ServletOutputStream getOutputStream()
545: throws IOException {
546: throw new IllegalStateException();
547: }
548:
549: public void resetBuffer() {
550: }
551: }
552:
553: if (jspURI == null) {
554: return;
555: }
556:
557: // prepend the request path if there is one in this path is not
558: // a relative path. It appears that the servlet context algorithm
559: // differs from the JspRuntime algorithm that allowed a relative
560: // path in the lockhart wizard.
561: //
562: try {
563: if (!jspURI.startsWith("/")) { //NOI18N
564: String contextPath = context.getExternalContext()
565: .getRequestContextPath();
566: jspURI = contextPath.concat("/").concat(jspURI); //NOI18N
567: }
568:
569: ServletRequest request = (ServletRequest) context
570: .getExternalContext().getRequest();
571: ServletResponse response = (ServletResponse) context
572: .getExternalContext().getResponse();
573:
574: RequestDispatcher rd = request.getRequestDispatcher(jspURI);
575:
576: // JSF is already buffering and suppressing output.
577: //
578: rd.include(request, new ResponseWrapper(
579: (HttpServletResponse) response, writer));
580:
581: } catch (Exception e) {
582: throw (IOException) new IOException().initCause(e);
583: }
584: }
585:
586: public static final String NL = "/n"; //NOI18N
587: public static final String SCRIPT = "script"; //NOI18N
588: public static final String TYPE = "type"; //NOI18N
589: public static final String SRC = "src"; //NOI18N
590: public static final String MEDIA = "text/javascript"; //NOI18N
591: // SJWUIC - Sun Java Web User Interface Components
592: public static final String SJWUIC_JSFILE = "sjwuic_jsfile"; //NOI18N
593: public static final String USCORE = "_"; //NOI18N
594: public static final String COLON = ":"; //NOI18N
595: public static final char USCORE_CHAR = '_'; //NOI18N
596: public static final char COLON_CHAR = ':'; //NOI18N
597:
598: /**
599: * Return true if the javascript file identified by
600: * <code>includeFile</code> is included.
601: * Return false if <code>includeFile</code> is null
602: * or there is no javascript file identified by
603: * <code>includeFile</code>.
604: * <p>
605: * renderJsInclude maintains a RequestMap attribute
606: * "sjwuic_jsfile" that resolves to a Map containing
607: * files that have previously been included.
608: * </p>
609: * @param context The current FacesContext
610: * @param component The current component being rendered
611: * @param theme The Theme to use to locate the Js file
612: * @param writer The current ResponseWriter
613: * @param includeFile The Js file to include
614: */
615: public static boolean renderJsInclude(FacesContext context,
616: UIComponent component, Theme theme, ResponseWriter writer,
617: String includeFile) throws IOException {
618:
619: if (includeFile == null) {
620: return false;
621: }
622:
623: Map requestMap = context.getExternalContext().getRequestMap();
624: Map jsFileMap = (Map) requestMap.get(SJWUIC_JSFILE);
625: if (jsFileMap == null) {
626: jsFileMap = new HashMap();
627: requestMap.put(SJWUIC_JSFILE, jsFileMap);
628: }
629:
630: String jsFile = (String) jsFileMap.get(includeFile);
631: if (jsFile != null) {
632: return true;
633: }
634:
635: jsFile = theme.getPathToJSFile(includeFile);
636: if (jsFile == null) {
637: return false;
638: }
639: jsFileMap.put(includeFile, jsFile);
640:
641: writer.startElement(SCRIPT, component);
642: writer.writeAttribute(TYPE, MEDIA, null);
643: writer.writeURIAttribute(SRC, jsFile, null);
644: writer.endElement(SCRIPT);
645:
646: return true;
647: }
648:
649: /**
650: * Return true if markup is rendered that creates a javascript
651: * object instance.
652: * Return false if markup is not rendered.
653: * <p>
654: * Look for an attribute on the component identified by the
655: * <code>attribute</code> parameter. If it exists and matches
656: * the component's clientId after ":" has been replaced with "_"
657: * return true. Otherwise render markup that creates a
658: * javascript object named by the clientId where ":" is replaced
659: * with "_" and then add this value to the component's
660: * attribute map as the value of an attribute identified by
661: * the <code>attribute</code> parameter.
662: * </p>
663: * @param context The current FacesContext
664: * @param component The current component being rendered
665: * @param theme The Theme to use to locate the Js file
666: * @param writer The current ResponseWriter
667: * @param includeFile The Js file to include
668: */
669:
670: private static final String newJsId = "var {0} = new {1}(''{2}'');"; //NOI18N
671: private static final String newJsIdAndArgs = "var {0} = new {1}(''{2}'',{3});"; //NOI18N
672:
673: public static boolean renderJsObject(FacesContext context,
674: UIComponent component, ResponseWriter writer,
675: String objectName, String jsObjectClass, String arguments)
676: throws IOException {
677:
678: if (context == null || component == null || writer == null
679: || jsObjectClass == null) {
680: return false;
681: }
682: if (objectName == null) {
683: objectName = jsObjectClass;
684: }
685: String clientId = component.getClientId(context);
686: if (clientId == null) {
687: return false;
688: }
689: String jsObject = getJsObjectName(clientId, objectName);
690:
691: String format = arguments != null ? newJsIdAndArgs : newJsId;
692: Object[] args = new Object[] { jsObject, jsObjectClass,
693: clientId, arguments };
694:
695: writer.startElement(SCRIPT, component);
696: writer.writeAttribute(TYPE, MEDIA, null);
697: writer.writeText(MessageFormat.format(format, args), null);
698: writer.endElement(SCRIPT);
699:
700: return true;
701: }
702:
703: public static String getJsObjectName(FacesContext context,
704: UIComponent component, String objectPrefix) {
705: return getJsObjectName(component.getClientId(context),
706: objectPrefix);
707: }
708:
709: public static String getJsObjectName(String clientId,
710: String objectPrefix) {
711: return objectPrefix.concat(USCORE).concat(
712: clientId.replace(COLON_CHAR, USCORE_CHAR));
713: }
714:
715: /**
716: * Helper method to render extra attributes.
717: *
718: * @param writer <code>ResponseWriter</code> to which the element
719: * end should be rendered
720: * @param extraHtml Extra HTML appended to the tag enclosing the header
721: *
722: * @exception IOException if an input/output error occurs
723: */
724: public static void renderExtraHtmlAttributes(ResponseWriter writer,
725: String extraHtml) throws IOException {
726: if (extraHtml == null) {
727: return;
728: }
729:
730: int n = extraHtml.length();
731: int i = 0;
732: while (i < n) {
733: StringBuffer name = new StringBuffer();
734: StringBuffer value = new StringBuffer();
735:
736: // Skip extra space characters.
737: while (i < n && Character.isWhitespace(extraHtml.charAt(i))) {
738: i++;
739: }
740:
741: // Find name.
742: for (; i < n; i++) {
743: char c = extraHtml.charAt(i);
744: if (c == '\'' || c == '"') {
745: return; // Not well formed.
746: } else if (c == '=') {
747: break;
748: } else {
749: name.append(c);
750: }
751: }
752: i++; // Skip =
753:
754: // Process quote character.
755: char quote = (i < n) ? extraHtml.charAt(i) : '\0';
756: if (!(quote == '\'' || quote == '"')) {
757: return; // Not well formed.
758: }
759: i++; // Skip quote character.
760:
761: // Find value.
762: for (; i < n; i++) {
763: char c = extraHtml.charAt(i);
764: if (c == quote) {
765: break;
766: } else {
767: value.append(c);
768: }
769: }
770: i++; // Skip quote character.
771:
772: writer.writeAttribute(name.toString(), value.toString(),
773: null); //NOI18N
774: }
775: }
776:
777: /**
778: * Helper function to render a typical URL
779: *
780: *
781: * @param writer The current ResponseWriter
782: * @param component The uicomponent
783: * @param name The attribute name of the url to write out
784: * @param url The value passed in by the developer for the url
785: * @param compPropName The property name of the component's property that
786: * specifies this property. Should be null if same as name.
787: *
788: */
789: public static void renderURLAttribute(FacesContext context,
790: ResponseWriter writer, UIComponent component, String name,
791: String url, String compPropName) throws IOException {
792: if (url == null) {
793: return;
794: }
795:
796: Param paramList[] = getParamList(context, component);
797: StringBuffer sb = new StringBuffer();
798: int i = 0;
799: int len = paramList.length;
800: sb = new StringBuffer();
801:
802: // Don't append context path here as themed images already include it.
803: sb.append(url);
804: if (0 < len) {
805: sb.append("?");
806: }
807: for (i = 0; i < len; i++) {
808: if (0 != i) {
809: sb.append("&");
810: }
811: sb.append(paramList[i].getName());
812: sb.append("=");
813: sb.append(paramList[i].getValue());
814: }
815:
816: String newName = null;
817: if (compPropName != null) {
818: newName = (compPropName.equals(name)) ? null : compPropName;
819: }
820:
821: //<RAVE>
822: //writer.writeURIAttribute(name, context.getExternalContext()
823: // .encodeResourceURL(sb.toString()), newName);
824:
825: if (url.trim().length() != 0)
826: writer.writeURIAttribute(name, context.getExternalContext()
827: .encodeResourceURL(sb.toString()), newName);
828: else
829: writer.writeURIAttribute(name, "", newName);
830: //<RAVE>
831: }
832:
833: static protected Param[] getParamList(FacesContext context,
834: UIComponent command) {
835: ArrayList parameterList = new ArrayList();
836:
837: Iterator kids = command.getChildren().iterator();
838: while (kids.hasNext()) {
839: UIComponent kid = (UIComponent) kids.next();
840:
841: if (kid instanceof UIParameter) {
842: UIParameter uiParam = (UIParameter) kid;
843: Object value = uiParam.getValue();
844: Param param = new Param(uiParam.getName(),
845: (value == null ? null : value.toString()));
846: parameterList.add(param);
847: }
848: }
849:
850: return (Param[]) parameterList.toArray(new Param[parameterList
851: .size()]);
852: }
853:
854: //inner class to store parameter name and value pairs
855: static protected class Param {
856:
857: public Param(String name, String value) {
858: set(name, value);
859: }
860:
861: private String name;
862: private String value;
863:
864: public void set(String name, String value) {
865: this .name = name;
866: this .value = value;
867: }
868:
869: public String getName() {
870: return name;
871: }
872:
873: public String getValue() {
874: return value;
875: }
876: }
877:
878: // This method is written in such a way that you can use it without
879: // using the component.
880:
881: static public void renderSkipLink(String anchorName,
882: String styleClass, String style, String toolTip,
883: Integer tabIndex, UIComponent component,
884: FacesContext context) throws IOException {
885:
886: ResponseWriter writer = context.getResponseWriter();
887:
888: String id = component.getClientId(context);
889: writer.startElement("div", component); //NOI18N
890: if (styleClass != null) {
891: writer.writeAttribute("class", styleClass, null);
892: }
893: if (style != null) {
894: writer.writeAttribute("style", styleClass, null);
895: }
896: writer.write("\n"); //NOI18N
897:
898: StringBuffer buffer = new StringBuffer(128);
899: buffer.append("#");
900: buffer.append(component.getClientId(context));
901: buffer.append("_");
902: buffer.append(anchorName);
903:
904: writer.startElement("a", component); //NOI18N
905: writer.writeAttribute("href", buffer.toString(), null);
906: if (toolTip != null) {
907: writer.writeAttribute("alt", toolTip, null);
908: }
909: if (tabIndex != null) {
910: writer
911: .writeAttribute("tabindex", tabIndex.toString(),
912: null);
913: }
914: writer.write("\n"); //NOI18N
915:
916: // <RAVE>
917: // Rendering 1x1 images in empty links causes layout problems in IE
918: //Icon icon = ThemeUtilities.getTheme(context).getIcon(ThemeImages.DOT);
919: //icon.setParent(component);
920: //icon.setWidth(1);
921: //icon.setHeight(1);
922: //icon.setBorder(0);
923: //icon.setId("icon");
924: //RenderingUtilities.renderComponent(icon, context);
925: //writer.write("\n"); //NOI18N
926: // </RAVE>
927:
928: writer.endElement("a"); //NOI18N
929: writer.write("\n"); //NOI18N
930:
931: writer.endElement("div"); //NOI18N
932: }
933:
934: // This method is written in such a way that you can use it without
935: // using the component.
936:
937: static public void renderAnchor(String anchorName,
938: UIComponent component, FacesContext context)
939: throws IOException {
940:
941: ResponseWriter writer = context.getResponseWriter();
942:
943: StringBuffer buffer = new StringBuffer(128);
944: buffer.append(component.getClientId(context));
945: buffer.append("_");
946: buffer.append(anchorName);
947:
948: writer.startElement("div", component); //NOI18N
949: writer.write("\n"); //NOI18N
950: writer.startElement("a", component); //NOI18N
951: writer.writeAttribute("name", buffer.toString(), null);
952: writer.endElement("a"); //NOI18N
953: writer.write("\n"); //NOI18N
954: writer.endElement("div"); //NOI18N
955: }
956:
957: /**
958: * <p> Return whether the given <code>UIComponent</code> is "visible".
959: * If the property is null, it will return true. Otherwise the value
960: * of the property is returned.</p>
961: *
962: * @param component The <code>UIComponent</code> to check
963: *
964: * @return True if the property is null or true, false otherwise.
965: */
966: public static boolean isVisible(UIComponent component) {
967: Object visible = component.getAttributes().get("visible"); //NOI18N
968: if (visible == null)
969: return true;
970: else
971: return ((Boolean) visible).booleanValue();
972: }
973: }
|