You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ak...@apache.org on 2007/09/29 03:14:58 UTC

svn commit: r580527 - in /directory/shared/branches/bigbang/ldap: ./ src/main/antlr/ src/main/java/org/apache/directory/shared/ldap/codec/util/ src/main/java/org/apache/directory/shared/ldap/filter/ src/main/java/org/apache/directory/shared/ldap/ldif/ ...

Author: akarasulu
Date: Fri Sep 28 18:14:52 2007
New Revision: 580527

URL: http://svn.apache.org/viewvc?rev=580527&view=rev
Log:
merging changes from 580525 and 580524 on trunks

Added:
    directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/filter/FilterParser.java
      - copied unchanged from r580526, directory/shared/trunk/ldap/src/main/java/org/apache/directory/shared/ldap/filter/FilterParser.java
    directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/filter/FilterParserTest.java
      - copied unchanged from r580526, directory/shared/trunk/ldap/src/test/java/org/apache/directory/shared/ldap/filter/FilterParserTest.java
Removed:
    directory/shared/branches/bigbang/ldap/src/main/antlr/filter-lexer.g
    directory/shared/branches/bigbang/ldap/src/main/antlr/filter-parser.g
    directory/shared/branches/bigbang/ldap/src/main/antlr/filter-value-lexer.g
    directory/shared/branches/bigbang/ldap/src/main/antlr/filter-value-parser.g
    directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/filter/FilterParserImpl.java
    directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/filter/FilterParserImplTest.java
Modified:
    directory/shared/branches/bigbang/ldap/pom.xml
    directory/shared/branches/bigbang/ldap/src/main/antlr/ACIItem.g
    directory/shared/branches/bigbang/ldap/src/main/antlr/SubtreeSpecificationChecker.g
    directory/shared/branches/bigbang/ldap/src/main/antlr/subtree-specification.g
    directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/codec/util/LdapURL.java
    directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/filter/BranchNormalizedVisitor.java
    directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/filter/ExtensibleNode.java
    directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/filter/SubstringNode.java
    directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/ldif/LdifUtils.java
    directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/name/RdnParser.java
    directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/util/AttributeUtils.java
    directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/util/StringTools.java
    directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/aci/ACIItemParserTest.java
    directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/filter/BranchNormalizedVisitorTest.java
    directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/ldif/LdifUtilsTest.java
    directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/subtree/SubtreeSpecificationParserTest.java

Modified: directory/shared/branches/bigbang/ldap/pom.xml
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/pom.xml?rev=580527&r1=580526&r2=580527&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/pom.xml (original)
+++ directory/shared/branches/bigbang/ldap/pom.xml Fri Sep 28 18:14:52 2007
@@ -113,7 +113,7 @@
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-antlr-plugin</artifactId>
         <configuration>
-          <grammars>filter-value-lexer.g filter-lexer.g filter-value-parser.g filter-parser.g ACIItem.g ACIItemChecker.g schema-extension.g schema-qdstring.g schema-value.g schema.g subtree-specification.g SubtreeSpecificationChecker.g TriggerSpecification.g</grammars>
+          <grammars>ACIItem.g ACIItemChecker.g schema-extension.g schema-qdstring.g schema-value.g schema.g subtree-specification.g SubtreeSpecificationChecker.g TriggerSpecification.g</grammars>
         </configuration>
         <executions>
            <execution>

Modified: directory/shared/branches/bigbang/ldap/src/main/antlr/ACIItem.g
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/main/antlr/ACIItem.g?rev=580527&r1=580526&r2=580527&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/main/antlr/ACIItem.g (original)
+++ directory/shared/branches/bigbang/ldap/src/main/antlr/ACIItem.g Fri Sep 28 18:14:52 2007
@@ -39,9 +39,9 @@
 import org.apache.directory.shared.ldap.filter.OrNode;
 import org.apache.directory.shared.ldap.filter.NotNode;
 import org.apache.directory.shared.ldap.filter.ExprNode;
-import org.apache.directory.shared.ldap.filter.FilterParserImpl;
 import org.apache.directory.shared.ldap.filter.LeafNode;
 import org.apache.directory.shared.ldap.filter.EqualityNode;
+import org.apache.directory.shared.ldap.filter.FilterParser;
 import org.apache.directory.shared.ldap.message.AttributeImpl;
 import org.apache.directory.shared.ldap.name.NameComponentNormalizer;
 import org.apache.directory.shared.ldap.subtree.SubtreeSpecification;
@@ -104,9 +104,6 @@
 {
     private static final Logger log = LoggerFactory.getLogger( AntlrACIItemParser.class );
     
-    // subordinate parser instances
-    private final FilterParserImpl filterParser = new FilterParserImpl();
-    
     NameComponentNormalizer normalizer;
     
     // nonshared global data needed to avoid extensive pass/return stuff
@@ -543,7 +540,7 @@
     {
         protectedItemsMap.put( "rangeOfValues",
                 new ProtectedItem.RangeOfValues(
-                        filterParser.parse( token.getText() ) ) );
+                        FilterParser.parse( token.getText() ) ) );
         log.debug( "filterParser parsed " + token.getText() );
     }
     ;

