1 19 20 package org.openide.awt; 21 22 import java.awt.Color ; 23 import java.awt.Font ; 24 import java.awt.FontMetrics ; 25 import java.awt.Graphics ; 26 import java.awt.Rectangle ; 27 import java.awt.Shape ; 28 import java.awt.font.LineMetrics ; 29 import java.awt.geom.Area ; 30 import java.awt.geom.Rectangle2D ; 31 import java.util.Arrays ; 32 import java.util.HashSet ; 33 import java.util.Set ; 34 import java.util.Stack ; 35 import java.util.StringTokenizer ; 36 import java.util.logging.Level ; 37 import java.util.logging.Logger ; 38 import javax.swing.Icon ; 39 import javax.swing.JLabel ; 40 import javax.swing.ListCellRenderer ; 41 import javax.swing.SwingUtilities ; 42 import javax.swing.UIManager ; 43 import javax.swing.table.TableCellRenderer ; 44 import javax.swing.tree.TreeCellRenderer ; 45 import org.openide.util.Utilities; 46 47 185 public final class HtmlRenderer { 186 private static HtmlRendererImpl LABEL = null; 187 188 190 private static Stack <Color > colorStack = new Stack <Color >(); 191 192 197 public static final int STYLE_CLIP = 0; 198 199 205 public static final int STYLE_TRUNCATE = 1; 206 207 214 private static final int STYLE_WORDWRAP = 2; 215 216 218 private static final boolean STRICT_HTML = Boolean.getBoolean("netbeans.lwhtml.strict"); 220 222 private static Set <String > badStrings = null; 223 224 private static Logger LOG = Logger.getLogger(HtmlRenderer.class.getName()); 225 226 227 private static final Object [] entities = new Object [] { 228 new char[] { 'g', 't' }, new char[] { 'l', 't' }, new char[] { 'q', 'u', 'o', 't' }, new char[] { 'a', 'm', 'p' }, new char[] { 'l', 's', 'q', 'u', 'o' }, new char[] { 'r', 's', 'q', 'u', 'o' }, new char[] { 'l', 'd', 'q', 'u', 'o' }, new char[] { 'r', 'd', 'q', 'u', 'o' }, new char[] { 'n', 'd', 'a', 's', 'h' }, new char[] { 'm', 'd', 'a', 's', 'h' }, new char[] { 'n', 'e' }, new char[] { 'l', 'e' }, new char[] { 'g', 'e' }, new char[] { 'c', 'o', 'p', 'y' }, new char[] { 'r', 'e', 'g' }, new char[] { 't', 'r', 'a', 'd', 'e' }, new char[] { 'n', 'b', 's', 'p' } 244 }; 246 247 private static final char[] entitySubstitutions = new char[] { 248 '>', '<', '"', '&', 8216, 8217, 8220, 8221, 8211, 8212, 8800, 8804, 8805, 169, 174, 8482, ' ' 250 }; 251 private HtmlRenderer() { 252 } 254 255 263 public static final Renderer createRenderer() { 264 return new HtmlRendererImpl(); 265 } 266 267 285 public static final JLabel createLabel() { 286 return new HtmlRendererImpl(); 287 } 288 289 299 public static double renderPlainString( 300 String s, Graphics g, int x, int y, int w, int h, Font f, Color defaultColor, int style, boolean paint 301 ) { 302 if ((style < 0) || (style > 1)) { 304 throw new IllegalArgumentException ("Unknown rendering mode: " + style); } 306 307 return _renderPlainString(s, g, x, y, w, h, f, defaultColor, style, paint); 308 } 309 310 private static double _renderPlainString( 311 String s, Graphics g, int x, int y, int w, int h, Font f, Color foreground, int style, boolean paint 312 ) { 313 if (f == null) { 314 f = UIManager.getFont("controlFont"); 316 if (f == null) { 317 int fs = 11; 318 Object cfs = UIManager.get("customFontSize"); 320 if (cfs instanceof Integer ) { 321 fs = ((Integer ) cfs).intValue(); 322 } 323 324 f = new Font ("Dialog", Font.PLAIN, fs); } 326 } 327 328 FontMetrics fm = g.getFontMetrics(f); 329 int wid; 331 if (Utilities.isMac()) { 332 wid = fm.stringWidth(s); 334 } else { 335 wid = (int)fm.getStringBounds(s, g).getWidth(); 336 } 337 338 if (paint) { 339 g.setColor(foreground); 340 g.setFont(f); 341 342 if ((wid <= w) || (style == STYLE_CLIP)) { 343 g.drawString(s, x, y); 344 } else { 345 char[] chars = s.toCharArray(); 346 347 if (chars.length == 0) { 348 return 0; 349 } 350 351 double chWidth = wid / chars.length; 352 int estCharsToPaint = new Double (w / chWidth).intValue(); 353 if( estCharsToPaint > chars.length ) 354 estCharsToPaint = chars.length; 355 while( estCharsToPaint > 3 ) { 357 if( estCharsToPaint < chars.length ) 358 Arrays.fill(chars, estCharsToPaint - 3, estCharsToPaint, '.'); int newWidth; 360 if (Utilities.isMac()) { 361 newWidth = fm.stringWidth(new String (chars, 0, estCharsToPaint)); 363 } else { 364 newWidth = (int)fm.getStringBounds(chars, 0, estCharsToPaint, g).getWidth(); 365 } 366 if( newWidth <= w ) 367 break; 368 estCharsToPaint--; 369 } 370 371 if (style == STYLE_TRUNCATE) { 372 int length = estCharsToPaint; 373 374 if (length <= 0) { 375 return 0; 376 } 377 378 if (paint) { 379 if (length > 3) { 380 g.drawChars(chars, 0, length, x, y); 381 } else { 382 Shape shape = g.getClip(); 383 384 if (s != null) { 385 Area area = new Area (shape); 386 area.intersect(new Area (new Rectangle (x, y, w, h))); 387 g.setClip(area); 388 } else { 389 g.setClip(new Rectangle (x, y, w, h)); 390 } 391 392 g.drawString("...", x, y); 393 g.setClip(shape); 394 } 395 } 396 } else { 397 } 399 } 400 } 401 402 return wid; 403 } 404 405 431 public static double renderString( 432 String s, Graphics g, int x, int y, int w, int h, Font f, Color defaultColor, int style, boolean paint 433 ) { 434 switch (style) { 435 case STYLE_CLIP: 436 case STYLE_TRUNCATE: 437 break; 438 439 default: 440 throw new IllegalArgumentException ("Unknown rendering mode: " + style); } 442 443 if (s.startsWith("<html") || s.startsWith("<HTML")) { 446 return _renderHTML(s, 6, g, x, y, w, h, f, defaultColor, style, paint, null); 447 } else { 448 return renderPlainString(s, g, x, y, w, h, f, defaultColor, style, paint); 449 } 450 } 451 452 492 public static double renderHTML( 493 String s, Graphics g, int x, int y, int w, int h, Font f, Color defaultColor, int style, boolean paint 494 ) { 495 if ((style < 0) || (style > 1)) { 497 throw new IllegalArgumentException ("Unknown rendering mode: " + style); } 499 500 return _renderHTML(s, 0, g, x, y, w, h, f, defaultColor, style, paint, null); 501 } 502 503 504 static double _renderHTML( 505 String s, int pos, Graphics g, int x, int y, int w, int h, Font f, Color defaultColor, int style, boolean paint, 506 Color background 507 ) { 508 if (f == null) { 510 f = UIManager.getFont("controlFont"); 512 if (f == null) { 513 int fs = 11; 514 Object cfs = UIManager.get("customFontSize"); 516 if (cfs instanceof Integer ) { 517 fs = ((Integer ) cfs).intValue(); 518 } 519 520 f = new Font ("Dialog", Font.PLAIN, fs); } 522 } 523 524 Stack <Color > colorStack = SwingUtilities.isEventDispatchThread() ? HtmlRenderer.colorStack : new Stack <Color >(); 526 527 g.setColor(defaultColor); 528 g.setFont(f); 529 530 char[] chars = s.toCharArray(); 531 int origX = x; 532 boolean done = false; boolean inTag = false; boolean inClosingTag = false; boolean strikethrough = false; boolean underline = false; boolean bold = false; boolean italic = false; boolean truncated = false; double widthPainted = 0; double heightPainted = 0; boolean lastWasWhitespace = false; double lastHeight = 0; 545 double dotWidth = 0; 546 547 if (style == STYLE_TRUNCATE) { 549 dotWidth = g.getFontMetrics().charWidth('.'); } 551 552 575 colorStack.clear(); 577 578 while (!done) { 580 if (pos == s.length()) { 581 return widthPainted; 582 } 583 584 try { 586 inTag |= (chars[pos] == '<'); 587 } catch (ArrayIndexOutOfBoundsException e) { 588 ArrayIndexOutOfBoundsException aib = new ArrayIndexOutOfBoundsException ( 591 "HTML rendering failed at position " + pos + " in String \"" +s + "\". Please report this at http://www.netbeans.org" 593 ); 595 if (STRICT_HTML) { 596 throw aib; 597 } else { 598 Logger.getLogger(HtmlRenderer.class.getName()).log(Level.WARNING, null, aib); 599 600 return renderPlainString(s, g, x, y, w, h, f, defaultColor, style, paint); 601 } 602 } 603 604 inClosingTag = inTag && ((pos + 1) < chars.length) && (chars[pos + 1] == '/'); 606 if (truncated) { 607 g.setColor(defaultColor); 609 g.setFont(f); 610 611 if (paint) { 612 g.drawString("...", x, y); } 614 615 done = true; 616 } else if (inTag) { 617 pos++; 619 620 int tagEnd = pos; 621 622 done = tagEnd >= (chars.length - 1); 624 625 while (!done && (chars[tagEnd] != '>')) { 626 done = tagEnd == (chars.length - 1); 627 tagEnd++; 628 } 629 630 if (done) { 631 throw new IllegalArgumentException ("HTML rendering failed on string \"" + s + "\""); 632 } 633 634 if (inClosingTag) { 635 pos++; 637 638 switch (chars[pos]) { 639 case 'P': case 'p': case 'H': case 'h': 643 break; 645 case 'B': case 'b': 648 if ((chars[pos + 1] == 'r') || (chars[pos + 1] == 'R')) { 649 break; 650 } 651 652 if (!bold) { 653 throwBadHTML("Closing bold tag w/o " + "opening bold tag", pos, chars 655 ); } 657 658 if (italic) { 659 g.setFont(deriveFont(f, Font.ITALIC)); 660 } else { 661 g.setFont(deriveFont(f, Font.PLAIN)); 662 } 663 664 bold = false; 665 666 break; 667 668 case 'E': case 'e': case 'I': case 'i': 673 if (bold) { 674 g.setFont(deriveFont(f, Font.BOLD)); 675 } else { 676 g.setFont(deriveFont(f, Font.PLAIN)); 677 } 678 679 if (!italic) { 680 throwBadHTML("Closing italics tag w/o" +"opening italics tag", pos, chars 682 ); } 684 685 italic = false; 686 687 break; 688 689 case 'S': case 's': 692 switch (chars[pos + 1]) { 693 case 'T': case 't': 695 696 if (italic) { g.setFont(deriveFont(f, Font.ITALIC)); 698 } else { 699 g.setFont(deriveFont(f, Font.PLAIN)); 700 } 701 702 bold = false; 703 704 break; 705 706 case '>': strikethrough = false; 708 709 break; 710 } 711 712 break; 713 714 case 'U': case 'u': 716 underline = false; 718 break; 719 720 case 'F': case 'f': 723 if (colorStack.isEmpty()) { 724 g.setColor(defaultColor); 725 } else { 726 g.setColor(colorStack.pop()); 727 } 728 729 break; 730 731 default: 732 throwBadHTML("Malformed or unsupported HTML", pos, chars 734 ); 735 } 736 } else { 737 switch (chars[pos]) { 739 case 'B': case 'b': 742 switch (chars[pos + 1]) { 743 case 'R': case 'r': 746 if (style == STYLE_WORDWRAP) { 747 x = origX; 748 749 int lineHeight = g.getFontMetrics().getHeight(); 750 y += lineHeight; 751 heightPainted += lineHeight; 752 widthPainted = 0; 753 } 754 755 break; 756 757 case '>': 758 bold = true; 759 760 if (italic) { 761 g.setFont(deriveFont(f, Font.BOLD | Font.ITALIC)); 762 } else { 763 g.setFont(deriveFont(f, Font.BOLD)); 764 } 765 766 break; 767 } 768 769 break; 770 771 case 'e': case 'E': case 'I': case 'i': italic = true; 776 777 if (bold) { 778 g.setFont(deriveFont(f, Font.ITALIC | Font.BOLD)); 779 } else { 780 g.setFont(deriveFont(f, Font.ITALIC)); 781 } 782 783 break; 784 785 case 'S': case 's': 788 switch (chars[pos + 1]) { 789 case '>': 790 strikethrough = true; 791 792 break; 793 794 case 'T': 795 case 't': 796 bold = true; 797 798 if (italic) { 799 g.setFont(deriveFont(f, Font.BOLD | Font.ITALIC)); 800 } else { 801 g.setFont(deriveFont(f, Font.BOLD)); 802 } 803 804 break; 805 } 806 807 break; 808 809 case 'U': case 'u': underline = true; 812 813 break; 814 815 case 'f': case 'F': 818 Color c = findColor(chars, pos, tagEnd); 819 colorStack.push(g.getColor()); 820 821 if (background != null) { 822 c = HtmlLabelUI.ensureContrastingColor(c, background); 823 } 824 825 g.setColor(c); 826 827 break; 828 829 case 'P': case 'p': 832 if (style == STYLE_WORDWRAP) { 833 x = origX; 834 835 int lineHeight = g.getFontMetrics().getHeight(); 836 y += (lineHeight + (lineHeight / 2)); 837 heightPainted = y + lineHeight; 838 widthPainted = 0; 839 } 840 841 break; 842 843 case 'H': 844 case 'h': 846 if (pos == 1) { 847 break; 848 } 849 850 default: 851 throwBadHTML("Malformed or unsupported HTML", pos, chars); } 853 } 854 855 pos = tagEnd + (done ? 0 : 1); 856 inTag = false; 857 } else { 858 if (lastWasWhitespace) { 860 while ((pos < (s.length() - 1)) && Character.isWhitespace(chars[pos])) { 862 pos++; 863 } 864 865 if (pos == (chars.length - 1)) { 868 return (style != STYLE_WORDWRAP) ? widthPainted : heightPainted; 869 } 870 } 871 872 boolean isAmp = false; 876 877 boolean nextLtIsEntity = false; 881 int nextTag = chars.length - 1; 882 883 if ((chars[pos] == '&')) { 885 boolean inEntity = pos != (chars.length - 1); 886 887 if (inEntity) { 888 int newPos = substEntity(chars, pos + 1); 889 inEntity = newPos != -1; 890 891 if (inEntity) { 892 pos = newPos; 893 isAmp = chars[pos] == '&'; 895 nextLtIsEntity = chars[pos] == '<'; 896 } else { 897 nextLtIsEntity = false; 898 isAmp = true; 899 } 900 } 901 } else { 902 nextLtIsEntity = false; 903 } 904 905 for (int i = pos; i < chars.length; i++) { 906 if ((chars[i] == '<' && !nextLtIsEntity) || (chars[i] == '&' && !isAmp && i != chars.length - 1)) { 907 nextTag = i - 1; 908 909 break; 910 } 911 912 isAmp = false; 914 nextLtIsEntity = false; 915 } 916 917 FontMetrics fm = g.getFontMetrics(); 918 919 Rectangle2D r = fm.getStringBounds(chars, pos, nextTag + 1, g); 921 if (Utilities.isMac()) { 922 r.setRect(r.getX(), r.getY(), (double)fm.stringWidth(new String (chars, pos, nextTag - pos + 1)), r.getHeight()); 924 } 925 926 lastHeight = r.getHeight(); 929 930 int length = (nextTag + 1) - pos; 932 933 boolean goToNextRow = false; 935 936 boolean brutalWrap = false; 939 940 double chWidth; 944 945 if (truncated) { 946 chWidth = dotWidth; 949 } else { 950 chWidth = r.getWidth() / (nextTag+1 - pos); 952 953 if ((chWidth == Double.POSITIVE_INFINITY) || (chWidth == Double.NEGATIVE_INFINITY)) { 955 chWidth = fm.getMaxAdvance(); 956 } 957 } 958 959 if ( 960 ((style != STYLE_CLIP) && 961 ((style == STYLE_TRUNCATE) && ((widthPainted + r.getWidth()) > (w )))) || 962 965 ((style == STYLE_WORDWRAP) && ((widthPainted + r.getWidth()) > w)) 966 ) { 967 if (chWidth > 3) { 968 double pixelsOff = (widthPainted + (r.getWidth() + 5)) - w; 969 970 double estCharsOver = pixelsOff / chWidth; 971 972 if (style == STYLE_TRUNCATE) { 973 int charsToPaint = Math.round(Math.round(Math.ceil((w - widthPainted) / chWidth))); 974 975 979 int startPeriodsPos = (pos + charsToPaint) - 3; 980 981 if (startPeriodsPos >= chars.length) { 982 startPeriodsPos = chars.length - 4; 983 } 984 985 length = (startPeriodsPos - pos); 986 987 if (length < 0) { 988 length = 0; 989 } 990 991 r = fm.getStringBounds(chars, pos, pos + length, g); 992 if (Utilities.isMac()) { 993 r.setRect(r.getX(), r.getY(), (double)fm.stringWidth(new String (chars, pos, length)), r.getHeight()); 995 } 996 997 truncated = true; 999 } else { 1000 goToNextRow = true; 1002 1003 int lastChar = new Double (nextTag - estCharsOver).intValue(); 1004 1005 brutalWrap = x == 0; 1008 1009 for (int i = lastChar; i > pos; i--) { 1010 lastChar--; 1011 1012 if (Character.isWhitespace(chars[i])) { 1013 length = (lastChar - pos) + 1; 1014 brutalWrap = false; 1015 1016 break; 1017 } 1018 } 1019 1020 if ((lastChar <= pos) && (length > estCharsOver) && !brutalWrap) { 1021 x = origX; 1022 y += r.getHeight(); 1023 heightPainted += r.getHeight(); 1024 1025 boolean boundsChanged = false; 1026 1027 while (!done && Character.isWhitespace(chars[pos]) && (pos < nextTag)) { 1028 pos++; 1029 boundsChanged = true; 1030 done = pos == (chars.length - 1); 1031 } 1032 1033 if (pos == nextTag) { 1034 lastWasWhitespace = true; 1035 } 1036 1037 if (boundsChanged) { 1038 r = fm.getStringBounds(chars, pos, nextTag + 1, g); 1040 if (Utilities.isMac()) { 1041 r.setRect(r.getX(), r.getY(), (double)fm.stringWidth(new String (chars, pos, nextTag - pos + 1)), r.getHeight()); 1043 } 1044 } 1045 1046 goToNextRow = false; 1047 widthPainted = 0; 1048 1049 if (chars[pos - 1 + length] == '<') { 1050 length--; 1051 } 1052 } else if (brutalWrap) { 1053 length = (new Double ((w - widthPainted) / chWidth)).intValue(); 1055 1056 if ((pos + length) > nextTag) { 1057 length = (nextTag - pos); 1058 } 1059 1060 goToNextRow = true; 1061 } 1062 } 1063 } 1064 } 1065 1066 if (!done) { 1067 if (paint) { 1068 g.drawChars(chars, pos, length, x, y); 1069 } 1070 1071 if (strikethrough || underline) { 1072 LineMetrics lm = fm.getLineMetrics(chars, pos, length - 1, g); 1073 int lineWidth = new Double (x + r.getWidth()).intValue(); 1074 1075 if (paint) { 1076 if (strikethrough) { 1077 int stPos = Math.round(lm.getStrikethroughOffset()) + 1078 g.getFont().getBaselineFor(chars[pos]) + 1; 1079 1080 g.drawLine(x, y + stPos, lineWidth, y + stPos); 1084 } 1085 1086 if (underline) { 1087 int stPos = Math.round(lm.getUnderlineOffset()) + 1088 g.getFont().getBaselineFor(chars[pos]) + 1; 1089 1090 g.drawLine(x, y + stPos, lineWidth, y + stPos); 1094 } 1095 } 1096 } 1097 1098 if (goToNextRow) { 1099 x = origX; 1102 y += r.getHeight(); 1103 heightPainted += r.getHeight(); 1104 widthPainted = 0; 1105 pos += (length); 1106 1107 while ((pos < chars.length) && (Character.isWhitespace(chars[pos])) && (chars[pos] != '<')) { 1109 pos++; 1110 } 1111 1112 lastWasWhitespace = true; 1113 done |= (pos >= chars.length); 1114 } else { 1115 x += r.getWidth(); 1116 widthPainted += r.getWidth(); 1117 lastWasWhitespace = Character.isWhitespace(chars[nextTag]); 1118 pos = nextTag + 1; 1119 } 1120 1121 done |= (nextTag == chars.length); 1122 } 1123 } 1124 } 1125 1126 if (style != STYLE_WORDWRAP) { 1127 return widthPainted; 1128 } else { 1129 return heightPainted + lastHeight; 1130 } 1131 } 1132 1133 1134 private static Color findColor(final char[] ch, final int pos, final int tagEnd) { 1135 int colorPos = pos; 1136 boolean useUIManager = false; 1137 1138 for (int i = pos; i < tagEnd; i++) { 1139 if (ch[i] == 'c') { 1140 colorPos = i + 6; 1141 1142 if ((ch[colorPos] == '\'') || (ch[colorPos] == '"')) { 1143 colorPos++; 1144 } 1145 1146 if (ch[colorPos] == '#') { 1148 colorPos++; 1149 } else if (ch[colorPos] == '!') { 1150 useUIManager = true; 1151 colorPos++; 1152 } 1153 1154 break; 1155 } 1156 } 1157 1158 if (colorPos == pos) { 1159 String out = "Could not find color identifier in font declaration"; throwBadHTML(out, pos, ch); 1161 } 1162 1163 String s; 1165 1166 if (useUIManager) { 1167 int end = ch.length - 1; 1168 1169 for (int i = colorPos; i < ch.length; i++) { 1170 if ((ch[i] == '"') || (ch[i] == '\'')) { end = i; 1172 1173 break; 1174 } 1175 } 1176 1177 s = new String (ch, colorPos, end - colorPos); 1178 } else { 1179 s = new String (ch, colorPos, 6); 1180 } 1181 1182 Color result = null; 1183 1184 if (useUIManager) { 1185 result = UIManager.getColor(s); 1186 1187 if (result == null) { 1189 throwBadHTML("Could not resolve logical font declared in HTML: " + s, pos, ch 1191 ); 1192 result = UIManager.getColor("textText"); 1194 if (result == null) { 1196 result = Color.BLACK; 1197 } 1198 } 1199 } else { 1200 try { 1201 int rgb = Integer.parseInt(s, 16); 1202 result = new Color (rgb); 1203 } catch (NumberFormatException nfe) { 1204 throwBadHTML("Illegal hexadecimal color text: " + s + " in HTML string", colorPos, ch 1206 ); } 1208 } 1209 1210 if (result == null) { 1211 throwBadHTML("Unresolvable html color: " + s +" in HTML string \n ", pos, ch 1213 ); } 1215 1216 return result; 1217 } 1218 1219 1225 private static final Font deriveFont(Font f, int style) { 1226 Font result = Utilities.isMac() ? new Font (f.getName(), style, f.getSize()) : f.deriveFont(style); 1229 1230 return result; 1231 } 1232 1233 1238 private static final int substEntity(char[] ch, int pos) { 1239 if (pos >= (ch.length - 2)) { 1241 return -1; 1242 } 1243 1244 if (ch[pos] == '#') { 1247 return substNumericEntity(ch, pos + 1); 1248 } 1249 1250 boolean match; 1252 1253 for (int i = 0; i < entities.length; i++) { 1254 char[] c = (char[]) entities[i]; 1255 match = true; 1256 1257 if (c.length < (ch.length - pos)) { 1258 for (int j = 0; j < c.length; j++) { 1259 match &= (c[j] == ch[j + pos]); 1260 } 1261 } else { 1262 match = false; 1263 } 1264 1265 if (match) { 1266 if (ch[pos + c.length] == ';') { 1269 ch[pos + c.length] = entitySubstitutions[i]; 1271 1272 return pos + c.length; 1273 } 1274 } 1275 } 1276 1277 return -1; 1278 } 1279 1280 1284 private static final int substNumericEntity(char[] ch, int pos) { 1285 for (int i = pos; i < ch.length; i++) { 1286 if (ch[i] == ';') { 1287 try { 1288 ch[i] = (char) Integer.parseInt(new String (ch, pos, i - pos)); 1289 1290 return i; 1291 } catch (NumberFormatException nfe) { 1292 throwBadHTML("Unparsable numeric entity: " + new String (ch, pos, i - pos), pos, ch 1294 ); } 1296 } 1297 } 1298 1299 return -1; 1300 } 1301 1302 1304 private static void throwBadHTML(String msg, int pos, char[] chars) { 1305 char[] chh = new char[pos]; 1306 Arrays.fill(chh, ' '); chh[pos - 1] = '^'; 1309 String out = msg + "\n " + new String (chars) + "\n " + new String (chh) + "\n Full HTML string:" + 1310 new String (chars); 1312 if (!STRICT_HTML) { 1313 if (LOG.isLoggable(Level.WARNING)) { 1314 if (badStrings == null) { 1315 badStrings = new HashSet <String >(); 1316 } 1317 1318 if (!badStrings.contains(msg)) { 1319 StringTokenizer tk = new StringTokenizer (out, "\n", false); 1323 1324 while (tk.hasMoreTokens()) { 1325 LOG.warning(tk.nextToken()); 1326 } 1327 1328 badStrings.add(msg.intern()); } 1330 } 1331 } else { 1332 throw new IllegalArgumentException (out); 1333 } 1334 } 1335 1336 1341 public interface Renderer extends TableCellRenderer , TreeCellRenderer , ListCellRenderer { 1342 1347 void setParentFocused(boolean parentFocused); 1348 1349 1355 void setCentered(boolean centered); 1356 1357 1363 void setIndent(int pixels); 1364 1365 1373 void setHtml(boolean val); 1374 1375 1381 void setRenderStyle(int style); 1382 1383 1387 void setIcon(Icon icon); 1388 1389 1396 void reset(); 1397 1398 1404 void setText(String txt); 1405 1406 1411 void setIconTextGap(int gap); 1412 } 1413 1414} 1415 | Popular Tags |