PL-8127 Queries with scalars and aggregates from the client side

This commit is contained in:
Konstantin Krivopustov 2016-11-16 15:20:46 +04:00
parent 03f2185a3b
commit 2851052d6e
36 changed files with 1128 additions and 101 deletions

View File

@ -20,6 +20,7 @@ package com.haulmont.cuba.client.sys;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.core.app.DataService;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.entity.KeyValueEntity;
import com.haulmont.cuba.core.global.*;
import org.springframework.stereotype.Component;
@ -131,6 +132,11 @@ public class DataManagerClientImpl implements DataManager {
commit(context);
}
@Override
public List<KeyValueEntity> loadValues(ValueLoadContext context) {
return dataService.loadValues(context);
}
@Override
public DataManager secure() {
return this;

View File

@ -18,10 +18,7 @@
package com.haulmont.cuba.core.app;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.core.entity.AbstractNotPersistentEntity;
import com.haulmont.cuba.core.entity.BaseEntityInternalAccess;
import com.haulmont.cuba.core.entity.BaseGenericIdEntity;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.entity.*;
import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.core.sys.AppContext;
import org.slf4j.Logger;
@ -207,6 +204,12 @@ public class DataManagerBean implements DataManager {
commit(context);
}
@Override
public List<KeyValueEntity> loadValues(ValueLoadContext context) {
DataStore store = storeFactory.get(context.getStoreName());
return store.loadValues(context);
}
protected boolean entityHasDynamicAttributes(Entity entity) {
return entity instanceof BaseGenericIdEntity
&& ((BaseGenericIdEntity) entity).getDynamicAttributes() != null;

View File

@ -17,9 +17,11 @@
package com.haulmont.cuba.core.app;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.entity.KeyValueEntity;
import com.haulmont.cuba.core.global.CommitContext;
import com.haulmont.cuba.core.global.DataManager;
import com.haulmont.cuba.core.global.LoadContext;
import com.haulmont.cuba.core.global.ValueLoadContext;
import org.springframework.stereotype.Service;
import javax.annotation.Nullable;
@ -53,4 +55,9 @@ public class DataServiceBean implements DataService {
public long getCount(LoadContext<? extends Entity> context) {
return dataManager.secure().getCount(context);
}
@Override
public List<KeyValueEntity> loadValues(ValueLoadContext context) {
return dataManager.secure().loadValues(context);
}
}

View File

@ -140,8 +140,8 @@ public class DataServiceQueryBuilder {
value = list;
}
if (value instanceof LoadContext.Query.TemporalValue) {
LoadContext.Query.TemporalValue temporalValue = (LoadContext.Query.TemporalValue) value;
if (value instanceof TemporalValue) {
TemporalValue temporalValue = (TemporalValue) value;
query.setParameter(name, temporalValue.date, temporalValue.type);
} else {
query.setParameter(name, value);

View File

@ -17,8 +17,10 @@
package com.haulmont.cuba.core.app;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.entity.KeyValueEntity;
import com.haulmont.cuba.core.global.CommitContext;
import com.haulmont.cuba.core.global.LoadContext;
import com.haulmont.cuba.core.global.ValueLoadContext;
import javax.annotation.Nullable;
import java.util.List;
@ -54,4 +56,6 @@ public interface DataStore {
* @return set of committed instances
*/
Set<Entity> commit(CommitContext context);
List<KeyValueEntity> loadValues(ValueLoadContext context);
}

View File

@ -16,6 +16,7 @@
package com.haulmont.cuba.core.app;
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.Session;
@ -390,6 +391,60 @@ public class RdbmsStore implements DataStore {
return res;
}
@Override
public List<KeyValueEntity> loadValues(ValueLoadContext context) {
Preconditions.checkNotNullArgument(context, "context is null");
Preconditions.checkNotNullArgument(context.getQuery(), "query is null");
ValueLoadContext.Query contextQuery = context.getQuery();
if (log.isDebugEnabled())
log.debug("query: " + (DataServiceQueryBuilder.printQuery(contextQuery.getQueryString()))
+ (contextQuery.getFirstResult() == 0 ? "" : ", first=" + contextQuery.getFirstResult())
+ (contextQuery.getMaxResults() == 0 ? "" : ", max=" + contextQuery.getMaxResults()));
List<KeyValueEntity> entities = new ArrayList<>();
try (Transaction tx = persistence.createTransaction(storeName)) {
EntityManager em = persistence.getEntityManager(storeName);
em.setSoftDeletion(context.isSoftDeletion());
List<String> keys = context.getProperties();
DataServiceQueryBuilder queryBuilder = AppBeans.get(DataServiceQueryBuilder.NAME);
queryBuilder.init(contextQuery.getQueryString(), contextQuery.getParameters(), null, metadata.getClassNN(KeyValueEntity.class).getName());
Query query = queryBuilder.getQuery(em);
if (contextQuery.getFirstResult() != 0)
query.setFirstResult(contextQuery.getFirstResult());
if (contextQuery.getMaxResults() != 0)
query.setMaxResults(contextQuery.getMaxResults());
List resultList = query.getResultList();
for (Object item : resultList) {
KeyValueEntity entity = new KeyValueEntity();
entity.setIdName(context.getIdName());
entities.add(entity);
if (item instanceof Object[]) {
Object[] row = (Object[]) item;
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
if (row.length > i) {
entity.setValue(key, row[i]);
}
}
} else if (!keys.isEmpty()) {
entity.setValue(keys.get(0), item);
}
}
tx.commit();
}
return entities;
}
protected View getViewFromContext(CommitContext context, Entity entity) {
View view = context.getViews().get(entity);
if (view == null) {

View File

@ -0,0 +1,80 @@
/*
* 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.core;
import com.haulmont.cuba.core.entity.KeyValueEntity;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.DataManager;
import com.haulmont.cuba.core.global.ValueLoadContext;
import com.haulmont.cuba.testsupport.TestContainer;
import com.haulmont.cuba.testsupport.TestSupport;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class NonEntityQueryTest {
@ClassRule
public static TestContainer cont = TestContainer.Common.INSTANCE;
private DataManager dataManager;
@Before
public void setUp() throws Exception {
dataManager = AppBeans.get(DataManager.class);
}
@Test
public void testScalars() throws Exception {
ValueLoadContext context = ValueLoadContext.create()
.setQuery(ValueLoadContext.createQuery("select u.id, u.login from sec$User u where u.id = :id1 or u.id = :id2 order by u.login")
.setParameter("id1", TestSupport.ADMIN_USER_ID)
.setParameter("id2", TestSupport.ANONYMOUS_USER_ID))
.addProperty("userId").addProperty("login");
List<KeyValueEntity> list = dataManager.loadValues(context);
assertEquals(2, list.size());
KeyValueEntity e = list.get(0);
assertEquals(TestSupport.ADMIN_USER_ID, e.getValue("userId"));
assertEquals("admin", e.getValue("login"));
e = list.get(1);
assertEquals(TestSupport.ANONYMOUS_USER_ID, e.getValue("userId"));
assertEquals("anonymous", e.getValue("login"));
}
@Test
public void testAggregates() throws Exception {
ValueLoadContext context = ValueLoadContext.create();
ValueLoadContext.Query query = context.setQueryString("select count(u) from sec$User u where u.id = :id1 or u.id = :id2");
query.setParameter("id1", TestSupport.ADMIN_USER_ID);
query.setParameter("id2", TestSupport.ANONYMOUS_USER_ID);
context.addProperty("count");
List<KeyValueEntity> list = dataManager.loadValues(context);
assertEquals(1, list.size());
KeyValueEntity e = list.get(0);
assertEquals(Long.valueOf(2), e.getValue("count"));
}
}

View File

@ -49,7 +49,7 @@ public class QueryFilter2Test {
@Test
public void testParse() throws Exception {
Element element = Dom4j.readDocument(xml).getRootElement();
QueryFilter queryFilter = new QueryFilter(element, "sec$User");
QueryFilter queryFilter = new QueryFilter(element);
Condition root = queryFilter.getRoot();
System.out.println(new GroovyGenerator().generateGroovy(root));

View File

@ -55,7 +55,7 @@ public class QueryFilterTest {
private QueryFilter createFilter(String name) {
InputStream stream = QueryFilterTest.class.getResourceAsStream("/com/haulmont/cuba/core/global/filter/" + name);
Document doc = Dom4j.readDocument(stream);
return new QueryFilter(doc.getRootElement(), "saneco$GenDoc");
return new QueryFilter(doc.getRootElement());
}
@Test

View File

@ -29,6 +29,8 @@ public class TestSupport {
public static final UUID ADMIN_USER_ID = UUID.fromString("60885987-1b61-4247-94c7-dff348347f93");
public static final UUID ANONYMOUS_USER_ID = UUID.fromString("a405db59-e674-4f63-8afe-269dda788fe8");
public static final UUID COMPANY_GROUP_ID = UUID.fromString("0fa2b1a5-1d68-4d69-9fbd-dff348347f93");
public static final UUID ADMIN_ROLE_ID = UUID.fromString("0c018061-b26f-4de2-a5be-dff348347f93");

View File

@ -17,8 +17,10 @@
package com.haulmont.cuba.core.app;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.entity.KeyValueEntity;
import com.haulmont.cuba.core.global.CommitContext;
import com.haulmont.cuba.core.global.LoadContext;
import com.haulmont.cuba.core.global.ValueLoadContext;
import javax.annotation.Nullable;
import java.util.List;
@ -65,4 +67,6 @@ public interface DataService {
* @return number of instances in the database
*/
long getCount(LoadContext<? extends Entity> context);
List<KeyValueEntity> loadValues(ValueLoadContext context);
}

View File

@ -42,6 +42,10 @@ public class KeyValueMetaClass extends MetadataObjectImpl implements MetaClass {
properties.remove(propertyName);
}
public KeyValueMetaClass() {
name = "sys$KeyValueEntity";
}
@Nullable
@Override
public MetaClass getAncestor() {

View File

@ -17,6 +17,7 @@
package com.haulmont.cuba.core.app.keyvalue;
import com.haulmont.chile.core.datatypes.Datatype;
import com.haulmont.chile.core.datatypes.Datatypes;
import com.haulmont.chile.core.model.*;
import com.haulmont.chile.core.model.impl.ClassRange;
@ -62,6 +63,16 @@ public class KeyValueMetaProperty extends MetadataObjectImpl implements MetaProp
}
}
public KeyValueMetaProperty(MetaClass metaClass, String name, Datatype datatype) {
this.name = name;
this.javaClass = datatype.getJavaClass();
this.metaClass = metaClass;
this.mandatory = false;
this.range = new DatatypeRange(datatype);
this.type = Type.DATATYPE;
}
@Override
public MetaModel getModel() {
return metaClass.getModel();

View File

@ -18,12 +18,16 @@
package com.haulmont.cuba.core.entity;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.chile.core.model.impl.AbstractInstance;
import com.haulmont.cuba.core.entity.annotation.SystemLevel;
import com.haulmont.cuba.core.global.UuidProvider;
import com.haulmont.cuba.core.sys.CubaEnhancingDisabled;
import org.apache.commons.lang.ObjectUtils;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
/**
* Entity that contains a variable set of attributes. For example:
@ -41,11 +45,21 @@ import java.util.Map;
*/
@com.haulmont.chile.core.annotations.MetaClass(name = "sys$KeyValueEntity")
@SystemLevel
public class KeyValueEntity extends AbstractNotPersistentEntity implements CubaEnhancingDisabled {
public class KeyValueEntity
extends AbstractInstance
implements Entity<Object>, CubaEnhancingDisabled {
private Map<String, Object> properties = new LinkedHashMap<>();
protected UUID uuid;
private MetaClass metaClass;
protected Map<String, Object> properties = new LinkedHashMap<>();
protected String idName;
protected MetaClass metaClass;
public KeyValueEntity() {
uuid = UuidProvider.createUuid();
}
@Override
public MetaClass getMetaClass() {
@ -58,6 +72,14 @@ public class KeyValueEntity extends AbstractNotPersistentEntity implements CubaE
this.metaClass = metaClass;
}
public String getIdName() {
return idName;
}
public void setIdName(String idName) {
this.idName = idName;
}
@Override
public <T> T getValue(String name) {
//noinspection unchecked
@ -72,4 +94,51 @@ public class KeyValueEntity extends AbstractNotPersistentEntity implements CubaE
propertyChanged(name, oldValue, value);
}
}
@Override
public Object getId() {
if (idName == null)
return uuid;
else
return properties.get(idName);
}
public void setId(Object id) {
if (idName == null)
throw new IllegalStateException("Id name is not set");
properties.put(idName, id);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
KeyValueEntity that = (KeyValueEntity) o;
Object id = getId();
Object thatId = that.getId();
if (id != null && thatId != null)
return id.equals(thatId);
return Objects.equals(uuid, that.uuid);
}
@Override
public int hashCode() {
Object id = getId();
if (id != null)
return id.hashCode();
return uuid.hashCode();
}
@Override
public String toString() {
Object id = null;
if (idName != null)
id = properties.get(idName);
if (id == null)
id = "?(" + uuid + ")";
return "sys$KeyValueEntity-" + id;
}
}

View File

@ -0,0 +1,22 @@
/*
* 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.core.global;
public interface DataLoadContext {
DataLoadContextQuery setQueryString(String queryString);
}

View File

@ -0,0 +1,37 @@
/*
* 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.core.global;
import javax.persistence.TemporalType;
import java.util.Date;
import java.util.Map;
public interface DataLoadContextQuery {
DataLoadContextQuery setParameter(String name, Object value);
DataLoadContextQuery setParameter(String name, Date value, TemporalType temporalType);
Map<String, Object> getParameters();
DataLoadContextQuery setParameters(Map<String, Object> parameters);
int getFirstResult();
DataLoadContextQuery setFirstResult(int firstResult);
int getMaxResults();
DataLoadContextQuery setMaxResults(int maxResults);
}

View File

@ -19,6 +19,7 @@ package com.haulmont.cuba.core.global;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.entity.KeyValueEntity;
import javax.annotation.Nullable;
import java.util.List;
@ -142,6 +143,8 @@ public interface DataManager {
*/
void remove(Entity entity);
List<KeyValueEntity> loadValues(ValueLoadContext context);
/**
* Returns the DataManager implementation that is guaranteed to apply security restrictions.
* <p>By default, DataManager does not apply security when used on the middleware. Use this method if you want

View File

@ -37,7 +37,7 @@ import java.util.stream.Collectors;
List&lt;User&gt; users = dataManager.loadList(context);
* </pre>
*/
public class LoadContext<E extends Entity> implements Serializable {
public class LoadContext<E extends Entity> implements DataLoadContext, Serializable {
private static final long serialVersionUID = -8808320502197308698L;
@ -118,6 +118,7 @@ public class LoadContext<E extends Entity> implements Serializable {
* @param queryString JPQL query string. Only named parameters are supported.
* @return query definition object
*/
@Override
public Query setQueryString(String queryString) {
final Query query = new Query(queryString);
setQuery(query);
@ -283,7 +284,7 @@ public class LoadContext<E extends Entity> implements Serializable {
/**
* Class that defines a query to be executed for data loading.
*/
public static class Query implements Serializable {
public static class Query implements DataLoadContextQuery, Serializable {
private static final long serialVersionUID = 3819951144050635838L;
@ -293,18 +294,6 @@ public class LoadContext<E extends Entity> implements Serializable {
private int maxResults;
private boolean cacheable;
public static class TemporalValue implements Serializable {
private static final long serialVersionUID = 4972088045550018312L;
public final Date date;
public final TemporalType type;
public TemporalValue(Date date, TemporalType type) {
this.date = date;
this.type = type;
}
}
/**
* @param queryString JPQL query string. Only named parameters are supported.
*/
@ -359,8 +348,9 @@ public class LoadContext<E extends Entity> implements Serializable {
/**
* @param parameters map of the query parameters
*/
public void setParameters(Map<String, Object> parameters) {
public Query setParameters(Map<String, Object> parameters) {
this.parameters.putAll(parameters);
return this;
}
/**

View File

@ -0,0 +1,34 @@
/*
* 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.core.global;
import javax.persistence.TemporalType;
import java.io.Serializable;
import java.util.Date;
public class TemporalValue implements Serializable {
private static final long serialVersionUID = 4972088045550018312L;
public final Date date;
public final TemporalType type;
public TemporalValue(Date date, TemporalType type) {
this.date = date;
this.type = type;
}
}

View File

@ -0,0 +1,208 @@
/*
* 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.core.global;
import javax.persistence.TemporalType;
import java.io.Serializable;
import java.util.*;
public class ValueLoadContext implements DataLoadContext, Serializable {
protected String storeName = Stores.MAIN;
protected Query query;
protected boolean softDeletion = true;
protected String idName;
protected List<String> properties = new ArrayList<>();
public static ValueLoadContext create() {
return new ValueLoadContext();
}
public static Query createQuery(String queryString) {
return new Query(queryString);
}
@Override
public Query setQueryString(String queryString) {
query = new Query(queryString);
return query;
}
public String getStoreName() {
return storeName;
}
public ValueLoadContext setStoreName(String storeName) {
this.storeName = storeName;
return this;
}
public ValueLoadContext setQuery(Query query) {
this.query = query;
return this;
}
public Query getQuery() {
return query;
}
public ValueLoadContext setSoftDeletion(boolean softDeletion) {
this.softDeletion = softDeletion;
return this;
}
public boolean isSoftDeletion() {
return softDeletion;
}
public String getIdName() {
return idName;
}
public void setIdName(String idName) {
this.idName = idName;
}
public ValueLoadContext addProperty(String name) {
properties.add(name);
return this;
}
public ValueLoadContext setProperties(List<String> properties) {
this.properties.clear();
this.properties.addAll(properties);
return this;
}
public List<String> getProperties() {
return properties;
}
@Override
public String toString() {
return String.format("ValuesContext{query=%s, softDeletion=%s, keys=%s}", query, softDeletion, properties);
}
public static class Query implements DataLoadContextQuery, Serializable {
private String queryString;
private int firstResult;
private int maxResults;
private Map<String, Object> parameters = new HashMap<>();
/**
* @param queryString JPQL query string. Only named parameters are supported.
*/
public Query(String queryString) {
this.queryString = queryString;
}
/**
* @return JPQL query string
*/
public String getQueryString() {
return queryString;
}
/**
* @param queryString JPQL query string. Only named parameters are supported.
*/
public void setQueryString(String queryString) {
this.queryString = queryString;
}
/**
* Set value for a query parameter.
* @param name parameter name
* @param value parameter value
* @return this query instance for chaining
*/
public Query setParameter(String name, Object value) {
parameters.put(name, value);
return this;
}
/**
* Set value for a parameter of java.util.Date type.
* @param name parameter name
* @param value date value
* @param temporalType temporal type
* @return this query instance for chaining
*/
public Query setParameter(String name, Date value, TemporalType temporalType) {
parameters.put(name, new TemporalValue(value, temporalType));
return this;
}
/**
* @return editable map of the query parameters
*/
public Map<String, Object> getParameters() {
return parameters;
}
/**
* @param parameters map of the query parameters
*/
public Query setParameters(Map<String, Object> parameters) {
this.parameters.putAll(parameters);
return this;
}
/**
* @param firstResult results offset
* @return this query instance for chaining
*/
public Query setFirstResult(int firstResult) {
this.firstResult = firstResult;
return this;
}
/**
* @param maxResults results limit
* @return this query instance for chaining
*/
public Query setMaxResults(int maxResults) {
this.maxResults = maxResults;
return this;
}
/**
* @return results offset
*/
public int getFirstResult() {
return firstResult;
}
/**
* @return results limit
*/
public int getMaxResults() {
return maxResults;
}
@Override
public String toString() {
return "Query{" +
"queryString='" + queryString + '\'' +
", firstResult=" + firstResult +
", maxResults=" + maxResults +
'}';
}
}
}

View File

@ -26,31 +26,24 @@ import org.dom4j.Element;
import java.util.*;
public class QueryFilter extends FilterParser {
private final String targetEntity;
public QueryFilter(Condition condition, String targetEntity) {
public QueryFilter(Condition condition) {
super(condition);
this.targetEntity = targetEntity;
}
public QueryFilter(Element element, String targetEntity) {
public QueryFilter(Element element) {
super(element);
this.targetEntity = targetEntity;
}
public static QueryFilter merge(QueryFilter src1, QueryFilter src2) {
if (src1 == null || src2 == null)
throw new IllegalArgumentException("Source query filter is null");
if (!src1.targetEntity.equals(src2.targetEntity))
throw new IllegalArgumentException("Target entities do not match");
Condition root = new LogicalCondition("root", LogicalOp.AND);
root.getConditions().add(src1.getRoot());
root.getConditions().add(src2.getRoot());
QueryFilter queryFilter = new QueryFilter(root, src1.targetEntity);
QueryFilter queryFilter = new QueryFilter(root);
return queryFilter;
}

View File

@ -1246,7 +1246,7 @@ public class FilterDelegateImpl implements FilterDelegate {
if (getResultingManualApplyRequired()) {
// set initial denying condition to get empty datasource before explicit filter applying
QueryFilter queryFilter = new QueryFilter(new DenyingClause(), datasource.getMetaClass().getName());
QueryFilter queryFilter = new QueryFilter(new DenyingClause());
if (dsQueryFilter != null) {
queryFilter = QueryFilter.merge(dsQueryFilter, queryFilter);
}
@ -1471,7 +1471,7 @@ public class FilterDelegateImpl implements FilterDelegate {
if (!Strings.isNullOrEmpty(currentFilterXml)) {
Element element = Dom4j.readDocument(currentFilterXml).getRootElement();
QueryFilter queryFilter = new QueryFilter(element, datasource.getMetaClass().getName());
QueryFilter queryFilter = new QueryFilter(element);
if (dsQueryFilter != null) {
queryFilter = QueryFilter.merge(dsQueryFilter, queryFilter);

View File

@ -433,7 +433,7 @@ public class DsBuilder {
return datasource;
}
public RuntimePropsDatasource buildRuntimePropsDataSource(String mainDsId, @Nullable MetaClass categorizedEntityClass) {
public RuntimePropsDatasource buildRuntimePropsDatasource(String mainDsId, @Nullable MetaClass categorizedEntityClass) {
init();
RuntimePropsDatasourceImpl datasource;
datasource = new RuntimePropsDatasourceImpl(dsContext, dataSupplier, id, mainDsId, categorizedEntityClass);
@ -441,6 +441,36 @@ public class DsBuilder {
return datasource;
}
public ValueCollectionDatasourceImpl buildValuesCollectionDatasource() {
ValueCollectionDatasourceImpl datasource = new ValueCollectionDatasourceImpl();
datasource.setup(dsContext, dataSupplier, id, metaClass, null);
if (maxResults > 0)
datasource.setMaxResults(maxResults);
datasource.setSoftDeletion(softDeletion);
registerDatasource(datasource);
return datasource;
}
public ValueGroupDatasourceImpl buildValuesGroupDatasource() {
ValueGroupDatasourceImpl datasource = new ValueGroupDatasourceImpl();
datasource.setup(dsContext, dataSupplier, id, metaClass, null);
if (maxResults > 0)
datasource.setMaxResults(maxResults);
datasource.setSoftDeletion(softDeletion);
registerDatasource(datasource);
return datasource;
}
public ValueHierarchicalDatasourceImpl buildValuesHierarchicalDatasourceImpl() {
ValueHierarchicalDatasourceImpl datasource = new ValueHierarchicalDatasourceImpl();
datasource.setup(dsContext, dataSupplier, id, metaClass, null);
if (maxResults > 0)
datasource.setMaxResults(maxResults);
datasource.setSoftDeletion(softDeletion);
registerDatasource(datasource);
return datasource;
}
private void registerDatasource(Datasource datasource) {
if (dsContext != null && id != null) {
((DsContextImplementation) dsContext).register(datasource);

View File

@ -494,8 +494,8 @@ public abstract class AbstractCollectionDatasource<T extends Entity<K>, K>
}
}
protected LoadContext.Query createLoadContextQuery(LoadContext context, Map<String, Object> params) {
LoadContext.Query q;
protected DataLoadContextQuery createDataQuery(DataLoadContext context, Map<String, Object> params) {
DataLoadContextQuery q = null;
if (query != null && queryParameters != null) {
Map<String, Object> parameters = getQueryParameters(params);
for (ParameterInfo info : queryParameters) {
@ -532,7 +532,7 @@ public abstract class AbstractCollectionDatasource<T extends Entity<K>, K>
if (paramNames.contains(entry.getKey()))
q.setParameter(entry.getKey(), entry.getValue());
}
} else {
} else if (!(context instanceof ValueLoadContext)) {
Collection<MetaProperty> properties = metadata.getTools().getNamePatternProperties(metaClass);
if (!properties.isEmpty()) {
StringBuilder orderBy = new StringBuilder();
@ -550,8 +550,8 @@ public abstract class AbstractCollectionDatasource<T extends Entity<K>, K>
} else
q = context.setQueryString("select e from " + metaClass.getName() + " e");
}
if (q != null) {
q.setCacheable(isCacheable());
if (q instanceof LoadContext.Query) {
((LoadContext.Query) q).setCacheable(isCacheable());
}
return q;
}
@ -563,7 +563,7 @@ public abstract class AbstractCollectionDatasource<T extends Entity<K>, K>
*/
public int getCount() {
LoadContext<Entity> context = new LoadContext<>(metaClass);
LoadContext.Query q = createLoadContextQuery(context, savedParameters == null ? Collections.<String, Object>emptyMap() : savedParameters);
LoadContext.Query q = (LoadContext.Query) createDataQuery(context, savedParameters == null ? Collections.<String, Object>emptyMap() : savedParameters);
context.setSoftDeletion(isSoftDeletion());
if (q == null)
return 0;

View File

@ -499,7 +499,7 @@ public class CollectionDatasourceImpl<T extends Entity<K>, K>
params = Collections.emptyMap();
} else
params = savedParameters;
LoadContext.Query q = createLoadContextQuery(context, params);
LoadContext.Query q = (LoadContext.Query) createDataQuery(context, params);
if (sortInfos != null && sortOnDb) {
setSortDirection(q);
}
@ -552,7 +552,7 @@ public class CollectionDatasourceImpl<T extends Entity<K>, K>
protected LoadContext beforeLoadData(Map<String, Object> params) {
final LoadContext context = new LoadContext(metaClass);
LoadContext.Query q = createLoadContextQuery(context, params);
LoadContext.Query q = (LoadContext.Query) createDataQuery(context, params);
if (q == null) {
detachListener(data.values());
data.clear();

View File

@ -18,6 +18,7 @@ package com.haulmont.cuba.gui.data.impl;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.entity.KeyValueEntity;
import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.gui.data.DataSupplier;
@ -78,6 +79,11 @@ public class GenericDataSupplier implements DataSupplier {
dataManager.remove(entity);
}
@Override
public List<KeyValueEntity> loadValues(ValueLoadContext context) {
return dataManager.loadValues(context);
}
@Override
public DataManager secure() {
return dataManager;

View File

@ -42,18 +42,18 @@ public class HierarchicalDatasourceImpl<T extends Entity<K>, K>
@Override
public Collection<K> getChildren(K itemId) {
if (hierarchyPropertyName != null) {
final Entity item = getItem(itemId);
if (item == null)
final Entity currentItem = getItem(itemId);
if (currentItem == null)
return Collections.emptyList();
List<K> res = new ArrayList<>();
Collection<K> ids = getItemIds();
for (K id : ids) {
Entity<K> currentItem = getItem(id);
Object parentItem = currentItem.getValue(hierarchyPropertyName);
if (parentItem != null && parentItem.equals(item))
res.add(currentItem.getId());
Entity<K> item = getItemNN(id);
Entity<K> parentItem = item.getValue(hierarchyPropertyName);
if (parentItem != null && parentItem.getId().equals(itemId))
res.add(item.getId());
}
return res;
@ -68,8 +68,8 @@ public class HierarchicalDatasourceImpl<T extends Entity<K>, K>
if (item == null)
return null;
else {
Entity<K> value = item.getValue(hierarchyPropertyName);
return value == null ? null : value.getId();
Entity<K> parentItem = item.getValue(hierarchyPropertyName);
return parentItem == null ? null : parentItem.getId();
}
}
return null;
@ -83,8 +83,8 @@ public class HierarchicalDatasourceImpl<T extends Entity<K>, K>
Set<K> result = new LinkedHashSet<>();
for (K id : ids) {
Entity<K> item = getItemNN(id);
Object value = item.getValue(hierarchyPropertyName);
if (value == null || !containsItem(((T) value).getId()))
Entity<K> parentItem = item.getValue(hierarchyPropertyName);
if (parentItem == null || !containsItem(parentItem.getId()))
result.add(item.getId());
}
return result;
@ -99,8 +99,8 @@ public class HierarchicalDatasourceImpl<T extends Entity<K>, K>
if (item == null) return false;
if (hierarchyPropertyName != null) {
Object value = item.getValue(hierarchyPropertyName);
return (value == null || !containsItem(((T) value).getId()));
Entity<K> parentItem = item.getValue(hierarchyPropertyName);
return (parentItem == null || !containsItem(parentItem.getId()));
} else {
return true;
}
@ -108,15 +108,16 @@ public class HierarchicalDatasourceImpl<T extends Entity<K>, K>
@Override
public boolean hasChildren(K itemId) {
final Entity item = getItem(itemId);
if (item == null) return false;
final Entity currentItem = getItem(itemId);
if (currentItem == null)
return false;
if (hierarchyPropertyName != null) {
Collection<K> ids = getItemIds();
for (K id : ids) {
Entity currentItem = getItem(id);
Object parentItem = currentItem.getValue(hierarchyPropertyName);
if (parentItem != null && parentItem.equals(item))
Entity item = getItemNN(id);
Entity parentItem = item.getValue(hierarchyPropertyName);
if (parentItem != null && parentItem.getId().equals(itemId))
return true;
}
}

View File

@ -17,23 +17,34 @@
package com.haulmont.cuba.gui.data.impl;
import com.haulmont.chile.core.datatypes.Datatype;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.core.app.keyvalue.KeyValueMetaClass;
import com.haulmont.cuba.core.app.keyvalue.KeyValueMetaProperty;
import com.haulmont.cuba.core.entity.KeyValueEntity;
import com.haulmont.cuba.core.global.View;
import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.haulmont.cuba.gui.data.DataSupplier;
import com.haulmont.cuba.gui.data.DsContext;
import com.haulmont.cuba.gui.logging.UIPerformanceLogger;
import org.apache.log4j.Logger;
import org.perf4j.StopWatch;
import org.perf4j.log4j.Log4JStopWatch;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.UUID;
/**
* {@link CollectionDatasource} that supports {@link KeyValueEntity}.
*/
public class KeyValueCollectionDatasourceImpl extends CollectionDatasourceImpl<KeyValueEntity, UUID>{
public class ValueCollectionDatasourceImpl
extends CollectionDatasourceImpl<KeyValueEntity, Object>
implements ValueDatasource {
protected final ValueDatasourceDelegate delegate;
public ValueCollectionDatasourceImpl() {
delegate = new ValueDatasourceDelegate(this);
}
@Override
public void setup(DsContext dsContext, DataSupplier dataSupplier, String id, MetaClass metaClass, @Nullable View view) {
@ -43,13 +54,35 @@ public class KeyValueCollectionDatasourceImpl extends CollectionDatasourceImpl<K
this.metaClass = new KeyValueMetaClass();
}
public KeyValueCollectionDatasourceImpl addProperty(String name) {
((KeyValueMetaClass) metaClass).addProperty(new KeyValueMetaProperty(metaClass, name, String.class));
@Override
public ValueCollectionDatasourceImpl setIdName(String name) {
delegate.setIdName(name);
return this;
}
public ValueCollectionDatasourceImpl addProperty(String name) {
delegate.addProperty(name);
return this;
}
public ValueCollectionDatasourceImpl addProperty(String name, Class aClass) {
delegate.addProperty(name, aClass);
return this;
}
public ValueCollectionDatasourceImpl addProperty(String name, Datatype type) {
delegate.addProperty(name, type);
return this;
}
@Override
protected void loadData(Map<String, Object> params) {
String tag = getLoggingTag("VDS");
StopWatch sw = new Log4JStopWatch(tag, Logger.getLogger(UIPerformanceLogger.class));
delegate.loadData(params);
sw.stop();
}
@Override
@ -63,4 +96,8 @@ public class KeyValueCollectionDatasourceImpl extends CollectionDatasourceImpl<K
super.addItem(item);
item.setMetaClass(metaClass);
}
public void setStoreName(String storeName) {
this.delegate.setStoreName(storeName);
}
}

View File

@ -0,0 +1,33 @@
/*
* 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.data.impl;
import com.haulmont.chile.core.datatypes.Datatype;
import com.haulmont.chile.core.model.MetaClass;
public interface ValueDatasource {
MetaClass getMetaClass();
ValueDatasource setIdName(String name);
ValueDatasource addProperty(String name);
ValueDatasource addProperty(String name, Class aClass);
ValueDatasource addProperty(String name, Datatype type);
}

View File

@ -0,0 +1,128 @@
/*
* 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.data.impl;
import com.haulmont.bali.util.Preconditions;
import com.haulmont.chile.core.datatypes.Datatype;
import com.haulmont.chile.core.model.MetaProperty;
import com.haulmont.cuba.core.app.keyvalue.KeyValueMetaClass;
import com.haulmont.cuba.core.app.keyvalue.KeyValueMetaProperty;
import com.haulmont.cuba.core.entity.KeyValueEntity;
import com.haulmont.cuba.core.global.ValueLoadContext;
import java.util.List;
import java.util.Map;
public class ValueDatasourceDelegate {
private String storeName;
private String idName;
protected CollectionDatasourceImpl ds;
public ValueDatasourceDelegate(CollectionDatasourceImpl datasource) {
this.ds = datasource;
}
public String getStoreName() {
return storeName;
}
public void setStoreName(String storeName) {
this.storeName = storeName;
}
public void setIdName(String name) {
this.idName = name;
}
public void addProperty(String name) {
Preconditions.checkNotNullArgument(name, "name is null");
((KeyValueMetaClass) ds.metaClass).addProperty(new KeyValueMetaProperty(ds.metaClass, name, String.class));
}
public void addProperty(String name, Class type) {
Preconditions.checkNotNullArgument(name, "name is null");
Preconditions.checkNotNullArgument(name, "type is null");
((KeyValueMetaClass) ds.metaClass).addProperty(new KeyValueMetaProperty(ds.metaClass, name, type));
}
public void addProperty(String name, Datatype type) {
Preconditions.checkNotNullArgument(name, "name is null");
Preconditions.checkNotNullArgument(name, "type is null");
((KeyValueMetaClass) ds.metaClass).addProperty(new KeyValueMetaProperty(ds.metaClass, name, type));
}
protected void loadData(Map<String, Object> params) {
if (ds.needLoading()) {
ValueLoadContext context = beforeLoadValues(params);
if (context == null) {
return;
}
try {
List<KeyValueEntity> entities = ds.dataSupplier.loadValues(context);
afterLoadValues(params, context, entities);
} catch (Throwable e) {
ds.dataLoadError = e;
}
}
}
protected ValueLoadContext beforeLoadValues(Map<String, Object> params) {
ValueLoadContext context = new ValueLoadContext();
ValueLoadContext.Query q = (ValueLoadContext.Query) ds.createDataQuery(context, params);
if (q == null) {
ds.detachListener(ds.data.values());
ds.data.clear();
return null;
}
if (ds.firstResult > 0)
q.setFirstResult(ds.firstResult);
if (ds.maxResults > 0) {
q.setMaxResults(ds.maxResults);
}
if (storeName != null)
context.setStoreName(storeName);
context.setSoftDeletion(ds.isSoftDeletion());
context.setIdName(idName);
for (MetaProperty property : ds.metaClass.getProperties()) {
context.addProperty(property.getName());
}
ds.dataLoadError = null;
return context;
}
protected void afterLoadValues(Map<String, Object> params, ValueLoadContext context, List<KeyValueEntity> entities) {
ds.detachListener(ds.data.values());
ds.data.clear();
for (KeyValueEntity entity : entities) {
ds.data.put(entity.getId(), entity);
ds.attachListener(entity);
entity.setMetaClass(ds.metaClass);
}
}
}

View File

@ -17,23 +17,34 @@
package com.haulmont.cuba.gui.data.impl;
import com.haulmont.chile.core.datatypes.Datatype;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.core.app.keyvalue.KeyValueMetaClass;
import com.haulmont.cuba.core.app.keyvalue.KeyValueMetaProperty;
import com.haulmont.cuba.core.entity.KeyValueEntity;
import com.haulmont.cuba.core.global.View;
import com.haulmont.cuba.gui.data.DataSupplier;
import com.haulmont.cuba.gui.data.DsContext;
import com.haulmont.cuba.gui.data.GroupDatasource;
import com.haulmont.cuba.gui.logging.UIPerformanceLogger;
import org.apache.log4j.Logger;
import org.perf4j.StopWatch;
import org.perf4j.log4j.Log4JStopWatch;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.UUID;
/**
* {@link GroupDatasource} that supports {@link KeyValueEntity}.
*/
public class KeyValueGroupDatasourceImpl extends GroupDatasourceImpl<KeyValueEntity, UUID> {
public class ValueGroupDatasourceImpl
extends GroupDatasourceImpl<KeyValueEntity, Object>
implements ValueDatasource {
protected final ValueDatasourceDelegate delegate;
public ValueGroupDatasourceImpl() {
delegate = new ValueDatasourceDelegate(this);
}
@Override
public void setup(DsContext dsContext, DataSupplier dataSupplier, String id, MetaClass metaClass, @Nullable View view) {
@ -43,13 +54,35 @@ public class KeyValueGroupDatasourceImpl extends GroupDatasourceImpl<KeyValueEnt
this.metaClass = new KeyValueMetaClass();
}
public KeyValueGroupDatasourceImpl addProperty(String name) {
((KeyValueMetaClass) metaClass).addProperty(new KeyValueMetaProperty(metaClass, name, String.class));
@Override
public ValueGroupDatasourceImpl setIdName(String name) {
delegate.setIdName(name);
return this;
}
public ValueGroupDatasourceImpl addProperty(String name) {
delegate.addProperty(name);
return this;
}
public ValueGroupDatasourceImpl addProperty(String name, Class aClass) {
delegate.addProperty(name, aClass);
return this;
}
public ValueGroupDatasourceImpl addProperty(String name, Datatype type) {
delegate.addProperty(name, type);
return this;
}
@Override
protected void loadData(Map<String, Object> params) {
String tag = getLoggingTag("VGDS");
StopWatch sw = new Log4JStopWatch(tag, Logger.getLogger(UIPerformanceLogger.class));
delegate.loadData(params);
sw.stop();
}
@Override
@ -63,4 +96,8 @@ public class KeyValueGroupDatasourceImpl extends GroupDatasourceImpl<KeyValueEnt
super.addItem(item);
item.setMetaClass(metaClass);
}
public void setStoreName(String storeName) {
this.delegate.setStoreName(storeName);
}
}

View File

@ -17,23 +17,34 @@
package com.haulmont.cuba.gui.data.impl;
import com.haulmont.chile.core.datatypes.Datatype;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.core.app.keyvalue.KeyValueMetaClass;
import com.haulmont.cuba.core.app.keyvalue.KeyValueMetaProperty;
import com.haulmont.cuba.core.entity.KeyValueEntity;
import com.haulmont.cuba.core.global.View;
import com.haulmont.cuba.gui.data.DataSupplier;
import com.haulmont.cuba.gui.data.DsContext;
import com.haulmont.cuba.gui.data.HierarchicalDatasource;
import com.haulmont.cuba.gui.logging.UIPerformanceLogger;
import org.apache.log4j.Logger;
import org.perf4j.StopWatch;
import org.perf4j.log4j.Log4JStopWatch;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.UUID;
/**
* {@link HierarchicalDatasource} that supports {@link KeyValueEntity}.
*/
public class KeyValueHierarchicalDatasourceImpl extends HierarchicalDatasourceImpl<KeyValueEntity, UUID>{
public class ValueHierarchicalDatasourceImpl
extends HierarchicalDatasourceImpl<KeyValueEntity, Object>
implements ValueDatasource {
protected final ValueDatasourceDelegate delegate;
public ValueHierarchicalDatasourceImpl() {
delegate = new ValueDatasourceDelegate(this);
}
@Override
public void setup(DsContext dsContext, DataSupplier dataSupplier, String id, MetaClass metaClass, @Nullable View view) {
@ -43,8 +54,24 @@ public class KeyValueHierarchicalDatasourceImpl extends HierarchicalDatasourceIm
this.metaClass = new KeyValueMetaClass();
}
public KeyValueHierarchicalDatasourceImpl addProperty(String name) {
((KeyValueMetaClass) metaClass).addProperty(new KeyValueMetaProperty(metaClass, name, String.class));
@Override
public ValueHierarchicalDatasourceImpl setIdName(String name) {
delegate.setIdName(name);
return this;
}
public ValueHierarchicalDatasourceImpl addProperty(String name) {
delegate.addProperty(name);
return this;
}
public ValueHierarchicalDatasourceImpl addProperty(String name, Class aClass) {
delegate.addProperty(name, aClass);
return this;
}
public ValueHierarchicalDatasourceImpl addProperty(String name, Datatype type) {
delegate.addProperty(name, type);
return this;
}
@ -52,14 +79,19 @@ public class KeyValueHierarchicalDatasourceImpl extends HierarchicalDatasourceIm
public void setHierarchyPropertyName(String hierarchyPropertyName) {
super.setHierarchyPropertyName(hierarchyPropertyName);
KeyValueMetaClass metaClass = (KeyValueMetaClass) this.metaClass;
if (metaClass.getProperty(hierarchyPropertyName) != null) {
metaClass.removeProperty(hierarchyPropertyName);
if (metaClass.getProperty(hierarchyPropertyName) == null) {
throw new IllegalStateException("Hierarchy property must be added to the datasource as property first");
}
metaClass.addProperty(new KeyValueMetaProperty(metaClass, hierarchyPropertyName, KeyValueEntity.class));
}
@Override
protected void loadData(Map<String, Object> params) {
String tag = getLoggingTag("VHDS");
StopWatch sw = new Log4JStopWatch(tag, Logger.getLogger(UIPerformanceLogger.class));
delegate.loadData(params);
sw.stop();
}
@Override
@ -73,4 +105,8 @@ public class KeyValueHierarchicalDatasourceImpl extends HierarchicalDatasourceIm
super.addItem(item);
item.setMetaClass(metaClass);
}
public void setStoreName(String storeName) {
this.delegate.setStoreName(storeName);
}
}

View File

@ -1926,6 +1926,76 @@
<xs:attribute name="categorizedEntityClass" type="xs:string" use="optional"/>
</xs:complexType>
<!-- ValueCollectionDatasource -->
<xs:complexType name="valueCollectionDatasourceType">
<xs:sequence>
<xs:element name="query" minOccurs="0" maxOccurs="1">
<xs:complexType mixed="true">
<xs:sequence>
<xs:element name="filter" type="filterType" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="properties" type="valueDatasourcePropertiesType"/>
</xs:sequence>
<xs:attribute name="id" type="xs:string" use="required"/>
<xs:attribute name="maxResults" type="xs:int"/>
<xs:attribute name="store" type="xs:string"/>
</xs:complexType>
<!-- ValueGroupDatasource -->
<xs:complexType name="valueGroupDatasourceType">
<xs:sequence>
<xs:element name="query" minOccurs="0" maxOccurs="1">
<xs:complexType mixed="true">
<xs:sequence>
<xs:element name="filter" type="filterType" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="properties" type="valueDatasourcePropertiesType"/>
</xs:sequence>
<xs:attribute name="id" type="xs:string" use="required"/>
<xs:attribute name="maxResults" type="xs:int"/>
<xs:attribute name="store" type="xs:string"/>
</xs:complexType>
<!-- ValueHierarchicalDatasource -->
<xs:complexType name="valueHierarchicalDatasourceType">
<xs:sequence>
<xs:element name="query" minOccurs="0" maxOccurs="1">
<xs:complexType mixed="true">
<xs:sequence>
<xs:element name="filter" type="filterType" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="properties" type="valueDatasourcePropertiesType"/>
</xs:sequence>
<xs:attribute name="id" type="xs:string" use="required"/>
<xs:attribute name="maxResults" type="xs:int"/>
<xs:attribute name="store" type="xs:string"/>
<xs:attribute name="hierarchyProperty" type="xs:string"/>
</xs:complexType>
<xs:complexType name="valueDatasourcePropertiesType">
<xs:sequence>
<xs:element name="property" minOccurs="1" maxOccurs="unbounded">
<xs:complexType mixed="true">
<xs:attribute name="name" type="xs:string" use="required"/>
<xs:attribute name="type" type="datatypeEnum" />
<xs:attribute name="class" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element name="properties" type="valueDatasourcePropertiesType"/>
</xs:sequence>
<xs:attribute name="id" type="xs:string"/>
</xs:complexType>
<!-- PropertyDatasource -->
<xs:group name="nestedDatasources">
<xs:sequence>
@ -2104,6 +2174,9 @@
<xs:element name="collectionDatasource" type="collectionDatasourceType"/>
<xs:element name="hierarchicalDatasource" type="hierarchicalDatasourceType"/>
<xs:element name="runtimePropsDatasource" type="runtimePropsDatasourceType"/>
<xs:element name="valueCollectionDatasource" type="valueCollectionDatasourceType"/>
<xs:element name="valueGroupDatasource" type="valueGroupDatasourceType"/>
<xs:element name="valueHierarchicalDatasource" type="valueHierarchicalDatasourceType"/>
</xs:choice>
</xs:sequence>
<xs:attribute name="class" type="xs:string"/>

View File

@ -17,15 +17,19 @@
package com.haulmont.cuba.gui.xml.data;
import com.haulmont.bali.util.Dom4j;
import com.haulmont.bali.util.ReflectionHelper;
import com.haulmont.chile.core.datatypes.Datatype;
import com.haulmont.chile.core.datatypes.Datatypes;
import com.haulmont.chile.core.datatypes.impl.StringDatatype;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.core.entity.KeyValueEntity;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.DevelopmentException;
import com.haulmont.cuba.core.global.Metadata;
import com.haulmont.cuba.core.global.Scripting;
import com.haulmont.cuba.gui.data.*;
import com.haulmont.cuba.gui.data.impl.DsContextImpl;
import com.haulmont.cuba.gui.data.impl.DsContextImplementation;
import com.haulmont.cuba.gui.data.impl.*;
import com.haulmont.cuba.core.global.filter.QueryFilter;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Element;
@ -110,7 +114,25 @@ public class DsContextLoader {
//noinspection unchecked
elements = element.elements("runtimePropsDatasource");
for (Element ds : elements) {
loadRuntimePropsDataSource(ds);
loadRuntimePropsDatasource(ds);
}
//noinspection unchecked
elements = element.elements("valueCollectionDatasource");
for (Element ds : elements) {
loadValueCollectionDatasource(ds);
}
//noinspection unchecked
elements = element.elements("valueGroupDatasource");
for (Element ds : elements) {
loadValueGroupDatasource(ds);
}
//noinspection unchecked
elements = element.elements("valueHierarchicalDatasource");
for (Element ds : elements) {
loadValueHierarchicalDatasource(ds);
}
context.executeLazyTasks();
@ -154,7 +176,7 @@ public class DsContextLoader {
if (datasource instanceof CollectionDatasource.Suspendable)
((CollectionDatasource.Suspendable) datasource).setSuspended(true);
loadQuery(element, metaClass, datasource);
loadQuery(element, datasource);
loadDatasources(element, datasource);
@ -190,7 +212,7 @@ public class DsContextLoader {
if (datasource instanceof CollectionDatasource.Suspendable)
((CollectionDatasource.Suspendable) datasource).setSuspended(true);
loadQuery(element, metaClass, datasource);
loadQuery(element, datasource);
loadDatasources(element, datasource);
@ -260,7 +282,7 @@ public class DsContextLoader {
//noinspection unchecked
elements = element.elements("runtimePropsDatasource");
for (Element ds : elements) {
loadRuntimePropsDataSource(ds);
loadRuntimePropsDatasource(ds);
}
}
@ -357,14 +379,14 @@ public class DsContextLoader {
if (datasource instanceof CollectionDatasource.Suspendable)
((CollectionDatasource.Suspendable) datasource).setSuspended(true);
loadQuery(element, metaClass, datasource);
loadQuery(element, datasource);
loadDatasources(element, datasource);
return datasource;
}
private void loadQuery(Element element, MetaClass metaClass, CollectionDatasource datasource) {
private void loadQuery(Element element, CollectionDatasource datasource) {
Element queryElem = element.element("query");
if (queryElem != null) {
Element filterElem = queryElem.element("filter");
@ -372,7 +394,7 @@ public class DsContextLoader {
String query = queryElem.getText();
if (!StringUtils.isBlank(query)) {
if (filterElem != null)
datasource.setQuery(query, new QueryFilter(filterElem, metaClass.getName()));
datasource.setQuery(query, new QueryFilter(filterElem));
else
datasource.setQuery(query);
}
@ -414,7 +436,7 @@ public class DsContextLoader {
return StringUtils.isEmpty(allowCommitStr) || Boolean.parseBoolean(allowCommitStr);
}
protected RuntimePropsDatasource loadRuntimePropsDataSource(Element element){
protected RuntimePropsDatasource loadRuntimePropsDatasource(Element element){
String id = getDatasourceId(element);
MetaClass metaClass = loadMetaClass(element);
@ -432,12 +454,103 @@ public class DsContextLoader {
builder.reset().setMetaClass(metaClass).setId(id);
RuntimePropsDatasource datasource = builder.buildRuntimePropsDataSource(mainDsId, categorizedEntityMetaClass);
RuntimePropsDatasource datasource = builder.buildRuntimePropsDatasource(mainDsId, categorizedEntityMetaClass);
loadDatasources(element, datasource);
return datasource;
}
private ValueCollectionDatasourceImpl loadValueCollectionDatasource(Element element) {
String id = getDatasourceId(element);
builder.reset().setMetaClass(metadata.getClassNN(KeyValueEntity.class)).setId(id);
ValueCollectionDatasourceImpl datasource = builder.buildValuesCollectionDatasource();
String maxResults = element.attributeValue("maxResults");
if (!StringUtils.isEmpty(maxResults))
datasource.setMaxResults(Integer.parseInt(maxResults));
datasource.setSuspended(true);
loadQuery(element, datasource);
loadProperties(element, datasource);
datasource.setStoreName(element.attributeValue("store"));
return datasource;
}
private ValueGroupDatasourceImpl loadValueGroupDatasource(Element element) {
String id = getDatasourceId(element);
builder.reset().setMetaClass(metadata.getClassNN(KeyValueEntity.class)).setId(id);
ValueGroupDatasourceImpl datasource = builder.buildValuesGroupDatasource();
String maxResults = element.attributeValue("maxResults");
if (!StringUtils.isEmpty(maxResults))
datasource.setMaxResults(Integer.parseInt(maxResults));
datasource.setSuspended(true);
loadQuery(element, datasource);
loadProperties(element, datasource);
datasource.setStoreName(element.attributeValue("store"));
return datasource;
}
private ValueHierarchicalDatasourceImpl loadValueHierarchicalDatasource(Element element) {
String id = getDatasourceId(element);
builder.reset().setMetaClass(metadata.getClassNN(KeyValueEntity.class)).setId(id);
ValueHierarchicalDatasourceImpl datasource = builder.buildValuesHierarchicalDatasourceImpl();
String maxResults = element.attributeValue("maxResults");
if (!StringUtils.isEmpty(maxResults))
datasource.setMaxResults(Integer.parseInt(maxResults));
datasource.setSuspended(true);
loadQuery(element, datasource);
loadProperties(element, datasource);
String hierarchyProperty = element.attributeValue("hierarchyProperty");
if (!StringUtils.isEmpty(hierarchyProperty)) {
datasource.setHierarchyPropertyName(hierarchyProperty);
}
datasource.setStoreName(element.attributeValue("store"));
return datasource;
}
private void loadProperties(Element element, ValueDatasource datasource) {
Element propsEl = element.element("properties");
if (propsEl != null) {
for (Element propEl : Dom4j.elements(propsEl)) {
String name = propEl.attributeValue("name");
String className = propEl.attributeValue("class");
if (className != null) {
datasource.addProperty(name, ReflectionHelper.getClass(className));
} else {
String typeName = propEl.attributeValue("type");
Datatype datatype = typeName == null ? Datatypes.get(StringDatatype.NAME) : Datatypes.get(typeName);
datasource.addProperty(name, datatype);
}
}
String idName = propsEl.attributeValue("id");
if (idName != null) {
if (datasource.getMetaClass().getProperty(idName) == null)
throw new DevelopmentException(String.format("Property '%s' is not defined", idName));
datasource.setIdName(idName);
}
}
}
protected String getDatasourceId(Element element) {
String id = element.attributeValue("id");
for (Datasource datasource : context.getAll()) {

View File

@ -19,10 +19,8 @@ package com.haulmont.cuba.gui.data.impl;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.global.CommitContext;
import com.haulmont.cuba.core.global.DataManager;
import com.haulmont.cuba.core.global.LoadContext;
import com.haulmont.cuba.core.global.View;
import com.haulmont.cuba.core.entity.KeyValueEntity;
import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.gui.data.DataSupplier;
import javax.annotation.Nonnull;
@ -116,6 +114,11 @@ public class TestDataSupplier implements DataSupplier {
public void remove(Entity entity) {
}
@Override
public List<KeyValueEntity> loadValues(ValueLoadContext context) {
return Collections.emptyList();
}
@Override
public DataManager secure() {
return this;

View File

@ -838,10 +838,8 @@ public abstract class WebAbstractTable<T extends com.vaadin.ui.Table & CubaEnhan
Collection<MetaPropertyPath> paths = datasource.getView() != null ?
// if a view is specified - use view properties
metadataTools.getViewPropertyPaths(datasource.getView(), datasource.getMetaClass()) :
// otherwise use only string properties from meta-class - the temporary solution for KeyValue datasources
metadataTools.getPropertyPaths(datasource.getMetaClass()).stream()
.filter(mpp -> mpp.getRangeJavaClass().equals(String.class))
.collect(Collectors.toList());
// otherwise use all properties from meta-class
metadataTools.getPropertyPaths(datasource.getMetaClass());
for (MetaPropertyPath metaPropertyPath : paths) {
MetaProperty property = metaPropertyPath.getMetaProperty();
if (!property.getRange().getCardinality().isMany() && !metadataTools.isSystem(property)) {