PL-8260 Ability to set up filtering conditions for dynamic attributes values with Entity type

This commit is contained in:
Maxim Gorbunkov 2016-11-25 12:17:16 +04:00
parent 692178f0ac
commit ec55904074
28 changed files with 471 additions and 43 deletions

View File

@ -718,6 +718,8 @@ create table SYS_CATEGORY_ATTR(
WIDTH varchar(20),
ROWS_COUNT integer,
IS_COLLECTION boolean,
JOIN_CLAUSE varchar(4000),
WHERE_CLAUSE varchar(4000),
--
primary key (ID)
)^

View File

@ -761,6 +761,8 @@ create table SYS_CATEGORY_ATTR (
WIDTH varchar(20),
ROWS_COUNT integer,
IS_COLLECTION tinyint,
JOIN_CLAUSE varchar(4000),
WHERE_CLAUSE varchar(4000),
--
primary key nonclustered (ID),
constraint SYS_CATEGORY_ATTR_CATEGORY_ID foreign key (CATEGORY_ID) references SYS_CATEGORY(ID)

View File

@ -766,6 +766,8 @@ create table SYS_CATEGORY_ATTR (
WIDTH varchar(20),
ROWS_COUNT integer,
IS_COLLECTION boolean,
JOIN_CLAUSE varchar(4000),
WHERE_CLAUSE varchar(4000),
--
primary key (ID),
constraint SYS_CATEGORY_ATTR_CATEGORY_ID foreign key (CATEGORY_ID) references SYS_CATEGORY(ID)

View File

@ -81,6 +81,9 @@ create table SYS_CATEGORY_ATTR (
WIDTH varchar2(20),
ROWS_COUNT integer,
IS_COLLECTION char(1),
JOIN_CLAUSE varchar2(4000),
WHERE_CLAUSE varchar2(4000),
primary key(ID)
)^
create index IDX_SYS_CATEGORY_ATTR_CATEGORY on SYS_CATEGORY_ATTR(CATEGORY_ID)^

View File

@ -730,6 +730,8 @@ create table SYS_CATEGORY_ATTR (
WIDTH varchar(20),
ROWS_COUNT integer,
IS_COLLECTION boolean,
JOIN_CLAUSE varchar(4000),
WHERE_CLAUSE varchar(4000),
--
primary key (ID),
constraint SYS_CATEGORY_ATTR_CATEGORY_ID foreign key (CATEGORY_ID) references SYS_CATEGORY(ID)

View File

@ -0,0 +1,2 @@
alter table SYS_CATEGORY_ATTR add JOIN_CLAUSE varchar(4000);
alter table SYS_CATEGORY_ATTR add WHERE_CLAUSE varchar(4000);

View File

@ -0,0 +1,2 @@
alter table SYS_CATEGORY_ATTR add JOIN_CLAUSE varchar(4000);
alter table SYS_CATEGORY_ATTR add WHERE_CLAUSE varchar(4000);

View File

@ -0,0 +1,2 @@
alter table SYS_CATEGORY_ATTR add JOIN_CLAUSE varchar(4000);
alter table SYS_CATEGORY_ATTR add WHERE_CLAUSE varchar(4000);

View File

@ -0,0 +1,2 @@
alter table SYS_CATEGORY_ATTR add JOIN_CLAUSE varchar2(4000)^
alter table SYS_CATEGORY_ATTR add WHERE_CLAUSE varchar2(4000)^

View File

@ -0,0 +1,2 @@
alter table SYS_CATEGORY_ATTR add JOIN_CLAUSE varchar(4000);
alter table SYS_CATEGORY_ATTR add WHERE_CLAUSE varchar(4000);

View File

@ -120,6 +120,26 @@ public class DesktopListEditor extends DesktopAbstractField<JPanel> implements L
delegate.setOptionsList(optionsList);
}
@Override
public String getEntityJoinClause() {
return delegate.getEntityJoinClause();
}
@Override
public void setEntityJoinClause(String entityJoinClause) {
delegate.setEntityJoinClause(entityJoinClause);
}
@Override
public String getEntityWhereClause() {
return delegate.getEntityWhereClause();
}
@Override
public void setEntityWhereClause(String entityWhereClause) {
delegate.setEntityWhereClause(entityWhereClause);
}
@Override
public Datasource getDatasource() {
return null;

View File

@ -109,6 +109,12 @@ public class CategoryAttribute extends StandardEntity {
@Column(name = "IS_COLLECTION")
private Boolean isCollection = false;
@Column(name = "WHERE_CLAUSE")
private String whereClause;
@Column(name = "JOIN_CLAUSE")
private String joinClause;
public void setCategory(Category entityType) {
this.category = entityType;
}
@ -300,6 +306,22 @@ public class CategoryAttribute extends StandardEntity {
this.isCollection = isCollection;
}
public String getWhereClause() {
return whereClause;
}
public void setWhereClause(String whereClause) {
this.whereClause = whereClause;
}
public String getJoinClause() {
return joinClause;
}
public void setJoinClause(String joinClause) {
this.joinClause = joinClause;
}
public Set<String> targetScreensSet() {
if (StringUtils.isNotBlank(targetScreens)) {
return new HashSet<>(Arrays.asList(targetScreens.split(",")));

View File

@ -104,6 +104,8 @@ CategoryAttribute.targetScreens = Target screens
CategoryAttribute.defaultDateIsCurrent = Default date is current
CategoryAttribute.width=Width
CategoryAttribute.rowsCount=Rows count
CategoryAttribute.joinClause=Join clause
CategoryAttribute.whereClause=Where clause
ScheduledTask=Scheduled Task
ScheduledTask.beanName=Bean Name

View File

@ -32,6 +32,8 @@ import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.gui.ScreensHelper;
import com.haulmont.cuba.gui.components.*;
import com.haulmont.cuba.gui.components.actions.RemoveAction;
import com.haulmont.cuba.gui.components.autocomplete.JpqlSuggestionFactory;
import com.haulmont.cuba.gui.components.autocomplete.Suggestion;
import com.haulmont.cuba.gui.config.WindowConfig;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.haulmont.cuba.gui.data.DataSupplier;
@ -53,6 +55,7 @@ import java.util.*;
public class AttributeEditor extends AbstractEditor<CategoryAttribute> {
protected static final Multimap<PropertyType, String> FIELDS_VISIBLE_FOR_DATATYPES = ArrayListMultimap.create();
protected static final Set<String> ALWAYS_VISIBLE_FIELDS = new HashSet<>(Arrays.asList("name", "code", "required", "dataType", "isCollection"));
protected static final String WHERE = " where ";
static {
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.BOOLEAN, "defaultBoolean");
@ -74,6 +77,8 @@ public class AttributeEditor extends AbstractEditor<CategoryAttribute> {
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.ENTITY, "lookup");
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.ENTITY, "defaultEntityId");
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.ENTITY, "width");
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.ENTITY, "joinClause");
FIELDS_VISIBLE_FOR_DATATYPES.put(PropertyType.ENTITY, "whereClause");
}
protected CategoryAttribute attribute;
@ -129,6 +134,8 @@ public class AttributeEditor extends AbstractEditor<CategoryAttribute> {
protected CollectionDatasource<ScreenAndComponent, UUID> screensDs;
private ListEditor enumerationListEditor;
private SourceCodeEditor joinField;
private SourceCodeEditor whereField;
@Override
public void init(Map<String, Object> params) {
@ -257,6 +264,28 @@ public class AttributeEditor extends AbstractEditor<CategoryAttribute> {
return enumerationListEditor;
});
attributeFieldGroup.addCustomField("whereClause", (datasource, propertyId) -> {
whereField = factory.createComponent(SourceCodeEditor.class);
whereField.setDatasource(attributeDs, "whereClause");
whereField.setWidth("100%");
whereField.setHeight(themeConstants.get("cuba.gui.customConditionFrame.whereField.height"));
whereField.setSuggester((source, text, cursorPosition) -> requestHint(whereField, text, cursorPosition));
whereField.setHighlightActiveLine(false);
whereField.setShowGutter(false);
return whereField;
});
attributeFieldGroup.addCustomField("joinClause", (datasource, propertyId) -> {
joinField = factory.createComponent(SourceCodeEditor.class);
joinField.setDatasource(attributeDs, "joinClause");
joinField.setWidth("100%");
joinField.setHeight(themeConstants.get("cuba.gui.customConditionFrame.joinField.height"));
joinField.setSuggester((source, text, cursorPosition) -> requestHint(joinField, text, cursorPosition));
joinField.setHighlightActiveLine(false);
joinField.setShowGutter(false);
return joinField;
});
attributeDs.addItemPropertyChangeListener(e -> {
String property = e.getProperty();
CategoryAttribute attribute = getItem();
@ -269,9 +298,8 @@ public class AttributeEditor extends AbstractEditor<CategoryAttribute> {
if ("name".equalsIgnoreCase(property)) {
fillAttributeCode();
}
if ("screen".equalsIgnoreCase(property)) {
dynamicAttributesGuiTools.initEntityLookupAction(entityLookupAction,
metadata.getClass(attribute.getJavaClassForEntity()), attribute.getScreen());
if ("screen".equalsIgnoreCase(property) || "joinClause".equals(property) || "whereClause".equals(property)) {
dynamicAttributesGuiTools.initEntityPickerField(defaultEntityField, attribute);
}
});
}
@ -298,7 +326,8 @@ public class AttributeEditor extends AbstractEditor<CategoryAttribute> {
defaultEntityField.setMetaClass(metaClass);
fillDefaultEntities(entityClass);
fillSelectEntityScreens(entityClass);
dynamicAttributesGuiTools.initEntityLookupAction(entityLookupAction, metaClass, attribute.getScreen());
dynamicAttributesGuiTools.initEntityPickerField(defaultEntityField, attribute);
} else {
defaultEntityField.setEditable(false);
}
@ -448,4 +477,37 @@ public class AttributeEditor extends AbstractEditor<CategoryAttribute> {
setupVisibility();
}
protected List<Suggestion> requestHint(SourceCodeEditor sender, String text, int senderCursorPosition) {
String joinStr = joinField.getValue();
String whereStr = whereField.getValue();
// CAUTION: the magic entity name! The length is three character to match "{E}" length in query
String entityAlias = "a39";
int queryPosition = -1;
Class javaClassForEntity = attribute.getJavaClassForEntity();
if (javaClassForEntity == null) return new ArrayList<>();
MetaClass metaClass = metadata.getClassNN(javaClassForEntity);
String queryStart = "select " + entityAlias + " from " + metaClass.getName() + " " + entityAlias + " ";
StringBuilder queryBuilder = new StringBuilder(queryStart);
if (joinStr != null && !joinStr.equals("")) {
if (sender == joinField) {
queryPosition = queryBuilder.length() + senderCursorPosition - 1;
}
queryBuilder.append(joinStr);
}
if (whereStr != null && !whereStr.equals("")) {
if (sender == whereField) {
queryPosition = queryBuilder.length() + WHERE.length() + senderCursorPosition - 1;
}
queryBuilder.append(WHERE).append(whereStr);
}
String query = queryBuilder.toString();
query = query.replace("{E}", entityAlias);
return JpqlSuggestionFactory.requestHint(query, queryPosition, sender.getAutoCompleteSupport(), senderCursorPosition);
}
}

View File

@ -45,6 +45,8 @@
<field id="entityClass" custom="true" required="true" width="100%"/>
<field id="screen" custom="true" width="100%"/>
<field id="lookup"/>
<field id="joinClause" width="100%" custom="true"/>
<field id="whereClause" width="100%" rows="3" custom="true"/>
<field id="width" width="100%">
<validator class="com.haulmont.cuba.gui.components.validators.PatternValidator"
pattern="^(\d*(\.\d+)?)(%|px)?$" message="msg://widthValidationMsg"/>

View File

@ -31,12 +31,10 @@ import com.haulmont.cuba.core.entity.CategoryAttribute;
import com.haulmont.cuba.core.entity.FileDescriptor;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.Messages;
import com.haulmont.cuba.core.global.View;
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.DsBuilder;
import com.haulmont.cuba.gui.data.RuntimePropsDatasource;
import com.haulmont.cuba.gui.dynamicattributes.DynamicAttributesGuiTools;
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
@ -334,25 +332,25 @@ public abstract class AbstractFieldFactory implements FieldFactory {
DynamicAttributesMetaProperty metaProperty = (DynamicAttributesMetaProperty) mpp.getMetaProperty();
CategoryAttribute attribute = metaProperty.getAttribute();
if (Boolean.TRUE.equals(attribute.getLookup())) {
optionsDatasource = new DsBuilder(datasource.getDsContext())
.setMetaClass(metaProperty.getRange().asClass())
.setViewName(View.MINIMAL)
.buildCollectionDatasource();
optionsDatasource.refresh();
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.LookupAction lookupAction = pickerField.addLookupAction();
pickerField.setDatasource(datasource, property);
if (DynamicAttributesUtils.isDynamicAttribute(mpp.getMetaProperty())) {
DynamicAttributesGuiTools dynamicAttributesGuiTools = AppBeans.get(DynamicAttributesGuiTools.class);
dynamicAttributesGuiTools.initEntityLookupAction(lookupAction, (DynamicAttributesMetaProperty) mpp.getMetaProperty());
DynamicAttributesMetaProperty dynamicAttributesMetaProperty = (DynamicAttributesMetaProperty) mpp.getMetaProperty();
dynamicAttributesGuiTools.initEntityPickerField(pickerField, dynamicAttributesMetaProperty.getAttribute());
}
pickerField.addClearAction();
} else {
LookupPickerField lookupPickerField = componentsFactory.createComponent(LookupPickerField.class);
lookupPickerField.setDatasource(datasource, property);
lookupPickerField.setOptionsDatasource(optionsDatasource);
pickerField = lookupPickerField;
@ -368,8 +366,6 @@ public abstract class AbstractFieldFactory implements FieldFactory {
}
}
pickerField.setDatasource(datasource, property);
return pickerField;
} else {
EntityLinkField linkField = componentsFactory.createComponent(EntityLinkField.class);

View File

@ -55,6 +55,14 @@ public interface ListEditor extends Field {
*/
void setOptionsList(List<Object> optionsList);
String getEntityJoinClause();
void setEntityJoinClause(String entityJoinClause);
String getEntityWhereClause();
void setEntityWhereClause(String entityWhereClause);
enum ItemType {
STRING,
DATE,

View File

@ -331,9 +331,12 @@ public interface PickerField extends Field, Component.ActionsHolder {
afterLookupCloseHandler.onClose(lookupWindow, actionId);
}
});
afterLookupWindowOpened(lookupWindow);
}
}
protected void afterLookupWindowOpened(Window lookupWindow) {}
/**
* Hook to be implemented in subclasses. Called by the action for new value selected from Lookup window.
* Can be used for reloading of entity with different view or to replace value with another value.

View File

@ -31,14 +31,11 @@ import com.haulmont.cuba.core.entity.CategoryAttribute;
import com.haulmont.cuba.core.global.DevelopmentException;
import com.haulmont.cuba.core.global.View;
import com.haulmont.cuba.gui.AppConfig;
import com.haulmont.cuba.gui.WindowManager;
import com.haulmont.cuba.gui.WindowParam;
import com.haulmont.cuba.gui.commonlookup.CommonLookupController;
import com.haulmont.cuba.gui.components.validators.DateValidator;
import com.haulmont.cuba.gui.components.validators.DoubleValidator;
import com.haulmont.cuba.gui.components.validators.IntegerValidator;
import com.haulmont.cuba.gui.components.validators.LongValidator;
import com.haulmont.cuba.gui.config.WindowConfig;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.haulmont.cuba.gui.data.Datasource;
import com.haulmont.cuba.gui.data.DsBuilder;
@ -48,7 +45,10 @@ import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
import org.apache.commons.lang.StringUtils;
import javax.inject.Inject;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static com.haulmont.cuba.gui.components.PickerField.LookupAction;
@ -252,8 +252,7 @@ public class RuntimePropertiesFrame extends AbstractWindow {
((LookupPickerField) pickerField).setOptionsDatasource(optionsDs);
} else {
pickerField = componentsFactory.createComponent(PickerField.class);
LookupAction lookupAction = pickerField.addLookupAction();
dynamicAttributesGuiTools.initEntityLookupAction(lookupAction, metaProperty);
dynamicAttributesGuiTools.initEntityPickerField(pickerField, metaProperty.getAttribute());
}
pickerField.setMetaClass(ds.getMetaClass());
pickerField.setFrame(RuntimePropertiesFrame.this);

View File

@ -56,4 +56,12 @@ public interface ListEditorDelegate {
void setOptionsList(List<Object> optionsList);
void setDisplayDescription(boolean displayDescription);
String getEntityJoinClause();
void setEntityJoinClause(String entityJoinClause);
String getEntityWhereClause();
void setEntityWhereClause(String entityWhereClause);
}

View File

@ -57,6 +57,8 @@ public class ListEditorDelegateImpl implements ListEditorDelegate{
protected String lookupScreen;
protected boolean useLookupField;
protected List<Object> optionsList;
protected String entityJoinClause;
protected String entityWhereClause;
protected TextField displayValuesField;
protected HBoxLayout layout;
@ -88,6 +90,8 @@ public class ListEditorDelegateImpl implements ListEditorDelegate{
params.put("useLookupField", useLookupField);
params.put("optionsList", optionsList);
params.put("lookupScreen", lookupScreen);
params.put("entityJoinClause", entityJoinClause);
params.put("entityWhereClause", entityWhereClause);
params.put("values", getValue());
ListEditorPopupWindow listEditorPopup = (ListEditorPopupWindow) windowManager
.openWindow(windowConfig.getWindowInfo("list-editor-popup"), WindowManager.OpenType.DIALOG, params);
@ -190,4 +194,24 @@ public class ListEditorDelegateImpl implements ListEditorDelegate{
public void setDisplayDescription(boolean displayDescription) {
this.displayDescription = displayDescription;
}
@Override
public String getEntityJoinClause() {
return entityJoinClause;
}
@Override
public void setEntityJoinClause(String entityJoinClause) {
this.entityJoinClause = entityJoinClause;
}
@Override
public String getEntityWhereClause() {
return entityWhereClause;
}
@Override
public void setEntityWhereClause(String entityWhereClause) {
this.entityWhereClause = entityWhereClause;
}
}

View File

@ -22,12 +22,14 @@ import com.haulmont.chile.core.datatypes.Datatype;
import com.haulmont.chile.core.datatypes.Datatypes;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.Metadata;
import com.haulmont.cuba.core.global.View;
import com.haulmont.cuba.gui.WindowParam;
import com.haulmont.cuba.gui.components.*;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.haulmont.cuba.gui.data.DsBuilder;
import com.haulmont.cuba.gui.dynamicattributes.DynamicAttributesGuiTools;
import com.haulmont.cuba.gui.theme.ThemeConstants;
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
import org.apache.commons.lang.BooleanUtils;
@ -63,6 +65,12 @@ public class ListEditorPopupWindow extends AbstractWindow {
@WindowParam
protected Boolean useLookupField;
@WindowParam
protected String entityJoinClause;
@WindowParam
protected String entityWhereClause;
@WindowParam(required = true)
protected ListEditor.ItemType itemType;
@ -78,6 +86,9 @@ public class ListEditorPopupWindow extends AbstractWindow {
@Inject
protected ThemeConstants theme;
@Inject
protected DynamicAttributesGuiTools dynamicAttributesGuiTools;
protected Map<Object, String> valuesMap;
@Override
@ -179,22 +190,33 @@ public class ListEditorPopupWindow extends AbstractWindow {
Field componentForEntity;
if (BooleanUtils.isNotTrue(useLookupField)) {
PickerField pickerField = componentsFactory.createComponent(PickerField.class);
if (!Strings.isNullOrEmpty(lookupScreen)) {
PickerField.LookupAction lookupAction = (PickerField.LookupAction) pickerField.getAction(PickerField.LookupAction.NAME);
if (lookupAction != null) {
lookupAction.setLookupScreen(lookupScreen);
}
}
pickerField.addLookupAction();
pickerField.setMetaClass(metaClass);
if (!Strings.isNullOrEmpty(entityJoinClause) || !Strings.isNullOrEmpty(entityWhereClause)) {
PickerField.LookupAction lookupAction = dynamicAttributesGuiTools.createLookupAction(pickerField, entityJoinClause, entityWhereClause);
pickerField.addAction(lookupAction);
} else {
if (!Strings.isNullOrEmpty(lookupScreen)) {
PickerField.LookupAction lookupAction = (PickerField.LookupAction) pickerField.getAction(PickerField.LookupAction.NAME);
if (lookupAction != null) {
lookupAction.setLookupScreen(lookupScreen);
}
}
pickerField.addLookupAction();
}
componentForEntity = pickerField;
} else {
LookupField lookupField = componentsFactory.createComponent(LookupField.class);
CollectionDatasource optionsDs = new DsBuilder()
.setMetaClass(metaClass)
.setViewName(View.MINIMAL)
.buildCollectionDatasource();
optionsDs.refresh();
CollectionDatasource optionsDs;
if (!Strings.isNullOrEmpty(entityJoinClause) || !Strings.isNullOrEmpty(entityWhereClause)) {
optionsDs = dynamicAttributesGuiTools.createOptionsDatasourceForLookup(metaClass, entityJoinClause, entityWhereClause);
} else {
optionsDs = new DsBuilder()
.setMetaClass(metaClass)
.setViewName(View.MINIMAL)
.buildCollectionDatasource();
optionsDs.refresh();
}
lookupField.setOptionsDatasource(optionsDs);
componentForEntity = lookupField;
}

View File

@ -24,7 +24,9 @@ import com.haulmont.cuba.core.app.dynamicattributes.PropertyType;
import com.haulmont.cuba.core.entity.CategoryAttribute;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.Metadata;
import com.haulmont.cuba.gui.components.*;
import com.haulmont.cuba.gui.components.Component;
import com.haulmont.cuba.gui.components.FieldGroup;
import com.haulmont.cuba.gui.components.ListEditor;
import com.haulmont.cuba.gui.data.Datasource;
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
import org.apache.commons.lang.BooleanUtils;
@ -57,6 +59,9 @@ public class DynamicAttributeCustomFieldGenerator implements FieldGroup.CustomFi
return null;
}
listEditor.setEntityJoinClause(categoryAttribute.getJoinClause());
listEditor.setEntityWhereClause(categoryAttribute.getWhereClause());
ListEditor.ItemType itemType = listEditorItemTypeFromDynamicAttrType(categoryAttribute.getDataType());
listEditor.setItemType(itemType);
Metadata metadata = AppBeans.get(Metadata.class);

View File

@ -17,23 +17,27 @@
package com.haulmont.cuba.gui.dynamicattributes;
import com.google.common.base.Strings;
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.cuba.core.app.dynamicattributes.DynamicAttributes;
import com.haulmont.cuba.core.app.dynamicattributes.DynamicAttributesMetaProperty;
import com.haulmont.cuba.core.app.dynamicattributes.DynamicAttributesUtils;
import com.haulmont.cuba.core.entity.BaseGenericIdEntity;
import com.haulmont.cuba.core.entity.Categorized;
import com.haulmont.cuba.core.entity.CategoryAttribute;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.MetadataTools;
import com.haulmont.cuba.core.global.TimeSource;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.gui.WindowManager;
import com.haulmont.cuba.gui.WindowParams;
import com.haulmont.cuba.gui.commonlookup.CommonLookupController;
import com.haulmont.cuba.gui.components.PickerField;
import com.haulmont.cuba.gui.config.WindowConfig;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.haulmont.cuba.gui.data.Datasource;
import com.haulmont.cuba.gui.data.DsBuilder;
import com.haulmont.cuba.gui.data.impl.DatasourceImplementation;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
@ -55,6 +59,9 @@ public class DynamicAttributesGuiTools {
@Inject
protected WindowConfig windowConfig;
@Inject
protected Metadata metadata;
/**
* Enforce the datasource to change modified status if dynamic attribute is changed
*/
@ -125,11 +132,31 @@ public class DynamicAttributesGuiTools {
});
}
public void initEntityLookupAction(PickerField.LookupAction lookupAction, DynamicAttributesMetaProperty metaProperty) {
initEntityLookupAction(lookupAction, metaProperty.getRange().asClass(), metaProperty.getAttribute().getScreen());
protected boolean attributeShouldBeShownOnTheScreen(String screen, String component, CategoryAttribute attribute) {
Set<String> targetScreensSet = attribute.targetScreensSet();
return targetScreensSet.contains(screen) || targetScreensSet.contains(screen + "#" + component);
}
public void initEntityLookupAction(PickerField.LookupAction lookupAction, MetaClass metaClass, String screen) {
/**
* Initializes the pickerField for selecting the dynamic attribute value. If the CategoryAttribute has "where" or
* "join" clauses then the data in lookup screens will be filtered with these clauses
*
* @param pickerField PickerField component whose lookup action must be initialized
* @param categoryAttribute CategoryAttribute that is represented by the pickerField
*/
public void initEntityPickerField(PickerField pickerField, CategoryAttribute categoryAttribute) {
Class javaClass = categoryAttribute.getJavaClassForEntity();
if (javaClass == null) {
throw new IllegalArgumentException("Entity type is not specified in category attribute");
}
MetaClass metaClass = metadata.getClassNN(javaClass);
PickerField.LookupAction lookupAction = (PickerField.LookupAction) pickerField.getAction(PickerField.LookupAction.NAME);
if (!Strings.isNullOrEmpty(categoryAttribute.getJoinClause()) || !Strings.isNullOrEmpty(categoryAttribute.getWhereClause())) {
lookupAction = createLookupAction(pickerField, categoryAttribute.getJoinClause(), categoryAttribute.getWhereClause());
pickerField.addAction(lookupAction);
}
String screen = categoryAttribute.getScreen();
if (StringUtils.isBlank(screen)) {
screen = windowConfig.getBrowseScreenId(metaClass);
if (windowConfig.findWindowInfo(screen) != null) {
@ -145,8 +172,43 @@ public class DynamicAttributesGuiTools {
}
}
protected boolean attributeShouldBeShownOnTheScreen(String screen, String component, CategoryAttribute attribute) {
Set<String> targetScreensSet = attribute.targetScreensSet();
return targetScreensSet.contains(screen) || targetScreensSet.contains(screen + "#" + component);
/**
* Creates the collection datasource that is used for selecting the dynamic attribute value. If the
* CategoryAttribute has "where" or "join" clauses then only items that satisfy these clauses will be presented in
* the options datasource
*/
public CollectionDatasource createOptionsDatasourceForLookup(MetaClass metaClass, String joinClause, String whereClause) {
CollectionDatasource optionsDatasource = new DsBuilder()
.setMetaClass(metaClass)
.setViewName(View.MINIMAL)
.buildCollectionDatasource();
String query = "select e from " + metaClass.getName() + " e";
if (!Strings.isNullOrEmpty(joinClause)) {
query += " " + joinClause;
}
if (!Strings.isNullOrEmpty(whereClause)) {
query += " where " + whereClause.replaceAll("\\{E\\}", "e");
}
optionsDatasource.setQuery(query);
optionsDatasource.refresh();
return optionsDatasource;
}
/**
* Creates the lookup action that will open the lookup screen with the dynamic filter applied. This filter contains
* a condition with join and where clauses
*/
public PickerField.LookupAction createLookupAction(PickerField pickerField,
String joinClause,
String whereClause) {
FilteringLookupAction filteringLookupAction = new FilteringLookupAction(pickerField, joinClause, whereClause);
Map<String, Object> params = new HashMap<>();
WindowParams.DISABLE_RESUME_SUSPENDED.set(params, true);
WindowParams.DISABLE_AUTO_REFRESH.set(params, true);
filteringLookupAction.setLookupScreenParams(params);
return filteringLookupAction;
}
}

View File

@ -0,0 +1,145 @@
/*
* Copyright (c) 2008-2016 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.dynamicattributes;
import com.google.common.base.Preconditions;
import com.haulmont.bali.datastruct.Node;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.core.entity.CategoryAttribute;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.ExtendedEntities;
import com.haulmont.cuba.core.global.Messages;
import com.haulmont.cuba.core.global.Metadata;
import com.haulmont.cuba.gui.AppConfig;
import com.haulmont.cuba.gui.ComponentsHelper;
import com.haulmont.cuba.gui.components.*;
import com.haulmont.cuba.gui.components.filter.ConditionParamBuilder;
import com.haulmont.cuba.gui.components.filter.ConditionsTree;
import com.haulmont.cuba.gui.components.filter.FilterParser;
import com.haulmont.cuba.gui.components.filter.Param;
import com.haulmont.cuba.gui.components.filter.condition.CustomCondition;
import com.haulmont.cuba.gui.data.impl.DsContextImplementation;
import com.haulmont.cuba.security.entity.FilterEntity;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import java.util.Arrays;
import java.util.Collections;
/**
* Extended PickerField.LookupAction. This action requires "join" and "where" clauses. When the lookup screen is
* opened these clauses are used for creating dynamic filter condition in the Filter component. So the data in the
* lookup screen is filtered.
*/
public class FilteringLookupAction extends PickerField.LookupAction {
private ExtendedEntities extendedEntities;
private String joinClause;
private String whereClause;
public FilteringLookupAction(PickerField pickerField, String joinClause, String whereClause) {
super(pickerField);
this.joinClause = joinClause;
this.whereClause = whereClause;
Preconditions.checkNotNull(pickerField.getMetaClass(), "MetaClass for PickerField is not set");
extendedEntities = AppBeans.get(ExtendedEntities.class);
}
@Override
protected void afterLookupWindowOpened(Window lookupWindow) {
boolean found = ComponentsHelper.walkComponents(lookupWindow, screenComponent -> {
if (!(screenComponent instanceof Filter)) {
return false;
} else {
MetaClass actualMetaClass = ((Filter) screenComponent).getDatasource().getMetaClass();
MetaClass propertyMetaClass = extendedEntities.getEffectiveMetaClass(pickerField.getMetaClass());
if (ObjectUtils.equals(actualMetaClass, propertyMetaClass)) {
applyFilter(((Filter) screenComponent));
return true;
}
return false;
}
});
if (!found) {
target.getFrame().showNotification(messages.getMainMessage("dynamicAttributes.entity.filter.filterNotFound"), Frame.NotificationType.WARNING);
}
((DsContextImplementation) lookupWindow.getDsContext()).resumeSuspended();
}
protected void applyFilter(Filter filterComponent) {
Metadata metadata = AppBeans.get(Metadata.class);
FilterEntity filterEntity = metadata.create(FilterEntity.class);
filterEntity.setComponentId(ComponentsHelper.getFilterComponentPath(filterComponent));
filterEntity.setName(messages.getMainMessage("dynamicAttributes.entity.filter"));
filterEntity.setXml(createFilterXml(filterComponent));
filterEntity.setUser(userSession.getCurrentOrSubstitutedUser());
filterComponent.setFilterEntity(filterEntity);
filterComponent.apply(true);
}
protected String createFilterXml(Filter filterComponent) {
ConditionsTree tree = new ConditionsTree();
CustomCondition condition = createCustomCondition(filterComponent);
tree.setRootNodes(Collections.singletonList(new Node<>(condition)));
return AppBeans.get(FilterParser.class).getXml(tree, Param.ValueProperty.VALUE);
}
protected CustomCondition createCustomCondition(Filter filterComponent) {
CustomCondition condition = new CustomCondition(createConditionXmlElement(),
AppConfig.getMessagesPack(),
getFilterComponentName(filterComponent),
filterComponent.getDatasource());
condition.setUnary(true);
condition.setHidden(true);
condition.setWhere(whereClause.replaceAll("\\?", ":" + condition.getParamName()));
condition.setJoin(joinClause);
ConditionParamBuilder paramBuilder = AppBeans.get(ConditionParamBuilder.class);
Param param = Param.Builder.getInstance().setName(paramBuilder.createParamName(condition))
.setJavaClass(Boolean.class)
.setEntityWhere("")
.setEntityView("")
.setDataSource(filterComponent.getDatasource())
.setInExpr(true)
.setRequired(true)
.build();
param.setValue(true);
condition.setParam(param);
return condition;
}
protected Element createConditionXmlElement() {
Element conditionElement = DocumentHelper.createDocument().addElement("c");
conditionElement.addAttribute("name", RandomStringUtils.randomAlphabetic(10));
conditionElement.addAttribute("width", "1");
conditionElement.addAttribute("type", "CUSTOM");
conditionElement.addAttribute("locCaption", messages.getMainMessage("dynamicAttributes.filter.conditionName"));
return conditionElement;
}
protected String getFilterComponentName(Filter filterComponent) {
String filterComponentPath = ComponentsHelper.getFilterComponentPath(filterComponent);
String[] strings = ValuePathHelper.parse(filterComponentPath);
return ValuePathHelper.format(Arrays.copyOfRange(strings, 1, strings.length));
}
}

View File

@ -311,6 +311,9 @@ excelExporter.false=No
excelExporter.empty=[Empty]
dynamicAttributes.category=Category
dynamicAttributes.entity.filter=Restricting dynamic filter
dynamicAttributes.filter.conditionName=Dynamic condition
dynamicAttributes.entity.filter.filterNotFound=Filter component not found
searchSelect.notFound=Not found items for filter: %s
searchSelect.minimumLengthOfFilter=Minimum length of search string is %s

View File

@ -311,6 +311,10 @@ actions.export.ALL_ROWS=Все строки
actions.export.SELECTED_ROWS=Выбранные строки
dynamicAttributes.category=Категория
dynamicAttributes.entity.filter=Ограничивающий фильтр
dynamicAttributes.filter.conditionName=Динамическое условие
dynamicAttributes.entity.filter.filterNotFound=Компонент фильтра не найден
mssqlDateOutOfRangeException.message=Допустимы даты в промежутке от 1 января 1753 до 31 декабря 9999
searchSelect.notFound=Не найдено записей для фильтра: %s

View File

@ -93,6 +93,26 @@ public class WebListEditor extends WebAbstractField<WebListEditor.CubaListEditor
delegate.setOptionsList(optionsList);
}
@Override
public String getEntityJoinClause() {
return delegate.getEntityJoinClause();
}
@Override
public void setEntityJoinClause(String entityJoinClause) {
delegate.setEntityJoinClause(entityJoinClause);
}
@Override
public String getEntityWhereClause() {
return delegate.getEntityWhereClause();
}
@Override
public void setEntityWhereClause(String entityWhereClause) {
delegate.setEntityWhereClause(entityWhereClause);
}
@Override
public void setValue(Object newValue) {
if (!(newValue instanceof List)) {