Paints the given WMessageBox.

@param component the WMessageBox to paint.
@param renderContext the RenderContext to paint to.
977725
@Override
	public void doRender(final WComponent component, final WebXmlRenderContext renderContext) {
		WMessageBox messageBox = (WMessageBox) component;
		XmlStringBuilder xml = renderContext.getWriter();

		if (messageBox.hasMessages()) {
			xml.appendTagOpen("ui:messagebox");
			xml.appendAttribute("id", component.getId());
			xml.appendOptionalAttribute("class", component.getHtmlClass());
			xml.appendOptionalAttribute("track", component.isTracking(), "true");

			switch (messageBox.getType()) {
				case SUCCESS:
					xml.appendOptionalAttribute("type", "success");
					break;

				case INFO:
					xml.appendOptionalAttribute("type", "info");
					break;

				case WARN:
					xml.appendOptionalAttribute("type", "warn");
					break;

				case ERROR:
				default:
					xml.appendOptionalAttribute("type", "error");
					break;
			}

			xml.appendOptionalAttribute("title", messageBox.getTitleText());

			xml.appendClose();

			for (String message : messageBox.getMessages()) {
				xml.appendTag("ui:message");
				xml.print(message);
				xml.appendEndTag("ui:message");
			}

			xml.appendEndTag("ui:messagebox");
		}
	}
350048
@Override
	public void doRender(final WComponent component, final WebXmlRenderContext renderContext) {
		WImageEditor editorComponent = (WImageEditor) component;
		XmlStringBuilder xml = renderContext.getWriter();

		xml.appendTagOpen("wc-imageedit");
		xml.appendAttribute("id", editorComponent.getId());
		xml.appendOptionalAttribute("class", editorComponent.getHtmlClass());
		xml.appendOptionalUrlAttribute("overlay", editorComponent.getOverlayUrl());
		xml.appendOptionalAttribute("camera", editorComponent.getUseCamera());
		xml.appendOptionalAttribute("face", editorComponent.getIsFace());
		xml.appendOptionalAttribute("inline", editorComponent.getRenderInline());

		// Check for size information on the image
		Dimension size = editorComponent.getSize();
		if (size != null) {
			if (size.getHeight() >= 0) {
				xml.appendAttribute("height", size.height);
			}

			if (size.getWidth() >= 0) {
				xml.appendAttribute("width", size.width);
			}
		}

		xml.appendEnd();
	}
870125
@Override
	public void doRender(final WComponent component, final WebXmlRenderContext renderContext) {

		WTree tree = (WTree) component;
		XmlStringBuilder xml = renderContext.getWriter();

		// Check if rendering an open item request
		String openId = tree.getOpenRequestItemId();
		if (openId != null) {
			handleOpenItemRequest(tree, xml, openId);
			return;
		}

		// Check the tree has tree items (WCAG requirement)
		TreeItemModel model = tree.getTreeModel();
		if (model == null || model.getRowCount() <= 0) {
			LOG.warn("Tree not rendered as it has no items.");
			return;
		}

		xml.appendTagOpen("ui:tree");
		xml.appendAttribute("id", component.getId());
		xml.appendOptionalAttribute("class", component.getHtmlClass());
		xml.appendOptionalAttribute("track", component.isTracking(), "true");
		xml.appendOptionalAttribute("htree", WTree.Type.HORIZONTAL == tree.getType(), "true");
		xml.appendOptionalAttribute("multiple", WTree.SelectMode.MULTIPLE == tree.getSelectMode(), "true");
		xml.appendOptionalAttribute("disabled", tree.isDisabled(), "true");
		xml.appendOptionalAttribute("hidden", tree.isHidden(), "true");
		xml.appendOptionalAttribute("required", tree.isMandatory(), "true");

		switch (tree.getExpandMode()) {
			case CLIENT:
				xml.appendAttribute("mode", "client");
				break;

			case DYNAMIC:
				xml.appendAttribute("mode", "dynamic");
				break;

			case LAZY:
				xml.appendAttribute("mode", "lazy");
				break;

			default:
				throw new IllegalStateException("Invalid expand mode: " + tree.getType());
		}
		xml.appendClose();

		// Render margin
		MarginRendererUtil.renderMargin(tree, renderContext);

		if (tree.getCustomTree() == null) {
			handlePaintItems(tree, xml);
		} else {
			handlePaintCustom(tree, xml);
		}

		DiagnosticRenderUtil.renderDiagnostics(tree, renderContext);
		xml.appendEndTag("ui:tree");
	}
