1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
/*
* Copyright 2000-2022 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);
}
}
|