/*
* � Copyright IBM Corp. 2009, 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: 3 Jun 2009
* PrintTagNamesAndProps.java
*/
package com.ibm.xsp.test.framework.version;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.ibm.commons.util.StringUtil;
import com.ibm.xsp.library.StandardRegistryMaintainer;
import com.ibm.xsp.registry.FacesComplexDefinition;
import com.ibm.xsp.registry.FacesComponentDefinition;
import com.ibm.xsp.registry.FacesDefinition;
import com.ibm.xsp.registry.FacesLibrary;
import com.ibm.xsp.registry.FacesLibraryFragment;
import com.ibm.xsp.registry.FacesRegistry;
import com.ibm.xsp.registry.FacesSharableRegistry;
import com.ibm.xsp.test.framework.XspTestUtil;
/**
*
* @author Maire Kehoe (mkehoe@ie.ibm.com)
* 3 Jun 2009
* Unit: PrintTagNamesAndProps.java
*/
public class PrintTagNamesAndProps {
private static final class TagsComparator implements Comparator<Object[]> {
public int compare(Object[] o1, Object[] o2) {
String s1 = getName(o1);
String s2 = getName(o2);
return s1.compareTo(s2);
}
}
public static void main(String[] args) {
FacesSharableRegistry registry = StandardRegistryMaintainer.getStandardRegistry();
System.out.println("PrintTagNamesAndProps.main()");
List<Object[]> tagsAndProps = getTagsAndProps(registry);
// removeAll(tagsAndProps, SinceVersion850List.tagsAndProps, true);
// removeAll(tagsAndProps, SinceVersion851List.tagsAndProps, true);
// removeAll(tagsAndProps, SinceVersion852List.tagsAndProps, true);
// removeAll(tagsAndProps, SinceVersion853List.tagsAndProps, true);
print(tagsAndProps);
}
/**
* @param tagsAndProps
*/
public static void print(List<Object[]> tagsAndProps) {
for (Object[] tagAndProp : tagsAndProps) {
String prefixedName = getName(tagAndProp);
if( isNoProps(tagAndProp) ){
// new Object[]{"xp:validator"}
System.out.println("new Object[]{\"" +
prefixedName +
"\", " +isNewTag(tagAndProp)+"},");
continue;
}
String[] propNames = getProps(tagAndProp);
//new Object[]{"xp:validator", {
// "validatorId",
//}};
System.out.println("new Object[]{\"" +
prefixedName +
"\", " +isNewTag(tagAndProp)+", new String[]{");
for (String name : propNames) {
System.out.println("\t\""+name+"\",");
}
System.out.println("}},");
}
}
/**
* Note, calling them a depends library to distinguish from a namespace library.
* @param registry
* @param dependsLibraryId
* @param fullList
* @return
*/
public static List<Object[]> filterToDependsLibrary(FacesSharableRegistry registry, String dependsLibraryId, List<Object[]>fullList){
FacesSharableRegistry filterToReg = null;
for (FacesSharableRegistry depend : registry.getDepends()) {
if(dependsLibraryId.equals(depend.getId()) ){
filterToReg = depend;
break;
}
}
if( null == filterToReg ){
throw new IllegalArgumentException("Cannot find the library registry with ID: "+dependsLibraryId);
}
Map<String, FacesLibrary> prefixToNamespace = getPrefixToNamespace(registry);
List<FacesDefinition> libDefs = filterToReg.findLocalDefs();
List<Object[]> output = new ArrayList<Object[]>(libDefs.size());
for (Object[] fullListTagArr : fullList) {
String prefixedName = getName(fullListTagArr);
FacesDefinition fullListDef = getDef(prefixToNamespace, prefixedName);
if( libDefs.contains(fullListDef) ){
output.add(fullListTagArr);
}
}
return output;
}
/**
* @param registry
* @return
*/
public static List<Object[]> getTagsAndProps(FacesSharableRegistry registry) {
return getTagsAndProps(registry, registry);
}
/**
* @param registryToList
* @param fullRegistry
* (if registryToList contains non-tag defs that have no
* descendants in registryToList, but do have descendants in
* fullRegistry, those defs will be included in the listing.)
* @return
*/
public static List<Object[]> getTagsAndProps(FacesSharableRegistry registryToList, FacesSharableRegistry fullRegistry) {
List<String> tagsAndParents = getTagsAndUsedParents(registryToList, fullRegistry);
Collections.sort(tagsAndParents);
Map<String, FacesLibrary> prefixToNamespace = getPrefixToNamespace(fullRegistry);
List<Object[]> tagsAndProps = new ArrayList<Object[]>();
for (String prefixedName : tagsAndParents) {
FacesDefinition def = getDef(prefixToNamespace, prefixedName);
Collection<String> propNames = def.getDefinedPropertyNames();
if( propNames.isEmpty() ){
tagsAndProps.add(new Object[]{prefixedName, true});
continue;
}
Object[] tagAndProp = new Object[]{prefixedName, true, null};
List<String> sortedNames = new ArrayList<String>(propNames);
Collections.sort(sortedNames);
Object[] editedTag = withProps(tagAndProp, StringUtil.toStringArray(sortedNames));
tagsAndProps.add(editedTag);
}
return tagsAndProps;
}
public static void addAll(List<Object[]> oldTags, Object[][] recentTags){
for (Object[] recentTag : recentTags) {
String recentPrefixedName = getName(recentTag);
int tagIndex = indexOf(oldTags, recentPrefixedName);
if( tagIndex < 0 ){
// new tag
int insertIndex = -(tagIndex+1);
oldTags.add(insertIndex, recentTag);
continue;
}
// change to existing tag, props added
Object[] oldTag = oldTags.get(tagIndex);
String[] oldProps = getProps(oldTag);
String[] newProps = getProps(recentTag);
oldProps = XspTestUtil.concat(oldProps, newProps);
Arrays.sort(oldProps);
Object[] editedTag = withProps(oldTag, oldProps);
oldTags.set(tagIndex, editedTag);
}
}
public static void removeAll(List<Object[]> current,
Object[][] oldTags, boolean markAsOld) {
for (Object[] oldTag : oldTags) {
String oldPrefixedName = getName(oldTag);
int tagIndex = indexOf(current, oldPrefixedName);
if( tagIndex < 0 ){
// old tag not available anymore
// (could lead to runtime errors)
continue;
}
Object[] currentTag = current.get(tagIndex);
String[] oldProps = getProps(oldTag);
String[] currentProps = getProps(currentTag);
if( Arrays.deepEquals(oldProps, currentProps) ){
// same props, so remove entire old tag
current.remove(tagIndex);
continue;
}
// some current props not in oldProps,
// so change current props to only have new props.
ArrayList<String> newProps = new ArrayList<String>();
newProps.addAll(Arrays.asList(currentProps));
newProps.removeAll(Arrays.asList(oldProps));
if( newProps.isEmpty() ){
// oldProps contained propertys not in currentProps
current.remove(tagIndex);
continue;
}
Object[] editedTag = withProps(currentTag, StringUtil.toStringArray(newProps));
current.set(tagIndex, editedTag);
if( markAsOld ){
setNewTag(currentTag, false);
}
}
}
private static int indexOf(List<Object[]> current, String oldPrefixedName) {
return Collections.binarySearch(current, new Object[]{oldPrefixedName},
new TagsComparator());
}
/**
* @param prefixToNamespace
* @param prefixedName
* @return
*/
public static FacesDefinition getDef(
Map<String, FacesLibrary> prefixToNamespace, String prefixedName) {
// boolean isTag = prefixedName.contains(":");
String normalized = prefixedName.replace('-', ':');
String[] prefixAndName = normalized.split(":");
FacesLibrary lib = prefixToNamespace.get(prefixAndName[0]);
if( null == lib ){
throw new RuntimeException("Internal logic error, "
+ "prefix map does not contain prefix for "
+ prefixAndName[0]+", which should originally have come from that map.");
}
FacesDefinition def = lib.getDefinition(prefixAndName[1]);
if( null != def ){
return def;
}
for (FacesLibraryFragment file : lib.getFiles()) {
for (FacesDefinition fileDef : file.getDefs()) {
if( fileDef.getReferenceId().equals(prefixAndName[1]) ){
return fileDef;
}
}
}
return null;
}
/**
* @param registry
* @return
*/
public static Map<String, FacesLibrary> getPrefixToNamespace(
FacesRegistry registry) {
Map<String, FacesLibrary> prefixToNamespace = new HashMap<String, FacesLibrary>();
for (String namespace : registry.getNamespaceUris()) {
FacesLibrary lib = registry.getLibrary(namespace);
String prefix = lib.getFirstDefaultPrefix();
prefixToNamespace.put(prefix, lib);
}
return prefixToNamespace;
}
private static List<String> getTagsAndUsedParents(FacesSharableRegistry registryToList, FacesSharableRegistry fullRegistry) {
String parentTag = "is-used-non-tag-parent";
markUsedNonTagParents(fullRegistry, parentTag);
List<String> tagsAndParents = new ArrayList<String>();
for (String namespace : registryToList.getNamespaceUris()) {
FacesLibrary namespaceLib = registryToList.getLibrary(namespace);
String prefix = namespaceLib.getFirstDefaultPrefix();
for (FacesDefinition def : namespaceLib.getDefs()) {
if( ! (def instanceof FacesComplexDefinition || def instanceof FacesComponentDefinition)){
continue;
}
String name = prefix + ":" + def.getTagName();
if( ! def.isTag() ){
boolean isCustomControlBase = "com.ibm.xsp.IncludeComposite".equals(def.getReferenceId());
if( null != def.getExtension(parentTag) ){
// non-tag that is ancestor of something in fullRegistry
name = prefix + "-"+def.getId();
}else if( isCustomControlBase ){
// UIIncludeComposite is actually used, even though it doesn't
// have a tag-name; it's the parent to all the custom controls.
name = prefix + "-"+def.getId();
}else{
continue;
}
}
if( tagsAndParents.contains(name) ){
throw new RuntimeException("More than 1 parent control named "+name);
}
tagsAndParents.add(name);
}
}
return tagsAndParents;
}
private static void markUsedNonTagParents(FacesSharableRegistry fullRegistry, String usedParentTag) {
for (FacesDefinition defInReg : fullRegistry.findDefs()) {
if( defInReg.isTag() ){
markAncestorsOf(defInReg, usedParentTag);
}
}
}
private static void markAncestorsOf(FacesDefinition defInReg,
String usedParentTag) {
FacesDefinition parent = defInReg.getParent();
if( null == parent ){
return;
}
String existingTag = (String) parent.getExtension(usedParentTag);
if( null != existingTag ){
return;
}
parent.setExtension(usedParentTag, usedParentTag);
markAncestorsOf(parent, usedParentTag);
}
public static boolean isNoProps(Object[] oldTag) {
return oldTag.length < 3;
}
public static String[] getProps(Object[] tagAndProp) {
if( isNoProps(tagAndProp) ){
return StringUtil.EMPTY_STRING_ARRAY;
}
return (String[])tagAndProp[2];
}
public static Object[] withProps(Object[] tag, String[] props) {
if( isNoProps(tag) ){
return new Object[]{tag[0], tag[1], props};
}
tag[2] = props;
return tag;
}
public static String getName(Object[] tagAndProp) {
return (String) tagAndProp[0];
}
public static boolean isNewTag(Object[] tag){
return (Boolean) tag[1];
}
public static void setNewTag(Object[] tag, boolean isNewTag){
tag[1] = isNewTag;
}
}