package php.runtime.memory; import php.runtime.Memory; import php.runtime.OperatorUtils; import php.runtime.env.TraceInfo; import java.nio.charset.Charset; public class StringMemory extends Memory { String value = ""; protected final static StringMemory[] CACHED_CHARS; public StringMemory(String value) { super(Type.STRING); if (value == null) this.value = ""; else this.value = value; } public StringMemory(char ch){ this(String.valueOf(ch)); } public static Memory valueOf(String value){ if (value == null) return NULL; if (value.isEmpty()) return Memory.CONST_EMPTY_STRING; if (value.length() == 1) { return getChar(value.charAt(0)); } return new StringMemory(value); } public static Memory valueOf(char ch) { return getChar(ch); } @Override public long toLong() { return toLongNumeric().toLong(); } @Override public double toDouble() { return toNumeric().toDouble(); } @Override public boolean toBoolean() { return (value != null && !value.isEmpty() && !value.equals("0")); } @Override public String toString() { return value; } public static Memory toLong(String value){ return toLong(value, false); } public static Memory toLong(String value, boolean bigNumbers){ int len = value.length(); if (len == 0) return null; int i = 0; int start = i; boolean neg = false; for(; i < len; i++){ char ch = value.charAt(i); if (!('9' >= ch && ch >= '0')){ if (ch == '-'){ if (i == start) { neg = true; continue; } } return null; } neg = false; } if (neg) return null; try { return LongMemory.valueOf(Long.parseLong(value)); } catch (NumberFormatException e) { return bigNumbers ? StringMemory.valueOf(value) : null; } } public static Memory toNumeric(String value){ return toNumeric(value, false, CONST_INT_0); } public Memory toLongNumeric(){ return toNumeric(value, true, CONST_INT_0); } public static Memory toNumeric(String value, boolean onlyInt, Memory def){ int len = value.length(); boolean real = false; int i = 0; for(; i < len; i++){ char ch = value.charAt(i); if (ch > 32) break; } int start = i; boolean canSign = true; boolean e_char = false; if (start == len) return def; for(; i < len; i++){ char ch = value.charAt(i); if (!('9' >= ch && ch >= '0')){ if (canSign && (ch == '-' || ch == '+')){ canSign = false; // fix bug for OpenJDK 1.6 because cannot convert +1234 numbers if (start == i && ch == '+') start += 1; continue; } if (!e_char && i != start && ((ch == 'e' || ch == 'E') && value.charAt(i - 1) != '.')){ if (onlyInt) break; e_char = true; real = true; canSign = true; continue; } canSign = false; if (ch == '.'){ if (real || onlyInt) break; real = true; continue; } if (i == start || (value.charAt(i - 1) == '-' || value.charAt(i - 1) == '+')) return def; else break; } canSign = false; } if (real) { if (len == 1 || len == 0) return def; try { if (len == i && start == 0) return new DoubleMemory(Double.parseDouble(value)); else return new DoubleMemory(Double.parseDouble(value.substring(start, i))); } catch (NumberFormatException e) { return def; } } else { if (len == 0) return def; try { if (len == i && start == 0) { return LongMemory.valueOf(Long.parseLong(value)); } else { return LongMemory.valueOf(Long.parseLong(value.substring(start, i))); } } catch (NumberFormatException e) { try { if (len == i && start == 0) return new DoubleMemory(Double.parseDouble(value)); else return new DoubleMemory(Double.parseDouble(value.substring(start, i))); } catch (NumberFormatException e2) { return def; } } } } @Override public Memory toNumeric(){ return toNumeric(value); } @Override public Memory inc() { String str = toString(); if (str.isEmpty()) return CONST_INT_1; char ch = str.charAt(str.length() - 1); Memory ret = toNumeric(str, false, null); if (ret == null || !Character.isDigit(ch)){ StringBuilder sb = new StringBuilder(); int length = str.length(), i = length - 1; int type = -1; while (i >= -1){ if (i >= 0) ch = str.charAt(i); else { switch (type){ case 0: sb.append('0'); break; case 1: sb.append('a'); break; case 2: sb.append('A'); break; } break; } if (ch >= '0' && ch <= '9') type = 0; else if (ch >= 'a' && ch <= 'z') type = 1; else if (ch >= 'A' && ch <= 'Z') type = 2; else { i += 1; break; } ch++; boolean next = false; switch (type){ case 0: if (ch > '9') { ch = '0'; next = true; } break; case 1: if (ch > 'z') { ch = 'a'; next = true; } break; case 2: if (ch > 'Z') { ch = 'A'; next = true; } } if (type > -1) sb.insert(0, ch); if (!next) break; i--; } if (i > 0) sb.insert(0, str.substring(0, i)); return new StringMemory(sb.toString()); } return ret.inc(); } @Override public Memory dec() { String str = toString(); Memory ret = toNumeric(str, false, null); // hack for php if (ret == null || str.isEmpty() || !Character.isDigit(str.charAt(str.length() - 1))) return this; return ret.dec(); } @Override public Memory negative() { return toNumeric().negative(); } @Override public Memory plus(Memory memory) { return toNumeric().plus(memory); } @Override public Memory minus(Memory memory) { return toNumeric().minus(memory); } @Override public Memory mul(Memory memory) { return toNumeric().mul(memory); } @Override public Memory pow(Memory memory) { return toNumeric().pow(memory); } @Override public Memory div(Memory memory) { return toNumeric().div(memory); } @Override public Memory div(long value) { return toNumeric().div(value); } @Override public Memory div(boolean value) { return div(value ? 1 : 0); } @Override public Memory div(double value) { return toNumeric().div(value); } @Override public Memory div(String value) { return toNumeric().div(value); } @Override public boolean identical(Memory memory) { return memory.getRealType() == Type.STRING && toString().equals(memory.toString()); } @Override public boolean equal(Memory memory) { switch (memory.type){ case NULL: return toString().equals(""); case DOUBLE: case INT: return toNumeric().equal(memory); case STRING: return toString().equals(memory.toString()); case OBJECT: case ARRAY: return false; case BOOL: return memory.equal(this.value); default: return equal(memory.toValue()); } } @Override public boolean equal(long value) { return toNumeric().equal(value); } @Override public boolean equal(double value) { return toNumeric().equal(value); } @Override public boolean equal(String value) { return toString().equals(value); } @Override public boolean notEqual(Memory memory) { return !equal(memory); } @Override public Memory minus(long value) { return toNumeric().minus(value); } @Override public String concat(Memory memory) { switch (memory.type){ case STRING: return toString().concat(memory.toString()); case REFERENCE: return concat(memory.toValue()); default: return (toString() + memory.toString()); } } @Override public boolean smaller(Memory memory) { switch (memory.type){ case STRING: return toString().compareTo(memory.toString()) < 0; case REFERENCE: return smaller(memory.toValue()); } return toNumeric().smaller(memory); } @Override public boolean smaller(String value) { return toString().compareTo(value) < 0; } @Override public boolean smallerEq(Memory memory) { switch (memory.type){ case STRING: return toString().compareTo(memory.toString()) <= 0; case REFERENCE: return smaller(memory.toValue()); } return toNumeric().smallerEq(memory); } @Override public boolean smallerEq(String value) { return toString().compareTo(value) <= 0; } @Override public boolean greater(Memory memory) { switch (memory.type){ case STRING: return toString().compareTo(memory.toString()) > 0; case REFERENCE: return greater(memory.toValue()); } return toNumeric().greater(memory); } @Override public boolean greater(String value) { return toString().compareTo(value) > 0; } @Override public boolean greaterEq(Memory memory) { switch (memory.type){ case STRING: return toString().compareTo(memory.toString()) >= 0; case REFERENCE: return greaterEq(memory.toValue()); } return toNumeric().greaterEq(memory); } @Override public boolean greaterEq(String value) { return toString().compareTo(value) >= 0; } @Override public String concat(String value) { return toString().concat(value); } @Override public Memory bitAnd(Memory memory) { if(memory.isString()) return OperatorUtils.binaryAnd(this, memory); else return super.bitAnd(memory); } @Override public Memory bitAnd(String memory) { return OperatorUtils.binaryAnd(this, new StringMemory(memory)); } @Override public Memory bitOr(Memory memory) { if(memory.isString()) return OperatorUtils.binaryOr(this, memory); else return super.bitOr(memory); } @Override public Memory bitOr(String memory) { return OperatorUtils.binaryOr(this, new StringMemory(memory)); } @Override public Memory bitXor(Memory memory) { if(memory.isString()) return OperatorUtils.binaryXor(this, memory); else return super.bitXor(memory); } @Override public Memory bitXor(String memory) { return OperatorUtils.binaryXor(this, new StringMemory(memory)); } @Override public Memory bitNot() { /*Memory value = toNumeric(toString(), null); if (value != null) return value.bitNot();*/ return OperatorUtils.binaryNot(this); } @Override public Memory bitShr(Memory memory) { /*if(memory.isString()) return OperatorUtils.binaryShr(this, memory); else*/ return toLongNumeric().bitShr(memory); } @Override public Memory bitShr(String memory) { return toLongNumeric().bitShr(memory); //return OperatorUtils.binaryShr(this, new StringMemory(memory)); } @Override public Memory bitShl(Memory memory) { /*if(memory.isString()) return OperatorUtils.binaryShl(this, memory); else */ return toLongNumeric().bitShl(memory); } @Override public Memory bitShl(String memory) { return toLongNumeric().bitShl(memory); //return OperatorUtils.binaryShl(this, new StringMemory(memory)); } @Override public Memory bitShrRight(String value) { return toNumeric(value, true, CONST_INT_0).bitShr(this); //return OperatorUtils.binaryShr(new StringMemory(value), this); } @Override public Memory bitShlRight(String value) { return toNumeric(value, true, CONST_INT_0).bitShl(this); //return OperatorUtils.binaryShl(new StringMemory(value), this); } @Override public Memory valueOfIndex(TraceInfo trace, Memory index) { int _index = -1; switch (index.type){ case STRING: Memory tmp = StringMemory.toLong(index.toString()); if (tmp != null) _index = tmp.toInteger(); break; case REFERENCE: return valueOfIndex(index.toValue()); default: _index = index.toInteger(); } if (_index < toString().length() && _index >= 0) return getChar(value.charAt(_index)); else return CONST_EMPTY_STRING; } @Override public Memory valueOfIndex(TraceInfo trace, long index) { int _index = (int)index; String string = toString(); if (_index >= 0 && _index < string.length()) return getChar(string.charAt(_index)); else return CONST_EMPTY_STRING; } @Override public Memory valueOfIndex(TraceInfo trace, double index) { int _index = (int)index; String string = toString(); if (_index >= 0 && _index < string.length()) return getChar(string.charAt(_index)); else return CONST_EMPTY_STRING; } @Override public Memory valueOfIndex(TraceInfo trace, boolean index) { int _index = index ? 1 : 0; String string = toString(); if (_index >= 0 && _index < string.length()) return getChar(string.charAt(_index)); else return CONST_EMPTY_STRING; } @Override public Memory valueOfIndex(TraceInfo trace, String index) { int _index = -1; Memory tmp = StringMemory.toLong(index); if (tmp != null) _index = tmp.toInteger(); String string = toString(); if (_index >= 0 && _index < string.length()) return getChar(string.charAt(_index)); else return CONST_EMPTY_STRING; } @Override public byte[] getBinaryBytes(Charset charset) { return toString().getBytes(charset); } @Override public boolean identical(long value) { return false; } @Override public boolean identical(double value) { return false; } @Override public boolean identical(boolean value) { return false; } protected static StringMemory getChar(char ch) { int i = (int)ch; if (i >= CACHED_CHARS.length) return new StringMemory(ch); else return CACHED_CHARS[i]; } static { CACHED_CHARS = new StringMemory[256]; for(int i = 0; i < CACHED_CHARS.length; i++) { CACHED_CHARS[i] = new StringMemory((char)i); } } }