Modified: directory/shared/branches/bigbang/ldap/src/main/antlr/SubtreeSpecificationChecker.g
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/main/antlr/SubtreeSpecificationChecker.g?rev=580527&r1=580526&r2=580527&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/main/antlr/SubtreeSpecificationChecker.g (original)
+++ directory/shared/branches/bigbang/ldap/src/main/antlr/SubtreeSpecificationChecker.g Fri Sep 28 18:14:52 2007
@@ -24,7 +24,7 @@
 package org.apache.directory.shared.ldap.subtree;
 
 import org.apache.directory.shared.ldap.name.LdapDN;
-import org.apache.directory.shared.ldap.filter.FilterParserImpl;
+import org.apache.directory.shared.ldap.filter.FilterParser;
 import org.apache.directory.shared.ldap.util.ComponentsMonitor;
 import org.apache.directory.shared.ldap.util.OptionalComponentsMonitor;
 
@@ -67,8 +67,6 @@
     
     private ComponentsMonitor subtreeSpecificationComponentsMonitor = null;
     
-    private final FilterParserImpl filterParser = new FilterParserImpl();
-
     /**
      * Does nothing.
      */
@@ -241,7 +239,7 @@
 	log.debug( "entered filter()" );
 }
 	:
-	( filterToken:FILTER { filterParser.parse( filterToken.getText() ); } )
+	( filterToken:FILTER { FilterParser.parse( filterToken.getText() ); } )
 	;
 	exception
     catch [Exception e]

Modified: directory/shared/branches/bigbang/ldap/src/main/antlr/subtree-specification.g
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/main/antlr/subtree-specification.g?rev=580527&r1=580526&r2=580527&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/main/antlr/subtree-specification.g (original)
+++ directory/shared/branches/bigbang/ldap/src/main/antlr/subtree-specification.g Fri Sep 28 18:14:52 2007
@@ -37,7 +37,7 @@
 import org.apache.directory.shared.ldap.filter.OrNode;
 import org.apache.directory.shared.ldap.filter.NotNode;
 import org.apache.directory.shared.ldap.filter.EqualityNode;
-import org.apache.directory.shared.ldap.filter.FilterParserImpl;
+import org.apache.directory.shared.ldap.filter.FilterParser;
 import org.apache.directory.shared.ldap.subtree.SubtreeSpecification;
 import org.apache.directory.shared.ldap.subtree.SubtreeSpecificationModifier;
 import org.apache.directory.shared.ldap.schema.NormalizerMappingResolver;
@@ -83,8 +83,6 @@
 {
     private static final Logger log = LoggerFactory.getLogger( AntlrSubtreeSpecificationParser.class );
     
-    private FilterParserImpl filterParser;
-    
     private NormalizerMappingResolver resolver;
     
     private Set<LdapDN> chopBeforeExclusions = null;
@@ -166,7 +164,6 @@
     chopBeforeExclusions = new HashSet<LdapDN>();
     chopAfterExclusions = new HashSet<LdapDN>();
     // always create a new filter parser in case we may have some statefulness problems with it
-    filterParser = new FilterParserImpl();
 }
     :
     OPEN_CURLY ( SP )*
@@ -324,7 +321,7 @@
 	log.debug( "entered filter()" );
 }
 	:
-	( filterToken:FILTER { filterExpr=filterParser.parse( filterToken.getText() ); } )
+	( filterToken:FILTER { filterExpr=FilterParser.parse( filterToken.getText() ); } )
 	;
 	exception
     catch [Exception e]

Modified: directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/codec/util/LdapURL.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/codec/util/LdapURL.java?rev=580527&r1=580526&r2=580527&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/codec/util/LdapURL.java (original)
+++ directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/codec/util/LdapURL.java Fri Sep 28 18:14:52 2007
@@ -21,12 +21,11 @@
 
 
 import org.apache.directory.shared.asn1.codec.DecoderException;
-import org.apache.directory.shared.ldap.filter.FilterParserImpl;
+import org.apache.directory.shared.ldap.filter.FilterParser;
 import org.apache.directory.shared.ldap.name.LdapDN;
 import org.apache.directory.shared.ldap.util.StringTools;
 
 import java.io.ByteArrayOutputStream;
-import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 
 import java.text.ParseException;
@@ -72,9 +71,6 @@
     /** A null LdapURL */
     public static final LdapURL EMPTY_URL = new LdapURL();
 
-    /** The filter parser */
-    private static FilterParserImpl filterParser = new FilterParserImpl();
-
     // ~ Instance fields
     // ----------------------------------------------------------------------------
 
@@ -898,16 +894,18 @@
             end++;
         }
 
+        if ( end == pos )
+        {
+            // We have no filter
+            return end;
+        }
+        
         try
         {
             filter = decode( new String( chars, pos, end - pos ) );
-            filterParser.parse( filter );
+            FilterParser.parse( filter );
         }
         catch ( URIException ue )
-        {
-            return -1;
-        }
-        catch ( IOException ioe )
         {
             return -1;
         }

Modified: directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/filter/BranchNormalizedVisitor.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/filter/BranchNormalizedVisitor.java?rev=580527&r1=580526&r2=580527&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/filter/BranchNormalizedVisitor.java (original)
+++ directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/filter/BranchNormalizedVisitor.java Fri Sep 28 18:14:52 2007
@@ -126,9 +126,7 @@
      */
     public static String getNormalizedFilter( String filter ) throws IOException, ParseException
     {
-        FilterParserImpl parser = new FilterParserImpl();
-
-        ExprNode originalNode = parser.parse( filter );
+        ExprNode originalNode = FilterParser.parse( filter );
 
         return getNormalizedFilter( originalNode );
     }

