/*
* Copyright 2010 Outerthought bvba
*
* 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 org.lilyproject.repository.impl.test;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.net.URI;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Random;
import org.apache.hadoop.hbase.util.Bytes;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.lilyproject.bytes.api.ByteArray;
import org.lilyproject.bytes.api.DataInput;
import org.lilyproject.bytes.api.DataOutput;
import org.lilyproject.bytes.impl.DataInputImpl;
import org.lilyproject.bytes.impl.DataOutputImpl;
import org.lilyproject.hadooptestfw.TestHelper;
import org.lilyproject.repository.api.Blob;
import org.lilyproject.repository.api.FieldType;
import org.lilyproject.repository.api.HierarchyPath;
import org.lilyproject.repository.api.IdGenerator;
import org.lilyproject.repository.api.IdentityRecordStack;
import org.lilyproject.repository.api.Link;
import org.lilyproject.repository.api.QName;
import org.lilyproject.repository.api.Record;
import org.lilyproject.repository.api.RecordException;
import org.lilyproject.repository.api.RecordType;
import org.lilyproject.repository.api.RecordTypeBuilder;
import org.lilyproject.repository.api.Repository;
import org.lilyproject.repository.api.RepositoryException;
import org.lilyproject.repository.api.Scope;
import org.lilyproject.repository.api.TypeException;
import org.lilyproject.repository.api.TypeManager;
import org.lilyproject.repository.api.UnknownValueTypeEncodingException;
import org.lilyproject.repository.api.ValueType;
import org.lilyproject.repository.api.ValueTypeFactory;
import org.lilyproject.repository.impl.valuetype.AbstractValueType;
import org.lilyproject.repotestfw.RepositorySetup;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class ValueTypeTest {
private static final RepositorySetup repoSetup = new RepositorySetup();
private static TypeManager typeManager;
private static Repository repository;
private static IdGenerator idGenerator;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
TestHelper.setupLogging();
// TODO this test relies on all blobs being inline blobs, since it reuses blob values
repoSetup.setBlobLimits(Long.MAX_VALUE, -1);
repoSetup.setupCore();
repoSetup.setupRepository();
typeManager = repoSetup.getTypeManager();
repository = (Repository)repoSetup.getRepositoryManager().getDefaultRepository().getDefaultTable();
idGenerator = repoSetup.getIdGenerator();
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
repoSetup.stop();
}
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@Test
public void testFooType() throws Exception {
try {
runValueTypeTests("fooRecordTypeId", "FOO", "foo", "bar", "pub");
fail("Excpeting TypeException");
} catch (TypeException expected) {
}
}
@Test
public void testStringType() throws Exception {
runValueTypeTests("stringRecordTypeId", "STRING", "foo", "bar", "pub");
}
@Test
public void testIntegerType() throws Exception {
runValueTypeTests("integerRecordTypeId", "INTEGER", Integer.MIN_VALUE, 0, Integer.MAX_VALUE);
}
@Test
public void testLongType() throws Exception {
runValueTypeTests("longRecordTypeId", "LONG", Long.MIN_VALUE, Long.valueOf(0), Long.MAX_VALUE);
}
@Test
public void testDoubleType() throws Exception {
runValueTypeTests("doubleRecordTypeId", "DOUBLE", Double.MIN_VALUE, Double.valueOf(0), Double.MAX_VALUE);
}
@Test
public void testDecimalType() throws Exception {
runValueTypeTests("decimalRecordTypeId", "DECIMAL", BigDecimal.valueOf(Double.MIN_EXPONENT), BigDecimal.ZERO, BigDecimal.valueOf(Long.MAX_VALUE));
}
@Test
public void testBooleanType() throws Exception {
runValueTypeTests("booleanRecordTypeId", "BOOLEAN", true, false, true);
}
@Test
public void testDateTimeType() throws Exception {
runValueTypeTests("dateTimeRecordTypeId", "DATETIME", new DateTime(DateTimeZone.UTC), new DateTime(Long.MAX_VALUE, DateTimeZone.UTC), new DateTime(Long.MIN_VALUE, DateTimeZone.UTC));
}
@Test
public void testDateType() throws Exception {
runValueTypeTests("dateRecordTypeId", "DATE", new LocalDate(DateTimeZone.UTC), new LocalDate(2900, 10, 14), new LocalDate(1300, 5, 4));
}
@Test
public void testUriType() throws Exception {
runValueTypeTests("uriRecordTypeId", "URI", URI.create("http://foo.com/bar"), URI.create("file://foo/com/bar.txt"), URI.create("https://site/index.html"));
}
@Test
public void testByteArrayType() throws Exception {
runValueTypeTests("byteArrayRecordTypeId", "BYTEARRAY", new ByteArray(Bytes.toBytes("bytes1")), new ByteArray(
Bytes.toBytes("bytes2")), new ByteArray(Bytes.toBytes("bbb")));
}
@Test
public void testBlobType() throws Exception {
Blob blob1 = new Blob("text/html", Long.MAX_VALUE, null);
writeBlob(blob1, Bytes.toBytes("aKey"));
Blob blob2 = new Blob("image/jpeg", Long.MIN_VALUE, "images/image.jpg");
writeBlob(blob2, Bytes.toBytes("anotherKey"));
Blob blob3 = new Blob("text/plain", Long.valueOf(0), null);
writeBlob(blob3, Bytes.toBytes("thirdKey"));
runValueTypeTests("blobTypeId", "BLOB", blob1, blob2, blob3);
}
private void writeBlob(Blob blob, byte[] bytes) throws RepositoryException, InterruptedException, IOException {
OutputStream bos = repository.getOutputStream(blob);
bos.write(bytes);
bos.close();
}
@Test
public void testRecordType() throws Exception {
FieldType fieldType1 = typeManager.createFieldType(typeManager.newFieldType(typeManager.getValueType("STRING"), new QName("testRecordType", "field1"), Scope.NON_VERSIONED));
FieldType fieldType2 = typeManager.createFieldType(typeManager.newFieldType(typeManager.getValueType("INTEGER"), new QName("testRecordType", "field2"), Scope.NON_VERSIONED));
RecordTypeBuilder rtBuilder = typeManager.recordTypeBuilder();
RecordType valueTypeRT = rtBuilder.name(new QName("testRecordType", "recordValueTypeRecordType"))
.field(fieldType1.getId(), false)
.field(fieldType2.getId(), true)
.create();
Record recordField1 = repository.recordBuilder()
.recordType(valueTypeRT.getName(), 1L)
.field(fieldType1.getName(), "abc")
.field(fieldType2.getName(), 123)
.build();
Record recordField2 = repository.recordBuilder()
.recordType(valueTypeRT.getName(), 1L)
.field(fieldType1.getName(), "def")
.field(fieldType2.getName(), 456)
.build();
Record recordField3 = repository.recordBuilder()
.recordType(valueTypeRT.getName(), 1L)
.field(fieldType1.getName(), "xyz")
.field(fieldType2.getName(), 888)
.build();
testType("recordValueTypeId", "RECORD<{testRecordType}recordValueTypeRecordType>", recordField1);
testType("recordValueTypeId", "LIST<RECORD<{testRecordType}recordValueTypeRecordType>>", Arrays.asList(recordField1, recordField2));
testType("recordValueTypeId", "PATH<RECORD<{testRecordType}recordValueTypeRecordType>>", new HierarchyPath(recordField1, recordField2));
testType("recordValueTypeId", "LIST<PATH<RECORD<{testRecordType}recordValueTypeRecordType>>>", Arrays.asList(new HierarchyPath(recordField1, recordField2), new HierarchyPath(recordField1, recordField3)));
}
@Test
public void testLinkType() throws Exception {
FieldType fieldType1 = typeManager.createFieldType(typeManager.newFieldType(typeManager.getValueType("STRING"), new QName("testLinkType", "field1"), Scope.NON_VERSIONED));
RecordTypeBuilder rtBuilder = typeManager.recordTypeBuilder();
RecordType valueTypeRT = rtBuilder.name(new QName("testLinkType", "linkValueTypeRecordType"))
.field(fieldType1.getId(), false)
.create();
testType("recordValueTypeId", "LINK", new Link(idGenerator.newRecordId()));
testType("recordValueTypeId", "LINK<{testLinkType}linkValueTypeRecordType>", new Link(idGenerator.newRecordId()));
testType("recordValueTypeId", "LIST<LINK<{testLinkType}linkValueTypeRecordType>>", Arrays.asList(new Link(idGenerator.newRecordId()), new Link(idGenerator.newRecordId())));
testType("recordValueTypeId", "LIST<LINK>", Arrays.asList(new Link(idGenerator.newRecordId()), new Link(idGenerator.newRecordId())));
testType("recordValueTypeId", "PATH<LINK<{testLinkType}linkValueTypeRecordType>>", new HierarchyPath(new Link(idGenerator.newRecordId()), new Link(idGenerator.newRecordId())));
testType("recordValueTypeId", "PATH<LINK>", new HierarchyPath(new Link(idGenerator.newRecordId()), new Link(idGenerator.newRecordId())));
testType("recordValueTypeId", "LIST<PATH<LINK<{testLinkType}linkValueTypeRecordType>>>", Arrays.asList(new HierarchyPath(new Link(idGenerator.newRecordId()), new Link(idGenerator.newRecordId())), new HierarchyPath(new Link(idGenerator.newRecordId()), new Link(idGenerator.newRecordId()))));
testType("recordValueTypeId", "LIST<PATH<LINK>>", Arrays.asList(new HierarchyPath(new Link(idGenerator.newRecordId()), new Link(idGenerator.newRecordId())), new HierarchyPath(new Link(idGenerator.newRecordId()), new Link(idGenerator.newRecordId()))));
}
@Test
public void testNewValueType() throws Exception {
typeManager.registerValueType(XYValueType.NAME, factory());
runValueTypeTests("xyRecordTypeId", "XY", new XYCoordinates(-1, 1), new XYCoordinates(Integer.MIN_VALUE, Integer.MAX_VALUE), new XYCoordinates(666, 777));
}
@Test
public void testComparators() throws Exception {
Comparator comparator = typeManager.getValueType("LONG").getComparator();
assertTrue(comparator.compare(new Long(2), new Long(5)) < 0);
assertTrue(comparator.compare(new Long(5), new Long(5)) == 0);
comparator = typeManager.getValueType("DOUBLE").getComparator();
assertTrue(comparator.compare(new Double(2.2d), new Double(5.5d)) < 0);
comparator = typeManager.getValueType("BLOB").getComparator();
assertNull(comparator);
comparator = typeManager.getValueType("DATE").getComparator();
assertTrue(comparator.compare(new LocalDate(2012, 5, 5), new LocalDate(2013, 5, 5)) < 0);
assertTrue(comparator.compare(new LocalDate(2012, 5, 5), new LocalDate(2012, 5, 5)) == 0);
comparator = typeManager.getValueType("URI").getComparator();
assertNull(comparator);
comparator = typeManager.getValueType("DECIMAL").getComparator();
assertTrue(comparator.compare(new BigDecimal("2"), new BigDecimal("5")) < 0);
assertTrue(comparator.compare(new BigDecimal("5"), new BigDecimal("5")) == 0);
comparator = typeManager.getValueType("BOOLEAN").getComparator();
assertTrue(comparator.compare(Boolean.FALSE, Boolean.TRUE) < 0);
assertTrue(comparator.compare(Boolean.TRUE, Boolean.TRUE) == 0);
comparator = typeManager.getValueType("DATETIME").getComparator();
assertTrue(comparator.compare(new DateTime(2012, 5, 5, 6, 7, 8, 9), new DateTime(2012, 5, 5, 6, 7, 8, 10)) < 0);
assertTrue(comparator.compare(new DateTime(2012, 5, 5, 6, 7, 8, 9), new DateTime(2012, 5, 5, 6, 7, 8, 9)) == 0);
comparator = typeManager.getValueType("STRING").getComparator();
assertTrue(comparator.compare("a", "b") < 0);
assertTrue(comparator.compare("a", "a") == 0);
comparator = typeManager.getValueType("INTEGER").getComparator();
assertTrue(comparator.compare(new Integer(2), new Integer(5)) < 0);
assertTrue(comparator.compare(new Integer(5), new Integer(5)) == 0);
comparator = typeManager.getValueType("LINK").getComparator();
assertNull(comparator);
comparator = typeManager.getValueType("LIST<STRING>").getComparator();
assertNull(comparator);
comparator = typeManager.getValueType("PATH<STRING>").getComparator();
assertNull(comparator);
comparator = typeManager.getValueType("RECORD<{testRecordType}recordValueTypeRecordType>").getComparator();
assertNull(comparator);
}
private void runValueTypeTests(String name, String valueType, Object value1, Object value2, Object value3) throws Exception {
testType(name, valueType, value1);
testType(name, "LIST<"+valueType+">", Arrays.asList(value1, value2));
testType(name, "PATH<"+valueType+">", new HierarchyPath(value1, value2));
testType(name, "LIST<PATH<"+valueType+">>", Arrays.asList(new HierarchyPath(value1, value2), new HierarchyPath(value1, value3)));
}
private void testType(String name, String valueType,
Object fieldValue) throws Exception {
String testName = name+valueType;
QName fieldTypeName = new QName("valueTypeTest", testName+"FieldId");
FieldType fieldType = typeManager.createFieldType(typeManager.newFieldType(typeManager.getValueType(
valueType), fieldTypeName, Scope.VERSIONED));
RecordType recordType = typeManager.newRecordType(new QName("valueTypeTest", testName+"RecordTypeId"));
recordType.addFieldTypeEntry(typeManager.newFieldTypeEntry(fieldType.getId(), true));
recordType = typeManager.createRecordType(recordType);
Record record = repository.newRecord(idGenerator.newRecordId());
record.setRecordType(recordType.getName(), recordType.getVersion());
record.setField(fieldType.getName(), fieldValue);
repository.create(record);
Record actualRecord = repository.read(record.getId());
assertEquals(fieldValue, actualRecord.getField(fieldType.getName()));
}
public class XYValueType extends AbstractValueType implements ValueType {
private Random random = new Random();
public static final String NAME = "XY";
@Override
public String getBaseName() {
return NAME;
}
@Override
public ValueType getDeepestValueType() {
return this;
}
@Override
public Object read(DataInput dataInput) throws UnknownValueTypeEncodingException {
byte encodingVersion = dataInput.readByte();
int x;
int y;
if ((byte)1 == encodingVersion) {
x = dataInput.readInt();
y = dataInput.readInt();
} else if ((byte)2 == encodingVersion) {
y = dataInput.readInt();
x = dataInput.readInt();
} else {
throw new UnknownValueTypeEncodingException(NAME, encodingVersion);
}
return new XYCoordinates(x, y);
}
@Override
public void write(Object value, DataOutput dataOutput, IdentityRecordStack parentRecords) {
if (random.nextBoolean()) {
dataOutput.writeByte((byte)1); // encoding version 1
dataOutput.writeInt(((XYCoordinates) value).getX());
dataOutput.writeInt(((XYCoordinates) value).getY());
} else {
dataOutput.writeByte((byte)2); // encoding version 2
dataOutput.writeInt(((XYCoordinates) value).getY());
dataOutput.writeInt(((XYCoordinates) value).getX());
}
}
@Override
public Class getType() {
return XYCoordinates.class;
}
@Override
public Comparator getComparator() {
return null;
}
}
//
// Factory
//
public ValueTypeFactory factory() {
return new XYValueTypeFactory();
}
public class XYValueTypeFactory implements ValueTypeFactory {
@Override
public ValueType getValueType(String typeParams) {
return new XYValueType();
}
}
private class XYCoordinates {
private final int x;
private final int y;
XYCoordinates(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
@Override
public String toString() {
return "["+x+","+y+"]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getOuterType().hashCode();
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
XYCoordinates other = (XYCoordinates) obj;
if (!getOuterType().equals(other.getOuterType())) {
return false;
}
if (x != other.x) {
return false;
}
if (y != other.y) {
return false;
}
return true;
}
private ValueTypeTest getOuterType() {
return ValueTypeTest.this;
}
}
@Test
public void testRecordVTTypeInVT() throws Exception {
String ns = "testRecordVTTypeInVT";
// Create a fieldType to be used as a field in a record type to be used as the type for a RecordValueType
FieldType fieldType1 = typeManager.createFieldType(typeManager.newFieldType(typeManager.getValueType("STRING"), new QName(ns, "field1"), Scope.NON_VERSIONED));
// Create a record type to be used as the type for a RecordValueType
RecordType rt1 = typeManager.recordTypeBuilder()
.name(new QName(ns, "rt1"))
.field(fieldType1.getId(), false)
.create();
// Make a RecordValueType with the record type specified
ValueType recordVT1 = typeManager.getValueType("RECORD<{"+ns+"}rt1>");
// Create a fieldType with as value type a RecordValueType
FieldType fieldType2 = typeManager.createFieldType(typeManager.newFieldType(recordVT1, new QName(ns, "field2"), Scope.NON_VERSIONED));
// Create a recordType with a field of this field type
RecordType rt2 = typeManager.recordTypeBuilder()
.name(new QName(ns, "rt2"))
.field(fieldType2.getId(), false)
.create();
// Make a record to be used as field value
Record recordField = repository.recordBuilder().field(new QName(ns, "field1"), "abc").build();
// Create a record with a record as field
Record createdRecord = repository.recordBuilder().recordType(new QName(ns, "rt2")).field(new QName(ns,"field2"), recordField).create();
// Read the record and check the field of the record-field
Record readRecord = repository.read(createdRecord.getId());
Record readRecordField = (Record)readRecord.getField(new QName(ns, "field2"));
assertEquals("abc", recordField.getField(new QName(ns, "field1")));
assertNull(readRecordField.getId()); // The record field should have no Id
assertNull(readRecordField.getVersion()); // The record field should have no version
}
@Test
public void testRecordVTNested() throws Exception {
String ns = "testRecordVTNested";
// Create a fieldType to be used as a field in a record type to be used as the type for a RecordValueType
FieldType fieldType1 = typeManager.createFieldType(typeManager.newFieldType(typeManager.getValueType("STRING"), new QName(ns, "field1"), Scope.NON_VERSIONED));
// Create a record type to be used as the type for a RecordValueType
RecordType rt1 = typeManager.recordTypeBuilder()
.name(new QName(ns, "rt1"))
.field(fieldType1.getId(), false)
.create();
// Make a RecordValueType with the record type specified
ValueType recordVT1 = typeManager.getValueType("RECORD<{"+ns+"}rt1>");
// Create a fieldType with as value type a RecordValueType
FieldType fieldType2 = typeManager.createFieldType(typeManager.newFieldType(recordVT1, new QName(ns, "field2"), Scope.NON_VERSIONED));
// Create a recordType with a field of this field type
RecordType rt2 = typeManager.recordTypeBuilder()
.name(new QName(ns, "rt2"))
.field(fieldType2.getId(), false)
.create();
// Create a fieldType with as value type a 'nested' RecordValueType
ValueType recordVT2 = typeManager.getValueType("RECORD<{"+ns+"}rt2>");
FieldType fieldType3 = typeManager.createFieldType(typeManager.newFieldType(recordVT2, new QName(ns, "field3"), Scope.NON_VERSIONED));
RecordType rt3 = typeManager.recordTypeBuilder()
.name(new QName(ns, "rt3"))
.field(fieldType3.getId(), false)
.create();
// Make nested records
Record recordField1 = repository.recordBuilder().field(new QName(ns, "field1"), "abc").build();
Record recordField2 = repository.recordBuilder().field(new QName(ns, "field2"), recordField1).build();
// Create a record with nested records
Record createdRecord = repository.recordBuilder().recordType(new QName(ns, "rt3")).field(new QName(ns,"field3"), recordField2).create();
// Read the record and check the field of the record-field
Record readRecord = repository.read(createdRecord.getId());
Record nestedRecord1 = readRecord.getField(new QName(ns, "field3"));
Record nestedRecord2 = nestedRecord1.getField(new QName(ns, "field2"));
assertEquals("abc", nestedRecord2.getField(new QName(ns, "field1")));
}
@Test
public void testRecordVTTypeInRecord() throws Exception {
String ns = "testRecordVTTypeInRecord";
// Create a fieldType to be used as a field in a record type to be used as the type for a RecordValueType
FieldType fieldType1 = typeManager.createFieldType(typeManager.newFieldType(typeManager.getValueType("STRING"), new QName(ns, "field1"), Scope.NON_VERSIONED));
// Create a record type to be used as the type for a RecordValueType
RecordType rt1 = typeManager.recordTypeBuilder().name(new QName(ns, "rt1")).field(fieldType1.getId(), false).create();
// Create record types to be used as versioned and versioned-mutable record types, which should be ignored
RecordType vrt = typeManager.recordTypeBuilder().name(new QName(ns, "vrt")).field(fieldType1.getId(), false).create();
RecordType vmrt = typeManager.recordTypeBuilder().name(new QName(ns, "vmrt")).field(fieldType1.getId(), false).create();
// Make a RecordValueType without the record type specified
ValueType recordVT1 = typeManager.getValueType("RECORD");
// Create a fieldType with as value type a RecordValueType
FieldType fieldType2 = typeManager.createFieldType(typeManager.newFieldType(recordVT1, new QName(ns, "field2"), Scope.NON_VERSIONED));
// Create a recordType with a field of this field type
RecordType rt2 = typeManager.recordTypeBuilder()
.name(new QName(ns, "rt2"))
.field(fieldType2.getId(), false)
.create();
// Make a record to be used as field value, specify the record type here
Record recordField = repository.recordBuilder().recordType(new QName(ns, "rt1")).field(new QName(ns, "field1"), "abc").build();
recordField.setRecordType(Scope.VERSIONED, new QName(ns, "vrt"), null); // This record type should be ignored
recordField.setRecordType(Scope.VERSIONED_MUTABLE, new QName(ns, "vmrt"), null); // This record type should be ignored
// Create a record with a record as field
Record createdRecord = repository.recordBuilder().recordType(new QName(ns, "rt2")).field(new QName(ns,"field2"), recordField).create();
// Read the record and check the field of the record-field
Record readRecord = repository.read(createdRecord.getId());
Record readRecordField = (Record)readRecord.getField(new QName(ns, "field2"));
assertEquals("abc", readRecordField.getField(new QName(ns, "field1")));
assertEquals(new QName(ns, "rt1"), readRecordField.getRecordTypeName(Scope.NON_VERSIONED));
assertNull(readRecordField.getRecordTypeName(Scope.VERSIONED));
assertNull(readRecordField.getRecordTypeName(Scope.VERSIONED_MUTABLE));
}
@Test
public void testRecordVTNoTypeDefined() throws Exception {
String ns = "testRecordVTNoTypeDefined";
// Create a fieldType to be used as a field in a record type to be used as the type for a RecordValueType
FieldType fieldType1 = typeManager.createFieldType(typeManager.newFieldType(typeManager.getValueType("STRING"), new QName(ns, "field1"), Scope.NON_VERSIONED));
// Create a record type to be used as the type for a RecordValueType
RecordType rt1 = typeManager.recordTypeBuilder()
.name(new QName(ns, "rt1"))
.field(fieldType1.getId(), false)
.create();
// Make a RecordValueType without the record type specified
ValueType recordVT1 = typeManager.getValueType("RECORD");
// Create a fieldType with as value type a RecordValueType
FieldType fieldType2 = typeManager.createFieldType(typeManager.newFieldType(recordVT1, new QName(ns, "field2"), Scope.NON_VERSIONED));
// Create a recordType with a field of this field type
RecordType rt2 = typeManager.recordTypeBuilder()
.name(new QName(ns, "rt2"))
.field(fieldType2.getId(), false)
.create();
// Make a record to be used as field value, don't specify the record type here either
Record recordField = repository.recordBuilder().field(new QName(ns, "field1"), "abc").build();
// Create a record with a record as field
try {
Record createdRecord = repository.recordBuilder().recordType(new QName(ns, "rt2")).field(new QName(ns,"field2"), recordField).create();
Assert.fail();
} catch (RepositoryException expected) {
}
}
@Test
public void testRecordVTUndefinedField() throws Exception {
String ns = "testRecordVTUndefinedField";
// Create a fieldType to be used as a field in a record type to be used as the type for a RecordValueType
FieldType fieldType1 = typeManager.createFieldType(typeManager.newFieldType(typeManager.getValueType("STRING"), new QName(ns, "field1"), Scope.NON_VERSIONED));
FieldType fieldType1b = typeManager.createFieldType(typeManager.newFieldType(typeManager.getValueType("STRING"), new QName(ns, "field1b"), Scope.NON_VERSIONED));
// Create a record type to be used as the type for a RecordValueType
RecordType rt1 = typeManager.recordTypeBuilder()
.name(new QName(ns, "rt1"))
.field(fieldType1.getId(), false)
.field(fieldType1b.getId(), false)
.create();
// Make a RecordValueType without the record type specified
ValueType recordVT1 = typeManager.getValueType("RECORD");
// Create a fieldType with as value type a RecordValueType
FieldType fieldType2 = typeManager.createFieldType(typeManager.newFieldType(recordVT1, new QName(ns, "field2"), Scope.NON_VERSIONED));
// Create a recordType with a field of this field type
RecordType rt2 = typeManager.recordTypeBuilder()
.name(new QName(ns, "rt2"))
.field(fieldType2.getId(), false)
.create();
// Make a record to be used as field value, specify the record type here
// Only fill in field1, not field1b
Record recordField = repository.recordBuilder().recordType(new QName(ns, "rt1")).field(new QName(ns, "field1"), "abc").build();
// Create a record with a record as field
Record createdRecord = repository.recordBuilder().recordType(new QName(ns, "rt2")).field(new QName(ns,"field2"), recordField).create();
// Read the record and check the field of the record-field
Record readRecord = repository.read(createdRecord.getId());
Record readRecordInRecord = (Record)readRecord.getField(new QName(ns, "field2"));
assertEquals("abc", readRecordInRecord.getField(new QName(ns, "field1")));
Assert.assertFalse(readRecordInRecord.hasField(new QName(ns, "field1b")));
}
@Test
public void testRecordVTUnallowedField() throws Exception {
String ns = "testRecordVTUnallowedField";
// Create a fieldType to be used as a field in a record type to be used as the type for a RecordValueType
FieldType fieldType1 = typeManager.createFieldType(typeManager.newFieldType(typeManager.getValueType("STRING"), new QName(ns, "field1"), Scope.NON_VERSIONED));
FieldType fieldType1b = typeManager.createFieldType(typeManager.newFieldType(typeManager.getValueType("STRING"), new QName(ns, "field1b"), Scope.NON_VERSIONED));
// Create a record type to be used as the type for a RecordValueType
// Do not add fieldType1b to the record type
RecordType rt1 = typeManager.recordTypeBuilder()
.name(new QName(ns, "rt1"))
.field(fieldType1.getId(), false)
.create();
// Make a RecordValueType without the record type specified
ValueType recordVT1 = typeManager.getValueType("RECORD");
// Create a fieldType with as value type a RecordValueType
FieldType fieldType2 = typeManager.createFieldType(typeManager.newFieldType(recordVT1, new QName(ns, "field2"), Scope.NON_VERSIONED));
// Create a recordType with a field of this field type
RecordType rt2 = typeManager.recordTypeBuilder()
.name(new QName(ns, "rt2"))
.field(fieldType2.getId(), false)
.create();
// Make a record to be used as field value, specify the record type here
Record recordField = repository.recordBuilder()
.recordType(new QName(ns, "rt1"))
.field(new QName(ns, "field1"), "abc")
.field(new QName(ns, "field1b"), "def") // Also fill in field1b, although the recordType does not specify it
.build();
// Create a record with a record as field
try {
repository.recordBuilder().recordType(new QName(ns, "rt2")).field(new QName(ns,"field2"), recordField).create();
Assert.fail();
} catch (RepositoryException expected) {
}
}
@Test
public void testRecordVTReadWrite() throws Exception {
String ns = "testRecordVTWrite";
// Create a fieldType to be used as a field in a record type to be used as the type for a RecordValueType
FieldType fieldType1 = typeManager.createFieldType(typeManager.newFieldType(typeManager.getValueType("STRING"), new QName(ns, "field1"), Scope.NON_VERSIONED));
// Create a record type to be used as the type for a RecordValueType
RecordType rt1 = typeManager.recordTypeBuilder().name(new QName(ns, "rt1")).field(fieldType1.getId(), false).create();
// Make a RecordValueType without the record type specified
ValueType recordVT = typeManager.getValueType("RECORD");
RecordType rt2 = typeManager.recordTypeBuilder()
.name(new QName(ns, "rt2"))
.supertype().id(rt1.getId()).add()
.create();
// Create a record with a record as field
Record createdRecord = repository.recordBuilder()
.recordType(new QName(ns, "rt2"))
.field(new QName(ns,"field1"), "def")
.create();
DataOutput dataOutput = new DataOutputImpl();
// Do a write and lets not get exceptions
recordVT.write(createdRecord, dataOutput, new IdentityRecordStack());
DataInput dataInput = new DataInputImpl(dataOutput.toByteArray());
// Do a read and lets not get exceptions
Record readRecord = recordVT.read(dataInput);
assertEquals(createdRecord.getFields(), readRecord.getFields());
}
@Test
public void testInvalidValueTypeSyntax() throws Exception {
try {
typeManager.getValueType("LIST<");
fail("expected exception");
} catch (IllegalArgumentException e) {
// expected
}
try {
typeManager.getValueType("LIST<>");
fail("expected exception");
} catch (IllegalArgumentException e) {
// expected
}
try {
typeManager.getValueType("LIST<!");
fail("expected exception");
} catch (IllegalArgumentException e) {
// expected
}
try {
typeManager.getValueType("LIST<STRING<>");
fail("expected exception");
} catch (IllegalArgumentException e) {
// expected
}
try {
typeManager.getValueType("RECORD<!namespace}name>");
fail("expected exception");
} catch (IllegalArgumentException e) {
// expected
}
}
}