63093
@Override
	public void doRender(final WComponent component, final WebXmlRenderContext renderContext) {
		WSelectToggle toggle = (WSelectToggle) component;
		XmlStringBuilder xml = renderContext.getWriter();

		xml.appendTagOpen("ui:selecttoggle");
		xml.appendAttribute("id", component.getId());
		xml.appendOptionalAttribute("class", component.getHtmlClass());
		xml.appendOptionalAttribute("track", component.isTracking(), "true");

		State state = toggle.getState();

		if (State.ALL.equals(state)) {
			xml.appendAttribute("selected", "all");
		} else if (State.NONE.equals(state)) {
			xml.appendAttribute("selected", "none");
		} else {
			xml.appendAttribute("selected", "some");
		}

		xml.appendOptionalAttribute("disabled", toggle.isDisabled(), "true");
		xml.appendAttribute("target", toggle.getTarget().getId());
		xml.appendAttribute("renderAs", toggle.isRenderAsText() ? "text" : "control");
		xml.appendOptionalAttribute("roundTrip", !toggle.isClientSide(), "true");
		xml.appendEnd();
	}
628667
@Override
	public void doRender(final WComponent component, final WebXmlRenderContext renderContext) {
		WRadioButtonSelect rbSelect = (WRadioButtonSelect) component;
		XmlStringBuilder xml = renderContext.getWriter();
		int cols = rbSelect.getButtonColumns();
		boolean readOnly = rbSelect.isReadOnly();

		xml.appendTagOpen("ui:radiobuttonselect");
		xml.appendAttribute("id", component.getId());
		xml.appendOptionalAttribute("class", component.getHtmlClass());
		xml.appendOptionalAttribute("track", component.isTracking(), "true");
		xml.appendOptionalAttribute("hidden", rbSelect.isHidden(), "true");
		if (readOnly) {
			xml.appendAttribute("readOnly", "true");
		} else {
			xml.appendOptionalAttribute("disabled", rbSelect.isDisabled(), "true");
			xml.appendOptionalAttribute("required", rbSelect.isMandatory(), "true");
			xml.appendOptionalAttribute("submitOnChange", rbSelect.isSubmitOnChange(), "true");
			xml.appendOptionalAttribute("toolTip", component.getToolTip());
			xml.appendOptionalAttribute("accessibleText", component.getAccessibleText());
		}
		xml.appendOptionalAttribute("frameless", rbSelect.isFrameless(), "true");

		switch (rbSelect.getButtonLayout()) {
			case COLUMNS:
				xml.appendAttribute("layout", "column");
				xml.appendOptionalAttribute("layoutColumnCount", cols > 0, String.valueOf(cols));
				break;
			case FLAT:
				xml.appendAttribute("layout", "flat");
				break;
			case STACKED:
				xml.appendAttribute("layout", "stacked");
				break;
			default:
				throw new SystemException("Unknown radio button layout: " + rbSelect.getButtonLayout());
		}

		xml.appendClose();

		// Options
		List<?> options = rbSelect.getOptions();
		boolean renderSelectionsOnly = readOnly;

		if (options != null) {
			int optionIndex = 0;
			Object selectedOption = rbSelect.getSelected();

			for (Object option : options) {
				if (option instanceof OptionGroup) {
					throw new SystemException("Option groups not supported in WRadioButtonSelect.");
				} else {
					renderOption(rbSelect, option, optionIndex++, xml, selectedOption, renderSelectionsOnly);
				}
			}
		}

		if (!readOnly) {
			DiagnosticRenderUtil.renderDiagnostics(rbSelect, renderContext);
		}
		xml.appendEndTag("ui:radiobuttonselect");

		if (rbSelect.isAjax()) {
			paintAjax(rbSelect, xml);
		}
	}
