-
Notifications
You must be signed in to change notification settings - Fork 1
/
FormDataAnnotationDescriptor.java
336 lines (284 loc) · 12.1 KB
/
FormDataAnnotationDescriptor.java
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
/*
* Copyright (c) 2010-2022 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
*/
package org.eclipse.scout.sdk.core.s.annotation;
import static java.util.Collections.addAll;
import static java.util.Collections.unmodifiableSet;
import java.util.LinkedHashSet;
import java.util.Set;
import org.eclipse.scout.sdk.core.model.api.IAnnotatable;
import org.eclipse.scout.sdk.core.model.api.IMember;
import org.eclipse.scout.sdk.core.model.api.IMethod;
import org.eclipse.scout.sdk.core.model.api.IType;
import org.eclipse.scout.sdk.core.s.annotation.FormDataAnnotation.DefaultSubtypeSdkCommand;
import org.eclipse.scout.sdk.core.s.annotation.FormDataAnnotation.SdkCommand;
import org.eclipse.scout.sdk.core.s.apidef.IScoutApi;
/**
* Descriptor of a form data annotation hierarchy holding all information required to create a derived resource.
*/
public class FormDataAnnotationDescriptor {
private final Set<IType> m_interfaceTypes;
private SdkCommand m_sdkCommand;
private DefaultSubtypeSdkCommand m_defaultSubtypeSdkCommand;
private int m_genericOrdinal = -1;
private IType m_formDataType;
private IType m_superType;
private IAnnotatable m_annotationOwner;
private IType m_genericOrdinalDefinitionType;
protected FormDataAnnotationDescriptor() {
m_interfaceTypes = new LinkedHashSet<>();
}
public static FormDataAnnotationDescriptor of(IType type) {
var descriptor = new FormDataAnnotationDescriptor();
if (type == null) {
return descriptor;
}
var scoutApi = type.javaEnvironment().requireApi(IScoutApi.class);
if (type.isInstanceOf(scoutApi.IFormExtension()) || type.isInstanceOf(scoutApi.IFormFieldExtension())) {
// extensions are annotated with @Data but behave like normal form fields -> bridge from @Data to @FormData
var dataAnnotation = DataAnnotationDescriptor.of(type);
dataAnnotation.ifPresent(dataAnnotationDescriptor -> {
descriptor.setAnnotationOwner(type);
descriptor.setDefaultSubtypeSdkCommand(DefaultSubtypeSdkCommand.CREATE);
descriptor.setFormDataType(dataAnnotationDescriptor.getDataType());
descriptor.setGenericOrdinal(-1);
descriptor.setSdkCommand(SdkCommand.CREATE);
descriptor.setSuperType(dataAnnotationDescriptor.getSuperDataType()
.orElseGet(() -> type.javaEnvironment().findType(scoutApi.AbstractFormFieldData()).orElse(null)));
});
}
else {
parseFormDataAnnotationRec(descriptor, type, scoutApi, true);
}
return descriptor;
}
private static void parseFormDataAnnotationRec(FormDataAnnotationDescriptor descriptorToFill, IType type, IScoutApi api, boolean isOwner) {
if (type == null) {
return;
}
var replaceAnnotationFqn = api.Replace().fqn();
var replaceAnnotationPresent = type.annotations().withName(replaceAnnotationFqn).existsAny();
var superType = type.superClass().orElse(null);
parseFormDataAnnotationRec(descriptorToFill, superType, api, replaceAnnotationPresent);
type.superInterfaces()
.forEach(superInterface -> parseFormDataAnnotationRec(descriptorToFill, superInterface, api, replaceAnnotationPresent));
if (replaceAnnotationPresent && superType != null && !superType.annotations().withName(replaceAnnotationFqn).existsAny()) {
// super type is the original field that is going to be replaced by the given type
// check whether the super type is embedded into a form field that is annotated by @FormData with SdkCommand.IGNORE.
var declaringType = superType.declaringType();
while (declaringType.isPresent()) {
var declaringTypeFormDataAnnotation = of(declaringType.orElseThrow());
if (isIgnore(declaringTypeFormDataAnnotation)) {
// super type is embedded into a ignored form field. Hence this field is ignored as well. Adjust parsed annotation.
descriptorToFill.setSdkCommand(SdkCommand.IGNORE);
break;
}
declaringType = declaringType.orElseThrow().declaringType();
}
}
// If a replace annotation is present, the original field defines the attributes of the form data. In that case these attributes can be ignored for a formData annotation on a level.
// An exception are attributes that are cumulative and may be added on any level. Those may be added even though the @Replace annotation is available.
// A field that is once marked so that a DTO should be created, can never be set to ignore again. But an ignored field may be changed to create. Afterwards it can never be set to ignore again.
// Therefore ignored fields may define all attributes and they are inherited from the first level that declares it to be created.
// Forms are excluded from this rule: If a form has a @Replace annotation, it even though may define a different dto.
var cumulativeAttribsOnly = replaceAnnotationPresent && !isIgnore(descriptorToFill) && !type.isInstanceOf(api.IForm());
fillFormDataAnnotation(type, descriptorToFill, isOwner, cumulativeAttribsOnly);
}
@SuppressWarnings("pmd:NPathComplexity")
private static void fillFormDataAnnotation(IAnnotatable element, FormDataAnnotationDescriptor descriptorToFill, boolean isOwner, boolean cumulativeAttributesOnly) {
var optFda = element.annotations().withManagedWrapper(FormDataAnnotation.class).first();
if (optFda.isEmpty()) {
return;
}
var formDataAnnotation = optFda.orElseThrow();
// value
IType dtoType = null;
if (!formDataAnnotation.isValueDefault()) {
dtoType = formDataAnnotation.value();
}
// sdk command
SdkCommand sdkCommand = null;
if (!formDataAnnotation.isSdkCommandDefault()) {
sdkCommand = formDataAnnotation.sdkCommand();
}
// subtype command
DefaultSubtypeSdkCommand subTypeCommand = null;
if (!formDataAnnotation.isDefaultSubtypeSdkCommandDefault()) {
subTypeCommand = formDataAnnotation.defaultSubtypeSdkCommand();
}
// generic ordinal
var genericOrdinal = formDataAnnotation.genericOrdinal();
// interfaces
var interfaces = formDataAnnotation.interfaces();
// default setup
if (!cumulativeAttributesOnly) {
if (dtoType != null) {
if (isOwner) {
descriptorToFill.setFormDataType(dtoType);
}
else {
descriptorToFill.setSuperType(dtoType);
}
}
if (isOwner && sdkCommand != null) {
descriptorToFill.setSdkCommand(sdkCommand);
}
if (subTypeCommand != null) {
descriptorToFill.setDefaultSubtypeSdkCommand(subTypeCommand);
}
if (genericOrdinal > -1) {
descriptorToFill.setGenericOrdinal(genericOrdinal);
if (element instanceof IType) {
descriptorToFill.setGenericOrdinalDefinitionType((IType) element);
}
else if (element instanceof IMethod) {
descriptorToFill.setGenericOrdinalDefinitionType(((IMethod) element).requireDeclaringType());
}
}
}
// always add cumulative attributes
descriptorToFill.setAnnotationOwner(element);
if (interfaces != null && interfaces.length > 0) {
descriptorToFill.addInterfaces(interfaces);
}
// correction
var isMemberType = element instanceof IMember && ((IMember) element).declaringType().isPresent();
if (isOwner && sdkCommand == SdkCommand.USE && dtoType != null && isMemberType) {
descriptorToFill.setSuperType(dtoType);
descriptorToFill.setFormDataType(null);
descriptorToFill.setSdkCommand(SdkCommand.CREATE);
}
if (element instanceof IMethod && descriptorToFill.getSdkCommand() == null) {
descriptorToFill.setSdkCommand(SdkCommand.CREATE);
}
}
public static boolean isCreate(FormDataAnnotationDescriptor descriptor) {
if (descriptor == null) {
return false;
}
return (descriptor.getSdkCommand() == SdkCommand.CREATE) || (descriptor.getSdkCommand() == null && descriptor.getDefaultSubtypeSdkCommand() == DefaultSubtypeSdkCommand.CREATE);
}
public static boolean isIgnore(FormDataAnnotationDescriptor descriptor) {
return (descriptor.getSdkCommand() == SdkCommand.IGNORE) || (descriptor.getSdkCommand() == null && descriptor.getDefaultSubtypeSdkCommand() == DefaultSubtypeSdkCommand.IGNORE);
}
public static boolean isSdkCommandDefault(FormDataAnnotationDescriptor descriptor) {
return descriptor != null && descriptor.getSdkCommand() == SdkCommand.DEFAULT;
}
public static boolean isSdkCommandCreate(FormDataAnnotationDescriptor descriptor) {
return descriptor != null && descriptor.getSdkCommand() == SdkCommand.CREATE;
}
public static boolean isSdkCommandUse(FormDataAnnotationDescriptor descriptor) {
return descriptor != null && descriptor.getSdkCommand() == SdkCommand.USE;
}
public static boolean isSdkCommandIgnore(FormDataAnnotationDescriptor descriptor) {
return descriptor != null && descriptor.getSdkCommand() == SdkCommand.IGNORE;
}
public static boolean isDefaultSubtypeSdkCommandCreate(FormDataAnnotationDescriptor descriptor) {
return descriptor != null && descriptor.getDefaultSubtypeSdkCommand() == DefaultSubtypeSdkCommand.CREATE;
}
public static boolean isDefaultSubtypeSdkCommandIgnore(FormDataAnnotationDescriptor descriptor) {
return descriptor != null && descriptor.getDefaultSubtypeSdkCommand() == DefaultSubtypeSdkCommand.IGNORE;
}
public static boolean isDefaultSubtypeSdkCommandDefault(FormDataAnnotationDescriptor descriptor) {
return descriptor != null && descriptor.getDefaultSubtypeSdkCommand() == DefaultSubtypeSdkCommand.DEFAULT;
}
/**
* @return the formDataType
*/
public IType getFormDataType() {
return m_formDataType;
}
/**
* @param formDataType
* the formDataType to set
*/
protected void setFormDataType(IType formDataType) {
m_formDataType = formDataType;
}
/**
* @return the sdkCommand
*/
public SdkCommand getSdkCommand() {
return m_sdkCommand;
}
/**
* @param sdkCommand
* the sdkCommand to set
*/
protected void setSdkCommand(SdkCommand sdkCommand) {
m_sdkCommand = sdkCommand;
}
/**
* @return the defaultSubtypeSdkCommand
*/
public DefaultSubtypeSdkCommand getDefaultSubtypeSdkCommand() {
return m_defaultSubtypeSdkCommand;
}
/**
* @param defaultSubtypeSdkCommand
* the defaultSubtypeSdkCommand to set
*/
protected void setDefaultSubtypeSdkCommand(DefaultSubtypeSdkCommand defaultSubtypeSdkCommand) {
m_defaultSubtypeSdkCommand = defaultSubtypeSdkCommand;
}
/**
* @return the genericOrdinal
*/
public int getGenericOrdinal() {
return m_genericOrdinal;
}
/**
* @param genericOrdinal
* the genericOrdinal to set
*/
protected void setGenericOrdinal(int genericOrdinal) {
m_genericOrdinal = genericOrdinal;
}
/**
* @return the superType
*/
public IType getSuperType() {
return m_superType;
}
/**
* @param superType
* the super {@link IType} to set
*/
protected void setSuperType(IType superType) {
m_superType = superType;
}
public IType getAnnotationOwnerAsType() {
if (m_annotationOwner instanceof IType) { // @FormData annotations can also be placed on Methods
return (IType) m_annotationOwner;
}
return null;
}
public IAnnotatable getAnnotationOwner() {
return m_annotationOwner;
}
protected void setAnnotationOwner(IAnnotatable annotationOwner) {
m_annotationOwner = annotationOwner;
}
protected void addInterface(IType ifc) {
m_interfaceTypes.add(ifc);
}
protected void addInterfaces(IType[] interfaces) {
addAll(m_interfaceTypes, interfaces);
}
public Set<IType> getInterfaces() {
return unmodifiableSet(m_interfaceTypes);
}
public IType getGenericOrdinalDefinitionType() {
return m_genericOrdinalDefinitionType;
}
protected void setGenericOrdinalDefinitionType(IType genericOrdinalDefinitionType) {
m_genericOrdinalDefinitionType = genericOrdinalDefinitionType;
}
}