GWT 嵌套编辑器 - 未正确刷新或给出周期检测错误
GWT Nested Editors - Not flushing correctly or giving cycle detected error
我正在创建一个与此类似的规则生成器:
可以有两种类型的规则对象:
- JoinMetadataCondition:这包含连接类型("and" 或 "or")和要连接的其他元数据条件列表
- LeafMetadataCondition:这包含一个规则定义(变量、运算符、值)
这两个条件都实现了一个 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 以正确处理错误。
我正在创建一个与此类似的规则生成器:
可以有两种类型的规则对象:
- JoinMetadataCondition:这包含连接类型("and" 或 "or")和要连接的其他元数据条件列表
- LeafMetadataCondition:这包含一个规则定义(变量、运算符、值)
这两个条件都实现了一个 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 以正确处理错误。