1310837
@Override
	public void doRender(final WComponent component, final WebXmlRenderContext renderContext) {
		WPanel panel = (WPanel) component;
		XmlStringBuilder xml = renderContext.getWriter();
		FlowLayout layout = (FlowLayout) panel.getLayout();
		Size gap = layout.getSpace();
		String gapString = gap != null ? gap.toString() : null;
		FlowLayout.Alignment align = layout.getAlignment();

		xml.appendTagOpen("ui:flowlayout");

		switch (align) {
			case RIGHT: {
				xml.appendAttribute("align", "right");
				break;
			}
			case CENTER: {
				xml.appendAttribute("align", "center");
				break;
			}
			case LEFT: {
				xml.appendAttribute("align", "left");
				break;
			}

			case VERTICAL:
				xml.appendAttribute("align", "vertical");
				break;

			default:
				throw new IllegalStateException("Unknown layout type: " + layout.getAlignment());
		}

		if (layout.getContentAlignment() != null) {
			switch (layout.getContentAlignment()) {
				case TOP: {
					xml.appendAttribute("valign", "top");
					break;
				}
				case MIDDLE: {
					xml.appendAttribute("valign", "middle");
					break;
				}
				case BASELINE: {
					xml.appendAttribute("valign", "baseline");
					break;
				}

				case BOTTOM:
					xml.appendAttribute("valign", "bottom");
					break;

				default:
					throw new IllegalStateException("Unknown content alignment type: " + layout.
							getContentAlignment());
			}
		}
		xml.appendOptionalAttribute("gap", gapString);

		xml.appendClose();

		int size = panel.getChildCount();

		for (int i = 0; i < size; i++) {
			xml.appendTag("ui:cell");
			WComponent child = panel.getChildAt(i);
			child.paint(renderContext);
			xml.appendEndTag("ui:cell");
		}

		xml.appendEndTag("ui:flowlayout");
	}
1258247
@Override
	public void doRender(final WComponent component, final WebXmlRenderContext renderContext) {
		WProgressBar progressBar = (WProgressBar) component;
		XmlStringBuilder xml = renderContext.getWriter();

		xml.appendTagOpen("html:progress");
		xml.appendAttribute("id", component.getId());
		xml.appendAttribute("class", getHtmlClass(progressBar));
		xml.appendOptionalAttribute("hidden", progressBar.isHidden(), "hidden");
		xml.appendOptionalAttribute("title", progressBar.getToolTip());
		xml.appendOptionalAttribute("aria-label", progressBar.getAccessibleText());
		xml.appendAttribute("value", progressBar.getValue());
		xml.appendOptionalAttribute("max", progressBar.getMax() > 0, progressBar.getMax());
		xml.appendClose();
		xml.appendEndTag("html:progress");
	}
644282
@Override
	public void doRender(final WComponent component, final WebXmlRenderContext renderContext) {
		WTimeoutWarning warning = (WTimeoutWarning) component;

		XmlStringBuilder xml = renderContext.getWriter();
		final int timoutPeriod = warning.getTimeoutPeriod();
		if (timoutPeriod > 0) {
			xml.appendTagOpen("ui:session");
			xml.appendAttribute("timeout", String.valueOf(timoutPeriod));
			int warningPeriod = warning.getWarningPeriod();
			xml.appendOptionalAttribute("warn", warningPeriod > 0, warningPeriod);
			xml.appendEnd();
		}

	}
