001    /*****************************************************************************
002     * Copyright (c) PicoContainer Organization. All rights reserved.            *
003     * ------------------------------------------------------------------------- *
004     * The software in this package is published under the terms of the BSD      *
005     * style license a copy of which has been included with this distribution in *
006     * the LICENSE.txt file.                                                     *
007     *                                                                           *
008     *****************************************************************************/
009    package org.picocontainer.injectors;
010    
011    import org.picocontainer.Parameter;
012    import org.picocontainer.ComponentMonitor;
013    import org.picocontainer.LifecycleStrategy;
014    import org.picocontainer.ParameterName;
015    import org.picocontainer.PicoContainer;
016    
017    import java.lang.reflect.AccessibleObject;
018    import java.lang.reflect.Method;
019    import java.lang.reflect.Constructor;
020    import java.lang.reflect.Field;
021    import java.lang.reflect.Modifier;
022    import java.lang.reflect.Member;
023    
024    /** @author Paul Hammant */
025    public abstract class SingleMemberInjector extends AbstractInjector {
026    
027        private transient ParanamerProxy paranamer;
028    
029        private static final String[] EMPTY_NAMES = new String[]{};
030        private static final String COMMA = ",";
031        private static final String SPACE = " ";
032    
033        public SingleMemberInjector(Object componentKey,
034                                    Class componentImplementation,
035                                    Parameter[] parameters,
036                                    ComponentMonitor monitor,
037                                    LifecycleStrategy lifecycleStrategy) {
038            super(componentKey, componentImplementation, parameters, monitor, lifecycleStrategy);
039        }
040    
041    
042        private void createIfNeededParanamerProxy() {
043            if (paranamer == null) {
044                try {
045                    paranamer = new ParanamerProxy();
046                } catch (NoClassDefFoundError e) {
047                }
048            }
049        }
050    
051    
052        // copied from DefaultParanamer
053        protected String[] lookupParameterNames(AccessibleObject methodOrCtor) {
054            Class[] types = null;
055            Class declaringClass = null;
056            String name = null;
057            if (methodOrCtor instanceof Method) {
058                Method method = (Method) methodOrCtor;
059                types = method.getParameterTypes();
060                name = method.getName();
061                declaringClass = method.getDeclaringClass();
062            } else {
063                Constructor constructor = (Constructor) methodOrCtor;
064                types = constructor.getParameterTypes();
065                declaringClass = constructor.getDeclaringClass();
066                name = "<init>";
067            }
068    
069            if (types.length == 0) {
070                // faster ?
071                return EMPTY_NAMES;
072            }
073            final String parameterTypeNames = getParameterTypeNamesCSV(types);
074            final String[] names = getParameterNames(declaringClass, parameterTypeNames, name + SPACE);
075    
076            if (names != null) {
077                return names;
078            }
079            createIfNeededParanamerProxy();
080            if (paranamer != null) {
081                return paranamer.lookupParameterNames((Constructor)methodOrCtor);
082            }
083            return new String[0];
084        }
085    
086        protected Object[] getMemberArguments(PicoContainer container, final AccessibleObject member, final Class[] parameterTypes) {
087            Object[] result = new Object[parameterTypes.length];
088            Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(parameterTypes);
089    
090            for (int i = 0; i < currentParameters.length; i++) {
091                result[i] = currentParameters[i].resolveInstance(container, this, parameterTypes[i],
092                                                                 new MemberInjectorParameterName(member, i));
093            }
094            return result;
095        }
096    
097    
098    
099        // copied from DefaultParanamer
100        private static String getParameterTypeNamesCSV(Class[] parameterTypes) {
101            StringBuffer sb = new StringBuffer();
102            for (int i = 0; i < parameterTypes.length; i++) {
103                sb.append(parameterTypes[i].getName());
104                if (i < parameterTypes.length - 1) {
105                    sb.append(COMMA);
106                }
107            }
108            return sb.toString();
109        }
110        // copied from DefaultParanamer
111        private static String[] getParameterNames(Class declaringClass, String parameterTypes, String prefix) {
112            String data = getParameterListResource(declaringClass);
113            String line = findFirstMatchingLine(data, prefix + parameterTypes);
114            String[] parts = line.split(SPACE);
115            // assumes line structure: constructorName parameterTypes parameterNames
116            if (parts.length == 3 && parts[1].equals(parameterTypes)) {
117                String parameterNames = parts[2];
118                return parameterNames.split(COMMA);
119            }
120            return null;
121        }
122        // copied from DefaultParanamer
123        private static String getParameterListResource(Class declaringClass) {
124            try {
125                Field field = declaringClass.getDeclaredField("__PARANAMER_DATA");
126                if(!Modifier.isStatic(field.getModifiers()) || !field.getType().equals(String.class)) {
127                    return null;
128                }
129                return (String) field.get(null);
130            } catch (NoSuchFieldException e) {
131                return null;
132            } catch (IllegalAccessException e) {
133                return null;
134            }
135        }
136        // copied from DefaultParanamer
137        private static String findFirstMatchingLine(String data, String prefix) {
138            if (data == null) {
139                return "";
140            }
141            int ix = data.indexOf(prefix);
142            if (ix > 0) {
143                int iy = data.indexOf("\n", ix);
144                if(iy >0) {
145                    return data.substring(ix,iy);
146                }
147            }
148            return "";
149        }
150    
151        protected class MemberInjectorParameterName implements ParameterName {
152            private final AccessibleObject member;
153            private final int index;
154    
155            public MemberInjectorParameterName(AccessibleObject member, int index) {
156                this.member = member;
157                this.index = index;
158            }
159    
160            public String getName() {
161                createIfNeededParanamerProxy();
162                if (paranamer != null) {
163                    String[] strings = lookupParameterNames(member);
164                    return strings.length == 0 ? "" : strings[index];
165                }
166                return null;
167            }
168        }
169    
170    
171    
172    
173    }