GWT 嵌套编辑器 - 未正确刷新或给出周期检测错误

GWT Nested Editors - Not flushing correctly or giving cycle detected error

我正在创建一个与此类似的规则生成器:

可以有两种类型的规则对象:

这两个条件都实现了一个 MetadataCondition 接口。

方法一

我已经使用了三个不同的编辑器来尝试编辑规则,但我收到了一个循环检测错误,这是有道理的,因为规则是递归的。下面是三位编辑。

元数据条件编辑器

这使用 here and here

中描述的 AbstractSubTypeEditor

这是规则编辑器的入口点。它将被赋予顶级元数据条件,并从那里附加相关的编辑器。

public class MetadataConditionEditor extends Composite implements Editor<MetadataCondition> {
interface Binder extends UiBinder<Widget, MetadataConditionEditor> {}

@Ignore
final JoinMetadataConditionEditor joinMetadataEditor = LibsFactory.injector().getJoinMetadataConditionEditor();

@Path("")
final AbstractSubTypeEditor<MetadataCondition, JoinMetadataCondition, JoinMetadataConditionEditor> joinMetadataEditorWrapper =
    new AbstractSubTypeEditor<MetadataCondition, JoinMetadataCondition, JoinMetadataConditionEditor>( joinMetadataEditor ) {
        @Override
        public void setValue( final MetadataCondition value )
        {
            setValue( value, isJoinMetadataCondition( value ) );
            if( isJoinMetadataCondition( value ) ) {
                container.clear();
                container.add( joinMetadataEditor );
            }
        }
};


@Ignore
final LeafMetadataConditionEditor leafMetadataEditor = LibsFactory.injector().getLeafMetadataConditionEditor();

@Path("")
final AbstractSubTypeEditor<MetadataCondition, LeafMetadataCondition, LeafMetadataConditionEditor> leafMetadataEditorWrapper =
    new AbstractSubTypeEditor<MetadataCondition, LeafMetadataCondition, LeafMetadataConditionEditor>( leafMetadataEditor ) {
        @Override
        public void setValue( final MetadataCondition value )
        {
            setValue( value, !isJoinMetadataCondition( value ) );
            if( !isJoinMetadataCondition( value ) ) {
                container.clear();
                container.add( leafMetadataEditor );
            }
        }
};


@UiField @Ignore SimplePanel container;


public MetadataConditionEditor() {
    initWidget( GWT.<Binder> create(Binder.class).createAndBindUi( this ) );
}


private Boolean isJoinMetadataCondition( MetadataCondition value ) {
    return value instanceof JoinMetadataCondition;
}}

加入元数据条件编辑器

用于编辑 JoinMetadataCondition 对象

public class JoinMetadataConditionEditor extends Composite implements Editor { 接口活页夹扩展 UiBinder {}

@UiField @Ignore FlowPanel container;

@UiField
@Path( "type" )
ManualSelectEditor type;

@Path( "conditions" )
ListEditor<MetadataCondition, MetadataConditionEditor> conditions;


public JoinMetadataConditionEditor() {
    initWidget( GWT.<Binder> create(Binder.class).createAndBindUi( this ) );

    conditions = ListEditor.of( new MetadataEditorSource() );
    type.setSelectOptions( EditorConstants.metadataJoins );
}


private class MetadataEditorSource extends EditorSource<MetadataConditionEditor>
{
    @Override
    public MetadataConditionEditor create( int index )
    {
        final MetadataConditionEditor subEditor = new MetadataConditionEditor();

        container.insert( subEditor, index );

        return subEditor;
    }

    public void dispose( MetadataConditionEditor subEditor ) {
        container.remove( subEditor );
    }
}}

LeafMetadataConditionEditor

最后是用于 LeafMetadataCondition 的编辑器

public class LeafMetadataConditionEditor extends Composite implements Editor<LeafMetadataCondition> {
    interface Binder extends UiBinder<Widget, LeafMetadataConditionEditor> {}

    @UiField TextBoxEditor name;
    @UiField TextBoxEditor value;
    @UiField ManualSelectEditor operator;


    public LeafMetadataConditionEditor() {
        initWidget(GWT.<Binder> create(Binder.class).createAndBindUi(this));
        operator.setSelectOptions( EditorConstants.metadataOperators );
    }
}

问题

问题是:如何避免循环检测错误或以有效的方式构建递归?

