diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/TransferredDataSetHandlerTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/TransferredDataSetHandlerTest.java index 4347124199aaa1a6f0fd1f45c94a240a806bb534..51ec503c86e44efab0f8bcb203b9029b0b93c61f 100644 --- a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/TransferredDataSetHandlerTest.java +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/TransferredDataSetHandlerTest.java @@ -59,12 +59,12 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService; import ch.systemsx.cisd.openbis.dss.generic.shared.dto.DataSetInformation; import ch.systemsx.cisd.openbis.dss.generic.shared.utils.PluginUtilTest; import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService; -import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetType; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseInstance; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.FileFormatType; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Group; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.LocatorType; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Person; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Project; diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/UploadingCommandTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/UploadingCommandTest.java index aa6fa5172932f5b25ae85dd4c4f6020a834318f7..b2703ec0fb6b54d69c06dc0b5e042520a5216b99 100644 --- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/UploadingCommandTest.java +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/UploadingCommandTest.java @@ -64,6 +64,7 @@ import ch.systemsx.cisd.openbis.generic.shared.translator.ExternalDataTranslator * @author Franz-Josef Elmer */ @Friend(toClasses = UploadingCommand.class) +// TODO 2009-09-10, Piotr Buczek: write tests with many parents public class UploadingCommandTest extends AssertJUnit { private static final String ZIP_FILENAME = "myData"; @@ -170,7 +171,7 @@ public class UploadingCommandTest extends AssertJUnit dataSetTypePE.setCode("D"); externalData.setDataSetType(dataSetTypePE); externalData.setExperiment(createExperiment()); - externalData.setParent(createParent("parent")); + externalData.addParent(createParent("parent")); externalData.setDataStore(new DataStorePE()); return externalData; } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/Dict.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/Dict.java index 5264232a3413e09561aa5a4d0ddf17136de3a796..cee44e5c67a58b6c483b5047f9cd8342d7a6f93f 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/Dict.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/Dict.java @@ -507,6 +507,14 @@ public abstract class Dict public static final String SAMPLES_LIST = "samples_list"; + // + // Data Set Edition + // + + public static final String PARENTS = "parents"; + + public static final String PARENTS_EMPTY = "parents_empty"; + // // Vocabulary Browser // diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBO.java index 455a6f2faf370e7b6e6cfe68d266ba4e1bcd7fb7..e13e05ac1f4cca8dc16c10fd72bbc1a0d67928f2 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBO.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBO.java @@ -16,11 +16,16 @@ package ch.systemsx.cisd.openbis.generic.server.business.bo; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Date; +import java.util.HashSet; import java.util.List; import java.util.Set; import ch.rinn.restrictions.Friend; +import ch.systemsx.cisd.common.collections.CollectionUtils; import ch.systemsx.cisd.common.exceptions.UserFailureException; import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory; import ch.systemsx.cisd.openbis.generic.server.dataaccess.IExternalDataDAO; @@ -156,11 +161,14 @@ public class ExternalDataBO extends AbstractExternalDataBusinessObject implement defineDataSetProperties(externalData, convertToDataSetProperties(data .getDataSetProperties())); - final String parentDataSetCode = data.tryToGetParentDataSetCode(); - if (parentDataSetCode != null) + final List<String> parentDataSetCodes = data.getParentDataSetCodes(); + if (parentDataSetCodes != null) { - final DataPE parent = getOrCreateParentData(parentDataSetCode, dataStore, sample); - externalData.setParent(parent); + for (String parentCode : parentDataSetCodes) + { + final DataPE parent = getOrCreateParentData(parentCode, dataStore, sample); + externalData.addParent(parent); + } } externalData.setSample(sample); @@ -266,7 +274,7 @@ public class ExternalDataBO extends AbstractExternalDataBusinessObject implement } updateProperties(updates.getProperties()); updateSample(updates.getSampleIdentifierOrNull()); - updateParent(updates.getParentDatasetCodeOrNull()); + updateParents(updates.getModifiedParentDatasetCodesOrNull()); // TODO 2009-09-10, Piotr Buczek: write updateExperiment when we allow to change it updateFileFormatType(updates.getFileFormatTypeCode()); entityPropertiesConverter.checkMandatoryProperties(externalData.getProperties(), @@ -288,36 +296,113 @@ public class ExternalDataBO extends AbstractExternalDataBusinessObject implement type, properties, registrator)); } - private void updateParent(String parentDatasetCodeOrNull) + private void updateParents(String[] modifiedParentDatasetCodesOrNull) { - DataPE parentOrNull = null; - if (parentDatasetCodeOrNull != null) + if (modifiedParentDatasetCodesOrNull == null) + { + return; // parents were not changed + } else { - if (parentDatasetCodeOrNull.equals(externalData.getCode())) + final Set<DataPE> currentParents = externalData.getParents(); + final Set<String> currentParentCodes = extractCodes(currentParents); + final Set<String> newCodes = asSet(modifiedParentDatasetCodesOrNull); + newCodes.removeAll(currentParentCodes); + + // quick check for direct cycle + if (newCodes.contains(externalData.getCode())) { - throw new UserFailureException("Data set '" + parentDatasetCodeOrNull + throw new UserFailureException("Data set '" + externalData.getCode() + "' can not be its own parent."); } - parentOrNull = getExternalDataDAO().tryToFindDataSetByCode(parentDatasetCodeOrNull); - if (parentOrNull == null) + // TODO 2009-09-11, Piotr Buczek: check cycles + + final List<DataPE> parentsToAdd = findDataSetsByCodes(newCodes); + addParents(parentsToAdd); + + final Set<String> removedCodes = asSet(modifiedParentDatasetCodesOrNull); + removedCodes.removeAll(currentParentCodes); + removeParents(filterDataSets(currentParents, removedCodes)); + } + } + + private Collection<DataPE> filterDataSets(Collection<DataPE> dataSets, + Collection<String> seekenCodes) + { + Collection<DataPE> result = new ArrayList<DataPE>(); + for (DataPE dataSet : dataSets) + { + if (seekenCodes.contains(dataSet.getCode())) { - throw new UserFailureException(String.format( - "Data set with code '%s' does not exist.", parentDatasetCodeOrNull)); + result.add(dataSet); } - ExperimentPE parentExperiment = parentOrNull.getExperiment(); - ExperimentPE experiment = externalData.getExperiment(); - if (parentExperiment.equals(experiment) == false) + } + return result; + } + + private void addParents(Collection<DataPE> parentsToAdd) + { + for (DataPE parent : parentsToAdd) + { + externalData.addParent(parent); + } + } + + private void removeParents(Collection<DataPE> parentsToRemove) + { + for (DataPE parent : parentsToRemove) + { + externalData.removeParent(parent); + } + } + + private List<DataPE> findDataSetsByCodes(Set<String> codes) + { + // GroupPE group = experiment.getProject().getGroup(); + // return findUnassignedSamples(getSampleDAO(), codesToAdd, group); + // + final IExternalDataDAO dao = getExternalDataDAO(); + final List<DataPE> dataSets = new ArrayList<DataPE>(); + final List<String> missingDataSetCodes = new ArrayList<String>(); + for (String code : codes) + { + DataPE dataSetOrNull = dao.tryToFindDataSetByCode(code); + // , dataStore, sample)sampleDAO.tryFindByCodeAndGroup(code, group); + if (dataSetOrNull == null) + { + missingDataSetCodes.add(code); + } else { - throw new UserFailureException("Parent data set '" + parentDatasetCodeOrNull - + "' has to be assigned to the same experiment as data set '" - + externalData.getCode() + "': Experiment of parent data set is '" - + parentExperiment.getIdentifier() + "', experiment of data set is '" - + experiment.getIdentifier() + "'."); + dataSets.add(dataSetOrNull); } } - externalData.setParent(parentOrNull); + if (missingDataSetCodes.size() > 0) + { + throw UserFailureException.fromTemplate( + "Data Sets with following codes do not exist: '%s'.", CollectionUtils + .abbreviate(missingDataSetCodes, 10)); + } else + { + return dataSets; + } + } + + private static Set<String> asSet(String[] objects) + { + return new HashSet<String>(Arrays.asList(objects)); + } + + private static Set<String> extractCodes(Collection<DataPE> parents) + { + Set<String> codes = new HashSet<String>(parents.size()); + for (DataPE parent : parents) + { + codes.add(parent.getCode()); + } + return codes; } + // + private void updateSample(SampleIdentifier sampleIdentifierOrNull) { SamplePE sampleOrNull = null; diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/DatasetLister.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/DatasetLister.java index 6d59b929dd2e6e463372b23957808466a77f3ab3..6ba6f248d270950adc42d0101a533ac08ea6d8de 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/DatasetLister.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/DatasetLister.java @@ -194,11 +194,6 @@ public class DatasetLister implements IDatasetLister { Invalidation invalidation = sample.getInvalidation(); dataset.setInvalidation(invalidation); - ExternalData parent = dataset.getParent(); - if (parent != null) - { - parent.setInvalidation(invalidation); - } } private static LongSet extractSampleIds(Long2ObjectMap<ExternalData> datasetMap) diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/BasicDataSetUpdates.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/BasicDataSetUpdates.java index a42c5fba947394d8d1517ccb32a467414229a458..30ec82f3a4293d25518cb6682a0ae17cf4c4f612 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/BasicDataSetUpdates.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/BasicDataSetUpdates.java @@ -37,7 +37,7 @@ public class BasicDataSetUpdates implements IsSerializable, Serializable // ----- the data which should be changed: - private String parentDatasetCodeOrNull; + private String[] modifiedParentDatasetCodesOrNull; private String fileFormatTypeCode; @@ -85,14 +85,14 @@ public class BasicDataSetUpdates implements IsSerializable, Serializable this.properties = properties; } - public String getParentDatasetCodeOrNull() + public String[] getModifiedParentDatasetCodesOrNull() { - return parentDatasetCodeOrNull; + return modifiedParentDatasetCodesOrNull; } - public void setParentDatasetCodeOrNull(String parentDatasetCodeOrNull) + public void setModifiedParentDatasetCodesOrNull(String[] modifiedParentDatasetCodesOrNull) { - this.parentDatasetCodeOrNull = parentDatasetCodeOrNull; + this.modifiedParentDatasetCodesOrNull = modifiedParentDatasetCodesOrNull; } public String getFileFormatTypeCode() diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/DataSetUpdateResult.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/DataSetUpdateResult.java new file mode 100644 index 0000000000000000000000000000000000000000..f9747e870392a9d4814929d5966fa4f76a4bb0c9 --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/DataSetUpdateResult.java @@ -0,0 +1,61 @@ +/* + * Copyright 2009 ETH Zuerich, CISD + * + * 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 ch.systemsx.cisd.openbis.generic.shared.basic.dto; + +import java.io.Serializable; +import java.util.Date; + +import com.google.gwt.user.client.rpc.IsSerializable; + +/** + * Stores result of data set update. + * + * @author Piotr Buczek + */ +public class DataSetUpdateResult implements IsSerializable, Serializable +{ + private static final long serialVersionUID = ServiceVersionHolder.VERSION; + + Date modificationDate; + + String[] parentCodes; + + public DataSetUpdateResult() + { + } + + public Date getModificationDate() + { + return modificationDate; + } + + public void setModificationDate(Date modificationDate) + { + this.modificationDate = modificationDate; + } + + public String[] getParentCodes() + { + return parentCodes; + } + + public void setParentCodes(String[] parentCodes) + { + this.parentCodes = parentCodes; + } + +} diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/ExternalData.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/ExternalData.java index 8d86ad4d2cf3ca37f9d9fd6cd3b7d067ccac35f6..4515c2e6dd13c5850045277b57f76ffad780a017 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/ExternalData.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/ExternalData.java @@ -19,7 +19,6 @@ package ch.systemsx.cisd.openbis.generic.shared.basic.dto; import java.util.ArrayList; import java.util.Collection; import java.util.Date; -import java.util.Iterator; import java.util.List; import ch.systemsx.cisd.openbis.generic.shared.basic.IEntityInformationHolder; @@ -210,22 +209,6 @@ public class ExternalData extends CodeWithRegistration<ExternalData> implements this.producerCode = producerCode; } - public ExternalData getParent() - { - if (parents == null) - { - return null; - } - final Iterator<ExternalData> it = parents.iterator(); - if (it.hasNext()) - { - return parents.iterator().next(); - } else - { - return null; - } - } - public Collection<ExternalData> getParents() { return parents; @@ -236,12 +219,6 @@ public class ExternalData extends CodeWithRegistration<ExternalData> implements this.parents = parents; } - public final String getParentCode() - { - final ExternalData parent = getParent(); - return (parent == null) ? null : parent.getCode(); - } - public final String getLocation() { return location; diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java index 14caf7cca4f69bcfed518a58cfd782fe95a6f13d..18af7777b6d76d93e6e94565f649989432ecc596 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java @@ -320,7 +320,6 @@ public class DataPE extends AbstractIdAndCodeHolder<DataPE> implements IEntityPr // bidirectional connection children-parents - // TODO 2009-06-09, Piotr Buczek: change to @ManyToOne and remove data_set_relationships table // we use cascade PERSIST, not ALL because we don't REMOVE parent when we delete a child @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) @JoinTable(name = TableNames.DATA_SET_RELATIONSHIPS_TABLE, joinColumns = @JoinColumn(name = ColumnNames.DATA_CHILD_COLUMN), inverseJoinColumns = @JoinColumn(name = ColumnNames.DATA_PARENT_COLUMN)) @@ -335,27 +334,20 @@ public class DataPE extends AbstractIdAndCodeHolder<DataPE> implements IEntityPr this.parents = parents; } - /** sets one parent (we don't support many parents currently) */ - public void setParent(final DataPE parent) + /** adds connection with specified parent */ + public void addParent(final DataPE parent) { - this.parents.clear(); - if (parent != null) - { - this.parents.add(parent); - parent.addChild(this); - } + assert parent != null; + this.parents.add(parent); + parent.addChild(this); } - @Transient - public DataPE tryGetParent() + /** removes connection with specified parent */ + public void removeParent(final DataPE parent) { - if (getParents() == null || getParents().size() == 0) - { - return null; - } else - { - return getParents().iterator().next(); - } + assert parent != null; + this.parents.remove(parent); + parent.removeChild(this); } @ManyToMany(fetch = FetchType.LAZY, mappedBy = "parents") @@ -375,6 +367,11 @@ public class DataPE extends AbstractIdAndCodeHolder<DataPE> implements IEntityPr this.children.add(child); } + private void removeChild(final DataPE child) + { + this.children.remove(child); + } + // // AbstractIdAndCodeHolder // diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExtractableData.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExtractableData.java index e0ff6999fb5682caf525ddfe0399e177ae29a22b..d08e21da5559a0ff8a9996b42d555b8f73945f94 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExtractableData.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExtractableData.java @@ -38,7 +38,7 @@ public class ExtractableData extends Code<ExtractableData> private Date productionDate; private String dataProducerCode; - + private List<String> parentDataSetCodes = new ArrayList<String>(); private List<NewProperty> dataSetProperties = new ArrayList<NewProperty>(); @@ -83,12 +83,6 @@ public class ExtractableData extends Code<ExtractableData> this.dataProducerCode = dataProducerCode; } - @Deprecated - public final String tryToGetParentDataSetCode() - { - return parentDataSetCodes.isEmpty() ? null : parentDataSetCodes.iterator().next(); - } - public final List<String> getParentDataSetCodes() { return parentDataSetCodes; diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/IGenericClientServiceAsync.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/IGenericClientServiceAsync.java index df1552e39a70dd79ef9fe3b5369dab43a07b0c04..b7410b1a53bcffb7633c546026ad70def6ab9844 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/IGenericClientServiceAsync.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/IGenericClientServiceAsync.java @@ -27,6 +27,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.dto.DataSetUpdates; import ch.systemsx.cisd.openbis.generic.client.web.client.dto.SampleUpdates; import ch.systemsx.cisd.openbis.generic.client.web.client.exception.UserFailureException; import ch.systemsx.cisd.openbis.generic.shared.basic.TechId; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetUpdateResult; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExperimentUpdateResult; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExperimentUpdates; @@ -56,8 +57,7 @@ public interface IGenericClientServiceAsync extends IClientServiceAsync /** * @see IGenericClientService#getSampleInfo(TechId) */ - public void getSampleInfo(final TechId sampleId, - AsyncCallback<Sample> asyncCallback); + public void getSampleInfo(final TechId sampleId, AsyncCallback<Sample> asyncCallback); /** * @see IGenericClientService#registerSample(String, NewSample) @@ -130,7 +130,7 @@ public interface IGenericClientServiceAsync extends IClientServiceAsync /** * @see IGenericClientService#updateDataSet(DataSetUpdates) */ - public void updateDataSet(DataSetUpdates updates, final AsyncCallback<Date> asyncCallback) - throws UserFailureException; + public void updateDataSet(DataSetUpdates updates, + final AsyncCallback<DataSetUpdateResult> asyncCallback) throws UserFailureException; } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/dataset/DataSetParentsArea.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/dataset/DataSetParentsArea.java new file mode 100644 index 0000000000000000000000000000000000000000..fb9c7ddaa5a1223732c83f879c2b2ed1189eb176 --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/dataset/DataSetParentsArea.java @@ -0,0 +1,108 @@ +/* + * Copyright 2009 ETH Zuerich, CISD + * + * 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 ch.systemsx.cisd.openbis.plugin.generic.client.web.client.application.dataset; + +import java.util.List; + +import com.extjs.gxt.ui.client.widget.form.TextArea; + +import ch.systemsx.cisd.openbis.generic.client.web.client.application.Dict; +import ch.systemsx.cisd.openbis.generic.client.web.client.application.GenericConstants; +import ch.systemsx.cisd.openbis.generic.client.web.client.application.util.IMessageProvider; +import ch.systemsx.cisd.openbis.generic.client.web.client.application.util.StringUtils; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData; + +/** + * A text area to specify parents of a data set. Parents are specified by codes separated by commas, + * spaces or new lines. + * + * @author Piotr Buczek + */ +final class DataSetParentsArea extends TextArea +{ + + public static final String ID_SUFFIX_PARENTS = "_parents"; + + public DataSetParentsArea(IMessageProvider messageProvider, String idPrefix) + { + super(); + setHeight("10em"); + this.setFieldLabel(messageProvider.getMessage(Dict.PARENTS)); + setEmptyText(messageProvider.getMessage(Dict.PARENTS_EMPTY)); + setId(createId(idPrefix)); + } + + public static String createId(String idPrefix) + { + return idPrefix + ID_SUFFIX_PARENTS; + } + + // null if the area has not been modified, the list of all data set parent codes otherwise + public final String[] tryGetModifiedParentCodes() + { + if (isDirty() == false) + { + return null; + } + String text = getValue(); + if (StringUtils.isBlank(text) == false) + { + return text.split(GenericConstants.CODES_TEXTAREA_REGEX); + } else + { + return new String[0]; + } + } + + public final void setParents(List<ExternalData> parents) + { + setParentCodes(extractCodes(parents)); + } + + private static String[] extractCodes(List<ExternalData> parents) + { + String[] codes = new String[parents.size()]; + int i = 0; + for (ExternalData parent : parents) + { + codes[i] = parent.getCode(); + i++; + } + return codes; + } + + public final void setParentCodes(String[] parentCodes) + { + String textValue = createTextValue(parentCodes); + setValue(textValue); + setOriginalValue(textValue); + } + + private static String createTextValue(String[] parentCodes) + { + StringBuffer sb = new StringBuffer(); + for (String parentCode : parentCodes) + { + if (sb.length() > 0) + { + sb.append(", "); + } + sb.append(parentCode); + } + return sb.toString(); + } +} diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/dataset/GenericDataSetEditForm.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/dataset/GenericDataSetEditForm.java index cce9ea903ff85730b33bb1b2bd8bf24f366a983a..a50578dfdfcbe015e15b6fb5e75b97849fb9f36e 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/dataset/GenericDataSetEditForm.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/dataset/GenericDataSetEditForm.java @@ -19,7 +19,6 @@ package ch.systemsx.cisd.openbis.plugin.generic.client.web.client.application.da import static ch.systemsx.cisd.openbis.generic.client.web.client.application.framework.DatabaseModificationAwareField.wrapUnaware; import java.util.ArrayList; -import java.util.Date; import java.util.List; import ch.systemsx.cisd.openbis.generic.client.web.client.ICommonClientServiceAsync; @@ -31,14 +30,17 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.framework. import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.AbstractRegistrationForm; import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.data.FileFormatTypeSelectionWidget; import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.data.FileFormatTypeSelectionWidget.FileFormatTypeModel; -import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.field.CodeField; import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.field.SampleChooserField; import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.field.SampleChooserField.SampleChooserFieldAdaptor; import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.widget.FieldUtil; import ch.systemsx.cisd.openbis.generic.client.web.client.dto.DataSetUpdates; +import ch.systemsx.cisd.openbis.generic.client.web.client.dto.DefaultResultSetConfig; +import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ResultSetWithEntityTypes; import ch.systemsx.cisd.openbis.generic.shared.basic.IIdentifiable; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetRelationshipRole; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetType; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetTypePropertyType; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetUpdateResult; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityKind; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData; import ch.systemsx.cisd.openbis.plugin.generic.client.web.client.IGenericClientServiceAsync; @@ -55,11 +57,13 @@ public final class GenericDataSetEditForm extends { private static final String SAMPLE_FIELD_ID_SUFFIX = "sample_field"; + private final String simpleId; + private FileFormatTypeSelectionWidget fileFormatTypeSelectionWidget; private SampleChooserFieldAdaptor sampleField; - private CodeField parentField; + private DataSetParentsArea parentsArea; private ExternalData originalDataSet; @@ -74,6 +78,7 @@ public final class GenericDataSetEditForm extends IIdentifiable identifiable) { super(viewContext, identifiable, EntityKind.DATA_SET); + simpleId = createSimpleId(identifiable, EntityKind.DATA_SET); } private String createChildId(String childSuffix) @@ -94,8 +99,8 @@ public final class GenericDataSetEditForm extends result.setDatasetId(techIdOrNull); result.setProperties(extractProperties()); result.setVersion(originalDataSet.getModificationDate()); - result.setParentDatasetCodeOrNull(extractParentDatasetCode()); - // TODO 2009-09-10, Piotr Buczel: check if it is null for empty string + result.setModifiedParentDatasetCodesOrNull(extractParentDatasetCodes()); + // TODO 2009-09-10, Piotr Buczek: check if it is null for empty string result.setSampleIdentifierOrNull(extractSampleIdentifier()); result.setFileFormatTypeCode(extractFileFormatTypeCode()); return result; @@ -106,9 +111,9 @@ public final class GenericDataSetEditForm extends return sampleField.getValue(); } - private String extractParentDatasetCode() + protected String[] extractParentDatasetCodes() { - return parentField.getValue(); + return parentsArea.tryGetModifiedParentCodes(); } private String extractFileFormatTypeCode() @@ -117,7 +122,7 @@ public final class GenericDataSetEditForm extends } public final class UpdateDataSetCallback extends - AbstractRegistrationForm.AbstractRegistrationCallback<Date> + AbstractRegistrationForm.AbstractRegistrationCallback<DataSetUpdateResult> { UpdateDataSetCallback(final IViewContext<?> viewContext) @@ -126,25 +131,25 @@ public final class GenericDataSetEditForm extends } @Override - protected void process(final Date result) + protected void process(final DataSetUpdateResult result) { - originalDataSet.setModificationDate(result); - updateOriginalValues(); + originalDataSet.setModificationDate(result.getModificationDate()); + updateOriginalValues(result.getParentCodes()); super.process(result); } @Override - protected String createSuccessfullRegistrationInfo(Date result) + protected String createSuccessfullRegistrationInfo(DataSetUpdateResult result) { return "Data set successfully updated"; } } - private void updateOriginalValues() + private void updateOriginalValues(String[] parentCodes) { updatePropertyFieldsOriginalValues(); sampleField.updateOriginalValue(); - parentField.updateOriginalValue(parentField.getValue()); + parentsArea.setParentCodes(parentCodes); fileFormatTypeSelectionWidget.updateOriginalValue(fileFormatTypeSelectionWidget.getValue()); } @@ -162,7 +167,7 @@ public final class GenericDataSetEditForm extends ArrayList<DatabaseModificationAwareField<?>> fields = new ArrayList<DatabaseModificationAwareField<?>>(); // TODO 2009-08-01, Piotr Buczek: add other fields specified in LMS-1003 - fields.add(wrapUnaware(parentField)); + fields.add(wrapUnaware(parentsArea)); fields.add(wrapUnaware(sampleField.getField())); fields.add(wrapUnaware(fileFormatTypeSelectionWidget)); return fields; @@ -171,19 +176,14 @@ public final class GenericDataSetEditForm extends @Override protected void createEntitySpecificFormFields() { - this.parentField = createParentField(); + this.parentsArea = createParentsArea(); this.sampleField = createSampleField(); this.fileFormatTypeSelectionWidget = createFileFormatTypeField(); } - private CodeField createParentField() + private DataSetParentsArea createParentsArea() { - final CodeField result = new CodeField(viewContext, viewContext.getMessage(Dict.PARENT)); - result.setEmptyText("Parent data set code"); - // by default CodeField is mandatory - result.setLabelSeparator(""); - result.setAllowBlank(true); - return result; + return new DataSetParentsArea(viewContext, simpleId); } private SampleChooserFieldAdaptor createSampleField() @@ -212,9 +212,12 @@ public final class GenericDataSetEditForm extends propertiesEditor.initWithProperties(originalDataSet.getDataSetType() .getAssignedPropertyTypes(), originalDataSet.getProperties()); codeField.setValue(originalDataSet.getCode()); - parentField.setValue(originalDataSet.getParentCode()); fileFormatTypeSelectionWidget.setValue(new FileFormatTypeModel(originalDataSet .getFileFormatType())); + // parents Area + parentsArea.setEnabled(false); + parentsArea.setValue(viewContext.getMessage(Dict.LOAD_IN_PROGRESS)); + loadParentsInBackground(); } private void setOriginalData(ExternalData data) @@ -244,4 +247,31 @@ public final class GenericDataSetEditForm extends initGUI(); } } + + private void loadParentsInBackground() + { + // not best performance but the same solution that is done for experiments + // only codes are needed but we extract 'full' object + DefaultResultSetConfig<String, ExternalData> config = + DefaultResultSetConfig.createFetchAll(); + viewContext.getCommonService().listDataSetRelationships(techIdOrNull, + DataSetRelationshipRole.CHILD, config, new ListParentsCallback(viewContext)); + } + + public class ListParentsCallback extends + AbstractAsyncCallback<ResultSetWithEntityTypes<ExternalData>> + { + + public ListParentsCallback(IViewContext<?> viewContext) + { + super(viewContext); + } + + @Override + protected void process(ResultSetWithEntityTypes<ExternalData> result) + { + parentsArea.setParents(result.getResultSet().getList()); + parentsArea.setEnabled(true); + } + } } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/experiment/AbstractGenericExperimentRegisterEditForm.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/experiment/AbstractGenericExperimentRegisterEditForm.java index e6fdd2fa952d528ab81323b8c8f12b3c7e8b79f6..a500c04ea54609b10cc59ed5a0a67c5c0f96de56 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/experiment/AbstractGenericExperimentRegisterEditForm.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/client/application/experiment/AbstractGenericExperimentRegisterEditForm.java @@ -73,7 +73,7 @@ abstract public class AbstractGenericExperimentRegisterEditForm extends protected ProjectSelectionWidget projectChooser; - ExperimentSamplesArea samplesArea; + protected ExperimentSamplesArea samplesArea; protected final String samplesSessionKey; diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/server/GenericClientService.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/server/GenericClientService.java index f010dba3a3f4f589ea5e37f8c4dc6727a9e2b9f1..1deaec1985be7c386160d305eaf228fb5b4cbae1 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/server/GenericClientService.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/generic/client/web/server/GenericClientService.java @@ -489,7 +489,8 @@ public final class GenericClientService extends AbstractClientService implements updatesDTO.setDatasetId(updates.getDatasetId()); updatesDTO.setProperties(updates.getProperties()); updatesDTO.setVersion(updates.getVersion()); - updatesDTO.setParentDatasetCodeOrNull(updates.getParentDatasetCodeOrNull()); + updatesDTO.setModifiedParentDatasetCodesOrNull(updates + .getModifiedParentDatasetCodesOrNull()); String sampleIdentifierOrNull = updates.getSampleIdentifierOrNull(); updatesDTO.setSampleIdentifierOrNull(sampleIdentifierOrNull == null ? null : SampleIdentifierFactory.parse(sampleIdentifierOrNull)); diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/common-dictionary.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/common-dictionary.js index 82a89bef1e5a199295476439ed08b8c5ff7b08d8..f307918c5c440ff2e9ece1028cc6cdf61e623fa1 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/public/common-dictionary.js +++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/common-dictionary.js @@ -320,7 +320,13 @@ var common = { experiment_registration: "Experiment Registration", samples: "Samples", samples_list: "List of samples (codes or identifiers) separated by commas (\",\") or one sample per line.", - + + // + // Data Set Edition + // + parents: "Parents", + parents_empty: "List of parent data set codes separated by spaces, commas (\",\") or one code per line.", + // // Vocabulary Browser // @@ -458,7 +464,7 @@ incorrect_experiment_syntax: "Incorrect experiment specification. Please provide // Grid Column Chooser // - grid_settings_title: "Table settings", + GRID_COLUMN_CHOOSER_TITLE: "Configure grid columns", GRID_COLUMN_NAME_HEADER: "Column", GRID_IS_COLUMN_VISIBLE_HEADER: "Visible?", GRID_COLUMN_HAS_FILTER_HEADER: "Has Filter?", @@ -486,16 +492,7 @@ all_radio: "all", data_sets_radio_group_label: "Data Sets", only_selected_radio: "selected ({0})", experiments_radio_group_label: "Experiments", - -// -// Filters -// - name: "Name", - is_public: "Is Public?", - expression: "Expression", - columns: "Columns", - custom_filters: "Custom Filters", - + // LAST LINE: KEEP IT AT THE END lastline: "" // we need a line without a comma }; diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBOTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBOTest.java index 92480fa318d7f778dff7836791d6c9eb96168302..101cf46952dcc6bc61f36a1f4d6e4c5b2f553623 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBOTest.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataBOTest.java @@ -63,7 +63,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.types.ProcedureTypeCode; /** * @author Franz-Josef Elmer */ -// TODO 2009-09-10, Piotr Buczek: write tests with no direct sample connection +// TODO 2009-09-10, Piotr Buczek: write tests with no direct sample connection, and many parents public class ExternalDataBOTest extends AbstractBOTest { private static final TechId TECH_ID = new TechId(42l); @@ -319,7 +319,9 @@ public class ExternalDataBOTest extends AbstractBOTest sample.setCode(SAMPLE_IDENTIFIER.getSampleCode()); final ExternalDataPE dataSet = createDataSet(sample); DataSetUpdatesDTO dataSetUpdatesDTO = createDataSetUpdates(dataSet); - dataSetUpdatesDTO.setParentDatasetCodeOrNull(dataSet.getCode()); + String[] parentCodes = + { dataSet.getCode() }; + dataSetUpdatesDTO.setModifiedParentDatasetCodesOrNull(parentCodes); prepareForUpdate(dataSet, sample); IExternalDataBO bo = createExternalDataBO(); @@ -342,7 +344,9 @@ public class ExternalDataBOTest extends AbstractBOTest sample.setCode(SAMPLE_IDENTIFIER.getSampleCode()); final ExternalDataPE dataSet = createDataSet(sample); DataSetUpdatesDTO dataSetUpdatesDTO = createDataSetUpdates(dataSet); - dataSetUpdatesDTO.setParentDatasetCodeOrNull(PARENT_CODE); + String[] parentCodes = + { PARENT_CODE }; + dataSetUpdatesDTO.setModifiedParentDatasetCodesOrNull(parentCodes); prepareForUpdate(dataSet, sample); context.checking(new Expectations() { @@ -359,43 +363,8 @@ public class ExternalDataBOTest extends AbstractBOTest fail("UserFailureException expected"); } catch (UserFailureException e) { - assertEquals("Data set with code 'parent' does not exist.", e.getMessage()); - } - - context.assertIsSatisfied(); - } - - @Test - public void testUpdateWithParentDataSetFromDifferentExperiment() - { - final SamplePE sample = new SamplePE(); - sample.setCode(SAMPLE_IDENTIFIER.getSampleCode()); - final ExternalDataPE dataSet = createDataSet(sample); - ExperimentPE experiment = createExperiment("EXP1"); - dataSet.setExperiment(experiment); - DataSetUpdatesDTO dataSetUpdatesDTO = createDataSetUpdates(dataSet); - dataSetUpdatesDTO.setParentDatasetCodeOrNull(PARENT_CODE); - prepareForUpdate(dataSet, sample); - context.checking(new Expectations() - { - { - one(externalDataDAO).tryToFindDataSetByCode(PARENT_CODE); - ExternalDataPE parentDataSet = new ExternalDataPE(); - parentDataSet.setExperiment(createExperiment("EXP2")); - will(returnValue(parentDataSet)); - } - }); - - IExternalDataBO bo = createExternalDataBO(); - try - { - bo.update(dataSetUpdatesDTO); - fail("UserFailureException expected"); - } catch (UserFailureException e) - { - assertEquals("Parent data set 'parent' has to be assigned to the same experiment " - + "as data set 'DS1': Experiment of parent data set is 'DB:/G/P/EXP2', " - + "experiment of data set is 'DB:/G/P/EXP1'.", e.getMessage()); + assertEquals("Data set with following codes do not exist: '[" + PARENT_CODE + "]'", e + .getMessage()); } context.assertIsSatisfied(); diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/translator/ExternalDataTranslatorTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/translator/ExternalDataTranslatorTest.java index 5069dba9cfc9343f8147be260b52d0560b9ae858..cbba4af4d9ae07d7601d4b544ede1210c43b0de5 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/translator/ExternalDataTranslatorTest.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/translator/ExternalDataTranslatorTest.java @@ -41,6 +41,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.SampleTypePE; /** * @author Franz-Josef Elmer */ +// TODO 2009-09-10, Piotr Buczek: write tests with many parents public class ExternalDataTranslatorTest extends AssertJUnit { private static final String BASE_URL = "url"; @@ -56,10 +57,10 @@ public class ExternalDataTranslatorTest extends AssertJUnit ExternalDataTranslator.translate(externalDataPE, BASE_URL, BASE_INDEX_URL); assertEquals(null, externalData.getCode()); - assertEquals(null, externalData.getParentCode()); assertEquals(null, externalData.getExperiment()); assertEquals(null, externalData.getProductionDate()); assertEquals(null, externalData.getComplete()); + assertEquals(0, externalData.getParents().size()); } @Test @@ -86,7 +87,7 @@ public class ExternalDataTranslatorTest extends AssertJUnit ExternalDataPE parent = new ExternalDataPE(); parent.setCode("parent"); parent.setDataStore(new DataStorePE()); - externalDataPE.setParent(parent); + externalDataPE.addParent(parent); ExperimentPE experimentPE = new ExperimentPE(); experimentPE.setCode("my-experiment"); experimentPE.setExperimentType(new ExperimentTypePE()); @@ -131,7 +132,8 @@ public class ExternalDataTranslatorTest extends AssertJUnit assertEquals("location", externalData.getLocation()); assertEquals("locatorTypeCode", externalData.getLocatorType().getCode()); assertEquals("locatorTypeDescription", externalData.getLocatorType().getDescription()); - assertEquals("parent", externalData.getParentCode()); + assertEquals(1, externalData.getParents().size()); + assertEquals("parent", externalData.getParents().iterator().next().getCode()); assertEquals("my-experiment", externalData.getExperiment().getCode()); assertEquals(1, externalData.getProductionDate().getTime()); assertEquals(2, externalData.getRegistrationDate().getTime());