/* * Copyright 2000-2016 Vaadin Ltd. * * 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.vaadin.server.widgetsetutils.metadata; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.TreeLogger.Type; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.JMethod; import com.google.gwt.core.ext.typeinfo.JParameterizedType; import com.google.gwt.core.ext.typeinfo.JType; import com.google.gwt.core.ext.typeinfo.NotFoundException; import com.vaadin.client.connectors.AbstractRendererConnector; import com.vaadin.client.connectors.grid.AbstractGridRendererConnector; import elemental.json.JsonValue; /** * Generates type data for renderer connectors. * <ul> * <li>Stores the return type of the overridden * {@link AbstractGridRendererConnector#getRenderer() getRenderer} method to * enable automatic creation of an instance of the proper renderer type. * <li>Stores the presentation type of the connector to enable the * {@link AbstractGridRendererConnector#decode(elemental.json.JsonValue) decode} * method to work without having to implement a "getPresentationType" method. * </ul> * * @see WidgetInitVisitor * * @since 7.4 * @author Vaadin Ltd */ public class RendererVisitor extends TypeVisitor { @Override public void visitConnector(TreeLogger logger, JClassType type, ConnectorBundle bundle) throws UnableToCompleteException { if (ConnectorBundle.isConnectedRendererConnector(type)) { doRendererType(logger, type, bundle); doPresentationType(logger, type, bundle); } } private static void doRendererType(TreeLogger logger, JClassType type, ConnectorBundle bundle) throws UnableToCompleteException { // The class in which createRenderer is implemented JClassType createRendererClass = ConnectorBundle .findInheritedMethod(type, "createRenderer").getEnclosingType(); // Needs GWT constructor if createRenderer is not overridden String connectorSrcName = createRendererClass.getQualifiedSourceName(); if (isAbstractRendererConnector(connectorSrcName)) { // createRenderer not overridden JMethod getRenderer = ConnectorBundle.findInheritedMethod(type, "getRenderer"); String rendererSrcName = getRenderer.getEnclosingType() .getQualifiedSourceName(); if (isAbstractRendererConnector(rendererSrcName)) { // getRenderer not overridden logger.log(Type.ERROR, type.getQualifiedSourceName() + " must override either createRenderer or getRenderer"); throw new UnableToCompleteException(); } JClassType rendererType = getRenderer.getReturnType().isClass(); bundle.setNeedsGwtConstructor(rendererType); // Also needs renderer type to find the right GWT constructor bundle.setNeedsReturnType(type, getRenderer); logger.log(Type.DEBUG, "Renderer type of " + type + " is " + rendererType); } } private static void doPresentationType(TreeLogger logger, JClassType type, ConnectorBundle bundle) throws UnableToCompleteException { JType presentationType = getPresentationType(type, logger); bundle.setPresentationType(type, presentationType); if (!hasCustomDecodeMethod(type, logger)) { bundle.setNeedsSerialize(presentationType); } logger.log(Type.DEBUG, "Presentation type of " + type + " is " + presentationType); } private static boolean hasCustomDecodeMethod(JClassType type, TreeLogger logger) throws UnableToCompleteException { try { JMethod decodeMethod = ConnectorBundle.findInheritedMethod(type, "decode", type.getOracle().getType(JsonValue.class.getName())); if (decodeMethod == null) { throw new NotFoundException(); } String decodeSrcName = decodeMethod.getEnclosingType() .getQualifiedSourceName(); return !isAbstractRendererConnector(decodeSrcName); } catch (NotFoundException e) { logger.log(Type.ERROR, "Can't find decode method for renderer " + type, e); throw new UnableToCompleteException(); } } private static JType getPresentationType(JClassType type, TreeLogger logger) throws UnableToCompleteException { JClassType originalType = type; while (type != null) { String typeBinName = type.getQualifiedBinaryName(); if (isAbstractRendererConnector(typeBinName)) { JParameterizedType parameterized = type.isParameterized(); if (parameterized == null) { logger.log(Type.ERROR, type.getQualifiedSourceName() + " must define the generic parameter of the inherited " + AbstractRendererConnector.class.getSimpleName()); throw new UnableToCompleteException(); } return parameterized.getTypeArgs()[0]; } type = type.getSuperclass(); } throw new IllegalArgumentException("The type " + originalType.getQualifiedSourceName() + " does not extend " + AbstractRendererConnector.class.getName()); } private static boolean isAbstractRendererConnector( String connectorSrcName) { return connectorSrcName .equals(AbstractRendererConnector.class.getName()) || connectorSrcName .equals(ConnectorBundle.OLD_RENDERER_CONNECTOR_NAME); } }