Modified: directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/filter/ExtensibleNode.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/filter/ExtensibleNode.java?rev=580527&r1=580526&r2=580527&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/filter/ExtensibleNode.java (original)
+++ directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/filter/ExtensibleNode.java Fri Sep 28 18:14:52 2007
@@ -32,16 +32,26 @@
 public class ExtensibleNode extends LeafNode
 {
     /** The value of the attribute to match for */
-    private final byte[] value;
+    private byte[] value;
 
     /** The matching rules id */
-    private final String matchingRuleId;
+    private String matchingRuleId;
 
     /** The name of the dn attributes */
     private boolean dnAttributes = false;
 
 
     /**
+     * Creates a new emptyExtensibleNode object.
+     */
+    public ExtensibleNode( String attribute )
+    {
+        super( attribute );
+        
+        dnAttributes = false;
+    }
+
+    /**
      * Creates a new ExtensibleNode object.
      * 
      * @param attribute the attribute used for the extensible assertion
@@ -63,7 +73,7 @@
      * @param matchingRuleId the OID of the matching rule
      * @param dnAttributes the dn attributes
      */
-    public ExtensibleNode(String attribute, byte[] value, String matchingRuleId, boolean dnAttributes)
+    public ExtensibleNode( String attribute, byte[] value, String matchingRuleId, boolean dnAttributes )
     {
         super( attribute );
 
@@ -78,10 +88,21 @@
      * 
      * @return the dn attributes
      */
-    public boolean dnAttributes()
+    public boolean hasDnAttributes()
     {
         return dnAttributes;
     }
+    
+    
+    /**
+     * Set the dnAttributes flag
+     *
+     * @param dnAttributes The flag to set
+     */
+    public void setDnAttributes( boolean dnAttributes )
+    {
+        this.dnAttributes = dnAttributes;
+    }
 
 
     /**
@@ -96,6 +117,15 @@
 
 
     /**
+     * Sets the matching rule id as an OID string.
+     */
+    public void setMatchingRuleId( String matchingRuleId )
+    {
+        this.matchingRuleId = matchingRuleId;
+    }
+
+
+    /**
      * Gets the value.
      * 
      * @return the value
@@ -106,6 +136,15 @@
     }
 
 
+    /**
+     * Sets the value.
+     */
+    public final void setValue( String value)
+    {
+        this.value = StringTools.getBytesUtf8( value );
+    }
+
+    
     /**
      * @see ExprNode#printRefinementToBuffer(StringBuilder)
      */

Modified: directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/filter/SubstringNode.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/filter/SubstringNode.java?rev=580527&r1=580526&r2=580527&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/filter/SubstringNode.java (original)
+++ directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/filter/SubstringNode.java Fri Sep 28 18:14:52 2007
@@ -164,6 +164,16 @@
 
 
     /**
+     * Add an any pattern
+     * @param anyPattern The any pattern
+     */
+    public void addAny( String anyPattern ) 
+    {
+        this.anyPattern.add( anyPattern );
+    }
+
+
+    /**
      * Gets the compiled regular expression for the substring expression.
      * 
      * @return the equivalent compiled regular expression

Modified: directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/ldif/LdifUtils.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/ldif/LdifUtils.java?rev=580527&r1=580526&r2=580527&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/ldif/LdifUtils.java (original)
+++ directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/ldif/LdifUtils.java Fri Sep 28 18:14:52 2007
@@ -45,10 +45,12 @@
     static
     {
     	// Initialization of the array that will be used to match the first char.
-    	for (int i = 0; i < 128; i++) {
+    	for (int i = 0; i < 128; i++) 
+        {
     		LDIF_SAFE_STARTING_CHAR_ALPHABET[i] = true;
 		}
-    	LDIF_SAFE_STARTING_CHAR_ALPHABET[0] = false; // 0 (NUL)
+    	
+        LDIF_SAFE_STARTING_CHAR_ALPHABET[0] = false; // 0 (NUL)
     	LDIF_SAFE_STARTING_CHAR_ALPHABET[10] = false; // 10 (LF)
     	LDIF_SAFE_STARTING_CHAR_ALPHABET[13] = false; // 13 (CR)
     	LDIF_SAFE_STARTING_CHAR_ALPHABET[32] = false; // 32 (SPACE)
@@ -56,10 +58,12 @@
     	LDIF_SAFE_STARTING_CHAR_ALPHABET[60] = false; // 60 (>)
     	
     	// Initialization of the array that will be used to match the other chars.
-    	for (int i = 0; i < 128; i++) {
+    	for (int i = 0; i < 128; i++) 
+        {
     		LDIF_SAFE_OTHER_CHARS_ALPHABET[i] = true;
 		}
-    	LDIF_SAFE_OTHER_CHARS_ALPHABET[0] = false; // 0 (NUL)
+    	
+        LDIF_SAFE_OTHER_CHARS_ALPHABET[0] = false; // 0 (NUL)
     	LDIF_SAFE_OTHER_CHARS_ALPHABET[10] = false; // 10 (LF)
     	LDIF_SAFE_OTHER_CHARS_ALPHABET[13] = false; // 13 (CR)
     }
@@ -94,7 +98,8 @@
     {
     	// Checking the first char
     	char currentChar = str.charAt(0);
-    	if ( currentChar > 127 || !LDIF_SAFE_STARTING_CHAR_ALPHABET[currentChar] )
+        
+    	if ( ( currentChar > 127 ) || !LDIF_SAFE_STARTING_CHAR_ALPHABET[currentChar] )
     	{
     		return false;
     	}
@@ -104,7 +109,7 @@
     	{
         	currentChar = str.charAt(i);
         	
-        	if ( currentChar > 127 || !LDIF_SAFE_OTHER_CHARS_ALPHABET[currentChar] )
+        	if ( ( currentChar > 127 ) || !LDIF_SAFE_OTHER_CHARS_ALPHABET[currentChar] )
         	{
         		return false;
         	}
@@ -117,20 +122,36 @@
     /**
      * Convert an Attributes as LDIF
      * @param attrs the Attributes to convert
+     * @param length the expectend line length
      * @return the corresponding LDIF code as a String
      * @throws NamingException If a naming exception is encountered.
      */
     public static String convertToLdif( Attributes attrs ) throws NamingException
     {
-		StringBuffer sb = new StringBuffer();
+        return convertToLdif( attrs, 80 );
+    }
+    
+    
+    /**
+     * Convert an Attributes as LDIF
+     * @param attrs the Attributes to convert
+     * @param length the expectend line length
+     * @return the corresponding LDIF code as a String
+     * @throws NamingException If a naming exception is encountered.
+     */
+    public static String convertToLdif( Attributes attrs, int length ) throws NamingException
+    {
+		StringBuilder sb = new StringBuilder();
 		
 		NamingEnumeration ne = attrs.getAll();
 		
 		while ( ne.hasMore() )
 		{
 			Object attribute = ne.next();
-			if (attribute instanceof Attribute) {
-				sb.append( convertToLdif( (Attribute) attribute ) );
+            
+			if ( attribute instanceof Attribute ) 
+            {
+				sb.append( convertToLdif( (Attribute) attribute, length ) );
 			}			
 		}
 		
@@ -138,23 +159,96 @@
 	}
     
     /**
+     * Convert an Entry to LDIF
+     * @param entry the entry to convert
+     * @return the corresponding LDIF as a String
+     * @throws NamingException If a naming exception is encountered.
+     */
+    public static String convertToLdif( Entry entry ) throws NamingException
+    {
+        return convertToLdif( entry, 80 );
+    }
+    
+    /**
+     * Convert an Entry to LDIF
+     * @param entry the entry to convert
+     * @return the corresponding LDIF as a String
+     * @throws NamingException If a naming exception is encountered.
+     */
+    public static String convertToLdif( Entry entry, int length ) throws NamingException
+    {
+        StringBuilder sb = new StringBuilder();
+        
+        // First, dump the DN
+        if ( isLDIFSafe( entry.getDn() ) )
+        {
+            sb.append( stripLineToNChars( "dn: " + entry.getDn(), length ) );
+        }
+        else
+        {
+            sb.append( stripLineToNChars( "dn:: " + encodeBase64( entry.getDn() ), length ) );
+        }
+        
+        sb.append( '\n' );
+        
+        // Dump the ChangeType
+        sb.append( stripLineToNChars( "changeType: " + entry.getChangeType(), length ) );
+        
+        sb.append( '\n' );
+
+        // Now, iterate through all the attributes
+        NamingEnumeration ne = entry.getAttributes().getAll();;
+        
+        while ( ne.hasMore() )
+        {
+            Attribute attribute = (Attribute)ne.next();
+            
+            sb.append( convertToLdif( (Attribute) attribute, length ) );
+        }
+        
+        sb.append( '\n' );
+        
+        return sb.toString();
+    }
+    
+    /**
+     * Base64 encode a String  
+     */
+    private static String encodeBase64( String str )
+    {
+        char[] encoded;
+        
+        try
+        {
+            // force encoding using UTF-8 charset, as required in RFC2849 note 7
+            encoded = Base64.encode( ( ( String ) str ).getBytes( "UTF-8" ) );
+        }
+        catch ( UnsupportedEncodingException e )
+        {
+            encoded = Base64.encode( ( ( String ) str ).getBytes() );
+        }
+        
+        return new String( encoded );
+    }
+
+    /**
      * Converts an Attribute as LDIF
      * @param attr the Attribute to convert
      * @return the corresponding LDIF code as a String
      * @throws NamingException If a naming exception is encountered.
      */
-	private static String convertToLdif(Attribute attr) throws NamingException
+	private static String convertToLdif( Attribute attr, int length ) throws NamingException
 	{
-		StringBuffer sb = new StringBuffer();
+		StringBuilder sb = new StringBuilder();
 		
 		// iterating on the attribute's values
 		for ( int i = 0; i < attr.size(); i++ )
         {
-			StringBuffer lineBuffer = new StringBuffer();
+			StringBuilder lineBuffer = new StringBuilder();
 			
-			lineBuffer.append( attr.getID() );
+            lineBuffer.append( attr.getID() );
 			
-			Object value = attr.get(i);
+			Object value = attr.get( i );
             
             // Checking if the value is binary
             if ( value instanceof byte[] )
@@ -168,20 +262,10 @@
             {
             	// It's a String but, we have to check if encoding isn't required
             	String str = (String) value;
+                
             	if ( !LdifUtils.isLDIFSafe( str ) )
             	{
-            		char[] encoded;
-                    try
-                    {
-                        // force encoding using UTF-8 charset, as required in RFC2849 note 7
-                        encoded = Base64.encode( ( ( String ) value ).getBytes( "UTF-8" ) );
-                    }
-                    catch ( UnsupportedEncodingException e )
-                    {
-                        encoded = Base64.encode( ( ( String ) value ).getBytes() );
-                    }
-
-                    lineBuffer.append( ":: " + new String( encoded ) );
+                    lineBuffer.append( ":: " + encodeBase64( (String)value ) );
             	}
             	else
             	{
@@ -190,7 +274,7 @@
             }
             
             lineBuffer.append( "\n" );
-            sb.append( stripLineToNChars(lineBuffer.toString(), 80));
+            sb.append( stripLineToNChars( lineBuffer.toString(), length ) );
         }
 		
 		return sb.toString();

Modified: directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/name/RdnParser.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/name/RdnParser.java?rev=580527&r1=580526&r2=580527&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/name/RdnParser.java (original)
+++ directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/name/RdnParser.java Fri Sep 28 18:14:52 2007
@@ -922,6 +922,7 @@
 
         pos.end = pos.start;
         pos.length = 0;
+        
         if ( ( type = parseAttributeType( dn, pos ) ) == null )
         {
             return DNUtils.PARSING_ERROR;

Modified: directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/util/AttributeUtils.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/util/AttributeUtils.java?rev=580527&r1=580526&r2=580527&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/util/AttributeUtils.java (original)
+++ directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/util/AttributeUtils.java Fri Sep 28 18:14:52 2007
@@ -20,12 +20,15 @@
 package org.apache.directory.shared.ldap.util;
 
 
+import java.text.ParseException;
 import java.util.Arrays;
 
 import org.apache.directory.shared.ldap.message.AttributeImpl;
 import org.apache.directory.shared.ldap.message.AttributesImpl;
 import org.apache.directory.shared.ldap.message.ModificationItemImpl;
 import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.schema.MatchingRule;
+import org.apache.directory.shared.ldap.schema.NoOpNormalizer;
 import org.apache.directory.shared.ldap.schema.Normalizer;
 
 import javax.naming.directory.Attribute;
@@ -333,7 +336,18 @@
             return true;
         }
         
-        Normalizer normalizer = type.getEquality().getNormalizer();
+        MatchingRule matchingRule = type.getEquality();
+        
+        Normalizer normalizer = null;
+        
+        if ( matchingRule != null )
+        {
+            normalizer = type.getEquality().getNormalizer();
+        }
+        else
+        {
+            normalizer = new NoOpNormalizer();
+        }
 
         if ( type.getSyntax().isHumanReadable() )
         {
@@ -352,15 +366,77 @@
         }
         else
         {
-            byte[] comparedBytes = ( byte[] ) compared;
+            byte[] comparedBytes = null;
+            
+            if ( compared instanceof String )
+            {
+                if ( ((String)compared).length() < 3 )
+                {
+                    return false;
+                }
+                
+                // Tansform the String to a byte array
+                int state = 1;
+                comparedBytes = new byte[((String)compared).length()/3];
+                int pos = 0;
+                
+                for ( char c:((String)compared).toCharArray() )
+                {
+                    switch ( state )
+                    {
+                        case 1 :
+                            if ( c != '\\' )
+                            {
+                                return false;
+                            }
+
+                            state++;
+                            break;
+                            
+                        case 2 :
+                            int high = StringTools.getHexValue( c );
+                            
+                            if ( high == -1 )
+                            {
+                                return false;
+                            }
+                            
+                            comparedBytes[pos] = (byte)(high << 4);
+                            
+                            state++;
+                            break;
+                            
+                        case 3 :
+                            int low = StringTools.getHexValue( c );
+                            
+                            if ( low == -1 )
+                            {
+                                return false;
+                            }
+                            
+                            comparedBytes[pos] += (byte)low;
+                            pos++;
+                            
+                            state = 1;
+                    }
+                }
+            }
+            else
+            {
+                comparedBytes = ( byte[] ) compared;
+            }
             
             for ( NamingEnumeration values = attr.getAll(); values.hasMoreElements(); /**/ )
             //for ( int ii = attr.size() - 1; ii >= 0; ii-- )
             {
-                String value = (String)values.nextElement();
-                if ( ArrayUtils.isEquals( comparedBytes, value ) )
+                Object value = values.nextElement();
+                
+                if ( value instanceof byte[] )
                 {
-                    return true;
+                    if ( ArrayUtils.isEquals( comparedBytes, value ) )
+                    {
+                        return true;
+                    }
                 }
             }
         }
@@ -759,6 +835,197 @@
         }
         
         return sb.toString();
+    }
+    
+    /**
+     * Parse attribute's options :
+     * 
+     * options = *( ';' option )
+     * option = 1*keychar
+     * keychar = 'a'-z' | 'A'-'Z' / '0'-'9' / '-'
+     */
+    private static void parseOptions( String str, Position pos ) throws ParseException
+    {
+        while ( StringTools.isCharASCII( str, pos.start, ';' ) )
+        {
+            pos.start++;
+            
+            // We have an option
+            if ( !StringTools.isAlphaDigitMinus( str, pos.start ) )
+            {
+                // We must have at least one keychar
+                throw new ParseException( "An empty option is not allowed", pos.start );
+            }
+            
+            pos.start++;
+            
+            while ( StringTools.isAlphaDigitMinus( str, pos.start ) )
+            {
+                pos.start++;
+            }
+        }
+    }
+    
+    /**
+     * Parse a number :
+     * 
+     * number = '0' | '1'..'9' digits
+     * digits = '0'..'9'*
+     * 
+     * @return true if a number has been found
+     */
+    private static boolean parseNumber( String filter, Position pos )
+    {
+        char c = StringTools.charAt( filter, pos.start );
+        
+        switch ( c )
+        {
+            case '0' :
+                // If we get a starting '0', we should get out
+                pos.start++;
+                return true;
+                
+            case '1' : 
+            case '2' : 
+            case '3' : 
+            case '4' : 
+            case '5' : 
+            case '6' : 
+            case '7' : 
+            case '8' : 
+            case '9' : 
+                pos.start++;
+                break;
+                
+            default :
+                // Not a number.
+                return false;
+        }
+        
+        while ( StringTools.isDigit( filter, pos.start ) )
+        {
+            pos.start++;
+        }
+        
+        return true;
+    }
+
+    
+    /**
+     * 
+     * Parse an OID.
+     *
+     * numericoid = number 1*( '.' number )
+     * number = '0'-'9' / ( '1'-'9' 1*'0'-'9' )
+     *
+     * @param str The OID to parse
+     * @param pos The current position in the string
+     * @return A valid OID
+     * @throws ParseException If we don't have a valid OID
+     */
+    public static void parseOID( String str, Position pos ) throws ParseException
+    {
+        // We have an OID
+        parseNumber( str, pos );
+        
+        // We must have at least one '.' number
+        if ( StringTools.isCharASCII( str, pos.start, '.' ) == false )
+        {
+            throw new ParseException( "Invalid OID, missing '.'", pos.start );
+        }
+        
+        pos.start++;
+        
+        if ( parseNumber( str, pos ) == false )
+        {
+            throw new ParseException( "Invalid OID, missing a number after a '.'", pos.start );
+        }
+        
+        while ( true )
+        {
+            // Break if we get something which is not a '.'
+            if ( StringTools.isCharASCII( str, pos.start, '.' ) == false )
+            {
+                break;
+            }
+            
+            pos.start++;
+            
+            if ( parseNumber( str, pos ) == false )
+            {
+                throw new ParseException( "Invalid OID, missing a number after a '.'", pos.start );
+            }
+        }
+    }
+    
+    /**
+     * Parse an attribute. The grammar is :
+     * attributedescription = attributetype options
+     * attributetype = oid
+     * oid = descr / numericoid
+     * descr = keystring
+     * numericoid = number 1*( '.' number )
+     * options = *( ';' option )
+     * option = 1*keychar
+     * keystring = leadkeychar *keychar
+     * leadkeychar = 'a'-z' | 'A'-'Z'
+     * keychar = 'a'-z' | 'A'-'Z' / '0'-'9' / '-'
+     * number = '0'-'9' / ( '1'-'9' 1*'0'-'9' )
+     *
+     * @param attr The parsed attribute,
+     * @param pos The position of the attribute in the current string
+     * @return The parsed attribute if valid
+     */
+    public static String parseAttribute( String str, Position pos, boolean withOption ) throws ParseException
+    {
+        // We must have an OID or an DESCR first
+        char c = StringTools.charAt( str, pos.start );
+        
+        if ( c == '\0' )
+        {
+            throw new ParseException( "Empty attributes", pos.start );
+        }
+        
+        int start = pos.start;
+
+        if ( StringTools.isAlpha( c ) )
+        {
+            // A DESCR
+            pos.start++;
+            
+            while ( StringTools.isAlphaDigitMinus( str, pos.start ) )
+            {
+                pos.start++;
+            }
+
+            // Parse the options if needed
+            if ( withOption )
+            {
+                parseOptions( str, pos );
+            }
+            
+            return str.substring( start, pos.start );
+        }
+        else if ( StringTools.isDigit( c ) )
+        {
+            // An OID
+            pos.start++;
+            
+            // Parse the OID
+            parseOID( str, pos );
+            
+            // Parse the options
+            if ( withOption )
+            {
+                parseOptions( str, pos );
+            }
+            
+            return str.substring( start, pos.start );
+        }
+        else
+        {
+            throw new ParseException( "Bad char in attribute", pos.start );
+        }
     }
 
 

Modified: directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/util/StringTools.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/util/StringTools.java?rev=580527&r1=580526&r2=580527&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/util/StringTools.java (original)
+++ directory/shared/branches/bigbang/ldap/src/main/java/org/apache/directory/shared/ldap/util/StringTools.java Fri Sep 28 18:14:52 2007
@@ -186,6 +186,27 @@
             true,  true,  true,  false, false, false, false, false 
         };
 
+    /** %01-%27 %2B-%5B %5D-%7F */
+    private static final boolean[] UNICODE_SUBSET =
+        { 
+            false, true,  true,  true,  true,  true,  true,  true, // '\0'
+            true,  true,  true,  true,  true,  true,  true,  true,
+            true,  true,  true,  true,  true,  true,  true,  true,
+            true,  true,  true,  true,  true,  true,  true,  true,
+            true,  true,  true,  true,  true,  true,  true,  true,
+            false, false, false, false, true,  true,  true,  true, // '(', ')', '*'
+            true,  true,  true,  true,  true,  true,  true,  true, 
+            true,  true,  true,  true,  true,  true,  true,  true,
+            true,  true,  true,  true,  true,  true,  true,  true,
+            true,  true,  true,  true,  true,  true,  true,  true,
+            true,  true,  true,  true,  true,  true,  true,  true,  
+            true,  true,  true,  true,  false, true,  true,  true, // '\'
+            true,  true,  true,  true,  true,  true,  true,  true,
+            true,  true,  true,  true,  true,  true,  true,  true, 
+            true,  true,  true,  true,  true,  true,  true,  true, 
+            true,  true,  true,  true,  true,  true,  true,  true,
+        };
+
     /** '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' */
     private static final boolean[] DIGIT =
         { 
@@ -1909,7 +1930,7 @@
      */
     public static final boolean isAlpha( byte c )
     {
-        return ( ( c > 127 ) || ( ALPHA[c] == false ) );
+        return ( ( c > 0 ) && ( c <= 127 ) && ALPHA[c] );
     }
 
     /**
@@ -1923,7 +1944,7 @@
      */
     public static final boolean isAlpha( char c )
     {
-        return ( ( c > 127 ) || ( ALPHA[c] == false ) );
+        return ( ( c > 0 ) && ( c <= 127 ) && ALPHA[c] );
     }
 
 
@@ -3408,4 +3429,36 @@
         return true;
     }
 
+    /**
+     * Check if the current char is in the unicodeSubset : all chars but
+     * '\0', '(', ')', '*' and '\'
+     *
+     * @param str The string to check
+     * @param pos Position of the current char
+     * @return True if the current char is in the unicode subset
+     */
+    public static boolean isUnicodeSubset( String str, int pos )
+    {
+        if ( ( str == null ) || ( str.length() <= pos ) || ( pos < 0 ) ) 
+        {
+            return false;
+        }
+        
+        char c = str.charAt( pos );
+        
+        return ( ( c > 127 ) || UNICODE_SUBSET[c] );
+        
+    }
+
+    /**
+     * Check if the current char is in the unicodeSubset : all chars but
+     * '\0', '(', ')', '*' and '\'
+     *
+     * @param c The char to check
+     * @return True if the current char is in the unicode subset
+     */
+    public static boolean isUnicodeSubset( char c )
+    {
+        return ( ( c > 127 ) || UNICODE_SUBSET[c] );
+    }
 }

Modified: directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/aci/ACIItemParserTest.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/aci/ACIItemParserTest.java?rev=580527&r1=580526&r2=580527&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/aci/ACIItemParserTest.java (original)
+++ directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/aci/ACIItemParserTest.java Fri Sep 28 18:14:52 2007
@@ -78,8 +78,8 @@
         
         spec = " { identificationTag \"id8\", precedence 0, authenticationLevel simple "
             + ", itemOrUserFirst userFirst: { userClasses { allUsers }, userPermissions { "
-            + " { protectedItems { rangeOfValues (& (cn=test) (sn=test) ) }, grantsAndDenials { grantAdd } }, "
-            + "{ protectedItems { rangeOfValues (| (! (cn=aaa) ) (sn=bbb) ) }, grantsAndDenials { grantAdd } } "
+            + " { protectedItems { rangeOfValues (&(cn=test)(sn=test)) }, grantsAndDenials { grantAdd } }, "
+            + "{ protectedItems { rangeOfValues (|(!(cn=aaa))(sn=bbb)) }, grantsAndDenials { grantAdd } } "
             + " } } }";
         
         parser.parse( spec );

Modified: directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/filter/BranchNormalizedVisitorTest.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/filter/BranchNormalizedVisitorTest.java?rev=580527&r1=580526&r2=580527&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/filter/BranchNormalizedVisitorTest.java (original)
+++ directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/filter/BranchNormalizedVisitorTest.java Fri Sep 28 18:14:52 2007
@@ -22,7 +22,6 @@
 
 import org.apache.directory.shared.ldap.filter.BranchNormalizedVisitor;
 import org.apache.directory.shared.ldap.filter.ExprNode;
-import org.apache.directory.shared.ldap.filter.FilterParserImpl;
 
 import junit.framework.TestCase;
 
@@ -37,13 +36,11 @@
 {
     public void testBranchNormalizedVisitor0() throws Exception
     {
-        FilterParserImpl parser = new FilterParserImpl();
+        String filter = "(ou=Human Resources)";
 
-        String filter = "( ou = Human Resources )";
+        ExprNode ori = FilterParser.parse( filter );
 
-        ExprNode ori = parser.parse( filter );
-
-        ExprNode altered = parser.parse( filter );
+        ExprNode altered = FilterParser.parse( filter );
 
         BranchNormalizedVisitor visitor = new BranchNormalizedVisitor();
 
@@ -55,13 +52,11 @@
 
     public void testBranchNormalizedVisitor1() throws Exception
     {
-        FilterParserImpl parser = new FilterParserImpl();
-
-        String filter = "( & ( ou = Human Resources ) ( uid = akarasulu ) )";
+        String filter = "(&(ou=Human Resources)(uid=akarasulu))";
 
-        ExprNode ori = parser.parse( filter );
+        ExprNode ori = FilterParser.parse( filter );
 
-        ExprNode altered = parser.parse( filter );
+        ExprNode altered = FilterParser.parse( filter );
 
         BranchNormalizedVisitor visitor = new BranchNormalizedVisitor();
 
@@ -73,15 +68,13 @@
 
     public void testBranchNormalizedVisitor2() throws Exception
     {
-        FilterParserImpl parser = new FilterParserImpl();
+        String filter = "(&(uid=akarasulu)(ou=Human Resources)";
 
-        String filter = "( & ( uid = akarasulu ) ( ou = Human Resources ) ";
+        filter += "(|(uid=akarasulu)(ou=Human Resources))) ";
 
-        filter += "(| ( uid = akarasulu ) ( ou = Human Resources ) ) ) ";
+        ExprNode ori = FilterParser.parse( filter );
 
-        ExprNode ori = parser.parse( filter );
-
-        ExprNode altered = parser.parse( filter );
+        ExprNode altered = FilterParser.parse( filter );
 
         BranchNormalizedVisitor visitor = new BranchNormalizedVisitor();
 
@@ -93,15 +86,13 @@
 
     public void testBranchNormalizedVisitor3() throws Exception
     {
-        FilterParserImpl parser = new FilterParserImpl();
-
-        String filter = "( & ( ou = Human Resources ) ( uid = akarasulu ) ";
+        String filter = "(&(ou=Human Resources)(uid=akarasulu)";
 
-        filter += "(| ( ou = Human Resources ) ( uid = akarasulu ) ) ) ";
+        filter += "(|(ou=Human Resources)(uid=akarasulu)))";
 
-        ExprNode ori = parser.parse( filter );
+        ExprNode ori = FilterParser.parse( filter );
 
-        ExprNode altered = parser.parse( filter );
+        ExprNode altered = FilterParser.parse( filter );
 
         BranchNormalizedVisitor visitor = new BranchNormalizedVisitor();
 
@@ -113,9 +104,9 @@
 
     public void testBranchNormalizedComplex() throws Exception
     {
-        String filter1 = "( & ( a = A ) ( | ( b = B ) ( c = C ) ) )";
+        String filter1 = "(&(a=A)(|(b=B)(c=C)))";
 
-        String filter2 = "( & ( a = A ) ( | ( c = C ) ( b = B ) ) )";
+        String filter2 = "(&(a=A)(|(c=C)(b=B)))";
 
         String normalizedFilter1 = BranchNormalizedVisitor.getNormalizedFilter( filter1 );
 

Modified: directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/ldif/LdifUtilsTest.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/ldif/LdifUtilsTest.java?rev=580527&r1=580526&r2=580527&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/ldif/LdifUtilsTest.java (original)
+++ directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/ldif/LdifUtilsTest.java Fri Sep 28 18:14:52 2007
@@ -20,7 +20,9 @@
 package org.apache.directory.shared.ldap.ldif;
 
 import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
 import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
 import javax.naming.directory.BasicAttributes;
 
 import junit.framework.TestCase;
@@ -241,5 +243,37 @@
         Attributes attributes = new BasicAttributes( "cn", "Saarbr\u00FCcken" );
         String ldif = LdifUtils.convertToLdif( attributes );
         assertEquals( "cn:: U2FhcmJyw7xja2Vu\n", ldif );
+    }
+    
+    public void testConvertEntryToLdif() throws NamingException
+    {
+        String expected = 
+            "dn:: Y249U2Fhcm\n" +
+            " Jyw7xja2VuLCBk\n" +
+            " Yz1leGFtcGxlLC\n" +
+            " BkYz1jb20=\n" +
+            "changeType: Add\n" +
+            "sn: test\n" +
+            "cn:: U2FhcmJyw7\n xja2Vu\n" +
+            "objectClass: to\n p\n" +
+            "objectClass: pe\n rson\n" +
+            "objectClass: in\n etorgPerson\n\n";
+        
+        Entry entry = new Entry();
+        entry.setDn( "cn=Saarbr\u00FCcken, dc=example, dc=com" );
+        entry.setChangeType( ChangeType.Add );
+        
+        Attribute oc = new BasicAttribute( "objectClass" );
+        oc.add( "top" );
+        oc.add( "person" );
+        oc.add( "inetorgPerson" );
+        
+        entry.addAttribute( oc );
+        
+        entry.addAttribute( "cn", "Saarbr\u00FCcken" );
+        entry.addAttribute( "sn", "test" );
+
+        String ldif = LdifUtils.convertToLdif( entry, 15 );
+        assertEquals( expected, ldif );
     }
 }

Modified: directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/subtree/SubtreeSpecificationParserTest.java
URL: http://svn.apache.org/viewvc/directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/subtree/SubtreeSpecificationParserTest.java?rev=580527&r1=580526&r2=580527&view=diff
==============================================================================
--- directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/subtree/SubtreeSpecificationParserTest.java (original)
+++ directory/shared/branches/bigbang/ldap/src/test/java/org/apache/directory/shared/ldap/subtree/SubtreeSpecificationParserTest.java Fri Sep 28 18:14:52 2007
@@ -31,7 +31,7 @@
 import org.apache.directory.shared.ldap.filter.BranchNode;
 import org.apache.directory.shared.ldap.filter.EqualityNode;
 import org.apache.directory.shared.ldap.filter.ExprNode;
-import org.apache.directory.shared.ldap.filter.FilterParserImpl;
+import org.apache.directory.shared.ldap.filter.FilterParser;
 import org.apache.directory.shared.ldap.filter.NotNode;
 import org.apache.directory.shared.ldap.filter.OrNode;
 import org.apache.directory.shared.ldap.filter.SimpleNode;
@@ -353,7 +353,7 @@
     {
         SubtreeSpecification ss = parser.parse( SPEC_WITH_FILTER );
 
-        ExprNode filter = new FilterParserImpl().parse( "(&(cn=test)(sn=test))" );
+        ExprNode filter = FilterParser.parse( "(&(cn=test)(sn=test))" );
 
         assertEquals( filter, ss.getRefinement() );
     }