blob: 8e088bc37d46e824c38530fc7d65617b1b4caeb7 [file] [log] [blame]
/*
* This file is part of the KDE libraries
* Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "kjs.h"
#include "operations.h"
#include "types.h"
#include "regexp.h"
#include "string_object.h"
#include <stdio.h>
using namespace KJS;
StringObject::StringObject(const Object &funcProto, const Object &stringProto)
: ConstructorImp(funcProto, 1)
{
// ECMA 15.5.3.1 String.prototype
setPrototypeProperty(stringProto);
}
KJSO StringObject::get(const UString &p) const
{
if (p == "fromCharCode")
return Function(new StringObjectFunc());
else
return Imp::get(p);
}
// ECMA 15.5.1
Completion StringObject::execute(const List &args)
{
KJSO v;
String s;
if (args.isEmpty())
s = String("");
else {
v = args[0];
s = v.toString();
}
return Completion(ReturnValue, s);
}
// ECMA 15.5.2
Object StringObject::construct(const List &args)
{
String s;
if (args.size() > 0)
s = args.begin()->toString();
else
s = String("");
return Object::create(StringClass, s);
}
// ECMA 15.5.3.2 fromCharCode()
Completion StringObjectFunc::execute(const List &args)
{
UString s;
if (args.size()) {
UChar *buf = new UChar[args.size()];
UChar *p = buf;
ListIterator it = args.begin();
while (it != args.end()) {
unsigned short u = it->toUInt16();
*p++ = UChar(u);
it++;
}
s = UString(buf, args.size(), false);
} else
s = "";
return Completion(ReturnValue, String(s));
}
// ECMA 15.5.4
StringPrototype::StringPrototype(const Object& proto)
: ObjectImp(StringClass, String(""), proto)
{
// The constructor will be added later in StringObject's constructor
}
KJSO StringPrototype::get(const UString &p) const
{
int id;
if (p == "toString")
id = StringProtoFunc::ToString;
else if (p == "valueOf")
id = StringProtoFunc::ValueOf;
else if (p == "charAt")
id = StringProtoFunc::CharAt;
else if (p == "charCodeAt")
id = StringProtoFunc::CharCodeAt;
else if (p == "indexOf")
id = StringProtoFunc::IndexOf;
else if (p == "lastIndexOf")
id = StringProtoFunc::LastIndexOf;
else if (p == "match")
id = StringProtoFunc::Match;
else if (p == "replace")
id = StringProtoFunc::Replace;
else if (p == "search")
id = StringProtoFunc::Search;
else if (p == "split")
id = StringProtoFunc::Split;
else if (p == "substr")
id = StringProtoFunc::Substr;
else if (p == "substring")
id = StringProtoFunc::Substring;
else if (p == "toLowerCase")
id = StringProtoFunc::ToLowerCase;
else if (p == "toUpperCase")
id = StringProtoFunc::ToUpperCase;
#ifndef KJS_PURE_ECMA
else if (p == "big")
id = StringProtoFunc::Big;
else if (p == "small")
id = StringProtoFunc::Small;
else if (p == "blink")
id = StringProtoFunc::Blink;
else if (p == "bold")
id = StringProtoFunc::Bold;
else if (p == "fixed")
id = StringProtoFunc::Fixed;
else if (p == "italics")
id = StringProtoFunc::Italics;
else if (p == "strike")
id = StringProtoFunc::Strike;
else if (p == "sub")
id = StringProtoFunc::Sub;
else if (p == "sup")
id = StringProtoFunc::Sup;
else if (p == "fontcolor")
id = StringProtoFunc::Fontcolor;
else if (p == "fontsize")
id = StringProtoFunc::Fontsize;
else if (p == "anchor")
id = StringProtoFunc::Anchor;
else if (p == "link")
id = StringProtoFunc::Link;
#endif
else
return Imp::get(p);
return Function(new StringProtoFunc(id));
}
StringProtoFunc::StringProtoFunc(int i)
: id(i)
{
}
// ECMA 15.5.4.2 - 15.5.4.20
Completion StringProtoFunc::execute(const List &args)
{
KJSO result;
Object thisObj = Object::dynamicCast(thisValue());
// toString and valueOf are no generic function.
if (id == ToString || id == ValueOf) {
if (thisObj.isNull() || thisObj.getClass() != StringClass) {
result = Error::create(TypeError);
return Completion(ReturnValue, result);
}
}
String s2;
Number n, m;
UString u, u2, u3;
int pos, p0, i;
double d, d2;
KJSO v = thisObj.internalValue();
String s = v.toString();
int len = s.value().size();
KJSO a0 = args[0];
KJSO a1 = args[1];
switch (id) {
case ToString:
case ValueOf:
result = v.toString();
break;
case CharAt:
n = a0.toInteger();
pos = (int) n.value();
if (pos < 0 || pos >= len)
u = "";
else
u = s.value().substr(pos, 1);
result = String(u);
break;
case CharCodeAt:
n = a0.toInteger();
pos = (int) n.value();
if (pos < 0 || pos >= len)
d = NaN;
else {
UChar c = s.value()[pos];
d = (c.high() << 8) + c.low();
}
result = Number(d);
break;
case IndexOf:
s2 = a0.toString();
if (a1.isA(UndefinedType))
pos = 0;
else
pos = a1.toInteger().intValue();
d = s.value().find(s2.value(), pos);
result = Number(d);
break;
case LastIndexOf:
s2 = a0.toString();
if (a1.isA(UndefinedType))
pos = len;
else
pos = a1.toInteger().intValue();
d = s.value().rfind(s2.value(), pos);
result = Number(d);
break;
case Match:
case Search:
u = s.value();
if (a0.isA(ObjectType) && a0.toObject().getClass() == RegExpClass) {
s2 = a0.get("source").toString();
RegExp reg(s2.value());
UString mstr = reg.match(u, -1, &pos);
if (id == Search) {
result = Number(pos);
break;
}
if (mstr.isNull()) {
result = Null();
break;
}
/* TODO return an array, with the matches, etc. */
result = String(mstr);
} else
{
printf("Match/Search. Argument is not a RegExp - returning Undefined\n");
result = Undefined(); // No idea what to do here
}
break;
case Replace:
/* TODO: this is just a hack to get the most common cases going */
u = s.value();
if (a0.isA(ObjectType) && a0.toObject().getClass() == RegExpClass) {
s2 = a0.get("source").toString();
RegExp reg(s2.value());
UString mstr = reg.match(u, -1, &pos);
len = mstr.size();
} else {
s2 = a0.toString();
u2 = s2.value();
pos = u.find(u2);
len = u2.size();
}
if (pos == -1)
result = s;
else {
u3 = u.substr(0, pos) + a1.toString().value() +
u.substr(pos + len);
result = String(u3);
}
break;
case Split:
result = Object::create(ArrayClass);
u = s.value();
i = p0 = 0;
d = a1.isDefined() ? a1.toInteger().intValue() : -1; // optional max number
if (a0.isA(ObjectType) && Object(a0.imp()).getClass() == RegExpClass) {
RegExp reg(a0.get("source").toString().value());
if (u.isEmpty() && !reg.match(u, 0).isNull()) {
// empty string matched by regexp -> empty array
result.put("length", 0);
break;
}
int mpos;
pos = 0;
while (1) {
/* TODO: back references */
UString mstr = reg.match(u, pos, &mpos);
if (mpos < 0)
break;
pos = mpos + (mstr.isEmpty() ? 1 : mstr.size());
if (mpos != p0 || !mstr.isEmpty()) {
result.put(UString::from(i), String(u.substr(p0, mpos-p0)));
p0 = mpos + mstr.size();
i++;
}
}
} else if (a0.isDefined()) {
u2 = a0.toString().value();
if (u2.isEmpty()) {
if (u.isEmpty()) {
// empty separator matches empty string -> empty array
put("length", 0);
break;
} else {
while (i != d && i < u.size())
result.put(UString::from(i++), String(u.substr(p0++, 1)));
}
} else {
while (i != d && (pos = u.find(u2, p0)) >= 0) {
result.put(UString::from(i), String(u.substr(p0, pos-p0)));
p0 = pos + u2.size();
i++;
}
}
}
// add remaining string, if any
if (i != d && (p0 < len || i == 0))
result.put(UString::from(i++), String(u.substr(p0)));
result.put("length", i);
break;
case Substr:
n = a0.toInteger();
m = a1.toInteger();
if (n.value() >= 0)
d = n.value();
else
d = max(len + n.value(), 0);
if (a1.isA(UndefinedType))
d2 = len - d;
else
d2 = min(max(m.value(), 0), len - d);
result = String(s.value().substr((int)d, (int)d2));
break;
case Substring:
n = a0.toInteger();
m = a1.toInteger();
d = min(max(n.value(), 0), len);
if (a1.isA(UndefinedType))
d2 = len - d;
else {
d2 = min(max(m.value(), 0), len);
d2 = max(d2-d, 0);
}
result = String(s.value().substr((int)d, (int)d2));
break;
case ToLowerCase:
u = UString(s.value());
for (i = 0; i < len; i++)
u[i] = u[i].toLower();
result = String(u);
break;
case ToUpperCase:
u = UString(s.value());
for (i = 0; i < len; i++)
u[i] = u[i].toUpper();
result = String(u);
break;
#ifndef KJS_PURE_ECMA
case Big:
result = String("<BIG>" + s.value() + "</BIG>");
break;
case Small:
result = String("<SMALL>" + s.value() + "</SMALL>");
break;
case Blink:
result = String("<BLINK>" + s.value() + "</BLINK>");
break;
case Bold:
result = String("<B>" + s.value() + "</B>");
break;
case Fixed:
result = String("<TT>" + s.value() + "</TT>");
break;
case Italics:
result = String("<I>" + s.value() + "</I>");
break;
case Strike:
result = String("<STRIKE>" + s.value() + "</STRIKE>");
break;
case Sub:
result = String("<SUB>" + s.value() + "</SUB>");
break;
case Sup:
result = String("<SUP>" + s.value() + "</SUP>");
break;
case Fontcolor:
result = String("<FONT COLOR=" + a0.toString().value() + ">"
+ s.value() + "</FONT>");
break;
case Fontsize:
result = String("<FONT SIZE=" + a0.toString().value() + ">"
+ s.value() + "</FONT>");
break;
case Anchor:
result = String("<a name=" + a0.toString().value() + ">"
+ s.value() + "</a>");
break;
case Link:
result = String("<a href=" + a0.toString().value() + ">"
+ s.value() + "</a>");
break;
#endif
}
return Completion(ReturnValue, result);
}