Вложенные редакторы GWT - неправильно очищается или выдает ошибку обнаружения цикла

Я создаю конструктор правил, подобный этому:

введите здесь описание изображения

Объекты правил могут быть двух типов:

  • JoinMetadataCondition: содержит тип соединения ("и" или "или") и список других условий метаданных для объединения.
  • LeafMetadataCondition: содержит определение правила (переменная, оператор, значение).

Оба этих условия реализуют интерфейс MetadataCondition.

Подход 1

Я использовал три разных редактора, чтобы попытаться отредактировать правила, но я получаю ошибку, обнаруженную циклом, что имеет смысл, поскольку правила рекурсивны. Ниже представлены три редактора.

Редактор условий метаданных

Здесь используется редактор 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.

открытый класс JoinMetadataConditionEditor расширяет Composite, реализует Editor { интерфейс Binder расширяет 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(), казалось, вызывались, но значения тогда не использовались родительскими редакторами.

Подход 2

Второй подход предполагает использование 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 );
        }
    }
}

Проблема

Этот второй подход кажется почти рабочим. Проблема в том, что когда я сбрасываю драйвер, поддрайверы также сбрасываются, но по какой-то причине родительские редакторы фактически не включают значения подредакторов.

Приведенные выше операторы журнала генерируют следующий вывод, когда у меня есть одно вложенное условие соединения:

----- 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) на самом деле не включает в себя содержимое своих подчиненных редакторов. Есть идеи, почему это может быть?


person Alessandro Giannone    schedule 06.10.2016    source источник
comment
Вы с этим чего-нибудь добились? Это довольно амбициозно! (Ницца ;) )   -  person slugmandrew    schedule 25.01.2017
comment
На самом деле я это сделал, но в основном мне пришлось свернуть свой собственный LeafValueEditor и создать форму вручную :-)   -  person Alessandro Giannone    schedule 26.01.2017


Ответы (1)


К сожалению, я не смог заставить это работать, используя подход структурированного редактора.

Окончательное решение состояло в том, чтобы создать LeafValueEditor и построить форму «вручную» на основе данных, введенных через setValue(). Я также реализовал HasEditorErrors для правильной обработки ошибок.

person Alessandro Giannone    schedule 26.01.2017