diff --git a/modules/client/src/com/haulmont/cuba/client/sys/DataManagerClientImpl.java b/modules/client/src/com/haulmont/cuba/client/sys/DataManagerClientImpl.java index 3cfcf31a6d..086dcddea4 100644 --- a/modules/client/src/com/haulmont/cuba/client/sys/DataManagerClientImpl.java +++ b/modules/client/src/com/haulmont/cuba/client/sys/DataManagerClientImpl.java @@ -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 loadValues(ValueLoadContext context) { + return dataService.loadValues(context); + } + @Override public DataManager secure() { return this; diff --git a/modules/core/src/com/haulmont/cuba/core/app/DataManagerBean.java b/modules/core/src/com/haulmont/cuba/core/app/DataManagerBean.java index 22a6a85173..54421e3fb0 100644 --- a/modules/core/src/com/haulmont/cuba/core/app/DataManagerBean.java +++ b/modules/core/src/com/haulmont/cuba/core/app/DataManagerBean.java @@ -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 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; diff --git a/modules/core/src/com/haulmont/cuba/core/app/DataServiceBean.java b/modules/core/src/com/haulmont/cuba/core/app/DataServiceBean.java index 75612205e9..6ee55adeb3 100644 --- a/modules/core/src/com/haulmont/cuba/core/app/DataServiceBean.java +++ b/modules/core/src/com/haulmont/cuba/core/app/DataServiceBean.java @@ -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 context) { return dataManager.secure().getCount(context); } + + @Override + public List loadValues(ValueLoadContext context) { + return dataManager.secure().loadValues(context); + } } \ No newline at end of file diff --git a/modules/core/src/com/haulmont/cuba/core/app/DataServiceQueryBuilder.java b/modules/core/src/com/haulmont/cuba/core/app/DataServiceQueryBuilder.java index 90be2dd4ea..92aa5733be 100644 --- a/modules/core/src/com/haulmont/cuba/core/app/DataServiceQueryBuilder.java +++ b/modules/core/src/com/haulmont/cuba/core/app/DataServiceQueryBuilder.java @@ -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); diff --git a/modules/core/src/com/haulmont/cuba/core/app/DataStore.java b/modules/core/src/com/haulmont/cuba/core/app/DataStore.java index 370b8e6174..fd587fd6bf 100644 --- a/modules/core/src/com/haulmont/cuba/core/app/DataStore.java +++ b/modules/core/src/com/haulmont/cuba/core/app/DataStore.java @@ -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 commit(CommitContext context); + + List loadValues(ValueLoadContext context); } diff --git a/modules/core/src/com/haulmont/cuba/core/app/RdbmsStore.java b/modules/core/src/com/haulmont/cuba/core/app/RdbmsStore.java index 25c0b82555..1ae02f0d0b 100644 --- a/modules/core/src/com/haulmont/cuba/core/app/RdbmsStore.java +++ b/modules/core/src/com/haulmont/cuba/core/app/RdbmsStore.java @@ -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 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 entities = new ArrayList<>(); + + try (Transaction tx = persistence.createTransaction(storeName)) { + EntityManager em = persistence.getEntityManager(storeName); + em.setSoftDeletion(context.isSoftDeletion()); + + List 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) { diff --git a/modules/core/test/com/haulmont/cuba/core/NonEntityQueryTest.java b/modules/core/test/com/haulmont/cuba/core/NonEntityQueryTest.java new file mode 100644 index 0000000000..c60fe901af --- /dev/null +++ b/modules/core/test/com/haulmont/cuba/core/NonEntityQueryTest.java @@ -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 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 list = dataManager.loadValues(context); + + assertEquals(1, list.size()); + KeyValueEntity e = list.get(0); + assertEquals(Long.valueOf(2), e.getValue("count")); + } +} diff --git a/modules/core/test/com/haulmont/cuba/core/global/filter/QueryFilter2Test.java b/modules/core/test/com/haulmont/cuba/core/global/filter/QueryFilter2Test.java index 1326b66591..2638cff52d 100644 --- a/modules/core/test/com/haulmont/cuba/core/global/filter/QueryFilter2Test.java +++ b/modules/core/test/com/haulmont/cuba/core/global/filter/QueryFilter2Test.java @@ -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)); diff --git a/modules/core/test/com/haulmont/cuba/core/global/filter/QueryFilterTest.java b/modules/core/test/com/haulmont/cuba/core/global/filter/QueryFilterTest.java index 3d557b1be0..2718474189 100644 --- a/modules/core/test/com/haulmont/cuba/core/global/filter/QueryFilterTest.java +++ b/modules/core/test/com/haulmont/cuba/core/global/filter/QueryFilterTest.java @@ -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 diff --git a/modules/core/test/com/haulmont/cuba/testsupport/TestSupport.java b/modules/core/test/com/haulmont/cuba/testsupport/TestSupport.java index 44aa1f8f99..1e3781e8fb 100644 --- a/modules/core/test/com/haulmont/cuba/testsupport/TestSupport.java +++ b/modules/core/test/com/haulmont/cuba/testsupport/TestSupport.java @@ -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"); diff --git a/modules/global/src/com/haulmont/cuba/core/app/DataService.java b/modules/global/src/com/haulmont/cuba/core/app/DataService.java index 5b1513db78..bacbd1ed00 100644 --- a/modules/global/src/com/haulmont/cuba/core/app/DataService.java +++ b/modules/global/src/com/haulmont/cuba/core/app/DataService.java @@ -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 context); + + List loadValues(ValueLoadContext context); } diff --git a/modules/global/src/com/haulmont/cuba/core/app/keyvalue/KeyValueMetaClass.java b/modules/global/src/com/haulmont/cuba/core/app/keyvalue/KeyValueMetaClass.java index 0412100b3d..cb5ebc9802 100644 --- a/modules/global/src/com/haulmont/cuba/core/app/keyvalue/KeyValueMetaClass.java +++ b/modules/global/src/com/haulmont/cuba/core/app/keyvalue/KeyValueMetaClass.java @@ -42,6 +42,10 @@ public class KeyValueMetaClass extends MetadataObjectImpl implements MetaClass { properties.remove(propertyName); } + public KeyValueMetaClass() { + name = "sys$KeyValueEntity"; + } + @Nullable @Override public MetaClass getAncestor() { diff --git a/modules/global/src/com/haulmont/cuba/core/app/keyvalue/KeyValueMetaProperty.java b/modules/global/src/com/haulmont/cuba/core/app/keyvalue/KeyValueMetaProperty.java index 31f7bd0146..4a8bf50fe7 100644 --- a/modules/global/src/com/haulmont/cuba/core/app/keyvalue/KeyValueMetaProperty.java +++ b/modules/global/src/com/haulmont/cuba/core/app/keyvalue/KeyValueMetaProperty.java @@ -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(); diff --git a/modules/global/src/com/haulmont/cuba/core/entity/KeyValueEntity.java b/modules/global/src/com/haulmont/cuba/core/entity/KeyValueEntity.java index 776b332f5c..ede8a9cb12 100644 --- a/modules/global/src/com/haulmont/cuba/core/entity/KeyValueEntity.java +++ b/modules/global/src/com/haulmont/cuba/core/entity/KeyValueEntity.java @@ -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, CubaEnhancingDisabled { - private Map properties = new LinkedHashMap<>(); + protected UUID uuid; - private MetaClass metaClass; + protected Map 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 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; + } } diff --git a/modules/global/src/com/haulmont/cuba/core/global/DataLoadContext.java b/modules/global/src/com/haulmont/cuba/core/global/DataLoadContext.java new file mode 100644 index 0000000000..a5ea685105 --- /dev/null +++ b/modules/global/src/com/haulmont/cuba/core/global/DataLoadContext.java @@ -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); +} diff --git a/modules/global/src/com/haulmont/cuba/core/global/DataLoadContextQuery.java b/modules/global/src/com/haulmont/cuba/core/global/DataLoadContextQuery.java new file mode 100644 index 0000000000..0de91c8f56 --- /dev/null +++ b/modules/global/src/com/haulmont/cuba/core/global/DataLoadContextQuery.java @@ -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 getParameters(); + DataLoadContextQuery setParameters(Map parameters); + + int getFirstResult(); + DataLoadContextQuery setFirstResult(int firstResult); + + int getMaxResults(); + DataLoadContextQuery setMaxResults(int maxResults); +} diff --git a/modules/global/src/com/haulmont/cuba/core/global/DataManager.java b/modules/global/src/com/haulmont/cuba/core/global/DataManager.java index dcf8543453..35eace95e8 100644 --- a/modules/global/src/com/haulmont/cuba/core/global/DataManager.java +++ b/modules/global/src/com/haulmont/cuba/core/global/DataManager.java @@ -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 loadValues(ValueLoadContext context); + /** * Returns the DataManager implementation that is guaranteed to apply security restrictions. *

