/* ============================================================================== This file is part of the JUCE library - "Jules' Utility Class Extensions" Copyright 2004-7 by Raw Material Software ltd. ------------------------------------------------------------------------------ JUCE can be redistributed and/or modified 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. JUCE 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 JUCE; if not, visit www.gnu.org/licenses or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ------------------------------------------------------------------------------ If you'd like to release a closed-source product which uses JUCE, commercial licenses are also available: visit www.rawmaterialsoftware.com/juce for more information. ============================================================================== */ #include "../jucer_Headers.h" #include "jucer_GeneratedCode.h" //============================================================================== GeneratedCode::GeneratedCode (const JucerDocument* const document_) : document (document_), suffix (0) { } GeneratedCode::~GeneratedCode() { } int GeneratedCode::getUniqueSuffix() { return ++suffix; } //============================================================================== String& GeneratedCode::getCallbackCode (const String& requiredParentClass, const String& returnType, const String& prototype, const bool hasPrePostUserSections) { String parentClass (requiredParentClass); if (parentClass.isNotEmpty() && ! (parentClass.startsWith (T("public ")) || parentClass.startsWith (T("private ")) || parentClass.startsWith (T("protected ")))) { parentClass = T("public ") + parentClass; } for (int i = callbacks.size(); --i >= 0;) { CallbackMethod* const cm = callbacks.getUnchecked(i); if (cm->requiredParentClass == parentClass && cm->returnType == returnType && cm->prototype == prototype) return cm->content; } CallbackMethod* const cm = new CallbackMethod(); callbacks.add (cm); cm->requiredParentClass = parentClass; cm->returnType = returnType; cm->prototype = prototype; cm->hasPrePostUserSections = hasPrePostUserSections; return cm->content; } void GeneratedCode::removeCallback (const String& returnType, const String& prototype) { for (int i = callbacks.size(); --i >= 0;) { CallbackMethod* const cm = callbacks.getUnchecked(i); if (cm->returnType == returnType && cm->prototype == prototype) callbacks.remove (i); } } void GeneratedCode::addImageResourceLoader (const String& imageMemberName, const String& resourceName) { const String initialiser (imageMemberName + T(" (0)")); if (! initialisers.contains (initialiser, false)) { initialisers.add (initialiser); privateMemberDeclarations << "Image* " << imageMemberName << ";\n"; if (resourceName.isNotEmpty()) { constructorCode << imageMemberName << " = ImageCache::getFromMemory (" << resourceName << ", " << resourceName << "Size);\n"; destructorCode << "ImageCache::release (" << imageMemberName << ");\n"; } } } const StringArray GeneratedCode::getExtraParentClasses() const { StringArray s; for (int i = 0; i < callbacks.size(); ++i) { CallbackMethod* const cm = callbacks.getUnchecked(i); s.add (cm->requiredParentClass); } return s; } const String GeneratedCode::getCallbackDeclarations() const { String s; for (int i = 0; i < callbacks.size(); ++i) { CallbackMethod* const cm = callbacks.getUnchecked(i); s << cm->returnType << " " << cm->prototype << ";\n"; } return s; } const String GeneratedCode::getCallbackDefinitions() const { String s; for (int i = 0; i < callbacks.size(); ++i) { CallbackMethod* const cm = callbacks.getUnchecked(i); const String userCodeBlockName (T("User") + makeValidCppIdentifier (cm->prototype.upToFirstOccurrenceOf (T("("), false, false), true, true, false).trim()); if (userCodeBlockName.isNotEmpty() && cm->hasPrePostUserSections) { s << cm->returnType << " " << className << "::" << cm->prototype << "\n{\n //[" << userCodeBlockName << "_Pre]\n //[/" << userCodeBlockName << "_Pre]\n\n " << indentCode (cm->content.trim(), 4) << "\n\n //[" << userCodeBlockName << "_Post]\n //[/" << userCodeBlockName << "_Post]\n}\n\n"; } else { s << cm->returnType << " " << className << "::" << cm->prototype << "\n{\n " << indentCode (cm->content.trim(), 4) << "\n}\n\n"; } } return s; } //============================================================================== const String GeneratedCode::getClassDeclaration() const { StringArray parentClassLines; parentClassLines.addTokens (parentClasses, T(","), 0); parentClassLines.addArray (getExtraParentClasses()); parentClassLines.trim(); parentClassLines.removeEmptyStrings(); parentClassLines.removeDuplicates (false); if (parentClassLines.contains (T("public Button"), false)) parentClassLines.removeString (("public Component"), false); String r (T("class ")); r << className << T(" : "); r += parentClassLines.joinIntoString (T(",\n") + String::repeatedString (T(" "), r.length())); return r; } const String GeneratedCode::getInitialiserList() const { StringArray inits (initialisers); if (parentClassInitialiser.isNotEmpty()) inits.insert (0, parentClassInitialiser); inits.trim(); inits.removeEmptyStrings(); inits.removeDuplicates (false); String s; if (inits.size() == 0) return s; s << " : "; for (int i = 0; i < inits.size(); ++i) { String init (inits[i]); while (init.endsWithChar (T(','))) init = init.dropLastCharacters (1); s << init; if (i < inits.size() - 1) s << ",\n "; else s << "\n"; } return s; } static const String getIncludeFileCode (StringArray files) { files.trim(); files.removeEmptyStrings(); files.removeDuplicates (false); String s; for (int i = 0; i < files.size(); ++i) s << T("#include \"") << files[i] << T("\"\n"); return s; } //============================================================================== static void replaceTemplate (String& text, const String& itemName, const String& value) { for (;;) { const int index = text.indexOf (T("%%") + itemName + T("%%")); if (index < 0) break; int indentLevel = 0; for (int i = index; --i >= 0;) { if (text[i] == T('\n')) break; ++indentLevel; } text = text.replaceSection (index, itemName.length() + 4, indentCode (value, indentLevel)); } } //============================================================================== static bool getUserSection (const StringArray& lines, const String& tag, StringArray& resultLines) { const int start = indexOfLineStartingWith (lines, T("//[") + tag + T("]"), 0); if (start < 0) return false; const int end = indexOfLineStartingWith (lines, T("//[/") + tag + T("]"), start + 1); for (int i = start + 1; i < end; ++i) resultLines.add (lines [i]); return true; } static void copyAcrossUserSections (String& dest, const String& src) { StringArray srcLines, dstLines; srcLines.addLines (src); dstLines.addLines (dest); for (int i = 0; i < dstLines.size(); ++i) { if (dstLines[i].trimStart().startsWith (T("//["))) { String tag (dstLines[i].trimStart().substring (3)); tag = tag.upToFirstOccurrenceOf (T("]"), false, false); jassert (! tag.startsWithChar (T('/'))); if (! tag.startsWithChar (T('/'))) { const int endLine = indexOfLineStartingWith (dstLines, T("//[/") + tag + T("]"), i + 1); if (endLine > i) { StringArray sourceLines; if (getUserSection (srcLines, tag, sourceLines)) { int j; for (j = endLine - i; --j > 0;) dstLines.remove (i + 1); for (j = 0; j < sourceLines.size(); ++j) dstLines.insert (++i, sourceLines [j].trimEnd()); ++i; } else { i = endLine; } } } } dstLines.set (i, dstLines[i].trimEnd()); } dest = dstLines.joinIntoString (T("\n")) + T("\n"); } //============================================================================== void GeneratedCode::applyToCode (String& code, const String& fileNameRoot, const bool isForPreview, const String& oldFileWithUserData) const { // header guard.. String headerGuard ("__JUCER_HEADER_"); headerGuard << className.toUpperCase().retainCharacters (T("ABCDEFGHIJKLMNOPQRSTUVWXYZ")) << "_" << fileNameRoot.toUpperCase().retainCharacters (T("ABCDEFGHIJKLMNOPQRSTUVWXYZ")) << "_" << String::toHexString (Random::getSystemRandom().nextInt()).toUpperCase() << "__"; replaceTemplate (code, "headerGuard", headerGuard); replaceTemplate (code, "creationTime", Time::getCurrentTime().toString (true, true, true)); replaceTemplate (code, "className", className); replaceTemplate (code, "constructorParams", constructorParams); replaceTemplate (code, "initialisers", getInitialiserList()); replaceTemplate (code, "classDeclaration", getClassDeclaration()); replaceTemplate (code, "privateMemberDeclarations", privateMemberDeclarations); replaceTemplate (code, "publicMemberDeclarations", getCallbackDeclarations() + "\n" + publicMemberDeclarations); replaceTemplate (code, "methodDefinitions", getCallbackDefinitions()); replaceTemplate (code, "includeFilesH", getIncludeFileCode (includeFilesH)); replaceTemplate (code, "includeFilesCPP", getIncludeFileCode (includeFilesCPP)); replaceTemplate (code, "constructor", constructorCode); replaceTemplate (code, "destructor", destructorCode); if (! isForPreview) { replaceTemplate (code, "metadata", jucerMetadata); replaceTemplate (code, "staticMemberDefinitions", staticMemberDefinitions); } else { replaceTemplate (code, "metadata", T(" << Metadata isn't shown in the code preview >>\n")); replaceTemplate (code, "staticMemberDefinitions", T("// Static member declarations and resources would go here... (these aren't shown in the code preview)")); } copyAcrossUserSections (code, oldFileWithUserData); }