mirror of
https://gitee.com/jmix/cuba.git
synced 2024-12-02 19:27:57 +08:00
New presentation data layer #474
This commit is contained in:
parent
86241d282f
commit
379cf5462f
@ -1090,6 +1090,9 @@ def generateClassDirs(Collection projects) {
|
||||
}
|
||||
}
|
||||
|
||||
zipProject {
|
||||
excludeFromZip = ['out']
|
||||
}
|
||||
|
||||
task aggregateJavadoc(type: Javadoc,
|
||||
description: 'Generate javadocs from all child projects as if it was a single project',
|
||||
|
@ -27,6 +27,8 @@ import com.haulmont.cuba.core.sys.FormatStringsRegistryImpl;
|
||||
import mockit.Mocked;
|
||||
import mockit.Expectations;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
@ -92,10 +94,14 @@ public class CubaClientTestCase {
|
||||
|
||||
protected TestBeanValidation beanValidation;
|
||||
|
||||
protected TestEntityStates entityStates;
|
||||
|
||||
protected ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
|
||||
protected MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
|
||||
|
||||
public CubaClientTestCase() {
|
||||
private Logger log = LoggerFactory.getLogger(CubaClientTestCase.class);
|
||||
|
||||
static {
|
||||
String property = System.getProperty("logback.configurationFile");
|
||||
if (StringUtils.isBlank(property)) {
|
||||
System.setProperty("logback.configurationFile", "test-logback.xml");
|
||||
@ -107,6 +113,8 @@ public class CubaClientTestCase {
|
||||
* @param packageName package FQN, e.g. <code>com.haulmont.cuba.core.entity</code>
|
||||
*/
|
||||
protected void addEntityPackage(String packageName) {
|
||||
log.debug("Adding entity package: " + packageName);
|
||||
|
||||
String packagePrefix = packageName.replace(".", "/") + "/**/*.class";
|
||||
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + packagePrefix;
|
||||
Resource[] resources;
|
||||
@ -156,6 +164,7 @@ public class CubaClientTestCase {
|
||||
* once in their @Before method.
|
||||
*/
|
||||
protected void setupInfrastructure() {
|
||||
log.debug("Setting up infrastructure");
|
||||
new Expectations() {
|
||||
{
|
||||
AppContext.getProperty("cuba.confDir"); result = System.getProperty("user.dir"); minTimes = 0;
|
||||
@ -184,6 +193,8 @@ public class CubaClientTestCase {
|
||||
|
||||
beanValidation = new TestBeanValidation();
|
||||
|
||||
entityStates = new TestEntityStates();
|
||||
|
||||
((TestMetadataTools) metadata.getTools()).setMessages(messages);
|
||||
((TestMetadataTools) metadata.getTools()).setUserSessionSource(userSessionSource);
|
||||
|
||||
@ -243,9 +254,13 @@ public class CubaClientTestCase {
|
||||
AppBeans.get(MessageTools.class); result = messageTools; minTimes = 0;
|
||||
AppBeans.get(MessageTools.NAME, MessageTools.class); result = messageTools; minTimes = 0;
|
||||
|
||||
AppBeans.get(BeanValidation.NAME); result = beanValidation; minTimes = 0;
|
||||
AppBeans.get(BeanValidation.class); result = beanValidation; minTimes = 0;
|
||||
AppBeans.get(BeanValidation.NAME, BeanValidation.class); result = beanValidation; minTimes = 0;
|
||||
AppBeans.get(BeanValidation.NAME); result = beanValidation;
|
||||
AppBeans.get(BeanValidation.class); result = beanValidation;
|
||||
AppBeans.get(BeanValidation.NAME, BeanValidation.class); result = beanValidation;
|
||||
|
||||
AppBeans.get(EntityStates.NAME); result = entityStates;
|
||||
AppBeans.get(EntityStates.class); result = entityStates;
|
||||
AppBeans.get(EntityStates.NAME, BeanValidation.class); result = entityStates;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -48,8 +48,6 @@
|
||||
|
||||
<logger name="eclipselink.sql" level="DEBUG"/>
|
||||
|
||||
<logger name="org.springframework" level="WARN"/>
|
||||
|
||||
<logger name="org.activiti" level="INFO"/>
|
||||
|
||||
<logger name="freemarker" level="INFO"/>
|
||||
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.client.testsupport;
|
||||
|
||||
import com.haulmont.cuba.core.global.EntityStates;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class TestEntityStates extends EntityStates {
|
||||
|
||||
@Override
|
||||
public boolean isLoaded(Object entity, String property) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkLoaded(Object entity, String... properties) {
|
||||
}
|
||||
}
|
@ -28,12 +28,14 @@ import com.haulmont.cuba.core.global.BeanValidation;
|
||||
import com.haulmont.cuba.core.global.MessageTools;
|
||||
import com.haulmont.cuba.core.global.MetadataTools;
|
||||
import com.haulmont.cuba.gui.components.Field;
|
||||
import com.haulmont.cuba.gui.components.PropertyBoundComponent;
|
||||
import com.haulmont.cuba.gui.components.RequiredValueMissingException;
|
||||
import com.haulmont.cuba.gui.components.ValidationException;
|
||||
import com.haulmont.cuba.gui.components.compatibility.ComponentValueListenerWrapper;
|
||||
import com.haulmont.cuba.gui.components.validators.BeanValidator;
|
||||
import com.haulmont.cuba.gui.data.Datasource;
|
||||
import com.haulmont.cuba.gui.data.ValueListener;
|
||||
import com.haulmont.cuba.gui.model.InstanceContainer;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import javax.swing.*;
|
||||
@ -44,8 +46,8 @@ import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public abstract class DesktopAbstractField<C extends JComponent> extends DesktopAbstractComponent<C>
|
||||
implements Field, DesktopComponent.HasContextHelpClickHandler {
|
||||
public abstract class DesktopAbstractField<C extends JComponent> extends DesktopAbstractComponent<C> implements Field,
|
||||
DesktopComponent.HasContextHelpClickHandler, PropertyBoundComponent {
|
||||
|
||||
protected List<ValueChangeListener> listeners = new ArrayList<>();
|
||||
|
||||
@ -358,4 +360,14 @@ public abstract class DesktopAbstractField<C extends JComponent> extends Desktop
|
||||
contextHelpIconClickHandler.accept(event);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstanceContainer getEntityContainer() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContainer(InstanceContainer container, String property) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
@ -45,6 +45,7 @@ import com.haulmont.cuba.gui.data.impl.CollectionDsActionsNotifier;
|
||||
import com.haulmont.cuba.gui.data.impl.DatasourceImplementation;
|
||||
import com.haulmont.cuba.gui.data.impl.WeakCollectionChangeListener;
|
||||
import com.haulmont.cuba.gui.data.impl.WeakItemPropertyChangeListener;
|
||||
import com.haulmont.cuba.gui.model.CollectionContainer;
|
||||
import com.haulmont.cuba.gui.presentations.Presentations;
|
||||
import net.miginfocom.layout.CC;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
@ -806,6 +807,11 @@ public abstract class DesktopAbstractTable<C extends JXTable, E extends Entity>
|
||||
setSortable(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContainer(CollectionContainer container) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
protected boolean canBeSorted(CollectionDatasource datasource) {
|
||||
//noinspection SimplifiableConditionalExpression
|
||||
return datasource instanceof PropertyDatasource ?
|
||||
|
31
modules/global/src/com/haulmont/bali/util/Numbers.java
Normal file
31
modules/global/src/com/haulmont/bali/util/Numbers.java
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.bali.util;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Numbers {
|
||||
|
||||
public static int nullToZero(Integer value) {
|
||||
return value == null ? 0 : value;
|
||||
}
|
||||
|
||||
public static long nullToZero(Long value) {
|
||||
return value == null ? 0 : value;
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.components;
|
||||
|
||||
import com.haulmont.chile.core.model.MetaPropertyPath;
|
||||
import com.haulmont.cuba.gui.model.InstanceContainer;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface PropertyBoundComponent {
|
||||
|
||||
InstanceContainer getEntityContainer();
|
||||
|
||||
MetaPropertyPath getMetaPropertyPath();
|
||||
|
||||
void setContainer(InstanceContainer container, String property);
|
||||
}
|
@ -22,6 +22,7 @@ import com.haulmont.cuba.core.entity.Entity;
|
||||
import com.haulmont.cuba.core.global.*;
|
||||
import com.haulmont.cuba.gui.data.CollectionDatasource;
|
||||
import com.haulmont.cuba.gui.data.Datasource;
|
||||
import com.haulmont.cuba.gui.model.CollectionContainer;
|
||||
import org.dom4j.Element;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -56,6 +57,8 @@ public interface Table<E extends Entity>
|
||||
|
||||
void setDatasource(CollectionDatasource datasource);
|
||||
|
||||
void setContainer(CollectionContainer container);
|
||||
|
||||
void setRequired(Column column, boolean required, String message);
|
||||
|
||||
void addValidator(Column column, com.haulmont.cuba.gui.components.Field.Validator validator);
|
||||
|
@ -19,7 +19,7 @@ package com.haulmont.cuba.gui.components;
|
||||
|
||||
import java.util.EventObject;
|
||||
|
||||
public interface TextInputField extends Field, Component.Buffered, Component.Focusable {
|
||||
public interface TextInputField extends Field, Component.Buffered, Component.Focusable, PropertyBoundComponent {
|
||||
|
||||
/**
|
||||
* Defines case conversion for text input fields,
|
||||
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model;
|
||||
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.EventObject;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface CollectionContainer<T extends Entity> extends InstanceContainer<T> {
|
||||
|
||||
List<T> getItems();
|
||||
|
||||
List<T> getMutableItems();
|
||||
|
||||
void setItems(@Nullable Collection<T> entities);
|
||||
|
||||
@Nullable
|
||||
T getItem(Object entityId);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class CollectionChangeEvent<T extends Entity> extends EventObject {
|
||||
|
||||
public CollectionChangeEvent(CollectionContainer<T> container) {
|
||||
super(container);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public CollectionContainer<T> getSource() {
|
||||
return (CollectionContainer) super.getSource();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
interface CollectionChangeListener<T extends Entity> {
|
||||
/**
|
||||
* Enclosed collection changed.
|
||||
*/
|
||||
void collectionChanged(CollectionChangeEvent<T> e);
|
||||
}
|
||||
|
||||
void addCollectionChangeListener(CollectionChangeListener<T> listener);
|
||||
void removeCollectionChangeListener(CollectionChangeListener<T> listener);
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model;
|
||||
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
import com.haulmont.cuba.core.global.View;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface CollectionLoader<T extends Entity> extends DataLoader {
|
||||
|
||||
CollectionContainer<T> getContainer();
|
||||
|
||||
void setContainer(CollectionContainer<T> container);
|
||||
|
||||
String getQuery();
|
||||
|
||||
void setQuery(String query);
|
||||
|
||||
int getMaxResults();
|
||||
|
||||
void setMaxResults(int maxResults);
|
||||
|
||||
boolean isSoftDeletion();
|
||||
|
||||
void setSoftDeletion(boolean softDeletion);
|
||||
|
||||
boolean isCacheable();
|
||||
|
||||
void setCacheable(boolean cacheable);
|
||||
|
||||
View getView();
|
||||
|
||||
void setView(View view);
|
||||
|
||||
void setView(String viewName);
|
||||
}
|
118
modules/gui/src/com/haulmont/cuba/gui/model/DataContext.java
Normal file
118
modules/gui/src/com/haulmont/cuba/gui/model/DataContext.java
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model;
|
||||
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.EventObject;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface DataContext {
|
||||
|
||||
@Nullable
|
||||
<T extends Entity<K>, K> T find(Class<T> entityClass, K entityId);
|
||||
|
||||
boolean contains(@Nullable Entity entity);
|
||||
|
||||
<T extends Entity> T merge(T entity);
|
||||
|
||||
<T extends Entity> T merge(T entity, boolean deep);
|
||||
|
||||
void remove(Entity entity);
|
||||
|
||||
void evict(Entity entity);
|
||||
|
||||
boolean hasChanges();
|
||||
|
||||
void commit();
|
||||
|
||||
DataContext getParent();
|
||||
void setParent(DataContext parentContext);
|
||||
|
||||
class PreCommitEvent extends EventObject {
|
||||
|
||||
private final Collection<Entity> modifiedInstances;
|
||||
private final Collection<Entity> removedInstances;
|
||||
private boolean commitPrevented;
|
||||
|
||||
public PreCommitEvent(DataContext dataContext, Collection<Entity> modified, Collection<Entity> removed) {
|
||||
super(dataContext);
|
||||
this.modifiedInstances = modified;
|
||||
this.removedInstances = removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataContext getSource() {
|
||||
return (DataContext) super.getSource();
|
||||
}
|
||||
|
||||
public Collection<Entity> getModifiedInstances() {
|
||||
return modifiedInstances;
|
||||
}
|
||||
|
||||
public Collection<Entity> getRemovedInstances() {
|
||||
return removedInstances;
|
||||
}
|
||||
|
||||
public void preventCommit() {
|
||||
commitPrevented = true;
|
||||
}
|
||||
|
||||
public boolean isCommitPrevented() {
|
||||
return commitPrevented;
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface PreCommitListener {
|
||||
void preCommit(PreCommitEvent e);
|
||||
}
|
||||
|
||||
void addPreCommitListener(PreCommitListener listener);
|
||||
void removePreCommitListener(PreCommitListener listener);
|
||||
|
||||
class PostCommitEvent extends EventObject {
|
||||
|
||||
private final Collection<Entity> committedInstances;
|
||||
|
||||
public PostCommitEvent(DataContext dataContext, Collection<Entity> committedInstances) {
|
||||
super(dataContext);
|
||||
this.committedInstances = committedInstances;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataContext getSource() {
|
||||
return (DataContext) super.getSource();
|
||||
}
|
||||
|
||||
public Collection<Entity> getCommittedInstances() {
|
||||
return committedInstances;
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface PostCommitListener {
|
||||
void postCommit(PostCommitEvent e);
|
||||
}
|
||||
|
||||
void addPostCommitListener(PostCommitListener listener);
|
||||
void removePostCommitListener(PostCommitListener listener);
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model;
|
||||
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
import com.haulmont.cuba.core.global.DataManager;
|
||||
import com.haulmont.cuba.core.global.EntityStates;
|
||||
import com.haulmont.cuba.core.global.Metadata;
|
||||
import com.haulmont.cuba.gui.model.impl.*;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Component("cuba_DataContextFactory")
|
||||
public class DataContextFactory {
|
||||
|
||||
@Inject
|
||||
protected Metadata metadata;
|
||||
|
||||
@Inject
|
||||
protected DataManager dataManager;
|
||||
|
||||
@Inject
|
||||
protected EntityStates entityStates;
|
||||
|
||||
public DataContext createDataContext() {
|
||||
return new StandardDataContext(metadata, dataManager, entityStates);
|
||||
}
|
||||
|
||||
public <T extends Entity> InstanceContainer<T> createInstanceContainer(Class<T> entityClass) {
|
||||
return new InstanceContainerImpl<>(metadata.getClassNN(entityClass));
|
||||
}
|
||||
|
||||
public <T extends Entity> CollectionContainer<T> createCollectionContainer(Class<T> entityClass) {
|
||||
return new CollectionContainerImpl<>(metadata.getClassNN(entityClass));
|
||||
}
|
||||
|
||||
public <T extends Entity<K>, K> InstanceLoader<T, K> createInstanceLoader() {
|
||||
return new StandardInstanceLoader<>(metadata, dataManager);
|
||||
}
|
||||
|
||||
public <T extends Entity> CollectionLoader<T> createCollectionLoader() {
|
||||
return new StandardCollectionLoader<>(metadata, dataManager);
|
||||
}
|
||||
}
|
32
modules/gui/src/com/haulmont/cuba/gui/model/DataLoader.java
Normal file
32
modules/gui/src/com/haulmont/cuba/gui/model/DataLoader.java
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface DataLoader {
|
||||
|
||||
@Nullable
|
||||
DataContext getDataContext();
|
||||
|
||||
void setDataContext(DataContext dataContext);
|
||||
|
||||
void load();
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model;
|
||||
|
||||
import com.haulmont.chile.core.model.MetaClass;
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.EventObject;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface InstanceContainer<T extends Entity> {
|
||||
|
||||
@Nullable
|
||||
T getItem();
|
||||
|
||||
T getItemNN();
|
||||
|
||||
void setItem(T entity);
|
||||
|
||||
MetaClass getMetaClass();
|
||||
|
||||
class ItemPropertyChangeEvent<T extends Entity> extends EventObject {
|
||||
private final T item;
|
||||
private final String property;
|
||||
private final Object prevValue;
|
||||
private final Object value;
|
||||
|
||||
public ItemPropertyChangeEvent(InstanceContainer<T> container, T item, String property, Object prevValue, Object value) {
|
||||
super(container);
|
||||
this.item = item;
|
||||
this.property = property;
|
||||
this.prevValue = prevValue;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public InstanceContainer<T> getSource() {
|
||||
return (InstanceContainer) super.getSource();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return item, which property value is changed
|
||||
*/
|
||||
public T getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return property name
|
||||
*/
|
||||
public String getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return previous value of item property
|
||||
*/
|
||||
@Nullable
|
||||
public Object getPrevValue() {
|
||||
return prevValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return current value of item property
|
||||
*/
|
||||
@Nullable
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener to datasource item property value change events.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
interface ItemPropertyChangeListener<T extends Entity> {
|
||||
/**
|
||||
* Property value of some datasource item changed. In case of {@link CollectionContainer} this method may be
|
||||
* called for any item of collection if its property value changed.
|
||||
*/
|
||||
void itemPropertyChanged(ItemPropertyChangeEvent<T> e);
|
||||
}
|
||||
|
||||
void addItemPropertyChangeListener(ItemPropertyChangeListener<T> listener);
|
||||
void removeItemPropertyChangeListener(ItemPropertyChangeListener<T> listener);
|
||||
|
||||
/**
|
||||
* Container item change event.
|
||||
*/
|
||||
class ItemChangeEvent<T extends Entity> extends EventObject {
|
||||
|
||||
private final T prevItem;
|
||||
private final T item;
|
||||
|
||||
public ItemChangeEvent(InstanceContainer<T> container, T prevItem, T item) {
|
||||
super(container);
|
||||
this.prevItem = prevItem;
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public InstanceContainer<T> getSource() {
|
||||
return (InstanceContainer) super.getSource();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return current item
|
||||
*/
|
||||
@Nullable
|
||||
public T getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return previously selected item
|
||||
*/
|
||||
@Nullable
|
||||
public T getPrevItem() {
|
||||
return prevItem;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener to container item change events.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
interface ItemChangeListener<T extends Entity> {
|
||||
/**
|
||||
* Current item changed, that is now {@link InstanceContainer#getItem()} returns a different instance.
|
||||
*/
|
||||
void itemChanged(ItemChangeEvent<T> e);
|
||||
}
|
||||
|
||||
void addItemChangeListener(ItemChangeListener<T> listener);
|
||||
void removeItemChangeListener(ItemChangeListener<T> listener);
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model;
|
||||
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
import com.haulmont.cuba.core.global.View;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface InstanceLoader<T extends Entity<K>, K> extends DataLoader {
|
||||
|
||||
InstanceContainer<T> getContainer();
|
||||
|
||||
void setContainer(InstanceContainer<T> container);
|
||||
|
||||
K getEntityId();
|
||||
|
||||
void setEntityId(K entityId);
|
||||
|
||||
boolean isSoftDeletion();
|
||||
|
||||
void setSoftDeletion(boolean softDeletion);
|
||||
|
||||
View getView();
|
||||
|
||||
void setView(View view);
|
||||
|
||||
void setView(String viewName);
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model.impl;
|
||||
|
||||
import com.haulmont.chile.core.model.MetaClass;
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
import com.haulmont.cuba.gui.model.CollectionContainer;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class CollectionContainerImpl<T extends Entity> extends InstanceContainerImpl<T> implements CollectionContainer<T> {
|
||||
|
||||
protected List<T> collection = new ArrayList<>();
|
||||
|
||||
public CollectionContainerImpl(MetaClass metaClass) {
|
||||
super(metaClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> getItems() {
|
||||
return Collections.unmodifiableList(collection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> getMutableItems() {
|
||||
return new ObservableList<>(collection, this::fireCollectionChanged);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setItems(@Nullable Collection<T> entities) {
|
||||
detachListener(collection);
|
||||
collection.clear();
|
||||
if (entities != null) {
|
||||
collection.addAll(entities);
|
||||
attachListener(collection);
|
||||
}
|
||||
fireCollectionChanged();
|
||||
|
||||
if (item != null && !collection.contains(item)) {
|
||||
setItem(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T getItem(Object entityId) {
|
||||
return collection.stream()
|
||||
.filter(entity -> entity.getId().equals(entityId))
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCollectionChangeListener(CollectionChangeListener<T> listener) {
|
||||
getEventRouter().addListener(CollectionChangeListener.class, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCollectionChangeListener(CollectionChangeListener<T> listener) {
|
||||
getEventRouter().removeListener(CollectionChangeListener.class, listener);
|
||||
}
|
||||
|
||||
protected void fireCollectionChanged() {
|
||||
CollectionChangeEvent<T> collectionChangeEvent = new CollectionChangeEvent<>(this);
|
||||
//noinspection unchecked
|
||||
getEventRouter().fireEvent(CollectionChangeListener.class, CollectionChangeListener::collectionChanged, collectionChangeEvent);
|
||||
}
|
||||
|
||||
protected void attachListener(Collection<T> entities) {
|
||||
for (T entity : entities) {
|
||||
attachListener(entity);
|
||||
}
|
||||
}
|
||||
|
||||
protected void detachListener(Collection<T> entities) {
|
||||
for (T entity : entities) {
|
||||
detachListener(entity);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model.impl;
|
||||
|
||||
import com.haulmont.bali.events.EventRouter;
|
||||
import com.haulmont.bali.util.ParamsMap;
|
||||
import com.haulmont.chile.core.model.Instance;
|
||||
import com.haulmont.chile.core.model.MetaClass;
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
import com.haulmont.cuba.core.global.DevelopmentException;
|
||||
import com.haulmont.cuba.core.global.Metadata;
|
||||
import com.haulmont.cuba.gui.model.InstanceContainer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class InstanceContainerImpl<T extends Entity> implements InstanceContainer<T> {
|
||||
|
||||
private Logger log = LoggerFactory.getLogger(InstanceContainerImpl.class);
|
||||
|
||||
protected T item;
|
||||
protected MetaClass metaClass;
|
||||
|
||||
protected EventRouter eventRouter;
|
||||
protected boolean listenersEnabled = true;
|
||||
protected Instance.PropertyChangeListener listener = new ItemListener();
|
||||
|
||||
public InstanceContainerImpl(MetaClass metaClass) {
|
||||
this.metaClass = metaClass;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public T getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getItemNN() {
|
||||
T item = getItem();
|
||||
if (item == null)
|
||||
throw new IllegalStateException("Item is null");
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(T item) {
|
||||
T prevItem = this.item;
|
||||
|
||||
if (this.item != null) {
|
||||
detachListener(this.item);
|
||||
}
|
||||
|
||||
if (item != null) {
|
||||
final MetaClass aClass = item.getMetaClass();
|
||||
if (!aClass.equals(metaClass) && !metaClass.getDescendants().contains(aClass)) {
|
||||
throw new DevelopmentException(String.format("Invalid item's metaClass '%s'", aClass),
|
||||
ParamsMap.of("datasource", toString(), "metaClass", aClass));
|
||||
}
|
||||
attachListener(item);
|
||||
}
|
||||
|
||||
this.item = item;
|
||||
|
||||
fireItemChanged(prevItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaClass getMetaClass() {
|
||||
return metaClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addItemPropertyChangeListener(ItemPropertyChangeListener<T> listener) {
|
||||
getEventRouter().addListener(ItemPropertyChangeListener.class, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeItemPropertyChangeListener(ItemPropertyChangeListener<T> listener) {
|
||||
getEventRouter().removeListener(ItemPropertyChangeListener.class, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addItemChangeListener(ItemChangeListener<T> listener) {
|
||||
getEventRouter().addListener(ItemChangeListener.class, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeItemChangeListener(ItemChangeListener<T> listener) {
|
||||
getEventRouter().removeListener(ItemChangeListener.class, listener);
|
||||
}
|
||||
|
||||
protected EventRouter getEventRouter() {
|
||||
if (eventRouter == null) {
|
||||
eventRouter = new EventRouter();
|
||||
}
|
||||
return eventRouter;
|
||||
}
|
||||
|
||||
protected void fireItemChanged(T prevItem) {
|
||||
ItemChangeEvent<T> itemChangeEvent = new ItemChangeEvent<>(this, prevItem, getItem());
|
||||
//noinspection unchecked
|
||||
getEventRouter().fireEvent(ItemChangeListener.class, ItemChangeListener::itemChanged, itemChangeEvent);
|
||||
}
|
||||
|
||||
protected void attachListener(Instance entity) {
|
||||
if (entity != null) {
|
||||
entity.addPropertyChangeListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
protected void detachListener(Instance entity) {
|
||||
if (entity != null) {
|
||||
entity.removePropertyChangeListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
protected class ItemListener implements Instance.PropertyChangeListener {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void propertyChanged(Instance.PropertyChangeEvent e) {
|
||||
if (!listenersEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.trace("propertyChanged: item={}, property={}, value={}, prevValue={}",
|
||||
e.getItem(), e.getProperty(), e.getValue(), e.getPrevValue());
|
||||
|
||||
ItemPropertyChangeEvent<T> itemPropertyChangeEvent = new ItemPropertyChangeEvent<>(InstanceContainerImpl.this,
|
||||
(T) e.getItem(), e.getProperty(), e.getPrevValue(), e.getValue());
|
||||
|
||||
getEventRouter().fireEvent(ItemPropertyChangeListener.class, ItemPropertyChangeListener::itemPropertyChanged,
|
||||
itemPropertyChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model.impl;
|
||||
|
||||
import com.google.common.collect.ForwardingIterator;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class ObservableIterator<T> extends ForwardingIterator<T> {
|
||||
|
||||
private Iterator<T> delegate;
|
||||
private Runnable onCollectionChanged;
|
||||
|
||||
protected ObservableIterator(Iterator<T> delegate, Runnable onCollectionChanged) {
|
||||
this.delegate = delegate;
|
||||
this.onCollectionChanged = onCollectionChanged;
|
||||
}
|
||||
|
||||
protected void fireCollectionChanged() {
|
||||
if (onCollectionChanged != null)
|
||||
onCollectionChanged.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterator<T> delegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
super.remove();
|
||||
fireCollectionChanged();
|
||||
}
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model.impl;
|
||||
|
||||
import com.google.common.collect.ForwardingList;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("NullableProblems")
|
||||
public class ObservableList<T> extends ForwardingList<T> implements Serializable {
|
||||
|
||||
private List<T> delegate;
|
||||
private Runnable onCollectionChanged;
|
||||
|
||||
public ObservableList(List<T> delegate, Runnable onCollectionChanged) {
|
||||
this.delegate = delegate;
|
||||
this.onCollectionChanged = onCollectionChanged;
|
||||
}
|
||||
|
||||
private Object writeReplace() throws ObjectStreamException {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
protected void fireCollectionChanged() {
|
||||
if (onCollectionChanged != null)
|
||||
onCollectionChanged.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<T> delegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, T element) {
|
||||
super.add(index, element);
|
||||
fireCollectionChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(T element) {
|
||||
boolean changed = super.add(element);
|
||||
if (changed)
|
||||
fireCollectionChanged();
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends T> elements) {
|
||||
boolean changed = super.addAll(index, elements);
|
||||
if (changed)
|
||||
fireCollectionChanged();
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends T> collection) {
|
||||
boolean changed = super.addAll(collection);
|
||||
if (changed)
|
||||
fireCollectionChanged();
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T set(int index, T element) {
|
||||
T prev = super.set(index, element);
|
||||
if (prev != element) {
|
||||
fireCollectionChanged();
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T remove(int index) {
|
||||
T entity = super.remove(index);
|
||||
fireCollectionChanged();
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object object) {
|
||||
boolean changed = super.remove(object);
|
||||
if (changed)
|
||||
fireCollectionChanged();
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> collection) {
|
||||
boolean changed = super.removeAll(collection);
|
||||
if (changed)
|
||||
fireCollectionChanged();
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> collection) {
|
||||
boolean changed = super.retainAll(collection);
|
||||
if (changed)
|
||||
fireCollectionChanged();
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
boolean wasEmpty = isEmpty();
|
||||
super.clear();
|
||||
if (!wasEmpty)
|
||||
fireCollectionChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<T> listIterator() {
|
||||
return new ObservableListIterator<>(super.listIterator(), onCollectionChanged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<T> listIterator(int index) {
|
||||
return new ObservableListIterator<>(super.listIterator(index), onCollectionChanged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return new ObservableIterator<>(super.iterator(), onCollectionChanged);
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model.impl;
|
||||
|
||||
import com.google.common.collect.ForwardingListIterator;
|
||||
|
||||
import java.util.ListIterator;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("NullableProblems")
|
||||
class ObservableListIterator<T> extends ForwardingListIterator<T> {
|
||||
|
||||
private ListIterator<T> delegate;
|
||||
private Runnable onCollectionChanged;
|
||||
|
||||
protected ObservableListIterator(ListIterator<T> delegate, Runnable onCollectionChanged) {
|
||||
this.delegate = delegate;
|
||||
this.onCollectionChanged = onCollectionChanged;
|
||||
}
|
||||
|
||||
protected void fireCollectionChanged() {
|
||||
if (onCollectionChanged != null)
|
||||
onCollectionChanged.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ListIterator<T> delegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(T element) {
|
||||
super.add(element);
|
||||
fireCollectionChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(T element) {
|
||||
super.set(element);
|
||||
fireCollectionChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
super.remove();
|
||||
fireCollectionChanged();
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model.impl;
|
||||
|
||||
import com.google.common.collect.ForwardingSet;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("NullableProblems")
|
||||
public class ObservableSet<T> extends ForwardingSet<T> {
|
||||
|
||||
private final Set<T> delegate;
|
||||
private final Runnable onCollectionChanged;
|
||||
|
||||
public ObservableSet(Set<T> delegate, Runnable onCollectionChanged) {
|
||||
this.delegate = delegate;
|
||||
this.onCollectionChanged = onCollectionChanged;
|
||||
}
|
||||
|
||||
private Object writeReplace() throws ObjectStreamException {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
protected void fireCollectionChanged() {
|
||||
if (onCollectionChanged != null)
|
||||
onCollectionChanged.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<T> delegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(T element) {
|
||||
boolean changed = super.add(element);
|
||||
if (changed)
|
||||
fireCollectionChanged();
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> collection) {
|
||||
boolean changed = super.removeAll(collection);
|
||||
if (changed)
|
||||
fireCollectionChanged();
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object object) {
|
||||
boolean changed = super.remove(object);
|
||||
if (changed)
|
||||
fireCollectionChanged();
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends T> collection) {
|
||||
boolean changed = super.addAll(collection);
|
||||
if (changed)
|
||||
fireCollectionChanged();
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> collection) {
|
||||
boolean changed = super.retainAll(collection);
|
||||
if (changed)
|
||||
fireCollectionChanged();
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
boolean wasEmpty = isEmpty();
|
||||
super.clear();
|
||||
if (!wasEmpty)
|
||||
fireCollectionChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return new ObservableIterator<>(super.iterator(), onCollectionChanged);
|
||||
}
|
||||
}
|
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model.impl;
|
||||
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
import com.haulmont.cuba.core.global.DataManager;
|
||||
import com.haulmont.cuba.core.global.LoadContext;
|
||||
import com.haulmont.cuba.core.global.Metadata;
|
||||
import com.haulmont.cuba.core.global.View;
|
||||
import com.haulmont.cuba.gui.model.CollectionContainer;
|
||||
import com.haulmont.cuba.gui.model.CollectionLoader;
|
||||
import com.haulmont.cuba.gui.model.DataContext;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class StandardCollectionLoader<T extends Entity> implements CollectionLoader<T> {
|
||||
|
||||
private Metadata metadata;
|
||||
private DataManager dataManager;
|
||||
private DataContext dataContext;
|
||||
private CollectionContainer<T> container;
|
||||
private String query;
|
||||
private int maxResults;
|
||||
private boolean softDeletion;
|
||||
private boolean cacheable;
|
||||
private View view;
|
||||
private String viewName;
|
||||
|
||||
public StandardCollectionLoader(Metadata metadata, DataManager dataManager) {
|
||||
this.metadata = metadata;
|
||||
this.dataManager = dataManager;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DataContext getDataContext() {
|
||||
return dataContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataContext(DataContext dataContext) {
|
||||
this.dataContext = dataContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
if (container == null)
|
||||
throw new IllegalStateException("container is null");
|
||||
if (query == null)
|
||||
throw new IllegalStateException("query is null");
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
LoadContext<T> loadContext = LoadContext.create(container.getMetaClass().getJavaClass());
|
||||
|
||||
LoadContext.Query query = loadContext.setQueryString(this.query);
|
||||
|
||||
query.setCacheable(cacheable);
|
||||
|
||||
if (maxResults > 0)
|
||||
query.setMaxResults(maxResults);
|
||||
|
||||
loadContext.setSoftDeletion(softDeletion);
|
||||
|
||||
if (view == null && viewName != null) {
|
||||
this.view = metadata.getViewRepository().getView(container.getMetaClass(), viewName);
|
||||
}
|
||||
if (view != null) {
|
||||
loadContext.setView(view);
|
||||
}
|
||||
|
||||
List<T> list = dataManager.loadList(loadContext);
|
||||
|
||||
if (dataContext != null) {
|
||||
for (T entity : list) {
|
||||
dataContext.merge(entity);
|
||||
}
|
||||
}
|
||||
container.setItems(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollectionContainer<T> getContainer() {
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContainer(CollectionContainer<T> container) {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQuery(String query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxResults() {
|
||||
return maxResults;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxResults(int maxResults) {
|
||||
this.maxResults = maxResults;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSoftDeletion() {
|
||||
return softDeletion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSoftDeletion(boolean softDeletion) {
|
||||
this.softDeletion = softDeletion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCacheable() {
|
||||
return cacheable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCacheable(boolean cacheable) {
|
||||
this.cacheable = cacheable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView() {
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setView(View view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setView(String viewName) {
|
||||
if (this.view != null)
|
||||
throw new IllegalStateException("view is already set");
|
||||
this.viewName = viewName;
|
||||
}
|
||||
}
|
@ -0,0 +1,402 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model.impl;
|
||||
|
||||
import com.haulmont.bali.events.EventRouter;
|
||||
import com.haulmont.bali.util.Numbers;
|
||||
import com.haulmont.bali.util.Preconditions;
|
||||
import com.haulmont.chile.core.model.Instance;
|
||||
import com.haulmont.chile.core.model.MetaProperty;
|
||||
import com.haulmont.chile.core.model.impl.AbstractInstance;
|
||||
import com.haulmont.cuba.core.entity.*;
|
||||
import com.haulmont.cuba.core.global.*;
|
||||
import com.haulmont.cuba.gui.model.DataContext;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class StandardDataContext implements DataContext {
|
||||
|
||||
protected final Metadata metadata;
|
||||
protected final DataManager dataManager;
|
||||
protected final EntityStates entityStates;
|
||||
|
||||
protected EventRouter eventRouter = new EventRouter();
|
||||
|
||||
protected Map<Class<?>, Map<Object, Entity>> content = new HashMap<>();
|
||||
|
||||
protected Set<Entity> modifiedInstances = new HashSet<>();
|
||||
|
||||
protected Set<Entity> removedInstances = new HashSet<>();
|
||||
|
||||
protected ChangeListener changeListener = new ChangeListener();
|
||||
|
||||
protected boolean disableListeners;
|
||||
|
||||
protected StandardDataContext parentContext;
|
||||
|
||||
public StandardDataContext(Metadata metadata, DataManager dataManager, EntityStates entityStates) {
|
||||
this.metadata = metadata;
|
||||
this.dataManager = dataManager;
|
||||
this.entityStates = entityStates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataContext getParent() {
|
||||
return parentContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParent(DataContext parentContext) {
|
||||
Preconditions.checkNotNullArgument(parentContext, "parentContext is null");
|
||||
if (!(parentContext instanceof StandardDataContext))
|
||||
throw new IllegalArgumentException("Unsupported DataContext type: " + parentContext.getClass().getName());
|
||||
this.parentContext = (StandardDataContext) parentContext;
|
||||
|
||||
for (Entity entity : this.parentContext.getAll()) {
|
||||
Entity entityCopy;
|
||||
try {
|
||||
entityCopy = entity.getClass().newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException e) {
|
||||
throw new RuntimeException("Cannot create a copy of " + entity, e);
|
||||
}
|
||||
copyState(entity, entityCopy);
|
||||
merge(entityCopy);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
@Nullable
|
||||
public <T extends Entity<K>, K> T find(Class<T> entityClass, K entityId) {
|
||||
Map<Object, Entity> entityMap = content.get(entityClass);
|
||||
if (entityMap != null)
|
||||
return (T) entityMap.get(entityId);
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean contains(@Nullable Entity entity) {
|
||||
if (entity == null)
|
||||
return false;
|
||||
else
|
||||
return find(entity.getClass(), entity.getId()) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Entity> T merge(T entity) {
|
||||
return merge(entity, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T extends Entity> T merge(T entity, boolean deep) {
|
||||
Preconditions.checkNotNullArgument(entity, "entity is null");
|
||||
|
||||
disableListeners = true;
|
||||
T result;
|
||||
try {
|
||||
result = (T) internalMerge(entity);
|
||||
if (deep) {
|
||||
metadata.getTools().traverseAttributes(entity, new MergingAttributeVisitor());
|
||||
}
|
||||
} finally {
|
||||
disableListeners = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected Entity internalMerge(Entity entity) {
|
||||
Map<Object, Entity> entityMap = content.computeIfAbsent(entity.getClass(), aClass -> new HashMap<>());
|
||||
Entity managedInstance = entityMap.get(entity.getId());
|
||||
if (managedInstance != null) {
|
||||
if (managedInstance != entity) {
|
||||
copyState(entity, managedInstance);
|
||||
}
|
||||
return managedInstance;
|
||||
}
|
||||
entityMap.put(entity.getId(), entity);
|
||||
|
||||
entity.addPropertyChangeListener(changeListener);
|
||||
|
||||
if (entityStates.isNew(entity)) {
|
||||
modifiedInstances.add(entity);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* (1) src.new -> dst.new : copy all non-null - should not happen (happens in setParent?)
|
||||
* (2) src.new -> dst.det : do nothing - should not happen
|
||||
* (3) src.det -> dst.new : copy all loaded, make detached - normal situation after commit
|
||||
* (4) src.det -> dst.det : if src.version >= dst.version, copy all loaded - normal situation after commit (and in setParent?)
|
||||
* if src.version < dst.version, do nothing - should not happen
|
||||
*/
|
||||
protected void copyState(Entity srcEntity, Entity dstEntity) {
|
||||
boolean srcNew = entityStates.isNew(srcEntity);
|
||||
boolean dstNew = entityStates.isNew(dstEntity);
|
||||
if (srcNew && !dstNew) {
|
||||
return;
|
||||
}
|
||||
if (!srcNew && !dstNew) {
|
||||
if (srcEntity instanceof Versioned) {
|
||||
int srcVer = Numbers.nullToZero(((Versioned) srcEntity).getVersion());
|
||||
int dstVer = Numbers.nullToZero(((Versioned) dstEntity).getVersion());
|
||||
if (srcVer < dstVer) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (MetaProperty property : metadata.getClassNN(srcEntity.getClass()).getProperties()) {
|
||||
String name = property.getName();
|
||||
if ((!property.getRange().isClass() || property.getRange().getCardinality().isMany()) // local and collections
|
||||
&& !property.isReadOnly() // read-write
|
||||
&& (srcNew || entityStates.isLoaded(srcEntity, name))) { // loaded
|
||||
AnnotatedElement annotatedElement = property.getAnnotatedElement();
|
||||
if (annotatedElement instanceof Field) {
|
||||
Field field = (Field) annotatedElement;
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
Object value = field.get(srcEntity);
|
||||
if (srcNew || property.getRange().getCardinality().isMany()) {
|
||||
if (value != null)
|
||||
copyValue(dstEntity, field, value);
|
||||
} else {
|
||||
copyValue(dstEntity, field, value);
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Error copying state of attribute " + name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!srcNew && dstNew) {
|
||||
if (dstEntity instanceof BaseGenericIdEntity) {
|
||||
BaseEntityInternalAccess.setNew((BaseGenericIdEntity) dstEntity, false);
|
||||
BaseEntityInternalAccess.setDetached((BaseGenericIdEntity) dstEntity, true);
|
||||
} else if (dstEntity instanceof AbstractNotPersistentEntity) {
|
||||
BaseEntityInternalAccess.setNew((AbstractNotPersistentEntity) dstEntity, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void copyValue(Object dstObject, Field field, Object srcValue) throws IllegalAccessException {
|
||||
if (srcValue instanceof Collection) {
|
||||
Collection srcCollection = (Collection) srcValue;
|
||||
Collection dstCollection = (Collection) field.get(dstObject);
|
||||
Collection newDstCollection = srcValue instanceof List ? new ArrayList() : new LinkedHashSet();
|
||||
if (dstCollection == null) {
|
||||
newDstCollection.addAll(srcCollection);
|
||||
} else {
|
||||
newDstCollection.addAll(dstCollection);
|
||||
for (Object o : srcCollection) {
|
||||
if (!newDstCollection.contains(o))
|
||||
newDstCollection.add(o);
|
||||
}
|
||||
}
|
||||
field.set(dstObject, newDstCollection);
|
||||
} else {
|
||||
field.set(dstObject, srcValue);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void remove(Entity entity) {
|
||||
Preconditions.checkNotNullArgument(entity, "entity is null");
|
||||
if (contains(entity)) {
|
||||
modifiedInstances.remove(entity);
|
||||
removedInstances.add(entity);
|
||||
entity.removePropertyChangeListener(changeListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evict(Entity entity) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasChanges() {
|
||||
return !(modifiedInstances.isEmpty() && removedInstances.isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
PreCommitEvent preCommitEvent = new PreCommitEvent(this, modifiedInstances, removedInstances);
|
||||
eventRouter.fireEvent(PreCommitListener.class, PreCommitListener::preCommit, preCommitEvent);
|
||||
if (preCommitEvent.isCommitPrevented())
|
||||
return;
|
||||
|
||||
Set<Entity> committed = performCommit();
|
||||
|
||||
PostCommitEvent postCommitEvent = new PostCommitEvent(this, committed);
|
||||
eventRouter.fireEvent(PostCommitListener.class, PostCommitListener::postCommit, postCommitEvent);
|
||||
|
||||
mergeCommitted(committed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPreCommitListener(PreCommitListener listener) {
|
||||
eventRouter.addListener(PreCommitListener.class, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePreCommitListener(PreCommitListener listener) {
|
||||
eventRouter.removeListener(PreCommitListener.class, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPostCommitListener(PostCommitListener listener) {
|
||||
eventRouter.addListener(PostCommitListener.class, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePostCommitListener(PostCommitListener listener) {
|
||||
eventRouter.removeListener(PostCommitListener.class, listener);
|
||||
}
|
||||
|
||||
protected Set<Entity> performCommit() {
|
||||
if (!hasChanges())
|
||||
return Collections.emptySet();
|
||||
|
||||
if (parentContext == null) {
|
||||
return commitToDataManager();
|
||||
} else {
|
||||
return commitToParentContext();
|
||||
}
|
||||
}
|
||||
|
||||
protected Set<Entity> commitToDataManager() {
|
||||
CommitContext commitContext = new CommitContext(modifiedInstances, removedInstances);
|
||||
return dataManager.commit(commitContext);
|
||||
}
|
||||
|
||||
protected Set<Entity> commitToParentContext() {
|
||||
HashSet<Entity> committedEntities = new HashSet<>();
|
||||
for (Entity entity : modifiedInstances) {
|
||||
committedEntities.add(parentContext.merge(entity));
|
||||
}
|
||||
for (Entity entity : removedInstances) {
|
||||
parentContext.remove(entity);
|
||||
}
|
||||
return committedEntities;
|
||||
}
|
||||
|
||||
protected void mergeCommitted(Set<Entity> committed) {
|
||||
for (Entity entity : committed) {
|
||||
if (contains(entity)) {
|
||||
merge(entity, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Collection<Entity> getAll() {
|
||||
List<Entity> resultList = new ArrayList<>();
|
||||
for (Map<Object, Entity> entityMap : content.values()) {
|
||||
resultList.addAll(entityMap.values());
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
|
||||
protected class ChangeListener implements Instance.PropertyChangeListener {
|
||||
@Override
|
||||
public void propertyChanged(Instance.PropertyChangeEvent e) {
|
||||
if (!disableListeners) {
|
||||
modifiedInstances.add((Entity) e.getItem());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected class MergingAttributeVisitor implements EntityAttributeVisitor {
|
||||
|
||||
@Override
|
||||
public boolean skip(MetaProperty property) {
|
||||
return !property.getRange().isClass() || property.isReadOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Entity e, MetaProperty property) {
|
||||
if (!entityStates.isLoaded(e, property.getName()))
|
||||
return;
|
||||
Object value = e.getValue(property.getName());
|
||||
if (value != null) {
|
||||
if (value instanceof Collection) {
|
||||
if (value instanceof List) {
|
||||
mergeList((List) value, e, property.getName());
|
||||
} else if (value instanceof Set) {
|
||||
mergeSet((Set) value, e, property.getName());
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Unsupported collection type: " + value.getClass().getName());
|
||||
}
|
||||
} else {
|
||||
mergeInstance((Entity) value, e, property.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void mergeList(List list, Entity owningEntity, String propertyName) {
|
||||
for (ListIterator<Entity> it = list.listIterator(); it.hasNext();) {
|
||||
Entity entity = it.next();
|
||||
Entity managed = internalMerge(entity);
|
||||
if (managed != entity) {
|
||||
it.set(managed);
|
||||
}
|
||||
}
|
||||
if (!(list instanceof ObservableList)) {
|
||||
ObservableList observableList = new ObservableList<>(list, () -> modified(owningEntity));
|
||||
((AbstractInstance) owningEntity).setValue(propertyName, observableList, false);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void mergeSet(Set set, Entity owningEntity, String propertyName) {
|
||||
for (Entity entity : new ArrayList<Entity>(set)) {
|
||||
Entity managed = internalMerge(entity);
|
||||
if (managed != entity) {
|
||||
set.remove(entity);
|
||||
set.add(managed);
|
||||
}
|
||||
}
|
||||
if (!(set instanceof ObservableList)) {
|
||||
ObservableSet observableSet = new ObservableSet<>(set, () -> modified(owningEntity));
|
||||
((AbstractInstance) owningEntity).setValue(propertyName, observableSet, false);
|
||||
}
|
||||
}
|
||||
|
||||
protected void mergeInstance(Entity entity, Entity owningEntity, String propertyName) {
|
||||
Entity managed = internalMerge(entity);
|
||||
if (managed != entity) {
|
||||
((AbstractInstance) owningEntity).setValue(propertyName, managed, false);
|
||||
}
|
||||
}
|
||||
|
||||
protected void modified(Entity entity) {
|
||||
if (!disableListeners) {
|
||||
modifiedInstances.add(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model.impl;
|
||||
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
import com.haulmont.cuba.core.global.DataManager;
|
||||
import com.haulmont.cuba.core.global.LoadContext;
|
||||
import com.haulmont.cuba.core.global.Metadata;
|
||||
import com.haulmont.cuba.core.global.View;
|
||||
import com.haulmont.cuba.gui.model.DataContext;
|
||||
import com.haulmont.cuba.gui.model.InstanceContainer;
|
||||
import com.haulmont.cuba.gui.model.InstanceLoader;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class StandardInstanceLoader<T extends Entity<K>, K> implements InstanceLoader<T, K> {
|
||||
|
||||
private Metadata metadata;
|
||||
private DataManager dataManager;
|
||||
private DataContext dataContext;
|
||||
private InstanceContainer<T> container;
|
||||
private K entityId;
|
||||
private boolean softDeletion;
|
||||
private View view;
|
||||
private String viewName;
|
||||
|
||||
public StandardInstanceLoader(Metadata metadata, DataManager dataManager) {
|
||||
this.metadata = metadata;
|
||||
this.dataManager = dataManager;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DataContext getDataContext() {
|
||||
return dataContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataContext(DataContext dataContext) {
|
||||
this.dataContext = dataContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
if (container == null)
|
||||
throw new IllegalStateException("container is null");
|
||||
if (entityId == null)
|
||||
throw new IllegalStateException("entityId is null");
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
LoadContext<T> loadContext = LoadContext.create(container.getMetaClass().getJavaClass());
|
||||
|
||||
loadContext.setId(entityId);
|
||||
|
||||
if (view == null && viewName != null) {
|
||||
this.view = metadata.getViewRepository().getView(container.getMetaClass(), viewName);
|
||||
}
|
||||
if (view != null) {
|
||||
loadContext.setView(view);
|
||||
}
|
||||
|
||||
T entity = dataManager.load(loadContext);
|
||||
|
||||
if (dataContext != null) {
|
||||
dataContext.merge(entity);
|
||||
}
|
||||
container.setItem(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstanceContainer<T> getContainer() {
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContainer(InstanceContainer<T> container) {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEntityId(K entityId) {
|
||||
this.entityId = entityId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSoftDeletion() {
|
||||
return softDeletion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSoftDeletion(boolean softDeletion) {
|
||||
this.softDeletion = softDeletion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView() {
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setView(View view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setView(String viewName) {
|
||||
if (this.view != null)
|
||||
throw new IllegalStateException("view is already set");
|
||||
this.viewName = viewName;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model.impl;
|
||||
|
||||
import com.haulmont.cuba.gui.model.CollectionContainer;
|
||||
import com.haulmont.cuba.gui.model.CollectionContainer.CollectionChangeListener;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class WeakCollectionChangeListener implements CollectionChangeListener {
|
||||
|
||||
private final WeakReference<CollectionChangeListener> reference;
|
||||
private final CollectionContainer collectionDatasource;
|
||||
|
||||
public WeakCollectionChangeListener(CollectionContainer collectionDatasource,
|
||||
CollectionChangeListener collectionChangeListener) {
|
||||
this.collectionDatasource = collectionDatasource;
|
||||
this.reference = new WeakReference<>(collectionChangeListener);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void collectionChanged(CollectionContainer.CollectionChangeEvent e) {
|
||||
CollectionChangeListener collectionChangeListener = reference.get();
|
||||
if (collectionChangeListener != null) {
|
||||
collectionChangeListener.collectionChanged(e);
|
||||
} else {
|
||||
collectionDatasource.removeCollectionChangeListener(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model.impl;
|
||||
|
||||
import com.haulmont.cuba.gui.model.CollectionContainer;
|
||||
import com.haulmont.cuba.gui.model.InstanceContainer;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class WeakContainerListenerAdapter implements InstanceContainer.ItemPropertyChangeListener, /*EntityContainer.StateChangeListener,*/ CollectionContainer.CollectionChangeListener {
|
||||
|
||||
private final CollectionContainer collectionDatasource;
|
||||
private final WeakReference<InstanceContainer.ItemPropertyChangeListener> itemPropertyChangeListenerReference;
|
||||
// private final WeakReference<EntityContainer.StateChangeListener> stateChangeListenerReference;
|
||||
private final WeakReference<CollectionContainer.CollectionChangeListener> collectionChangeListenerReference;
|
||||
|
||||
public WeakContainerListenerAdapter(CollectionContainer datasource, InstanceContainer.ItemPropertyChangeListener itemPropertyChangeListener,
|
||||
/*EntityContainer.StateChangeListener stateChangeListener,*/ CollectionContainer.CollectionChangeListener collectionChangeListener) {
|
||||
this.collectionDatasource = datasource;
|
||||
|
||||
this.itemPropertyChangeListenerReference = new WeakReference<>(itemPropertyChangeListener);
|
||||
// this.stateChangeListenerReference = new WeakReference<>(stateChangeListener);
|
||||
this.collectionChangeListenerReference = new WeakReference<>(collectionChangeListener);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void collectionChanged(CollectionContainer.CollectionChangeEvent e) {
|
||||
CollectionContainer.CollectionChangeListener collectionChangeListener = collectionChangeListenerReference.get();
|
||||
if (collectionChangeListener != null) {
|
||||
collectionChangeListener.collectionChanged(e);
|
||||
} else {
|
||||
collectionDatasource.removeCollectionChangeListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void itemPropertyChanged(InstanceContainer.ItemPropertyChangeEvent e) {
|
||||
InstanceContainer.ItemPropertyChangeListener itemPropertyChangeListener = itemPropertyChangeListenerReference.get();
|
||||
if (itemPropertyChangeListener != null) {
|
||||
itemPropertyChangeListener.itemPropertyChanged(e);
|
||||
} else {
|
||||
collectionDatasource.removeItemPropertyChangeListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
// @SuppressWarnings("unchecked")
|
||||
// @Override
|
||||
// public void stateChanged(EntityContainer.StateChangeEvent e) {
|
||||
// EntityContainer.StateChangeListener stateChangeListener = stateChangeListenerReference.get();
|
||||
// if (stateChangeListener != null) {
|
||||
// stateChangeListener.stateChanged(e);
|
||||
// } else {
|
||||
// collectionDatasource.removeStateChangeListener(this);
|
||||
// }
|
||||
// }
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model.impl;
|
||||
|
||||
import com.haulmont.cuba.gui.model.InstanceContainer;
|
||||
import com.haulmont.cuba.gui.model.InstanceContainer.ItemChangeListener;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class WeakItemChangeListener implements ItemChangeListener {
|
||||
|
||||
private final InstanceContainer container;
|
||||
private final WeakReference<ItemChangeListener> reference;
|
||||
|
||||
public WeakItemChangeListener(InstanceContainer datasource, ItemChangeListener itemChangeListener) {
|
||||
this.container = datasource;
|
||||
this.reference = new WeakReference<>(itemChangeListener);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void itemChanged(InstanceContainer.ItemChangeEvent e) {
|
||||
ItemChangeListener itemChangeListener = reference.get();
|
||||
if (itemChangeListener != null) {
|
||||
itemChangeListener.itemChanged(e);
|
||||
} else {
|
||||
container.removeItemChangeListener(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model.impl;
|
||||
|
||||
import com.haulmont.cuba.gui.model.InstanceContainer;
|
||||
import com.haulmont.cuba.gui.model.InstanceContainer.ItemPropertyChangeEvent;
|
||||
import com.haulmont.cuba.gui.model.InstanceContainer.ItemPropertyChangeListener;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class WeakItemPropertyChangeListener implements ItemPropertyChangeListener {
|
||||
|
||||
private final InstanceContainer datasource;
|
||||
private final WeakReference<ItemPropertyChangeListener> reference;
|
||||
|
||||
public WeakItemPropertyChangeListener(InstanceContainer datasource,
|
||||
ItemPropertyChangeListener itemPropertyChangeListener) {
|
||||
this.datasource = datasource;
|
||||
this.reference = new WeakReference<>(itemPropertyChangeListener);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void itemPropertyChanged(ItemPropertyChangeEvent e) {
|
||||
ItemPropertyChangeListener itemPropertyChangeListener = reference.get();
|
||||
if (itemPropertyChangeListener != null) {
|
||||
itemPropertyChangeListener.itemPropertyChanged(e);
|
||||
} else {
|
||||
datasource.removeItemPropertyChangeListener(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model.impl;
|
||||
|
||||
import com.haulmont.cuba.core.sys.serialization.Serialization;
|
||||
import com.haulmont.cuba.core.sys.serialization.StandardSerialization;
|
||||
import com.haulmont.cuba.security.entity.Role;
|
||||
import com.haulmont.cuba.security.entity.User;
|
||||
import com.haulmont.cuba.security.entity.UserRole;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ObservableListTest {
|
||||
|
||||
@Test
|
||||
public void testSerialization() throws Exception {
|
||||
Serialization serialization = new StandardSerialization();
|
||||
|
||||
User user1 = new User();
|
||||
user1.setLogin("u1");
|
||||
user1.setName("User 1");
|
||||
user1.setUserRoles(new ObservableList<>(new ArrayList<>(), () -> {}));
|
||||
|
||||
Role role1 = new Role();
|
||||
role1.setName("Role 1");
|
||||
|
||||
UserRole user1Role1 = new UserRole();
|
||||
user1Role1.setUser(user1);
|
||||
user1Role1.setRole(role1);
|
||||
|
||||
user1.getUserRoles().add(user1Role1);
|
||||
|
||||
User deserializedUser = (User) serialization.deserialize(serialization.serialize(user1));
|
||||
assertTrue(deserializedUser.getUserRoles() instanceof ArrayList);
|
||||
}
|
||||
}
|
@ -0,0 +1,438 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.gui.model.impl;
|
||||
|
||||
import com.haulmont.cuba.client.testsupport.CubaClientTestCase;
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
import com.haulmont.cuba.core.global.AppBeans;
|
||||
import com.haulmont.cuba.core.global.DataManager;
|
||||
import com.haulmont.cuba.core.global.EntityStates;
|
||||
import com.haulmont.cuba.core.global.Metadata;
|
||||
import com.haulmont.cuba.security.entity.Role;
|
||||
import com.haulmont.cuba.security.entity.User;
|
||||
import com.haulmont.cuba.security.entity.UserRole;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class StandardDataContextTest extends CubaClientTestCase {
|
||||
|
||||
private Metadata metadata;
|
||||
private DataManager dataManager;
|
||||
private EntityStates entityStates;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
addEntityPackage("com.haulmont.cuba");
|
||||
setupInfrastructure();
|
||||
|
||||
metadata = AppBeans.get(Metadata.class);
|
||||
dataManager = AppBeans.get(DataManager.class);
|
||||
entityStates = AppBeans.get(EntityStates.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMerge() throws Exception {
|
||||
StandardDataContext context = new StandardDataContext(metadata, dataManager, entityStates);
|
||||
|
||||
User user1 = new User();
|
||||
user1.setLogin("u1");
|
||||
user1.setName("User 1");
|
||||
|
||||
User mergedUser1 = context.merge(user1);
|
||||
assertTrue(mergedUser1 == context.find(User.class, user1.getId()));
|
||||
|
||||
User user11 = new User();
|
||||
user11.setLogin("u11");
|
||||
user11.setName("User 11");
|
||||
user11.setId(user1.getId());
|
||||
|
||||
User mergedUser11 = context.merge(user11);
|
||||
assertTrue(mergedUser11 == mergedUser1);
|
||||
assertTrue(mergedUser11 != user11);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeGraph() throws Exception {
|
||||
StandardDataContext context = new StandardDataContext(metadata, dataManager, entityStates);
|
||||
|
||||
// an object being merged
|
||||
User user1 = new User();
|
||||
entityStates.makeDetached(user1);
|
||||
user1.setLogin("u1");
|
||||
user1.setName("User 1");
|
||||
user1.setUserRoles(new ArrayList<>());
|
||||
|
||||
Role role1 = new Role();
|
||||
entityStates.makeDetached(role1);
|
||||
role1.setName("Role 1");
|
||||
|
||||
UserRole user1Role1 = new UserRole();
|
||||
entityStates.makeDetached(user1Role1);
|
||||
user1Role1.setUser(user1);
|
||||
user1Role1.setRole(role1);
|
||||
|
||||
user1.getUserRoles().add(user1Role1);
|
||||
|
||||
// somewhere in the object graph another object with the same id
|
||||
User user11 = new User();
|
||||
entityStates.makeDetached(user11);
|
||||
user11.setLogin("u11");
|
||||
user11.setName("User 11");
|
||||
user11.setId(user1.getId());
|
||||
|
||||
UserRole user11Role1 = new UserRole();
|
||||
entityStates.makeDetached(user11Role1);
|
||||
user11Role1.setUser(user11);
|
||||
user11Role1.setRole(role1);
|
||||
|
||||
user1.getUserRoles().add(user11Role1);
|
||||
|
||||
User mergedUser1 = context.merge(user1);
|
||||
|
||||
// instance of the first object
|
||||
assertTrue(mergedUser1 == context.find(User.class, user1.getId()));
|
||||
|
||||
// local attributes of the second object
|
||||
assertEquals("u11", mergedUser1.getLogin());
|
||||
assertEquals("User 11", mergedUser1.getName());
|
||||
|
||||
assertTrue(user1Role1 == context.find(UserRole.class, user1Role1.getId()));
|
||||
assertTrue(user11Role1 == context.find(UserRole.class, user11Role1.getId()));
|
||||
assertTrue(role1 == context.find(Role.class, role1.getId()));
|
||||
|
||||
assertTrue(mergedUser1.getUserRoles().get(1).getUser() == mergedUser1);
|
||||
|
||||
assertFalse(context.hasChanges());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeGraph_firstObjectWithNullCollection() throws Exception {
|
||||
StandardDataContext context = new StandardDataContext(metadata, dataManager, entityStates);
|
||||
|
||||
User user1 = new User();
|
||||
user1.setLogin("u1");
|
||||
user1.setName("User 1");
|
||||
|
||||
Role role1 = new Role();
|
||||
role1.setName("Role 1");
|
||||
|
||||
User user11 = new User();
|
||||
user11.setLogin("u11");
|
||||
user11.setName("User 11");
|
||||
user11.setId(user1.getId());
|
||||
|
||||
UserRole user11Role1 = new UserRole();
|
||||
user11Role1.setUser(user11);
|
||||
user11Role1.setRole(role1);
|
||||
user11.setUserRoles(new ArrayList<>());
|
||||
user11.getUserRoles().add(user11Role1);
|
||||
|
||||
User mergedUser1 = context.merge(user1);
|
||||
|
||||
User mergedUser11 = context.merge(user11);
|
||||
|
||||
assertTrue(mergedUser11 == mergedUser1);
|
||||
|
||||
// instance of the first object
|
||||
assertTrue(mergedUser1 == context.find(User.class, user1.getId()));
|
||||
|
||||
// local attributes of the second object
|
||||
assertEquals("u11", mergedUser1.getLogin());
|
||||
assertEquals("User 11", mergedUser1.getName());
|
||||
|
||||
// collection of the second object
|
||||
assertNotNull(mergedUser1.getUserRoles());
|
||||
assertTrue(mergedUser1.getUserRoles().get(0).getUser() == mergedUser1);
|
||||
|
||||
assertTrue(user11Role1 == context.find(UserRole.class, user11Role1.getId()));
|
||||
assertTrue(role1 == context.find(Role.class, role1.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergedNew() throws Exception {
|
||||
StandardDataContext context = new StandardDataContext(metadata, dataManager, entityStates) {
|
||||
@Override
|
||||
protected Set<Entity> performCommit() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
};
|
||||
|
||||
User user1 = new User();
|
||||
user1.setLogin("u1");
|
||||
user1.setName("User 1");
|
||||
user1.setUserRoles(new ArrayList<>());
|
||||
|
||||
Role role1 = new Role();
|
||||
role1.setName("Role 1");
|
||||
|
||||
Role role2 = new Role();
|
||||
role1.setName("Role 2");
|
||||
|
||||
UserRole user1Role1 = new UserRole();
|
||||
user1Role1.setUser(user1);
|
||||
user1Role1.setRole(role1);
|
||||
|
||||
user1.getUserRoles().add(user1Role1);
|
||||
|
||||
UserRole user1Role2 = new UserRole();
|
||||
user1Role2.setUser(user1);
|
||||
user1Role2.setRole(role2);
|
||||
|
||||
user1.getUserRoles().add(user1Role2);
|
||||
|
||||
context.merge(user1);
|
||||
|
||||
context.addPreCommitListener(e -> {
|
||||
assertEquals(5, e.getModifiedInstances().size());
|
||||
assertTrue(e.getModifiedInstances().contains(user1));
|
||||
assertTrue(e.getModifiedInstances().contains(role1));
|
||||
assertTrue(e.getModifiedInstances().contains(role2));
|
||||
assertTrue(e.getModifiedInstances().contains(user1Role1));
|
||||
assertTrue(e.getModifiedInstances().contains(user1Role2));
|
||||
|
||||
assertEquals(0, e.getRemovedInstances().size());
|
||||
});
|
||||
|
||||
context.commit();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testModified() throws Exception {
|
||||
StandardDataContext context = new StandardDataContext(metadata, dataManager, entityStates) {
|
||||
@Override
|
||||
protected Set<Entity> performCommit() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
};
|
||||
|
||||
User user1 = new User();
|
||||
user1.setLogin("u1");
|
||||
user1.setName("User 1");
|
||||
user1.setUserRoles(new ArrayList<>());
|
||||
entityStates.makeDetached(user1);
|
||||
|
||||
Role role1 = new Role();
|
||||
role1.setName("Role 1");
|
||||
entityStates.makeDetached(role1);
|
||||
|
||||
Role role2 = new Role();
|
||||
role1.setName("Role 2");
|
||||
entityStates.makeDetached(role2);
|
||||
|
||||
UserRole user1Role1 = new UserRole();
|
||||
user1Role1.setUser(user1);
|
||||
user1Role1.setRole(role1);
|
||||
entityStates.makeDetached(user1Role1);
|
||||
|
||||
user1.getUserRoles().add(user1Role1);
|
||||
|
||||
UserRole user1Role2 = new UserRole();
|
||||
user1Role2.setUser(user1);
|
||||
user1Role2.setRole(role2);
|
||||
entityStates.makeDetached(user1Role2);
|
||||
|
||||
user1.getUserRoles().add(user1Role2);
|
||||
|
||||
context.merge(user1);
|
||||
|
||||
role1.setName("Role 1 modified");
|
||||
|
||||
user1.getUserRoles().remove(user1Role2);
|
||||
context.remove(user1Role2);
|
||||
|
||||
context.addPreCommitListener(e -> {
|
||||
assertEquals(2, e.getModifiedInstances().size());
|
||||
assertTrue(e.getModifiedInstances().contains(role1));
|
||||
assertTrue(e.getModifiedInstances().contains(user1));
|
||||
|
||||
assertEquals(1, e.getRemovedInstances().size());
|
||||
assertTrue(e.getRemovedInstances().contains(user1Role2));
|
||||
});
|
||||
|
||||
context.commit();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyState() throws Exception {
|
||||
StandardDataContext context = new StandardDataContext(metadata, dataManager, entityStates);
|
||||
|
||||
User src, dst;
|
||||
// (1) src.new > dst.new : copy all non-null
|
||||
|
||||
src = new User();
|
||||
src.setLogin("u-src");
|
||||
|
||||
dst = new User();
|
||||
dst.setId(src.getId());
|
||||
dst.setLogin("u-dst");
|
||||
dst.setName("Dest User");
|
||||
dst.setUserRoles(new ArrayList<>());
|
||||
|
||||
context.copyState(src, dst);
|
||||
|
||||
assertTrue(entityStates.isNew(dst));
|
||||
assertNull(dst.getVersion());
|
||||
assertEquals("u-src", dst.getLogin());
|
||||
assertEquals("Dest User", dst.getName());
|
||||
assertNotNull(dst.getUserRoles());
|
||||
|
||||
// (2) src.new -> dst.det : do nothing
|
||||
|
||||
src = new User();
|
||||
src.setLogin("u-src");
|
||||
|
||||
dst = new User();
|
||||
dst.setId(src.getId());
|
||||
dst.setVersion(1);
|
||||
dst.setLogin("u-dst");
|
||||
dst.setName("Dest User");
|
||||
dst.setUserRoles(new ArrayList<>());
|
||||
entityStates.makeDetached(dst);
|
||||
|
||||
context.copyState(src, dst);
|
||||
|
||||
assertTrue(entityStates.isDetached(dst));
|
||||
assertNotNull(dst.getVersion());
|
||||
assertEquals("u-dst", dst.getLogin());
|
||||
assertEquals("Dest User", dst.getName());
|
||||
assertNotNull(dst.getUserRoles());
|
||||
|
||||
// (3) src.det -> dst.new : copy all loaded, make detached
|
||||
|
||||
src = new User();
|
||||
src.setVersion(1);
|
||||
src.setLogin("u-src");
|
||||
entityStates.makeDetached(src);
|
||||
|
||||
dst = new User();
|
||||
dst.setId(src.getId());
|
||||
dst.setLogin("u-dst");
|
||||
dst.setName("Dest User");
|
||||
dst.setUserRoles(new ArrayList<>());
|
||||
|
||||
context.copyState(src, dst);
|
||||
|
||||
assertTrue(entityStates.isDetached(dst));
|
||||
assertEquals(Integer.valueOf(1), dst.getVersion());
|
||||
assertEquals("u-src", dst.getLogin());
|
||||
assertNull(dst.getName());
|
||||
assertNotNull(dst.getUserRoles());
|
||||
|
||||
// (4) src.det -> dst.det : if src.version >= dst.version, copy all loaded
|
||||
|
||||
src = new User();
|
||||
src.setVersion(2);
|
||||
src.setLogin("u-src");
|
||||
entityStates.makeDetached(src);
|
||||
|
||||
dst = new User();
|
||||
dst.setId(src.getId());
|
||||
dst.setVersion(1);
|
||||
dst.setLogin("u-dst");
|
||||
dst.setName("Dest User");
|
||||
dst.setUserRoles(new ArrayList<>());
|
||||
entityStates.makeDetached(dst);
|
||||
|
||||
context.copyState(src, dst);
|
||||
|
||||
assertTrue(entityStates.isDetached(dst));
|
||||
assertEquals(Integer.valueOf(2), dst.getVersion());
|
||||
assertEquals("u-src", dst.getLogin());
|
||||
assertNull(dst.getName());
|
||||
assertNotNull(dst.getUserRoles());
|
||||
|
||||
// (4) src.det -> dst.det : if src.version < dst.version, do nothing
|
||||
|
||||
src = new User();
|
||||
src.setVersion(1);
|
||||
src.setLogin("u-src");
|
||||
entityStates.makeDetached(src);
|
||||
|
||||
dst = new User();
|
||||
dst.setId(src.getId());
|
||||
dst.setVersion(2);
|
||||
dst.setLogin("u-dst");
|
||||
dst.setName("Dest User");
|
||||
dst.setUserRoles(new ArrayList<>());
|
||||
entityStates.makeDetached(dst);
|
||||
|
||||
context.copyState(src, dst);
|
||||
|
||||
assertTrue(entityStates.isDetached(dst));
|
||||
assertEquals(Integer.valueOf(2), dst.getVersion());
|
||||
assertEquals("u-dst", dst.getLogin());
|
||||
assertEquals("Dest User", dst.getName());
|
||||
assertNotNull(dst.getUserRoles());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParentContext() throws Exception {
|
||||
StandardDataContext context = new StandardDataContext(metadata, dataManager, entityStates) {
|
||||
@Override
|
||||
protected Set<Entity> commitToDataManager() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
};
|
||||
|
||||
User user1 = new User();
|
||||
user1.setLogin("u1");
|
||||
user1.setName("User 1");
|
||||
user1.setUserRoles(new ArrayList<>());
|
||||
|
||||
context.merge(user1);
|
||||
|
||||
StandardDataContext childContext = new StandardDataContext(metadata, dataManager, entityStates);
|
||||
|
||||
childContext.setParent(context);
|
||||
|
||||
User user1InChild = childContext.find(User.class, user1.getId());
|
||||
assertNotNull(user1InChild);
|
||||
assertTrue(user1InChild != user1);
|
||||
|
||||
UserRole user1Role1 = new UserRole();
|
||||
user1Role1.setUser(user1InChild);
|
||||
assertNotNull(user1InChild.getUserRoles());
|
||||
user1InChild.getUserRoles().add(user1Role1);
|
||||
|
||||
childContext.merge(user1Role1);
|
||||
|
||||
childContext.addPreCommitListener(e -> {
|
||||
assertEquals(2, e.getModifiedInstances().size());
|
||||
assertTrue(e.getModifiedInstances().stream().anyMatch(entity -> entity == user1InChild));
|
||||
assertTrue(e.getModifiedInstances().stream().anyMatch(entity -> entity == user1Role1));
|
||||
});
|
||||
|
||||
childContext.commit();
|
||||
|
||||
assertNotNull(user1.getUserRoles());
|
||||
assertEquals(1, user1.getUserRoles().size());
|
||||
|
||||
context.addPreCommitListener(e -> {
|
||||
assertEquals(2, e.getModifiedInstances().size());
|
||||
assertTrue(e.getModifiedInstances().stream().anyMatch(entity -> entity == user1));
|
||||
assertTrue(e.getModifiedInstances().stream().anyMatch(entity -> entity == user1Role1));
|
||||
});
|
||||
|
||||
context.commit();
|
||||
}
|
||||
}
|
@ -508,7 +508,7 @@ public class CubaFoldersPane extends VerticalLayout {
|
||||
protected Component createSearchFoldersPane() {
|
||||
searchFoldersTree = new CubaTree();
|
||||
searchFoldersTree.setSelectable(true);
|
||||
searchFoldersLabel.setCubaId("searchFoldersTree");
|
||||
searchFoldersTree.setCubaId("searchFoldersTree");
|
||||
searchFoldersTree.setItemStyleGenerator(new FolderTreeStyleProvider());
|
||||
searchFoldersTree.addShortcutListener(new ShortcutListener("applySearchFolder", ShortcutAction.KeyCode.ENTER, (int[]) null) {
|
||||
@Override
|
||||
|
@ -28,36 +28,31 @@ import com.haulmont.cuba.core.global.AppBeans;
|
||||
import com.haulmont.cuba.core.global.BeanValidation;
|
||||
import com.haulmont.cuba.core.global.MessageTools;
|
||||
import com.haulmont.cuba.core.global.MetadataTools;
|
||||
import com.haulmont.cuba.gui.components.Component;
|
||||
import com.haulmont.cuba.gui.components.Field;
|
||||
import com.haulmont.cuba.gui.components.RequiredValueMissingException;
|
||||
import com.haulmont.cuba.gui.components.ValidationException;
|
||||
import com.haulmont.cuba.gui.components.ValidationFailedException;
|
||||
import com.haulmont.cuba.gui.components.*;
|
||||
import com.haulmont.cuba.gui.components.compatibility.ComponentValueListenerWrapper;
|
||||
import com.haulmont.cuba.gui.components.validators.BeanValidator;
|
||||
import com.haulmont.cuba.gui.data.Datasource;
|
||||
import com.haulmont.cuba.gui.data.ValueListener;
|
||||
import com.haulmont.cuba.gui.data.impl.WeakItemChangeListener;
|
||||
import com.haulmont.cuba.gui.model.InstanceContainer;
|
||||
import com.haulmont.cuba.web.gui.data.ItemWrapper;
|
||||
import com.haulmont.cuba.web.gui.model.ItemAdapter;
|
||||
import com.vaadin.ui.Component.HasContextHelp.ContextHelpIconClickListener;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.metadata.BeanDescriptor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.haulmont.cuba.gui.ComponentsHelper.handleFilteredAttributes;
|
||||
|
||||
public abstract class WebAbstractField<T extends com.vaadin.v7.ui.AbstractField>
|
||||
extends WebAbstractComponent<T> implements Field {
|
||||
extends WebAbstractComponent<T> implements Field, PropertyBoundComponent /* todo ds: move to Field */ {
|
||||
|
||||
protected static final int VALIDATORS_LIST_INITIAL_CAPACITY = 4;
|
||||
|
||||
protected InstanceContainer instanceContainer;
|
||||
protected Datasource<Entity> datasource;
|
||||
protected MetaProperty metaProperty;
|
||||
protected MetaPropertyPath metaPropertyPath;
|
||||
@ -69,6 +64,7 @@ public abstract class WebAbstractField<T extends com.vaadin.v7.ui.AbstractField>
|
||||
protected boolean editable = true;
|
||||
|
||||
protected ItemWrapper itemWrapper;
|
||||
protected ItemAdapter itemAdapter;
|
||||
|
||||
protected Datasource.ItemChangeListener<Entity> securityItemChangeListener;
|
||||
protected WeakItemChangeListener securityWeakItemChangeListener;
|
||||
@ -488,4 +484,67 @@ public abstract class WebAbstractField<T extends com.vaadin.v7.ui.AbstractField>
|
||||
contextHelpIconClickHandler.accept(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public InstanceContainer getEntityContainer() {
|
||||
return instanceContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContainer(InstanceContainer container, String property) {
|
||||
if (this.instanceContainer != null) {
|
||||
metaProperty = null;
|
||||
metaPropertyPath = null;
|
||||
|
||||
component.setPropertyDataSource(null);
|
||||
|
||||
// todo dc
|
||||
// //noinspection unchecked
|
||||
// this.entityContainer.removeItemChangeListener(securityWeakItemChangeListener);
|
||||
// securityWeakItemChangeListener = null;
|
||||
|
||||
this.instanceContainer = null;
|
||||
|
||||
if (itemAdapter != null) {
|
||||
itemAdapter.unsubscribe();
|
||||
}
|
||||
|
||||
disableBeanValidator();
|
||||
}
|
||||
|
||||
if (container != null) {
|
||||
//noinspection unchecked
|
||||
this.instanceContainer = container;
|
||||
|
||||
final MetaClass metaClass = container.getMetaClass();
|
||||
resolveMetaPropertyPath(metaClass, property);
|
||||
|
||||
initFieldConverter();
|
||||
|
||||
itemAdapter = createItemAdapter(container, Collections.singleton(metaPropertyPath));
|
||||
component.setPropertyDataSource(itemAdapter.getItemProperty(metaPropertyPath));
|
||||
|
||||
initRequired(metaPropertyPath);
|
||||
|
||||
if (metaProperty.isReadOnly()) {
|
||||
setEditable(false);
|
||||
}
|
||||
|
||||
// todo dc
|
||||
// handleFilteredAttributes(this, this.datasource, metaPropertyPath);
|
||||
// securityItemChangeListener = e -> handleFilteredAttributes(this, this.datasource, metaPropertyPath);
|
||||
//
|
||||
// securityWeakItemChangeListener = new WeakItemChangeListener(datasource, securityItemChangeListener);
|
||||
// //noinspection unchecked
|
||||
// this.datasource.addItemChangeListener(securityWeakItemChangeListener);
|
||||
|
||||
initBeanValidator();
|
||||
}
|
||||
}
|
||||
|
||||
protected ItemAdapter createItemAdapter(InstanceContainer container, Collection<MetaPropertyPath> propertyPaths) {
|
||||
return new ItemAdapter(container, container.getMetaClass(), propertyPaths);
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ import com.haulmont.cuba.core.entity.Entity;
|
||||
import com.haulmont.cuba.gui.components.Action;
|
||||
import com.haulmont.cuba.gui.components.ListComponent;
|
||||
import com.haulmont.cuba.gui.data.CollectionDatasource;
|
||||
import com.haulmont.cuba.gui.model.CollectionContainer;
|
||||
import com.vaadin.v7.ui.AbstractSelect;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@ -36,6 +37,7 @@ public abstract class WebAbstractList<T extends AbstractSelect, E extends Entity
|
||||
ListComponent<E> {
|
||||
|
||||
protected CollectionDatasource datasource;
|
||||
protected CollectionContainer container;
|
||||
|
||||
@Override
|
||||
public boolean isMultiSelect() {
|
||||
@ -62,7 +64,7 @@ public abstract class WebAbstractList<T extends AbstractSelect, E extends Entity
|
||||
if (itemIds != null) {
|
||||
Set res = new LinkedHashSet<>();
|
||||
for (Object id : itemIds) {
|
||||
Entity item = datasource.getItem(id);
|
||||
Entity item = container != null ? container.getItem(id) : datasource.getItem(id);
|
||||
if (item != null)
|
||||
res.add(item);
|
||||
}
|
||||
@ -101,8 +103,13 @@ public abstract class WebAbstractList<T extends AbstractSelect, E extends Entity
|
||||
public void setSelected(Collection<E> items) {
|
||||
Set itemIds = new HashSet();
|
||||
for (Entity item : items) {
|
||||
if (!datasource.containsItem(item.getId())) {
|
||||
throw new IllegalStateException("Datasource doesn't contain item to select: " + item);
|
||||
boolean found = true;
|
||||
if (container != null)
|
||||
found = container.getItem(item.getId()) != null;
|
||||
else if (datasource != null)
|
||||
found = datasource.containsItem(item.getId());
|
||||
if (!found) {
|
||||
throw new IllegalStateException("Collection doesn't contain item to select: " + item);
|
||||
}
|
||||
itemIds.add(item.getId());
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ import com.haulmont.cuba.gui.data.aggregation.Aggregation;
|
||||
import com.haulmont.cuba.gui.data.aggregation.Aggregations;
|
||||
import com.haulmont.cuba.gui.data.impl.CollectionDsListenersWrapper;
|
||||
import com.haulmont.cuba.gui.data.impl.DatasourceImplementation;
|
||||
import com.haulmont.cuba.gui.model.CollectionContainer;
|
||||
import com.haulmont.cuba.gui.presentations.Presentations;
|
||||
import com.haulmont.cuba.gui.presentations.PresentationsImpl;
|
||||
import com.haulmont.cuba.gui.theme.ThemeConstants;
|
||||
@ -59,6 +60,7 @@ import com.haulmont.cuba.web.gui.data.CollectionDsWrapper;
|
||||
import com.haulmont.cuba.web.gui.data.ItemWrapper;
|
||||
import com.haulmont.cuba.web.gui.data.PropertyWrapper;
|
||||
import com.haulmont.cuba.web.gui.icons.IconResolver;
|
||||
import com.haulmont.cuba.web.gui.model.CollectionContainerAdapter;
|
||||
import com.haulmont.cuba.web.widgets.CubaEnhancedTable;
|
||||
import com.haulmont.cuba.web.widgets.CubaResizableTextAreaWrapper;
|
||||
import com.haulmont.cuba.web.widgets.CubaTextArea;
|
||||
@ -147,6 +149,8 @@ public abstract class WebAbstractTable<T extends com.vaadin.v7.ui.Table & CubaEn
|
||||
|
||||
protected CollectionDsListenersWrapper collectionDsListenersWrapper;
|
||||
|
||||
protected CollectionContainer.CollectionChangeListener containerCollectionChangeListener;
|
||||
|
||||
protected CollectionDsWrapper containerDatasource;
|
||||
|
||||
protected boolean ignoreUnfetchedAttributes = false;
|
||||
@ -673,14 +677,16 @@ public abstract class WebAbstractTable<T extends com.vaadin.v7.ui.Table & CubaEn
|
||||
shortcutsDelegate.setAllowEnterShortcut(false);
|
||||
|
||||
component.addValueChangeListener(event -> {
|
||||
if (datasource == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Set<? extends Entity> selected = getSelected();
|
||||
if (selected.isEmpty()) {
|
||||
Entity dsItem = datasource.getItemIfValid();
|
||||
datasource.setItem(null);
|
||||
Entity dsItem = null;
|
||||
if (datasource != null) {
|
||||
dsItem = datasource.getItemIfValid();
|
||||
datasource.setItem(null);
|
||||
} else {
|
||||
dsItem = container.getItem();
|
||||
container.setItem(null);
|
||||
}
|
||||
if (dsItem == null) {
|
||||
// in this case item change event will not be generated
|
||||
refreshActionsState();
|
||||
@ -688,12 +694,21 @@ public abstract class WebAbstractTable<T extends com.vaadin.v7.ui.Table & CubaEn
|
||||
} else {
|
||||
// reset selection and select new item
|
||||
if (isMultiSelect()) {
|
||||
datasource.setItem(null);
|
||||
if (datasource != null)
|
||||
datasource.setItem(null);
|
||||
else
|
||||
container.setItem(null);
|
||||
}
|
||||
|
||||
Entity newItem = selected.iterator().next();
|
||||
Entity dsItem = datasource.getItemIfValid();
|
||||
datasource.setItem(newItem);
|
||||
Entity dsItem;
|
||||
if (datasource != null) {
|
||||
dsItem = datasource.getItemIfValid();
|
||||
datasource.setItem(newItem);
|
||||
} else {
|
||||
dsItem = container.getItem();
|
||||
container.setItem(newItem);
|
||||
}
|
||||
|
||||
if (Objects.equals(dsItem, newItem)) {
|
||||
// in this case item change event will not be generated
|
||||
@ -1042,7 +1057,7 @@ public abstract class WebAbstractTable<T extends com.vaadin.v7.ui.Table & CubaEn
|
||||
|
||||
createStubsForGeneratedColumns();
|
||||
|
||||
setVisibleColumns(getInitialVisibleColumns());
|
||||
setVisibleColumns(getInitialVisibleColumns(datasource.getMetaClass()));
|
||||
|
||||
if (security.isSpecificPermitted(ShowInfoAction.ACTION_PERMISSION)) {
|
||||
ShowInfoAction action = (ShowInfoAction) getAction(ShowInfoAction.ACTION_ID);
|
||||
@ -1073,6 +1088,187 @@ public abstract class WebAbstractTable<T extends com.vaadin.v7.ui.Table & CubaEn
|
||||
return new TableCollectionDsListenersWrapper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContainer(CollectionContainer container) {
|
||||
Preconditions.checkNotNullArgument(container, "container is null");
|
||||
|
||||
if (this.container != null) {
|
||||
throw new UnsupportedOperationException("Changing container is not supported by the Table component");
|
||||
}
|
||||
|
||||
MessageTools messageTools = AppBeans.get(MessageTools.NAME);
|
||||
|
||||
final Collection<Object> columns;
|
||||
// if (this.columns.isEmpty()) {
|
||||
// Collection<MetaPropertyPath> paths = datasource.getView() != null ?
|
||||
// // if a view is specified - use view properties
|
||||
// metadataTools.getViewPropertyPaths(datasource.getView(), datasource.getMetaClass()) :
|
||||
// // 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)) {
|
||||
// Table.Column column = new Table.Column(metaPropertyPath);
|
||||
//
|
||||
// String propertyName = property.getName();
|
||||
// MetaClass propertyMetaClass = metadataTools.getPropertyEnclosingMetaClass(metaPropertyPath);
|
||||
//
|
||||
// column.setCaption(messageTools.getPropertyCaption(propertyMetaClass, propertyName));
|
||||
// column.setType(metaPropertyPath.getRangeJavaClass());
|
||||
//
|
||||
// Element element = DocumentHelper.createElement("column");
|
||||
// column.setXmlDescriptor(element);
|
||||
//
|
||||
// addColumn(column);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
columns = this.columns.keySet();
|
||||
|
||||
this.container = container;
|
||||
|
||||
// drop cached datasources for components before update table cells on client
|
||||
// collectionChangeListener = e -> {
|
||||
// if (fieldDatasources != null) {
|
||||
// switch (e.getOperation()) {
|
||||
// case CLEAR:
|
||||
// case REFRESH:
|
||||
// fieldDatasources.clear();
|
||||
// break;
|
||||
//
|
||||
// case UPDATE:
|
||||
// case REMOVE:
|
||||
// for (Object entity : e.getItems()) {
|
||||
// fieldDatasources.remove(entity);
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
//noinspection unchecked
|
||||
this.container.addCollectionChangeListener(
|
||||
new com.haulmont.cuba.gui.model.impl.WeakCollectionChangeListener(this.container, containerCollectionChangeListener));
|
||||
|
||||
CollectionContainerAdapter containerAdapter = createContainerAdapter(this.container, getPropertyColumns());
|
||||
|
||||
component.setContainerDataSource(containerAdapter);
|
||||
|
||||
List<MetaPropertyPath> editableColumns = null;
|
||||
if (isEditable()) {
|
||||
editableColumns = new LinkedList<>();
|
||||
}
|
||||
|
||||
MetaClass metaClass = this.container.getMetaClass();
|
||||
for (final Object columnId : columns) {
|
||||
final Table.Column column = this.columns.get(columnId);
|
||||
|
||||
final String caption;
|
||||
if (column != null) {
|
||||
caption = StringUtils.capitalize(column.getCaption() != null ? column.getCaption() : getColumnCaption(columnId));
|
||||
} else {
|
||||
caption = StringUtils.capitalize(getColumnCaption(columnId));
|
||||
}
|
||||
|
||||
setColumnHeader(columnId, caption);
|
||||
|
||||
if (column != null) {
|
||||
if (editableColumns != null && column.isEditable() && (columnId instanceof MetaPropertyPath)) {
|
||||
MetaPropertyPath propertyPath = ((MetaPropertyPath) columnId);
|
||||
if (security.isEntityAttrUpdatePermitted(metaClass, propertyPath.toString())) {
|
||||
editableColumns.add(propertyPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (column.isCollapsed() && component.isColumnCollapsingAllowed()) {
|
||||
component.setColumnCollapsed(column.getId(), true);
|
||||
}
|
||||
|
||||
if (column.getAggregation() != null && isAggregatable()) {
|
||||
checkAggregation(column.getAggregation());
|
||||
|
||||
component.addContainerPropertyAggregation(column.getId(),
|
||||
WebComponentsHelper.convertAggregationType(column.getAggregation().getType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (editableColumns != null && !editableColumns.isEmpty()) {
|
||||
setEditableColumns(editableColumns);
|
||||
}
|
||||
|
||||
createColumns(containerAdapter);
|
||||
|
||||
for (Table.Column column : this.columnsOrder) {
|
||||
if (editable && column.getAggregation() != null
|
||||
&& (BooleanUtils.isTrue(column.isEditable()) || BooleanUtils.isTrue(column.isCalculatable()))) {
|
||||
addAggregationCell(column);
|
||||
}
|
||||
}
|
||||
|
||||
// aggregationDatasourceListener = createAggregationDatasourceListener();
|
||||
// //noinspection unchecked
|
||||
// collectionContainer.addItemPropertyChangeListener(
|
||||
// new com.haulmont.cuba.gui.model.impl.WeakItemPropertyChangeListener(collectionContainer, aggregationDatasourceListener));
|
||||
|
||||
createStubsForGeneratedColumns();
|
||||
|
||||
setVisibleColumns(getInitialVisibleColumns(this.container.getMetaClass()));
|
||||
|
||||
// if (security.isSpecificPermitted(ShowInfoAction.ACTION_PERMISSION)) {
|
||||
// ShowInfoAction action = (ShowInfoAction) getAction(ShowInfoAction.ACTION_ID);
|
||||
// if (action == null) {
|
||||
// action = new ShowInfoAction();
|
||||
// addAction(action);
|
||||
// }
|
||||
// action.setDatasource(datasource);
|
||||
// }
|
||||
|
||||
// if (rowsCount != null) {
|
||||
// rowsCount.setDatasource(datasource);
|
||||
// }
|
||||
|
||||
// collectionChangeSelectionListener = e -> {
|
||||
// // #PL-2035, reload selection from ds
|
||||
// Set<Object> selectedItemIds = getSelectedItemIds();
|
||||
// if (selectedItemIds == null) {
|
||||
// selectedItemIds = Collections.emptySet();
|
||||
// }
|
||||
//
|
||||
// Set<Object> newSelection = new HashSet<>();
|
||||
// for (Object entityId : selectedItemIds) {
|
||||
// //noinspection unchecked
|
||||
// if (e.getDs().containsItem(entityId)) {
|
||||
// newSelection.add(entityId);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (e.getDs().getState() == Datasource.State.VALID && e.getDs().getItem() != null) {
|
||||
// newSelection.add(e.getDs().getItem().getId());
|
||||
// }
|
||||
//
|
||||
// if (newSelection.isEmpty()) {
|
||||
// setSelected((E) null);
|
||||
// } else {
|
||||
// setSelectedIds(newSelection);
|
||||
// }
|
||||
// };
|
||||
// //noinspection unchecked
|
||||
// datasource.addCollectionChangeListener(new WeakCollectionChangeListener(datasource, collectionChangeSelectionListener));
|
||||
|
||||
//noinspection unchecked
|
||||
// collectionDsActionsNotifier = new CollectionDsActionsNotifier(this);
|
||||
// collectionDsActionsNotifier.bind(datasource);
|
||||
|
||||
// for (Action action : getActions()) {
|
||||
// action.refreshState();
|
||||
// }
|
||||
|
||||
// if (!canBeSorted(datasource))
|
||||
// setSortable(false);
|
||||
|
||||
assignAutoDebugId();
|
||||
}
|
||||
|
||||
protected boolean canBeSorted(CollectionDatasource datasource) {
|
||||
//noinspection SimplifiableConditionalExpression
|
||||
return datasource instanceof PropertyDatasource ?
|
||||
@ -1173,10 +1369,9 @@ public abstract class WebAbstractTable<T extends com.vaadin.v7.ui.Table & CubaEn
|
||||
}
|
||||
}
|
||||
|
||||
protected List<Object> getInitialVisibleColumns() {
|
||||
protected List<Object> getInitialVisibleColumns(MetaClass metaClass) {
|
||||
List<Object> result = new ArrayList<>();
|
||||
|
||||
MetaClass metaClass = datasource.getMetaClass();
|
||||
for (Column column : columnsOrder) {
|
||||
if (column.getId() instanceof MetaPropertyPath) {
|
||||
MetaPropertyPath propertyPath = (MetaPropertyPath) column.getId();
|
||||
@ -1193,7 +1388,7 @@ public abstract class WebAbstractTable<T extends com.vaadin.v7.ui.Table & CubaEn
|
||||
protected List<MetaPropertyPath> getPropertyColumns() {
|
||||
List<MetaPropertyPath> result = new ArrayList<>();
|
||||
|
||||
MetaClass metaClass = datasource.getMetaClass();
|
||||
MetaClass metaClass = container != null ? container.getMetaClass() : datasource.getMetaClass();
|
||||
for (Column column : columnsOrder) {
|
||||
if (column.getId() instanceof MetaPropertyPath) {
|
||||
MetaPropertyPath propertyPath = (MetaPropertyPath) column.getId();
|
||||
@ -1209,6 +1404,11 @@ public abstract class WebAbstractTable<T extends com.vaadin.v7.ui.Table & CubaEn
|
||||
Collection<MetaPropertyPath> columns,
|
||||
CollectionDsListenersWrapper collectionDsListenersWrapper);
|
||||
|
||||
protected CollectionContainerAdapter createContainerAdapter(CollectionContainer container,
|
||||
Collection<MetaPropertyPath> columns) {
|
||||
return new CollectionContainerAdapter(container, columns, true);
|
||||
};
|
||||
|
||||
protected void setVisibleColumns(List<?> columnsOrder) {
|
||||
component.setVisibleColumns(columnsOrder.toArray());
|
||||
}
|
||||
|
@ -0,0 +1,309 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.haulmont.cuba.web.gui.model;
|
||||
|
||||
import com.haulmont.chile.core.model.MetaClass;
|
||||
import com.haulmont.chile.core.model.MetaPropertyPath;
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
import com.haulmont.cuba.gui.model.CollectionContainer;
|
||||
import com.haulmont.cuba.gui.model.InstanceContainer;
|
||||
import com.haulmont.cuba.gui.model.impl.WeakContainerListenerAdapter;
|
||||
import com.haulmont.cuba.web.gui.data.StaticItemSetChangeEvent;
|
||||
import com.haulmont.cuba.web.gui.data.UnsubscribableDsWrapper;
|
||||
import com.vaadin.ui.UI;
|
||||
import com.vaadin.v7.data.Container;
|
||||
import com.vaadin.v7.data.Item;
|
||||
import com.vaadin.v7.data.Property;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CollectionContainerAdapter implements Container, Container.ItemSetChangeNotifier, UnsubscribableDsWrapper {
|
||||
|
||||
protected boolean autoRefresh;
|
||||
protected boolean ignoreListeners;
|
||||
|
||||
protected CollectionContainer<Entity> collectionContainer;
|
||||
protected Collection<MetaPropertyPath> properties = new ArrayList<>();
|
||||
|
||||
// lazily initialized listeners list
|
||||
protected List<Container.ItemSetChangeListener> itemSetChangeListeners = null;
|
||||
|
||||
// protected EntityContainer.StateChangeListener cdsStateChangeListener;
|
||||
protected InstanceContainer.ItemPropertyChangeListener cdsItemPropertyChangeListener;
|
||||
protected CollectionContainer.CollectionChangeListener cdsCollectionChangeListener;
|
||||
|
||||
protected WeakContainerListenerAdapter weakDsListenerAdapter;
|
||||
|
||||
public CollectionContainerAdapter(CollectionContainer collectionContainer, boolean autoRefresh) {
|
||||
this(collectionContainer, null, autoRefresh);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public CollectionContainerAdapter(CollectionContainer collectionContainer, Collection<MetaPropertyPath> properties,
|
||||
boolean autoRefresh) {
|
||||
this.collectionContainer = collectionContainer;
|
||||
this.autoRefresh = autoRefresh;
|
||||
|
||||
// final View view = collectionContainer.getView();
|
||||
final MetaClass metaClass = collectionContainer.getMetaClass();
|
||||
|
||||
// if (properties == null) {
|
||||
// createProperties(view, metaClass);
|
||||
// } else {
|
||||
this.properties.addAll(properties);
|
||||
// }
|
||||
|
||||
cdsItemPropertyChangeListener = createItemPropertyChangeListener();
|
||||
// cdsStateChangeListener = createStateChangeListener();
|
||||
cdsCollectionChangeListener = createCollectionChangeListener();
|
||||
|
||||
weakDsListenerAdapter = new WeakContainerListenerAdapter(collectionContainer, cdsItemPropertyChangeListener,
|
||||
/*cdsStateChangeListener,*/ cdsCollectionChangeListener);
|
||||
|
||||
collectionContainer.addItemPropertyChangeListener(weakDsListenerAdapter);
|
||||
// collectionContainer.addStateChangeListener(weakDsListenerAdapter);
|
||||
collectionContainer.addCollectionChangeListener(weakDsListenerAdapter);
|
||||
}
|
||||
|
||||
protected CollectionContainer.CollectionChangeListener createCollectionChangeListener() {
|
||||
return new ContainerDatasourceCollectionChangeListener();
|
||||
}
|
||||
|
||||
protected InstanceContainer.ItemPropertyChangeListener createItemPropertyChangeListener() {
|
||||
return new ContainerDatasourceItemPropertyChangeListener();
|
||||
}
|
||||
|
||||
// protected EntityContainer.StateChangeListener createStateChangeListener() {
|
||||
// return new ContainerDatasourceStateChangeListener();
|
||||
// }
|
||||
|
||||
// protected void createProperties(View view, MetaClass metaClass) {
|
||||
// properties.addAll(CollectionDsHelper.createProperties(view, metaClass));
|
||||
// }
|
||||
|
||||
protected void fireItemSetChanged() {
|
||||
if (ignoreListeners) {
|
||||
return;
|
||||
}
|
||||
|
||||
ignoreListeners = true;
|
||||
|
||||
if (UI.getCurrent().getConnectorTracker().isWritingResponse()) {
|
||||
// Suppress containerItemSetChange listeners during painting, undefined behavior may be occurred
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemSetChangeListeners != null) {
|
||||
StaticItemSetChangeEvent event = new StaticItemSetChangeEvent(this);
|
||||
for (ItemSetChangeListener listener : itemSetChangeListeners) {
|
||||
listener.containerItemSetChange(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item getItem(Object itemId) {
|
||||
// CollectionDsHelper.autoRefreshInvalid(collectionContainer, autoRefresh);
|
||||
//noinspection unchecked
|
||||
final Object item = collectionContainer.getItem(itemId);
|
||||
return item == null ? null : getItemAdapter(item);
|
||||
}
|
||||
|
||||
protected Map<Object, ItemAdapter> itemsCache = new HashMap<>();
|
||||
|
||||
protected Item getItemAdapter(Object item) {
|
||||
ItemAdapter wrapper = itemsCache.get(item);
|
||||
if (wrapper == null) {
|
||||
wrapper = createItemAdapter(item);
|
||||
itemsCache.put(item, wrapper);
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
protected ItemAdapter createItemAdapter(Object item) {
|
||||
return new ItemAdapter(item, collectionContainer.getMetaClass(), properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection getContainerPropertyIds() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection getItemIds() {
|
||||
// CollectionDsHelper.autoRefreshInvalid(collectionContainer, autoRefresh);
|
||||
return collectionContainer.getItems().stream()
|
||||
.map(Entity::getId)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Property getContainerProperty(Object itemId, Object propertyId) {
|
||||
final Item item = getItem(itemId);
|
||||
return item == null ? null : item.getItemProperty(propertyId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class getType(Object propertyId) {
|
||||
MetaPropertyPath propertyPath = (MetaPropertyPath) propertyId;
|
||||
return propertyPath.getRangeJavaClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
// CollectionDsHelper.autoRefreshInvalid(collectionContainer, autoRefresh);
|
||||
return collectionContainer.getItems().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsId(Object itemId) {
|
||||
// CollectionDsHelper.autoRefreshInvalid(collectionContainer, autoRefresh);
|
||||
//noinspection unchecked
|
||||
return collectionContainer.getItem(itemId) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item addItem(Object itemId) throws UnsupportedOperationException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object addItem() throws UnsupportedOperationException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeItem(Object itemId) throws UnsupportedOperationException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addContainerProperty(Object propertyId, Class type, Object defaultValue) throws UnsupportedOperationException {
|
||||
if (propertyId instanceof MetaPropertyPath) {
|
||||
return this.properties.add((MetaPropertyPath) propertyId);
|
||||
}
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeContainerProperty(Object propertyId) throws UnsupportedOperationException {
|
||||
//noinspection SuspiciousMethodCalls
|
||||
return this.properties.remove(propertyId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAllItems() throws UnsupportedOperationException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addItemSetChangeListener(ItemSetChangeListener listener) {
|
||||
if (itemSetChangeListeners == null) {
|
||||
itemSetChangeListeners = new LinkedList<>();
|
||||
}
|
||||
|
||||
itemSetChangeListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(ItemSetChangeListener listener) {
|
||||
addItemSetChangeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeItemSetChangeListener(ItemSetChangeListener listener) {
|
||||
if (itemSetChangeListeners != null) {
|
||||
itemSetChangeListeners.remove(listener);
|
||||
|
||||
if (itemSetChangeListeners.isEmpty()) {
|
||||
itemSetChangeListeners = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(ItemSetChangeListener listener) {
|
||||
removeItemSetChangeListener(listener);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
collectionContainer.removeCollectionChangeListener(weakDsListenerAdapter);
|
||||
collectionContainer.removeItemPropertyChangeListener(weakDsListenerAdapter);
|
||||
// collectionContainer.removeStateChangeListener(weakDsListenerAdapter);
|
||||
|
||||
weakDsListenerAdapter = null;
|
||||
collectionContainer = null;
|
||||
}
|
||||
|
||||
// protected class ContainerDatasourceStateChangeListener implements EntityContainer.StateChangeListener {
|
||||
//
|
||||
// public ContainerDatasourceStateChangeListener() {
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void stateChanged(EntityContainer.StateChangeEvent e) {
|
||||
// itemsCache.clear();
|
||||
// }
|
||||
// }
|
||||
|
||||
protected class ContainerDatasourceCollectionChangeListener implements CollectionContainer.CollectionChangeListener {
|
||||
|
||||
public ContainerDatasourceCollectionChangeListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collectionChanged(CollectionContainer.CollectionChangeEvent e) {
|
||||
itemsCache.clear();
|
||||
|
||||
final boolean prevIgnoreListeners = ignoreListeners;
|
||||
try {
|
||||
fireItemSetChanged();
|
||||
} finally {
|
||||
ignoreListeners = prevIgnoreListeners;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected class ContainerDatasourceItemPropertyChangeListener implements InstanceContainer.ItemPropertyChangeListener {
|
||||
|
||||
public ContainerDatasourceItemPropertyChangeListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void itemPropertyChanged(InstanceContainer.ItemPropertyChangeEvent e) {
|
||||
Item wrapper = getItemAdapter(e.getItem());
|
||||
|
||||
// MetaProperty worked wrong with properties from inherited superclasses
|
||||
MetaPropertyPath metaPropertyPath = collectionContainer.getMetaClass().getPropertyPath(e.getProperty());
|
||||
if (metaPropertyPath == null) {
|
||||
return;
|
||||
}
|
||||
Property itemProperty = wrapper.getItemProperty(metaPropertyPath);
|
||||
if (itemProperty instanceof PropertyAdapter) {
|
||||
((PropertyAdapter) itemProperty).fireValueChangeEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{ds=" + (collectionContainer == null ? "null" : collectionContainer.toString() + "}");
|
||||
}
|
||||
}
|
169
modules/web/src/com/haulmont/cuba/web/gui/model/ItemAdapter.java
Normal file
169
modules/web/src/com/haulmont/cuba/web/gui/model/ItemAdapter.java
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.haulmont.cuba.web.gui.model;
|
||||
|
||||
import com.haulmont.chile.core.model.MetaClass;
|
||||
import com.haulmont.chile.core.model.MetaProperty;
|
||||
import com.haulmont.chile.core.model.MetaPropertyPath;
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
import com.haulmont.cuba.core.global.AppBeans;
|
||||
import com.haulmont.cuba.core.global.MetadataTools;
|
||||
import com.haulmont.cuba.gui.model.CollectionContainer;
|
||||
import com.haulmont.cuba.gui.model.InstanceContainer;
|
||||
import com.haulmont.cuba.gui.model.impl.WeakCollectionChangeListener;
|
||||
import com.haulmont.cuba.web.gui.data.UnsubscribableDsWrapper;
|
||||
import com.vaadin.v7.data.Item;
|
||||
import com.vaadin.v7.data.Property;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ItemAdapter implements Item, Item.PropertySetChangeNotifier, UnsubscribableDsWrapper {
|
||||
|
||||
protected Map<MetaPropertyPath, PropertyAdapter> properties = new HashMap<>();
|
||||
|
||||
// lazily initialized listeners list
|
||||
protected List<PropertySetChangeListener> listeners = null;
|
||||
|
||||
protected Object item;
|
||||
protected MetaClass metaClass;
|
||||
protected WeakCollectionChangeListener weakCollectionChangeListener;
|
||||
|
||||
public ItemAdapter(Object item, MetaClass metaClass) {
|
||||
this(item, metaClass, AppBeans.<MetadataTools>get(MetadataTools.NAME).getPropertyPaths(metaClass));
|
||||
}
|
||||
|
||||
public ItemAdapter(Object item, MetaClass metaClass, Collection<MetaPropertyPath> properties) {
|
||||
this.item = item;
|
||||
this.metaClass = metaClass;
|
||||
|
||||
for (MetaPropertyPath property : properties) {
|
||||
this.properties.put(property, createPropertyModelAdapter(item, property));
|
||||
}
|
||||
|
||||
if (item instanceof CollectionContainer) {
|
||||
CollectionContainer container = (CollectionContainer) item;
|
||||
weakCollectionChangeListener = new WeakCollectionChangeListener(container, e -> fireItemPropertySetChanged());
|
||||
//noinspection unchecked
|
||||
container.addCollectionChangeListener(weakCollectionChangeListener);
|
||||
}
|
||||
}
|
||||
|
||||
protected void fireItemPropertySetChanged() {
|
||||
if (listeners != null) {
|
||||
PropertySetChangeEvent event = new PropertySetChangeEvent();
|
||||
|
||||
for (PropertySetChangeListener listener : listeners) {
|
||||
listener.itemPropertySetChange(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected PropertyAdapter createPropertyModelAdapter(Object item, MetaPropertyPath propertyPath) {
|
||||
return new PropertyAdapter(item, propertyPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Property getItemProperty(Object id) {
|
||||
if (id instanceof MetaPropertyPath) {
|
||||
return properties.get(id);
|
||||
} else if (id instanceof MetaProperty) {
|
||||
final MetaProperty metaProperty = (MetaProperty) id;
|
||||
return properties.get(new MetaPropertyPath(metaClass, metaProperty));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection getItemPropertyIds() {
|
||||
return properties.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addItemProperty(Object id, Property property) throws UnsupportedOperationException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeItemProperty(Object id) throws UnsupportedOperationException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPropertySetChangeListener(PropertySetChangeListener listener) {
|
||||
if (listeners == null) {
|
||||
listeners = new LinkedList<>();
|
||||
}
|
||||
|
||||
if (!listeners.contains(listener)) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(PropertySetChangeListener listener) {
|
||||
addPropertySetChangeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePropertySetChangeListener(PropertySetChangeListener listener) {
|
||||
if (listeners != null) {
|
||||
listeners.remove(listener);
|
||||
|
||||
if (listeners.isEmpty()) {
|
||||
listeners = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(PropertySetChangeListener listener) {
|
||||
removePropertySetChangeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
if (item instanceof CollectionContainer) {
|
||||
CollectionContainer container = (CollectionContainer) item;
|
||||
// noinspection unchecked
|
||||
container.removeCollectionChangeListener(weakCollectionChangeListener);
|
||||
weakCollectionChangeListener = null;
|
||||
}
|
||||
|
||||
item = null;
|
||||
metaClass = null;
|
||||
|
||||
properties.values().forEach(PropertyAdapter::unsubscribe);
|
||||
properties.clear();
|
||||
}
|
||||
|
||||
private class PropertySetChangeEvent implements Item.PropertySetChangeEvent {
|
||||
@Override
|
||||
public Item getItem() {
|
||||
return ItemAdapter.this;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final Entity entity = getItem();
|
||||
return entity == null ? "" : entity.getInstanceName();
|
||||
}
|
||||
|
||||
public Entity getItem() {
|
||||
return item instanceof InstanceContainer ? ((InstanceContainer) item).getItem() : (Entity) item;
|
||||
}
|
||||
}
|
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.haulmont.cuba.web.gui.model;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.haulmont.chile.core.datatypes.Datatype;
|
||||
import com.haulmont.chile.core.datatypes.Datatypes;
|
||||
import com.haulmont.chile.core.model.Instance;
|
||||
import com.haulmont.chile.core.model.MetaPropertyPath;
|
||||
import com.haulmont.chile.core.model.Range;
|
||||
import com.haulmont.chile.core.model.utils.InstanceUtils;
|
||||
import com.haulmont.cuba.core.global.AppBeans;
|
||||
import com.haulmont.cuba.core.global.MetadataTools;
|
||||
import com.haulmont.cuba.core.global.UserSessionSource;
|
||||
import com.haulmont.cuba.gui.model.InstanceContainer;
|
||||
import com.haulmont.cuba.gui.model.impl.WeakItemChangeListener;
|
||||
import com.haulmont.cuba.gui.model.impl.WeakItemPropertyChangeListener;
|
||||
import com.haulmont.cuba.web.gui.data.AbstractPropertyWrapper;
|
||||
import com.haulmont.cuba.web.gui.data.UnsubscribableDsWrapper;
|
||||
import com.haulmont.cuba.web.widgets.data.PropertyValueStringify;
|
||||
import com.vaadin.v7.data.util.converter.Converter;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
public class PropertyAdapter extends AbstractPropertyWrapper implements PropertyValueStringify, UnsubscribableDsWrapper {
|
||||
|
||||
protected MetaPropertyPath propertyPath;
|
||||
|
||||
protected MetadataTools metadataTools = AppBeans.get(MetadataTools.NAME);
|
||||
|
||||
protected InstanceContainer.ItemChangeListener dsItemChangeListener;
|
||||
protected WeakItemChangeListener weakItemChangeListener;
|
||||
|
||||
protected InstanceContainer.ItemPropertyChangeListener dsItemPropertyChangeListener;
|
||||
protected WeakItemPropertyChangeListener weakItemPropertyChangeListener;
|
||||
|
||||
public PropertyAdapter(Object item, MetaPropertyPath propertyPath) {
|
||||
this.item = item;
|
||||
this.propertyPath = propertyPath;
|
||||
|
||||
if (item instanceof InstanceContainer) {
|
||||
dsItemChangeListener = e -> fireValueChangeEvent();
|
||||
dsItemPropertyChangeListener = e -> {
|
||||
if (e.getProperty().equals(this.propertyPath.toString())) {
|
||||
fireValueChangeEvent();
|
||||
}
|
||||
};
|
||||
|
||||
InstanceContainer datasource = (InstanceContainer) item;
|
||||
|
||||
weakItemChangeListener = new WeakItemChangeListener(datasource, dsItemChangeListener);
|
||||
//noinspection unchecked
|
||||
datasource.addItemChangeListener(weakItemChangeListener);
|
||||
|
||||
weakItemPropertyChangeListener = new WeakItemPropertyChangeListener(datasource, dsItemPropertyChangeListener);
|
||||
//noinspection unchecked
|
||||
datasource.addItemPropertyChangeListener(weakItemPropertyChangeListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
final Instance instance = getInstance();
|
||||
return instance == null ? null : InstanceUtils.getValueEx(instance, propertyPath.getPath());
|
||||
}
|
||||
|
||||
protected Instance getInstance() {
|
||||
if (item instanceof InstanceContainer) {
|
||||
return ((InstanceContainer) item).getItem();
|
||||
} else {
|
||||
return (Instance) item;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Object newValue) throws ReadOnlyException, Converter.ConversionException {
|
||||
final Instance instance = getInstance();
|
||||
|
||||
if (instance != null) {
|
||||
InstanceUtils.setValueEx(instance, propertyPath.getPath(), valueOf(newValue));
|
||||
}
|
||||
}
|
||||
|
||||
protected Object valueOf(Object newValue) throws Converter.ConversionException {
|
||||
if (newValue == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Range range = propertyPath.getRange();
|
||||
if (range == null) {
|
||||
return newValue;
|
||||
} else {
|
||||
final Object obj;
|
||||
if (range.isDatatype()) {
|
||||
Datatype<Object> datatype = range.asDatatype();
|
||||
if (newValue instanceof String) {
|
||||
try {
|
||||
newValue = Strings.emptyToNull((String) newValue);
|
||||
UserSessionSource sessionSource = AppBeans.get(UserSessionSource.NAME);
|
||||
obj = datatype.parse((String) newValue, sessionSource.getLocale());
|
||||
} catch (ParseException e) {
|
||||
throw new Converter.ConversionException(e);
|
||||
}
|
||||
} else {
|
||||
if (newValue.getClass().equals(datatype.getJavaClass())) {
|
||||
return newValue;
|
||||
} else {
|
||||
Datatype newValueDatatype = Datatypes.getNN(newValue.getClass());
|
||||
//noinspection unchecked
|
||||
String str = newValueDatatype.format(newValue);
|
||||
try {
|
||||
obj = datatype.parse(str);
|
||||
} catch (ParseException e) {
|
||||
throw new Converter.ConversionException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
obj = newValue;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class getType() {
|
||||
return propertyPath.getRangeJavaClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormattedValue() {
|
||||
return metadataTools.format(getValue(), propertyPath.getMetaProperty());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void unsubscribe() {
|
||||
InstanceContainer container = (InstanceContainer) item;
|
||||
container.removeItemChangeListener(weakItemChangeListener);
|
||||
weakItemChangeListener = null;
|
||||
|
||||
container.removeItemPropertyChangeListener(weakItemPropertyChangeListener);
|
||||
weakItemPropertyChangeListener = null;
|
||||
|
||||
propertyPath = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireValueChangeEvent() {
|
||||
super.fireValueChangeEvent();
|
||||
}
|
||||
}
|
61
modules/web/src/com/haulmont/cuba/web/tmp/DcScreen1.java
Normal file
61
modules/web/src/com/haulmont/cuba/web/tmp/DcScreen1.java
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.web.tmp;
|
||||
|
||||
import com.haulmont.cuba.gui.components.AbstractWindow;
|
||||
import com.haulmont.cuba.gui.components.TextField;
|
||||
import com.haulmont.cuba.gui.model.DataContextFactory;
|
||||
import com.haulmont.cuba.gui.model.InstanceContainer;
|
||||
import com.haulmont.cuba.gui.model.InstanceLoader;
|
||||
import com.haulmont.cuba.security.entity.User;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class DcScreen1 extends AbstractWindow {
|
||||
|
||||
@Inject
|
||||
private TextField textField1;
|
||||
@Inject
|
||||
private TextField textField2;
|
||||
|
||||
@Inject
|
||||
private DataContextFactory dataContextFactory;
|
||||
|
||||
private InstanceContainer<User> container;
|
||||
|
||||
@Override
|
||||
public void init(Map<String, Object> params) {
|
||||
container = dataContextFactory.createInstanceContainer(User.class);
|
||||
|
||||
textField1.setContainer(container, "name");
|
||||
textField2.setContainer(container, "name");
|
||||
|
||||
InstanceLoader<User, UUID> loader = dataContextFactory.createInstanceLoader();
|
||||
loader.setContainer(container);
|
||||
loader.setEntityId(UUID.fromString("60885987-1b61-4247-94c7-dff348347f93"));
|
||||
loader.load();
|
||||
}
|
||||
|
||||
public void changeName() {
|
||||
container.getItemNN().setName("User-" + System.currentTimeMillis());
|
||||
}
|
||||
}
|
152
modules/web/src/com/haulmont/cuba/web/tmp/DcScreen2.java
Normal file
152
modules/web/src/com/haulmont/cuba/web/tmp/DcScreen2.java
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.web.tmp;
|
||||
|
||||
import com.haulmont.chile.core.model.MetaClass;
|
||||
import com.haulmont.cuba.core.global.DataManager;
|
||||
import com.haulmont.cuba.core.global.LoadContext;
|
||||
import com.haulmont.cuba.core.global.Metadata;
|
||||
import com.haulmont.cuba.core.global.View;
|
||||
import com.haulmont.cuba.gui.components.AbstractWindow;
|
||||
import com.haulmont.cuba.gui.components.Table;
|
||||
import com.haulmont.cuba.gui.components.TextField;
|
||||
import com.haulmont.cuba.gui.model.CollectionContainer;
|
||||
import com.haulmont.cuba.gui.model.CollectionLoader;
|
||||
import com.haulmont.cuba.gui.model.DataContext;
|
||||
import com.haulmont.cuba.gui.model.DataContextFactory;
|
||||
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
|
||||
import com.haulmont.cuba.security.entity.Group;
|
||||
import com.haulmont.cuba.security.entity.User;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class DcScreen2 extends AbstractWindow {
|
||||
|
||||
@Inject
|
||||
private TextField textField1;
|
||||
@Inject
|
||||
private Metadata metadata;
|
||||
@Inject
|
||||
private DataContextFactory dataContextFactory;
|
||||
@Inject
|
||||
private ComponentsFactory componentsFactory;
|
||||
@Inject
|
||||
private DataManager dataManager;
|
||||
|
||||
private List<User> users;
|
||||
private int index;
|
||||
private CollectionContainer<User> container;
|
||||
private boolean reverse;
|
||||
private boolean filter;
|
||||
private Table<User> table;
|
||||
private CollectionLoader<User> loader;
|
||||
private DataContext dataContext;
|
||||
|
||||
@Override
|
||||
public void init(Map<String, Object> params) {
|
||||
dataContext = dataContextFactory.createDataContext();
|
||||
|
||||
table = componentsFactory.createComponent(Table.class);
|
||||
table.setWidthFull();
|
||||
add(table);
|
||||
expand(table);
|
||||
|
||||
MetaClass metaClass = metadata.getClassNN(User.class);
|
||||
table.addColumn(new Table.Column(metaClass.getPropertyPath("login")));
|
||||
table.addColumn(new Table.Column(metaClass.getPropertyPath("name")));
|
||||
|
||||
container = dataContextFactory.createCollectionContainer(User.class);
|
||||
|
||||
loader = dataContextFactory.createCollectionLoader();
|
||||
loader.setDataContext(dataContext);
|
||||
loader.setContainer(container);
|
||||
loader.setQuery("select u from sec$User u order by u.name");
|
||||
loader.setView(View.LOCAL);
|
||||
loader.load();
|
||||
|
||||
// users = dataManager.loadList(LoadContext.create(User.class)
|
||||
// .setQuery(LoadContext.createQuery("select u from sec$User u order by u.name"))
|
||||
// .setView(View.LOCAL));
|
||||
|
||||
// container.setItems(users);
|
||||
|
||||
table.setContainer(container);
|
||||
textField1.setContainer(container, "name");
|
||||
}
|
||||
|
||||
public void sort() {
|
||||
reverse = !reverse;
|
||||
List<User> items = new ArrayList<>(container.getItems());
|
||||
items.sort(Comparator.nullsLast(Comparator.comparing(User::getName)));
|
||||
if (reverse)
|
||||
Collections.reverse(items);
|
||||
container.setItems(items);
|
||||
}
|
||||
|
||||
public void filter() {
|
||||
filter = !filter;
|
||||
if (!filter) {
|
||||
loader.load();
|
||||
} else {
|
||||
List<User> items = container.getItems().stream()
|
||||
.filter(user -> user.getLogin().startsWith("u"))
|
||||
.collect(Collectors.toList());
|
||||
container.setItems(items);
|
||||
}
|
||||
}
|
||||
|
||||
public void nextUser() {
|
||||
index = container.getItems().indexOf(container.getItem());
|
||||
index++;
|
||||
if (index > users.size() - 1)
|
||||
index = 0;
|
||||
|
||||
container.setItem(users.get(index));
|
||||
}
|
||||
|
||||
public void addUser() {
|
||||
Group group = dataManager.load(LoadContext.create(Group.class).setId(UUID.fromString("0fa2b1a5-1d68-4d69-9fbd-dff348347f93")));
|
||||
|
||||
User user = metadata.create(User.class);
|
||||
long millis = System.currentTimeMillis();
|
||||
user.setLogin("u-" + millis);
|
||||
user.setName("User-" + millis);
|
||||
user.setGroup(group);
|
||||
|
||||
user = dataContext.merge(user);
|
||||
container.getMutableItems().add(user);
|
||||
|
||||
table.setSelected(user);
|
||||
}
|
||||
|
||||
public void removeUser() {
|
||||
User user = container.getItem();
|
||||
if (user != null) {
|
||||
container.getMutableItems().remove(user);
|
||||
dataContext.remove(user);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveChanges() {
|
||||
dataContext.commit();
|
||||
}
|
||||
}
|
95
modules/web/src/com/haulmont/cuba/web/tmp/DcScreen3.java
Normal file
95
modules/web/src/com/haulmont/cuba/web/tmp/DcScreen3.java
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2017 Haulmont.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.web.tmp;
|
||||
|
||||
import com.haulmont.chile.core.model.MetaClass;
|
||||
import com.haulmont.cuba.core.global.DataManager;
|
||||
import com.haulmont.cuba.core.global.LoadContext;
|
||||
import com.haulmont.cuba.core.global.Metadata;
|
||||
import com.haulmont.cuba.core.global.View;
|
||||
import com.haulmont.cuba.gui.components.AbstractWindow;
|
||||
import com.haulmont.cuba.gui.components.SplitPanel;
|
||||
import com.haulmont.cuba.gui.components.Table;
|
||||
import com.haulmont.cuba.gui.components.TextField;
|
||||
import com.haulmont.cuba.gui.model.CollectionContainer;
|
||||
import com.haulmont.cuba.gui.model.CollectionLoader;
|
||||
import com.haulmont.cuba.gui.model.DataContextFactory;
|
||||
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
|
||||
import com.haulmont.cuba.security.entity.User;
|
||||
import com.haulmont.cuba.security.entity.UserRole;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class DcScreen3 extends AbstractWindow {
|
||||
|
||||
@Inject
|
||||
private TextField textField1;
|
||||
@Inject
|
||||
private SplitPanel split;
|
||||
@Inject
|
||||
private Metadata metadata;
|
||||
@Inject
|
||||
private DataContextFactory dataContextFactory;
|
||||
@Inject
|
||||
private ComponentsFactory componentsFactory;
|
||||
|
||||
private CollectionContainer<User> usersContainer;
|
||||
private CollectionContainer<UserRole> userRolesContainer;
|
||||
|
||||
@Override
|
||||
public void init(Map<String, Object> params) {
|
||||
Table usersTable = componentsFactory.createComponent(Table.class);
|
||||
usersTable.setSizeFull();
|
||||
split.add(usersTable);
|
||||
|
||||
Table userRolesTable = componentsFactory.createComponent(Table.class);
|
||||
userRolesTable.setSizeFull();
|
||||
split.add(userRolesTable);
|
||||
|
||||
MetaClass userMetaClass = metadata.getClassNN(User.class);
|
||||
usersTable.addColumn(new Table.Column(userMetaClass.getPropertyPath("login")));
|
||||
usersTable.addColumn(new Table.Column(userMetaClass.getPropertyPath("name")));
|
||||
|
||||
MetaClass userRoleMetaClass = metadata.getClassNN(UserRole.class);
|
||||
userRolesTable.addColumn(new Table.Column(userRoleMetaClass.getPropertyPath("role.name")));
|
||||
|
||||
usersContainer = dataContextFactory.createCollectionContainer(User.class);
|
||||
userRolesContainer = dataContextFactory.createCollectionContainer(UserRole.class);
|
||||
|
||||
CollectionLoader<User> loader = dataContextFactory.createCollectionLoader();
|
||||
loader.setContainer(usersContainer);
|
||||
loader.setQuery("select u from sec$User u order by u.name");
|
||||
loader.setView("user.edit");
|
||||
|
||||
loader.load();
|
||||
|
||||
usersContainer.addItemChangeListener(e -> {
|
||||
User user = e.getItem();
|
||||
userRolesContainer.setItems(user != null ? user.getUserRoles() : null);
|
||||
});
|
||||
|
||||
usersTable.setContainer(usersContainer);
|
||||
userRolesTable.setContainer(userRolesContainer);
|
||||
|
||||
textField1.setContainer(usersContainer, "name");
|
||||
}
|
||||
}
|
14
modules/web/src/com/haulmont/cuba/web/tmp/dc-screen-1.xml
Normal file
14
modules/web/src/com/haulmont/cuba/web/tmp/dc-screen-1.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
|
||||
class="com.haulmont.cuba.web.tmp.DcScreen1"
|
||||
caption="Screen 1"
|
||||
messagesPack="com.haulmont.cuba.web.tmp">
|
||||
|
||||
<dsContext/>
|
||||
|
||||
<layout expand="spacer" spacing="true">
|
||||
<textField id="textField1"/>
|
||||
<textField id="textField2"/>
|
||||
<button caption="Change" invoke="changeName"/>
|
||||
<label id="spacer"/>
|
||||
</layout>
|
||||
</window>
|
35
modules/web/src/com/haulmont/cuba/web/tmp/dc-screen-2.xml
Normal file
35
modules/web/src/com/haulmont/cuba/web/tmp/dc-screen-2.xml
Normal file
@ -0,0 +1,35 @@
|
||||
<!--
|
||||
~ Copyright (c) 2008-2017 Haulmont.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
|
||||
class="com.haulmont.cuba.web.tmp.DcScreen2"
|
||||
caption="Screen 2"
|
||||
messagesPack="com.haulmont.cuba.web.tmp">
|
||||
|
||||
<dsContext/>
|
||||
|
||||
<layout spacing="true">
|
||||
<textField id="textField1"/>
|
||||
<hbox spacing="true">
|
||||
<button caption="Next User" invoke="nextUser"/>
|
||||
<button caption="Sort" invoke="sort"/>
|
||||
<button caption="Filter" invoke="filter"/>
|
||||
<button caption="Add User" invoke="addUser"/>
|
||||
<button caption="Remove User" invoke="removeUser"/>
|
||||
<button caption="Save Changes" invoke="saveChanges"/>
|
||||
</hbox>
|
||||
</layout>
|
||||
</window>
|
33
modules/web/src/com/haulmont/cuba/web/tmp/dc-screen-3.xml
Normal file
33
modules/web/src/com/haulmont/cuba/web/tmp/dc-screen-3.xml
Normal file
@ -0,0 +1,33 @@
|
||||
<!--
|
||||
~ Copyright (c) 2008-2017 Haulmont.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
|
||||
class="com.haulmont.cuba.web.tmp.DcScreen3"
|
||||
caption="Screen 3"
|
||||
messagesPack="com.haulmont.cuba.web.tmp">
|
||||
|
||||
<dsContext/>
|
||||
|
||||
<layout spacing="true" expand="split">
|
||||
<textField id="textField1"/>
|
||||
<split id="split" orientation="horizontal" width="100%">
|
||||
|
||||
</split>
|
||||
<!--<button caption="Change" invoke="changeName"/>-->
|
||||
<!--<button caption="Next User" invoke="nextUser"/>-->
|
||||
<!--<label id="spacer"/>-->
|
||||
</layout>
|
||||
</window>
|
@ -18,6 +18,10 @@
|
||||
<menu-config xmlns="http://schemas.haulmont.com/cuba/menu.xsd">
|
||||
|
||||
<menu id="administration">
|
||||
<item id="dcScreen1"/>
|
||||
<item id="dcScreen2"/>
|
||||
<item id="dcScreen3"/>
|
||||
<separator/>
|
||||
<item id="sec$User.browse"/>
|
||||
<item id="sec$Group.browse"/>
|
||||
<item id="sec$Role.browse"/>
|
||||
|
@ -76,4 +76,11 @@
|
||||
|
||||
<screen id="loginWindow"
|
||||
template="/com/haulmont/cuba/web/app/loginwindow/loginwindow.xml"/>
|
||||
|
||||
<screen id="dcScreen1"
|
||||
template="/com/haulmont/cuba/web/tmp/dc-screen-1.xml"/>
|
||||
<screen id="dcScreen2"
|
||||
template="/com/haulmont/cuba/web/tmp/dc-screen-2.xml"/>
|
||||
<screen id="dcScreen3"
|
||||
template="/com/haulmont/cuba/web/tmp/dc-screen-3.xml"/>
|
||||
</screen-config>
|
Loading…
Reference in New Issue
Block a user