901909
@Override
	public void doRender(final WComponent component, final WebXmlRenderContext renderContext) {
		WTab tab = (WTab) component;
		XmlStringBuilder xml = renderContext.getWriter();

		xml.appendTagOpen("ui:tab");
		xml.appendAttribute("id", component.getId());
		xml.appendOptionalAttribute("class", component.getHtmlClass());
		xml.appendOptionalAttribute("track", component.isTracking(), "true");
		xml.appendOptionalAttribute("open", tab.isOpen(), "true");
		xml.appendOptionalAttribute("disabled", tab.isDisabled(), "true");
		xml.appendOptionalAttribute("hidden", tab.isHidden(), "true");
		xml.appendOptionalAttribute("toolTip", tab.getToolTip());

		switch (tab.getMode()) {
			case CLIENT:
				xml.appendAttribute("mode", "client");
				break;
			case LAZY:
				xml.appendAttribute("mode", "lazy");
				break;
			case EAGER:
				xml.appendAttribute("mode", "eager");
				break;
			case DYNAMIC:
				xml.appendAttribute("mode", "dynamic");
				break;
			case SERVER:
				xml.appendAttribute("mode", "server");
				break;
			default:
				throw new SystemException("Unknown tab mode: " + tab.getMode());
		}

		if (tab.getAccessKey() != 0) {
			xml.appendAttribute("accessKey", String.valueOf(Character.
					toUpperCase(tab.getAccessKey())));
		}

		xml.appendClose();

		// Paint label
		tab.getTabLabel().paint(renderContext);

		// Paint content
		WComponent content = tab.getContent();

		xml.appendTagOpen("ui:tabcontent");
		xml.appendAttribute("id", tab.getId() + "-content");
		xml.appendClose();

		// Render content if not EAGER Mode or is EAGER and is the current AJAX trigger
		if (content != null && (TabMode.EAGER != tab.getMode() || AjaxHelper.isCurrentAjaxTrigger(
				tab))) {
			// Visibility of content set in prepare paint
			content.paint(renderContext);
		}

		xml.appendEndTag("ui:tabcontent");

		xml.appendEndTag("ui:tab");
	}
1278776
@Override
	public void doRender(final WComponent component, final WebXmlRenderContext renderContext) {
		WList list = (WList) component;
		XmlStringBuilder xml = renderContext.getWriter();
		WList.Type type = list.getType();
		WList.Separator separator = list.getSeparator();
		Size gap = list.getSpace();
		String gapString = gap == null ? null : gap.toString();
		xml.appendTagOpen("ui:panel");
		xml.appendAttribute("id", component.getId());
		xml.appendOptionalAttribute("class", component.getHtmlClass());
		xml.appendOptionalAttribute("track", component.isTracking(), "true");
		xml.appendOptionalAttribute("type", list.isRenderBorder(), "box");
		xml.appendClose();

		// Render margin
		MarginRendererUtil.renderMargin(list, renderContext);

		xml.appendTagOpen("ui:listlayout");
		xml.appendOptionalAttribute("gap", gapString);

		if (type != null) {
			switch (type) {
				case FLAT:
					xml.appendAttribute("type", "flat");
					break;

				case STACKED:
					xml.appendAttribute("type", "stacked");
					break;

				case STRIPED:
					xml.appendAttribute("type", "striped");
					break;

				default:
					throw new SystemException("Unknown list type: " + type);
			}
		}

		if (separator != null) {
			switch (separator) {
				case BAR:
					xml.appendAttribute("separator", "bar");
					break;

				case DOT:
					xml.appendAttribute("separator", "dot");
					break;

				case NONE:
					break;

				default:
					throw new SystemException("Unknown list type: " + type);
			}
		}

		xml.appendClose();

		paintRows(list, renderContext);

		xml.appendEndTag("ui:listlayout");
		xml.appendEndTag("ui:panel");
	}
