2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
19 * Modifications copyright (c) 2019 Nokia
20 * ================================================================================
22 package org.openecomp.sdc.be.components.impl;
24 import static java.util.stream.Collectors.toList;
26 import fj.data.Either;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.List;
32 import java.util.Map.Entry;
33 import java.util.function.BiFunction;
34 import java.util.function.Consumer;
35 import java.util.function.Function;
36 import java.util.stream.Collectors;
37 import org.apache.commons.lang3.tuple.ImmutablePair;
38 import org.openecomp.sdc.be.components.impl.ImportUtils.ResultStatusEnum;
39 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
40 import org.openecomp.sdc.be.components.impl.model.ToscaTypeImportData;
41 import org.openecomp.sdc.be.config.BeEcompErrorManager;
42 import org.openecomp.sdc.be.dao.api.ActionStatus;
43 import org.openecomp.sdc.be.datatypes.elements.ToscaTypeDataDefinition;
44 import org.openecomp.sdc.be.datatypes.tosca.ToscaDataDefinition;
45 import org.openecomp.sdc.be.impl.ComponentsUtils;
46 import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
47 import org.openecomp.sdc.be.model.DataTypeDefinition;
48 import org.openecomp.sdc.be.model.GroupTypeDefinition;
49 import org.openecomp.sdc.be.model.PolicyTypeDefinition;
50 import org.openecomp.sdc.be.model.PropertyDefinition;
51 import org.openecomp.sdc.be.model.normatives.ElementTypeEnum;
52 import org.openecomp.sdc.be.model.normatives.ToscaTypeMetadata;
53 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
54 import org.openecomp.sdc.be.model.operations.api.TypeOperations;
55 import org.openecomp.sdc.be.model.operations.impl.ModelOperation;
56 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
57 import org.openecomp.sdc.common.log.wrappers.Logger;
58 import org.openecomp.sdc.exception.ResponseFormat;
59 import org.springframework.beans.factory.annotation.Autowired;
60 import org.springframework.stereotype.Component;
61 import org.yaml.snakeyaml.Yaml;
63 @Component("commonImportManager")
64 public class CommonImportManager {
66 private static final Logger log = Logger.getLogger(CommonImportManager.class.getName());
67 private final ComponentsUtils componentsUtils;
68 private final PropertyOperation propertyOperation;
69 private final ModelOperation modelOperation;
72 public CommonImportManager(final ComponentsUtils componentsUtils,
73 final PropertyOperation propertyOperation,
74 final ModelOperation modelOperation) {
75 this.componentsUtils = componentsUtils;
76 this.propertyOperation = propertyOperation;
77 this.modelOperation = modelOperation;
80 public static void setProperties(Map<String, Object> toscaJson, Consumer<List<PropertyDefinition>> consumer) {
81 consumer.accept(getProperties(toscaJson));
84 private static List<PropertyDefinition> getProperties(Map<String, Object> toscaJson) {
85 List<PropertyDefinition> values = null;
86 Either<Map<String, PropertyDefinition>, ResultStatusEnum> properties = ImportUtils.getProperties(toscaJson);
87 if (properties.isLeft()) {
88 values = new ArrayList<>();
89 Map<String, PropertyDefinition> propertiesMap = properties.left().value();
90 if (propertiesMap != null && !propertiesMap.isEmpty()) {
91 for (Entry<String, PropertyDefinition> entry : propertiesMap.entrySet()) {
92 String propName = entry.getKey();
93 PropertyDefinition propertyDefinition = entry.getValue();
94 PropertyDefinition newPropertyDefinition = new PropertyDefinition(propertyDefinition);
95 newPropertyDefinition.setName(propName);
96 values.add(newPropertyDefinition);
103 private static <T> List<T> append(List<T> list, T value) {
108 protected void setPropertiesMap(Map<String, Object> toscaJson, Consumer<Map<String, PropertyDefinition>> consumer) {
109 final List<PropertyDefinition> properties = getProperties(toscaJson);
110 if (properties != null) {
111 Map<String, PropertyDefinition> collect = properties.stream().collect(Collectors.toMap(PropertyDefinition::getName, Function.identity()));
112 consumer.accept(collect);
116 protected <T> Either<List<T>, ActionStatus> createElementTypesFromYml(String elementTypesYml,
117 ICreateElementType<String, Map<String, Object>, T> createApi) {
118 List<T> elementTypes;
119 Map<String, Object> toscaJson = convertToFieldMap(elementTypesYml);
120 if (toscaJson == null) {
121 return Either.right(ActionStatus.INVALID_YAML_FILE);
123 elementTypes = createElementTypesFromToscaJsonMap(createApi, toscaJson);
124 return Either.left(elementTypes);
127 @SuppressWarnings("unchecked")
128 private Map<String, Object> convertToFieldMap(String elementTypesYml) {
129 Map<String, Object> toscaJson = null;
131 toscaJson = (Map<String, Object>) new Yaml().load(elementTypesYml);
132 } catch (Exception e) {
133 log.debug("Failed to yaml file {}", elementTypesYml, e);
138 protected <T extends ToscaDataDefinition> List<T> createTypesFromToscaJsonMap(BiFunction<String, Map<String, Object>, T> createApi,
139 Map<String, Object> toscaJson) {
140 List<T> elementTypes = new ArrayList<>();
141 for (Entry<String, Object> elementTypeNameDataEntry : toscaJson.entrySet()) {
142 String elementTypeName = elementTypeNameDataEntry.getKey();
143 Map<String, Object> elementTypeJsonData = (Map<String, Object>) elementTypeNameDataEntry.getValue();
144 T elementDefinition = createApi.apply(elementTypeName, elementTypeJsonData);
145 elementTypes.add(elementDefinition);
150 protected <T> List<T> createElementTypesFromToscaJsonMap(ICreateElementType<String, Map<String, Object>, T> createApi,
151 Map<String, Object> toscaJson) {
152 List<T> elementTypes = new ArrayList<>();
153 for (Entry<String, Object> elementTypeNameDataEntry : toscaJson.entrySet()) {
154 String elementTypeName = elementTypeNameDataEntry.getKey();
155 Map<String, Object> elementTypeJsonData = (Map<String, Object>) elementTypeNameDataEntry.getValue();
156 T elementDefinition = createApi.createElement(elementTypeName, elementTypeJsonData);
157 elementTypes.add(elementDefinition);
162 protected <T> Map<String, T> createElementTypesMapFromToscaJsonMap(ICreateElementType<String, Map<String, Object>, T> createApi,
163 Map<String, Object> toscaJson) {
164 Map<String, T> elementTypesMap = new HashMap<>();
165 Iterator<Entry<String, Object>> elementTypesEntryItr = toscaJson.entrySet().iterator();
166 while (elementTypesEntryItr.hasNext()) {
167 Entry<String, Object> elementTypeNameDataEntry = elementTypesEntryItr.next();
168 String elementTypeName = elementTypeNameDataEntry.getKey();
169 Map<String, Object> elementTypeJsonData = (Map<String, Object>) elementTypeNameDataEntry.getValue();
170 T elementDefinition = createApi.createElement(elementTypeName, elementTypeJsonData);
171 elementTypesMap.put(elementTypeName, elementDefinition);
173 return elementTypesMap;
176 protected <F> void setField(Map<String, Object> toscaJson, String fieldName, Consumer<F> setter) {
177 if (toscaJson.containsKey(fieldName)) {
178 F fieldValue = (F) toscaJson.get(fieldName);
179 setter.accept(fieldValue);
183 private ActionStatus convertFromStorageResponseForElementType(StorageOperationStatus status, ElementTypeEnum elementTypeEnum) {
185 switch (elementTypeEnum) {
187 ret = componentsUtils.convertFromStorageResponseForGroupType(status);
190 ret = componentsUtils.convertFromStorageResponseForPolicyType(status);
193 ret = componentsUtils.convertFromStorageResponseForDataType(status);
195 case CAPABILITY_TYPE:
196 ret = componentsUtils.convertFromStorageResponseForCapabilityType(status);
198 case INTERFACE_LIFECYCLE_TYPE:
199 ret = componentsUtils.convertFromStorageResponseForLifecycleType(status);
201 case RELATIONSHIP_TYPE:
202 ret = componentsUtils.convertFromStorageResponseForRelationshipType(status);
205 ret = componentsUtils.convertFromStorageResponse(status);
211 private <T> ResponseFormat getResponseFormatForElementType(ActionStatus actionStatus, ElementTypeEnum elementTypeEnum, T elementTypeDefinition) {
213 switch (elementTypeEnum) {
215 ret = componentsUtils.getResponseFormatByGroupType(actionStatus, (GroupTypeDefinition) elementTypeDefinition);
218 ret = componentsUtils.getResponseFormatByPolicyType(actionStatus, (PolicyTypeDefinition) elementTypeDefinition);
221 ret = componentsUtils.getResponseFormatByDataType(actionStatus, (DataTypeDefinition) elementTypeDefinition, null);
223 case CAPABILITY_TYPE:
224 ret = componentsUtils.getResponseFormatByCapabilityType(actionStatus, (CapabilityTypeDefinition) elementTypeDefinition);
227 ret = componentsUtils.getResponseFormat(actionStatus);
233 private <T extends ToscaDataDefinition> List<ImmutablePair<T, Boolean>> createTypesByDao(List<T> elementTypesToCreate,
234 TypeOperations<T> typeOperations) {
235 List<ImmutablePair<T, Boolean>> createdElementTypes = new ArrayList<>();
236 for (T newTypeDefinition : elementTypesToCreate) {
238 String typeName = newTypeDefinition.getType();
239 T existingDefinition = typeOperations.getLatestType(typeName);
240 if (existingDefinition == null /*new type*/) {
241 typeOperations.addType(newTypeDefinition);
243 if (typeOperations.isSameType(newTypeDefinition, existingDefinition)) {
244 propertyOperation.getJanusGraphGenericDao().rollback();
245 createdElementTypes.add(new ImmutablePair<>(newTypeDefinition, null));
248 typeOperations.updateType(existingDefinition, newTypeDefinition);
251 propertyOperation.getJanusGraphGenericDao().commit();
252 createdElementTypes.add(new ImmutablePair<>(newTypeDefinition, true));
253 } catch (Exception e) {
254 propertyOperation.getJanusGraphGenericDao().rollback();
255 createdElementTypes.add(new ImmutablePair<>(newTypeDefinition, false));
259 return createdElementTypes;
262 protected <T> Either<List<ImmutablePair<T, Boolean>>, ResponseFormat> createElementTypesByDao(List<T> elementTypesToCreate,
263 Function<T, Either<ActionStatus, ResponseFormat>> validator,
264 Function<T, ImmutablePair<ElementTypeEnum, String>> elementInfoGetter,
265 Function<String, Either<T, StorageOperationStatus>> elementFetcher,
266 Function<T, Either<T, StorageOperationStatus>> elementAdder,
267 BiFunction<T, T, Either<T, StorageOperationStatus>> elementUpgrader) {
269 List<ImmutablePair<T, Boolean>> createdElementTypes = new ArrayList<>();
271 Either<List<ImmutablePair<T, Boolean>>, ResponseFormat> eitherResult = Either.left(createdElementTypes);
272 Iterator<T> elementTypeItr = elementTypesToCreate.iterator();
275 while (elementTypeItr.hasNext()) {
276 T elementType = elementTypeItr.next();
277 eitherResult = handleType(elementType, validator, elementInfoGetter, elementFetcher, elementAdder, elementUpgrader)
279 .map(elem -> append(createdElementTypes, elem));
281 if (eitherResult.isRight()) {
285 if (!elementTypeItr.hasNext()) {
286 log.info("all {} were created successfully!!!", elementType);
289 } catch (Exception e) {
290 eitherResult = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
293 if (eitherResult.isLeft()) {
294 propertyOperation.getJanusGraphGenericDao().commit();
296 propertyOperation.getJanusGraphGenericDao().rollback();
303 private <T> Either<ImmutablePair<T, Boolean>, ResponseFormat> handleType(T elementType,
304 Function<T, Either<ActionStatus, ResponseFormat>> validator,
305 Function<T, ImmutablePair<ElementTypeEnum, String>> elementInfoGetter,
306 Function<String, Either<T, StorageOperationStatus>> elementFetcher,
307 Function<T, Either<T, StorageOperationStatus>> elementAdder,
308 BiFunction<T, T, Either<T, StorageOperationStatus>> elementUpgrader) {
310 final ImmutablePair<ElementTypeEnum, String> elementInfo = elementInfoGetter.apply(elementType);
311 ElementTypeEnum elementTypeEnum = elementInfo.left;
312 String elementName = elementInfo.right;
314 Either<ActionStatus, ResponseFormat> validateElementType = validator.apply(elementType);
315 if (validateElementType.isRight()) {
316 ResponseFormat responseFormat = validateElementType.right().value();
317 log.debug("Failed in validation of element type: {}. Response is {}", elementType, responseFormat.getFormattedMessage());
318 return Either.right(responseFormat);
321 log.info("send {} : {} to dao for create", elementTypeEnum, elementName);
323 Either<T, StorageOperationStatus> findElementType = elementFetcher.apply(elementName);
324 if (findElementType.isRight()) {
325 StorageOperationStatus status = findElementType.right().value();
326 log.debug("searched {} finished with result:{}", elementTypeEnum, status);
327 if (status != StorageOperationStatus.NOT_FOUND) {
328 ResponseFormat responseFormat = getResponseFormatForElementType(convertFromStorageResponseForElementType(status, elementTypeEnum),
329 elementTypeEnum, elementType);
330 return Either.right(responseFormat);
332 return addElementType(elementType, elementAdder, elementTypeEnum, elementName);
336 if (elementUpgrader != null) {
337 return updateElementType(elementType, elementUpgrader, elementTypeEnum, elementName, findElementType.left().value());
340 // mshitrit Once GroupType Versions are supported add
342 log.debug("{} : {} already exists.", elementTypeEnum, elementName);
343 return Either.left(new ImmutablePair<>(elementType, false));
349 protected <T> Either<List<ImmutablePair<T, Boolean>>, ResponseFormat> createElementTypesWithVersionByDao(List<T> elementTypesToCreate,
350 Function<T, Either<ActionStatus, ResponseFormat>> validator,
351 Function<T, ImmutablePair<ElementTypeEnum, String>> elementInfoGetter,
352 BiFunction<String, String, Either<T, StorageOperationStatus>> elementFetcher,
353 Function<T, Either<T, StorageOperationStatus>> elementAdder,
354 BiFunction<T, T, Either<T, StorageOperationStatus>> elementUpgrader,
357 List<ImmutablePair<T, Boolean>> createdElementTypes = new ArrayList<>();
359 Either<List<ImmutablePair<T, Boolean>>, ResponseFormat> eitherResult = Either.left(createdElementTypes);
360 Iterator<T> elementTypeItr = elementTypesToCreate.iterator();
363 while (elementTypeItr.hasNext()) {
364 T elementType = elementTypeItr.next();
365 eitherResult = handleTypeByDao(elementType, validator, elementInfoGetter, elementFetcher, elementAdder, elementUpgrader, modelName)
367 .map(elem -> append(createdElementTypes, elem));
369 if (eitherResult.isRight()) {
373 if (!elementTypeItr.hasNext()) {
374 log.info("all {} were created successfully!!!", elementType);
377 } catch (Exception e) {
378 eitherResult = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
381 if (eitherResult.isLeft()) {
382 propertyOperation.getJanusGraphGenericDao().commit();
384 propertyOperation.getJanusGraphGenericDao().rollback();
391 private <T> Either<ImmutablePair<T, Boolean>, ResponseFormat> handleTypeByDao(T elementType,
392 Function<T, Either<ActionStatus, ResponseFormat>> validator,
393 Function<T, ImmutablePair<ElementTypeEnum, String>> elementInfoGetter,
394 BiFunction<String, String, Either<T, StorageOperationStatus>> elementFetcher,
395 Function<T, Either<T, StorageOperationStatus>> elementAdder,
396 BiFunction<T, T, Either<T, StorageOperationStatus>> elementUpgrader,
399 final ImmutablePair<ElementTypeEnum, String> elementInfo = elementInfoGetter.apply(elementType);
400 ElementTypeEnum elementTypeEnum = elementInfo.left;
401 String elementName = elementInfo.right;
403 Either<ActionStatus, ResponseFormat> validateElementType = validator.apply(elementType);
404 if (validateElementType.isRight()) {
405 ResponseFormat responseFormat = validateElementType.right().value();
406 log.debug("Failed in validation of element type: {}. Response is {}", elementType, responseFormat.getFormattedMessage());
407 return Either.right(responseFormat);
410 log.info("send {} : {} to dao for create", elementTypeEnum, elementName);
412 Either<T, StorageOperationStatus> findElementType = elementFetcher.apply(elementName, modelName);
413 if (findElementType.isRight()) {
414 StorageOperationStatus status = findElementType.right().value();
415 log.debug("searched {} finished with result:{}", elementTypeEnum, status);
416 if (status != StorageOperationStatus.NOT_FOUND) {
417 ResponseFormat responseFormat = getResponseFormatForElementType(convertFromStorageResponseForElementType(status, elementTypeEnum),
418 elementTypeEnum, elementType);
419 return Either.right(responseFormat);
421 return addElementType(elementType, elementAdder, elementTypeEnum, elementName);
425 if (elementUpgrader != null) {
426 return updateElementType(elementType, elementUpgrader, elementTypeEnum, elementName, findElementType.left().value());
429 // mshitrit Once GroupType Versions are supported add
431 log.debug("{} : {} already exists.", elementTypeEnum, elementName);
432 return Either.left(new ImmutablePair<>(elementType, false));
438 private <T> Either<ImmutablePair<T, Boolean>, ResponseFormat> addElementType(T elementType,
439 Function<T, Either<T, StorageOperationStatus>> elementAdder,
440 ElementTypeEnum elementTypeEnum, String elementName) {
441 Either<T, StorageOperationStatus> dataModelResponse = elementAdder.apply(elementType);
443 if (dataModelResponse.isRight()) {
444 BeEcompErrorManager.getInstance().logBeFailedAddingNodeTypeError("Create {}", elementTypeEnum.name());
445 log.debug("failed to create {}: {}", elementTypeEnum, elementName);
446 if (dataModelResponse.right().value() != StorageOperationStatus.OK) {
447 ResponseFormat responseFormat = getResponseFormatForElementType(
448 convertFromStorageResponseForElementType(dataModelResponse.right().value(), elementTypeEnum), elementTypeEnum, elementType);
450 return Either.right(responseFormat);
452 return Either.left(new ImmutablePair<>(elementType, false));
455 log.debug("{} : {} was created successfully.", elementTypeEnum, elementName);
456 return Either.left(new ImmutablePair<>(elementType, true));
460 private <T> Either<ImmutablePair<T, Boolean>, ResponseFormat> updateElementType(T elementType,
461 BiFunction<T, T, Either<T, StorageOperationStatus>> elementUpgrader,
462 ElementTypeEnum elementTypeEnum, String elementName,
463 T existingElementType) {
464 Either<T, StorageOperationStatus> upgradeResponse = elementUpgrader.apply(elementType, existingElementType);
465 if (upgradeResponse.isRight()) {
466 StorageOperationStatus status = upgradeResponse.right().value();
467 if (status == StorageOperationStatus.OK) {
468 return Either.left(new ImmutablePair<>(elementType, false));
470 ResponseFormat responseFormat = getResponseFormatForElementType(
471 convertFromStorageResponseForElementType(upgradeResponse.right().value(), elementTypeEnum), elementTypeEnum, elementType);
472 return Either.right(responseFormat);
475 log.debug("{} : {} was upgraded successfully.", elementTypeEnum, elementName);
476 return Either.left(new ImmutablePair<>(elementType, true));
480 public <T extends ToscaTypeDataDefinition> Either<List<ImmutablePair<T, Boolean>>, ResponseFormat> createElementTypes(
481 ToscaTypeImportData toscaTypeImportData, BiFunction<String, String, Either<List<T>, ActionStatus>> elementTypeFromYmlCreater,
482 BiFunction<List<T>, String, Either<List<ImmutablePair<T, Boolean>>, ResponseFormat>> elementTypeDaoCreater, String modelName) {
483 Either<List<T>, ActionStatus> elementTypes = elementTypeFromYmlCreater.apply(toscaTypeImportData.getToscaTypesYml(), modelName);
486 .map(err -> componentsUtils.getResponseFormat(err, ""))
488 .map(toscaTypes -> enrichTypesWithNonToscaMetadata(toscaTypes, toscaTypeImportData.getToscaTypeMetadata()))
490 .bind(elementTypeList -> elementTypeDaoCreater.apply(elementTypeList, modelName));
493 public <T extends ToscaDataDefinition> List<ImmutablePair<T, Boolean>> createElementTypes(String toscaTypesYml,
494 BiFunction<String, Map<String, Object>, T> createApi,
495 TypeOperations<T> typeOperations) {
496 Map<String, Object> fieldMap = convertToFieldMap(toscaTypesYml);
497 if (fieldMap == null) {
498 throw new ByActionStatusComponentException(ActionStatus.INVALID_YAML_FILE);
500 List<T> elementTypes = createTypesFromToscaJsonMap(createApi, fieldMap);
501 return createTypesByDao(elementTypes, typeOperations);
504 private <T extends ToscaTypeDataDefinition> List<T> enrichTypesWithNonToscaMetadata(List<T> toscaTypes,
505 Map<String, ToscaTypeMetadata> toscaTypeMetadata) {
506 return toscaTypes.stream()
507 .map(toscaType -> setNonToscaMetaDataOnType(toscaTypeMetadata, toscaType))
511 private <T extends ToscaTypeDataDefinition> T setNonToscaMetaDataOnType(Map<String, ToscaTypeMetadata> toscaTypeMetadata, T toscaTypeDefinition) {
512 String toscaType = toscaTypeDefinition.getType();
513 ToscaTypeMetadata typeMetaData = toscaTypeMetadata.get(toscaType);
514 if (typeMetaData == null) {
515 log.debug("failing while trying to associate metadata for type {}. type not exist", toscaType);
516 throw new ByActionStatusComponentException(ActionStatus.GENERAL_ERROR);
518 toscaTypeDefinition.setIcon(typeMetaData.getIcon());
519 toscaTypeDefinition.setName(typeMetaData.getDisplayName());
520 return toscaTypeDefinition;
523 public <T> Either<List<ImmutablePair<T, Boolean>>, ResponseFormat> createElementTypes(String elementTypesYml,
524 Function<String, Either<List<T>, ActionStatus>> elementTypeFromYmlCreater,
525 Function<List<T>, Either<List<ImmutablePair<T, Boolean>>, ResponseFormat>> elementTypeDaoCreater,
526 ElementTypeEnum elementTypeEnum) {
528 Either<List<T>, ActionStatus> elementTypes = elementTypeFromYmlCreater.apply(elementTypesYml);
529 if (elementTypes.isRight()) {
530 ActionStatus status = elementTypes.right().value();
531 ResponseFormat responseFormat = getResponseFormatForElementType(status, elementTypeEnum, null);
532 return Either.right(responseFormat);
534 return elementTypeDaoCreater.apply(elementTypes.left().value());
538 public void addTypesToDefaultImports(final ElementTypeEnum elementTypeEnum, final String typesYaml, final String modelName) {
539 modelOperation.addTypesToDefaultImports(elementTypeEnum, typesYaml, modelName);
542 public void updateTypesInAdditionalTypesImport(final ElementTypeEnum elementTypeEnum, final String dataTypeYml, final String modelName) {
543 modelOperation.updateTypesInAdditionalTypesImport(elementTypeEnum, dataTypeYml, modelName);
546 public interface ICreateElementType<T1, T2, T3> {
548 T3 createElement(T1 firstArg, T2 secondArg);