注意:我乐于接受新的方法。我尝试了其他一些方法,例如为每个子编辑器初始化驱动程序并将它们全部设为 CompositeEditors。这已编译并加载,但是当需要刷新驱动程序时,即使似乎调用了 flush() 方法,子编辑器的值也没有正确构造,但父编辑器随后没有使用这些值。

方法二

第二种方法涉及使用 CompositeEditor 并让子编辑器创建自己的驱动程序,然后将其刷新以创建最终结果。

元数据条件编辑器

public class MetadataConditionEditor extends Composite implements
                            CompositeEditor<MetadataCondition, MetadataCondition, SimpleDriverEditor<MetadataCondition>>,
                            LeafValueEditor<MetadataCondition>
{
    interface Binder extends UiBinder<Widget, MetadataConditionEditor> {}

//    private SimpleBeanEditorDriver<MetadataCondition, ? extends Editor<MetadataCondition>> subDriver;
    private EditorChain<MetadataCondition, SimpleDriverEditor<MetadataCondition>> chain;
    private SimpleDriverEditor<MetadataCondition> subEditor;
    private EditorDelegate<MetadataCondition> delegate;
    private MetadataCondition value;

    @UiField @Ignore SimplePanel container;


    public MetadataConditionEditor() {
        initWidget( GWT.<Binder> create(Binder.class).createAndBindUi( this ) );
    }


    @Override
    public void flush()
    {
        if( Utils.isNull( subEditor ) )
            return;

        GWT.log( "----- flush-pre - " + System.identityHashCode(this) + " - " + value );// TODO

        value = subEditor.flush();

        GWT.log( "----- flush-post-1 - " + System.identityHashCode(this) + " - " + value.toJson() );// TODO

    }

    @Override
    public void onPropertyChange(String... paths) {}

    @Override
    @SuppressWarnings("unchecked")
    public void setValue( MetadataCondition value )
    {
        this.value = value;
//        subDriver = null;

        if( Utils.isNull( value ) )
            return;

        GWT.log( "----- setValue - " + value );// TODO

        if( value instanceof JoinMetadataCondition ) {
            SimpleDriverEditor<JoinMetadataCondition> newSubEditor = LibsFactory.injector().getJoinMetadataConditionEditor();
            SimpleDriverEditor<? extends MetadataCondition> newSubEditor1 = newSubEditor;
            subEditor = (SimpleDriverEditor<MetadataCondition>) newSubEditor1;

            newSubEditor.edit( (JoinMetadataCondition) value );
        }

        container.clear();
        container.add( subEditor );
    }

    @Override
    public void setDelegate( EditorDelegate<MetadataCondition> delegate ) {
        GWT.log( "----- setDelegate - " + delegate );// TODO
        this.delegate = delegate;
    }

    @Override
    public MetadataCondition getValue() {
        GWT.log( "----- getValue - " + System.identityHashCode(this) + " - " + value );// TODO
        return value;
    }

    @Override
    public SimpleDriverEditor<MetadataCondition> createEditorForTraversal() {
        GWT.log( "----- createEditorForTraversal - " + subEditor );// TODO
        return subEditor;
    }

    @Override
    public String getPathElement( SimpleDriverEditor<MetadataCondition> subEditor ) {
        GWT.log( "----- getPathElement - " + delegate.getPath() );// TODO
        return delegate.getPath();
    }

    @Override
    public void setEditorChain( EditorChain<MetadataCondition, SimpleDriverEditor<MetadataCondition>> chain ) {
        GWT.log( "----- setEditorChain - " + chain );// TODO
        this.chain = chain;
    }
}

加入元数据条件编辑器

public class JoinMetadataConditionEditor extends Composite implements SimpleDriverEditor<JoinMetadataCondition>
{
    interface Binder extends UiBinder<Widget, JoinMetadataConditionEditor> {}
    interface Driver extends SimpleBeanEditorDriver<JoinMetadataCondition, JoinMetadataConditionEditor> {}

    private Driver driver = GWT.create(Driver.class);

    @Inject
    ModelFactory factory;

    @UiField @Ignore HTML label;
    @UiField @Ignore FlowPanel container;
    @UiField @Ignore Button deleteMetadata;

    @UiField
    @Path( "type" )
    ManualSelectEditor type;

    @Path( "conditions" )
    ListEditor<MetadataCondition, MetadataConditionEditor> conditions = ListEditor.of( new MetadataEditorSource() );


