/* * JBoss, Home of Professional Open Source * Copyright 2013, Red Hat, Inc. and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.richfaces.context; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.el.ELContext; import javax.el.ExpressionFactory; import javax.el.ValueExpression; import javax.faces.application.Application; import javax.faces.component.UIColumn; import javax.faces.component.UIComponent; import javax.faces.component.UIForm; import javax.faces.component.UIOutput; import javax.faces.component.UIViewRoot; import javax.faces.component.html.HtmlOutputText; import javax.faces.component.visit.VisitCallback; import javax.faces.component.visit.VisitContext; import javax.faces.component.visit.VisitHint; import javax.faces.component.visit.VisitResult; import javax.faces.context.FacesContext; import org.jboss.test.faces.FacesEnvironment; import org.jboss.test.faces.FacesEnvironment.FacesRequest; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.sun.faces.component.visit.PartialVisitContext; /** * Tested view structure: * <ul> * <li>r:table is AjaxTableComponentImpl</li> * </ul> * * <pre> * <h:form id="myForm"> * <r:outputText id="outerOutput" /> * * <r:table id="table" var="item" value="['Item 0',... ,'Item 1']"> * <f:facet name="header"> * <h:outputText id="theHeader" /> * </f:facet> * * <h:column> * <r:outputText id="nestedOutput" value="#{item}" /> * * <h:outputText id="nestedText" value="#{item}" /> * * <r:table id="nestedTable" value="['Nested item 0',... ,'Nested item 2']" var="nestedItem"> * <f:facet name="footer"> * <h:outputText id="nestedTableFooter" value="#{item}" /> * </f:facet> * * <h:column> * <h:outputText id="nestedTableText" value="#{nestedItem}" /> * </h:column> * </r:table> * </h:column> * </r:table> * </h:form> * </pre> * * * @author Nick Belaevski * */ public class ExtendedPartialVisitContextTest { private class TrackingVisitCallback implements VisitCallback { private List<String> visitedIds = new ArrayList<String>(); public VisitResult visit(VisitContext context, UIComponent target) { if (context instanceof ExtendedVisitContext) { visitedIds.add(((ExtendedVisitContext) context).buildExtendedClientId(target)); } else { visitedIds.add(target.getClientId(context.getFacesContext())); } return VisitResult.REJECT; } public List<String> getVisitedIds() { return visitedIds; } public void reset() { visitedIds.clear(); } } private FacesEnvironment environment; private FacesRequest facesRequest; private FacesContext facesContext; private Application application; private UIViewRoot viewRoot; private UIForm form; private AjaxOutputComponentImpl outerOutput; private AjaxTableComponentImpl table; private UIOutput dataHeader; private AjaxOutputComponentImpl nestedOutput; private UIOutput nestedText; private List<String> tableData; private BaseExtendedVisitContext renderingContext; private TrackingVisitCallback trackingVisitCallback; private ArrayList<String> nestedTableData; private AjaxTableComponentImpl nestedTable; private UIOutput nestedTableText; private UIOutput nestedTableFooter; private static void assertEqualSets(Collection<?> expected, Collection<?> actual) { assertEquals(asComparableCollection(expected), asComparableCollection(actual)); } private static <T> Collection<T> asComparableCollection(Collection<T> c) { if (c instanceof Set || c instanceof List) { return c; } else { if (c == VisitContext.ALL_IDS) { return c; } if (c != null) { return new HashSet<T>(c); } else { return null; } } } private static <T> Set<T> asSet(T... args) { Set<T> set = new HashSet<T>(); for (T argItem : args) { set.add(argItem); } return set; } private void createVisitContext(boolean limitRender) { Set<String> rendererIds = Collections.<String>emptySet(); PartialVisitContext partialVisitContext = new PartialVisitContext(facesContext, rendererIds); renderingContext = new ExtendedRenderVisitContext(partialVisitContext, facesContext, rendererIds, EnumSet.<VisitHint>of(VisitHint.SKIP_UNRENDERED), limitRender); } private void createNestedTableData() { nestedTableData = new ArrayList<String>(); for (int i = 0; i < 3; i++) { nestedTableData.add(MessageFormat.format("Nested item {0}", i)); } } private void createTableData() { tableData = new ArrayList<String>(); for (int i = 0; i < 2; i++) { tableData.add(MessageFormat.format("Item {0}", i)); } } private ValueExpression createTableVarValueExpression() { ExpressionFactory expressionFactory = application.getExpressionFactory(); ELContext elContext = facesContext.getELContext(); return expressionFactory.createValueExpression(elContext, "#{item}", String.class); } private ValueExpression createNestedTableVarValueExpression() { ExpressionFactory expressionFactory = application.getExpressionFactory(); ELContext elContext = facesContext.getELContext(); return expressionFactory.createValueExpression(elContext, "#{nestedItem}", String.class); } private void createNestedText() { nestedText = (UIOutput) application.createComponent(HtmlOutputText.COMPONENT_TYPE); nestedText.setId("nestedText"); nestedText.setValueExpression("value", createTableVarValueExpression()); table.getChildren().get(0).getChildren().add(nestedText); } private void createNestedOutput() { nestedOutput = new AjaxOutputComponentImpl(); nestedOutput.setAjaxRendered(true); nestedOutput.setId("nestedOutput"); nestedOutput.setValueExpression("value", createTableVarValueExpression()); table.getChildren().get(0).getChildren().add(nestedOutput); } private void createOuterOutput() { outerOutput = new AjaxOutputComponentImpl(); outerOutput.setAjaxRendered(true); outerOutput.setId("outerOutput"); form.getChildren().add(outerOutput); } private void createTableHeader() { dataHeader = (UIOutput) application.createComponent(HtmlOutputText.COMPONENT_TYPE); dataHeader.setId("theHeader"); table.getFacets().put("header", dataHeader); } private void createNestedTable() { nestedTable = new AjaxTableComponentImpl(); nestedTable.setId("nestedTable"); nestedTable.setVar("nestedItem"); createNestedTableData(); nestedTable.setValue(nestedTableData); nestedTable.getChildren().add(new UIColumn()); table.getChildren().get(0).getChildren().add(nestedTable); createNestedTableText(); createNestedTableFooter(); } private void createNestedTableFooter() { nestedTableFooter = (UIOutput) application.createComponent(HtmlOutputText.COMPONENT_TYPE); nestedTableFooter.setId("nestedTableFooter"); nestedTableFooter.setValueExpression("value", createTableVarValueExpression()); nestedTable.getFacets().put("footer", nestedTableFooter); } private void createNestedTableText() { nestedTableText = (UIOutput) application.createComponent(HtmlOutputText.COMPONENT_TYPE); nestedTableText.setId("nestedTableText"); nestedTableText.setValueExpression("value", createNestedTableVarValueExpression()); nestedTable.getChildren().get(0).getChildren().add(nestedTableText); } private void createTable() { table = new AjaxTableComponentImpl(); table.setId("table"); table.setVar("item"); createTableData(); table.setValue(tableData); table.getChildren().add(new UIColumn()); form.getChildren().add(table); createNestedOutput(); createNestedText(); createNestedTable(); createTableHeader(); } private void createForm() { form = (UIForm) application.createComponent(UIForm.COMPONENT_TYPE); form.setId("myForm"); viewRoot.getChildren().add(form); createOuterOutput(); createTable(); } private void createView() { viewRoot = facesContext.getViewRoot(); createForm(); } @Before public void setUp() throws Exception { environment = FacesEnvironment.createEnvironment(); environment.start(); facesRequest = environment.createFacesRequest(); facesRequest.start(); facesContext = FacesContext.getCurrentInstance(); application = facesContext.getApplication(); createView(); trackingVisitCallback = new TrackingVisitCallback(); } @After public void tearDown() throws Exception { renderingContext = null; trackingVisitCallback = null; facesContext = null; application = null; table = null; dataHeader = null; form = null; nestedOutput = null; nestedTable = null; nestedTableData = null; nestedTableFooter = null; nestedTableText = null; nestedText = null; outerOutput = null; tableData = null; viewRoot = null; facesRequest.release(); facesRequest = null; environment.release(); environment = null; } @Test public void testCollectionProxy() throws Exception { createVisitContext(false); Iterator<String> iterator; Collection<String> idsToVisit = renderingContext.getIdsToVisit(); assertTrue(idsToVisit.isEmpty()); assertTrue(idsToVisit.size() == 0); iterator = idsToVisit.iterator(); assertNotNull(iterator); assertFalse(iterator.hasNext()); Set<String> idsToAdd = asSet("someIds", "thisIs:evenBetter", "myForm:table:0:nestedText"); idsToVisit.addAll(idsToAdd); assertFalse(idsToVisit.isEmpty()); assertTrue(idsToVisit.size() == 3); iterator = idsToVisit.iterator(); while (iterator.hasNext()) { String nextId = iterator.next(); assertTrue(idsToAdd.remove(nextId)); iterator.remove(); } assertTrue(idsToVisit.isEmpty()); assertTrue(idsToVisit.size() == 0); assertTrue(idsToAdd.isEmpty()); iterator = idsToVisit.iterator(); try { iterator.remove(); fail(); } catch (IllegalStateException e) { // ignore } idsToVisit.add("testId"); assertFalse(idsToVisit.isEmpty()); iterator = idsToVisit.iterator(); try { iterator.remove(); fail(); } catch (IllegalStateException e) { // ignore } } @Test public void testSubtreeIdsToForAjaxOutputs() throws Exception { createVisitContext(false); assertSame(VisitContext.ALL_IDS, renderingContext.getSubtreeIdsToVisit(form)); assertEqualSets(asSet("table", "outerOutput"), renderingContext.getDirectSubtreeIdsToVisit(form)); assertSame(VisitContext.ALL_IDS, renderingContext.getSubtreeIdsToVisit(table)); assertEqualSets(asSet("nestedOutput"), renderingContext.getDirectSubtreeIdsToVisit(table)); assertTrue(renderingContext.getSubtreeIdsToVisit(nestedTable).isEmpty()); assertTrue(renderingContext.getDirectSubtreeIdsToVisit(nestedTable).isEmpty()); } @Test public void testSubtreeIdsForEmptyIdsWithLimitRender() throws Exception { createVisitContext(true); assertTrue(renderingContext.getSubtreeIdsToVisit(form).isEmpty()); assertTrue(renderingContext.getDirectSubtreeIdsToVisit(form).isEmpty()); assertTrue(renderingContext.getSubtreeIdsToVisit(table).isEmpty()); assertTrue(renderingContext.getDirectSubtreeIdsToVisit(table).isEmpty()); assertTrue(renderingContext.getSubtreeIdsToVisit(nestedTable).isEmpty()); assertTrue(renderingContext.getDirectSubtreeIdsToVisit(nestedTable).isEmpty()); } @Test public void testSubtreeIdsForOuterOutputWithLimitRender() throws Exception { createVisitContext(true); renderingContext.getIdsToVisit().add("myForm:outerOutput"); assertEqualSets(asSet("myForm:outerOutput"), renderingContext.getSubtreeIdsToVisit(form)); assertEqualSets(asSet("outerOutput"), renderingContext.getDirectSubtreeIdsToVisit(form)); assertTrue(renderingContext.getSubtreeIdsToVisit(table).isEmpty()); assertTrue(renderingContext.getDirectSubtreeIdsToVisit(table).isEmpty()); } @Test public void testSubtreeIdsForNestedComponentsWithLimitRender() throws Exception { createVisitContext(true); renderingContext.getIdsToVisit().add("myForm:outerOutput"); renderingContext.getIdsToVisit().add("myForm:table:theHeader"); renderingContext.getIdsToVisit().add("myForm:table:1:nestedOutput"); renderingContext.getIdsToVisit().add("myForm:table:0:nestedText"); renderingContext.getIdsToVisit().add("myForm:table:0:nestedTable:1:nestedTableText"); renderingContext.getIdsToVisit().add("myForm:table:0:nestedTable:nestedFooter"); Set<String> formClientIds = asSet("myForm:outerOutput", "myForm:table:0:nestedText", "myForm:table:1:nestedOutput", "myForm:table:theHeader", "myForm:table:0:nestedTable:1:nestedTableText", "myForm:table:0:nestedTable:nestedFooter"); Set<String> formIds = asSet("table", "outerOutput"); assertEqualSets(formClientIds, renderingContext.getSubtreeIdsToVisit(form)); assertEqualSets(formIds, renderingContext.getDirectSubtreeIdsToVisit(form)); Set<String> tableClientIds = asSet("myForm:table:0:nestedText", "myForm:table:1:nestedOutput", "myForm:table:theHeader", "myForm:table:0:nestedTable:1:nestedTableText", "myForm:table:0:nestedTable:nestedFooter"); Set<String> tableIds = asSet("0", "1", "theHeader"); assertEqualSets(tableClientIds, renderingContext.getSubtreeIdsToVisit(table)); assertEqualSets(tableIds, renderingContext.getDirectSubtreeIdsToVisit(table)); table.setRowIndex(0); Set<String> nestedTableClientIds = asSet("myForm:table:0:nestedTable:1:nestedTableText", "myForm:table:0:nestedTable:nestedFooter"); Set<String> nestedTableIds = asSet("nestedFooter", "1"); assertEqualSets(nestedTableClientIds, renderingContext.getSubtreeIdsToVisit(nestedTable)); assertEqualSets(nestedTableIds, renderingContext.getDirectSubtreeIdsToVisit(nestedTable)); table.setRowIndex(-1); } @Test public void testVisitCallbackForEmptyIds() throws Exception { createVisitContext(false); viewRoot.visitTree(renderingContext, trackingVisitCallback); assertEquals(Arrays.asList("myForm:outerOutput", "myForm:table:0:nestedOutput", "myForm:table:1:nestedOutput"), trackingVisitCallback.getVisitedIds()); } @Test public void testVisitCallbackForEmptyIdsWithLimitRender() throws Exception { createVisitContext(true); viewRoot.visitTree(renderingContext, trackingVisitCallback); assertEquals(Arrays.asList(), trackingVisitCallback.getVisitedIds()); } @Test public void testVisitCallbackForEmptyIdsForFalseAjaxRendered() throws Exception { outerOutput.setAjaxRendered(false); createVisitContext(false); viewRoot.visitTree(renderingContext, trackingVisitCallback); assertEquals(Arrays.asList("myForm:table:0:nestedOutput", "myForm:table:1:nestedOutput"), trackingVisitCallback.getVisitedIds()); } @Test public void testVisitCallback() throws Exception { createVisitContext(false); renderingContext.getIdsToVisit().add("myForm:table:1:nestedTable:1:nestedTableText"); renderingContext.getIdsToVisit().add("myForm:table:1:nestedTable:nestedTableFooter"); viewRoot.visitTree(renderingContext, trackingVisitCallback); assertEquals(Arrays.asList("myForm:outerOutput", "myForm:table:0:nestedOutput", "myForm:table:1:nestedOutput", "myForm:table:1:nestedTable:nestedTableFooter", "myForm:table:1:nestedTable:1:nestedTableText"), trackingVisitCallback.getVisitedIds()); } @Test public void testVisitMetaComponentsWithLimitRender() throws Exception { createVisitContext(true); renderingContext.getIdsToVisit().add("myForm:table:1:nestedTable@footer"); boolean visitResult = viewRoot.visitTree(renderingContext, trackingVisitCallback); assertEquals(Arrays.asList("myForm:table:1:nestedTable@footer"), trackingVisitCallback.getVisitedIds()); assertTrue(visitResult); } @Test public void testFormVisitContext() throws Exception { createVisitContext(false); Collection<String> formDirectIds = renderingContext.getDirectSubtreeIdsToVisit(form); assertNotSame(VisitContext.ALL_IDS, formDirectIds); ExtendedVisitContext formVisitContext = (ExtendedVisitContext) renderingContext.createNamingContainerVisitContext(form, formDirectIds); assertFalse(formVisitContext.getIdsToVisit().isEmpty()); assertSame(VisitContext.ALL_IDS, formVisitContext.getSubtreeIdsToVisit(form)); assertTrue(formVisitContext.getSubtreeIdsToVisit(table).isEmpty()); Collection<String> directIds = formVisitContext.getDirectSubtreeIdsToVisit(form); assertEqualSets(asSet("outerOutput", "table"), directIds); assertTrue(formVisitContext.getDirectSubtreeIdsToVisit(table).isEmpty()); boolean visitResult = form.visitTree(formVisitContext, trackingVisitCallback); assertTrue(visitResult); assertEquals(Arrays.asList("myForm:outerOutput", "myForm:table"), trackingVisitCallback.getVisitedIds()); } @Test public void testFormVisitContextWithLimitRender() throws Exception { createVisitContext(true); renderingContext.getIdsToVisit().add("myForm:table:0:nestedText"); Collection<String> formDirectIds = renderingContext.getDirectSubtreeIdsToVisit(form); assertNotSame(VisitContext.ALL_IDS, formDirectIds); ExtendedVisitContext formVisitContext = (ExtendedVisitContext) renderingContext.createNamingContainerVisitContext(form, formDirectIds); assertFalse(formVisitContext.getIdsToVisit().isEmpty()); assertSame(VisitContext.ALL_IDS, formVisitContext.getSubtreeIdsToVisit(form)); assertTrue(formVisitContext.getSubtreeIdsToVisit(table).isEmpty()); Collection<String> directIds = formVisitContext.getDirectSubtreeIdsToVisit(form); assertEqualSets(asSet("table"), directIds); assertTrue(formVisitContext.getSubtreeIdsToVisit(table).isEmpty()); boolean visitResult = form.visitTree(formVisitContext, trackingVisitCallback); assertTrue(visitResult); assertEquals(Arrays.asList("myForm:table"), trackingVisitCallback.getVisitedIds()); } @Test public void testFormVisitContextNonexistentIdsWithLimitRender() throws Exception { createVisitContext(true); renderingContext.getIdsToVisit().add("myForm:nonExistentId"); Collection<String> formDirectIds = renderingContext.getDirectSubtreeIdsToVisit(form); assertNotSame(VisitContext.ALL_IDS, formDirectIds); ExtendedVisitContext formVisitContext = (ExtendedVisitContext) renderingContext.createNamingContainerVisitContext(form, formDirectIds); assertFalse(formVisitContext.getIdsToVisit().isEmpty()); assertSame(VisitContext.ALL_IDS, formVisitContext.getSubtreeIdsToVisit(form)); assertTrue(formVisitContext.getSubtreeIdsToVisit(table).isEmpty()); Collection<String> directIds = formVisitContext.getDirectSubtreeIdsToVisit(form); assertEqualSets(asSet("nonExistentId"), directIds); assertTrue(formVisitContext.getSubtreeIdsToVisit(table).isEmpty()); boolean visitResult = form.visitTree(formVisitContext, trackingVisitCallback); assertFalse(visitResult); assertEquals(Arrays.asList(), trackingVisitCallback.getVisitedIds()); } @Test public void testVisitMultiple() throws Exception { createVisitContext(true); String idFormat = "myForm:table:{0}:nestedTable:1:nestedTableText"; renderingContext.getIdsToVisit().add(MessageFormat.format(idFormat, 0)); renderingContext.getIdsToVisit().add(MessageFormat.format(idFormat, 1)); boolean visitResult = viewRoot.visitTree(renderingContext, trackingVisitCallback); assertTrue(visitResult); assertEquals( Arrays.asList("myForm:table:0:nestedTable:1:nestedTableText", "myForm:table:1:nestedTable:1:nestedTableText"), trackingVisitCallback.getVisitedIds()); } @Test public void testVisitMultipleWithPatternAndMetacomponent() throws Exception { createVisitContext(true); String idFormat = "myForm:table:{0}:nestedTable:{1}:nestedTableText"; renderingContext.getIdsToVisit().add(MessageFormat.format(idFormat, 0, 0)); renderingContext.getIdsToVisit().add(MessageFormat.format(idFormat, 0, 1)); renderingContext.getIdsToVisit().add(MessageFormat.format(idFormat, 1, 0)); renderingContext.getIdsToVisit().add(MessageFormat.format(idFormat, 1, 1)); renderingContext.getIdsToVisit().add("myForm:table:0:nestedTable@footer"); viewRoot.visitTree(renderingContext, trackingVisitCallback); assertEquals(Arrays.asList("myForm:table:0:nestedTable@footer", "myForm:table:0:nestedTable:0:nestedTableText", "myForm:table:0:nestedTable:1:nestedTableText", "myForm:table:1:nestedTable:0:nestedTableText", "myForm:table:1:nestedTable:1:nestedTableText"), trackingVisitCallback.getVisitedIds()); } // TODO nick - add alike tests @Test public void testMultipleMetaComponentIds() throws Exception { createVisitContext(true); renderingContext.getIdsToVisit().add("myForm:table@header"); renderingContext.getIdsToVisit().add("myForm:table@footer"); assertTrue(renderingContext.getIdsToVisit().contains("myForm:table@header")); assertTrue(renderingContext.getIdsToVisit().contains("myForm:table@footer")); assertFalse(renderingContext.getIdsToVisit().contains("myForm")); assertFalse(renderingContext.getIdsToVisit().contains("table")); assertFalse(renderingContext.getIdsToVisit().contains("myForm:table")); } @Test public void testVisitForm() throws Exception { createVisitContext(true); renderingContext.getIdsToVisit().add("myForm"); boolean result = facesContext.getViewRoot().visitTree(renderingContext, trackingVisitCallback); assertTrue(result); assertEquals(Arrays.asList("myForm"), trackingVisitCallback.getVisitedIds()); } }