mirror of
https://gitee.com/jmix/cuba.git
synced 2024-11-30 10:17:43 +08:00
New presentation data layer (compositions), CollectionContainerOptions
This commit is contained in:
parent
aa694610d2
commit
d14876a5a3
@ -49,6 +49,11 @@
|
||||
</property>
|
||||
</view>
|
||||
|
||||
<view class="com.haulmont.cuba.security.entity.UserRole" name="tmp.user.edit">
|
||||
<property name="user" view="_minimal"/>
|
||||
<property name="role" view="_minimal"/>
|
||||
</view>
|
||||
|
||||
<view class="com.haulmont.cuba.security.entity.User" name="user.browse" extends="_local" systemProperties="true">
|
||||
<property name="group" view="_minimal"/>
|
||||
</view>
|
||||
|
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2018 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.data.options;
|
||||
|
||||
import com.haulmont.bali.events.EventHub;
|
||||
import com.haulmont.bali.events.Subscription;
|
||||
import com.haulmont.chile.core.model.MetaClass;
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
import com.haulmont.cuba.gui.components.data.BindingState;
|
||||
import com.haulmont.cuba.gui.components.data.EntityOptionsSource;
|
||||
import com.haulmont.cuba.gui.components.data.OptionsSource;
|
||||
import com.haulmont.cuba.gui.model.CollectionContainer;
|
||||
import com.haulmont.cuba.gui.model.CollectionLoader;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class CollectionContainerOptions<E extends Entity<K>, K> implements OptionsSource<E>, EntityOptionsSource<E> {
|
||||
|
||||
protected CollectionContainer<E> container;
|
||||
|
||||
private CollectionLoader loader;
|
||||
|
||||
protected EventHub events = new EventHub();
|
||||
|
||||
protected BindingState state = BindingState.INACTIVE;
|
||||
|
||||
protected E deferredSelectedItem;
|
||||
|
||||
public CollectionContainerOptions(CollectionContainer<E> container, @Nullable CollectionLoader loader) {
|
||||
this.container = container;
|
||||
this.loader = loader;
|
||||
|
||||
this.container.addCollectionChangeListener(this::containerCollectionChanged);
|
||||
this.container.addItemPropertyChangeListener(this::containerItemPropertyChanged);
|
||||
}
|
||||
|
||||
protected void containerCollectionChanged(CollectionContainer.CollectionChangeEvent<E> e) {
|
||||
if (deferredSelectedItem != null) {
|
||||
container.setItem(deferredSelectedItem);
|
||||
deferredSelectedItem = null;
|
||||
}
|
||||
events.publish(OptionsChangeEvent.class, new OptionsChangeEvent<>(this));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void containerItemPropertyChanged(CollectionContainer.ItemPropertyChangeEvent<E> e) {
|
||||
events.publish(ValueChangeEvent.class, new ValueChangeEvent(this, e.getPrevValue(), e.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaClass getEntityMetaClass() {
|
||||
return container.getEntityMetaClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectedItem(E item) {
|
||||
if (item == null) {
|
||||
container.setItem(null);
|
||||
} else {
|
||||
if (container.getItems().size() > 0) {
|
||||
container.setItem(item);
|
||||
} else {
|
||||
this.deferredSelectedItem = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsItem(E item) {
|
||||
return item != null && container.getItemIndex(item.getId()) > -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(E item) {
|
||||
container.replaceItem(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
if (loader != null)
|
||||
loader.load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh(Map<String, Object> parameters) {
|
||||
if (loader != null)
|
||||
loader.load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<E> getOptions() {
|
||||
return container.getItems().stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindingState getState() {
|
||||
return BindingState.ACTIVE;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Subscription addStateChangeListener(Consumer<StateChangeEvent<E>> listener) {
|
||||
return events.subscribe(StateChangeEvent.class, (Consumer) listener);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Subscription addValueChangeListener(Consumer<ValueChangeEvent<E>> listener) {
|
||||
return events.subscribe(ValueChangeEvent.class, (Consumer) listener);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Subscription addOptionsChangeListener(Consumer<OptionsChangeEvent<E>> listener) {
|
||||
return events.subscribe(OptionsChangeEvent.class, (Consumer) listener);
|
||||
}
|
||||
}
|
@ -43,6 +43,8 @@ public interface CollectionContainer<E extends Entity> extends InstanceContainer
|
||||
|
||||
int getItemIndex(Object entityId);
|
||||
|
||||
void replaceItem(E entity);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -17,6 +17,7 @@
|
||||
package com.haulmont.cuba.gui.model.impl;
|
||||
|
||||
import com.haulmont.bali.events.Subscription;
|
||||
import com.haulmont.bali.util.Preconditions;
|
||||
import com.haulmont.chile.core.model.MetaClass;
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
import com.haulmont.cuba.gui.model.CollectionContainer;
|
||||
@ -48,8 +49,8 @@ public class CollectionContainerImpl<E extends Entity>
|
||||
@Override
|
||||
public void setItem(@Nullable E item) {
|
||||
if (item != null) {
|
||||
Integer idx = getItemIndex(item.getId());
|
||||
if (idx < -1) {
|
||||
int idx = getItemIndex(item.getId());
|
||||
if (idx == -1) {
|
||||
throw new IllegalArgumentException("CollectionContainer does not contain " + item);
|
||||
}
|
||||
E existingItem = collection.get(idx);
|
||||
@ -89,7 +90,7 @@ public class CollectionContainerImpl<E extends Entity>
|
||||
@Nullable
|
||||
@Override
|
||||
public E getItemOrNull(Object entityId) {
|
||||
Integer idx = getItemIndex(entityId);
|
||||
int idx = getItemIndex(entityId);
|
||||
return idx != -1 ? collection.get(idx) : null;
|
||||
}
|
||||
|
||||
@ -99,6 +100,27 @@ public class CollectionContainerImpl<E extends Entity>
|
||||
return idx != null ? idx : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceItem(E entity) {
|
||||
Preconditions.checkNotNullArgument(entity, "entity is null");
|
||||
Object id = entity.getId();
|
||||
int idx = getItemIndex(id);
|
||||
if (idx > -1) {
|
||||
E prev = collection.get(idx);
|
||||
detachListener(prev);
|
||||
if (prev == getItemOrNull()) {
|
||||
this.item = entity;
|
||||
fireItemChanged(prev);
|
||||
}
|
||||
collection.set(idx, entity);
|
||||
} else {
|
||||
collection.add(entity);
|
||||
}
|
||||
attachListener(entity);
|
||||
buildIdMap();
|
||||
fireCollectionChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E getItem(Object entityId) {
|
||||
E item = getItemOrNull(entityId);
|
||||
@ -149,8 +171,23 @@ public class CollectionContainerImpl<E extends Entity>
|
||||
}
|
||||
|
||||
protected void clearItemIfNotExists() {
|
||||
if (item != null && getItemIndex(item.getId()) == -1) {
|
||||
setItem(null);
|
||||
if (item != null) {
|
||||
int idx = getItemIndex(item.getId());
|
||||
if (idx == -1) {
|
||||
// item doesn't exist in the collection
|
||||
E prevItem = item;
|
||||
detachListener(prevItem);
|
||||
item = null;
|
||||
fireItemChanged(prevItem);
|
||||
} else {
|
||||
E newItem = collection.get(idx);
|
||||
if (newItem != item) {
|
||||
E prevItem = item;
|
||||
detachListener(prevItem);
|
||||
item = newItem;
|
||||
fireItemChanged(prevItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ public class InstanceContainerImpl<T extends Entity> implements InstanceContaine
|
||||
final MetaClass aClass = item.getMetaClass();
|
||||
if (!aClass.equals(entityMetaClass) && !entityMetaClass.getDescendants().contains(aClass)) {
|
||||
throw new DevelopmentException(String.format("Invalid item's metaClass '%s'", aClass),
|
||||
ParamsMap.of("datasource", toString(), "metaClass", aClass));
|
||||
ParamsMap.of("container", toString(), "metaClass", aClass));
|
||||
}
|
||||
attachListener(item);
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ public class ScreenDataXmlLoader {
|
||||
|
||||
for (Element el : element.elements()) {
|
||||
if (el.getName().equals("collection")) {
|
||||
loadNestedContainer(screenData, el);
|
||||
loadCollectionContainer(screenData, el);
|
||||
} else if (el.getName().equals("instance")) {
|
||||
loadInstanceContainer(screenData, el);
|
||||
}
|
||||
@ -80,7 +80,7 @@ public class ScreenDataXmlLoader {
|
||||
}
|
||||
}
|
||||
|
||||
protected void loadNestedContainer(ScreenData screenData, Element element) {
|
||||
protected void loadCollectionContainer(ScreenData screenData, Element element) {
|
||||
String containerId = getRequiredAttr(element, "id");
|
||||
|
||||
CollectionContainer<Entity> container = factory.createCollectionContainer(getEntityClass(element));
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.haulmont.cuba.gui.model.impl;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.haulmont.bali.events.EventRouter;
|
||||
import com.haulmont.bali.util.Numbers;
|
||||
import com.haulmont.bali.util.Preconditions;
|
||||
@ -25,6 +26,7 @@ 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 org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@ -139,6 +141,7 @@ public class StandardDataContext implements DataContext {
|
||||
if (managedInstance != null) {
|
||||
if (managedInstance != entity) {
|
||||
copyState(entity, managedInstance);
|
||||
copyReferences(entity, managedInstance);
|
||||
}
|
||||
return managedInstance;
|
||||
}
|
||||
@ -152,6 +155,31 @@ public class StandardDataContext implements DataContext {
|
||||
return entity;
|
||||
}
|
||||
|
||||
protected void copyReferences(Entity srcEntity, Entity dstEntity) {
|
||||
EntityStates entityStates = getEntityStates();
|
||||
|
||||
for (MetaProperty property : getMetadata().getClassNN(srcEntity.getClass()).getProperties()) {
|
||||
String propertyName = property.getName();
|
||||
if (!property.getRange().isClass()
|
||||
|| property.getRange().getCardinality().isMany()
|
||||
|| !entityStates.isLoaded(srcEntity, propertyName)
|
||||
|| !entityStates.isLoaded(dstEntity, propertyName)) {
|
||||
continue;
|
||||
}
|
||||
Object value = srcEntity.getValue(propertyName);
|
||||
boolean srcNew = entityStates.isNew(srcEntity);
|
||||
if (!srcNew || value != null) {
|
||||
if (value == null) {
|
||||
dstEntity.setValue(propertyName, null);
|
||||
} else {
|
||||
Entity srcRef = (Entity) value;
|
||||
Entity dstRef = internalMerge(srcRef);
|
||||
((AbstractInstance) dstEntity).setValue(propertyName, dstRef, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* (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
|
||||
@ -426,6 +454,52 @@ public class StandardDataContext implements DataContext {
|
||||
return resultList;
|
||||
}
|
||||
|
||||
public String printContent() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Map.Entry<Class<?>, Map<Object, Entity>> entry : content.entrySet()) {
|
||||
sb.append("=== ").append(entry.getKey().getSimpleName()).append(" ===\n");
|
||||
for (Entity entity : entry.getValue().values()) {
|
||||
sb.append(printEntity(entity, 1, Sets.newIdentityHashSet())).append('\n');
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
protected String printEntity(Entity entity, int level, Set<Entity> visited) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(printObject(entity)).append(" ").append(entity.toString()).append("\n");
|
||||
|
||||
if (visited.contains(entity)) {
|
||||
return sb.toString();
|
||||
}
|
||||
visited.add(entity);
|
||||
|
||||
for (MetaProperty property : getMetadata().getClassNN(entity.getClass()).getProperties()) {
|
||||
if (!property.getRange().isClass() || !getEntityStates().isLoaded(entity, property.getName()))
|
||||
continue;
|
||||
Object value = entity.getValue(property.getName());
|
||||
String prefix = StringUtils.repeat(" ", level);
|
||||
if (value instanceof Entity) {
|
||||
String str = printEntity((Entity) value, level + 1, visited);
|
||||
if (!str.equals(""))
|
||||
sb.append(prefix).append(str);
|
||||
} else if (value instanceof Collection) {
|
||||
sb.append(prefix).append(value.getClass().getSimpleName()).append("[\n");
|
||||
for (Object item : (Collection) value) {
|
||||
String str = printEntity((Entity) item, level + 1, visited);
|
||||
if (!str.equals(""))
|
||||
sb.append(prefix).append(str);
|
||||
}
|
||||
sb.append(prefix).append("]\n");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
protected String printObject(Object object) {
|
||||
return "{" + object.getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(object)) + "}";
|
||||
}
|
||||
|
||||
protected class ChangeListener implements Instance.PropertyChangeListener {
|
||||
@Override
|
||||
public void propertyChanged(Instance.PropertyChangeEvent e) {
|
||||
|
@ -22,8 +22,15 @@ import com.haulmont.cuba.gui.components.CaptionMode;
|
||||
import com.haulmont.cuba.gui.components.DatasourceComponent;
|
||||
import com.haulmont.cuba.gui.components.Frame;
|
||||
import com.haulmont.cuba.gui.components.LookupField;
|
||||
import com.haulmont.cuba.gui.components.data.options.CollectionContainerOptions;
|
||||
import com.haulmont.cuba.gui.components.data.value.CollectionContainerTableSource;
|
||||
import com.haulmont.cuba.gui.data.CollectionDatasource;
|
||||
import com.haulmont.cuba.gui.data.Datasource;
|
||||
import com.haulmont.cuba.gui.model.CollectionContainer;
|
||||
import com.haulmont.cuba.gui.model.InstanceContainer;
|
||||
import com.haulmont.cuba.gui.model.ScreenData;
|
||||
import com.haulmont.cuba.gui.screen.FrameOwner;
|
||||
import com.haulmont.cuba.gui.screen.ScreenUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dom4j.Element;
|
||||
|
||||
@ -131,6 +138,24 @@ public class LookupFieldLoader extends AbstractFieldLoader<LookupField> {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void loadContainer(LookupField component, Element element) {
|
||||
super.loadContainer(component, element);
|
||||
|
||||
String containerId = element.attributeValue("optionsContainer");
|
||||
if (containerId != null) {
|
||||
FrameOwner frameOwner = context.getFrame().getFrameOwner();
|
||||
ScreenData screenData = ScreenUtils.getScreenData(frameOwner);
|
||||
InstanceContainer container = screenData.getContainer(containerId);
|
||||
if (!(container instanceof CollectionContainer)) {
|
||||
throw new GuiDevelopmentException("Not a CollectionContainer: " + containerId, context.getCurrentFrameId());
|
||||
}
|
||||
component.setOptionsSource(
|
||||
new CollectionContainerOptions((CollectionContainer) container, screenData.findLoaderOf(container)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadDatasource(DatasourceComponent component, Element element) {
|
||||
super.loadDatasource(component, element);
|
||||
|
@ -18,6 +18,7 @@ package com.haulmont.cuba.web.gui.components;
|
||||
|
||||
import com.haulmont.bali.events.Subscription;
|
||||
import com.haulmont.chile.core.datatypes.Datatype;
|
||||
import com.haulmont.chile.core.model.Range;
|
||||
import com.haulmont.cuba.core.global.UserSessionSource;
|
||||
import com.haulmont.cuba.gui.components.data.DataAwareComponentsTools;
|
||||
import com.haulmont.cuba.gui.components.TextField;
|
||||
@ -100,7 +101,13 @@ public class WebTextField<V> extends WebV8AbstractField<CubaTextField, String, V
|
||||
if (valueBinding != null
|
||||
&& valueBinding.getSource() instanceof EntityValueSource) {
|
||||
EntityValueSource entityValueSource = (EntityValueSource) valueBinding.getSource();
|
||||
Datatype<V> propertyDataType = entityValueSource.getMetaPropertyPath().getRange().asDatatype();
|
||||
Range range = entityValueSource.getMetaPropertyPath().getRange();
|
||||
if (!range.isDatatype()) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Property '%s' has %s. TextField can be bound only to a simple data type",
|
||||
entityValueSource.getMetaPropertyPath().getMetaProperty().getName(), range));
|
||||
}
|
||||
Datatype<V> propertyDataType = range.asDatatype();
|
||||
return nullToEmpty(propertyDataType.format(modelValue));
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ public class DcScreen5 extends Screen {
|
||||
dcScreen6.addAfterCloseListener(afterCloseEvent -> {
|
||||
CloseAction closeAction = afterCloseEvent.getCloseAction();
|
||||
if ((closeAction instanceof StandardCloseAction) && ((StandardCloseAction) closeAction).getActionId().equals(Window.COMMIT_ACTION_ID)) {
|
||||
usersCont.getMutableItems().add(0, (User) dcScreen6.getEditedEntity());
|
||||
usersCont.getMutableItems().add(0, dcScreen6.getEditedEntity());
|
||||
}
|
||||
});
|
||||
screens.show(dcScreen6);
|
||||
|
@ -18,22 +18,26 @@ package com.haulmont.cuba.web.tmp;
|
||||
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
import com.haulmont.cuba.core.global.EntityStates;
|
||||
import com.haulmont.cuba.gui.Screens;
|
||||
import com.haulmont.cuba.gui.components.Button;
|
||||
import com.haulmont.cuba.gui.components.Window;
|
||||
import com.haulmont.cuba.gui.model.CollectionContainer;
|
||||
import com.haulmont.cuba.gui.model.InstanceContainer;
|
||||
import com.haulmont.cuba.gui.screen.StandardEditor;
|
||||
import com.haulmont.cuba.gui.screen.Subscribe;
|
||||
import com.haulmont.cuba.gui.screen.UiController;
|
||||
import com.haulmont.cuba.gui.screen.UiDescriptor;
|
||||
import com.haulmont.cuba.gui.screen.*;
|
||||
import com.haulmont.cuba.gui.screen.events.BeforeShowEvent;
|
||||
import com.haulmont.cuba.security.entity.User;
|
||||
import com.haulmont.cuba.security.entity.UserRole;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@UiController("dcScreen6")
|
||||
@UiDescriptor("dc-screen-6.xml")
|
||||
public class DcScreen6 extends StandardEditor<User> {
|
||||
|
||||
@Inject
|
||||
protected EntityStates entityStates;
|
||||
protected Screens screens;
|
||||
@Inject
|
||||
protected CollectionContainer<UserRole> userRolesCont;
|
||||
|
||||
@Override
|
||||
protected InstanceContainer<Entity> getEditedEntityContainer() {
|
||||
@ -45,6 +49,25 @@ public class DcScreen6 extends StandardEditor<User> {
|
||||
getScreenData().loadAll();
|
||||
}
|
||||
|
||||
@Subscribe("editBtn")
|
||||
private void onEditClick(Button.ClickEvent event) {
|
||||
UserRole selectedUserRole = userRolesCont.getItemOrNull();
|
||||
if (selectedUserRole != null) {
|
||||
TmpUserRoleEdit userRoleEdit = screens.create(TmpUserRoleEdit.class, OpenMode.THIS_TAB);
|
||||
userRoleEdit.setEntityToEdit(selectedUserRole);
|
||||
|
||||
ScreenUtils.getScreenData(userRoleEdit).getDataContext().setParent(getScreenData().getDataContext());
|
||||
|
||||
userRoleEdit.addAfterCloseListener(afterCloseEvent -> {
|
||||
CloseAction closeAction = afterCloseEvent.getCloseAction();
|
||||
if ((closeAction instanceof StandardCloseAction) && ((StandardCloseAction) closeAction).getActionId().equals(Window.COMMIT_ACTION_ID)) {
|
||||
userRolesCont.replaceItem(userRoleEdit.getEditedEntity());
|
||||
}
|
||||
});
|
||||
screens.show(userRoleEdit);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe("okBtn")
|
||||
protected void onOkClick(Button.ClickEvent event) {
|
||||
closeWithCommit();
|
||||
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2018 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.core.entity.Entity;
|
||||
import com.haulmont.cuba.gui.components.Button;
|
||||
import com.haulmont.cuba.gui.model.InstanceContainer;
|
||||
import com.haulmont.cuba.gui.screen.StandardEditor;
|
||||
import com.haulmont.cuba.gui.screen.Subscribe;
|
||||
import com.haulmont.cuba.gui.screen.UiController;
|
||||
import com.haulmont.cuba.gui.screen.UiDescriptor;
|
||||
import com.haulmont.cuba.gui.screen.events.BeforeShowEvent;
|
||||
import com.haulmont.cuba.security.entity.UserRole;
|
||||
|
||||
@UiController("tmpUserRoleEdit")
|
||||
@UiDescriptor("tmp-user-role-edit.xml")
|
||||
public class TmpUserRoleEdit extends StandardEditor<UserRole> {
|
||||
|
||||
@Override
|
||||
protected InstanceContainer<Entity> getEditedEntityContainer() {
|
||||
return getScreenData().getContainer("userRoleCont");
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
protected void beforeShow(BeforeShowEvent event) {
|
||||
getScreenData().loadAll();
|
||||
}
|
||||
|
||||
@Subscribe("okBtn")
|
||||
protected void onOkClick(Button.ClickEvent event) {
|
||||
closeWithCommit();
|
||||
}
|
||||
|
||||
@Subscribe("cancelBtn")
|
||||
protected void onCancelClick(Button.ClickEvent event) {
|
||||
close(WINDOW_CLOSE_ACTION);
|
||||
}
|
||||
}
|
@ -14,13 +14,13 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<window caption="Screen 5">
|
||||
<window caption="User Browser">
|
||||
|
||||
<data>
|
||||
<collection id="usersCont"
|
||||
class="com.haulmont.cuba.security.entity.User" view="user.browse">
|
||||
|
||||
<loader id="usersLoader">
|
||||
<loader id="usersLoader">
|
||||
<query>
|
||||
select u from sec$User u
|
||||
order by u.name
|
||||
|
@ -14,21 +14,35 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<window caption="Screen 6">
|
||||
<window caption="User Editor">
|
||||
|
||||
<data>
|
||||
<instance id="userCont" class="com.haulmont.cuba.security.entity.User" view="user.edit">
|
||||
<loader id="userLoader"/>
|
||||
|
||||
<collection id="userRolesCont" property="userRoles"/>
|
||||
</instance>
|
||||
</data>
|
||||
|
||||
<layout spacing="true" expand="spacer">
|
||||
<layout spacing="true" expand="userRolesTable">
|
||||
<textField id="loginField" container="userCont" property="login"/>
|
||||
<textField id="nameField" container="userCont" property="name"/>
|
||||
<table id="userRolesTable"
|
||||
width="100%">
|
||||
<buttonsPanel>
|
||||
<!--<button id="createBtn" caption="Create"/>-->
|
||||
<button id="editBtn" caption="Edit"/>
|
||||
<!--<button id="removeBtn" caption="Remove"/>-->
|
||||
</buttonsPanel>
|
||||
<columns>
|
||||
<column id="role.name"/>
|
||||
<column id="role.locName"/>
|
||||
</columns>
|
||||
<rows container="userRolesCont"/>
|
||||
</table>
|
||||
<hbox spacing="true">
|
||||
<button id="okBtn" caption="OK"/>
|
||||
<button id="cancelBtn" caption="Cancel"/>
|
||||
</hbox>
|
||||
<label id="spacer"/>
|
||||
</layout>
|
||||
</window>
|
||||
|
@ -0,0 +1,43 @@
|
||||
<!--
|
||||
~ Copyright (c) 2008-2018 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 caption="User Role">
|
||||
|
||||
<data>
|
||||
<instance id="userRoleCont" class="com.haulmont.cuba.security.entity.UserRole" view="tmp.user.edit">
|
||||
<loader id="userRoleLoader"/>
|
||||
</instance>
|
||||
|
||||
<collection id="rolesCont" class="com.haulmont.cuba.security.entity.Role" view="_minimal">
|
||||
<loader id="rolesLoader">
|
||||
<query>
|
||||
select r from sec$Role r
|
||||
order by r.name
|
||||
</query>
|
||||
</loader>
|
||||
</collection>
|
||||
</data>
|
||||
|
||||
<layout spacing="true" expand="spacer">
|
||||
<textField id="userField" container="userRoleCont" property="user.login"/>
|
||||
<lookupField id="roleField" container="userRoleCont" property="role" optionsContainer="rolesCont"/>
|
||||
<hbox spacing="true">
|
||||
<button id="okBtn" caption="OK"/>
|
||||
<button id="cancelBtn" caption="Cancel"/>
|
||||
</hbox>
|
||||
<label id="spacer"/>
|
||||
</layout>
|
||||
</window>
|
@ -50,4 +50,14 @@ public class Customer extends StandardEntity {
|
||||
public void setStatus(Status status) {
|
||||
this.status = status.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Customer{" +
|
||||
"id=" + id +
|
||||
", version=" + version +
|
||||
", name='" + name + '\'' +
|
||||
", status='" + status + '\'' +
|
||||
"}@" + Integer.toHexString(System.identityHashCode(this));
|
||||
}
|
||||
}
|
||||
|
@ -102,4 +102,15 @@ public class Order extends StandardEntity {
|
||||
public void setOrderLines(List<OrderLine> orderLines) {
|
||||
this.orderLines = orderLines;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Order{" +
|
||||
"id=" + id +
|
||||
", version=" + version +
|
||||
", number='" + number + '\'' +
|
||||
", date=" + date +
|
||||
", amount=" + amount +
|
||||
"}@" + Integer.toHexString(System.identityHashCode(this));
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,10 @@ public class OrderLine extends StandardEntity {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + '@' + System.identityHashCode(this);
|
||||
return "OrderLine{" +
|
||||
"id=" + id +
|
||||
", version=" + version +
|
||||
", quantity=" + quantity +
|
||||
"}@" + Integer.toHexString(System.identityHashCode(this));
|
||||
}
|
||||
}
|
@ -63,4 +63,14 @@ public class Product extends StandardEntity {
|
||||
public void setTags(List<ProductTag> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Product{" +
|
||||
"id=" + id +
|
||||
", version=" + version +
|
||||
", name='" + name + '\'' +
|
||||
", price=" + price +
|
||||
"}@" + Integer.toHexString(System.identityHashCode(this));
|
||||
}
|
||||
}
|
||||
|
@ -38,4 +38,13 @@ public class ProductTag extends StandardEntity {
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ProductTag{" +
|
||||
"id=" + id +
|
||||
", version=" + version +
|
||||
", name='" + name + '\'' +
|
||||
"}@" + Integer.toHexString(System.identityHashCode(this));
|
||||
}
|
||||
}
|
||||
|
@ -340,6 +340,29 @@ class CompositionTest extends WebSpec {
|
||||
committed.upd.find { it == orderLine11}.quantity == 12
|
||||
}
|
||||
|
||||
def "one level of composition - changed reference"() {
|
||||
|
||||
def orderScreen = new OrderScreen()
|
||||
def orderLineScreen = new LineScreen()
|
||||
|
||||
orderScreen.open(order1)
|
||||
|
||||
orderScreen.linesCnt.item = orderLine11
|
||||
orderLineScreen.open(orderScreen.linesCnt.item, orderScreen.dataContext)
|
||||
orderLineScreen.lineCnt.item.product = product12
|
||||
|
||||
when:
|
||||
|
||||
def committed = mockCommit()
|
||||
orderLineScreen.dataContext.commit()
|
||||
orderScreen.dataContext.commit()
|
||||
|
||||
then:
|
||||
|
||||
committed.upd.size() == 1
|
||||
committed.upd[0].product == product12
|
||||
}
|
||||
|
||||
def "one level of composition - remove item"() {
|
||||
|
||||
def orderScreen = new OrderScreen()
|
||||
|
@ -16,7 +16,9 @@
|
||||
|
||||
package spec.cuba.web.datacontext
|
||||
|
||||
import com.haulmont.cuba.client.testsupport.TestSupport
|
||||
import com.haulmont.cuba.core.app.DataService
|
||||
import com.haulmont.cuba.core.entity.Entity
|
||||
import com.haulmont.cuba.core.global.CommitContext
|
||||
import com.haulmont.cuba.core.global.EntityStates
|
||||
import com.haulmont.cuba.core.global.Metadata
|
||||
@ -26,8 +28,8 @@ import com.haulmont.cuba.gui.model.impl.DataContextAccessor
|
||||
import com.haulmont.cuba.security.entity.Role
|
||||
import com.haulmont.cuba.security.entity.User
|
||||
import com.haulmont.cuba.security.entity.UserRole
|
||||
import com.haulmont.cuba.web.testmodel.sales.OrderLine
|
||||
import com.haulmont.cuba.web.testmodel.sales.Product
|
||||
import com.haulmont.cuba.web.testmodel.sales.ProductTag
|
||||
import com.haulmont.cuba.web.testsupport.TestContainer
|
||||
import com.haulmont.cuba.web.testsupport.TestServiceProxy
|
||||
import org.junit.ClassRule
|
||||
@ -603,36 +605,36 @@ class DataContextTest extends Specification {
|
||||
removed.isEmpty()
|
||||
}
|
||||
|
||||
def "track many-to-many"() {
|
||||
def "commit returns different reference"() {
|
||||
DataContext context = factory.createDataContext()
|
||||
|
||||
Product product1 = new Product(name: "p1", price: 100)
|
||||
Product product2 = new Product(name: "p2", price: 200)
|
||||
OrderLine line = new OrderLine(quantity: 10, product: product1)
|
||||
makeDetached(product1, product2, line)
|
||||
context.merge(line)
|
||||
|
||||
Collection committed = []
|
||||
TestServiceProxy.mock(DataService, Mock(DataService) {
|
||||
commit(_) >> Collections.emptySet()
|
||||
commit(_) >> { CommitContext cc ->
|
||||
committed.addAll(cc.commitInstances)
|
||||
def entities = TestServiceProxy.getDefault(DataService).commit(cc)
|
||||
entities.find { it == line }.product = TestSupport.reserialize(product2)
|
||||
entities
|
||||
}
|
||||
})
|
||||
|
||||
Product product = new Product(name: 'p1', price: 100, tags: [])
|
||||
makeDetached(product)
|
||||
context.merge(product)
|
||||
|
||||
ProductTag tag = new ProductTag(name: 't1')
|
||||
context.merge(tag)
|
||||
|
||||
when:
|
||||
|
||||
Collection modified = []
|
||||
|
||||
context.addPreCommitListener({ e->
|
||||
modified.addAll(e.modifiedInstances)
|
||||
})
|
||||
|
||||
product.tags.add(tag)
|
||||
|
||||
line.quantity = 20
|
||||
context.commit()
|
||||
|
||||
then:
|
||||
|
||||
modified.contains(product)
|
||||
modified.contains(tag)
|
||||
def line1 = context.find(OrderLine, line.id)
|
||||
line1.quantity == 20
|
||||
line1.product == product2
|
||||
|
||||
}
|
||||
|
||||
private <T> T createDetached(Class<T> entityClass) {
|
||||
@ -645,6 +647,12 @@ class DataContextTest extends Specification {
|
||||
entityStates.makeDetached(entity)
|
||||
}
|
||||
|
||||
private void makeDetached(Entity... entities) {
|
||||
for (Entity entity : entities) {
|
||||
entityStates.makeDetached(entity)
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> T makeSaved(T entity) {
|
||||
TestServiceProxy.getDefault(DataService).commit(new CommitContext().addInstanceToCommit(entity))[0] as T
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user