generator.cc 13.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
linphone
Copyright (C) 2013 Belledonne Communications SARL
Simon Morlat (simon.morlat@linphone.org)

This program 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.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
18
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 20 21 22 23 24 25
*/


#include <sstream>

#include "generator.hh"

26
#ifdef _WIN32
27
#include <direct.h>
28 29

#define strncasecmp _strnicmp
30 31 32
#endif


Simon Morlat's avatar
Simon Morlat committed
33 34 35 36 37 38 39 40
string to_lower(const string &str){
	string res=str;
	for(string::iterator it=res.begin();it!=res.end();++it){
		*it=tolower(*it);
	}
	return res;
}

41 42 43 44 45 46
CplusplusGenerator::CplusplusGenerator(){
}

void CplusplusGenerator::generate(Project *proj){
	list<Class*> classes=proj->getClasses();
	mCurProj=proj;
47
#ifndef _WIN32
48 49 50 51 52 53 54
	mkdir(proj->getName().c_str(),S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IROTH);
#else
	_mkdir(proj->getName().c_str());
#endif
	for_each(classes.begin(),classes.end(),bind1st(mem_fun(&CplusplusGenerator::writeClass),this));
}

Simon Morlat's avatar
Simon Morlat committed
55 56
void CplusplusGenerator::writeEnumMember(ConstField *cf, bool isLast){
	writeTabs(1);
57
	mOutfile<<cf->getName()<<"="<<cf->getValue();
Simon Morlat's avatar
Simon Morlat committed
58 59 60 61 62
	if (!isLast) mOutfile<<",";
	if (!cf->getHelp().empty()) mOutfile<<"\t/**< "<<cf->getHelp()<<" */";
	mOutfile<<endl;
}

63 64
void CplusplusGenerator::writeClass(Class *klass){
	ostringstream filename;
65

66 67 68 69 70 71
	filename<<mCurProj->getName()<<"/"<<klass->getName()<<".hh";
	mOutfile.open(filename.str().c_str());
	if (!mOutfile.is_open()){
		cerr<<"Could not write into "<<filename.str()<<endl;
		return;
	}
Simon Morlat's avatar
Simon Morlat committed
72 73
	list<Method*> methods=klass->getMethods();
	list<ConstField*> constFields=klass->getConstFields();
74 75 76 77 78 79 80
	mCurClass=klass;
	mOutfile<<"/* Wrapper generated by lp-gen-wrappers, do not edit*/"<<endl;
	mOutfile<<endl;
	mOutfile<<"#include <string>"<<endl;
	mOutfile<<endl;
	if (!mCurProj->getName().empty())
		mOutfile<<"namespace "<<mCurProj->getName()<<"{"<<endl<<endl;
Simon Morlat's avatar
Simon Morlat committed
81 82 83 84 85 86 87 88 89 90 91 92
	if (klass->getType()==Type::Enum){
		mOutfile<<"enum "<<klass->getName()<<"{"<<endl;
		list<ConstField*>::iterator cfit,next;
		for (cfit=constFields.begin();cfit!=constFields.end();){
			ConstField *cf=*cfit;
			writeEnumMember(cf,++cfit==constFields.end());
		}
	}else{
		mOutfile<<"class "<<klass->getName()<<"{"<<endl;
		mOutfile<<"public:"<<endl;
		for_each(methods.begin(),methods.end(),bind1st(mem_fun(&CplusplusGenerator::writeMethod),this));
	}
93

Simon Morlat's avatar
Simon Morlat committed
94
	mOutfile<<"};"<<endl<<endl;
95 96 97 98 99 100 101 102
	if (!mCurProj->getName().empty())
		mOutfile<<"} //end of namespace "<<mCurProj->getName()<<endl;
	mOutfile<<endl;
	mOutfile.close();
}

void CplusplusGenerator::writeArgument(Argument *arg, bool isReturn){
	Type *type=arg->getType();
103

104 105 106 107 108 109 110 111 112 113 114 115 116 117
	if (type->getBasicType()==Type::Class){
		if (arg->isConst()){
			mOutfile<<"const ";
		}
		mOutfile<<type->getName();
		if (arg->isPointer())
			mOutfile<<"*";
	}else if (type->getBasicType()==Type::Integer){
		mOutfile<<"int";
	}else if (type->getBasicType()==Type::Enum){
		mOutfile<<type->getName();
	}else if (type->getBasicType()==Type::String){
		if (!isReturn)
			mOutfile<<"const std::string &";
118
		else
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
			mOutfile<<"std::string";
	}else if (type->getBasicType()==Type::Void){
		mOutfile<<"void";
	}else if (type->getBasicType()==Type::Boolean){
		mOutfile<<"bool";
	}
	if (!isReturn && !arg->getName().empty())
		mOutfile<<" "<<arg->getName();
}