1026696
@Override
	public void doRender(final WComponent component, final WebXmlRenderContext renderContext) {
		WTextField textField = (WTextField) component;
		XmlStringBuilder xml = renderContext.getWriter();
		boolean readOnly = textField.isReadOnly();

		xml.appendTagOpen("ui:textfield");
		xml.appendAttribute("id", component.getId());
		xml.appendOptionalAttribute("class", component.getHtmlClass());
		xml.appendOptionalAttribute("track", component.isTracking(), "true");
		xml.appendOptionalAttribute("hidden", component.isHidden(), "true");
		if (readOnly) {
			xml.appendAttribute("readOnly", "true");
		} else {
			int cols = textField.getColumns();
			int minLength = textField.getMinLength();
			int maxLength = textField.getMaxLength();
			String pattern = textField.getPattern();

			WSuggestions suggestions = textField.getSuggestions();
			String suggestionsId = suggestions == null ? null : suggestions.getId();

			WComponent submitControl = textField.getDefaultSubmitButton();
			String submitControlId = submitControl == null ? null : submitControl.getId();

			xml.appendOptionalAttribute("disabled", textField.isDisabled(), "true");
			xml.appendOptionalAttribute("required", textField.isMandatory(), "true");
			xml.appendOptionalAttribute("minLength", minLength > 0, minLength);
			xml.appendOptionalAttribute("maxLength", maxLength > 0, maxLength);
			xml.appendOptionalAttribute("toolTip", textField.getToolTip());
			xml.appendOptionalAttribute("accessibleText", textField.getAccessibleText());
			xml.appendOptionalAttribute("size", cols > 0, cols);
			xml.appendOptionalAttribute("buttonId", submitControlId);
			xml.appendOptionalAttribute("pattern", !Util.empty(pattern), pattern);
			xml.appendOptionalAttribute("list", suggestionsId);
			String placeholder = textField.getPlaceholder();
			xml.appendOptionalAttribute("placeholder", !Util.empty(placeholder), placeholder);
			String autocomplete = textField.getAutocomplete();
			xml.appendOptionalAttribute("autocomplete", !Util.empty(autocomplete), autocomplete);
		}

		xml.appendClose();

		xml.appendEscaped(textField.getText());
		if (!readOnly) {
			DiagnosticRenderUtil.renderDiagnostics(textField, renderContext);
		}
		xml.appendEndTag("ui:textfield");
	}
50336
@Override
	public void doRender(final WComponent component, final WebXmlRenderContext renderContext) {
		WSingleSelect listBox = (WSingleSelect) component;
		XmlStringBuilder xml = renderContext.getWriter();
		String dataKey = listBox.getListCacheKey();
		boolean readOnly = listBox.isReadOnly();
		int rows = listBox.getRows();

		xml.appendTagOpen("ui:listbox");
		xml.appendAttribute("id", component.getId());
		xml.appendOptionalAttribute("class", component.getHtmlClass());
		xml.appendOptionalAttribute("track", component.isTracking(), "true");
		xml.appendOptionalAttribute("hidden", listBox.isHidden(), "true");
		if (readOnly) {
			xml.appendAttribute("readOnly", "true");
		} else {
			xml.appendOptionalAttribute("data", dataKey != null, dataKey);
			xml.appendOptionalAttribute("disabled", listBox.isDisabled(), "true");
			xml.appendOptionalAttribute("required", listBox.isMandatory(), "true");
			xml.appendOptionalAttribute("submitOnChange", listBox.isSubmitOnChange(), "true");
			xml.appendOptionalAttribute("toolTip", component.getToolTip());
			xml.appendOptionalAttribute("accessibleText", component.getAccessibleText());
			xml.appendOptionalAttribute("rows", rows >= 2, rows);
			String autocomplete = listBox.getAutocomplete();
			xml.appendOptionalAttribute("autocomplete", !Util.empty(autocomplete), autocomplete);
		}
		xml.appendAttribute("single", "true");

		xml.appendClose();

		// Options
		List<?> options = listBox.getOptions();
		boolean renderSelectionsOnly = readOnly || dataKey != null;

		if (options != null) {
			int optionIndex = 0;
			Object selectedOption = listBox.getSelected();

			for (Object option : options) {
				if (option instanceof OptionGroup) {
					xml.appendTagOpen("ui:optgroup");
					xml.appendAttribute("label", ((OptionGroup) option).getDesc());
					xml.appendClose();

					for (Object nestedOption : ((OptionGroup) option).getOptions()) {
						renderOption(listBox, nestedOption, optionIndex++, xml, selectedOption, renderSelectionsOnly);
					}

					xml.appendEndTag("ui:optgroup");
				} else {
					renderOption(listBox, option, optionIndex++, xml, selectedOption, renderSelectionsOnly);
				}
			}
		}

		if (!readOnly) {
			DiagnosticRenderUtil.renderDiagnostics(listBox, renderContext);
		}
		xml.appendEndTag("ui:listbox");
	}
