function SerializedDataParser(type_desc)
{
	this._parse_type_desc(type_desc);
}

SerializedDataParser.prototype.unserialize=function(serialized_data)
{
	this.serialized_data=serialized_data;
	this.offset=0;
	return this._unserialize('root', null);
}

SerializedDataParser.prototype._parse_type_desc=function(type_desc)
{
	var STATE_STRUCT_TYPE=0;
	var STATE_STRUCT_CONTENT=1;
	var STATE_STRUCT_MEMBER_NAME=2;
	var STATE_STRUCT_MEMBER_BASE_TYPE=3;
	var STATE_STRUCT_MEMBER_SUBSCRIPT_BEGIN=4;
	var STATE_STRUCT_MEMBER_SUBSCRIPT_END=5;
	var state=STATE_STRUCT_TYPE;
	var current_type;
	var member_count;
	var word='';
	
	this.type_info=new Object();
	for (var offset=0; offset < type_desc.length; ++offset) {
		var c=type_desc.charAt(offset);
		if (state==STATE_STRUCT_TYPE) {
			if (c=='{') {
				if (!word.length)
					throw "type name is missing";
				// Create type
				this.type_info[word]=current_type=new Object();
				current_type.members=new Object();
				member_count=0;
				state=STATE_STRUCT_MEMBER_NAME;
				word='';
			} else
				word+=c;
		} else if (state==STATE_STRUCT_MEMBER_NAME) {
			if (c==':') {
				if (!word.length)
					throw "member name is missing";
				current_type[word]=current_member=new Object();
				current_type.members[member_count++]=word;
				state=STATE_STRUCT_MEMBER_BASE_TYPE;
				word='';
			} else if (c=='}') {
				state=STATE_STRUCT_TYPE;
				word='';
			} else
				word+=c;
		} else if (state==STATE_STRUCT_MEMBER_BASE_TYPE) {
			if (c=='[') {
				if (word!='dict' && word!='list')
					throw "only dict or list type requires a subscript type, type is" + word;
				else {
					current_member.type=word;
					state=STATE_STRUCT_MEMBER_SUBSCRIPT_BEGIN;
					word='';
				}
			} else if (c==';') {
				if (word=='string') {
					current_member.type=word;
					current_member.encoding='none';
					state=STATE_STRUCT_MEMBER_NAME;
					word='';
				} else if (word=='dict' || word=='list')
					throw "dict or list requires a subscript type";
				else
					throw "Unexpected type";
			} else
				word+=c;
		} else if (state==STATE_STRUCT_MEMBER_SUBSCRIPT_BEGIN) {
			if (c==']') {
				state=STATE_STRUCT_MEMBER_SUBSCRIPT_END;
				if (this.type_info[word]==null)
					throw "undefined type: " + word;
				current_member.subscript_type=word;
				word='';
			} else
				word+=c;
		} else if (state==STATE_STRUCT_MEMBER_SUBSCRIPT_END) {
			if (c==';')
				state=STATE_STRUCT_MEMBER_NAME;
			else
				throw "; expected";
		}
	}				
}
SerializedDataParser.prototype._unserialize=function(base_type, subscript_type)
{
	if (base_type=='string') {
		var pos=this.serialized_data.indexOf(';', this.offset);
		if (pos==-1)
			throw "Expected ';' when parsing string";
		var str=this.serialized_data.substr(this.offset, pos - this.offset);
		this.offset=pos + 1;
		return unescape(str);
	} else if (base_type=='list') {
		var list=new Array();
		var index=0;
		while (this.offset < this.serialized_data.length) {
			if (this.serialized_data.charAt(this.offset)=='{') {
				this.offset++;
				result=this._unserialize(subscript_type, null)
				if (result)
					list[index++]=result;
				else
					throw "Error when unserializing type " + subscript_type;
				if (this.serialized_data.charAt(this.offset)!='}')
					throw "Expected }";
				else
					++this.offset;
			} else if (this.serialized_data.charAt(this.offset)==';') {
				++this.offset;
				break;
			} else
				throw "list Unexpected character " + this.offset;
		}
		return list;
	} else if (base_type=='dict') {
		var dict=new Object();
		while (this.offset < this.serialized_data.length) {
			if (this.serialized_data.charAt(this.offset)=='{') {
				++this.offset;	
				var pos=this.serialized_data.indexOf('#', this.offset);
				if (pos==-1)
					throw "Expected '#' when parsing string";
				var key=this.serialized_data.substr(this.offset, pos - this.offset);
				this.offset=pos + 1;
				result=this._unserialize(subscript_type, null)
				if (result)
					dict[key]=result;
				else
					throw "Error when unserializing type " + subscript_type;
				if (this.serialized_data.charAt(this.offset)!='}')
					throw "Expected }";
				else
					++this.offset;
			} else if (this.serialized_data.charAt(this.offset)==';') {
				++this.offset;
				break;
			} else
				throw "dict Unexpected character" + this.offset;
		}
		return dict;
	} else {
		var current_type=this.type_info[base_type];
		var dict=new Object();
		for (index in current_type.members) {
			var member_name=current_type.members[index];
			result=this._unserialize(current_type[member_name].type, current_type[member_name].subscript_type);
			if (result)
				dict[member_name]=result;
			else
				throw "Error when unserializing member " + member_name;
		}
		return dict;
	}	
}
