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

View File

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

View File

@ -17,9 +17,11 @@
package com.haulmont.cuba.core.app; package com.haulmont.cuba.core.app;
import com.haulmont.cuba.core.entity.Entity; 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.CommitContext;
import com.haulmont.cuba.core.global.DataManager; import com.haulmont.cuba.core.global.DataManager;
import com.haulmont.cuba.core.global.LoadContext; import com.haulmont.cuba.core.global.LoadContext;
import com.haulmont.cuba.core.global.ValueLoadContext;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -53,4 +55,9 @@ public class DataServiceBean implements DataService {
public long getCount(LoadContext<? extends Entity> context) { public long getCount(LoadContext<? extends Entity> context) {
return dataManager.secure().getCount(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; value = list;
} }
if (value instanceof LoadContext.Query.TemporalValue) { if (value instanceof TemporalValue) {
LoadContext.Query.TemporalValue temporalValue = (LoadContext.Query.TemporalValue) value; TemporalValue temporalValue = (TemporalValue) value;
query.setParameter(name, temporalValue.date, temporalValue.type); query.setParameter(name, temporalValue.date, temporalValue.type);
} else { } else {
query.setParameter(name, value); query.setParameter(name, value);

View File

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

View File

@ -16,6 +16,7 @@
package com.haulmont.cuba.core.app; 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.MetaClass;
import com.haulmont.chile.core.model.MetaProperty; import com.haulmont.chile.core.model.MetaProperty;
import com.haulmont.chile.core.model.Session; import com.haulmont.chile.core.model.Session;
@ -390,6 +391,60 @@ public class RdbmsStore implements DataStore {
return res; 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) { protected View getViewFromContext(CommitContext context, Entity entity) {
View view = context.getViews().get(entity); View view = context.getViews().get(entity);
if (view == null) { 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 @Test
public void testParse() throws Exception { public void testParse() throws Exception {
Element element = Dom4j.readDocument(xml).getRootElement(); Element element = Dom4j.readDocument(xml).getRootElement();
QueryFilter queryFilter = new QueryFilter(element, "sec$User"); QueryFilter queryFilter = new QueryFilter(element);
Condition root = queryFilter.getRoot(); Condition root = queryFilter.getRoot();
System.out.println(new GroovyGenerator().generateGroovy(root)); System.out.println(new GroovyGenerator().generateGroovy(root));

View File

@ -55,7 +55,7 @@ public class QueryFilterTest {
private QueryFilter createFilter(String name) { private QueryFilter createFilter(String name) {
InputStream stream = QueryFilterTest.class.getResourceAsStream("/com/haulmont/cuba/core/global/filter/" + name); InputStream stream = QueryFilterTest.class.getResourceAsStream("/com/haulmont/cuba/core/global/filter/" + name);
Document doc = Dom4j.readDocument(stream); Document doc = Dom4j.readDocument(stream);
return new QueryFilter(doc.getRootElement(), "saneco$GenDoc"); return new QueryFilter(doc.getRootElement());
} }
@Test @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 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 COMPANY_GROUP_ID = UUID.fromString("0fa2b1a5-1d68-4d69-9fbd-dff348347f93");
public static final UUID ADMIN_ROLE_ID = UUID.fromString("0c018061-b26f-4de2-a5be-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; package com.haulmont.cuba.core.app;
import com.haulmont.cuba.core.entity.Entity; 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.CommitContext;
import com.haulmont.cuba.core.global.LoadContext; import com.haulmont.cuba.core.global.LoadContext;
import com.haulmont.cuba.core.global.ValueLoadContext;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.List; import java.util.List;
@ -65,4 +67,6 @@ public interface DataService {
* @return number of instances in the database * @return number of instances in the database
*/ */
long getCount(LoadContext<? extends Entity> context); 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); properties.remove(propertyName);
} }
public KeyValueMetaClass() {
name = "sys$KeyValueEntity";
}
@Nullable @Nullable
@Override @Override
public MetaClass getAncestor() { public MetaClass getAncestor() {

View File

@ -17,6 +17,7 @@
package com.haulmont.cuba.core.app.keyvalue; 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.datatypes.Datatypes;
import com.haulmont.chile.core.model.*; import com.haulmont.chile.core.model.*;
import com.haulmont.chile.core.model.impl.ClassRange; 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 @Override
public MetaModel getModel() { public MetaModel getModel() {
return metaClass.getModel(); return metaClass.getModel();

View File

@ -18,12 +18,16 @@
package com.haulmont.cuba.core.entity; package com.haulmont.cuba.core.entity;
import com.haulmont.chile.core.model.MetaClass; 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.entity.annotation.SystemLevel;
import com.haulmont.cuba.core.global.UuidProvider;
import com.haulmont.cuba.core.sys.CubaEnhancingDisabled; import com.haulmont.cuba.core.sys.CubaEnhancingDisabled;
import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.ObjectUtils;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.UUID;
/** /**
* Entity that contains a variable set of attributes. For example: * 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") @com.haulmont.chile.core.annotations.MetaClass(name = "sys$KeyValueEntity")
@SystemLevel @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 @Override
public MetaClass getMetaClass() { public MetaClass getMetaClass() {
@ -58,6 +72,14 @@ public class KeyValueEntity extends AbstractNotPersistentEntity implements CubaE
this.metaClass = metaClass; this.metaClass = metaClass;
} }
public String getIdName() {
return idName;
}
public void setIdName(String idName) {
this.idName = idName;
}
@Override @Override
public <T> T getValue(String name) { public <T> T getValue(String name) {
//noinspection unchecked //noinspection unchecked
@ -72,4 +94,51 @@ public class KeyValueEntity extends AbstractNotPersistentEntity implements CubaE
propertyChanged(name, oldValue, value); 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.chile.core.model.MetaClass;
import com.haulmont.cuba.core.entity.Entity; import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.entity.KeyValueEntity;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.List; import java.util.List;
@ -142,6 +143,8 @@ public interface DataManager {
*/ */
void remove(Entity entity); void remove(Entity entity);
List<KeyValueEntity> loadValues(ValueLoadContext context);
/** /**
* Returns the DataManager implementation that is guaranteed to apply security restrictions. * 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 * <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); List&lt;User&gt; users = dataManager.loadList(context);
* </pre> * </pre>
*/ */
public class LoadContext<E extends Entity> implements Serializable { public class LoadContext<E extends Entity> implements DataLoadContext, Serializable {
private static final long serialVersionUID = -8808320502197308698L; 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. * @param queryString JPQL query string. Only named parameters are supported.
* @return query definition object * @return query definition object
*/ */
@Override
public Query setQueryString(String queryString) { public Query setQueryString(String queryString) {
final Query query = new Query(queryString); final Query query = new Query(queryString);
setQuery(query); 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. * 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; private static final long serialVersionUID = 3819951144050635838L;
@ -293,18 +294,6 @@ public class LoadContext<E extends Entity> implements Serializable {
private int maxResults; private int maxResults;
private boolean cacheable; 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. * @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 * @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); 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.*; import java.util.*;
public class QueryFilter extends FilterParser { public class QueryFilter extends FilterParser {
private final String targetEntity;
public QueryFilter(Condition condition, String targetEntity) { public QueryFilter(Condition condition) {
super(condition); super(condition);
this.targetEntity = targetEntity;
} }
public QueryFilter(Element element, String targetEntity) { public QueryFilter(Element element) {
super(element); super(element);
this.targetEntity = targetEntity;
} }
public static QueryFilter merge(QueryFilter src1, QueryFilter src2) { public static QueryFilter merge(QueryFilter src1, QueryFilter src2) {
if (src1 == null || src2 == null) if (src1 == null || src2 == null)
throw new IllegalArgumentException("Source query filter is 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); Condition root = new LogicalCondition("root", LogicalOp.AND);
root.getConditions().add(src1.getRoot()); root.getConditions().add(src1.getRoot());
root.getConditions().add(src2.getRoot()); root.getConditions().add(src2.getRoot());
QueryFilter queryFilter = new QueryFilter(root, src1.targetEntity); QueryFilter queryFilter = new QueryFilter(root);
return queryFilter; return queryFilter;
} }

View File

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

View File

@ -433,7 +433,7 @@ public class DsBuilder {
return datasource; return datasource;
} }
public RuntimePropsDatasource buildRuntimePropsDataSource(String mainDsId, @Nullable MetaClass categorizedEntityClass) { public RuntimePropsDatasource buildRuntimePropsDatasource(String mainDsId, @Nullable MetaClass categorizedEntityClass) {
init(); init();
RuntimePropsDatasourceImpl datasource; RuntimePropsDatasourceImpl datasource;
datasource = new RuntimePropsDatasourceImpl(dsContext, dataSupplier, id, mainDsId, categorizedEntityClass); datasource = new RuntimePropsDatasourceImpl(dsContext, dataSupplier, id, mainDsId, categorizedEntityClass);
@ -441,6 +441,36 @@ public class DsBuilder {
return datasource; 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) { private void registerDatasource(Datasource datasource) {
if (dsContext != null && id != null) { if (dsContext != null && id != null) {
((DsContextImplementation) dsContext).register(datasource); ((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) { protected DataLoadContextQuery createDataQuery(DataLoadContext context, Map<String, Object> params) {
LoadContext.Query q; DataLoadContextQuery q = null;
if (query != null && queryParameters != null) { if (query != null && queryParameters != null) {
Map<String, Object> parameters = getQueryParameters(params); Map<String, Object> parameters = getQueryParameters(params);
for (ParameterInfo info : queryParameters) { for (ParameterInfo info : queryParameters) {
@ -532,7 +532,7 @@ public abstract class AbstractCollectionDatasource<T extends Entity<K>, K>
if (paramNames.contains(entry.getKey())) if (paramNames.contains(entry.getKey()))
q.setParameter(entry.getKey(), entry.getValue()); q.setParameter(entry.getKey(), entry.getValue());
} }
} else { } else if (!(context instanceof ValueLoadContext)) {
Collection<MetaProperty> properties = metadata.getTools().getNamePatternProperties(metaClass); Collection<MetaProperty> properties = metadata.getTools().getNamePatternProperties(metaClass);
if (!properties.isEmpty()) { if (!properties.isEmpty()) {
StringBuilder orderBy = new StringBuilder(); StringBuilder orderBy = new StringBuilder();
@ -550,8 +550,8 @@ public abstract class AbstractCollectionDatasource<T extends Entity<K>, K>
} else } else
q = context.setQueryString("select e from " + metaClass.getName() + " e"); q = context.setQueryString("select e from " + metaClass.getName() + " e");
} }
if (q != null) { if (q instanceof LoadContext.Query) {
q.setCacheable(isCacheable()); ((LoadContext.Query) q).setCacheable(isCacheable());
} }
return q; return q;
} }
@ -563,7 +563,7 @@ public abstract class AbstractCollectionDatasource<T extends Entity<K>, K>
*/ */
public int getCount() { public int getCount() {
LoadContext<Entity> context = new LoadContext<>(metaClass); 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()); context.setSoftDeletion(isSoftDeletion());
if (q == null) if (q == null)
return 0; return 0;

View File

@ -499,7 +499,7 @@ public class CollectionDatasourceImpl<T extends Entity<K>, K>
params = Collections.emptyMap(); params = Collections.emptyMap();
} else } else
params = savedParameters; params = savedParameters;
LoadContext.Query q = createLoadContextQuery(context, params); LoadContext.Query q = (LoadContext.Query) createDataQuery(context, params);
if (sortInfos != null && sortOnDb) { if (sortInfos != null && sortOnDb) {
setSortDirection(q); setSortDirection(q);
} }
@ -552,7 +552,7 @@ public class CollectionDatasourceImpl<T extends Entity<K>, K>
protected LoadContext beforeLoadData(Map<String, Object> params) { protected LoadContext beforeLoadData(Map<String, Object> params) {
final LoadContext context = new LoadContext(metaClass); final LoadContext context = new LoadContext(metaClass);
LoadContext.Query q = createLoadContextQuery(context, params); LoadContext.Query q = (LoadContext.Query) createDataQuery(context, params);
if (q == null) { if (q == null) {
detachListener(data.values()); detachListener(data.values());
data.clear(); 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.chile.core.model.MetaClass;
import com.haulmont.cuba.core.entity.Entity; import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.entity.KeyValueEntity;
import com.haulmont.cuba.core.global.*; import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.gui.data.DataSupplier; import com.haulmont.cuba.gui.data.DataSupplier;
@ -78,6 +79,11 @@ public class GenericDataSupplier implements DataSupplier {
dataManager.remove(entity); dataManager.remove(entity);
} }
@Override
public List<KeyValueEntity> loadValues(ValueLoadContext context) {
return dataManager.loadValues(context);
}
@Override @Override
public DataManager secure() { public DataManager secure() {
return dataManager; return dataManager;

View File

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

View File

@ -17,23 +17,34 @@
package com.haulmont.cuba.gui.data.impl; package com.haulmont.cuba.gui.data.impl;
import com.haulmont.chile.core.datatypes.Datatype;
import com.haulmont.chile.core.model.MetaClass; import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.core.app.keyvalue.KeyValueMetaClass; 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.entity.KeyValueEntity;
import com.haulmont.cuba.core.global.View; import com.haulmont.cuba.core.global.View;
import com.haulmont.cuba.gui.data.CollectionDatasource; import com.haulmont.cuba.gui.data.CollectionDatasource;
import com.haulmont.cuba.gui.data.DataSupplier; import com.haulmont.cuba.gui.data.DataSupplier;
import com.haulmont.cuba.gui.data.DsContext; 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 javax.annotation.Nullable;
import java.util.Map; import java.util.Map;
import java.util.UUID;
/** /**
* {@link CollectionDatasource} that supports {@link KeyValueEntity}. * {@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 @Override
public void setup(DsContext dsContext, DataSupplier dataSupplier, String id, MetaClass metaClass, @Nullable View view) { 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(); this.metaClass = new KeyValueMetaClass();
} }
public KeyValueCollectionDatasourceImpl addProperty(String name) { @Override
((KeyValueMetaClass) metaClass).addProperty(new KeyValueMetaProperty(metaClass, name, String.class)); 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; return this;
} }
@Override @Override
protected void loadData(Map<String, Object> params) { 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 @Override
@ -63,4 +96,8 @@ public class KeyValueCollectionDatasourceImpl extends CollectionDatasourceImpl<K
super.addItem(item); super.addItem(item);
item.setMetaClass(metaClass); 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; package com.haulmont.cuba.gui.data.impl;
import com.haulmont.chile.core.datatypes.Datatype;
import com.haulmont.chile.core.model.MetaClass; import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.core.app.keyvalue.KeyValueMetaClass; 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.entity.KeyValueEntity;
import com.haulmont.cuba.core.global.View; import com.haulmont.cuba.core.global.View;
import com.haulmont.cuba.gui.data.DataSupplier; import com.haulmont.cuba.gui.data.DataSupplier;
import com.haulmont.cuba.gui.data.DsContext; import com.haulmont.cuba.gui.data.DsContext;
import com.haulmont.cuba.gui.data.GroupDatasource; 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 javax.annotation.Nullable;
import java.util.Map; import java.util.Map;
import java.util.UUID;
/** /**
* {@link GroupDatasource} that supports {@link KeyValueEntity}. * {@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 @Override
public void setup(DsContext dsContext, DataSupplier dataSupplier, String id, MetaClass metaClass, @Nullable View view) { 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(); this.metaClass = new KeyValueMetaClass();
} }
public KeyValueGroupDatasourceImpl addProperty(String name) { @Override
((KeyValueMetaClass) metaClass).addProperty(new KeyValueMetaProperty(metaClass, name, String.class)); 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; return this;
} }
@Override @Override
protected void loadData(Map<String, Object> params) { 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 @Override
@ -63,4 +96,8 @@ public class KeyValueGroupDatasourceImpl extends GroupDatasourceImpl<KeyValueEnt
super.addItem(item); super.addItem(item);
item.setMetaClass(metaClass); 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; package com.haulmont.cuba.gui.data.impl;
import com.haulmont.chile.core.datatypes.Datatype;
import com.haulmont.chile.core.model.MetaClass; import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.core.app.keyvalue.KeyValueMetaClass; 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.entity.KeyValueEntity;
import com.haulmont.cuba.core.global.View; import com.haulmont.cuba.core.global.View;
import com.haulmont.cuba.gui.data.DataSupplier; import com.haulmont.cuba.gui.data.DataSupplier;
import com.haulmont.cuba.gui.data.DsContext; import com.haulmont.cuba.gui.data.DsContext;
import com.haulmont.cuba.gui.data.HierarchicalDatasource; 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 javax.annotation.Nullable;
import java.util.Map; import java.util.Map;
import java.util.UUID;
/** /**
* {@link HierarchicalDatasource} that supports {@link KeyValueEntity}. * {@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 @Override
public void setup(DsContext dsContext, DataSupplier dataSupplier, String id, MetaClass metaClass, @Nullable View view) { 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(); this.metaClass = new KeyValueMetaClass();
} }
public KeyValueHierarchicalDatasourceImpl addProperty(String name) { @Override
((KeyValueMetaClass) metaClass).addProperty(new KeyValueMetaProperty(metaClass, name, String.class)); 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; return this;
} }
@ -52,14 +79,19 @@ public class KeyValueHierarchicalDatasourceImpl extends HierarchicalDatasourceIm
public void setHierarchyPropertyName(String hierarchyPropertyName) { public void setHierarchyPropertyName(String hierarchyPropertyName) {
super.setHierarchyPropertyName(hierarchyPropertyName); super.setHierarchyPropertyName(hierarchyPropertyName);
KeyValueMetaClass metaClass = (KeyValueMetaClass) this.metaClass; KeyValueMetaClass metaClass = (KeyValueMetaClass) this.metaClass;
if (metaClass.getProperty(hierarchyPropertyName) != null) { if (metaClass.getProperty(hierarchyPropertyName) == null) {
metaClass.removeProperty(hierarchyPropertyName); throw new IllegalStateException("Hierarchy property must be added to the datasource as property first");
} }
metaClass.addProperty(new KeyValueMetaProperty(metaClass, hierarchyPropertyName, KeyValueEntity.class));
} }
@Override @Override
protected void loadData(Map<String, Object> params) { 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 @Override
@ -73,4 +105,8 @@ public class KeyValueHierarchicalDatasourceImpl extends HierarchicalDatasourceIm
super.addItem(item); super.addItem(item);
item.setMetaClass(metaClass); 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:attribute name="categorizedEntityClass" type="xs:string" use="optional"/>
</xs:complexType> </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 --> <!-- PropertyDatasource -->
<xs:group name="nestedDatasources"> <xs:group name="nestedDatasources">
<xs:sequence> <xs:sequence>
@ -2104,6 +2174,9 @@
<xs:element name="collectionDatasource" type="collectionDatasourceType"/> <xs:element name="collectionDatasource" type="collectionDatasourceType"/>
<xs:element name="hierarchicalDatasource" type="hierarchicalDatasourceType"/> <xs:element name="hierarchicalDatasource" type="hierarchicalDatasourceType"/>
<xs:element name="runtimePropsDatasource" type="runtimePropsDatasourceType"/> <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:choice>
</xs:sequence> </xs:sequence>
<xs:attribute name="class" type="xs:string"/> <xs:attribute name="class" type="xs:string"/>

View File

@ -17,15 +17,19 @@
package com.haulmont.cuba.gui.xml.data; package com.haulmont.cuba.gui.xml.data;
import com.haulmont.bali.util.Dom4j;
import com.haulmont.bali.util.ReflectionHelper; 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.chile.core.model.MetaClass;
import com.haulmont.cuba.core.entity.KeyValueEntity;
import com.haulmont.cuba.core.global.AppBeans; import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.DevelopmentException; import com.haulmont.cuba.core.global.DevelopmentException;
import com.haulmont.cuba.core.global.Metadata; import com.haulmont.cuba.core.global.Metadata;
import com.haulmont.cuba.core.global.Scripting; import com.haulmont.cuba.core.global.Scripting;
import com.haulmont.cuba.gui.data.*; import com.haulmont.cuba.gui.data.*;
import com.haulmont.cuba.gui.data.impl.DsContextImpl; import com.haulmont.cuba.gui.data.impl.*;
import com.haulmont.cuba.gui.data.impl.DsContextImplementation;
import com.haulmont.cuba.core.global.filter.QueryFilter; import com.haulmont.cuba.core.global.filter.QueryFilter;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.dom4j.Element; import org.dom4j.Element;
@ -110,7 +114,25 @@ public class DsContextLoader {
//noinspection unchecked //noinspection unchecked
elements = element.elements("runtimePropsDatasource"); elements = element.elements("runtimePropsDatasource");
for (Element ds : elements) { 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(); context.executeLazyTasks();
@ -154,7 +176,7 @@ public class DsContextLoader {
if (datasource instanceof CollectionDatasource.Suspendable) if (datasource instanceof CollectionDatasource.Suspendable)
((CollectionDatasource.Suspendable) datasource).setSuspended(true); ((CollectionDatasource.Suspendable) datasource).setSuspended(true);
loadQuery(element, metaClass, datasource); loadQuery(element, datasource);
loadDatasources(element, datasource); loadDatasources(element, datasource);
@ -190,7 +212,7 @@ public class DsContextLoader {
if (datasource instanceof CollectionDatasource.Suspendable) if (datasource instanceof CollectionDatasource.Suspendable)
((CollectionDatasource.Suspendable) datasource).setSuspended(true); ((CollectionDatasource.Suspendable) datasource).setSuspended(true);
loadQuery(element, metaClass, datasource); loadQuery(element, datasource);
loadDatasources(element, datasource); loadDatasources(element, datasource);
@ -260,7 +282,7 @@ public class DsContextLoader {
//noinspection unchecked //noinspection unchecked
elements = element.elements("runtimePropsDatasource"); elements = element.elements("runtimePropsDatasource");
for (Element ds : elements) { for (Element ds : elements) {
loadRuntimePropsDataSource(ds); loadRuntimePropsDatasource(ds);
} }
} }
@ -357,14 +379,14 @@ public class DsContextLoader {
if (datasource instanceof CollectionDatasource.Suspendable) if (datasource instanceof CollectionDatasource.Suspendable)
((CollectionDatasource.Suspendable) datasource).setSuspended(true); ((CollectionDatasource.Suspendable) datasource).setSuspended(true);
loadQuery(element, metaClass, datasource); loadQuery(element, datasource);
loadDatasources(element, datasource); loadDatasources(element, datasource);
return datasource; return datasource;
} }
private void loadQuery(Element element, MetaClass metaClass, CollectionDatasource datasource) { private void loadQuery(Element element, CollectionDatasource datasource) {
Element queryElem = element.element("query"); Element queryElem = element.element("query");
if (queryElem != null) { if (queryElem != null) {
Element filterElem = queryElem.element("filter"); Element filterElem = queryElem.element("filter");
@ -372,7 +394,7 @@ public class DsContextLoader {
String query = queryElem.getText(); String query = queryElem.getText();
if (!StringUtils.isBlank(query)) { if (!StringUtils.isBlank(query)) {
if (filterElem != null) if (filterElem != null)
datasource.setQuery(query, new QueryFilter(filterElem, metaClass.getName())); datasource.setQuery(query, new QueryFilter(filterElem));
else else
datasource.setQuery(query); datasource.setQuery(query);
} }
@ -414,7 +436,7 @@ public class DsContextLoader {
return StringUtils.isEmpty(allowCommitStr) || Boolean.parseBoolean(allowCommitStr); return StringUtils.isEmpty(allowCommitStr) || Boolean.parseBoolean(allowCommitStr);
} }
protected RuntimePropsDatasource loadRuntimePropsDataSource(Element element){ protected RuntimePropsDatasource loadRuntimePropsDatasource(Element element){
String id = getDatasourceId(element); String id = getDatasourceId(element);
MetaClass metaClass = loadMetaClass(element); MetaClass metaClass = loadMetaClass(element);
@ -432,12 +454,103 @@ public class DsContextLoader {
builder.reset().setMetaClass(metaClass).setId(id); builder.reset().setMetaClass(metaClass).setId(id);
RuntimePropsDatasource datasource = builder.buildRuntimePropsDataSource(mainDsId, categorizedEntityMetaClass); RuntimePropsDatasource datasource = builder.buildRuntimePropsDatasource(mainDsId, categorizedEntityMetaClass);
loadDatasources(element, datasource); loadDatasources(element, datasource);
return 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) { protected String getDatasourceId(Element element) {
String id = element.attributeValue("id"); String id = element.attributeValue("id");
for (Datasource datasource : context.getAll()) { 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.chile.core.model.MetaClass;
import com.haulmont.cuba.core.entity.Entity; import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.global.CommitContext; import com.haulmont.cuba.core.entity.KeyValueEntity;
import com.haulmont.cuba.core.global.DataManager; import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.core.global.LoadContext;
import com.haulmont.cuba.core.global.View;
import com.haulmont.cuba.gui.data.DataSupplier; import com.haulmont.cuba.gui.data.DataSupplier;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -116,6 +114,11 @@ public class TestDataSupplier implements DataSupplier {
public void remove(Entity entity) { public void remove(Entity entity) {
} }
@Override
public List<KeyValueEntity> loadValues(ValueLoadContext context) {
return Collections.emptyList();
}
@Override @Override
public DataManager secure() { public DataManager secure() {
return this; 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 ? Collection<MetaPropertyPath> paths = datasource.getView() != null ?
// if a view is specified - use view properties // if a view is specified - use view properties
metadataTools.getViewPropertyPaths(datasource.getView(), datasource.getMetaClass()) : metadataTools.getViewPropertyPaths(datasource.getView(), datasource.getMetaClass()) :
// otherwise use only string properties from meta-class - the temporary solution for KeyValue datasources // otherwise use all properties from meta-class
metadataTools.getPropertyPaths(datasource.getMetaClass()).stream() metadataTools.getPropertyPaths(datasource.getMetaClass());
.filter(mpp -> mpp.getRangeJavaClass().equals(String.class))
.collect(Collectors.toList());
for (MetaPropertyPath metaPropertyPath : paths) { for (MetaPropertyPath metaPropertyPath : paths) {
MetaProperty property = metaPropertyPath.getMetaProperty(); MetaProperty property = metaPropertyPath.getMetaProperty();
if (!property.getRange().getCardinality().isMany() && !metadataTools.isSystem(property)) { if (!property.getRange().getCardinality().isMany() && !metadataTools.isSystem(property)) {