/*========================================================================= Program: Insight Segmentation & Registration Toolkit Module: $RCSfile$ Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) 2002 Insight Consortium. All rights reserved. See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "cmCableClassSet.h" /** * Add to the set of required sources to define the class. */ void cmCableClass::AddSources(const Sources& sources) { for(Sources::const_iterator s = sources.begin(); s != sources.end(); ++s) { m_Sources.insert(*s); } } /** * Add to the set of required sources to define the class. */ void cmCableClass::AddSource(const char* source) { m_Sources.insert(source); } /** * The destructor frees all the cmCableClass instances in the set. */ cmCableClassSet::~cmCableClassSet() { for(CableClassMap::const_iterator i = m_CableClassMap.begin(); i != m_CableClassMap.end(); ++i) { delete i->second; } } /** * Add a class to the set. * Automatically replace ">>" with "> >" to prevent template class name * problems after replacements. */ void cmCableClassSet::AddClass(const char* in_name, cmCableClass* cableClass) { cmStdString name = in_name; for(cmStdString::size_type pos = name.find(">>"); pos != cmStdString::npos; pos = name.find(">>", pos+2)) { name.replace(pos, 2, "> >"); } m_CableClassMap.insert(CableClassMap::value_type(name, cableClass)); } /** * Add a source to every class in the set. This should only be done after * all classes have been inserted. */ void cmCableClassSet::AddSource(const char* name) { for(CableClassMap::iterator c = m_CableClassMap.begin(); c != m_CableClassMap.end(); ++c) { c->second->AddSource(name); } } /** * Get the size of the internal CableClassMap used to store the set. */ size_t cmCableClassSet::Size() const { return m_CableClassMap.size(); } /** * Get a begin iterator to the internal CableClassMap used to store the * set. */ cmCableClassSet::CableClassMap::const_iterator cmCableClassSet::Begin() const { return m_CableClassMap.begin(); } /** * Get an end iterator to the internal CableClassMap used to store the * set. */ cmCableClassSet::CableClassMap::const_iterator cmCableClassSet::End() const { return m_CableClassMap.end(); } /** * A utility class to generate element combinations from all possible * substitutions of set members into a $ token. */ class ElementCombinationGenerator { public: ElementCombinationGenerator(const char* in_element, cmMakefile* in_makefile, cmCableClassSet* out_set): m_Makefile(in_makefile), m_OutputSet(out_set) { this->ParseInputElement(in_element); } ~ElementCombinationGenerator(); void Generate(); public: /** * Represent a substitution. */ class Substitution { public: Substitution() {} void Bind(const cmStdString& in_code, const cmCableClass* in_class) { m_Code = in_code; m_Class = in_class; } const cmCableClass* GetClass() const { return m_Class; } const cmStdString& GetCode() const { return m_Code; } private: /** * The cmCableClass associated with this substitution. */ const cmCableClass* m_Class; /** * The code to be used for the substitution. */ cmStdString m_Code; }; /** * Interface to the parts of an input string of code, possibly with * $SomeSetName tokens in it. An indivitual Portion will be either * a StringPortion, which has no substitutions, or a ReplacePortion, * which has only a substitution, and no hard-coded text. * * This is used by cmCableClassSet::GenerateElementCombinations() to * hold the pieces of a string after the set substitution tokens * have been extracted. */ class Portion { public: /** * Get the C++ code corresponding to this Portion of a string. */ virtual cmStdString GetCode() const =0; /** * Get the class corresponding to this Portion of a string. This is NULL * for StringPortion, and points to a cmCableClass for ReplacePortion. */ virtual const cmCableClass* GetClass() const { return NULL; } virtual ~Portion() {} }; /** * Represent a hard-coded part of an input string, that has no substitutions * in it. The tag for this part of a string is always empty. */ class StringPortion: public Portion { public: StringPortion(const cmStdString& in_code): m_Code(in_code) {} virtual cmStdString GetCode() const { return m_Code; } virtual const cmCableClass* GetClass() const { return NULL; } virtual ~StringPortion() {} private: /** * Hold this Portion's contribution to the output string. */ cmStdString m_Code; }; /** * Represent the "$SomeSetName" portion of an input string. This has a * reference to the Substitution holding the real output to generate. */ class ReplacePortion; friend class ReplacePortion; class ReplacePortion: public Portion { public: ReplacePortion(const Substitution& in_substitution): m_Substitution(in_substitution) {} virtual cmStdString GetCode() const { return m_Substitution.GetCode(); } virtual const cmCableClass* GetClass() const { return m_Substitution.GetClass(); } virtual ~ReplacePortion() {} private: /** * Refer to the real Substitution for this Portion's contribution. */ const Substitution& m_Substitution; }; typedef std::list Portions; typedef std::map Substitutions; /** * The makefile in which to lookup set names. */ cmMakefile* m_Makefile; /** * The cmCableClassSet instance to be filled with combinations. */ cmCableClassSet* m_OutputSet; /** * The class name parsed out for this element, before set expansion. */ cmStdString m_ClassName; /** * The tag name parsed out or generated for this element. */ cmStdString m_Tag; /** * The set of sources parsed out for this element. */ cmCableClass::Sources m_Sources; /** * The parts of the input string after parsing of the tokens. */ Portions m_Portions; /** * Map from substitution's Set to actual Substitution. */ Substitutions m_Substitutions; private: void Generate(Substitutions::const_iterator); void ParseInputElement(const char*); void SplitClassName(); cmStdString ParseSetName(cmStdString::const_iterator&, cmStdString::const_iterator) const; void FindTagSource(); bool GenerateTag(const cmStdString&); }; /** * Destructor frees portions and substitutions that were allocated by * constructor. */ ElementCombinationGenerator ::~ElementCombinationGenerator() { // Free the string portions that were allocated. for(Portions::iterator portion = m_Portions.begin(); portion != m_Portions.end(); ++portion) { delete *portion; } // Free the substitutions that were allocated. for(Substitutions::iterator sub = m_Substitutions.begin(); sub != m_Substitutions.end(); ++sub) { delete sub->second; } } /** * Generate all element combinations possible with the set of * substitutions available. The given output set is filled with * all the combinations. */ void ElementCombinationGenerator ::Generate() { // If there are no substitutions to be made, just generate this // single combination. if(m_Substitutions.empty()) { cmCableClass* cableClass = new cmCableClass(m_Tag); cableClass->AddSources(m_Sources); m_OutputSet->AddClass(m_ClassName.c_str(), cableClass); return; } // We must generate all combinations of substitutions. // Begin the recursion with the first substitution. this->Generate(m_Substitutions.begin()); } /** * Internal helper to Generate() which generates all * combinations in a recursive, depth-first order. */ void ElementCombinationGenerator ::Generate(Substitutions::const_iterator substitution) { // Test our position in the list of substitutions to be bound. if(substitution == m_Substitutions.end()) { // All substitutions have been prepared. Generate this combination. cmStdString tag = m_Tag; cmStdString code = ""; // The set of sources for the generated combination. It will // always include the sources parsed from the original element // string. cmCableClass::Sources sources = m_Sources; // Put together all the pieces, with substitutions. for(Portions::const_iterator i = m_Portions.begin(); i != Portions::const_iterator(m_Portions.end()); ++i) { // See if there is a class associated with this portion. const cmCableClass* curClassPortion = (*i)->GetClass(); if(curClassPortion) { // Append the tag from the class portion. tag.append(curClassPortion->GetTag()); // Include any sources needed by the class in this combination's set. for(cmCableClass::Sources::const_iterator s = curClassPortion->SourcesBegin(); s != curClassPortion->SourcesEnd(); ++s) { sources.insert(*s); } } // Append the portion's code to this combination's code. code.append((*i)->GetCode()); } // Add this combination to the output set. cmCableClass* cableClass = new cmCableClass(tag); cableClass->AddSources(sources); m_OutputSet->AddClass(code.c_str(), cableClass); } else { // Get the set for this substitution. const cmCableClassSet* set = substitution->first; if(set == m_OutputSet) { // We cannot iterate over the set currently being defined. cmSystemTools::Error("CABLE class set self-reference!"); return; } // Prepare an iterator to the next substitution. Substitutions::const_iterator nextSubstitution = substitution; ++nextSubstitution; // We must iterate over all possible values for this substitution. for(cmCableClassSet::CableClassMap::const_iterator element = set->Begin(); element != set->End(); ++element) { // Bind the substitution to this element. substitution->second->Bind(element->first, element->second); // Move on to the next substitution. this->Generate(nextSubstitution); } } } /** * Called from constructor. Parses the given string to extract the * class information specified. * * The format of the string is * [tag:]class_name[;source1;source2;...] */ void ElementCombinationGenerator ::ParseInputElement(const char* in_element) { // A regular expression to match the tagged element specification. cmRegularExpression taggedElement = "^([A-Za-z_0-9]*)[ \t]*:[ \t]*([^:].*|::.*)$"; // A regular expression to match the element when more source files are given. cmRegularExpression sourcesRemain("^([^;]*);(.*)$"); cmStdString elementWithoutTag; cmStdString sourceString; bool tagGiven = false; // See if the element was tagged, and if so, pull off the tag. if(taggedElement.find(in_element)) { // A tag was given. Use it. tagGiven = true; m_Tag = taggedElement.match(1); elementWithoutTag = taggedElement.match(2); } else { // No tag was given. We will try to generate it later. elementWithoutTag = in_element; } // Separate the class name. if(sourcesRemain.find(elementWithoutTag.c_str())) { m_ClassName = sourcesRemain.match(1); sourceString = sourcesRemain.match(2); } else { m_ClassName = elementWithoutTag; } // Find any source files specified with the ";source" syntax. while(sourcesRemain.find(sourceString.c_str())) { m_Sources.insert(sourcesRemain.match(1)); sourceString = sourcesRemain.match(2); } if(sourceString != "") { m_Sources.insert(sourceString); } // If no tag was given, try to generate one. if(!tagGiven) { if(!this->GenerateTag(m_ClassName)) { cmSystemTools::Error("Cannot generate tag for class name: ", m_ClassName.c_str(), "\nPlease supply one with the \"tag:..\" syntax."); } } // If there is a .h with the name of the tag, add it as a source. this->FindTagSource(); // Split the class name up into portions for the combination // generation method. this->SplitClassName(); } /** * Parses the class name into portions. Plain text in the string is * held by a StringPortion, and a $ token for replacement is * represented by a ReplacePortion. */ void ElementCombinationGenerator ::SplitClassName() { // Break the input code into blocks alternating between literal code and // set-substitution tokens (like $SomeSetName). cmStdString currentPortion = ""; for(cmStdString::const_iterator c=m_ClassName.begin(); c != m_ClassName.end(); ++c) { // Look for the '$' to mark the beginning of a token. if(*c != '$') { currentPortion.insert(currentPortion.end(), *c); } else { // If there is a portion of the string, record it. if(currentPortion.length() > 0) { m_Portions.push_back(new StringPortion(currentPortion)); currentPortion = ""; } // Skip over the '$' character. ++c; // Get element set name token. cmStdString setName = this->ParseSetName(c, m_ClassName.end()); // We have a complete set name. Look it up in makefile's data // collection. cmData* d = m_Makefile->LookupData(setName.c_str()); // This should be a dynamic_cast, but we don't want to require RTTI. cmCableClassSet* set = static_cast(d); if(set) { // We have a valid set name. Prepare the substitution entry // for it. Substitution* sub; if(m_Substitutions.count(set) == 0) { sub = new Substitution(); m_Substitutions[set] = sub; } else { sub = m_Substitutions[set]; } m_Portions.push_back(new ReplacePortion(*sub)); setName = ""; } else { // Invalid set name. Complain. cmSystemTools::Error("Unknown name of CABLE class set: ", setName.c_str()); } // Let the loop look at this character again. --c; } } // If there is a final portion of the string, record it. if(currentPortion.length() > 0) { m_Portions.push_back(new StringPortion(currentPortion)); } } /** * Parse out the name of a Set specified after a $ in the element's string. * This is called with "c" pointing to the first character after the $, * and "end" equal to the string's end iterator. * * Returns the set name after parsing. "c" will point to the first * character after the end of the set name. */ cmStdString ElementCombinationGenerator ::ParseSetName(cmStdString::const_iterator& c, cmStdString::const_iterator end) const { cmStdString setName = ""; // Check for the $(setName) syntax. // If the first character after the '$' is a left paren, we scan for the // matching paren, and take everything in-between as the set name. if((c != end) && (*c == '(')) { unsigned int depth = 1; ++c; while(c != end) { char ch = *c++; if(ch == '(') { ++depth; } else if(ch == ')') { --depth; } if(depth == 0) { break; } setName.insert(setName.end(), ch); } return setName; } // The $(setName) syntax was not used. // Look for all characters that can be part of a qualified C++ // identifier. while(c != end) { char ch = *c; if(((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) || ((ch >= '0') && (ch <= '9')) || (ch == '_') || (ch == ':')) { setName.insert(setName.end(), ch); ++c; } else { break; } } return setName; } /** * After the tag for an element has been determined, but before * combination expansion is done, this is called to search for a * header file in the makefile's include path with the name of the * tag. This makes specifying lists of classes that are declared in * header files with their own name very convenient. */ void ElementCombinationGenerator::FindTagSource() { // If there is no tag, don't bother with this step. if(m_Tag == "") { return; } // Get the makefile's include path. const std::vector& includePath = m_Makefile->GetIncludeDirectories(); // Search the path for a file called "(m_Tag).h". for(std::vector::const_iterator dir = includePath.begin(); dir != includePath.end(); ++dir) { cmStdString filePath = *dir; m_Makefile->ExpandVariablesInString(filePath); filePath += "/"+m_Tag+".h"; if(cmSystemTools::FileExists(filePath.c_str())) { m_Sources.insert(m_Tag+".h"); return; } } } /** * Given the string representing a set element, automatically generate * the element tag for it. This function determines how the output * language of all CABLE-generated wrappers will look. */ bool ElementCombinationGenerator::GenerateTag(const cmStdString& element) { // Hold the regular expressions for matching against the element. cmRegularExpression regex; // If the element's code begins in a $, it is referring to a set name. // The set's elements have their own tags, so we don't need one. regex.compile("^[ \t]*\\$"); if(regex.find(element)) { m_Tag = ""; return true; } // Test for simple integer regex.compile("^[ \t]*([0-9]*)[ \t]*$"); if(regex.find(element)) { m_Tag = "_"; m_Tag.append(regex.match(1)); return true; } // Test for basic integer type regex.compile("^[ \t]*(unsigned[ ]|signed[ ])?[ \t]*(char|short|int|long|long[ ]long)[ \t]*$"); if(regex.find(element)) { m_Tag = "_"; if(regex.match(1) == "unsigned ") { m_Tag.append("u"); } if(regex.match(2) == "long long") { m_Tag.append("llong"); } else { m_Tag.append(regex.match(2)); } return true; } // Test for basic floating-point type regex.compile("^[ \t]*(long[ ])?[ \t]*(float|double)[ \t]*$"); if(regex.find(element)) { m_Tag = "_"; if(static_cast(regex.start(1)) > 0 && regex.match(1) == "long ") { m_Tag.append("l"); } m_Tag.append(regex.match(2)); return true; } // Test for basic wide-character type regex.compile("^[ \t]*(wchar_t)[ \t]*$"); if(regex.find(element)) { m_Tag = "_wchar"; return true; } // Test for type name (possibly with template arguments). regex.compile("^[ \t]*([A-Za-z_][A-Za-z0-9_]*)(<.*)?[ \t]*$"); if(regex.find(element)) { // The tag is the same as the type. If there were template arguments, // they are ignored since they may have their own tags. m_Tag = regex.match(1); return true; } // Test for a name with a single namespace qualifier. regex.compile("^[ \t]*([A-Za-z_][A-Za-z0-9_]*)::([A-Za-z_][A-Za-z0-9_]*)(<.*)?[ \t]*$"); if(regex.find(element)) { // The tag is the same as the namespace and type concatenated together. m_Tag = regex.match(1); m_Tag.append(regex.match(2)); return true; } // We can't generate a tag. m_Tag = ""; return false; } /** * Given an element in string form, parse out the information from it, * generate the combinations of set substitutions, and add all the * elements that result. */ void cmCableClassSet::ParseAndAddElement(const char* in_element, cmMakefile* makefile) { // Create an object to handle the generation. ElementCombinationGenerator combinationGenerator(in_element, makefile, this); // Generate the combinations. combinationGenerator.Generate(); }