void CplusplusGenerator::writeTabs(int ntabs){
	int i;
	for(i=0;i<ntabs;++i)
		mOutfile<<"\t";
}

void CplusplusGenerator::writeHelpComment(const std::string &comment, int ntabs){
	size_t i;
	int curindex=0;
	writeTabs(ntabs);
	mOutfile<<" * ";
	for(i=0;i<comment.size();i++,curindex++){
141

142 143 144 145 146 147 148 149 150 151
		if (comment[i]=='\n' || (curindex>100 && comment[i]==' ')){
			mOutfile<<endl;
			writeTabs(ntabs);
			mOutfile<<" * ";
			curindex=0;
		}else mOutfile<<comment[i];
	}
}

void CplusplusGenerator::writeMethod(Method *method){
152
	if (method->isCallback()) return;
153

154 155 156
	Argument *retarg=method->getReturnArg();
	const list<Argument*> &args=method->getArgs();
	list<Argument*>::const_iterator it;
157

158 159 160 161 162 163
	writeTabs(1);
	mOutfile<<"/**"<<endl;
	writeHelpComment(method->getHelp(),1);
	mOutfile<<endl;
	writeTabs(1);
	mOutfile<<"**/"<<endl;
164

165 166 167
	writeTabs(1);
	writeArgument(retarg,true);
	mOutfile<<" "<<method->getName()<<"(";
168

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
	for(it=args.begin();it!=args.end();++it){
		if (it!=args.begin()) mOutfile<<", ";
		writeArgument(*it);
	}
	mOutfile<<")";
	if (method->isConst()) mOutfile<<"const";
	mOutfile<<";"<<endl;
	mOutfile<<endl;
}


JavascriptGenerator::JavascriptGenerator(){
}

void JavascriptGenerator::generate(Project *proj){
	list<Class*> classes=proj->getClasses();
	mCurProj=proj;
186
#ifndef _WIN32
Simon Morlat's avatar
Simon Morlat committed
187 188
	remove(to_lower(proj->getName()).c_str());
	mkdir(to_lower(proj->getName()).c_str(),S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IROTH);
189
#else
Simon Morlat's avatar
Simon Morlat committed
190
	_mkdir(to_lower(proj->getName()).c_str());
191
#endif
Simon Morlat's avatar
Simon Morlat committed
192
	ostringstream filename;
193

Simon Morlat's avatar
Simon Morlat committed
194 195 196 197 198 199 200 201 202 203 204 205 206 207
	/*write a file for the namespace*/
	filename<<to_lower(mCurProj->getName())<<"/"<<to_lower(mCurProj->getName())<<".js";
	mOutfile.open(filename.str().c_str());
	if (!mOutfile.is_open()){
		cerr<<"Could not write into "<<filename.str()<<endl;
		return;
	}
	mOutfile<<"/**"<<endl;
	mOutfile<<" * Namespace for non-external variables and objects."<<endl;
	mOutfile<<" * @namespace "<<mCurProj->getName()<<endl;
	mOutfile<<"**/"<<endl;
	mOutfile<<"var "<<proj->getName()<<" = {};"<<endl;
	mOutfile.close();
	for_each(classes.begin(),classes.end(),bind1st(mem_fun(&JavascriptGenerator::writeEnum),this));
208 209 210
	for_each(classes.begin(),classes.end(),bind1st(mem_fun(&JavascriptGenerator::writeClass),this));
}

211 212 213 214 215 216 217 218 219
string JavascriptGenerator::getEnumName(Class *klass){
	string enum_name=klass->getName();
	if (strncasecmp(enum_name.c_str(),mCurProj->getName().c_str(),mCurProj->getName().size())==0){
		//since enum is part of the namespace, drop the namespace part of the enum if any.
		enum_name.erase(0,mCurProj->getName().size());
	}
	return enum_name;
}

