1/* GENERATED SOURCE. DO NOT MODIFY. */ 2// © 2016 and later: Unicode, Inc. and others. 3// License & terms of use: http://www.unicode.org/copyright.html#License 4/* 5 ******************************************************************************* 6 * Copyright (C) 1996-2015, International Business Machines Corporation and * 7 * others. All Rights Reserved. * 8 ******************************************************************************* 9 */ 10package android.icu.dev.test; 11 12import java.lang.reflect.Constructor; 13import java.lang.reflect.Method; 14import java.lang.reflect.Modifier; 15import java.security.Policy; 16import java.util.ArrayList; 17import java.util.Arrays; 18import java.util.List; 19import java.util.Locale; 20import java.util.Map; 21import java.util.Properties; 22import java.util.Random; 23import java.util.TreeMap; 24 25import org.junit.After; 26import org.junit.Assert; 27import org.junit.Before; 28 29import android.icu.util.TimeZone; 30import android.icu.util.ULocale; 31 32/** 33 * TestFmwk is a base class for tests that can be run conveniently from the 34 * command line as well as under the Java test harness. 35 * <p> 36 * Sub-classes implement a set of methods named Test <something>. Each of these 37 * methods performs some test. Test methods should indicate errors by calling 38 * either err or errln. This will increment the errorCount field and may 39 * optionally print a message to the log. Debugging information may also be 40 * added to the log via the log and logln methods. These methods will add their 41 * arguments to the log only if the test is being run in verbose mode. 42 */ 43abstract public class TestFmwk extends AbstractTestLog { 44 /** 45 * The default time zone for all of our tests. Used in @Before 46 */ 47 private final static TimeZone defaultTimeZone = TimeZone.getTimeZone("America/Los_Angeles"); 48 49 /** 50 * The default locale used for all of our tests. Used in @Before 51 */ 52 private final static Locale defaultLocale = Locale.US; 53 54 private static final String EXHAUSTIVENESS = "ICU.exhaustive"; 55 private static final int DEFAULT_EXHAUSTIVENESS = 0; 56 private static final int MAX_EXHAUSTIVENESS = 10; 57 58 private static final String LOGGING_LEVEL = "ICU.logging"; 59 private static final int DEFAULT_LOGGING_LEVEL = 0; 60 private static final int MAX_LOGGING_LEVEL = 3; 61 62 public static final int LOGGING_NONE = 0; 63 public static final int LOGGING_WARN = 1; 64 public static final int LOGGING_INFO = 2; 65 public static final int LOGGING_DEBUG = 3; 66 67 private static final String SEED = "ICU.seed"; 68 private static final String SECURITY_POLICY = "ICU.securitypolicy"; 69 70 private static final TestParams testParams; 71 static { 72 testParams = TestParams.create(); 73 } 74 75 protected TestFmwk() { 76 } 77 78 @Before 79 public void testInitialize() { 80 Locale.setDefault(defaultLocale); 81 TimeZone.setDefault(defaultTimeZone); 82 83 if (getParams().testSecurityManager != null) { 84 System.setSecurityManager(getParams().testSecurityManager); 85 } 86 } 87 88 @After 89 public void testTeardown() { 90 if (getParams().testSecurityManager != null) { 91 System.setSecurityManager(getParams().originalSecurityManager); 92 } 93 } 94 95 private static TestParams getParams() { 96 //return paramsReference.get(); 97 return testParams; 98 } 99 100 protected static boolean isVerbose() { 101 return getParams().getLoggingLevel() >= LOGGING_INFO; 102 } 103 104 /** 105 * 0 = fewest tests, 5 is normal build, 10 is most tests 106 */ 107 protected static int getExhaustiveness() { 108 return getParams().inclusion; 109 } 110 111 protected static boolean isQuick() { 112 return getParams().getInclusion() == 0; 113 } 114 115 // use this instead of new random so we get a consistent seed 116 // for our tests 117 protected Random createRandom() { 118 return new Random(getParams().getSeed()); 119 } 120 121 /** 122 * Integer Random number generator, produces positive int values. 123 * Similar to C++ std::minstd_rand, with the same algorithm & constants. 124 * Provided for compatibility with ICU4C. 125 * Get & set of the seed allows for reproducible monkey tests. 126 */ 127 protected class ICU_Rand { 128 private int fLast; 129 130 public ICU_Rand(int seed) { 131 seed(seed); 132 } 133 134 public int next() { 135 fLast = (int)((fLast * 48271L) % 2147483647L); 136 return fLast; 137 } 138 139 public void seed(int seed) { 140 if (seed <= 0) { 141 seed = 1; 142 } 143 seed %= 2147483647; // = 0x7FFFFFFF 144 fLast = seed > 0 ? seed : 1; 145 } 146 147 public int getSeed() { 148 return fLast; 149 } 150 151 } 152 153 static final String ICU_TRAC_URL = "http://bugs.icu-project.org/trac/ticket/"; 154 static final String CLDR_TRAC_URL = "http://unicode.org/cldr/trac/ticket/"; 155 static final String CLDR_TICKET_PREFIX = "cldrbug:"; 156 157 /** 158 * Log the known issue. 159 * This method returns true unless -prop:logKnownIssue=no is specified 160 * in the argument list. 161 * 162 * @param ticket A ticket number string. For an ICU ticket, use numeric characters only, 163 * such as "10245". For a CLDR ticket, use prefix "cldrbug:" followed by ticket number, 164 * such as "cldrbug:5013". 165 * @param comment Additional comment, or null 166 * @return true unless -prop:logKnownIssue=no is specified in the test command line argument. 167 */ 168 protected static boolean logKnownIssue(String ticket, String comment) { 169 if (!getBooleanProperty("logKnownIssue", true)) { 170 return false; 171 } 172 173 StringBuffer descBuf = new StringBuffer(); 174 // TODO(junit) : what to do about this? 175 //getParams().stack.appendPath(descBuf); 176 if (comment != null && comment.length() > 0) { 177 descBuf.append(" (" + comment + ")"); 178 } 179 String description = descBuf.toString(); 180 181 String ticketLink = "Unknown Ticket"; 182 if (ticket != null && ticket.length() > 0) { 183 boolean isCldr = false; 184 ticket = ticket.toLowerCase(Locale.ENGLISH); 185 if (ticket.startsWith(CLDR_TICKET_PREFIX)) { 186 isCldr = true; 187 ticket = ticket.substring(CLDR_TICKET_PREFIX.length()); 188 } 189 ticketLink = (isCldr ? CLDR_TRAC_URL : ICU_TRAC_URL) + ticket; 190 } 191 192 if (getParams().knownIssues == null) { 193 getParams().knownIssues = new TreeMap<String, List<String>>(); 194 } 195 List<String> lines = getParams().knownIssues.get(ticketLink); 196 if (lines == null) { 197 lines = new ArrayList<String>(); 198 getParams().knownIssues.put(ticketLink, lines); 199 } 200 if (!lines.contains(description)) { 201 lines.add(description); 202 } 203 204 return true; 205 } 206 207 protected static String getProperty(String key) { 208 return getParams().getProperty(key); 209 } 210 211 protected static boolean getBooleanProperty(String key) { 212 return getParams().getBooleanProperty(key); 213 } 214 215 protected static boolean getBooleanProperty(String key, boolean defVal) { 216 return getParams().getBooleanProperty(key, defVal); 217 } 218 219 protected static int getIntProperty(String key, int defVal) { 220 return getParams().getIntProperty(key, defVal); 221 } 222 223 protected static int getIntProperty(String key, int defVal, int maxVal) { 224 return getParams().getIntProperty(key, defVal, maxVal); 225 } 226 227 protected static TimeZone safeGetTimeZone(String id) { 228 TimeZone tz = TimeZone.getTimeZone(id); 229 if (tz == null) { 230 // should never happen 231 errln("FAIL: TimeZone.getTimeZone(" + id + ") => null"); 232 } 233 if (!tz.getID().equals(id)) { 234 warnln("FAIL: TimeZone.getTimeZone(" + id + ") => " + tz.getID()); 235 } 236 return tz; 237 } 238 239 240 // Utility Methods 241 242 protected static String hex(char[] s){ 243 StringBuffer result = new StringBuffer(); 244 for (int i = 0; i < s.length; ++i) { 245 if (i != 0) result.append(','); 246 result.append(hex(s[i])); 247 } 248 return result.toString(); 249 } 250 251 protected static String hex(byte[] s){ 252 StringBuffer result = new StringBuffer(); 253 for (int i = 0; i < s.length; ++i) { 254 if (i != 0) result.append(','); 255 result.append(hex(s[i])); 256 } 257 return result.toString(); 258 } 259 260 protected static String hex(char ch) { 261 StringBuffer result = new StringBuffer(); 262 String foo = Integer.toString(ch, 16).toUpperCase(); 263 for (int i = foo.length(); i < 4; ++i) { 264 result.append('0'); 265 } 266 return result + foo; 267 } 268 269 protected static String hex(int ch) { 270 StringBuffer result = new StringBuffer(); 271 String foo = Integer.toString(ch, 16).toUpperCase(); 272 for (int i = foo.length(); i < 4; ++i) { 273 result.append('0'); 274 } 275 return result + foo; 276 } 277 278 protected static String hex(CharSequence s) { 279 StringBuilder result = new StringBuilder(); 280 for (int i = 0; i < s.length(); ++i) { 281 if (i != 0) 282 result.append(','); 283 result.append(hex(s.charAt(i))); 284 } 285 return result.toString(); 286 } 287 288 protected static String prettify(CharSequence s) { 289 StringBuilder result = new StringBuilder(); 290 int ch; 291 for (int i = 0; i < s.length(); i += Character.charCount(ch)) { 292 ch = Character.codePointAt(s, i); 293 if (ch > 0xfffff) { 294 result.append("\\U00"); 295 result.append(hex(ch)); 296 } else if (ch > 0xffff) { 297 result.append("\\U000"); 298 result.append(hex(ch)); 299 } else if (ch < 0x20 || 0x7e < ch) { 300 result.append("\\u"); 301 result.append(hex(ch)); 302 } else { 303 result.append((char) ch); 304 } 305 306 } 307 return result.toString(); 308 } 309 310 private static java.util.GregorianCalendar cal; 311 312 /** 313 * Return a Date given a year, month, and day of month. This is similar to 314 * new Date(y-1900, m, d). It uses the default time zone at the time this 315 * method is first called. 316 * 317 * @param year 318 * use 2000 for 2000, unlike new Date() 319 * @param month 320 * use Calendar.JANUARY etc. 321 * @param dom 322 * day of month, 1-based 323 * @return a Date object for the given y/m/d 324 */ 325 protected static synchronized java.util.Date getDate(int year, int month, 326 int dom) { 327 if (cal == null) { 328 cal = new java.util.GregorianCalendar(); 329 } 330 cal.clear(); 331 cal.set(year, month, dom); 332 return cal.getTime(); 333 } 334 335 private static class TestParams { 336 337 private int inclusion; 338 private long seed; 339 private int loggingLevel; 340 341 private String policyFileName; 342 private SecurityManager testSecurityManager; 343 private SecurityManager originalSecurityManager; 344 345 private Map<String, List<String>> knownIssues; 346 347 private Properties props; 348 349 350 private TestParams() { 351 } 352 353 static TestParams create() { 354 TestParams params = new TestParams(); 355 Properties props = System.getProperties(); 356 params.parseProperties(props); 357 return params; 358 } 359 360 private void parseProperties(Properties props) { 361 this.props = props; 362 363 inclusion = getIntProperty(EXHAUSTIVENESS, DEFAULT_EXHAUSTIVENESS, MAX_EXHAUSTIVENESS); 364 seed = getLongProperty(SEED, System.currentTimeMillis()); 365 loggingLevel = getIntProperty(LOGGING_LEVEL, DEFAULT_LOGGING_LEVEL, MAX_LOGGING_LEVEL); 366 367 policyFileName = getProperty(SECURITY_POLICY); 368 if (policyFileName != null) { 369 String originalPolicyFileName = System.getProperty("java.security.policy"); 370 originalSecurityManager = System.getSecurityManager(); 371 System.setProperty("java.security.policy", policyFileName); 372 Policy.getPolicy().refresh(); 373 testSecurityManager = new SecurityManager(); 374 System.setProperty("java.security.policy", originalPolicyFileName==null ? "" : originalPolicyFileName); 375 } 376 } 377 378 public String getProperty(String key) { 379 String val = null; 380 if (key != null && key.length() > 0) { 381 val = props.getProperty(key); 382 } 383 return val; 384 } 385 386 public boolean getBooleanProperty(String key) { 387 return getBooleanProperty(key, false); 388 } 389 390 public boolean getBooleanProperty(String key, boolean defVal) { 391 String s = getProperty(key); 392 if (s == null) { 393 return defVal; 394 } 395 if (s.equalsIgnoreCase("yes") || s.equalsIgnoreCase("true") || s.equals("1")) { 396 return true; 397 } 398 return false; 399 } 400 401 public int getIntProperty(String key, int defVal) { 402 return getIntProperty(key, defVal, -1); 403 } 404 405 public int getIntProperty(String key, int defVal, int maxVal) { 406 String s = getProperty(key); 407 if (s == null) { 408 return defVal; 409 } 410 return (maxVal == -1) ? Integer.valueOf(s) : Math.max(Integer.valueOf(s), maxVal); 411 } 412 413 public long getLongProperty(String key, long defVal) { 414 String s = getProperty(key); 415 if (s == null) { 416 return defVal; 417 } 418 return Long.valueOf(s); 419 } 420 421 public int getInclusion() { 422 return inclusion; 423 } 424 425 public long getSeed() { 426 return seed; 427 } 428 429 public int getLoggingLevel() { 430 return loggingLevel; 431 } 432 } 433 434 /** 435 * Check the given array to see that all the strings in the expected array 436 * are present. 437 * 438 * @param msg 439 * string message, for log output 440 * @param array 441 * array of strings to check 442 * @param expected 443 * array of strings we expect to see, or null 444 * @return the length of 'array', or -1 on error 445 */ 446 protected static int checkArray(String msg, String array[], String expected[]) { 447 int explen = (expected != null) ? expected.length : 0; 448 if (!(explen >= 0 && explen < 31)) { // [sic] 31 not 32 449 errln("Internal error"); 450 return -1; 451 } 452 int i = 0; 453 StringBuffer buf = new StringBuffer(); 454 int seenMask = 0; 455 for (; i < array.length; ++i) { 456 String s = array[i]; 457 if (i != 0) 458 buf.append(", "); 459 buf.append(s); 460 // check expected list 461 for (int j = 0, bit = 1; j < explen; ++j, bit <<= 1) { 462 if ((seenMask & bit) == 0) { 463 if (s.equals(expected[j])) { 464 seenMask |= bit; 465 logln("Ok: \"" + s + "\" seen"); 466 } 467 } 468 } 469 } 470 logln(msg + " = [" + buf + "] (" + i + ")"); 471 // did we see all expected strings? 472 if (((1 << explen) - 1) != seenMask) { 473 for (int j = 0, bit = 1; j < expected.length; ++j, bit <<= 1) { 474 if ((seenMask & bit) == 0) { 475 errln("\"" + expected[j] + "\" not seen"); 476 } 477 } 478 } 479 return array.length; 480 } 481 482 /** 483 * Check the given array to see that all the locales in the expected array 484 * are present. 485 * 486 * @param msg 487 * string message, for log output 488 * @param array 489 * array of locales to check 490 * @param expected 491 * array of locales names we expect to see, or null 492 * @return the length of 'array' 493 */ 494 protected static int checkArray(String msg, Locale array[], String expected[]) { 495 String strs[] = new String[array.length]; 496 for (int i = 0; i < array.length; ++i) { 497 strs[i] = array[i].toString(); 498 } 499 return checkArray(msg, strs, expected); 500 } 501 502 /** 503 * Check the given array to see that all the locales in the expected array 504 * are present. 505 * 506 * @param msg 507 * string message, for log output 508 * @param array 509 * array of locales to check 510 * @param expected 511 * array of locales names we expect to see, or null 512 * @return the length of 'array' 513 */ 514 protected static int checkArray(String msg, ULocale array[], String expected[]) { 515 String strs[] = new String[array.length]; 516 for (int i = 0; i < array.length; ++i) { 517 strs[i] = array[i].toString(); 518 } 519 return checkArray(msg, strs, expected); 520 } 521 522 // JUnit-like assertions. 523 524 protected static boolean assertTrue(String message, boolean condition) { 525 return handleAssert(condition, message, "true", null); 526 } 527 528 protected static boolean assertFalse(String message, boolean condition) { 529 return handleAssert(!condition, message, "false", null); 530 } 531 532 protected static boolean assertEquals(String message, boolean expected, 533 boolean actual) { 534 return handleAssert(expected == actual, message, String 535 .valueOf(expected), String.valueOf(actual)); 536 } 537 538 protected static boolean assertEquals(String message, long expected, long actual) { 539 return handleAssert(expected == actual, message, String 540 .valueOf(expected), String.valueOf(actual)); 541 } 542 543 // do NaN and range calculations to precision of float, don't rely on 544 // promotion to double 545 protected static boolean assertEquals(String message, float expected, 546 float actual, double error) { 547 boolean result = Float.isInfinite(expected) 548 ? expected == actual 549 : !(Math.abs(expected - actual) > error); // handles NaN 550 return handleAssert(result, message, String.valueOf(expected) 551 + (error == 0 ? "" : " (within " + error + ")"), String 552 .valueOf(actual)); 553 } 554 555 protected static boolean assertEquals(String message, double expected, 556 double actual, double error) { 557 boolean result = Double.isInfinite(expected) 558 ? expected == actual 559 : !(Math.abs(expected - actual) > error); // handles NaN 560 return handleAssert(result, message, String.valueOf(expected) 561 + (error == 0 ? "" : " (within " + error + ")"), String 562 .valueOf(actual)); 563 } 564 565 protected static <T> boolean assertEquals(String message, T[] expected, T[] actual) { 566 // Use toString on a List to get useful, readable messages 567 String expectedString = expected == null ? "null" : Arrays.asList(expected).toString(); 568 String actualString = actual == null ? "null" : Arrays.asList(actual).toString(); 569 return assertEquals(message, expectedString, actualString); 570 } 571 572 protected static boolean assertEquals(String message, Object expected, 573 Object actual) { 574 boolean result = expected == null ? actual == null : expected 575 .equals(actual); 576 return handleAssert(result, message, stringFor(expected), 577 stringFor(actual)); 578 } 579 580 protected static boolean assertNotEquals(String message, Object expected, 581 Object actual) { 582 boolean result = !(expected == null ? actual == null : expected 583 .equals(actual)); 584 return handleAssert(result, message, stringFor(expected), 585 stringFor(actual), "not equal to", true); 586 } 587 588 protected boolean assertSame(String message, Object expected, Object actual) { 589 return handleAssert(expected == actual, message, stringFor(expected), 590 stringFor(actual), "==", false); 591 } 592 593 protected static boolean assertNotSame(String message, Object expected, 594 Object actual) { 595 return handleAssert(expected != actual, message, stringFor(expected), 596 stringFor(actual), "!=", true); 597 } 598 599 protected static boolean assertNull(String message, Object actual) { 600 return handleAssert(actual == null, message, null, stringFor(actual)); 601 } 602 603 protected static boolean assertNotNull(String message, Object actual) { 604 return handleAssert(actual != null, message, null, stringFor(actual), 605 "!=", true); 606 } 607 608 protected static void fail() { 609 fail(""); 610 } 611 612 protected static void fail(String message) { 613 if (message == null) { 614 message = ""; 615 } 616 if (!message.equals("")) { 617 message = ": " + message; 618 } 619 errln(sourceLocation() + message); 620 } 621 622 private static boolean handleAssert(boolean result, String message, 623 String expected, String actual) { 624 return handleAssert(result, message, expected, actual, null, false); 625 } 626 627 public static boolean handleAssert(boolean result, String message, 628 Object expected, Object actual, String relation, boolean flip) { 629 if (!result || isVerbose()) { 630 if (message == null) { 631 message = ""; 632 } 633 if (!message.equals("")) { 634 message = ": " + message; 635 } 636 relation = relation == null ? ", got " : " " + relation + " "; 637 if (result) { 638 logln("OK " + message + ": " 639 + (flip ? expected + relation + actual : expected)); 640 } else { 641 // assert must assume errors are true errors and not just warnings 642 // so cannot warnln here 643 errln( message 644 + ": expected" 645 + (flip ? relation + expected : " " + expected 646 + (actual != null ? relation + actual : ""))); 647 } 648 } 649 return result; 650 } 651 652 private static final String stringFor(Object obj) { 653 if (obj == null) { 654 return "null"; 655 } 656 if (obj instanceof String) { 657 return "\"" + obj + '"'; 658 } 659 return obj.getClass().getName() + "<" + obj + ">"; 660 } 661 662 // Return the source code location of the caller located callDepth frames up the stack. 663 protected static String sourceLocation() { 664 // Walk up the stack to the first call site outside this file 665 for (StackTraceElement st : new Throwable().getStackTrace()) { 666 String source = st.getFileName(); 667 if (source != null && !source.equals("TestFmwk.java") && !source.equals("AbstractTestLog.java")) { 668 String methodName = st.getMethodName(); 669 if (methodName != null && 670 (methodName.startsWith("Test") || methodName.startsWith("test") || methodName.equals("main"))) { 671 return "(" + source + ":" + st.getLineNumber() + ") "; 672 } 673 } 674 } 675 throw new InternalError(); 676 } 677 678 protected static boolean checkDefaultPrivateConstructor(String fullyQualifiedClassName) throws Exception { 679 return checkDefaultPrivateConstructor(Class.forName(fullyQualifiedClassName)); 680 } 681 682 protected static boolean checkDefaultPrivateConstructor(Class<?> classToBeTested) throws Exception { 683 Constructor<?> constructor = classToBeTested.getDeclaredConstructor(); 684 685 // Check that the constructor is private. 686 boolean isPrivate = Modifier.isPrivate(constructor.getModifiers()); 687 688 // Call the constructor for coverage. 689 constructor.setAccessible(true); 690 constructor.newInstance(); 691 692 if (!isPrivate) { 693 errln("Default private constructor for class: " + classToBeTested.getName() + " is not private."); 694 } 695 return isPrivate; 696 } 697 698 /** 699 * Tests the toString method on a private or hard-to-reach class. Assumes constructor of the class does not 700 * take any arguments. 701 * @param fullyQualifiedClassName 702 * @return The output of the toString method. 703 * @throws Exception 704 */ 705 protected static String invokeToString(String fullyQualifiedClassName) throws Exception { 706 return invokeToString(fullyQualifiedClassName, new Class<?>[]{}, new Object[]{}); 707 } 708 709 /** 710 * Tests the toString method on a private or hard-to-reach class. Assumes constructor of the class does not 711 * take any arguments. 712 * @param classToBeTested 713 * @return The output of the toString method. 714 * @throws Exception 715 */ 716 protected static String invokeToString(Class<?> classToBeTested) throws Exception { 717 return invokeToString(classToBeTested, new Class<?>[]{}, new Object[]{}); 718 } 719 720 /** 721 * Tests the toString method on a private or hard-to-reach class. Allows you to specify the argument types for 722 * the constructor. 723 * @param fullyQualifiedClassName 724 * @return The output of the toString method. 725 * @throws Exception 726 */ 727 protected static String invokeToString(String fullyQualifiedClassName, 728 Class<?>[] constructorParamTypes, Object[] constructorParams) throws Exception { 729 return invokeToString(Class.forName(fullyQualifiedClassName), constructorParamTypes, constructorParams); 730 } 731 732 /** 733 * Tests the toString method on a private or hard-to-reach class. Allows you to specify the argument types for 734 * the constructor. 735 * @param classToBeTested 736 * @return The output of the toString method. 737 * @throws Exception 738 */ 739 protected static String invokeToString(Class<?> classToBeTested, 740 Class<?>[] constructorParamTypes, Object[] constructorParams) throws Exception { 741 Constructor<?> constructor = classToBeTested.getDeclaredConstructor(constructorParamTypes); 742 constructor.setAccessible(true); 743 Object obj = constructor.newInstance(constructorParams); 744 Method toStringMethod = classToBeTested.getDeclaredMethod("toString"); 745 toStringMethod.setAccessible(true); 746 return (String) toStringMethod.invoke(obj); 747 } 748 749 750 // End JUnit-like assertions 751 752 // TODO (sgill): added to keep errors away 753 /* (non-Javadoc) 754 * @see android.icu.dev.test.TestLog#msg(java.lang.String, int, boolean, boolean) 755 */ 756 //@Override 757 protected static void msg(String message, int level, boolean incCount, boolean newln) { 758 if (level == TestLog.WARN || level == TestLog.ERR) { 759 Assert.fail(message); 760 } 761 // TODO(stuartg): turned off - causing OOM running under ant 762// while (level > 0) { 763// System.out.print(" "); 764// level--; 765// } 766// System.out.print(message); 767// if (newln) { 768// System.out.println(); 769// } 770 } 771 772} 773