View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.directory.api.ldap.model.ldif;
21  
22  
23  import java.io.BufferedReader;
24  import java.io.IOException;
25  import java.io.StringReader;
26  import java.util.ArrayList;
27  
28  import javax.naming.directory.Attributes;
29  import javax.naming.directory.BasicAttributes;
30  
31  import org.apache.directory.api.i18n.I18n;
32  import org.apache.directory.api.ldap.model.entry.Attribute;
33  import org.apache.directory.api.ldap.model.entry.DefaultEntry;
34  import org.apache.directory.api.ldap.model.entry.Entry;
35  import org.apache.directory.api.ldap.model.exception.LdapException;
36  import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
37  import org.apache.directory.api.ldap.model.schema.AttributeType;
38  import org.apache.directory.api.ldap.model.schema.SchemaManager;
39  import org.apache.directory.api.util.Strings;
40  import org.slf4j.Logger;
41  import org.slf4j.LoggerFactory;
42  
43  
44  /**
45   * <pre>
46   *  &lt;ldif-file&gt; ::= &quot;version:&quot; &lt;fill&gt; &lt;number&gt; &lt;seps&gt; &lt;dn-spec&gt; &lt;sep&gt;
47   *  &lt;ldif-content-change&gt;
48   *
49   *  &lt;ldif-content-change&gt; ::=
50   *    &lt;number&gt; &lt;oid&gt; &lt;options-e&gt; &lt;value-spec&gt; &lt;sep&gt; &lt;attrval-specs-e&gt;
51   *    &lt;ldif-attrval-record-e&gt; |
52   *    &lt;alpha&gt; &lt;chars-e&gt; &lt;options-e&gt; &lt;value-spec&gt; &lt;sep&gt; &lt;attrval-specs-e&gt;
53   *    &lt;ldif-attrval-record-e&gt; |
54   *    &quot;control:&quot; &lt;fill&gt; &lt;number&gt; &lt;oid&gt; &lt;spaces-e&gt; &lt;criticality&gt;
55   *    &lt;value-spec-e&gt; &lt;sep&gt; &lt;controls-e&gt;
56   *        &quot;changetype:&quot; &lt;fill&gt; &lt;changerecord-type&gt; &lt;ldif-change-record-e&gt; |
57   *    &quot;changetype:&quot; &lt;fill&gt; &lt;changerecord-type&gt; &lt;ldif-change-record-e&gt;
58   *
59   *  &lt;ldif-attrval-record-e&gt; ::= &lt;seps&gt; &lt;dn-spec&gt; &lt;sep&gt; &lt;attributeType&gt;
60   *    &lt;options-e&gt; &lt;value-spec&gt; &lt;sep&gt; &lt;attrval-specs-e&gt;
61   *    &lt;ldif-attrval-record-e&gt; | e
62   *
63   *  &lt;ldif-change-record-e&gt; ::= &lt;seps&gt; &lt;dn-spec&gt; &lt;sep&gt; &lt;controls-e&gt;
64   *    &quot;changetype:&quot; &lt;fill&gt; &lt;changerecord-type&gt; &lt;ldif-change-record-e&gt; | e
65   *
66   *  &lt;dn-spec&gt; ::= &quot;dn:&quot; &lt;fill&gt; &lt;safe-string&gt; | &quot;dn::&quot; &lt;fill&gt; &lt;base64-string&gt;
67   *
68   *  &lt;controls-e&gt; ::= &quot;control:&quot; &lt;fill&gt; &lt;number&gt; &lt;oid&gt; &lt;spaces-e&gt; &lt;criticality&gt;
69   *    &lt;value-spec-e&gt; &lt;sep&gt; &lt;controls-e&gt; | e
70   *
71   *  &lt;criticality&gt; ::= &quot;true&quot; | &quot;false&quot; | e
72   *
73   *  &lt;oid&gt; ::= '.' &lt;number&gt; &lt;oid&gt; | e
74   *
75   *  &lt;attrval-specs-e&gt; ::= &lt;number&gt; &lt;oid&gt; &lt;options-e&gt; &lt;value-spec&gt; &lt;sep&gt;
76   *  &lt;attrval-specs-e&gt; |
77   *    &lt;alpha&gt; &lt;chars-e&gt; &lt;options-e&gt; &lt;value-spec&gt; &lt;sep&gt; &lt;attrval-specs-e&gt; | e
78   *
79   *  &lt;value-spec-e&gt; ::= &lt;value-spec&gt; | e
80   *
81   *  &lt;value-spec&gt; ::= ':' &lt;fill&gt; &lt;safe-string-e&gt; |
82   *    &quot;::&quot; &lt;fill&gt; &lt;base64-chars&gt; |
83   *    &quot;:&lt;&quot; &lt;fill&gt; &lt;url&gt;
84   *
85   *  &lt;attributeType&gt; ::= &lt;number&gt; &lt;oid&gt; | &lt;alpha&gt; &lt;chars-e&gt;
86   *
87   *  &lt;options-e&gt; ::= ';' &lt;char&gt; &lt;chars-e&gt; &lt;options-e&gt; |e
88   *
89   *  &lt;chars-e&gt; ::= &lt;char&gt; &lt;chars-e&gt; |  e
90   *
91   *  &lt;changerecord-type&gt; ::= &quot;add&quot; &lt;sep&gt; &lt;attributeType&gt; &lt;options-e&gt; &lt;value-spec&gt;
92   *  &lt;sep&gt; &lt;attrval-specs-e&gt; |
93   *    &quot;delete&quot; &lt;sep&gt; |
94   *    &quot;modify&quot; &lt;sep&gt; &lt;mod-type&gt; &lt;fill&gt; &lt;attributeType&gt; &lt;options-e&gt; &lt;sep&gt;
95   *    &lt;attrval-specs-e&gt; &lt;sep&gt; '-' &lt;sep&gt; &lt;mod-specs-e&gt; |
96   *    &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;
97   *    &lt;newsuperior-e&gt; &lt;sep&gt; |
98   *    &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;
99   *    &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  */
150 public 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 ( attributeType.equals( "dn" ) )
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( 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 ( attributeName.equals( "dn" ) )
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( line, colonIndex );
240 
241         // Update the entry
242         Attribute attribute = null;
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.size() == 0 ) )
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.toLowerCase( 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      * &lt;ldif-file&gt; ::= &lt;ldif-attrval-record&gt; &lt;ldif-attrval-records&gt; |
373      * &lt;ldif-change-record&gt; &lt;ldif-change-records&gt; &lt;ldif-attrval-record&gt; ::=
374      * &lt;dn-spec&gt; &lt;sep&gt; &lt;attrval-spec&gt; &lt;attrval-specs&gt; &lt;ldif-change-record&gt; ::=
375      * &lt;dn-spec&gt; &lt;sep&gt; &lt;controls-e&gt; &lt;changerecord&gt; &lt;dn-spec&gt; ::= "dn:" &lt;fill&gt;
376      * &lt;distinguishedName&gt; | "dn::" &lt;fill&gt; &lt;base64-distinguishedName&gt;
377      * &lt;changerecord&gt; ::= "changetype:" &lt;fill&gt; &lt;change-op&gt;
378      *
379      * @return The read entry
380      * @throws LdapLdifException If the entry can't be read or is invalid
381      */
382     private Attributes parseAttributes() throws LdapLdifException
383     {
384         if ( ( lines == null ) || ( lines.size() == 0 ) )
385         {
386             LOG.debug( "The entry is empty : end of ldif file" );
387             return null;
388         }
389 
390         Attributes attributes = new BasicAttributes( true );
391 
392         // Now, let's iterate through the other lines
393         for ( String line : lines )
394         {
395             // Each line could start either with an OID, an attribute type, with
396             // "control:" or with "changetype:"
397             String lowerLine = Strings.toLowerCase( line );
398 
399             // We have three cases :
400             // 1) The first line after the Dn is a "control:" -> this is an error
401             // 2) The first line after the Dn is a "changeType:" -> this is an error
402             // 3) The first line after the Dn is anything else
403             if ( lowerLine.startsWith( "control:" ) )
404             {
405                 LOG.error( I18n.err( I18n.ERR_12004_CHANGE_NOT_ALLOWED ) );
406                 throw new LdapLdifException( I18n.err( I18n.ERR_12005_NO_CHANGE ) );
407             }
408             else if ( lowerLine.startsWith( "changetype:" ) )
409             {
410                 LOG.error( I18n.err( I18n.ERR_12004_CHANGE_NOT_ALLOWED ) );
411                 throw new LdapLdifException( I18n.err( I18n.ERR_12005_NO_CHANGE ) );
412             }
413             else if ( line.indexOf( ':' ) > 0 )
414             {
415                 parseAttribute( attributes, line, lowerLine );
416             }
417             else
418             {
419                 // Invalid attribute Value
420                 LOG.error( I18n.err( I18n.ERR_12006_EXPECTING_ATTRIBUTE_TYPE ) );
421                 throw new LdapLdifException( I18n.err( I18n.ERR_12007_BAD_ATTRIBUTE ) );
422             }
423         }
424 
425         LOG.debug( "Read an attributes : {}", attributes );
426 
427         return attributes;
428     }
429 
430 
431     /**
432      * A method which parses a ldif string and returns a list of Attributes.
433      *
434      * @param ldif The ldif string
435      * @return A list of Attributes, or an empty List
436      * @throws LdapLdifException If something went wrong
437      */
438     public Attributes parseAttributes( String ldif ) throws LdapLdifException
439     {
440         lines = new ArrayList<String>();
441         position = 0;
442 
443         LOG.debug( "Starts parsing ldif buffer" );
444 
445         if ( Strings.isEmpty( ldif ) )
446         {
447             return new BasicAttributes( true );
448         }
449 
450         StringReader strIn = new StringReader( ldif );
451         reader = new BufferedReader( strIn );
452 
453         try
454         {
455             readLines();
456 
457             Attributes attributes = parseAttributes();
458 
459             if ( LOG.isDebugEnabled() )
460             {
461                 if ( attributes == null )
462                 {
463                     LOG.debug( "Parsed no entry." );
464                 }
465                 else
466                 {
467                     LOG.debug( "Parsed one entry." );
468                 }
469             }
470 
471             return attributes;
472         }
473         catch ( LdapLdifException ne )
474         {
475             LOG.error( I18n.err( I18n.ERR_12008_CANNOT_PARSE_LDIF_BUFFER, ne.getLocalizedMessage() ) );
476             throw new LdapLdifException( I18n.err( I18n.ERR_12009_ERROR_PARSING_LDIF_BUFFER ), ne );
477         }
478         finally
479         {
480             try
481             {
482                 reader.close();
483             }
484             catch ( IOException ioe )
485             {
486                 throw new LdapLdifException( I18n.err( I18n.ERR_12024_CANNOT_CLOSE_FILE ), ioe );
487             }
488         }
489     }
490 
491 
492     /**
493      * A method which parses a ldif string and returns an Entry.
494      *
495      * @param ldif The ldif string
496      * @return An entry
497      * @throws LdapLdifException If something went wrong
498      */
499     public Entry parseEntry( String ldif ) throws LdapLdifException
500     {
501         lines = new ArrayList<String>();
502         position = 0;
503 
504         LOG.debug( "Starts parsing ldif buffer" );
505 
506         if ( Strings.isEmpty( ldif ) )
507         {
508             return new DefaultEntry();
509         }
510 
511         StringReader strIn = new StringReader( ldif );
512         reader = new BufferedReader( strIn );
513 
514         try
515         {
516             readLines();
517 
518             Entry entry = parseEntry( ( SchemaManager ) null );
519 
520             if ( LOG.isDebugEnabled() )
521             {
522                 if ( entry == null )
523                 {
524                     LOG.debug( "Parsed no entry." );
525                 }
526                 else
527                 {
528                     LOG.debug( "Parsed one entry." );
529                 }
530 
531             }
532 
533             return entry;
534         }
535         catch ( LdapLdifException ne )
536         {
537             LOG.error( I18n.err( I18n.ERR_12008_CANNOT_PARSE_LDIF_BUFFER, ne.getLocalizedMessage() ) );
538             throw new LdapLdifException( I18n.err( I18n.ERR_12009_ERROR_PARSING_LDIF_BUFFER ), ne );
539         }
540         finally
541         {
542             try
543             {
544                 reader.close();
545             }
546             catch ( IOException ioe )
547             {
548                 throw new LdapLdifException( I18n.err( I18n.ERR_12024_CANNOT_CLOSE_FILE ), ioe );
549             }
550         }
551     }
552 
553 
554     /**
555      * A method which parses a ldif string and returns an Entry.
556      *
557      * @param schemaManager The SchemaManager
558      * @param ldif The ldif string
559      * @return An entry
560      * @throws LdapLdifException If something went wrong
561      */
562     public Entry parseEntry( SchemaManager schemaManager, String ldif ) throws LdapLdifException
563     {
564         lines = new ArrayList<String>();
565         position = 0;
566 
567         LOG.debug( "Starts parsing ldif buffer" );
568 
569         if ( Strings.isEmpty( ldif ) )
570         {
571             return new DefaultEntry( schemaManager );
572         }
573 
574         StringReader strIn = new StringReader( ldif );
575         reader = new BufferedReader( strIn );
576 
577         try
578         {
579             readLines();
580 
581             Entry entry = parseEntry( schemaManager );
582 
583             if ( LOG.isDebugEnabled() )
584             {
585                 if ( entry == null )
586                 {
587                     LOG.debug( "Parsed no entry." );
588                 }
589                 else
590                 {
591                     LOG.debug( "Parsed one entry." );
592                 }
593 
594             }
595 
596             return entry;
597         }
598         catch ( LdapLdifException ne )
599         {
600             LOG.error( I18n.err( I18n.ERR_12008_CANNOT_PARSE_LDIF_BUFFER, ne.getLocalizedMessage() ) );
601             throw new LdapLdifException( I18n.err( I18n.ERR_12009_ERROR_PARSING_LDIF_BUFFER ), ne );
602         }
603         finally
604         {
605             try
606             {
607                 reader.close();
608             }
609             catch ( IOException ioe )
610             {
611                 throw new LdapLdifException( I18n.err( I18n.ERR_12024_CANNOT_CLOSE_FILE ), ioe );
612             }
613         }
614     }
615 }