Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  // Copyright 2004, 2005 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  org.apache.hivemind.ApplicationRuntimeException;
 import  org.apache.hivemind.ClassResolver;
 import  org.apache.hivemind.HiveMind;
 import  org.apache.hivemind.Location;
 import  org.apache.hivemind.service.BodyBuilder;
 import  org.apache.hivemind.service.ClassFab;
 import  org.apache.hivemind.service.ClassFactory;
 import  org.apache.hivemind.service.MethodSignature;
 import  org.apache.hivemind.util.Defense;
 import  org.apache.hivemind.util.ToStringBuilder;
 
 import java.util.*;

Implementation of org.apache.tapestry.enhance.EnhancementOperationthat knows how to collect class changes from enhancements. The method getConstructor() finalizes the enhancement into a org.apache.tapestry.services.ComponentConstructor.

Author(s):
Howard M. Lewis Ship
Since:
4.0
 
 public class EnhancementOperationImpl implements EnhancementOperation
 {
     static int _uid = 0;
 
     private ClassResolver _resolver;
 
 
     private Class _baseClass;
 
     private ClassFab _classFab;
 
     private final Set _claimedProperties = new HashSet();
 
     private final JavaClassMapping _javaClassMapping = new JavaClassMapping();
 
     private final List _constructorTypes = new ArrayList();
 
     private final List _constructorArguments = new ArrayList();
 
     private final ObjectIdentityMap _finalFields = new ObjectIdentityMap();

    
Set of interfaces added to the enhanced class.
 
 
     private Set _addedInterfaces = new HashSet();

    
Map of BodyBuilder, keyed on MethodSignature.
 
 
     private Map _incompleteMethods = new HashMap();

    
Map of property names to PropertyDescriptor.
 
 
     private Map _properties = new HashMap();

    
Used to incrementally assemble the constructor for the enhanced class.
 
 
     private BodyBuilder _constructorBuilder;

    
Makes sure that names created by addInjectedField(String, Class, Object) have unique names.
    private final IdAllocator _idAllocator = new IdAllocator();

    
Map keyed on MethodSignature, value is Location. Used to track which methods have been created, based on which location data (identified conflicts).
    private final Map _methods = new HashMap();
    // May be null
    private final Log _log;

    
Alternate package private constructor used by the test suite, to bypass the defense checks above.
    {
         = null;
    }
    public EnhancementOperationImpl(ClassResolver classResolver,
                                    IComponentSpecification specificationClass baseClass,
                                    ClassFactory classFactoryLog log)
    {
        Defense.notNull(classResolver"classResolver");
        Defense.notNull(specification"specification");
        Defense.notNull(baseClass"baseClass");
        Defense.notNull(classFactory"classFactory");
         = classResolver;
         = specification;
         = baseClass;
        introspectBaseClass();
        String name = newClassName();
         = classFactory.newClass(name);
         = log;
    }
    public String toString()
    {
        ToStringBuilder builder = new ToStringBuilder(this);
        builder.append("baseClass".getName());
        builder.append("claimedProperties");
        builder.append("classFab");
        return builder.toString();
    }

    
We want to find the properties of the class, but in many cases, the class is abstract. Some JDK's (Sun) will include public methods from interfaces implemented by the class in the public declared methods for the class (which is used by the Introspector). Eclipse's built-in compiler does not appear to (this may have to do with compiler options I've been unable to track down). The solution is to augment the information provided directly by the Introspector with additional information compiled by Introspecting the interfaces directly or indirectly implemented by the class.
    private void introspectBaseClass()
    {
        try
        {
            synchronized(HiveMind.INTROSPECTOR_MUTEX)
            {
                addPropertiesDeclaredInBaseClass();
            }
        }
        catch (IntrospectionException ex)
        {
            throw new ApplicationRuntimeException(EnhanceMessages.unabelToIntrospectClass(ex), ex);
        }
    }
    private void addPropertiesDeclaredInBaseClass()
      throws IntrospectionException
    {
        Class introspectClass = ;
        addPropertiesDeclaredInClass(introspectClass);
        List interfaceQueue = new ArrayList();
        while(introspectClass != null)
        {
            addInterfacesToQueue(introspectClassinterfaceQueue);
            introspectClass = introspectClass.getSuperclass();
        }
        while(!interfaceQueue.isEmpty())
        {
            Class interfaceClass = (ClassinterfaceQueue.remove(0);
            addPropertiesDeclaredInClass(interfaceClass);
            addInterfacesToQueue(interfaceClassinterfaceQueue);
        }
    }
    private void addInterfacesToQueue(Class introspectClassList interfaceQueue)
    {
        Class[] interfaces = introspectClass.getInterfaces();
        for(int i = 0; i < interfaces.lengthi++)
            interfaceQueue.add(interfaces[i]);
    }
    private void addPropertiesDeclaredInClass(Class introspectClass)
      throws IntrospectionException
    {
        BeanInfo bi = Introspector.getBeanInfo(introspectClass);
        PropertyDescriptor[] pds = bi.getPropertyDescriptors();
        for(int i = 0; i < pds.lengthi++)
        {
            PropertyDescriptor pd = pds[i];
            String name = pd.getName();
            if (!.containsKey(name))
                .put(namepd);
        }
    }
    public void claimProperty(String propertyName)
    {
        Defense.notNull(propertyName"propertyName");
        if (.contains(propertyName))
            throw new ApplicationRuntimeException(EnhanceMessages.claimedProperty(propertyName));
        .add(propertyName);
    }

    
    public boolean canClaimAsReadOnlyProperty(String propertyName)
    {
        if(.contains(propertyName))
            return false;
        PropertyDescriptor pd = getPropertyDescriptor(propertyName);
        if (pd == null)
            return false;
        return pd.getWriteMethod() == null ? true : false;
    }
    public void claimReadonlyProperty(String propertyName)
    {
        claimProperty(propertyName);
        PropertyDescriptor pd = getPropertyDescriptor(propertyName);
        if (pd != null && pd.getWriteMethod() != null)
            throw new ApplicationRuntimeException(EnhanceMessages.readonlyProperty(propertyNamepd.getWriteMethod()));
    }
    public void addField(String nameClass type)
    {
        .addField(nametype);
    }
    public String addInjectedField(String fieldNameClass fieldTypeObject value)
    {
        Defense.notNull(fieldName"fieldName");
        Defense.notNull(fieldType"fieldType");
        Defense.notNull(value"value");
        String existing = (String.get(value);
        // See if this object has been previously added.
        if (existing != null)
            return existing;
        // TODO: Should be ensure that the name is unique?
        // Make sure that the field has a unique name (at least, among anything
        // added
        // via addFinalField().
        String uniqueName = .allocateId(fieldName);
        // ClassFab doesn't have an option for saying the field should be final,
        // just private.
        // Doesn't make a huge difference.
        .addField(uniqueNamefieldType);
        int parameterIndex = addConstructorParameter(fieldTypevalue);
        constructorBuilder().addln("{0} = ${1};"uniqueName, Integer.toString(parameterIndex));
        // Remember the mapping from the value to the field name.
        .put(valueuniqueName);
        return uniqueName;
    }
    public Class convertTypeName(String type)
    {
        Defense.notNull(type"type");
        Class result = .getType(type);
        if (result == null)
        {
            result = .findClass(type);
            .recordType(typeresult);
        }
        return result;
    }
    public Class getPropertyType(String name)
    {
        Defense.notNull(name"name");
        PropertyDescriptor pd = getPropertyDescriptor(name);
        return pd == null ? null : pd.getPropertyType();
    }
    public void validateProperty(String nameClass expectedType)
    {
        Defense.notNull(name"name");
        Defense.notNull(expectedType"expectedType");
        PropertyDescriptor pd = getPropertyDescriptor(name);
        if (pd == null)
            return;
        Class propertyType = pd.getPropertyType();
        if (propertyType.equals(expectedType))
            return;
        throw new ApplicationRuntimeException(EnhanceMessages.propertyTypeMismatch(namepropertyTypeexpectedType));
    }
    {
        return (PropertyDescriptor.get(name);
    }
    public String getAccessorMethodName(String propertyName)
    {
        Defense.notNull(propertyName"propertyName");
        PropertyDescriptor pd = getPropertyDescriptor(propertyName);
        if (pd != null && pd.getReadMethod() != null)
            return pd.getReadMethod().getName();
        return EnhanceUtils.createAccessorMethodName(propertyName);
    }
    public void addMethod(int modifierMethodSignature sigString methodBody, Location location)
    {
        Defense.notNull(sig"sig");
        Defense.notNull(methodBody"methodBody");
        Defense.notNull(location"location");
        Location existing = (Location) .get(sig);
        if (existing != null)
            throw new ApplicationRuntimeException(EnhanceMessages.methodConflict(sigexisting), locationnull);
        .put(siglocation);
        .addMethod(modifiersigmethodBody);
    }
    public Class getBaseClass()
    {
        return ;
    }
    public String getClassReference(Class clazz)
    {
        Defense.notNull(clazz"clazz");
        String result = (String.get(clazz);
        if (result == null)
            result = addClassReference(clazz);
        return result;
    }
    private String addClassReference(Class clazz)
    {
        StringBuffer buffer = new StringBuffer("_class$");
        Class c = clazz;
        while(c.isArray())
        {
            buffer.append("array$");
            c = c.getComponentType();
        }
        buffer.append(c.getName().replace('.''$'));
        String fieldName = buffer.toString();
        return addInjectedField(fieldNameClass.classclazz);
    }

    
Adds a new constructor parameter, returning the new count. This is convienient, because the first element added is accessed as $1, etc.
    private int addConstructorParameter(Class typeObject value)
    {
        .add(type);
        .add(value);
        return .size();
    }
    private BodyBuilder constructorBuilder()
    {
        if ( == null)
        {
             = new BodyBuilder();
            .begin();
        }
        return ;
    }

    
Returns an object that can be used to construct instances of the enhanced component subclass. This should only be called once.
    {
        try
        {
            finalizeEnhancedClass();
            Constructor c = findConstructor();
            Object[] params = .toArray();
            return new ComponentConstructorImpl(cparams.toString(), .getLocation());
        }
        catch (Throwable t)
        {
            throw new ApplicationRuntimeException(EnhanceMessages.classEnhancementFailure(t), nullt);
        }
    }
    void finalizeEnhancedClass()
    {
        finalizeIncompleteMethods();
        if ( != null)
        {
            .end();
            Class[] types = (Class[]) .toArray(new Class[.size()]);
            .addConstructor(typesnull.toString());
        }
        if ( != null && .isDebugEnabled())
            .debug("Creating class:\n\n" + );
    }
    private void finalizeIncompleteMethods()
    {
        Iterator i = .entrySet().iterator();
        while(i.hasNext())
        {
            Map.Entry e = (Map.Entryi.next();
            MethodSignature sig = (MethodSignaturee.getKey();
            BodyBuilder builder = (BodyBuilder) e.getValue();
            // Each BodyBuilder is created and given a begin(), this is
            // the matching end()
            builder.end();
            .addMethod(.sigbuilder.toString());
        }
    }
    private Constructor findConstructor()
    {
        Class componentClass = .createClass();
        // The fabricated base class always has exactly one constructor
        return componentClass.getConstructors()[0];
    }
    private String newClassName()
    {
        String baseName = .getName();
        int dotx = baseName.lastIndexOf('.');
        return "$" + baseName.substring(dotx + 1) + "_" + ++;
    }
    public void extendMethodImplementation(Class interfaceClassMethodSignature methodSignatureString code)
    {
        addInterfaceIfNeeded(interfaceClass);
        BodyBuilder builder = (BodyBuilder) .get(methodSignature);
        if (builder == null)
        {
            builder = createIncompleteMethod(methodSignature);
            .put(methodSignaturebuilder);
        }
        builder.addln(code);
    }
    private void addInterfaceIfNeeded(Class interfaceClass)
    {
        if (implementsInterface(interfaceClass))
            return;
        .addInterface(interfaceClass);
        .add(interfaceClass);
    }
    public boolean implementsInterface(Class interfaceClass)
    {
        if (interfaceClass.isAssignableFrom())
            return true;
        Iterator i = .iterator();
        while(i.hasNext())
        {
            Class addedInterface = (Classi.next();
            if (interfaceClass.isAssignableFrom(addedInterface))
                return true;
        }
        return false;
    }
    private BodyBuilder createIncompleteMethod(MethodSignature sig)
    {
        BodyBuilder result = new BodyBuilder();
        // Matched inside finalizeIncompleteMethods()
        result.begin();
        if (existingImplementation(sig))
            result.addln("super.{0}($$);"sig.getName());
        return result;
    }

    
Returns true if the base class implements the provided method as either a public or a protected method.
    private boolean existingImplementation(MethodSignature sig)
    {
        Method m = findMethod(sig);
        return m != null && !Modifier.isAbstract(m.getModifiers());
    }

    
Finds a public or protected method in the base class.
    private Method findMethod(MethodSignature sig)
    {
        // Finding a public method is easy:
        try
        {
            return .getMethod(sig.getName(), sig.getParameterTypes());
        }
        catch (NoSuchMethodException ex)
        {
            // Good; no super-implementation to invoke.
        }
        Class c = ;
        while(c != Object.class)
        {
            try
            {
                return c.getDeclaredMethod(sig.getName(), sig
                  .getParameterTypes());
            }
            catch (NoSuchMethodException ex)
            {
                // Ok, continue loop up to next base class.
            }
            c = c.getSuperclass();
        }
        return null;
    }
    {
        List result = new ArrayList();
        Iterator i = .values().iterator();
        while(i.hasNext())
        {
            PropertyDescriptor pd = (PropertyDescriptori.next();
            String name = pd.getName();
            if (.contains(name))
                continue;
            if (isAbstractProperty(pd))
                result.add(name);
        }
        return result;
    }

    
A property is abstract if either its read method or it write method is abstract. We could do some additional checking to ensure that both are abstract if either is. Note that in many cases, there will only be one accessor (a reader or a writer).
    private boolean isAbstractProperty(PropertyDescriptor pd)
    {
        return isExistingAbstractMethod(pd.getReadMethod())
               || isExistingAbstractMethod(pd.getWriteMethod());
    }
    private boolean isExistingAbstractMethod(Method m)
    {
        return m != null && Modifier.isAbstract(m.getModifiers());
    }
    {
        return ;
    }
New to GrepCode? Check out our FAQ X