001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2009 Sun Microsystems, Inc. 015 * Portions Copyright 2013-2016 ForgeRock AS. 016 */ 017package org.opends.server.util; 018 019import java.io.BufferedWriter; 020import java.io.Closeable; 021import java.io.IOException; 022import java.util.Collection; 023import java.util.Iterator; 024import java.util.List; 025import java.util.regex.Pattern; 026 027import org.forgerock.i18n.LocalizableMessage; 028import org.forgerock.opendj.ldap.ByteSequence; 029import org.forgerock.opendj.ldap.ByteString; 030import org.forgerock.opendj.ldap.DN; 031import org.forgerock.opendj.ldap.RDN; 032import org.forgerock.opendj.ldap.schema.AttributeType; 033import org.opends.server.tools.makeldif.TemplateEntry; 034import org.opends.server.types.Attribute; 035import org.opends.server.types.Entry; 036import org.opends.server.types.LDIFExportConfig; 037import org.opends.server.types.Modification; 038import org.opends.server.types.RawAttribute; 039import org.opends.server.types.RawModification; 040 041import static org.forgerock.util.Reject.*; 042import static org.opends.server.util.StaticUtils.*; 043 044/** 045 * This class provides a mechanism for writing entries in LDIF form to a file or 046 * an output stream. 047 */ 048@org.opends.server.types.PublicAPI( 049 stability=org.opends.server.types.StabilityLevel.UNCOMMITTED, 050 mayInstantiate=true, 051 mayExtend=false, 052 mayInvoke=true) 053public final class LDIFWriter implements Closeable 054{ 055 // FIXME -- Add support for generating a hash when writing the data. 056 // FIXME -- Add support for signing the hash that is generated. 057 058 /** The writer to which the LDIF information will be written. */ 059 private BufferedWriter writer; 060 061 /** The configuration to use for the export. */ 062 private LDIFExportConfig exportConfig; 063 064 /** Regular expression used for splitting comments on line-breaks. */ 065 private static final Pattern SPLIT_NEWLINE = Pattern.compile("\\r?\\n"); 066 067 068 069 /** 070 * Creates a new LDIF writer with the provided configuration. 071 * 072 * @param exportConfig The configuration to use for the export. It must not 073 * be <CODE>null</CODE>. 074 * 075 * @throws IOException If a problem occurs while opening the writer. 076 */ 077 public LDIFWriter(LDIFExportConfig exportConfig) 078 throws IOException 079 { 080 ifNull(exportConfig); 081 this.exportConfig = exportConfig; 082 083 writer = exportConfig.getWriter(); 084 } 085 086 087 088 /** 089 * Writes the provided comment to the LDIF file, optionally wrapping near the 090 * specified column. Each line will be prefixed by the octothorpe (#) 091 * character followed by a space. If the comment should be wrapped at a 092 * specified column, then it will attempt to do so at the first whitespace 093 * character at or before that column (so it will try not wrap in the middle 094 * of a word). 095 * <BR><BR> 096 * This comment will be ignored by the 097 * Directory Server's LDIF reader, as well as any other compliant LDIF parsing 098 * software. 099 * 100 * @param comment The comment to be written. Any line breaks that it 101 * contains will be honored, and potentially new line 102 * breaks may be introduced by the wrapping process. It 103 * must not be <CODE>null</CODE>. 104 * @param wrapColumn The column at which long lines should be wrapped, or 105 * -1 to indicate that no additional wrapping should be 106 * added. This will override the wrap column setting 107 * specified in the LDIF export configuration. 108 * 109 * @throws IOException If a problem occurs while attempting to write the 110 * comment to the LDIF file. 111 */ 112 public void writeComment(LocalizableMessage comment, int wrapColumn) 113 throws IOException 114 { 115 ifNull(comment); 116 117 118 // First, break up the comment into multiple lines to preserve the original 119 // spacing that it contained. 120 String[] lines = SPLIT_NEWLINE.split(comment); 121 122 // Now iterate through the lines and write them out, prefixing and wrapping 123 // them as necessary. 124 for (String l : lines) 125 { 126 if (wrapColumn <= 0) 127 { 128 writer.write("# "); 129 writer.write(l); 130 writer.newLine(); 131 } 132 else 133 { 134 int breakColumn = wrapColumn - 2; 135 136 if (l.length() <= breakColumn) 137 { 138 writer.write("# "); 139 writer.write(l); 140 writer.newLine(); 141 } 142 else 143 { 144 int startPos = 0; 145outerLoop: 146 while (startPos < l.length()) 147 { 148 if (startPos + breakColumn >= l.length()) 149 { 150 writer.write("# "); 151 writer.write(l.substring(startPos)); 152 writer.newLine(); 153 startPos = l.length(); 154 } 155 else 156 { 157 int endPos = startPos + breakColumn; 158 159 int i=endPos - 1; 160 while (i > startPos) 161 { 162 if (l.charAt(i) == ' ') 163 { 164 writer.write("# "); 165 writer.write(l.substring(startPos, i)); 166 writer.newLine(); 167 168 startPos = i+1; 169 continue outerLoop; 170 } 171 172 i--; 173 } 174 175 // If we've gotten here, then there are no spaces on the entire 176 // line. If that happens, then we'll have to break in the middle 177 // of a word. 178 writer.write("# "); 179 writer.write(l.substring(startPos, endPos)); 180 writer.newLine(); 181 182 startPos = endPos; 183 } 184 } 185 } 186 } 187 } 188 } 189 190 /** 191 * Iterates over each entry contained in the map and writes out the entry in 192 * LDIF format. The main benefit of this method is that the entries can be 193 * sorted by DN and output in sorted order. 194 * 195 * @param entries The Map containing the entries keyed by DN. 196 * 197 * @return <CODE>true</CODE>of all of the entries were 198 * written out, <CODE>false</CODE>if it was not 199 * because of the export configuration. 200 * 201 * @throws IOException If a problem occurs while writing the entry to LDIF. 202 * 203 * @throws LDIFException If a problem occurs while trying to determine 204 * whether to include the entry in the export. 205 */ 206 public boolean writeEntries(Collection<Entry> entries) throws IOException, 207 LDIFException 208 { 209 for (Entry entry : entries) 210 { 211 if (!writeEntry(entry)) 212 { 213 return false; 214 } 215 } 216 return true; 217 } 218 219 220 /** 221 * Writes the provided entry to LDIF. 222 * 223 * @param entry The entry to be written. It must not be <CODE>null</CODE>. 224 * 225 * @return <CODE>true</CODE> if the entry was actually written, or 226 * <CODE>false</CODE> if it was not because of the export 227 * configuration. 228 * 229 * @throws IOException If a problem occurs while writing the entry to LDIF. 230 * 231 * @throws LDIFException If a problem occurs while trying to determine 232 * whether to include the entry in the export. 233 */ 234 public boolean writeEntry(Entry entry) 235 throws IOException, LDIFException 236 { 237 ifNull(entry); 238 return entry.toLDIF(exportConfig); 239 } 240 241 242 /** 243 * Writes the provided template entry to LDIF. 244 * 245 * @param templateEntry The template entry to be written. It must not be 246 * <CODE>null</CODE>. 247 * 248 * @return <CODE>true</CODE> if the entry was actually written, or 249 * <CODE>false</CODE> if it was not because of the export 250 * configuration. 251 * 252 * @throws IOException If a problem occurs while writing the template entry 253 * to LDIF. 254 * 255 * @throws LDIFException If a problem occurs while trying to determine 256 * whether to include the template entry in the 257 * export. 258 */ 259 public boolean writeTemplateEntry(TemplateEntry templateEntry) 260 throws IOException, LDIFException 261 { 262 ifNull(templateEntry); 263 return templateEntry.toLDIF(exportConfig); 264 } 265 266 /** 267 * Writes a change record entry for the provided change record. 268 * 269 * @param changeRecord The change record entry to be written. 270 * 271 * @throws IOException If a problem occurs while writing the change record. 272 */ 273 public void writeChangeRecord(ChangeRecordEntry changeRecord) 274 throws IOException 275 { 276 ifNull(changeRecord); 277 278 279 // Get the information necessary to write the LDIF. 280 BufferedWriter writer = exportConfig.getWriter(); 281 int wrapColumn = exportConfig.getWrapColumn(); 282 boolean wrapLines = wrapColumn > 1; 283 284 285 // First, write the DN. 286 writeDN("dn", changeRecord.getDN(), writer, wrapLines, wrapColumn); 287 288 289 // Figure out what type of change it is and act accordingly. 290 if (changeRecord instanceof AddChangeRecordEntry) 291 { 292 StringBuilder changeTypeLine = new StringBuilder("changetype: add"); 293 writeLDIFLine(changeTypeLine, writer, wrapLines, wrapColumn); 294 295 AddChangeRecordEntry addRecord = (AddChangeRecordEntry) changeRecord; 296 for (Attribute a : addRecord.getAttributes()) 297 { 298 for (ByteString v : a) 299 { 300 final String attrName = a.getAttributeDescription().toString(); 301 writeAttribute(attrName, v, writer, wrapLines, wrapColumn); 302 } 303 } 304 } 305 else if (changeRecord instanceof DeleteChangeRecordEntry) 306 { 307 StringBuilder changeTypeLine = new StringBuilder("changetype: delete"); 308 writeLDIFLine(changeTypeLine, writer, wrapLines, wrapColumn); 309 } 310 else if (changeRecord instanceof ModifyChangeRecordEntry) 311 { 312 StringBuilder changeTypeLine = new StringBuilder("changetype: modify"); 313 writeLDIFLine(changeTypeLine, writer, wrapLines, wrapColumn); 314 315 ModifyChangeRecordEntry modifyRecord = 316 (ModifyChangeRecordEntry) changeRecord; 317 List<RawModification> mods = modifyRecord.getModifications(); 318 Iterator<RawModification> iterator = mods.iterator(); 319 while (iterator.hasNext()) 320 { 321 RawModification m = iterator.next(); 322 RawAttribute a = m.getAttribute(); 323 String attrName = a.getAttributeType(); 324 StringBuilder modTypeLine = new StringBuilder(); 325 modTypeLine.append(m.getModificationType()); 326 modTypeLine.append(": "); 327 modTypeLine.append(attrName); 328 writeLDIFLine(modTypeLine, writer, wrapLines, wrapColumn); 329 330 for (ByteString s : a.getValues()) 331 { 332 StringBuilder valueLine = new StringBuilder(attrName); 333 String stringValue = s.toString(); 334 335 if (needsBase64Encoding(stringValue)) 336 { 337 valueLine.append(":: "); 338 valueLine.append(Base64.encode(s)); 339 } 340 else 341 { 342 valueLine.append(": "); 343 valueLine.append(stringValue); 344 } 345 346 writeLDIFLine(valueLine, writer, wrapLines, wrapColumn); 347 } 348 349 if (iterator.hasNext()) 350 { 351 StringBuilder dashLine = new StringBuilder("-"); 352 writeLDIFLine(dashLine, writer, wrapLines, wrapColumn); 353 } 354 } 355 } 356 else if (changeRecord instanceof ModifyDNChangeRecordEntry) 357 { 358 StringBuilder changeTypeLine = new StringBuilder("changetype: moddn"); 359 writeLDIFLine(changeTypeLine, writer, wrapLines, wrapColumn); 360 361 ModifyDNChangeRecordEntry modifyDNRecord = 362 (ModifyDNChangeRecordEntry) changeRecord; 363 364 StringBuilder newRDNLine = new StringBuilder("newrdn: "); 365 newRDNLine.append(modifyDNRecord.getNewRDN()); 366 writeLDIFLine(newRDNLine, writer, wrapLines, wrapColumn); 367 368 StringBuilder deleteOldRDNLine = new StringBuilder("deleteoldrdn: "); 369 deleteOldRDNLine.append(modifyDNRecord.deleteOldRDN() ? "1" : "0"); 370 writeLDIFLine(deleteOldRDNLine, writer, wrapLines, wrapColumn); 371 372 DN newSuperiorDN = modifyDNRecord.getNewSuperiorDN(); 373 if (newSuperiorDN != null) 374 { 375 StringBuilder newSuperiorLine = new StringBuilder("newsuperior: "); 376 newSuperiorLine.append(newSuperiorDN); 377 writeLDIFLine(newSuperiorLine, writer, wrapLines, wrapColumn); 378 } 379 } 380 381 382 // Make sure there is a blank line after the entry. 383 writer.newLine(); 384 } 385 386 387 388 /** 389 * Writes an add change record for the provided entry. No filtering will be 390 * performed for this entry, nor will any export plugins be invoked. Further, 391 * only the user attributes will be included. 392 * 393 * @param entry The entry to include in the add change record. It must not 394 * be <CODE>null</CODE>. 395 * 396 * @throws IOException If a problem occurs while writing the add record. 397 */ 398 public void writeAddChangeRecord(Entry entry) 399 throws IOException 400 { 401 ifNull(entry); 402 403 404 // Get the information necessary to write the LDIF. 405 BufferedWriter writer = exportConfig.getWriter(); 406 int wrapColumn = exportConfig.getWrapColumn(); 407 boolean wrapLines = wrapColumn > 1; 408 409 410 // First, write the DN. 411 writeDN("dn", entry.getName(), writer, wrapLines, wrapColumn); 412 413 414 // Next, the changetype. 415 StringBuilder changeTypeLine = new StringBuilder("changetype: add"); 416 writeLDIFLine(changeTypeLine, writer, wrapLines, wrapColumn); 417 418 419 // Now the objectclasses. 420 for (String s : entry.getObjectClasses().values()) 421 { 422 StringBuilder ocLine = new StringBuilder(); 423 ocLine.append("objectClass: "); 424 ocLine.append(s); 425 writeLDIFLine(ocLine, writer, wrapLines, wrapColumn); 426 } 427 428 429 // Finally, the set of user attributes. 430 for (AttributeType attrType : entry.getUserAttributes().keySet()) 431 { 432 for (Attribute a : entry.getUserAttribute(attrType)) 433 { 434 String attrName = a.getAttributeDescription().toString(); 435 for (ByteString v : a) 436 { 437 writeAttribute(attrName, v, writer, wrapLines, wrapColumn); 438 } 439 } 440 } 441 442 443 // Make sure there is a blank line after the entry. 444 writer.newLine(); 445 } 446 447 448 449 /** 450 * Writes a delete change record for the provided entry, optionally including 451 * a comment with the full entry contents. No filtering will be performed for 452 * this entry, nor will any export plugins be invoked. Further, only the user 453 * attributes will be included. 454 * 455 * @param entry The entry to include in the delete change record. It 456 * must not be <CODE>null</CODE>. 457 * @param commentEntry Indicates whether to include a comment with the 458 * contents of the entry. 459 * 460 * @throws IOException If a problem occurs while writing the delete record. 461 */ 462 public void writeDeleteChangeRecord(Entry entry, boolean commentEntry) 463 throws IOException 464 { 465 ifNull(entry); 466 467 // Get the information necessary to write the LDIF. 468 BufferedWriter writer = exportConfig.getWriter(); 469 int wrapColumn = exportConfig.getWrapColumn(); 470 boolean wrapLines = wrapColumn > 1; 471 472 473 // Add the DN and changetype lines. 474 writeDN("dn", entry.getName(), writer, wrapLines, wrapColumn); 475 476 StringBuilder changeTypeLine = new StringBuilder("changetype: delete"); 477 writeLDIFLine(changeTypeLine, writer, wrapLines, wrapColumn); 478 479 480 // If we should include a comment with the rest of the entry contents, then 481 // do so now. 482 if (commentEntry) 483 { 484 // Write the objectclasses. 485 for (String s : entry.getObjectClasses().values()) 486 { 487 StringBuilder ocLine = new StringBuilder(); 488 ocLine.append("# objectClass: "); 489 ocLine.append(s); 490 writeLDIFLine(ocLine, writer, wrapLines, wrapColumn); 491 } 492 493 // Write the set of user attributes. 494 for (AttributeType attrType : entry.getUserAttributes().keySet()) 495 { 496 for (Attribute a : entry.getUserAttribute(attrType)) 497 { 498 final String attrDesc = a.getAttributeDescription().toString(); 499 final StringBuilder attrName = new StringBuilder(2 + attrDesc.length()); 500 attrName.append("# "); 501 attrName.append(attrDesc); 502 503 for (ByteString v : a) 504 { 505 writeAttribute(attrName, v, writer, wrapLines, wrapColumn); 506 } 507 } 508 } 509 } 510 511 512 // Make sure there is a blank line after the entry. 513 writer.newLine(); 514 } 515 516 517 518 /** 519 * Writes a modify change record with the provided information. No filtering 520 * will be performed, nor will any export plugins be invoked. 521 * 522 * @param dn The DN of the entry being modified. It must not be 523 * <CODE>null</CODE>. 524 * @param modifications The set of modifications to include in the change 525 * record. It must not be <CODE>null</CODE>. 526 * 527 * @throws IOException If a problem occurs while writing the modify record. 528 */ 529 public void writeModifyChangeRecord(DN dn, List<Modification> modifications) 530 throws IOException 531 { 532 ifNull(dn, modifications); 533 534 // If there aren't any modifications, then there's nothing to do. 535 if (modifications.isEmpty()) 536 { 537 return; 538 } 539 540 541 // Get the information necessary to write the LDIF. 542 BufferedWriter writer = exportConfig.getWriter(); 543 int wrapColumn = exportConfig.getWrapColumn(); 544 boolean wrapLines = wrapColumn > 1; 545 546 547 // Write the DN and changetype. 548 writeDN("dn", dn, writer, wrapLines, wrapColumn); 549 550 StringBuilder changeTypeLine = new StringBuilder("changetype: modify"); 551 writeLDIFLine(changeTypeLine, writer, wrapLines, wrapColumn); 552 553 554 // Iterate through the modifications and write them to the LDIF. 555 Iterator<Modification> iterator = modifications.iterator(); 556 while (iterator.hasNext()) 557 { 558 Modification m = iterator.next(); 559 Attribute a = m.getAttribute(); 560 561 String name = a.getAttributeDescription().toString(); 562 563 StringBuilder modTypeLine = new StringBuilder(); 564 modTypeLine.append(m.getModificationType()); 565 modTypeLine.append(": "); 566 modTypeLine.append(name); 567 writeLDIFLine(modTypeLine, writer, wrapLines, wrapColumn); 568 569 for (ByteString v : a) 570 { 571 writeAttribute(name, v, writer, wrapLines, wrapColumn); 572 } 573 574 575 // If this is the last modification, then append blank line. Otherwise 576 // write a line with just a dash. 577 if (iterator.hasNext()) 578 { 579 writer.write("-"); 580 } 581 writer.newLine(); 582 } 583 } 584 585 586 587 /** 588 * Writes a modify DN change record with the provided information. No 589 * filtering will be performed, nor will any export plugins be invoked. 590 * 591 * @param dn The DN of the entry before the rename. It must not 592 * be <CODE>null</CODE>. 593 * @param newRDN The new RDN for the entry. It must not be 594 * <CODE>null</CODE>. 595 * @param deleteOldRDN Indicates whether the old RDN value should be removed 596 * from the entry. 597 * @param newSuperior The new superior DN for the entry, or 598 * <CODE>null</CODE> if the entry will stay below the 599 * same parent. 600 * 601 * @throws IOException If a problem occurs while writing the modify record. 602 */ 603 public void writeModifyDNChangeRecord(DN dn, RDN newRDN, boolean deleteOldRDN, 604 DN newSuperior) 605 throws IOException 606 { 607 ifNull(dn, newRDN); 608 609 610 // Get the information necessary to write the LDIF. 611 BufferedWriter writer = exportConfig.getWriter(); 612 int wrapColumn = exportConfig.getWrapColumn(); 613 boolean wrapLines = wrapColumn > 1; 614 615 616 // Write the current DN. 617 writeDN("dn", dn, writer, wrapLines, wrapColumn); 618 619 620 // Write the changetype. Some older tools may not support the "moddn" 621 // changetype, so only use it if a newSuperior element has been provided, 622 // but use modrdn elsewhere. 623 String changeType = newSuperior == null ? "changetype: modrdn" : "changetype: moddn"; 624 writeLDIFLine(new StringBuilder(changeType), writer, wrapLines, wrapColumn); 625 626 627 // Write the newRDN element. 628 StringBuilder rdnLine = new StringBuilder("newrdn"); 629 appendLDIFSeparatorAndValue(rdnLine, ByteString.valueOfUtf8(newRDN.toString())); 630 writeLDIFLine(rdnLine, writer, wrapLines, wrapColumn); 631 632 633 // Write the deleteOldRDN element. 634 StringBuilder deleteOldRDNLine = new StringBuilder(); 635 deleteOldRDNLine.append("deleteoldrdn: "); 636 deleteOldRDNLine.append(deleteOldRDN ? "1" : "0"); 637 writeLDIFLine(deleteOldRDNLine, writer, wrapLines, wrapColumn); 638 639 if (newSuperior != null) 640 { 641 writeDN("newsuperior", newSuperior, writer, wrapLines, wrapColumn); 642 } 643 644 645 // Make sure there is a blank line after the entry. 646 writer.newLine(); 647 } 648 649 private void writeDN(String attrType, DN dn, BufferedWriter writer, 650 boolean wrapLines, int wrapColumn) throws IOException 651 { 652 final StringBuilder newLine = new StringBuilder(attrType); 653 appendLDIFSeparatorAndValue(newLine, ByteString.valueOfUtf8(dn.toString())); 654 writeLDIFLine(newLine, writer, wrapLines, wrapColumn); 655 } 656 657 private void writeAttribute(CharSequence attrName, ByteString v, 658 BufferedWriter writer, boolean wrapLines, int wrapColumn) 659 throws IOException 660 { 661 StringBuilder newLine = new StringBuilder(attrName); 662 appendLDIFSeparatorAndValue(newLine, v); 663 writeLDIFLine(newLine, writer, wrapLines, wrapColumn); 664 } 665 666 /** 667 * Flushes the data written to the output stream or underlying file. 668 * 669 * @throws IOException If a problem occurs while flushing the output. 670 */ 671 public void flush() 672 throws IOException 673 { 674 writer.flush(); 675 } 676 677 678 679 /** 680 * Closes the LDIF writer and the underlying output stream or file. 681 * 682 * @throws IOException If a problem occurs while closing the writer. 683 */ 684 @Override 685 public void close() 686 throws IOException 687 { 688 writer.flush(); 689 writer.close(); 690 } 691 692 693 694 /** 695 * Appends an LDIF separator and properly-encoded form of the given 696 * value to the provided buffer. If the value is safe to include 697 * as-is, then a single colon, a single space, space, and the 698 * provided value will be appended. Otherwise, two colons, a single 699 * space, and a base64-encoded form of the value will be appended. 700 * 701 * @param buffer The buffer to which the information should be 702 * appended. It must not be <CODE>null</CODE>. 703 * @param valueBytes The value to append to the buffer. It must not be 704 * <CODE>null</CODE>. 705 */ 706 public static void appendLDIFSeparatorAndValue(StringBuilder buffer, 707 ByteSequence valueBytes) 708 { 709 appendLDIFSeparatorAndValue(buffer, valueBytes, false, false); 710 } 711 712 /** 713 * Appends an LDIF separator and properly-encoded form of the given 714 * value to the provided buffer. If the value is safe to include 715 * as-is, then a single colon, a single space, space, and the 716 * provided value will be appended. Otherwise, two colons, a single 717 * space, and a base64-encoded form of the value will be appended. 718 * @param buffer The buffer to which the information should be 719 * appended. It must not be <CODE>null</CODE>. 720 * @param valueBytes The value to append to the buffer. It must not be 721 * <CODE>null</CODE>. 722 * @param isURL Whether the provided value is an URL value or not. 723 * @param isBase64 Whether the provided value is a base 64 value or not. 724 */ 725 public static void appendLDIFSeparatorAndValue(StringBuilder buffer, 726 ByteSequence valueBytes, boolean isURL, boolean isBase64) 727 { 728 ifNull(buffer, valueBytes); 729 730 731 // If the value is empty, then just append a single colon (the URL '<' if 732 // required) and a single space. 733 final boolean valueIsEmpty = valueBytes == null || valueBytes.length() == 0; 734 if (isURL) 735 { 736 buffer.append(":< "); 737 if (!valueIsEmpty) 738 { 739 buffer.append(valueBytes.toString()); 740 } 741 } 742 else if (isBase64) 743 { 744 buffer.append(":: "); 745 if (!valueIsEmpty) 746 { 747 buffer.append(valueBytes.toString()); 748 } 749 } 750 else if (needsBase64Encoding(valueBytes)) 751 { 752 buffer.append(":: "); 753 buffer.append(Base64.encode(valueBytes)); 754 } 755 else 756 { 757 buffer.append(": "); 758 if (!valueIsEmpty) 759 { 760 buffer.append(valueBytes.toString()); 761 } 762 } 763 } 764 765 766 767 /** 768 * Writes the provided line to LDIF using the provided information. 769 * 770 * @param line The line of information to write. It must not be 771 * <CODE>null</CODE>. 772 * @param writer The writer to which the data should be written. It 773 * must not be <CODE>null</CODE>. 774 * @param wrapLines Indicates whether to wrap long lines. 775 * @param wrapColumn The column at which long lines should be wrapped. 776 * 777 * @throws IOException If a problem occurs while writing the information. 778 */ 779 public static void writeLDIFLine(StringBuilder line, BufferedWriter writer, 780 boolean wrapLines, int wrapColumn) 781 throws IOException 782 { 783 ifNull(line, writer); 784 785 int length = line.length(); 786 if (wrapLines && length > wrapColumn) 787 { 788 writer.write(line.substring(0, wrapColumn)); 789 writer.newLine(); 790 791 int pos = wrapColumn; 792 while (pos < length) 793 { 794 int writeLength = Math.min(wrapColumn-1, length-pos); 795 writer.write(' '); 796 writer.write(line.substring(pos, pos+writeLength)); 797 writer.newLine(); 798 799 pos += wrapColumn-1; 800 } 801 } 802 else 803 { 804 writer.write(line.toString()); 805 writer.newLine(); 806 } 807 } 808}