1 29 30 package com.caucho.quercus.env; 31 32 import com.caucho.quercus.Quercus; 33 import com.caucho.quercus.QuercusModuleException; 34 import com.caucho.vfs.TempBuffer; 35 import com.caucho.vfs.TempCharBuffer; 36 import com.caucho.vfs.TempStream; 37 import com.caucho.vfs.WriteStream; 38 39 import java.io.ByteArrayInputStream ; 40 import java.io.IOException ; 41 import java.io.InputStream ; 42 import java.io.InputStreamReader ; 43 import java.io.Reader ; 44 import java.io.UnsupportedEncodingException ; 45 import java.util.IdentityHashMap ; 46 47 50 abstract public class StringValue extends Value implements CharSequence { 51 public static final StringValue EMPTY = new StringValueImpl(""); 52 53 private final static StringValue []CHAR_STRINGS; 54 55 protected static final int IS_STRING = 0; 56 protected static final int IS_LONG = 1; 57 protected static final int IS_DOUBLE = 2; 58 59 62 public static Value create(String value) 63 { 64 if (value == null) 65 return NullValue.NULL; 66 else 67 return new StringValueImpl(value); 68 } 69 70 73 public static StringValue create(char value) 74 { 75 if (value < CHAR_STRINGS.length) 76 return CHAR_STRINGS[value]; 77 else 78 return new StringValueImpl(String.valueOf(value)); 79 } 80 81 84 public static Value create(Object value) 85 { 86 if (value == null) 87 return NullValue.NULL; 88 else 89 return new StringValueImpl(value.toString()); 90 } 91 92 96 99 public String getType() 100 { 101 return "string"; 102 } 103 104 107 public boolean isLongConvertible() 108 { 109 int len = length(); 110 111 if (len == 0) 112 return true; 113 114 int i = 0; 115 char ch = charAt(0); 116 117 if (ch == '-' || ch == '+') 118 i++; 119 120 for (; i < len; i++) { 121 ch = charAt(i); 122 123 if (! ('0' <= ch && ch <= '9')) 124 return false; 125 } 126 127 return true; 128 } 129 130 133 public boolean isDoubleConvertible() 134 { 135 return getNumericType() == IS_DOUBLE; 136 } 137 138 141 public boolean isNumber() 142 { 143 return getNumericType() != IS_STRING; 144 } 145 146 149 @Override 150 public boolean isNumeric() 151 { 152 154 return getNumericType() != IS_STRING; 155 } 156 157 160 public boolean isScalar() 161 { 162 return true; 163 } 164 165 168 @Override 169 public boolean isString() 170 { 171 return true; 172 } 173 174 177 public boolean eq(Value rValue) 178 { 179 rValue = rValue.toValue(); 180 181 if (rValue instanceof BooleanValue) { 182 return toBoolean() == rValue.toBoolean(); 183 } 184 185 int type = getNumericType(); 186 187 if (type == IS_STRING) { 188 if (rValue instanceof StringValue) 189 return equals(rValue); 190 else if (rValue.isLongConvertible()) 191 return toLong() == rValue.toLong(); 192 else if (rValue instanceof BooleanValue) 193 return toLong() == rValue.toLong(); 194 else 195 return equals(rValue.toStringValue()); 196 } 197 else if (rValue.isNumberConvertible()) 198 return toDouble() == rValue.toDouble(); 199 else 200 return equals(rValue.toStringValue()); 201 } 202 203 206 public int cmp(Value rValue) 207 { 208 if (isNumberConvertible() || rValue.isNumberConvertible()) { 209 double l = toDouble(); 210 double r = rValue.toDouble(); 211 212 if (l == r) 213 return 0; 214 else if (l < r) 215 return -1; 216 else 217 return 1; 218 } 219 else 220 return toString().compareTo(rValue.toString()); 221 } 222 223 226 public boolean eql(Value rValue) 227 { 228 rValue = rValue.toValue(); 229 230 if (! (rValue instanceof StringValue)) 231 return false; 232 233 String rString = rValue.toString(); 234 235 return toString().equals(rString); 236 } 237 238 241 public int cmpString(StringValue rValue) 242 { 243 if (isNumberConvertible() && rValue.isNumberConvertible()) { 244 245 double thisDouble = toDouble(); 246 247 double rDouble = rValue.toDouble(); 248 249 if (thisDouble < rDouble) 250 return -1; 251 else if (thisDouble > rDouble) 252 return 1; 253 else 254 return 0; 255 } 256 return toString().compareTo(rValue.toString()); 257 } 258 259 262 protected int getNumericType() 263 { 264 int len = length(); 265 266 if (len == 0) 267 return IS_STRING; 268 269 int i = 0; 270 int ch = 0; 271 boolean hasPoint = false; 272 273 if (i < len && ((ch = charAt(i)) == '+' || ch == '-')) { 274 i++; 275 } 276 277 if (len <= i) 278 return IS_STRING; 279 280 ch = charAt(i); 281 282 if (ch == '.') { 283 for (i++; i < len && '0' <= (ch = charAt(i)) && ch <= '9'; i++) { 284 return IS_DOUBLE; 285 } 286 287 return IS_STRING; 288 } 289 else if (! ('0' <= ch && ch <= '9')) 290 return IS_STRING; 291 292 for (; i < len && '0' <= (ch = charAt(i)) && ch <= '9'; i++) { 293 } 294 295 if (len <= i) 296 return IS_LONG; 297 else if (ch == '.' || ch == 'e' || ch == 'E') { 298 for (i++; 299 i < len && ('0' <= (ch = charAt(i)) && ch <= '9' || 300 ch == '+' || ch == '-' || ch == 'e' || ch == 'E'); 301 i++) { 302 } 303 304 if (i < len) 305 return IS_STRING; 306 else 307 return IS_DOUBLE; 308 } 309 else 310 return IS_STRING; 311 } 312 313 315 318 public StringValue toStringValue() 319 { 320 return this; 321 } 322 323 326 public static long toLong(String string) 327 { 328 if (string.equals("")) 329 return 0; 330 331 int len = string.length(); 332 333 long value = 0; 334 long sign = 1; 335 336 int i = 0; 337 char ch = string.charAt(0); 338 339 if (ch == '-') { 340 sign = -1; 341 i = 1; 342 } 343 else if (ch == '+') 344 i = 1; 345 346 for (; i < len; i++) { 347 ch = string.charAt(i); 348 349 if ('0' <= ch && ch <= '9') 350 value = 10 * value + ch - '0'; 351 else 352 return sign * value; 353 } 354 355 return value; 356 } 357 358 361 public double toDouble() 362 { 363 int len = length(); 364 int i = 0; 365 int ch = 0; 366 367 if (i < len && ((ch = charAt(i)) == '+' || ch == '-')) { 368 i++; 369 } 370 371 for (; i < len && '0' <= (ch = charAt(i)) && ch <= '9'; i++) { 372 } 373 374 if (ch == '.') { 375 for (i++; i < len && '0' <= (ch = charAt(i)) && ch <= '9'; i++) { 376 } 377 } 378 379 if (ch == 'e' || ch == 'E') { 380 int e = i++; 381 382 if (i < len && (ch = charAt(i)) == '+' || ch == '-') { 383 i++; 384 } 385 386 for (; i < len && '0' <= (ch = charAt(i)) && ch <= '9'; i++) { 387 } 388 389 if (i == e + 1) 390 i = e; 391 } 392 393 if (i == 0) 394 return 0; 395 else if (i == len) 396 return Double.parseDouble(toString()); 397 else 398 return Double.parseDouble(substring(0, i).toString()); 399 } 400 401 404 public boolean toBoolean() 405 { 406 int length = length(); 407 408 if (length == 0) 409 return false; 410 else if (length > 1) 411 return true; 412 else 413 return charAt(0) != '0'; 414 } 415 416 419 public Value toKey() 420 { 421 int len = length(); 422 423 if (len == 0) 424 return this; 425 426 int sign = 1; 427 long value = 0; 428 429 int i = 0; 430 char ch = charAt(i); 431 if (ch == '-') { 432 sign = -1; 433 i++; 434 } 435 436 for (; i < len; i++) { 437 ch = charAt(i); 438 439 if ('0' <= ch && ch <= '9') 440 value = 10 * value + ch - '0'; 441 else 442 return this; 443 } 444 445 return LongValue.create(sign * value); 446 } 447 448 451 public Object toJavaObject() 452 { 453 return toString(); 454 } 455 456 460 @Override 461 public Object valuesToArray(Env env, Class elementType) 462 { 463 if (char.class.equals(elementType)) { 464 return toUnicodeValue(env).toCharArray(); 465 } 466 else if (Character .class.equals(elementType)) { 467 char[] chars = toUnicodeValue(env).toCharArray(); 468 469 int length = chars.length; 470 471 Character [] charObjects = new Character [length]; 472 473 for (int i = 0; i <length; i++) { 474 charObjects[i] = Character.valueOf(chars[i]); 475 } 476 477 return charObjects; 478 } 479 else if (byte.class.equals(elementType)) { 480 return toBinaryValue(env).toBytes(); 481 } 482 else if (Byte .class.equals(elementType)) { 483 byte[] bytes = toBinaryValue(env).toBytes(); 484 485 int length = bytes.length; 486 487 Byte [] byteObjects = new Byte [length]; 488 489 for (int i = 0; i <length; i++) { 490 byteObjects[i] = Byte.valueOf(bytes[i]); 491 } 492 493 return byteObjects; 494 } 495 else { 496 env.error(L.l("Can't assign {0} with type {1} to {2}", this, this.getClass(), elementType)); 497 return null; 498 } 499 } 500 501 504 public Value toAutoArray() 505 { 506 if (length() == 0) 507 return new ArrayValueImpl(); 508 else 509 return this; 510 } 511 512 514 517 public Value get(Value key) 518 { 519 return charValueAt(key.toLong()); 520 } 521 522 525 public Value getArg(Value key) 526 { 527 return charValueAt(key.toLong()); 529 } 530 531 534 public Value getRef(Value key) 535 { 536 return charValueAt(key.toLong()); 537 } 538 539 542 @Override 543 public Value charValueAt(long index) 544 { 545 int len = length(); 546 547 if (index < 0 || len <= index) 548 return StringValue.EMPTY; 549 else { 550 return StringValue.create(charAt((int) index)); 551 } 552 } 553 554 557 @Override 558 public Value setCharValueAt(long index, String value) 559 { 560 int len = length(); 561 562 if (index < 0 || len <= index) 563 return this; 564 else { 565 return (new StringBuilderValue() 566 .append(this, 0, (int) index) 567 .append(value) 568 .append(this, (int) (index + 1), length())); 569 } 570 } 571 572 575 public Value preincr(int incr) 576 { 577 return postincr(incr); 578 } 579 580 583 public Value postincr(int incr) 584 { 585 if (length() == 0) 587 return LongValue.create(incr); 588 589 if (incr > 0) { 590 StringBuilder tail = new StringBuilder (); 591 592 for (int i = length() - 1; i >= 0; i--) { 593 char ch = charAt(i); 594 595 if (ch == 'z') { 596 if (i == 0) 597 return new StringBuilderValue().append("aa").append(tail); 598 else 599 tail.insert(0, 'a'); 600 } 601 else if ('a' <= ch && ch < 'z') { 602 return (new StringBuilderValue() 603 .append(this, 0, i) 604 .append((char) (ch + 1)) 605 .append(tail)); 606 } 607 else if (ch == 'Z') { 608 if (i == 0) 609 return new StringBuilderValue().append("AA").append(tail); 610 else 611 tail.insert(0, 'A'); 612 } 613 else if ('A' <= ch && ch < 'Z') { 614 return (new StringBuilderValue() 615 .append(this, 0, i) 616 .append((char) (ch + 1)) 617 .append(tail)); 618 } 619 else if ('0' <= ch && ch <= '9' && i == length() - 1) { 620 return LongValue.create(toLong() + 1); 621 } 622 } 623 624 return new StringBuilderValue(tail.toString()); 625 } 626 else if (isLongConvertible()) { 627 return LongValue.create(toLong() - 1); 628 } 629 else { 630 return this; 631 } 632 } 633 634 637 public Value add(long rValue) 638 { 639 if (isLongConvertible()) 640 return LongValue.create(toLong() + rValue); 641 642 return DoubleValue.create(toDouble() + rValue); 643 } 644 645 648 @Override 649 public void serialize(StringBuilder sb) 650 { 651 sb.append("s:"); 652 sb.append(length()); 653 sb.append(":\""); 654 sb.append(toString()); 655 sb.append("\";"); 656 } 657 658 662 665 public StringValue append(String s) 666 { 667 throw new UnsupportedOperationException (getClass().getName()); 668 } 669 670 673 public StringValue append(String s, int start, int end) 674 { 675 throw new UnsupportedOperationException (getClass().getName()); 676 } 677 678 681 public StringValue append(char []buf, int offset, int length) 682 { 683 throw new UnsupportedOperationException (getClass().getName()); 684 } 685 686 689 public final StringValue append(char []buf) 690 { 691 return append(buf, 0, buf.length); 692 } 693 694 697 public StringValue append(CharSequence buf, int head, int tail) 698 { 699 throw new UnsupportedOperationException (getClass().getName()); 700 } 701 702 705 public StringValue append(StringBuilderValue sb, int head, int tail) 706 { 707 throw new UnsupportedOperationException (getClass().getName()); 708 } 709 710 713 public StringValue append(char v) 714 { 715 throw new UnsupportedOperationException (getClass().getName()); 716 } 717 718 721 public StringValue append(byte v) 722 { 723 throw new UnsupportedOperationException (getClass().getName()); 724 } 725 726 729 public StringValue append(boolean v) 730 { 731 return append(v ? "true" : "false"); 732 } 733 734 737 public StringValue append(long v) 738 { 739 return append(String.valueOf(v)); 740 } 741 742 745 public StringValue append(double v) 746 { 747 return append(String.valueOf(v)); 748 } 749 750 753 public StringValue append(Object v) 754 { 755 return append(String.valueOf(v)); 756 } 757 758 761 public StringValue append(Value v) 762 { 763 throw new UnsupportedOperationException (getClass().getName()); 764 } 765 766 769 @Override 770 public void appendTo(StringBuilderValue sb) 771 { 772 int length = length(); 773 774 for (int i = 0; i < length; i++) 775 sb.append(charAt(i)); 776 } 777 778 781 @Override 782 public void varExport(StringBuilder sb) 783 { 784 sb.append("'"); 785 786 String value = toString(); 787 int len = value.length(); 788 for (int i = 0; i < len; i++) { 789 char ch = value.charAt(i); 790 791 switch (ch) { 792 case '\'': 793 sb.append("\\'"); 794 break; 795 case '\\': 796 sb.append("\\\\"); 797 break; 798 default: 799 sb.append(ch); 800 } 801 } 802 sb.append("'"); 803 } 804 805 808 public InternStringValue intern(Quercus quercus) 809 { 810 return quercus.intern(toString()); 811 } 812 813 817 820 public int length() 821 { 822 return toString().length(); 823 } 824 825 828 public char charAt(int index) 829 { 830 return toString().charAt(index); 831 } 832 833 836 public CharSequence subSequence(int start, int end) 837 { 838 return new StringValueImpl(toString().substring(start, end)); 839 } 840 841 845 848 public final int indexOf(CharSequence match) 849 { 850 return indexOf(match, 0); 851 } 852 853 856 public int indexOf(CharSequence match, int head) 857 { 858 int length = length(); 859 int matchLength = match.length(); 860 861 if (matchLength <= 0) 862 return -1; 863 else if (head < 0) 864 return -1; 865 866 int end = length - matchLength; 867 char first = match.charAt(0); 868 869 loop: 870 for (; head <= end; head++) { 871 if (charAt(head) != first) 872 continue; 873 874 for (int i = 1; i < matchLength; i++) { 875 if (charAt(head + i) != match.charAt(i)) 876 continue loop; 877 } 878 879 return head; 880 } 881 882 return -1; 883 } 884 885 888 public int indexOf(char match) 889 { 890 return lastIndexOf(match, 0); 891 } 892 893 896 public int indexOf(char match, int head) 897 { 898 int length = length(); 899 900 for (; head < length; head++) { 901 if (charAt(head) == match) 902 return head; 903 } 904 905 return -1; 906 } 907 908 911 public final int lastIndexOf(char match) 912 { 913 return lastIndexOf(match, Integer.MAX_VALUE); 914 } 915 916 919 public int lastIndexOf(char match, int tail) 920 { 921 int length = length(); 922 923 if (tail >= length) 924 tail = length - 1; 925 926 for (; tail >= 0; tail--) { 927 if (charAt(tail) == match) 928 return tail; 929 } 930 931 return -1; 932 } 933 934 937 public int lastIndexOf(CharSequence match) 938 { 939 return lastIndexOf(match, Integer.MAX_VALUE); 940 } 941 942 945 public int lastIndexOf(CharSequence match, int tail) 946 { 947 int length = length(); 948 int matchLength = match.length(); 949 950 if (matchLength <= 0) 951 return -1; 952 if (tail < 0) 953 return -1; 954 955 if (tail > length - matchLength) 956 tail = length - matchLength; 957 958 char first = match.charAt(0); 959 960 loop: 961 for (; tail >= 0; tail--) { 962 if (charAt(tail) != first) 963 continue; 964 965 for (int i = 1; i < matchLength; i++) { 966 if (charAt(tail + i) != match.charAt(i)) 967 continue loop; 968 } 969 970 return tail; 971 } 972 973 return -1; 974 } 975 976 979 public StringValue substring(int head) 980 { 981 return (StringValue) subSequence(head, length()); 982 } 983 984 987 public StringValue substring(int begin, int end) 988 { 989 return (StringValue) subSequence(begin, end); 990 } 991 992 995 public char []toCharArray() 996 { 997 int length = length(); 998 999 char []array = new char[length()]; 1000 1001 getChars(0, array, 0, length); 1002 1003 return array; 1004 } 1005 1006 1009 public void getChars(int stringOffset, char []buffer, int offset, int length) 1010 { 1011 for (int i = 0; i < length; i++) 1012 buffer[offset + i] = charAt(stringOffset + i); 1013 } 1014 1015 1018 public StringValue toLowerCase() 1019 { 1020 int length = length(); 1021 1022 StringBuilderValue string = new StringBuilderValue(length); 1023 1024 char []buffer = string.getBuffer(); 1025 getChars(0, buffer, 0, length); 1026 1027 for (int i = 0; i < length; i++) { 1028 char ch = buffer[i]; 1029 1030 if ('A' <= ch && ch <= 'Z') 1031 buffer[i] = (char) (ch + 'a' - 'A'); 1032 else if (ch < 0x80) { 1033 } 1034 else if (Character.isUpperCase(ch)) 1035 buffer[i] = Character.toLowerCase(ch); 1036 } 1037 1038 string.setLength(length); 1039 1040 return string; 1041 } 1042 1043 1046 public StringValue toUpperCase() 1047 { 1048 int length = length(); 1049 1050 StringBuilderValue string = new StringBuilderValue(length); 1051 1052 char []buffer = string.getBuffer(); 1053 getChars(0, buffer, 0, length); 1054 1055 for (int i = 0; i < length; i++) { 1056 char ch = buffer[i]; 1057 1058 if ('a' <= ch && ch <= 'z') 1059 buffer[i] = (char) (ch + 'A' - 'a'); 1060 else if (ch < 0x80) { 1061 } 1062 else if (Character.isLowerCase(ch)) 1063 buffer[i] = Character.toUpperCase(ch); 1064 } 1065 1066 string.setLength(length); 1067 1068 return string; 1069 } 1070 1071 1077 public InputStream toInputStream() 1078 { 1079 return new StringValueInputStream(); 1080 } 1081 1082 1086 public InputStream toInputStream(String charset) 1087 throws UnsupportedEncodingException 1088 { 1089 return new ByteArrayInputStream (toString().getBytes(charset)); 1090 } 1091 1092 1098 public Reader toReader(String charset) 1099 throws UnsupportedEncodingException 1100 { 1101 return new InputStreamReader ( 1102 new ByteArrayInputStream (toString().getBytes()), charset); 1103 } 1104 1105 1111 public BinaryValue toBinaryValue(Env env, String charset) 1112 { 1113 TempBuffer tb = TempBuffer.allocate(); 1114 byte[] buffer = tb.getBuffer(); 1115 1116 try { 1117 InputStream in = toInputStream(charset); 1118 TempStream out = new TempStream(); 1119 1120 int sublen = in.read(buffer, 0, buffer.length); 1121 1122 while (sublen >= 0) { 1123 out.write(buffer, 0, sublen, false); 1124 sublen = in.read(buffer, 0, buffer.length); 1125 } 1126 1127 out.flush(); 1128 return new TempBufferStringValue(out.getHead()); 1129 1130 } catch (IOException e) { 1131 throw new QuercusModuleException(e.getMessage()); 1132 } finally { 1133 TempBuffer.free(tb); 1134 } 1135 } 1136 1137 1143 public UnicodeValue toUnicodeValue(Env env, String charset) 1144 { 1145 StringBuilderValue sb = new StringBuilderValue(); 1146 1147 TempCharBuffer tb = TempCharBuffer.allocate(); 1148 char[] charBuf = tb.getBuffer(); 1149 1150 try { 1151 Reader in = toReader(charset); 1152 1153 int sublen; 1154 while ((sublen = in.read(charBuf, 0, charBuf.length)) >= 0) { 1155 sb.append(charBuf, 0, sublen); 1156 } 1157 1158 } catch (IOException e) { 1159 throw new QuercusModuleException(e.getMessage()); 1160 1161 } finally { 1162 TempCharBuffer.free(tb); 1163 } 1164 1165 return sb; 1166 } 1167 1168 1172 1175 public int hashCode() 1176 { 1177 int hash = 37; 1178 1179 int length = length(); 1180 1181 for (int i = 0; i < length; i++) { 1182 hash = 65521 * hash + charAt(i); 1183 } 1184 1185 return hash; 1186 } 1187 1188 1191 public boolean equals(Object o) 1192 { 1193 if (this == o) 1194 return true; 1195 else if (! (o instanceof StringValue)) 1196 return false; 1197 1198 StringValue s = (StringValue) o; 1199 1200 int aLength = length(); 1201 int bLength = s.length(); 1202 1203 if (aLength != bLength) 1204 return false; 1205 1206 for (int i = aLength - 1; i >= 0; i--) { 1207 if (charAt(i) != s.charAt(i)) 1208 return false; 1209 } 1210 1211 return true; 1212 } 1213 1214 public void varDumpImpl(Env env, 1215 WriteStream out, 1216 int depth, 1217 IdentityHashMap <Value, String > valueSet) 1218 throws IOException 1219 { 1220 String s = toString(); 1221 1222 out.print("string(" + s.length() + ") \"" + s + "\""); 1223 } 1224 1225 class StringValueInputStream extends java.io.InputStream { 1226 private final int _length; 1227 private int _index; 1228 1229 StringValueInputStream() 1230 { 1231 _length = length(); 1232 } 1233 1234 1237 public int read() 1238 { 1239 if (_index < _length) 1240 return charAt(_index++); 1241 else 1242 return -1; 1243 } 1244 1245 1248 public int read(byte []buffer, int offset, int length) 1249 { 1250 int sublen = _length - _index; 1251 1252 if (length < sublen) 1253 sublen = length; 1254 1255 if (sublen <= 0) 1256 return -1; 1257 1258 int index = _index; 1259 1260 for (int i = 0; i < sublen; i++) 1261 buffer[offset + i] = (byte) charAt(index + i); 1262 1263 _index += sublen; 1264 1265 return sublen; 1266 } 1267 } 1268 1269 static { 1270 CHAR_STRINGS = new StringValue[256]; 1271 1272 for (int i = 0; i < CHAR_STRINGS.length; i++) 1273 CHAR_STRINGS[i] = new StringValueImpl(String.valueOf((char) i)); 1274 } 1275} 1276 1277 | Popular Tags |