MYFACESTEST-44 MockApplication20 should implement createComponent methods and scan for @ListenerFor and @ResourceDependency annotations
diff --git a/test20/src/main/java/org/apache/myfaces/test/mock/MockApplication20.java b/test20/src/main/java/org/apache/myfaces/test/mock/MockApplication20.java
index e3fe3f8..c2e1417 100644
--- a/test20/src/main/java/org/apache/myfaces/test/mock/MockApplication20.java
+++ b/test20/src/main/java/org/apache/myfaces/test/mock/MockApplication20.java
@@ -17,21 +17,11 @@
package org.apache.myfaces.test.mock;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.myfaces.test.mock.resource.MockResourceHandler;
-
-import javax.faces.FacesException;
-import javax.faces.application.ProjectStage;
-import javax.faces.application.ResourceHandler;
-import javax.faces.component.UIViewRoot;
-import javax.faces.component.behavior.Behavior;
-import javax.faces.context.FacesContext;
-import javax.faces.event.AbortProcessingException;
-import javax.faces.event.SystemEvent;
-import javax.faces.event.SystemEventListener;
-import javax.faces.event.SystemEventListenerHolder;
+import java.beans.BeanDescriptor;
+import java.beans.BeanInfo;
import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@@ -40,6 +30,38 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
+import javax.el.ELContext;
+import javax.el.ValueExpression;
+import javax.faces.FacesException;
+import javax.faces.application.Application;
+import javax.faces.application.ProjectStage;
+import javax.faces.application.Resource;
+import javax.faces.application.ResourceDependencies;
+import javax.faces.application.ResourceDependency;
+import javax.faces.application.ResourceHandler;
+import javax.faces.component.UIComponent;
+import javax.faces.component.UINamingContainer;
+import javax.faces.component.UIOutput;
+import javax.faces.component.UIViewRoot;
+import javax.faces.component.behavior.Behavior;
+import javax.faces.context.FacesContext;
+import javax.faces.convert.Converter;
+import javax.faces.el.ValueBinding;
+import javax.faces.event.AbortProcessingException;
+import javax.faces.event.ComponentSystemEventListener;
+import javax.faces.event.ListenerFor;
+import javax.faces.event.ListenersFor;
+import javax.faces.event.SystemEvent;
+import javax.faces.event.SystemEventListener;
+import javax.faces.event.SystemEventListenerHolder;
+import javax.faces.render.Renderer;
+import javax.faces.validator.Validator;
+import javax.faces.view.ViewDeclarationLanguage;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.myfaces.test.mock.resource.MockResourceHandler;
+
/**
* <p>Mock implementation of <code>Application</code> that includes the semantics
* added by JavaServer Faces 2.0.</p>
@@ -211,6 +233,242 @@
return Collections.unmodifiableMap(_defaultValidatorsIds);
}
+ private void _handleAnnotations(FacesContext context, Object inspected, UIComponent component)
+ {
+ // determine the ProjectStage setting via the given FacesContext
+ // note that a local getProjectStage() could cause problems in wrapped environments
+ boolean isProduction = context.isProjectStage(ProjectStage.Production);
+
+ Class<?> inspectedClass = inspected.getClass();
+ _handleListenerForAnnotations(context, inspected, inspectedClass, component, isProduction);
+
+ _handleResourceDependencyAnnotations(context, inspectedClass, component, isProduction);
+ }
+
+ private void _handleListenerForAnnotations(FacesContext context, Object inspected, Class<?> inspectedClass, UIComponent component, boolean isProduction)
+ {
+ List<ListenerFor> listenerForList = null;
+
+ if(listenerForList == null) //not in production or the class hasn't been inspected yet
+ {
+ ListenerFor listener = inspectedClass.getAnnotation(ListenerFor.class);
+ ListenersFor listeners = inspectedClass.getAnnotation(ListenersFor.class);
+ if(listener != null || listeners != null)
+ {
+ //listeners were found using one or both annotations, create and build a new list
+ listenerForList = new ArrayList<ListenerFor>();
+
+ if(listener != null)
+ listenerForList.add(listener);
+
+ if(listeners != null)
+ listenerForList.addAll(Arrays.asList(listeners.value()));
+ }
+ }
+
+ if (listenerForList != null) //listeners were found through inspection or from cache, handle them
+ {
+ for (ListenerFor listenerFor : listenerForList)
+ {
+ _handleListenerFor(context, inspected, component, listenerFor);
+ }
+ }
+ }
+
+ private void _handleListenerFor(FacesContext context, Object inspected, UIComponent component,
+ ListenerFor annotation)
+ {
+ // If this annotation is not present on the class in question, no action must be taken.
+ if (annotation != null)
+ {
+ // Determine the "target" on which to call subscribeToEvent.
+ // If the class to which this annotation is attached implements ComponentSystemEventListener
+ if (inspected instanceof ComponentSystemEventListener)
+ {
+ // If the class to which this annotation is attached is a UIComponent instance, "target" is the
+ // UIComponent instance.
+
+ // If the class to which this annotation is attached is a Renderer instance, "target" is the
+ // UIComponent instance.
+
+ /*
+ * If "target" is a UIComponent call UIComponent.subscribeToEvent(Class, ComponentSystemEventListener)
+ * passing the systemEventClass() of the annotation as the first argument and the instance of the class
+ * to which this annotation is attached (which must implement ComponentSystemEventListener) as the
+ * second argument.
+ */
+ component.subscribeToEvent(annotation.systemEventClass(), (ComponentSystemEventListener) inspected);
+ }
+ // If the class to which this annotation is attached implements SystemEventListener and does not implement
+ // ComponentSystemEventListener, "target" is the Application instance.
+ else if (component instanceof SystemEventListener)
+ {
+ // use the Application object from the FacesContext (note that a
+ // direct use of subscribeToEvent() could cause problems if the
+ // Application is wrapped)
+ Application application = context.getApplication();
+
+ // If "target" is the Application instance, inspect the value of the sourceClass() annotation attribute
+ // value.
+ if (Void.class.equals(annotation.sourceClass()))
+ {
+ /*
+ * If the value is Void.class, call Application.subscribeToEvent(Class, SystemEventListener),
+ * passing the value of systemEventClass() as the first argument and the instance of the class to
+ * which this annotation is attached (which must implement SystemEventListener) as the second
+ * argument.
+ */
+ application.subscribeToEvent(annotation.systemEventClass(), (SystemEventListener) inspected);
+ }
+ else
+ {
+ /*
+ * Otherwise, call Application.subscribeToEvent(Class, Class, SystemEventListener), passing the
+ * value of systemEventClass() as the first argument, the value of sourceClass() as the second
+ * argument, and the instance of the class to which this annotation is attached (which must
+ * implement SystemEventListener) as the third argument.
+ */
+ application.subscribeToEvent(annotation.systemEventClass(), annotation.sourceClass(),
+ (SystemEventListener) inspected);
+ }
+ }
+
+ /*
+ * If the class to which this annotation is attached implements ComponentSystemEventListener and is neither
+ * an instance of Renderer nor UIComponent, the action taken is unspecified. This case must not trigger any
+ * kind of error.
+ */
+ }
+ }
+
+ private void _handleResourceDependencyAnnotations(FacesContext context, Class<?> inspectedClass, UIComponent component, boolean isProduction)
+ {
+ List<ResourceDependency> dependencyList = null;
+
+ if(dependencyList == null) //not in production or the class hasn't been inspected yet
+ {
+ ResourceDependency dependency = inspectedClass.getAnnotation(ResourceDependency.class);
+ ResourceDependencies dependencies = inspectedClass.getAnnotation(ResourceDependencies.class);
+ if(dependency != null || dependencies != null)
+ {
+ //resource dependencies were found using one or both annotations, create and build a new list
+ dependencyList = new ArrayList<ResourceDependency>();
+
+ if(dependency != null)
+ dependencyList.add(dependency);
+
+ if(dependencies != null)
+ dependencyList.addAll(Arrays.asList(dependencies.value()));
+ }
+ }
+
+ if (dependencyList != null) //resource dependencies were found through inspection or from cache, handle them
+ {
+ for (ResourceDependency dependency : dependencyList)
+ {
+ _handleResourceDependency(context, component, dependency);
+ }
+ }
+ }
+
+ private void _handleResourceDependency(FacesContext context, UIComponent component, ResourceDependency annotation)
+ {
+ // If this annotation is not present on the class in question, no action must be taken.
+ if (annotation != null)
+ {
+ // Create a UIOutput instance by passing javax.faces.Output. to
+ // Application.createComponent(java.lang.String).
+ UIOutput output = (UIOutput) createComponent(UIOutput.COMPONENT_TYPE);
+
+ // Get the annotation instance from the class and obtain the values of the name, library, and
+ // target attributes.
+ String name = annotation.name();
+ if (name != null && name.length() > 0)
+ {
+ name = _ELText.parse(getExpressionFactory(), context.getELContext(), name).toString(context.getELContext());
+ }
+
+ // Obtain the renderer-type for the resource name by passing name to
+ // ResourceHandler.getRendererTypeForResourceName(java.lang.String).
+ // (note that we can not use this.getResourceHandler(), because the Application might be wrapped)
+ String rendererType = context.getApplication().getResourceHandler().getRendererTypeForResourceName(name);
+
+ // Call setRendererType on the UIOutput instance, passing the renderer-type.
+ output.setRendererType(rendererType);
+
+ // Obtain the Map of attributes from the UIOutput component by calling UIComponent.getAttributes().
+ Map<String, Object> attributes = output.getAttributes();
+
+ // Store the name into the attributes Map under the key "name".
+ attributes.put("name", name);
+
+ // If library is the empty string, let library be null.
+ String library = annotation.library();
+ if (library != null && library.length() > 0)
+ {
+ library = _ELText.parse(getExpressionFactory(), context.getELContext(), library).toString(context.getELContext());
+ // If library is non-null, store it under the key "library".
+ if ("this".equals(library))
+ {
+ // Special "this" behavior
+ Resource resource = (Resource)component.getAttributes().get(Resource.COMPONENT_RESOURCE_KEY);
+ if (resource != null)
+ {
+ attributes.put("library", resource.getLibraryName());
+ }
+ }
+ else
+ {
+ attributes.put("library", library);
+ }
+ }
+
+ // If target is the empty string, let target be null.
+ String target = annotation.target();
+ if (target != null && target.length() > 0)
+ {
+ target = _ELText.parse(getExpressionFactory(), context.getELContext(), target).toString(context.getELContext());
+ // If target is non-null, store it under the key "target".
+ attributes.put("target", target);
+ context.getViewRoot().addComponentResource(context, output, target);
+ }
+ else
+ {
+ // Otherwise, if target is null, call UIViewRoot.addComponentResource(javax.faces.context.FacesContext,
+ // javax.faces.component.UIComponent), passing the UIOutput instance as the second argument.
+ context.getViewRoot().addComponentResource(context, output);
+ }
+ }
+ }
+
+ private void _inspectRenderer(FacesContext context, UIComponent component, String componentType, String rendererType)
+ {
+ /*
+ * The Renderer instance to inspect must be obtained by calling FacesContext.getRenderKit() and calling
+ * RenderKit.getRenderer(java.lang.String, java.lang.String) on the result, passing the argument componentFamily
+ * of the newly created component as the first argument and the argument rendererType as the second argument.
+ */
+ Renderer renderer = context.getRenderKit().getRenderer(component.getFamily(), rendererType);
+ if (renderer == null)
+ {
+ // If no such Renderer can be found, a message must be logged with a helpful error message.
+ log.error("renderer cannot be found for component type " + componentType + " and renderer type "
+ + rendererType);
+ }
+ else
+ {
+ // Otherwise, UIComponent.setRendererType(java.lang.String) must be called on the newly created
+ // UIComponent instance, passing the argument rendererType as the argument.
+ component.setRendererType(rendererType);
+
+ /*
+ * except the Renderer for the component to be returned must be inspected for the annotations mentioned in
+ * createComponent(ValueExpression, FacesContext, String) as specified in the documentation for that method.
+ */
+ _handleAnnotations(context, renderer, component);
+ }
+ }
+
private static SystemEvent _traverseListenerList(
List<? extends SystemEventListener> listeners,
Class<? extends SystemEvent> systemEventClass, Object source,
@@ -417,6 +675,7 @@
try
{
final Behavior behavior = (Behavior) behaviorClass.newInstance();
+ _handleAttachedResourceDependencyAnnotations(FacesContext.getCurrentInstance(), behaviorClass);
return behavior;
}
catch (Exception e)
@@ -512,4 +771,408 @@
systemListenerEntry.removeListener(listener, sourceClass);
}
}
+
+ @Override
+ public UIComponent createComponent(String componentType)
+ {
+ UIComponent component = super.createComponent(componentType);
+ _handleAnnotations(FacesContext.getCurrentInstance(), component, component);
+ return component;
+ }
+
+
+
+ @Override
+ public UIComponent createComponent(ValueBinding componentBinding,
+ FacesContext context, String componentType) throws FacesException
+ {
+ UIComponent component = null;
+ try
+ {
+ component = (UIComponent) componentBinding.getValue(context);
+ if (component == null)
+ {
+ component = createComponent(componentType);
+ componentBinding.setValue(context, component);
+ }
+ else
+ {
+ _handleAnnotations(context, component, component);
+ }
+
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+ return component;
+ }
+
+ @Override
+ public UIComponent createComponent(ValueExpression componentExpression,
+ FacesContext facesContext, String componentType)
+ throws FacesException, NullPointerException
+ {
+
+ /*
+ * Before the component instance is returned, it must be inspected for the presence of a ListenerFor (or
+ * ListenersFor) or ResourceDependency (or ResourceDependencies) annotation. If any of these annotations are
+ * present, the action listed in ListenerFor or ResourceDependency must be taken on the component, before it is
+ * returned from this method. This variant of createComponent must not inspect the Renderer for the component to
+ * be returned for any of the afore mentioned annotations. Such inspection is the province of
+ */
+
+ checkNull(componentExpression, "componentExpression");
+ checkNull(facesContext, "facesContext");
+ checkNull(componentType, "componentType");
+
+ ELContext elContext = facesContext.getELContext();
+
+ try
+ {
+ Object retVal = componentExpression.getValue(elContext);
+
+ UIComponent createdComponent;
+
+ if (retVal instanceof UIComponent)
+ {
+ createdComponent = (UIComponent) retVal;
+ _handleAnnotations(facesContext, createdComponent, createdComponent);
+ }
+ else
+ {
+ createdComponent = createComponent(componentType);
+ componentExpression.setValue(elContext, createdComponent);
+ }
+
+ return createdComponent;
+ }
+ catch (FacesException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+ }
+
+ @Override
+ public UIComponent createComponent(ValueExpression componentExpression, FacesContext context, String componentType,
+ String rendererType)
+ {
+ // Like createComponent(ValueExpression, FacesContext, String)
+ UIComponent component = createComponent(componentExpression, context, componentType);
+
+ _inspectRenderer(context, component, componentType, rendererType);
+
+ return component;
+ }
+
+
+ @Override
+ public UIComponent createComponent(FacesContext context, String componentType, String rendererType)
+ {
+ checkNull(context, "context");
+ checkNull(componentType, "componentType");
+
+ // Like createComponent(String)
+ UIComponent component = createComponent(componentType);
+
+ _inspectRenderer(context, component, componentType, rendererType);
+
+ return component;
+ }
+
+ @Override
+ public UIComponent createComponent(FacesContext context, Resource componentResource)
+ {
+ checkNull(context, "context");
+ checkNull(componentResource, "componentResource");
+
+ UIComponent component = null;
+ Resource resource;
+ String fqcn;
+ Class<? extends UIComponent> componentClass = null;
+
+ /*
+ * Obtain a reference to the ViewDeclarationLanguage for this Application instance by calling
+ * ViewHandler.getViewDeclarationLanguage(javax.faces.context.FacesContext, java.lang.String), passing the
+ * viewId found by calling UIViewRoot.getViewId() on the UIViewRoot in the argument FacesContext.
+ */
+ UIViewRoot view = context.getViewRoot();
+ Application application = context.getApplication();
+ ViewDeclarationLanguage vdl = application.getViewHandler().getViewDeclarationLanguage(context, view.getViewId());
+
+ /*
+ * Obtain a reference to the composite component metadata for this composite component by calling
+ * ViewDeclarationLanguage.getComponentMetadata(javax.faces.context.FacesContext,
+ * javax.faces.application.Resource), passing the facesContext and componentResource arguments to this method.
+ * This version of JSF specification uses JavaBeans as the API to the component metadata.
+ */
+ BeanInfo metadata = vdl.getComponentMetadata(context, componentResource);
+ if (metadata == null)
+ {
+ throw new FacesException("Could not get component metadata for "
+ + componentResource.getResourceName()
+ + ". Did you forget to specify <composite:interface>?");
+ }
+
+ /*
+ * Determine if the component author declared a component-type for this component instance by obtaining the
+ * BeanDescriptor from the component metadata and calling its getValue() method, passing
+ * UIComponent.COMPOSITE_COMPONENT_TYPE_KEY as the argument. If non-null, the result must be a ValueExpression
+ * whose value is the component-type of the UIComponent to be created for this Resource component. Call through
+ * to createComponent(java.lang.String) to create the component.
+ */
+ BeanDescriptor descriptor = metadata.getBeanDescriptor();
+ ValueExpression componentType = (ValueExpression) descriptor.getValue(UIComponent.COMPOSITE_COMPONENT_TYPE_KEY);
+ boolean annotationsApplied = false;
+ if (componentType != null)
+ {
+ component = application.createComponent((String) componentType.getValue(context.getELContext()));
+ annotationsApplied = true;
+ }
+ else
+ {
+ /*
+ * Otherwise, determine if a script based component for this Resource can be found by calling
+ * ViewDeclarationLanguage.getScriptComponentResource(javax.faces.context.FacesContext,
+ * javax.faces.application.Resource). If the result is non-null, and is a script written in one of the
+ * languages listed in JSF 4.3 of the specification prose document, create a UIComponent instance from the
+ * script resource.
+ */
+ resource = vdl.getScriptComponentResource(context, componentResource);
+ if (resource != null)
+ {
+ String name = resource.getResourceName();
+ String className = name.substring(0, name.lastIndexOf('.'));
+
+ Class clazz;
+ try
+ {
+ clazz = Class.forName(className);
+ component = (UIComponent)clazz.newInstance();
+ }
+ catch (Exception e)
+ {
+ throw new FacesException(e);
+ }
+ }
+ else
+ {
+ /*
+ * Otherwise, let library-name be the return from calling Resource.getLibraryName() on the argument
+ * componentResource and resource-name be the return from calling Resource.getResourceName() on the
+ * argument componentResource. Create a fully qualified Java class name by removing any file extension
+ * from resource-name and let fqcn be library-name + "." + resource-name. If a class with the name of
+ * fqcn cannot be found, take no action and continue to the next step. If any of InstantiationException,
+ * IllegalAccessException, or ClassCastException are thrown, wrap the exception in a FacesException and
+ * re-throw it. If any other exception is thrown, log the exception and continue to the next step.
+ */
+
+ String name = componentResource.getResourceName();
+ String className = name.substring(0, name.lastIndexOf('.'));
+ fqcn = componentResource.getLibraryName() + "." + className;
+
+ try
+ {
+ componentClass = classForName(fqcn);
+ }
+ catch (ClassNotFoundException e)
+ {
+ }
+
+ if (componentClass != null)
+ {
+ try
+ {
+ component = componentClass.newInstance();
+ }
+ catch (InstantiationException e)
+ {
+ log.error("Could not instantiate component class name = " + fqcn, e);
+ throw new FacesException("Could not instantiate component class name = " + fqcn, e);
+ }
+ catch (IllegalAccessException e)
+ {
+ log.error("Could not instantiate component class name = " + fqcn, e);
+ throw new FacesException("Could not instantiate component class name = " + fqcn, e);
+ }
+ catch (Exception e)
+ {
+ log.error("Could not instantiate component class name = " + fqcn, e);
+ }
+ }
+
+ /*
+ * If none of the previous steps have yielded a UIComponent instance, call
+ * createComponent(java.lang.String) passing "javax.faces.NamingContainer" as the argument.
+ */
+ if (component == null)
+ {
+ component = application.createComponent(UINamingContainer.COMPONENT_TYPE);
+ annotationsApplied = true;
+ }
+ }
+ }
+
+ /*
+ * Call UIComponent.setRendererType(java.lang.String) on the UIComponent instance, passing
+ * "javax.faces.Composite" as the argument.
+ */
+ component.setRendererType("javax.faces.Composite");
+
+ /*
+ * Store the argument Resource in the attributes Map of the UIComponent under the key,
+ * Resource.COMPONENT_RESOURCE_KEY.
+ */
+ component.getAttributes().put(Resource.COMPONENT_RESOURCE_KEY, componentResource);
+
+ /*
+ * Store composite component metadata in the attributes Map of the UIComponent under the key,
+ * UIComponent.BEANINFO_KEY.
+ */
+ component.getAttributes().put(UIComponent.BEANINFO_KEY, metadata);
+
+ /*
+ * Before the component instance is returned, it must be inspected for the presence of a ListenerFor annotation.
+ * If this annotation is present, the action listed in ListenerFor must be taken on the component, before it is
+ * returned from this method.
+ */
+ if (!annotationsApplied)
+ {
+ _handleAnnotations(context, component, component);
+ }
+
+ return component;
+ }
+
+ private static Class classForName(String type)
+ throws ClassNotFoundException
+ {
+ if (type == null) throw new NullPointerException("type");
+ try
+ {
+ // Try WebApp ClassLoader first
+ return Class.forName(type,
+ false, // do not initialize for faster startup
+ Thread.currentThread().getContextClassLoader());
+ }
+ catch (ClassNotFoundException ignore)
+ {
+ // fallback: Try ClassLoader for ClassUtils (i.e. the myfaces.jar lib)
+ return Class.forName(type,
+ false, // do not initialize for faster startup
+ MockApplication20.class.getClassLoader());
+ }
+ }
+
+ private void _handleAttachedResourceDependencyAnnotations(FacesContext context, Object inspected)
+ {
+ if (inspected == null) {
+ return;
+ }
+
+ ResourceDependency annotation = inspected.getClass().getAnnotation(ResourceDependency.class);
+
+ if (annotation == null)
+ {
+ // If the ResourceDependency annotation is not present, the argument must be inspected for the presence
+ // of the ResourceDependencies annotation.
+ ResourceDependencies dependencies = inspected.getClass().getAnnotation(ResourceDependencies.class);
+ if (dependencies != null)
+ {
+ // If the ResourceDependencies annotation is present, the action described in ResourceDependencies
+ // must be taken.
+ for (ResourceDependency dependency : dependencies.value())
+ {
+ _handleAttachedResourceDependency(context, dependency);
+ }
+ }
+ }
+ else
+ {
+ // If the ResourceDependency annotation is present, the action described in ResourceDependency must be
+ // taken.
+ _handleAttachedResourceDependency(context, annotation);
+ }
+ }
+
+ private void _handleAttachedResourceDependency(FacesContext context, ResourceDependency annotation)
+ {
+ // If this annotation is not present on the class in question, no action must be taken.
+ if (annotation != null)
+ {
+ Application application = context.getApplication();
+
+ // Create a UIOutput instance by passing javax.faces.Output. to
+ // Application.createComponent(java.lang.String).
+ UIOutput output = (UIOutput) application.createComponent(UIOutput.COMPONENT_TYPE);
+
+ // Get the annotation instance from the class and obtain the values of the name, library, and
+ // target attributes.
+ String name = annotation.name();
+ if (name != null && name.length() > 0)
+ {
+ name = _ELText.parse(getExpressionFactory(), context.getELContext(), name).toString(context.getELContext());
+ }
+
+ // Obtain the renderer-type for the resource name by passing name to
+ // ResourceHandler.getRendererTypeForResourceName(java.lang.String).
+ String rendererType = application.getResourceHandler().getRendererTypeForResourceName(name);
+
+ // Call setRendererType on the UIOutput instance, passing the renderer-type.
+ output.setRendererType(rendererType);
+
+ // Obtain the Map of attributes from the UIOutput component by calling UIComponent.getAttributes().
+ Map<String, Object> attributes = output.getAttributes();
+
+ // Store the name into the attributes Map under the key "name".
+ attributes.put("name", name);
+
+ // If library is the empty string, let library be null.
+ String library = annotation.library();
+ if (library != null && library.length() > 0)
+ {
+ library = _ELText.parse(getExpressionFactory(), context.getELContext(), library).toString(context.getELContext());
+ // If library is non-null, store it under the key "library".
+ attributes.put("library", library);
+ }
+
+ // If target is the empty string, let target be null.
+ String target = annotation.target();
+ if (target != null && target.length() > 0)
+ {
+ target = _ELText.parse(getExpressionFactory(), context.getELContext(), target).toString(context.getELContext());
+ // If target is non-null, store it under the key "target".
+ attributes.put("target", target);
+ context.getViewRoot().addComponentResource(context, output, target);
+ }
+ else
+ {
+ // Otherwise, if target is null, call UIViewRoot.addComponentResource(javax.faces.context.FacesContext,
+ // javax.faces.component.UIComponent), passing the UIOutput instance as the second argument.
+ context.getViewRoot().addComponentResource(context, output);
+ }
+ }
+ }
+
+ @Override
+ public Converter createConverter(String converterId)
+ {
+ Converter converter = super.createConverter(converterId);
+ _handleAttachedResourceDependencyAnnotations(FacesContext.getCurrentInstance(), converter);
+ return converter;
+ }
+
+ @Override
+ public Validator createValidator(String validatorId)
+ {
+ Validator validator = super.createValidator(validatorId);
+ _handleAttachedResourceDependencyAnnotations(FacesContext.getCurrentInstance(), validator);
+ return validator;
+ }
+
+
}
diff --git a/test20/src/main/java/org/apache/myfaces/test/mock/_ELText.java b/test20/src/main/java/org/apache/myfaces/test/mock/_ELText.java
new file mode 100644
index 0000000..6371c13
--- /dev/null
+++ b/test20/src/main/java/org/apache/myfaces/test/mock/_ELText.java
@@ -0,0 +1,444 @@
+/*
+ * 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.myfaces.test.mock;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.ExpressionFactory;
+import javax.el.ValueExpression;
+import javax.faces.context.ResponseWriter;
+
+/**
+ * Handles parsing EL Strings in accordance with the EL-API Specification. The parser accepts either <code>${..}</code>
+ * or <code>#{..}</code>.
+ *
+ * @author Jacob Hookom
+ * @version $Id: ELText.java,v 1.8 2008/07/13 19:01:42 rlubke Exp $
+ */
+class _ELText
+{
+
+ private static final class LiteralValueExpression extends ValueExpression
+ {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ private final String text;
+
+ public LiteralValueExpression(String text)
+ {
+ this.text = text;
+ }
+
+ public boolean isLiteralText()
+ {
+ return false;
+ }
+
+ public int hashCode()
+ {
+ return 0;
+ }
+
+ public String getExpressionString()
+ {
+ return this.text;
+ }
+
+ public boolean equals(Object obj)
+ {
+ return false;
+ }
+
+ public void setValue(ELContext context, Object value)
+ {
+ }
+
+ public boolean isReadOnly(ELContext context)
+ {
+ return false;
+ }
+
+ public Object getValue(ELContext context)
+ {
+ return null;
+ }
+
+ public Class<?> getType(ELContext context)
+ {
+ return null;
+ }
+
+ public Class<?> getExpectedType()
+ {
+ return null;
+ }
+
+ }
+
+ private static final class ELTextComposite extends _ELText
+ {
+ private final _ELText[] txt;
+
+ public ELTextComposite(_ELText[] txt)
+ {
+ super(null);
+ this.txt = txt;
+ }
+
+ public void write(Writer out, ELContext ctx) throws ELException, IOException
+ {
+ for (int i = 0; i < this.txt.length; i++)
+ {
+ this.txt[i].write(out, ctx);
+ }
+ }
+
+ public void writeText(ResponseWriter out, ELContext ctx) throws ELException, IOException
+ {
+ for (int i = 0; i < this.txt.length; i++)
+ {
+ this.txt[i].writeText(out, ctx);
+ }
+ }
+
+ public String toString(ELContext ctx)
+ {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < this.txt.length; i++)
+ {
+ sb.append(this.txt[i].toString(ctx));
+ }
+ return sb.toString();
+ }
+
+ /*
+ * public String toString(ELContext ctx) { StringBuffer sb = new StringBuffer(); for (int i = 0; i <
+ * this.txt.length; i++) { sb.append(this.txt[i].toString(ctx)); } return sb.toString(); }
+ */
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < this.txt.length; i++)
+ {
+ sb.append(this.txt[i].toString());
+ }
+ return sb.toString();
+ }
+
+ public boolean isLiteral()
+ {
+ return false;
+ }
+
+ public _ELText apply(ExpressionFactory factory, ELContext ctx)
+ {
+ int len = this.txt.length;
+ _ELText[] nt = new _ELText[len];
+ for (int i = 0; i < len; i++)
+ {
+ nt[i] = this.txt[i].apply(factory, ctx);
+ }
+ return new ELTextComposite(nt);
+ }
+ }
+
+ private static final class ELTextVariable extends _ELText
+ {
+ private final ValueExpression ve;
+
+ public ELTextVariable(ValueExpression ve)
+ {
+ super(ve.getExpressionString());
+ this.ve = ve;
+ }
+
+ public boolean isLiteral()
+ {
+ return false;
+ }
+
+ public _ELText apply(ExpressionFactory factory, ELContext ctx)
+ {
+ return new ELTextVariable(factory.createValueExpression(ctx, this.ve.getExpressionString(), String.class));
+ }
+
+ public void write(Writer out, ELContext ctx) throws ELException, IOException
+ {
+ Object v = this.ve.getValue(ctx);
+ if (v != null)
+ {
+ out.write((String) v);
+ }
+ }
+
+ public String toString(ELContext ctx) throws ELException
+ {
+ Object v = this.ve.getValue(ctx);
+ if (v != null)
+ {
+ return v.toString();
+ }
+
+ return null;
+ }
+
+ public void writeText(ResponseWriter out, ELContext ctx) throws ELException, IOException
+ {
+ Object v = this.ve.getValue(ctx);
+ if (v != null)
+ {
+ out.writeText((String) v, null);
+ }
+ }
+ }
+
+ protected final String literal;
+
+ public _ELText(String literal)
+ {
+ this.literal = literal;
+ }
+
+ /**
+ * If it's literal text
+ *
+ * @return true if the String is literal (doesn't contain <code>#{..}</code> or <code>${..}</code>)
+ */
+ public boolean isLiteral()
+ {
+ return true;
+ }
+
+ /**
+ * Return an instance of <code>this</code> that is applicable given the ELContext and ExpressionFactory state.
+ *
+ * @param factory
+ * the ExpressionFactory to use
+ * @param ctx
+ * the ELContext to use
+ * @return an ELText instance
+ */
+ public _ELText apply(ExpressionFactory factory, ELContext ctx)
+ {
+ return this;
+ }
+
+ /**
+ * Allow this instance to write to the passed Writer, given the ELContext state
+ *
+ * @param out
+ * Writer to write to
+ * @param ctx
+ * current ELContext state
+ * @throws ELException
+ * @throws IOException
+ */
+ public void write(Writer out, ELContext ctx) throws ELException, IOException
+ {
+ out.write(this.literal);
+ }
+
+ public void writeText(ResponseWriter out, ELContext ctx) throws ELException, IOException
+ {
+ out.writeText(this.literal, null);
+ }
+
+ /**
+ * Evaluates the ELText to a String
+ *
+ * @param ctx
+ * current ELContext state
+ * @throws ELException
+ * @return the evaluated String
+ */
+ public String toString(ELContext ctx) throws ELException
+ {
+ return this.literal;
+ }
+
+ public String toString()
+ {
+ return this.literal;
+ }
+
+ /**
+ * Parses the passed string to determine if it's literal or not
+ *
+ * @param in
+ * input String
+ * @return true if the String is literal (doesn't contain <code>#{..}</code> or <code>${..}</code>)
+ */
+ public static boolean isLiteral(String in)
+ {
+ _ELText txt = parse(in);
+ return txt == null || txt.isLiteral();
+ }
+
+ /**
+ * Factory method for creating an unvalidated ELText instance. NOTE: All expressions in the passed String are
+ * treated as {@link org.apache.myfaces.view.facelets.el.LiteralValueExpression LiteralValueExpressions}.
+ *
+ * @param in
+ * String to parse
+ * @return ELText instance that knows if the String was literal or not
+ * @throws javax.el.ELException
+ */
+ public static _ELText parse(String in) throws ELException
+ {
+ return parse(null, null, in);
+ }
+
+ /**
+ * Factory method for creating a validated ELText instance. When an Expression is hit, it will use the
+ * ExpressionFactory to create a ValueExpression instance, resolving any functions at that time. <p/> Variables and
+ * properties will not be evaluated.
+ *
+ * @param fact
+ * ExpressionFactory to use
+ * @param ctx
+ * ELContext to validate against
+ * @param in
+ * String to parse
+ * @return ELText that can be re-applied later
+ * @throws javax.el.ELException
+ */
+ public static _ELText parse(ExpressionFactory fact, ELContext ctx, String in) throws ELException
+ {
+ char[] ca = in.toCharArray();
+ int i = 0;
+ char c = 0;
+ int len = ca.length;
+ int end = len - 1;
+ boolean esc = false;
+ int vlen = 0;
+
+ StringBuffer buff = new StringBuffer(128);
+ List<_ELText> text = new ArrayList<_ELText>();
+ _ELText t = null;
+ ValueExpression ve = null;
+
+ while (i < len)
+ {
+ c = ca[i];
+ if ('\\' == c)
+ {
+ esc = !esc;
+ if (esc && i < end && (ca[i + 1] == '$' || ca[i + 1] == '#'))
+ {
+ i++;
+ continue;
+ }
+ }
+ else if (!esc && ('$' == c || '#' == c))
+ {
+ if (i < end)
+ {
+ if ('{' == ca[i + 1])
+ {
+ if (buff.length() > 0)
+ {
+ text.add(new _ELText(buff.toString()));
+ buff.setLength(0);
+ }
+ vlen = findVarLength(ca, i);
+ if (ctx != null && fact != null)
+ {
+ ve = fact.createValueExpression(ctx, new String(ca, i, vlen), String.class);
+ t = new ELTextVariable(ve);
+ }
+ else
+ {
+ t = new ELTextVariable(new LiteralValueExpression(new String(ca, i, vlen)));
+ }
+ text.add(t);
+ i += vlen;
+ continue;
+ }
+ }
+ }
+ esc = false;
+ buff.append(c);
+ i++;
+ }
+
+ if (buff.length() > 0)
+ {
+ text.add(new _ELText(new String(buff.toString())));
+ buff.setLength(0);
+ }
+
+ if (text.size() == 0)
+ {
+ return null;
+ }
+ else if (text.size() == 1)
+ {
+ return (_ELText) text.get(0);
+ }
+ else
+ {
+ _ELText[] ta = (_ELText[]) text.toArray(new _ELText[text.size()]);
+ return new ELTextComposite(ta);
+ }
+ }
+
+ private static int findVarLength(char[] ca, int s) throws ELException
+ {
+ int i = s;
+ int len = ca.length;
+ char c = 0;
+ int str = 0;
+ while (i < len)
+ {
+ c = ca[i];
+ if ('\\' == c && i < len - 1)
+ {
+ i++;
+ }
+ else if ('\'' == c || '"' == c)
+ {
+ if (str == c)
+ {
+ str = 0;
+ }
+ else
+ {
+ str = c;
+ }
+ }
+ else if (str == 0 && ('}' == c))
+ {
+ return i - s + 1;
+ }
+ i++;
+ }
+ throw new ELException("EL Expression Unbalanced: ... " + new String(ca, s, i - s));
+ }
+
+}