PL-9948 Pluggable Field factories for FieldGroup / DataGrid / Table

This commit is contained in:
Gleb Gorelov 2017-12-22 17:41:54 +04:00
parent 095c87246a
commit 2c46876b81
16 changed files with 953 additions and 1008 deletions

View File

@ -23,13 +23,18 @@ import com.haulmont.cuba.gui.ComponentPalette;
import com.haulmont.cuba.gui.components.*;
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
import javax.inject.Inject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@org.springframework.stereotype.Component(ComponentsFactory.NAME)
public class DesktopComponentsFactory implements ComponentsFactory {
@Inject
protected List<ComponentGenerationStrategy> componentGenerationStrategies;
private static Map<String, Class<? extends Component>> classes = new HashMap<>();
private static Map<Class, String> names = new ConcurrentHashMap<>();
@ -139,8 +144,27 @@ public class DesktopComponentsFactory implements ComponentsFactory {
return type.cast(createComponent(name));
}
@Override
public Component createComponent(ComponentGenerationContext context) {
List<ComponentGenerationStrategy> strategies = getComponentGenerationStrategies();
for (ComponentGenerationStrategy strategy : strategies) {
Component component = strategy.createComponent(context);
if (component != null) {
return component;
}
}
throw new IllegalArgumentException(String.format("Can't create component for the '%s' with " +
"given meta class '%s'", context.getProperty(), context.getMetaClass()));
}
@Override
public Timer createTimer() {
return new DesktopTimer();
}
protected List<ComponentGenerationStrategy> getComponentGenerationStrategies() {
return componentGenerationStrategies;
}
}

View File

