/*
* Copyright (c) 1998-2010 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.quercus.env;
import java.io.*;
import java.util.*;
import com.caucho.vfs.*;
import com.caucho.quercus.QuercusRuntimeException;
import com.caucho.quercus.marshal.Marshal;
import com.caucho.util.*;
/**
* Represents a 8-bit PHP 6 style binary builder (unicode.semantics = on)
*/
public class BinaryBuilderValue
extends StringBuilderValue {
public static final BinaryBuilderValue EMPTY = new BinaryBuilderValue("");
private final static BinaryBuilderValue[] CHAR_STRINGS;
public BinaryBuilderValue() {
super(128);
}
public BinaryBuilderValue(BinaryBuilderValue v) {
super(v);
}
public BinaryBuilderValue(int capacity) {
super(capacity);
}
public BinaryBuilderValue(byte[] buffer, int offset, int length) {
super(buffer, offset, length);
}
public BinaryBuilderValue(byte[] buffer) {
super(buffer);
}
public BinaryBuilderValue(String s) {
super(s);
}
public BinaryBuilderValue(char[] buffer) {
super(buffer);
}
public BinaryBuilderValue(char[] s, Value v1) {
super(s, v1);
}
public BinaryBuilderValue(TempBuffer head) {
this();
// php/0c4l
append(head);
}
public BinaryBuilderValue(byte ch) {
super(ch);
}
/**
* Creates the string.
*/
public static StringValue create(int value) {
if (value < CHAR_STRINGS.length) {
return CHAR_STRINGS[value];
} else {
return new BinaryBuilderValue(value);
}
}
/**
* Creates the string.
*/
public static StringValue create(char value) {
// php/3jb1
if (value < CHAR_STRINGS.length) {
return CHAR_STRINGS[value];
} else {
return new BinaryBuilderValue(value);
}
}
/**
* Returns the type.
*/
@Override
public String getType() {
return "string";
}
/**
* Returns true for a BinaryValue.
*/
@Override
public boolean isBinary() {
return true;
}
//
// marshal costs
//
/**
* Cost to convert to a byte
*/
@Override
public int toByteMarshalCost() {
if (isLongConvertible()) {
return Marshal.COST_NUMERIC_LOSSLESS;
} else if (isDoubleConvertible()) {
return Marshal.COST_NUMERIC_LOSSY;
} else {
return Marshal.COST_BINARY_TO_BYTE;
}
}
/**
* Cost to convert to a String
*/
@Override
public int toStringMarshalCost() {
return Marshal.COST_BINARY_TO_STRING;
}
/**
* Cost to convert to a char[]
*/
@Override
public int toCharArrayMarshalCost() {
return Marshal.COST_BINARY_TO_STRING + 5;
}
/**
* Cost to convert to a byte[]
*/
@Override
public int toByteArrayMarshalCost() {
return Marshal.COST_EQUAL;
}
/**
* Cost to convert to a binary value
*/
@Override
public int toBinaryValueMarshalCost() {
return Marshal.COST_IDENTICAL;
}
/**
* Cost to convert to a string value
*/
@Override
public int toStringValueMarshalCost() {
return Marshal.COST_IDENTICAL + 1;
}
/**
* Converts to a Unicode, 16-bit string.
*/
@Override
public StringValue toUnicode(Env env) {
return new UnicodeBuilderValue(this);
}
/**
* Converts to a UnicodeValue.
*/
@Override
public StringValue toUnicodeValue() {
return new UnicodeBuilderValue(this);
}
/**
* Converts to a UnicodeValue.
*/
@Override
public StringValue toUnicodeValue(Env env) {
return new UnicodeBuilderValue(this);
}
/**
* Converts to a UnicodeValue in desired charset.
*/
@Override
public StringValue toUnicodeValue(Env env, String charset) {
return toUnicodeValue(env);
}
/**
* Converts to a string builder
*/
@Override
public StringValue toStringBuilder() {
// TODO: can this just return this, or does it need to return a copy?
return new BinaryBuilderValue(this);
}
/**
* Returns the character at an index
*/
@Override
public Value charValueAt(long index) {
int len = getOffset();
if (index < 0 || len <= index) {
return UnsetBinaryValue.UNSET;
} else {
return BinaryBuilderValue.create(getBuffer()[(int) index] & 0xff);
}
}
/**
* Returns a subsequence
*/
@Override
public CharSequence subSequence(int start, int end) {
if (end <= start) {
return EMPTY;
}
return new BinaryBuilderValue(getBuffer(), start, end - start);
}
/**
* Convert to lower case.
*/
@Override
public StringValue toLowerCase() {
int length = getOffset();
BinaryBuilderValue string = new BinaryBuilderValue(length);
byte[] srcBuffer = getBuffer();
byte[] dstBuffer = string.getBuffer();
for (int i = 0; i < length; i++) {
byte ch = srcBuffer[i];
if ('A' <= ch && ch <= 'Z') {
dstBuffer[i] = (byte) (ch + 'a' - 'A');
} else {
dstBuffer[i] = ch;
}
}
string.setOffset(length);
return string;
}
/**
* Convert to lower case.
*/
@Override
public StringValue toUpperCase() {
int length = getOffset();
BinaryBuilderValue string = new BinaryBuilderValue(getOffset());
byte[] srcBuffer = getBuffer();
byte[] dstBuffer = string.getBuffer();
for (int i = 0; i < length; i++) {
byte ch = srcBuffer[i];
if ('a' <= ch && ch <= 'z') {
dstBuffer[i] = (byte) (ch + 'A' - 'a');
} else {
dstBuffer[i] = ch;
}
}
string.setOffset(length);
return string;
}
//
// append code
//
/**
* Creates a string builder of the same type.
*/
@Override
public BinaryBuilderValue createStringBuilder() {
return new BinaryBuilderValue();
}
/**
* Creates a string builder of the same type.
*/
@Override
public BinaryBuilderValue createStringBuilder(int length) {
return new BinaryBuilderValue(length);
}
/**
* Creates a string builder of the same type.
*/
@Override
public BinaryBuilderValue createStringBuilder(byte[] buffer, int offset, int length) {
return new BinaryBuilderValue(length);
}
/**
* Converts to a string builder
*/
@Override
public StringValue toStringBuilder(Env env) {
return new BinaryBuilderValue(getBuffer(), 0, getOffset());
}
/**
* Converts to a string builder
*/
@Override
public StringValue toStringBuilder(Env env, Value value) {
if (value.isUnicode()) {
UnicodeBuilderValue sb = new UnicodeBuilderValue(this);
value.appendTo(sb);
return sb;
} else {
BinaryBuilderValue v = new BinaryBuilderValue(this);
value.appendTo(v);
return v;
}
}
/**
* Converts to a string builder
*/
@Override
public StringValue toStringBuilder(Env env, StringValue value) {
if (value.isUnicode()) {
UnicodeBuilderValue sb = new UnicodeBuilderValue(this);
value.appendTo(sb);
return sb;
} else {
BinaryBuilderValue v = new BinaryBuilderValue(this);
value.appendTo(v);
return v;
}
}
/**
* Append a Java buffer to the value.
*/
// @Override
public final StringValue append(BinaryBuilderValue sb, int head, int tail) {
int length = tail - head;
byte[] buffer = getBuffer();
byte[] sbBuffer = sb.getBuffer();
int offset = getOffset();
if (buffer.length < offset + length) {
ensureCapacity(offset + length);
}
System.arraycopy(sbBuffer, head, buffer, offset, tail - head);
setOffset(offset + tail - head);
return this;
}
/**
* Append a Java buffer to the value.
*/
@Override
public final StringValue appendUnicode(char[] buf, int offset, int length) {
UnicodeBuilderValue sb = new UnicodeBuilderValue();
appendTo(sb);
sb.append(buf, offset, length);
return sb;
}
/**
* Append a Java string to the value.
*/
@Override
public final StringValue appendUnicode(String s) {
UnicodeBuilderValue sb = new UnicodeBuilderValue();
appendTo(sb);
sb.append(s);
return sb;
}
/**
* Append a Java string to the value.
*/
@Override
public final StringValue appendUnicode(String s, int start, int end) {
UnicodeBuilderValue sb = new UnicodeBuilderValue();
appendTo(sb);
sb.append(s, start, end);
return sb;
}
/**
* Append a value to the value.
*/
@Override
public final StringValue appendUnicode(Value value) {
value = value.toValue();
if (value instanceof BinaryBuilderValue) {
append((BinaryBuilderValue) value);
return this;
} else if (value.isString()) {
UnicodeBuilderValue sb = new UnicodeBuilderValue();
appendTo(sb);
sb.append(value);
return sb;
} else {
return value.appendTo(this);
}
}
/**
* Append a Java char to the value.
*/
@Override
public final StringValue appendUnicode(char ch) {
UnicodeBuilderValue sb = new UnicodeBuilderValue();
appendTo(sb);
sb.append(ch);
return sb;
}
/**
* Append a Java boolean to the value.
*/
@Override
public final StringValue appendUnicode(boolean v) {
return append(v ? "true" : "false");
}
/**
* Append a Java long to the value.
*/
@Override
public StringValue appendUnicode(long v) {
// TODO: this probably is frequent enough to special-case
return append(String.valueOf(v));
}
/**
* Append a Java double to the value.
*/
@Override
public StringValue appendUnicode(double v) {
return append(String.valueOf(v));
}
/**
* Append a Java object to the value.
*/
@Override
public StringValue appendUnicode(Object v) {
if (v instanceof String) {
return appendUnicode(v.toString());
} else {
return append(v.toString());
}
}
/**
* Append to a string builder.
*/
@Override
public StringValue appendTo(UnicodeBuilderValue sb) {
if (length() == 0) {
return sb;
}
Env env = Env.getInstance();
try {
Reader reader = env.getRuntimeEncodingFactory().create(toInputStream());
if (reader != null) {
sb.append(reader);
reader.close();
}
return sb;
} catch (IOException e) {
throw new QuercusRuntimeException(e);
}
}
/**
* Returns true for equality
*/
@Override
public boolean eq(Value rValue) {
ValueType typeA = getValueType();
ValueType typeB = rValue.getValueType();
if (typeB.isNumber()) {
double l = toDouble();
double r = rValue.toDouble();
return l == r;
} else if (typeB.isBoolean()) {
return toBoolean() == rValue.toBoolean();
} else if (typeA.isNumberCmp() && typeB.isNumberCmp()) {
double l = toDouble();
double r = rValue.toDouble();
return l == r;
}
rValue = rValue.toValue();
if (rValue instanceof BinaryBuilderValue) {
BinaryBuilderValue value = (BinaryBuilderValue) rValue;
int length = getOffset();
if (length != value.getOffset()) {
return false;
}
byte[] bufferA = getBuffer();
byte[] bufferB = value.getBuffer();
for (int i = length - 1; i >= 0; i--) {
if (bufferA[i] != bufferB[i]) {
return false;
}
}
return true;
} else {
return toString().equals(rValue.toString());
}
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o instanceof BinaryBuilderValue) {
BinaryBuilderValue value = (BinaryBuilderValue) o;
int length = getOffset();
if (length != value.getOffset()) {
return false;
}
byte[] bufferA = getBuffer();
byte[] bufferB = value.getBuffer();
for (int i = length - 1; i >= 0; i--) {
if (bufferA[i] != bufferB[i]) {
return false;
}
}
return true;
} /*
else if (o instanceof UnicodeValue) {
UnicodeValue value = (UnicodeValue)o;
return value.equals(this);
}
*/ else {
return false;
}
}
@Override
public boolean eql(Value o) {
o = o.toValue();
if (o == this) {
return true;
}
if (o instanceof BinaryBuilderValue) {
BinaryBuilderValue value = (BinaryBuilderValue) o;
int length = getOffset();
if (length != value.getOffset()) {
return false;
}
byte[] bufferA = getBuffer();
byte[] bufferB = value.getBuffer();
for (int i = length - 1; i >= 0; i--) {
if (bufferA[i] != bufferB[i]) {
return false;
}
}
return true;
} else {
return false;
}
}
@Override
public String toDebugString() {
StringBuilder sb = new StringBuilder();
int length = length();
sb.append("binary(");
sb.append(length);
sb.append(") \"");
int appendLength = length > 256 ? 256 : length;
for (int i = 0; i < appendLength; i++) {
sb.append(charAt(i));
}
if (length > 256) {
sb.append(" ...");
}
sb.append('"');
return sb.toString();
}
@Override
public void varDumpImpl(Env env,
WriteStream out,
int depth,
IdentityHashMap<Value, String> valueSet)
throws IOException {
int length = length();
if (length < 0) {
length = 0;
}
// QA needs to distinguish php5 string from php6 binary
if (Alarm.isTest()) {
out.print("binary");
} else {
out.print("string");
}
out.print("(");
out.print(length);
out.print(") \"");
for (int i = 0; i < length; i++) {
char ch = charAt(i);
if (0x20 <= ch && ch <= 0x7f || ch == '\t' || ch == '\r' || ch == '\n') {
out.print(ch);
} else if (ch <= 0xff) {
out.print("\\x"
+ Integer.toHexString(ch / 16)
+ Integer.toHexString(ch % 16));
} else {
out.print("\\u"
+ Integer.toHexString((ch >> 12) & 0xf)
+ Integer.toHexString((ch >> 8) & 0xf)
+ Integer.toHexString((ch >> 4) & 0xf)
+ Integer.toHexString((ch) & 0xf));
}
}
out.print("\"");
}
static {
CHAR_STRINGS = new BinaryBuilderValue[256];
for (int i = 0; i < CHAR_STRINGS.length; i++) {
CHAR_STRINGS[i] = new BinaryBuilderValue((byte) i);
}
}
}