blob: b1551b5c7269d05b23f86800af96c251f02995bb [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
******************************************************************************/
package org.eclipse.scout.rt.client.ui.form;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.security.Permission;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EventListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.scout.commons.BeanUtility;
import org.eclipse.scout.commons.CollectionUtility;
import org.eclipse.scout.commons.ConfigurationUtility;
import org.eclipse.scout.commons.EventListenerList;
import org.eclipse.scout.commons.StoppableThread;
import org.eclipse.scout.commons.annotations.ClassId;
import org.eclipse.scout.commons.annotations.ConfigOperation;
import org.eclipse.scout.commons.annotations.ConfigProperty;
import org.eclipse.scout.commons.annotations.FormData;
import org.eclipse.scout.commons.annotations.FormData.SdkCommand;
import org.eclipse.scout.commons.annotations.Order;
import org.eclipse.scout.commons.annotations.OrderedComparator;
import org.eclipse.scout.commons.beans.AbstractPropertyObserver;
import org.eclipse.scout.commons.beans.FastPropertyDescriptor;
import org.eclipse.scout.commons.beans.IPropertyFilter;
import org.eclipse.scout.commons.exception.IProcessingStatus;
import org.eclipse.scout.commons.exception.ProcessingException;
import org.eclipse.scout.commons.exception.VetoException;
import org.eclipse.scout.commons.holders.Holder;
import org.eclipse.scout.commons.holders.IHolder;
import org.eclipse.scout.commons.logger.IScoutLogger;
import org.eclipse.scout.commons.logger.ScoutLogManager;
import org.eclipse.scout.commons.xmlparser.SimpleXmlElement;
import org.eclipse.scout.rt.client.BlockingCondition;
import org.eclipse.scout.rt.client.ClientSyncJob;
import org.eclipse.scout.rt.client.IClientSession;
import org.eclipse.scout.rt.client.extension.ui.form.FormChains.FormAddSearchTermsChain;
import org.eclipse.scout.rt.client.extension.ui.form.FormChains.FormCheckFieldsChain;
import org.eclipse.scout.rt.client.extension.ui.form.FormChains.FormCloseTimerChain;
import org.eclipse.scout.rt.client.extension.ui.form.FormChains.FormCreateFormDataChain;
import org.eclipse.scout.rt.client.extension.ui.form.FormChains.FormDataChangedChain;
import org.eclipse.scout.rt.client.extension.ui.form.FormChains.FormDisposeFormChain;
import org.eclipse.scout.rt.client.extension.ui.form.FormChains.FormFormActivatedChain;
import org.eclipse.scout.rt.client.extension.ui.form.FormChains.FormInactivityTimerChain;
import org.eclipse.scout.rt.client.extension.ui.form.FormChains.FormInitFormChain;
import org.eclipse.scout.rt.client.extension.ui.form.FormChains.FormOnCloseRequestChain;
import org.eclipse.scout.rt.client.extension.ui.form.FormChains.FormOnVetoExceptionChain;
import org.eclipse.scout.rt.client.extension.ui.form.FormChains.FormResetSearchFilterChain;
import org.eclipse.scout.rt.client.extension.ui.form.FormChains.FormStoredChain;
import org.eclipse.scout.rt.client.extension.ui.form.FormChains.FormTimerChain;
import org.eclipse.scout.rt.client.extension.ui.form.FormChains.FormValidateChain;
import org.eclipse.scout.rt.client.extension.ui.form.IFormExtension;
import org.eclipse.scout.rt.client.extension.ui.form.MoveFormFieldsHandler;
import org.eclipse.scout.rt.client.services.common.search.ISearchFilterService;
import org.eclipse.scout.rt.client.ui.DataChangeListener;
import org.eclipse.scout.rt.client.ui.IEventHistory;
import org.eclipse.scout.rt.client.ui.WeakDataChangeListener;
import org.eclipse.scout.rt.client.ui.action.ActionUtility;
import org.eclipse.scout.rt.client.ui.action.tool.IToolButton;
import org.eclipse.scout.rt.client.ui.basic.filechooser.FileChooser;
import org.eclipse.scout.rt.client.ui.desktop.AbstractDesktop;
import org.eclipse.scout.rt.client.ui.desktop.IDesktop;
import org.eclipse.scout.rt.client.ui.form.fields.AbstractFormField;
import org.eclipse.scout.rt.client.ui.form.fields.ICompositeField;
import org.eclipse.scout.rt.client.ui.form.fields.IFormField;
import org.eclipse.scout.rt.client.ui.form.fields.IFormFieldFilter;
import org.eclipse.scout.rt.client.ui.form.fields.IValidateContentDescriptor;
import org.eclipse.scout.rt.client.ui.form.fields.IValueField;
import org.eclipse.scout.rt.client.ui.form.fields.button.ButtonEvent;
import org.eclipse.scout.rt.client.ui.form.fields.button.ButtonListener;
import org.eclipse.scout.rt.client.ui.form.fields.button.IButton;
import org.eclipse.scout.rt.client.ui.form.fields.composer.IComposerField;
import org.eclipse.scout.rt.client.ui.form.fields.groupbox.IGroupBox;
import org.eclipse.scout.rt.client.ui.form.fields.tabbox.ITabBox;
import org.eclipse.scout.rt.client.ui.form.fields.wrappedform.IWrappedFormField;
import org.eclipse.scout.rt.client.ui.form.internal.FindFieldByFormDataIdVisitor;
import org.eclipse.scout.rt.client.ui.form.internal.FindFieldByXmlIdsVisitor;
import org.eclipse.scout.rt.client.ui.form.internal.FormDataPropertyFilter;
import org.eclipse.scout.rt.client.ui.messagebox.IMessageBox;
import org.eclipse.scout.rt.client.ui.messagebox.MessageBox;
import org.eclipse.scout.rt.client.ui.profiler.DesktopProfiler;
import org.eclipse.scout.rt.client.ui.wizard.IWizard;
import org.eclipse.scout.rt.client.ui.wizard.IWizardStep;
import org.eclipse.scout.rt.shared.ScoutTexts;
import org.eclipse.scout.rt.shared.TEXTS;
import org.eclipse.scout.rt.shared.data.form.AbstractFormData;
import org.eclipse.scout.rt.shared.data.form.FormDataUtility;
import org.eclipse.scout.rt.shared.data.form.IPropertyHolder;
import org.eclipse.scout.rt.shared.data.form.fields.AbstractFormFieldData;
import org.eclipse.scout.rt.shared.data.form.fields.AbstractValueFieldData;
import org.eclipse.scout.rt.shared.data.form.fields.tablefield.AbstractTableFieldData;
import org.eclipse.scout.rt.shared.data.form.properties.AbstractPropertyData;
import org.eclipse.scout.rt.shared.extension.AbstractExtension;
import org.eclipse.scout.rt.shared.extension.ContributionComposite;
import org.eclipse.scout.rt.shared.extension.ExtensionUtility;
import org.eclipse.scout.rt.shared.extension.IContributionOwner;
import org.eclipse.scout.rt.shared.extension.IExtensibleObject;
import org.eclipse.scout.rt.shared.extension.IExtension;
import org.eclipse.scout.rt.shared.extension.ObjectExtensions;
import org.eclipse.scout.rt.shared.services.common.exceptionhandler.IExceptionHandlerService;
import org.eclipse.scout.rt.shared.services.common.jdbc.SearchFilter;
import org.eclipse.scout.rt.shared.services.common.security.IAccessControlService;
import org.eclipse.scout.service.SERVICES;
@SuppressWarnings("deprecation")
@FormData(value = AbstractFormData.class, sdkCommand = SdkCommand.USE)
public abstract class AbstractForm extends AbstractPropertyObserver implements IForm, IExtensibleObject, IContributionOwner {
private static final IScoutLogger LOG = ScoutLogManager.getLogger(AbstractForm.class);
private boolean m_initialized;
private final EventListenerList m_listenerList = new EventListenerList();
private IFormUIFacade m_uiFacade;
private IWizardStep m_wizardStep;
private boolean m_modal;// no property, is fixed
private boolean m_cacheBounds; // no property is fixed
private boolean m_askIfNeedSave;
private boolean m_buttonsArmed;
private boolean m_closeTimerArmed;
private boolean m_formStored;
private boolean m_formLoading;
private final BlockingCondition m_blockingCondition;
private boolean m_autoRegisterInDesktopOnStart;
private int m_displayHint;// no property, is fixed
private String m_displayViewId;// no property, is fixed
private boolean m_displayHintLocked = false;// no property, is fixed
private int m_closeType = IButton.SYSTEM_TYPE_NONE;
private String m_basicTitle;
private String m_subTitle;
private String m_cancelVerificationText;
private File m_lastXmlFileForStorage;
private IGroupBox m_mainBox;
private IWrappedFormField m_wrappedFormField;
private P_SystemButtonListener m_systemButtonListener;
private List<IToolButton> m_toolbuttons;
private IFormHandler m_handler;
// access control
private boolean m_enabledGranted;
private boolean m_visibleGranted;
// search
private SearchFilter m_searchFilter;
//validate content assistant
private IValidateContentDescriptor m_currentValidateContentDescriptor;
// current timers
private P_CloseTimer m_scoutCloseTimer;
private Map<String, P_Timer> m_scoutTimerMap;
private String m_iconId;
private DataChangeListener m_internalDataChangeListener;
private final IEventHistory<FormEvent> m_eventHistory;
// field replacement support
private Map<Class<?>, Class<? extends IFormField>> m_fieldReplacements;
private IContributionOwner m_contributionHolder;
private final ObjectExtensions<AbstractForm, IFormExtension<? extends AbstractForm>> m_objectExtensions;
private String m_classId;
private int m_toolbarLocation;
public AbstractForm() throws ProcessingException {
this(true);
}
public AbstractForm(boolean callInitializer) throws ProcessingException {
if (DesktopProfiler.getInstance().isEnabled()) {
DesktopProfiler.getInstance().registerForm(this);
}
m_eventHistory = createEventHistory();
m_enabledGranted = true;
m_visibleGranted = true;
m_formLoading = true;
m_blockingCondition = new BlockingCondition(false);
m_objectExtensions = new ObjectExtensions<AbstractForm, IFormExtension<? extends AbstractForm>>(this);
if (callInitializer) {
callInitializer();
}
}
@Override
public final List<? extends IFormExtension<? extends AbstractForm>> getAllExtensions() {
return m_objectExtensions.getAllExtensions();
}
@Override
public <T extends IExtension<?>> T getExtension(Class<T> c) {
return m_objectExtensions.getExtension(c);
}
@Override
public final List<Object> getAllContributions() {
return m_contributionHolder.getAllContributions();
}
@Override
public final <T> List<T> getContributionsByClass(Class<T> type) {
return m_contributionHolder.getContributionsByClass(type);
}
@Override
public final <T> T getContribution(Class<T> contribution) {
return m_contributionHolder.getContribution(contribution);
}
protected void callInitializer() throws ProcessingException {
if (!m_initialized) {
interceptInitConfig();
postInitConfig();
m_initialized = true;
}
}
protected IFormExtension<? extends AbstractForm> createLocalExtension() {
return new LocalFormExtension<AbstractForm>(this);
}
/*
* Configuration
*/
/**
* @return the localized title property of the form. Use {@link TEXTS}.
*/
@ConfigProperty(ConfigProperty.TEXT)
@Order(10)
protected String getConfiguredTitle() {
return null;
}
@ConfigProperty(ConfigProperty.TEXT)
@Order(11)
protected String getConfiguredSubTitle() {
return null;
}
@ConfigProperty(ConfigProperty.INTEGER)
@Order(20)
protected int/* seconds */getConfiguredCloseTimer() {
return 0;
}
@ConfigProperty(ConfigProperty.INTEGER)
@Order(40)
protected int/* seconds */getConfiguredCustomTimer() {
return 0;
}
@ConfigProperty(ConfigProperty.TEXT)
@Order(90)
protected String getConfiguredCancelVerificationText() {
return ScoutTexts.get("FormSaveChangesQuestion");
}
@ConfigProperty(ConfigProperty.FORM_DISPLAY_HINT)
@Order(100)
protected int getConfiguredDisplayHint() {
return DISPLAY_HINT_DIALOG;
}
@ConfigProperty(ConfigProperty.FORM_VIEW_ID)
@Order(105)
protected String getConfiguredDisplayViewId() {
return null;
}
/**
* @return if the form can be minimized.
*/
@ConfigProperty(ConfigProperty.BOOLEAN)
@Order(108)
protected boolean getConfiguredMinimizeEnabled() {
return false;
}
/**
* @return if the form can be maximized.
*/
@ConfigProperty(ConfigProperty.BOOLEAN)
@Order(109)
protected boolean getConfiguredMaximizeEnabled() {
return false;
}
@ConfigProperty(ConfigProperty.BOOLEAN)
@Order(110)
protected boolean getConfiguredMinimized() {
return false;
}
@ConfigProperty(ConfigProperty.BOOLEAN)
@Order(112)
protected boolean getConfiguredMaximized() {
return false;
}
/**
* Modal defines if a form is the only form a user can interact with within the same process as long as this form is
* open.
*
* @return <code>true</code> if form is modal, <code>false</code> otherwise.
*/
@ConfigProperty(ConfigProperty.BOOLEAN)
@Order(120)
protected boolean getConfiguredModal() {
return true;
}
@ConfigProperty(ConfigProperty.BOOLEAN)
@Order(140)
protected boolean getConfiguredCacheBounds() {
return false;
}
@ConfigProperty(ConfigProperty.BOOLEAN)
@Order(150)
protected boolean getConfiguredAskIfNeedSave() {
return true;
}
/**
* @return configured icon ID for this form
*/
@ConfigProperty(ConfigProperty.ICON_ID)
@Order(160)
protected String getConfiguredIconId() {
return null;
}
/**
* one of {@link IForm#TOOLBAR_FORM_HEADER} or {@link IForm#TOOLBAR_VIEW_PART}. The {@link IForm#TOOLBAR_VIEW_PART} is
* only considered if the form is displayed as a view (display hint {@link IForm#DISPLAY_HINT_VIEW} and SWT
* representation. In all other cases the fallback {@link IForm#TOOLBAR_FORM_HEADER} is taken.
*
* @return {@link IForm#TOOLBAR_FORM_HEADER} | {@link IForm#TOOLBAR_VIEW_PART}
*/
@ConfigProperty(ConfigProperty.TOOLBAR_LOCATION)
@Order(170)
protected int getConfiguredToolbarLocation() {
return TOOLBAR_FORM_HEADER;
}
/**
* This method is called to get an exclusive key of the form. The key is used
* to open the same form with the same handler only once. Obviously this
* behavior can only be used for view forms.
*
* @see AbstractDesktop#getSimilarViewForms(IForm)
* @return null for exclusive form behavior an exclusive key to ensure similar
* handling.
* @throws ProcessingException
*/
@Override
public Object computeExclusiveKey() throws ProcessingException {
return null;
}
/**
* Initialize the form and all of its fields.
* By default any of the #start* methods of the form call this method
* <p>
* This method is called in the process of the initialization. The UI is not ready yet.
*
* @throws ProcessingException
*/
@ConfigOperation
@Order(10)
protected void execInitForm() throws ProcessingException {
}
/**
* This method is called when UI is ready.
*
* @throws ProcessingException
*/
@ConfigOperation
@Order(11)
protected void execFormActivated() throws ProcessingException {
}
/**
* see {@link IDesktop#dataChanged(Object...)}
*/
@ConfigOperation
@Order(13)
protected void execDataChanged(Object... dataTypes) throws ProcessingException {
}
/**
* This method is called in order to check field validity.<br>
* This method is called before {@link IFormHandler#interceptCheckFields()} and
* before the form is validated and stored.<br>
* After this method, the form is checking fields itself and displaying a
* dialog with missing and invalid fields.
*
* @return true when this check is done and further checks can continue, false
* to silently cancel the current process
* @throws ProcessingException
* to cancel the current process with error handling and user
* notification such as a dialog
*/
@ConfigOperation
@Order(13)
protected boolean execCheckFields() throws ProcessingException {
return true;
}
/**
* This method is called in order to update derived states like button
* enablings.<br>
* This method is called before {@link IFormHandler#interceptValidate()} and before
* the form is stored.
*
* @return true when validate is successful, false to silently cancel the
* current process
* @throws ProcessingException
* to cancel the current process with error handling and user
* notification such as a dialog
*/
@ConfigOperation
@Order(14)
protected boolean execValidate() throws ProcessingException {
return true;
}
/**
* This method is called in order to update pages on the desktop after the
* form stored data.<br>
* This method is called after {@link IFormHandler#execStore()}.
*/
@ConfigOperation
@Order(16)
protected void execStored() throws ProcessingException {
}
/**
* @throws ProcessingException
* / {@link VetoException} if the exception should produce further
* info messages (default)
*/
@ConfigOperation
@Order(17)
protected void execOnVetoException(VetoException e, int code) throws ProcessingException {
throw e;
}
/**
* @param kill
* true if a widget close icon (normally the X on the titlebar) was
* pressed or ESC was pressed
* @param enabledButtonSystemTypes
* set of all {@link IButton#SYSTEM_TYPE_*} of all enabled and
* visible buttons of this form
*/
@ConfigOperation
@Order(18)
protected void execOnCloseRequest(boolean kill, final HashSet<Integer> enabledButtonSystemTypes) throws ProcessingException {
if (enabledButtonSystemTypes.contains(IButton.SYSTEM_TYPE_CLOSE)) {
doClose();
}
else if (enabledButtonSystemTypes.contains(IButton.SYSTEM_TYPE_CANCEL)) {
doCancel();
}
else if (!isAskIfNeedSave()) {
doClose();
}
else {
LOG.info("Trying to close a form (" + getClass().getName() + " - " + getTitle() + ") with no enabled close button! override getConfiguredAskIfNeedSave() to false to make this form is unsaveable.");
}
}
@ConfigOperation
@Order(19)
protected void execDisposeForm() throws ProcessingException {
}
@ConfigOperation
@Order(20)
protected void execCloseTimer() throws ProcessingException {
doClose();
}
@ConfigOperation
@Order(30)
protected void execInactivityTimer() throws ProcessingException {
doClose();
}
@ConfigOperation
@Order(40)
protected void execTimer(String timerId) throws ProcessingException {
LOG.info("execTimer " + timerId);
}
/**
* add verbose information to the search filter
*/
@ConfigOperation
@Order(50)
protected void execAddSearchTerms(SearchFilter search) {
}
private Class<? extends IGroupBox> getConfiguredMainBox() {
Class[] dca = ConfigurationUtility.getDeclaredPublicClasses(getClass());
return ConfigurationUtility.filterClassIgnoringInjectFieldAnnotation(dca, IGroupBox.class);
}
private List<Class<IFormField>> getConfiguredInjectedFields() {
Class[] dca = ConfigurationUtility.getDeclaredPublicClasses(getClass());
return ConfigurationUtility.filterClassesWithInjectFieldAnnotation(dca, IFormField.class);
}
protected List<Class<IToolButton>> getConfiguredToolButtons() {
Class<?>[] dca = ConfigurationUtility.getDeclaredPublicClasses(getClass());
return ConfigurationUtility.filterClasses(dca, IToolButton.class);
}
protected final void interceptInitConfig() throws ProcessingException {
final Holder<ProcessingException> exceptionHolder = new Holder<ProcessingException>(ProcessingException.class, null);
m_objectExtensions.initConfig(createLocalExtension(), new Runnable() {
@Override
public void run() {
try {
initConfig();
}
catch (ProcessingException e) {
exceptionHolder.setValue(e);
}
}
});
if (exceptionHolder.getValue() != null) {
throw exceptionHolder.getValue();
}
}
protected void initConfig() throws ProcessingException {
m_uiFacade = new P_UIFacade();
m_scoutTimerMap = new HashMap<String, P_Timer>();
m_autoRegisterInDesktopOnStart = true;
m_contributionHolder = new ContributionComposite(this);
setToolbarLocation(getConfiguredToolbarLocation());
// tool buttons
List<Class<IToolButton>> configuredToolButtons = getConfiguredToolButtons();
List<IToolButton> toolButtonList = new ArrayList<IToolButton>(configuredToolButtons.size());
for (Class<? extends IToolButton> clazz : configuredToolButtons) {
try {
IToolButton b = ConfigurationUtility.newInnerInstance(this, clazz);
toolButtonList.add(b);
}
catch (Throwable t) {
SERVICES.getService(IExceptionHandlerService.class).handleException(new ProcessingException("Unable to create ToolButton '" + clazz.getName() + "'.", t));
}
}
List<IToolButton> contributedToolButtons = m_contributionHolder.getContributionsByClass(IToolButton.class);
toolButtonList.addAll(contributedToolButtons);
ExtensionUtility.moveModelObjects(toolButtonList);
Collections.sort(toolButtonList, new OrderedComparator());
m_toolbuttons = toolButtonList;
// prepare injected fields
List<Class<IFormField>> fieldArray = getConfiguredInjectedFields();
DefaultFormFieldInjection injectedFields = null;
IGroupBox rootBox = getRootGroupBox();
try {
if (fieldArray.size() > 0) {
injectedFields = new DefaultFormFieldInjection(this);
injectedFields.addFields(fieldArray);
FormFieldInjectionThreadLocal.push(injectedFields);
}
// add mainbox if getter returns null
if (rootBox == null) {
List<IGroupBox> contributedFields = m_contributionHolder.getContributionsByClass(IGroupBox.class);
rootBox = CollectionUtility.firstElement(contributedFields);
if (rootBox == null) {
Class<? extends IGroupBox> mainBoxClass = getConfiguredMainBox();
try {
rootBox = ConfigurationUtility.newInnerInstance(this, mainBoxClass);
}
catch (Throwable t) {
String mainBoxName = null;
if (mainBoxClass == null) {
mainBoxName = "null";
}
else {
mainBoxName = mainBoxClass.getName();
}
SERVICES.getService(IExceptionHandlerService.class).handleException(new ProcessingException("error creating instance of class '" + mainBoxName + "'.", t));
}
}
m_mainBox = rootBox;
}
}
finally {
if (injectedFields != null) {
m_fieldReplacements = injectedFields.getReplacementMapping();
FormFieldInjectionThreadLocal.pop(injectedFields);
}
}
if (rootBox != null) {
rootBox.setFormInternal(this);
rootBox.setMainBox(true);
rootBox.updateKeyStrokes();
}
// move form fields
new MoveFormFieldsHandler(this).moveFields();
//
if (getConfiguredCloseTimer() > 0) {
setCloseTimer(getConfiguredCloseTimer());
}
if (getConfiguredCustomTimer() > 0) {
setTimer("custom", getConfiguredCustomTimer());
}
setModal(getConfiguredModal());
setDisplayHint(getConfiguredDisplayHint());
setDisplayViewId(getConfiguredDisplayViewId());
if (getConfiguredCancelVerificationText() != null) {
setCancelVerificationText(getConfiguredCancelVerificationText());
}
if (getConfiguredTitle() != null) {
setTitle(getConfiguredTitle());
}
if (getConfiguredSubTitle() != null) {
setSubTitle(getConfiguredSubTitle());
}
setMinimizeEnabled(getConfiguredMinimizeEnabled());
setMaximizeEnabled(getConfiguredMaximizeEnabled());
setMinimized(getConfiguredMinimized());
setMaximized(getConfiguredMaximized());
setCacheBounds(getConfiguredCacheBounds());
setAskIfNeedSave(getConfiguredAskIfNeedSave());
setIconId(getConfiguredIconId());
// visit all system buttons and attach observer
m_systemButtonListener = new P_SystemButtonListener();// is auto-detaching
IFormFieldVisitor v2 = new IFormFieldVisitor() {
@Override
public boolean visitField(IFormField field, int level, int fieldIndex) {
if (field instanceof IButton) {
if (((IButton) field).getSystemType() != IButton.SYSTEM_TYPE_NONE) {
((IButton) field).addButtonListener(m_systemButtonListener);
}
}
return true;
}
};
visitFields(v2);
getRootGroupBox().addPropertyChangeListener(new P_MainBoxPropertyChangeProxy());
setButtonsArmed(true);
}
@Override
public void setEnabledPermission(Permission p) {
boolean b;
if (p != null) {
b = SERVICES.getService(IAccessControlService.class).checkPermission(p);
}
else {
b = true;
}
setEnabledGranted(b);
}
@Override
public boolean isEnabledGranted() {
return m_enabledGranted;
}
@Override
public void setEnabledGranted(boolean b) {
m_enabledGranted = b;
IGroupBox box = getRootGroupBox();
if (box != null) {
box.setEnabledGranted(b);
}
}
@Override
public void setVisiblePermission(Permission p) {
boolean b;
if (p != null) {
b = SERVICES.getService(IAccessControlService.class).checkPermission(p);
}
else {
b = true;
}
setVisibleGranted(b);
}
@Override
public boolean isVisibleGranted() {
return m_visibleGranted;
}
@Override
public void setVisibleGranted(boolean b) {
m_visibleGranted = b;
IGroupBox box = getRootGroupBox();
if (box != null) {
box.setVisibleGranted(b);
}
}
@Override
public String getIconId() {
return propertySupport.getPropertyString(PROP_ICON_ID);
}
@Override
public void setIconId(String iconId) {
propertySupport.setPropertyString(PROP_ICON_ID, iconId);
}
@Override
public String getPerspectiveId() {
return propertySupport.getPropertyString(PROP_PERSPECTIVE_ID);
}
@Override
public void setPerspectiveId(String perspectiveId) {
propertySupport.setPropertyString(PROP_PERSPECTIVE_ID, perspectiveId);
}
@Override
public List<IToolButton> getToolButtons() {
return CollectionUtility.arrayList(m_toolbuttons);
}
/**
* @param configuredToolbuttonLocation
*/
private void setToolbarLocation(int toolbarLocation) {
m_toolbarLocation = toolbarLocation;
}
@Override
public int getToolbarLocation() {
return m_toolbarLocation;
}
@Override
public <T extends IToolButton> T getToolButtonByClass(Class<T> clazz) {
for (IToolButton b : m_toolbuttons) {
if (b.getClass() == clazz) {
@SuppressWarnings("unchecked")
T button = (T) b;
return button;
}
}
return null;
}
/**
* Register a {@link DataChangeListener} on the desktop for these dataTypes<br>
* Example:
*
* <pre>
* registerDataChangeListener(CRMEnum.Company, CRMEnum.Project, CRMEnum.Task);
* </pre>
*/
public void registerDataChangeListener(Object... dataTypes) {
if (m_internalDataChangeListener == null) {
m_internalDataChangeListener = new WeakDataChangeListener() {
@Override
public void dataChanged(Object... innerDataTypes) throws ProcessingException {
interceptDataChanged(innerDataTypes);
}
};
}
IDesktop desktop = getDesktop();
if (desktop == null) {
desktop = ClientSyncJob.getCurrentSession().getVirtualDesktop();
}
desktop.addDataChangeListener(m_internalDataChangeListener, dataTypes);
}
/**
* Unregister the {@link DataChangeListener} from the desktop for these
* dataTypes<br>
* Example:
*
* <pre>
* unregisterDataChangeListener(CRMEnum.Company, CRMEnum.Project, CRMEnum.Task);
* </pre>
*/
public void unregisterDataChangeListener(Object... dataTypes) {
if (m_internalDataChangeListener != null) {
getDesktop().removeDataChangeListener(m_internalDataChangeListener, dataTypes);
}
}
protected IForm startInternalExclusive(IFormHandler handler) throws ProcessingException {
if (m_blockingCondition.isBlocking()) {
throw new ProcessingException("The form " + getFormId() + " has already been started");
}
for (IForm simCandidate : getDesktop().getSimilarViewForms(this)) {
if (handler != null && simCandidate.getHandler() != null && handler.getClass().getName() == simCandidate.getHandler().getClass().getName()) {
if (simCandidate.getHandler().isOpenExclusive() && handler.isOpenExclusive()) {
getDesktop().ensureVisible(simCandidate);
return simCandidate;
}
}
}
return startInternal(handler);
}
/**
* This method is called from the implemented handler methods in a explicit
* form subclass
*/
protected IForm startInternal(final IFormHandler handler) throws ProcessingException {
if (m_blockingCondition.isBlocking()) {
throw new ProcessingException("The form " + getFormId() + " has already been started");
}
setHandler(handler);
m_closeType = IButton.SYSTEM_TYPE_NONE;
m_displayHintLocked = true;
m_blockingCondition.setBlocking(true);
try {
// check if form was made invisible ( = access control denied access)
if (!getRootGroupBox().isVisible()) {
disposeFormInternal();
return this;
}
initForm();
// check if form was made invisible ( = access control denied access)
if (!getRootGroupBox().isVisible()) {
// make sure the form is storing since it is not showing
disposeFormInternal();
return this;
}
loadStateInternal();
// check if form was made invisible ( = access control denied access)
if (!isFormOpen()) {
disposeFormInternal();
return this;
}
if (!getRootGroupBox().isVisible()) {
disposeFormInternal();
return this;
}
if (getHandler().isGuiLess()) {
// make sure the form is storing since it is not showing
storeStateInternal();
markSaved();
doFinally();
disposeFormInternal();
return this;
}
}
catch (ProcessingException e) {
e.addContextMessage(AbstractForm.this.getClass().getSimpleName());
disposeFormInternal();
if (e instanceof VetoException) {
interceptOnVetoException((VetoException) e, e.getStatus().getCode());
}
else {
throw e;
}
}
catch (Throwable t) {
disposeFormInternal();
throw new ProcessingException("failed showing " + getTitle(), t);
}
// request a gui
setButtonsArmed(true);
setCloseTimerArmed(true);
// register in desktop or wizard (legacy wizard only)
if (isAutoAddRemoveOnDesktop()) {
IDesktop desktop = getDesktop();
if (desktop != null) {
desktop.addForm(this);
}
}
return this;
}
@Override
public void startWizardStep(IWizardStep wizardStep, Class<? extends IFormHandler> handlerType) throws ProcessingException {
setAutoAddRemoveOnDesktop(false);
IFormHandler formHandler = null;
if (handlerType != null) {
try {
formHandler = ConfigurationUtility.newInnerInstance(this, handlerType);
}
catch (Exception e) {
SERVICES.getService(IExceptionHandlerService.class).handleException(new ProcessingException("error creating instance of class '" + handlerType.getName() + "'.", e));
}
}
m_wizardStep = wizardStep;
setModal(false);
setAskIfNeedSave(false);
// hide top level process buttons with a system type
for (IFormField f : getRootGroupBox().getFields()) {
if (f instanceof IButton) {
IButton b = (IButton) f;
if (b.getSystemType() != IButton.SYSTEM_TYPE_NONE) {
// hide
b.setVisible(false);
b.setVisibleGranted(false);
}
}
}
//
// start
startInternal(formHandler);
}
@Override
public void waitFor() throws ProcessingException {
// check if the desktop is observing this process
IDesktop desktop = ClientSyncJob.getCurrentSession().getDesktop();
if (desktop == null || !desktop.isOpened()) {
throw new ProcessingException("Cannot wait for " + getClass().getName() + ". There is no desktop or the desktop has not yet been opened in the ui", null, WAIT_FOR_ERROR_CODE);
}
// wait
try {
m_blockingCondition.waitFor();
}
catch (InterruptedException e) {
throw new ProcessingException(ScoutTexts.get("UserInterrupted"), e);
}
}
private void exportExtensionProperties(Object o, IPropertyHolder target) throws ProcessingException {
if (!(o instanceof IExtensibleObject)) {
return;
}
for (IExtension<?> ex : ((IExtensibleObject) o).getAllExtensions()) {
Class<?> dto = FormDataUtility.getDataAnnotationValue(ex.getClass());
if (dto != null && !Object.class.equals(dto)) {
Object propertyTarget = target.getContribution(dto);
Map<String, Object> fieldProperties = BeanUtility.getProperties(ex, AbstractFormField.class, new FormDataPropertyFilter());
BeanUtility.setProperties(propertyTarget, fieldProperties, false, null);
}
}
}
@Override
public void exportFormData(final AbstractFormData target) throws ProcessingException {
// locally declared form properties
Map<String, Object> properties = BeanUtility.getProperties(this, AbstractForm.class, new FormDataPropertyFilter());
BeanUtility.setProperties(target, properties, false, null);
// properties in extensions of form
exportExtensionProperties(this, target);
final Set<IFormField> exportedFields = new HashSet<IFormField>();
// all fields
Map<Integer, Map<String/* qualified field id */, AbstractFormFieldData>> breadthFirstMap = target.getAllFieldsRec();
for (Map<String/* qualified field id */, AbstractFormFieldData> targetMap : breadthFirstMap.values()) {
for (Map.Entry<String, AbstractFormFieldData> e : targetMap.entrySet()) {
String fieldQId = e.getKey();
AbstractFormFieldData data = e.getValue();
FindFieldByFormDataIdVisitor v = new FindFieldByFormDataIdVisitor(fieldQId, this);
visitFields(v);
IFormField f = v.getField();
if (f != null) {
// field properties
properties = BeanUtility.getProperties(f, AbstractFormField.class, new FormDataPropertyFilter());
BeanUtility.setProperties(data, properties, false, null);
exportExtensionProperties(f, data);
// field state
f.exportFormFieldData(data);
// remember exported fields
exportedFields.add(f);
}
else {
LOG.warn("Cannot find field with id '" + fieldQId + "' in form '" + getClass().getName() + "' for DTO '" + data.getClass().getName() + "'.");
}
}
}
// visit remaining fields (there could be an extension with properties e.g. on a groupbox)
final Holder<ProcessingException> exHolder = new Holder<ProcessingException>(ProcessingException.class);
visitFields(new IFormFieldVisitor() {
@Override
public boolean visitField(IFormField field, int level, int fieldIndex) {
if (exportedFields.contains(field)) {
// already exported -> skip
return true;
}
try {
exportExtensionProperties(field, target);
}
catch (ProcessingException e) {
exHolder.setValue(e);
}
return exHolder.getValue() == null;
}
});
if (exHolder.getValue() != null) {
throw exHolder.getValue();
}
}
@Override
public void importFormData(AbstractFormData source) throws ProcessingException {
importFormData(source, false, null);
}
@Override
public void importFormData(AbstractFormData source, boolean valueChangeTriggersEnabled) throws ProcessingException {
importFormData(source, valueChangeTriggersEnabled, null);
}
@Override
public void importFormData(AbstractFormData source, boolean valueChangeTriggersEnabled, IPropertyFilter filter) throws ProcessingException {
importFormData(source, valueChangeTriggersEnabled, filter, null);
}
private void removeNotSetProperties(IPropertyHolder dto, Map<String, Object> properties) {
for (Iterator<String> it = properties.keySet().iterator(); it.hasNext();) {
String propertyId = it.next();
AbstractPropertyData pd = dto.getPropertyById(propertyId);
if (pd != null && !pd.isValueSet()) {
it.remove();
}
}
}
private void importProperties(IPropertyHolder source, Object target, Class<?> stopClass, IPropertyFilter filter) throws ProcessingException {
// local properties
Map<String, Object> properties = BeanUtility.getProperties(source, stopClass, filter);
if (!properties.isEmpty()) {
removeNotSetProperties(source, properties);
BeanUtility.setProperties(target, properties, false, null);
}
// properties of the extensions
List<Object> allContributions = source.getAllContributions();
if (!allContributions.isEmpty()) {
for (Object con : allContributions) {
if (con instanceof IPropertyHolder) {
IPropertyHolder data = (IPropertyHolder) con;
Map<String, Object> extensionProperties = BeanUtility.getProperties(data, stopClass, filter);
if (!extensionProperties.isEmpty()) {
Object clientPart = getClientPartOfExtensionOrContributionRec(data, target);
if (clientPart != null) {
removeNotSetProperties(data, extensionProperties);
BeanUtility.setProperties(clientPart, extensionProperties, false, null);
}
else {
LOG.warn("cannot find extension for property data '" + data.getClass().getName() + "' in form '" + this.getClass().getName() + "'.");
}
}
}
}
}
}
private Object getClientPartOfExtensionOrContribution(Object extToSearch, Object owner) {
if (owner instanceof IExtensibleObject) {
IExtensibleObject exOwner = (IExtensibleObject) owner;
for (IExtension<?> ex : exOwner.getAllExtensions()) {
Class<?> dto = FormDataUtility.getDataAnnotationValue(ex.getClass());
if (extToSearch.getClass().equals(dto)) {
return ex;
}
}
}
if (owner instanceof IContributionOwner) {
IContributionOwner compOwner = (IContributionOwner) owner;
for (Object o : compOwner.getAllContributions()) {
FormData annotation = o.getClass().getAnnotation(FormData.class);
if (annotation != null && annotation.value().equals(extToSearch.getClass())) {
return o;
}
}
}
return null;
}
private Object getClientPartOfExtensionOrContributionRec(final Object extToSearch, Object owner) {
Object ext = getClientPartOfExtensionOrContribution(extToSearch, owner);
if (ext != null) {
return ext;
}
// search for the extension in the children
final IHolder<Object> result = new Holder<Object>(Object.class);
IFormFieldVisitor visitor = new IFormFieldVisitor() {
@Override
public boolean visitField(IFormField field, int level, int fieldIndex) {
result.setValue(getClientPartOfExtensionOrContribution(extToSearch, field));
return result.getValue() == null;
}
};
if (owner instanceof IForm) {
((IForm) owner).visitFields(visitor);
}
else if (owner instanceof ICompositeField) {
((ICompositeField) owner).visitFields(visitor, 0);
}
else if (owner instanceof IWrappedFormField) {
((IWrappedFormField) owner).visitFields(visitor, 0);
}
return result.getValue();
}
private static Class<?> getFieldStopClass(Object data) {
if (data instanceof AbstractTableFieldData) {
return AbstractTableFieldData.class;
}
else if (data instanceof AbstractValueFieldData) {
return AbstractValueFieldData.class;
}
else {
return AbstractFormFieldData.class;
}
}
@Override
public void importFormData(AbstractFormData source, boolean valueChangeTriggersEnabled, IPropertyFilter filter, IFormFieldFilter formFieldFilter) throws ProcessingException {
if (filter == null) {
filter = new FormDataPropertyFilter();
}
// form properties
importProperties(source, this, AbstractFormData.class, filter);
// sort fields, first non-slave fields, then slave fields in transitive order
LinkedList<IFormField> masterList = new LinkedList<IFormField>();
LinkedList<IFormField> slaveList = new LinkedList<IFormField>();
HashMap<IFormField, AbstractFormFieldData> dataMap = new HashMap<IFormField, AbstractFormFieldData>();
// collect fields and split them into masters/slaves
Map<Integer, Map<String/* qualified field id */, AbstractFormFieldData>> breadthFirstMap = source.getAllFieldsRec();
for (Map<String/* qualified field id */, AbstractFormFieldData> sourceMap : breadthFirstMap.values()) {
for (Map.Entry<String, AbstractFormFieldData> e : sourceMap.entrySet()) {
String fieldQId = e.getKey();
AbstractFormFieldData data = e.getValue();
FindFieldByFormDataIdVisitor v = new FindFieldByFormDataIdVisitor(fieldQId, this);
visitFields(v);
IFormField f = v.getField();
if (f != null) {
if (formFieldFilter == null || formFieldFilter.accept(f)) {
dataMap.put(f, data);
if (f.getMasterField() != null) {
int index = slaveList.indexOf(f.getMasterField());
if (index >= 0) {
slaveList.add(index + 1, f);
}
else {
slaveList.addFirst(f);
}
}
else {
masterList.add(f);
}
}
}
else {
LOG.warn("cannot find field data for '" + fieldQId + "' in form '" + getClass().getName() + "'.");
}
}
}
for (IFormField f : masterList) {
importFormField(f, dataMap, valueChangeTriggersEnabled, filter);
}
for (IFormField f : slaveList) {
importFormField(f, dataMap, valueChangeTriggersEnabled, filter);
}
}
private void importFormField(IFormField f, Map<IFormField, AbstractFormFieldData> dataMap, boolean valueChangeTriggersEnabled, IPropertyFilter filter) throws ProcessingException {
AbstractFormFieldData data = dataMap.get(f);
// form field properties
importProperties(data, f, getFieldStopClass(data), filter);
// field state
f.importFormFieldData(data, valueChangeTriggersEnabled);
}
public static String parseFormId(String className) {
String s = className;
int i = Math.max(s.lastIndexOf('$'), s.lastIndexOf('.'));
s = s.substring(i + 1);
return s;
}
@Override
public String getFormId() {
return parseFormId(getClass().getName());
}
/**
* <p>
* <li>If a classId was set with {@link #setClassId(String)} this value is returned.
* <li>Else if the class is annotated with {@link ClassId}, the annotation value is returned.
* <li>Otherwise the class name is returned.
*/
@Override
public String classId() {
if (m_classId != null) {
return m_classId;
}
String simpleClassId = ConfigurationUtility.getAnnotatedClassIdWithFallback(getClass());
if (getOuterFormField() != null) {
return simpleClassId + ID_CONCAT_SYMBOL + getOuterFormField().classId();
}
return simpleClassId;
}
@Override
public void setClassId(String classId) {
m_classId = classId;
}
@Override
public IFormHandler getHandler() {
return m_handler;
}
@Override
public void setHandler(IFormHandler handler) {
if (handler != m_handler) {
if (m_handler != null) {
m_handler.setFormInternal(null);
}
if (handler == null) {
handler = new NullFormHandler();
}
m_handler = handler;
m_handler.setFormInternal(this);
}
}
@Override
public IWizard getWizard() {
return (getWizardStep() != null) ? getWizardStep().getWizard() : null;
}
@Override
public IWizardStep getWizardStep() {
return m_wizardStep;
}
@Override
public IFormField getFocusOwner() {
if (getDesktop() != null) {
IFormField field = getDesktop().getFocusOwner();
if (field != null) {
IForm form = field.getForm();
while (form != null) {
if (form == this) {
return field;
}
// next
form = form.getOuterForm();
}
}
}
return null;
}
@Override
public List<IFormField> getAllFields() {
P_AbstractCollectingFieldVisitor<IFormField> v = new P_AbstractCollectingFieldVisitor<IFormField>() {
@Override
public boolean visitField(IFormField field, int level, int fieldIndex) {
collect(field);
return true;
}
};
visitFields(v);
return v.getCollection();
}
@Override
public boolean visitFields(IFormFieldVisitor visitor) {
return getRootGroupBox().visitFields(visitor, 0);
}
/**
* Convenience for ClientJob.getCurrentSession().getDesktop()
*/
public IDesktop getDesktop() {
return ClientSyncJob.getCurrentSession().getDesktop();
}
@Override
public final SearchFilter getSearchFilter() {
if (m_searchFilter == null) {
resetSearchFilter();
}
return m_searchFilter;
}
@Override
public final void setSearchFilter(SearchFilter searchFilter) {
m_searchFilter = searchFilter;
}
/**
* Alias for {@link #resetSearchFilter()}
*/
public void rebuildSearchFilter() {
resetSearchFilter();
}
@Override
public void resetSearchFilter() {
if (m_searchFilter == null) {
SearchFilter filter;
ISearchFilterService sfs = SERVICES.getService(ISearchFilterService.class);
if (sfs != null) {
filter = sfs.createNewSearchFilter();
}
else {
filter = new SearchFilter();
}
m_searchFilter = filter;
}
try {
interceptResetSearchFilter(m_searchFilter);
}
catch (ProcessingException e) {
SERVICES.getService(IExceptionHandlerService.class).handleException(e);
}
catch (Throwable t) {
SERVICES.getService(IExceptionHandlerService.class).handleException(new ProcessingException("Unexpected", t));
}
}
/**
* Called when saving the form via {@link #resetSearchFilter()}.
* <p>
* This operation fills up the search filter and subclass override sets the formData property of the
* {@link SearchFilter#setFormData(AbstractFormData)} and adds verbose texts with
* {@link SearchFilter#addDisplayText(String)}
* <p>
* May call {@link #setSearchFilter(SearchFilter)}
* <p>
* Implement {@link #interceptCreateFormData()} to automatically attach a filled form data to the search filter (since
* 3.8).
*
* @param searchFilter
* is never null
*/
@ConfigOperation
@Order(10)
protected void execResetSearchFilter(final SearchFilter searchFilter) throws ProcessingException {
searchFilter.clear();
// add verbose field texts
// do not use visitor, so children can block traversal on whole subtrees
getRootGroupBox().applySearch(searchFilter);
// add verbose form texts
interceptAddSearchTerms(searchFilter);
// override may add form data
try {
AbstractFormData data = interceptCreateFormData();
if (data != null) {
exportFormData(data);
getSearchFilter().setFormData(data);
}
}
catch (ProcessingException e) {
throw e;
}
catch (Throwable t) {
throw new ProcessingException("Create form data", t);
}
}
/**
* Convenience to add form data to search filter and load/store form data.
* override this method to create an empty search form data that can be used for example by
* {@link #interceptResetSearchFilter(SearchFilter)}
*
* @since 3.8
*/
@ConfigOperation
@Order(11)
protected AbstractFormData execCreateFormData() throws ProcessingException {
return null;
}
@Override
public boolean isFormStored() {
return m_formStored;
}
@Override
public void setFormStored(boolean b) {
m_formStored = b;
}
@Override
public boolean isFormLoading() {
return m_formLoading;
}
private void setFormLoading(boolean b) {
m_formLoading = b;
}
/**
* Mainbox getter
*/
@Override
public IGroupBox getRootGroupBox() {
return m_mainBox;
}
@Override
public IForm getOuterForm() {
return m_wrappedFormField != null ? m_wrappedFormField.getForm() : null;
}
@Override
public IWrappedFormField getOuterFormField() {
return m_wrappedFormField;
}
@Override
public void setWrapperFieldInternal(IWrappedFormField w) {
m_wrappedFormField = w;
}
@Override
public IFormField getFieldById(final String id) {
return getRootGroupBox().getFieldById(id);
}
@Override
public <T extends IFormField> T getFieldById(String id, Class<T> type) {
return getRootGroupBox().getFieldById(id, type);
}
@Override
public <T extends IFormField> T getFieldByClass(Class<T> c) {
return getRootGroupBox().getFieldByClass(c);
}
/**
* override in subclasses to perform form initialization before handler starts
*/
protected void postInitConfig() throws ProcessingException {
FormUtility.postInitConfig(this);
FormUtility.rebuildFieldGrid(this, true);
}
@Override
public void initForm() throws ProcessingException {
// form
initFormInternal();
// fields
FormUtility.initFormFields(this);
ActionUtility.initActions(getToolButtons());
// custom
interceptInitForm();
}
protected void initFormInternal() throws ProcessingException {
}
@Override
public int getCloseSystemType() {
return m_closeType;
}
@Override
public void setCloseSystemType(int type) {
m_closeType = type;
}
/**
* do not use or override this internal method
*/
protected void loadStateInternal() throws ProcessingException {
fireFormLoadBefore();
if (!isFormOpen()) {
return;
}
getHandler().onLoad();
if (!isFormOpen()) {
return;
}
fireFormLoadAfter();
if (!isFormOpen()) {
return;
}
// set all values to 'unchanged'
markSaved();
setFormLoading(false);
getHandler().onPostLoad();
if (!isFormOpen()) {
return;
}
// if not visible mode, mark form changed
if (getHandler().isGuiLess()) {
touch();
}
fireFormLoadComplete();
}
/**
* Store state of form, regardless of validity and completeness do not use or
* override this internal method directly
*/
protected void storeStateInternal() throws ProcessingException {
if (!m_blockingCondition.isBlocking()) {
String msg = TEXTS.get("FormDisposedMessage", getTitle());
LOG.error(msg);
throw new VetoException(msg);
}
fireFormStoreBefore();
m_formStored = true;
try {
rebuildSearchFilter();
m_searchFilter.setCompleted(true);
getHandler().onStore();
interceptStored();
if (!m_formStored) {
//the form was marked as not stored in AbstractFormHandler#execStore() or AbstractForm#execStored().
ProcessingException e = new ProcessingException("Form was marked as not stored.");
e.consume();
throw e;
}
}
catch (ProcessingException e) {
// clear search
if (m_searchFilter != null) {
m_searchFilter.clear();
}
// store was not successfully stored
m_formStored = false;
throwVetoExceptionInternal(e);
// if exception was caught and suppressed, this form was after all successfully stored
// normally this code is not reached since the exception will be passed out
m_formStored = true;
}
catch (Throwable t) {
// clear search
if (m_searchFilter != null) {
m_searchFilter.clear();
}
throw new ProcessingException("form: " + getTitle(), t);
}
fireFormStoreAfter();
}
/**
* do not use or override this internal method
*/
protected void discardStateInternal() throws ProcessingException {
fireFormDiscarded();
getHandler().onDiscard();
}
@Override
public void setCloseTimer(int seconds) {
if (seconds > 0) {
setCloseTimerArmed(true);
m_scoutCloseTimer = new P_CloseTimer(seconds);
m_scoutCloseTimer.start();
}
else {
removeCloseTimer();
}
}
/**
* do not use or override this internal method
*/
protected void throwVetoExceptionInternal(ProcessingException e) throws ProcessingException {
if (e instanceof VetoException) {
if (!e.isConsumed()) {
interceptOnVetoException((VetoException) e, e.getStatus().getCode());
// if it was not re-thrown it is assumed to be consumed
e.consume();
}
}
throw e;
}
@Override
public void removeCloseTimer() {
setCloseTimerArmed(false);
m_scoutCloseTimer = null;
setSubTitle(null);
}
@Override
public void validateForm() throws ProcessingException {
m_currentValidateContentDescriptor = null;
if (!interceptCheckFields()) {
VetoException veto = new VetoException("Validate " + getClass().getSimpleName());
veto.consume();
throw veto;
}
if (!getHandler().onCheckFields()) {
VetoException veto = new VetoException("Validate " + getClass().getSimpleName());
veto.consume();
throw veto;
}
// check all fields that might be invalid
final ArrayList<String> invalidTexts = new ArrayList<String>();
final ArrayList<String> mandatoryTexts = new ArrayList<String>();
P_AbstractCollectingFieldVisitor<IValidateContentDescriptor> v = new P_AbstractCollectingFieldVisitor<IValidateContentDescriptor>() {
@Override
public boolean visitField(IFormField f, int level, int fieldIndex) {
IValidateContentDescriptor desc = f.validateContent();
if (desc != null) {
if (desc.getErrorStatus() != null) {
invalidTexts.add(desc.getDisplayText() + ": " + desc.getErrorStatus().getMessage());
}
else {
mandatoryTexts.add(desc.getDisplayText());
}
if (getCollectionCount() == 0) {
collect(desc);
}
}
return true;
}
};
visitFields(v);
if (v.getCollectionCount() > 0) {
IValidateContentDescriptor firstProblem = v.getCollection().get(0);
if (LOG.isInfoEnabled()) {
LOG.info("there are fields with errors");
}
StringBuffer buf = new StringBuffer();
if (mandatoryTexts.size() > 0) {
buf.append(ScoutTexts.get("FormEmptyMandatoryFieldsMessage") + "\n\n");
for (Iterator it = mandatoryTexts.iterator(); it.hasNext();) {
buf.append("- " + it.next() + "\n");
}
buf.append("\n");
}
if (invalidTexts.size() > 0) {
buf.append(ScoutTexts.get("FormInvalidFieldsMessage") + "\n\n");
for (Iterator it = invalidTexts.iterator(); it.hasNext();) {
buf.append("- " + it.next() + "\n");
}
}
String introText = ScoutTexts.get("FormIncompleteIntro");
m_currentValidateContentDescriptor = firstProblem;
//
VetoException veto = new VetoException(introText, buf.toString());
throw veto;
}
if (!interceptValidate()) {
VetoException veto = new VetoException("Validate " + getClass().getSimpleName());
veto.consume();
throw veto;
}
if (!getHandler().onValidate()) {
VetoException veto = new VetoException("Validate " + getClass().getSimpleName());
veto.consume();
throw veto;
}
}
/**
* attach a statement (mode) that is executed every interval
*
* @since Build 195 09.02.2005, imo
*/
@Override
public void setTimer(String timerId, int seconds) {
removeTimer(timerId);
if (seconds > 0) {
P_Timer tim = new P_Timer(seconds, timerId);
m_scoutTimerMap.put(timerId, tim);
tim.start();
}
}
/**
* remove a statement (mode) that is executed every interval
*
* @since Build 195 09.02.2005, imo
*/
@Override
public void removeTimer(String timerId) {
P_Timer tim = m_scoutTimerMap.remove(timerId);
if (tim != null) {
tim.setStopSignal();
}
}
@Override
public void doClose() throws ProcessingException {
if (!isFormOpen()) {
return;
}
m_closeType = IButton.SYSTEM_TYPE_CLOSE;
discardStateInternal();
doFinally();
disposeFormInternal();
}
@Override
public void doCancel() throws ProcessingException {
if (!isFormOpen()) {
return;
}
m_closeType = IButton.SYSTEM_TYPE_CANCEL;
try {
// ensure all fields have the right save-needed-state
checkSaveNeeded();
// find any fields that needs save
P_AbstractCollectingFieldVisitor<IFormField> collector = new P_AbstractCollectingFieldVisitor<IFormField>() {
@Override
public boolean visitField(IFormField field, int level, int fieldIndex) {
if (field.isSaveNeeded()) {
collect(field);
return false;
}
else {
return true;
}
}
};
visitFields(collector);
if (collector.getCollectionCount() > 0 && isAskIfNeedSave()) {
MessageBox messageBox = new MessageBox(
null,
getCancelVerificationText(),
null,
TEXTS.get("YesButton"),
TEXTS.get("NoButton"),
TEXTS.get("CancelButton")
);
messageBox.setSeverity(IProcessingStatus.INFO);
int result = messageBox.startMessageBox();
if (result == IMessageBox.YES_OPTION) {
doOk();
return;
}
else if (result == IMessageBox.NO_OPTION) {
doClose();
return;
}
else {
VetoException e = new VetoException(ScoutTexts.get("UserCancelledOperation"));
e.consume();
throw e;
}
}
discardStateInternal();
doFinally();
disposeFormInternal();
}
catch (ProcessingException e) {
m_closeType = IButton.SYSTEM_TYPE_NONE;
throw e;
}
}
@Override
public void doReset() {
setFormLoading(true);
// reset values
P_AbstractCollectingFieldVisitor v = new P_AbstractCollectingFieldVisitor() {
@Override
public boolean visitField(IFormField field, int level, int fieldIndex) {
if (field instanceof IValueField) {
IValueField f = (IValueField) field;
f.resetValue();
}
else if (field instanceof IComposerField) {
IComposerField f = (IComposerField) field;
f.resetValue();
}
return true;
}
};
visitFields(v);
try {
// init again
initForm();
// load again
loadStateInternal();
}
catch (ProcessingException e) {
e.addContextMessage(ScoutTexts.get("FormReset") + " " + getTitle());
SERVICES.getService(IExceptionHandlerService.class).handleException(e);
}
}
/**
* Save data and close the form.
* It will make this decision based on {@link #isSaveNeeded}.
* Saving usually involves calling the <code>execStore</code> method
* of the current form handler.
*
* @see AbstractFormHandler#execStore()
*/
@Override
public void doOk() throws ProcessingException {
if (!isFormOpen()) {
return;
}
try {
m_closeType = IButton.SYSTEM_TYPE_NONE;
checkSaveNeeded();
validateForm();
m_closeType = IButton.SYSTEM_TYPE_OK;
if (isSaveNeeded()) {
storeStateInternal();
markSaved();
}
doFinally();
disposeFormInternal();
}
catch (ProcessingException e) {
m_closeType = IButton.SYSTEM_TYPE_NONE;
throwVetoExceptionInternal(e);
}
}
@Override
public void doSaveWithoutMarkerChange() throws ProcessingException {
if (!isFormOpen()) {
return;
}
try {
m_closeType = IButton.SYSTEM_TYPE_NONE;
// ensure all fields have the right save-needed-state
checkSaveNeeded();
validateForm();
m_closeType = IButton.SYSTEM_TYPE_SAVE_WITHOUT_MARKER_CHANGE;
storeStateInternal();
// do not set to "markSaved"
}
catch (ProcessingException e) {
m_closeType = IButton.SYSTEM_TYPE_NONE;
throwVetoExceptionInternal(e);
}
}
@Override
public void doSave() throws ProcessingException {
if (!isFormOpen()) {
return;
}
try {
m_closeType = IButton.SYSTEM_TYPE_NONE;
// ensure all fields have the right save-needed-state
checkSaveNeeded();
validateForm();
m_closeType = IButton.SYSTEM_TYPE_SAVE;
if (isSaveNeeded()) {
storeStateInternal();
markSaved();
}
}
catch (ProcessingException e) {
m_closeType = IButton.SYSTEM_TYPE_NONE;
throwVetoExceptionInternal(e);
}
}
@Override
public void setAllEnabled(final boolean b) {
P_AbstractCollectingFieldVisitor v = new P_AbstractCollectingFieldVisitor() {
@Override
public boolean visitField(IFormField f, int level, int fieldIndex) {
boolean filteredB = b;
/*
* @since 3.0 all items are enabled/disabled. a dialog can still be
* closed using the X in the window header if(f instanceof IButton){
* IButton b=(IButton)f; if(b.isProcessButton()){
* switch(b.getSystemType()){ case IButton.SYSTEM_TYPE_CLOSE: case
* IButton.SYSTEM_TYPE_CANCEL:{ filteredB=true; break; } } } }
*/
//
f.setEnabled(filteredB);
return true;
}
};
visitFields(v);
}
@Override
public void doFinally() {
try {
getHandler().onFinally();
}
catch (ProcessingException se) {
se.addContextMessage(ScoutTexts.get("FormFinally") + " " + getTitle());
SERVICES.getService(IExceptionHandlerService.class).handleException(se);
}
catch (Throwable t) {
ProcessingException e = new ProcessingException(ScoutTexts.get("FormFinally") + " " + getTitle(), t);
SERVICES.getService(IExceptionHandlerService.class).handleException(e);
}
}
@Override
public String getCancelVerificationText() {
return m_cancelVerificationText;
}
@Override
public void setCancelVerificationText(String text) {
m_cancelVerificationText = text;
}
@Override
public List<? extends IFormField> getInvalidFields() {
// check all fields that might be invalid
P_AbstractCollectingFieldVisitor<IFormField> v = new P_AbstractCollectingFieldVisitor<IFormField>() {
@Override
public boolean visitField(IFormField f, int level, int fieldIndex) {
if (!f.isContentValid()) {
collect(f);
}
return true;
}
};
visitFields(v);
return v.getCollection();
}
@Override
public final void checkSaveNeeded() {
// call checkSaveNeeded on all fields
P_AbstractCollectingFieldVisitor<IFormField> v = new P_AbstractCollectingFieldVisitor<IFormField>() {
@Override
public boolean visitField(IFormField f, int level, int fieldIndex) {
if (f instanceof IFormField) {
f.checkSaveNeeded();
}
return true;
}
};
visitFields(v);
}
private boolean/* ok */checkForVerifyingFields() {
// check all fields that might be invalid
P_AbstractCollectingFieldVisitor v = new P_AbstractCollectingFieldVisitor() {
@Override
public boolean visitField(IFormField field, int level, int fieldIndex) {
if (field instanceof IValueField) {
IValueField f = (IValueField) field;
if (f.isValueChanging() || f.isValueParsing()) {
return false;
}
}
return true;
}
};
return visitFields(v);
}
private void closeFormInternal(boolean kill) {
if (isFormOpen()) {
try {
// check if there is an active close, cancel or finish button
final HashSet<Integer> enabledSystemTypes = new HashSet<Integer>();
final HashSet<IButton> enabledSystemButtons = new HashSet<IButton>();
IFormFieldVisitor v = new IFormFieldVisitor() {
@Override
public boolean visitField(IFormField field, int level, int fieldIndex) {
if (field instanceof IButton) {
IButton b = (IButton) field;
if (b.isEnabled() && b.isVisible() && b.isEnabledProcessingButton()) {
enabledSystemTypes.add(b.getSystemType());
enabledSystemButtons.add(b);
}
}
return true;
}
};
try {
visitFields(v);
for (IButton b : enabledSystemButtons) {
b.setEnabledProcessingButton(false);
}
interceptOnCloseRequest(kill, enabledSystemTypes);
}
finally {
for (IButton b : enabledSystemButtons) {
b.setEnabledProcessingButton(true);
}
}
}
catch (ProcessingException se) {
se.addContextMessage(ScoutTexts.get("FormClosing") + " " + getTitle());
SERVICES.getService(IExceptionHandlerService.class).handleException(se);
}
catch (Throwable t) {
ProcessingException e = new ProcessingException(ScoutTexts.get("FormClosing") + " " + getTitle(), t);
SERVICES.getService(IExceptionHandlerService.class).handleException(e);
}
}
}
@Override
public void touch() {
getRootGroupBox().touch();
}
@Override
public boolean isSaveNeeded() {
return getRootGroupBox().isSaveNeeded();
}
@Override
public void markSaved() {
getRootGroupBox().markSaved();
}
@Override
public boolean isEmpty() {
return getRootGroupBox().isEmpty();
}
/**
* do not use or override this internal method
*/
protected void disposeFormInternal() {
if (!isFormOpen()) {
return;
}
try {
setButtonsArmed(false);
setCloseTimerArmed(false);
//
m_scoutCloseTimer = null;
for (Iterator it = m_scoutTimerMap.values().iterator(); it.hasNext();) {
((P_Timer) it.next()).setStopSignal();
}
m_scoutTimerMap.clear();
m_displayHintLocked = false;
m_formLoading = true;
}
catch (Throwable t) {
LOG.warn("Form " + getClass().getName(), t);
}
// dispose fields
FormUtility.disposeFormFields(this);
// dispose form configuration
try {
interceptDisposeForm();
}
catch (Throwable t) {
LOG.warn("Form " + getClass().getName(), t);
}
try {
// deregister of wizard / desktop
if (isAutoAddRemoveOnDesktop()) {
IDesktop desktop = getDesktop();
if (desktop != null) {
desktop.removeForm(this);
}
}
}
finally {
// unlock
m_blockingCondition.release();
// fire
fireFormClosed();
}
}
@Override
public boolean isShowing() {
IDesktop desktop = getDesktop();
if (desktop != null) {
return desktop.isShowing(this);
}
else {
return false;
}
}
@Override
public boolean isFormClosed() {
return !isFormOpen();
}
@Override
public boolean isFormOpen() {
return m_blockingCondition.isBlocking();
}
@Override
public Object getProperty(String name) {
return propertySupport.getProperty(name);
}
@Override
public void setProperty(String name, Object value) {
propertySupport.setProperty(name, value);
}
@Override
public boolean hasProperty(String name) {
return propertySupport.hasProperty(name);
}
@Override
public void setXML(String xml) throws ProcessingException {
if (xml == null) {
return;
}
SimpleXmlElement root = new SimpleXmlElement();
try {
root.parseString(xml);
}
catch (Throwable t) {
throw new ProcessingException("Loading xml", t);
}
loadXML(root);
}
@Override
public String getXML(String encoding) throws ProcessingException {
if (encoding == null) {
encoding = "UTF-8";
}
try {
SimpleXmlElement e = storeXML();
ByteArrayOutputStream bo = new ByteArrayOutputStream();
e.writeDocument(bo, null, encoding);
return new String(bo.toByteArray(), Charset.forName(encoding));
}
catch (Throwable e) {
if (e instanceof ProcessingException) {
throw (ProcessingException) e;
}
else {
throw new ProcessingException("form : " + getTitle() + ", encoding: " + encoding, e);
}
}
}
@Override
public SimpleXmlElement storeXML() throws ProcessingException {
SimpleXmlElement root = new SimpleXmlElement("form-state");
storeXML(root);
return root;
}
@Override
public void storeXML(SimpleXmlElement root) throws ProcessingException {
root.setAttribute("formId", getFormId());
root.setAttribute("formQname", getClass().getName());
// add custom properties
SimpleXmlElement xProps = new SimpleXmlElement("properties");
root.addChild(xProps);
IPropertyFilter filter = new IPropertyFilter() {
@Override
public boolean accept(FastPropertyDescriptor descriptor) {
if (descriptor.getPropertyType().isInstance(IFormField.class)) {
return false;
}
if (!descriptor.getPropertyType().isPrimitive() && !Serializable.class.isAssignableFrom(descriptor.getPropertyType())) {
return false;
}
if (descriptor.getReadMethod() == null || descriptor.getWriteMethod() == null) {
return false;
}
return true;
}
};
Map props = BeanUtility.getProperties(this, AbstractForm.class, filter);
for (Iterator it = props.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
try {
SimpleXmlElement xProp = new SimpleXmlElement("property");
xProps.addChild(xProp);
xProp.setAttribute("name", entry.getKey());
xProp.setObjectAttribute("value", entry.getValue());
}
catch (Exception e) {
throw new ProcessingException("property " + entry.getKey() + " with value " + entry.getValue(), e);
}
}
// add fields
final SimpleXmlElement xFields = new SimpleXmlElement("fields");
root.addChild(xFields);
final Holder<ProcessingException> exceptionHolder = new Holder<ProcessingException>(ProcessingException.class);
P_AbstractCollectingFieldVisitor v = new P_AbstractCollectingFieldVisitor() {
@Override
public boolean visitField(IFormField field, int level, int fieldIndex) {
SimpleXmlElement xField = new SimpleXmlElement("field");
try {
field.storeXML(xField);
xFields.addChild(xField);
}
catch (ProcessingException e) {
exceptionHolder.setValue(e);
return false;
}
return true;
}
};
visitFields(v);
if (exceptionHolder.getValue() != null) {
throw exceptionHolder.getValue();
}
}
@Override
public void loadXML(SimpleXmlElement root) throws ProcessingException {
String formId = getFormId();
String xmlId = root.getStringAttribute("formId", "");
if (!formId.equals(xmlId)) {
throw new ProcessingException("xml id=" + xmlId + " does not match form id=" + formId);
}
// load properties
HashMap<String, Object> props = new HashMap<String, Object>();
SimpleXmlElement xProps = root.getChild("properties");
if (xProps != null) {
for (Iterator it = xProps.getChildren("property").iterator(); it.hasNext();) {
SimpleXmlElement xProp = (SimpleXmlElement) it.next();
String name = xProp.getStringAttribute("name");
try {
Object value = xProp.getObjectAttribute("value", null);
props.put(name, value);
}
catch (Exception e) {
LOG.warn("property " + name, e);
}
}
}
BeanUtility.setProperties(this, props, true, null);
// load fields
SimpleXmlElement xFields = root.getChild("fields");
if (xFields != null) {
for (SimpleXmlElement xField : xFields.getChildren("field")) {
List<String> xmlFieldIds = new LinkedList<String>();
// add enclosing field path to xml field IDs
for (SimpleXmlElement element : xField.getChildren("enclosingField")) {
xmlFieldIds.add(element.getStringAttribute("fieldId"));
}
xmlFieldIds.add(xField.getStringAttribute("fieldId"));
FindFieldByXmlIdsVisitor v = new FindFieldByXmlIdsVisitor(xmlFieldIds.toArray(new String[xmlFieldIds.size()]));
visitFields(v);
IFormField f = v.getField();
if (f != null) {
f.loadXML(xField);
}
}
}
// in all tabboxes select the first tab that contains data, iff the current
// tab has no values set
getRootGroupBox().visitFields(new IFormFieldVisitor() {
@Override
public boolean visitField(IFormField field, int level, int fieldIndex) {
if (field instanceof ITabBox) {
ITabBox tabBox = (ITabBox) field;
IGroupBox selbox = tabBox.getSelectedTab();
if (selbox == null || !selbox.isSaveNeeded()) {
for (IGroupBox g : tabBox.getGroupBoxes()) {
if (g.isSaveNeeded()) {
tabBox.setSelectedTab(g);
break;
}
}
}
}
return true;
}
}, 0);
}
@Override
public void doExportXml(boolean saveAs) {
while (true) {
File path = m_lastXmlFileForStorage;
if (saveAs || path == null) {
File dir = m_lastXmlFileForStorage;
if (dir != null) {
dir = dir.getParentFile();
}
List<File> a = new FileChooser(dir, Collections.singletonList("xml"), false).startChooser();
if (a.isEmpty()) {
break;
}
else {
path = a.get(0);
}
}
// export search parameters
try {
storeXML().writeDocument(new OutputStreamWriter(new FileOutputStream(path), "UTF-8"), null, "UTF-8");
if (path != null) {
m_lastXmlFileForStorage = path;
}
break;
}
catch (Throwable t) {
SERVICES.getService(IExceptionHandlerService.class).handleException(new ProcessingException(ScoutTexts.get("FormExportXml") + " " + getTitle(), t));
saveAs = true;
}
}// end while nok
}
@Override
public void doImportXml() {
File dir = m_lastXmlFileForStorage;
if (dir != null) {
dir = dir.getParentFile();
}
List<File> a = new FileChooser(dir, Collections.singletonList("xml"), true).startChooser();
if (a.size() == 1) {
File newPath = a.get(0);
String text = null;
try {
SimpleXmlElement e = new SimpleXmlElement();
e.parseStream(new FileInputStream(newPath));
// load xml to search
m_lastXmlFileForStorage = newPath;
loadXML(e);
}
catch (Exception e) {
LOG.warn("loading: " + newPath + " text: " + text + " Exception: " + e);
new MessageBox(
null,
TEXTS.get("LoadFormXmlFailedText"),
null,
TEXTS.get("OkButton"),
null,
null).startMessageBox();
m_lastXmlFileForStorage = null;
}
}
}
@Override
public void printForm(PrintDevice device, Map<String, Object> parameters) {
try {
firePrint(null, device, parameters);
}
catch (ProcessingException e) {
e.addContextMessage(ScoutTexts.get("FormPrint") + " " + getTitle());
SERVICES.getService(IExceptionHandlerService.class).handleException(e);
}
}
@Override
public void printField(IFormField field, PrintDevice device, Map<String, Object> parameters) {
try {
firePrint(field, device, parameters);
}
catch (ProcessingException e) {
e.addContextMessage(ScoutTexts.get("FormPrint") + " " + (field != null ? field.getLabel() : getTitle()));
SERVICES.getService(IExceptionHandlerService.class).handleException(e);
}
}
@Override
public void activate() {
if (getDesktop() != null) {
getDesktop().ensureVisible(this);
}
}
@Override
public void requestFocus(IFormField f) {
if (f == null || f.getForm() != this) {
return;
}
fireRequestFocus(f);
}
/**
* @return Returns a map having old field classes as keys and replacement field classes as values. <code>null</code>
* is returned if no form fields are replaced. Do not use this internal method.
* @since 3.8.2
*/
public Map<Class<?>, Class<? extends IFormField>> getFormFieldReplacementsInternal() {
return m_fieldReplacements;
}
/**
* Registers the given form field replacements on this form. Do not use this internal method.
*
* @param replacements
* Map having old field classes as key and replacing field classes as values.
* @since 3.8.2
*/
public void registerFormFieldReplacementsInternal(Map<Class<?>, Class<? extends IFormField>> replacements) {
if (replacements == null || replacements.isEmpty()) {
return;
}
if (m_fieldReplacements == null) {
m_fieldReplacements = new HashMap<Class<?>, Class<? extends IFormField>>();
}
m_fieldReplacements.putAll(replacements);
}
/**
* Model Observer.
*/
@Override
public void addFormListener(FormListener listener) {
m_listenerList.add(FormListener.class, listener);
}
@Override
public void removeFormListener(FormListener listener) {
m_listenerList.remove(FormListener.class, listener);
}
protected IEventHistory<FormEvent> createEventHistory() {
return new DefaultFormEventHistory(5000L);
}
@Override
public IEventHistory<FormEvent> getEventHistory() {
return m_eventHistory;
}
private void fireFormLoadBefore() throws ProcessingException {
fireFormEvent(new FormEvent(this, FormEvent.TYPE_LOAD_BEFORE));
}
private void fireFormLoadAfter() throws ProcessingException {
fireFormEvent(new FormEvent(this, FormEvent.TYPE_LOAD_AFTER));
}
private void fireFormLoadComplete() throws ProcessingException {
fireFormEvent(new FormEvent(this, FormEvent.TYPE_LOAD_COMPLETE));
}
private void fireFormStoreBefore() throws ProcessingException {
fireFormEvent(new FormEvent(this, FormEvent.TYPE_STORE_BEFORE));
}
private void fireFormDiscarded() {
try {
fireFormEvent(new FormEvent(this, FormEvent.TYPE_DISCARDED));
}
catch (ProcessingException e) {
e.addContextMessage(ScoutTexts.get("FormFireDiscarded") + " " + getTitle());
SERVICES.getService(IExceptionHandlerService.class).handleException(e);
}
}
private void fireFormStoreAfter() throws ProcessingException {
fireFormEvent(new FormEvent(this, FormEvent.TYPE_STORE_AFTER));
}
private void firePrint(IFormField root, PrintDevice device, Map<String, Object> parameters) throws ProcessingException {
fireFormEvent(new FormEvent(this, FormEvent.TYPE_PRINT, root, device, parameters));
}
private void fireFormPrinted(File outputFile) {
try {
fireFormEvent(new FormEvent(this, FormEvent.TYPE_PRINTED, outputFile));
}
catch (ProcessingException e) {
e.addContextMessage(ScoutTexts.get("FormFirePrinted") + " " + getTitle());
SERVICES.getService(IExceptionHandlerService.class).handleException(e);
}
}
/**
* send request that form was activated by gui
*/
private void fireFormActivated() {
try {
interceptFormActivated();
fireFormEvent(new FormEvent(this, FormEvent.TYPE_ACTIVATED));
}
catch (ProcessingException e) {
e.addContextMessage(ScoutTexts.get("FormFireActivated") + " " + getTitle());
SERVICES.getService(IExceptionHandlerService.class).handleException(e);
}
}
/**
* send request that form was closed by gui
*/
private void fireFormClosed() {
try {
fireFormEvent(new FormEvent(this, FormEvent.TYPE_CLOSED));
}
catch (ProcessingException e) {
e.addContextMessage(ScoutTexts.get("FormFireClosed") + " " + getTitle());
SERVICES.getService(IExceptionHandlerService.class).handleException(e);
}
}
protected void fireFormEvent(FormEvent e) throws ProcessingException {
EventListener[] listeners = m_listenerList.getListeners(FormListener.class);
if (listeners != null && listeners.length > 0) {
ProcessingException pe = null;
for (int i = 0; i < listeners.length; i++) {
try {
((FormListener) listeners[i]).formChanged(e);
}
catch (ProcessingException ex) {
if (pe == null) {
pe = ex;
}
}
catch (Throwable t) {
if (pe == null) {
pe = new ProcessingException("Unexpected", t);
}
}
}
if (pe != null) {
throw pe;
}
}
IEventHistory<FormEvent> h = getEventHistory();
if (h != null) {
h.notifyEvent(e);
}
}
@Override
public void structureChanged(IFormField causingField) {
fireFormStructureChanged(causingField);
}
private void fireFormStructureChanged(IFormField causingField) {
try {
fireFormEvent(new FormEvent(this, FormEvent.TYPE_STRUCTURE_CHANGED, causingField));
}
catch (ProcessingException e) {
e.addContextMessage(ScoutTexts.get("FormFireStructureChanged") + " " + getTitle());
SERVICES.getService(IExceptionHandlerService.class).handleException(e);
}
}
private void fireFormToFront() {
try {
fireFormEvent(new FormEvent(this, FormEvent.TYPE_TO_FRONT));
}
catch (ProcessingException e) {
e.addContextMessage(ScoutTexts.get("FormFireToFront") + " " + getTitle());
SERVICES.getService(IExceptionHandlerService.class).handleException(e);
}
}
private void fireFormToBack() {
try {
fireFormEvent(new FormEvent(this, FormEvent.TYPE_TO_BACK));
}
catch (ProcessingException e) {
e.addContextMessage(ScoutTexts.get("FormFireToBack") + " " + getTitle());
SERVICES.getService(IExceptionHandlerService.class).handleException(e);
}
}
private void fireRequestFocus(IFormField f) {
try {
fireFormEvent(new FormEvent(this, FormEvent.TYPE_REQUEST_FOCUS, f));
}
catch (ProcessingException e) {
e.addContextMessage(ScoutTexts.get("FormFireRequestFocus") + " " + getTitle() + " for " + f.getLabel());
SERVICES.getService(IExceptionHandlerService.class).handleException(e);
}
}
/**
* @deprecated will be removed with the N-Release. See Bug 456408.
* With the N-Release you should use {@link #getTitle()} instead.
*/
@Deprecated
@Override
public String getBasicTitle() {
return m_basicTitle;
}
/**
* @deprecated will be removed with the N-Release. See Bug 456408.
* With the N-Release you should use {@link #getTitle()} instead.
*/
@Deprecated
@Override
public void setBasicTitle(String basicTitle) {
m_basicTitle = basicTitle;
composeTitle();
}
@Override
public String getSubTitle() {
return m_subTitle;
}
@Override
public void setSubTitle(String subTitle) {
m_subTitle = subTitle;
composeTitle();
}
@Override
public void setTitle(String s) {
m_basicTitle = s;
m_subTitle = null;
composeTitle();
}
private void composeTitle() {
StringBuffer buf = new StringBuffer();
String basic = getBasicTitle();
if (basic != null) {
if (buf.length() > 0) {
buf.append(" - ");
}
buf.append(basic);
}
String sub = getSubTitle();
if (sub != null) {
if (buf.length() > 0) {
buf.append(" - ");
}
buf.append(sub);
}
propertySupport.setPropertyString(PROP_TITLE, buf.toString());
}
@Override
public String getTitle() {
return propertySupport.getPropertyString(PROP_TITLE);
}
@Override
public boolean isMaximizeEnabled() {
return propertySupport.getPropertyBool(PROP_MAXIMIZE_ENABLED);
}
@Override
public void setMaximizeEnabled(boolean b) {
propertySupport.setPropertyBool(PROP_MAXIMIZE_ENABLED, b);
}
@Override
public boolean isMinimizeEnabled() {
return propertySupport.getPropertyBool(PROP_MINIMIZE_ENABLED);
}
@Override
public void setMinimizeEnabled(boolean b) {
propertySupport.setPropertyBool(PROP_MINIMIZE_ENABLED, b);
}
@Override
public boolean isMaximized() {
return propertySupport.getPropertyBool(PROP_MAXIMIZED);
}
@Override
public void setMaximized(boolean b) {
if (isMaximizeEnabled()) {
if (b) {
propertySupport.setPropertyBool(PROP_MINIMIZED, false);
}
// maximized state of ui could be out of sync, fire always
propertySupport.setPropertyAlwaysFire(PROP_MAXIMIZED, b);
}
}
@Override
public boolean isMinimized() {
return propertySupport.getPropertyBool(PROP_MINIMIZED);
}
@Override
public void setMinimized(boolean b) {
if (isMinimizeEnabled()) {
if (b) {
propertySupport.setPropertyBool(PROP_MAXIMIZED, false);
}
// minimized state of ui could be out of sync, fire always
propertySupport.setPropertyAlwaysFire(PROP_MINIMIZED, b);
}
}
@Override
public boolean isAutoAddRemoveOnDesktop() {
return m_autoRegisterInDesktopOnStart;
}
@Override
public void setAutoAddRemoveOnDesktop(boolean b) {
m_autoRegisterInDesktopOnStart = b;
}
@Override
public boolean isModal() {
return m_modal;
}
@Override
public void setModal(boolean b) {
if (b) {
switch (getDisplayHint()) {
case DISPLAY_HINT_POPUP_DIALOG:
case DISPLAY_HINT_DIALOG: {
m_modal = b;
break;
}
default: {
LOG.warn("cannot set property 'modal' to true with current display hint type");
}
}
}
else {
m_modal = b;
}
}
@Override
public void setCacheBounds(boolean cacheBounds) {
m_cacheBounds = cacheBounds;
}
@Override
public boolean isCacheBounds() {
return m_cacheBounds;
}
@Override
public String computeCacheBoundsKey() {
return getClass().getName();
}
@Override
public int getDisplayHint() {
return m_displayHint;
}
@Override
public void setDisplayHint(int i) {
switch (i) {
case DISPLAY_HINT_DIALOG: {
m_displayHint = i;
break;
}
case DISPLAY_HINT_POPUP_WINDOW: {
m_displayHint = i;
setModal(false);
break;
}
case DISPLAY_HINT_POPUP_DIALOG: {
m_displayHint = i;
break;
}
case DISPLAY_HINT_VIEW: {
m_displayHint = i;
setModal(false);
break;
}
default: {
throw new IllegalArgumentException("invalid displayHint " + i);
}
}
}
@Override
public String getDisplayViewId() {
return m_displayViewId;
}
@Override
public void setDisplayViewId(String viewId) {
m_displayViewId = viewId;
}
@Override
public boolean isAskIfNeedSave() {
return m_askIfNeedSave;
}
@Override
public void setAskIfNeedSave(boolean b) {
m_askIfNeedSave = b;
}
@Override
public void toFront() {
fireFormToFront();
}
@Override
public void toBack() {
fireFormToBack();
}
@Override
public boolean isButtonsArmed() {
return m_buttonsArmed;
}
@Override
public void setButtonsArmed(boolean b) {
m_buttonsArmed = b;
}
@Override
public boolean isCloseTimerArmed() {
return m_closeTimerArmed;
}
@Override
public void setCloseTimerArmed(boolean b) {
m_closeTimerArmed = b;
}
@Override
public String toString() {
return "Form " + getFormId();
}
@Override
public IFormUIFacade getUIFacade() {
return m_uiFacade;
}
protected void handleSystemButtonEventInternal(ButtonEvent e) {
switch (e.getType()) {
case ButtonEvent.TYPE_CLICKED: {
// disable close timer
setCloseTimerArmed(false);
if (isButtonsArmed()) {
if (checkForVerifyingFields()) {
try {
IButton src = (IButton) e.getSource();
switch (src.getSystemType()) {
case IButton.SYSTEM_TYPE_CANCEL: {
doCancel();
break;
}
case IButton.SYSTEM_TYPE_CLOSE: {
doClose();
break;
}
case IButton.SYSTEM_TYPE_OK: {
doOk();
break;
}
case IButton.SYSTEM_TYPE_RESET: {
doReset();
break;
}
case IButton.SYSTEM_TYPE_SAVE: {
doSave();
break;
}
case IButton.SYSTEM_TYPE_SAVE_WITHOUT_MARKER_CHANGE: {
doSaveWithoutMarkerChange();
break;
}
}
}
catch (ProcessingException se) {
se.addContextMessage(ScoutTexts.get("FormButtonClicked") + " " + getTitle() + "." + e.getButton().getLabel());
SERVICES.getService(IExceptionHandlerService.class).handleException(se);
}
if (m_currentValidateContentDescriptor != null) {
m_currentValidateContentDescriptor.activateProblemLocation();
m_currentValidateContentDescriptor = null;
}
}// end
}
break;
}
}
}
/**
* Button controller for ok,cancel, save etc.
*/
private class P_SystemButtonListener implements ButtonListener {
@Override
public void buttonChanged(ButtonEvent e) {
// auto-detaching
if (m_systemButtonListener != this) {
((IButton) e.getSource()).removeButtonListener(this);
return;
}
handleSystemButtonEventInternal(e);
}
}// end private class
private class P_MainBoxPropertyChangeProxy implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent e) {
if (IFormField.PROP_SAVE_NEEDED.equals(e.getPropertyName())) {
propertySupport.firePropertyChange(PROP_SAVE_NEEDED, e.getOldValue(), e.getNewValue());
}
else if (IFormField.PROP_EMPTY.equals(e.getPropertyName())) {
propertySupport.firePropertyChange(PROP_EMPTY, e.getOldValue(), e.getNewValue());
}
}
}
/**
* form custom timer
*/
private class P_Timer extends StoppableThread {
private final long m_intervalMillis;
private final String m_timerId;
private boolean m_execPending = false;// do not execute while exec is pending
private final IClientSession m_session;
public P_Timer(long seconds, String timerId) {
setDaemon(true);
m_intervalMillis = seconds * 1000L;
m_timerId = timerId;
m_session = ClientSyncJob.getCurrentSession();
}
@Override
public void run() {
long next = ((System.currentTimeMillis() + 999L) / 1000L) * 1000L + m_intervalMillis;
while (m_scoutTimerMap != null && !isStopSignal()) {
try {
sleep(250);
}
catch (InterruptedException ex) {
}
// active?
if ((!m_execPending) && (!isStopSignal())) {
// next ready?
if (next < System.currentTimeMillis()) {
m_execPending = true;
new ClientSyncJob("Form timer", m_session) {
@Override
protected void runVoid(IProgressMonitor monitor) throws Throwable {
try {
if (LOG.isInfoEnabled()) {
LOG.info("timer " + m_timerId);
}
interceptTimer(m_timerId);
}
catch (ProcessingException se) {
se.addContextMessage(ScoutTexts.get("FormTimerActivated") + " " + getTitle() + "." + m_timerId);
SERVICES.getService(IExceptionHandlerService.class).handleException(se);
}
finally {
m_execPending = false;// continue scheduling
}
}
}.schedule();
}
}
// update next
while (next < System.currentTimeMillis()) {
next = next + m_intervalMillis;
}
}
}
}// end private class
/**
* form close timer
*/
private class P_CloseTimer extends StoppableThread {
private long m_seconds;
private final IClientSession m_session;
public P_CloseTimer(long seconds) {
setName("IForm.P_CloseTimer");
setDaemon(true);
m_seconds = seconds;
m_session = ClientSyncJob.getCurrentSession();
}
@Override
public void run() {
while (this == m_scoutCloseTimer && m_seconds > 0 && isCloseTimerArmed()) {
new ClientSyncJob("Form close countdown", m_session) {
@Override
protected void runVoid(IProgressMonitor monitor) throws Throwable {
setSubTitle("" + m_seconds);
}
}.schedule();
try {
sleep(1000);
}
catch (InterruptedException ex) {
}
m_seconds--;
}
if (this == m_scoutCloseTimer) {
new ClientSyncJob("Form close timer", m_session) {
@Override
protected void runVoid(IProgressMonitor monitor) throws Throwable {
try {
if (isCloseTimerArmed()) {
interceptCloseTimer();
}
else {
setSubTitle(null);
}
}
catch (ProcessingException se) {
se.addContextMessage(ScoutTexts.get("FormCloseTimerActivated") + " " + getTitle());
SERVICES.getService(IExceptionHandlerService.class).handleException(se);
}
}
}.schedule();
}
}
}// end private class
private abstract static class P_AbstractCollectingFieldVisitor<T> implements IFormFieldVisitor {
private final ArrayList<T> m_list = new ArrayList<T>();
public void collect(T o) {
m_list.add(o);
}
public int getCollectionCount() {
return m_list.size();
}
public List<T> getCollection() {
return m_list;
}
}
private class P_UIFacade implements IFormUIFacade {
@Override
public void fireFormActivatedFromUI() {
fireFormActivated();
}
@Override
public void fireFormClosingFromUI() {
// check if some field is verifying input. In this case cancel ui call
if (!checkForVerifyingFields()) {
return;
}
closeFormInternal(false);
}
@Override
public void fireFormKilledFromUI() {
closeFormInternal(true);
}
@Override
public void fireFormPrintedFromUI(File outputFile) {
fireFormPrinted(outputFile);
}
}// end private class
/**
* The extension delegating to the local methods. This Extension is always at the end of the chain and will not call
* any further chain elements.
*/
protected static class LocalFormExtension<FORM extends AbstractForm> extends AbstractExtension<FORM> implements IFormExtension<FORM> {
/**
* @param owner
*/
public LocalFormExtension(FORM owner) {
super(owner);
}
@Override
public void execCloseTimer(FormCloseTimerChain chain) throws ProcessingException {
getOwner().execCloseTimer();
}
@Override
public void execInactivityTimer(FormInactivityTimerChain chain) throws ProcessingException {
getOwner().execInactivityTimer();
}
@Override
public void execStored(FormStoredChain chain) throws ProcessingException {
getOwner().execStored();
}
@Override
public boolean execCheckFields(FormCheckFieldsChain chain) throws ProcessingException {
return getOwner().execCheckFields();
}
@Override
public void execResetSearchFilter(FormResetSearchFilterChain chain, SearchFilter searchFilter) throws ProcessingException {
getOwner().execResetSearchFilter(searchFilter);
}
@Override
public void execAddSearchTerms(FormAddSearchTermsChain chain, SearchFilter search) {
getOwner().execAddSearchTerms(search);
}
@Override
public void execOnVetoException(FormOnVetoExceptionChain chain, VetoException e, int code) throws ProcessingException {
getOwner().execOnVetoException(e, code);
}
@Override
public void execFormActivated(FormFormActivatedChain chain) throws ProcessingException {
getOwner().execFormActivated();
}
@Override
public void execDisposeForm(FormDisposeFormChain chain) throws ProcessingException {
getOwner().execDisposeForm();
}
@Override
public void execTimer(FormTimerChain chain, String timerId) throws ProcessingException {
getOwner().execTimer(timerId);
}
@Override
public AbstractFormData execCreateFormData(FormCreateFormDataChain chain) throws ProcessingException {
return getOwner().execCreateFormData();
}
@Override
public void execInitForm(FormInitFormChain chain) throws ProcessingException {
getOwner().execInitForm();
}
@Override
public boolean execValidate(FormValidateChain chain) throws ProcessingException {
return getOwner().execValidate();
}
@Override
public void execOnCloseRequest(FormOnCloseRequestChain chain, boolean kill, HashSet<Integer> enabledButtonSystemTypes) throws ProcessingException {
getOwner().execOnCloseRequest(kill, enabledButtonSystemTypes);
}
@Override
public void execDataChanged(FormDataChangedChain chain, Object... dataTypes) throws ProcessingException {
getOwner().execDataChanged(dataTypes);
}
}
protected final void interceptCloseTimer() throws ProcessingException {
List<? extends IFormExtension<? extends AbstractForm>> extensions = getAllExtensions();
FormCloseTimerChain chain = new FormCloseTimerChain(extensions);
chain.execCloseTimer();
}
protected final void interceptInactivityTimer() throws ProcessingException {
List<? extends IFormExtension<? extends AbstractForm>> extensions = getAllExtensions();
FormInactivityTimerChain chain = new FormInactivityTimerChain(extensions);
chain.execInactivityTimer();
}
protected final void interceptStored() throws ProcessingException {
List<? extends IFormExtension<? extends AbstractForm>> extensions = getAllExtensions();
FormStoredChain chain = new FormStoredChain(extensions);
chain.execStored();
}
protected final boolean interceptCheckFields() throws ProcessingException {
List<? extends IFormExtension<? extends AbstractForm>> extensions = getAllExtensions();
FormCheckFieldsChain chain = new FormCheckFieldsChain(extensions);
return chain.execCheckFields();
}
protected final void interceptResetSearchFilter(SearchFilter searchFilter) throws ProcessingException {
List<? extends IFormExtension<? extends AbstractForm>> extensions = getAllExtensions();
FormResetSearchFilterChain chain = new FormResetSearchFilterChain(extensions);
chain.execResetSearchFilter(searchFilter);
}
protected final void interceptAddSearchTerms(SearchFilter search) {
List<? extends IFormExtension<? extends AbstractForm>> extensions = getAllExtensions();
FormAddSearchTermsChain chain = new FormAddSearchTermsChain(extensions);
chain.execAddSearchTerms(search);
}
protected final void interceptOnVetoException(VetoException e, int code) throws ProcessingException {
List<? extends IFormExtension<? extends AbstractForm>> extensions = getAllExtensions();
FormOnVetoExceptionChain chain = new FormOnVetoExceptionChain(extensions);
chain.execOnVetoException(e, code);
}
protected final void interceptFormActivated() throws ProcessingException {
List<? extends IFormExtension<? extends AbstractForm>> extensions = getAllExtensions();
FormFormActivatedChain chain = new FormFormActivatedChain(extensions);
chain.execFormActivated();
}
protected final void interceptDisposeForm() throws ProcessingException {
List<? extends IFormExtension<? extends AbstractForm>> extensions = getAllExtensions();
FormDisposeFormChain chain = new FormDisposeFormChain(extensions);
chain.execDisposeForm();
}
protected final void interceptTimer(String timerId) throws ProcessingException {
List<? extends IFormExtension<? extends AbstractForm>> extensions = getAllExtensions();
FormTimerChain chain = new FormTimerChain(extensions);
chain.execTimer(timerId);
}
protected final AbstractFormData interceptCreateFormData() throws ProcessingException {
List<? extends IFormExtension<? extends AbstractForm>> extensions = getAllExtensions();
FormCreateFormDataChain chain = new FormCreateFormDataChain(extensions);
return chain.execCreateFormData();
}
protected final void interceptInitForm() throws ProcessingException {
List<? extends IFormExtension<? extends AbstractForm>> extensions = getAllExtensions();
FormInitFormChain chain = new FormInitFormChain(extensions);
chain.execInitForm();
}
protected final boolean interceptValidate() throws ProcessingException {
List<? extends IFormExtension<? extends AbstractForm>> extensions = getAllExtensions();
FormValidateChain chain = new FormValidateChain(extensions);
return chain.execValidate();
}
protected final void interceptOnCloseRequest(boolean kill, HashSet<Integer> enabledButtonSystemTypes) throws ProcessingException {
List<? extends IFormExtension<? extends AbstractForm>> extensions = getAllExtensions();
FormOnCloseRequestChain chain = new FormOnCloseRequestChain(extensions);
chain.execOnCloseRequest(kill, enabledButtonSystemTypes);
}
protected final void interceptDataChanged(Object... dataTypes) throws ProcessingException {
List<? extends IFormExtension<? extends AbstractForm>> extensions = getAllExtensions();
FormDataChangedChain chain = new FormDataChangedChain(extensions);
chain.execDataChanged(dataTypes);
}
}