package org.nocket.gen.page.element.synchronizer; import java.text.DecimalFormat; import java.util.HashSet; import java.util.Set; import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.time.FastDateFormat; import org.apache.wicket.Component; import org.apache.wicket.model.IModel; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.util.lang.Objects; import org.nocket.gen.page.element.PageElementI; import org.nocket.gen.page.guiservice.TouchedListener; // TODO: Auto-generated Javadoc /** * The Class TouchedListenerModelWrapper. * * @param <E> the element type */ @SuppressWarnings("serial") public class TouchedListenerModelWrapper<E> implements IModel<E> { /** The listeners. */ private final Set<IModel<? extends TouchedListener>> listeners = new HashSet<IModel<? extends TouchedListener>>(); /** The helper. */ private final SynchronizerHelper helper; /** The delegate. */ private final IModel<E> delegate; /** The touched. */ private boolean touched; /** The preserved state. */ private State<E> preservedState; /** * Instantiates a new touched listener model wrapper. * * @param e the e * @param delegate the delegate */ public TouchedListenerModelWrapper(PageElementI<E> e, IModel<E> delegate) { this.helper = new SynchronizerHelper(e); this.delegate = delegate; e.getContext().getTouchedRegistryData().registerModel(this); preserveState(null); } /** * Preserve state. * * @param object the object * @return the state */ protected State<E> preserveState(Component object) { return preservedState = StateFactory.create(this, object, helper); } /** * Register touched listener. * * @param listener the listener * @return true, if successful */ public boolean registerTouchedListener(IModel<? extends TouchedListener> listener) { return listeners.add(listener); } /** * Unregister touched listener. * * @param listener the listener * @return true, if successful */ public boolean unregisterTouchedListener(IModel<? extends TouchedListener> listener) { return listeners.remove(listener); } /** * Gets the wicket id. * * @return the wicket id */ public String getWicketId() { return helper.getWicketId(); } /** * Checks if is touched. * * @return true, if is touched */ public boolean isTouched() { return touched; } /** * Sets the touched. * * @param touched the new touched */ public void setTouched(boolean touched) { boolean changed = this.touched != touched; this.touched = touched; if (changed) { for (IModel<? extends TouchedListener> l : listeners) { if (touched) { l.getObject().touched(helper.getWicketId()); } else { l.getObject().untouched(helper.getWicketId()); } } } } /* (non-Javadoc) * @see org.apache.wicket.model.IDetachable#detach() */ @Override public void detach() { delegate.detach(); } /* (non-Javadoc) * @see org.apache.wicket.model.IModel#getObject() */ @Override public E getObject() { E object = delegate.getObject(); return object; } /** * Model changed between request processing. * * @param component the component * @return true if object changed between the begin of the request and * construction of the response */ public boolean modelChangedBetweenRequestProcessing(Component component) { return !StateFactory.create(this, component, helper).equals(preservedState); } /* (non-Javadoc) * @see org.apache.wicket.model.IModel#setObject(java.lang.Object) */ @Override public void setObject(E object) { if (!touched && !modelUnchanged(object)) { setTouched(true); } /** * Dies ist ein halbgarer Workaround. Bisher stand an dieser Stelle der * direkte zugriff auf den Delegate "delegate.setObject(object);". Dies * funktioniert zwar immer, aber ist nicht korrekt. Ist ein Setter * intercepted, so wuerde die Interception ignoriert. Für die Fälle, * dass der Delegate ein PropertyModel ist, d.h. direkt auf das Model * zugegriffen wird, wird nun der SynchronizerHelper benutzt. In den * Fällen, dass es ein anderes Model ist, wird gehofft, dass diese * Aufgabe das "andere" Model sprich das Delegate uebernimmt.<br> * Offen:<br> * - FileDownloadElement<br> * - LinkElement<br> * - RepeatingPanelElement<br> * - TableElement<br> * Bei diesen Elementen wuerder ein Intercepted des Setters nicht * funktionieren. */ if (delegate instanceof PropertyModel<?>) { helper.invokeSetterMethod(object); } else { delegate.setObject(object); } } /** * Model unchanged. * * @param newValue the new value * @return true, if successful */ private boolean modelUnchanged(E newValue) { E oldValue = getObject(); return modelUnchanged(oldValue, newValue); } /** * Model unchanged. * * @param oldValue the old value * @param newValue the new value * @return true, if successful */ boolean modelUnchanged(E oldValue, E newValue) { String format = helper.getFormat(); if (helper.isBooleanType()) { boolean oldValueFormatted = BooleanUtils.isTrue((Boolean) oldValue); boolean newValueFormatted = BooleanUtils.isTrue((Boolean) newValue); return Objects.equal(oldValueFormatted, newValueFormatted); } if (format != null) { if (helper.isNumberType()) { String oldValueFormatted = formatNumberNullSafe(format, oldValue); String newValueFormatted = formatNumberNullSafe(format, newValue); return Objects.equal(oldValueFormatted, newValueFormatted); } else if (helper.isDateType()) { String oldValueFormatted = formatDateNullSafe(format, oldValue); String newValueFormatted = formatDateNullSafe(format, newValue); return Objects.equal(oldValueFormatted, newValueFormatted); } } return Objects.equal(oldValue, newValue); } /** * Format number null safe. * * @param format the format * @param value the value * @return the string */ private String formatNumberNullSafe(String format, E value) { if (value == null) { return null; } DecimalFormat df = new DecimalFormat(format); return df.format(value); } /** * Format date null safe. * * @param format the format * @param value the value * @return the string */ private String formatDateNullSafe(String format, E value) { if (value == null) { return null; } FastDateFormat df = FastDateFormat.getInstance(format); return df.format(value); } /** * Gets the delegate. * * @return the delegate */ protected IModel<E> getDelegate() { return delegate; } }