Simon Morlat's avatar
Simon Morlat committed
220 221
void JavascriptGenerator::writeEnum(Class *klass){
	if (klass->getType()!=Type::Enum) return;
222

Simon Morlat's avatar
Simon Morlat committed
223 224 225
	ostringstream filename;
	list<ConstField*> members=klass->getConstFields();
	list<ConstField*>::iterator it;
226
	string enum_name=getEnumName(klass);
227

Simon Morlat's avatar
Simon Morlat committed
228 229 230 231 232 233 234
	filename<<to_lower(mCurProj->getName())<<"/"<<to_lower(enum_name)<<".js";
	mOutfile.open(filename.str().c_str());
	if (!mOutfile.is_open()){
		cerr<<"Could not write into "<<filename.str()<<endl;
		return;
	}
	mOutfile<<"/* Wrapper generated by lp-gen-wrappers, do not edit*/"<<endl<<endl;
235

Simon Morlat's avatar
Simon Morlat committed
236 237 238 239 240 241 242 243 244 245
	mOutfile<<"var "<<mCurProj->getName()<<" = "<<mCurProj->getName()<<" || {};"<<endl;
	mOutfile<<"/**"<<endl;
	writeHelpComment(klass->getHelp(),0);
	mOutfile<<endl;
	mOutfile<<" * "<<"@readonly"<<endl;
	mOutfile<<" * "<<"@enum {number}"<<endl;
	mOutfile<<"**/"<<endl;
	mOutfile<<mCurProj->getName()<<"."<<enum_name<<" = {"<<endl;
	string prefix=ConstField::getCommonPrefix(members);
	size_t prefix_size=prefix.size();
246

247
	for(it=members.begin();it!=members.end();){
Simon Morlat's avatar
Simon Morlat committed
248 249 250 251 252 253 254 255 256 257
		ConstField *cf=(*it);
		if (!cf->getHelp().empty()){
			writeTabs(1);
			mOutfile<<"/**"<<endl;
			writeHelpComment(cf->getHelp(),1);
			mOutfile<<endl;
			writeTabs(1);
			mOutfile<<"*/"<<endl;
		}
		writeTabs(1);
258
		mOutfile<<cf->getName().substr(prefix_size,string::npos)<<" : "<<cf->getValue();
Simon Morlat's avatar
Simon Morlat committed
259 260 261 262
		if (++it!=members.end()) mOutfile<<",";
		mOutfile<<endl;
	}
	mOutfile<<"};"<<endl;
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280

	mOutfile << "/**" << endl;
	mOutfile << " * Get the name of a value of the " << enum_name << " enum as a string." << endl;
	mOutfile << " * @function linphone#get" << enum_name << "Text" << endl;
	mOutfile << " * @param { number } value - One of the values of the " << enum_name << " enum." << endl;
	mOutfile << "**/" << endl;
	mOutfile << mCurProj->getName() << ".get" << enum_name << "Text = function(value) {" << endl;
	mOutfile << "\tswitch (value) {" << endl;
	for (it = members.begin(); it != members.end(); it++) {
		ConstField *cf = *it;
		mOutfile << "\tcase " << mCurProj->getName() << "." << enum_name << "." << cf->getName().substr(prefix_size, string::npos) << ":" << endl;
		mOutfile << "\t\treturn \"" << cf->getName().substr(prefix_size, string::npos) << "\";" << endl;
	}
	mOutfile << "\tdefault:" << endl;
	mOutfile << "\t\treturn \"?\";" << endl;
	mOutfile << "\t}" << endl;
	mOutfile << "};" << endl;

Simon Morlat's avatar
Simon Morlat committed
281 282 283
	mOutfile.close();
}

284 285
void JavascriptGenerator::writeClass(Class *klass){
	ostringstream filename;
286

Simon Morlat's avatar
Simon Morlat committed
287 288 289
	if (klass->getType()==Type::Enum) {
		return;
	}
290 291
	const list<Method*> &methods=klass->getMethods();
	if (methods.empty()) return;//skip empty classes
292

Simon Morlat's avatar
Simon Morlat committed
293
	filename<<to_lower(mCurProj->getName())<<"/"<<to_lower(klass->getName())<<".js";
294 295 296 297 298 299 300 301
	mOutfile.open(filename.str().c_str());
	if (!mOutfile.is_open()){
		cerr<<"Could not write into "<<filename.str()<<endl;
		return;
	}
	mCurClass=klass;
	mOutfile<<"/* Wrapper generated by lp-gen-wrappers, do not edit*/"<<endl;
	mOutfile<<endl;
302

303 304 305 306 307 308
	//if (!mCurProj->getName().empty())
	//	mOutfile<<"namespace "<<mCurProj->getName()<<"{"<<endl<<endl;
	mOutfile<<"/**"<<endl;
	mOutfile<<" * "<<klass->getHelp()<<endl;
	mOutfile<<" * @external "<<klass->getName()<<endl;
	mOutfile<<"**/"<<endl;
309

310 311 312 313
	list<Property*> properties=klass->getProperties();
	for_each(properties.begin(),properties.end(),bind1st(mem_fun(&JavascriptGenerator::writeProperty),this));
	mOutfile<<endl;
	for_each(methods.begin(),methods.end(),bind1st(mem_fun(&JavascriptGenerator::writeMethod),this));
314
	for_each(methods.begin(),methods.end(),bind1st(mem_fun(&JavascriptGenerator::writeEvent),this));
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
	//if (!mCurProj->getName().empty())
	//	mOutfile<<"} //end of namespace "<<mCurProj->getName()<<endl;
	mOutfile<<endl;
	mOutfile.close();
}

