All Downloads are FREE. Search and download functionalities are using the official Maven repository.

xy.reflect.ui.control.swing.renderer.FieldControlPlaceHolder Maven / Gradle / Ivy

package xy.reflect.ui.control.swing.renderer;

import java.awt.BorderLayout;
import java.awt.Component;
import java.util.Map;

import javax.swing.JPanel;

import xy.reflect.ui.control.FieldContext;
import xy.reflect.ui.control.FieldControlDataProxy;
import xy.reflect.ui.control.IContext;
import xy.reflect.ui.control.IFieldControlData;
import xy.reflect.ui.control.IFieldControlInput;
import xy.reflect.ui.control.swing.DialogAccessControl;
import xy.reflect.ui.control.swing.EmbeddedFormControl;
import xy.reflect.ui.control.swing.IAdvancedFieldControl;
import xy.reflect.ui.control.swing.NullControl;
import xy.reflect.ui.control.swing.NullableControl;
import xy.reflect.ui.info.IInfo;
import xy.reflect.ui.info.ValueReturnMode;
import xy.reflect.ui.info.field.FieldInfoProxy;
import xy.reflect.ui.info.field.IFieldInfo;
import xy.reflect.ui.info.field.ValueOptionsAsEnumerationFieldInfo;
import xy.reflect.ui.info.filter.IInfoFilter;
import xy.reflect.ui.info.type.ITypeInfo;
import xy.reflect.ui.info.type.factory.ITypeInfoProxyFactory;
import xy.reflect.ui.undo.AbstractModification;
import xy.reflect.ui.undo.ModificationStack;
import xy.reflect.ui.util.DelayedUpdateProcess;
import xy.reflect.ui.util.ReflectionUIError;
import xy.reflect.ui.util.ReflectionUIUtils;
import xy.reflect.ui.util.SwingRendererUtils;

public class FieldControlPlaceHolder extends JPanel implements IFieldControlInput {

	protected static final long serialVersionUID = 1L;

	protected final SwingRenderer swingRenderer;
	protected Component fieldControl;
	protected JPanel form;
	protected IFieldInfo field;
	protected String errorMessageDisplayedOnPlaceHolder;
	protected IFieldControlData controlData;
	protected IFieldControlData lastInitialControlData;
	protected boolean layoutUpdateNeeded = true;

	public FieldControlPlaceHolder(SwingRenderer swingRenderer, JPanel form, IFieldInfo field) {
		super();
		this.swingRenderer = swingRenderer;
		this.form = form;
		this.field = field;
		setLayout(new BorderLayout());
		refreshUI(false);
	}

	public IFieldInfo getField() {
		return field;
	}

	public Object getObject() {
		return this.swingRenderer.getObjectByForm().get(form);
	}

	public JPanel getForm() {
		return form;
	}

	@Override
	public IFieldControlData getControlData() {
		return controlData;
	}

	public void setControlData(IFieldControlData controlData) {
		this.controlData = controlData;
	}

	public boolean isLayoutUpdateNeeded() {
		return layoutUpdateNeeded;
	}

	public void setLayoutUpdateNeeded(boolean layoutUpdateNeeded) {
		this.layoutUpdateNeeded = layoutUpdateNeeded;
	}

	@Override
	public IContext getContext() {
		ITypeInfo objectType = this.swingRenderer.reflectionUI
				.getTypeInfo(this.swingRenderer.reflectionUI.getTypeInfoSource(getObject()));
		return new FieldContext(objectType, field);
	}

	@Override
	public ModificationStack getModificationStack() {
		return this.swingRenderer.getModificationStackByForm().get(form);
	}

	public DelayedUpdateProcess createDelayedUpdateProcess(final Runnable updateJob) {
		DelayedUpdateProcess result = new DelayedUpdateProcess() {

			@Override
			protected void run() {
				updateJob.run();
			}
		};
		result.setDelayMilliseconds(500);
		return result;
	}

	public IFieldControlData handleStressfulUpdates(final IFieldControlData data) {
		return new FieldControlDataProxy(data) {

			Object delayedFieldValue;
			boolean delaying = false;
			DelayedUpdateProcess delayedUpdateProcess = createDelayedUpdateProcess(new Runnable() {
				@Override
				public void run() {
					try {
						data.setValue(delayedFieldValue);
					} finally {
						delaying = false;
					}
				}
			});

			@Override
			public Object getValue() {
				if (delaying) {
					return delayedFieldValue;
				} else {
					return data.getValue();
				}
			}

			@Override
			public void setValue(Object newValue) {
				delayedFieldValue = newValue;
				delaying = true;
				delayedUpdateProcess.schedule();
			}
		};
	}

