1 /**
2   @file
3   @author Stefan Frings
4 */
5 
6 #include "template.h"
7 #include <QFileInfo>
8 #include <QRegularExpression>
9 
Template(QString source,QString sourceName)10 Template::Template(QString source, QString sourceName)
11 	: QString(source)
12 {
13 	this->sourceName=sourceName;
14 	this->warnings=false;
15 }
16 
Template(QFile & file,QTextCodec * textCodec)17 Template::Template(QFile& file, QTextCodec* textCodec)
18 {
19 	this->warnings=false;
20 	sourceName=QFileInfo(file.fileName()).baseName();
21 	if (file.open(QFile::ReadOnly | QFile::Text))
22 	{
23 		QByteArray data=file.readAll();
24 		file.close();
25 
26 		if(textCodec)
27 			append(textCodec->toUnicode(data));
28 		else
29 			append(fromUtf8(data));
30 	}
31 	else
32 	{
33 		qCritical("Template: cannot read from %s, %s",qPrintable(sourceName),qPrintable(file.errorString()));
34 	}
35 }
36 
37 
setVariable(QString name,QString value)38 int Template::setVariable(QString name, QString value)
39 {
40     int count=0;
41     QString variable="{"+name+"}";
42     int start=indexOf(variable);
43     while (start>=0)
44     {
45         replace(start, variable.length(), value);
46         count++;
47         start=indexOf(variable,start+value.length());
48     }
49     if (count==0 && warnings)
50     {
51         qWarning("Template: missing variable %s in %s",qPrintable(variable),qPrintable(sourceName));
52     }
53     return count;
54 }
55 
setCondition(QString name,bool value)56 int Template::setCondition(QString name, bool value)
57 {
58     int count=0;
59     QString startTag=QString("{if %1}").arg(name);
60     QString elseTag=QString("{else %1}").arg(name);
61     QString endTag=QString("{end %1}").arg(name);
62     // search for if-else-end
63     int start=indexOf(startTag);
64     while (start>=0)
65     {
66         int end=indexOf(endTag,start+startTag.length());
67         if (end>=0)
68         {
69             count++;
70             int ellse=indexOf(elseTag,start+startTag.length());
71             if (ellse>start && ellse<end)
72             {
73                 // there is an else part
74                 if (value==true)
75                 {
76                     QString truePart=mid(start+startTag.length(), ellse-start-startTag.length());
77                     replace(start, end-start+endTag.length(), truePart);
78                 }
79                 else
80                 {
81                     // value==false
82                     QString falsePart=mid(ellse+elseTag.length(), end-ellse-elseTag.length());
83                     replace(start, end-start+endTag.length(), falsePart);
84                 }
85             }
86             else if (value==true)
87             {
88                 // and no else part
89                 QString truePart=mid(start+startTag.length(), end-start-startTag.length());
90                 replace(start, end-start+endTag.length(), truePart);
91             }
92             else
93             {
94                 // value==false and no else part
95                 replace(start, end-start+endTag.length(), "");
96             }
97             start=indexOf(startTag,start);
98         }
99         else
100         {
101             qWarning("Template: missing condition end %s in %s",qPrintable(endTag),qPrintable(sourceName));
102         }
103     }
104     // search for ifnot-else-end
105     QString startTag2="{ifnot "+name+"}";
106     start=indexOf(startTag2);
107     while (start>=0)
108     {
109         int end=indexOf(endTag,start+startTag2.length());
110         if (end>=0)
111         {
112             count++;
113             int ellse=indexOf(elseTag,start+startTag2.length());
114             if (ellse>start && ellse<end)
115             {
116                 // there is an else part
117                 if (value==false)
118                 {
119                     QString falsePart=mid(start+startTag2.length(), ellse-start-startTag2.length());
120                     replace(start, end-start+endTag.length(), falsePart);
121                 }
122                 else
123                 {
124                     // value==true
125                     QString truePart=mid(ellse+elseTag.length(), end-ellse-elseTag.length());
126                     replace(start, end-start+endTag.length(), truePart);
127                 }
128             }
129             else if (value==false)
130             {
131                 // and no else part
132                 QString falsePart=mid(start+startTag2.length(), end-start-startTag2.length());
133                 replace(start, end-start+endTag.length(), falsePart);
134             }
135             else
136             {
137                 // value==true and no else part
138                 replace(start, end-start+endTag.length(), "");
139             }
140             start=indexOf(startTag2,start);
141         }
142         else
143         {
144             qWarning("Template: missing condition end %s in %s",qPrintable(endTag),qPrintable(sourceName));
145         }
146     }
147     if (count==0 && warnings)
148     {
149         qWarning("Template: missing condition %s or %s in %s",qPrintable(startTag),qPrintable(startTag2),qPrintable(sourceName));
150     }
151     return count;
152 }
153 
loop(QString name,int repetitions)154 int Template::loop(QString name, int repetitions)
155 {
156     Q_ASSERT(repetitions>=0);
157     int count=0;
158     QString startTag="{loop "+name+"}";
159     QString elseTag="{else "+name+"}";
160     QString endTag="{end "+name+"}";
161     // search for loop-else-end
162     int start=indexOf(startTag);
163     while (start>=0)
164     {
165         int end=indexOf(endTag,start+startTag.length());
166         if (end>=0)
167         {
168             count++;
169             int ellse=indexOf(elseTag,start+startTag.length());
170             if (ellse>start && ellse<end)
171             {
172                 // there is an else part
173                 if (repetitions>0)
174                 {
175                     QString loopPart=mid(start+startTag.length(), ellse-start-startTag.length());
176                     QString insertMe;
177                     for (int i=0; i<repetitions; ++i)
178                     {
179                         // number variables, conditions and sub-loop within the loop
180                         QString nameNum=name+QString::number(i);
181                         QString s=loopPart;
182                         s.replace(QString("{%1.").arg(name), QString("{%1.").arg(nameNum));
183                         s.replace(QString("{if %1.").arg(name), QString("{if %1.").arg(nameNum));
184                         s.replace(QString("{ifnot %1.").arg(name), QString("{ifnot %1.").arg(nameNum));
185                         s.replace(QString("{else %1.").arg(name), QString("{else %1.").arg(nameNum));
186                         s.replace(QString("{end %1.").arg(name), QString("{end %1.").arg(nameNum));
187                         s.replace(QString("{loop %1.").arg(name), QString("{loop %1.").arg(nameNum));
188                         insertMe.append(s);
189                     }
190                     replace(start, end-start+endTag.length(), insertMe);
191                 }
192                 else
193                 {
194                     // repetitions==0
195                     QString elsePart=mid(ellse+elseTag.length(), end-ellse-elseTag.length());
196                     replace(start, end-start+endTag.length(), elsePart);
197                 }
198             }
199             else if (repetitions>0)
200             {
201                 // and no else part
202                 QString loopPart=mid(start+startTag.length(), end-start-startTag.length());
203                 QString insertMe;
204                 for (int i=0; i<repetitions; ++i)
205                 {
206                     // number variables, conditions and sub-loop within the loop
207                     QString nameNum=name+QString::number(i);
208                     QString s=loopPart;
209                     s.replace(QString("{%1.").arg(name), QString("{%1.").arg(nameNum));
210                     s.replace(QString("{if %1.").arg(name), QString("{if %1.").arg(nameNum));
211                     s.replace(QString("{ifnot %1.").arg(name), QString("{ifnot %1.").arg(nameNum));
212                     s.replace(QString("{else %1.").arg(name), QString("{else %1.").arg(nameNum));
213                     s.replace(QString("{end %1.").arg(name), QString("{end %1.").arg(nameNum));
214                     s.replace(QString("{loop %1.").arg(name), QString("{loop %1.").arg(nameNum));
215                     insertMe.append(s);
216                 }
217                 replace(start, end-start+endTag.length(), insertMe);
218             }
219             else
220             {
221                 // repetitions==0 and no else part
222                 replace(start, end-start+endTag.length(), "");
223             }
224             start=indexOf(startTag,start);
225         }
226         else
227         {
228             qWarning("Template: missing loop end %s in %s",qPrintable(endTag),qPrintable(sourceName));
229         }
230     }
231     if (count==0 && warnings)
232     {
233         qWarning("Template: missing loop %s in %s",qPrintable(startTag),qPrintable(sourceName));
234     }
235     return count;
236 }
237 
enableWarnings(bool enable)238 void Template::enableWarnings(bool enable)
239 {
240     warnings=enable;
241 }
242 
translate(ITemplateTranslationProvider & provider)243 void Template::translate(ITemplateTranslationProvider &provider)
244 {
245 	//This regex captures expressions of the form
246 	//<?= tr("This is a test") ?> and <?= tr("optional %1 parameters %2","bla","blu") ?>
247 	//The first capture group is the key (untranslated string), the second the optional list of parameters
248 	static const QRegularExpression regexp = QRegularExpression("<\\?=\\s*tr\\(\"([^\"\\\\]*(?:\\\\.[^\"\\\\]*)*)\"((?:,\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\")*)\\s*\\)\\?>");
249 	//This one is used to extract the parameters using global matching
250 	static const QRegularExpression paramExp = QRegularExpression(",\"([^\"\\\\]*(?:\\\\.[^\"\\\\]*)*)\"");
251 
252 	int offset = 0;
253 	QRegularExpressionMatch match;
254 	do
255 	{
256 		match = regexp.match(*this,offset);
257 
258 		if(match.hasMatch())
259 		{
260 			int start = match.capturedStart(0);
261 			int len = match.capturedLength(0);
262 			QString key = match.captured(1);
263 
264 			//replace escaped double and single quotes
265 			key.replace("\\\"","\"");
266 			key.replace("\\'", "'");
267 
268 			QString translation = provider.getTranslation(key);
269 
270 			//find out if we have optional parameters
271 			if(match.capturedLength(2)>0)
272 			{
273 				QString params = match.captured(2);
274 				//extract each optional parameter
275 				QRegularExpressionMatchIterator it = paramExp.globalMatch(params);
276 				while(it.hasNext())
277 				{
278 					QRegularExpressionMatch paramMatch = it.next();
279 					QString param = paramMatch.captured(1);
280 
281 					//replace escaped quotes
282 					param.replace("\\\"","\"");
283 					param.replace("\\'", "'");
284 
285 					//apply the param
286 					translation = translation.arg(param);
287 				}
288 			}
289 
290 			this->replace(start,len,translation);
291 			offset = start+translation.length();
292 		}
293 	}while(match.hasMatch());
294 }
295