ibessonov commented on a change in pull request #366:
URL: https://github.com/apache/ignite-3/pull/366#discussion_r739135292
##########
File path:
modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationUtil.java
##########
@@ -637,125 +453,431 @@ public static boolean hasDefault(Field field) {
}
/**
- * Get the default value of a {@link Value}.
+ * Collect all configuration schemes with {@link ConfigurationRoot},
{@link Config} or {@link PolymorphicConfig}
Review comment:
Please either use "schemes" or "schemas". I don't like inconsistencies
that we have right now
##########
File path:
modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGenerator.java
##########
@@ -555,46 +801,73 @@ else if (isNamedConfigValue(schemaField))
returnType
);
- // result = this.field;
- viewMtd.getBody().append(viewMtd.getThis().getField(fieldDef));
+ BytecodeBlock bytecodeBlock = new BytecodeBlock();
+
+ // result = this.field; OR this.field.field.
+ bytecodeBlock.append(getFieldCodeFun.apply(viewMtd));
- // result = Box.boxValue(result); // Unboxing.
if (schemaFieldType.isPrimitive()) {
- viewMtd.getBody().invokeVirtual(
+ // result = Box.boxValue(result); // Unboxing.
+ bytecodeBlock.invokeVirtual(
box(schemaFieldType),
schemaFieldType.getSimpleName() + "Value",
schemaFieldType
);
}
-
- // retuls = result.clone();
- if (schemaFieldType.isArray())
- viewMtd.getBody().invokeVirtual(schemaFieldType, "clone",
Object.class).checkCast(schemaFieldType);
+ else if (schemaFieldType.isArray()) {
+ // result = result.clone();
+ bytecodeBlock.invokeVirtual(schemaFieldType, "clone",
Object.class).checkCast(schemaFieldType);
+ }
+ else if (isPolymorphicConfig(schemaFieldType) &&
isConfigValue(schemaField)) {
+ // result = result.specificNode();
+ bytecodeBlock.invokeVirtual(SPECIFIC_NODE_MTD);
+ }
// return result;
- viewMtd.getBody().ret(schemaFieldType);
+ bytecodeBlock.ret(schemaFieldType);
+
+ if (getPolymorphicTypeIdFieldFun != null) {
+ assert
isPolymorphicConfigInstance(schemaField.getDeclaringClass()) : schemaField;
+
+ // tmpVar = this.typeId; OR this.field.typeId.
+ BytecodeExpression getPolymorphicTypeIdField =
getPolymorphicTypeIdFieldFun.apply(viewMtd);
+ String polymorphicInstanceId =
polymorphicInstanceId(schemaField.getDeclaringClass());
+
+ // if ("first".equals(tmpVar)) return result;
+ // else throw Ex;
+ viewMtd.getBody().append(
+ new IfStatement()
+
.condition(constantString(polymorphicInstanceId).invoke(STRING_EQUALS_MTD,
getPolymorphicTypeIdField))
+ .ifTrue(bytecodeBlock)
+
.ifFalse(throwException(ConfigurationWrongPolymorphicTypeIdException.class,
getPolymorphicTypeIdField))
Review comment:
Shouldn't it be the value of the field, not the name? Name says nothing
useful
##########
File path:
modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationFlattener.java
##########
@@ -120,6 +126,11 @@
if (oldNode == null)
visitAsymmetricInnerNode(newNode, false);
+ else if (oldNode.schemaType() != newNode.schemaType()) {
+ visitAsymmetricInnerNode(oldNode, true);
Review comment:
I don't see a comment with an explanation here, maybe it should be
extracted into a method?
##########
File path:
modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationUtil.java
##########
@@ -150,6 +161,10 @@ else if (node == null)
throw new KeyNotFoundException(
"Configuration value '" + join(keys.subList(0, i))
+ "' has not been found"
);
+ } catch (ConfigurationWrongPolymorphicTypeIdException e) {
+ throw new WrongPolymorphicTypeIdException(
+ "Polymorphic configuration type is not correct: "
+ e.getMessage()
Review comment:
should you also pass a cause?
##########
File path:
modules/configuration/src/test/java/org/apache/ignite/internal/configuration/hocon/HoconConverterTest.java
##########
@@ -185,15 +227,16 @@ public void before() throws Exception {
configuration.change(cfg -> cfg
.changePrimitivesList(list ->
list.namedListKeys().forEach(list::delete))
.changeArraysList(list ->
list.namedListKeys().forEach(list::delete))
+ .changePolymorphicCfg(c -> {})
).get(1, SECONDS);
}
/** */
@Test
public void toHoconBasic() {
- assertEquals("root{arraysList=[],primitivesList=[]}",
asHoconStr(List.of()));
+
assertEquals("root{arraysList=[],polymorphicCfg{longVal=0,typeId=first},primitivesList=[]}",
asHoconStr(List.of()));
Review comment:
I wander if we could render these values in a proper order
##########
File path:
modules/configuration/src/test/java/org/apache/ignite/internal/configuration/util/ConfigurationUtilTest.java
##########
@@ -614,34 +656,176 @@ void testCollectSchemas() {
assertThrows(IllegalArgumentException.class, () ->
collectSchemas(List.of(Object.class)));
+ Set<Class<?>> schemas = Set.of(
+ LocalFirstConfigurationSchema.class,
+ InternalConfigurationSchema.class,
+ PolymorphicConfigurationSchema.class
+ );
+
+ assertEquals(schemas, collectSchemas(schemas));
+
assertEquals(
- Set.of(LocalFirstConfigurationSchema.class,
SimpleConfigurationSchema.class),
- collectSchemas(List.of(LocalFirstConfigurationSchema.class,
SimpleConfigurationSchema.class))
+ Set.of(
+ InternalRootConfigurationSchema.class,
+ PolymorphicRootConfigurationSchema.class,
+ InternalConfigurationSchema.class,
+ PolymorphicConfigurationSchema.class
+ ),
+ collectSchemas(List.of(InternalRootConfigurationSchema.class,
PolymorphicRootConfigurationSchema.class))
+ );
+ }
+
+ /** */
+ @Test
+ void testPolymorphicSchemaExtensions() {
+ assertTrue(polymorphicSchemaExtensions(List.of()).isEmpty());
+
+ assertThrows(IllegalArgumentException.class, () ->
polymorphicSchemaExtensions(List.of(Object.class)));
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
polymorphicSchemaExtensions(List.of(LocalFirstConfigurationSchema.class))
+ );
+
+ Set<Class<?>> extensions = Set.of(
+ FirstPolymorphicInstanceConfigurationSchema.class,
+ SecondPolymorphicInstanceConfigurationSchema.class
);
assertEquals(
- Set.of(SimpleRootConfigurationSchema.class,
SimpleConfigurationSchema.class),
- collectSchemas(List.of(SimpleRootConfigurationSchema.class))
+ Map.of(PolymorphicConfigurationSchema.class, extensions),
+ polymorphicSchemaExtensions(extensions)
);
}
+ /** */
+ @Test
+ void testCompressDeletedEntries() {
+ Map<String, String> containsNullLeaf = new HashMap<>();
+
+ containsNullLeaf.put("first", "1");
+ containsNullLeaf.put("second", null);
+
+ Map<String, String> deletedNamedListElement = new HashMap<>();
+
+ deletedNamedListElement.put("third", null);
+ deletedNamedListElement.put(NAME, null);
+
+ Map<String, Object> regular = new HashMap<>();
+
+ regular.put("strVal", "foo");
+ regular.put("intVal", 10);
+
+ Map<String, Object> prefixMap = new HashMap<>();
+
+ prefixMap.put("0", containsNullLeaf);
+ prefixMap.put("1", deletedNamedListElement);
+ prefixMap.put("2", regular);
+
+ Map<String, Object> exp = new HashMap<>();
+
+ exp.put("0", Map.of("first", "1"));
+ exp.put("1", null);
+ exp.put("2", Map.of("strVal", "foo", "intVal", 10));
+
+ compressDeletedEntries(prefixMap);
+
+ assertEquals(exp, prefixMap);
+ }
+
+ /** */
+ @Test
+ void testFlattenedMapPolymorphicConfig() {
+ InnerNode polymorphicRootInnerNode =
newNodeInstance(PolymorphicRootConfigurationSchema.class);
+
+ addDefaults(polymorphicRootInnerNode);
+
+ RootKey<?, ?> rootKey = PolymorphicRootConfiguration.KEY;
+
+ SuperRoot superRoot = new SuperRoot(key -> null, Map.of(rootKey,
polymorphicRootInnerNode));
+
+ Map<String, Serializable> act = flattenedMap(
+ superRoot,
+ rootKey,
+ node -> {
+ ((PolymorphicRootChange)node).changePolymorphicSubCfg(c ->
c.convert(SecondPolymorphicInstanceChange.class));
+
+ addDefaults(node);
Review comment:
Please remove this "addDefault", I believe it's not required
##########
File path:
modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationUtil.java
##########
@@ -150,6 +161,10 @@ else if (node == null)
throw new KeyNotFoundException(
"Configuration value '" + join(keys.subList(0, i))
+ "' has not been found"
);
+ } catch (ConfigurationWrongPolymorphicTypeIdException e) {
Review comment:
```suggestion
}
catch (ConfigurationWrongPolymorphicTypeIdException e) {
```
##########
File path:
modules/core/src/test/java/org/apache/ignite/internal/util/CollectionUtilsTest.java
##########
@@ -51,7 +53,7 @@ void testConcatIterables() {
/** */
@Test
- void testUnion() {
+ void testSetUnion() {
Review comment:
Can you please explain, why you need explicit arrays allocations in this
test if "union" is vararg?
##########
File path:
modules/configuration/src/test/java/org/apache/ignite/internal/configuration/ConfigurationRegistryTest.java
##########
@@ -50,7 +55,51 @@ void testValidationInternalConfigurationExtensions() {
List.of(FirstRootConfiguration.KEY, SecondRootConfiguration.KEY),
Map.of(),
new TestConfigurationStorage(LOCAL),
- List.of(ExtendedFirstRootConfigurationSchema.class)
+ List.of(ExtendedFirstRootConfigurationSchema.class),
+ List.of()
+ );
+ }
+
+ /** */
+ @Test
+ void testValidationPolymorphicConfigurationExtensions() {
+ // There is a polymorphic extension that is missing from the schema.
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new ConfigurationRegistry(
+ List.of(ThirdRootConfiguration.KEY),
+ Map.of(),
+ new TestConfigurationStorage(LOCAL),
+ List.of(),
+ List.of(Second0PolymorphicConfigurationSchema.class)
+ )
+ );
+
+ // There are two polymorphic extensions with the same id.
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new ConfigurationRegistry(
+ List.of(ThirdRootConfiguration.KEY),
+ Map.of(),
+ new TestConfigurationStorage(LOCAL),
+ List.of(),
+ List.of(First0PolymorphicConfigurationSchema.class,
ErrorFirst0PolymorphicConfigurationSchema.class)
+ )
+ );
+
+ // Check that everything is fine.
+ new ConfigurationRegistry(
Review comment:
You should probably stop this instance, because it has a thread pool in
it
##########
File path:
modules/configuration/src/test/java/org/apache/ignite/internal/configuration/util/ConfigurationUtilTest.java
##########
@@ -614,34 +656,176 @@ void testCollectSchemas() {
assertThrows(IllegalArgumentException.class, () ->
collectSchemas(List.of(Object.class)));
+ Set<Class<?>> schemas = Set.of(
+ LocalFirstConfigurationSchema.class,
+ InternalConfigurationSchema.class,
+ PolymorphicConfigurationSchema.class
+ );
+
+ assertEquals(schemas, collectSchemas(schemas));
+
assertEquals(
- Set.of(LocalFirstConfigurationSchema.class,
SimpleConfigurationSchema.class),
- collectSchemas(List.of(LocalFirstConfigurationSchema.class,
SimpleConfigurationSchema.class))
+ Set.of(
+ InternalRootConfigurationSchema.class,
+ PolymorphicRootConfigurationSchema.class,
+ InternalConfigurationSchema.class,
+ PolymorphicConfigurationSchema.class
+ ),
+ collectSchemas(List.of(InternalRootConfigurationSchema.class,
PolymorphicRootConfigurationSchema.class))
+ );
+ }
+
+ /** */
+ @Test
+ void testPolymorphicSchemaExtensions() {
+ assertTrue(polymorphicSchemaExtensions(List.of()).isEmpty());
+
+ assertThrows(IllegalArgumentException.class, () ->
polymorphicSchemaExtensions(List.of(Object.class)));
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
polymorphicSchemaExtensions(List.of(LocalFirstConfigurationSchema.class))
+ );
+
+ Set<Class<?>> extensions = Set.of(
+ FirstPolymorphicInstanceConfigurationSchema.class,
+ SecondPolymorphicInstanceConfigurationSchema.class
);
assertEquals(
- Set.of(SimpleRootConfigurationSchema.class,
SimpleConfigurationSchema.class),
- collectSchemas(List.of(SimpleRootConfigurationSchema.class))
+ Map.of(PolymorphicConfigurationSchema.class, extensions),
+ polymorphicSchemaExtensions(extensions)
);
}
+ /** */
+ @Test
+ void testCompressDeletedEntries() {
+ Map<String, String> containsNullLeaf = new HashMap<>();
+
+ containsNullLeaf.put("first", "1");
+ containsNullLeaf.put("second", null);
+
+ Map<String, String> deletedNamedListElement = new HashMap<>();
+
+ deletedNamedListElement.put("third", null);
+ deletedNamedListElement.put(NAME, null);
+
+ Map<String, Object> regular = new HashMap<>();
+
+ regular.put("strVal", "foo");
+ regular.put("intVal", 10);
+
+ Map<String, Object> prefixMap = new HashMap<>();
+
+ prefixMap.put("0", containsNullLeaf);
+ prefixMap.put("1", deletedNamedListElement);
+ prefixMap.put("2", regular);
+
+ Map<String, Object> exp = new HashMap<>();
+
+ exp.put("0", Map.of("first", "1"));
+ exp.put("1", null);
+ exp.put("2", Map.of("strVal", "foo", "intVal", 10));
+
+ compressDeletedEntries(prefixMap);
+
+ assertEquals(exp, prefixMap);
+ }
+
+ /** */
+ @Test
+ void testFlattenedMapPolymorphicConfig() {
+ InnerNode polymorphicRootInnerNode =
newNodeInstance(PolymorphicRootConfigurationSchema.class);
+
+ addDefaults(polymorphicRootInnerNode);
+
+ RootKey<?, ?> rootKey = PolymorphicRootConfiguration.KEY;
+
+ SuperRoot superRoot = new SuperRoot(key -> null, Map.of(rootKey,
polymorphicRootInnerNode));
+
+ Map<String, Serializable> act = flattenedMap(
+ superRoot,
+ rootKey,
+ node -> {
+ ((PolymorphicRootChange)node).changePolymorphicSubCfg(c ->
c.convert(SecondPolymorphicInstanceChange.class));
+
+ addDefaults(node);
Review comment:
Maybe others as well, please check them
##########
File path:
modules/configuration-api/src/main/java/org/apache/ignite/configuration/PolymorphicInstance.java
##########
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.configuration;
+
+import org.apache.ignite.configuration.annotation.PolymorphicConfigInstance;
+
+/**
+ * Marker interface for {@code *Node} classes whose schemas are marked with
annotation {@link PolymorphicConfigInstance},
Review comment:
Would it be better to rename it to PolymorphicNode?
Anyway, this interface is designed for VIEW / CHANGE interfaces, not the
node itself, right?
##########
File path:
modules/configuration-annotation-processor/src/integrationTest/resources/org/apache/ignite/internal/configuration/processor/polymorphic/ErrorPolymorphicInstance0ConfigurationSchema.java
##########
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.internal.configuration.processor.polymorphic;
+
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
+import org.apache.ignite.configuration.annotation.PolymorphicConfigInstance;
+
+/**
+ * Class cannot have {@link PolymorphicConfigInstance} and {@link
ConfigurationRoot}.
Review comment:
Why tho? Maybe we should revisit this option in the future
##########
File path:
modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGenerator.java
##########
@@ -1244,196 +1866,932 @@ private static String capitalize(String name) {
}
/**
- * Create bytecode blocks that invokes of {@link ConfigurationVisitor}'s
methods for
- * {@link InnerNode#traverseChildren(ConfigurationVisitor, boolean)}.
+ * Get interfaces for {@link InnerNode} definition for a configuration
schema.
*
- * @param schemaFields Fields of the schema.
- * @param fieldDefs Definitions for all fields in {@code schemaFields}.
- * @param traverseChildrenMtd Method definition {@link
InnerNode#traverseChildren(ConfigurationVisitor, boolean)}
- * defined in {@code *Node} class.
- * @return Created bytecode blocks that invokes of {@link
ConfigurationVisitor}'s methods for fields.
+ * @param schemaClass Configuration schema class.
+ * @param schemaExtensions Internal extensions of the configuration schema.
+ * @return Interfaces for {@link InnerNode} definition for a configuration
schema.
+ */
+ private static ParameterizedType[] nodeClassInterfaces(Class<?>
schemaClass, Set<Class<?>> schemaExtensions) {
+ Collection<ParameterizedType> res = new ArrayList<>();
+
+ for (Class<?> cls : concat(List.of(schemaClass), schemaExtensions)) {
+ res.add(typeFromJavaClassName(viewClassName(cls)));
+ res.add(typeFromJavaClassName(changeClassName(cls)));
+ }
+
+ if (isPolymorphicConfigInstance(schemaClass))
+ res.add(type(PolymorphicInstance.class));
+
+ return res.toArray(ParameterizedType[]::new);
+ }
+
+ /**
+ * Get interfaces for {@link DynamicConfiguration} definition for a
configuration schema.
+ *
+ * @param schemaClass Configuration schema class.
+ * @param schemaExtensions Internal extensions of the configuration schema.
+ * @return Interfaces for {@link DynamicConfiguration} definition for a
configuration schema.
*/
- private static Collection<BytecodeNode> invokeVisitForTraverseChildren(
+ private ParameterizedType[] configClassInterfaces(Class<?> schemaClass,
Set<Class<?>> schemaExtensions) {
+ List<ParameterizedType> result = Stream.concat(Stream.of(schemaClass),
schemaExtensions.stream())
+ .map(cls -> typeFromJavaClassName(configurationClassName(cls)))
+ .collect(toCollection(ArrayList::new));
+
+ if (schemasInfo.get(schemaClass).direct)
+ result.add(type(DirectConfigurationProperty.class));
+
+ return result.toArray(new ParameterizedType[0]);
+ }
+
+ /**
+ * Add {@link DynamicConfiguration#configType} method implementation to
the class. It looks like the following code:
+ * <pre><code>
+ * public Class configType() {
+ * return RootConfiguration.class;
+ * }
+ * </code></pre>
+ * @param classDef Class definition.
+ * @param clazz Definition of the configuration interface, for example
{@code RootConfiguration}.
+ */
+ private static void addCfgImplConfigTypeMethod(ClassDefinition classDef,
ParameterizedType clazz) {
+ classDef.declareMethod(of(PUBLIC), "configType", type(Class.class))
+ .getBody()
+ .append(constantClass(clazz))
+ .retObject();
+ }
+
+ /**
+ * Create a {@code *Node} for the polymorphic configuration instance
schema.
+ *
+ * @param schemaClass Polymorphic configuration schema (parent).
+ * @param polymorphicExtension Polymorphic configuration instance schema
(child).
+ * @param schemaInnerNodeClassDef {@link InnerNode} definition for the
polymorphic configuration schema {@code schemaClass}.
+ * @param schemaFields Schema fields of polymorphic configuration {@code
schemaClass}.
+ * @param polymorphicFields Schema fields of a polymorphic configuration
instance {@code polymorphicExtension}.
+ */
+ private ClassDefinition createPolymorphicExtensionNodeClass(
+ Class<?> schemaClass,
+ Class<?> polymorphicExtension,
+ ClassDefinition schemaInnerNodeClassDef,
Collection<Field> schemaFields,
+ Collection<Field> polymorphicFields
+ ) {
+ SchemaClassesInfo schemaClassInfo = schemasInfo.get(schemaClass);
+ SchemaClassesInfo polymorphicExtensionClassInfo =
schemasInfo.get(polymorphicExtension);
+
+ // Node class definition.
+ ClassDefinition classDef = new ClassDefinition(
+ of(PUBLIC, FINAL),
+ internalName(polymorphicExtensionClassInfo.nodeClassName),
+ type(Object.class),
+ nodeClassInterfaces(polymorphicExtension, Set.of())
+ );
+
+ // private final ParentNode this$0;
+ FieldDefinition parentInnerNodeFieldDef = classDef.declareField(
+ of(PRIVATE, FINAL),
+ "this$0",
+ typeFromJavaClassName(schemaClassInfo.nodeClassName)
+ );
+
+ // Constructor.
+ MethodDefinition constructorMtd = classDef.declareConstructor(
+ of(PUBLIC),
+ arg("delegate",
typeFromJavaClassName(schemaClassInfo.nodeClassName))
+ );
+
+ Variable delegateVar =
constructorMtd.getScope().getVariable("delegate");
+
+ // Constructor body.
+ constructorMtd.getBody()
+ .append(constructorMtd.getThis())
+ .invokeConstructor(Object.class)
+ .append(constructorMtd.getThis().setField(
+ parentInnerNodeFieldDef,
+ delegateVar
+ ))
+ .ret();
+
+ Map<String, FieldDefinition> fieldDefs =
schemaInnerNodeClassDef.getFields().stream()
+ .collect(toMap(FieldDefinition::getName, identity()));
+
+ // Creates view and change methods for parent schema.
+ for (Field schemaField : schemaFields) {
+ FieldDefinition schemaFieldDef =
fieldDefs.get(fieldName(schemaField));
+
+ addNodeViewMethod(
+ classDef,
+ schemaField,
+ viewMtd -> getThisFieldCode(viewMtd, parentInnerNodeFieldDef,
schemaFieldDef),
+ null
+ );
+
+ // Read only.
+ if (isPolymorphicId(schemaField))
+ continue;
+
+ MethodDefinition changeMtd = addNodeChangeMethod(
+ classDef,
+ schemaField,
+ polymorphicExtensionClassInfo.nodeClassName,
+ parentInnerNodeFieldDef,
+ schemaFieldDef
+ );
+
+ addNodeChangeBridgeMethod(classDef,
schemaClassInfo.changeClassName, changeMtd);
+ }
+
+ FieldDefinition polymorphicTypeIdFieldDef =
fieldDefs.get(polymorphicIdField(schemaClass).getName());
+
+ // Creates view and change methods for specific polymorphic instance
schema.
+ for (Field polymorphicField : polymorphicFields) {
+ FieldDefinition polymorphicFieldDef =
fieldDefs.get(fieldName(polymorphicField));
+
+ addNodeViewMethod(
+ classDef,
+ polymorphicField,
+ viewMtd -> getThisFieldCode(viewMtd, parentInnerNodeFieldDef,
polymorphicFieldDef),
+ viewMtd -> getThisFieldCode(viewMtd, parentInnerNodeFieldDef,
polymorphicTypeIdFieldDef)
+ );
+
+ MethodDefinition changeMtd = addNodeChangeMethod(
+ classDef,
+ polymorphicField,
+ polymorphicExtensionClassInfo.nodeClassName,
+ parentInnerNodeFieldDef,
+ polymorphicFieldDef
+ );
+
+ addNodeChangeBridgeMethod(classDef,
polymorphicExtensionClassInfo.changeClassName, changeMtd);
+ }
+
+ ParameterizedType returnType =
typeFromJavaClassName(schemaClassInfo.changeClassName);
+
+ // Creates Node#convert.
+ MethodDefinition convertMtd = classDef.declareMethod(
+ of(PUBLIC),
+ CONVERT_MTD_NAME,
+ returnType,
+ arg("changeClass", Class.class)
+ );
+
+ // return this.this$0.convert(changeClass);
+ convertMtd.getBody()
+ .append(getThisFieldCode(convertMtd, parentInnerNodeFieldDef))
+ .append(convertMtd.getScope().getVariable("changeClass"))
+ .invokeVirtual(schemaInnerNodeClassDef.getType(),
CONVERT_MTD_NAME, returnType, type(Class.class))
+ .retObject();
+
+ return classDef;
+ }
+
+ /**
+ * Create a {@code *CfgImpl} for the polymorphic configuration instance
schema.
+ *
+ * @param schemaClass Polymorphic configuration schema (parent).
+ * @param polymorphicExtension Polymorphic configuration instance schema
(child).
+ * @param schemaCfgImplClassDef {@link DynamicConfiguration} definition
for the polymorphic configuration schema {@code schemaClass}.
+ * @param schemaFields Schema fields of polymorphic configuration {@code
schemaClass}.
+ * @param polymorphicFields Schema fields of a polymorphic configuration
instance {@code polymorphicExtension}.
+ */
+ private ClassDefinition createPolymorphicExtensionCfgImplClass(
+ Class<?> schemaClass,
+ Class<?> polymorphicExtension,
+ ClassDefinition schemaCfgImplClassDef,
+ Collection<Field> schemaFields,
+ Collection<Field> polymorphicFields
+ ) {
+ SchemaClassesInfo schemaClassInfo = schemasInfo.get(schemaClass);
+ SchemaClassesInfo polymorphicExtensionClassInfo =
schemasInfo.get(polymorphicExtension);
+
+ Class<?> superClass = schemaClassInfo.direct ||
polymorphicExtensionClassInfo.direct
+ ? DirectConfigurationTreeWrapper.class :
ConfigurationTreeWrapper.class;
+
+ // Configuration impl class definition.
+ ClassDefinition classDef = new ClassDefinition(
+ of(PUBLIC, FINAL),
+ internalName(polymorphicExtensionClassInfo.cfgImplClassName),
+ type(superClass),
+ configClassInterfaces(polymorphicExtension, Set.of())
+ );
+
+ // private final ParentCfgImpl this$0;
+ FieldDefinition parentCfgImplFieldDef = classDef.declareField(
+ of(PRIVATE, FINAL),
+ "this$0",
+ typeFromJavaClassName(schemaClassInfo.cfgImplClassName)
+ );
+
+ // Constructor.
+ MethodDefinition constructorMtd = classDef.declareConstructor(
+ of(PUBLIC),
+ arg("delegate",
typeFromJavaClassName(schemaClassInfo.cfgImplClassName))
+ );
+
+ Variable delegateVar =
constructorMtd.getScope().getVariable("delegate");
+
+ // Constructor body.
+ // super(parent);
+ // this.this$0 = parent;
+ constructorMtd.getBody()
+ .append(constructorMtd.getThis())
+ .append(delegateVar)
+ .invokeConstructor(superClass, ConfigurationTree.class)
+ .append(constructorMtd.getThis().setField(
+ parentCfgImplFieldDef,
+ delegateVar
+ ))
+ .ret();
+
+ Map<String, FieldDefinition> fieldDefs =
schemaCfgImplClassDef.getFields().stream()
+ .collect(toMap(FieldDefinition::getName, identity()));
+
+ for (Field schemaField : concat(schemaFields, polymorphicFields)) {
+ addConfigurationImplGetMethod(
+ classDef,
+ schemaField,
+ parentCfgImplFieldDef,
+ fieldDefs.get(fieldName(schemaField))
+ );
+ }
+
+ return classDef;
+ }
+
+ /**
+ * Adds a {@link InnerNode#specificNode} override for the polymorphic
configuration case.
+ *
+ * @param classDef Definition of a polymorphic
configuration class (parent).
+ * @param polymorphicExtensions Polymorphic configuration instance
schemas (children).
+ * @param polymorphicTypeIdFieldDef Identification field for the
polymorphic configuration instance.
+ */
+ private void addNodeSpecificNodeMethod(
+ ClassDefinition classDef,
+ Set<Class<?>> polymorphicExtensions,
+ FieldDefinition polymorphicTypeIdFieldDef
+ ) {
+ MethodDefinition specificNodeMtd = classDef.declareMethod(
+ of(PUBLIC),
+ SPECIFIC_NODE_MTD.getName(),
+ type(Object.class)
+ );
+
+ StringSwitchBuilder switchBuilder =
typeIdSwitchBuilder(specificNodeMtd, polymorphicTypeIdFieldDef);
+
+ for (Class<?> polymorphicExtension : polymorphicExtensions) {
+ SchemaClassesInfo polymorphicExtensionClassInfo =
schemasInfo.get(polymorphicExtension);
+
+ switchBuilder.addCase(
+ polymorphicInstanceId(polymorphicExtension),
+ newInstance(
+
typeFromJavaClassName(polymorphicExtensionClassInfo.nodeClassName),
+ specificNodeMtd.getThis()
+ ).ret()
+ );
+ }
+
+ specificNodeMtd.getBody().append(switchBuilder.build());
+ }
+
+ /**
+ * Adds a {@code *Node#convert} for the polymorphic configuration case.
+ *
+ * @param classDef Definition of a polymorphic
configuration class {@code schemaClass}.
+ * @param schemaClass Polymorphic configuration schema
(parent).
+ * @param polymorphicExtensions Polymorphic configuration instance
schemas (children).
+ * @param changePolymorphicTypeIdMtd Method for changing the type of
polymorphic configuration.
+ */
+ private void addNodeConvertMethod(
+ ClassDefinition classDef,
+ Class<?> schemaClass,
+ Set<Class<?>> polymorphicExtensions,
+ MethodDefinition changePolymorphicTypeIdMtd
+ ) {
+ SchemaClassesInfo schemaClassInfo = schemasInfo.get(schemaClass);
+
+ MethodDefinition convertMtd = classDef.declareMethod(
+ of(PUBLIC),
+ CONVERT_MTD_NAME,
+ typeFromJavaClassName(schemaClassInfo.changeClassName),
+ arg("changeClass", Class.class)
+ );
+
+ // changeClass.getName();
+ BytecodeExpression changeClassName = convertMtd.getScope()
+ .getVariable("changeClass")
+ .invoke(CLASS_GET_NAME_MTD);
+
+ StringSwitchBuilder switchBuilder = new
StringSwitchBuilder(convertMtd.getScope())
+ .expression(changeClassName)
+
.defaultCase(throwException(ConfigurationWrongPolymorphicTypeIdException.class,
changeClassName));
+
+ for (Class<?> polymorphicExtension : polymorphicExtensions) {
+ SchemaClassesInfo polymorphicExtensionClassInfo =
schemasInfo.get(polymorphicExtension);
+
+ // this.changePolymorphicTypeId(polymorphicTypeId);
+ // return new ChildNode(this);
+ switchBuilder.addCase(
+ polymorphicExtensionClassInfo.changeClassName,
+ new BytecodeBlock()
+
.append(constantString(polymorphicInstanceId(polymorphicExtension)))
+ .invokeVirtual(changePolymorphicTypeIdMtd)
+ .append(newInstance(
+
typeFromJavaClassName(polymorphicExtensionClassInfo.nodeClassName),
+ convertMtd.getThis()
+ ))
+ .retObject()
+ );
+ }
+
+ convertMtd.getBody()
+ .append(convertMtd.getThis())
+ .append(switchBuilder.build())
+ .ret();
+ }
+
+ /**
+ * Adds a {@code Node#changeTypeId} for the polymorphic configuration case.
+ *
+ * @param classDef Definition of a polymorphic
configuration class (parent).
+ * @param fieldDefs Definitions for all fields in {@code
classDef}.
+ * @param polymorphicExtensions Polymorphic configuration instance
schemas (children).
+ * @param polymorphicFields Fields of polymorphic extensions.
+ * @param polymorphicTypeIdFieldDef Identification field for the
polymorphic configuration instance.
+ * @return Method definition.
+ */
+ private MethodDefinition addNodeChangePolymorphicTypeIdMethod(
+ ClassDefinition classDef,
Map<String, FieldDefinition> fieldDefs,
- MethodDefinition traverseChildrenMtd
+ Set<Class<?>> polymorphicExtensions,
+ Collection<Field> polymorphicFields,
+ FieldDefinition polymorphicTypeIdFieldDef
) {
- if (schemaFields.isEmpty())
- return List.of();
- else {
- return schemaFields.stream()
- .map(field -> invokeVisit(traverseChildrenMtd, field,
fieldDefs.get(field.getName())).pop())
+ MethodDefinition changePolymorphicTypeIdMtd = classDef.declareMethod(
+ of(PUBLIC),
+ changeMethodName(polymorphicTypeIdFieldDef.getName()),
+ type(void.class),
+ arg("typeId", String.class)
+ );
+
+ Variable typeIdVar =
changePolymorphicTypeIdMtd.getScope().getVariable("typeId");
+
+ StringSwitchBuilder switchBuilder = new
StringSwitchBuilder(changePolymorphicTypeIdMtd.getScope())
+ .expression(typeIdVar)
+
.defaultCase(throwException(ConfigurationWrongPolymorphicTypeIdException.class,
typeIdVar));
+
+ for (Class<?> polymorphicExtension : polymorphicExtensions) {
+ // Fields that need to be cleared when changing the type of the
polymorphic configuration instance.
+ Collection<Field> resetFields = polymorphicFields.stream()
+ .filter(f ->
!polymorphicExtension.equals(f.getDeclaringClass()))
.collect(toList());
+
+ // this.typeId = typeId;
+ BytecodeBlock codeBlock = new BytecodeBlock()
+ .append(setThisFieldCode(changePolymorphicTypeIdMtd,
typeIdVar, polymorphicTypeIdFieldDef));
+
+ // Reset fields.
+ for (Field resetField : resetFields) {
+ FieldDefinition fieldDef =
fieldDefs.get(fieldName(resetField));
+
+ if (isValue(resetField) || isConfigValue(resetField)) {
+ // this.field = null;
+ codeBlock.append(setThisFieldCode(
+ changePolymorphicTypeIdMtd,
+ constantNull(fieldDef.getType()),
+ fieldDef
+ ));
+ }
+ else {
+ // this.field = new NamedListNode<>(key, ValueNode::new,
"polymorphicIdFieldName");
+
codeBlock.append(setThisFieldCode(changePolymorphicTypeIdMtd,
newNamedListNode(resetField), fieldDef));
+ }
+ }
+
+ // ConfigurationUtil.addDefaults(this);
+ codeBlock
+ .append(changePolymorphicTypeIdMtd.getThis())
+ .invokeStatic(ADD_DEFAULTS_MTD);
+
+ switchBuilder.addCase(polymorphicInstanceId(polymorphicExtension),
codeBlock);
}
+
+ // if(typeId.equals(this.typeId)) return;
+ // else switch(typeId)...
+ changePolymorphicTypeIdMtd.getBody()
+ .append(typeIdVar)
+ .append(getThisFieldCode(changePolymorphicTypeIdMtd,
polymorphicTypeIdFieldDef))
+ .append(
+ new IfStatement()
+ .condition(new
BytecodeBlock().invokeVirtual(STRING_EQUALS_MTD))
+ .ifTrue(new BytecodeBlock().ret())
+ .ifFalse(switchBuilder.build().ret())
+ )
+ .ret();
Review comment:
Is this extra ret() really required?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]