void JavascriptGenerator::writeType(Type *type){
	switch(type->getBasicType()){
		case Type::Float:
		case Type::Integer:
			mOutfile<<"number";
		break;
		case Type::String:
			mOutfile<<"string";
		break;
		case Type::Boolean:
			mOutfile<<"boolean";
		break;
		case Type::Class:
			mOutfile<<"external:"<<type->getName();
		break;
336 337 338
		case Type::Enum:
			mOutfile<<mCurProj->getName()<<"."<<getEnumName(mCurProj->getClass(type->getName()));
		break;
339 340 341 342 343
		case Type::Void:
			mOutfile<<"void";
		break;
		case Type::Callback:
		break;
344 345 346
		case Type::Array:
			mOutfile<<"Array.<Object>";
		break;
347 348 349
	}
}

350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
void JavascriptGenerator::writeArgument(Argument *arg, ArgKind kind){
	switch(kind){
		case Normal:
			mOutfile<<" * @param {";
			writeType(arg->getType());
			mOutfile<<"} "<<arg->getName()<<" - "<<arg->getHelp()<<endl;
		break;
		case Return:
			mOutfile<<" * @returns {";
			writeType(arg->getType());
			mOutfile<<"} "<<arg->getHelp()<<endl;
		break;
		case PropertyArg:
			mOutfile<<" * @property {";
			writeType(arg->getType());
			mOutfile<<"} "<<arg->getName()<<" - "<<arg->getHelp()<<endl;
		break;
367 368 369 370 371 372 373 374 375 376 377 378
	}
}

void JavascriptGenerator::writeTabs(int ntabs){
	int i;
	for(i=0;i<ntabs;++i)
		mOutfile<<"\t";
}

void JavascriptGenerator::writeHelpComment(const std::string &comment, int ntabs){
	size_t i;
	int curindex=0;
Simon Morlat's avatar
Simon Morlat committed
379
	writeTabs(ntabs);
380 381 382 383
	mOutfile<<" * ";
	for(i=0;i<comment.size();i++,curindex++){
		if (comment[i]=='\n' || (curindex>100 && comment[i]==' ')){
			mOutfile<<endl;
Simon Morlat's avatar
Simon Morlat committed
384
			writeTabs(ntabs);
385 386 387 388 389 390 391
			mOutfile<<" * ";
			curindex=0;
		}else mOutfile<<comment[i];
	}
}

void JavascriptGenerator::writeProperty(Property *prop){
Simon Morlat's avatar
Simon Morlat committed
392
	if (prop->getName()=="userData" || prop->getName()=="userPointer") return;
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
	mOutfile<<"/**"<<endl;
	writeHelpComment(prop->getHelp(),0);
	mOutfile<<endl;
	mOutfile<<" * @member {";
	writeType(prop->getType());
	mOutfile<<"} external:"<<mCurClass->getName()<<"#"<<prop->getName()<<endl;
	if (prop->getAttribute()==Property::ReadOnly)
		mOutfile<<" * @readonly"<<endl;
	mOutfile<<"**/"<<endl;
}

void JavascriptGenerator::writeMethod(Method *method){
	Argument *retarg=method->getReturnArg();
	const list<Argument*> &args=method->getArgs();
	list<Argument*>::const_iterator it;
408

409
	if (method->isCallback()) return;
410 411
	if (method->getPropertyBehaviour()!=Method::None) return;
	if (method->getName()=="ref" || method->getName()=="unref") return;
412

413 414 415 416
	mOutfile<<"/**"<<endl;
	writeHelpComment(method->getHelp(),0);
	mOutfile<<endl;
	mOutfile<<" * @function external:"<<mCurClass->getName()<<"#"<<method->getName()<<endl;
417

418 419 420
	for(it=args.begin();it!=args.end();++it){
		writeArgument(*it);
	}
421
	writeArgument(retarg,Return);
422 423 424
	mOutfile<<"**/"<<endl;
	mOutfile<<endl;
}
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439

string JavascriptGenerator::getEventHelp(const string &help){
	size_t i=help.find("Callback");
	if (i==string::npos){
		i=help.find("callback");
		if (i==string::npos) return help;
	}
	string res(help);
	res.replace(i,8,"event");
	return res;
}

void JavascriptGenerator::writeEvent(Method* event){
	const list<Argument*> &args=event->getArgs();
	list<Argument*>::const_iterator it;
440

441 442 443 444 445 446
	if (!event->isCallback()) return;
	mOutfile<<"/**"<<endl;
	writeHelpComment(getEventHelp(event->getHelp()),0);
	mOutfile<<endl;
	mOutfile<<" * @event external:"<<mCurClass->getName()<<"#"<<event->getName()<<endl;
	mOutfile<<" * @type {object}"<<endl;
447

448 449 450 451 452 453 454
	for(it=args.begin();it!=args.end();++it){
		writeArgument(*it,PropertyArg);
	}
	mOutfile<<"**/"<<endl;
	mOutfile<<endl;
}