By default, DataManager does not apply security when used on the middleware. Use this method if you want diff --git a/modules/global/src/com/haulmont/cuba/core/global/LoadContext.java b/modules/global/src/com/haulmont/cuba/core/global/LoadContext.java index 3052bc0e78..e3b9897628 100644 --- a/modules/global/src/com/haulmont/cuba/core/global/LoadContext.java +++ b/modules/global/src/com/haulmont/cuba/core/global/LoadContext.java @@ -37,7 +37,7 @@ import java.util.stream.Collectors; List<User> users = dataManager.loadList(context); * */ -public class LoadContext implements Serializable { +public class LoadContext implements DataLoadContext, Serializable { private static final long serialVersionUID = -8808320502197308698L; @@ -118,6 +118,7 @@ public class LoadContext 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 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 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 implements Serializable { /** * @param parameters map of the query parameters */ - public void setParameters(Map parameters) { + public Query setParameters(Map parameters) { this.parameters.putAll(parameters); + return this; } /** diff --git a/modules/global/src/com/haulmont/cuba/core/global/TemporalValue.java b/modules/global/src/com/haulmont/cuba/core/global/TemporalValue.java new file mode 100644 index 0000000000..b2a714fa26 --- /dev/null +++ b/modules/global/src/com/haulmont/cuba/core/global/TemporalValue.java @@ -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; + } +} diff --git a/modules/global/src/com/haulmont/cuba/core/global/ValueLoadContext.java b/modules/global/src/com/haulmont/cuba/core/global/ValueLoadContext.java new file mode 100644 index 0000000000..17f1fa8e2b --- /dev/null +++ b/modules/global/src/com/haulmont/cuba/core/global/ValueLoadContext.java @@ -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 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 properties) { + this.properties.clear(); + this.properties.addAll(properties); + return this; + } + + public List 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 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 getParameters() { + return parameters; + } + + /** + * @param parameters map of the query parameters + */ + public Query setParameters(Map 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 + + '}'; + } + } +} diff --git a/modules/global/src/com/haulmont/cuba/core/global/filter/QueryFilter.java b/modules/global/src/com/haulmont/cuba/core/global/filter/QueryFilter.java index aeb162c257..c2d9175c25 100644 --- a/modules/global/src/com/haulmont/cuba/core/global/filter/QueryFilter.java +++ b/modules/global/src/com/haulmont/cuba/core/global/filter/QueryFilter.java @@ -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; } diff --git a/modules/gui/src/com/haulmont/cuba/gui/components/filter/FilterDelegateImpl.java b/modules/gui/src/com/haulmont/cuba/gui/components/filter/FilterDelegateImpl.java index ed86a4b6b2..e70f350db7 100644 --- a/modules/gui/src/com/haulmont/cuba/gui/components/filter/FilterDelegateImpl.java +++ b/modules/gui/src/com/haulmont/cuba/gui/components/filter/FilterDelegateImpl.java @@ -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); diff --git a/modules/gui/src/com/haulmont/cuba/gui/data/DsBuilder.java b/modules/gui/src/com/haulmont/cuba/gui/data/DsBuilder.java index f183cd7b77..d44735cc31 100644 --- a/modules/gui/src/com/haulmont/cuba/gui/data/DsBuilder.java +++ b/modules/gui/src/com/haulmont/cuba/gui/data/DsBuilder.java @@ -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); diff --git a/modules/gui/src/com/haulmont/cuba/gui/data/impl/AbstractCollectionDatasource.java b/modules/gui/src/com/haulmont/cuba/gui/data/impl/AbstractCollectionDatasource.java index a563fc4ca7..098469fce5 100644 --- a/modules/gui/src/com/haulmont/cuba/gui/data/impl/AbstractCollectionDatasource.java +++ b/modules/gui/src/com/haulmont/cuba/gui/data/impl/AbstractCollectionDatasource.java @@ -494,8 +494,8 @@ public abstract class AbstractCollectionDatasource, K> } } - protected LoadContext.Query createLoadContextQuery(LoadContext context, Map params) { - LoadContext.Query q; + protected DataLoadContextQuery createDataQuery(DataLoadContext context, Map params) { + DataLoadContextQuery q = null; if (query != null && queryParameters != null) { Map parameters = getQueryParameters(params); for (ParameterInfo info : queryParameters) { @@ -532,7 +532,7 @@ public abstract class AbstractCollectionDatasource, K> if (paramNames.contains(entry.getKey())) q.setParameter(entry.getKey(), entry.getValue()); } - } else { + } else if (!(context instanceof ValueLoadContext)) { Collection properties = metadata.getTools().getNamePatternProperties(metaClass); if (!properties.isEmpty()) { StringBuilder orderBy = new StringBuilder(); @@ -550,8 +550,8 @@ public abstract class AbstractCollectionDatasource, 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, K> */ public int getCount() { LoadContext context = new LoadContext<>(metaClass); - LoadContext.Query q = createLoadContextQuery(context, savedParameters == null ? Collections.emptyMap() : savedParameters); + LoadContext.Query q = (LoadContext.Query) createDataQuery(context, savedParameters == null ? Collections.emptyMap() : savedParameters); context.setSoftDeletion(isSoftDeletion()); if (q == null) return 0; diff --git a/modules/gui/src/com/haulmont/cuba/gui/data/impl/CollectionDatasourceImpl.java b/modules/gui/src/com/haulmont/cuba/gui/data/impl/CollectionDatasourceImpl.java index bada1fc8ba..7bbd7c029c 100644 --- a/modules/gui/src/com/haulmont/cuba/gui/data/impl/CollectionDatasourceImpl.java +++ b/modules/gui/src/com/haulmont/cuba/gui/data/impl/CollectionDatasourceImpl.java @@ -499,7 +499,7 @@ public class CollectionDatasourceImpl, 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, K> protected LoadContext beforeLoadData(Map 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(); diff --git a/modules/gui/src/com/haulmont/cuba/gui/data/impl/GenericDataSupplier.java b/modules/gui/src/com/haulmont/cuba/gui/data/impl/GenericDataSupplier.java index dad3799728..5fda50bf42 100644 --- a/modules/gui/src/com/haulmont/cuba/gui/data/impl/GenericDataSupplier.java +++ b/modules/gui/src/com/haulmont/cuba/gui/data/impl/GenericDataSupplier.java @@ -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 loadValues(ValueLoadContext context) { + return dataManager.loadValues(context); + } + @Override public DataManager secure() { return dataManager; diff --git a/modules/gui/src/com/haulmont/cuba/gui/data/impl/HierarchicalDatasourceImpl.java b/modules/gui/src/com/haulmont/cuba/gui/data/impl/HierarchicalDatasourceImpl.java index b06624ec11..f112e61fbb 100644 --- a/modules/gui/src/com/haulmont/cuba/gui/data/impl/HierarchicalDatasourceImpl.java +++ b/modules/gui/src/com/haulmont/cuba/gui/data/impl/HierarchicalDatasourceImpl.java @@ -42,18 +42,18 @@ public class HierarchicalDatasourceImpl, K> @Override public Collection 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 res = new ArrayList<>(); Collection ids = getItemIds(); for (K id : ids) { - Entity currentItem = getItem(id); - Object parentItem = currentItem.getValue(hierarchyPropertyName); - if (parentItem != null && parentItem.equals(item)) - res.add(currentItem.getId()); + Entity item = getItemNN(id); + Entity parentItem = item.getValue(hierarchyPropertyName); + if (parentItem != null && parentItem.getId().equals(itemId)) + res.add(item.getId()); } return res; @@ -68,8 +68,8 @@ public class HierarchicalDatasourceImpl, K> if (item == null) return null; else { - Entity value = item.getValue(hierarchyPropertyName); - return value == null ? null : value.getId(); + Entity parentItem = item.getValue(hierarchyPropertyName); + return parentItem == null ? null : parentItem.getId(); } } return null; @@ -83,8 +83,8 @@ public class HierarchicalDatasourceImpl, K> Set result = new LinkedHashSet<>(); for (K id : ids) { Entity item = getItemNN(id); - Object value = item.getValue(hierarchyPropertyName); - if (value == null || !containsItem(((T) value).getId())) + Entity parentItem = item.getValue(hierarchyPropertyName); + if (parentItem == null || !containsItem(parentItem.getId())) result.add(item.getId()); } return result; @@ -99,8 +99,8 @@ public class HierarchicalDatasourceImpl, K> if (item == null) return false; if (hierarchyPropertyName != null) { - Object value = item.getValue(hierarchyPropertyName); - return (value == null || !containsItem(((T) value).getId())); + Entity parentItem = item.getValue(hierarchyPropertyName); + return (parentItem == null || !containsItem(parentItem.getId())); } else { return true; } @@ -108,15 +108,16 @@ public class HierarchicalDatasourceImpl, 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 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; } } diff --git a/modules/gui/src/com/haulmont/cuba/gui/data/impl/KeyValueCollectionDatasourceImpl.java b/modules/gui/src/com/haulmont/cuba/gui/data/impl/ValueCollectionDatasourceImpl.java similarity index 57% rename from modules/gui/src/com/haulmont/cuba/gui/data/impl/KeyValueCollectionDatasourceImpl.java rename to modules/gui/src/com/haulmont/cuba/gui/data/impl/ValueCollectionDatasourceImpl.java index ee1a4b8928..83d043642f 100644 --- a/modules/gui/src/com/haulmont/cuba/gui/data/impl/KeyValueCollectionDatasourceImpl.java +++ b/modules/gui/src/com/haulmont/cuba/gui/data/impl/ValueCollectionDatasourceImpl.java @@ -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{ +public class ValueCollectionDatasourceImpl + extends CollectionDatasourceImpl + 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 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 params) { + if (ds.needLoading()) { + ValueLoadContext context = beforeLoadValues(params); + if (context == null) { + return; + } + try { + List entities = ds.dataSupplier.loadValues(context); + + afterLoadValues(params, context, entities); + } catch (Throwable e) { + ds.dataLoadError = e; + } + } + } + + protected ValueLoadContext beforeLoadValues(Map 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 params, ValueLoadContext context, List 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); + } + } +} diff --git a/modules/gui/src/com/haulmont/cuba/gui/data/impl/KeyValueGroupDatasourceImpl.java b/modules/gui/src/com/haulmont/cuba/gui/data/impl/ValueGroupDatasourceImpl.java similarity index 57% rename from modules/gui/src/com/haulmont/cuba/gui/data/impl/KeyValueGroupDatasourceImpl.java rename to modules/gui/src/com/haulmont/cuba/gui/data/impl/ValueGroupDatasourceImpl.java index e2b7e12da3..d2b260adc9 100644 --- a/modules/gui/src/com/haulmont/cuba/gui/data/impl/KeyValueGroupDatasourceImpl.java +++ b/modules/gui/src/com/haulmont/cuba/gui/data/impl/ValueGroupDatasourceImpl.java @@ -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 { +public class ValueGroupDatasourceImpl + extends GroupDatasourceImpl + 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 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{ +public class ValueHierarchicalDatasourceImpl + extends HierarchicalDatasourceImpl + 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 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); + } } diff --git a/modules/gui/src/com/haulmont/cuba/gui/window.xsd b/modules/gui/src/com/haulmont/cuba/gui/window.xsd index c7756aa6fc..6b07b5c97c 100644 --- a/modules/gui/src/com/haulmont/cuba/gui/window.xsd +++ b/modules/gui/src/com/haulmont/cuba/gui/window.xsd @@ -1926,6 +1926,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2104,6 +2174,9 @@ + + + diff --git a/modules/gui/src/com/haulmont/cuba/gui/xml/data/DsContextLoader.java b/modules/gui/src/com/haulmont/cuba/gui/xml/data/DsContextLoader.java index 06ec0a9834..c31c6012b3 100644 --- a/modules/gui/src/com/haulmont/cuba/gui/xml/data/DsContextLoader.java +++ b/modules/gui/src/com/haulmont/cuba/gui/xml/data/DsContextLoader.java @@ -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()) { diff --git a/modules/gui/test/com/haulmont/cuba/gui/data/impl/TestDataSupplier.java b/modules/gui/test/com/haulmont/cuba/gui/data/impl/TestDataSupplier.java index aa4971eeea..f6a2ae4dfd 100644 --- a/modules/gui/test/com/haulmont/cuba/gui/data/impl/TestDataSupplier.java +++ b/modules/gui/test/com/haulmont/cuba/gui/data/impl/TestDataSupplier.java @@ -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 loadValues(ValueLoadContext context) { + return Collections.emptyList(); + } + @Override public DataManager secure() { return this; diff --git a/modules/web/src/com/haulmont/cuba/web/gui/components/WebAbstractTable.java b/modules/web/src/com/haulmont/cuba/web/gui/components/WebAbstractTable.java index b73272e9c6..b323571dc9 100644 --- a/modules/web/src/com/haulmont/cuba/web/gui/components/WebAbstractTable.java +++ b/modules/web/src/com/haulmont/cuba/web/gui/components/WebAbstractTable.java @@ -838,10 +838,8 @@ public abstract class WebAbstractTable 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)) {