/* * � Copyright IBM Corp. 2013, 2015 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. */ /* * Author: Maire Kehoe (mkehoe@ie.ibm.com) * Date: 13 Aug 2008 * PropertyAllowsValueTest.java */ package com.ibm.xsp.test.framework.registry; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import com.ibm.commons.util.StringUtil; import com.ibm.xsp.registry.FacesComplexDefinition; import com.ibm.xsp.registry.FacesComponentDefinition; import com.ibm.xsp.registry.FacesContainerProperty; import com.ibm.xsp.registry.FacesDefinition; import com.ibm.xsp.registry.FacesLibraryFragment; import com.ibm.xsp.registry.FacesProject; import com.ibm.xsp.registry.FacesProperty; import com.ibm.xsp.registry.FacesPropertyType; import com.ibm.xsp.registry.FacesSharableRegistry; import com.ibm.xsp.registry.FacesSimpleProperty; import com.ibm.xsp.registry.RegistryUtil; import com.ibm.xsp.registry.types.FacesSimpleTypes; import com.ibm.xsp.test.framework.AbstractXspTest; import com.ibm.xsp.test.framework.TestProject; import com.ibm.xsp.test.framework.XspTestUtil; import com.ibm.xsp.test.framework.setup.SkipFileContent; /** * @author Maire Kehoe (mkehoe@ie.ibm.com) * 13 Aug 2008 * Unit: PropertyAllowsValueTest.java */ public class PropertyAllowsValueTest extends AbstractXspTest { @Override public String getDescription() { return "that every property has some value that can be set"; } public void testCanSetProperty() throws Exception { FacesSharableRegistry reg = TestProject.createRegistry(this); HashMap<Class<?>, Boolean> propertyClassToHasComplex = new HashMap<Class<?>, Boolean>(); List<Class<?>> propertyClassSkips = getPropertyClassSkips(); List<Class<?>> checkedPropClassSkips = new ArrayList<Class<?>>(propertyClassSkips); String fails = ""; for(FacesDefinition def : TestProject.getLibCompComplexDefs(reg, this)){ // for each local property for (String name : def.getDefinedPropertyNames()) { FacesProperty localProp = def.getProperty(name); FacesSimpleProperty simple = toSimple(localProp); if( null == simple ){ // it's a method binding prop, assume they're valid continue; } if( isPrimitive(simple) ){ if (!simple.isAllowNonBinding() && !simple.isAllowLoadTimeBinding() && !simple.isAllowRunTimeBinding()) { fails += def.getFile().getFilePath()+" "+descr(def, name)+" property cannot be set to a primitive nor a binding\n"; } }else{ // non-primitive, usually complex-type if( !simple.isAllowNonBinding() ){ // not allow complex-type, so should allow runtime or load time binding. if( !simple.isAllowLoadTimeBinding() && !simple.isAllowRunTimeBinding() ){ fails += def.getFile().getFilePath()+" "+descr(def, name)+" property cannot be set to a binding nor a complex-type\n"; } }else{ // allow non-binding, so should have a corresponding complex-type Class<?> propertyClass = simple.getJavaClass(); Boolean hasComplex = propertyClassToHasComplex.get(propertyClass); if( null == hasComplex ){ hasComplex = computeHasComplex(reg,simple); if( !hasComplex){ int skipIndex = propertyClassSkips.indexOf(propertyClass); if( -1 != skipIndex ){ hasComplex = true; // mark propertyClassSkip used checkedPropClassSkips.set(skipIndex, null); } } propertyClassToHasComplex.put(propertyClass, hasComplex); } if( ! hasComplex.booleanValue() ){ String fail = def.getFile().getFilePath()+" "+descr(def, name) + " No complex-type that can be set for property-class " + propertyClass.getName() + " and not explicitly preventing non-binding"; fails += fail + "\n"; } } } } } List<Object[]>extraToTest = getExtraLibPropClassesToTest(); for (Object[] libToClassArr : extraToTest) { String libId = (String) libToClassArr[0]; Class<?>[] propClassArr = (Class<?>[]) libToClassArr[1]; List<Class<?>> extraPropClassList = Arrays.asList(propClassArr); boolean[] foundPropClass = new boolean[propClassArr.length]; for (FacesDefinition def : getLibCompComplexDefs(reg, libId) ) { // for each local property for (String name : def.getDefinedPropertyNames()) { FacesProperty localProp = def.getProperty(name); FacesSimpleProperty simple = toSimple(localProp); if( null == simple || isPrimitive(simple)){ // not an extra class continue; } Class<?> propertyClass = simple.getJavaClass(); int extraPropClassIndex = extraPropClassList.indexOf(propertyClass); if( -1 == extraPropClassIndex ){ // not in the list of extra property-classes to check continue; } foundPropClass[extraPropClassIndex] = true; Boolean hasComplex = propertyClassToHasComplex.get(propertyClass); if( null == hasComplex ){ hasComplex = computeHasComplex(reg,simple); if( !hasComplex){ int skipIndex = propertyClassSkips.indexOf(propertyClass); if( -1 != skipIndex ){ hasComplex = false; // mark propertyClassSkip used checkedPropClassSkips.set(skipIndex, null); } } propertyClassToHasComplex.put(propertyClass, hasComplex); } if( ! hasComplex.booleanValue() ){ String fail = def.getFile().getFilePath()+" "+descr(def, name) + " No complex-type that can be set for the extra property-class " + propertyClass.getName(); fails += fail+"\n"; } } } int i = 0; for (boolean found : foundPropClass) { if( ! found ){ fails += "Extra library property-class not found: "+propClassArr[i].getName()+"\n"; } i++; } } for (Class<?> skip : checkedPropClassSkips) { if( null != skip ){ fails += "Unused property-class skip: "+skip.getName()+"\n"; } } fails = XspTestUtil.removeMultilineFailSkips(fails, SkipFileContent.concatSkips(getSkips(), this, "testCanSetProperty")); if( fails.length() > 0 ){ fail(XspTestUtil.getMultilineFailMessage(fails)); } } private List<FacesDefinition> getLibCompComplexDefs(FacesSharableRegistry reg, String libId) { boolean libraryFound = false; List<FacesDefinition> list = null; for (FacesSharableRegistry depend : reg.getDepends()) { if( !depend.getId().endsWith(libId) ){ continue; } libraryFound = true; for (FacesProject proj : depend.getLocalProjectList()) { for (FacesLibraryFragment file : proj.getFiles()) { for (FacesDefinition def : file.getDefs()) { if( def instanceof FacesComplexDefinition || def instanceof FacesComponentDefinition ){ // found one if( null == list ){ list = new ArrayList<FacesDefinition>(); } list.add(def); } } } } } if( ! libraryFound ){ throw new RuntimeException("Library not found in depends registries: "+libId); } if( null == list ){ return Collections.emptyList(); } return list; } protected List<Class<?>> getPropertyClassSkips() { return new ArrayList<Class<?>>(); } /** * {stringLibName1, Class[]{propClass1, propClass2} }, * {stringLibNam2, Class[]{propClass1, propClass2, propClass3} } * @return */ protected List<Object[]> getExtraLibPropClassesToTest(){ return new ArrayList<Object[]>(); } private String descr(FacesDefinition def, String name) { return XspRegistryTestUtil.descr(def, name); } protected String[] getSkips() { return StringUtil.EMPTY_STRING_ARRAY; } private Boolean computeHasComplex(FacesSharableRegistry reg, FacesSimpleProperty simple) { // find if there's some complex-type tag that can be used for the // property. This is the same mechanism DDE uses to populate the [+] // menu in the All Properties view. FacesDefinition typeDef = simple.getTypeDefinition(); if( typeDef instanceof FacesPropertyType ) { return Boolean.TRUE; } FacesComplexDefinition complexInterface = (FacesComplexDefinition) typeDef; for(FacesDefinition def : RegistryUtil.getSubstitutableDefinitions(complexInterface, reg)){ if( def.isTag() && def instanceof FacesComplexDefinition ){ return Boolean.TRUE; } } return Boolean.FALSE; } private boolean isPrimitive(FacesSimpleProperty simple) { int type = simple.getType(); return FacesSimpleTypes.isPrimitive(type) || FacesSimpleTypes.isPrimitiveObject(type) || FacesSimpleTypes.isGeneric(type); } private FacesSimpleProperty toSimple(FacesProperty prop) { if( prop instanceof FacesContainerProperty ){ prop = ((FacesContainerProperty)prop).getItemProperty(); } if( prop instanceof FacesSimpleProperty){ return (FacesSimpleProperty) prop; } return null; } }