1341234
@Override
	public void doRender(final WComponent component, final WebXmlRenderContext renderContext) {
		WComponentGroup group = (WComponentGroup) component;
		XmlStringBuilder xml = renderContext.getWriter();
		List<WComponent> components = group.getComponents();

		if (components != null && !components.isEmpty()) {
			xml.appendTagOpen("ui:componentGroup");
			xml.appendAttribute("id", component.getId());
			xml.appendOptionalAttribute("track", component.isTracking(), "true");
			xml.appendClose();

			for (WComponent comp : components) {
				xml.appendTagOpen("ui:component");
				xml.appendAttribute("id", comp.getId());
				xml.appendEnd();
			}

			xml.appendEndTag("ui:componentGroup");
		}
	}
146442
@Override
	public void doRender(final WComponent component, final WebXmlRenderContext renderContext) {
		WPopup popup = (WPopup) component;
		XmlStringBuilder xml = renderContext.getWriter();
		int width = popup.getWidth();
		int height = popup.getHeight();
		String targetWindow = popup.getTargetWindow();

		xml.appendTagOpen("ui:popup");
		xml.appendUrlAttribute("url", popup.getUrl());
		xml.appendOptionalAttribute("width", width > 0, width);
		xml.appendOptionalAttribute("height", height > 0, height);
		xml.appendOptionalAttribute("resizable", popup.isResizable(), "true");
		xml.appendOptionalAttribute("showScrollbars", popup.isScrollable(), "true");
		xml.appendOptionalAttribute("targetWindow", !Util.empty(targetWindow), targetWindow);
		xml.appendClose();
		xml.appendEndTag("ui:popup");
	}
403589
@Override
	public void doRender(final WComponent component, final WebXmlRenderContext renderContext) {
		WPanel panel = (WPanel) component;
		XmlStringBuilder xml = renderContext.getWriter();
		WButton submitButton = panel.getDefaultSubmitButton();
		String submitId = submitButton == null ? null : submitButton.getId();
		String titleText = panel.getTitleText();

		boolean renderChildren = isRenderContent(panel);

		xml.appendTagOpen("ui:panel");
		xml.appendAttribute("id", component.getId());
		xml.appendOptionalAttribute("class", component.getHtmlClass());
		xml.appendOptionalAttribute("track", component.isTracking(), "true");
		if (PanelMode.LAZY.equals(panel.getMode())) {
			xml.appendOptionalAttribute("hidden", !renderChildren, "true");
		} else {
			xml.appendOptionalAttribute("hidden", component.isHidden(), "true");
		}
		xml.appendOptionalAttribute("buttonId", submitId);
		xml.appendOptionalAttribute("title", titleText);
		xml.appendOptionalAttribute("accessKey", Util.upperCase(panel.getAccessKeyAsString()));
		xml.appendOptionalAttribute("type", getPanelType(panel));
		xml.appendOptionalAttribute("mode", getPanelMode(panel));

		xml.appendClose();

		// Render margin
		MarginRendererUtil.renderMargin(panel, renderContext);

		if (renderChildren) {
			renderChildren(panel, renderContext);
		} else {
			// Content will be loaded via AJAX
			xml.append("<ui:content/>");
		}

		xml.appendEndTag("ui:panel");
	}