    public JoinMetadataConditionEditor() {
        initWidget( GWT.<Binder> create(Binder.class).createAndBindUi( this ) );
        type.setSelectOptions( EditorConstants.metadataJoins );
    }


    public SimpleBeanEditorDriver<JoinMetadataCondition, JoinMetadataConditionEditor> createDriver() {
        driver.initialize( this );
        return driver;
    }


    @Override
    public JoinMetadataCondition flush()
    {
        GWT.log( "---------- flush-pre - " + System.identityHashCode(this) + " - " + type.getValue() );// TODO
        GWT.log( "---------- flush-pre - " + System.identityHashCode(this) + " - " + conditions.getList() );// TODO
        JoinMetadataCondition value = driver.flush();
        GWT.log( "---------- flush-post - " + System.identityHashCode(this) + " - " + value.toJson() );// TODO
        return value;
    }


    @Override
    public void edit( JoinMetadataCondition object ) {
        createDriver();
        driver.edit( object );
        label.setText( getLabel( object ) );
    }


    private String getLabel( JoinMetadataCondition value ) {
        if( StringUtils.equals( value.getType(), JoinMetadataTypes.AND.value() ) )
            return LibsFactory.lang().allConditionsAreTrue();
        return LibsFactory.lang().anyConditionsAreTrue();
    }


    @Override
    public HandlerRegistration addDeleteHandler( MetadataDeletedHandler handler ) {
        return addHandler( handler, MetadataDeletedEvent.TYPE );
    }


    @UiHandler("deleteMetadata")
    protected void onDeleteMetadata( ClickEvent event ) {
        fireEvent( new MetadataDeletedEvent( (Event) event.getNativeEvent() ) );
    }


    @UiHandler("addAllMetadata")
    protected void onAddAllMetadata(ClickEvent event) {
        add( factory.newJoinMetadataCondition( JoinMetadataTypes.AND ) );
    }


    @UiHandler("addOrMetadata")
    protected void onAddOrMetadata(ClickEvent event) {
        add( factory.newJoinMetadataCondition( JoinMetadataTypes.OR ) );
    }

    @UiHandler("addLeafMetadata")
    protected void onAddLeafMetadata(ClickEvent event) {
        add( factory.newLeafMetadataCondition() );
    }


    private void add( MetadataCondition metadata ) {
        try {
            GWT.log("--------------------------------- add() - pre");// TODO
            conditions.getList().add( metadata );
            clearErrors();
        } catch (Exception e) {
            GWT.log("--------------------------------- add() - " + e.getMessage()); // TODO
        }
    }


    public void clearErrors() {
        type.getErrorHandler().clearErrors();
    }


    private class MetadataEditorSource extends EditorSource<MetadataConditionEditor>
    {
        @Override
        public MetadataConditionEditor create( int index )
        {
            final MetadataConditionEditor subEditor = new MetadataConditionEditor();
            container.insert( subEditor, index );
            return subEditor;
        }

        public void dispose( MetadataConditionEditor subEditor ) {
            container.remove( subEditor );
        }
    }
}

问题

第二种方法似乎几乎奏效了。问题是,当我刷新驱动程序时,子驱动程序也会被刷新,但出于某种原因,父编辑器实际上并不包含子编辑器的值。

当我有一个嵌套的 Join 条件时,上面的日志语句生成以下输出:

----- flush-pre - 111 - JoinMetadataCondition@2d1
---------- flush-pre - 761 - null
---------- flush-pre - 761 - [JoinMetadataCondition@379]
----- flush-pre - 886 - JoinMetadataCondition@379
---------- flush-pre - 929 - and
---------- flush-pre - 929 - []
---------- flush-post - 929 - {"type":"and", "conditions":[]}
----- flush-post-1 - 886 - {"type":"and", "conditions":[]}
----- getValue - 886 - JoinMetadataCondition@379
---------- flush-post - 761 - {"type":"and", "conditions":[]}
----- flush-post-1 - 111 - {"type":"and", "conditions":[]}
----- getValue - 111 - JoinMetadataCondition@2d1
{"type":"and", "conditions":[]}

如您所见,主编辑器 (111) 实际上并不包含其子编辑器的内容。知道为什么会这样吗?

不幸的是,我无法使用结构化编辑器方法让它工作。

最终的解决方案是创建一个 LeafValueEditor 并根据通过 setValue() 提供的数据输入构建表单 "manually"。我还实现了 HasEditorErrors 以正确处理错误。