/*
    XMLHandler.cpp -- Translates XML tags into objects
    Copyright (C) 2011 Marcos Alba Soler (marcos dot alba at estudiants dot urv dot cat)

    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.1 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*/

#include <fstream>

#include "InterfaceXML.h"
#include "mod_sdl/sdlsurfacetype.h"
#include "spcore/conversion.h"
#include <string.h>
#include <vector>

#include <Poco/Exception.h>
#include "Poco/SAX/Attributes.h"
#include "Poco/SAX/Locator.h"

using std::string;
using std::vector;
using mod_sdl::CTypeSDLSurface;

using boost::shared_ptr;
using Poco::XML::XMLString;
using Poco::XML::Attributes;
using Poco::XML::XMLChar;
using Poco::FileNotFoundException;

using namespace Kernel;
using namespace Pictures;

using namespace spcore;		// For conversions

namespace XMLImplementation{ 

	/*
	 * Returns a string whithout blanks and new line characters before and after it.
	 */
	string trim(string s){
		string st = s;
		//deleting of spaces before and after string
		string::size_type pos = st.find_last_not_of(' ');
		if(pos != string::npos) {
			st.erase(pos + 1);
			pos = st.find_first_not_of(' ');
			if(pos != string::npos) 
				st.erase(0, pos);
		}
		else 
			st.erase(st.begin(), st.end());
		//deleting of new lines before and after string
		pos = st.find_last_not_of('\n');
		if(pos != string::npos) {
			st.erase(pos + 1);
			pos = st.find_first_not_of('\n');
			if(pos != string::npos) 
				st.erase(0, pos);
		}
		else 
			st.erase(st.begin(), st.end());
  		return st;
	}
	/*
	 * Returns typeTransition given a string
	 */
	typeTransition getTypeFactory(string s){
		if(s.compare("ALPHA")==0) return ALPHA;
		if(s.compare("SCALE")==0) return SCALE;
		if(s.compare("ROTATE")==0) return ROTATE;
		if(s.compare("CHANGE")==0) return CHANGE;
		if(s.compare("VIBRATE")==0) return VIBRATE;
		if(s.compare("RANDOM")==0) return RANDOM;
		if(s.compare("IDENTITY")==0) return IDENTITY;
		if(s.compare("TRANSLATE")==0) return TRANSLATE;
		return typeTransition(0);
	}
	/*
	 * Returns kernelType given a string
	 */
	kernelType getTypeKernel(string s){
		if(s.compare("COLLAGE")==0) return COLLAGE;
		if(s.compare("CYCLE")==0) return CICLIC;
		return kernelType(0);
	}
	/*
	 * Returns delayType given a string
	 */
	delayType getDelayType(string s){
		if(s.compare("NODELAY")==0) return NODELAY;
		if(s.compare("RAND")==0) return RAND;
		if(s.compare("MOTION")==0) return MOTION;
		return delayType(0);
	}
	/*
	 * Returns the transitionfactory given its type, and the necessary arguments
	 */
	shared_ptr<ITransitionFactory> createTransitionFactory(typeTransition trType, SmartPtr<CTypeSDLSurface> surf, int degrees, float x, float y){
		switch(trType){
			case ALPHA:
				return shared_ptr<ITransitionFactory>(new AlphaTransitionFactory);
				break;
			case SCALE:
				return shared_ptr<ITransitionFactory>(new ScaleTransitionFactory);
				break;
			case ROTATE:
				return shared_ptr<ITransitionFactory>(new RotateTransitionFactory(degrees));
				break;
			case CHANGE:
				return shared_ptr<ITransitionFactory>(new ChangePictureTransitionFactory(surf));
				break;
			case VIBRATE:
				return shared_ptr<ITransitionFactory>(new VibratePictureTransitionFactory);
				break;
			case RANDOM:
				return shared_ptr<ITransitionFactory>(new RandomTransitionFactory);
				break;
			case IDENTITY:
				return shared_ptr<ITransitionFactory>(new IdentityTransitionFactory);
				break;
			case TRANSLATE:
				return shared_ptr<ITransitionFactory>(new TranslateTransitionFactory(x,y));
				break;
			default:
				return shared_ptr<ITransitionFactory>();
				break;
		}
	}
	/*
	 * Sets the position into the XMLFile
	 */
	void XMLHandler::setDocumentLocator(const Poco::XML::Locator* loc){
		m_pLocator = loc;
	}
	/*
	 * Constructor from class XMLHandler, initializes varibles
	 */
	XMLHandler::XMLHandler(string dir, shared_ptr<Activity> activity, shared_ptr<LogError> errors, shared_ptr<DBImages> images)
	: m_pLocator(0)
	{
		m_activitat = activity;
		m_errors = errors;
		m_image = shared_ptr<Picture>();
		m_mod = shared_ptr<Module>();
		m_defTrIn= m_defTrOut= m_trIn= m_trOut= Pictures::NIL;
		m_errorKrnl = false;
		m_errorSrc = false;
		m_trans = false;
		m_def = false;
		m_tagError = false;
		m_level = 0;
		m_dir = dir;
		m_degrees = 90;		
		m_x = 0.0f;
		m_y = 0.0f;
		m_images = images;		
	}