@ -21,12 +21,16 @@ import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.desktop.gui.DesktopComponentsFactory;
import com.haulmont.cuba.desktop.gui.components.DesktopFieldGroup;
import com.haulmont.cuba.desktop.gui.executors.impl.DesktopBackgroundWorker;
import com.haulmont.cuba.gui.components.ComponentGenerationStrategy;
import com.haulmont.cuba.gui.components.DefaultComponentGenerationStrategy;
import com.haulmont.cuba.gui.components.FieldGroup;
import com.haulmont.cuba.gui.components.FieldGroupTest;
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
import mockit.Mocked;
import mockit.NonStrictExpectations;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
public class DesktopFieldGroupTest extends FieldGroupTest {
@ -47,7 +51,14 @@ public class DesktopFieldGroupTest extends FieldGroupTest {
@Override
protected ComponentsFactory createComponentsFactory() {
return new DesktopComponentsFactory();
return new DesktopComponentsFactory() {
@Override
public List<ComponentGenerationStrategy> getComponentGenerationStrategies() {
DefaultComponentGenerationStrategy strategy = new DefaultComponentGenerationStrategy(messages);
strategy.setComponentsFactory(this);
return Collections.singletonList(strategy);
}
};
}
@Override

View File

@ -0,0 +1,451 @@
/*
* Copyright (c) 2008-2017 Haulmont.
*
* 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.
*/
package com.haulmont.cuba.gui.components;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.chile.core.model.MetaProperty;
import com.haulmont.chile.core.model.MetaPropertyPath;
import com.haulmont.chile.core.model.Range;
import com.haulmont.cuba.core.app.dynamicattributes.DynamicAttributesMetaProperty;
import com.haulmont.cuba.core.app.dynamicattributes.DynamicAttributesUtils;
import com.haulmont.cuba.core.app.dynamicattributes.PropertyType;
import com.haulmont.cuba.core.entity.CategoryAttribute;
import com.haulmont.cuba.core.entity.FileDescriptor;
import com.haulmont.cuba.core.entity.annotation.CurrencyValue;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.Messages;
import com.haulmont.cuba.gui.ComponentsHelper;
import com.haulmont.cuba.gui.WindowManager;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.haulmont.cuba.gui.dynamicattributes.DynamicAttributesGuiTools;
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Element;
import javax.annotation.Nullable;
import java.lang.reflect.Method;
import java.sql.Time;
import java.util.Collection;
import java.util.Date;
import java.util.UUID;
public abstract class AbstractComponentGenerationStrategy implements ComponentGenerationStrategy {
protected Messages messages;
protected ComponentsFactory componentsFactory;
public AbstractComponentGenerationStrategy(Messages messages) {
this.messages = messages;
}
protected Component createComponentInternal(ComponentGenerationContext context) {
MetaClass metaClass = context.getMetaClass();
MetaPropertyPath mpp = resolveMetaPropertyPath(metaClass, context.getProperty());
Element xmlDescriptor = context.getXmlDescriptor();
if (mpp != null) {
Range mppRange = mpp.getRange();
if (mppRange.isDatatype()) {
Class type = mppRange.asDatatype().getJavaClass();
MetaProperty metaProperty = mpp.getMetaProperty();
if (DynamicAttributesUtils.isDynamicAttribute(metaProperty)) {
CategoryAttribute categoryAttribute = DynamicAttributesUtils.getCategoryAttribute(metaProperty);
if (categoryAttribute != null && categoryAttribute.getDataType() == PropertyType.ENUMERATION) {
return createEnumField(context);
}
}
if (xmlDescriptor != null
&& "true".equalsIgnoreCase(xmlDescriptor.attributeValue("link"))) {
return createDatatypeLinkField(context);
} else {
boolean hasMaskAttribute = xmlDescriptor != null
&& xmlDescriptor.attribute("mask") != null;
if (type.equals(String.class)) {
if (hasMaskAttribute) {
return createMaskedField(context);
} else {
return createStringField(context, mpp);
}
} else if (type.equals(UUID.class)) {
return createUuidField(context);
} else if (type.equals(Boolean.class)) {
return createBooleanField(context);
} else if (type.equals(java.sql.Date.class) || type.equals(Date.class)) {
return createDateField(context);
} else if (type.equals(Time.class)) {
return createTimeField(context);
} else if (Number.class.isAssignableFrom(type)) {
if (hasMaskAttribute) {
return createMaskedField(context);
} else {
Field currencyField = createCurrencyField(context, mpp);
if (currencyField != null) {
return currencyField;
}
return createNumberField(context);
}
}
}
} else if (mppRange.isClass()) {
MetaProperty metaProperty = mpp.getMetaProperty();
Class<?> javaType = metaProperty.getJavaType();
if (FileDescriptor.class.isAssignableFrom(javaType)) {
return createFileUploadField(context);
}
if (!Collection.class.isAssignableFrom(javaType)) {
return createEntityField(context, mpp);
}
} else if (mppRange.isEnum()) {
return createEnumField(context);
}
}
return null;
}
protected Component createDatatypeLinkField(ComponentGenerationContext context) {
EntityLinkField linkField = componentsFactory.createComponent(EntityLinkField.class);
setDatasource(linkField, context);
setLinkFieldAttributes(linkField, context);
return linkField;
}
protected Field createEnumField(ComponentGenerationContext context) {
LookupField component = componentsFactory.createComponent(LookupField.class);
setDatasource(component, context);
return component;
}
protected Component createMaskedField(ComponentGenerationContext context) {
MaskedField maskedField = componentsFactory.createComponent(MaskedField.class);
setDatasource(maskedField, context);
Element xmlDescriptor = context.getXmlDescriptor();
if (xmlDescriptor != null) {
maskedField.setMask(xmlDescriptor.attributeValue("mask"));
String valueModeStr = xmlDescriptor.attributeValue("valueMode");
if (StringUtils.isNotEmpty(valueModeStr)) {
maskedField.setValueMode(MaskedField.ValueMode.valueOf(valueModeStr.toUpperCase()));
}
}
maskedField.setValueMode(MaskedField.ValueMode.MASKED);
maskedField.setSendNullRepresentation(false);
return maskedField;
}
protected Component createStringField(ComponentGenerationContext context, MetaPropertyPath mpp) {
TextInputField textField = null;
Element xmlDescriptor = context.getXmlDescriptor();
if (xmlDescriptor != null) {
final String rows = xmlDescriptor.attributeValue("rows");
if (!StringUtils.isEmpty(rows)) {
TextArea textArea = componentsFactory.createComponent(TextArea.class);
textArea.setRows(Integer.parseInt(rows));
textField = textArea;
}
}
if (DynamicAttributesUtils.isDynamicAttribute(context.getProperty()) && mpp != null) {
CategoryAttribute categoryAttribute = DynamicAttributesUtils.getCategoryAttribute(mpp.getMetaProperty());
if (categoryAttribute != null && categoryAttribute.getDataType() == PropertyType.STRING
&& categoryAttribute.getRowsCount() != null && categoryAttribute.getRowsCount() > 1) {
TextArea textArea = componentsFactory.createComponent(TextArea.class);
textArea.setRows(categoryAttribute.getRowsCount());
textField = textArea;
}
}
if (textField == null) {
textField = componentsFactory.createComponent(TextField.class);
}
setDatasource(textField, context);
String maxLength = xmlDescriptor != null ? xmlDescriptor.attributeValue("maxLength") : null;
if (StringUtils.isNotEmpty(maxLength)) {
((TextInputField.MaxLengthLimited) textField).setMaxLength(Integer.parseInt(maxLength));
}
return textField;
}
protected Field createUuidField(ComponentGenerationContext context) {
MaskedField maskedField = componentsFactory.createComponent(MaskedField.class);
setDatasource(maskedField, context);
maskedField.setMask("hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh");
maskedField.setSendNullRepresentation(false);
return maskedField;
}
protected Field createBooleanField(ComponentGenerationContext context) {
CheckBox component = componentsFactory.createComponent(CheckBox.class);
setDatasource(component, context);
return component;
}
protected Component createDateField(ComponentGenerationContext context) {
DateField dateField = componentsFactory.createComponent(DateField.class);
setDatasource(dateField, context);
Element xmlDescriptor = context.getXmlDescriptor();
final String resolution = xmlDescriptor == null ? null : xmlDescriptor.attributeValue("resolution");
String dateFormat = xmlDescriptor == null ? null : xmlDescriptor.attributeValue("dateFormat");
DateField.Resolution dateResolution = DateField.Resolution.MIN;
if (StringUtils.isNotEmpty(resolution)) {
dateResolution = DateField.Resolution.valueOf(resolution);
dateField.setResolution(dateResolution);
}
if (dateFormat == null) {
if (dateResolution == DateField.Resolution.DAY) {
dateFormat = "msg://dateFormat";
} else if (dateResolution == DateField.Resolution.MIN) {
dateFormat = "msg://dateTimeFormat";
}
}
if (StringUtils.isNotEmpty(dateFormat)) {
if (dateFormat.startsWith("msg://")) {
dateFormat = messages.getMainMessage(dateFormat.substring(6, dateFormat.length()));
}
dateField.setDateFormat(dateFormat);
}
return dateField;
}
protected Component createTimeField(ComponentGenerationContext context) {
TimeField timeField = componentsFactory.createComponent(TimeField.class);
setDatasource(timeField, context);
Element xmlDescriptor = context.getXmlDescriptor();
if (xmlDescriptor != null) {
String showSeconds = xmlDescriptor.attributeValue("showSeconds");
if (Boolean.parseBoolean(showSeconds)) {
timeField.setShowSeconds(true);
}
}
return timeField;
}
protected Field createNumberField(ComponentGenerationContext context) {
TextField component = componentsFactory.createComponent(TextField.class);
setDatasource(component, context);
return component;
}
@Nullable
protected Field createCurrencyField(ComponentGenerationContext context, MetaPropertyPath mpp) {
if (DynamicAttributesUtils.isDynamicAttribute(mpp.getMetaProperty()))
return null;
Object currencyAnnotation = mpp.getMetaProperty().getAnnotations().get(CurrencyValue.class.getName());
if (currencyAnnotation == null) {
return null;
}
CurrencyField component = componentsFactory.createComponent(CurrencyField.class);
setDatasource(component, context);
return component;
}
protected Field createFileUploadField(ComponentGenerationContext context) {
FileUploadField fileUploadField = (FileUploadField) componentsFactory.createComponent(FileUploadField.NAME);
fileUploadField.setMode(FileUploadField.FileStoragePutMode.IMMEDIATE);
fileUploadField.setUploadButtonCaption(null);
fileUploadField.setUploadButtonDescription(messages.getMainMessage("upload.submit"));
fileUploadField.setUploadButtonIcon("icons/upload.png");
fileUploadField.setClearButtonCaption(null);
fileUploadField.setClearButtonDescription(messages.getMainMessage("upload.clear"));
fileUploadField.setClearButtonIcon("icons/remove.png");
fileUploadField.setShowFileName(true);
fileUploadField.setShowClearButton(true);
setDatasource(fileUploadField, context);
return fileUploadField;
}
protected Component createEntityField(ComponentGenerationContext context, MetaPropertyPath mpp) {
String linkAttribute = null;
Element xmlDescriptor = context.getXmlDescriptor();
if (xmlDescriptor != null) {
linkAttribute = xmlDescriptor.attributeValue("link");
}
if (!Boolean.parseBoolean(linkAttribute)) {
CollectionDatasource optionsDatasource = context.getOptionsDatasource();
if (DynamicAttributesUtils.isDynamicAttribute(mpp.getMetaProperty())) {
DynamicAttributesMetaProperty metaProperty = (DynamicAttributesMetaProperty) mpp.getMetaProperty();
CategoryAttribute attribute = metaProperty.getAttribute();
if (Boolean.TRUE.equals(attribute.getLookup())) {
DynamicAttributesGuiTools dynamicAttributesGuiTools = AppBeans.get(DynamicAttributesGuiTools.class);
optionsDatasource = dynamicAttributesGuiTools
.createOptionsDatasourceForLookup(metaProperty.getRange().asClass(),
attribute.getJoinClause(), attribute.getWhereClause());
}
}
PickerField pickerField;
if (optionsDatasource == null) {
pickerField = componentsFactory.createComponent(PickerField.class);
setDatasource(pickerField, context);
if (mpp.getMetaProperty().getType() == MetaProperty.Type.ASSOCIATION) {
pickerField.addLookupAction();
if (DynamicAttributesUtils.isDynamicAttribute(mpp.getMetaProperty())) {
DynamicAttributesGuiTools dynamicAttributesGuiTools =
AppBeans.get(DynamicAttributesGuiTools.class);
DynamicAttributesMetaProperty dynamicAttributesMetaProperty =
(DynamicAttributesMetaProperty) mpp.getMetaProperty();
dynamicAttributesGuiTools.initEntityPickerField(pickerField,
dynamicAttributesMetaProperty.getAttribute());
}
boolean actionsByMetaAnnotations = ComponentsHelper.createActionsByMetaAnnotations(pickerField);
if (!actionsByMetaAnnotations) {
pickerField.addClearAction();
}
} else {
pickerField.addOpenAction();
pickerField.addClearAction();
}
} else {
LookupPickerField lookupPickerField = componentsFactory.createComponent(LookupPickerField.class);
setDatasource(lookupPickerField, context);
lookupPickerField.setOptionsDatasource(optionsDatasource);
pickerField = lookupPickerField;
ComponentsHelper.createActionsByMetaAnnotations(pickerField);
}
if (xmlDescriptor != null) {
String captionProperty = xmlDescriptor.attributeValue("captionProperty");
if (StringUtils.isNotEmpty(captionProperty)) {
pickerField.setCaptionMode(CaptionMode.PROPERTY);
pickerField.setCaptionProperty(captionProperty);
}
}
return pickerField;
} else {
EntityLinkField linkField = componentsFactory.createComponent(EntityLinkField.class);
setDatasource(linkField, context);
setLinkFieldAttributes(linkField, context);
return linkField;
}
}
protected void setLinkFieldAttributes(EntityLinkField linkField, ComponentGenerationContext context) {
Element xmlDescriptor = context.getXmlDescriptor();
if (xmlDescriptor != null) {
String linkScreen = xmlDescriptor.attributeValue("linkScreen");
if (StringUtils.isNotEmpty(linkScreen)) {
linkField.setScreen(linkScreen);
}
final String invokeMethodName = xmlDescriptor.attributeValue("linkInvoke");
if (StringUtils.isNotEmpty(invokeMethodName)) {
linkField.setCustomClickHandler(new InvokeEntityLinkClickHandler(invokeMethodName));
}
String openTypeAttribute = xmlDescriptor.attributeValue("linkScreenOpenType");
if (StringUtils.isNotEmpty(openTypeAttribute)) {
WindowManager.OpenType openType = WindowManager.OpenType.valueOf(openTypeAttribute);
linkField.setScreenOpenType(openType);
}
}
}
protected MetaPropertyPath resolveMetaPropertyPath(MetaClass metaClass, String property) {
MetaPropertyPath mpp = metaClass.getPropertyPath(property);
if (mpp == null && DynamicAttributesUtils.isDynamicAttribute(property)) {
mpp = DynamicAttributesUtils.getMetaPropertyPath(metaClass, property);
}
return mpp;
}
protected void setDatasource(Field field, ComponentGenerationContext context) {
if (context.getDatasource() != null && StringUtils.isNotEmpty(context.getProperty())) {
field.setDatasource(context.getDatasource(), context.getProperty());
}
}
protected static class InvokeEntityLinkClickHandler implements EntityLinkField.EntityLinkClickHandler {
protected final String invokeMethodName;
public InvokeEntityLinkClickHandler(String invokeMethodName) {
this.invokeMethodName = invokeMethodName;
}
@Override
public void onClick(EntityLinkField field) {
Window frame = ComponentsHelper.getWindow(field);
if (frame == null) {
throw new IllegalStateException("Please specify Frame for EntityLinkField");
}
Object controller = ComponentsHelper.getFrameController(frame);
Method method;
try {
method = controller.getClass().getMethod(invokeMethodName, EntityLinkField.class);
try {
method.invoke(controller, field);
} catch (Exception e) {
throw new RuntimeException(String.format("Can't invoke method with name '%s'",
invokeMethodName), e);
}
} catch (NoSuchMethodException e) {
try {
method = controller.getClass().getMethod(invokeMethodName);
try {
method.invoke(controller);
} catch (Exception ex) {
throw new RuntimeException(String.format("Can't invoke method with name '%s'",
invokeMethodName), ex);
}
} catch (NoSuchMethodException e1) {
throw new IllegalStateException(String.format("No suitable methods named '%s' for invoke",
invokeMethodName));
}
}
}
}
}

View File

@ -17,38 +17,15 @@
package com.haulmont.cuba.gui.components;
import com.haulmont.bali.util.Preconditions;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.chile.core.model.MetaProperty;
import com.haulmont.chile.core.model.MetaPropertyPath;
import com.haulmont.chile.core.model.Range;
import com.haulmont.cuba.core.app.dynamicattributes.DynamicAttributesMetaProperty;
import com.haulmont.cuba.core.app.dynamicattributes.DynamicAttributesUtils;
import com.haulmont.cuba.core.app.dynamicattributes.PropertyType;
import com.haulmont.cuba.core.entity.CategoryAttribute;
import com.haulmont.cuba.core.entity.FileDescriptor;
import com.haulmont.cuba.core.entity.annotation.CurrencyValue;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.Messages;
import com.haulmont.cuba.gui.AppConfig;
import com.haulmont.cuba.gui.ComponentsHelper;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.haulmont.cuba.gui.data.Datasource;
import com.haulmont.cuba.gui.data.RuntimePropsDatasource;
import com.haulmont.cuba.gui.dynamicattributes.DynamicAttributesGuiTools;
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Element;
import javax.annotation.Nullable;
import java.lang.reflect.Method;
import java.sql.Time;
import java.util.Collection;
import java.util.Date;
import java.util.UUID;
import static com.haulmont.cuba.gui.WindowManager.OpenType;
import static com.haulmont.cuba.gui.components.EntityLinkField.EntityLinkClickHandler;
public abstract class AbstractFieldFactory implements FieldFactory {
@ -56,403 +33,22 @@ public abstract class AbstractFieldFactory implements FieldFactory {
@Override
public Component createField(Datasource datasource, String property, Element xmlDescriptor) {
MetaClass metaClass = datasource.getMetaClass();
MetaPropertyPath mpp = metaClass.getPropertyPath(property);
MetaClass metaClass = resolveMetaClass(datasource);
if (mpp == null && DynamicAttributesUtils.isDynamicAttribute(property)) {
mpp = DynamicAttributesUtils.getMetaPropertyPath(metaClass, property);
}
ComponentGenerationContext context = new ComponentGenerationContext(metaClass, property)
.setDatasource(datasource)
.setOptionsDatasource(getOptionsDatasource(datasource, property))
.setXmlDescriptor(xmlDescriptor)
.setComponentClass(Table.class);
if (mpp != null) {
Range mppRange = mpp.getRange();
if (mppRange.isDatatype()) {
Class type = mppRange.asDatatype().getJavaClass();
MetaProperty metaProperty = mpp.getMetaProperty();
if (DynamicAttributesUtils.isDynamicAttribute(metaProperty)) {
CategoryAttribute categoryAttribute = DynamicAttributesUtils.getCategoryAttribute(metaProperty);
if (categoryAttribute != null && categoryAttribute.getDataType() == PropertyType.ENUMERATION) {
return createEnumField(datasource, property);
}
}
if (xmlDescriptor != null
&& "true".equalsIgnoreCase(xmlDescriptor.attributeValue("link"))) {
return createDatatypeLinkField(datasource, property, xmlDescriptor);
} else if (type.equals(String.class)) {
if (xmlDescriptor != null
&& xmlDescriptor.attribute("mask") != null) {
return createMaskedField(datasource, property, xmlDescriptor);
} else {
return createStringField(datasource, property, xmlDescriptor);
}
} else if (type.equals(UUID.class)) {
return createUuidField(datasource, property);
} else if (type.equals(Boolean.class)) {
return createBooleanField(datasource, property);
} else if (type.equals(java.sql.Date.class) || type.equals(Date.class)) {
return createDateField(datasource, property, mpp, xmlDescriptor);
} else if (type.equals(Time.class)) {
return createTimeField(datasource, property, xmlDescriptor);
} else if (Number.class.isAssignableFrom(type)) {
if (xmlDescriptor != null
&& xmlDescriptor.attribute("mask") != null) {
MaskedField maskedField = (MaskedField) createMaskedField(datasource, property, xmlDescriptor);
maskedField.setValueMode(MaskedField.ValueMode.MASKED);
maskedField.setSendNullRepresentation(false);
return maskedField;
} else {
return createNumberField(datasource, property);
}
}
} else if (mppRange.isClass()) {
MetaProperty metaProperty = mpp.getMetaProperty();
Class<?> javaType = metaProperty.getJavaType();
if (FileDescriptor.class.isAssignableFrom(javaType)) {
return createFileUploadField(datasource, property);
} else if (!Collection.class.isAssignableFrom(javaType)) {
return createEntityField(datasource, property, mpp, xmlDescriptor);
}
} else if (mppRange.isEnum()) {
return createEnumField(datasource, property);
}
}
String exceptionMessage;
if (mpp != null) {
String name = mpp.getRange().isDatatype()
? mpp.getRange().asDatatype().toString()
: mpp.getRange().asClass().getName();
exceptionMessage = String.format("Can't create field \"%s\" with data type: %s", property, name);
} else {
exceptionMessage = String.format("Can't create field \"%s\" with given data type", property);
}
throw new UnsupportedOperationException(exceptionMessage);
return componentsFactory.createComponent(context);
}
protected Component createFileUploadField(Datasource datasource, String property) {
UploadField uploadField = (FileUploadField) componentsFactory.createComponent(FileUploadField.NAME);
FileUploadField fileUploadField = ((FileUploadField) uploadField);
fileUploadField.setMode(FileUploadField.FileStoragePutMode.IMMEDIATE);
Messages messages = AppBeans.get(Messages.NAME);
fileUploadField.setUploadButtonCaption(null);
fileUploadField.setUploadButtonDescription(messages.getMainMessage("upload.submit"));
fileUploadField.setUploadButtonIcon("icons/upload.png");
fileUploadField.setClearButtonCaption(null);
fileUploadField.setClearButtonIcon("icons/remove.png");
fileUploadField.setClearButtonDescription(messages.getMainMessage("upload.clear"));
fileUploadField.setShowFileName(true);
fileUploadField.setShowClearButton(true);
fileUploadField.setDatasource(datasource, property);
return uploadField;
}
protected Component createDatatypeLinkField(Datasource datasource, String property, Element xmlDescriptor) {
EntityLinkField linkField = componentsFactory.createComponent(EntityLinkField.class);
linkField.setDatasource(datasource, property);
if (xmlDescriptor != null) {
String linkScreen = xmlDescriptor.attributeValue("linkScreen");
if (StringUtils.isNotEmpty(linkScreen)) {
linkField.setScreen(linkScreen);
}
final String invokeMethodName = xmlDescriptor.attributeValue("linkInvoke");
if (StringUtils.isNotEmpty(invokeMethodName)) {
linkField.setCustomClickHandler(new InvokeEntityLinkClickHandler(invokeMethodName));
}
String openTypeAttribute = xmlDescriptor.attributeValue("linkScreenOpenType");
if (StringUtils.isNotEmpty(openTypeAttribute)) {
OpenType openType = OpenType.valueOf(openTypeAttribute);
linkField.setScreenOpenType(openType);
}
}
return linkField;
}
protected Component createUuidField(Datasource datasource, String property) {
MaskedField maskedField = componentsFactory.createComponent(MaskedField.class);
maskedField.setDatasource(datasource, property);
maskedField.setMask("hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh");
maskedField.setSendNullRepresentation(false);
return maskedField;
}
protected Component createNumberField(Datasource datasource, String property) {
Component currencyField = createCurrencyField(datasource, property);
if (currencyField != null) {
return currencyField;
}
TextField textField = componentsFactory.createComponent(TextField.class);
textField.setDatasource(datasource, property);
return textField;
}
@Nullable
protected Component createCurrencyField(Datasource datasource, String property) {
if (DynamicAttributesUtils.isDynamicAttribute(property))
return null;
MetaPropertyPath mpp = datasource.getMetaClass().getPropertyPath(property);
Preconditions.checkNotNullArgument(mpp, "Could not resolve property path '%s' in '%s'",
property, datasource.getMetaClass());
Object currencyAnnotation = mpp.getMetaProperty().getAnnotations().get(CurrencyValue.class.getName());
if (currencyAnnotation == null) {
return null;
}
CurrencyField currencyField = componentsFactory.createComponent(CurrencyField.class);
currencyField.setDatasource(datasource, property);
return currencyField;
}
protected Component createBooleanField(Datasource datasource, String property) {
CheckBox checkBox = componentsFactory.createComponent(CheckBox.class);
checkBox.setDatasource(datasource, property);
return checkBox;
}
protected Component createMaskedField(Datasource datasource, String property, Element xmlDescriptor) {
MaskedField maskedField = componentsFactory.createComponent(MaskedField.class);
maskedField.setDatasource(datasource, property);
if (xmlDescriptor != null) {
maskedField.setMask(xmlDescriptor.attributeValue("mask"));
String valueModeStr = xmlDescriptor.attributeValue("valueMode");
if (StringUtils.isNotEmpty(valueModeStr)) {
maskedField.setValueMode(MaskedField.ValueMode.valueOf(valueModeStr.toUpperCase()));
}
}
return maskedField;
}
protected Component createStringField(Datasource datasource, String property, Element xmlDescriptor) {
TextInputField textField = null;
if (xmlDescriptor != null) {
final String rows = xmlDescriptor.attributeValue("rows");
if (!StringUtils.isEmpty(rows)) {
TextArea textArea = componentsFactory.createComponent(TextArea.class);
textArea.setRows(Integer.parseInt(rows));
textField = textArea;
}
}
if (DynamicAttributesUtils.isDynamicAttribute(property)) {
MetaClass metaClass = datasource instanceof RuntimePropsDatasource ?
((RuntimePropsDatasource) datasource).resolveCategorizedEntityClass() : datasource.getMetaClass();
MetaPropertyPath mpp = DynamicAttributesUtils.getMetaPropertyPath(metaClass, property);
if (mpp != null) {
CategoryAttribute categoryAttribute = DynamicAttributesUtils.getCategoryAttribute(mpp.getMetaProperty());
if (categoryAttribute != null && categoryAttribute.getDataType() == PropertyType.STRING
&& categoryAttribute.getRowsCount() != null && categoryAttribute.getRowsCount() > 1) {
TextArea textArea = componentsFactory.createComponent(TextArea.class);
textArea.setRows(categoryAttribute.getRowsCount());
textField = textArea;
}
}
}
if (textField == null) {
textField = componentsFactory.createComponent(TextField.class);
}
textField.setDatasource(datasource, property);
String maxLength = xmlDescriptor != null ? xmlDescriptor.attributeValue("maxLength") : null;
if (StringUtils.isNotEmpty(maxLength)) {
((TextInputField.MaxLengthLimited) textField).setMaxLength(Integer.parseInt(maxLength));
}
return textField;
}
protected Component createDateField(Datasource datasource, String property, MetaPropertyPath mpp,
Element xmlDescriptor) {
DateField dateField = componentsFactory.createComponent(DateField.class);
dateField.setDatasource(datasource, property);
final String resolution = xmlDescriptor == null ? null : xmlDescriptor.attributeValue("resolution");
String dateFormat = xmlDescriptor == null ? null : xmlDescriptor.attributeValue("dateFormat");
DateField.Resolution dateResolution = DateField.Resolution.MIN;
if (StringUtils.isNotEmpty(resolution)) {
dateResolution = DateField.Resolution.valueOf(resolution);
dateField.setResolution(dateResolution);
}
if (dateFormat == null) {
if (dateResolution == DateField.Resolution.DAY) {
dateFormat = "msg://dateFormat";
} else if (dateResolution == DateField.Resolution.MIN) {
dateFormat = "msg://dateTimeFormat";
}
}
Messages messages = AppBeans.get(Messages.NAME);
if (StringUtils.isNotEmpty(dateFormat)) {
if (dateFormat.startsWith("msg://")) {
dateFormat = messages.getMainMessage(dateFormat.substring(6, dateFormat.length()));
}
dateField.setDateFormat(dateFormat);
}
return dateField;
}
protected Component createTimeField(Datasource datasource, String property, Element xmlDescriptor) {
TimeField timeField = componentsFactory.createComponent(TimeField.class);
timeField.setDatasource(datasource, property);
if (xmlDescriptor != null) {
String showSeconds = xmlDescriptor.attributeValue("showSeconds");
if (Boolean.parseBoolean(showSeconds)) {
timeField.setShowSeconds(true);
}
}
return timeField;
}
protected Component createEntityField(Datasource datasource, String property, MetaPropertyPath mpp, Element xmlDescriptor) {
String linkAttribute = null;
if (xmlDescriptor != null) {
linkAttribute = xmlDescriptor.attributeValue("link");
}
if (!Boolean.parseBoolean(linkAttribute)) {
CollectionDatasource optionsDatasource = getOptionsDatasource(datasource, property);
if (DynamicAttributesUtils.isDynamicAttribute(mpp.getMetaProperty())) {
DynamicAttributesMetaProperty metaProperty = (DynamicAttributesMetaProperty) mpp.getMetaProperty();
CategoryAttribute attribute = metaProperty.getAttribute();
if (Boolean.TRUE.equals(attribute.getLookup())) {
DynamicAttributesGuiTools dynamicAttributesGuiTools = AppBeans.get(DynamicAttributesGuiTools.class);
optionsDatasource = dynamicAttributesGuiTools.createOptionsDatasourceForLookup(metaProperty.getRange().asClass(),
attribute.getJoinClause(), attribute.getWhereClause());
}
}
PickerField pickerField;
if (optionsDatasource == null) {
pickerField = componentsFactory.createComponent(PickerField.class);
pickerField.setDatasource(datasource, property);
if (mpp.getMetaProperty().getType() == MetaProperty.Type.ASSOCIATION) {
pickerField.addLookupAction();
if (DynamicAttributesUtils.isDynamicAttribute(mpp.getMetaProperty())) {
DynamicAttributesGuiTools dynamicAttributesGuiTools = AppBeans.get(DynamicAttributesGuiTools.class);
DynamicAttributesMetaProperty dynamicAttributesMetaProperty = (DynamicAttributesMetaProperty) mpp.getMetaProperty();
dynamicAttributesGuiTools.initEntityPickerField(pickerField, dynamicAttributesMetaProperty.getAttribute());
}
boolean actionsByMetaAnnotations = ComponentsHelper.createActionsByMetaAnnotations(pickerField);
if (!actionsByMetaAnnotations) {
pickerField.addClearAction();
}
} else {
pickerField.addOpenAction();
pickerField.addClearAction();
}
} else {
LookupPickerField lookupPickerField = componentsFactory.createComponent(LookupPickerField.class);
lookupPickerField.setDatasource(datasource, property);
lookupPickerField.setOptionsDatasource(optionsDatasource);
pickerField = lookupPickerField;
ComponentsHelper.createActionsByMetaAnnotations(pickerField);
}
if (xmlDescriptor != null) {
String captionProperty = xmlDescriptor.attributeValue("captionProperty");
if (StringUtils.isNotEmpty(captionProperty)) {
pickerField.setCaptionMode(CaptionMode.PROPERTY);
pickerField.setCaptionProperty(captionProperty);
}
}
return pickerField;
} else {
EntityLinkField linkField = componentsFactory.createComponent(EntityLinkField.class);
linkField.setDatasource(datasource, property);
if (xmlDescriptor != null) {
String linkScreen = xmlDescriptor.attributeValue("linkScreen");
if (StringUtils.isNotEmpty(linkScreen)) {
linkField.setScreen(linkScreen);
}
final String invokeMethodName = xmlDescriptor.attributeValue("linkInvoke");
if (StringUtils.isNotEmpty(invokeMethodName)) {
linkField.setCustomClickHandler(new InvokeEntityLinkClickHandler(invokeMethodName));
}
String openTypeAttribute = xmlDescriptor.attributeValue("linkScreenOpenType");
if (StringUtils.isNotEmpty(openTypeAttribute)) {
OpenType openType = OpenType.valueOf(openTypeAttribute);
linkField.setScreenOpenType(openType);
}
}
return linkField;
}
}
protected Component createEnumField(Datasource datasource, String property) {
LookupField lookupField = componentsFactory.createComponent(LookupField.class);
lookupField.setDatasource(datasource, property);
return lookupField;
protected MetaClass resolveMetaClass(Datasource datasource) {
return datasource instanceof RuntimePropsDatasource ?
((RuntimePropsDatasource) datasource).resolveCategorizedEntityClass() : datasource.getMetaClass();
}
@Nullable
protected abstract CollectionDatasource getOptionsDatasource(Datasource datasource, String property);
protected static class InvokeEntityLinkClickHandler implements EntityLinkClickHandler {
protected final String invokeMethodName;
public InvokeEntityLinkClickHandler(String invokeMethodName) {
this.invokeMethodName = invokeMethodName;
}
@Override
public void onClick(EntityLinkField field) {
Window frame = ComponentsHelper.getWindow(field);
if (frame == null) {
throw new IllegalStateException("Please specify Frame for EntityLinkField");
}
Object controller = ComponentsHelper.getFrameController(frame);
Method method;
try {
method = controller.getClass().getMethod(invokeMethodName, EntityLinkField.class);
try {
method.invoke(controller, field);
} catch (Exception e) {
throw new RuntimeException(e);
}
} catch (NoSuchMethodException e) {
try {
method = controller.getClass().getMethod(invokeMethodName);
try {
method.invoke(controller);
} catch (Exception e1) {
throw new RuntimeException(e1);
}
} catch (NoSuchMethodException e1) {
throw new IllegalStateException(String.format("No suitable methods named %s for invoke", invokeMethodName));
}
}
}
}
}

View File

@ -0,0 +1,160 @@
/*
* Copyright (c) 2008-2017 Haulmont.
*
* 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.
*/
package com.haulmont.cuba.gui.components;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.haulmont.cuba.gui.data.Datasource;
import org.dom4j.Element;
import javax.annotation.Nullable;
/**
* A class that stores information that can be used to create
* a component by the {@link com.haulmont.cuba.gui.xml.layout.ComponentsFactory}.
*/
public class ComponentGenerationContext {
protected MetaClass metaClass;
protected String property;
protected Datasource datasource;
protected CollectionDatasource optionsDatasource;
protected Element xmlDescriptor;
protected Class componentClass;
/**
* Creates an instance of MetaContext.
*
* @param metaClass an instance of {@link MetaClass} for which a component should be created
* @param property a property of meta class for which a component should be created
*/
public ComponentGenerationContext(MetaClass metaClass, String property) {
this.metaClass = metaClass;
this.property = property;
}
/**
* @return an instance of {@link MetaClass} for which a component should be created
*/
public MetaClass getMetaClass() {
return metaClass;
}
/**
* Sets an instance of {@link MetaClass} for which a component should be created, using fluent API method.
*
* @param metaClass an instance of {@link MetaClass} for which a component should be created
* @return this object
*/
public ComponentGenerationContext setMetaClass(MetaClass metaClass) {
this.metaClass = metaClass;
return this;
}
/**
* @return a property of meta class for which a component should be created
*/
public String getProperty() {
return property;
}
/**
* Sets a property of meta class for which a component should be created, using fluent API method.
*
* @param property a property of meta class for which a component should be created
* @return this object
*/
public ComponentGenerationContext setProperty(String property) {
this.property = property;
return this;
}
/**
* @return a datasource that can be used to create the component
*/
@Nullable
public Datasource getDatasource() {
return datasource;
}
/**
* Sets a datasource that can be used to create the component, using fluent API method.
*
* @param datasource a datasource that can be used to create the component
* @return this object
*/
public ComponentGenerationContext setDatasource(Datasource datasource) {
this.datasource = datasource;
return this;
}
/**
* @return a datasource that can be used as optional to create the component
*/
@Nullable
public CollectionDatasource getOptionsDatasource() {
return optionsDatasource;
}
/**
* Sets a datasource that can be used as optional to create the component, using fluent API method.
*
* @param optionsDatasource a datasource that can be used as optional to create the component
* @return this object
*/
public ComponentGenerationContext setOptionsDatasource(CollectionDatasource optionsDatasource) {
this.optionsDatasource = optionsDatasource;
return this;
}
/**
* @return an XML descriptor which contains additional information
*/
@Nullable
public Element getXmlDescriptor() {
return xmlDescriptor;
}
/**
* Sets an XML descriptor which contains additional information, using fluent API method.
*
* @param xmlDescriptor an XML descriptor which contains additional information
* @return this object
*/
public ComponentGenerationContext setXmlDescriptor(Element xmlDescriptor) {
this.xmlDescriptor = xmlDescriptor;
return this;
}
/**
* @return a component class for which a component should be created
*/
@Nullable
public Class getComponentClass() {
return componentClass;
}
/**
* Sets a component class for which a component should be created, using fluent API method.
*
* @param componentClass a component class for which a component should be created
* @return this object
*/
public ComponentGenerationContext setComponentClass(Class componentClass) {
this.componentClass = componentClass;
return this;
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2008-2017 Haulmont.
*
* 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.
*/
package com.haulmont.cuba.gui.components;
import javax.annotation.Nullable;
public interface ComponentGenerationStrategy {
/**
* Defines the highest precedence for {@link org.springframework.core.Ordered} component strategies of the platform.
*/
int HIGHEST_PLATFORM_PRECEDENCE = 100;
/**
* Defines the lowest precedence for {@link org.springframework.core.Ordered} component strategies of the platform.
*/
int LOWEST_PLATFORM_PRECEDENCE = 1000;
/**
* Creates a component according to the given {@link ComponentGenerationContext}.
*
* @param context the {@link ComponentGenerationContext} instance
* @return a component according to the given {@link ComponentGenerationContext}
*/
@Nullable
Component createComponent(ComponentGenerationContext context);
}

View File

@ -0,0 +1,121 @@
/*
* Copyright (c) 2008-2017 Haulmont.
*
* 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.
*/
package com.haulmont.cuba.gui.components;
import com.haulmont.bali.util.ParamsMap;
import com.haulmont.chile.core.model.MetaPropertyPath;
import com.haulmont.cuba.core.app.dynamicattributes.DynamicAttributesMetaProperty;
import com.haulmont.cuba.core.app.dynamicattributes.DynamicAttributesUtils;
import com.haulmont.cuba.core.entity.CategoryAttribute;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.Messages;
import com.haulmont.cuba.gui.ComponentsHelper;
import com.haulmont.cuba.gui.WindowManager;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.haulmont.cuba.gui.dynamicattributes.DynamicAttributesGuiTools;
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
import org.springframework.core.Ordered;
import javax.annotation.Nullable;
import javax.inject.Inject;
@org.springframework.stereotype.Component(DataGridEditorComponentGenerationStrategy.NAME)
public class DataGridEditorComponentGenerationStrategy extends AbstractComponentGenerationStrategy implements Ordered {
public static final String NAME = "cuba_DataGridEditorMetaComponentStrategy";
@Inject
public DataGridEditorComponentGenerationStrategy(Messages messages) {
super(messages);
}
@Inject
public void setComponentsFactory(ComponentsFactory componentsFactory) {
this.componentsFactory = componentsFactory;
}
@Nullable
@Override
public Component createComponent(ComponentGenerationContext context) {
if (context.getComponentClass() == null
|| !DataGrid.class.isAssignableFrom(context.getComponentClass())) {
return null;
}
return createComponentInternal(context);
}
@Override
protected Component createStringField(ComponentGenerationContext context, MetaPropertyPath mpp) {
TextField component = componentsFactory.createComponent(TextField.class);
setDatasource(component, context);
return component;
}
@Override
protected Field createEntityField(ComponentGenerationContext context, MetaPropertyPath mpp) {
CollectionDatasource optionsDatasource = null;
if (DynamicAttributesUtils.isDynamicAttribute(mpp.getMetaProperty())) {
DynamicAttributesMetaProperty metaProperty = (DynamicAttributesMetaProperty) mpp.getMetaProperty();
CategoryAttribute attribute = metaProperty.getAttribute();
if (Boolean.TRUE.equals(attribute.getLookup())) {
DynamicAttributesGuiTools dynamicAttributesGuiTools = AppBeans.get(DynamicAttributesGuiTools.class);
optionsDatasource = dynamicAttributesGuiTools.createOptionsDatasourceForLookup(metaProperty.getRange()
.asClass(), attribute.getJoinClause(), attribute.getWhereClause());
}
}
PickerField pickerField;
if (optionsDatasource == null) {
pickerField = componentsFactory.createComponent(PickerField.class);
setDatasource(pickerField, context);
pickerField.addLookupAction();
if (DynamicAttributesUtils.isDynamicAttribute(mpp.getMetaProperty())) {
DynamicAttributesGuiTools dynamicAttributesGuiTools = AppBeans.get(DynamicAttributesGuiTools.class);
DynamicAttributesMetaProperty dynamicAttributesMetaProperty =
(DynamicAttributesMetaProperty) mpp.getMetaProperty();
dynamicAttributesGuiTools.initEntityPickerField(pickerField,
dynamicAttributesMetaProperty.getAttribute());
}
PickerField.LookupAction lookupAction =
(PickerField.LookupAction) pickerField.getActionNN(PickerField.LookupAction.NAME);
// Opening lookup screen in another mode will close editor
lookupAction.setLookupScreenOpenType(WindowManager.OpenType.DIALOG);
// In case of adding special logic for lookup screen opened from DataGrid editor
lookupAction.setLookupScreenParams(ParamsMap.of("dataGridEditor", true));
boolean actionsByMetaAnnotations = ComponentsHelper.createActionsByMetaAnnotations(pickerField);
if (!actionsByMetaAnnotations) {
pickerField.addClearAction();
}
} else {
LookupPickerField lookupPickerField = componentsFactory.createComponent(LookupPickerField.class);
setDatasource(lookupPickerField, context);
lookupPickerField.setOptionsDatasource(optionsDatasource);
pickerField = lookupPickerField;
ComponentsHelper.createActionsByMetaAnnotations(pickerField);
}
return pickerField;
}
@Override
public int getOrder() {
return HIGHEST_PLATFORM_PRECEDENCE + 30;
}
}

View File

@ -30,6 +30,7 @@ public interface DataGridEditorFieldFactory {
* @param datasource editing item datasource
* @param property editing item property
* @return generated component
* @throws IllegalStateException if created component doesn't implement the {@link Field} interface
*/
Field createField(Datasource datasource, String property);
}

View File

@ -16,40 +16,16 @@
package com.haulmont.cuba.gui.components;
import com.haulmont.bali.util.ParamsMap;
import com.haulmont.bali.util.Preconditions;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.chile.core.model.MetaProperty;
import com.haulmont.chile.core.model.MetaPropertyPath;
import com.haulmont.chile.core.model.Range;
import com.haulmont.cuba.core.app.dynamicattributes.DynamicAttributesMetaProperty;
import com.haulmont.cuba.core.app.dynamicattributes.DynamicAttributesUtils;
import com.haulmont.cuba.core.app.dynamicattributes.PropertyType;
import com.haulmont.cuba.core.entity.CategoryAttribute;
import com.haulmont.cuba.core.entity.FileDescriptor;
import com.haulmont.cuba.core.entity.annotation.CurrencyValue;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.Messages;
import com.haulmont.cuba.gui.ComponentsHelper;
import com.haulmont.cuba.gui.WindowManager;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.haulmont.cuba.gui.data.Datasource;
import com.haulmont.cuba.gui.dynamicattributes.DynamicAttributesGuiTools;
import com.haulmont.cuba.gui.data.RuntimePropsDatasource;
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
import javax.annotation.Nullable;
import javax.inject.Inject;
import java.sql.Time;
import java.util.Collection;
import java.util.Date;
import java.util.UUID;
@org.springframework.stereotype.Component(DataGridEditorFieldFactory.NAME)
public class DataGridEditorFieldFactoryImpl implements DataGridEditorFieldFactory {
@Inject
protected Messages messages;
@Inject
protected ComponentsFactory componentsFactory;
@ -59,196 +35,22 @@ public class DataGridEditorFieldFactoryImpl implements DataGridEditorFieldFactor
}
protected Field createFieldComponent(Datasource datasource, String property) {
MetaClass metaClass = datasource.getMetaClass();
MetaPropertyPath mpp = metaClass.getPropertyPath(property);
MetaClass metaClass = resolveMetaClass(datasource);
if (mpp == null && DynamicAttributesUtils.isDynamicAttribute(property)) {
mpp = DynamicAttributesUtils.getMetaPropertyPath(metaClass, property);
ComponentGenerationContext context = new ComponentGenerationContext(metaClass, property)
.setDatasource(datasource)
.setComponentClass(DataGrid.class);
Component component = componentsFactory.createComponent(context);
if (component instanceof Field) {
return (Field) component;
}
if (mpp != null) {
Range mppRange = mpp.getRange();
if (mppRange.isDatatype()) {
Class type = mppRange.asDatatype().getJavaClass();
MetaProperty metaProperty = mpp.getMetaProperty();
if (DynamicAttributesUtils.isDynamicAttribute(metaProperty)) {
CategoryAttribute categoryAttribute = DynamicAttributesUtils.getCategoryAttribute(metaProperty);
if (categoryAttribute != null && categoryAttribute.getDataType() == PropertyType.ENUMERATION) {
return createEnumField(datasource, property);
}
}
if (type.equals(String.class)) {
return createStringField(datasource, property);
} else if (type.equals(UUID.class)) {
return createUuidField(datasource, property);
} else if (type.equals(Boolean.class)) {
return createBooleanField(datasource, property);
} else if (type.equals(java.sql.Date.class) || type.equals(Date.class)) {
return createDateField(datasource, property);
} else if (type.equals(Time.class)) {
return createTimeField(datasource, property);
} else if (Number.class.isAssignableFrom(type)) {
return createNumberField(datasource, property);
}
} else if (mppRange.isClass()) {
MetaProperty metaProperty = mpp.getMetaProperty();
Class<?> javaType = metaProperty.getJavaType();
if (FileDescriptor.class.isAssignableFrom(javaType)) {
return createFileUploadField(datasource, property);
}
if (!Collection.class.isAssignableFrom(javaType)) {
return createEntityField(datasource, property, mpp);
}
} else if (mppRange.isEnum()) {
return createEnumField(datasource, property);
}
}
String exceptionMessage;
if (mpp != null) {
String name = mpp.getRange().isDatatype()
? mpp.getRange().asDatatype().toString()
: mpp.getRange().asClass().getName();
exceptionMessage = String.format("Can't create field \"%s\" with data type: %s", property, name);
} else {
exceptionMessage = String.format("Can't create field \"%s\" with given data type", property);
}
throw new UnsupportedOperationException(exceptionMessage);
throw new IllegalStateException("Editor field must implement com.haulmont.cuba.gui.components.Field");
}
protected Field createFileUploadField(Datasource datasource, String property) {
FileUploadField fileUploadField = (FileUploadField) componentsFactory.createComponent(FileUploadField.NAME);
fileUploadField.setMode(FileUploadField.FileStoragePutMode.IMMEDIATE);
fileUploadField.setUploadButtonCaption(null);
fileUploadField.setUploadButtonDescription(messages.getMainMessage("upload.submit"));
fileUploadField.setUploadButtonIcon("icons/upload.png");
fileUploadField.setClearButtonCaption(null);
fileUploadField.setClearButtonDescription(messages.getMainMessage("upload.clear"));
fileUploadField.setClearButtonIcon("icons/remove.png");
fileUploadField.setShowFileName(true);
fileUploadField.setShowClearButton(true);
fileUploadField.setDatasource(datasource, property);
return fileUploadField;
}
protected Field createUuidField(Datasource datasource, String property) {
MaskedField maskedField = componentsFactory.createComponent(MaskedField.class);
maskedField.setDatasource(datasource, property);
maskedField.setMask("hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh");
maskedField.setSendNullRepresentation(false);
return maskedField;
}
protected Field createNumberField(Datasource datasource, String property) {
Field currencyField = createCurrencyField(datasource, property);
if (currencyField != null) {
return currencyField;
}
TextField numberField = componentsFactory.createComponent(TextField.class);
numberField.setDatasource(datasource, property);
return numberField;
}
@Nullable
protected Field createCurrencyField(Datasource datasource, String property) {
if (DynamicAttributesUtils.isDynamicAttribute(property))
return null;
MetaPropertyPath mpp = datasource.getMetaClass().getPropertyPath(property);
Preconditions.checkNotNullArgument(mpp, "Could not resolve property path '%s' in '%s'",
property, datasource.getMetaClass());
Object currencyAnnotation = mpp.getMetaProperty().getAnnotations().get(CurrencyValue.class.getName());
if (currencyAnnotation == null) {
return null;
}
CurrencyField currencyField = componentsFactory.createComponent(CurrencyField.class);
currencyField.setDatasource(datasource, property);
return currencyField;
}
protected Field createBooleanField(Datasource datasource, String property) {
CheckBox checkBox = componentsFactory.createComponent(CheckBox.class);
checkBox.setDatasource(datasource, property);
return checkBox;
}
protected Field createStringField(Datasource datasource, String property) {
TextField textField = componentsFactory.createComponent(TextField.class);
textField.setDatasource(datasource, property);
return textField;
}
protected Field createDateField(Datasource datasource, String property) {
DateField dateField = componentsFactory.createComponent(DateField.class);
dateField.setDatasource(datasource, property);
return dateField;
}
protected Field createTimeField(Datasource datasource, String property) {
TimeField timeField = componentsFactory.createComponent(TimeField.class);
timeField.setDatasource(datasource, property);
return timeField;
}
protected Field createEntityField(Datasource datasource, String property, MetaPropertyPath mpp) {
CollectionDatasource optionsDatasource = null;
if (DynamicAttributesUtils.isDynamicAttribute(mpp.getMetaProperty())) {
DynamicAttributesMetaProperty metaProperty = (DynamicAttributesMetaProperty) mpp.getMetaProperty();
CategoryAttribute attribute = metaProperty.getAttribute();
if (Boolean.TRUE.equals(attribute.getLookup())) {
DynamicAttributesGuiTools dynamicAttributesGuiTools = AppBeans.get(DynamicAttributesGuiTools.class);
optionsDatasource = dynamicAttributesGuiTools.createOptionsDatasourceForLookup(metaProperty.getRange()
.asClass(), attribute.getJoinClause(), attribute.getWhereClause());
}
}
PickerField pickerField;
if (optionsDatasource == null) {
pickerField = componentsFactory.createComponent(PickerField.class);
pickerField.setDatasource(datasource, property);
pickerField.addLookupAction();
if (DynamicAttributesUtils.isDynamicAttribute(mpp.getMetaProperty())) {
DynamicAttributesGuiTools dynamicAttributesGuiTools = AppBeans.get(DynamicAttributesGuiTools.class);
DynamicAttributesMetaProperty dynamicAttributesMetaProperty = (DynamicAttributesMetaProperty) mpp.getMetaProperty();
dynamicAttributesGuiTools.initEntityPickerField(pickerField, dynamicAttributesMetaProperty.getAttribute());
}
PickerField.LookupAction lookupAction =
(PickerField.LookupAction) pickerField.getActionNN(PickerField.LookupAction.NAME);
// Opening lookup screen in another mode will close editor
lookupAction.setLookupScreenOpenType(WindowManager.OpenType.DIALOG);
// In case of adding special logic for lookup screen opened from DataGrid editor
lookupAction.setLookupScreenParams(ParamsMap.of("dataGridEditor", true));
boolean actionsByMetaAnnotations = ComponentsHelper.createActionsByMetaAnnotations(pickerField);
if (!actionsByMetaAnnotations) {
pickerField.addClearAction();
}
} else {
LookupPickerField lookupPickerField = componentsFactory.createComponent(LookupPickerField.class);
lookupPickerField.setDatasource(datasource, property);
lookupPickerField.setOptionsDatasource(optionsDatasource);
pickerField = lookupPickerField;
ComponentsHelper.createActionsByMetaAnnotations(pickerField);
}
return pickerField;
}
protected Field createEnumField(Datasource datasource, String property) {
LookupField lookupField = componentsFactory.createComponent(LookupField.class);
lookupField.setDatasource(datasource, property);
return lookupField;
protected MetaClass resolveMetaClass(Datasource datasource) {
return datasource instanceof RuntimePropsDatasource ?
((RuntimePropsDatasource) datasource).resolveCategorizedEntityClass() : datasource.getMetaClass();
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2008-2017 Haulmont.
*
* 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.
*/
package com.haulmont.cuba.gui.components;
import com.haulmont.cuba.core.global.Messages;
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
import org.springframework.core.Ordered;
import javax.annotation.Nullable;
import javax.inject.Inject;
@org.springframework.stereotype.Component(DefaultComponentGenerationStrategy.NAME)
public class DefaultComponentGenerationStrategy extends AbstractComponentGenerationStrategy implements Ordered {
public static final String NAME = "cuba_DefaultMetaComponentStrategy";
@Inject
public DefaultComponentGenerationStrategy(Messages messages) {
super(messages);
}
@Inject
public void setComponentsFactory(ComponentsFactory componentsFactory) {
this.componentsFactory = componentsFactory;
}
@Nullable
@Override
public Component createComponent(ComponentGenerationContext context) {
return createComponentInternal(context);
}
@Override
public int getOrder() {
return LOWEST_PLATFORM_PRECEDENCE;
}
}

View File

@ -22,5 +22,13 @@ import org.dom4j.Element;
public interface FieldFactory {
/**
* Creates a component for {@link Table}.
*
* @param datasource a datasource
* @param property a property
* @param xmlDescriptor an xml descriptor
* @return created component
*/
Component createField(Datasource datasource, String property, Element xmlDescriptor);
}

View File

@ -16,41 +16,15 @@
package com.haulmont.cuba.gui.components;
import com.google.common.base.Strings;
import com.haulmont.bali.util.Preconditions;
import com.haulmont.chile.core.datatypes.Datatype;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.chile.core.model.MetaProperty;
import com.haulmont.chile.core.model.MetaPropertyPath;
import com.haulmont.chile.core.model.Range;
import com.haulmont.cuba.core.app.dynamicattributes.DynamicAttributesMetaProperty;
import com.haulmont.cuba.core.app.dynamicattributes.DynamicAttributesUtils;
import com.haulmont.cuba.core.app.dynamicattributes.PropertyType;
import com.haulmont.cuba.core.entity.CategoryAttribute;
import com.haulmont.cuba.core.entity.FileDescriptor;
import com.haulmont.cuba.core.entity.annotation.CurrencyValue;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.Messages;
import com.haulmont.cuba.gui.ComponentsHelper;
import com.haulmont.cuba.gui.WindowManager;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.haulmont.cuba.gui.data.Datasource;
import com.haulmont.cuba.gui.data.RuntimePropsDatasource;
import com.haulmont.cuba.gui.dynamicattributes.DynamicAttributesGuiTools;
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Element;
import javax.annotation.Nullable;
import javax.inject.Inject;
import java.sql.Time;
import java.util.Date;
import java.util.UUID;
@org.springframework.stereotype.Component(FieldGroupFieldFactory.NAME)
public class FieldGroupFieldFactoryImpl implements FieldGroupFieldFactory {
@Inject
protected Messages messages;
@Inject
protected ComponentsFactory componentsFactory;
@ -61,363 +35,19 @@ public class FieldGroupFieldFactoryImpl implements FieldGroupFieldFactory {
}
protected GeneratedField createFieldComponent(FieldGroup.FieldConfig fc) {
Datasource targetDs = fc.getTargetDatasource();
MetaClass metaClass = resolveMetaClass(fc.getTargetDatasource());
MetaClass metaClass = targetDs.getMetaClass();
MetaPropertyPath mpp = resolveMetaPropertyPath(metaClass, fc.getProperty());
ComponentGenerationContext context = new ComponentGenerationContext(metaClass, fc.getProperty())
.setDatasource(fc.getTargetDatasource())
.setOptionsDatasource(fc.getOptionsDatasource())
.setXmlDescriptor(fc.getXmlDescriptor())
.setComponentClass(FieldGroup.class);
if (mpp != null) {
Range mppRange = mpp.getRange();
if (mppRange.isDatatype()) {
Datatype datatype = mppRange.asDatatype();
MetaProperty metaProperty = mpp.getMetaProperty();
if (DynamicAttributesUtils.isDynamicAttribute(metaProperty)) {
CategoryAttribute categoryAttribute = DynamicAttributesUtils.getCategoryAttribute(metaProperty);
if (categoryAttribute != null && categoryAttribute.getDataType() == PropertyType.ENUMERATION) {
return createEnumField(fc);
}
}
if (fc.getXmlDescriptor() != null
&& "true".equalsIgnoreCase(fc.getXmlDescriptor().attributeValue("link"))) {
return createDatatypeLinkField(fc);
} else if (datatype.getJavaClass().equals(String.class)) {
if (fc.getXmlDescriptor() != null
&& fc.getXmlDescriptor().attribute("mask") != null) {
return createMaskedField(fc);
} else {
return createStringField(fc);
}
} else if (datatype.getJavaClass().equals(UUID.class)) {
return createUuidField(fc);
} else if (datatype.getJavaClass().equals(Boolean.class)) {
return createBooleanField(fc);
} else if ((datatype.getJavaClass().equals(java.sql.Date.class)) || (datatype.getJavaClass().equals(Date.class))) {
return createDateField(fc);
} else if (datatype.getJavaClass().equals(Time.class)) {
return createTimeField(fc);
} else if (Number.class.isAssignableFrom(datatype.getJavaClass())) {
if (fc.getXmlDescriptor() != null
&& fc.getXmlDescriptor().attribute("mask") != null) {
GeneratedField generatedField = createMaskedField(fc);
MaskedField maskedField = (MaskedField) generatedField.getComponent();
maskedField.setValueMode(MaskedField.ValueMode.MASKED);
maskedField.setSendNullRepresentation(false);
return new GeneratedField(maskedField);
} else {
GeneratedField currencyField = createCurrencyField(fc);
if (currencyField != null) {
return currencyField;
}
return createNumberField(fc);
}
}
} else if (mppRange.isClass()) {
MetaProperty metaProperty = mpp.getMetaProperty();
Class<?> javaType = metaProperty.getJavaType();
if (FileDescriptor.class.isAssignableFrom(javaType)) {
return createFileUploadField(fc);
}
return createEntityField(fc);
} else if (mppRange.isEnum()) {
return createEnumField(fc);
}
}
String exceptionMessage;
if (mpp != null) {
exceptionMessage = String.format("Can't create field \"%s\" with data type: %s", fc.getProperty(),
mpp.getRange().asDatatype());
} else {
exceptionMessage = String.format("Can't create field \"%s\" with given data type", fc.getProperty());
}
throw new UnsupportedOperationException(exceptionMessage);
return new GeneratedField(componentsFactory.createComponent(context));
}
@Nullable
protected GeneratedField createCurrencyField(FieldGroup.FieldConfig fc) {
String property = fc.getProperty();
if (DynamicAttributesUtils.isDynamicAttribute(property))
return null;
Datasource datasource = fc.getTargetDatasource();
MetaPropertyPath mpp = datasource.getMetaClass().getPropertyPath(property);
Preconditions.checkNotNullArgument(mpp, "Could not resolve property path '%s' in '%s'",
property, datasource.getMetaClass());
Object currencyAnnotation = mpp.getMetaProperty().getAnnotations().get(CurrencyValue.class.getName());
if (currencyAnnotation == null) {
return null;
}
CurrencyField currencyField = componentsFactory.createComponent(CurrencyField.class);
currencyField.setDatasource(datasource, property);
return new GeneratedField(currencyField);
}
protected GeneratedField createUuidField(FieldGroup.FieldConfig fc) {
MaskedField maskedField = componentsFactory.createComponent(MaskedField.class);
maskedField.setDatasource(fc.getTargetDatasource(), fc.getProperty());
maskedField.setMask("hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh");
maskedField.setSendNullRepresentation(false);
return new GeneratedField(maskedField);
}
protected GeneratedField createNumberField(FieldGroup.FieldConfig fc) {
TextField textField = componentsFactory.createComponent(TextField.class);
textField.setDatasource(fc.getTargetDatasource(), fc.getProperty());
return new GeneratedField(textField);
}
protected GeneratedField createBooleanField(FieldGroup.FieldConfig fc) {
CheckBox checkBox = componentsFactory.createComponent(CheckBox.class);
checkBox.setDatasource(fc.getTargetDatasource(), fc.getProperty());
return new GeneratedField(checkBox);
}
protected GeneratedField createMaskedField(FieldGroup.FieldConfig fc) {
MaskedField maskedField = componentsFactory.createComponent(MaskedField.class);
maskedField.setDatasource(fc.getTargetDatasource(), fc.getProperty());
if (fc.getXmlDescriptor() != null) {
maskedField.setMask(fc.getXmlDescriptor().attributeValue("mask"));
String valueModeStr = fc.getXmlDescriptor().attributeValue("valueMode");
if (StringUtils.isNotEmpty(valueModeStr)) {
maskedField.setValueMode(MaskedField.ValueMode.valueOf(valueModeStr.toUpperCase()));
}
}
return new GeneratedField(maskedField);
}
protected GeneratedField createStringField(FieldGroup.FieldConfig fc) {
TextInputField textField = null;
if (fc.getXmlDescriptor() != null) {
final String rows = fc.getXmlDescriptor().attributeValue("rows");
if (!StringUtils.isEmpty(rows)) {
TextArea textArea = componentsFactory.createComponent(TextArea.class);
textArea.setRows(Integer.parseInt(rows));
textField = textArea;
}
}
if (DynamicAttributesUtils.isDynamicAttribute(fc.getProperty())) {
MetaClass metaClass = fc.getTargetDatasource() instanceof RuntimePropsDatasource ?
((RuntimePropsDatasource) fc.getTargetDatasource()).resolveCategorizedEntityClass() : fc.getTargetDatasource().getMetaClass();
MetaPropertyPath mpp = DynamicAttributesUtils.getMetaPropertyPath(metaClass, fc.getProperty());
if (mpp != null) {
CategoryAttribute categoryAttribute = DynamicAttributesUtils.getCategoryAttribute(mpp.getMetaProperty());
if (categoryAttribute != null && categoryAttribute.getDataType() == PropertyType.STRING
&& categoryAttribute.getRowsCount() != null && categoryAttribute.getRowsCount() > 1) {
TextArea textArea = componentsFactory.createComponent(TextArea.class);
textArea.setRows(categoryAttribute.getRowsCount());
textField = textArea;
}
}
}
if (textField == null) {
textField = componentsFactory.createComponent(TextField.class);
}
textField.setDatasource(fc.getTargetDatasource(), fc.getProperty());
String maxLength = fc.getXmlDescriptor() != null ? fc.getXmlDescriptor().attributeValue("maxLength") : null;
if (!Strings.isNullOrEmpty(maxLength)) {
((TextInputField.MaxLengthLimited) textField).setMaxLength(Integer.parseInt(maxLength));
}
return new GeneratedField(textField);
}
protected GeneratedField createEnumField(FieldGroup.FieldConfig fc) {
LookupField lookupField = componentsFactory.createComponent(LookupField.class);
lookupField.setDatasource(fc.getTargetDatasource(), fc.getProperty());
return new GeneratedField(lookupField);
}
protected GeneratedField createDatatypeLinkField(FieldGroup.FieldConfig fc) {
EntityLinkField linkField = componentsFactory.createComponent(EntityLinkField.class);
linkField.setDatasource(fc.getTargetDatasource(), fc.getProperty());
setLinkFieldAttributes(fc, linkField);
return new GeneratedField(linkField);
}
protected GeneratedField createDateField(FieldGroup.FieldConfig fc) {
DateField dateField = componentsFactory.createComponent(DateField.class);
dateField.setDatasource(fc.getTargetDatasource(), fc.getProperty());
Element xmlDescriptor = fc.getXmlDescriptor();
String resolution = xmlDescriptor == null ? null : xmlDescriptor.attributeValue("resolution");
String dateFormat = xmlDescriptor == null ? null : xmlDescriptor.attributeValue("dateFormat");
DateField.Resolution dateResolution = DateField.Resolution.MIN;
if (StringUtils.isNotEmpty(resolution)) {
dateResolution = DateField.Resolution.valueOf(resolution);
dateField.setResolution(dateResolution);
}
if (dateFormat == null) {
if (dateResolution == DateField.Resolution.DAY) {
dateFormat = "msg://dateFormat";
} else if (dateResolution == DateField.Resolution.MIN) {
dateFormat = "msg://dateTimeFormat";
}
}
if (StringUtils.isNotEmpty(dateFormat)) {
if (dateFormat.startsWith("msg://")) {
dateFormat = messages.getMainMessage(dateFormat.substring(6, dateFormat.length()));
}
dateField.setDateFormat(dateFormat);
}
return new GeneratedField(dateField);
}
protected GeneratedField createTimeField(FieldGroup.FieldConfig fc) {
TimeField timeField = componentsFactory.createComponent(TimeField.class);
timeField.setDatasource(fc.getTargetDatasource(), fc.getProperty());
if (fc.getXmlDescriptor() != null) {
String showSeconds = fc.getXmlDescriptor().attributeValue("showSeconds");
if (Boolean.parseBoolean(showSeconds)) {
timeField.setShowSeconds(true);
}
}
return new GeneratedField(timeField);
}
protected GeneratedField createFileUploadField(FieldGroup.FieldConfig fc) {
FileUploadField fileUploadField = componentsFactory.createComponent(FileUploadField.class);
fileUploadField.setMode(FileUploadField.FileStoragePutMode.IMMEDIATE);
fileUploadField.setUploadButtonCaption(null);
fileUploadField.setUploadButtonDescription(messages.getMainMessage("upload.submit"));
fileUploadField.setUploadButtonIcon("icons/upload.png");
fileUploadField.setClearButtonCaption(null);
fileUploadField.setClearButtonIcon("icons/remove.png");
fileUploadField.setClearButtonDescription(messages.getMainMessage("upload.clear"));
fileUploadField.setShowFileName(true);
fileUploadField.setShowClearButton(true);
fileUploadField.setDatasource(fc.getTargetDatasource(), fc.getProperty());
return new GeneratedField(fileUploadField);
}
protected GeneratedField createEntityField(FieldGroup.FieldConfig fc) {
MetaClass metaClass = fc.getTargetDatasource().getMetaClass();
MetaPropertyPath mpp = resolveMetaPropertyPath(metaClass, fc.getProperty());
String linkAttribute = null;
if (fc.getXmlDescriptor() != null) {
linkAttribute = fc.getXmlDescriptor().attributeValue("link");
}
if (!Boolean.parseBoolean(linkAttribute)) {
CollectionDatasource optionsDatasource = fc.getOptionsDatasource();
if (DynamicAttributesUtils.isDynamicAttribute(mpp.getMetaProperty())) {
DynamicAttributesMetaProperty metaProperty = (DynamicAttributesMetaProperty) mpp.getMetaProperty();
CategoryAttribute attribute = metaProperty.getAttribute();
if (Boolean.TRUE.equals(attribute.getLookup())) {
DynamicAttributesGuiTools dynamicAttributesGuiTools = AppBeans.get(DynamicAttributesGuiTools.class);
optionsDatasource = dynamicAttributesGuiTools.createOptionsDatasourceForLookup(metaProperty.getRange().asClass(),
attribute.getJoinClause(), attribute.getWhereClause());
}
}
PickerField pickerField;
if (optionsDatasource == null) {
pickerField = componentsFactory.createComponent(PickerField.class);
pickerField.setDatasource(fc.getTargetDatasource(), fc.getProperty());
if (mpp.getMetaProperty().getType() == MetaProperty.Type.ASSOCIATION) {
pickerField.addLookupAction();
if (DynamicAttributesUtils.isDynamicAttribute(mpp.getMetaProperty())) {
DynamicAttributesGuiTools dynamicAttributesGuiTools = AppBeans.get(DynamicAttributesGuiTools.class);
DynamicAttributesMetaProperty dynamicAttributesMetaProperty = (DynamicAttributesMetaProperty) mpp.getMetaProperty();
dynamicAttributesGuiTools.initEntityPickerField(pickerField, dynamicAttributesMetaProperty.getAttribute());
}
boolean actionsByMetaAnnotations = ComponentsHelper.createActionsByMetaAnnotations(pickerField);
if (!actionsByMetaAnnotations) {
pickerField.addClearAction();
}
} else {
pickerField.addOpenAction();
pickerField.addClearAction();
}
} else {
LookupPickerField lookupPickerField = componentsFactory.createComponent(LookupPickerField.class);
lookupPickerField.setDatasource(fc.getTargetDatasource(), fc.getProperty());
lookupPickerField.setOptionsDatasource(optionsDatasource);
pickerField = lookupPickerField;
ComponentsHelper.createActionsByMetaAnnotations(pickerField);
}
if (fc.getXmlDescriptor() != null) {
String captionProperty = fc.getXmlDescriptor().attributeValue("captionProperty");
if (StringUtils.isNotEmpty(captionProperty)) {
pickerField.setCaptionMode(CaptionMode.PROPERTY);
pickerField.setCaptionProperty(captionProperty);
}
}
return new GeneratedField(pickerField);
} else {
EntityLinkField linkField = componentsFactory.createComponent(EntityLinkField.class);
linkField.setDatasource(fc.getTargetDatasource(), fc.getProperty());
setLinkFieldAttributes(fc, linkField);
return new GeneratedField(linkField);
}
}
protected void setLinkFieldAttributes(FieldGroup.FieldConfig fc, EntityLinkField linkField) {
if (fc.getXmlDescriptor() != null) {
String linkScreen = fc.getXmlDescriptor().attributeValue("linkScreen");
if (StringUtils.isNotEmpty(linkScreen)) {
linkField.setScreen(linkScreen);
}
final String invokeMethodName = fc.getXmlDescriptor().attributeValue("linkInvoke");
if (StringUtils.isNotEmpty(invokeMethodName)) {
linkField.setCustomClickHandler(new AbstractFieldFactory.InvokeEntityLinkClickHandler(invokeMethodName));
}
String openTypeAttribute = fc.getXmlDescriptor().attributeValue("linkScreenOpenType");
if (StringUtils.isNotEmpty(openTypeAttribute)) {
WindowManager.OpenType openType = WindowManager.OpenType.valueOf(openTypeAttribute);
linkField.setScreenOpenType(openType);
}
}
}
protected MetaPropertyPath resolveMetaPropertyPath(MetaClass metaClass, String property) {
MetaPropertyPath mpp = metaClass.getPropertyPath(property);
if (mpp == null && DynamicAttributesUtils.isDynamicAttribute(property)) {
mpp = DynamicAttributesUtils.getMetaPropertyPath(metaClass, property);
}
return mpp;
protected MetaClass resolveMetaClass(Datasource datasource) {
return datasource instanceof RuntimePropsDatasource ?
((RuntimePropsDatasource) datasource).resolveCategorizedEntityClass() : datasource.getMetaClass();
}
}

View File

@ -17,6 +17,8 @@
package com.haulmont.cuba.gui.xml.layout;
import com.haulmont.cuba.gui.components.Component;
import com.haulmont.cuba.gui.components.ComponentGenerationContext;
import com.haulmont.cuba.gui.components.ComponentGenerationStrategy;
import com.haulmont.cuba.gui.components.Timer;
/**
@ -45,6 +47,21 @@ public interface ComponentsFactory {
*/
<T extends Component> T createComponent(Class<T> type);
/**
* Creates a component according to the given {@link ComponentGenerationContext}.
* <p>
* Trying to find {@link ComponentGenerationStrategy} realisations. If at least one strategy exists, then:
* <ol>
* <li>Iterates over strategies according to the {@link org.springframework.core.Ordered} interface.</li>
* <li>The first not {@code null} component will be returned.</li>
* </ol>
*
* @param context the {@link ComponentGenerationContext} instance
* @return a component instance for the current client type (web or desktop)
* @throws IllegalArgumentException if no component can be created for a given context
*/
Component createComponent(ComponentGenerationContext context);
/**
* Create a timer instance.
* @return client-specific implementation of the timer

View File

@ -55,7 +55,6 @@ public class FieldGroupTest extends CubaClientTestCase {
fieldFactory = new TestFieldGroupFieldFactoryImpl() {
{
this.messages = FieldGroupTest.this.messages;
this.componentsFactory = FieldGroupTest.this.componentsFactory;
}
};

View File

@ -24,12 +24,17 @@ import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
import com.haulmont.cuba.web.gui.components.*;
import com.haulmont.cuba.web.gui.components.mainwindow.*;
import javax.inject.Inject;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@org.springframework.stereotype.Component(ComponentsFactory.NAME)
public class WebComponentsFactory implements ComponentsFactory {
@Inject
protected List<ComponentGenerationStrategy> componentGenerationStrategies;
private static Map<String, Class<? extends Component>> classes = new ConcurrentHashMap<>();
private static Map<Class, String> names = new ConcurrentHashMap<>();
@ -176,8 +181,27 @@ public class WebComponentsFactory implements ComponentsFactory {
return type.cast(createComponent(name));
}
@Override
public Component createComponent(ComponentGenerationContext context) {
List<ComponentGenerationStrategy> strategies = getComponentGenerationStrategies();
for (ComponentGenerationStrategy strategy : strategies) {
Component component = strategy.createComponent(context);
if (component != null) {
return component;
}
}
throw new IllegalArgumentException(String.format("Can't create component for the '%s' with " +
"given meta class '%s'", context.getProperty(), context.getMetaClass()));
}
@Override
public Timer createTimer() {
return new WebTimer();
}
protected List<ComponentGenerationStrategy> getComponentGenerationStrategies() {
return componentGenerationStrategies;
}
}

View File

@ -18,6 +18,8 @@ package com.haulmont.cuba.web.test.ui;
import com.google.common.collect.ImmutableMap;
import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.gui.components.ComponentGenerationStrategy;
import com.haulmont.cuba.gui.components.DefaultComponentGenerationStrategy;
import com.haulmont.cuba.gui.components.FieldGroup;
import com.haulmont.cuba.gui.components.FieldGroupTest;
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
@ -28,6 +30,8 @@ import com.vaadin.ui.GridLayout;
import mockit.Mocked;
import mockit.NonStrictExpectations;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
public class WebFieldGroupTest extends FieldGroupTest {
@ -53,7 +57,14 @@ public class WebFieldGroupTest extends FieldGroupTest {
@Override
protected ComponentsFactory createComponentsFactory() {
return new WebComponentsFactory();
return new WebComponentsFactory() {
@Override
public List<ComponentGenerationStrategy> getComponentGenerationStrategies() {
DefaultComponentGenerationStrategy strategy = new DefaultComponentGenerationStrategy(messages);
strategy.setComponentsFactory(this);
return Collections.singletonList(strategy);
}
};
}
@Override