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