	/*
	 * Destructor from class XMLHandler
	 */
	XMLHandler::~XMLHandler(){
	}
	/*
	 * This method is launched when locator is at startDocument
	 */
	void XMLHandler::startDocument(){}
	/*
	 * This method is launched when locator is at endDocument
	 */
	void XMLHandler::endDocument(){}
	/*
	 * This method is launched at any start element tag.
	 * 
	 * Level code, to know in which tag we are
	 * 0 --> Document
	 * 1 --> Activity
	 * 2 --> Module
	 * 3 --> Default
	 * 3 --> Picture
	 * 4 --> Transition
	 * * */
	void XMLHandler::startElement(const XMLString& , const XMLString& localName, const XMLString& , const Attributes& attributes)
	{
		string tag = localName;
		//input string to upper case
		std::transform(tag.begin(), tag.end(), tag.begin(), ::toupper);
		//if tag is an activity and ther is no error, then level up
		if(tag.compare("ACTIVITY")==0){
			if(m_level==0&&!m_tagError){
				m_level++;
			}
		}
		//if tag is a module and there is no error then level up
		else if(tag.compare("MODULE")==0){
			if(m_level==1&&!m_tagError){
				m_level++;
				string kernel = attributes.getValue("moduleType");
				//input string to upper case
				std::transform(kernel.begin(), kernel.end(), kernel.begin(), ::toupper);
				//if no moduleType defined then it is an error creating the module
				if(kernel.size()==0){
					m_errorKrnl = true;
					shared_ptr<Error> e = shared_ptr<Error>(new Error(-12,"module","moduleType",this->m_pLocator->getLineNumber(),"Error: Required attribute missing."));
					m_errors->addError(e);
				}
				else{
					//if no delay defined dtd file has a default delay
					string delay = attributes.getValue("delayType");
					std::transform(delay.begin(), delay.end(), delay.begin(), ::toupper);
					//all list of sources in a single string, they are tokenized with a comma.
					char* ground = (char*)attributes.getValue("listSrcBackground").c_str();					
					char*  listBack = strtok(ground,",");
					vector<SmartPtr<CTypeSDLSurface> > lBg;
					SmartPtr<CTypeSDLSurface> surf = CTypeSDLSurface::CreateInstance();
					string path = m_dir;
					//while there is a source string, we split them and create a new surface if exists
					while (listBack != NULL)
					{
						//eliminate blanks and new lines
						path += trim(listBack);
						surf = m_images->getImage(path);
						path = m_dir;
						//if there is an error loading the image we inform about that
						if(surf!=NULL)
							lBg.push_back(surf);
						else{
							shared_ptr<Error> e = shared_ptr<Error>(new Error(-4,"module","listSrcBackground",this->m_pLocator->getLineNumber(),"warning: Source image of listSrcBackground doesn't exist."));
							m_errors->addError(e);
						}
						listBack = strtok (NULL, ",");
					}
					float lapse = 0.0f;
					string lap = attributes.getValue("lapseAnimation");
					//conversion from string to float, if not a number takes the default lapse: -1
					if(!StrToFloat(lap.c_str(), &lapse)) lapse = -1.0f;
					//There is no sense a lapseAnimation without any background image 
					if(lBg.size()==0){
						if(lapse> -1.0f){
							shared_ptr<Error> e = shared_ptr<Error>(new Error(-3,"module","listSrcBackground",this->m_pLocator->getLineNumber(),"warning: Attribute listSrcBackground has no sources, although lapseAnimation has been specified, so no animation."));
							m_errors->addError(e);
						}
						lapse = -1.0f;
					}
					//Also, there is no sense a list of background images without a lapseAnimation
					else{
						if(lapse<0){
							lapse = -1;
							shared_ptr<Error> e = shared_ptr<Error>(new Error(-2,"module","lapseAnimation",this->m_pLocator->getLineNumber(),"warning: Attribute lapseAnimation has an invalid value, listSrcBackground ignored."));
							m_errors->addError(e);
						}
					}
					kernelType k = getTypeKernel(kernel);
					delayType d = getDelayType(delay);
					//if kernelType and delayType are right, we can create the module
					if(k!=kernelType(0)&&d!=delayType(0))
						m_mod = shared_ptr<Module> (new Module(shared_ptr<AbstractKernelFactory>(AbstractKernelFactory::getKernelFactory(k)),d,lBg,lapse));
					//else if kernelType is wrong, then we can't create it
					else if(k==kernelType(0)){
						m_errorKrnl = true;
						shared_ptr<Error> e = shared_ptr<Error>(new Error(-15,"module","moduleType",this->m_pLocator->getLineNumber(), "Error: Attribute moduleType has an invalid value, module ignored."));
						m_errors->addError(e);
					}
					//else, then delayType is wrong, so we create the module with delayType=NODELAY which is the default
					else{
						m_mod = shared_ptr<Module> (new Module(shared_ptr<AbstractKernelFactory>(AbstractKernelFactory::getKernelFactory(k)),delayType(1),lBg,lapse));
						shared_ptr<Error> e = shared_ptr<Error>(new Error(-9,"module","delayType",this->m_pLocator->getLineNumber(),"warning: Attribute delayType has an invalid value, changed to 'NODELAY'."));
						m_errors->addError(e);
					}
				}			
			}
		}
		//if tag is a default and there is no error then level up and set a boolean to know that default tag exists
		else if(tag.compare("DEFAULT")==0){
			if(m_level==2&&!m_tagError){
				m_level++;
				m_def = true;
			}
		}
		//if tag is a picture and there is no error then level up
		else if(tag.compare("PICTURE")==0){
			if(m_level==2&&!m_tagError){
				m_level++;
				string tagSrc = attributes.getValue("src");
				//if no sources defined we can't create the picture
				if(tagSrc.size()==0){
					m_errorSrc = true;
					shared_ptr<Error> e = shared_ptr<Error>(new Error(-12,"picture","src",this->m_pLocator->getLineNumber(),"Error: Required attribute missing."));
					m_errors->addError(e);
				}
				else{
					char* srcs = (char*)tagSrc.c_str();
					//all list of sources in a single string, they are tokenized with a comma.
					char*  listPic = strtok(srcs,",");
					vector<SmartPtr<CTypeSDLSurface> > listImages; 
					SmartPtr<CTypeSDLSurface> surf = CTypeSDLSurface::CreateInstance();
					string path;
					string tagRotated = attributes.getValue("rotated");
					std::transform(tag.begin(), tag.end(), tag.begin(), ::toupper);
					int rot=0;
					if(tagRotated.size()!=0)
						//if rotated defined, convert the string to integer, if is not an integer then rotated=0 (angle is degrees);
						if(!StrToInt(tagRotated.c_str(), &rot)) rot=0;
					//while there is a source string, we split them and create a new surface if exists
					while (listPic != NULL)
					{
						path.assign(listPic);
						path = m_dir+trim(path);
						surf = m_images->getImage(path,rot);
						if(surf!=NULL)
							listImages.push_back(surf);
						//is an error with the image
						else{
							shared_ptr<Error> e = shared_ptr<Error>(new Error(-13,"picture","src",this->m_pLocator->getLineNumber(),"Error: File source doesn't exist."));
							m_errors->addError(e);
						}
						listPic = strtok (NULL, ",");
						//if there is no images we can't create the picture
						if(listImages.size()!=0){
							string quant = attributes.getValue("quantity");
							string zoom = attributes.getValue("scale");
							
							
							//if no quantity defined or not an integer it takes the default: 1
							int q= 1;
							StrToInt(quant.c_str(), &q);
							//if no scale defined or not a float it takes the default: o.25
							float scale= 0.25f;
							StrToFloat(zoom.c_str(), &scale);

							//if scale given is negative, then error launched and takes the default
							if(scale<=0){
								shared_ptr<Error> e = shared_ptr<Error>(new Error(-10,"picture","scale",this->m_pLocator->getLineNumber(),"Warning:  Attribute scale has an invalid value, changed to default (0.25)."));
								m_errors->addError(e);
								scale = 0.25;
							}
							//if quantity is negative then error launched and takes the default
							if(q<=0){
								shared_ptr<Error> e = shared_ptr<Error>(new Error(-11,"picture","quantity",this->m_pLocator->getLineNumber(),"Warning:  Attribute quantity has an invalid value, changed to default (1)."));
								m_errors->addError(e);
								q = 1;
							}
							//the position is given by two integers, the relative coordinates, beetwen parenthesis and separated with a comma
							string pos = attributes.getValue("position");
							int size = pos.size();
							//if no position defined then the position takes the default: (0,0)
							if(size!=0){
								int comma = (int)pos.find(',');
								int par1 = (int)pos.find('(');
								int par2 = (int)pos.find(')');
								if(comma!=-1&&par1!=-1&&par2!=-1){
									string sx = pos.substr(par1+1,comma);
									string sy = pos.substr(comma+1,par2-1);
									float x= 0.0f, y= 0.0f;
									//may be float is malformed
									StrToFloat (sx.c_str(), &x);
									StrToFloat (sy.c_str(), &y);
									m_image = shared_ptr<Picture>(new Picture(listImages,q,scale,x,y));
								}
								else{
									m_image = shared_ptr<Picture>(new Picture(listImages,q,scale));
									shared_ptr<Error> e = shared_ptr<Error>(new Error(-8,"picture","position",this->m_pLocator->getLineNumber(),"Warning: Attribute malformed , it should be: position=\"(x,y)\", default position: (0,0)."));
									m_errors->addError(e);
								}
							}
							else
								m_image = shared_ptr<Picture>(new Picture(listImages,q,scale));
						}
						else m_errorSrc=true;
					}
				}
			}
		}
		//if tag is a transition and there is no error then level up
		else if(tag.compare("TRANSITION")==0){
			if(m_level==3&&!m_tagError){
				m_level++;
				//indicates that ther is a transition defined
				m_trans = true;
				string io = attributes.getValue("inout");
				std::transform(io.begin(), io.end(), io.begin(), ::toupper);
				string tr = attributes.getValue("transitionType");
				std::transform(tr.begin(), tr.end(), tr.begin(), ::toupper);
				bool in;
				//if transition is not in or out then is a mix, and in and out are the same transition
				if(io.compare("IN")==0){
					m_trIn = getTypeFactory(tr);
					in = true;
				}
				else if (io.compare("OUT")==0){
					m_trOut = getTypeFactory(tr);
					in = false;
				}
				else{
					m_trIn = getTypeFactory(tr);
					m_trOut = getTypeFactory(tr);
					in = false;
				}
				//this if scope is to prevent the error that change transition is given without a source/s to change to.
				//And also to treat the attributte destinationSrc wich only takes sense with this kind of transition
				if((!m_def)&&((in&&m_trIn==typeTransition(5))||(!in&&m_trOut==typeTransition(5)))){
					string dSrc = attributes.getValue("destinationSrc");
					if(dSrc.size()>0){
						char* dSrcs = (char*)dSrc.c_str();						
						char*  listDest = strtok(dSrcs,",");
						SmartPtr<CTypeSDLSurface> surf = CTypeSDLSurface::CreateInstance();
						string path = m_dir;
						while (listDest != NULL)
						{
							path += trim(listDest);
							surf = m_images->getImage(path);
							path = m_dir;
							if(surf!=NULL)
								m_surfs.push_back(surf);
							else{
								shared_ptr<Error> e = shared_ptr<Error>(new Error(-4,"transition","destinationSrc",this->m_pLocator->getLineNumber(),"warning: Source image of destinationSrc doesn't exist."));
								m_errors->addError(e);
							}
							listDest = strtok (NULL, ",");
						}
					}
					else{
						m_errorSrc=true;
						shared_ptr<Error> e = shared_ptr<Error>(new Error(-13,"transition","destinationSrc",this->m_pLocator->getLineNumber(),"Error: File source doesn't exist."));
						m_errors->addError(e);
					}
				}
				//this if scope is to prevent the error that translate transition is given without a position to go to.
				//And also to treat the attributte destinationPos wich only takes sense with this kind of transition
				if(!m_def&&((in&&m_trIn==typeTransition(6))||(!in&&m_trOut==typeTransition(6)))){
					string pos = attributes.getValue("destinationPos");
					int size = pos.size();
					if(size!=0){
						int comma = (int)pos.find(',');
						int par1 = (int)pos.find('(');
						int par2 = (int)pos.find(')');
						if(comma!=-1&&par1!=-1&&par2!=-1){
							string sx = pos.substr(par1+1,comma);
							string sy = pos.substr(comma+1,par2-1);
							m_x= 0.0f, m_y= 0.0f;
							StrToFloat(sx.c_str(), &m_x);
							StrToFloat(sy.c_str(), &m_y);							
						}
						else{
							shared_ptr<Error> e = shared_ptr<Error>(new Error(-8,"transition","destinationPos",this->m_pLocator->getLineNumber(),"Warning: Attribute malformed , it should be: position=\"(x,y)\", default position: (0,0)."));
							m_errors->addError(e);
						}
					}

				}
				string deg = attributes.getValue("degreesRotation");
				//this rotation is for transition, default rotation is 90 degrees
				m_degrees = 90;
				StrToInt (deg.c_str(), &m_degrees);
			}
		}
		//if the tag we are in doesn't match any of the ones before then there are an error tag
		else{
			m_tagError = true;
		}
	}
	/*
	 * This method is launched at any end element tag
	 */
	void XMLHandler::endElement(const XMLString&, const XMLString& localName, const XMLString&)
	{
		string tag = localName;
		std::transform(tag.begin(), tag.end(), tag.begin(), ::toupper);
		//if the tag is an activity and there is no error then level down
		if(tag.compare("ACTIVITY")==0){
			if(!m_tagError){
				//if no modules defined in activity then we inform about that
				if(m_activitat->isEmpty()){
					shared_ptr<Error> e = shared_ptr<Error>(new Error(-5,"activity","",this->m_pLocator->getLineNumber(),"Warning: No modules defined in activity."));
					m_errors->addError(e);
				}
				m_level--;
			}
			else m_tagError = false;
		}
		//if the tag is a mdule and there is no error then level down
		else if(tag.compare("MODULE")==0){
			if(!m_tagError){
				if(m_mod.get()!=NULL){
					//if the module is right is added to the activity
					if(!m_errorKrnl){
						m_activitat->addModule(m_mod);
						//if no pictures defined in module then we inform about that
						if(m_mod->isEmpty()){
						// IGNORE: no pictures erro
						//	shared_ptr<Error> e = shared_ptr<Error>(new Error(-6,"module","",this->m_pLocator->getLineNumber(),"Warning: No pictures defined in module."));
						//	m_errors->addError(e);
						}
					}
					else m_errorKrnl = false;
					//if there is no default element we inform about that
					if(m_defTrIn == typeTransition(0) || m_defTrOut == typeTransition(0)){
						shared_ptr<Error> e = shared_ptr<Error>(new Error(-1,"default","",this->m_pLocator->getLineNumber(),"Warning: Element default missing."));
						m_errors->addError(e);
					}
					m_mod = shared_ptr<Module>();
				}
				m_level--;
			}
			else m_tagError = false;
		}
		//if the tag is a default and there is no error then level down
		else if(tag.compare("DEFAULT")==0){
			if(!m_tagError){
				//the default transition are setted
				m_defTrIn = m_trIn;
				m_defTrOut = m_trOut;
				m_def = false;
				m_level--;
			}
			else m_tagError = false;
		}
		//if the tag is a picture and there is no error then level down
		else if(tag.compare("PICTURE")==0){
			if(!m_tagError){
				if(m_level==3){
					//if there is an error with the module or the source of pictures we can't add that picture to the module
					if(!m_errorKrnl){
						if(!m_errorSrc){
							bool errDef = false;
							bool errTrans = false;
							typeTransition trIn = typeTransition(0);
							typeTransition trOut = typeTransition(0);
							//if transition 'in' type is unknown, get the default, if there is.
							if(m_trIn == typeTransition(0)){
								errTrans = true;
								if(m_defTrIn != typeTransition(0)){
									trIn = m_defTrIn;
								}
								else 
									errDef = true;
							}
							else if(m_trans) 
								trIn = m_trIn;
							else if(m_defTrIn != typeTransition(0))
								trIn = m_defTrIn;
							else errDef = true;
							//if errDef==true menas that transition is unknown an no default defined, or transition default is also unknown
							if(!errDef){
								//the same principle is applied to transition 'out'
								if(m_trOut == typeTransition(0)){
									errTrans = true;
									if(m_defTrOut != typeTransition(0)){
										trOut = m_defTrOut;
									}
									else 
										errDef = true;
								}
								else if(m_trans) 
									trOut = m_trOut;
								else if(m_defTrOut != typeTransition(0))
									trOut = m_defTrOut;
								else 
									errDef = true;	
							}
							//transitino unknown
							if(errTrans){
								shared_ptr<Error> e = shared_ptr<Error>(new Error(-7,"transition","transitionType",this->m_pLocator->getLineNumber(),"warning: Transition type unknown, getting the default if exists."));
								m_errors->addError(e);							
							}
							//if transitions are right then we can create the transitions through the factory and add the picture to the module
							if(!errDef){
								vector<shared_ptr<ITransitionFactory> > trin;
								vector<shared_ptr<ITransitionFactory> > trout;
								shared_ptr<ITransitionFactory> trf;
								if(m_surfs.size()>0){
									//we create input and output transitions for each image defined
									for(unsigned int i=0; i<m_surfs.size();i++){
										trf = createTransitionFactory(trIn,m_surfs[i],m_degrees,m_x,m_y);
										trin.push_back(trf);
										trf = createTransitionFactory(trOut,m_surfs[i],m_degrees,m_x,m_y);
										trout.push_back(trf);
									}									
								}
								else{
									trf = createTransitionFactory(trIn,NULL,m_degrees,m_x,m_y);
									trin.push_back(trf);
									trf = createTransitionFactory(trOut,NULL,m_degrees,m_x,m_y);
									trout.push_back(trf);
								}
								m_image->setTransitionInFactory(trin);
								m_image->setTransitionOutFactory(trout);
								m_mod->addPicture(m_image);
							}
							else{
								shared_ptr<Error> e = shared_ptr<Error>(new Error(-14,"transition","",this->m_pLocator->getLineNumber(),"Error: Element default missing, having pictures without transitions defined."));
								m_errors->addError(e);
							}
							m_trans = false;
						}
						else m_errorSrc = false;
						m_surfs.clear();
						m_image = shared_ptr<Picture>();
					}
					m_level--;
				}
			}
			else m_tagError = false;
		}
		//if the tag is a transition and there is no error then level down
		else if(tag.compare("TRANSITION")==0){
			if(!m_tagError){
				m_level--;
			}
			else m_tagError = false;
		}
		//if the tag we are in doesn't match any of the ones before then there are an error tag
		else{
			m_tagError = true;
		}
	}

	// CHECK THIS: aquests mčtodes no fan res i no són virtuals. Són realment necessaris?

	void XMLHandler::characters(const XMLChar ch[], int, int){}
	
	void XMLHandler::ignorableWhitespace(const XMLChar ch[], int, int){}
	
	void XMLHandler::processingInstruction(const XMLString&, const XMLString&){}
	
	void XMLHandler::startPrefixMapping(const XMLString&, const XMLString&){}
	
	void XMLHandler::endPrefixMapping(const XMLString&){}
	
	void XMLHandler::skippedEntity(const XMLString&){}

}

