001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.util;
016    
017    import com.liferay.portal.kernel.io.Deserializer;
018    import com.liferay.portal.kernel.io.Serializer;
019    
020    import java.io.Externalizable;
021    import java.io.IOException;
022    import java.io.ObjectInput;
023    import java.io.ObjectOutput;
024    
025    import java.lang.reflect.Method;
026    
027    import java.nio.ByteBuffer;
028    
029    import java.util.Arrays;
030    
031    /**
032     * Provides a serializable loose representation for {@link Method}, considering
033     * the declaring class, name, and parameter types of the {@link Method}, while
034     * ignoring its return type and exceptions. This means the compiler generated
035     * bridging method is considered logically the same as it source counterpart. On
036     * deserialization for a generic {@link Method}, the {@link Method} that is
037     * resolved (bridge method or source method) is runtime environment dependent.
038     * Whether it is resolved to a bridge method or source method is of no
039     * consequence, as a force cast is performed on the method's return value,
040     * assuring the same result.
041     *
042     * @author Brian Wing Shun Chan
043     * @author Shuyang Zhou
044     */
045    public class MethodKey implements Externalizable {
046    
047            /**
048             * The empty constructor is required by {@link java.io.Externalizable}. Do
049             * not use this for any other purpose.
050             */
051            public MethodKey() {
052            }
053    
054            public MethodKey(
055                    Class<?> declaringClass, String methodName,
056                    Class<?>... parameterTypes) {
057    
058                    _declaringClass = declaringClass;
059                    _methodName = methodName;
060                    _parameterTypes = parameterTypes;
061            }
062    
063            public MethodKey(Method method) {
064                    this(
065                            method.getDeclaringClass(), method.getName(),
066                            method.getParameterTypes());
067            }
068    
069            /**
070             * @deprecated As of 6.2.0, replaced by {@link #MethodKey(Class, String,
071             *             Class...)}
072             */
073            public MethodKey(
074                    String declaringClassName, String methodName,
075                    Class<?>... parameterTypes) {
076    
077                    Thread currentThread = Thread.currentThread();
078    
079                    ClassLoader classLoader = currentThread.getContextClassLoader();
080    
081                    try {
082                            _declaringClass = classLoader.loadClass(declaringClassName);
083                    }
084                    catch (ClassNotFoundException cnfe) {
085                            throw new RuntimeException(cnfe);
086                    }
087    
088                    _methodName = methodName;
089                    _parameterTypes = parameterTypes;
090            }
091    
092            @Override
093            public boolean equals(Object obj) {
094                    if (this == obj) {
095                            return true;
096                    }
097    
098                    if (!(obj instanceof MethodKey)) {
099                            return false;
100                    }
101    
102                    MethodKey methodKey = (MethodKey)obj;
103    
104                    if ((_declaringClass == methodKey._declaringClass) &&
105                            Validator.equals(_methodName, methodKey._methodName) &&
106                            Arrays.equals(_parameterTypes, methodKey._parameterTypes)) {
107    
108                            return true;
109                    }
110    
111                    return false;
112            }
113    
114            public Class<?> getDeclaringClass() {
115                    return _declaringClass;
116            }
117    
118            public Method getMethod() throws NoSuchMethodException {
119                    return MethodCache.get(this);
120            }
121    
122            public String getMethodName() {
123                    return _methodName;
124            }
125    
126            public Class<?>[] getParameterTypes() {
127                    return _parameterTypes;
128            }
129    
130            @Override
131            public int hashCode() {
132    
133                    // Using the same hash algorithm as java.lang.reflect.Method
134    
135                    return _declaringClass.getName().hashCode() ^ _methodName.hashCode();
136            }
137    
138            @Override
139            public void readExternal(ObjectInput objectInput)
140                    throws ClassNotFoundException, IOException {
141    
142                    int size = objectInput.readInt();
143    
144                    byte[] data = new byte[size];
145    
146                    objectInput.readFully(data);
147    
148                    Deserializer deserializer = new Deserializer(ByteBuffer.wrap(data));
149    
150                    _declaringClass = deserializer.readObject();
151                    _methodName = deserializer.readString();
152    
153                    int parameterTypesLength = deserializer.readInt();
154    
155                    _parameterTypes = new Class<?>[parameterTypesLength];
156    
157                    for (int i = 0; i < parameterTypesLength; i++) {
158                            _parameterTypes[i] = deserializer.readObject();
159                    }
160            }
161    
162            @Override
163            public String toString() {
164                    if (_toString != null) {
165                            return _toString;
166                    }
167    
168                    StringBundler sb = new StringBundler(4 + _parameterTypes.length * 2);
169    
170                    sb.append(_declaringClass.getName());
171                    sb.append(StringPool.PERIOD);
172                    sb.append(_methodName);
173                    sb.append(StringPool.OPEN_PARENTHESIS);
174    
175                    for (Class<?> parameterType : _parameterTypes) {
176                            sb.append(parameterType.getName());
177                            sb.append(StringPool.COMMA);
178                    }
179    
180                    sb.setIndex(sb.index() - 1);
181    
182                    sb.append(StringPool.CLOSE_PARENTHESIS);
183    
184                    _toString = sb.toString();
185    
186                    return _toString;
187            }
188    
189            public MethodKey transform(ClassLoader classLoader)
190                    throws ClassNotFoundException {
191    
192                    Class<?> declaringClass = classLoader.loadClass(
193                            _declaringClass.getName());
194    
195                    Class<?>[] parameterTypes = new Class<?>[_parameterTypes.length];
196    
197                    for (int i = 0; i < _parameterTypes.length; i++) {
198                            parameterTypes[i] = classLoader.loadClass(
199                                    _parameterTypes[i].getName());
200                    }
201    
202                    return new MethodKey(declaringClass, _methodName, parameterTypes);
203            }
204    
205            @Override
206            public void writeExternal(ObjectOutput objectOutput) throws IOException {
207                    Serializer serializer = new Serializer();
208    
209                    serializer.writeObject(_declaringClass);
210                    serializer.writeString(_methodName);
211                    serializer.writeInt(_parameterTypes.length);
212    
213                    for (Class<?> parameterType : _parameterTypes) {
214                            serializer.writeObject(parameterType);
215                    }
216    
217                    ByteBuffer byteBuffer = serializer.toByteBuffer();
218    
219                    objectOutput.writeInt(byteBuffer.remaining());
220                    objectOutput.write(
221                            byteBuffer.array(), byteBuffer.position(), byteBuffer.remaining());
222            }
223    
224            private static final long serialVersionUID = 1L;
225    
226            private Class<?> _declaringClass;
227            private String _methodName;
228            private Class<?>[] _parameterTypes;
229    
230            // Transient cache
231    
232            private String _toString;
233    
234    }