package fr.openwide.core.wicket.more.link.descriptor; import org.apache.wicket.behavior.Behavior; import org.apache.wicket.markup.ComponentTag; import org.apache.wicket.markup.html.image.Image; import org.apache.wicket.model.IModel; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.request.resource.ResourceReference; import org.apache.wicket.util.lang.Args; import fr.openwide.core.wicket.more.condition.Condition; import fr.openwide.core.wicket.more.link.descriptor.builder.LinkDescriptorBuilder; import fr.openwide.core.wicket.more.link.descriptor.builder.state.parameter.chosen.common.IChosenParameterState; import fr.openwide.core.wicket.more.link.descriptor.builder.state.parameter.mapping.IAddedParameterMappingState; import fr.openwide.core.wicket.more.link.descriptor.builder.state.validator.IValidatorState; import fr.openwide.core.wicket.more.link.descriptor.parameter.validator.ILinkParameterValidator; import fr.openwide.core.wicket.more.link.descriptor.parameter.validator.LinkParameterValidationException; import fr.openwide.core.wicket.more.link.descriptor.parameter.validator.LinkParameterValidationRuntimeException; import fr.openwide.core.wicket.more.link.descriptor.parameter.validator.LinkParameterValidators; /** * A {@link Image} whose targeted {@link ResourceReference} and {@link PageParameters} may change during the page life * cycle (for instance on an Ajax refresh). * <p><strong>WARNING:</strong> if this image is rendered while its parameters are invalid, then a * {@link LinkParameterValidationRuntimeException} will be thrown when executing * {@link #onComponentTag(org.apache.wicket.markup.ComponentTag) onComponentTag}. Similarly, if the target * {@link ResourceReference} is invalid, then a {@link LinkInvalidTargetRuntimeException} will be thrown. * <br/>This is an expected behavior: you should either ensure that your target and parameters are always valid, * or that this image is hidden when they are not. * <br />The latter can be obtained by either using {@link #hideIfInvalid()}, or adding custom * {@link Behavior behaviors} that would use the {@link #setVisibilityAllowed(boolean)} method. * @see LinkInvalidTargetRuntimeException * @see LinkParameterValidationRuntimeException * @see LinkDescriptorBuilder * @see IAddedParameterMappingState#mandatory() * @see IAddedParameterMappingState#optional() * @see IChosenParameterState#validator(fr.openwide.core.wicket.more.markup.html.factory.IDetachableFactory) * @see IChosenParameterState#validator(fr.openwide.core.wicket.more.link.descriptor.parameter.validator.factory.ILinkParameterValidatorFactory) * @see IValidatorState#validator(fr.openwide.core.wicket.more.link.descriptor.parameter.validator.ILinkParameterValidator) */ public class DynamicImage extends Image { private static final long serialVersionUID = 9089339534901852292L; private final IModel<PageParameters> parametersMapping; private final ILinkParameterValidator parametersValidator; private boolean autoHideIfInvalid = false; private final IModel<? extends ResourceReference> resourceReferenceModel; /* * Defaults to false, since Wicket's default to true: * 1. Causes performance issues: if the same image is on a given page multiple times, the browser is forced to * download it multiple times anyway. * 2. Is useless most of the time, as usage is to add the date of last modification to the query parameter, so * that cache is used only if the image has not been changed since last download. */ private Condition shouldAddAntiCacheParameterCondition = Condition.alwaysFalse(); public DynamicImage( String wicketId, IModel<? extends ResourceReference> resourceReferenceModel, IModel<PageParameters> parametersMapping, ILinkParameterValidator parametersValidator) { super(wicketId); Args.notNull(resourceReferenceModel, "resourceReferenceModel"); this.resourceReferenceModel = wrap(resourceReferenceModel); this.parametersMapping = parametersMapping; this.parametersValidator = parametersValidator; } /** * Same as {@link AbstractDynamicBookmarkableLink#setAutoHideIfInvalid()} * @deprecated Use {@link #hideIfInvalid()} instead. */ @Deprecated public DynamicImage setAutoHideIfInvalid(boolean autoHideIfInvalid) { this.autoHideIfInvalid = autoHideIfInvalid; return this; } /** * Sets the link up so that it will automatically hide (using {@link #setVisible(boolean)}) when its target or parameters are invalid. * <p>Default behavior is throwing a {@link LinkInvalidTargetRuntimeException} or a {@link LinkParameterValidationRuntimeException} * if the target or the parameters are found to be invalid when executing {@link #onComponentTag(org.apache.wicket.markup.ComponentTag)}. */ public DynamicImage hideIfInvalid() { setAutoHideIfInvalid(true); return this; } private PageParameters getParameters() { return parametersMapping.getObject(); } @Override protected void onConfigure() { super.onConfigure(); if (autoHideIfInvalid) { setVisible(isValid()); } else { setVisible(true); } } private boolean isValid() { if (getImageResourceReference() != null) { if (LinkParameterValidators.isModelValid(parametersValidator)) { PageParameters parameters = getParameters(); if (LinkParameterValidators.isSerializedValid(parameters, parametersValidator)) { return true; } } } return false; } @Override protected final ResourceReference getImageResourceReference() { ResourceReference resourceReference = resourceReferenceModel.getObject(); return resourceReference; } @Override protected void onComponentTag(ComponentTag tag) { ResourceReference resourceReference = getImageResourceReference(); if (resourceReference == null) { throw new IllegalStateException("The target ResourceReference of an image of type " + getClass() + " was null when trying to render the url."); } PageParameters parameters; try { LinkParameterValidators.checkModel(parametersValidator); parameters = getParameters(); LinkParameterValidators.checkSerialized(parameters, parametersValidator); } catch(LinkParameterValidationException e) { throw new LinkParameterValidationRuntimeException(e); } setImageResourceReference(resourceReference, parameters); super.onComponentTag(tag); } /** * Sets whether Wicket should automatically add a timestamp parameter so as to bypass browser cache. * <strong>Warning:</strong> use with caution, since this bypasses browser cache completely. If there are multiple * references to the same image in a single page, the browser will have to download the image once for each * reference. * @param shouldAddAntiCacheParameterCondition The condition on which to add the anticache parameter automatically. */ public DynamicImage setShouldAddAntiCacheParameterCondition(Condition shouldAddAntiCacheParameterCondition) { this.shouldAddAntiCacheParameterCondition = shouldAddAntiCacheParameterCondition; return this; } @Override protected final boolean shouldAddAntiCacheParameter() { return shouldAddAntiCacheParameterCondition.applies(); } @Override protected void onDetach() { super.onDetach(); parametersMapping.detach(); resourceReferenceModel.detach(); } }