blob: 74fa09241d523d06f6f02687d7865ebcb8a88535 [file] [log] [blame]
/*
* 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);
}
}