diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/MaterialReportingTask.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/MaterialReportingTask.java index a39774bd1a83642e00889b8d08c46e38214838e9..42c006cbe8c79026eba4c44bbf117316935d8355 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/MaterialReportingTask.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/task/MaterialReportingTask.java @@ -17,15 +17,19 @@ package ch.systemsx.cisd.openbis.generic.server.task; import java.io.File; +import java.io.Serializable; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Types; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -60,6 +64,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchField; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Material; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialAttributeSearchFieldKind; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.PropertyType; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SearchCriteriaConnection; import ch.systemsx.cisd.openbis.generic.shared.dto.SessionContextDTO; import ch.systemsx.cisd.openbis.generic.shared.util.DataTypeUtils; @@ -74,31 +79,56 @@ public class MaterialReportingTask implements IMaintenanceTask @Private static final String MAPPING_FILE_KEY = "mapping-file"; - @Private - static final class MappingInfo + private static final class Column { - private static final class Column + private final String name; + + private final int index; + + private DataTypeCode dataTypeCode; + + Column(String name, int index) { - private final String name; + this.name = name; + this.index = index; + } + } - private final int index; + private enum IndexingSchema + { + INSERT(0, 2), UPDATE(-1, 1); - private DataTypeCode dataTypeCode; + private final int codeIndex; - Column(String name, int index) - { - this.name = name; - this.index = index; - } + private final int offset; + + private IndexingSchema(int codeIndex, int offset) + { + this.codeIndex = codeIndex; + this.offset = offset; + } + + public int getCodeIndex(int size) + { + return 1 + (size + 1 + codeIndex) % (size + 1); } + public int getPropertyIndexOffset() + { + return offset; + } + } + + @Private + static final class MappingInfo + { private final String materialTypeCode; private final String tableName; private final String codeColumnName; - private final Map<String, Column> propertyMapping = new TreeMap<String, Column>(); + private final Map<String, Column> propertyMapping = new LinkedHashMap<String, Column>(); MappingInfo(String materialTypeCode, String tableName, String codeColumnName) { @@ -117,6 +147,11 @@ public class MaterialReportingTask implements IMaintenanceTask return codeColumnName; } + boolean hasProperties() + { + return propertyMapping.isEmpty() == false; + } + void addPropertyMapping(String propertyTypeCode, String propertyColumnName) { Column column = new Column(propertyColumnName, propertyMapping.size()); @@ -147,16 +182,42 @@ public class MaterialReportingTask implements IMaintenanceTask } column.dataTypeCode = dataTypeCode; } + } + Map<String, Map<String, Object>> groupByMaterials(List<Map<String, Object>> rows) + { + HashMap<String, Map<String, Object>> result = + new HashMap<String, Map<String, Object>>(); + for (Map<String, Object> map : rows) + { + String materialCode = map.remove(codeColumnName).toString(); + result.put(materialCode, map); + } + return result; + } + + String createSelectStatement(List<Material> materials) + { + StringBuilder builder = new StringBuilder("select * from "); + builder.append(tableName).append(" where "); + builder.append(codeColumnName).append(" in "); + String delim = "("; + for (Material material : materials) + { + builder.append(delim).append('\'').append(material.getCode()).append('\''); + delim = ", "; + } + builder.append(")"); + return builder.toString(); } String createInsertStatement() { StringBuilder builder = new StringBuilder("insert into ").append(tableName); builder.append(" (").append(codeColumnName); - for (Column nameAndIndex : propertyMapping.values()) + for (Column column : propertyMapping.values()) { - builder.append(", ").append(nameAndIndex.name); + builder.append(", ").append(column.name); } builder.append(") values(?"); for (int i = 0; i < propertyMapping.size(); i++) @@ -167,7 +228,21 @@ public class MaterialReportingTask implements IMaintenanceTask return builder.toString(); } - BatchPreparedStatementSetter createSetter(final List<Material> materials) + String createUpdateStatement() + { + StringBuilder builder = new StringBuilder("update ").append(tableName); + String delim = " set "; + for (Column column : propertyMapping.values()) + { + builder.append(delim).append(column.name).append("=?"); + delim = ", "; + } + builder.append(" where ").append(codeColumnName).append("=?"); + return builder.toString(); + } + + BatchPreparedStatementSetter createSetter(final List<Material> materials, + final IndexingSchema indexing) { return new BatchPreparedStatementSetter() { @@ -175,18 +250,31 @@ public class MaterialReportingTask implements IMaintenanceTask { Material material = materials.get(index); List<IEntityProperty> properties = material.getProperties(); - ps.setObject(1, material.getCode()); + ps.setObject(indexing.getCodeIndex(propertyMapping.size()), + material.getCode()); + int propertyIndexOffset = indexing.getPropertyIndexOffset(); for (int i = 0; i < propertyMapping.size(); i++) { - ps.setObject(i + 2, null); + ps.setObject(i + propertyIndexOffset, null); } for (IEntityProperty property : properties) { - String code = property.getPropertyType().getCode(); - Column nameAndIndex = propertyMapping.get(code); - if (nameAndIndex != null) + PropertyType propertyType = property.getPropertyType(); + String code = propertyType.getCode(); + Column column = propertyMapping.get(code); + if (column != null) { - ps.setObject(nameAndIndex.index + 2, property.tryGetAsString()); + String value = getValue(property); + Serializable typedValue = + DataTypeUtils.convertValueTo(column.dataTypeCode, value); + if (typedValue instanceof Date) + { + ps.setObject(column.index + propertyIndexOffset, typedValue, + Types.TIMESTAMP); + } else + { + ps.setObject(column.index + propertyIndexOffset, typedValue); + } } } } @@ -197,6 +285,24 @@ public class MaterialReportingTask implements IMaintenanceTask } }; } + + private String getValue(IEntityProperty property) + { + String value; + switch (property.getPropertyType().getDataType().getCode()) + { + case CONTROLLEDVOCABULARY: + value = property.getVocabularyTerm().getCodeOrLabel(); + break; + case MATERIAL: + value = property.getMaterial().getCode(); + break; + default: + value = property.getValue(); + } + return value; + } + } private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, @@ -293,15 +399,6 @@ public class MaterialReportingTask implements IMaintenanceTask } } - @Private - void closeDatabaseConnections() - { - if (dbConfigurationContext != null) - { - dbConfigurationContext.closeConnections(); - } - } - public void execute() { SessionContextDTO contextOrNull = server.tryToAuthenticateAsSystem(); @@ -317,25 +414,62 @@ public class MaterialReportingTask implements IMaintenanceTask { String materialTypeCode = entry.getKey(); final List<Material> materials = entry.getValue(); - addAndUpdate(materialTypeCode, materials); + MappingInfo mappingInfo = mapping.get(materialTypeCode); + if (mappingInfo != null) + { + addOrUpdate(mappingInfo, materials); + operationLog.info(materials.size() + " materials of type " + materialTypeCode + + " reported."); + } } } - private void addAndUpdate(String materialTypeCode, final List<Material> materials) + private void addOrUpdate(MappingInfo mappingInfo, final List<Material> materials) { - MappingInfo mappingInfo = mapping.get(materialTypeCode); - if (mappingInfo != null) + String sql = mappingInfo.createSelectStatement(materials); + List<Map<String, Object>> rows = retrieveRowsToBeUpdated(sql); + Map<String, Map<String, Object>> reportedMaterials = mappingInfo.groupByMaterials(rows); + List<Material> newMaterials = new ArrayList<Material>(); + List<Material> updateMaterials = new ArrayList<Material>(); + for (Material material : materials) + { + Map<String, Object> reportedMaterial = reportedMaterials.get(material.getCode()); + if (reportedMaterial != null) + { + updateMaterials.add(material); + } else + { + newMaterials.add(material); + } + } + if (updateMaterials.isEmpty() == false && mappingInfo.hasProperties()) + { + String updateStatement = mappingInfo.createUpdateStatement(); + jdbcTemplate.batchUpdate(updateStatement, + mappingInfo.createSetter(updateMaterials, IndexingSchema.UPDATE)); + } + if (newMaterials.isEmpty() == false) { - List<?> rows = - jdbcTemplate.query("select * from " + mappingInfo.getTableName(), - new ColumnMapRowMapper()); String insertStatement = mappingInfo.createInsertStatement(); - jdbcTemplate.batchUpdate(insertStatement, mappingInfo.createSetter(materials)); - operationLog.info(materials.size() + " materials of type " + materialTypeCode - + " reported."); + jdbcTemplate.batchUpdate(insertStatement, + mappingInfo.createSetter(newMaterials, IndexingSchema.INSERT)); } } + @SuppressWarnings("unchecked") + private List<Map<String, Object>> retrieveRowsToBeUpdated(String sql) + { + List<Map<String, Object>> rows = jdbcTemplate.query(sql, new ColumnMapRowMapper() + { + @Override + protected String getColumnKey(String columnName) + { + return columnName.toLowerCase(); + } + }); + return rows; + } + private Map<String, List<Material>> getRecentlyAddedOrChangedMaterials(String sessionToken) { DetailedSearchCriteria criteria = new DetailedSearchCriteria(); @@ -392,10 +526,10 @@ public class MaterialReportingTask implements IMaintenanceTask splitAndCheck(line.substring(0, line.length() - 1).substring(1), ":", 2, factory); String materialTypeCode = - trimeAndCheck(splittedLine[0], factory, "material type code"); + trimAndCheck(splittedLine[0], factory, "material type code"); splittedLine = splitAndCheck(splittedLine[1], ",", 2, factory); - String tableName = trimeAndCheck(splittedLine[0], factory, "table name"); - String codeColumnName = trimeAndCheck(splittedLine[1], factory, "code column name"); + String tableName = trimAndCheck(splittedLine[0], factory, "table name"); + String codeColumnName = trimAndCheck(splittedLine[1], factory, "code column name"); currentMappingInfo = new MappingInfo(materialTypeCode, tableName.toLowerCase(), codeColumnName.toLowerCase()); @@ -404,7 +538,7 @@ public class MaterialReportingTask implements IMaintenanceTask { String[] splittedLine = splitAndCheck(line, ":", 2, factory); String propertyTypeCode = - trimeAndCheck(splittedLine[0], factory, "property type code"); + trimAndCheck(splittedLine[0], factory, "property type code"); currentMappingInfo.addPropertyMapping(propertyTypeCode, splittedLine[1].trim() .toLowerCase()); } else @@ -428,7 +562,7 @@ public class MaterialReportingTask implements IMaintenanceTask return splittedString; } - private static String trimeAndCheck(String string, ExecptionFactory factory, String name) + private static String trimAndCheck(String string, ExecptionFactory factory, String name) { String trimmedString = string.trim(); if (trimmedString.length() == 0) @@ -460,4 +594,13 @@ public class MaterialReportingTask implements IMaintenanceTask } } + @Private + void closeDatabaseConnections() + { + if (dbConfigurationContext != null) + { + dbConfigurationContext.closeConnections(); + } + } + } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/util/DataTypeUtils.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/util/DataTypeUtils.java index fd88ede018c1c55a9d7c85e15cfb5695f3ef8ba5..2072df8b2fcbd73d5585a5b90ca067a787cc1aed 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/util/DataTypeUtils.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/util/DataTypeUtils.java @@ -18,16 +18,22 @@ package ch.systemsx.cisd.openbis.generic.shared.util; import java.io.Serializable; import java.sql.Types; +import java.text.ParseException; +import java.util.Date; import java.util.HashMap; import java.util.Map; +import org.apache.commons.lang.time.DateUtils; + import ch.systemsx.cisd.common.shared.basic.utils.StringUtils; import ch.systemsx.cisd.openbis.generic.shared.basic.BasicConstant; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataTypeCode; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DateTableCell; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DoubleTableCell; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ISerializableComparable; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IntegerTableCell; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.StringTableCell; +import ch.systemsx.cisd.openbis.generic.shared.util.SimplePropertyValidator.SupportedDatePattern; /** * Utility functions around data types. @@ -81,9 +87,32 @@ public class DataTypeUtils } } }, + TIMESTAMP(DataTypeCode.TIMESTAMP) + { + @Override + public ISerializableComparable doConversion(String value) + { + return new DateTableCell(doSimpleConversion(value)); + } + + @Override + public Date doSimpleConversion(String value) + { + try + { + String pattern = SupportedDatePattern.CANONICAL_DATE_PATTERN.getPattern(); + return DateUtils.parseDate(value, new String[] + { pattern }); + } catch (ParseException ex) + { + throw new IllegalArgumentException("Is not a date in canonical format: " + + value); + } + } + }, STRING(DataTypeCode.VARCHAR, DataTypeCode.MULTILINE_VARCHAR, DataTypeCode.BOOLEAN, DataTypeCode.XML, DataTypeCode.CONTROLLEDVOCABULARY, DataTypeCode.MATERIAL, - DataTypeCode.HYPERLINK, DataTypeCode.TIMESTAMP) + DataTypeCode.HYPERLINK) { @Override public ISerializableComparable doConversion(String value) diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/task/MaterialReportingTaskTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/task/MaterialReportingTaskTest.java index 7ad1e3ca7f8a01e6cc85245f4115a71c3ba62fca..0c2cefdecaa299f513162d3e7fd54119184aaa16 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/task/MaterialReportingTaskTest.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/task/MaterialReportingTaskTest.java @@ -21,12 +21,15 @@ import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.Arrays; +import java.util.Date; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.TreeMap; import org.jmock.Expectations; import org.jmock.Mockery; +import org.jmock.Sequence; import org.springframework.jdbc.core.ColumnMapRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.testng.annotations.AfterMethod; @@ -42,9 +45,11 @@ import ch.systemsx.cisd.common.test.RecordingMatcher; import ch.systemsx.cisd.dbmigration.DatabaseConfigurationContext; import ch.systemsx.cisd.openbis.generic.server.ICommonServerForInternalUse; import ch.systemsx.cisd.openbis.generic.server.task.MaterialReportingTask.MappingInfo; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataTypeCode; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Material; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.builders.MaterialBuilder; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.builders.VocabularyTermBuilder; import ch.systemsx.cisd.openbis.generic.shared.dto.SessionContextDTO; /** @@ -87,7 +92,8 @@ public class MaterialReportingTaskTest extends AbstractFileSystemTestCase createTestDatabase(); createTables( "create table report1 (id bigint, code varchar(20), description varchar(200))", - "create table report2 (code varchar(20), prop1 varchar(200), prop2 varchar(200))"); + "create table report2 (code varchar(20), rank integer, greetings varchar(200), " + + "size double precision, organism varchar(100), material varchar(30), timestamp timestamp)"); dbConfigContext.closeConnections(); mappingFile = new File(workingDirectory, "mapping-file.txt"); properties = new Properties(); @@ -117,7 +123,7 @@ public class MaterialReportingTaskTest extends AbstractFileSystemTestCase MappingInfo mappingInfo1 = mapping.get("T1"); assertEquals("insert into table1 (code) values(?)", mappingInfo1.createInsertStatement()); MappingInfo mappingInfo2 = mapping.get("T2"); - assertEquals("insert into table2 (code, prop1, prop2) values(?, ?, ?)", + assertEquals("insert into table2 (code, prop2, prop1) values(?, ?, ?)", mappingInfo2.createInsertStatement()); assertEquals(2, mapping.size()); } @@ -326,17 +332,24 @@ public class MaterialReportingTaskTest extends AbstractFileSystemTestCase } @Test - public void test() throws Exception + public void testInsert() throws Exception { FileUtilities.writeToFile(mappingFile, "# my mapping\n[T1:REPORT1,CODE]\n\n" - + "[T2: REPORT2, code]\nP2: prop2\nP1:prop1"); + + "[T2: REPORT2, code]\nM:MATERIAL\nS:size\nP2: GREETINGS\nP1:RANK\nORG:ORGANISM\n" + + "T:timestamp"); materialReportingTask.setUp("", properties); final Material m1 = new MaterialBuilder().code("M1").type("T1").property("P1", "42").getMaterial(); - final Material m2 = - new MaterialBuilder().code("M2").type("T2").property("P1", "42").getMaterial(); + MaterialBuilder mb2 = new MaterialBuilder().code("M2").type("T2"); + mb2.property("P1").type(DataTypeCode.INTEGER).value(42); + mb2.property("S").type(DataTypeCode.REAL).value(1e7 + 0.5); + mb2.property("ORG").type(DataTypeCode.CONTROLLEDVOCABULARY) + .value(new VocabularyTermBuilder("FLY").getTerm()); + mb2.property("M").type(DataTypeCode.MATERIAL).value(m1); + mb2.property("T").type(DataTypeCode.TIMESTAMP).value(new Date(24 * 3600L * 1000L * 33)); + final Material m2 = mb2.getMaterial(); final Material m3 = - new MaterialBuilder().code("M3").type("T2").property("P2", "137").getMaterial(); + new MaterialBuilder().code("M3").type("T2").property("P2", "hello").getMaterial(); final RecordingMatcher<DetailedSearchCriteria> criteriaRecorder = new RecordingMatcher<DetailedSearchCriteria>(); context.checking(new Expectations() @@ -354,18 +367,93 @@ public class MaterialReportingTaskTest extends AbstractFileSystemTestCase materialReportingTask.execute(); - List<?> result = - new JdbcTemplate(dbConfigContext.getDataSource()).query("select * from report1", - new ColumnMapRowMapper()); - assertEquals("[{id=null, code=M1, description=null}]", result.toString()); - result = - new JdbcTemplate(dbConfigContext.getDataSource()).query( - "select * from report2 order by code", new ColumnMapRowMapper()); - assertEquals("[{code=M2, prop1=null, prop2=42}, {code=M3, prop1=137, prop2=null}]", - result.toString()); + List<?> result = loadTable("report1"); + assertEquals("[{code=M1, description=null, id=null}]", result.toString()); + result = loadTable("report2"); + assertEquals("[{code=M2, greetings=null, material=M1, organism=FLY, rank=42, " + + "size=1.00000005E7, timestamp=1970-02-03 01:00:00.0}, " + + "{code=M3, greetings=hello, material=null, organism=null, rank=null, " + + "size=null, timestamp=null}]", result.toString()); context.assertIsSatisfied(); } + @Test + public void testInsertUpdate() throws Exception + { + FileUtilities.writeToFile(mappingFile, "[T1:REPORT1,CODE]\n" + + "[T2: REPORT2, code]\nM:MATERIAL\nS:size\nP1:RANK\nORG:ORGANISM\n" + + "P2: GREETINGS\nT:timestamp"); + materialReportingTask.setUp("", properties); + final Material m1 = + new MaterialBuilder().code("M1").type("T1").property("P1", "42").getMaterial(); + MaterialBuilder mb2 = new MaterialBuilder().code("M2").type("T2"); + mb2.property("P1").type(DataTypeCode.INTEGER).value(42); + final Material m2 = mb2.getMaterial(); + final Material m3 = + new MaterialBuilder().code("M3").type("T2").property("P2", "hello").getMaterial(); + MaterialBuilder mb2v2 = new MaterialBuilder().code("M2").type("T2"); + mb2v2.property("P1").type(DataTypeCode.INTEGER).value(137); + mb2v2.property("ORG").type(DataTypeCode.CONTROLLEDVOCABULARY) + .value(new VocabularyTermBuilder("TIGER").label("Tiger").getTerm()); + final Material m2v2 = mb2v2.property("P2", "blabla").getMaterial(); + final Material m4 = + new MaterialBuilder().code("M4").type("T2").property("P2", "hi").getMaterial(); + final RecordingMatcher<DetailedSearchCriteria> criteriaRecorder = + new RecordingMatcher<DetailedSearchCriteria>(); + final Sequence sequence = context.sequence("materials"); + context.checking(new Expectations() + { + { + exactly(2).of(server).tryToAuthenticateAsSystem(); + SessionContextDTO session = new SessionContextDTO(); + session.setSessionToken(SESSION_TOKEN); + will(returnValue(session)); + + one(server).searchForMaterials(with(SESSION_TOKEN), with(criteriaRecorder)); + will(returnValue(Arrays.asList(m1, m2, m3))); + inSequence(sequence); + + one(server).searchForMaterials(with(SESSION_TOKEN), with(criteriaRecorder)); + will(returnValue(Arrays.asList(m1, m2v2, m4))); + inSequence(sequence); + } + }); + + materialReportingTask.execute(); + materialReportingTask.execute(); + + List<?> result = loadTable("report1"); + assertEquals("[{code=M1, description=null, id=null}]", result.toString()); + result = loadTable("report2"); + assertEquals("[{code=M2, greetings=blabla, material=null, organism=Tiger, " + + "rank=137, size=null, timestamp=null}, " + + "{code=M3, greetings=hello, material=null, organism=null, " + + "rank=null, size=null, timestamp=null}, " + + "{code=M4, greetings=hi, material=null, organism=null, " + + "rank=null, size=null, timestamp=null}]", result.toString()); + context.assertIsSatisfied(); + } + + private List<?> loadTable(String tableName) + { + return new JdbcTemplate(dbConfigContext.getDataSource()).query("select * from " + tableName + + " order by code", new ColumnMapRowMapper() + { + @SuppressWarnings("rawtypes") + @Override + protected Map createColumnMap(int columnCount) + { + return new TreeMap(); + } + + @Override + protected String getColumnKey(String columnName) + { + return columnName.toLowerCase(); + } + }); + } + private void createTables(String... creationStatements) throws Exception { Connection connection = null; diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/builders/VocabularyTermBuilder.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/builders/VocabularyTermBuilder.java index 493852e792a90520fdfad4f1b65992e0960da2bc..251980543a17de8a472ada80c01bf14c57e4d0f7 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/builders/VocabularyTermBuilder.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/builders/VocabularyTermBuilder.java @@ -20,21 +20,27 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.VocabularyTerm; /** * Builder for {@link VocabularyTerm} instance. - * + * * @author Franz-Josef Elmer */ public class VocabularyTermBuilder { private final VocabularyTerm term = new VocabularyTerm(); - + public VocabularyTermBuilder(String code) { term.setCode(code); } + public VocabularyTermBuilder label(String label) + { + term.setLabel(label); + return this; + } + public VocabularyTerm getTerm() { return term; } - + }