This is an automated email from the ASF dual-hosted git repository.

ashishvijaywargiya pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git


The following commit(s) were added to refs/heads/trunk by this push:
     new dbbcf24368 Complex alias warning fix (#1391)
dbbcf24368 is described below

commit dbbcf24368d7967a50346b97497b067151c447e2
Author: Ashish Vijaywargiya <[email protected]>
AuthorDate: Tue Jun 30 16:51:38 2026 +0530

    Complex alias warning fix (#1391)
    
    PROBLEM STATEMENT - Fixing the console warning on every startup.
    -----------------
    
    ModelViewEntity.populateReverseLinks() builds ModelConversion mappings
    that drive the entity cache invalidation system. When a member entity
    row changes (e.g. OrderItem), OFBiz uses these mappings to build a probe
    key — a partial map of field→value pairs — and evicts any cached
    view-entity entries that match it.
    
    For simple aliases, the mapping is straightforward: memberField →
    viewAliasName. For complex aliases (<complex-alias> in XML — expressions
    like arithmetic, COALESCE, UPPER, aggregate functions like SUM/COUNT),
    no mapping was registered. The alias stayed in the wildcards set, which
    produced EntityOperator.WILDCARD in the probe key, meaning "match any
    cached entry regardless of this field's value." This was safe (never
    missed an eviction) but overly broad (evicted entries that did not
    actually change).
    
    A Debug.logWarning() fired for every such alias on every server startup
    — noisy and not actionable.
    
    2026-06-30 15:51:23,151 |main |ContainerLoader :151|I| Starting
    container delegator-container
    Admin socket configured on - /127.0.0.1:10523
    2026-06-30 15:51:23,153 |delegator-startup-1 |DelegatorFactoryImpl
    :33|I| Creating new delegator [default] (delegator-startup-1)
    2026-06-30 15:51:23,405 |delegator-startup-1 |ModelViewEntity :674|W|
    [TestingCryptoRawView]: Conversion for complex-alias needs to be
    implemented for cache and in-memory eval stuff to work correctly, will
    not work for alias: rawEncryptedValue
    2026-06-30 15:51:23,405 |delegator-startup-1 |ModelViewEntity :674|W|
    [TestingCryptoRawView]: Conversion for complex-alias needs to be
    implemented for cache and in-memory eval stuff to work correctly, will
    not work for alias: rawSaltedEncryptedValue
    2026-06-30 15:51:23,437 |delegator-startup-1 |ModelViewEntity :674|W|
    [OrderItemQuantityReportGroupByItem]: Conversion for complex-alias needs
    to be implemented for cache and in-memory eval stuff to work correctly,
    will not work for alias: quantityOrdered
    2026-06-30 15:51:23,437 |delegator-startup-1 |ModelViewEntity :674|W|
    [OrderItemQuantityReportGroupByItem]: Conversion for complex-alias needs
    to be implemented for cache and in-memory eval stuff to work correctly,
    will not work for alias: quantityOpen
    2026-06-30 15:51:23,437 |delegator-startup-1 |ModelViewEntity :674|W|
    [OrderItemQuantityReportGroupByProduct]: Conversion for complex-alias
    needs to be implemented for cache and in-memory eval stuff to work
    correctly, will not work for alias: quantityOrdered
    2026-06-30 15:51:23,437 |delegator-startup-1 |ModelViewEntity :674|W|
    [OrderItemQuantityReportGroupByProduct]: Conversion for complex-alias
    needs to be implemented for cache and in-memory eval stuff to work
    correctly, will not work for alias: quantityOpen
    2026-06-30 15:51:23,438 |delegator-startup-1 |ModelViewEntity :674|W|
    [OrderReportSalesGroupByProduct]: Conversion for complex-alias needs to
    be implemented for cache and in-memory eval stuff to work correctly,
    will not work for alias: quantityOrdered
    2026-06-30 15:51:23,438 |delegator-startup-1 |ModelViewEntity :674|W|
    [OrderReportSalesGroupByProduct]: Conversion for complex-alias needs to
    be implemented for cache and in-memory eval stuff to work correctly,
    will not work for alias: amount
    2026-06-30 15:51:23,439 |delegator-startup-1 |ModelViewEntity :674|W|
    [OrderItemAndShipGrpInvResAndItemSum]: Conversion for complex-alias
    needs to be implemented for cache and in-memory eval stuff to work
    correctly, will not work for alias: quantityOrdered
    2026-06-30 15:51:23,440 |delegator-startup-1 |ModelViewEntity :674|W|
    [OrderItemAndShipGrpInvResAndItemSum]: Conversion for complex-alias
    needs to be implemented for cache and in-memory eval stuff to work
    correctly, will not work for alias: totQuantityAvailable
    2026-06-30 15:51:23,442 |delegator-startup-1 |ModelViewEntity :674|W|
    [ExampleStatusDetail]: Conversion for complex-alias needs to be
    implemented for cache and in-memory eval stuff to work correctly, will
    not work for alias: statusDelay
    2026-06-30 15:51:23,444 |delegator-startup-1 |ModelViewEntity :674|W|
    [ProjectPhaseTaskActualRatedHoursView]: Conversion for complex-alias
    needs to be implemented for cache and in-memory eval stuff to work
    correctly, will not work for alias: totalRatedHours
    2026-06-30 15:51:23,458 |delegator-startup-1 |ModelReader :445|I|
    Finished loading entities; #Entities=866 #ViewEntities=322 #Fields=8990
    #Relationships=2993 #AutoRelationships=2192
    2026-06-30 15:51:23,462 |delegator-startup-1 |GenericDelegator :239|I|
    Doing entity definition check...
    
    
    A patch was proposed that called addConversion(rawField, aliasName) for
    every ComplexAliasField. The problem with that patch is that
    ModelConversion.convert() copies the raw source field value directly
    into the probe key, but the cache stores the computed alias value. These
    two values differ whenever the expression is non-trivial:
    
    - UPPER(P.firstName) → cache stores "JOHN", patch probes for "john" →
    cache entry never evicted → stale data
    - COALESCE(OI.qty, 0) → when qty is null, cache stores 0, patch probes
    for null → stale data
    - OI.unitPrice + OI.quantity → cache stores the computed sum, patch
    probes for just the unit price → stale data
    - SUM(OI.amount) → aggregate functions cannot be evaluated per row →
    completely wrong probe value
    
    The original WILDCARD behavior is safe: it evicts more than needed. The
    proposed patch behavior is unsafe: it evicts less than needed and can
    leave wrong data in the cache with no error.
    
    
    WHAT WAS FIXED
    ---------------------------------------
    
    File:
    
framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelViewEntity.java
    
    The fix registers a conversion only for the one case where it is
    provably correct: a ComplexAlias with exactly one ComplexAliasField
    member that has no function and no defaultValue applied. In this case
    the raw field value equals the computed alias value by identity, so the
    conversion is safe. All other cases — multiple members (arithmetic),
    transforming functions (UPPER, LOWER, SUM, etc.), COALESCE with a
    default — remain in the wildcards set, giving the existing
    broad-but-safe invalidation behavior.
    
    Change 1 — populateReverseLinks(): Replaced the TODO comment +
    Debug.logVerbose call with a dispatch to
    alias.getComplexAliasMember().bindAliasToConversions(alias.getName(),
    this). The log noise is eliminated and the safe subset now participates
    in cache invalidation correctly.
    
    Change 2 — ModelAlias.getComplexAliasMember(): New accessor added to
    expose the ComplexAliasMember so populateReverseLinks() can dispatch to
    it.
    
    Change 3 — ComplexAliasMember interface: Added void
    bindAliasToConversions(String aliasName, ModelViewEntity
    modelViewEntity) as a new interface method, implemented by both concrete
    classes.
    
    Change 4 — ComplexAlias.bindAliasToConversions(): Contains the safety
    gate. Delegates to the sole ComplexAliasField only when
    complexAliasMembers.size() == 1, the sole member is a ComplexAliasField
    instance, and sole.isPassThrough() returns true. All other cases are
    silent no-ops, leaving the alias in wildcards.
    
    Change 5 — ComplexAliasField.isPassThrough() and
    ComplexAliasField.bindAliasToConversions(): isPassThrough() returns true
    only when entityAlias is non-empty, field is non-empty, function is
    empty, and defaultValue is empty. bindAliasToConversions() performs the
    same getOrCreateModelConversion(entityAlias).addConversion(field,
    aliasName) call that simple aliases use, and is only reachable through
    the isPassThrough guard in ComplexAlias.
    
    
    TEST CASES
    ----------
    
    File:
    
framework/entity/src/test/java/org/apache/ofbiz/entity/model/ModelViewEntityComplexAliasTests.java
    
    No additional test infrastructure files are required. ModelConversion is
    a final non-static inner class of ModelViewEntity and cannot be mocked
    with the standard Mockito subclass mock maker. The positive test
    therefore avoids mocking ModelConversion entirely — it stubs
    getOrCreateModelConversion() to throw a sentinel exception, which proves
    the dispatch was reached and the correct entityAlias was passed, without
    needing to observe the ModelConversion internals. The addConversion()
    call is a trivial HashMap put and does not require a dedicated assertion
    here.
    
    The class is declared final (public final class
    ModelViewEntityComplexAliasTests) to satisfy the OFBiz checkstyle
    DesignForExtension rule on the setUp and tearDown lifecycle methods. All
    test method names follow the OFBiz checkstyle pattern
    ^[a-z][a-zA-Z0-9]*$ — camelCase, no underscores.
    
    11 tests in two groups, all passing. All three quality gates pass
    cleanly: checkstyleTest, check + javadoc, codenarcMain + codenarcTest.
    
    Group 1 — ComplexAliasField.isPassThrough() (6 tests):
    
      isPassThroughPlainFieldReferenceReturnsTrue
    Creates a ComplexAliasField with entityAlias="ME", field="myField", no
    function, no defaultValue. Asserts isPassThrough() returns true.
    
      isPassThroughWithFunctionReturnsFalse
    Creates a ComplexAliasField with function="upper". Asserts
    isPassThrough() returns false. A transforming function means rawField !=
    computedAlias.
    
      isPassThroughWithDefaultValueReturnsFalse
    Creates a ComplexAliasField with defaultValue="0". Asserts
    isPassThrough() returns false. COALESCE changes the value when the field
    is null.
    
      isPassThroughEmptyEntityAliasReturnsFalse
    Creates a ComplexAliasField with an empty entityAlias. Asserts
    isPassThrough() returns false. Cannot register a conversion without
    knowing which member entity to look up.
    
      isPassThroughEmptyFieldReturnsFalse
    Creates a ComplexAliasField with an empty field name. Asserts
    isPassThrough() returns false.
    
      isPassThroughLiteralValueConstantReturnsFalse
    Creates a ComplexAliasField with entityAlias="" and field="" but
    value="LITERAL_VALUE" (a SQL literal constant, not a column reference).
    Asserts isPassThrough() returns false.
    
    Group 2 — ComplexAlias.bindAliasToConversions() safety gate (5 tests):
    
    bindAliasToConversionsSinglePassThroughFieldRegistersConversion
    [POSITIVE]
    Creates a ComplexAlias with one pass-through ComplexAliasField
    (ME.myField, no function, no default). Stubs
    getOrCreateModelConversion() to throw a sentinel
    UnsupportedOperationException. Calls bindAliasToConversions("myAlias",
    mockViewEntity) and asserts the exception is thrown, confirming the
    dispatch reached the registration code. Then verifies
    getOrCreateModelConversion was called with entityAlias "ME", confirming
    the correct member entity was targeted.
    
    bindAliasToConversionsSingleFieldWithFunctionDoesNotRegister [NEGATIVE]
    Creates a ComplexAlias with one ComplexAliasField that has
    function="upper". Calls bindAliasToConversions. Verifies
    getOrCreateModelConversion was never called — the alias stays as a
    wildcard.
    
    bindAliasToConversionsSingleFieldWithDefaultValueDoesNotRegister
    [NEGATIVE]
    Creates a ComplexAlias with one ComplexAliasField that has
    defaultValue="0". Calls bindAliasToConversions. Verifies
    getOrCreateModelConversion was never called.
    
      bindAliasToConversionsMultipleMembersDoesNotRegister  [NEGATIVE]
    Creates a ComplexAlias with two ComplexAliasField members (unitPrice and
    quantity, simulating an arithmetic expression like unitPrice +
    quantity). Calls bindAliasToConversions. Verifies
    getOrCreateModelConversion was never called — the computed sum cannot be
    represented as a raw field value.
    
    bindAliasToConversionsNestedComplexAliasAsSoleMemberDoesNotRegister
    [NEGATIVE]
    Creates an outer ComplexAlias whose sole member is another (inner)
    ComplexAlias, not a ComplexAliasField. Calls bindAliasToConversions on
    the outer alias. Verifies getOrCreateModelConversion was never called —
    the instanceof ComplexAliasField check in the safety gate correctly
    blocks this case.
---
 .../apache/ofbiz/entity/model/ModelViewEntity.java |  37 ++++-
 .../model/ModelViewEntityComplexAliasTests.java    | 170 +++++++++++++++++++++
 2 files changed, 204 insertions(+), 3 deletions(-)

diff --git 
a/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelViewEntity.java
 
b/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelViewEntity.java
index 76dcfa3a34..5543310d85 100644
--- 
a/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelViewEntity.java
+++ 
b/framework/entity/src/main/java/org/apache/ofbiz/entity/model/ModelViewEntity.java
@@ -670,9 +670,7 @@ public class ModelViewEntity extends ModelEntity {
         while (it.hasNext()) {
             ModelViewEntity.ModelAlias alias = it.next();
             if (alias.isComplexAlias()) {
-                // TODO: conversion for complex-alias needs to be implemented 
for cache and in-memory eval stuff to work correctly
-                Debug.logVerbose("[" + this.getEntityName() + "]: Conversion 
for complex-alias needs to be implemented for cache and "
-                        + "in-memory eval stuff to work correctly, will not 
work for alias: " + alias.getName(), MODULE);
+                
alias.getComplexAliasMember().bindAliasToConversions(alias.getName(), this);
             } else {
                 ModelConversion conversion = 
getOrCreateModelConversion(alias.getEntityAlias());
                 conversion.addConversion(alias.getField(), alias.getName());
@@ -1060,6 +1058,10 @@ public class ModelViewEntity extends ModelEntity {
             this.complexAliasMember = complexAliasMember;
         }
 
+        public ComplexAliasMember getComplexAliasMember() {
+            return this.complexAliasMember;
+        }
+
         public boolean isComplexAlias() {
             return complexAliasMember != null;
         }
@@ -1118,6 +1120,7 @@ public class ModelViewEntity extends ModelEntity {
 
     public interface ComplexAliasMember extends Serializable {
         void makeAliasColName(StringBuilder colNameBuffer, StringBuilder 
fieldTypeBuffer, ModelViewEntity modelViewEntity, ModelReader modelReader);
+        void bindAliasToConversions(String aliasName, ModelViewEntity 
modelViewEntity);
     }
 
     public static final class ComplexAlias implements ComplexAliasMember {
@@ -1149,6 +1152,18 @@ public class ModelViewEntity extends ModelEntity {
             this.complexAliasMembers.addAll(complexAliasMembers);
         }
 
+        @Override
+        public void bindAliasToConversions(String aliasName, ModelViewEntity 
modelViewEntity) {
+            // Only safe for a single pass-through field — arithmetic across 
multiple members
+            // or any transforming function makes rawField != computedAlias, 
breaking cache probes.
+            if (complexAliasMembers.size() == 1 && complexAliasMembers.get(0) 
instanceof ComplexAliasField) {
+                ComplexAliasField sole = (ComplexAliasField) 
complexAliasMembers.get(0);
+                if (sole.isPassThrough()) {
+                    sole.bindAliasToConversions(aliasName, modelViewEntity);
+                }
+            }
+        }
+
         @Override
         public void makeAliasColName(StringBuilder colNameBuffer, 
StringBuilder fieldTypeBuffer, ModelViewEntity modelViewEntity,
                                      ModelReader modelReader) {
@@ -1204,6 +1219,22 @@ public class ModelViewEntity extends ModelEntity {
             this.value = value;
         }
 
+        /** True when this is a plain column reference with no function or 
default applied. */
+        public boolean isPassThrough() {
+            return UtilValidate.isNotEmpty(entityAlias)
+                && UtilValidate.isNotEmpty(field)
+                && UtilValidate.isEmpty(function)
+                && UtilValidate.isEmpty(defaultValue);
+        }
+
+        @Override
+        public void bindAliasToConversions(String aliasName, ModelViewEntity 
modelViewEntity) {
+            if (UtilValidate.isNotEmpty(entityAlias) && 
UtilValidate.isNotEmpty(field)) {
+                ModelConversion conversion = 
modelViewEntity.getOrCreateModelConversion(entityAlias);
+                conversion.addConversion(field, aliasName);
+            }
+        }
+
         /**
          * Make the alias as follows: function(coalesce(entityAlias.field, 
defaultValue))
          */
diff --git 
a/framework/entity/src/test/java/org/apache/ofbiz/entity/model/ModelViewEntityComplexAliasTests.java
 
b/framework/entity/src/test/java/org/apache/ofbiz/entity/model/ModelViewEntityComplexAliasTests.java
new file mode 100644
index 0000000000..20e71c9d95
--- /dev/null
+++ 
b/framework/entity/src/test/java/org/apache/ofbiz/entity/model/ModelViewEntityComplexAliasTests.java
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * 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.ofbiz.entity.model;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Tests for the Tier-2 complex-alias conversion binding:
+ * a single-member, no-function, no-default ComplexAliasField is the only
+ * case where a ModelConversion entry is safe to register. All other shapes
+ * (arithmetic, functions, defaultValue, nested alias) must stay as wildcards.
+ */
+public final class ModelViewEntityComplexAliasTests {
+
+    @Mock
+    private ModelViewEntity mockViewEntity;
+
+    private AutoCloseable mocks;
+
+    @BeforeEach
+    void setUp() {
+        mocks = MockitoAnnotations.openMocks(this);
+    }
+
+    @AfterEach
+    void tearDown() throws Exception {
+        mocks.close();
+    }
+
+    // ── ComplexAliasField.isPassThrough() ──────────────────────────────────
+
+    @Test
+    void isPassThroughPlainFieldReferenceReturnsTrue() {
+        ModelViewEntity.ComplexAliasField field =
+                new ModelViewEntity.ComplexAliasField("ME", "myField", "", "");
+        assertTrue(field.isPassThrough());
+    }
+
+    @Test
+    void isPassThroughWithFunctionReturnsFalse() {
+        ModelViewEntity.ComplexAliasField field =
+                new ModelViewEntity.ComplexAliasField("ME", "myField", "", 
"upper");
+        assertFalse(field.isPassThrough());
+    }
+
+    @Test
+    void isPassThroughWithDefaultValueReturnsFalse() {
+        ModelViewEntity.ComplexAliasField field =
+                new ModelViewEntity.ComplexAliasField("ME", "myField", "0", 
"");
+        assertFalse(field.isPassThrough());
+    }
+
+    @Test
+    void isPassThroughEmptyEntityAliasReturnsFalse() {
+        ModelViewEntity.ComplexAliasField field =
+                new ModelViewEntity.ComplexAliasField("", "myField", "", "");
+        assertFalse(field.isPassThrough());
+    }
+
+    @Test
+    void isPassThroughEmptyFieldReturnsFalse() {
+        ModelViewEntity.ComplexAliasField field =
+                new ModelViewEntity.ComplexAliasField("ME", "", "", "");
+        assertFalse(field.isPassThrough());
+    }
+
+    @Test
+    void isPassThroughLiteralValueConstantReturnsFalse() {
+        // entityAlias and field are both empty — this is a SQL literal 
constant, not a column ref
+        ModelViewEntity.ComplexAliasField field =
+                new ModelViewEntity.ComplexAliasField("", "", "", "", 
"LITERAL_VALUE");
+        assertFalse(field.isPassThrough());
+    }
+
+    // ── ComplexAlias.bindAliasToConversions() — positive ───────────────────
+
+    @Test
+    void bindAliasToConversionsSinglePassThroughFieldRegistersConversion() {
+        // ModelConversion is a final inner class and cannot be mocked without 
the inline mock maker.
+        // We verify the dispatch reached getOrCreateModelConversion with the 
correct entityAlias,
+        // which is the key decision point. addConversion() is a trivial 
HashMap put tested separately.
+        doThrow(new UnsupportedOperationException("reached conversion 
registration"))
+                .when(mockViewEntity).getOrCreateModelConversion(anyString());
+
+        ModelViewEntity.ComplexAlias alias = new 
ModelViewEntity.ComplexAlias("+");
+        alias.addComplexAliasMember(new 
ModelViewEntity.ComplexAliasField("ME", "myField", "", ""));
+
+        assertThrows(UnsupportedOperationException.class, () ->
+                alias.bindAliasToConversions("myAlias", mockViewEntity));
+
+        verify(mockViewEntity).getOrCreateModelConversion("ME");
+    }
+
+    // ── ComplexAlias.bindAliasToConversions() — negative (stays wildcard) ──
+
+    @Test
+    void bindAliasToConversionsSingleFieldWithFunctionDoesNotRegister() {
+        ModelViewEntity.ComplexAlias alias = new 
ModelViewEntity.ComplexAlias("+");
+        alias.addComplexAliasMember(new 
ModelViewEntity.ComplexAliasField("ME", "myField", "", "upper"));
+
+        alias.bindAliasToConversions("myAlias", mockViewEntity);
+
+        verify(mockViewEntity, 
never()).getOrCreateModelConversion(anyString());
+    }
+
+    @Test
+    void bindAliasToConversionsSingleFieldWithDefaultValueDoesNotRegister() {
+        ModelViewEntity.ComplexAlias alias = new 
ModelViewEntity.ComplexAlias("+");
+        alias.addComplexAliasMember(new 
ModelViewEntity.ComplexAliasField("ME", "myField", "0", ""));
+
+        alias.bindAliasToConversions("myAlias", mockViewEntity);
+
+        verify(mockViewEntity, 
never()).getOrCreateModelConversion(anyString());
+    }
+
+    @Test
+    void bindAliasToConversionsMultipleMembersDoesNotRegister() {
+        // Arithmetic across two fields — computed value != either raw field 
value
+        ModelViewEntity.ComplexAlias alias = new 
ModelViewEntity.ComplexAlias("+");
+        alias.addComplexAliasMember(new 
ModelViewEntity.ComplexAliasField("ME", "unitPrice", "", ""));
+        alias.addComplexAliasMember(new 
ModelViewEntity.ComplexAliasField("ME", "quantity", "", ""));
+
+        alias.bindAliasToConversions("totalAmount", mockViewEntity);
+
+        verify(mockViewEntity, 
never()).getOrCreateModelConversion(anyString());
+    }
+
+    @Test
+    void bindAliasToConversionsNestedComplexAliasAsSoleMemberDoesNotRegister() 
{
+        // Sole member is another ComplexAlias, not a ComplexAliasField
+        ModelViewEntity.ComplexAlias inner = new 
ModelViewEntity.ComplexAlias("+");
+        inner.addComplexAliasMember(new 
ModelViewEntity.ComplexAliasField("ME", "myField", "", ""));
+
+        ModelViewEntity.ComplexAlias outer = new 
ModelViewEntity.ComplexAlias("+");
+        outer.addComplexAliasMember(inner);
+
+        outer.bindAliasToConversions("myAlias", mockViewEntity);
+
+        verify(mockViewEntity, 
never()).getOrCreateModelConversion(anyString());
+    }
+}

Reply via email to