001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.directory.api.ldap.model.ldif;
021
022
023import java.io.BufferedReader;
024import java.io.IOException;
025import java.io.StringReader;
026import java.util.ArrayList;
027
028import javax.naming.directory.Attributes;
029import javax.naming.directory.BasicAttributes;
030
031import org.apache.directory.api.i18n.I18n;
032import org.apache.directory.api.ldap.model.entry.Attribute;
033import org.apache.directory.api.ldap.model.entry.DefaultEntry;
034import org.apache.directory.api.ldap.model.entry.Entry;
035import org.apache.directory.api.ldap.model.exception.LdapException;
036import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
037import org.apache.directory.api.ldap.model.schema.AttributeType;
038import org.apache.directory.api.ldap.model.schema.SchemaManager;
039import org.apache.directory.api.util.Strings;
040import org.slf4j.Logger;
041import org.slf4j.LoggerFactory;
042
043
044/**
045 * <pre>
046 *  &lt;ldif-file&gt; ::= &quot;version:&quot; &lt;fill&gt; &lt;number&gt; &lt;seps&gt; &lt;dn-spec&gt; &lt;sep&gt;
047 *  &lt;ldif-content-change&gt;
048 *
049 *  &lt;ldif-content-change&gt; ::=
050 *    &lt;number&gt; &lt;oid&gt; &lt;options-e&gt; &lt;value-spec&gt; &lt;sep&gt; &lt;attrval-specs-e&gt;
051 *    &lt;ldif-attrval-record-e&gt; |
052 *    &lt;alpha&gt; &lt;chars-e&gt; &lt;options-e&gt; &lt;value-spec&gt; &lt;sep&gt; &lt;attrval-specs-e&gt;
053 *    &lt;ldif-attrval-record-e&gt; |
054 *    &quot;control:&quot; &lt;fill&gt; &lt;number&gt; &lt;oid&gt; &lt;spaces-e&gt; &lt;criticality&gt;
055 *    &lt;value-spec-e&gt; &lt;sep&gt; &lt;controls-e&gt;
056 *        &quot;changetype:&quot; &lt;fill&gt; &lt;changerecord-type&gt; &lt;ldif-change-record-e&gt; |
057 *    &quot;changetype:&quot; &lt;fill&gt; &lt;changerecord-type&gt; &lt;ldif-change-record-e&gt;
058 *
059 *  &lt;ldif-attrval-record-e&gt; ::= &lt;seps&gt; &lt;dn-spec&gt; &lt;sep&gt; &lt;attributeType&gt;
060 *    &lt;options-e&gt; &lt;value-spec&gt; &lt;sep&gt; &lt;attrval-specs-e&gt;
061 *    &lt;ldif-attrval-record-e&gt; | e
062 *
063 *  &lt;ldif-change-record-e&gt; ::= &lt;seps&gt; &lt;dn-spec&gt; &lt;sep&gt; &lt;controls-e&gt;
064 *    &quot;changetype:&quot; &lt;fill&gt; &lt;changerecord-type&gt; &lt;ldif-change-record-e&gt; | e
065 *
066 *  &lt;dn-spec&gt; ::= &quot;dn:&quot; &lt;fill&gt; &lt;safe-string&gt; | &quot;dn::&quot; &lt;fill&gt; &lt;base64-string&gt;
067 *
068 *  &lt;controls-e&gt; ::= &quot;control:&quot; &lt;fill&gt; &lt;number&gt; &lt;oid&gt; &lt;spaces-e&gt; &lt;criticality&gt;
069 *    &lt;value-spec-e&gt; &lt;sep&gt; &lt;controls-e&gt; | e
070 *
071 *  &lt;criticality&gt; ::= &quot;true&quot; | &quot;false&quot; | e
072 *
073 *  &lt;oid&gt; ::= '.' &lt;number&gt; &lt;oid&gt; | e
074 *
075 *  &lt;attrval-specs-e&gt; ::= &lt;number&gt; &lt;oid&gt; &lt;options-e&gt; &lt;value-spec&gt; &lt;sep&gt;
076 *  &lt;attrval-specs-e&gt; |
077 *    &lt;alpha&gt; &lt;chars-e&gt; &lt;options-e&gt; &lt;value-spec&gt; &lt;sep&gt; &lt;attrval-specs-e&gt; | e
078 *
079 *  &lt;value-spec-e&gt; ::= &lt;value-spec&gt; | e
080 *
081 *  &lt;value-spec&gt; ::= ':' &lt;fill&gt; &lt;safe-string-e&gt; |
082 *    &quot;::&quot; &lt;fill&gt; &lt;base64-chars&gt; |
083 *    &quot;:&lt;&quot; &lt;fill&gt; &lt;url&gt;
084 *
085 *  &lt;attributeType&gt; ::= &lt;number&gt; &lt;oid&gt; | &lt;alpha&gt; &lt;chars-e&gt;
086 *
087 *  &lt;options-e&gt; ::= ';' &lt;char&gt; &lt;chars-e&gt; &lt;options-e&gt; |e
088 *
089 *  &lt;chars-e&gt; ::= &lt;char&gt; &lt;chars-e&gt; |  e
090 *
091 *  &lt;changerecord-type&gt; ::= &quot;add&quot; &lt;sep&gt; &lt;attributeType&gt; &lt;options-e&gt; &lt;value-spec&gt;
092 *  &lt;sep&gt; &lt;attrval-specs-e&gt; |
093 *    &quot;delete&quot; &lt;sep&gt; |
094 *    &quot;modify&quot; &lt;sep&gt; &lt;mod-type&gt; &lt;fill&gt; &lt;attributeType&gt; &lt;options-e&gt; &lt;sep&gt;
095 *    &lt;attrval-specs-e&gt; &lt;sep&gt; '-' &lt;sep&gt; &lt;mod-specs-e&gt; |
096 *    &quot;moddn&quot; &lt;sep&gt; &lt;newrdn&gt; &lt;sep&gt; &quot;deleteoldrdn:&quot; &lt;fill&gt; &lt;0-1&gt; &lt;sep&gt;
097 *    &lt;newsuperior-e&gt; &lt;sep&gt; |
098 *    &quot;modrdn&quot; &lt;sep&gt; &lt;newrdn&gt; &lt;sep&gt; &quot;deleteoldrdn:&quot; &lt;fill&gt; &lt;0-1&gt; &lt;sep&gt;
099 *    &lt;newsuperior-e&gt; &lt;sep&gt;
100 *
101 *  &lt;newrdn&gt; ::= ':' &lt;fill&gt; &lt;safe-string&gt; | &quot;::&quot; &lt;fill&gt; &lt;base64-chars&gt;
102 *
103 *  &lt;newsuperior-e&gt; ::= &quot;newsuperior&quot; &lt;newrdn&gt; | e
104 *
105 *  &lt;mod-specs-e&gt; ::= &lt;mod-type&gt; &lt;fill&gt; &lt;attributeType&gt; &lt;options-e&gt;
106 *    &lt;sep&gt; &lt;attrval-specs-e&gt; &lt;sep&gt; '-' &lt;sep&gt; &lt;mod-specs-e&gt; | e
107 *
108 *  &lt;mod-type&gt; ::= &quot;add:&quot; | &quot;delete:&quot; | &quot;replace:&quot;
109 *
110 *  &lt;url&gt; ::= &lt;a Uniform Resource Locator, as defined in [6]&gt;
111 *
112 *
113 *
114 *  LEXICAL
115 *  -------
116 *
117 *  &lt;fill&gt;           ::= ' ' &lt;fill&gt; | e
118 *  &lt;char&gt;           ::= &lt;alpha&gt; | &lt;digit&gt; | '-'
119 *  &lt;number&gt;         ::= &lt;digit&gt; &lt;digits&gt;
120 *  &lt;0-1&gt;            ::= '0' | '1'
121 *  &lt;digits&gt;         ::= &lt;digit&gt; &lt;digits&gt; | e
122 *  &lt;digit&gt;          ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
123 *  &lt;seps&gt;           ::= &lt;sep&gt; &lt;seps-e&gt;
124 *  &lt;seps-e&gt;         ::= &lt;sep&gt; &lt;seps-e&gt; | e
125 *  &lt;sep&gt;            ::= 0x0D 0x0A | 0x0A
126 *  &lt;spaces&gt;         ::= ' ' &lt;spaces-e&gt;
127 *  &lt;spaces-e&gt;       ::= ' ' &lt;spaces-e&gt; | e
128 *  &lt;safe-string-e&gt;  ::= &lt;safe-string&gt; | e
129 *  &lt;safe-string&gt;    ::= &lt;safe-init-char&gt; &lt;safe-chars&gt;
130 *  &lt;safe-init-char&gt; ::= [0x01-0x09] | 0x0B | 0x0C | [0x0E-0x1F] | [0x21-0x39] | 0x3B | [0x3D-0x7F]
131 *  &lt;safe-chars&gt;     ::= &lt;safe-char&gt; &lt;safe-chars&gt; | e
132 *  &lt;safe-char&gt;      ::= [0x01-0x09] | 0x0B | 0x0C | [0x0E-0x7F]
133 *  &lt;base64-string&gt;  ::= &lt;base64-char&gt; &lt;base64-chars&gt;
134 *  &lt;base64-chars&gt;   ::= &lt;base64-char&gt; &lt;base64-chars&gt; | e
135 *  &lt;base64-char&gt;    ::= 0x2B | 0x2F | [0x30-0x39] | 0x3D | [0x41-9x5A] | [0x61-0x7A]
136 *  &lt;alpha&gt;          ::= [0x41-0x5A] | [0x61-0x7A]
137 *
138 *  COMMENTS
139 *  --------
140 *  - The ldap-oid VN is not correct in the RFC-2849. It has been changed from 1*DIGIT 0*1(&quot;.&quot; 1*DIGIT) to
141 *  DIGIT+ (&quot;.&quot; DIGIT+)*
142 *  - The mod-spec lacks a sep between *attrval-spec and &quot;-&quot;.
143 *  - The BASE64-UTF8-STRING should be BASE64-CHAR BASE64-STRING
144 *  - The ValueSpec rule must accept multilines values. In this case, we have a LF followed by a
145 *  single space before the continued value.
146 * </pre>
147 *
148 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
149 */
150public class LdifAttributesReader extends LdifReader
151{
152    /** A logger */
153    private static final Logger LOG = LoggerFactory.getLogger( LdifAttributesReader.class );
154
155
156    /**
157     * Constructors
158     */
159    public LdifAttributesReader()
160    {
161        lines = new ArrayList<String>();
162        position = 0;
163        version = DEFAULT_VERSION;
164    }
165
166
167    /**
168     * Parse an AttributeType/AttributeValue
169     *
170     * @param attributes The entry where to store the value
171     * @param line The line to parse
172     * @param lowerLine The same line, lowercased
173     * @throws LdapLdifException If anything goes wrong
174     */
175    private void parseAttribute( Attributes attributes, String line, String lowerLine ) throws LdapLdifException
176    {
177        int colonIndex = line.indexOf( ':' );
178
179        String attributeType = lowerLine.substring( 0, colonIndex );
180
181        // We should *not* have a Dn twice
182        if ( "dn".equals( attributeType ) )
183        {
184            LOG.error( I18n.err( I18n.ERR_12002_ENTRY_WITH_TWO_DNS ) );
185            throw new LdapLdifException( I18n.err( I18n.ERR_12003_LDIF_ENTRY_WITH_TWO_DNS ) );
186        }
187
188        Object attributeValue = parseValue( attributeType, line, colonIndex );
189
190        // Update the entry
191        javax.naming.directory.Attribute attribute = attributes.get( attributeType );
192
193        if ( attribute == null )
194        {
195            attributes.put( attributeType, attributeValue );
196        }
197        else
198        {
199            attribute.add( attributeValue );
200        }
201    }
202
203
204    /**
205     * Parse an AttributeType/AttributeValue
206     *
207     * @param schemaManager The SchemaManager
208     * @param entry The entry where to store the value
209     * @param line The line to parse
210     * @param lowerLine The same line, lowercased
211     * @throws LdapLdifException If anything goes wrong
212     */
213    private void parseEntryAttribute( SchemaManager schemaManager, Entry entry, String line, String lowerLine )
214        throws LdapLdifException
215    {
216        int colonIndex = line.indexOf( ':' );
217
218        String attributeName = lowerLine.substring( 0, colonIndex );
219        AttributeType attributeType = null;
220
221        // We should *not* have a Dn twice
222        if ( "dn".equals( attributeName ) )
223        {
224            LOG.error( I18n.err( I18n.ERR_12002_ENTRY_WITH_TWO_DNS ) );
225            throw new LdapLdifException( I18n.err( I18n.ERR_12003_LDIF_ENTRY_WITH_TWO_DNS ) );
226        }
227
228        if ( schemaManager != null )
229        {
230            attributeType = schemaManager.getAttributeType( attributeName );
231
232            if ( attributeType == null )
233            {
234                LOG.error( "" );
235                throw new LdapLdifException( "" );
236            }
237        }
238
239        Object attributeValue = parseValue( attributeName, line, colonIndex );
240
241        // Update the entry
242        Attribute attribute;
243
244        if ( schemaManager == null )
245        {
246            attribute = entry.get( attributeName );
247        }
248        else
249        {
250            attribute = entry.get( attributeType );
251        }
252
253        if ( attribute == null )
254        {
255            if ( schemaManager == null )
256            {
257                if ( attributeValue instanceof String )
258                {
259                    entry.put( attributeName, ( String ) attributeValue );
260                }
261                else
262                {
263                    entry.put( attributeName, ( byte[] ) attributeValue );
264                }
265            }
266            else
267            {
268                try
269                {
270                    if ( attributeValue instanceof String )
271                    {
272                        entry.put( attributeName, attributeType, ( String ) attributeValue );
273                    }
274                    else
275                    {
276                        entry.put( attributeName, attributeType, ( byte[] ) attributeValue );
277                    }
278                }
279                catch ( LdapException le )
280                {
281                    throw new LdapLdifException( I18n.err( I18n.ERR_12057_BAD_ATTRIBUTE ), le );
282                }
283            }
284        }
285        else
286        {
287            try
288            {
289                if ( attributeValue instanceof String )
290                {
291                    attribute.add( ( String ) attributeValue );
292                }
293                else
294                {
295                    attribute.add( ( byte[] ) attributeValue );
296                }
297            }
298            catch ( LdapInvalidAttributeValueException liave )
299            {
300                throw new LdapLdifException( liave.getMessage(), liave );
301            }
302        }
303    }
304
305
306    /**
307     * Parse a ldif file. The following rules are processed :
308     *
309     * &lt;ldif-file&gt; ::= &lt;ldif-attrval-record&gt; &lt;ldif-attrval-records&gt; |
310     * &lt;ldif-change-record&gt; &lt;ldif-change-records&gt; &lt;ldif-attrval-record&gt; ::=
311     * &lt;dn-spec&gt; &lt;sep&gt; &lt;attrval-spec&gt; &lt;attrval-specs&gt; &lt;ldif-change-record&gt; ::=
312     * &lt;dn-spec&gt; &lt;sep&gt; &lt;controls-e&gt; &lt;changerecord&gt; &lt;dn-spec&gt; ::= "dn:" &lt;fill&gt;
313     * &lt;distinguishedName&gt; | "dn::" &lt;fill&gt; &lt;base64-distinguishedName&gt;
314     * &lt;changerecord&gt; ::= "changetype:" &lt;fill&gt; &lt;change-op&gt;
315     *
316     * @param schemaManager The SchemaManager
317     * @return The read entry
318     * @throws LdapLdifException If the entry can't be read or is invalid
319     */
320    private Entry parseEntry( SchemaManager schemaManager ) throws LdapLdifException
321    {
322        if ( ( lines == null ) || lines.isEmpty() )
323        {
324            LOG.debug( "The entry is empty : end of ldif file" );
325            return null;
326        }
327
328        Entry entry = new DefaultEntry( schemaManager );
329
330        // Now, let's iterate through the other lines
331        for ( String line : lines )
332        {
333            // Each line could start either with an OID, an attribute type, with
334            // "control:" or with "changetype:"
335            String lowerLine = Strings.toLowerCaseAscii( line );
336
337            // We have three cases :
338            // 1) The first line after the Dn is a "control:" -> this is an error
339            // 2) The first line after the Dn is a "changeType:" -> this is an error
340            // 3) The first line after the Dn is anything else
341            if ( lowerLine.startsWith( "control:" ) )
342            {
343                LOG.error( I18n.err( I18n.ERR_12004_CHANGE_NOT_ALLOWED ) );
344                throw new LdapLdifException( I18n.err( I18n.ERR_12005_NO_CHANGE ) );
345            }
346            else if ( lowerLine.startsWith( "changetype:" ) )
347            {
348                LOG.error( I18n.err( I18n.ERR_12004_CHANGE_NOT_ALLOWED ) );
349                throw new LdapLdifException( I18n.err( I18n.ERR_12005_NO_CHANGE ) );
350            }
351            else if ( line.indexOf( ':' ) > 0 )
352            {
353                parseEntryAttribute( schemaManager, entry, line, lowerLine );
354            }
355            else
356            {
357                // Invalid attribute Value
358                LOG.error( I18n.err( I18n.ERR_12006_EXPECTING_ATTRIBUTE_TYPE ) );
359                throw new LdapLdifException( I18n.err( I18n.ERR_12007_BAD_ATTRIBUTE ) );
360            }
361        }
362
363        LOG.debug( "Read an attributes : {}", entry );
364
365        return entry;
366    }
367
368
369    /**
370     * Parse a ldif file. The following rules are processed :
371     *
372     * <pre>
373     * &lt;ldif-file&gt; ::= &lt;ldif-attrval-record&gt; &lt;ldif-attrval-records&gt; |
374     * &lt;ldif-change-record&gt; &lt;ldif-change-records&gt; &lt;ldif-attrval-record&gt; ::=
375     * &lt;dn-spec&gt; &lt;sep&gt; &lt;attrval-spec&gt; &lt;attrval-specs&gt; &lt;ldif-change-record&gt; ::=
376     * &lt;dn-spec&gt; &lt;sep&gt; &lt;controls-e&gt; &lt;changerecord&gt; &lt;dn-spec&gt; ::= "dn:" &lt;fill&gt;
377     * &lt;distinguishedName&gt; | "dn::" &lt;fill&gt; &lt;base64-distinguishedName&gt;
378     * &lt;changerecord&gt; ::= "changetype:" &lt;fill&gt; &lt;change-op&gt;
379     * </pre>
380     *
381     * @return The read entry
382     * @throws LdapLdifException If the entry can't be read or is invalid
383     */
384    private Attributes parseAttributes() throws LdapLdifException
385    {
386        if ( ( lines == null ) || lines.isEmpty() )
387        {
388            LOG.debug( "The entry is empty : end of ldif file" );
389            return null;
390        }
391
392        Attributes attributes = new BasicAttributes( true );
393
394        // Now, let's iterate through the other lines
395        for ( String line : lines )
396        {
397            // Each line could start either with an OID, an attribute type, with
398            // "control:" or with "changetype:"
399            String lowerLine = Strings.toLowerCaseAscii( line );
400
401            // We have three cases :
402            // 1) The first line after the Dn is a "control:" -> this is an error
403            // 2) The first line after the Dn is a "changeType:" -> this is an error
404            // 3) The first line after the Dn is anything else
405            if ( lowerLine.startsWith( "control:" ) )
406            {
407                LOG.error( I18n.err( I18n.ERR_12004_CHANGE_NOT_ALLOWED ) );
408                throw new LdapLdifException( I18n.err( I18n.ERR_12005_NO_CHANGE ) );
409            }
410            else if ( lowerLine.startsWith( "changetype:" ) )
411            {
412                LOG.error( I18n.err( I18n.ERR_12004_CHANGE_NOT_ALLOWED ) );
413                throw new LdapLdifException( I18n.err( I18n.ERR_12005_NO_CHANGE ) );
414            }
415            else if ( line.indexOf( ':' ) > 0 )
416            {
417                parseAttribute( attributes, line, lowerLine );
418            }
419            else
420            {
421                // Invalid attribute Value
422                LOG.error( I18n.err( I18n.ERR_12006_EXPECTING_ATTRIBUTE_TYPE ) );
423                throw new LdapLdifException( I18n.err( I18n.ERR_12007_BAD_ATTRIBUTE ) );
424            }
425        }
426
427        LOG.debug( "Read an attributes : {}", attributes );
428
429        return attributes;
430    }
431
432
433    /**
434     * A method which parses a ldif string and returns a list of Attributes.
435     *
436     * @param ldif The ldif string
437     * @return A list of Attributes, or an empty List
438     * @throws LdapLdifException If something went wrong
439     */
440    public Attributes parseAttributes( String ldif ) throws LdapLdifException
441    {
442        lines = new ArrayList<String>();
443        position = 0;
444
445        LOG.debug( "Starts parsing ldif buffer" );
446
447        if ( Strings.isEmpty( ldif ) )
448        {
449            return new BasicAttributes( true );
450        }
451
452        StringReader strIn = new StringReader( ldif );
453        reader = new BufferedReader( strIn );
454
455        try
456        {
457            readLines();
458
459            Attributes attributes = parseAttributes();
460
461            if ( LOG.isDebugEnabled() )
462            {
463                if ( attributes == null )
464                {
465                    LOG.debug( "Parsed no entry." );
466                }
467                else
468                {
469                    LOG.debug( "Parsed one entry." );
470                }
471            }
472
473            return attributes;
474        }
475        catch ( LdapLdifException ne )
476        {
477            LOG.error( I18n.err( I18n.ERR_12008_CANNOT_PARSE_LDIF_BUFFER, ne.getLocalizedMessage() ) );
478            throw new LdapLdifException( I18n.err( I18n.ERR_12009_ERROR_PARSING_LDIF_BUFFER ), ne );
479        }
480        finally
481        {
482            try
483            {
484                reader.close();
485            }
486            catch ( IOException ioe )
487            {
488                throw new LdapLdifException( I18n.err( I18n.ERR_12024_CANNOT_CLOSE_FILE ), ioe );
489            }
490        }
491    }
492
493
494    /**
495     * A method which parses a ldif string and returns an Entry.
496     *
497     * @param ldif The ldif string
498     * @return An entry
499     * @throws LdapLdifException If something went wrong
500     */
501    public Entry parseEntry( String ldif ) throws LdapLdifException
502    {
503        lines = new ArrayList<String>();
504        position = 0;
505
506        LOG.debug( "Starts parsing ldif buffer" );
507
508        if ( Strings.isEmpty( ldif ) )
509        {
510            return new DefaultEntry();
511        }
512
513        StringReader strIn = new StringReader( ldif );
514        reader = new BufferedReader( strIn );
515
516        try
517        {
518            readLines();
519
520            Entry entry = parseEntry( ( SchemaManager ) null );
521
522            if ( LOG.isDebugEnabled() )
523            {
524                if ( entry == null )
525                {
526                    LOG.debug( "Parsed no entry." );
527                }
528                else
529                {
530                    LOG.debug( "Parsed one entry." );
531                }
532
533            }
534
535            return entry;
536        }
537        catch ( LdapLdifException ne )
538        {
539            LOG.error( I18n.err( I18n.ERR_12008_CANNOT_PARSE_LDIF_BUFFER, ne.getLocalizedMessage() ) );
540            throw new LdapLdifException( I18n.err( I18n.ERR_12009_ERROR_PARSING_LDIF_BUFFER ), ne );
541        }
542        finally
543        {
544            try
545            {
546                reader.close();
547            }
548            catch ( IOException ioe )
549            {
550                throw new LdapLdifException( I18n.err( I18n.ERR_12024_CANNOT_CLOSE_FILE ), ioe );
551            }
552        }
553    }
554
555
556    /**
557     * A method which parses a ldif string and returns an Entry.
558     *
559     * @param schemaManager The SchemaManager
560     * @param ldif The ldif string
561     * @return An entry
562     * @throws LdapLdifException If something went wrong
563     */
564    public Entry parseEntry( SchemaManager schemaManager, String ldif ) throws LdapLdifException
565    {
566        lines = new ArrayList<String>();
567        position = 0;
568
569        LOG.debug( "Starts parsing ldif buffer" );
570
571        if ( Strings.isEmpty( ldif ) )
572        {
573            return new DefaultEntry( schemaManager );
574        }
575
576        StringReader strIn = new StringReader( ldif );
577        reader = new BufferedReader( strIn );
578
579        try
580        {
581            readLines();
582
583            Entry entry = parseEntry( schemaManager );
584
585            if ( LOG.isDebugEnabled() )
586            {
587                if ( entry == null )
588                {
589                    LOG.debug( "Parsed no entry." );
590                }
591                else
592                {
593                    LOG.debug( "Parsed one entry." );
594                }
595
596            }
597
598            return entry;
599        }
600        catch ( LdapLdifException ne )
601        {
602            LOG.error( I18n.err( I18n.ERR_12008_CANNOT_PARSE_LDIF_BUFFER, ne.getLocalizedMessage() ) );
603            throw new LdapLdifException( I18n.err( I18n.ERR_12009_ERROR_PARSING_LDIF_BUFFER ), ne );
604        }
605        finally
606        {
607            try
608            {
609                reader.close();
610            }
611            catch ( IOException ioe )
612            {
613                throw new LdapLdifException( I18n.err( I18n.ERR_12024_CANNOT_CLOSE_FILE ), ioe );
614            }
615        }
616    }
617}