// The majority of the application is dedicated to building the // current contribitors list by parsing the ChangeLog, it just takes // one line in the main itself to report the version number. #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; #if defined(_MSC_VER) #pragma setlocale("C") #endif typedef pair NamePair; typedef map NameMap; typedef vector Words; NamePair EmptyNamePair; NamePair NameRobertOsfield("Robert", "Osfield"); NamePair NameDonBurns ("Don", "Burns"); const char* validNames[] = { "de", "den", "van" }; const char* invalidNames[] = { "Added", "AnimationPath", "AnimationPathCallback", "AnimationPathManipulator", "ArgumentParser", "AttrData", "AutoTransform", "AutoTransform.", "Azimuth", "BlenColour(Vec4", "Camera", "CameraBarrierCallback", "CameraNode", "Canvas", "CluserCullingCallback", "ClusterCullingCallback", "CoordinateSystem", "CoordinateSystemNode", "CoordinateSystemNode&", "CopyOp", "Copyright", "Core", "Creator", "CullCallbacks", "CullingSettngs", "CullVisitor", "CullVistor", "Cygwin", "DatabasePager", "DataSet", "DataVariance", "DeleteHandler", "DisplaySettings", "Drawable", "DrawElementsUByte", "Endian", "Escape", "EventQueue", "Face", "Files", "Fixed", "Fixes", "FluidProgram", "FontImplementation", "FrameStats", "Framstamp", "FreeBSD", "Geode", "GeoemtryTechnique", "GeometryTechnique", "GeoSet", "GraphicsContext", "GraphicsContextImplementation", "GraphicsThread", "GraphicsWindowX11", "GraphicsHandleX11", "Group", "Image", "ImageOptions", "Images", "Images/SolarSystem", "ImageStream", "Improved", "IndexBlock", "IndexFaceSets", "IntersectVisitor", "Inventor", "KdTree", "KeyboardMouse", "KeyboardMouseCallback", "LightModel", "LightPointNode", "LineSegment", "LineStipple", "Logos", "LongIDRecord", "Make", "Makefile", "Material", "Matrix", "Matrix*", "MatrixManipulator", "MatrixTransform", "MemoryManager", "MergeGeometryVisitor", "MeshRecord", "Mode", "Mr", "Multigen", "New", "NewCullVisitor", "NodeVisitor", "Object", "OpenDX", "OpenSceneGraph", "OpenSceneGraph-2", "OpenSceneGraph-osgWidget-dev", "OpenThreads", "OperationThread", "Optimizer", "OrientationConverter", "Osfields", "Ouput", "Output", "OveralyNode", "PagedLOD", "PageLOD", "Paging", "ParentList", "Performer", "PickHandler", "PixelDataBufferObject", "PolygonStipple", "PolytopeVisitor", "PrimitiveIndexFunctor", "Prodcuer", "Producer", "ProducerEventCallback", "Program", "Proxy", "ProxyNode", "Quake3", "Quicktime", "ReaderWriters", "ReaderWriterTXP", "ReentrantMutex", "Referenced", "ReferenceFrame", "Removed", "RemoveLoadedProxyNodes", "RenderStage", "RenderTargetFallback", "RenderToTextureStage", "SceneGraphBuilder", "SceneView", "Sequence", "Shape", "SimpleViewer", "SimpleViewer", "SmokeTrailEffect", "Source", "SoftImage", "SpotExponent", "State", "StateAttribute", "Stats", "StateSet", "StatsVisitor", "Studio", "Support", "Switch", "TechniqueEventHandler", "TerraPage", "TessellationHints", "TestManipulator", "TestSupportCallback", "TexEnv", "TexMat(Matrix", "Texture", "Texture2DArray", "Texture3D", "TextureCubeMap", "TextureObjectManager", "TextureRectangle", "TextureRectangle(Image*", "TextureType", "Texuture", "TriStripVisitor", "TrPageViewer", "Uniform", "UseFarLineSegments", "UserData", "Valve", "Vec*", "Vec2d", "Vec3d", "Vec4ub", "Version", "VertexData", "View", "Viewer", "ViewPoint", "Viewport", "Viewport", "Visual", "VisualStudio", "WindowData", "Windows", "X", "Xcode", "GraphicsWindowWin32", "ImageLayer", "Xml", "Y", "UpdateSkeleton" }; struct TypoCorrection { const char* original; const char* corrected; }; TypoCorrection typoCorrections[] = { {"Aderian", "Adrian"}, {"Adndre", "Andre"}, {"Adrain", "Adrian"}, {"Amueller", "Aumueller"}, {"Andew", "Andrew"}, {"André", "André"}, {"Antione", "Antoine"}, {"Antonoine", "Antoine"}, {"Atr", "Art"}, {"Baverage", "Beverage"}, {"Bistroviae", "Bistrovic"}, {"Callue", "Callu"}, {"Christaiansen", "Christiansen"}, {"Cobin", "Corbin"}, {"Comporesi", "Camporesi"}, {"Connel", "Connell"}, {"Cullu", "Callu"}, {"Daneil", "Daniel"}, {"Daust", "Daoust"}, {"Daved", "David"}, {"Drederic", "Frederic"}, {"Eileman", "Eilemann"}, {"Elgi", "Egli"}, {"Fabian", "Fabien"}, {"Fautre", "Fautré"}, {"Frashid", "Farshid"}, {"Fred", "Frederic"}, {"Fredrick", "Frederic"}, {"Fredric", "Frederic"}, {"Froechlich", "Fröhlich"}, {"Froehilch", "Fröhlich"}, {"Froehlich", "Fröhlich"}, {"Froelich", "Fröhlich"}, {"Froenlich", "Fröhlich"}, {"Fröhlich", "Fröhlich"}, {"Fruciel", "Frauciel"}, {"García", "Garcea"}, {"Garrat", "Garrett"}, {"Garret", "Garrett"}, {"Geof", "Geoff"}, {"Giatan", "Gaitan"}, {"Gronenger", "Gronager"}, {"Gronger", "Gronager"}, {"Haritchablaet", "Haritchabalet"}, {"Hebelin", "Herbelin"}, {"Heirtlein", "Hertlein"}, {"Heirtlein", "Hertlein"}, {"Hertleinm", "Hertlein"}, {"Happalahti", "Haapalahti"}, {"Hertlien", "Hertlein"}, {"Hatwig", "Hartwig"}, {"Hi", "He"}, {"Hooper", "Hopper"}, {"Inverson", "Iverson"}, {"Iversion", "Iverson"}, {"Jean-Sebastein", "Jean-Sebastien"}, {"Jean-Sebastian", "Jean-Sebastien"}, {"Jean-Sebastirn", "Jean-Sebastien"}, {"Jea-Sebastien", "Jean-Sebastien"}, {"Johhansen", "Johansen"}, {"Johnansen", "Johansen"}, {"Johnasen", "Johansen"}, {"Jolly", "Jolley"}, {"Jose", "José"}, {"José", "José"}, {"Joson", "Jason"}, {"J", "José"}, {"Keuhne", "Kuehne"}, {"Kheune", "Kuehne"}, {"Krulthof", "Kruithof"}, {"Lagrade", "Lagarde"}, {"Largade", "Lagarde"}, {"Larshkari", "Lashkari"}, {"Lashakari", "Lashkari"}, {"Lashari", "Lashkari"}, {"Lasharki", "Lashkari"}, {"Laskari", "Lashkari"}, {"Leandowski", "Lewandowski"}, {"Lawandowski", "Lewandowski"}, {"Luacas", "Luaces"}, {"Luacas", "Luaces"}, {"Liech", "Leich"}, {"Lownman", "Lowman"}, {"Lugi", "Luigi"}, {"Lweandowski", "Lewandowski"}, {"Maceij", "Maciej"}, {"Machler", "Mächler"}, {"Macro", "Marco"}, {"Maechler", "Mächler"}, {"Mahai", "Mihai"}, {"Mammond", "Marmond"}, {"March", "Marco"}, {"Martsz", "Martz"}, {"Marz", "Martz"}, {"Matz", "Martz"}, {"Melchoir", "Melchior"}, {"Mellis", "Melis"}, {"Messerschimdt", "Messerschmidt"}, {"Micheal", "Michael"}, {"Mihair", "Mihai"}, {"Molishtan", "Moloshtan"}, {"Molishtan", "Moloshtan"}, {"Moloshton", "Moloshtan"}, {"Morné", "Morné"}, {"Morne", "Morné"}, {"Moule", "Moiule"}, {"Narache", "Marache"}, {"Nicklov", "Nikolov"}, {"Nickolov", "Nikolov"}, {"Nilson", "Nilsson"}, {"Olad", "Olaf"}, {"Olar", "Olaf"}, {"Oritz", "Ortiz"}, {"Osfeld", "Osfield"}, {"Osfied", "Osfield"}, {"Pail", "Paul"}, {"Perciva", "Peciva"}, {"Pecvia", "Peciva"}, {"Priyadashi", "Priyadarshi"}, {"Rajce", "Trajce"}, {"Raymon", "Raymond"}, {"Randal", "Randall"}, {"Robet", "Robert"}, {"Rodger", "Roger"}, {"Rolad", "Roland"}, {"Riddel", "Riddell"}, {"Rucard", "Richard"}, {"Sekender", "Sukender"}, {"Sewel", "Sewell"}, {"Simmonson", "Simonsson"}, {"Simmonsson", "Simonsson"}, {"Sjolie", "Sjölie"}, {"Sjölie", "Sjölie"}, {"Skinnder", "Skinner"}, {"Seberion", "Seberino"}, {"Sokolosky", "Sokolowsky"}, {"Sokolowky", "Sokolowsky"}, {"Sokolowki", "Sokolowsky"}, {"Sokolowski", "Sokolowsky"}, {"Sokolsky", "Sokolowsky"}, {"Sokolwsky", "Sokolowsky"}, {"Sonda", "Sondra"}, {"Stansilav", "Stanislav"}, {"Stefan", "Stephan"}, {"Stell", "Steel"}, {"Sylvan", "Sylvain"}, {"Takeahei", "Takahei"}, {"Takehei", "Takahei"}, {"Tery", "Terry"}, {"Tarantilils", "Tarantilis"}, {"Trastenjak", "Trstenjak"}, {"Urlich", "Ulrich"}, {"Vines", "Vine"}, {"Waldrom", "Waldron"}, {"Wedner", "Weidner"}, {"Weidemann", "Wiedemann"}, {"Wieblen", "Weiblen"}, {"Woesnner", "Woessner"}, {"Wojchiech", "Wojciech"}, {"Wojcoech", "Wojciech"}, {"Wojiech", "Wojciech"}, {"Xennon", "Hanson"}, {"Yefrei", "Yefei"}, {"Yfei", "Yefei"}, {"Gurhrie", "Guthrie"}, {"Byran", "Bryan"} }; struct NameCorrection { const char* originalFirst; const char* originalSecond; const char* correctedFirst; const char* correctedSecond; }; NameCorrection nameCorrections[] = { {"Nguyen", "Van", "Nguyen", "Van Truong"}, {"Thom", "Carlo", "Thom", "DeCarlo"}, {"Stephan", "Lamoliatte", "Stephane", "Lamoliatte"}, {"Ronald", "van", "Ronald", "van Maarseveen"}, {"Lee", "Bulter", "Lee", "Butler"}, {"Tery", "Welsh", "Terry", "Welsh"}, {"Cesar", "L", "César", "L. B. Silveira"}, {"Marc", "Sciabica", "Mark", "Sciabica"}, {"Tom", "Moore", "Tim", "Moore"}, {"Time", "Moore", "Tim", "Moore"}, {"Jean", "Sebastien", "Jean-Sebastien", "Guay"}, {"Michale", "Platings", "Michael", "Platings"}, {"Mick", "", "Maik", "Keller"}, {"Gary", "Quin", "Gary", "Quinn"}, {"BjornHein", "", "Björn", "Hein"}, {"Bjorn", "Hein", "Björn", "Hein"}, {"Erik", "van", "Erik", "den Dekker"}, {"Erik", "den", "Erik", "den Dekker"}, {"Jeoen", "den", "Jeroen", "den Dekker"}, {"John", "Vidar", "John", "Vidar Larring"}, {"John", "Vida", "John", "Vidar Larring"}, {"Sebastien", "Messerschmidt", "Sebastian", "Messerschmidt"}, {"Mattias", "Fröhlich", "Mathias", "Fröhlich"}, {"Mathias", "Helsing", "Mattias", "Helsing"}, {"Jose", "Delport", "J.P.", "Delport"}, {"José", "Delport", "J.P.", "Delport"}, {"Franz", "Melchior", "Melchior", "Franz"}, {"Glen", "Waldon", "Glenn", "Waldron"}, {"Ralf", "Karn", "Ralf", "Kern"}, {"Donny", "Cipperly", "Donald", "Cipperly"}, {"Gino", "van", "Gino", "van den Bergen"}, {"Radu", "Mihai", "Mihai", "Radu"}, {"Art", "Trevs", "Art", "Tevs"}, {"Tim", "More", "Tim", "Moore"}, {"Andre", "Garneau", "André", "Garneau"}, {"Eric", "Hammil", "Chris", "Hanson"}, {"Paul", "de", "Paul", "de Repentigny"}, {"Raymond", "de", "Raymond", "de Vries"}, {"Nick", "", "Trajce", "Nikolov"}, {"Daniel", "", "Daniel", "Sjölie"}, {"Julia", "Ortiz", "Julian", "Ortiz"}, {"Rune", "Schmidt", "Rune", "Schmidt Jensen"}, {"Romano", "José", "Romano", "José Magacho da Silva"}, {"Rommano", "Silva", "Romano", "José Magacho da Silva"}, {"Leandro", "Motta", "Leandro", "Motta Barros"}, {"A", "Botorabi", "Ali", "Botorabi"}, {"Waltice", "", "Walter", "J. Altice"}, {"Drew", "", "Drew", "Whitehouse"}, {"Douglas", "A", "Douglas", "A. Pouk"}, {"Colin", "MacDonald", "Colin", "McDonald"}, {"Nikolov", "Trajce", "Trajce", "Nikolov"}, {"Frauciel", "Luc", "Luc", "Frauciel"}, {"Alberto", "Lucas", "Alberto", "Luaces"}, {"Tyge", "", "Tyge", "Løvset"} }; bool validName(const string& first) { // Check for valid names for (unsigned int i = 0; i < sizeof(validNames) / sizeof(char*); ++i) { if (first == validNames[i]) { return true; } } if (first.empty()) return false; if (first[0] < 'A' || first[0] > 'Z') return false; if (first.size() >= 2 && (first[1] < 'a' || first[1] > 'z') && (first[1] != '.') && (first[1] != ',')) return false; // Check for invalid names for (unsigned int i = 0; i < sizeof(invalidNames) / sizeof(char*); ++i) { if (first == invalidNames[i]) { return false; } } // Default to a valid name return true; } string typoCorrection(const string& name) { // Loop over all known typo corrections for (unsigned int i = 0; i < sizeof(typoCorrections) / sizeof(TypoCorrection); ++i) { // If a typo is found if (name == typoCorrections[i].original) { // Return the correction return typoCorrections[i].corrected; } } return name; } void nameCorrection(NamePair& name) { // Loop over all known name corrections for (unsigned int i = 0; i < sizeof(nameCorrections) / sizeof(NameCorrection); ++i) { // If a matching name is found if (name.first == nameCorrections[i].originalFirst && name.second == nameCorrections[i].originalSecond) { // Return the correction name.first = nameCorrections[i].correctedFirst; name.second = nameCorrections[i].correctedSecond; // Early out, since we don't want corrections of corrections return; } } } void lastValidCharacter(const string& name, unsigned int& pos, char c) { for (unsigned int i = 0; i < pos; ++i) { if (name[i] == c) { pos = i; return; } } } void lastValidCharacter(const string& name, unsigned int& last) { lastValidCharacter(name, last, '.'); lastValidCharacter(name, last, ','); lastValidCharacter(name, last, '\''); lastValidCharacter(name, last, '/'); lastValidCharacter(name, last, '\\'); lastValidCharacter(name, last, ':'); lastValidCharacter(name, last, ';'); lastValidCharacter(name, last, ')'); } NamePair createName(const string& first, const string& second) { if (first.empty()) return EmptyNamePair; // cout << "first = " << first << " second = " << second << endl; unsigned int last = first.size(); lastValidCharacter(first, last); if (last == 0) return EmptyNamePair; string name; name.append(first.begin(), first.begin() + last); if (!validName(name)) return EmptyNamePair; name = typoCorrection(name); if (second.empty() || !validName(second)) { // filter any single or two letter words as unlike to be names. if (name.size() <= 2) return EmptyNamePair; return NamePair(name, ""); } last = second.size(); lastValidCharacter(second, last); if (last > 0) { string surname(second.begin(), second.begin() + last); if (validName(surname)) { surname = typoCorrection(surname); return NamePair(name, surname); } } // filter any single or two letter words as unlike to be names. if (name.size() <= 2) return EmptyNamePair; return NamePair(name, ""); } bool submissionsSequence(const Words& words, unsigned int& i) { if (i + 1 >= words.size()) return false; if (words[i] == "From" || words[i] == "from" || words[i] == "From:" || words[i] == "from:" || words[i] == "Merged" || words[i] == "Integrated") return true; if (i + 2 >= words.size()) return false; if (words[i] == "submitted" && words[i + 1] == "by") { i += 1; return true; } if (words[i] == "Folded" && words[i + 1] == "in") { i += 1; return true; } if (words[i] == "Rolled" && words[i + 1] == "in") { i += 1; return true; } if (words[i] == "Checked" && words[i + 1] == "in") { i += 1; return true; } if (i + 3 >= words.size()) return false; if (words[i] == "sent" && words[i + 1] == "in" && words[i + 2] == "by") { i += 2; return true; } return false; } void readContributors(NameMap& names, const string& file) { osgDB::ifstream fin(file.c_str()); Words words; while(fin) { string keyword; fin >> keyword; words.push_back(keyword); } string blank_string; for(unsigned int i = 0; i < words.size(); ++i) { if (submissionsSequence(words, i)) { if (i + 2 < words.size() && validName(words[i + 1])) { NamePair name = createName(words[i + 1], words[i + 2]); nameCorrection(name); if (!name.first.empty()) ++names[name]; i += 2; } else if (i + 1 < words.size() && validName(words[i + 1])) { NamePair name = createName(words[i + 1], blank_string); nameCorrection(name); if (!name.first.empty()) ++names[name]; i += 1; } } else { if (words[i] == "robert") { ++names[NameRobertOsfield]; } else if (words[i] == "don") { ++names[NameDonBurns]; } } } // reassign fisrt name entries to their full names entries if (names.size() > 1) { for (NameMap::iterator itr = names.begin(); itr != names.end(); ) { if (itr->first.second.empty()) { NameMap::iterator next_itr = itr; ++next_itr; if (next_itr != names.end() && itr->first.first == next_itr->first.first) { next_itr->second += itr->second; names.erase(itr); itr = next_itr; } else { ++itr; } } else { ++itr; } } } // remove the double entries from Robert's contributions if (names.size() > 1) { for (NameMap::iterator itr = names.begin(); itr != names.end(); ++itr) { if (itr->first != NameRobertOsfield && itr->first != NameDonBurns) { names[NameRobertOsfield] -= itr->second; } } } } void buildContributors(NameMap& names) { // top five contributors ++names[NamePair("Robert", "Osfield")]; ++names[NamePair("Don", "Burns")]; ++names[NamePair("Marco", "Jez")]; ++names[NamePair("Mike", "Weiblen")]; ++names[NamePair("Geoff", "Michel")]; ++names[NamePair("Ben", "van Basten")]; // contributors that don't appear in the ChangeLog due to their contributions // being before CVS started for the OSG, or before the name logging began. ++names[NamePair("Karsten", "Weiss")]; ++names[NamePair("Graeme", "Harkness")]; ++names[NamePair("Axel", "Volley")]; ++names[NamePair("Nikolaus", "Hanekamp")]; ++names[NamePair("Kristopher", "Bixler")]; ++names[NamePair("Tanguy", "Fautré")]; ++names[NamePair("J.E.", "Hoffmann")]; } void printContributors(const std::string& changeLog, bool printNumEntries) { NameMap names; buildContributors(names); if (!changeLog.empty()) { readContributors(names, changeLog); } typedef multimap SortedNameMap; SortedNameMap sortedNames; for (NameMap::iterator itr = names.begin(); itr != names.end(); ++itr) { sortedNames.insert(SortedNameMap::value_type(itr->second, itr->first)); } cout << names.size() << " Contributors:" << endl << endl; if (printNumEntries) { cout << "Entries Firstname Surname" << endl; cout << "-------------------------" << endl; for (SortedNameMap::reverse_iterator sitr = sortedNames.rbegin(); sitr != sortedNames.rend(); ++sitr) { cout << sitr->first << "\t" << sitr->second.first << " " << sitr->second.second << endl; } } else { cout << "Firstname Surname" << endl; cout << "-----------------" << endl; for (SortedNameMap::reverse_iterator sitr = sortedNames.rbegin(); sitr != sortedNames.rend(); ++sitr) { cout << sitr->second.first << " " << sitr->second.second << endl; } } }