/******************************************************************************* * Copyright (c) 2007 Exadel, Inc. and Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is made available under the terms of the * Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Exadel, Inc. and Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.jsf.vpe.richfaces.template; import static org.jboss.tools.vpe.xulrunner.util.XPCOM.queryInterface; import java.util.ArrayList; import org.jboss.tools.jsf.vpe.richfaces.ComponentUtil; import org.jboss.tools.jsf.vpe.richfaces.RichFacesTemplatesActivator; import org.jboss.tools.vpe.editor.context.VpePageContext; import org.jboss.tools.vpe.editor.template.VpeAbstractTemplate; import org.jboss.tools.vpe.editor.template.VpeChildrenInfo; import org.jboss.tools.vpe.editor.template.VpeCreationData; import org.jboss.tools.vpe.editor.util.Constants; import org.jboss.tools.vpe.editor.util.HTML; import org.jboss.tools.vpe.editor.util.VisualDomUtil; import org.jboss.tools.vpe.editor.util.VpeStyleUtil; import org.mozilla.interfaces.nsIDOMDocument; import org.mozilla.interfaces.nsIDOMElement; import org.mozilla.interfaces.nsIDOMNode; import org.mozilla.interfaces.nsIDOMNodeList; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * Displays template for scrollableDataTable * * @author dsakovich@exadel.com * */ public class RichFacesScrollableDataTableTemplate extends VpeAbstractTemplate { private static final String COLUMN = ':' + RichFaces.TAG_COLUMN; private static final String COLUMNS = ':' + RichFaces.TAG_COLUMNS; private static final String DEFAULT_HEIGHT = "500px"; //$NON-NLS-1$ private static final String DEFAULT_WIDTH = "700px"; //$NON-NLS-1$ private static final String CSS_STYLE_PATH = "scrollableDataTable/scrollableDataTable.css"; //$NON-NLS-1$ private static final String COMPONENT_NAME = "richFacesDataTable"; //$NON-NLS-1$ private static final String CSS_DR_TABLE = "dr-table"; //$NON-NLS-1$ private static final String CSS_DR_TABLE_HIDDEN = "dr-table-hidden"; //$NON-NLS-1$ private static final String CSS_RICH_SDT = "rich-sdt"; //$NON-NLS-1$ private static final String CSS_RICH_SDT_HEADER_CELL = "rich-sdt-header-cell"; //$NON-NLS-1$ private static final String CSS_RICH_SDT_HEADER_ROW = "rich-sdt-header-row"; //$NON-NLS-1$ private static final String CSS_RICH_SDT_COLUMN_CELL = "rich-sdt-column-cell"; //$NON-NLS-1$ private static final String CSS_RICH_SDT_FOOTER_CELL = "rich-sdt-footer-cell"; //$NON-NLS-1$ private static final String CSS_RICH_SDT_FOOTER_ROW = "rich-sdt-footer-row"; //$NON-NLS-1$ private static final String CSS_RICH_SDT_HSEP = "rich-sdt-hsep"; //$NON-NLS-1$ private static final int NUM_ROW = 5; private static final String TAG_MAIN_TABLE_WRAPPER = "mainTable-wrapper"; //$NON-NLS-1$ /** * Creates a node of the visual tree on the node of the source tree. This * visual node should not have the parent node This visual node can have * child nodes. * * @param pageContext * Contains the information on edited page. * @param sourceNode * The current node of the source tree. * @param visualDocument * The document of the visual tree. * @return The information on the created node of the visual tree. */ public VpeCreationData create(VpePageContext pageContext, Node sourceNode, nsIDOMDocument visualDocument) { Element sourceElement = (Element) sourceNode; String width = DEFAULT_WIDTH; if (sourceElement.hasAttribute(HTML.ATTR_WIDTH)) { String widthAttrVal = sourceElement.getAttribute(HTML.ATTR_WIDTH); width = VpeStyleUtil.addPxIfNecessary(widthAttrVal); } String height = DEFAULT_HEIGHT; if (sourceElement.hasAttribute(HTML.ATTR_HEIGHT)) { String heightAttrVal = sourceElement.getAttribute(HTML.ATTR_HEIGHT); height = VpeStyleUtil.addPxIfNecessary(heightAttrVal); } nsIDOMElement div = visualDocument .createElement(HTML.TAG_DIV); div.setAttribute(HTML.ATTR_CLASS, CSS_DR_TABLE_HIDDEN); String divStyle = HTML.ATTR_WIDTH + Constants.COLON + width + Constants.SEMICOLON + HTML.ATTR_HEIGHT + Constants.COLON + height + ";overflow:auto;"; //$NON-NLS-1$ VpeCreationData creationData = new VpeCreationData(div); div.setAttribute(HTML.ATTR_STYLE, divStyle); nsIDOMElement mainTable = visualDocument .createElement(HTML.TAG_TABLE); mainTable.removeAttribute(HTML.ATTR_HEIGHT); nsIDOMElement mainTableWrapper = visualDocument.createElement(TAG_MAIN_TABLE_WRAPPER); mainTableWrapper.appendChild(mainTable); div.appendChild(mainTableWrapper); ComponentUtil.setCSSLink(pageContext, CSS_STYLE_PATH, COMPONENT_NAME); String tableClass = CSS_DR_TABLE + Constants.WHITE_SPACE + CSS_RICH_SDT; if (sourceElement.hasAttribute(RichFaces.ATTR_STYLE_CLASS)) { tableClass += Constants.WHITE_SPACE + sourceElement.getAttribute(RichFaces.ATTR_STYLE_CLASS); } mainTable.setAttribute(HTML.ATTR_CLASS, tableClass); // Encode colgroup definition. ArrayList<Element> columns = getColumns(sourceElement); int columnsLength = getColumnsCount(sourceElement, columns); nsIDOMElement colgroup = visualDocument .createElement(HTML.TAG_COLGROUP); colgroup.setAttribute(HTML.ATTR_SPAN, String.valueOf(columnsLength)); mainTable.appendChild(colgroup); // Encode Caption encodeCaption(creationData, sourceElement, visualDocument, mainTable); // Encode Header Element header = ComponentUtil.getFacet(sourceElement, RichFaces.NAME_FACET_HEADER); ArrayList<Element> columnsHeaders = ComponentUtil.getColumnsWithFacet(columns, RichFaces.NAME_FACET_HEADER); if (header != null || !columnsHeaders.isEmpty()) { nsIDOMElement thead = visualDocument .createElement(HTML.TAG_THEAD); mainTable.appendChild(thead); String headerClass = sourceElement.hasAttribute(RichFaces.ATTR_HEADER_CLASS) ? sourceElement.getAttribute(RichFaces.ATTR_HEADER_CLASS) : null; if (header != null) { /* * JBIDE-3204 #2:No one style or styleClass should be applyed * for the footer and header of scrollableDataTable as default */ encodeTableHeaderOrFooterFacet(pageContext, creationData, thead, columnsLength, visualDocument, header, Constants.EMPTY, Constants.EMPTY, Constants.EMPTY, headerClass, HTML.TAG_TD); } if (!columnsHeaders.isEmpty()) { nsIDOMElement tr = visualDocument.createElement(HTML.TAG_TR); thead.appendChild(tr); String styleClass = ComponentUtil.encodeStyleClass(null, "dr-table-subheader dr-sdt-hr", null, //$NON-NLS-1$ headerClass); if (styleClass != null) { tr.setAttribute(HTML.ATTR_CLASS, styleClass); } encodeHeaderOrFooterFacets(creationData, tr, visualDocument, columnsHeaders, "dr-table-subheadercell rich-table-subheadercell", //$NON-NLS-1$ headerClass, RichFaces.NAME_FACET_HEADER, HTML.TAG_TD); } } // Encode Footer Element footer = ComponentUtil.getFacet(sourceElement, RichFaces.NAME_FACET_FOOTER); ArrayList<Element> columnsFooters = ComponentUtil.getColumnsWithFacet(columns, RichFaces.NAME_FACET_FOOTER); if (footer != null || !columnsFooters.isEmpty()) { nsIDOMElement tfoot = visualDocument .createElement(HTML.TAG_TFOOT); mainTable.appendChild(tfoot); String footerClass = sourceElement.hasAttribute(RichFaces.ATTR_FOOTER_CLASS) ? sourceElement.getAttribute(RichFaces.ATTR_FOOTER_CLASS) : null; if (!columnsFooters.isEmpty()) { nsIDOMElement tr = visualDocument .createElement(HTML.TAG_TR); tfoot.appendChild(tr); String styleClass = ComponentUtil.encodeStyleClass(null, "dr-table-subfooter rich-table-subfooter", null, //$NON-NLS-1$ footerClass); if (styleClass != null) { tr.setAttribute(HTML.ATTR_CLASS, styleClass); } encodeHeaderOrFooterFacets(creationData, tr, visualDocument, columnsFooters, "dr-table-subfootercell rich-table-subfootercell", //$NON-NLS-1$ footerClass, RichFaces.NAME_FACET_FOOTER, HTML.TAG_TD); } if (footer != null) { /* * JBIDE-3204 #2:No one style or styleClass should be applyed * for the footer and header of scrollableDataTable as default */ encodeTableHeaderOrFooterFacet(pageContext, creationData, tfoot, columnsLength, visualDocument, footer, Constants.EMPTY, Constants.EMPTY, Constants.EMPTY, footerClass, HTML.TAG_TD); } } nsIDOMElement tbody = visualDocument .createElement(HTML.TAG_TBODY); mainTable.appendChild(tbody); VisualDomUtil.copyAttributes(sourceNode, tbody); for (int i = 0; i < NUM_ROW; i++) { new RichFacesDataTableChildrenEncoder(creationData, visualDocument, sourceElement, mainTable).encodeChildren(); } return creationData; } /** * * @param creationData * @param sourceElement * @param visualDocument * @param table */ protected void encodeCaption(VpeCreationData creationData, Element sourceElement, nsIDOMDocument visualDocument, nsIDOMElement table) { // Encode caption Element captionFromFacet = ComponentUtil.getFacet(sourceElement, HTML.TAG_CAPTION); if (captionFromFacet != null) { nsIDOMElement caption = visualDocument .createElement(HTML.TAG_CAPTION); table.appendChild(caption); String captionClass = "dr-table-caption rich-table-caption"; //$NON-NLS-1$ if (table.hasAttribute(RichFaces.ATTR_CAPTION_CLASS)) { captionClass += Constants.WHITE_SPACE + table.getAttribute(RichFaces.ATTR_CAPTION_CLASS); } caption.setAttribute(HTML.ATTR_CLASS, captionClass); if (table.hasAttribute(RichFaces.ATTR_CAPTION_STYLE)) { String captionStyle = table.getAttribute(RichFaces.ATTR_CAPTION_STYLE); caption.setAttribute(HTML.ATTR_STYLE, captionStyle); } VpeChildrenInfo cap = new VpeChildrenInfo(caption); cap.addSourceChild(captionFromFacet); creationData.addChildrenInfo(cap); } } /** * * @param creationData * @param parentTr * @param visualDocument * @param headersOrFooters * @param skinCellClass * @param headerClass * @param facetName * @param element */ public static void encodeHeaderOrFooterFacets(VpeCreationData creationData, nsIDOMElement parentTr, nsIDOMDocument visualDocument, ArrayList<Element> headersOrFooters, String skinCellClass, String headerClass, String facetName, String element) { for (Element column : headersOrFooters) { String classAttribute = facetName + "Class"; //$NON-NLS-1$ String columnHeaderClass = column.hasAttribute(classAttribute) ? column.getAttribute(classAttribute) : null; nsIDOMElement td = visualDocument.createElement(element); parentTr.appendChild(td); String styleClass = ComponentUtil.encodeStyleClass(null, skinCellClass, headerClass, columnHeaderClass); if (!RichFacesColumnTemplate.isVisible(column)) { VisualDomUtil.setSubAttribute(td, HTML.ATTR_STYLE, HTML.STYLE_PARAMETER_DISPLAY, HTML.STYLE_VALUE_NONE); } td.setAttribute(HTML.ATTR_CLASS, styleClass); td.setAttribute("scop", "col"); //$NON-NLS-1$ //$NON-NLS-2$ if (column.hasAttribute(HTML.ATTR_COLSPAN)) { String colspan = column.getAttribute(HTML.ATTR_COLSPAN); td.setAttribute(HTML.ATTR_COLSPAN, colspan); } Element facetBody = ComponentUtil.getFacet(column, facetName); VpeChildrenInfo child = new VpeChildrenInfo(td); child.addSourceChild(facetBody); creationData.addChildrenInfo(child); } } /** * * @param creationData * @param parentTheadOrTfood * @param columns * @param visualDocument * @param facetBody * @param skinFirstRowClass * @param skinRowClass * @param skinCellClass * @param facetBodyClass * @param element */ protected void encodeTableHeaderOrFooterFacet(final VpePageContext pageContext, VpeCreationData creationData, nsIDOMElement parentTheadOrTfood, int columns, nsIDOMDocument visualDocument, Element facetBody, String skinFirstRowClass, String skinRowClass, String skinCellClass, String facetBodyClass, String element) { boolean isColumnGroup = facetBody.getNodeName() .endsWith(":columnGroup"); //$NON-NLS-1$ boolean isSubTable = facetBody.getNodeName().endsWith(":subTable"); //$NON-NLS-1$ if (isColumnGroup) { RichFacesColumnGroupTemplate.DEFAULT_INSTANCE.encodeSubTable(pageContext, creationData, facetBody, visualDocument, parentTheadOrTfood); } else if (isSubTable) { RichFacesSubTableTemplate.DEFAULT_INSTANCE.encodeSubTable(pageContext, creationData, facetBody, visualDocument, parentTheadOrTfood); } else { nsIDOMElement tr = visualDocument .createElement(HTML.TAG_TR); parentTheadOrTfood.appendChild(tr); String styleClass = ComponentUtil.encodeStyleClass(null, skinFirstRowClass, facetBodyClass, null); if (styleClass != null) { tr.setAttribute(HTML.ATTR_CLASS, styleClass); } nsIDOMElement td = visualDocument.createElement(element); tr.appendChild(td); styleClass = ComponentUtil.encodeStyleClass(null, skinCellClass, facetBodyClass, null); if (styleClass != null) { td.setAttribute(HTML.ATTR_CLASS, styleClass); } // the cell spans the entire row td.setAttribute(HTML.ATTR_COLSPAN, HTML.VALUE_COLSPAN_ALL); td.setAttribute(HTML.ATTR_SCOPE, HTML.TAG_COLGROUP); VpeChildrenInfo child = new VpeChildrenInfo(td); child.addSourceChild(facetBody); creationData.addChildrenInfo(child); } } /** * * @param parentSourceElement * @return list of columns */ public static ArrayList<Element> getColumns(Element parentSourceElement) { ArrayList<Element> columns = new ArrayList<Element>(); NodeList children = parentSourceElement.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node child = children.item(i); String nodeName = child.getNodeName(); if ((child instanceof Element) && (nodeName.endsWith(COLUMN) || nodeName.endsWith(COLUMNS))) { columns.add((Element) child); } } return columns; } /** * * @param sourceElement * @param columns * @return */ protected int getColumnsCount(Element sourceElement, ArrayList<Element> columns) { int count = 0; // check for exact value in component try { int span = Integer.parseInt(sourceElement.getAttribute("columns")); //$NON-NLS-1$ count = count > 0 ? span : calculateRowColumns(sourceElement, columns); } catch (NumberFormatException e) { count = calculateRowColumns(sourceElement, columns); } return count; } /* * Calculate max number of columns per row. For rows, recursive calculate * max length. */ private int calculateRowColumns(Element sourceElement, ArrayList<Element> columns) { int count = 0; int currentLength = 0; for (Element column : columns) { if (ComponentUtil.isRendered(column)) { String nodeName = column.getNodeName(); if (nodeName.endsWith(":columnGroup")) { //$NON-NLS-1$ // Store max calculated value of previsous rows. if (currentLength > count) { count = currentLength; } // Calculate number of columns in row. currentLength = calculateRowColumns(sourceElement, getColumns(column)); // Store max calculated value if (currentLength > count) { count = currentLength; } currentLength = 0; } else if (nodeName.equals(sourceElement.getPrefix() + COLUMN) || nodeName.equals(sourceElement.getPrefix() + COLUMNS)) { // For new row, save length of previsous. if (RichFacesColumnTemplate.isBreakBefore(column)) { if (currentLength > count) { count = currentLength; } currentLength = 0; } String colspanStr = column .getAttribute(HTML.ATTR_COLSPAN); try { int colspan = Integer.parseInt(colspanStr); currentLength += colspan > 0 ? colspan : 1; } catch (NumberFormatException e) { currentLength++; } } else if (nodeName.endsWith(COLUMN)) { // UIColumn always have colspan == 1. currentLength++; } } } if (currentLength > count) { count = currentLength; } return count; } /** * Checks, whether it is necessary to re-create an element at change of * attribute * * @param pageContext * Contains the information on edited page. * @param sourceElement * The current element of the source tree. * @param visualDocument * The document of the visual tree. * @param visualNode * The current node of the visual tree. * @param data * The arbitrary data, built by a method <code>create</code> * @param name * Attribute name * @param value * Attribute value * @return <code>true</code> if it is required to re-create an element at * a modification of attribute, <code>false</code> otherwise. */ public boolean recreateAtAttrChange(VpePageContext pageContext, Element sourceElement, nsIDOMDocument visualDocument, nsIDOMElement visualNode, Object data, String name, String value) { return true; } /* (non-Javadoc) * @see org.jboss.tools.vpe.editor.template.VpeAbstractTemplate#validate(org.jboss.tools.vpe.editor.context.VpePageContext, org.w3c.dom.Node, org.mozilla.interfaces.nsIDOMDocument, org.jboss.tools.vpe.editor.template.VpeCreationData) */ @Override public void validate(VpePageContext pageContext, Node sourceNode, nsIDOMDocument visualDocument, VpeCreationData data) { RichFacesDataTableChildrenEncoder.validateChildren(pageContext, sourceNode, visualDocument, data); applyStyleClasses(pageContext, sourceNode, visualDocument, data); } private void applyStyleClasses(VpePageContext pageContext, Node sourceNode, nsIDOMDocument visualDocument, VpeCreationData data) { nsIDOMElement element = (nsIDOMElement) data.getNode(); final nsIDOMNodeList mainTableWrappers = element.getElementsByTagName(TAG_MAIN_TABLE_WRAPPER); if (mainTableWrappers == null || mainTableWrappers.getLength() != 1) { final RuntimeException e = new RuntimeException("This is probably a bug. There should be exatly one " + TAG_MAIN_TABLE_WRAPPER);//$NON-NLS-1$ RichFacesTemplatesActivator.getPluginLog().logError(e); } final nsIDOMNode mainTableWrapper = mainTableWrappers.item(0); final nsIDOMNodeList mainTableWrapperChildren = mainTableWrapper.getChildNodes(); if (mainTableWrapperChildren == null || mainTableWrapperChildren.getLength() != 1) { final RuntimeException e = new RuntimeException("This is probably a bug. " + TAG_MAIN_TABLE_WRAPPER + " should have exactly one child.");//$NON-NLS-1$ //$NON-NLS-2$ RichFacesTemplatesActivator.getPluginLog().logError(e); } final nsIDOMElement mainTable = queryInterface(mainTableWrapperChildren.item(0), nsIDOMElement.class); final RichFacesDataTableStyleClassesApplier styleClassesApplier = new RichFacesDataTableStyleClassesApplier(visualDocument, pageContext, sourceNode); styleClassesApplier.applyClasses(mainTable); VisualDomUtil.replaceNodeByItsChildren(mainTableWrapper); } // @Override // public void removeAttribute(VpePageContext pageContext, // Element sourceElement, nsIDOMDocument visualDocument, // nsIDOMNode visualNode, Object data, String name) { // nsIDOMElement visualElement = (nsIDOMElement) visualNode // .queryInterface(nsIDOMElement.NS_IDOMELEMENT_IID); // if (name.equalsIgnoreCase(HTML.ATTR_WIDTH)) { // String style = visualElement // .getAttribute(HtmlComponentUtil.HTML_S // Append colspan of this column // visualElement.removeAttribute(HTML.ATTR_STYLE); // style += "; " + HTML.ATTR_WIDTH + " : " // + DEFAULT_WIDTH + ";"; // visualElement // .setAttribute(HTML.ATTR_STYLE, style); // // } else // // if (name.equalsIgnoreCase(HTML.ATTR_HEIGHT)) { // String style = visualElement // .getAttribute(HTML.ATTR_STYLE); // visualElement.removeAttribute(HTML.ATTR_STYLE); // style += "; " + HTML.ATTR_HEIGHT + " : " // + DEFAULT_HEIGHT + ";"; // visualElement // .setAttribute(HTML.ATTR_STYLE, style); // // } else { // visualElement.removeAttribute(name); // } // } // // @Override // public void setAttribute(VpePageContext pageContext, Element // sourceElement, // nsIDOMDocument visualDocument, nsIDOMNode visualNode, Object data, // String name, String value) { // nsIDOMElement visualElement = (nsIDOMElement) visualNode // .queryInterface(nsIDOMElement.NS_IDOMELEMENT_IID); // if (name.equalsIgnoreCase(HTML.ATTR_WIDTH)) { // String style = visualElement // .getAttribute(HTML.ATTR_STYLE); // visualElement.removeAttribute(HTML.ATTR_STYLE); // style += "; " + HTML.ATTR_WIDTH + " : " + value // + ";"; // visualElement // .setAttribute(HTML.ATTR_STYLE, style); // // } // // if (name.equalsIgnoreCase(HTML.ATTR_HEIGHT)) { // String style = visualElement // .getAttribute(HTML.ATTR_STYLE); // visualElement.removeAttribute(HTML.ATTR_STYLE); // style += "; " + HTML.ATTR_HEIGHT + " : " + value // + ";"; // visualElement // .setAttribute(HTML.ATTR_STYLE, style); // // } // visualElement.setAttribute(name, value); // } } // html code // <table style="border: 1px solid;"> // <tr> // <td> // <table> // <tr> // <td> // <input type="text"/>ibsert content // sdfsdfsdf // </td> // </tr> // </table> // </td> // <td> // <div style="overflow: scroll; width: 17px; height: 100%;"> // </div> // </td> // </tr> // <tr> // <td> // <div style="overflow: scroll; width: 100%; height: 17px;"> // </div> // </td> // </tr> // <table/>