/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.deltaspike.jsf.impl.config.view;
import org.apache.deltaspike.core.api.config.view.ViewConfig;
import org.apache.deltaspike.core.api.config.view.metadata.Aggregated;
import org.apache.deltaspike.core.api.config.view.metadata.ViewMetaData;
import org.apache.deltaspike.core.spi.config.view.ViewConfigInheritanceStrategy;
import org.apache.deltaspike.core.spi.config.view.ViewConfigNode;
import org.apache.deltaspike.jsf.api.config.view.Folder;
import org.apache.deltaspike.jsf.api.config.view.View;
import org.apache.deltaspike.jsf.impl.util.ViewConfigUtils;
import javax.enterprise.inject.Stereotype;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
//TODO remove parts which aren't needed any longer
public class DefaultViewConfigInheritanceStrategy implements ViewConfigInheritanceStrategy
{
@Override
public List<Annotation> resolveInheritedMetaData(ViewConfigNode viewConfigNode)
{
List<Annotation> inheritedAnnotations = new ArrayList<Annotation>();
Set<Class> processedTypes = new HashSet<Class>();
processedTypes.add(ViewConfig.class); //filter the base interface in any case
Stack<Class> classesToAnalyze = new Stack<Class>();
addInterfaces(processedTypes, classesToAnalyze, viewConfigNode.getSource()); //don't add the page-class itself
while (!classesToAnalyze.empty())
{
Class currentClass = classesToAnalyze.pop();
if (processedTypes.contains(currentClass))
{
continue;
}
processedTypes.add(currentClass);
addInterfaces(processedTypes, classesToAnalyze, currentClass);
//don't add the annotations of the final view-config class itself (we just need the inherited annotations)
if (ViewConfigUtils.isFolderConfig(currentClass))
{
inheritedAnnotations.addAll(findViewMetaData(currentClass, viewConfigNode));
}
Class nextClass = currentClass.getSuperclass();
if (nextClass != null && !Object.class.equals(nextClass))
{
if (!processedTypes.contains(nextClass))
{
classesToAnalyze.push(nextClass);
}
}
}
//add meta-data inherited via stereotypes on the node itself
inheritedAnnotations.addAll(findViewMetaData(viewConfigNode.getSource(), viewConfigNode));
return inheritedAnnotations;
}
protected List<Annotation> findViewMetaData(Class currentClass, ViewConfigNode viewConfigNode)
{
//don't include meta-data from the node itself, because it would be stored as inherited meta-data
if (currentClass.equals(viewConfigNode.getSource()))
{
return Collections.emptyList();
}
List<Annotation> result = new ArrayList<Annotation>();
for (Annotation annotation : currentClass.getAnnotations())
{
Class<? extends Annotation> annotationClass = annotation.annotationType();
if (annotationClass.getName().startsWith("java"))
{
continue;
}
addViewMetaData(annotation, result);
}
result = tryToReplaceWithMergedMetaDataFromAncestor(currentClass, viewConfigNode.getParent(), result);
return result;
}
//only supported for meta-data which isn't aggregated
protected List<Annotation> tryToReplaceWithMergedMetaDataFromAncestor(
Class currentClass, ViewConfigNode parentViewConfigNode, List<Annotation> foundResult)
{
ViewConfigNode ancestorNode = findNodeWithClass(currentClass, parentViewConfigNode);
if (ancestorNode == null)
{
return foundResult;
}
List<Annotation> result = new ArrayList<Annotation>(foundResult.size());
//only replace the meta-data found for the node and don't add all meta-data from the ancestor-node
for (Annotation annotation : foundResult)
{
Annotation finalMetaData = getFinalMetaDataFromNode(ancestorNode, annotation);
result.add(finalMetaData);
}
return result;
}
//the meta-data returned by this method is merged and potentially customized by a ConfigPreProcessor
private Annotation getFinalMetaDataFromNode(ViewConfigNode viewConfigNode, Annotation annotation)
{
Class<? extends Annotation> targetType = annotation.annotationType();
//skip @View and @Folder, because they get created dynamically to support their optional usage
//the dynamic generation depends on the level and if it is a synthetic information
if (View.class.equals(targetType) || Folder.class.equals(targetType))
{
return annotation;
}
//skip aggregated meta-data, because it can't be replaced
//(there is no info available about the instance which replaced the original one
// which might be equivalent to the annotation passed to this method)
ViewMetaData viewMetaData = annotation.annotationType().getAnnotation(ViewMetaData.class);
if (viewMetaData == null)
{
return annotation;
}
Aggregated aggregated = viewMetaData.annotationType().getAnnotation(Aggregated.class);
if (aggregated == null || aggregated.value())
{
return annotation;
}
for (Annotation nodeMetaData : viewConfigNode.getMetaData())
{
if (targetType.equals(nodeMetaData.annotationType()))
{
return nodeMetaData;
}
}
return annotation;
}
private ViewConfigNode findNodeWithClass(Class nodeClass, ViewConfigNode viewConfigNode)
{
if (viewConfigNode == null || nodeClass == null)
{
return null;
}
if (nodeClass.equals(viewConfigNode.getSource()))
{
return viewConfigNode;
}
return findNodeWithClass(nodeClass, viewConfigNode.getParent());
}
protected void addViewMetaData(Annotation currentAnnotation, List<Annotation> metaDataList)
{
Class<? extends Annotation> annotationClass = currentAnnotation.annotationType();
if (annotationClass.isAnnotationPresent(ViewMetaData.class))
{
metaDataList.add(currentAnnotation);
}
if (annotationClass.isAnnotationPresent(Stereotype.class))
{
for (Annotation inheritedViaStereotype : annotationClass.getAnnotations())
{
if (inheritedViaStereotype.annotationType().isAnnotationPresent(ViewMetaData.class))
{
metaDataList.add(inheritedViaStereotype);
}
}
}
}
protected void addInterfaces(Set<Class> processedTypes, Stack<Class> classesToAnalyze, Class nextClass)
{
for (Class<?> interfaceToAdd : nextClass.getInterfaces())
{
addInterfaces(processedTypes, classesToAnalyze, interfaceToAdd);
if (!processedTypes.contains(interfaceToAdd))
{
classesToAnalyze.push(interfaceToAdd);
}
}
}
}