| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 |
| * |
| * https://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.apache.directory.api.ldap.model.entry; |
| |
| |
| import static org.junit.jupiter.api.Assertions.assertEquals; |
| import static org.junit.jupiter.api.Assertions.assertFalse; |
| import static org.junit.jupiter.api.Assertions.assertNotSame; |
| import static org.junit.jupiter.api.Assertions.assertNull; |
| import static org.junit.jupiter.api.Assertions.assertTrue; |
| import static org.junit.jupiter.api.Assertions.fail; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashSet; |
| import java.util.List; |
| |
| import org.apache.directory.api.ldap.model.exception.LdapException; |
| import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException; |
| import org.apache.directory.api.ldap.model.schema.AttributeType; |
| import org.apache.directory.api.ldap.model.schema.SyntaxChecker; |
| import org.apache.directory.api.ldap.model.schema.comparators.StringComparator; |
| import org.apache.directory.api.ldap.model.schema.normalizers.DeepTrimToLowerNormalizer; |
| import org.apache.directory.api.ldap.model.schema.normalizers.NoOpNormalizer; |
| import org.apache.directory.api.ldap.model.schema.syntaxCheckers.OctetStringSyntaxChecker; |
| |
| import org.junit.jupiter.api.BeforeEach; |
| import org.junit.jupiter.api.Test; |
| import org.junit.jupiter.api.parallel.Execution; |
| import org.junit.jupiter.api.parallel.ExecutionMode; |
| |
| |
| /** |
| * Tests that the Value class works properly as expected. |
| * |
| * Some notes while conducting tests: |
| * |
| * <ul> |
| * <li>comparing values with different types - how does this behave</li> |
| * <li>exposing access to at from value or to a comparator?</li> |
| * </ul> |
| * |
| * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> |
| */ |
| @Execution(ExecutionMode.CONCURRENT) |
| public class StringValueAttributeTypeTest |
| { |
| private EntryUtils.S s; |
| private EntryUtils.AT at; |
| private EntryUtils.MR mr; |
| |
| |
| /** |
| * Initialize an AttributeType and the associated MatchingRule |
| * and Syntax |
| */ |
| @BeforeEach |
| public void initAT() |
| { |
| s = new EntryUtils.S( "1.1.1.1", true ); |
| s.setSyntaxChecker( OctetStringSyntaxChecker.INSTANCE ); |
| mr = new EntryUtils.MR( "1.1.2.1" ); |
| mr.setSyntax( s ); |
| mr.setLdapComparator( new StringComparator( "1.1.2.1" ) ); |
| mr.setNormalizer( new DeepTrimToLowerNormalizer( "1.1.2.1" ) ); |
| at = new EntryUtils.AT( "1.1.3.1" ); |
| at.setEquality( mr ); |
| at.setOrdering( mr ); |
| at.setSubstring( mr ); |
| at.setSyntax( s ); |
| } |
| |
| |
| /** |
| * Serialize a Value |
| */ |
| private ByteArrayOutputStream serializeValue( Value value ) throws IOException |
| { |
| ObjectOutputStream oOut = null; |
| ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| |
| try |
| { |
| oOut = new ObjectOutputStream( out ); |
| value.writeExternal( oOut ); |
| } |
| catch ( IOException ioe ) |
| { |
| throw ioe; |
| } |
| finally |
| { |
| try |
| { |
| if ( oOut != null ) |
| { |
| oOut.flush(); |
| oOut.close(); |
| } |
| } |
| catch ( IOException ioe ) |
| { |
| throw ioe; |
| } |
| } |
| |
| return out; |
| } |
| |
| |
| /** |
| * Deserialize a Value |
| */ |
| private Value deserializeValue( ByteArrayOutputStream out ) throws IOException, ClassNotFoundException |
| { |
| ObjectInputStream oIn = null; |
| ByteArrayInputStream in = new ByteArrayInputStream( out.toByteArray() ); |
| |
| try |
| { |
| oIn = new ObjectInputStream( in ); |
| |
| Value value = Value.createValue( at ); |
| |
| value.readExternal( oIn ); |
| |
| return value; |
| } |
| catch ( IOException ioe ) |
| { |
| throw ioe; |
| } |
| finally |
| { |
| try |
| { |
| if ( oIn != null ) |
| { |
| oIn.close(); |
| } |
| } |
| catch ( IOException ioe ) |
| { |
| throw ioe; |
| } |
| } |
| } |
| |
| |
| /** |
| * Test the constructor with a null value |
| */ |
| @Test |
| public void testClientStringValueNullValue() throws LdapInvalidAttributeValueException |
| { |
| AttributeType attribute = EntryUtils.getIA5StringAttributeType(); |
| |
| Value value = new Value( attribute, (String)null ); |
| |
| assertNull( value.getString() ); |
| assertTrue( value.isNull() ); |
| } |
| |
| |
| /** |
| * Test the getNormValue method |
| */ |
| @Test |
| public void testGetNormalizedValue() throws LdapInvalidAttributeValueException |
| { |
| AttributeType attribute = EntryUtils.getIA5StringAttributeType(); |
| |
| Value sv = new Value( attribute, (String)null ); |
| |
| assertTrue( sv.isSchemaAware() ); |
| assertNull( sv.getString() ); |
| assertTrue( sv.isSchemaAware() ); |
| |
| sv = new Value( attribute, "" ); |
| assertTrue( sv.isSchemaAware() ); |
| assertEquals( 0, sv.compareTo( " " ) ); |
| assertTrue( sv.isSchemaAware() ); |
| |
| sv = new Value( attribute, "TEST" ); |
| assertTrue( sv.isSchemaAware() ); |
| assertEquals( 0, sv.compareTo( " test " ) ); |
| } |
| |
| |
| /** |
| * Test the isValid method |
| * |
| * The SyntaxChecker does not accept values longer than 5 chars. |
| */ |
| @Test |
| public void testIsValid() throws LdapInvalidAttributeValueException |
| { |
| AttributeType attribute = EntryUtils.getIA5StringAttributeType(); |
| |
| new Value( attribute, (String)null ); |
| new Value( attribute, "" ); |
| new Value( attribute, "TEST" ); |
| |
| try |
| { |
| new Value( attribute, "testlong" ); |
| fail(); |
| } |
| catch ( LdapInvalidAttributeValueException liave ) |
| { |
| assertTrue( true ); |
| } |
| } |
| |
| |
| /** |
| * Test the normalize method |
| */ |
| @Test |
| public void testApply() throws LdapException |
| { |
| AttributeType attribute = EntryUtils.getIA5StringAttributeType(); |
| Value sv = Value.createValue( attribute ); |
| |
| sv = new Value( at, sv ); |
| assertEquals( 0, sv.compareTo( ( String ) null ) ); |
| |
| sv = new Value( attribute, "" ); |
| sv = new Value( at, sv ); |
| assertEquals( 0, sv.compareTo( " " ) ); |
| |
| sv = new Value( attribute, " A TEST " ); |
| assertEquals( 0, sv.compareTo( " a test " ) ); |
| } |
| |
| |
| /** |
| * Test the instanceOf method |
| */ |
| @Test |
| public void testInstanceOf() throws LdapException |
| { |
| AttributeType attribute = EntryUtils.getIA5StringAttributeType(); |
| Value ssv = Value.createValue( attribute ); |
| |
| assertTrue( ssv.isInstanceOf( attribute ) ); |
| |
| attribute = EntryUtils.getBytesAttributeType(); |
| |
| assertFalse( ssv.isInstanceOf( attribute ) ); |
| } |
| |
| |
| /** |
| * Test the getAttributeType method |
| */ |
| @Test |
| public void testgetAttributeType() |
| { |
| AttributeType attribute = EntryUtils.getIA5StringAttributeType(); |
| Value ssv = Value.createValue( attribute ); |
| |
| assertEquals( attribute, ssv.getAttributeType() ); |
| } |
| |
| |
| /** |
| * Test the equals method |
| */ |
| @Test |
| public void testEquals() throws LdapInvalidAttributeValueException |
| { |
| AttributeType at1 = EntryUtils.getIA5StringAttributeType(); |
| AttributeType at2 = EntryUtils.getBytesAttributeType(); |
| |
| Value value1 = new Value( at1, "test" ); |
| Value value2 = new Value( at1, "test" ); |
| Value value3 = new Value( at1, "TEST" ); |
| Value value4 = new Value( at1, "tes" ); |
| Value value5 = new Value( at1, (byte[])null ); |
| Value valueBytes = new Value( at2, new byte[] |
| { 0x01 } ); |
| Value valueString = new Value( at, "test" ); |
| |
| assertTrue( value1.equals( value1 ) ); |
| assertTrue( value1.equals( value2 ) ); |
| assertTrue( value1.equals( value3 ) ); |
| assertFalse( value1.equals( value4 ) ); |
| assertFalse( value1.equals( value5 ) ); |
| assertTrue( value1.equals( "test" ) ); |
| assertFalse( value1.equals( null ) ); |
| |
| assertFalse( value1.equals( valueString ) ); |
| assertFalse( value1.equals( valueBytes ) ); |
| } |
| |
| |
| /** |
| * Test the constructor with bad AttributeType |
| */ |
| @Test |
| public void testBadConstructor() |
| { |
| // create a AT without any syntax |
| AttributeType attribute = new EntryUtils.AT( "1.1.3.1" ); |
| |
| Value value = Value.createValue( attribute ); |
| |
| assertTrue( value.isHumanReadable() ); |
| } |
| |
| |
| /** |
| * Tests to make sure the hashCode method is working properly. |
| * @throws Exception on errors |
| */ |
| @Test |
| public void testHashCode() throws LdapInvalidAttributeValueException |
| { |
| AttributeType at1 = EntryUtils.getCaseIgnoringAttributeNoNumbersType(); |
| Value v0 = new Value( at1, "Alex" ); |
| Value v1 = new Value( at1, "ALEX" ); |
| Value v2 = new Value( at1, "alex" ); |
| |
| assertEquals( v0.hashCode(), v1.hashCode() ); |
| assertEquals( v0.hashCode(), v2.hashCode() ); |
| assertEquals( v1.hashCode(), v2.hashCode() ); |
| |
| assertEquals( v0, v1 ); |
| assertEquals( v0, v2 ); |
| assertEquals( v1, v2 ); |
| |
| Value v3 = new Value( at1, "Timber" ); |
| |
| assertNotSame( v0.hashCode(), v3.hashCode() ); |
| |
| Value v4 = new Value( at, "Alex" ); |
| |
| assertNotSame( v0.hashCode(), v4.hashCode() ); |
| } |
| |
| |
| /** |
| * Test the compareTo method |
| */ |
| @Test |
| public void testCompareTo() throws LdapInvalidAttributeValueException |
| { |
| AttributeType at1 = EntryUtils.getCaseIgnoringAttributeNoNumbersType(); |
| Value v0 = new Value( at1, "Alex" ); |
| Value v1 = new Value( at1, "ALEX" ); |
| |
| assertEquals( 0, v0.compareTo( v1 ) ); |
| assertEquals( 0, v1.compareTo( v0 ) ); |
| |
| Value v2 = new Value( at1, (String)null ); |
| |
| assertEquals( 1, v0.compareTo( v2 ) ); |
| assertEquals( -1, v2.compareTo( v0 ) ); |
| } |
| |
| |
| /** |
| * Test the clone method |
| */ |
| @Test |
| public void testClone() throws LdapException |
| { |
| AttributeType at1 = EntryUtils.getCaseIgnoringAttributeNoNumbersType(); |
| Value sv = new Value( at1, "Test" ); |
| |
| Value sv1 = sv.clone(); |
| |
| assertEquals( sv, sv1 ); |
| |
| sv = new Value( "" ); |
| |
| assertNotSame( sv, sv1 ); |
| assertEquals( "", sv.getString() ); |
| |
| sv = new Value( " This is a TEST " ); |
| sv1 = sv.clone(); |
| |
| assertEquals( sv, sv1 ); |
| assertEquals( sv, sv1 ); |
| } |
| |
| |
| /** |
| * Presumes an attribute which constrains it's values to some constant |
| * strings: LOW, MEDIUM, HIGH. Normalization does nothing. MatchingRules |
| * are exact case matching. |
| * |
| * @throws Exception on errors |
| */ |
| @Test |
| public void testConstrainedString() throws LdapInvalidAttributeValueException |
| { |
| s.setSyntaxChecker( new SyntaxChecker( "1.1.1.1" ) |
| { |
| public static final long serialVersionUID = 1L; |
| |
| |
| public boolean isValidSyntax( Object value ) |
| { |
| if ( value instanceof String ) |
| { |
| String strval = ( String ) value; |
| return strval.equals( "HIGH" ) || strval.equals( "LOW" ) || strval.equals( "MEDIUM" ); |
| } |
| return false; |
| } |
| } ); |
| |
| mr.setSyntax( s ); |
| |
| mr.setNormalizer( new NoOpNormalizer( mr.getOid() ) ); |
| at.setEquality( mr ); |
| at.setSyntax( s ); |
| |
| // check that normalization and syntax checks work as expected |
| Value value = new Value( at, "HIGH" ); |
| assertEquals( value.getString(), value.getString() ); |
| |
| try |
| { |
| new Value( at, "high" ); |
| fail(); |
| } |
| catch ( LdapInvalidAttributeValueException liave ) |
| { |
| // expected |
| } |
| |
| // create a bunch to best tested for equals and in containers |
| Value v0 = new Value( at, "LOW" ); |
| Value v1 = new Value( at, "LOW" ); |
| Value v2 = new Value( at, "MEDIUM" ); |
| Value v3 = new Value( at, "HIGH" ); |
| |
| // check equals |
| assertTrue( v0.equals( v1 ) ); |
| assertTrue( v1.equals( v0 ) ); |
| assertEquals( 0, v0.compareTo( v1 ) ); |
| |
| assertFalse( v2.equals( v3 ) ); |
| assertFalse( v3.equals( v2 ) ); |
| assertTrue( v2.compareTo( v3 ) > 0 ); |
| assertTrue( v3.compareTo( v2 ) < 0 ); |
| |
| // add all except v1 and v5 to a set |
| HashSet<Value> set = new HashSet<Value>(); |
| set.add( v0 ); |
| set.add( v2 ); |
| set.add( v3 ); |
| |
| // check contains method |
| assertTrue( set.contains( v1 ), "since v1.equals( v0 ) and v0 was added then this should be true" ); |
| |
| // check ordering based on the comparator |
| List<Value> list = new ArrayList<Value>(); |
| list.add( v1 ); |
| list.add( v3 ); |
| list.add( v0 ); |
| list.add( v2 ); |
| |
| Collections.sort( list ); |
| |
| // High, low, low, medium |
| assertTrue( list.get( 0 ).equals( v3 ), "since v0 equals v1 either could be at index 0 & 1" ); |
| assertTrue( list.get( 1 ).equals( v0 ), "since v0 equals v1 either could be at index 0 & 1" ); |
| assertTrue( list.get( 2 ).equals( v1 ), "since v2 \"MEDIUM\" should be at index 2" ); |
| assertTrue( list.get( 3 ).equals( v2 ), "since v3 \"HIGH\" should be at index 3" ); |
| |
| assertEquals( 4, list.size() ); |
| } |
| |
| |
| /** |
| * Creates a string value with an attribute type that is of a syntax |
| * which accepts anything. Also there is no normalization since the |
| * value is the same as the normalized value. This makes the at technically |
| * a binary value however it can be dealt with as a string so this test |
| * is still OK. |
| * @throws Exception on errors |
| */ |
| @Test |
| public void testAcceptAllNoNormalization() throws LdapInvalidAttributeValueException |
| { |
| // check that normalization and syntax checks work as expected |
| Value value = new Value( at, "hello" ); |
| assertEquals( value.getString(), value.getString() ); |
| |
| // create a bunch to best tested for equals and in containers |
| Value v0 = new Value( at, "hello" ); |
| Value v1 = new Value( at, "hello" ); |
| Value v2 = new Value( at, "next0" ); |
| Value v3 = new Value( at, "next1" ); |
| Value v4 = Value.createValue( at ); |
| Value v5 = Value.createValue( at ); |
| |
| // check equals |
| assertTrue( v0.equals( v1 ) ); |
| assertTrue( v1.equals( v0 ) ); |
| assertTrue( v4.equals( v5 ) ); |
| assertTrue( v5.equals( v4 ) ); |
| assertFalse( v2.equals( v3 ) ); |
| assertFalse( v3.equals( v2 ) ); |
| |
| // add all except v1 and v5 to a set |
| HashSet<Value> set = new HashSet<Value>(); |
| set.add( v0 ); |
| set.add( v2 ); |
| set.add( v3 ); |
| set.add( v4 ); |
| |
| // check contains method |
| assertTrue( set.contains( v1 ), "since v1.equals( v0 ) and v0 was added then this should be true" ); |
| assertTrue( set.contains( v5 ), "since v4.equals( v5 ) and v4 was added then this should be true" ); |
| |
| // check ordering based on the comparator |
| ArrayList<Value> list = new ArrayList<Value>(); |
| list.add( v1 ); |
| list.add( v3 ); |
| list.add( v5 ); |
| list.add( v0 ); |
| list.add( v2 ); |
| list.add( v4 ); |
| |
| Comparator<Value> c = new Comparator<Value>() |
| { |
| public int compare( Value o1, Value o2 ) |
| { |
| String n1 = null; |
| String n2 = null; |
| |
| if ( o1 != null ) |
| { |
| n1 = o1.getString(); |
| } |
| |
| if ( o2 != null ) |
| { |
| n2 = o2.getString(); |
| } |
| |
| if ( n1 == null ) |
| { |
| return ( n2 == null ) ? 0 : -1; |
| } |
| else if ( n2 == null ) |
| { |
| return 1; |
| } |
| |
| return mr.getLdapComparator().compare( n1, n2 ); |
| } |
| }; |
| |
| Collections.sort( list, c ); |
| |
| assertTrue( list.get( 0 ).equals( v4 ), "since v4 equals v5 and has no value either could be at index 0 & 1" ); |
| assertTrue( list.get( 0 ).equals( v5 ), "since v4 equals v5 and has no value either could be at index 0 & 1" ); |
| assertTrue( list.get( 1 ).equals( v4 ), "since v4 equals v5 and has no value either could be at index 0 & 1" ); |
| assertTrue( list.get( 1 ).equals( v5 ), "since v4 equals v5 and has no value either could be at index 0 & 1" ); |
| |
| assertTrue( list.get( 2 ).equals( v0 ), "since v0 equals v1 either could be at index 2 & 3" ); |
| assertTrue( list.get( 2 ).equals( v1 ), "since v0 equals v1 either could be at index 2 & 3" ); |
| assertTrue( list.get( 3 ).equals( v0 ), "since v0 equals v1 either could be at index 2 & 3" ); |
| assertTrue( list.get( 3 ).equals( v1 ), "since v0 equals v1 either could be at index 2 & 3" ); |
| |
| assertTrue( list.get( 4 ).equals( v2 ), "since v2 \"next0\" should be at index 4" ); |
| assertTrue( list.get( 5 ).equals( v3 ), "since v3 \"next1\" should be at index 5" ); |
| |
| assertEquals( 6, list.size() ); |
| } |
| |
| |
| /** |
| * Test serialization of a Value which has a normalized value |
| */ |
| @Test |
| public void testNormalizedStringValueSerialization() throws LdapException, IOException, ClassNotFoundException |
| { |
| // First check with a value which will be normalized |
| Value ssv = new Value( at, " Test Test " ); |
| |
| assertEquals( 0, ssv.compareTo( " test test " ) ); |
| assertEquals( " Test Test ", ssv.getString() ); |
| |
| Value ssvSer = deserializeValue( serializeValue( ssv ) ); |
| |
| assertEquals( ssv, ssvSer ); |
| } |
| |
| |
| /** |
| * Test serialization of a Value which does not have a normalized value |
| */ |
| @Test |
| public void testNoNormalizedStringValueSerialization() throws LdapException, IOException, ClassNotFoundException |
| { |
| // First check with a value which will be normalized |
| Value ssv = new Value( at, "test" ); |
| |
| assertEquals( 0, ssv.compareTo( " test " ) ); |
| assertEquals( "test", ssv.getString() ); |
| |
| Value ssvSer = deserializeValue( serializeValue( ssv ) ); |
| |
| assertEquals( ssv, ssvSer ); |
| } |
| |
| |
| /** |
| * Test serialization of a null Value |
| */ |
| @Test |
| public void testNullStringValueSerialization() throws LdapException, IOException, ClassNotFoundException |
| { |
| // First check with a value which will be normalized |
| Value ssv = Value.createValue( at ); |
| |
| assertEquals( 0, ssv.compareTo( ( String ) null ) ); |
| assertNull( ssv.getString() ); |
| |
| Value ssvSer = deserializeValue( serializeValue( ssv ) ); |
| |
| assertEquals( ssv, ssvSer ); |
| } |
| |
| |
| /** |
| * Test serialization of an empty Value |
| */ |
| @Test |
| public void testEmptyStringValueSerialization() throws LdapException, IOException, ClassNotFoundException |
| { |
| // First check with a value which will be normalized |
| Value ssv = new Value( at, "" ); |
| |
| assertEquals( 0, ssv.compareTo( " " ) ); |
| assertEquals( "", ssv.getString() ); |
| |
| Value ssvSer = deserializeValue( serializeValue( ssv ) ); |
| |
| assertEquals( ssv, ssvSer ); |
| } |
| |
| |
| /** |
| * Test serialization of an empty Value |
| */ |
| @Test |
| public void testStringValueEmptyNormalizedSerialization() throws LdapException, IOException, ClassNotFoundException |
| { |
| // First check with a value which will be normalized |
| Value ssv = new Value( " " ); |
| |
| assertEquals( " ", ssv.getString() ); |
| |
| Value ssvSer = deserializeValue( serializeValue( ssv ) ); |
| |
| assertEquals( ssv, ssvSer ); |
| } |
| } |