/*
* Copyright (c) 2005-2016 Vincent Vandenschrick. All rights reserved.
*
* This file is part of the Jspresso framework.
*
* Jspresso is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Jspresso is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Jspresso. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jspresso.framework.view.descriptor.basic;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.jspresso.framework.model.descriptor.IComponentDescriptor;
import org.jspresso.framework.model.descriptor.IComponentDescriptorProvider;
import org.jspresso.framework.util.gate.IGate;
import org.jspresso.framework.util.gate.IGateAccessible;
import org.jspresso.framework.util.gui.Icon;
import org.jspresso.framework.view.descriptor.ELabelPosition;
import org.jspresso.framework.view.descriptor.IComponentViewDescriptor;
import org.jspresso.framework.view.descriptor.IPropertyViewDescriptor;
/**
* Abstract class for component view descriptors.
*
* @author Vincent Vandenschrick
*/
public abstract class AbstractComponentViewDescriptor extends BasicViewDescriptor implements IComponentViewDescriptor {
private ELabelPosition labelsPosition;
private List<String> renderedProperties;
private List<IPropertyViewDescriptor> propertyViewDescriptors;
private AbstractComponentViewDescriptor readOnlyClone;
private String labelFont;
private String valueFont;
/**
* Instantiates a new Abstract component view descriptor.
*/
public AbstractComponentViewDescriptor() {
labelsPosition = ELabelPosition.ASIDE;
}
/**
* {@inheritDoc}
*/
@Override
public Icon getIcon() {
Icon icon = super.getIcon();
if (icon == null) {
icon = ((IComponentDescriptorProvider<?>) getModelDescriptor()).getComponentDescriptor().getIcon();
setIcon(icon);
}
return icon;
}
/**
* {@inheritDoc}
*/
@Override
public ELabelPosition getLabelsPosition() {
return labelsPosition;
}
/**
* If you need to override the behaviour, override {@link #getPropertyViewDescriptors(boolean)}.
* <p/>
* {@inheritDoc}
*/
@Override
public final List<IPropertyViewDescriptor> getPropertyViewDescriptors() {
return getPropertyViewDescriptors(true);
}
/**
* {@inheritDoc}
*/
@Override
public List<IPropertyViewDescriptor> getPropertyViewDescriptors(boolean explodeComponentReferences) {
IComponentDescriptor<?> componentDescriptor = ((IComponentDescriptorProvider<?>) getModelDescriptor())
.getComponentDescriptor();
List<IPropertyViewDescriptor> declaredPropertyViewDescriptors = propertyViewDescriptors;
if (declaredPropertyViewDescriptors == null) {
List<String> viewRenderedProperties = getRenderedProperties();
declaredPropertyViewDescriptors = new ArrayList<>();
for (String renderedProperty : viewRenderedProperties) {
BasicPropertyViewDescriptor propertyViewDescriptor = new BasicPropertyViewDescriptor();
propertyViewDescriptor.setName(renderedProperty);
propertyViewDescriptor.setWidth(getPropertyWidth(renderedProperty));
propertyViewDescriptor.setRenderedChildProperties(computeDefaultRenderedChildProperties(renderedProperty));
propertyViewDescriptor.setModelDescriptor(componentDescriptor.getPropertyDescriptor(renderedProperty));
propertyViewDescriptor.setLabelFont(getLabelFont());
propertyViewDescriptor.setFont(getValueFont());
declaredPropertyViewDescriptors.add(propertyViewDescriptor);
}
} else {
for (IPropertyViewDescriptor pvd : declaredPropertyViewDescriptors) {
if (pvd.getModelDescriptor() == null && pvd instanceof BasicPropertyViewDescriptor) {
((BasicPropertyViewDescriptor) pvd).setModelDescriptor(componentDescriptor.getPropertyDescriptor(
pvd.getName()));
if (pvd.getLabelFont() == null) {
((BasicPropertyViewDescriptor) pvd).setLabelFont(getLabelFont());
}
if (pvd.getFont() == null) {
((BasicPropertyViewDescriptor) pvd).setFont(getValueFont());
}
}
}
}
List<IPropertyViewDescriptor> actualPropertyViewDescriptors;
if (explodeComponentReferences) {
actualPropertyViewDescriptors = new ArrayList<>();
for (IPropertyViewDescriptor propertyViewDescriptor : declaredPropertyViewDescriptors) {
List<IPropertyViewDescriptor> exploded = PropertyViewDescriptorHelper.explodeComponentReferences(
propertyViewDescriptor, (IComponentDescriptorProvider<?>) getModelDescriptor());
if (exploded.size() > 0) {
if (propertyViewDescriptor.getWidth() != null && propertyViewDescriptor.getWidth() > exploded.size()) {
((BasicPropertyViewDescriptor) exploded.get(exploded.size() - 1)).setWidth(
propertyViewDescriptor.getWidth() - exploded.size() + 1);
}
actualPropertyViewDescriptors.addAll(exploded);
}
}
} else {
actualPropertyViewDescriptors = declaredPropertyViewDescriptors;
}
return actualPropertyViewDescriptors;
}
/**
* Gets the readabilityGates.
*
* @return the readabilityGates.
*/
@Override
public Collection<IGate> getReadabilityGates() {
Collection<IGate> gates = super.getReadabilityGates();
if (gates == null && getModelDescriptor() != null) {
if (getModelDescriptor() instanceof IGateAccessible) {
return ((IGateAccessible) getModelDescriptor()).getReadabilityGates();
}
}
return gates;
}
/**
* Gets the writabilityGates.
*
* @return the writabilityGates.
*/
@Override
public Collection<IGate> getWritabilityGates() {
Collection<IGate> gates = super.getWritabilityGates();
if (gates == null && getModelDescriptor() != null) {
if (getModelDescriptor() instanceof IGateAccessible) {
return ((IGateAccessible) getModelDescriptor()).getWritabilityGates();
}
}
return gates;
}
/**
* Instructs Jspresso where to place the fields label. This is either a value
* of the {@code ELabelPosition} enum or its equivalent string
* representation :
* <ul>
* <li>{@code ABOVE} for placing each field label above the property UI
* component</li>
* <li>{@code ASIDE} for placing each field label aside the property UI
* component</li>
* <li>{@code NONE} for completely disabling fields labelling on the view
* </li>
* </ul>
* Default value is {@code ELabelPosition.ASIDE}, i.e. fields label next
* to the property UI component.
*
* @param labelsPosition
* the labelsPosition to set.
*/
public void setLabelsPosition(ELabelPosition labelsPosition) {
this.labelsPosition = labelsPosition;
}
/**
* This property allows for configuring the fields of the component view in a
* very customizable manner, thus overriding the model descriptor defaults.
* Each property view descriptor contained in the list describes a form field
* that will be rendered in the UI accordingly.
* <p/>
* For instance, a writable property can be made specifically read-only on
* this component view by specifying its property view descriptor read-only.
* In that case, the model remains untouched and only the view is impacted.
* <p/>
* Following the same scheme, you can assign a list of writability gates on a
* field to introduce dynamic field editability on the view without modifying
* the model.
* <p/>
* A last, yet important, example of column view descriptor usage is the
* role-based field set configuration. Whenever you want a field to be
* available only for certain user roles (profiles), you can configure a field
* property view descriptor with a list of granted roles. If the user doesn't
* have the field(s)required role, the forbidden field(s) simply won't be
* displayed. This allows for high authorization-based versatility.
* <p/>
* There are many other usages of defining field property view descriptors
* (like individual labels color and font), all of them being linked to
* customizing the form fields without impacting the model.
*
* @param propertyViewDescriptors
* the propertyViewDescriptors to set.
*/
public void setPropertyViewDescriptors(List<IPropertyViewDescriptor> propertyViewDescriptors) {
this.propertyViewDescriptors = propertyViewDescriptors;
}
/**
* Gets property width.
*
* @param propertyName
* the property name
* @return the property width
*/
protected abstract Integer getPropertyWidth(String propertyName);
/**
* Compute default rendered child properties.
*
* @param propertyName
* the property name
* @return the list
*/
protected abstract List<String> computeDefaultRenderedChildProperties(String propertyName);
/**
* This is somehow a shortcut to using the
* {@code propertyViewDescriptors} property. Instead of providing a
* full-blown list of property view descriptors to configure the component
* view fields, you just pass-in a list of property names. view fields are
* then created from this list, keeping model defaults for all fields
* characteristics.
* <p/>
* Whenever the property value is {@code null} (default), the fields list
* is determined from the component descriptor {@code renderedProperties}
* property.
*
* @param renderedProperties
* the renderedProperties to set.
*/
public void setRenderedProperties(List<String> renderedProperties) {
this.renderedProperties = renderedProperties;
}
/**
* Gets the renderedProperties.
*
* @return the renderedProperties.
*/
private List<String> getRenderedProperties() {
if (renderedProperties == null) {
renderedProperties = ((IComponentDescriptorProvider<?>) getModelDescriptor()).getComponentDescriptor()
.getRenderedProperties();
}
return renderedProperties;
}
/**
* Queries the model property descriptor to determine read-only state.
* <p/>
* {@inheritDoc}
*/
@Override
public boolean isReadOnly() {
boolean readOnly = super.isReadOnly();
if (!readOnly && getModelDescriptor() != null) {
if (getModelDescriptor() instanceof IComponentDescriptorProvider<?>) {
return ((IComponentDescriptorProvider<?>) getModelDescriptor()).getComponentDescriptor().isReadOnly();
}
}
return readOnly;
}
/**
* Clone the component view in read only mode.
*
* @return the read-only component view descriptor.
*/
protected synchronized AbstractComponentViewDescriptor cloneReadOnly() {
if (readOnlyClone == null && getModelDescriptor() != null) {
readOnlyClone = (AbstractComponentViewDescriptor) clone();
List<IPropertyViewDescriptor> readOnlyDescriptors = new ArrayList<>();
for (IPropertyViewDescriptor descriptor : getPropertyViewDescriptors(false)) {
BasicPropertyViewDescriptor readOnlyDescriptor = (BasicPropertyViewDescriptor) ((BasicPropertyViewDescriptor)
descriptor)
.clone();
readOnlyDescriptor.setReadOnly(true);
readOnlyDescriptors.add(readOnlyDescriptor);
}
readOnlyClone.setPropertyViewDescriptors(readOnlyDescriptors);
}
return readOnlyClone;
}
/**
* Gets label font.
*
* @return the label font
*/
@Override
public String getLabelFont() {
return labelFont;
}
/**
* This property defines the font of the property labels. It might differ from
* the field component one. The font must be string encoded using the pattern
* <b>"[name];[style];[size]"</b> :
* <ul>
* <li><b>[name]</b> is the name of the font, e.g. <i>arial</i>.</li>
* <li><b>[style]</b> is PLAIN, BOLD, ITALIC or a union of BOLD and ITALIC
* combined with the '|' character, e.g. <i>BOLD|ITALIC</i>.</li>
* <li><b>[size]</b> is the size of the font, e.g. <i>10</i>.</li>
* </ul>
* Any of the above pattern section can be left empty, thus falling back to
* the component default.
* <p/>
* This property can be overridden on a field basis using an explicit property view definition.
* <p/>
* Default value is {@code null}, meaning use default component font.
*
* @param labelFont
* the labelFont to set.
*/
public void setLabelFont(String labelFont) {
this.labelFont = labelFont;
}
/**
* Gets value font.
*
* @return the value font
*/
@Override
public String getValueFont() {
return valueFont;
}
/**
* This property defines the font of the property values. The font must be string encoded using the pattern
* <b>"[name];[style];[size]"</b> :
* <ul>
* <li><b>[name]</b> is the name of the font, e.g. <i>arial</i>.</li>
* <li><b>[style]</b> is PLAIN, BOLD, ITALIC or a union of BOLD and ITALIC
* combined with the '|' character, e.g. <i>BOLD|ITALIC</i>.</li>
* <li><b>[size]</b> is the size of the font, e.g. <i>10</i>.</li>
* </ul>
* Any of the above pattern section can be left empty, thus falling back to
* the component default.
* <p/>
* This property can be overridden on a field basis using an explicit property view definition.
* <p/>
* Default value is {@code null}, meaning use default component font.
*
* @param valueFont
* the valueFont to set.
*/
public void setValueFont(String valueFont) {
this.valueFont = valueFont;
}
}