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            @Override
070            public boolean equals(Object obj) {
071                    if (this == obj) {
072                            return true;
073                    }
074    
075                    if (!(obj instanceof MethodKey)) {
076                            return false;
077                    }
078    
079                    MethodKey methodKey = (MethodKey)obj;
080    
081                    if ((_declaringClass == methodKey._declaringClass) &&
082                            Validator.equals(_methodName, methodKey._methodName) &&
083                            Arrays.equals(_parameterTypes, methodKey._parameterTypes)) {
084    
085                            return true;
086                    }
087    
088                    return false;
089            }
090    
091            public Class<?> getDeclaringClass() {
092                    return _declaringClass;
093            }
094    
095            public Method getMethod() throws NoSuchMethodException {
096                    return MethodCache.get(this);
097            }
098    
099            public String getMethodName() {
100                    return _methodName;
101            }
102    
103            public Class<?>[] getParameterTypes() {
104                    return _parameterTypes;
105            }
106    
107            @Override
108            public int hashCode() {
109    
110                    // Using the same hash algorithm as java.lang.reflect.Method
111    
112                    return _declaringClass.getName().hashCode() ^ _methodName.hashCode();
113            }
114    
115            public void readExternal(ObjectInput objectInput)
116                    throws ClassNotFoundException, IOException {
117    
118                    int size = objectInput.readInt();
119    
120                    byte[] data = new byte[size];
121    
122                    objectInput.readFully(data);
123    
124                    Deserializer deserializer = new Deserializer(ByteBuffer.wrap(data));
125    
126                    _declaringClass = deserializer.readObject();
127                    _methodName = deserializer.readString();
128    
129                    int parameterTypesLength = deserializer.readInt();
130    
131                    _parameterTypes = new Class<?>[parameterTypesLength];
132    
133                    for (int i = 0; i < parameterTypesLength; i++) {
134                            _parameterTypes[i] = deserializer.readObject();
135                    }
136            }
137    
138            @Override
139            public String toString() {
140                    if (_toString != null) {
141                            return _toString;
142                    }
143    
144                    StringBundler sb = new StringBundler(4 + _parameterTypes.length * 2);
145    
146                    sb.append(_declaringClass.getName());
147                    sb.append(StringPool.PERIOD);
148                    sb.append(_methodName);
149                    sb.append(StringPool.OPEN_PARENTHESIS);
150    
151                    for (Class<?> parameterType : _parameterTypes) {
152                            sb.append(parameterType.getName());
153                            sb.append(StringPool.COMMA);
154                    }
155    
156                    sb.setIndex(sb.index() - 1);
157    
158                    sb.append(StringPool.CLOSE_PARENTHESIS);
159    
160                    _toString = sb.toString();
161    
162                    return _toString;
163            }
164    
165            public MethodKey transform(ClassLoader classLoader)
166                    throws ClassNotFoundException {
167    
168                    Class<?> declaringClass = classLoader.loadClass(
169                            _declaringClass.getName());
170    
171                    Class<?>[] parameterTypes = new Class<?>[_parameterTypes.length];
172    
173                    for (int i = 0; i < _parameterTypes.length; i++) {
174                            parameterTypes[i] = classLoader.loadClass(
175                                    _parameterTypes[i].getName());
176                    }
177    
178                    return new MethodKey(declaringClass, _methodName, parameterTypes);
179            }
180    
181            public void writeExternal(ObjectOutput objectOutput) throws IOException {
182                    Serializer serializer = new Serializer();
183    
184                    serializer.writeObject(_declaringClass);
185                    serializer.writeString(_methodName);
186                    serializer.writeInt(_parameterTypes.length);
187    
188                    for (Class<?> parameterType : _parameterTypes) {
189                            serializer.writeObject(parameterType);
190                    }
191    
192                    ByteBuffer byteBuffer = serializer.toByteBuffer();
193    
194                    objectOutput.writeInt(byteBuffer.remaining());
195                    objectOutput.write(
196                            byteBuffer.array(), byteBuffer.position(), byteBuffer.remaining());
197            }
198    
199            private static final long serialVersionUID = 1L;
200    
201            private Class<?> _declaringClass;
202    
203            private String _methodName;
204            private Class<?>[] _parameterTypes;
205    
206            // Transient cache
207    
208            private String _toString;
209    
210    }