| /* |
| * Copyright 2009 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.gwt.uibinder.rebind; |
| |
| import com.google.gwt.core.ext.typeinfo.JClassType; |
| import com.google.gwt.core.ext.typeinfo.JPrimitiveType; |
| import com.google.gwt.core.ext.typeinfo.JType; |
| import com.google.gwt.core.ext.typeinfo.TypeOracle; |
| |
| import java.util.LinkedHashSet; |
| |
| /** |
| * Represents a <code>{field.reference}</code>. Collects all the types a |
| * particular reference has been asked to return, and can validate that it |
| * actually does so. |
| */ |
| public class FieldReference { |
| private final FieldManager fieldManager; |
| |
| private final String debugString; |
| private final String[] elements; |
| private final LinkedHashSet<JType> leftHandTypes = new LinkedHashSet<JType>(); |
| |
| private final TypeOracle types; |
| |
| FieldReference(String reference, FieldManager fieldManager, TypeOracle types) { |
| this.debugString = "{" + reference + "}"; |
| this.fieldManager = fieldManager; |
| this.types = types; |
| elements = reference.split("\\."); |
| } |
| |
| public void addLeftHandType(JType type) { |
| leftHandTypes.add(type); |
| } |
| |
| public String getFieldName() { |
| return elements[0]; |
| } |
| |
| @Override |
| public String toString() { |
| return debugString; |
| } |
| |
| public void validate(MonitoredLogger logger) { |
| JType myReturnType = findReturnType(logger); |
| if (myReturnType == null) { |
| return; |
| } |
| |
| for (JType t : leftHandTypes) { |
| ensureAssignable(t, myReturnType, logger); |
| } |
| } |
| |
| /** |
| * Returns a failure message if the types don't mesh, or null on success. |
| */ |
| private void ensureAssignable(JType leftHandType, JType rightHandType, |
| MonitoredLogger logger) { |
| |
| if (leftHandType == rightHandType) { |
| return; |
| } |
| |
| if (handleMismatchedNumbers(leftHandType, rightHandType)) { |
| return; |
| } |
| |
| if (handleMismatchedNonNumericPrimitives(leftHandType, rightHandType, |
| logger)) { |
| return; |
| } |
| |
| JClassType leftClass = leftHandType.isClassOrInterface(); |
| if (leftClass != null) { |
| JClassType rightClass = rightHandType.isClassOrInterface(); |
| if ((rightClass == null) || !leftClass.isAssignableFrom(rightClass)) { |
| logTypeMismatch(leftHandType, rightHandType, logger); |
| } |
| } |
| |
| /* |
| * We're conservative and fall through here, allowing success. Not yet |
| * confident that we know every error case, and are more worried about being |
| * artificially restrictive. |
| */ |
| } |
| |
| private JType findReturnType(MonitoredLogger logger) { |
| FieldWriter field = fieldManager.lookup(elements[0]); |
| if (field == null) { |
| logger.error("No field named %s", elements[0]); |
| return null; |
| } |
| |
| return field.getReturnType(elements, logger); |
| } |
| |
| private boolean handleMismatchedNonNumericPrimitives(JType leftHandType, |
| JType rightHandType, MonitoredLogger logger) { |
| JPrimitiveType leftPrimitive = leftHandType.isPrimitive(); |
| JPrimitiveType rightPrimitive = rightHandType.isPrimitive(); |
| |
| if (leftPrimitive == null && rightPrimitive == null) { |
| return false; |
| } |
| |
| if (leftPrimitive != null) { |
| JClassType autobox = types.findType(leftPrimitive.getQualifiedBoxedSourceName()); |
| if (rightHandType != autobox) { |
| logger.error("Returns %s, can't be used as %s", rightHandType, |
| leftHandType); |
| } |
| } else { // rightPrimitive != null |
| JClassType autobox = types.findType(rightPrimitive.getQualifiedBoxedSourceName()); |
| if (leftHandType != autobox) { |
| logger.error("Returns %s, can't be used as %s", rightHandType, |
| leftHandType); |
| } |
| } |
| |
| return true; |
| } |
| |
| private boolean handleMismatchedNumbers(JType leftHandType, |
| JType rightHandType) { |
| /* |
| * int i = (int) 1.0 is okay |
| * Integer i = (int) 1.0 is okay |
| * int i = (int) Double.valueOf(1.0) is not |
| */ |
| if (isNumber(leftHandType) && isNumber(rightHandType) |
| && (rightHandType.isPrimitive() != null)) { |
| return true; // They will be cast into submission |
| } |
| |
| return false; |
| } |
| |
| private boolean isNumber(JType t) { |
| JClassType numberType = types.findType(Number.class.getCanonicalName()); |
| |
| JClassType asClass = t.isClass(); |
| if (asClass != null) { |
| return numberType.isAssignableFrom(asClass); |
| } |
| |
| JPrimitiveType asPrimitive = t.isPrimitive(); |
| if (asPrimitive != null) { |
| JClassType autoboxed = types.findType(asPrimitive.getQualifiedBoxedSourceName()); |
| return numberType.isAssignableFrom(autoboxed); |
| } |
| |
| return false; |
| } |
| |
| private void logTypeMismatch(JType leftHandType, JType rightHandType, |
| MonitoredLogger logger) { |
| logger.error("Returns %s, can't be used as %s", rightHandType, leftHandType); |
| } |
| } |