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 ( "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 }