/*
* ModeShape (http://www.modeshape.org)
*
* 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.modeshape.jcr;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.junit.Assert.assertThat;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.TreeSet;
import javax.jcr.Binary;
import javax.jcr.ImportUUIDBehavior;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PropertyType;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
import javax.jcr.nodetype.NodeTypeManager;
import javax.jcr.nodetype.NodeTypeTemplate;
import javax.jcr.nodetype.PropertyDefinition;
import javax.jcr.nodetype.PropertyDefinitionTemplate;
import org.hamcrest.collection.IsIn;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.modeshape.common.FixFor;
import org.modeshape.jcr.api.value.DateTime;
import org.modeshape.jcr.value.DateTimeFactory;
/**
* @author jverhaeg
*/
public class JcrMultiValuePropertyTest extends MultiUseAbstractTest {
private org.modeshape.jcr.api.Property prop;
private byte[][] binaryValue;
private DateTime[] dateValue;
private double[] doubleValue;
private long[] longValue;
private String[] stringValue;
private boolean[] booleanValue;
private String[] nameValue;
private String[] pathValue;
private DateTimeFactory dateFactory;
protected AbstractJcrNode cars;
protected AbstractJcrNode altima;
protected AbstractJcrNode aston;
/**
* Initialize the expensive activities, and in particular the RepositoryNodeTypeManager instance.
*
* @throws Exception
*/
@BeforeClass
public static void beforeAll() throws Exception {
MultiUseAbstractTest.beforeAll();
NodeTypeManager mgr = session.getWorkspace().getNodeTypeManager();
// Define the node definition that will have all the different kinds of properties ...
NodeTypeTemplate nodeType = mgr.createNodeTypeTemplate();
nodeType.setMixin(true);
nodeType.setName("mixinWithAllPropTypes");
@SuppressWarnings( "unchecked" )
List<PropertyDefinitionTemplate> propDefns = nodeType.getPropertyDefinitionTemplates();
// Add a property for each type ...
PropertyDefinitionTemplate binaryDefn = mgr.createPropertyDefinitionTemplate();
binaryDefn.setName("binaryProperty");
binaryDefn.setRequiredType(PropertyType.BINARY);
binaryDefn.setMultiple(true);
propDefns.add(binaryDefn);
PropertyDefinitionTemplate booleanDefn = mgr.createPropertyDefinitionTemplate();
booleanDefn.setName("booleanProperty");
booleanDefn.setRequiredType(PropertyType.BOOLEAN);
booleanDefn.setMultiple(true);
propDefns.add(booleanDefn);
PropertyDefinitionTemplate dateDefn = mgr.createPropertyDefinitionTemplate();
dateDefn.setName("dateProperty");
dateDefn.setRequiredType(PropertyType.DATE);
dateDefn.setMultiple(true);
propDefns.add(dateDefn);
PropertyDefinitionTemplate doubleDefn = mgr.createPropertyDefinitionTemplate();
doubleDefn.setName("doubleProperty");
doubleDefn.setRequiredType(PropertyType.DOUBLE);
doubleDefn.setMultiple(true);
propDefns.add(doubleDefn);
PropertyDefinitionTemplate longDefn = mgr.createPropertyDefinitionTemplate();
longDefn.setName("longProperty");
longDefn.setRequiredType(PropertyType.LONG);
longDefn.setMultiple(true);
propDefns.add(longDefn);
PropertyDefinitionTemplate nameDefn = mgr.createPropertyDefinitionTemplate();
nameDefn.setName("nameProperty");
nameDefn.setRequiredType(PropertyType.NAME);
nameDefn.setMultiple(true);
propDefns.add(nameDefn);
PropertyDefinitionTemplate pathDefn = mgr.createPropertyDefinitionTemplate();
pathDefn.setName("pathProperty");
pathDefn.setRequiredType(PropertyType.PATH);
pathDefn.setMultiple(true);
propDefns.add(pathDefn);
PropertyDefinitionTemplate refDefn = mgr.createPropertyDefinitionTemplate();
refDefn.setName("referenceProperty");
refDefn.setRequiredType(PropertyType.REFERENCE);
refDefn.setMultiple(true);
propDefns.add(refDefn);
PropertyDefinitionTemplate ref2Defn = mgr.createPropertyDefinitionTemplate();
ref2Defn.setName("referenceProperty2");
ref2Defn.setRequiredType(PropertyType.REFERENCE);
ref2Defn.setMultiple(true);
propDefns.add(ref2Defn);
PropertyDefinitionTemplate stringDefn = mgr.createPropertyDefinitionTemplate();
stringDefn.setName("stringProperty");
stringDefn.setRequiredType(PropertyType.STRING);
stringDefn.setMultiple(true);
propDefns.add(stringDefn);
PropertyDefinitionTemplate undefinedDefn = mgr.createPropertyDefinitionTemplate();
undefinedDefn.setName("undefinedProperty");
undefinedDefn.setRequiredType(PropertyType.UNDEFINED);
undefinedDefn.setMultiple(true);
propDefns.add(undefinedDefn);
// Add the node type ...
mgr.registerNodeType(nodeType, true);
// Import the node types and the data ...
registerNodeTypes("cars.cnd");
importContent("/", "io/cars-system-view-with-uuids.xml", ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW);
}
@Override
@Before
public void beforeEach() throws Exception {
super.beforeEach();
session.getWorkspace().getNamespaceRegistry().registerNamespace("acme", "http://example.com");
dateFactory = session.dateFactory();
binaryValue = new byte[][] {"This is a binary value1".getBytes(), "This is a binary value2".getBytes()};
dateValue = new DateTime[] {dateFactory.create(), dateFactory.create().plusDays(1)};
doubleValue = new double[] {3.14159d, 1.0d};
longValue = new long[] {100L, 101L};
booleanValue = new boolean[] {true, false};
stringValue = new String[] {"stringValue1", "string value 2"};
nameValue = new String[] {"acme:SomeName1", "acme:SomeName2"};
pathValue = new String[] {"/Cars/Hybrid/Toyota Highlander/acme:SomethingElse", "/Cars/acme:Wow"};
// Add the mixin to the 'Cars' node ...
cars = session.getNode("/Cars");
cars.addMixin("mixinWithAllPropTypes");
altima = session.getNode("/Cars/Hybrid/Nissan Altima");
altima.addMixin("mix:referenceable");
aston = session.getNode("/Cars/Sports/Aston Martin DB9");
aston.addMixin("mix:referenceable");
// Set each property ...
cars.setProperty("booleanProperty", values(booleanValue));
cars.setProperty("dateProperty", values(dateValue));
cars.setProperty("doubleProperty", values(doubleValue));
cars.setProperty("binaryProperty", values(binaryValue));
cars.setProperty("longProperty", values(longValue));
cars.setProperty("referenceProperty", values(new Node[] {altima, aston}));
cars.setProperty("referenceProperty2",
values(PropertyType.STRING, new String[] {altima.getIdentifier(), aston.getIdentifier()}));
cars.setProperty("stringProperty", values(PropertyType.STRING, stringValue));
cars.setProperty("pathProperty", values(PropertyType.PATH, pathValue));
cars.setProperty("nameProperty", values(PropertyType.NAME, nameValue));
cars.setProperty("undefinedProperty", values(PropertyType.STRING, new String[] {"100", "200"}));
}
protected Value[] values( boolean[] values ) throws Exception {
Value[] result = new Value[values.length];
for (int i = 0; i != values.length; ++i) {
result[i] = session.getValueFactory().createValue(values[i]);
}
return result;
}
protected Value[] values( long[] values ) throws Exception {
Value[] result = new Value[values.length];
for (int i = 0; i != values.length; ++i) {
result[i] = session.getValueFactory().createValue(values[i]);
}
return result;
}
protected Value[] values( double[] values ) throws Exception {
Value[] result = new Value[values.length];
for (int i = 0; i != values.length; ++i) {
result[i] = session.getValueFactory().createValue(values[i]);
}
return result;
}
protected Value[] values( byte[][] values ) throws Exception {
Value[] result = new Value[values.length];
for (int i = 0; i != values.length; ++i) {
Binary binary = session.getValueFactory().createBinary(values[i]);
result[i] = session.getValueFactory().createValue(binary);
}
return result;
}
protected Value[] values( DateTime[] values ) throws Exception {
Value[] result = new Value[values.length];
for (int i = 0; i != values.length; ++i) {
result[i] = session.getValueFactory().createValue(values[i].toCalendar());
}
return result;
}
protected Value[] values( Node[] values ) throws Exception {
Value[] result = new Value[values.length];
for (int i = 0; i != values.length; ++i) {
result[i] = session.getValueFactory().createValue(values[i]);
}
return result;
}
protected Value[] values( int type,
String[] values ) throws Exception {
Value[] result = new Value[values.length];
for (int i = 0; i != values.length; ++i) {
result[i] = session.getValueFactory().createValue(values[i]);
}
return result;
}
@Test
public void shouldIndicateHasMultipleValues() throws Exception {
prop = cars.getProperty("booleanProperty");
PropertyDefinition def = prop.getDefinition();
assertThat(def.isMultiple(), is(true));
}
@Test( expected = ValueFormatException.class )
public void shouldNotProvideBooleanForMultiValuedProperty() throws Exception {
prop = cars.getProperty("booleanProperty");
prop.getBoolean();
}
@Test( expected = ValueFormatException.class )
public void shouldNotProvideBooleanConversionForMultiValuedProperty() throws Exception {
prop = cars.getProperty("booleanProperty");
prop.getAs(Boolean.class);
}
@Test( expected = ValueFormatException.class )
public void shouldNotProvideDateForMultiValuedProperty() throws Exception {
prop = cars.getProperty("dateProperty");
prop.getDate();
}
@Test( expected = ValueFormatException.class )
public void shouldNotProvideDoubleForMultiValuedProperty() throws Exception {
prop = cars.getProperty("doubleProperty");
prop.getDouble();
}
@Test( expected = ValueFormatException.class )
public void shouldNotProvideLongForMultiValuedProperty() throws Exception {
prop = cars.getProperty("longProperty");
prop.getLong();
}
@SuppressWarnings( "deprecation" )
@Test( expected = ValueFormatException.class )
public void shouldNotProvideStreamForMultiValuedProperty() throws Exception {
prop = cars.getProperty("binaryProperty");
prop.getStream();
}
@Test( expected = ValueFormatException.class )
public void shouldNotProvideStringForMultiValuedProperty() throws Exception {
prop = cars.getProperty("stringProperty");
prop.getString();
}
@Test( expected = ValueFormatException.class )
public void shouldNotProvideValue() throws Exception {
prop = cars.getProperty("stringProperty");
prop.getValue();
}
@Test
public void shouldProvideBooleanValues() throws Exception {
prop = cars.getProperty("booleanProperty");
Value[] vals = prop.getValues();
assertThat(vals, notNullValue());
assertThat(vals.length, is(2));
assertThat(vals[0].getBoolean(), is(true));
assertThat(vals[1].getBoolean(), is(false));
assertThat(prop.getAs(Boolean[].class), is(new Boolean[]{true, false}));
assertThat(prop.getAs(Boolean.class, 0), is(true));
assertThat(prop.getAs(Boolean.class, 1), is(false));
}
@Test
public void shouldProvideBinaryValues() throws Exception {
prop = cars.getProperty("binaryProperty");
Value[] vals = prop.getValues();
assertThat(vals, notNullValue());
assertThat(vals.length, is(2));
TreeSet<String> expectedBinaryValues = new TreeSet<String>(Arrays.asList("This is a binary value1", "This is a binary value2"));
assertThat(expectedBinaryValues.contains(vals[0].getString()), is(true));
assertThat(expectedBinaryValues.contains(vals[1].getString()), is(true));
assertThat(expectedBinaryValues.contains(prop.getAs(String.class, 0)), is(true));
assertThat(expectedBinaryValues.contains(prop.getAs(String.class, 1)), is(true));
assertThat(prop.getAs(org.modeshape.jcr.api.Binary[].class).length, is(2));
assertThat(prop.getAs(Binary[].class).length, is(2));
assertThat(prop.getAs(InputStream[].class).length, is(2));
}
@Test( expected = ValueFormatException.class )
public void shouldNotProvideLength() throws Exception {
prop = cars.getProperty("stringProperty");
prop.getLength();
}
@FixFor( "MODE-1720" )
@Test
public void shouldPropertyDereferenceReferenceProperties() throws Exception {
prop = cars.getProperty("referenceProperty");
Value[] vals = prop.getValues();
for (Value value : vals) {
String str = value.getString();
Node node = cars.getSession().getNodeByIdentifier(str);
assertThat(node, IsIn.isOneOf((Node)altima, (Node)aston));
}
Node[] nodes = prop.getAs(Node[].class);
for (Node node : nodes) {
assertThat(node, IsIn.isOneOf((Node)altima, (Node)aston));
}
NodeIterator nodeIterator = prop.getAs(NodeIterator.class);
while (nodeIterator.hasNext()) {
assertThat(nodeIterator.nextNode(), IsIn.isOneOf((Node)altima, (Node)aston));
}
assertThat(prop.getAs(Node.class, 0), IsIn.isOneOf((Node)altima, (Node)aston));
assertThat(prop.getAs(Node.class, 1), IsIn.isOneOf((Node)altima, (Node)aston));
}
@FixFor( "MODE-1720" )
@Test
public void shouldPropertyDereferenceReferencePropertiesCreatedFromStringValues() throws Exception {
prop = cars.getProperty("referenceProperty2");
Value[] vals = prop.getValues();
for (Value value : vals) {
String str = value.getString();
Node node = cars.getSession().getNodeByIdentifier(str);
assertThat(node, IsIn.isOneOf((Node)altima, (Node)aston));
}
}
}