1 //=============================================================================
2 //
3 //   File : KviKvsTreeNodeSpecialCommandClass.cpp
4 //   Creation date : Fri 12 Aug 2005 03:23:31 by Szymon Stefanek
5 //
6 //   This file is part of the KVIrc IRC Client distribution
7 //   Copyright (C) 2005-2010 Szymon Stefanek <pragma at kvirc dot net>
8 //
9 //   This program is FREE software. You can redistribute it and/or
10 //   modify it under the terms of the GNU General Public License
11 //   as published by the Free Software Foundation; either version 2
12 //   of the License, or (at your option) any later version.
13 //
14 //   This program is distributed in the HOPE that it will be USEFUL,
15 //   but WITHOUT ANY WARRANTY; without even the implied warranty of
16 //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 //   See the GNU General Public License for more details.
18 //
19 //   You should have received a copy of the GNU General Public License
20 //   along with this program. If not, write to the Free Software Foundation,
21 //   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 //=============================================================================
24 #include "KviLocale.h"
25 #include "KviKvsVariantList.h"
26 #include "KviKvsVariant.h"
27 #include "KviKvsTreeNodeSpecialCommandClass.h"
28 #include "KviKvsKernel.h"
29 #include "KviKvsObjectController.h"
30 #include "KviKvsObjectClass.h"
31 
KviKvsTreeNodeSpecialCommandClassFunctionDefinition(const QChar * pLocation,const QString & szName,const QString & szBuffer,const QString & szReminder,unsigned int uHandlerFlags)32 KviKvsTreeNodeSpecialCommandClassFunctionDefinition::KviKvsTreeNodeSpecialCommandClassFunctionDefinition(const QChar * pLocation, const QString & szName, const QString & szBuffer, const QString & szReminder, unsigned int uHandlerFlags)
33     : KviKvsTreeNode(pLocation)
34 {
35 	m_uHandlerFlags = uHandlerFlags;
36 	m_szName = szName;
37 	m_szReminder = szReminder;
38 	m_szBuffer = szBuffer;
39 }
40 
dump(const char * prefix)41 void KviKvsTreeNodeSpecialCommandClassFunctionDefinition::dump(const char * prefix)
42 {
43 	qDebug("%s SpecialCommandClassFunctionDefinition(%s)", prefix, m_szName.toUtf8().data());
44 	qDebug("%s    (command buffer with %d characters)", prefix, m_szBuffer.length());
45 }
46 
contextDescription(QString & szBuffer)47 void KviKvsTreeNodeSpecialCommandClassFunctionDefinition::contextDescription(QString & szBuffer)
48 {
49 	szBuffer = QString("Object Member Function Definition '%1'").arg(m_szName);
50 }
51 
KviKvsTreeNodeSpecialCommandClass(const QChar * pLocation,KviKvsTreeNodeDataList * pParams)52 KviKvsTreeNodeSpecialCommandClass::KviKvsTreeNodeSpecialCommandClass(const QChar * pLocation, KviKvsTreeNodeDataList * pParams)
53     : KviKvsTreeNodeSpecialCommand(pLocation, "class")
54 {
55 	m_pParams = pParams;
56 	m_pParams->setParent(this);
57 	m_pFunctions = new KviPointerList<KviKvsTreeNodeSpecialCommandClassFunctionDefinition>;
58 	m_pFunctions->setAutoDelete(true);
59 }
60 
~KviKvsTreeNodeSpecialCommandClass()61 KviKvsTreeNodeSpecialCommandClass::~KviKvsTreeNodeSpecialCommandClass()
62 {
63 	delete m_pParams;
64 	delete m_pFunctions;
65 }
66 
addFunctionDefinition(KviKvsTreeNodeSpecialCommandClassFunctionDefinition * pDef)67 void KviKvsTreeNodeSpecialCommandClass::addFunctionDefinition(KviKvsTreeNodeSpecialCommandClassFunctionDefinition * pDef)
68 {
69 	pDef->setParent(this);
70 	m_pFunctions->append(pDef);
71 }
72 
contextDescription(QString & szBuffer)73 void KviKvsTreeNodeSpecialCommandClass::contextDescription(QString & szBuffer)
74 {
75 	szBuffer = "Special Command 'class'";
76 }
77 
dump(const char * prefix)78 void KviKvsTreeNodeSpecialCommandClass::dump(const char * prefix)
79 {
80 	qDebug("%s SpecialCommandClass", prefix);
81 	QString tmp = prefix;
82 	tmp.append("  ");
83 	m_pParams->dump(tmp.toUtf8().data());
84 	for(KviKvsTreeNodeSpecialCommandClassFunctionDefinition * d = m_pFunctions->first(); d; d = m_pFunctions->next())
85 		d->dump(tmp.toUtf8().data());
86 }
87 
execute(KviKvsRunTimeContext * c)88 bool KviKvsTreeNodeSpecialCommandClass::execute(KviKvsRunTimeContext * c)
89 {
90 	KviKvsVariantList l;
91 	if(!m_pParams->evaluate(c, &l))
92 		return false;
93 
94 	KviKvsVariant * pClassName = l.first();
95 	if(!pClassName)
96 	{
97 		c->error(this, __tr2qs_ctx("Missing class name", "kvs"));
98 		return false;
99 	}
100 
101 	KviKvsVariant * pBaseClassName = l.next();
102 
103 	QString szClassName;
104 	QString szBaseClassName;
105 	pClassName->asString(szClassName);
106 	QRegExp re("[\\w:]+");
107 	if(!re.exactMatch(szClassName))
108 	{
109 		c->error(this, __tr2qs_ctx("Class names can contain only letters, digits, underscores and '::' namespace separators", "kvs"));
110 		return false;
111 	}
112 	if(pBaseClassName)
113 		pBaseClassName->asString(szBaseClassName);
114 
115 	if(szClassName.isEmpty())
116 	{
117 		c->error(this, __tr2qs_ctx("Missing class name", "kvs"));
118 		return false;
119 	}
120 
121 	if(szBaseClassName.isEmpty())
122 		szBaseClassName = "object";
123 
124 	// avoid infinite recursion in loading the base class
125 	if(KviQString::equalCI(szBaseClassName, szClassName))
126 	{
127 		c->error(__tr2qs_ctx("A class can't be a subclass of itself", "kvs"));
128 		return false;
129 	}
130 
131 	KviKvsObjectClass * pBaseClass = KviKvsKernel::instance()->objectController()->lookupClass(szBaseClassName);
132 	if(!pBaseClass)
133 	{
134 		c->error(this, __tr2qs_ctx("Couln't find base class named '%Q'", "kvs"), &szBaseClassName);
135 		return false;
136 	}
137 
138 	// walk the inheritance tree of the base class in order to detect loops
139 	KviKvsObjectClass * pClass = pBaseClass;
140 	while(pClass)
141 	{
142 		if(KviQString::equalCI(pClass->name(), szClassName))
143 		{
144 			c->error(this, __tr2qs_ctx("Detected a loop in the inheritance tree of the base class '%Q': redefine that class first", "kvs"), &szBaseClassName);
145 			return false;
146 		}
147 		pClass = pClass->parentClass();
148 	}
149 
150 	KviKvsObjectClass * pActualClass = KviKvsKernel::instance()->objectController()->lookupClass(szClassName, true);
151 	if(pActualClass)
152 	{
153 		c->error(this, __tr2qs_ctx("Can't override the builtin class '%Q'", "kvs"), &szClassName);
154 		return false;
155 	}
156 	pActualClass = new KviKvsObjectClass(pBaseClass, szClassName, nullptr, false);
157 
158 	for(KviKvsTreeNodeSpecialCommandClassFunctionDefinition * d = m_pFunctions->first(); d; d = m_pFunctions->next())
159 	{
160 		pActualClass->registerFunctionHandler(d->name(), d->buffer(), d->reminder(), d->handlerFlags());
161 	}
162 	return true;
163 }
164