1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "customwizardparameters.h"
35 #include "customwizardpreprocessor.h"
36 #include "customwizardscriptgenerator.h"
38 #include <coreplugin/mimedatabase.h>
39 #include <coreplugin/icore.h>
40 #include <cpptools/cpptoolsconstants.h>
42 #include <utils/qtcassert.h>
44 #include <QtCore/QDebug>
45 #include <QtCore/QCoreApplication>
46 #include <QtCore/QLocale>
47 #include <QtCore/QFile>
48 #include <QtCore/QDir>
49 #include <QtCore/QFileInfo>
50 #include <QtCore/QXmlStreamReader>
51 #include <QtCore/QXmlStreamAttribute>
52 #include <QtCore/QTemporaryFile>
53 #include <QtScript/QScriptEngine>
55 #include <QtGui/QIcon>
59 static const char customWizardElementC[] = "wizard";
60 static const char iconElementC[] = "icon";
61 static const char descriptionElementC[] = "description";
62 static const char displayNameElementC[] = "displayname";
63 static const char wizardEnabledAttributeC[] = "enabled";
64 static const char idAttributeC[] = "id";
65 static const char kindAttributeC[] = "kind";
66 static const char klassAttributeC[] = "class";
67 static const char firstPageAttributeC[] = "firstpage";
68 static const char langAttributeC[] = "xml:lang";
69 static const char categoryAttributeC[] = "category";
70 static const char displayCategoryElementC[] = "displaycategory";
71 static const char fieldPageTitleElementC[] = "fieldpagetitle";
72 static const char fieldsElementC[] = "fields";
73 static const char fieldElementC[] = "field";
74 static const char comboEntriesElementC[] = "comboentries";
75 static const char comboEntryElementC[] = "comboentry";
76 static const char comboEntryTextElementC[] = "comboentrytext";
78 static const char generatorScriptElementC[] = "generatorscript";
79 static const char generatorScriptBinaryAttributeC[] = "binary";
80 static const char generatorScriptWorkingDirectoryAttributeC[] = "workingdirectory";
81 static const char generatorScriptArgumentElementC[] = "argument";
82 static const char generatorScriptArgumentValueAttributeC[] = "value";
83 static const char generatorScriptArgumentOmitEmptyAttributeC[] = "omit-empty";
84 static const char generatorScriptArgumentWriteFileAttributeC[] = "write-file";
86 static const char fieldDescriptionElementC[] = "fielddescription";
87 static const char fieldNameAttributeC[] = "name";
88 static const char fieldMandatoryAttributeC[] = "mandatory";
89 static const char fieldControlElementC[] = "fieldcontrol";
91 static const char filesElementC[] = "files";
92 static const char filesGeneratorScriptAttributeC[] = "generatorscript";
93 static const char fileElementC[] = "file";
94 static const char fileSourceAttributeC[] = "source";
95 static const char fileTargetAttributeC[] = "target";
96 static const char fileBinaryAttributeC[] = "binary";
98 static const char rulesElementC[] = "validationrules";
99 static const char ruleElementC[] = "validationrule";
100 static const char ruleConditionAttributeC[] = "condition";
101 static const char ruleMessageElementC[] = "message";
108 ParseWithinFieldDescription,
109 ParseWithinFieldControl,
110 ParseWithinComboEntries,
111 ParseWithinComboEntry,
112 ParseWithinComboEntryText,
116 ParseWithinScriptArguments,
117 ParseWithinValidationRules,
118 ParseWithinValidationRule,
119 ParseWithinValidationRuleMessage,
123 namespace ProjectExplorer {
126 const char customWizardFileOpenEditorAttributeC[] = "openeditor";
127 const char customWizardFileOpenProjectAttributeC[] = "openproject";
129 CustomWizardField::CustomWizardField() :
134 void CustomWizardField::clear()
139 controlAttributes.clear();
142 // Attribute map keys for combo entries
143 QString CustomWizardField::comboEntryValueKey(int i)
145 return QLatin1String("comboValue") + QString::number(i);
148 QString CustomWizardField::comboEntryTextKey(int i)
150 return QLatin1String("comboText") + QString::number(i);
153 CustomWizardFile::CustomWizardFile() :
154 openEditor(false), openProject(false), binary(false)
158 bool CustomWizardValidationRule::validateRules(const QList<CustomWizardValidationRule> &rules,
159 const QMap<QString, QString> &replacementMap,
160 QString *errorMessage)
162 errorMessage->clear();
165 QScriptEngine engine;
166 foreach(const CustomWizardValidationRule &rule, rules)
167 if (!rule.validate(engine, replacementMap)) {
168 *errorMessage = rule.message;
169 CustomWizardContext::replaceFields(replacementMap, errorMessage);
175 bool CustomWizardValidationRule::validate(QScriptEngine &engine, const QMap<QString, QString> &replacementMap) const
177 // Apply parameters and evaluate using JavaScript
178 QString cond = condition;
179 CustomWizardContext::replaceFields(replacementMap, &cond);
181 QString errorMessage;
182 if (!evaluateBooleanJavaScriptExpression(engine, cond, &valid, &errorMessage)) {
183 qWarning("Error in custom wizard validation expression '%s': %s",
184 qPrintable(cond), qPrintable(errorMessage));
190 CustomWizardParameters::CustomWizardParameters() :
195 void CustomWizardParameters::clear()
200 filesGeneratorScript.clear();
201 filesGeneratorScriptArguments.clear();
206 // Resolve icon file path relative to config file directory.
207 static inline QIcon wizardIcon(const QString &configFileFullPath,
208 const QString &xmlIconFileName)
210 const QFileInfo fi(xmlIconFileName);
212 return QIcon(fi.absoluteFilePath());
213 if (!fi.isRelative())
215 // Expand by config path
216 const QFileInfo absFi(QFileInfo(configFileFullPath).absolutePath() +
217 QLatin1Char('/') + xmlIconFileName);
219 return QIcon(absFi.absoluteFilePath());
223 // Forward a reader over element text
224 static inline bool skipOverElementText(QXmlStreamReader &reader)
226 QXmlStreamReader::TokenType next = QXmlStreamReader::EndElement;
228 next = reader.readNext();
229 } while (next == QXmlStreamReader::Characters || next == QXmlStreamReader::EntityReference
230 || next == QXmlStreamReader::ProcessingInstruction || next == QXmlStreamReader::Comment);
231 return next == QXmlStreamReader::EndElement;
234 // Helper for reading text element depending on a language.
235 // Assign the element text to the string passed on if the language matches,
236 // that is, the element has no language attribute or there is an exact match.
237 // If there is no match, skip over the element text.
238 static inline bool assignLanguageElementText(QXmlStreamReader &reader,
239 const QString &desiredLanguage,
242 const QStringRef elementLanguage = reader.attributes().value(langAttributeC);
243 if (elementLanguage.isEmpty()) {
244 // Try to find a translation for our built-in Wizards
245 *target = QCoreApplication::translate("ProjectExplorer::CustomWizard", reader.readElementText().toLatin1().constData());
248 if (elementLanguage == desiredLanguage) {
249 *target = reader.readElementText();
252 // Language mismatch: forward to end element.
253 skipOverElementText(reader);
257 // Copy&paste from above to call a setter of BaseFileParameters.
258 // Implementation of a sophisticated mem_fun pattern is left
259 // as an exercise to the reader.
260 static inline bool assignLanguageElementText(QXmlStreamReader &reader,
261 const QString &desiredLanguage,
262 Core::BaseFileWizardParameters *bp,
263 void (Core::BaseFileWizardParameters::*setter)(const QString &))
265 const QStringRef elementLanguage = reader.attributes().value(langAttributeC);
266 if (elementLanguage.isEmpty()) {
267 // Try to find a translation for our built-in Wizards
268 const QString translated = QCoreApplication::translate("ProjectExplorer::CustomWizard", reader.readElementText().toLatin1().constData());
269 (bp->*setter)(translated);
272 if (elementLanguage == desiredLanguage) {
273 (bp->*setter)(reader.readElementText());
276 // Language mismatch: forward to end element.
277 skipOverElementText(reader);
281 // Read level sub-elements of "wizard"
282 static bool parseCustomProjectElement(QXmlStreamReader &reader,
283 const QString &configFileFullPath,
284 const QString &language,
285 CustomWizardParameters *p,
286 Core::BaseFileWizardParameters *bp)
288 const QStringRef elementName = reader.name();
289 if (elementName == QLatin1String(iconElementC)) {
290 const QString path = reader.readElementText();
291 const QIcon icon = wizardIcon(configFileFullPath, path);
292 if (icon.availableSizes().isEmpty()) {
293 qWarning("Invalid icon path '%s' encountered in custom project template %s.",
294 qPrintable(path), qPrintable(configFileFullPath));
300 if (elementName == QLatin1String(descriptionElementC)) {
301 assignLanguageElementText(reader, language, bp,
302 &Core::BaseFileWizardParameters::setDescription);
305 if (elementName == QLatin1String(displayNameElementC)) {
306 assignLanguageElementText(reader, language, bp,
307 &Core::BaseFileWizardParameters::setDisplayName);
310 if (elementName == QLatin1String(displayCategoryElementC)) {
311 assignLanguageElementText(reader, language, bp,
312 &Core::BaseFileWizardParameters::setDisplayCategory);
315 if (elementName == QLatin1String(fieldPageTitleElementC)) {
316 assignLanguageElementText(reader, language, &p->fieldPageTitle);
322 static inline QMap<QString, QString> attributesToStringMap(const QXmlStreamAttributes &attributes)
324 QMap<QString, QString> rc;
325 foreach(const QXmlStreamAttribute &attribute, attributes)
326 rc.insert(attribute.name().toString(), attribute.value().toString());
330 // Switch parser state depending on opening element name.
331 static ParseState nextOpeningState(ParseState in, const QStringRef &name)
335 if (name == QLatin1String(customWizardElementC))
336 return ParseWithinWizard;
338 case ParseWithinWizard:
339 if (name == QLatin1String(fieldsElementC))
340 return ParseWithinFields;
341 if (name == QLatin1String(filesElementC))
342 return ParseWithinFiles;
343 if (name == QLatin1String(generatorScriptElementC))
344 return ParseWithinScript;
345 if (name == QLatin1String(rulesElementC))
346 return ParseWithinValidationRules;
348 case ParseWithinFields:
349 if (name == QLatin1String(fieldElementC))
350 return ParseWithinField;
352 case ParseWithinField:
353 if (name == QLatin1String(fieldDescriptionElementC))
354 return ParseWithinFieldDescription;
355 if (name == QLatin1String(fieldControlElementC))
356 return ParseWithinFieldControl;
358 case ParseWithinFieldControl:
359 if (name == QLatin1String(comboEntriesElementC))
360 return ParseWithinComboEntries;
362 case ParseWithinComboEntries:
363 if (name == QLatin1String(comboEntryElementC))
364 return ParseWithinComboEntry;
366 case ParseWithinComboEntry:
367 if (name == QLatin1String(comboEntryTextElementC))
368 return ParseWithinComboEntryText;
370 case ParseWithinFiles:
371 if (name == QLatin1String(fileElementC))
372 return ParseWithinFile;
374 case ParseWithinScript:
375 if (name == QLatin1String(generatorScriptArgumentElementC))
376 return ParseWithinScriptArguments;
378 case ParseWithinValidationRules:
379 if (name == QLatin1String(ruleElementC))
380 return ParseWithinValidationRule;
382 case ParseWithinValidationRule:
383 if (name == QLatin1String(ruleMessageElementC))
384 return ParseWithinValidationRuleMessage;
386 case ParseWithinFieldDescription: // No subelements
387 case ParseWithinComboEntryText:
388 case ParseWithinFile:
390 case ParseWithinScriptArguments:
391 case ParseWithinValidationRuleMessage:
397 // Switch parser state depending on closing element name.
398 static ParseState nextClosingState(ParseState in, const QStringRef &name)
403 case ParseWithinWizard:
404 if (name == QLatin1String(customWizardElementC))
405 return ParseBeginning;
407 case ParseWithinFields:
408 if (name == QLatin1String(fieldsElementC))
409 return ParseWithinWizard;
411 case ParseWithinField:
412 if (name == QLatin1String(fieldElementC))
413 return ParseWithinFields;
415 case ParseWithinFiles:
416 if (name == QLatin1String(filesElementC))
417 return ParseWithinWizard;
419 case ParseWithinFile:
420 if (name == QLatin1String(fileElementC))
421 return ParseWithinFiles;
423 case ParseWithinFieldDescription:
424 if (name == QLatin1String(fieldDescriptionElementC))
425 return ParseWithinField;
427 case ParseWithinFieldControl:
428 if (name == QLatin1String(fieldControlElementC))
429 return ParseWithinField;
431 case ParseWithinComboEntries:
432 if (name == QLatin1String(comboEntriesElementC))
433 return ParseWithinFieldControl;
435 case ParseWithinComboEntry:
436 if (name == QLatin1String(comboEntryElementC))
437 return ParseWithinComboEntries;
439 case ParseWithinComboEntryText:
440 if (name == QLatin1String(comboEntryTextElementC))
441 return ParseWithinComboEntry;
443 case ParseWithinScript:
444 if (name == QLatin1String(generatorScriptElementC))
445 return ParseWithinWizard;
447 case ParseWithinScriptArguments:
448 if (name == QLatin1String(generatorScriptArgumentElementC))
449 return ParseWithinScript;
451 case ParseWithinValidationRuleMessage:
452 return ParseWithinValidationRule;
453 case ParseWithinValidationRule:
454 return ParseWithinValidationRules;
455 case ParseWithinValidationRules:
456 return ParseWithinWizard;
463 // Parse kind attribute
464 static inline Core::IWizard::WizardKind kindAttribute(const QXmlStreamReader &r)
466 const QStringRef value = r.attributes().value(QLatin1String(kindAttributeC));
467 if (!value.isEmpty()) {
468 if (value == QLatin1String("file"))
469 return Core::IWizard::FileWizard;
470 if (value == QLatin1String("class"))
471 return Core::IWizard::ClassWizard;
473 return Core::IWizard::ProjectWizard;
476 static inline QString msgError(const QXmlStreamReader &reader,
477 const QString &fileName,
480 return QString::fromLatin1("Error in %1 at line %2, column %3: %4").
481 arg(fileName).arg(reader.lineNumber()).arg(reader.columnNumber()).arg(what);
484 static inline bool booleanAttributeValue(const QXmlStreamReader &r, const char *nameC,
487 const QStringRef attributeValue = r.attributes().value(QLatin1String(nameC));
488 if (attributeValue.isEmpty())
490 return attributeValue == QLatin1String("true");
493 static inline int integerAttributeValue(const QXmlStreamReader &r, const char *name, int defaultValue)
495 const QString sValue = r.attributes().value(QLatin1String(name)).toString();
496 if (sValue.isEmpty())
499 const int value = sValue.toInt(&ok);
501 qWarning("Invalid integer value specification '%s' for attribute '%s'.",
502 qPrintable(sValue), name);
508 static inline QString attributeValue(const QXmlStreamReader &r, const char *name)
510 return r.attributes().value(QLatin1String(name)).toString();
513 // Return locale language attribute "de_UTF8" -> "de", empty string for "C"
514 static inline QString languageSetting()
516 QString name = Core::ICore::instance()->userInterfaceLanguage();
517 const int underScorePos = name.indexOf(QLatin1Char('_'));
518 if (underScorePos != -1)
519 name.truncate(underScorePos);
520 if (name.compare(QLatin1String("C"), Qt::CaseInsensitive) == 0)
525 GeneratorScriptArgument::GeneratorScriptArgument(const QString &v) :
530 // Main parsing routine
531 CustomWizardParameters::ParseResult
532 CustomWizardParameters::parse(QIODevice &device,
533 const QString &configFileFullPath,
534 Core::BaseFileWizardParameters *bp,
535 QString *errorMessage)
537 int comboEntryCount = 0;
538 QXmlStreamReader reader(&device);
539 QXmlStreamReader::TokenType token = QXmlStreamReader::EndDocument;
540 ParseState state = ParseBeginning;
543 bp->setKind(Core::IWizard::ProjectWizard);
544 const QString language = languageSetting();
545 CustomWizardField field;
547 token = reader.readNext();
549 case QXmlStreamReader::Invalid:
550 *errorMessage = msgError(reader, configFileFullPath, reader.errorString());
552 case QXmlStreamReader::StartElement:
554 // Read out subelements applicable to current state
555 if (state == ParseWithinWizard && parseCustomProjectElement(reader, configFileFullPath, language, this, bp))
557 // switch to next state
558 state = nextOpeningState(state, reader.name());
559 // Read attributes post state-switching
562 *errorMessage = msgError(reader, configFileFullPath,
563 QString::fromLatin1("Unexpected start element %1").arg(reader.name().toString()));
565 case ParseWithinWizard:
566 if (!booleanAttributeValue(reader, wizardEnabledAttributeC, true))
567 return ParseDisabled;
568 bp->setId(attributeValue(reader, idAttributeC));
569 bp->setCategory(attributeValue(reader, categoryAttributeC));
570 bp->setKind(kindAttribute(reader));
571 klass = attributeValue(reader, klassAttributeC);
572 firstPageId = integerAttributeValue(reader, firstPageAttributeC, -1);
574 case ParseWithinField: // field attribute
575 field.name = attributeValue(reader, fieldNameAttributeC);
576 field.mandatory = booleanAttributeValue(reader, fieldMandatoryAttributeC, false);
578 case ParseWithinFieldDescription:
579 assignLanguageElementText(reader, language, &field.description);
580 state = ParseWithinField; // The above reads away the end tag, set state.
582 case ParseWithinFieldControl: // Copy widget control attributes
583 field.controlAttributes = attributesToStringMap(reader.attributes());
585 case ParseWithinComboEntries:
587 case ParseWithinComboEntry: // Combo entry with 'value' attribute
588 field.controlAttributes.insert(CustomWizardField::comboEntryValueKey(comboEntryCount),
589 attributeValue(reader, "value"));
591 case ParseWithinComboEntryText: {
593 // This reads away the end tag, set state here.
595 if (assignLanguageElementText(reader, language, &text))
596 field.controlAttributes.insert(CustomWizardField::comboEntryTextKey(comboEntryCount),
598 state = ParseWithinComboEntry;
601 case ParseWithinFiles:
603 case ParseWithinFile: { // file attribute
604 CustomWizardFile file;
605 file.source = attributeValue(reader, fileSourceAttributeC);
606 file.target = attributeValue(reader, fileTargetAttributeC);
607 file.openEditor = booleanAttributeValue(reader, customWizardFileOpenEditorAttributeC, false);
608 file.openProject = booleanAttributeValue(reader, customWizardFileOpenProjectAttributeC, false);
609 file.binary = booleanAttributeValue(reader, fileBinaryAttributeC, false);
610 if (file.target.isEmpty())
611 file.target = file.source;
612 if (file.source.isEmpty()) {
613 qWarning("Skipping empty file name in custom project.");
615 files.push_back(file);
619 case ParseWithinScript:
620 filesGeneratorScript = fixGeneratorScript(configFileFullPath, attributeValue(reader, generatorScriptBinaryAttributeC));
621 if (filesGeneratorScript.isEmpty()) {
622 *errorMessage = QString::fromLatin1("No binary specified for generator script.");
625 filesGeneratorScriptWorkingDirectory = attributeValue(reader, generatorScriptWorkingDirectoryAttributeC);
627 case ParseWithinScriptArguments: {
628 GeneratorScriptArgument argument(attributeValue(reader, generatorScriptArgumentValueAttributeC));
629 if (argument.value.isEmpty()) {
630 *errorMessage = QString::fromLatin1("No value specified for generator script argument.");
633 if (booleanAttributeValue(reader, generatorScriptArgumentOmitEmptyAttributeC, false))
634 argument.flags |= GeneratorScriptArgument::OmitEmpty;
635 if (booleanAttributeValue(reader, generatorScriptArgumentWriteFileAttributeC, false))
636 argument.flags |= GeneratorScriptArgument::WriteFile;
637 filesGeneratorScriptArguments.push_back(argument);
640 case ParseWithinValidationRule: {
641 CustomWizardValidationRule rule;
642 rule.condition = reader.attributes().value(QLatin1String(ruleConditionAttributeC)).toString();
643 rules.push_back(rule);
646 case ParseWithinValidationRuleMessage:
647 QTC_ASSERT(!rules.isEmpty(), return ParseFailed; )
648 // This reads away the end tag, set state here.
649 assignLanguageElementText(reader, language, &(rules.back().message));
650 state = ParseWithinValidationRule;
657 case QXmlStreamReader::EndElement:
658 state = nextClosingState(state, reader.name());
661 *errorMessage = msgError(reader, configFileFullPath,
662 QString::fromLatin1("Unexpected end element %1").arg(reader.name().toString()));
664 case ParseWithinFields: // Leaving a field element
665 fields.push_back(field);
668 case ParseWithinComboEntries:
678 } while (token != QXmlStreamReader::EndDocument);
682 CustomWizardParameters::ParseResult
683 CustomWizardParameters::parse(const QString &configFileFullPath,
684 Core::BaseFileWizardParameters *bp,
685 QString *errorMessage)
687 QFile configFile(configFileFullPath);
688 if (!configFile.open(QIODevice::ReadOnly|QIODevice::Text)) {
689 *errorMessage = QString::fromLatin1("Cannot open %1: %2").arg(configFileFullPath, configFile.errorString());
692 return parse(configFile, configFileFullPath, bp, errorMessage);
695 QString CustomWizardParameters::toString() const
698 QTextStream str(&rc);
699 str << "Directory: " << directory << " Klass: '" << klass << "'\n";
700 if (!filesGeneratorScriptArguments.isEmpty()) {
702 foreach(const QString &a, filesGeneratorScript)
703 str << " '" << a << '\'';
704 if (!filesGeneratorScriptWorkingDirectory.isEmpty())
705 str << "\nrun in '" << filesGeneratorScriptWorkingDirectory << '\'';
706 str << "\nArguments: ";
707 foreach(const GeneratorScriptArgument &a, filesGeneratorScriptArguments) {
708 str << " '" << a.value << '\'';
709 if (a.flags & GeneratorScriptArgument::OmitEmpty)
710 str << " [omit empty]";
711 if (a.flags & GeneratorScriptArgument::WriteFile)
712 str << " [write file]";
717 foreach(const CustomWizardFile &f, files) {
718 str << " File source: " << f.source << " Target: " << f.target;
727 foreach(const CustomWizardField &f, fields) {
728 str << " Field name: " << f.name;
731 str << " Description: '" << f.description << '\'';
732 if (!f.controlAttributes.isEmpty()) {
733 typedef CustomWizardField::ControlAttributeMap::const_iterator AttrMapConstIt;
735 const AttrMapConstIt cend = f.controlAttributes.constEnd();
736 for (AttrMapConstIt it = f.controlAttributes.constBegin(); it != cend; ++it)
737 str << '\'' << it.key() << "' -> '" << it.value() << "' ";
741 foreach(const CustomWizardValidationRule &r, rules)
742 str << " Rule: '" << r.condition << "'->'" << r.message << '\n';
746 // ------------ CustomWizardContext
748 static inline QString passThrough(const QString &in) { return in; }
750 // Do field replacements applying modifiers and string transformation
752 template <class ValueStringTransformation>
753 bool replaceFieldHelper(ValueStringTransformation transform,
754 const CustomWizardContext::FieldReplacementMap &fm,
757 bool nonEmptyReplacements = false;
759 qDebug().nospace() << "CustomWizardContext::replaceFields with " <<
762 const QChar delimiter = QLatin1Char('%');
763 const QChar modifierDelimiter = QLatin1Char(':');
765 while (pos < s->size()) {
766 pos = s->indexOf(delimiter, pos);
769 int nextPos = s->indexOf(delimiter, pos + 1);
772 nextPos++; // Point past 2nd delimiter
773 if (nextPos == pos + 2) {
774 pos = nextPos; // Skip '%%'
777 // Evaluate field specification for modifiers
779 QString fieldSpec = s->mid(pos + 1, nextPos - pos - 2);
780 const int fieldSpecSize = fieldSpec.size();
781 char modifier = '\0';
782 if (fieldSpecSize >= 3 && fieldSpec.at(fieldSpecSize - 2) == modifierDelimiter) {
783 modifier = fieldSpec.at(fieldSpecSize - 1).toLatin1();
784 fieldSpec.truncate(fieldSpecSize - 2);
786 const CustomWizardContext::FieldReplacementMap::const_iterator it = fm.constFind(fieldSpec);
787 if (it == fm.constEnd()) {
788 pos = nextPos; // Not found, skip
792 QString replacement = it.value();
795 replacement = it.value().toLower();
798 replacement = it.value().toUpper();
800 case 'c': // Capitalize first letter
801 replacement = it.value();
802 if (!replacement.isEmpty())
803 replacement[0] = replacement.at(0).toUpper();
808 if (!replacement.isEmpty())
809 nonEmptyReplacements = true;
810 // Apply transformation to empty values as well.
811 s->replace(pos, nextPos - pos, transform(replacement));
812 nonEmptyReplacements = true;
813 pos += replacement.size();
815 return nonEmptyReplacements;
818 bool CustomWizardContext::replaceFields(const FieldReplacementMap &fm, QString *s)
820 return replaceFieldHelper(passThrough, fm, s);
823 // Transformation to be passed to replaceFieldHelper(). Writes the
824 // value to a text file and returns the file name to be inserted
825 // instead of the expanded field in the parsed template,
826 // used for the arguments of a generator script.
827 class TemporaryFileTransform {
829 typedef CustomWizardContext::TemporaryFilePtr TemporaryFilePtr;
830 typedef CustomWizardContext::TemporaryFilePtrList TemporaryFilePtrList;
832 explicit TemporaryFileTransform(TemporaryFilePtrList *f);
834 QString operator()(const QString &) const;
837 TemporaryFilePtrList *m_files;
841 TemporaryFileTransform::TemporaryFileTransform(TemporaryFilePtrList *f) :
842 m_files(f), m_pattern(QDir::tempPath())
844 if (!m_pattern.endsWith(QLatin1Char('/')))
845 m_pattern += QLatin1Char('/');
846 m_pattern += QLatin1String("qtcreatorXXXXXX.txt");
849 QString TemporaryFileTransform::operator()(const QString &value) const
851 TemporaryFilePtr temporaryFile(new QTemporaryFile(m_pattern));
852 QTC_ASSERT(temporaryFile->open(), return QString(); )
854 temporaryFile->write(value.toLocal8Bit());
855 const QString name = temporaryFile->fileName();
856 temporaryFile->flush();
857 temporaryFile->close();
858 m_files->push_back(temporaryFile);
862 bool CustomWizardContext::replaceFields(const FieldReplacementMap &fm, QString *s,
863 TemporaryFilePtrList *files)
865 return replaceFieldHelper(TemporaryFileTransform(files), fm, s);
868 void CustomWizardContext::reset()
870 // Basic replacement fields: Suffixes.
871 baseReplacements.clear();
872 const Core::MimeDatabase *mdb = Core::ICore::instance()->mimeDatabase();
873 baseReplacements.insert(QLatin1String("CppSourceSuffix"),
874 mdb->preferredSuffixByType(QLatin1String(CppTools::Constants::CPP_SOURCE_MIMETYPE)));
875 baseReplacements.insert(QLatin1String("CppHeaderSuffix"),
876 mdb->preferredSuffixByType(QLatin1String(CppTools::Constants::CPP_HEADER_MIMETYPE)));
877 replacements.clear();
882 QString CustomWizardContext::processFile(const FieldReplacementMap &fm, QString in)
889 replaceFields(fm, &in);
892 QString errorMessage;
893 if (!customWizardPreprocess(in, &out, &errorMessage)) {
894 qWarning("Error preprocessing custom widget file: %s\nFile:\n%s",
895 qPrintable(errorMessage), qPrintable(in));
901 } // namespace Internal
902 } // namespace ProjectExplorer