/*
* � Copyright IBM Corp. 2013
*
* 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: 24-Mar-2006
* PropertiesHaveCategoriesTest.java
*/
package com.ibm.xsp.test.framework.registry.annotate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.ibm.commons.util.StringUtil;
import com.ibm.xsp.registry.AbstractFacesDefinition;
import com.ibm.xsp.registry.FacesComplexDefinition;
import com.ibm.xsp.registry.FacesComponentDefinition;
import com.ibm.xsp.registry.FacesDefinition;
import com.ibm.xsp.registry.FacesExtensibleNode;
import com.ibm.xsp.registry.FacesGroupDefinition;
import com.ibm.xsp.registry.FacesProperty;
import com.ibm.xsp.registry.FacesSharableRegistry;
import com.ibm.xsp.registry.RegistryUtil;
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.registry.annotate.DesignerExtensionSubsetAnnotater;
import com.ibm.xsp.test.framework.setup.SkipFileContent;
/**
* @author Maire Kehoe (mkehoe@ie.ibm.com) 24-Mar-2006
*
* Unit: PropertiesHaveCategoriesTest.java
*/
public class PropertiesHaveCategoriesTest extends AbstractXspTest{
@Override
public String getDescription() {
return "that most <property>s have a <category> in the <designer-extensio>, " +
"except for <property>s in <complex-type> defs " +
"(or <validator>s, <converter>s or <group>s used in complex defs)";
}
public void testPropertyCategory() throws Exception {
FacesSharableRegistry reg = TestProject.createRegistryWithAnnotater(
this, new PropertyCategoryAnnotater(), new DefinitionTagsAnnotater());
String fails = "";
// scan through all defs, except for the groups, will handle them below
for (FacesDefinition def : TestProject.getLibDefinitions(reg, this)) {
if( ! def.isTag() ){
continue;
}
boolean expectPropCategory;
if( def instanceof FacesComplexDefinition ){
expectPropCategory = false;
}else if( def instanceof FacesComponentDefinition ){
expectPropCategory = true;
}else{
continue; // group definition, check below
}
for (FacesProperty prop : RegistryUtil.getProperties(def)) {
String propCategory = (String) prop.getExtension("category");
boolean hasPropCategory = null != propCategory;
if( expectPropCategory != hasPropCategory ){
String propName = prop.getName();
String ref = def.getFile().getFilePath()+" "+def.getFirstDefaultPrefix()+":"+def.getTagName()+" \"" +propName+"\"";
if( expectPropCategory ){
fails += ref+" Category not present in <component> <property>.\n";
}else{
fails += ref+" Category should not be present in <complex-type> <property>, was " +propCategory+".\n";
}
}
}
}
// find uses of groups.
List<FacesGroupDefinition> groupsInAllLibs = new ArrayList<FacesGroupDefinition>();
Map<String, List<FacesDefinition>> groupIdToUses = new HashMap<String, List<FacesDefinition>>();
for (FacesDefinition def : reg.findDefs()) {
if( def instanceof FacesGroupDefinition ){
groupsInAllLibs.add((FacesGroupDefinition)def);
}
Collection<String> groupTypeRefs = ((AbstractFacesDefinition)def).getGroupTypeRefs();
if( ! groupTypeRefs.isEmpty() ){
for (String groupRef : groupTypeRefs) {
List<FacesDefinition> uses = groupIdToUses.get(groupRef);
if( null == uses ){
uses = new ArrayList<FacesDefinition>();
groupIdToUses.put(groupRef, uses);
}
uses.add(def);
}
}
}
// Build a map of <String groupReferenceId,
// FacesComplexDefinition.class|FacesComponentDefinition.class|null|int.class>
Map<String, Class<?>> groupIdToType = buildUseTypeMap(groupsInAllLibs, groupIdToUses);
// for the groups in the library (not all groups in the reg)
List<FacesGroupDefinition> groupsToTest = new ArrayList<FacesGroupDefinition>();
for (FacesDefinition def : TestProject.getLibDefinitions(reg, this)) {
if( def instanceof FacesGroupDefinition ){
groupsToTest.add((FacesGroupDefinition)def);
}
}
for (String groupId : getExtraGroupsToTest()) {
FacesGroupDefinition group = (FacesGroupDefinition) reg.findDef(groupId);
if( null == group ){
fails += groupId+" Cannot find group for id in getExtraGroupsToTest()\n";
}else{
groupsToTest.add(group);
}
}
for (FacesGroupDefinition group : groupsToTest) {
Class<?> type = groupIdToType.get(group.getReferenceId());
boolean expectPropCategory;
if( null == type ){
fails += group.getFile().getFilePath()+" "+group.getReferenceId()+" Unused group, cannot check <property>s for <category>s\n";
continue;
}else if( int.class.equals(type) ){
fails += group.getFile().getFilePath()+" "+group.getReferenceId()+" Dependancy loop, cannot check <property>s for <category>s\n";
continue;
}else if( FacesComplexDefinition.class.equals(type) ){
expectPropCategory = false;
}else if( FacesComponentDefinition.class.equals(type) ){
expectPropCategory = true;
}else{
throw new RuntimeException("Unexpected problem problem: " +type.getName()+" "+group.getReferenceId());
}
boolean isTaggedGroupInComplex = DefinitionTagsAnnotater.isGroupTaggedGroupInComplex(group);
if( (isTaggedGroupInComplex) != (!expectPropCategory) ){
// This test is the only place that actually computes whether a group is a complex
// or a component group, so this is where we verify the presence of
// <tags>group-in-complex<. That tag is not actually needed by this test,
// it is used in PropertyStyleTest and other tests where the <category> is verified.
if( expectPropCategory ){
// control.
fails += group.getFile().getFilePath()+" "+group.getReferenceId()
+" Unexpected <tags>group-in-complex< in a <component> <group>\n";
}else{
// complex.
fails += group.getFile().getFilePath()+" "+group.getReferenceId()
+" Missing <tags>group-in-complex< in a <complex-type> <group>\n";
}
}
boolean isTaggedGroupInControl = DefinitionTagsAnnotater.isGroupTaggedGroupInControl(group);
if( (isTaggedGroupInControl) != (expectPropCategory) ){
// This test is the only place that actually computes whether a group is a complex
// or a component group, so this is where we verify the presence of
// <tags>group-in-control<. That tag is not actually needed by this test,
// it is used in PropertyStyleTest and other tests where the <category> is verified.
if( expectPropCategory ){
// control.
fails += group.getFile().getFilePath()+" "+group.getReferenceId()
+" Missing <tags>group-in-control< in a <component> <group>\n";
}else{
// complex.
fails += group.getFile().getFilePath()+" "+group.getReferenceId()
+" Unexpected <tags>group-in-control< in a <complex-type> <group>\n";
}
}
for (FacesProperty prop : RegistryUtil.getProperties(group)) {
String propCategory = (String) prop.getExtension("category");
boolean hasPropCategory = null != propCategory;
if( expectPropCategory != hasPropCategory ){
String propName = prop.getName();
String ref = group.getFile().getFilePath()+" "+group.getReferenceId()+" \"" +propName+"\"";
if( expectPropCategory ){
fails += ref+" Category not present in <component> <group>.\n";
}else{
fails += ref+" Category should not be present in <complex-type> <group>, was " +propCategory+".\n";
}
}
}
// have the value expectPropCategory based on the 1st def that uses this group,
// check that all the defs that use this group have the type
List<FacesDefinition> uses = groupIdToUses.get(group.getReferenceId());
for (FacesDefinition use : uses) {
boolean useExpectPropCategory;
if( use instanceof FacesComplexDefinition ){
useExpectPropCategory = false;
}else if( use instanceof FacesComponentDefinition ){
useExpectPropCategory = true;
}else{
Class<?> useType = groupIdToType.get(use.getReferenceId());
if( FacesComplexDefinition.class.equals(useType) ){
useExpectPropCategory = false;
}else if( FacesComponentDefinition.class.equals(useType) ){
useExpectPropCategory = true;
}else{
continue; // will add a fail when iterating through the groups
}
}
if( expectPropCategory != useExpectPropCategory ){
fails +=group.getFile().getFilePath()+" "+group.getReferenceId()+ " is used by "
+ use.getReferenceId()+ " as a "
+ (useExpectPropCategory ? "<component> <group>" : "<complex-type> <group>")
+ " but it is normally used as a "
+ (expectPropCategory ? "<component> <group>" : "<complex-type> <group>")
+ " (expectPropCategory=" + expectPropCategory + ")\n";
}
}
}
fails = XspTestUtil.removeMultilineFailSkips(fails,
SkipFileContent.concatSkips(getSkips(), this, "testPropertyCategory"));
if( fails.length() > 0 ){
fail(XspTestUtil.getMultilineFailMessage(fails));
}
}
protected List<String> getExtraGroupsToTest() {
return new ArrayList<String>();
}
protected String[] getSkips(){
return StringUtil.EMPTY_STRING_ARRAY;
}
private Map<String, Class<?>> buildUseTypeMap(
List<FacesGroupDefinition> groups,
Map<String, List<FacesDefinition>> groupIdToUses) {
Map<String, Class<?>> groupIdToUseType = new HashMap<String, Class<?>>();
for (FacesGroupDefinition group : groups) {
String groupId = group.getReferenceId();
if( groupIdToUseType.containsKey(groupId) ){
continue;
}
Class<?> useType = findUseType(groupId, groupIdToUseType, groupIdToUses);
groupIdToUseType.put(groupId, useType);
}
return groupIdToUseType;
}
private Class<?> findUseType(String groupId,
Map<String, Class<?>> groupIdToUseType,
Map<String, List<FacesDefinition>> groupIdToUses) {
if( groupIdToUseType.containsKey(groupId) ){
return groupIdToUseType.get(groupId);
}
List<FacesDefinition> uses = groupIdToUses.get(groupId);
if( null == uses ){
return null;
}
for (FacesDefinition use : uses) {
if( use instanceof FacesComplexDefinition ){
return FacesComplexDefinition.class;
}else if( use instanceof FacesComponentDefinition ){
return FacesComponentDefinition.class;
}
}
// else all the uses are groups
FacesGroupDefinition use = (FacesGroupDefinition)uses.get(0);
// use int.class as a marker to prevent StackOverflowError when find dependancy loops
groupIdToUseType.put(groupId, int.class);
Class<?> useType = findUseType(use.getReferenceId(), groupIdToUseType, groupIdToUses);
groupIdToUseType.put(use.getReferenceId(), useType);
return useType;
}
public static class PropertyCategoryAnnotater extends DesignerExtensionSubsetAnnotater{
@Override
protected boolean isApplicableExtensibleNode(FacesExtensibleNode parsed) {
return parsed instanceof FacesProperty;
}
@Override
protected String[] createExtNameArr() {
return new String[]{
// http://www-10.lotus.com/ldd/ddwiki.nsf/dx/XPages_configuration_file_format_page_3#ext-property-category
"category",
};
}
}
}