	public IFieldControlData makeFieldModificationsUndoable(final IFieldControlData data) {
		return new FieldControlDataProxy(data) {

			@Override
			public void setValue(Object newValue) {
				Component c = fieldControl;
				if ((c instanceof IAdvancedFieldControl)) {
					IAdvancedFieldControl fieldControl = (IAdvancedFieldControl) c;
					if (fieldControl.handlesModificationStackUpdate()) {
						data.setValue(newValue);
						return;
					}
				}
				ReflectionUIUtils.setValueThroughModificationStack(data, newValue, getModificationStack(),
						getModificationsTarget());
			}
		};
	}

	public IFieldControlData handleValueAccessIssues(final IFieldControlData data) {
		return new FieldControlDataProxy(data) {

			Object lastFieldValue;
			boolean lastFieldValueInitialized = false;
			Throwable lastValueUpdateError;

			@Override
			public Object getValue() {
				try {
					if (lastValueUpdateError != null) {
						throw lastValueUpdateError;
					}
					lastFieldValue = data.getValue();
					lastFieldValueInitialized = true;
					displayError(null);
				} catch (final Throwable t) {
					if (!lastFieldValueInitialized) {
						throw new ReflectionUIError(t);
					} else {
						t.printStackTrace();
						displayError(ReflectionUIUtils.getPrettyErrorMessage(t));
					}
				}
				return lastFieldValue;

			}

			@Override
			public void setValue(Object newValue) {
				try {
					lastFieldValue = newValue;
					data.setValue(newValue);
					lastValueUpdateError = null;
				} catch (Throwable t) {
					lastValueUpdateError = t;
				}
			}

		};
	}

	public IFieldControlData indicateWhenBusy(final IFieldControlData data) {
		return new FieldControlDataProxy(data) {

			private boolean isBusyIndicationDisabled() {
				JPanel form = SwingRendererUtils.findParentForm(FieldControlPlaceHolder.this, swingRenderer);
				return Boolean.TRUE
						.equals(FieldControlPlaceHolder.this.swingRenderer.getBusyIndicationDisabledByForm().get(form));
			}

			@Override
			public Object getValue() {
				if (isBusyIndicationDisabled()) {
					return super.getValue();
				}
				return SwingRendererUtils.showBusyDialogWhileGettingFieldValue(FieldControlPlaceHolder.this,
						swingRenderer, data);
			}

			@Override
			public void setValue(final Object value) {
				if (isBusyIndicationDisabled()) {
					super.setValue(value);
					return;
				}
				SwingRendererUtils.showBusyDialogWhileSettingFieldValue(FieldControlPlaceHolder.this, swingRenderer,
						data, value);
			}

			@Override
			public Runnable getNextUpdateCustomUndoJob(Object newValue) {
				if (isBusyIndicationDisabled()) {
					return super.getNextUpdateCustomUndoJob(newValue);
				}
				final Runnable result = data.getNextUpdateCustomUndoJob(newValue);
				if (result == null) {
					return null;
				}
				return new Runnable() {
					@Override
					public void run() {
						FieldControlPlaceHolder.this.swingRenderer.showBusyDialogWhile(FieldControlPlaceHolder.this,
								new Runnable() {
									public void run() {
										result.run();
									}
								}, AbstractModification.getUndoTitle("Setting " + data.getCaption()));
					}
				};
			}

		};
	}

	public Component getFieldControl() {
		return fieldControl;
	}

	@Override
	public IInfo getModificationsTarget() {
		return field;
	}

	public void refreshUI(boolean recreate) {
		if (recreate) {
			if (fieldControl != null) {
				remove(fieldControl);
				fieldControl = null;
			}
		}
		if (fieldControl == null) {
			try {
				controlData = lastInitialControlData = getInitialControlData();
				fieldControl = createFieldControl();
			} catch (Throwable t) {
				fieldControl = this.swingRenderer.createErrorControl(t);
			}
			add(fieldControl, BorderLayout.CENTER);
			layoutUpdateNeeded = true;
		} else {
			if (isFieldControlObsolete()) {
				refreshUI(true);
			} else {
				boolean refreshed = false;
				if (fieldControl instanceof IAdvancedFieldControl) {
					try {
						refreshed = ((IAdvancedFieldControl) fieldControl).refreshUI();
					} catch (Throwable ignore) {
					}
				}
				if (!refreshed) {
					refreshUI(true);
				}
			}
		}
	}

	public boolean isFieldControlObsolete() {
		IFieldControlData newInitialControlData;
		try {
			newInitialControlData = getInitialControlData();
		} catch (Throwable t) {
			return true;
		}
		if (!newInitialControlData.equals(lastInitialControlData)) {
			return true;
		}
		return false;
	}

