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 21 22 package org.apache.james.util.io; 23 24 import java.io.BufferedInputStream; 25 import java.io.ByteArrayInputStream; 26 import java.io.ByteArrayOutputStream; 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.io.InputStreamReader; 30 import java.io.OutputStream; 31 import java.io.OutputStreamWriter; 32 import java.io.Reader; 33 import java.io.StringReader; 34 import java.io.StringWriter; 35 import java.io.Writer; 36 import java.net.Socket; 37 38 /** 39 * General IO Stream manipulation. 40 * <p> 41 * This class provides static utility methods for input/output operations, particularly buffered 42 * copying between sources (<code>InputStream</code>, <code>Reader</code>, <code>String</code> and 43 * <code>byte[]</code>) and destinations (<code>OutputStream</code>, <code>Writer</code>, 44 * <code>String</code> and <code>byte[]</code>). 45 * </p> 46 * 47 * <p>Unless otherwise noted, these <code>copy</code> methods do <em>not</em> flush or close the 48 * streams. Often, doing so would require making non-portable assumptions about the streams' origin 49 * and further use. This means that both streams' <code>close()</code> methods must be called after 50 * copying. if one omits this step, then the stream resources (sockets, file descriptors) are 51 * released when the associated Stream is garbage-collected. It is not a good idea to rely on this 52 * mechanism. For a good overview of the distinction between "memory management" and "resource 53 * management", see <a href="http://www.unixreview.com/articles/1998/9804/9804ja/ja.htm">this 54 * UnixReview article</a></p> 55 * 56 * <p>For each <code>copy</code> method, a variant is provided that allows the caller to specify the 57 * buffer size (the default is 4k). As the buffer size can have a fairly large impact on speed, this 58 * may be worth tweaking. Often "large buffer -> faster" does not hold, even for large data 59 * transfers.</p> 60 * 61 * <p>For byte-to-char methods, a <code>copy</code> variant allows the encoding to be selected 62 * (otherwise the platform default is used).</p> 63 * 64 * <p>The <code>copy</code> methods use an internal buffer when copying. It is therefore advisable 65 * <em>not</em> to deliberately wrap the stream arguments to the <code>copy</code> methods in 66 * <code>Buffered*</code> streams. For example, don't do the 67 * following:</p> 68 * 69 * <code>copy( new BufferedInputStream( in ), new BufferedOutputStream( out ) );</code> 70 * 71 * <p>The rationale is as follows:</p> 72 * 73 * <p>Imagine that an InputStream's read() is a very expensive operation, which would usually suggest 74 * wrapping in a BufferedInputStream. The BufferedInputStream works by issuing infrequent 75 * {@link java.io.InputStream#read(byte[] b, int off, int len)} requests on the underlying InputStream, to 76 * fill an internal buffer, from which further <code>read</code> requests can inexpensively get 77 * their data (until the buffer runs out).</p> 78 * <p>However, the <code>copy</code> methods do the same thing, keeping an internal buffer, 79 * populated by {@link InputStream#read(byte[] b, int off, int len)} requests. Having two buffers 80 * (or three if the destination stream is also buffered) is pointless, and the unnecessary buffer 81 * management hurts performance slightly (about 3%, according to some simple experiments).</p> 82 * 83 * @version CVS $Revision: 516973 $ $Date: 2007-03-11 17:57:46 +0000 (Sun, 11 Mar 2007) $ 84 */ 85 86 /* 87 * Behold, intrepid explorers; a map of this class: 88 * 89 * Method Input Output Dependency 90 * ------ ----- ------ ------- 91 * 1 copy InputStream OutputStream (primitive) 92 * 2 copy Reader Writer (primitive) 93 * 94 * 3 copy InputStream Writer 2 95 * 4 toString InputStream String 3 96 * 5 toByteArray InputStream byte[] 1 97 * 98 * 6 copy Reader OutputStream 2 99 * 7 toString Reader String 2 100 * 8 toByteArray Reader byte[] 6 101 * 102 * 9 copy String OutputStream 2 103 * 10 copy String Writer (trivial) 104 * 11 toByteArray String byte[] 9 105 * 106 * 12 copy byte[] Writer 3 107 * 13 toString byte[] String 12 108 * 14 copy byte[] OutputStream (trivial) 109 * 110 * 111 * Note that only the first two methods shuffle bytes; the rest use these two, or (if possible) copy 112 * using native Java copy methods. As there are method variants to specify buffer size and encoding, 113 * each row may correspond to up to 4 methods. 114 * 115 */ 116 117 public final class IOUtil 118 { 119 private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; 120 121 /** 122 * Private constructor to prevent instantiation. 123 */ 124 private IOUtil() 125 { 126 } 127 128 /** 129 * Unconditionally close an <code>Reader</code>. 130 * Equivalent to {@link Reader#close()}, except any exceptions will be ignored. 131 * 132 * @param input A (possibly null) Reader 133 */ 134 public static void shutdownReader( final Reader input ) 135 { 136 if( null == input ) 137 { 138 return; 139 } 140 141 try 142 { 143 input.close(); 144 } 145 catch( final IOException ioe ) 146 { 147 } 148 } 149 150 /** 151 * Unconditionally close an <code>Writer</code>. 152 * Equivalent to {@link Writer#close()}, except any exceptions will be ignored. 153 * 154 * @param output A (possibly null) Writer 155 */ 156 public static void shutdownWriter( final Writer output ) 157 { 158 if( null == output ) 159 { 160 return; 161 } 162 163 try 164 { 165 output.close(); 166 } 167 catch( final IOException ioe ) 168 { 169 } 170 } 171 172 /** 173 * Unconditionally close an <code>OutputStream</code>. 174 * Equivalent to {@link OutputStream#close()}, except any exceptions will be ignored. 175 * @param output A (possibly null) OutputStream 176 */ 177 public static void shutdownStream( final OutputStream output ) 178 { 179 if( null == output ) 180 { 181 return; 182 } 183 184 try 185 { 186 output.close(); 187 } 188 catch( final IOException ioe ) 189 { 190 } 191 } 192 193 /** 194 * Unconditionally close an <code>InputStream</code>. 195 * Equivalent to {@link InputStream#close()}, except any exceptions will be ignored. 196 * @param input A (possibly null) InputStream 197 */ 198 public static void shutdownStream( final InputStream input ) 199 { 200 if( null == input ) 201 { 202 return; 203 } 204 205 try 206 { 207 input.close(); 208 } 209 catch( final IOException ioe ) 210 { 211 } 212 } 213 214 /** 215 * Unconditionally close an <code>Socket</code>. 216 * Equivalent to {@link Socket#close()}, except any exceptions will be ignored. 217 * @param socket A (possibly null) Socket 218 */ 219 public static void shutdownSocket( final Socket socket ) { 220 if( null == socket ) { 221 return; 222 } 223 224 try { 225 socket.close(); 226 } catch( final IOException ioe ){ 227 } 228 } 229 230 /////////////////////////////////////////////////////////////// 231 // Core copy methods 232 /////////////////////////////////////////////////////////////// 233 234 /** 235 * Copy bytes from an <code>InputStream</code> to an <code>OutputStream</code>. 236 */ 237 public static void copy( final InputStream input, final OutputStream output ) 238 throws IOException 239 { 240 copy( input, output, DEFAULT_BUFFER_SIZE ); 241 } 242 243 /** 244 * Copy bytes from an <code>InputStream</code> to an <code>OutputStream</code>. 245 * @param bufferSize Size of internal buffer to use. 246 */ 247 public static void copy( final InputStream input, 248 final OutputStream output, 249 final int bufferSize ) 250 throws IOException 251 { 252 final byte[] buffer = new byte[ bufferSize ]; 253 int n = 0; 254 while( -1 != ( n = input.read( buffer ) ) ) 255 { 256 output.write( buffer, 0, n ); 257 } 258 } 259 260 /** 261 * Copy chars from a <code>Reader</code> to a <code>Writer</code>. 262 */ 263 public static void copy( final Reader input, final Writer output ) 264 throws IOException 265 { 266 copy( input, output, DEFAULT_BUFFER_SIZE ); 267 } 268 269 /** 270 * Copy chars from a <code>Reader</code> to a <code>Writer</code>. 271 * @param bufferSize Size of internal buffer to use. 272 */ 273 public static void copy( final Reader input, final Writer output, final int bufferSize ) 274 throws IOException 275 { 276 final char[] buffer = new char[ bufferSize ]; 277 int n = 0; 278 while( -1 != ( n = input.read( buffer ) ) ) 279 { 280 output.write( buffer, 0, n ); 281 } 282 } 283 284 /////////////////////////////////////////////////////////////// 285 // Derived copy methods 286 // InputStream -> * 287 /////////////////////////////////////////////////////////////// 288 289 290 /////////////////////////////////////////////////////////////// 291 // InputStream -> Writer 292 293 /** 294 * Copy and convert bytes from an <code>InputStream</code> to chars on a 295 * <code>Writer</code>. 296 * The platform's default encoding is used for the byte-to-char conversion. 297 */ 298 public static void copy( final InputStream input, final Writer output ) 299 throws IOException 300 { 301 copy( input, output, DEFAULT_BUFFER_SIZE ); 302 } 303 304 /** 305 * Copy and convert bytes from an <code>InputStream</code> to chars on a 306 * <code>Writer</code>. 307 * The platform's default encoding is used for the byte-to-char conversion. 308 * @param bufferSize Size of internal buffer to use. 309 */ 310 public static void copy( final InputStream input, final Writer output, final int bufferSize ) 311 throws IOException 312 { 313 final InputStreamReader in = new InputStreamReader( input ); 314 copy( in, output, bufferSize ); 315 } 316 317 /** 318 * Copy and convert bytes from an <code>InputStream</code> to chars on a 319 * <code>Writer</code>, using the specified encoding. 320 * @param encoding The name of a supported character encoding. See the 321 * <a href="http://www.iana.org/assignments/character-sets">IANA 322 * Charset Registry</a> for a list of valid encoding types. 323 */ 324 public static void copy( final InputStream input, final Writer output, final String encoding ) 325 throws IOException 326 { 327 final InputStreamReader in = new InputStreamReader( input, encoding ); 328 copy( in, output ); 329 } 330 331 /** 332 * Copy and convert bytes from an <code>InputStream</code> to chars on a 333 * <code>Writer</code>, using the specified encoding. 334 * @param encoding The name of a supported character encoding. See the 335 * <a href="http://www.iana.org/assignments/character-sets">IANA 336 * Charset Registry</a> for a list of valid encoding types. 337 * @param bufferSize Size of internal buffer to use. 338 */ 339 public static void copy( final InputStream input, 340 final Writer output, 341 final String encoding, 342 final int bufferSize ) 343 throws IOException 344 { 345 final InputStreamReader in = new InputStreamReader( input, encoding ); 346 copy( in, output, bufferSize ); 347 } 348 349 350 /////////////////////////////////////////////////////////////// 351 // InputStream -> String 352 353 /** 354 * Get the contents of an <code>InputStream</code> as a String. 355 * The platform's default encoding is used for the byte-to-char conversion. 356 */ 357 public static String toString( final InputStream input ) 358 throws IOException 359 { 360 return toString( input, DEFAULT_BUFFER_SIZE ); 361 } 362 363 /** 364 * Get the contents of an <code>InputStream</code> as a String. 365 * The platform's default encoding is used for the byte-to-char conversion. 366 * @param bufferSize Size of internal buffer to use. 367 */ 368 public static String toString( final InputStream input, final int bufferSize ) 369 throws IOException 370 { 371 final StringWriter sw = new StringWriter(); 372 copy( input, sw, bufferSize ); 373 return sw.toString(); 374 } 375 376 /** 377 * Get the contents of an <code>InputStream</code> as a String. 378 * @param encoding The name of a supported character encoding. See the 379 * <a href="http://www.iana.org/assignments/character-sets">IANA 380 * Charset Registry</a> for a list of valid encoding types. 381 */ 382 public static String toString( final InputStream input, final String encoding ) 383 throws IOException 384 { 385 return toString( input, encoding, DEFAULT_BUFFER_SIZE ); 386 } 387 388 /** 389 * Get the contents of an <code>InputStream</code> as a String. 390 * @param encoding The name of a supported character encoding. See the 391 * <a href="http://www.iana.org/assignments/character-sets">IANA 392 * Charset Registry</a> for a list of valid encoding types. 393 * @param bufferSize Size of internal buffer to use. 394 */ 395 public static String toString( final InputStream input, 396 final String encoding, 397 final int bufferSize ) 398 throws IOException 399 { 400 final StringWriter sw = new StringWriter(); 401 copy( input, sw, encoding, bufferSize ); 402 return sw.toString(); 403 } 404 405 /////////////////////////////////////////////////////////////// 406 // InputStream -> byte[] 407 408 /** 409 * Get the contents of an <code>InputStream</code> as a <code>byte[]</code>. 410 */ 411 public static byte[] toByteArray( final InputStream input ) 412 throws IOException 413 { 414 return toByteArray( input, DEFAULT_BUFFER_SIZE ); 415 } 416 417 /** 418 * Get the contents of an <code>InputStream</code> as a <code>byte[]</code>. 419 * @param bufferSize Size of internal buffer to use. 420 */ 421 public static byte[] toByteArray( final InputStream input, final int bufferSize ) 422 throws IOException 423 { 424 final ByteArrayOutputStream output = new ByteArrayOutputStream(); 425 copy( input, output, bufferSize ); 426 return output.toByteArray(); 427 } 428 429 430 /////////////////////////////////////////////////////////////// 431 // Derived copy methods 432 // Reader -> * 433 /////////////////////////////////////////////////////////////// 434 435 /////////////////////////////////////////////////////////////// 436 // Reader -> OutputStream 437 /** 438 * Serialize chars from a <code>Reader</code> to bytes on an <code>OutputStream</code>, and 439 * flush the <code>OutputStream</code>. 440 */ 441 public static void copy( final Reader input, final OutputStream output ) 442 throws IOException 443 { 444 copy( input, output, DEFAULT_BUFFER_SIZE ); 445 } 446 447 /** 448 * Serialize chars from a <code>Reader</code> to bytes on an <code>OutputStream</code>, and 449 * flush the <code>OutputStream</code>. 450 * @param bufferSize Size of internal buffer to use. 451 */ 452 public static void copy( final Reader input, final OutputStream output, final int bufferSize ) 453 throws IOException 454 { 455 final OutputStreamWriter out = new OutputStreamWriter( output ); 456 copy( input, out, bufferSize ); 457 // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush 458 // here. 459 out.flush(); 460 } 461 462 /////////////////////////////////////////////////////////////// 463 // Reader -> String 464 /** 465 * Get the contents of a <code>Reader</code> as a String. 466 */ 467 public static String toString( final Reader input ) 468 throws IOException 469 { 470 return toString( input, DEFAULT_BUFFER_SIZE ); 471 } 472 473 /** 474 * Get the contents of a <code>Reader</code> as a String. 475 * @param bufferSize Size of internal buffer to use. 476 */ 477 public static String toString( final Reader input, final int bufferSize ) 478 throws IOException 479 { 480 final StringWriter sw = new StringWriter(); 481 copy( input, sw, bufferSize ); 482 return sw.toString(); 483 } 484 485 486 /////////////////////////////////////////////////////////////// 487 // Reader -> byte[] 488 /** 489 * Get the contents of a <code>Reader</code> as a <code>byte[]</code>. 490 */ 491 public static byte[] toByteArray( final Reader input ) 492 throws IOException 493 { 494 return toByteArray( input, DEFAULT_BUFFER_SIZE ); 495 } 496 497 /** 498 * Get the contents of a <code>Reader</code> as a <code>byte[]</code>. 499 * @param bufferSize Size of internal buffer to use. 500 */ 501 public static byte[] toByteArray( final Reader input, final int bufferSize ) 502 throws IOException 503 { 504 ByteArrayOutputStream output = new ByteArrayOutputStream(); 505 copy( input, output, bufferSize ); 506 return output.toByteArray(); 507 } 508 509 510 /////////////////////////////////////////////////////////////// 511 // Derived copy methods 512 // String -> * 513 /////////////////////////////////////////////////////////////// 514 515 516 /////////////////////////////////////////////////////////////// 517 // String -> OutputStream 518 519 /** 520 * Serialize chars from a <code>String</code> to bytes on an <code>OutputStream</code>, and 521 * flush the <code>OutputStream</code>. 522 */ 523 public static void copy( final String input, final OutputStream output ) 524 throws IOException 525 { 526 copy( input, output, DEFAULT_BUFFER_SIZE ); 527 } 528 529 /** 530 * Serialize chars from a <code>String</code> to bytes on an <code>OutputStream</code>, and 531 * flush the <code>OutputStream</code>. 532 * @param bufferSize Size of internal buffer to use. 533 */ 534 public static void copy( final String input, final OutputStream output, final int bufferSize ) 535 throws IOException 536 { 537 final StringReader in = new StringReader( input ); 538 final OutputStreamWriter out = new OutputStreamWriter( output ); 539 copy( in, out, bufferSize ); 540 // NOTE: Unless anyone is planning on rewriting OutputStreamWriter, we have to flush 541 // here. 542 out.flush(); 543 } 544 545 546 547 /////////////////////////////////////////////////////////////// 548 // String -> Writer 549 550 /** 551 * Copy chars from a <code>String</code> to a <code>Writer</code>. 552 */ 553 public static void copy( final String input, final Writer output ) 554 throws IOException 555 { 556 output.write( input ); 557 } 558 559 /////////////////////////////////////////////////////////////// 560 // String -> byte[] 561 /** 562 * Get the contents of a <code>String</code> as a <code>byte[]</code>. 563 */ 564 public static byte[] toByteArray( final String input ) 565 throws IOException 566 { 567 return toByteArray( input, DEFAULT_BUFFER_SIZE ); 568 } 569 570 /** 571 * Get the contents of a <code>String</code> as a <code>byte[]</code>. 572 * @param bufferSize Size of internal buffer to use. 573 */ 574 public static byte[] toByteArray( final String input, final int bufferSize ) 575 throws IOException 576 { 577 ByteArrayOutputStream output = new ByteArrayOutputStream(); 578 copy( input, output, bufferSize ); 579 return output.toByteArray(); 580 } 581 582 583 584 /////////////////////////////////////////////////////////////// 585 // Derived copy methods 586 // byte[] -> * 587 /////////////////////////////////////////////////////////////// 588 589 590 /////////////////////////////////////////////////////////////// 591 // byte[] -> Writer 592 593 /** 594 * Copy and convert bytes from a <code>byte[]</code> to chars on a 595 * <code>Writer</code>. 596 * The platform's default encoding is used for the byte-to-char conversion. 597 */ 598 public static void copy( final byte[] input, final Writer output ) 599 throws IOException 600 { 601 copy( input, output, DEFAULT_BUFFER_SIZE ); 602 } 603 604 /** 605 * Copy and convert bytes from a <code>byte[]</code> to chars on a 606 * <code>Writer</code>. 607 * The platform's default encoding is used for the byte-to-char conversion. 608 * @param bufferSize Size of internal buffer to use. 609 */ 610 public static void copy( final byte[] input, final Writer output, final int bufferSize ) 611 throws IOException 612 { 613 final ByteArrayInputStream in = new ByteArrayInputStream( input ); 614 copy( in, output, bufferSize ); 615 } 616 617 /** 618 * Copy and convert bytes from a <code>byte[]</code> to chars on a 619 * <code>Writer</code>, using the specified encoding. 620 * @param encoding The name of a supported character encoding. See the 621 * <a href="http://www.iana.org/assignments/character-sets">IANA 622 * Charset Registry</a> for a list of valid encoding types. 623 */ 624 public static void copy( final byte[] input, final Writer output, final String encoding ) 625 throws IOException 626 { 627 final ByteArrayInputStream in = new ByteArrayInputStream( input ); 628 copy( in, output, encoding ); 629 } 630 631 /** 632 * Copy and convert bytes from a <code>byte[]</code> to chars on a 633 * <code>Writer</code>, using the specified encoding. 634 * @param encoding The name of a supported character encoding. See the 635 * <a href="http://www.iana.org/assignments/character-sets">IANA 636 * Charset Registry</a> for a list of valid encoding types. 637 * @param bufferSize Size of internal buffer to use. 638 */ 639 public static void copy( final byte[] input, 640 final Writer output, 641 final String encoding, 642 final int bufferSize ) 643 throws IOException 644 { 645 final ByteArrayInputStream in = new ByteArrayInputStream( input ); 646 copy( in, output, encoding, bufferSize ); 647 } 648 649 650 /////////////////////////////////////////////////////////////// 651 // byte[] -> String 652 653 /** 654 * Get the contents of a <code>byte[]</code> as a String. 655 * The platform's default encoding is used for the byte-to-char conversion. 656 */ 657 public static String toString( final byte[] input ) 658 throws IOException 659 { 660 return toString( input, DEFAULT_BUFFER_SIZE ); 661 } 662 663 /** 664 * Get the contents of a <code>byte[]</code> as a String. 665 * The platform's default encoding is used for the byte-to-char conversion. 666 * @param bufferSize Size of internal buffer to use. 667 */ 668 public static String toString( final byte[] input, final int bufferSize ) 669 throws IOException 670 { 671 final StringWriter sw = new StringWriter(); 672 copy( input, sw, bufferSize ); 673 return sw.toString(); 674 } 675 676 /** 677 * Get the contents of a <code>byte[]</code> as a String. 678 * @param encoding The name of a supported character encoding. See the 679 * <a href="http://www.iana.org/assignments/character-sets">IANA 680 * Charset Registry</a> for a list of valid encoding types. 681 */ 682 public static String toString( final byte[] input, final String encoding ) 683 throws IOException 684 { 685 return toString( input, encoding, DEFAULT_BUFFER_SIZE ); 686 } 687 688 /** 689 * Get the contents of a <code>byte[]</code> as a String. 690 * @param encoding The name of a supported character encoding. See the 691 * <a href="http://www.iana.org/assignments/character-sets">IANA 692 * Charset Registry</a> for a list of valid encoding types. 693 * @param bufferSize Size of internal buffer to use. 694 */ 695 public static String toString( final byte[] input, 696 final String encoding, 697 final int bufferSize ) 698 throws IOException 699 { 700 final StringWriter sw = new StringWriter(); 701 copy( input, sw, encoding, bufferSize ); 702 return sw.toString(); 703 } 704 705 706 /////////////////////////////////////////////////////////////// 707 // byte[] -> OutputStream 708 709 /** 710 * Copy bytes from a <code>byte[]</code> to an <code>OutputStream</code>. 711 */ 712 public static void copy( final byte[] input, final OutputStream output ) 713 throws IOException 714 { 715 copy( input, output, DEFAULT_BUFFER_SIZE ); 716 } 717 718 /** 719 * Copy bytes from a <code>byte[]</code> to an <code>OutputStream</code>. 720 * @param bufferSize Size of internal buffer to use. 721 */ 722 public static void copy( final byte[] input, 723 final OutputStream output, 724 final int bufferSize ) 725 throws IOException 726 { 727 output.write( input ); 728 } 729 730 /** 731 * Compare the contents of two Streams to determine if they are equal or not. 732 * 733 * @param input1 the first stream 734 * @param input2 the second stream 735 * @return true if the content of the streams are equal or they both don't exist, false otherwise 736 */ 737 public static boolean contentEquals( final InputStream input1, 738 final InputStream input2 ) 739 throws IOException 740 { 741 final InputStream bufferedInput1 = new BufferedInputStream( input1 ); 742 final InputStream bufferedInput2 = new BufferedInputStream( input2 ); 743 744 int ch = bufferedInput1.read(); 745 while( -1 != ch ) 746 { 747 final int ch2 = bufferedInput2.read(); 748 if( ch != ch2 ) 749 { 750 return false; 751 } 752 ch = bufferedInput1.read(); 753 } 754 755 final int ch2 = bufferedInput2.read(); 756 if( -1 != ch2 ) 757 { 758 return false; 759 } 760 else 761 { 762 return true; 763 } 764 } 765 }