/******************************************************************************* * Copyright (c) 2007-2009 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 java.math.BigDecimal; import java.util.HashMap; import java.util.Map; import org.jboss.tools.jsf.vpe.richfaces.ComponentUtil; 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.mozilla.interfaces.nsIDOMDocument; import org.mozilla.interfaces.nsIDOMElement; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class RichFacesLayoutTemplate extends VpeAbstractTemplate { private static final String FLOAT_LEFT_STYLE = ";float: left;"; //$NON-NLS-1$ private static final String FLOAT_RIGHT_STYLE = ";float: right;"; //$NON-NLS-1$ private static final String BOTTOM_SECONDARY_DIV_STYLE = ";display: block; height: 0; clear: both; visibility: hidden;"; //$NON-NLS-1$ private static final String BOTTOM_SECONDARY_DIV_TEXT = "."; //$NON-NLS-1$ private static final String LAYOUT_PANEL_NAME = ":layoutPanel"; //$NON-NLS-1$ /** * Constructor */ public RichFacesLayoutTemplate() { super(); } public VpeCreationData create(VpePageContext pageContext, Node sourceNode, nsIDOMDocument visualDocument) { VpeCreationData creationData = null; Element sourceElement = (Element)sourceNode; nsIDOMElement mainDiv = visualDocument.createElement(HTML.TAG_DIV); String style = sourceElement.getAttribute(HTML.ATTR_STYLE); if (ComponentUtil.isNotBlank(style)) { mainDiv.setAttribute(HTML.ATTR_STYLE, style); } nsIDOMElement topDiv = visualDocument.createElement(HTML.TAG_DIV); nsIDOMElement centerDiv = visualDocument.createElement(HTML.TAG_DIV); nsIDOMElement leftDiv = visualDocument.createElement(HTML.TAG_DIV); nsIDOMElement rightDiv = visualDocument.createElement(HTML.TAG_DIV); nsIDOMElement bottomDiv = visualDocument.createElement(HTML.TAG_DIV); nsIDOMElement bottomSecondaryDiv = visualDocument.createElement(HTML.TAG_DIV); creationData = new VpeCreationData(mainDiv); bottomSecondaryDiv.setAttribute(HTML.ATTR_STYLE, BOTTOM_SECONDARY_DIV_STYLE); bottomSecondaryDiv.appendChild(visualDocument .createTextNode(BOTTOM_SECONDARY_DIV_TEXT)); bottomDiv.appendChild(bottomSecondaryDiv); NodeList children = sourceNode.getChildNodes(); Map<String, Element> panels = new HashMap<String, Element>(); /* * Array of columns weights * 0 - for left panel * 1 - for center panel * 2 - for right panel */ String[] widthStrings = {Constants.EMPTY, Constants.EMPTY, Constants.EMPTY}; for (int i = 0; i < children.getLength(); i++) { Node node = children.item(i); if (node instanceof Element && node.getNodeName() != null && node.getNodeName().indexOf(LAYOUT_PANEL_NAME) > 0) { Element element = (Element) node; String position = element.getAttribute(RichFaces.ATTR_POSITION); /* * Adding several panel with the same position is forbidden. * During adding to the map only the last panel * with repeating position name will be displayed. * Counting columns quantity and reading 'width attribute' * at the same time. */ if (ComponentUtil.isNotBlank(position)) { if (RichFaces.VALUE_TOP.equalsIgnoreCase(position)) { panels.put(RichFaces.VALUE_TOP, element); } else if (RichFaces.VALUE_LEFT.equalsIgnoreCase(position)) { panels.put(RichFaces.VALUE_LEFT, element); widthStrings[0] = element.getAttribute(HTML.ATTR_WIDTH); } else if (RichFaces.VALUE_CENTER.equalsIgnoreCase(position)) { panels.put(RichFaces.VALUE_CENTER, element); widthStrings[1] = element.getAttribute(HTML.ATTR_WIDTH); } else if (RichFaces.VALUE_RIGHT.equalsIgnoreCase(position)) { panels.put(RichFaces.VALUE_RIGHT, element); widthStrings[2] = element.getAttribute(HTML.ATTR_WIDTH); } else if (RichFaces.VALUE_BOTTOM.equalsIgnoreCase(position)) { panels.put(RichFaces.VALUE_BOTTOM, element); } } } } /* * Columns weights processing: * 1) If column has no weight specified * a) it should have a percent weight - when other weights are less than 100% summary, * if there are some columns without weight - they should share * total free weight between each other equally. * b) if other columns has weights specified in percents * and in summary it's more than 100% * than no weight should be set. * 2) If there are some columns has weight in '%' and their weights' * sum is more or less 100% then new weight should be set in range of 100% * in proportion to specified weights. * 3) If column has weight set in '%' and the value is greater than 100% * the value is added to style without changes. * 4) Weights in 'px' and 'em' are set in style without changes. */ /* * Array of columns weights * 0 - for left panel * 1 - for center panel * 2 - for right panel */ double[] widths = {-1, -1, -1}; for (int i = 0; i < widthStrings.length; i++) { widths[i] = parseWidthFromPercents(widthStrings[i]); } /* * A) Find any >100% weight. * Leave it as is. */ boolean widthOverflow = false; for (double w : widths) { if (w > 100) { widthOverflow = true; } } if (!widthOverflow){ /* * B) When weights are less than 100 * Count total weight in '%' (<100 || >100) */ double totalWidth = 0; for (double w : widths) { if (w > 0) { totalWidth += w; } } /* * Count columns with no width specified. */ int noWeightColumns = 0; for (String ws : widthStrings) { if ((null == ws) || Constants.EMPTY.equalsIgnoreCase(ws)) { noWeightColumns++; } } /* * Free width to add to total width to 100. */ double totalFreeWidth = 100 - totalWidth; /* * Total available width should always be less or equal 100. */ double availableWidth = 100; if ((totalWidth < 100) && (noWeightColumns > 0)){ /* * Set specified width, free space will be filled * with columns without width attribute. */ availableWidth = totalWidth; } /* * C) Adjust existed weights in '%' */ double[] coeffs = {-1, -1, -1}; for (int i = 0; i < widths.length; i++) { if (widths[i] > 0) { coeffs[i] = widths[i] / totalWidth; BigDecimal b = new BigDecimal(availableWidth*coeffs[i]).setScale(2, BigDecimal.ROUND_HALF_UP); widthStrings[i] = b.doubleValue() + Constants.PERCENT; } } /* * D) Adjust empty weight * When there is some free space to adjust - * divide it equally between width free columns. */ if ((totalFreeWidth > 0) && (noWeightColumns > 0)) { BigDecimal b = new BigDecimal(totalFreeWidth/noWeightColumns).setScale(2, BigDecimal.ROUND_HALF_UP); for (int i = 0; i < widthStrings.length; i++) { if ((null == widthStrings[i]) || Constants.EMPTY.equalsIgnoreCase(widthStrings[i])) { widthStrings[i] = b.doubleValue() + Constants.PERCENT; } } } } /* * E) Leave 'px' and 'em' without changes */ /* * Adding panels' divs. * Order is important! */ addPanelFromMap(RichFaces.VALUE_TOP, panels, mainDiv, topDiv, Constants.EMPTY, null, creationData); addPanelFromMap(RichFaces.VALUE_LEFT, panels, mainDiv, leftDiv, FLOAT_LEFT_STYLE, widthStrings[0], creationData); addPanelFromMap(RichFaces.VALUE_CENTER, panels, mainDiv, centerDiv, FLOAT_LEFT_STYLE, widthStrings[1], creationData); addPanelFromMap(RichFaces.VALUE_RIGHT, panels, mainDiv, rightDiv, FLOAT_RIGHT_STYLE, widthStrings[2], creationData); addPanelFromMap(RichFaces.VALUE_BOTTOM, panels, mainDiv, bottomDiv, Constants.EMPTY, null, creationData); return creationData; } /** * Parse width string from percents form to a number. * @param widthStr panel's width string * @return panel's width number or -1 when parsing failed */ private double parseWidthFromPercents(String widthStr) { double result = -1; if ((null != widthStr) && widthStr.endsWith(Constants.PERCENT)) { try { result = Double.parseDouble(widthStr.substring(0, widthStr.length()-1)); } catch (NumberFormatException e) { /* * Cannot parse - skip. */ } } return result; } /** * Adds rich:layoutPanel to current rich:layout. * Styles and width will be set. * Panel will be added to children info. * * @param panelPositionMapName panel name, also used to get the panel from the map * @param panelsMap map with panels elements from source * @param mainDiv rich:layout's div * @param panelDiv div to render layoutPanel * @param style panel's css style * @param panelWidth panel's width * @param creationData VpeCreationData */ private void addPanelFromMap(String panelPositionMapName, Map<String, Element> panelsMap, nsIDOMElement mainDiv, nsIDOMElement panelDiv, String style, String panelWidth, VpeCreationData creationData) { Element panel = panelsMap.get(panelPositionMapName); if (null != panel) { String widthStr = panel.getAttribute(HTML.ATTR_WIDTH); /* * Apply column width for left, center and right panels only. */ if (!RichFaces.VALUE_TOP.equalsIgnoreCase(panelPositionMapName) && !RichFaces.VALUE_BOTTOM.equalsIgnoreCase(panelPositionMapName) && (null != panelWidth)) { style += "; width: " + panelWidth + ";"; //$NON-NLS-1$ //$NON-NLS-2$ } else { } /* * Set the original width to 'width' attribute * as richfaces do. */ if (ComponentUtil.isNotBlank(widthStr)) { panelDiv.setAttribute(HTML.ATTR_WIDTH, widthStr); } panelDiv.setAttribute(HTML.ATTR_STYLE, style); mainDiv.appendChild(panelDiv); VpeChildrenInfo panelInfo = new VpeChildrenInfo(panelDiv); panelInfo.addSourceChild(panel); creationData.addChildrenInfo(panelInfo); } } }