1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.css;
16
17 import static org.htmlunit.BrowserVersionFeatures.CSS_BACKGROUND_INITIAL;
18 import static org.htmlunit.BrowserVersionFeatures.CSS_BACKGROUND_RGBA;
19 import static org.htmlunit.css.CssStyleSheet.FIXED;
20 import static org.htmlunit.css.CssStyleSheet.INITIAL;
21 import static org.htmlunit.css.CssStyleSheet.NONE;
22 import static org.htmlunit.css.CssStyleSheet.REPEAT;
23 import static org.htmlunit.css.CssStyleSheet.SCROLL;
24
25 import java.io.Serializable;
26 import java.util.LinkedHashMap;
27 import java.util.Map;
28 import java.util.regex.Matcher;
29 import java.util.regex.Pattern;
30
31 import org.htmlunit.BrowserVersion;
32 import org.htmlunit.BrowserVersionFeatures;
33 import org.htmlunit.css.StyleAttributes.Definition;
34 import org.htmlunit.cssparser.dom.AbstractCSSRuleImpl;
35 import org.htmlunit.html.DomElement;
36 import org.htmlunit.html.impl.Color;
37 import org.htmlunit.util.StringUtils;
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 public abstract class AbstractCssStyleDeclaration implements Serializable {
55
56 private static final Pattern URL_PATTERN =
57 Pattern.compile("url\\(\\s*[\"']?(.*?)[\"']?\\s*\\)");
58
59 private static final Pattern POSITION_PATTERN =
60 Pattern.compile("(\\d+\\s*(%|px|cm|mm|in|pt|pc|em|ex))\\s*"
61 + "(\\d+\\s*(%|px|cm|mm|in|pt|pc|em|ex)|top|bottom|center)");
62 private static final Pattern POSITION_PATTERN2 =
63 Pattern.compile("(left|right|center)\\s*(\\d+\\s*(%|px|cm|mm|in|pt|pc|em|ex)|top|bottom|center)");
64 private static final Pattern POSITION_PATTERN3 =
65 Pattern.compile("(top|bottom|center)\\s*(\\d+\\s*(%|px|cm|mm|in|pt|pc|em|ex)|left|right|center)");
66
67
68
69
70
71
72
73 public abstract String getStylePriority(String name);
74
75
76
77
78
79 public abstract String getCssText();
80
81
82
83
84
85
86 public abstract String getStyleAttribute(String name);
87
88
89
90
91
92
93
94
95
96 public abstract String getStyleAttribute(Definition definition, boolean getDefaultValueIfEmpty);
97
98
99
100
101
102
103 public abstract boolean hasFeature(BrowserVersionFeatures property);
104
105
106
107
108
109
110 public abstract BrowserVersion getBrowserVersion();
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131 public String getStyleAttribute(final Definition definition1, final Definition definition2) {
132 final StyleElement element1 = getStyleElement(definition1.getAttributeName());
133 final StyleElement element2 = getStyleElement(definition2.getAttributeName());
134
135 if (element2 == null) {
136 if (element1 == null) {
137 return "";
138 }
139 return element1.getValue();
140 }
141 if (element1 != null) {
142 if (StyleElement.compareToByImportanceAndSpecificity(element1, element2) > 0) {
143 return element1.getValue();
144 }
145 }
146
147 final String[] values = StringUtils.splitAtJavaWhitespace(element2.getValue());
148 if (definition1.name().contains("TOP")) {
149 if (values.length > 0) {
150 return values[0];
151 }
152 return "";
153 }
154 else if (definition1.name().contains("RIGHT")) {
155 if (values.length > 1) {
156 return values[1];
157 }
158 else if (values.length > 0) {
159 return values[0];
160 }
161 return "";
162 }
163 else if (definition1.name().contains("BOTTOM")) {
164 if (values.length > 2) {
165 return values[2];
166 }
167 else if (values.length > 0) {
168 return values[0];
169 }
170 return "";
171 }
172 else if (definition1.name().contains("LEFT")) {
173 if (values.length > 3) {
174 return values[3];
175 }
176 else if (values.length > 1) {
177 return values[1];
178 }
179 else if (values.length > 0) {
180 return values[0];
181 }
182 else {
183 return "";
184 }
185 }
186 else {
187 throw new IllegalStateException("Unsupported definition: " + definition1);
188 }
189 }
190
191
192
193
194
195 public abstract void setCssText(String value);
196
197
198
199
200
201
202
203 public abstract void setStyleAttribute(String name, String newValue, String important);
204
205
206
207
208
209
210 public abstract String removeStyleAttribute(String name);
211
212
213
214
215
216 public abstract int getLength();
217
218
219
220
221
222
223
224 public abstract String item(int index);
225
226
227
228
229
230
231
232 public abstract AbstractCSSRuleImpl getParentRule();
233
234
235
236
237
238
239
240 public abstract StyleElement getStyleElement(String name);
241
242
243
244
245
246
247
248
249 public abstract StyleElement getStyleElementCaseInSensitive(String name);
250
251
252
253
254
255
256
257 public abstract Map<String, StyleElement> getStyleMap();
258
259
260
261
262
263
264 public boolean isComputed() {
265 return false;
266 }
267
268
269
270
271
272 public String getBackgroundAttachment() {
273 String value = getStyleAttribute(Definition.BACKGROUND_ATTACHMENT, false);
274 if (StringUtils.isBlank(value)) {
275 final String bg = getStyleAttribute(Definition.BACKGROUND, true);
276 if (StringUtils.isNotBlank(bg)) {
277 value = findAttachment(bg);
278 if (value == null) {
279 if (hasFeature(CSS_BACKGROUND_INITIAL) && !isComputed()) {
280 return INITIAL;
281 }
282 return SCROLL;
283 }
284 return value;
285 }
286 return "";
287 }
288
289 return value;
290 }
291
292
293
294
295
296 public String getBackgroundColor() {
297 String value = getStyleAttribute(Definition.BACKGROUND_COLOR, false);
298 if (StringUtils.isBlank(value)) {
299 final String bg = getStyleAttribute(Definition.BACKGROUND, false);
300 if (StringUtils.isBlank(bg)) {
301 return "";
302 }
303 value = findColor(bg);
304 if (value == null) {
305 if (hasFeature(CSS_BACKGROUND_INITIAL)) {
306 if (!isComputed()) {
307 return INITIAL;
308 }
309 return "rgba(0, 0, 0, 0)";
310 }
311 if (hasFeature(CSS_BACKGROUND_RGBA)) {
312 return "rgba(0, 0, 0, 0)";
313 }
314 return "transparent";
315 }
316 return value;
317 }
318 if (StringUtils.isBlank(value)) {
319 return "";
320 }
321 return value;
322 }
323
324
325
326
327
328 public String getBackgroundImage() {
329 String value = getStyleAttribute(Definition.BACKGROUND_IMAGE, false);
330 if (StringUtils.isBlank(value)) {
331 final String bg = getStyleAttribute(Definition.BACKGROUND, false);
332 if (StringUtils.isNotBlank(bg)) {
333 value = findImageUrl(bg);
334 final boolean backgroundInitial = hasFeature(CSS_BACKGROUND_INITIAL);
335 if (value == null) {
336 return backgroundInitial && !isComputed() ? INITIAL : NONE;
337 }
338 if (isComputed()) {
339 try {
340 value = value.substring(5, value.length() - 2);
341 final DomElement domElement = ((ComputedCssStyleDeclaration) this).getDomElement();
342 return "url(\"" + domElement.getHtmlPageOrNull()
343 .getFullyQualifiedUrl(value) + "\")";
344 }
345 catch (final Exception ignored) {
346
347 }
348 }
349 return value;
350 }
351 return "";
352 }
353
354 return value;
355 }
356
357
358
359
360
361 public String getBackgroundPosition() {
362 String value = getStyleAttribute(Definition.BACKGROUND_POSITION, false);
363 if (value == null) {
364 return null;
365 }
366 if (StringUtils.isBlank(value)) {
367 final String bg = getStyleAttribute(Definition.BACKGROUND, false);
368 if (bg == null) {
369 return null;
370 }
371 if (StringUtils.isNotBlank(bg)) {
372 value = findPosition(bg);
373 final boolean isInitial = hasFeature(CSS_BACKGROUND_INITIAL);
374 if (value == null) {
375 if (isInitial) {
376 return isComputed() ? "" : INITIAL;
377 }
378 return "0% 0%";
379 }
380 if (isComputed()) {
381 final String[] values = StringUtils.splitAtBlank(value);
382 switch (values[0]) {
383 case "left":
384 values[0] = "0%";
385 break;
386
387 case "center":
388 values[0] = "50%";
389 break;
390
391 case "right":
392 values[0] = "100%";
393 break;
394
395 default:
396 }
397 switch (values[1]) {
398 case "top":
399 values[1] = "0%";
400 break;
401
402 case "center":
403 values[1] = "50%";
404 break;
405
406 case "bottom":
407 values[1] = "100%";
408 break;
409
410 default:
411 }
412 value = values[0] + ' ' + values[1];
413 }
414 return value;
415 }
416 return "";
417 }
418
419 return value;
420 }
421
422
423
424
425
426 public String getBackgroundRepeat() {
427 String value = getStyleAttribute(Definition.BACKGROUND_REPEAT, false);
428 if (StringUtils.isBlank(value)) {
429 final String bg = getStyleAttribute(Definition.BACKGROUND, false);
430 if (StringUtils.isNotBlank(bg)) {
431 value = findRepeat(bg);
432 if (value == null) {
433 if (hasFeature(CSS_BACKGROUND_INITIAL) && !isComputed()) {
434 return INITIAL;
435 }
436 return REPEAT;
437 }
438 return value;
439 }
440 return "";
441 }
442
443 return value;
444 }
445
446
447
448
449
450 public String getBlockSize() {
451 return getStyleAttribute(Definition.BLOCK_SIZE, true);
452 }
453
454
455
456
457
458 public String getBorderBottomColor() {
459 String value = getStyleAttribute(Definition.BORDER_BOTTOM_COLOR, false);
460 if (value.isEmpty()) {
461 value = findColor(getStyleAttribute(Definition.BORDER_BOTTOM, false));
462 if (value == null) {
463 value = findColor(getStyleAttribute(Definition.BORDER, false));
464 }
465 if (value == null) {
466 value = "";
467 }
468 }
469 return value;
470 }
471
472
473
474
475
476 public String getBorderBottomStyle() {
477 String value = getStyleAttribute(Definition.BORDER_BOTTOM_STYLE, false);
478 if (value.isEmpty()) {
479 value = findBorderStyle(getStyleAttribute(Definition.BORDER_BOTTOM, false));
480 if (value == null) {
481 value = findBorderStyle(getStyleAttribute(Definition.BORDER, false));
482 }
483 if (value == null) {
484 value = "";
485 }
486 }
487 return value;
488 }
489
490
491
492
493
494 public String getBorderBottomWidth() {
495 return getBorderWidth(Definition.BORDER_BOTTOM_WIDTH, Definition.BORDER_BOTTOM);
496 }
497
498
499
500
501
502 public String getBorderLeftColor() {
503 String value = getStyleAttribute(Definition.BORDER_LEFT_COLOR, false);
504 if (value.isEmpty()) {
505 value = findColor(getStyleAttribute(Definition.BORDER_LEFT, false));
506 if (value == null) {
507 value = findColor(getStyleAttribute(Definition.BORDER, false));
508 }
509 if (value == null) {
510 value = "";
511 }
512 }
513 return value;
514 }
515
516
517
518
519
520 public String getBorderLeftStyle() {
521 String value = getStyleAttribute(Definition.BORDER_LEFT_STYLE, false);
522 if (value.isEmpty()) {
523 value = findBorderStyle(getStyleAttribute(Definition.BORDER_LEFT, false));
524 if (value == null) {
525 value = findBorderStyle(getStyleAttribute(Definition.BORDER, false));
526 }
527 if (value == null) {
528 value = "";
529 }
530 }
531 return value;
532 }
533
534
535
536
537
538 public String getBorderLeftWidth() {
539 return getBorderWidth(Definition.BORDER_LEFT_WIDTH, Definition.BORDER_LEFT);
540 }
541
542
543
544
545
546
547
548 private String getBorderWidth(final Definition borderSideWidth, final Definition borderSide) {
549 String value = getStyleAttribute(borderSideWidth, false);
550 if (value.isEmpty()) {
551 value = findBorderWidth(getStyleAttribute(borderSide, false));
552 if (value == null) {
553 final String borderWidth = getStyleAttribute(Definition.BORDER_WIDTH, false);
554 if (!StringUtils.isEmptyOrNull(borderWidth)) {
555 final String[] values = StringUtils.splitAtJavaWhitespace(borderWidth);
556 int index = values.length;
557 if (borderSideWidth.name().contains("TOP")) {
558 index = 0;
559 }
560 else if (borderSideWidth.name().contains("RIGHT")) {
561 index = 1;
562 }
563 else if (borderSideWidth.name().contains("BOTTOM")) {
564 index = 2;
565 }
566 else if (borderSideWidth.name().contains("LEFT")) {
567 index = 3;
568 }
569 if (index < values.length) {
570 value = values[index];
571 }
572 }
573 }
574 if (value == null) {
575 value = findBorderWidth(getStyleAttribute(Definition.BORDER, false));
576 }
577 if (value == null) {
578 value = "";
579 }
580 }
581 return value;
582 }
583
584
585
586
587
588 public String getBorderRightColor() {
589 String value = getStyleAttribute(Definition.BORDER_RIGHT_COLOR, false);
590 if (value.isEmpty()) {
591 value = findColor(getStyleAttribute(Definition.BORDER_RIGHT, false));
592 if (value == null) {
593 value = findColor(getStyleAttribute(Definition.BORDER, false));
594 }
595 if (value == null) {
596 value = "";
597 }
598 }
599 return value;
600 }
601
602
603
604
605
606 public String getBorderRightStyle() {
607 String value = getStyleAttribute(Definition.BORDER_RIGHT_STYLE, false);
608 if (value.isEmpty()) {
609 value = findBorderStyle(getStyleAttribute(Definition.BORDER_RIGHT, false));
610 if (value == null) {
611 value = findBorderStyle(getStyleAttribute(Definition.BORDER, false));
612 }
613 if (value == null) {
614 value = "";
615 }
616 }
617 return value;
618 }
619
620
621
622
623
624 public String getBorderRightWidth() {
625 return getBorderWidth(Definition.BORDER_RIGHT_WIDTH, Definition.BORDER_RIGHT);
626 }
627
628
629
630
631
632 public String getBorderTop() {
633 return getStyleAttribute(Definition.BORDER_TOP, true);
634 }
635
636
637
638
639
640 public String getBorderTopColor() {
641 String value = getStyleAttribute(Definition.BORDER_TOP_COLOR, false);
642 if (value.isEmpty()) {
643 value = findColor(getStyleAttribute(Definition.BORDER_TOP, false));
644 if (value == null) {
645 value = findColor(getStyleAttribute(Definition.BORDER, false));
646 }
647 if (value == null) {
648 value = "";
649 }
650 }
651 return value;
652 }
653
654
655
656
657
658 public String getBorderTopStyle() {
659 String value = getStyleAttribute(Definition.BORDER_TOP_STYLE, false);
660 if (value.isEmpty()) {
661 value = findBorderStyle(getStyleAttribute(Definition.BORDER_TOP, false));
662 if (value == null) {
663 value = findBorderStyle(getStyleAttribute(Definition.BORDER, false));
664 }
665 if (value == null) {
666 value = "";
667 }
668 }
669 return value;
670 }
671
672
673
674
675
676 public String getBorderTopWidth() {
677 return getBorderWidth(Definition.BORDER_TOP_WIDTH, Definition.BORDER_TOP);
678 }
679
680
681
682
683
684 public String getBottom() {
685 return getStyleAttribute(Definition.BOTTOM, true);
686 }
687
688
689
690
691
692 public String getColor() {
693 return getStyleAttribute(Definition.COLOR, true);
694 }
695
696
697
698
699
700 public String getCssFloat() {
701 return getStyleAttribute(Definition.FLOAT, true);
702 }
703
704
705
706
707
708 public String getDisplay() {
709 return getStyleAttribute(Definition.DISPLAY, true);
710 }
711
712
713
714
715
716 public String getFont() {
717 return getStyleAttribute(Definition.FONT, true);
718 }
719
720
721
722
723
724 public String getFontFamily() {
725 return getStyleAttribute(Definition.FONT_FAMILY, true);
726 }
727
728
729
730
731
732 public String getFontSize() {
733 return getStyleAttribute(Definition.FONT_SIZE, true);
734 }
735
736
737
738
739
740 public String getHeight() {
741 return getStyleAttribute(Definition.HEIGHT, true);
742 }
743
744
745
746
747
748
749 public String getLeft() {
750 return getStyleAttribute(Definition.LEFT, true);
751 }
752
753
754
755
756
757
758 public String getLetterSpacing() {
759 return getStyleAttribute(Definition.LETTER_SPACING, true);
760 }
761
762
763
764
765
766
767 public String getLineHeight() {
768 return getStyleAttribute(Definition.LINE_HEIGHT, true);
769 }
770
771
772
773
774
775
776 public String getMargin() {
777 return getStyleAttribute(Definition.MARGIN, true);
778 }
779
780
781
782
783
784
785 public String getMarginBottom() {
786 return getStyleAttribute(Definition.MARGIN_BOTTOM, Definition.MARGIN);
787 }
788
789
790
791
792
793
794 public String getMarginLeft() {
795 return getStyleAttribute(Definition.MARGIN_LEFT, Definition.MARGIN);
796 }
797
798
799
800
801
802
803 public String getMarginRight() {
804 return getStyleAttribute(Definition.MARGIN_RIGHT, Definition.MARGIN);
805 }
806
807
808
809
810
811
812 public String getMarginTop() {
813 return getStyleAttribute(Definition.MARGIN_TOP, Definition.MARGIN);
814 }
815
816
817
818
819
820
821 public String getMaxHeight() {
822 return getStyleAttribute(Definition.MAX_HEIGHT, true);
823 }
824
825
826
827
828
829
830 public String getMaxWidth() {
831 return getStyleAttribute(Definition.MAX_WIDTH, true);
832 }
833
834
835
836
837
838
839 public String getMinHeight() {
840 return getStyleAttribute(Definition.MIN_HEIGHT, true);
841 }
842
843
844
845
846
847
848 public String getMinWidth() {
849 return getStyleAttribute(Definition.MIN_WIDTH, true);
850 }
851
852
853
854
855
856
857 public String getOpacity() {
858 final String opacity = getStyleAttribute(Definition.OPACITY, false);
859 if (opacity == null || opacity.isEmpty()) {
860 return "";
861 }
862
863 final String trimmedOpacity = opacity.trim();
864 try {
865 final double value = Double.parseDouble(trimmedOpacity);
866 if (value % 1 == 0) {
867 return Integer.toString((int) value);
868 }
869 return Double.toString(value);
870 }
871 catch (final NumberFormatException ignored) {
872
873 }
874 return "";
875 }
876
877
878
879
880
881
882 public String getOrphans() {
883 return getStyleAttribute(Definition.ORPHANS, true);
884 }
885
886
887
888
889
890
891 public String getOutline() {
892 return getStyleAttribute(Definition.OUTLINE, true);
893 }
894
895
896
897
898
899
900 public String getOutlineWidth() {
901 return getStyleAttribute(Definition.OUTLINE_WIDTH, true);
902 }
903
904
905
906
907
908
909 public String getPadding() {
910 return getStyleAttribute(Definition.PADDING, true);
911 }
912
913
914
915
916
917
918 public String getPaddingBottom() {
919 return getStyleAttribute(Definition.PADDING_BOTTOM, Definition.PADDING);
920 }
921
922
923
924
925
926
927 public String getPaddingLeft() {
928 return getStyleAttribute(Definition.PADDING_LEFT, Definition.PADDING);
929 }
930
931
932
933
934
935
936 public String getPaddingRight() {
937 return getStyleAttribute(Definition.PADDING_RIGHT, Definition.PADDING);
938 }
939
940
941
942
943
944
945 public String getPaddingTop() {
946 return getStyleAttribute(Definition.PADDING_TOP, Definition.PADDING);
947 }
948
949
950
951
952
953
954 public String getPosition() {
955 return getStyleAttribute(Definition.POSITION, true);
956 }
957
958
959
960
961
962
963 public String getRight() {
964 return getStyleAttribute(Definition.RIGHT, true);
965 }
966
967
968
969
970
971
972 public String getRubyAlign() {
973 return getStyleAttribute(Definition.RUBY_ALIGN, true);
974 }
975
976
977
978
979
980
981 public String getSize() {
982 return getStyleAttribute(Definition.SIZE, true);
983 }
984
985
986
987
988
989
990 public String getTextIndent() {
991 return getStyleAttribute(Definition.TEXT_INDENT, true);
992 }
993
994
995
996
997
998
999 public String getTop() {
1000 return getStyleAttribute(Definition.TOP, true);
1001 }
1002
1003
1004
1005
1006
1007
1008 public String getVerticalAlign() {
1009 return getStyleAttribute(Definition.VERTICAL_ALIGN, true);
1010 }
1011
1012
1013
1014
1015
1016
1017 public String getWidows() {
1018 return getStyleAttribute(Definition.WIDOWS, true);
1019 }
1020
1021
1022
1023
1024
1025
1026 public String getWidth() {
1027 return getStyleAttribute(Definition.WIDTH, true);
1028 }
1029
1030
1031
1032
1033
1034
1035 public String getWordSpacing() {
1036 return getStyleAttribute(Definition.WORD_SPACING, true);
1037 }
1038
1039
1040
1041
1042
1043
1044 public String getZIndex() {
1045 final String value = getStyleAttribute(Definition.Z_INDEX_, true);
1046 try {
1047 Integer.parseInt(value);
1048 return value;
1049 }
1050 catch (final NumberFormatException e) {
1051 return "";
1052 }
1053 }
1054
1055
1056
1057
1058
1059
1060
1061 private static String findAttachment(final String text) {
1062 if (text.contains(SCROLL)) {
1063 return SCROLL;
1064 }
1065 if (text.contains(FIXED)) {
1066 return FIXED;
1067 }
1068 return null;
1069 }
1070
1071
1072
1073
1074
1075
1076
1077 private static String findColor(final String text) {
1078 Color tmpColor = StringUtils.findColorRGB(text);
1079 if (tmpColor != null) {
1080 return StringUtils.formatColor(tmpColor);
1081 }
1082
1083 final String[] tokens = StringUtils.splitAtBlank(text);
1084 for (final String token : tokens) {
1085 if (CssColors.isColorKeyword(token)) {
1086 return token;
1087 }
1088
1089 tmpColor = StringUtils.asColorHexadecimal(token);
1090 if (tmpColor != null) {
1091 return StringUtils.formatColor(tmpColor);
1092 }
1093 }
1094 return null;
1095 }
1096
1097
1098
1099
1100
1101
1102
1103 private static String findImageUrl(final String text) {
1104 final Matcher m = URL_PATTERN.matcher(text);
1105 if (m.find()) {
1106 return "url(\"" + m.group(1) + "\")";
1107 }
1108 return null;
1109 }
1110
1111
1112
1113
1114
1115
1116
1117 private static String findPosition(final String text) {
1118 Matcher m = POSITION_PATTERN.matcher(text);
1119 if (m.find()) {
1120 return m.group(1) + " " + m.group(3);
1121 }
1122 m = POSITION_PATTERN2.matcher(text);
1123 if (m.find()) {
1124 return m.group(1) + " " + m.group(2);
1125 }
1126 m = POSITION_PATTERN3.matcher(text);
1127 if (m.find()) {
1128 return m.group(2) + " " + m.group(1);
1129 }
1130 return null;
1131 }
1132
1133
1134
1135
1136
1137
1138
1139 private static String findRepeat(final String text) {
1140 if (text.contains("repeat-x")) {
1141 return "repeat-x";
1142 }
1143 if (text.contains("repeat-y")) {
1144 return "repeat-y";
1145 }
1146 if (text.contains("no-repeat")) {
1147 return "no-repeat";
1148 }
1149 if (text.contains(REPEAT)) {
1150 return REPEAT;
1151 }
1152 return null;
1153 }
1154
1155
1156
1157
1158
1159
1160
1161 private static String findBorderStyle(final String text) {
1162 for (final String token : StringUtils.splitAtBlank(text)) {
1163 if (isBorderStyle(token)) {
1164 return token;
1165 }
1166 }
1167 return null;
1168 }
1169
1170
1171
1172
1173
1174
1175
1176 private static boolean isBorderStyle(final String token) {
1177 return NONE.equalsIgnoreCase(token) || "hidden".equalsIgnoreCase(token)
1178 || "dotted".equalsIgnoreCase(token) || "dashed".equalsIgnoreCase(token)
1179 || "solid".equalsIgnoreCase(token) || "double".equalsIgnoreCase(token)
1180 || "groove".equalsIgnoreCase(token) || "ridge".equalsIgnoreCase(token)
1181 || "inset".equalsIgnoreCase(token) || "outset".equalsIgnoreCase(token);
1182 }
1183
1184
1185
1186
1187
1188
1189
1190 private static String findBorderWidth(final String text) {
1191 for (final String token : StringUtils.splitAtBlank(text)) {
1192 if (isBorderWidth(token)) {
1193 return token;
1194 }
1195 }
1196 return null;
1197 }
1198
1199
1200
1201
1202
1203
1204
1205 private static boolean isBorderWidth(final String token) {
1206 return "thin".equalsIgnoreCase(token) || "medium".equalsIgnoreCase(token)
1207 || "thick".equalsIgnoreCase(token) || isLength(token);
1208 }
1209
1210
1211
1212
1213
1214
1215
1216 static boolean isLength(final String token) {
1217 if (token.endsWith("%")) {
1218 try {
1219 Double.parseDouble(token.substring(0, token.length() - 1));
1220 return true;
1221 }
1222 catch (final NumberFormatException ignored) {
1223
1224 }
1225 return false;
1226 }
1227
1228 if (token.endsWith("em") || token.endsWith("ex") || token.endsWith("px") || token.endsWith("in")
1229 || token.endsWith("cm") || token.endsWith("mm") || token.endsWith("pt") || token.endsWith("pc")) {
1230
1231 try {
1232 Double.parseDouble(token.substring(0, token.length() - 2));
1233 return true;
1234 }
1235 catch (final NumberFormatException ignored) {
1236
1237 }
1238 return false;
1239 }
1240
1241 return false;
1242 }
1243 }