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.renderer;
042:
043: import java.io.IOException;
044: import java.util.Map;
045:
046: import javax.faces.FacesException;
047: import javax.faces.component.UIComponent;
048: import javax.faces.context.FacesContext;
049: import javax.faces.context.ResponseWriter;
050: import javax.faces.el.ValueBinding;
051: import javax.faces.render.Renderer;
052: import com.sun.rave.web.ui.component.Field;
053: import com.sun.rave.web.ui.component.TextArea;
054: import com.sun.rave.web.ui.component.Upload;
055: import com.sun.rave.web.ui.component.Icon;
056: import com.sun.rave.web.ui.theme.Theme;
057: import com.sun.rave.web.ui.theme.ThemeStyles;
058: import com.sun.rave.web.ui.theme.ThemeImages;
059: import com.sun.rave.web.ui.util.ConversionUtilities;
060: import com.sun.rave.web.ui.util.LogUtil;
061: import com.sun.rave.web.ui.util.MessageUtil;
062: import com.sun.rave.web.ui.util.RenderingUtilities;
063: import com.sun.rave.web.ui.util.ThemeUtilities;
064:
065: /**
066: * <p>Renderer for {@link com.sun.rave.web.ui.component.TextField} components.</p>
067: */
068: public class FieldRenderer extends Renderer {
069:
070: /**
071: * <p>The list of attribute names in the HTML 4.01 Specification that
072: * correspond to the entity type <em>%events;</em>.</p>
073: */
074: public static final String[] STRING_ATTRIBUTES = { "onBlur",
075: "onChange", "onClick", "onDblClick", //NOI18N
076: "onFocus", "onMouseDown", "onMouseUp", "onMouseOver", //NOI18N
077: "onMouseMove", "onMouseOut", //NOI18N
078: "onKeyDown", "onKeyPress", "onKeyUp", "onSelect" //NOI18N
079: };
080:
081: public static final String SPACER_ID = "_spacer";
082:
083: private final static boolean DEBUG = false;
084:
085: public void encodeEnd(FacesContext context, UIComponent component)
086: throws IOException {
087:
088: if (!(component instanceof Field)) {
089: Object[] params = { component.toString(),
090: this .getClass().getName(), Field.class.getName() };
091: String message = MessageUtil.getMessage(
092: "com.sun.rave.web.ui.resources.LogMessages", //NOI18N
093: "Renderer.component", params); //NOI18N
094: throw new FacesException(message);
095: }
096:
097: renderField(context, (Field) component, "text",
098: getStyles(context));
099: }
100:
101: /**
102: * <p>Render the TextField depending on the value of the
103: * <code>type</code> property.</p>
104: * @param context <code>FacesContext</code> for the current request
105: * @param component <code>UIComponent</code> to be rendered
106: * @exception IOException if an input/output error occurs
107: */
108: public boolean renderField(FacesContext context, Field component,
109: String type, String[] styles) throws IOException {
110:
111: String id = component.getClientId(context);
112: ResponseWriter writer = context.getResponseWriter();
113: UIComponent label = component.getLabelComponent(context,
114: styles[3]);
115: boolean spanRendered = false;
116:
117: if (label != null) {
118: renderOpeningSpan(component, id, styles[2], writer);
119: spanRendered = true;
120: writer.writeText("\n", null);
121: RenderingUtilities.renderComponent(label, context);
122: writer.writeText("\n", null);
123:
124: // Add a 10 px spacer image after the label text so that the
125: // label has the right distance (10px) from the field, since
126: // a developer has no control over the layout in this case.
127: Theme theme = ThemeUtilities.getTheme(context);
128: Icon icon = theme.getIcon(ThemeImages.DOT);
129: icon.setId(component.getId().concat(SPACER_ID));
130: icon.setHeight(1);
131: icon.setWidth(10);
132: RenderingUtilities.renderComponent(icon, context);
133: writer.writeText("\n", null); //NOI18N
134:
135: // Set the ID to use on the inner component
136: if (component instanceof Upload) {
137: id = id.concat(Upload.INPUT_ID);
138: } else {
139: id = id.concat(Field.INPUT_ID);
140: }
141: }
142: if (component.isReadOnly()) {
143: UIComponent text = component.getReadOnlyComponent(context);
144: if (label == null) {
145: text.getAttributes().put("style", component.getStyle());
146: text.getAttributes().put("styleClass",
147: component.getStyleClass());
148: }
149: RenderingUtilities.renderComponent(text, context);
150: } else {
151: renderInput(component, type, id, label == null, styles,
152: context, writer);
153: }
154: if (label != null) {
155: writer.writeText("\n", null);
156: writer.endElement("span");
157: }
158: return spanRendered;
159: }
160:
161: /**
162: * <p>Render the TextField depending on the value of the
163: * <code>type</code> property.</p>
164: * @param context <code>FacesContext</code> for the current request
165: * @param component <code>UIComponent</code> to be rendered
166: * @exception IOException if an input/output error occurs
167: */
168: private void renderOpeningSpan(Field component, String id,
169: String hiddenStyle, ResponseWriter writer)
170: throws IOException {
171:
172: writer.startElement("span", component); //NOI18N
173: writer.writeAttribute("id", id, "id"); //NOI18N
174:
175: String style = component.getStyle();
176: if (style != null && style.length() > 0) {
177: writer.writeAttribute("style", style, "style"); //NOI18N
178: }
179:
180: style = getStyleClass(component, hiddenStyle);
181: if (style != null) {
182: writer.writeAttribute("class", style, "class"); //NOI18N
183: }
184: }
185:
186: protected void renderInput(Field component, String type, String id,
187: boolean renderUserStyles, String[] styles,
188: FacesContext context, ResponseWriter writer)
189: throws IOException {
190:
191: if (component instanceof TextArea) {
192: writer.startElement("textarea", component); //NOI18N
193: int rows = ((TextArea) component).getRows();
194: if (rows > 0) {
195: writer.writeAttribute("rows", String.valueOf(rows),
196: "rows"); // NOI18N
197: }
198: int columns = component.getColumns();
199:
200: if (columns > 0) {
201: writer.writeAttribute("cols", //NOI18N
202: String.valueOf(columns), "columns"); //NOI18N
203: }
204: } else {
205: writer.startElement("input", component); //NOI18N
206: writer.writeAttribute("type", type, null); //NOI18N
207:
208: int columns = component.getColumns();
209: if (columns > 0) {
210: writer.writeAttribute("size", //NOI18N
211: String.valueOf(columns), "columns"); //NOI18N
212: }
213: }
214:
215: writer.writeAttribute("id", id, null); //NOI18N
216: writer.writeAttribute("name", id, null); //NOI18N
217:
218: if (component.isDisabled()) {
219: writer.writeAttribute("disabled", "disabled", null); //NOI18N
220: }
221:
222: int maxlength = component.getMaxLength();
223: if (maxlength > 0) {
224: writer.writeAttribute("maxlength", String
225: .valueOf(maxlength), "maxLength"); // NOI18N
226: }
227:
228: // Render attributes based on the "toolTip" properties
229: String toolTip = component.getToolTip();
230: if (toolTip != null && toolTip.length() > 0) {
231: writer.writeAttribute("title", toolTip, "toolTip"); // NOI18N
232: }
233:
234: int tabIndex = component.getTabIndex();
235: if (tabIndex > 0) {
236: writer.writeAttribute("tabindex", // NOI18N
237: String.valueOf(tabIndex), "tabIndex"); // NOI18N
238: }
239:
240: RenderingUtilities.writeStringAttributes(component, writer,
241: STRING_ATTRIBUTES);
242:
243: String styleClass = null;
244: if (component.isDisabled()) {
245: styleClass = styles[1];
246: } else {
247: styleClass = styles[0];
248: }
249:
250: String style = null;
251:
252: if (renderUserStyles) {
253:
254: String compStyleClass = getStyleClass(component, styles[2]);
255: if (compStyleClass != null) {
256: styleClass = compStyleClass + " " + styleClass;
257: }
258:
259: style = component.getStyle();
260: if (style != null && style.length() == 0) {
261: style = null;
262: }
263: }
264:
265: writer.writeAttribute("class", styleClass, null); //NOI18N
266: if (style != null) {
267: writer.writeAttribute("style", style, null); //NOI18N
268: }
269:
270: // Record the value that is rendered.
271: // Note that getValueAsString conforms to JSF conventions
272: // for NULL values, in that it returns "" if the component
273: // value is NULL. This value cannot be trusted since
274: // the fidelity of the data must be preserved, i.e. if the
275: // value is null, it must remain null if the component is unchanged
276: // by the user..
277: //
278: // If submittedValue is not null then the component's
279: // model value or local value has not been updated
280: // therefore assume that this is an immediate or premature
281: // render response. Therefore just assume that if the rendered
282: // value was null, the saved information is still valid.
283: //
284: if (component.getSubmittedValue() == null) {
285: ConversionUtilities.setRenderedValue(component, component
286: .getValue());
287: }
288:
289: // Still call the component's getValueAsString method
290: // in order to render it.
291: //
292: String value = component.getValueAsString(context);
293: if (component instanceof TextArea) {
294: writer.writeText(value, "value"); //NOI18N
295: writer.endElement("textarea");
296: } else {
297: if (value != null) {
298: writer.writeAttribute("value", value, "value"); //NOI18N
299: }
300: writer.endElement("input");
301: }
302: }
303:
304: // Prepend the hidden style to the user's added style if necessary
305: protected String getStyleClass(Field component, String hiddenStyle) {
306:
307: String style = component.getStyleClass();
308: if (style != null && style.length() == 0) {
309: style = null;
310: }
311:
312: if (!component.isVisible()) {
313: if (style == null) {
314: style = hiddenStyle;
315: } else {
316: style = style + " " + hiddenStyle;
317: }
318: }
319: return style;
320: }
321:
322: String[] getStyles(FacesContext context) {
323: Theme theme = ThemeUtilities.getTheme(context);
324: String[] styles = new String[4];
325: styles[0] = theme.getStyleClass(ThemeStyles.TEXT_FIELD);
326: styles[1] = theme
327: .getStyleClass(ThemeStyles.TEXT_FIELD_DISABLED);
328: styles[2] = theme.getStyleClass(ThemeStyles.HIDDEN);
329: styles[3] = "";
330: return styles;
331: }
332:
333: /**
334: * Log an error - only used during development time.
335: */
336: void log(String s) {
337: System.out.println(this .getClass().getName() + "::" + s); //NOI18N
338: }
339:
340: /**
341: * Decode the component component
342: * @param context The FacesContext associated with this request
343: * @param component The TextField component to decode
344: */
345: public void decode(FacesContext context, UIComponent component) {
346: HiddenFieldRenderer.decodeInput(context, component);
347: }
348:
349: /**
350: * <p>No-op.</p>
351: *
352: * @param context {@link FacesContext} for the response we are creating
353: * @param component {@link UIComponent} whose children are to be rendered
354: *
355: * @exception IOException if an input/output error occurs while rendering
356: * @exception NullPointerException if <code>context</code>
357: * or <code>component</code> is <code>null</code>
358: */
359: public void encodeChildren(FacesContext context,
360: UIComponent component) {
361: return;
362: }
363:
364: /**
365: * <p>No-op</p>
366: *
367: * @param context {@link FacesContext} for the request we are processing
368: * @param component {@link UIComponent} to be rendered
369: */
370: public void encodeBegin(FacesContext context, UIComponent component) {
371: return;
372: }
373:
374: /**
375: * <p>Returns true, meaning that the FieldRenderer is responsible
376: * for rendering the children the component it is asked to render.</p>
377: * @return false;
378: */
379: public boolean getRendersChildren() {
380: return true;
381: }
382:
383: }
|