001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.dev.shell;
017:
018: import java.lang.reflect.Field;
019: import java.lang.reflect.Member;
020: import java.lang.reflect.Method;
021: import java.util.ArrayList;
022: import java.util.HashMap;
023:
024: /**
025: * Helper class for dispatching methods to Java objects. It takes methods on
026: * various Java classes and assigns DISPID's to them.
027: */
028: public class DispatchClassInfo {
029:
030: private Class<?> cls;
031:
032: private final int clsId;
033:
034: private ArrayList<Member> memberById;
035:
036: private HashMap<String, Integer> memberIdByName;
037:
038: public DispatchClassInfo(Class<?> cls, int classId) {
039: this .cls = cls;
040: clsId = classId;
041: }
042:
043: public int getClassId() {
044: return clsId;
045: }
046:
047: public Member getMember(int id) {
048: lazyInitTargetMembers();
049: id &= 0xffff;
050: return memberById.get(id);
051: }
052:
053: public int getMemberId(String mangledMemberName) {
054: lazyInitTargetMembers();
055:
056: Integer id = memberIdByName.get(mangledMemberName);
057: if (id == null) {
058: return -1;
059: }
060:
061: return id.intValue();
062: }
063:
064: private void addMember(Member member, String sig) {
065: memberById.add(member);
066: int index = memberById.size() - 1;
067: memberIdByName.put(sig, new Integer(index));
068: }
069:
070: /**
071: * @see com.google.gwt.server.magic.js.MethodInfo for the corresponding
072: * algorithm written in terms of the QDox model.
073: */
074: private String getJsniSignature(Method method) {
075: String name = method.getName();
076:
077: StringBuffer sb = new StringBuffer();
078: sb.append(name);
079: sb.append("(");
080: Class<?>[] paramTypes = method.getParameterTypes();
081: for (int i = 0; i < paramTypes.length; ++i) {
082: Class<?> type = paramTypes[i];
083: String typeSig = getTypeSig(type);
084: sb.append(typeSig);
085: }
086: sb.append(")");
087:
088: String mangledName = sb.toString();
089: return mangledName;
090: }
091:
092: /*
093: * TODO(jat): generics?
094: */
095: private String getTypeSig(Class<?> type) {
096: if (type.isArray()) {
097: return "[" + getTypeSig(type.getComponentType());
098: }
099:
100: if (type.isPrimitive()) {
101: if (type.equals(int.class)) {
102: return "I";
103: } else if (type.equals(boolean.class)) {
104: return "Z";
105: } else if (type.equals(char.class)) {
106: return "C";
107: } else if (type.equals(long.class)) {
108: return "J";
109: } else if (type.equals(short.class)) {
110: return "S";
111: } else if (type.equals(float.class)) {
112: return "F";
113: } else if (type.equals(double.class)) {
114: return "D";
115: } else if (type.equals(byte.class)) {
116: return "B";
117: } else {
118: throw new RuntimeException(
119: "Unexpected primitive type: " + type.getName());
120: }
121: } else {
122: StringBuffer sb = new StringBuffer();
123: sb.append("L");
124: sb.append(type.getName().replace('.', '/'));
125: sb.append(";");
126: return sb.toString();
127: }
128: }
129:
130: private void lazyInitTargetMembers() {
131: if (memberById == null) {
132: memberById = new ArrayList<Member>();
133: memberById.add(null); // 0 is reserved; it's magic on Win32
134: memberIdByName = new HashMap<String, Integer>();
135: lazyInitTargetMembersUsingReflectionHelper(cls);
136: }
137: }
138:
139: private void lazyInitTargetMembersUsingReflectionHelper(
140: Class<?> targetClass) {
141: // Start by analyzing the superclass recursively.
142: Class<?> super class = targetClass.getSuperclass();
143: if (super class != null) {
144: lazyInitTargetMembersUsingReflectionHelper(super class);
145: }
146:
147: /*
148: * TODO(mmendez): How should we handle the case where a user writes JSNI
149: * code to interact with an instance that is typed as a particular
150: * interface? Should a user write JSNI code as follows:
151: *
152: * x.@com.google.gwt.HasFocus::equals(Ljava/lang/Object;)(y)
153: *
154: * or
155: *
156: * x.@java.lang.Object::equals(Ljava/lang/Object;)(y)
157: *
158: */
159:
160: // Get the methods on this class/interface.
161: //
162: Method[] methods = targetClass.getDeclaredMethods();
163: for (int i = 0; i < methods.length; i++) {
164: methods[i].setAccessible(true);
165: String sig = getJsniSignature(methods[i]);
166: addMember(methods[i], sig);
167: }
168:
169: // Get the fields on this class/interface.
170: Field[] fields = targetClass.getDeclaredFields();
171: for (int i = 0; i < fields.length; i++) {
172: fields[i].setAccessible(true);
173: addMember(fields[i], fields[i].getName());
174: }
175: }
176: }
|