1285790
@Override
	public void doRender(final WComponent component, final WebXmlRenderContext renderContext) {
		WRow row = (WRow) component;
		XmlStringBuilder xml = renderContext.getWriter();
		int cols = row.getChildCount();
		Size gap = row.getSpace();
		String gapString = gap != null ? gap.toString() : null;

		if (cols > 0) {
			xml.appendTagOpen("ui:row");
			xml.appendAttribute("id", component.getId());
			xml.appendOptionalAttribute("class", component.getHtmlClass());
			xml.appendOptionalAttribute("track", component.isTracking(), "true");
			xml.appendOptionalAttribute("gap", gapString);
			xml.appendClose();

			// Render margin
			MarginRendererUtil.renderMargin(row, renderContext);

			paintChildren(row, renderContext);

			xml.appendEndTag("ui:row");
		}
	}
11767
@Override
	public void doRender(final WComponent component, final WebXmlRenderContext renderContext) {
		WTextArea textArea = (WTextArea) component;
		XmlStringBuilder xml = renderContext.getWriter();
		boolean readOnly = textArea.isReadOnly();
		xml.appendTagOpen("ui:textarea");
		xml.appendAttribute("id", component.getId());
		xml.appendOptionalAttribute("class", component.getHtmlClass());
		xml.appendOptionalAttribute("track", component.isTracking(), "true");
		xml.appendOptionalAttribute("hidden", textArea.isHidden(), "true");

		if (readOnly) {
			xml.appendAttribute("readOnly", "true");
		} else {
			int cols = textArea.getColumns();
			int rows = textArea.getRows();
			int minLength = textArea.getMinLength();
			int maxLength = textArea.getMaxLength();
			WComponent submitControl = textArea.getDefaultSubmitButton();
			String submitControlId = submitControl == null ? null : submitControl.getId();

			xml.appendOptionalAttribute("disabled", textArea.isDisabled(), "true");
			xml.appendOptionalAttribute("required", textArea.isMandatory(), "true");
			xml.appendOptionalAttribute("minLength", minLength > 0, minLength);
			xml.appendOptionalAttribute("maxLength", maxLength > 0, maxLength);
			xml.appendOptionalAttribute("toolTip", textArea.getToolTip());
			xml.appendOptionalAttribute("accessibleText", textArea.getAccessibleText());
			xml.appendOptionalAttribute("rows", rows > 0, rows);
			xml.appendOptionalAttribute("cols", cols > 0, cols);
			xml.appendOptionalAttribute("buttonId", submitControlId);
			String placeholder = textArea.getPlaceholder();
			xml.appendOptionalAttribute("placeholder", !Util.empty(placeholder), placeholder);

			String autocomplete = textArea.getAutocomplete();
			xml.appendOptionalAttribute("autocomplete", !Util.empty(autocomplete), autocomplete);
		}
		xml.appendClose();

		if (textArea.isRichTextArea()) {
			/*
			 * This is a nested element instead of an attribute to cater for future enhancements
			 * such as turning rich text features on or off, or specifying JSON config either as
			 * a URL attribute or a nested CDATA section.
			 */
			xml.append("<ui:rtf />");
		}

		String textString = textArea.getText();
		if (textString != null) {
			if (textArea.isReadOnly() && textArea.isRichTextArea()) {
				// read only we want to output unescaped, but it must still be XML valid.
				xml.write(HtmlToXMLUtil.unescapeToXML(textString));
			} else {
				xml.appendEscaped(textString);
			}
		}

		if (!readOnly) {
			DiagnosticRenderUtil.renderDiagnostics(textArea, renderContext);
		}
		xml.appendEndTag("ui:textarea");
	}