/*
* Copyright 2009 Richard Nichols.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package com.visural.wicket.component.viewmode;
import com.visural.wicket.security.IPrivilege;
import com.visural.wicket.security.ISecureEnableInstance;
import com.visural.wicket.security.ISecureRenderInstance;
import org.apache.wicket.Component;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.AbstractChoice;
import org.apache.wicket.markup.html.form.CheckBox;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.markup.html.form.FormComponentPanel;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IComponentInheritedModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.IWrapModel;
import org.apache.wicket.model.Model;
/**
* A wrapper for a FormComponent which replaces the component with a Label
* when the wrapped control is in "view mode".
*
* Apply to a containing DOM element, e.g. `<span></span>` or `<div></div>`
*
* The component you provide to the constructor must be given as ID matching
* `ViewOrEdit.COMP_ID`.
*
* The idea is to allow the rendering of web-like modern applications where
* non-editable elements are represented in text form, using the same wicket
* page / form implementation.
*
* What is "view mode" for a given component is determined by the method
* `isViewMode()`. The default implementation of this method uses the component
* and its form's `isEnabled()` state to determine view or edit.
*
* You may override this implementation and create any sort of complex logic you
* wish for your specific use case.
*
* The `ViewOrEdit` container automatically creates a dynamic wrapping model so
* that you can still apply `CompoundPropertyModels` to your form and have them
* apply to form elements wrapped in a `ViewOrEdit` container. Just name your
* `ViewOrEdit` element as per the property you wish to apply to the child
* control.
*
* @version $Id: ViewOrEdit.java 217 2010-09-30 23:57:10Z tibes80@gmail.com $
* @author Richard Nichols
*/
public class ViewOrEdit extends FormComponentPanel implements ISecureRenderInstance, ISecureEnableInstance {
private static final long serialVersionUID = 1L;
/**
* This is the id of the component to be contained within the ViewOrEdit container.
* Any component provided to the ViewOrEdit component should be identified as ViewOrEdit.COMP_ID
*/
public final static String COMP_ID = "component";
private final FormComponent component;
private Label label;
public ViewOrEdit(String id, FormComponent component) {
this(id, component, null, null);
}
public ViewOrEdit(String id, FormComponent component, IModel labelModel) {
this(id, component, labelModel, null);
}
public ViewOrEdit(String id, FormComponent component, ComponentModelToLabel componentModelToLabel) {
this(id, component, null, componentModelToLabel);
}
protected ViewOrEdit(final String id, final FormComponent component, IModel labelModel, final ComponentModelToLabel componentModelToLabel) {
super(id);
this.component = component;
if (labelModel == null && componentModelToLabel != null) {
labelModel = new AbstractReadOnlyModel() {
private final ComponentModelToLabel converter = componentModelToLabel;
@Override
public Object getObject() {
return converter.convertToLabel(component.getModelObject());
}
};
}
if (component == null || !component.getId().equals(COMP_ID)) {
throw new IllegalArgumentException("The component provided to LabelOnViewOnly(...) must have the id \"" + COMP_ID + "\"");
}
Fragment f = resolveComponentFragment(labelModel);
if (f == null) {
throw new UnsupportedOperationException("No view mode fragment for component of type " + component.getClass().getName());
}
add(f);
}
/**
* Provide support for using CompountPropertyModel or similar on form
* and having that property model chain to the form component being wrapped
* in a ViewOrEdit.
*
* @return
*/
@Override
protected IModel<?> initModel() {
final IModel parentModel = super.initModel();
if (parentModel != null && parentModel instanceof IWrapModel) {
// we want to set this model to wrap the form component
return new WrappedParentModel((IWrapModel)parentModel);
}
// we do this in case there is no parent model to avoid
// "Attempt to set model object on null model of component" errors
return new Model();
}
@Override
protected void convertInput() {
// this forces a call to initModel()
getDefaultModel();
}
/**
* Determine whether the component is in view mode or not.
*
* By default, the implementation will assume view mode when this component
* is not enabled or the form component is not enabled or the form itself
* is not enabled, however this behaviour may be overriden as req'd.
*
* @return
*/
public boolean isViewMode() {
return !this.isEnabled() || !component.isEnabled() || !component.getForm().isEnabled();
}
@Override
protected void onBeforeRender() {
// this forces a call to initModel()
getDefaultModel();
// now proceed as normal
super.onBeforeRender();
boolean isView = this.isViewMode();
label.setVisible(isView);
component.setVisible(!isView);
}
private Fragment resolveComponentFragment(IModel labelModel) {
if (labelModel == null) {
// TODO: rather than doing this, maybe lookup converter?
labelModel = new IModel() {
public Object getObject() {
return (component.getModelObject() == null ? null : component.getModelObject().toString());
}
public void setObject(Object arg0) {
}
public void detach() {
}
};
}
label = new Label("viewLabel", labelModel);
label.setEscapeModelStrings(isEscapeLabelModelStrings());
if (TextField.class.isAssignableFrom(component.getClass())) {
Fragment f = new Fragment("controlPair", "textfield", this);
f.add(label);
f.add(component);
return f;
}
if (CheckBox.class.isAssignableFrom(component.getClass())) {
Fragment f = new Fragment("controlPair", "checkbox", this);
f.add(label);
f.add(component);
return f;
}
if (TextArea.class.isAssignableFrom(component.getClass())) {
Fragment f = new Fragment("controlPair", "textarea", this);
f.add(label);
f.add(component);
return f;
}
if (AbstractChoice.class.isAssignableFrom(component.getClass())) {
Fragment f = new Fragment("controlPair", "choice", this);
f.add(label);
f.add(component);
return f;
}
return null;
}
public FormComponent getComponent() {
return component;
}
public boolean isEscapeLabelModelStrings() {
return false;
}
class WrappedParentModel implements IComponentInheritedModel {
private final IWrapModel realParent;
public WrappedParentModel(IWrapModel realParent) {
this.realParent = realParent;
}
public IWrapModel wrapOnInheritance(Component arg0) {
if (arg0 == ViewOrEdit.this.component) {
return realParent;
} else {
return null;
}
}
public Object getObject() {
return null;
}
public void setObject(Object arg0) {
}
public void detach() {
}
}
public IPrivilege getRenderPrivilege() {
return IPrivilege.NULL;
}
public IPrivilege getEnablePrivilege() {
return IPrivilege.NULL;
}
}