package org.ovirt.engine.ui.common.editor; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import org.ovirt.engine.ui.common.widget.HasAccess; import org.ovirt.engine.ui.common.widget.HasEnabledWithHints; import org.ovirt.engine.ui.common.widget.HasValidation; import org.ovirt.engine.ui.common.widget.editor.HasEditorValidityState; import org.ovirt.engine.ui.common.widget.editor.ListModelMultipleSelectListBox; import org.ovirt.engine.ui.common.widget.editor.TakesConstrainedValueEditor; import org.ovirt.engine.ui.common.widget.editor.TakesConstrainedValueListEditor; import org.ovirt.engine.ui.uicommonweb.models.EntityModel; import org.ovirt.engine.ui.uicommonweb.models.ListModel; import org.ovirt.engine.ui.uicommonweb.models.Model; import com.google.gwt.editor.client.EditorContext; import com.google.gwt.editor.client.EditorVisitor; import com.google.gwt.editor.client.LeafValueEditor; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.logical.shared.HasValueChangeHandlers; /** * Editor visitor that implements integration with UiCommon models. * * @see UiCommonEditor */ public class UiCommonEditorVisitor<M extends Model> extends EditorVisitor { private final UiCommonEventMap eventMap; private final Map<String, Model> ownerModels; private int tabIndexCounter = 0; /** * A Visitor for UICommon Edited Models. */ public UiCommonEditorVisitor(UiCommonEventMap eventMap, Map<String, Model> ownerModels) { this.eventMap = eventMap; this.ownerModels = ownerModels; } @SuppressWarnings("unchecked") @Override public <T> boolean visit(final EditorContext<T> ctx) { final String absolutePath = ctx.getAbsolutePath(); LeafValueEditor<T> currentLeafEditor = ctx.asLeafValueEditor(); if (currentLeafEditor == null) { // Ignore non-leaf Editors return super.visit(ctx); } final LeafValueEditor<T> editor = getActualEditor(currentLeafEditor); // If this Editor implements HasValueChangeHandlers, register a value change listener if (editor instanceof HasValueChangeHandlers) { ((HasValueChangeHandlers<T>) editor).addValueChangeHandler(event -> setInModel(ctx, event.getSource(), event.getValue())); } final UiCommonEditor<T> functionalEditor = getFunctionalEditor(currentLeafEditor); if (functionalEditor != null) { // Set tab index, unless it's being set manually (i.e. already been set) if (functionalEditor.getTabIndex() <= 0) { functionalEditor.setTabIndex(++tabIndexCounter); } // Add key press handler functionalEditor.addKeyPressHandler(event -> { if (KeyCodes.KEY_ENTER == event.getNativeEvent().getKeyCode()) { setInModel(ctx, editor, editor.getValue()); } }); } // Handle owner entity models if (ownerModels.containsKey(absolutePath)) { Model ownerModel = ownerModels.get(absolutePath); // If this editor edits a ListModel, initialize it if (editor instanceof TakesConstrainedValueListEditor && ownerModel instanceof ListModel) { updateListEditor((TakesConstrainedValueListEditor<T>) editor, (ListModel) ownerModel); } else if (editor instanceof TakesConstrainedValueEditor && ownerModel instanceof ListModel) { updateListEditor((TakesConstrainedValueEditor<T>) editor, (ListModel) ownerModel); } if (functionalEditor != null) { // Register a property change listener on the owner entity model ownerModel.getPropertyChangedEvent().addListener((ev, sender, args) -> { Model owner = (Model) sender; String propName = args.propertyName; // IsValid if ("IsValid".equals(propName)) { //$NON-NLS-1$ onIsValidPropertyChange(functionalEditor, owner); } // IsChangable else if ("IsChangable".equals(propName)) { //$NON-NLS-1$ onIsChangablePropertyChange(functionalEditor, owner); } // ChangeProhibitionReason else if ("ChangeProhibitionReason".equals(propName)) { //$NON-NLS-1$ onChangeProhibitionReasonChange(functionalEditor, owner); } // IsAvailable else if ("IsAvailable".equals(propName)) { //$NON-NLS-1$ onIsAvailablePropertyChange(functionalEditor, owner); } }); // Update editor since we might have missed property change // events fired as part of the entity model constructor onIsValidPropertyChange(functionalEditor, ownerModel); onIsChangablePropertyChange(functionalEditor, ownerModel); onChangeProhibitionReasonChange(functionalEditor, ownerModel); onIsAvailablePropertyChange(functionalEditor, ownerModel); } } // Register listeners eventMap.registerListener(absolutePath, "EntityChanged", //$NON-NLS-1$ (ev, sender, args) -> editor.setValue((T) ((EntityModel) sender).getEntity())); eventMap.registerListener(absolutePath, "ItemsChanged", //$NON-NLS-1$ (ev, sender, args) -> updateListEditor((TakesConstrainedValueEditor<T>) editor, (ListModel) sender)); eventMap.registerListener(absolutePath, "SelectedItemChanged", (ev, sender, args) -> { //$NON-NLS-1$ T selectedItem = (T) ((ListModel) sender).getSelectedItem(); if (editor instanceof TakesConstrainedValueListEditor && ownerModels.get(absolutePath) instanceof ListModel) { editor.setValue((T)Arrays.asList(selectedItem)); } else { editor.setValue(selectedItem); } }); eventMap.registerListener(absolutePath, "SelectedItemsChanged", (ev, sender, args) -> { //$NON-NLS-1$ if (editor instanceof TakesConstrainedValueListEditor && ownerModels.get(absolutePath) instanceof ListModel) { ((TakesConstrainedValueListEditor)editor).setListValue((List<T>)((ListModel) sender).getSelectedItems()); } }); return super.visit(ctx); } private <T> void setInModel(final EditorContext<T> ctx, Object editor, T value) { if (ctx.canSetInModel()) { boolean editorValid = true; if (editor instanceof HasEditorValidityState) { editorValid = ((HasEditorValidityState)editor).isStateValid(); } if (editorValid) { if (editor instanceof ListModelMultipleSelectListBox) { @SuppressWarnings("unchecked") T listValue = (T) ((ListModelMultipleSelectListBox<T>) editor).selectedItems(); ctx.setInModel(listValue); } else { ctx.setInModel(value); } } } } @SuppressWarnings("unchecked") <T> LeafValueEditor<T> getActualEditor(LeafValueEditor<T> editor) { if (editor instanceof UiCommonEditor) { return ((UiCommonEditor<T>) editor).getActualEditor(); } else { return editor; } } @SuppressWarnings("unchecked") <T> UiCommonEditor<T> getFunctionalEditor(LeafValueEditor<T> editor) { if (editor instanceof UiCommonEditor) { return (UiCommonEditor<T>) editor; } else { return null; } } /** * Update a ListEditor according to the items in the ListModel <BR> * (this is required since the Editor is bound to the "selectedItem" property, and not to the "items" property). */ @SuppressWarnings("unchecked") <O> void updateListEditor(TakesConstrainedValueEditor<O> listEditor, ListModel<O> parentModel) { Collection<O> items = parentModel.getItems(); if (items != null) { if (items.size() > 0) { O value; if (parentModel.getSelectedItem() != null) { value = parentModel.getSelectedItem(); } else { value = items.iterator().next(); // Order is important parentModel.setSelectedItem(value); } if (items.contains(value)) { if (listEditor instanceof TakesConstrainedValueListEditor) { if (parentModel.getSelectedItems() != null) { ((TakesConstrainedValueListEditor<O>)listEditor).setListValue(parentModel.getSelectedItems()); } } else { listEditor.setValue(value); } } } if (listEditor instanceof TakesConstrainedValueListEditor) { ((TakesConstrainedValueListEditor<O>)listEditor).setAcceptableListValues(items); } else { listEditor.setAcceptableValues(items); } } } void onIsValidPropertyChange(HasValidation editor, Model model) { if (model.getIsValid()) { editor.markAsValid(); } else { //The entity validator will mark the entity valid before running the validation //this will cause the editor to be marked valid if it was invalid due to an entity validation //error. Then if the validation is invalid again this will update the error message. So there is //no possibility to go from one invalid reason to another without the editor message being updated. if (editor.isValid()) { editor.markAsInvalid(model.getInvalidityReasons()); } } } void onIsChangablePropertyChange(HasEnabledWithHints editor, Model model) { if (model.getIsChangable()) { editor.setEnabled(true); } else { editor.disable(model.getChangeProhibitionReason()); } } void onChangeProhibitionReasonChange(HasEnabledWithHints editor, Model model) { if (!editor.isEnabled()) { editor.disable(model.getChangeProhibitionReason()); } } void onIsAvailablePropertyChange(HasAccess editor, Model model) { boolean isAvailable = model.getIsAvailable(); editor.setAccessible(isAvailable); } }