/* $$ Clover has instrumented this file $$ */// Copyright 2004 The Apache Software Foundation
//
// 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.
package org.apache.tapestry.enhance;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hivemind.ApplicationRuntimeException;
import org.apache.hivemind.ClassResolver;
import org.apache.hivemind.Location;
import org.apache.hivemind.service.ClassFactory;
import org.apache.tapestry.IBinding;
import org.apache.tapestry.Tapestry;
import org.apache.tapestry.spec.Direction;
import org.apache.tapestry.spec.IComponentSpecification;
import org.apache.tapestry.spec.IParameterSpecification;
import org.apache.tapestry.spec.IPropertySpecification;
/**
* Contains the logic for analyzing and enhancing a single component class. Internally, this class
* makes use of {@link IEnhancedClassFactory}.
*
* @author Howard Lewis Ship
* @since 3.0
*/
public class ComponentClassFactory
{public static com.cortexeb.tools.clover.d __CLOVER_106_0 = com.cortexeb.tools.clover.aq.getRecorder(new char[] {67,58,92,119,111,114,107,115,112,97,99,101,92,106,97,107,97,114,116,97,45,116,97,112,101,115,116,114,121,92,102,114,97,109,101,119,111,114,107,92,116,97,114,103,101,116,92,99,108,111,118,101,114,45,100,98},1096998272901L);
private static final Log LOG = LogFactory.getLog(ComponentClassFactory.class);
/**
* Package prefix used for classes that come out of the java. or javax. packages.
*/
private static final String ENHANCED_PACKAGE = "org.apache.tapestry.enhanced.";
/**
* UID used to generate new class names.
*/
private static int _uid = 0;
private EnhancementWorklist _worklist;
private Map _beanProperties = new HashMap();
private ClassResolver _resolver;
private IComponentSpecification _specification;
private Class _componentClass;
private ClassFactory _classFactory;
private JavaClassMapping _classMapping = new JavaClassMapping();
public ComponentClassFactory(ClassResolver resolver, IComponentSpecification specification,
Class componentClass, ClassFactory classFactory)
{try { __CLOVER_106_0.M[588]++;
__CLOVER_106_0.S[2527]++;_resolver = resolver;
__CLOVER_106_0.S[2528]++;_specification = specification;
__CLOVER_106_0.S[2529]++;_componentClass = componentClass;
__CLOVER_106_0.S[2530]++;_classFactory = classFactory;
__CLOVER_106_0.S[2531]++;buildBeanProperties();
} finally { }}
/**
* We synchronize this method to, hopefully, prevent multiple threads from introspecting any
* singe class at one time. The Introspector is not thread safe. Unforunately, PropertyUtils,
* OGNL, here (and elsewhere?) may attempt to introspect at the same time ... we're safe as long
* as its not the same class being introspected.
*/
private synchronized void buildBeanProperties()
{try { __CLOVER_106_0.M[589]++;
__CLOVER_106_0.S[2532]++;BeanInfo info = null;
__CLOVER_106_0.S[2533]++;try
{
__CLOVER_106_0.S[2534]++;info = Introspector.getBeanInfo(_componentClass);
}
catch (IntrospectionException ex)
{
__CLOVER_106_0.S[2535]++;throw new ApplicationRuntimeException(EnhanceMessages.unabelToIntrospectClass(
_componentClass,
ex), ex);
}
__CLOVER_106_0.S[2536]++;PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
__CLOVER_106_0.S[2537]++;for (int i = 0; (((i < descriptors.length) && (++__CLOVER_106_0.CT[468] != 0)) || (++__CLOVER_106_0.CF[468] == 0)); i++){
{
__CLOVER_106_0.S[2538]++;_beanProperties.put(descriptors[i].getName(), descriptors[i]);
}}
} finally { }}
protected PropertyDescriptor getPropertyDescriptor(String name)
{try { __CLOVER_106_0.M[590]++;
__CLOVER_106_0.S[2539]++;return (PropertyDescriptor) _beanProperties.get(name);
} finally { }}
/**
* Invokes {@link #scanForEnhancements()}to identify any enhancements needed on the class,
* returning true if there are any enhancements to be performed.
*/
public boolean needsEnhancement()
{try { __CLOVER_106_0.M[591]++;
__CLOVER_106_0.S[2540]++;scanForEnhancements();
__CLOVER_106_0.S[2541]++;return _worklist != null && _worklist.hasModifications();
} finally { }}
/**
* @return true if pd is not null and both read/write methods are implemented
*/
public boolean isImplemented(PropertyDescriptor pd)
{try { __CLOVER_106_0.M[592]++;
__CLOVER_106_0.S[2542]++;if ((((pd == null) && (++__CLOVER_106_0.CT[469] != 0)) || (++__CLOVER_106_0.CF[469] == 0))){
__CLOVER_106_0.S[2543]++;return false;}
__CLOVER_106_0.S[2544]++;return isImplemented(pd.getReadMethod()) && isImplemented(pd.getWriteMethod());
} finally { }}
/**
* @return true if m is not null and is abstract.
*/
public boolean isAbstract(Method m)
{try { __CLOVER_106_0.M[593]++;
__CLOVER_106_0.S[2545]++;if ((((m == null) && (++__CLOVER_106_0.CT[470] != 0)) || (++__CLOVER_106_0.CF[470] == 0))){
__CLOVER_106_0.S[2546]++;return false;}
__CLOVER_106_0.S[2547]++;return Modifier.isAbstract(m.getModifiers());
} finally { }}
/**
* @return true if m is not null and not abstract
*/
public boolean isImplemented(Method m)
{try { __CLOVER_106_0.M[594]++;
__CLOVER_106_0.S[2548]++;if ((((m == null) && (++__CLOVER_106_0.CT[471] != 0)) || (++__CLOVER_106_0.CF[471] == 0))){
__CLOVER_106_0.S[2549]++;return false;}
__CLOVER_106_0.S[2550]++;return !Modifier.isAbstract(m.getModifiers());
} finally { }}
/**
* Given a class name, returns the corresponding class. In addition, scalar types, arrays of
* scalar types, java.lang.Object[] and java.lang.String[] are supported.
*
* @param type
* to convert to a Class
* @param location
* of the involved specification element (for exception reporting)
*/
public Class convertPropertyType(String type, Location location)
{try { __CLOVER_106_0.M[595]++;
__CLOVER_106_0.S[2551]++;Class result = _classMapping.getType(type);
__CLOVER_106_0.S[2552]++;if ((((result == null) && (++__CLOVER_106_0.CT[472] != 0)) || (++__CLOVER_106_0.CF[472] == 0))){
{
__CLOVER_106_0.S[2553]++;try
{
__CLOVER_106_0.S[2554]++;String typeName = JavaTypeUtils.getJVMClassName(type);
__CLOVER_106_0.S[2555]++;result = _resolver.findClass(typeName);
}
catch (Exception ex)
{
__CLOVER_106_0.S[2556]++;throw new ApplicationRuntimeException(EnhanceMessages.badPropertyType(type, ex),
location, ex);
}
__CLOVER_106_0.S[2557]++;_classMapping.recordType(type, result);
}}
__CLOVER_106_0.S[2558]++;return result;
} finally { }}
private void checkPropertyType(PropertyDescriptor pd, Class propertyType, Location location)
{try { __CLOVER_106_0.M[596]++;
__CLOVER_106_0.S[2559]++;if ((((!pd.getPropertyType().equals(propertyType)) && (++__CLOVER_106_0.CT[473] != 0)) || (++__CLOVER_106_0.CF[473] == 0))){
__CLOVER_106_0.S[2560]++;throw new ApplicationRuntimeException(EnhanceMessages.propertyTypeMismatch(
_componentClass,
pd.getName(),
pd.getPropertyType(),
propertyType), location, null);}
} finally { }}
/**
* Checks to see that that class either doesn't provide the property, or does but the
* accessor(s) are abstract. Returns the name of the read accessor if found, or fabricates an
* appropriate name (if not). Returns null only if the property does not exist.
*/
private String findReadMethodName(String propertyName, Class propertyType, Location location)
{try { __CLOVER_106_0.M[597]++;
__CLOVER_106_0.S[2561]++;PropertyDescriptor d = getPropertyDescriptor(propertyName);
__CLOVER_106_0.S[2562]++;if ((((d == null) && (++__CLOVER_106_0.CT[474] != 0)) || (++__CLOVER_106_0.CF[474] == 0))){
__CLOVER_106_0.S[2563]++;return CreateAccessorUtils.buildMethodName("get", propertyName);}
__CLOVER_106_0.S[2564]++;checkPropertyType(d, propertyType, location);
__CLOVER_106_0.S[2565]++;Method write = d.getWriteMethod();
__CLOVER_106_0.S[2566]++;Method read = d.getReadMethod();
__CLOVER_106_0.S[2567]++;if ((((isImplemented(write)) && (++__CLOVER_106_0.CT[475] != 0)) || (++__CLOVER_106_0.CF[475] == 0))){
__CLOVER_106_0.S[2568]++;throw new ApplicationRuntimeException(EnhanceMessages.nonAbstractWrite(write
.getDeclaringClass(), propertyName), location, null);}
__CLOVER_106_0.S[2569]++;if ((((isImplemented(read)) && (++__CLOVER_106_0.CT[476] != 0)) || (++__CLOVER_106_0.CF[476] == 0))){
__CLOVER_106_0.S[2570]++;throw new ApplicationRuntimeException(EnhanceMessages.nonAbstractRead(read
.getDeclaringClass(), propertyName), location, null);}
__CLOVER_106_0.S[2571]++;if ((((read != null) && (++__CLOVER_106_0.CT[477] != 0)) || (++__CLOVER_106_0.CF[477] == 0))){
__CLOVER_106_0.S[2572]++;return read.getName();}
__CLOVER_106_0.S[2573]++;return CreateAccessorUtils.buildMethodName("get", propertyName);
} finally { }}
private boolean isMissingProperty(String propertyName)
{try { __CLOVER_106_0.M[598]++;
__CLOVER_106_0.S[2574]++;PropertyDescriptor pd = getPropertyDescriptor(propertyName);
__CLOVER_106_0.S[2575]++;return !isImplemented(pd);
} finally { }}
/**
* Invoked by {@link org.apache.tapestry.enhance.DefaultComponentClassEnhancer}to create an
* enahanced subclass of the component class. This means creating a default constructor, new
* fields, and new accessor and mutator methods. Properties are created for connected
* parameters, for all formal parameters (the binding property), and for all specified
* parameters (which may be transient or persistent).
*/
public Class createEnhancedSubclass()
{try { __CLOVER_106_0.M[599]++;
__CLOVER_106_0.S[2576]++;EnhancementWorklist worklist = getWorklist();
__CLOVER_106_0.S[2577]++;String startClassName = _componentClass.getName();
__CLOVER_106_0.S[2578]++;String subclassName = worklist.getClassName();
__CLOVER_106_0.S[2579]++;if ((((LOG.isDebugEnabled()) && (++__CLOVER_106_0.CT[478] != 0)) || (++__CLOVER_106_0.CF[478] == 0))){
__CLOVER_106_0.S[2580]++;LOG.debug("Enhancing subclass of " + startClassName + " for "
+ _specification.getSpecificationLocation());}
__CLOVER_106_0.S[2581]++;Class result = worklist.createEnhancedSubclass();
__CLOVER_106_0.S[2582]++;if ((((LOG.isDebugEnabled()) && (++__CLOVER_106_0.CT[479] != 0)) || (++__CLOVER_106_0.CF[479] == 0))){
__CLOVER_106_0.S[2583]++;LOG.debug("Finished creating enhanced class " + subclassName);}
__CLOVER_106_0.S[2584]++;return result;
} finally { }}
/**
* Invoked by {@link #needsEnhancement()}to find any enhancements that may be needed. Should
* create an {@link org.apache.tapestry.enhance.IEnhancer}for each one, and add it to the
* queue.
*/
private void scanForEnhancements()
{try { __CLOVER_106_0.M[600]++;
__CLOVER_106_0.S[2585]++;scanForParameterEnhancements();
__CLOVER_106_0.S[2586]++;scanForSpecifiedPropertyEnhancements();
} finally { }}
/**
* Invoked by {@link #scanForEnhancements()}to locate any enhancements needed for component
* parameters (this includes binding properties and connected parameter property).
*/
private void scanForParameterEnhancements()
{try { __CLOVER_106_0.M[601]++;
__CLOVER_106_0.S[2587]++;List names = _specification.getParameterNames();
__CLOVER_106_0.S[2588]++;int count = names.size();
__CLOVER_106_0.S[2589]++;for (int i = 0; (((i < count) && (++__CLOVER_106_0.CT[480] != 0)) || (++__CLOVER_106_0.CF[480] == 0)); i++){
{
__CLOVER_106_0.S[2590]++;String name = (String) names.get(i);
__CLOVER_106_0.S[2591]++;IParameterSpecification ps = _specification.getParameter(name);
__CLOVER_106_0.S[2592]++;scanForBindingProperty(name, ps);
__CLOVER_106_0.S[2593]++;scanForParameterProperty(name, ps);
}}
} finally { }}
private void scanForSpecifiedPropertyEnhancements()
{try { __CLOVER_106_0.M[602]++;
__CLOVER_106_0.S[2594]++;List names = _specification.getPropertySpecificationNames();
__CLOVER_106_0.S[2595]++;int count = names.size();
__CLOVER_106_0.S[2596]++;for (int i = 0; (((i < count) && (++__CLOVER_106_0.CT[481] != 0)) || (++__CLOVER_106_0.CF[481] == 0)); i++){
{
__CLOVER_106_0.S[2597]++;String name = (String) names.get(i);
__CLOVER_106_0.S[2598]++;IPropertySpecification ps = _specification.getPropertySpecification(name);
__CLOVER_106_0.S[2599]++;scanForSpecifiedProperty(ps);
}}
} finally { }}
private void scanForBindingProperty(String parameterName, IParameterSpecification ps)
{try { __CLOVER_106_0.M[603]++;
__CLOVER_106_0.S[2600]++;String propertyName = parameterName + Tapestry.PARAMETER_PROPERTY_NAME_SUFFIX;
__CLOVER_106_0.S[2601]++;PropertyDescriptor pd = getPropertyDescriptor(propertyName);
// only enhance custom parameter binding properties if they are declared abstract
__CLOVER_106_0.S[2602]++;if ((((ps.getDirection() == Direction.CUSTOM) && (++__CLOVER_106_0.CT[482] != 0)) || (++__CLOVER_106_0.CF[482] == 0))){
{
__CLOVER_106_0.S[2603]++;if ((((pd == null) && (++__CLOVER_106_0.CT[483] != 0)) || (++__CLOVER_106_0.CF[483] == 0))){
__CLOVER_106_0.S[2604]++;return;}
__CLOVER_106_0.S[2605]++;if ((((!(isAbstract(pd.getReadMethod()) || isAbstract(pd.getWriteMethod()))) && (++__CLOVER_106_0.CT[484] != 0)) || (++__CLOVER_106_0.CF[484] == 0))){
__CLOVER_106_0.S[2606]++;return;}
}}
__CLOVER_106_0.S[2607]++;if ((((isImplemented(pd)) && (++__CLOVER_106_0.CT[485] != 0)) || (++__CLOVER_106_0.CF[485] == 0))){
__CLOVER_106_0.S[2608]++;return;}
__CLOVER_106_0.S[2609]++;IEnhancer enhancer = new CreatePropertyEnhancer(propertyName, IBinding.class);
__CLOVER_106_0.S[2610]++;addEnhancer(enhancer);
} finally { }}
private void scanForParameterProperty(String parameterName, IParameterSpecification ps)
{try { __CLOVER_106_0.M[604]++;
__CLOVER_106_0.S[2611]++;Direction direction = ps.getDirection();
__CLOVER_106_0.S[2612]++;if ((((direction == Direction.CUSTOM) && (++__CLOVER_106_0.CT[486] != 0)) || (++__CLOVER_106_0.CF[486] == 0))){
__CLOVER_106_0.S[2613]++;return;}
__CLOVER_106_0.S[2614]++;if ((((direction == Direction.AUTO) && (++__CLOVER_106_0.CT[487] != 0)) || (++__CLOVER_106_0.CF[487] == 0))){
{
__CLOVER_106_0.S[2615]++;addAutoParameterEnhancer(parameterName, ps);
__CLOVER_106_0.S[2616]++;return;
}}
__CLOVER_106_0.S[2617]++;String propertyName = ps.getPropertyName();
// Yes, but does it *need* a property created?
__CLOVER_106_0.S[2618]++;if ((((!isMissingProperty(propertyName)) && (++__CLOVER_106_0.CT[488] != 0)) || (++__CLOVER_106_0.CF[488] == 0))){
__CLOVER_106_0.S[2619]++;return;}
__CLOVER_106_0.S[2620]++;Location location = ps.getLocation();
__CLOVER_106_0.S[2621]++;Class propertyType = convertPropertyType(ps.getType(), location);
__CLOVER_106_0.S[2622]++;String readMethodName = findReadMethodName(propertyName, propertyType, location);
__CLOVER_106_0.S[2623]++;IEnhancer enhancer = new CreatePropertyEnhancer(propertyName, propertyType, readMethodName,
false);
__CLOVER_106_0.S[2624]++;addEnhancer(enhancer);
} finally { }}
private void addAutoParameterEnhancer(String parameterName, IParameterSpecification ps)
{try { __CLOVER_106_0.M[605]++;
__CLOVER_106_0.S[2625]++;Location location = ps.getLocation();
__CLOVER_106_0.S[2626]++;String propertyName = ps.getPropertyName();
__CLOVER_106_0.S[2627]++;if ((((!ps.isRequired() && ps.getDefaultValue() == null) && (++__CLOVER_106_0.CT[489] != 0)) || (++__CLOVER_106_0.CF[489] == 0))){
__CLOVER_106_0.S[2628]++;throw new ApplicationRuntimeException(EnhanceMessages.autoMustBeRequired(propertyName),
location, null);}
__CLOVER_106_0.S[2629]++;Class propertyType = convertPropertyType(ps.getType(), location);
__CLOVER_106_0.S[2630]++;String readMethodName = findReadMethodName(propertyName, propertyType, location);
__CLOVER_106_0.S[2631]++;IEnhancer enhancer = new CreateAutoParameterEnhancer(propertyName, parameterName,
propertyType, readMethodName);
__CLOVER_106_0.S[2632]++;addEnhancer(enhancer);
} finally { }}
private void addEnhancer(IEnhancer eh)
{try { __CLOVER_106_0.M[606]++;
__CLOVER_106_0.S[2633]++;if ((((_worklist == null) && (++__CLOVER_106_0.CT[490] != 0)) || (++__CLOVER_106_0.CF[490] == 0))){
{
__CLOVER_106_0.S[2634]++;String subclassName = constructEnhancedClassName();
__CLOVER_106_0.S[2635]++;_worklist = new EnhancementWorklistImpl(subclassName, _componentClass, _resolver,
_classFactory);
}}
__CLOVER_106_0.S[2636]++;_worklist.addEnhancer(eh);
} finally { }}
private String constructEnhancedClassName()
{try { __CLOVER_106_0.M[607]++;
__CLOVER_106_0.S[2637]++;String startClassName = _componentClass.getName();
__CLOVER_106_0.S[2638]++;int lastdot = startClassName.lastIndexOf('.');
// Get package name, INCLUDING, seperator dot
__CLOVER_106_0.S[2639]++;String packageName = (((lastdot < 1 ) && (++__CLOVER_106_0.CT[491] != 0)) || (++__CLOVER_106_0.CF[491] == 0))? "" : startClassName.substring(0, lastdot + 1);
__CLOVER_106_0.S[2640]++;String rawName = startClassName.substring(lastdot + 1);
// If the new class is located in a 'restricted' package,
// use a neutral package name
__CLOVER_106_0.S[2641]++;if ((((packageName.startsWith("java.") || packageName.startsWith("javax.")) && (++__CLOVER_106_0.CT[492] != 0)) || (++__CLOVER_106_0.CF[492] == 0))){
__CLOVER_106_0.S[2642]++;packageName = ENHANCED_PACKAGE;}
__CLOVER_106_0.S[2643]++;return packageName + "$" + rawName + "_" + generateUID();
} finally { }}
private void scanForSpecifiedProperty(IPropertySpecification ps)
{try { __CLOVER_106_0.M[608]++;
__CLOVER_106_0.S[2644]++;String propertyName = ps.getName();
__CLOVER_106_0.S[2645]++;Location location = ps.getLocation();
__CLOVER_106_0.S[2646]++;Class propertyType = convertPropertyType(ps.getType(), location);
__CLOVER_106_0.S[2647]++;PropertyDescriptor pd = getPropertyDescriptor(propertyName);
__CLOVER_106_0.S[2648]++;if ((((isImplemented(pd)) && (++__CLOVER_106_0.CT[493] != 0)) || (++__CLOVER_106_0.CF[493] == 0))){
{
// Make sure the property is at least the right type.
__CLOVER_106_0.S[2649]++;checkPropertyType(pd, propertyType, location);
__CLOVER_106_0.S[2650]++;return;
}}
__CLOVER_106_0.S[2651]++;String readMethodName = findReadMethodName(propertyName, propertyType, location);
__CLOVER_106_0.S[2652]++;IEnhancer enhancer = new CreatePropertyEnhancer(propertyName, propertyType, readMethodName,
ps.isPersistent());
__CLOVER_106_0.S[2653]++;addEnhancer(enhancer);
} finally { }}
public EnhancementWorklist getWorklist()
{try { __CLOVER_106_0.M[609]++;
__CLOVER_106_0.S[2654]++;return _worklist;
} finally { }}
private static synchronized int generateUID()
{try { __CLOVER_106_0.M[610]++;
__CLOVER_106_0.S[2655]++;return _uid++;
} finally { }}
}