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 *    https://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        super();
162        lines = new ArrayList<String>();
163        position = 0;
164        version = DEFAULT_VERSION;
165    }
166
167
168    /**
169     * Parse an AttributeType/AttributeValue
170     *
171     * @param attributes The entry where to store the value
172     * @param line The line to parse
173     * @param lowerLine The same line, lowercased
174     * @throws LdapLdifException If anything goes wrong
175     */
176    private void parseAttribute( Attributes attributes, String line, String lowerLine ) throws LdapLdifException
177    {
178        int colonIndex = line.indexOf( ':' );
179
180        String attributeType = lowerLine.substring( 0, colonIndex );
181
182        // We should *not* have a Dn twice
183        if ( "dn".equals( attributeType ) )
184        {
185            LOG.error( I18n.err( I18n.ERR_13400_ENTRY_WITH_TWO_DNS ) );
186            throw new LdapLdifException( I18n.err( I18n.ERR_13439_LDIF_ENTRY_WITH_TWO_DNS ) );
187        }
188
189        Object attributeValue = parseValue( attributeType, line, colonIndex );
190
191        // Update the entry
192        javax.naming.directory.Attribute attribute = attributes.get( attributeType );
193
194        if ( attribute == null )
195        {
196            attributes.put( attributeType, attributeValue );
197        }
198        else
199        {
200            attribute.add( attributeValue );
201        }
202    }
203
204
205    /**
206     * Parse an AttributeType/AttributeValue
207     *
208     * @param schemaManager The SchemaManager
209     * @param entry The entry where to store the value
210     * @param line The line to parse
211     * @param lowerLine The same line, lowercased
212     * @throws LdapLdifException If anything goes wrong
213     */
214    private void parseEntryAttribute( SchemaManager schemaManager, Entry entry, String line, String lowerLine )
215        throws LdapLdifException
216    {
217        int colonIndex = line.indexOf( ':' );
218
219        String attributeName = lowerLine.substring( 0, colonIndex );
220        AttributeType attributeType = null;
221
222        // We should *not* have a Dn twice
223        if ( "dn".equals( attributeName ) )
224        {
225            LOG.error( I18n.err( I18n.ERR_13400_ENTRY_WITH_TWO_DNS ) );
226            throw new LdapLdifException( I18n.err( I18n.ERR_13439_LDIF_ENTRY_WITH_TWO_DNS ) );
227        }
228
229        if ( schemaManager != null )
230        {
231            attributeType = schemaManager.getAttributeType( attributeName );
232
233            if ( attributeType == null )
234            {
235                String msg = I18n.err( I18n.ERR_13475_UNKNOWN_ATTRIBUTETYPE,  attributeName );
236                LOG.error( msg );
237                throw new LdapLdifException( msg );
238            }
239        }
240
241        Object attributeValue = parseValue( attributeName, line, colonIndex );
242
243        // Update the entry
244        Attribute attribute;
245
246        if ( schemaManager == null )
247        {
248            attribute = entry.get( attributeName );
249        }
250        else
251        {
252            attribute = entry.get( attributeType );
253        }
254
255        if ( attribute == null )
256        {
257            if ( schemaManager == null )
258            {
259                if ( attributeValue instanceof String )
260                {
261                    entry.put( attributeName, ( String ) attributeValue );
262                }
263                else
264                {
265                    entry.put( attributeName, ( byte[] ) attributeValue );
266                }
267            }
268            else
269            {
270                try
271                {
272                    if ( attributeValue instanceof String )
273                    {
274                        entry.put( attributeName, attributeType, ( String ) attributeValue );
275                    }
276                    else
277                    {
278                        entry.put( attributeName, attributeType, ( byte[] ) attributeValue );
279                    }
280                }
281                catch ( LdapException le )
282                {
283                    throw new LdapLdifException( I18n.err( I18n.ERR_13460_BAD_ATTRIBUTE ), le );
284                }
285            }
286        }
287        else
288        {
289            try
290            {
291                if ( attributeValue instanceof String )
292                {
293                    attribute.add( ( String ) attributeValue );
294                }
295                else
296                {
297                    attribute.add( ( byte[] ) attributeValue );
298                }
299            }
300            catch ( LdapInvalidAttributeValueException liave )
301            {
302                throw new LdapLdifException( liave.getMessage(), liave );
303            }
304        }
305    }
306
307
308    /**
309     * Parse a ldif file. The following rules are processed :
310     *
311     * &lt;ldif-file&gt; ::= &lt;ldif-attrval-record&gt; &lt;ldif-attrval-records&gt; |
312     * &lt;ldif-change-record&gt; &lt;ldif-change-records&gt; &lt;ldif-attrval-record&gt; ::=
313     * &lt;dn-spec&gt; &lt;sep&gt; &lt;attrval-spec&gt; &lt;attrval-specs&gt; &lt;ldif-change-record&gt; ::=
314     * &lt;dn-spec&gt; &lt;sep&gt; &lt;controls-e&gt; &lt;changerecord&gt; &lt;dn-spec&gt; ::= "dn:" &lt;fill&gt;
315     * &lt;distinguishedName&gt; | "dn::" &lt;fill&gt; &lt;base64-distinguishedName&gt;
316     * &lt;changerecord&gt; ::= "changetype:" &lt;fill&gt; &lt;change-op&gt;
317     *
318     * @param schemaManager The SchemaManager
319     * @return The read entry
320     * @throws LdapLdifException If the entry can't be read or is invalid
321     */
322    private Entry parseEntry( SchemaManager schemaManager ) throws LdapLdifException
323    {
324        if ( ( lines == null ) || lines.isEmpty() )
325        {
326            if ( LOG.isDebugEnabled() )
327            {
328                LOG.debug( I18n.msg( I18n.MSG_13408_END_OF_LDIF ) );
329            }
330            
331            return null;
332        }
333
334        Entry entry = new DefaultEntry( schemaManager );
335
336        // Now, let's iterate through the other lines
337        for ( String line : lines )
338        {
339            // Each line could start either with an OID, an attribute type, with
340            // "control:" or with "changetype:"
341            String lowerLine = Strings.toLowerCaseAscii( line );
342
343            // We have three cases :
344            // 1) The first line after the Dn is a "control:" -> this is an error
345            // 2) The first line after the Dn is a "changeType:" -> this is an error
346            // 3) The first line after the Dn is anything else
347            if ( lowerLine.startsWith( "control:" ) )
348            {
349                LOG.error( I18n.err( I18n.ERR_13401_CHANGE_NOT_ALLOWED ) );
350                throw new LdapLdifException( I18n.err( I18n.ERR_13440_NO_CHANGE ) );
351            }
352            else if ( lowerLine.startsWith( "changetype:" ) )
353            {
354                LOG.error( I18n.err( I18n.ERR_13401_CHANGE_NOT_ALLOWED ) );
355                throw new LdapLdifException( I18n.err( I18n.ERR_13440_NO_CHANGE ) );
356            }
357            else if ( line.indexOf( ':' ) > 0 )
358            {
359                parseEntryAttribute( schemaManager, entry, line, lowerLine );
360            }
361            else
362            {
363                // Invalid attribute Value
364                LOG.error( I18n.err( I18n.ERR_13402_EXPECTING_ATTRIBUTE_TYPE ) );
365                throw new LdapLdifException( I18n.err( I18n.ERR_13441_BAD_ATTRIBUTE ) );
366            }
367        }
368
369        if ( LOG.isDebugEnabled() )
370        {
371            LOG.debug( I18n.msg( I18n.MSG_13405_READ_ATTR, entry ) );
372        }
373
374        return entry;
375    }
376
377
378    /**
379     * Parse a ldif file. The following rules are processed :
380     *
381     * <pre>
382     * &lt;ldif-file&gt; ::= &lt;ldif-attrval-record&gt; &lt;ldif-attrval-records&gt; |
383     * &lt;ldif-change-record&gt; &lt;ldif-change-records&gt; &lt;ldif-attrval-record&gt; ::=
384     * &lt;dn-spec&gt; &lt;sep&gt; &lt;attrval-spec&gt; &lt;attrval-specs&gt; &lt;ldif-change-record&gt; ::=
385     * &lt;dn-spec&gt; &lt;sep&gt; &lt;controls-e&gt; &lt;changerecord&gt; &lt;dn-spec&gt; ::= "dn:" &lt;fill&gt;
386     * &lt;distinguishedName&gt; | "dn::" &lt;fill&gt; &lt;base64-distinguishedName&gt;
387     * &lt;changerecord&gt; ::= "changetype:" &lt;fill&gt; &lt;change-op&gt;
388     * </pre>
389     * 
390     * @return The read entry
391     * @throws LdapLdifException If the entry can't be read or is invalid
392     */
393    private Attributes parseAttributes() throws LdapLdifException
394    {
395        if ( ( lines == null ) || lines.isEmpty() )
396        {
397            if ( LOG.isDebugEnabled() )
398            {
399                LOG.debug( I18n.msg( I18n.MSG_13408_END_OF_LDIF ) );
400            }
401
402            return null;
403        }
404
405        Attributes attributes = new BasicAttributes( true );
406
407        // Now, let's iterate through the other lines
408        for ( String line : lines )
409        {
410            // Each line could start either with an OID, an attribute type, with
411            // "control:" or with "changetype:"
412            String lowerLine = Strings.toLowerCaseAscii( line );
413
414            // We have three cases :
415            // 1) The first line after the Dn is a "control:" -> this is an error
416            // 2) The first line after the Dn is a "changeType:" -> this is an error
417            // 3) The first line after the Dn is anything else
418            if ( lowerLine.startsWith( "control:" ) )
419            {
420                LOG.error( I18n.err( I18n.ERR_13401_CHANGE_NOT_ALLOWED ) );
421                throw new LdapLdifException( I18n.err( I18n.ERR_13440_NO_CHANGE ) );
422            }
423            else if ( lowerLine.startsWith( "changetype:" ) )
424            {
425                LOG.error( I18n.err( I18n.ERR_13401_CHANGE_NOT_ALLOWED ) );
426                throw new LdapLdifException( I18n.err( I18n.ERR_13440_NO_CHANGE ) );
427            }
428            else if ( line.indexOf( ':' ) > 0 )
429            {
430                parseAttribute( attributes, line, lowerLine );
431            }
432            else
433            {
434                // Invalid attribute Value
435                LOG.error( I18n.err( I18n.ERR_13402_EXPECTING_ATTRIBUTE_TYPE ) );
436                throw new LdapLdifException( I18n.err( I18n.ERR_13441_BAD_ATTRIBUTE ) );
437            }
438        }
439
440        if ( LOG.isDebugEnabled() )
441        {
442            LOG.debug( I18n.msg( I18n.MSG_13405_READ_ATTR, attributes ) );
443        }
444
445        return attributes;
446    }
447
448
449    /**
450     * A method which parses a ldif string and returns a list of Attributes.
451     *
452     * @param ldif The ldif string
453     * @return A list of Attributes, or an empty List
454     * @throws LdapLdifException If something went wrong
455     */
456    public Attributes parseAttributes( String ldif ) throws LdapLdifException
457    {
458        lines = new ArrayList<String>();
459        position = 0;
460
461        if ( LOG.isDebugEnabled() )
462        {
463            LOG.debug( I18n.msg( I18n.MSG_13407_STARTS_PARSING_LDIF ) );
464        }
465
466        if ( Strings.isEmpty( ldif ) )
467        {
468            return new BasicAttributes( true );
469        }
470
471        StringReader strIn = new StringReader( ldif );
472        reader = new BufferedReader( strIn );
473
474        try
475        {
476            readLines();
477
478            Attributes attributes = parseAttributes();
479
480            if ( LOG.isDebugEnabled() )
481            {
482                if ( attributes == null )
483                {
484                    LOG.debug( I18n.msg( I18n.MSG_13401_PARSED_NO_ENTRY ) );
485                }
486                else
487                {
488                    LOG.debug( I18n.msg( I18n.MSG_13402_PARSED_ONE_ENTRY ) );
489                }
490            }
491
492            return attributes;
493        }
494        catch ( LdapLdifException ne )
495        {
496            LOG.error( I18n.err( I18n.ERR_13403_CANNOT_PARSE_LDIF_BUFFER, ne.getLocalizedMessage() ) );
497            throw new LdapLdifException( I18n.err( I18n.ERR_13442_ERROR_PARSING_LDIF_BUFFER ), ne );
498        }
499        finally
500        {
501            try
502            {
503                reader.close();
504            }
505            catch ( IOException ioe )
506            {
507                throw new LdapLdifException( I18n.err( I18n.ERR_13450_CANNOT_CLOSE_FILE ), ioe );
508            }
509        }
510    }
511
512
513    /**
514     * A method which parses a ldif string and returns an Entry.
515     *
516     * @param ldif The ldif string
517     * @return An entry
518     * @throws LdapLdifException If something went wrong
519     */
520    public Entry parseEntry( String ldif ) throws LdapLdifException
521    {
522        lines = new ArrayList<String>();
523        position = 0;
524
525        if ( LOG.isDebugEnabled() )
526        {
527            LOG.debug( I18n.msg( I18n.MSG_13407_STARTS_PARSING_LDIF ) );
528        }
529
530        if ( Strings.isEmpty( ldif ) )
531        {
532            return new DefaultEntry();
533        }
534
535        StringReader strIn = new StringReader( ldif );
536        reader = new BufferedReader( strIn );
537
538        try
539        {
540            readLines();
541
542            Entry entry = parseEntry( ( SchemaManager ) null );
543
544            if ( LOG.isDebugEnabled() )
545            {
546                if ( entry == null )
547                {
548                    LOG.debug( I18n.msg( I18n.MSG_13401_PARSED_NO_ENTRY ) );
549                }
550                else
551                {
552                    LOG.debug( I18n.msg( I18n.MSG_13402_PARSED_ONE_ENTRY ) );
553                }
554            }
555
556            return entry;
557        }
558        catch ( LdapLdifException ne )
559        {
560            LOG.error( I18n.err( I18n.ERR_13403_CANNOT_PARSE_LDIF_BUFFER, ne.getLocalizedMessage() ) );
561            throw new LdapLdifException( I18n.err( I18n.ERR_13442_ERROR_PARSING_LDIF_BUFFER ), ne );
562        }
563        finally
564        {
565            try
566            {
567                reader.close();
568            }
569            catch ( IOException ioe )
570            {
571                throw new LdapLdifException( I18n.err( I18n.ERR_13450_CANNOT_CLOSE_FILE ), ioe );
572            }
573        }
574    }
575
576
577    /**
578     * A method which parses a ldif string and returns an Entry.
579     *
580     * @param schemaManager The SchemaManager
581     * @param ldif The ldif string
582     * @return An entry
583     * @throws LdapLdifException If something went wrong
584     */
585    public Entry parseEntry( SchemaManager schemaManager, String ldif ) throws LdapLdifException
586    {
587        lines = new ArrayList<String>();
588        position = 0;
589
590        if ( LOG.isDebugEnabled() )
591        {
592            LOG.debug( I18n.msg( I18n.MSG_13407_STARTS_PARSING_LDIF ) );
593        }
594
595        if ( Strings.isEmpty( ldif ) )
596        {
597            return new DefaultEntry( schemaManager );
598        }
599
600        StringReader strIn = new StringReader( ldif );
601        reader = new BufferedReader( strIn );
602
603        try
604        {
605            readLines();
606
607            Entry entry = parseEntry( schemaManager );
608
609            if ( LOG.isDebugEnabled() )
610            {
611                if ( entry == null )
612                {
613                    LOG.debug( I18n.msg( I18n.MSG_13401_PARSED_NO_ENTRY ) );
614                }
615                else
616                {
617                    LOG.debug( I18n.msg( I18n.MSG_13402_PARSED_ONE_ENTRY ) );
618                }
619
620            }
621
622            return entry;
623        }
624        catch ( LdapLdifException ne )
625        {
626            LOG.error( I18n.err( I18n.ERR_13403_CANNOT_PARSE_LDIF_BUFFER, ne.getLocalizedMessage() ) );
627            throw new LdapLdifException( I18n.err( I18n.ERR_13442_ERROR_PARSING_LDIF_BUFFER ), ne );
628        }
629        finally
630        {
631            try
632            {
633                reader.close();
634            }
635            catch ( IOException ioe )
636            {
637                throw new LdapLdifException( I18n.err( I18n.ERR_13450_CANNOT_CLOSE_FILE ), ioe );
638            }
639        }
640    }
641}