001    // Copyright 2005 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry.annotations;
016    
017    import java.beans.Introspector;
018    import java.lang.annotation.Annotation;
019    import java.lang.reflect.Method;
020    import java.util.Iterator;
021    
022    import org.apache.hivemind.ApplicationRuntimeException;
023    import org.apache.hivemind.Location;
024    import org.apache.hivemind.Resource;
025    import org.apache.tapestry.spec.IBindingSpecification;
026    import org.apache.tapestry.spec.IContainedComponent;
027    import org.apache.tapestry.util.DescribedLocation;
028    
029    /**
030     * @author Howard M. Lewis Ship
031     * @since 4.0
032     */
033    
034    public final class AnnotationUtils
035    {
036        /* defeat instantiation */
037        private AnnotationUtils() { }
038        
039        /**
040         * Determines the property name for a method, by stripping off the is/get/set prefix and
041         * decapitalizing the first name.
042         * 
043         * @param method
044         *            accessor method (get/set/is)
045         * @return the property name for the method
046         * @throws ApplicationRuntimeException
047         *             if the method is not an accessor or mutator method
048         */
049        public static String getPropertyName(Method method)
050        {
051            String name = method.getName();
052    
053            if (name.startsWith("is"))
054            {
055                checkGetter(method);
056                return Introspector.decapitalize(name.substring(2));
057            }
058    
059            if (name.startsWith("get"))
060            {
061                checkGetter(method);
062                return Introspector.decapitalize(name.substring(3));
063            }
064    
065            if (name.startsWith("set"))
066            {
067                checkSetter(method);
068                return Introspector.decapitalize(name.substring(3));
069            }
070    
071            throw new ApplicationRuntimeException(AnnotationMessages.notAccessor(method));
072        }
073        
074        /**
075         * Converts a method name to a property key. The steps performed are:
076         * <p>The prefix "get" is stripped off (if present)
077         * <p>The letter following "get" is converted to lower case
078         * <p>Other capitalized letters are converted to lower case and preceded with a dash ("-")
079         * 
080         * @param methodName the method to convert
081         * @return the converted key
082         * 
083         * @since 4.1.1 
084         */
085        public static String convertMethodNameToKeyName(String methodName)
086        {
087            StringBuffer buffer = new StringBuffer();
088        
089            int cursorx = methodName.startsWith("get") ? 3 : 0;
090            int length = methodName.length();
091            boolean atStart = true;
092        
093            while (cursorx < length)
094            {
095                char ch = methodName.charAt(cursorx);
096        
097                if (Character.isUpperCase(ch))
098                {
099                    if (!atStart)
100                        buffer.append('-');
101                    buffer.append(Character.toLowerCase(ch));
102                }
103                else
104                    buffer.append(ch);
105        
106                atStart = false;
107        
108                cursorx++;
109            }
110        
111            return buffer.toString();
112        }    
113        
114        /**
115         * Copies all bindings of a component to another one.
116         * @param source
117         * @param target
118         * 
119         * @since 4.1.1
120         */
121        public static void copyBindings(IContainedComponent source, IContainedComponent target)
122        {
123            Iterator i = source.getBindingNames().iterator();
124            while (i.hasNext())
125            {
126                String bindingName = (String) i.next();
127                IBindingSpecification binding = source.getBinding(bindingName);
128                target.setBinding(bindingName, binding);
129            }
130        
131            target.setType(source.getType());
132        }    
133    
134        private static void checkGetter(Method method)
135        {
136            if (method.getParameterTypes().length > 0)
137                throw new ApplicationRuntimeException(AnnotationMessages.noParametersExpected(method));
138    
139            if (method.getReturnType().equals(void.class))
140                throw new ApplicationRuntimeException(AnnotationMessages.voidAccessor(method));
141    
142        }
143    
144        private static void checkSetter(Method method)
145        {
146            if (!method.getReturnType().equals(void.class))
147                throw new ApplicationRuntimeException(AnnotationMessages.nonVoidMutator(method));
148    
149            if (method.getParameterTypes().length != 1)
150                throw new ApplicationRuntimeException(AnnotationMessages.wrongParameterCount(method));
151        }
152    
153        public static Location buildLocationForAnnotation(Method method, Annotation annotation,
154                Resource classResource)
155        {
156            return new DescribedLocation(classResource, AnnotationMessages.methodAnnotation(
157                    annotation,
158                    method));
159        }
160    }