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 com.sun.rave.web.ui.component.Property;
044: import com.sun.rave.web.ui.theme.Theme;
045: import com.sun.rave.web.ui.theme.ThemeStyles;
046: import com.sun.rave.web.ui.util.RenderingUtilities;
047: import com.sun.rave.web.ui.util.ThemeUtilities;
048: import java.io.IOException;
049: import java.util.Iterator;
050: import java.util.List;
051:
052: import javax.faces.component.UIComponent;
053: import javax.faces.context.FacesContext;
054: import javax.faces.context.ResponseWriter;
055: import javax.faces.render.Renderer;
056:
057: public class PropertyRenderer extends Renderer {
058:
059: /**
060: * Creates a new instance of PropertyRenderer.
061: */
062: public PropertyRenderer() {
063: }
064:
065: /**
066: * This renderer renders the component's children.
067: */
068: public boolean getRendersChildren() {
069: return true;
070: }
071:
072: /**
073: * Render a property component.
074: *
075: * @param context The current FacesContext
076: * @param component The Property object to render
077: * @param writer The current ResponseWriter
078: *
079: * @exception IOException if an input/output error occurs
080: */
081: public void encodeEnd(FacesContext context, UIComponent component)
082: throws IOException {
083:
084: if (context == null || component == null) {
085: throw new NullPointerException();
086: }
087:
088: if (!component.isRendered()) {
089: return;
090: }
091:
092: ResponseWriter writer = context.getResponseWriter();
093: Property property = (Property) component;
094:
095: // Get the theme
096: //
097: Theme theme = ThemeUtilities.getTheme(context);
098:
099: // Ideally the PropertyRenderer shouldn't have to know
100: // what it is contained by. But this implementation assumes
101: // that a table has been used as the container and therefore
102: // the responsibility for rendering rows and cells
103: // is left to the PropertyRenderer. This is where a
104: // LayoutComponent could be useful. The PropertyLayoutComponent
105: // knows that a Property consists of a Label and some other
106: // components and lays it out accordingly.
107: //
108: writer.startElement("tr", property);
109:
110: // Its not clear if the developer realizes that the styleClass
111: // and style attributes are applied to a "tr".
112: //
113: String propValue = RenderingUtilities.getStyleClasses(context,
114: component, null);
115: if (propValue != null) {
116: writer.writeAttribute("class", propValue, null);
117: }
118:
119: propValue = property.getStyle();
120: if (propValue != null) {
121: writer.writeAttribute("style", propValue, "style");
122: }
123:
124: writer.startElement("td", property);
125: writer.writeAttribute("valign", "top", null);//NOI18N
126:
127: // Always render the nowrap, and labelAlign even if there
128: // isn't a label.
129: //
130: boolean nowrap = property.isNoWrap();
131: if (nowrap) {
132: writer.writeAttribute("nowrap", "nowrap", null); //NOI18N
133: }
134: String labelAlign = property.getLabelAlign();
135: if (labelAlign != null) {
136: writer.writeAttribute("align", labelAlign, null);
137: }
138:
139: // If overlapLabel is true, then render the TD with colspan 2.
140: // and the property components share the div.
141: // If overlapLabel is false then render two TD's and two DIV's
142: //
143: boolean overlapLabel = property.isOverlapLabel();
144: if (overlapLabel) {
145: writer.writeAttribute("colspan", "2", null);//NOI18N
146: }
147:
148: writer.startElement("div", property);
149: writer.writeAttribute("class", theme
150: .getStyleClass(ThemeStyles.CONTENT_TABLE_COL1_DIV),
151: null);
152:
153: renderLabel(context, property, theme, writer);
154:
155: // If overlapLabel is false, close the label TD and DIV
156: // and open another TD and DIV for the property components.
157: //
158: if (!overlapLabel) {
159: writer.endElement("div");
160: writer.endElement("td");
161: writer.startElement("td", property);
162: writer.startElement("div", property);
163: writer.writeAttribute("class", theme
164: .getStyleClass(ThemeStyles.CONTENT_TABLE_COL2_DIV),
165: null);
166: }
167:
168: renderPropertyComponents(context, property, theme, writer);
169: renderHelpText(context, property, theme, writer);
170:
171: writer.endElement("div");
172: writer.endElement("td");
173: writer.endElement("tr");
174: }
175:
176: /**
177: * Render the property components.
178: * If the <code>content</code> facet it defined it takes precendence
179: * over the existence of child components. If there is not
180: * <code>content</code> facet, then the children are rendered.
181: *
182: * @param context The current FacesContext
183: * @param property The Property object to render
184: * @param theme The Theme to reference.
185: * @param writer The current ResponseWriter
186: *
187: * @exception IOException if an input/output error occurs
188: */
189: protected void renderPropertyComponents(FacesContext context,
190: Property property, Theme theme, ResponseWriter writer)
191: throws IOException {
192:
193: // The presence of a content facet takes priority over
194: // children. I'm not sure why there is both a content facet
195: // and children.
196: //
197: UIComponent content = property.getContentComponent();
198: if (content != null) {
199: RenderingUtilities.renderComponent(content, context);
200: return;
201: }
202:
203: // If there isn't a content facet, render any children.
204: //
205: Iterator childrenIterator = property.getChildren().iterator();
206: while (childrenIterator.hasNext()) {
207: UIComponent child = (UIComponent) childrenIterator.next();
208: RenderingUtilities.renderComponent(child, context);
209: }
210: }
211:
212: /**
213: * Render a label for the propertySheetSection
214: *
215: * @param context The current FacesContext
216: * @param property The Property object to render
217: * @param theme The Theme to reference.
218: * @param writer The current ResponseWriter
219: *
220: * @exception IOException if an input/output error occurs
221: */
222: protected void renderLabel(FacesContext context, Property property,
223: Theme theme, ResponseWriter writer) throws IOException {
224:
225: // Render a label facet if there is one, else a span
226: // and " ".
227: //
228: UIComponent label = property.getLabelComponent();
229: if (label != null) {
230: RenderingUtilities.renderComponent(label, context);
231: return;
232: }
233:
234: writer.startElement("span", property);
235:
236: // The class selector should be minimally themeable.
237: // Not sure why we even need it for "nbsp".
238: // Other components offer a labelLevel attribute as well.
239: //
240: writer.writeAttribute("class", theme
241: .getStyleClass(ThemeStyles.LABEL_LEVEL_TWO_TEXT), null);
242: writer.write(" "); // NOI18N
243: writer.endElement("span");
244: }
245:
246: /**
247: * Render help text for the property
248: *
249: * @param context The current FacesContext
250: * @param property The Property object to render
251: * @param theme The Theme to reference.
252: * @param writer The current ResponseWriter
253: *
254: * @exception IOException if an input/output error occurs
255: */
256: protected void renderHelpText(FacesContext context,
257: Property property, Theme theme, ResponseWriter writer)
258: throws IOException {
259:
260: // Render a help text facet if there is one
261: //
262: UIComponent helpText = property.getHelpTextComponent();
263: if (helpText != null) {
264: RenderingUtilities.renderComponent(helpText, context);
265: }
266: }
267:
268: /**
269: * Does nothing.
270: *
271: * @param context The current FacesContext
272: * @param component The Property object to render
273: * @param writer The current ResponseWriter
274: *
275: * @exception IOException if an input/output error occurs
276: */
277: public void encodeChildren(FacesContext context,
278: UIComponent component) throws IOException {
279: if (context == null || component == null) {
280: throw new NullPointerException();
281: }
282: }
283: }
|