	public IFieldControlData getInitialControlData() {
		Object object = getObject();
		IFieldInfo field = FieldControlPlaceHolder.this.field;
		if (ValueOptionsAsEnumerationFieldInfo.hasValueOptions(object, field)) {
			field = new ValueOptionsAsEnumerationFieldInfo(this.swingRenderer.reflectionUI, object, field);
		}
		final ITypeInfoProxyFactory typeSpecificities = field.getTypeSpecificities();
		if (typeSpecificities != null) {
			field = new FieldInfoProxy(field) {
				@Override
				public ITypeInfo getType() {
					return typeSpecificities.get(super.getType());
				}
			};
		}
		final IFieldInfo finalField = field;
		IFieldControlData result = new InitialFieldControlData(finalField);
		result = indicateWhenBusy(result);
		result = handleValueAccessIssues(result);
		result = makeFieldModificationsUndoable(result);
		result = handleStressfulUpdates(result);
		return result;
	}

	public Component createFieldControl() {
		if (!controlData.isFormControlMandatory()) {
			Component result = this.swingRenderer.createCustomFieldControl(this);
			if (result != null) {
				return result;
			}
		}
		if (controlData.isNullValueDistinct()) {
			return new NullableControl(this.swingRenderer, this);
		}
		Object value = controlData.getValue();
		if (value == null) {
			return new NullControl(swingRenderer, this);
		}
		final ITypeInfo actualValueType = this.swingRenderer.reflectionUI
				.getTypeInfo(this.swingRenderer.reflectionUI.getTypeInfoSource(value));
		if (!controlData.getType().getName().equals(actualValueType.getName())) {
			controlData = new FieldControlDataProxy(controlData) {
				@Override
				public ITypeInfo getType() {
					return actualValueType;
				}
			};
			return createFieldControl();
		}
		if (controlData.isFormControlEmbedded()) {
			return new EmbeddedFormControl(this.swingRenderer, this);
		} else {
			return new DialogAccessControl(this.swingRenderer, this);
		}
	}

	public void displayError(String msg) {
		boolean done = (fieldControl instanceof IAdvancedFieldControl)
				&& ((IAdvancedFieldControl) fieldControl).displayError(msg);
		if (!done && (msg != null)) {
			if (errorMessageDisplayedOnPlaceHolder == null) {
				errorMessageDisplayedOnPlaceHolder = msg;
				SwingRendererUtils.setErrorBorder(this);
				this.swingRenderer.handleExceptionsFromDisplayedUI(fieldControl, new ReflectionUIError(msg));
			}
		} else {
			errorMessageDisplayedOnPlaceHolder = null;
			setBorder(null);
		}
	}

	public boolean showsCaption() {
		if (((fieldControl instanceof IAdvancedFieldControl)
				&& ((IAdvancedFieldControl) fieldControl).showsCaption())) {
			return true;
		} else {
			return false;
		}
	}

	@Override
	public String toString() {
		return "FieldControlPlaceHolder [form=" + form + ", field=" + field + "]";
	}

	protected class InitialFieldControlData implements IFieldControlData {

		protected IFieldInfo finalField;

		public InitialFieldControlData(IFieldInfo finalField) {
			this.finalField = finalField;
		}

		@Override
		public Object getValue() {
			return finalField.getValue(getObject());
		}

		@Override
		public void setValue(Object value) {
			finalField.setValue(getObject(), value);
		}

		@Override
		public String getCaption() {
			return finalField.getCaption();
		}

		@Override
		public Runnable getNextUpdateCustomUndoJob(Object newValue) {
			return finalField.getNextUpdateCustomUndoJob(getObject(), newValue);
		}

		@Override
		public ITypeInfo getType() {
			return finalField.getType();
		}

		@Override
		public boolean isGetOnly() {
			return finalField.isGetOnly();
		}

		@Override
		public ValueReturnMode getValueReturnMode() {
			return finalField.getValueReturnMode();
		}

		@Override
		public boolean isNullValueDistinct() {
			return finalField.isNullValueDistinct();
		}

		@Override
		public String getNullValueLabel() {
			return finalField.getNullValueLabel();
		}

		public boolean isFormControlMandatory() {
			return finalField.isFormControlMandatory();
		}

		public boolean isFormControlEmbedded() {
			return finalField.isFormControlEmbedded();
		}

		public IInfoFilter getFormControlFilter() {
			return finalField.getFormControlFilter();
		}

		@Override
		public Map getSpecificProperties() {
			return finalField.getSpecificProperties();
		}

		private FieldControlPlaceHolder getOuterType() {
			return FieldControlPlaceHolder.this;
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + getOuterType().hashCode();
			result = prime * result + ((finalField == null) ? 0 : finalField.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			InitialFieldControlData other = (InitialFieldControlData) obj;
			if (!getOuterType().equals(other.getOuterType()))
				return false;
			if (finalField == null) {
				if (other.finalField != null)
					return false;
			} else if (!finalField.equals(other.finalField))
				return false;
			return true;
		}

		@Override
		public String toString() {
			return "InitialFieldControlData [of=" + getOuterType() + ", finalField=" + finalField + "]";
		}

	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy