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.user.rebind.rpc;
017:
018: import com.google.gwt.core.ext.BadPropertyValueException;
019: import com.google.gwt.core.ext.PropertyOracle;
020: import com.google.gwt.core.ext.TreeLogger;
021: import com.google.gwt.core.ext.typeinfo.JArrayType;
022: import com.google.gwt.core.ext.typeinfo.JClassType;
023: import com.google.gwt.core.ext.typeinfo.JConstructor;
024: import com.google.gwt.core.ext.typeinfo.JParameterizedType;
025: import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
026: import com.google.gwt.core.ext.typeinfo.JType;
027:
028: import java.util.Locale;
029:
030: class Shared {
031: /**
032: * Property used to control whether or not the RPC system will enforce the
033: * versioning scheme or not.
034: */
035: static final String RPC_PROP_ENFORCE_TYPE_VERSIONING = "gwt.enforceRPCTypeVersioning";
036:
037: /**
038: * Capitalizes a name.
039: *
040: * @param name the string to be capitalized
041: * @return the capitalized string
042: */
043: static String capitalize(String name) {
044: return name.substring(0, 1).toUpperCase(Locale.US)
045: + name.substring(1);
046: }
047:
048: static String getStreamReadMethodNameFor(JType type) {
049: return "read" + getCallSuffix(type);
050: }
051:
052: static String getStreamWriteMethodNameFor(JType type) {
053: return "write" + getCallSuffix(type);
054: }
055:
056: /**
057: * Returns <code>true</code> if the type is a non-abstract class that either
058: * has no constructors or it has a non-private, no argument constructor.
059: *
060: * @param type
061: * @return <code>true</code> if the type is a non-abstract class that either
062: * has no constructors or it has a non-private, no argument
063: * constructor
064: */
065: static boolean isDefaultInstantiable(JClassType type) {
066: if (type.isInterface() != null || type.isAbstract()) {
067: return false;
068: }
069:
070: boolean isDefaultInstantiable = false;
071: if (type.getConstructors().length == 0) {
072: isDefaultInstantiable = true;
073: } else {
074: JConstructor ctor = type.findConstructor(new JType[0]);
075: if (ctor != null && !ctor.isPrivate()) {
076: isDefaultInstantiable = true;
077: }
078: }
079:
080: return isDefaultInstantiable;
081: }
082:
083: /**
084: * Returns <code>true</code> if the generated code should enforce type
085: * versioning.
086: */
087: static boolean shouldEnforceTypeVersioning(TreeLogger logger,
088: PropertyOracle propertyOracle) {
089: try {
090: String propVal = propertyOracle.getPropertyValue(logger,
091: RPC_PROP_ENFORCE_TYPE_VERSIONING);
092: if (propVal.equals("false")) {
093: return false;
094: }
095: } catch (BadPropertyValueException e) {
096: // Purposely ignored, because we want to enforce RPC versioning if
097: // the property is not defined.
098: }
099: // Assume type versioning unless the user explicitly turns it off.
100: return true;
101: }
102:
103: /**
104: * Computes a good name for a class related to the specified type, such that
105: * the computed name can be a top-level class in the same package as the
106: * specified type.
107: *
108: * <p>
109: * This method does not currently check for collisions between the synthesized
110: * name and an existing top-level type in the same package. It is actually
111: * tricky to do so, because on subsequent runs, we'll view our own generated
112: * classes as collisions. There's probably some trick we can use in the future
113: * to make it totally bulletproof.
114: * </p>
115: *
116: * @param type the name of the base type, whose name will be built upon to
117: * synthesize a new type name
118: * @param suffix a suffix to be used to make the new synthesized type name
119: * @return an array of length 2 such that the first element is the package
120: * name and the second element is the synthesized class name
121: */
122: static String[] synthesizeTopLevelClassName(JClassType type,
123: String suffix) {
124: // Gets the basic name of the type. If it's a nested type, the type name
125: // will contains dots.
126: //
127: String className;
128: String packageName;
129:
130: JType leafType = type.getLeafType();
131: if (leafType.isPrimitive() != null) {
132: className = leafType.getSimpleSourceName();
133: packageName = "";
134: } else {
135: JClassType classOrInterface = leafType.isClassOrInterface();
136: assert (classOrInterface != null);
137: className = classOrInterface.getName();
138: packageName = classOrInterface.getPackage().getName();
139: }
140:
141: JArrayType isArray = type.isArray();
142: if (isArray != null) {
143: className += "_Array_Rank_" + isArray.getRank();
144: }
145:
146: // Add the meaningful suffix.
147: //
148: className += suffix;
149:
150: // Make it a top-level name.
151: //
152: className = className.replace('.', '_');
153:
154: return new String[] { packageName, className };
155: }
156:
157: /**
158: * Determines whether a particular type needs to be cast to become its final
159: * type. Primitives and Strings do not, as they are read directly as the
160: * correct type. All other Objects need a cast, except for Object itself.
161: *
162: * @param type the type in question
163: * @return <code>true</code> if the results of a read method must be cast,
164: * otherwise <code>false</code>.
165: */
166: static boolean typeNeedsCast(JType type) {
167: return type.isPrimitive() == null
168: && !type.getQualifiedSourceName().equals(
169: "java.lang.String")
170: && !type.getQualifiedSourceName().equals(
171: "java.lang.Object");
172: }
173:
174: /**
175: * Gets the suffix needed to make a call for a particular type. For example,
176: * the <code>int</code> class needs methods named "readInt" and "writeInt".
177: *
178: * @param type the type in question
179: * @return the suffix of the method to call
180: */
181: private static String getCallSuffix(JType type) {
182: JParameterizedType isParameterized = type.isParameterized();
183: if (isParameterized != null) {
184: return getCallSuffix(isParameterized.getRawType());
185: } else if (type.isPrimitive() != null) {
186: if (type == JPrimitiveType.BOOLEAN) {
187: return "Boolean";
188: } else if (type == JPrimitiveType.BYTE) {
189: return "Byte";
190: } else if (type == JPrimitiveType.CHAR) {
191: return "Char";
192: } else if (type == JPrimitiveType.DOUBLE) {
193: return "Double";
194: } else if (type == JPrimitiveType.FLOAT) {
195: return "Float";
196: } else if (type == JPrimitiveType.INT) {
197: return "Int";
198: } else if (type == JPrimitiveType.LONG) {
199: return "Long";
200: } else if (type == JPrimitiveType.SHORT) {
201: return "Short";
202: } else {
203: return null;
204: }
205: } else if (type.getQualifiedSourceName().equals(
206: "java.lang.String")) {
207: return "String";
208: } else {
209: return "Object";
210: }
211: }
212: }
|