├── .gitattributes ├── .gitignore ├── .nuget └── packages.config ├── .vs └── config │ └── applicationhost.config ├── CommentXMLParser ├── CommentXMLParser.csproj ├── CommentXMLParser.sln ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── app.config ├── packages.config └── packages │ ├── protobuf-net.2.0.0.480 │ ├── lib │ │ ├── ios │ │ │ ├── protobuf-net.dll │ │ │ └── protobuf-net.xml │ │ ├── mono │ │ │ ├── protobuf-net.dll │ │ │ └── protobuf-net.xml │ │ ├── net20 │ │ │ ├── protobuf-net.dll │ │ │ └── protobuf-net.xml │ │ ├── net30 │ │ │ ├── protobuf-net.dll │ │ │ └── protobuf-net.xml │ │ ├── net35 │ │ │ ├── protobuf-net.dll │ │ │ └── protobuf-net.xml │ │ ├── net40 │ │ │ ├── protobuf-net.dll │ │ │ └── protobuf-net.xml │ │ ├── sl3-wp │ │ │ ├── protobuf-net.XML │ │ │ └── protobuf-net.dll │ │ └── sl4 │ │ │ ├── protobuf-net.dll │ │ │ └── protobuf-net.xml │ └── protobuf-net.2.0.0.480.nupkg │ └── repositories.config ├── Questions.bin ├── README.md ├── SO Tag Server - explanation.xlsx ├── Sample Question - original.xml ├── Sample Question - parts used in TagServer.png ├── Sample Question - tidied up.xml ├── Sample Question.png ├── Sample Question.xml ├── Shared ├── Properties │ └── AssemblyInfo.cs ├── Question.cs └── Shared.csproj ├── StackOverflowTagServer.sln ├── TagServer ├── App.config ├── BitMapIndexHandler.cs ├── CLR │ ├── BitHelper.cs │ ├── HashHelpers.cs │ ├── HashSet.cs │ └── HashSetCache.cs ├── DataStructures │ ├── DisposableAction.cs │ ├── GCCollectionInfo.cs │ ├── Int32Converter.cs │ ├── QueryType.cs │ ├── SimpleBloomFilter.cs │ ├── TrieEntry.cs │ └── Trie`1.cs ├── EWAH │ ├── BufferedRunningLengthWord.cs │ ├── EwahCompressedBitArray.cs │ ├── EwahCompressedBitArraySerializer.cs │ ├── EwahEnumerator.cs │ └── RunningLengthWord.cs ├── Logger.cs ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── Querying │ ├── BaseQueryProcessor.cs │ ├── BitMapQueryProcessor.cs │ ├── ComplexQueryProcessor.cs │ ├── IntComparer.cs │ ├── QueryInfo.cs │ ├── QueryProcessor.cs │ ├── QueryResult.cs │ └── QueryTester.cs ├── Resources │ ├── leppie - excluded tags - old (pre July 2015).txt │ └── leppie - excluded tags.txt ├── Results.cs ├── Serialisation.cs ├── TagServer.cs ├── TagServer.csproj ├── Utils.cs ├── Validator.cs ├── WildcardProcessor.cs └── packages.config ├── WebSite ├── App_Start │ ├── BundleConfig.cs │ ├── FilterConfig.cs │ ├── RouteConfig.cs │ └── WebApiConfig.cs ├── ApplicationInsights.config ├── Areas │ └── HelpPage │ │ ├── ApiDescriptionExtensions.cs │ │ ├── App_Start │ │ └── HelpPageConfig.cs │ │ ├── Controllers │ │ └── HelpController.cs │ │ ├── HelpPage.css │ │ ├── HelpPageAreaRegistration.cs │ │ ├── HelpPageConfigurationExtensions.cs │ │ ├── ModelDescriptions │ │ ├── CollectionModelDescription.cs │ │ ├── ComplexTypeModelDescription.cs │ │ ├── DictionaryModelDescription.cs │ │ ├── EnumTypeModelDescription.cs │ │ ├── EnumValueDescription.cs │ │ ├── IModelDocumentationProvider.cs │ │ ├── KeyValuePairModelDescription.cs │ │ ├── ModelDescription.cs │ │ ├── ModelDescriptionGenerator.cs │ │ ├── ModelNameAttribute.cs │ │ ├── ModelNameHelper.cs │ │ ├── ParameterAnnotation.cs │ │ ├── ParameterDescription.cs │ │ └── SimpleTypeModelDescription.cs │ │ ├── Models │ │ └── HelpPageApiModel.cs │ │ ├── SampleGeneration │ │ ├── HelpPageSampleGenerator.cs │ │ ├── HelpPageSampleKey.cs │ │ ├── ImageSample.cs │ │ ├── InvalidSample.cs │ │ ├── ObjectGenerator.cs │ │ ├── SampleDirection.cs │ │ └── TextSample.cs │ │ ├── Views │ │ ├── Help │ │ │ ├── Api.cshtml │ │ │ ├── DisplayTemplates │ │ │ │ ├── ApiGroup.cshtml │ │ │ │ ├── CollectionModelDescription.cshtml │ │ │ │ ├── ComplexTypeModelDescription.cshtml │ │ │ │ ├── DictionaryModelDescription.cshtml │ │ │ │ ├── EnumTypeModelDescription.cshtml │ │ │ │ ├── HelpPageApiModel.cshtml │ │ │ │ ├── ImageSample.cshtml │ │ │ │ ├── InvalidSample.cshtml │ │ │ │ ├── KeyValuePairModelDescription.cshtml │ │ │ │ ├── ModelDescriptionLink.cshtml │ │ │ │ ├── Parameters.cshtml │ │ │ │ ├── Samples.cshtml │ │ │ │ ├── SimpleTypeModelDescription.cshtml │ │ │ │ └── TextSample.cshtml │ │ │ ├── Index.cshtml │ │ │ └── ResourceModel.cshtml │ │ ├── Shared │ │ │ └── _Layout.cshtml │ │ ├── Web.config │ │ └── _ViewStart.cshtml │ │ └── XmlDocumentationProvider.cs ├── Content │ ├── Site.css │ ├── bootstrap.css │ └── bootstrap.min.css ├── Controllers │ ├── HomeController.cs │ ├── InfoController.cs │ ├── QueryController.cs │ ├── QuestionsController.cs │ ├── TagsController.cs │ └── WildcardController.cs ├── Global.asax ├── Global.asax.cs ├── Infrastructure │ ├── ActionWebApiFilter.cs │ ├── BrowserJsonFormatter.cs │ ├── Extensions.cs │ └── QueryStringProcessor.cs ├── Project_Readme.html ├── Properties │ └── AssemblyInfo.cs ├── RunInIISExpress.bat ├── Scripts │ ├── _references.js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── jquery-1.10.2.intellisense.js │ ├── jquery-1.10.2.js │ ├── jquery-1.10.2.min.js │ ├── jquery-1.10.2.min.map │ ├── modernizr-2.6.2.js │ ├── respond.js │ └── respond.min.js ├── Views │ ├── Home │ │ └── Index.cshtml │ ├── Shared │ │ ├── Error.cshtml │ │ └── _Layout.cshtml │ ├── Web.config │ └── _ViewStart.cshtml ├── Web.Debug.config ├── Web.Release.config ├── Web.config ├── WebSite.csproj ├── favicon.ico ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff └── packages.config └── packages └── repositories.config /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | packages/ 134 | CommentXMLParser/packages/ 135 | 136 | # Windows Azure Build Output 137 | csx 138 | *.build.csdef 139 | 140 | # Windows Store app package directory 141 | AppPackages/ 142 | 143 | # Others 144 | sql/ 145 | *.Cache 146 | ClientBin/ 147 | [Ss]tyle[Cc]op.* 148 | ~$* 149 | *~ 150 | *.dbmdl 151 | *.[Pp]ublish.xml 152 | *.pfx 153 | *.publishsettings 154 | 155 | # RIA/Silverlight projects 156 | Generated_Code/ 157 | 158 | # Backup & report files from converting an old project file to a newer 159 | # Visual Studio version. Backup files are not needed, because we have git ;-) 160 | _UpgradeReport_Files/ 161 | Backup*/ 162 | UpgradeLog*.XML 163 | UpgradeLog*.htm 164 | 165 | # SQL Server files 166 | App_Data/*.mdf 167 | App_Data/*.ldf 168 | 169 | ############# 170 | ## Windows detritus 171 | ############# 172 | 173 | # Windows image file caches 174 | Thumbs.db 175 | ehthumbs.db 176 | 177 | # Folder config file 178 | Desktop.ini 179 | 180 | # Recycle Bin used on file shares 181 | $RECYCLE.BIN/ 182 | 183 | # Mac crap 184 | .DS_Store 185 | 186 | 187 | ############# 188 | ## Python 189 | ############# 190 | 191 | *.py[co] 192 | 193 | # Packages 194 | *.egg 195 | *.egg-info 196 | dist/ 197 | build/ 198 | eggs/ 199 | parts/ 200 | var/ 201 | sdist/ 202 | develop-eggs/ 203 | .installed.cfg 204 | 205 | # Installer logs 206 | pip-log.txt 207 | 208 | # Unit test / coverage reports 209 | .coverage 210 | .tox 211 | 212 | #Translations 213 | *.mo 214 | 215 | #Mr Developer 216 | .mr.developer.cfg 217 | 218 | # StackOverflow Tag Server specific folders/files 219 | BinaryData/ 220 | Results/ 221 | WebSite/Data/ 222 | -------------------------------------------------------------------------------- /.nuget/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /CommentXMLParser/CommentXMLParser.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {1D4E5979-FF19-45BF-B260-345178BE5B50} 9 | Exe 10 | Properties 11 | CommentXMLParser 12 | CommentXMLParser 13 | v4.5 14 | 15 | 16 | 512 17 | 18 | 19 | x86 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | false 28 | 29 | 30 | x86 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | false 38 | 39 | 40 | true 41 | bin\x64\Debug\ 42 | DEBUG;TRACE 43 | full 44 | x64 45 | prompt 46 | MinimumRecommendedRules.ruleset 47 | false 48 | 49 | 50 | bin\x64\Release\ 51 | TRACE 52 | true 53 | pdbonly 54 | x64 55 | prompt 56 | MinimumRecommendedRules.ruleset 57 | false 58 | 59 | 60 | true 61 | bin\Debug\ 62 | DEBUG;TRACE 63 | full 64 | AnyCPU 65 | prompt 66 | MinimumRecommendedRules.ruleset 67 | false 68 | 69 | 70 | bin\Release\ 71 | TRACE 72 | true 73 | pdbonly 74 | AnyCPU 75 | prompt 76 | MinimumRecommendedRules.ruleset 77 | false 78 | 79 | 80 | 81 | ..\packages\protobuf-net.2.0.0.668\lib\net40\protobuf-net.dll 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | {267821ff-a334-449d-8de8-5fdbf588a678} 102 | Shared 103 | 104 | 105 | 106 | 113 | -------------------------------------------------------------------------------- /CommentXMLParser/CommentXMLParser.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommentXMLParser", "CommentXMLParser\CommentXMLParser.csproj", "{1D4E5979-FF19-45BF-B260-345178BE5B50}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|x86 = Debug|x86 9 | Release|x86 = Release|x86 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {1D4E5979-FF19-45BF-B260-345178BE5B50}.Debug|x86.ActiveCfg = Debug|x86 13 | {1D4E5979-FF19-45BF-B260-345178BE5B50}.Debug|x86.Build.0 = Debug|x86 14 | {1D4E5979-FF19-45BF-B260-345178BE5B50}.Release|x86.ActiveCfg = Release|x86 15 | {1D4E5979-FF19-45BF-B260-345178BE5B50}.Release|x86.Build.0 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /CommentXMLParser/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Globalization; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Xml; 8 | using System.Xml.Linq; 9 | using ProtoBuf; 10 | using Shared; 11 | 12 | namespace CommentXMLParser 13 | { 14 | class Program 15 | { 16 | //From http://blogs.msdn.com/b/xmlteam/archive/2007/03/24/streaming-with-linq-to-xml-part-2.aspx 17 | static IEnumerable SimpleStreamAxis(string inputUrl, string matchName = "") 18 | { 19 | using (XmlReader reader = XmlReader.Create(inputUrl)) 20 | { 21 | reader.MoveToContent(); 22 | while (reader.Read()) 23 | { 24 | switch (reader.NodeType) 25 | { 26 | case XmlNodeType.Element: 27 | //if (reader.Name == matchName) 28 | { 29 | //XElement el = XElement.ReadFrom(reader) as XElement; 30 | var el = XNode.ReadFrom(reader) as XElement; 31 | if (el != null) 32 | yield return el; 33 | } 34 | break; 35 | } 36 | } 37 | reader.Close(); 38 | } 39 | } 40 | 41 | static void Main(string[] args) 42 | { 43 | //string inputUrl = @"D:\Source\__RavenDB__\SO Datadump - 20-04-2010\posts.xml"; 44 | var inputUrl = @"C:\Users\warma11\Desktop\SO Posts\Posts - latest version 22-10-2013.xml"; 45 | 46 | var filename = "Questions-NEW.bin"; 47 | //var recreate = false; 48 | var recreate = true; 49 | if (recreate) 50 | { 51 | var timer = Stopwatch.StartNew(); 52 | var questions = SimpleStreamAxis(inputUrl) 53 | .Where(el => (string) el.Attribute("PostTypeId") == "1") 54 | .Select(el => 55 | { 56 | return new Question 57 | { 58 | Id = long.Parse((string)el.Attribute("Id")), 59 | Title = (string)el.Attribute("Title"), 60 | RawTags = (string)el.Attribute("Tags"), 61 | CreationDate = 62 | DateTime.Parse((string)el.Attribute("CreationDate"), null, DateTimeStyles.RoundtripKind), 63 | LastActivityDate = 64 | DateTime.Parse((string)el.Attribute("LastActivityDate"), null, DateTimeStyles.RoundtripKind), 65 | Score = ParseIntOrNullable((string)el.Attribute("Score")), 66 | ViewCount = ParseIntOrNullable((string)el.Attribute("ViewCount")), 67 | AnswerCount = ParseIntOrNullable((string)el.Attribute("AnswerCount")), 68 | AcceptedAnswerId = ParseIntOrNullable((string)el.Attribute("AcceptedAnswerId")) 69 | }; 70 | }) 71 | //.Take(1000) 72 | .ToList(); 73 | timer.Stop(); 74 | 75 | Console.WriteLine("Took {0} ({1,6} ms) to process {2} items", 76 | timer.Elapsed, timer.ElapsedMilliseconds, questions.Count); 77 | 78 | foreach (var value in questions.Take(10)) 79 | { 80 | Console.WriteLine(value); 81 | } 82 | 83 | if (File.Exists(filename)) 84 | File.Delete(filename); 85 | 86 | var fileWriteTimer = Stopwatch.StartNew(); 87 | using (var file = File.Create(filename)) 88 | { 89 | //Serializer.Serialize(file, values.Where(x => x.AcceptedAnswerId == null).Take(100).ToList()); 90 | //Serializer.Serialize(file, values.Where(x => x.AnswerCount == null).Take(100).ToList()); 91 | Serializer.Serialize(file, questions); 92 | //Serializer.Serialize() 93 | } 94 | fileWriteTimer.Stop(); 95 | Console.WriteLine("Took {0} to serialise {1} items to the file", fileWriteTimer.Elapsed, questions.Count); 96 | } 97 | 98 | List rttQuestions; 99 | var fileReadTimer = Stopwatch.StartNew(); 100 | using (var file = File.OpenRead(filename)) 101 | { 102 | rttQuestions = Serializer.Deserialize>(file); 103 | } 104 | fileReadTimer.Stop(); 105 | 106 | Console.WriteLine("Took {0} to DE-serialise {1} items from the file", fileReadTimer.Elapsed, rttQuestions.Count); 107 | Console.WriteLine("Serialised file is {0}\n", HumanReadableFileSize(new FileInfo(filename).Length)); 108 | 109 | //int questionsCount = 0, answersCount = 0, otherCount = 0; 110 | //foreach (var el in SimpleStreamAxis(inputUrl)) 111 | //{ 112 | // var postTypeId = (string)el.Attribute("PostTypeId"); 113 | // if (postTypeId == "1") 114 | // questionsCount++; 115 | // else if (postTypeId == "2") 116 | // answersCount++; 117 | // else 118 | // otherCount++; 119 | //} 120 | //Console.WriteLine("File contains {0} questions, {1} answers and {2} unknowns", 121 | // questionsCount, answersCount, otherCount); 122 | 123 | var orderingTimer = Stopwatch.StartNew(); 124 | var mostViews = rttQuestions.OrderByDescending(x => x.ViewCount).Take(100).ToList(); 125 | orderingTimer.Stop(); 126 | Console.WriteLine("Took {0} to get the top 100 most popular pages (by view count)", orderingTimer.Elapsed); 127 | var mostViewsPages = mostViews.First(); 128 | } 129 | 130 | private static string HumanReadableFileSize(long numBytes) 131 | { 132 | string[] sizes = { "B", "KB", "MB", "GB" }; 133 | int order = 0; 134 | while (numBytes >= 1024 && order + 1 < sizes.Length) 135 | { 136 | order++; 137 | numBytes = numBytes / 1024; 138 | } 139 | string result = String.Format("{0:0.##} {1}", numBytes, sizes[order]); 140 | return result; 141 | } 142 | 143 | private static int? ParseIntOrNullable(string text) 144 | { 145 | if (String.IsNullOrEmpty(text)) 146 | return null; 147 | 148 | return int.Parse(text); 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /CommentXMLParser/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("CommentXMLParser")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("CommentXMLParser")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2012")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("3d7ccdc6-4043-448a-a0f3-b1660f9abdae")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /CommentXMLParser/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /CommentXMLParser/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /CommentXMLParser/packages/protobuf-net.2.0.0.480/lib/ios/protobuf-net.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwarren/StackOverflowTagServer/94da1a4876d7e77e94295c11a1be7e595f3ac8bf/CommentXMLParser/packages/protobuf-net.2.0.0.480/lib/ios/protobuf-net.dll -------------------------------------------------------------------------------- /CommentXMLParser/packages/protobuf-net.2.0.0.480/lib/mono/protobuf-net.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwarren/StackOverflowTagServer/94da1a4876d7e77e94295c11a1be7e595f3ac8bf/CommentXMLParser/packages/protobuf-net.2.0.0.480/lib/mono/protobuf-net.dll -------------------------------------------------------------------------------- /CommentXMLParser/packages/protobuf-net.2.0.0.480/lib/net20/protobuf-net.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwarren/StackOverflowTagServer/94da1a4876d7e77e94295c11a1be7e595f3ac8bf/CommentXMLParser/packages/protobuf-net.2.0.0.480/lib/net20/protobuf-net.dll -------------------------------------------------------------------------------- /CommentXMLParser/packages/protobuf-net.2.0.0.480/lib/net30/protobuf-net.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwarren/StackOverflowTagServer/94da1a4876d7e77e94295c11a1be7e595f3ac8bf/CommentXMLParser/packages/protobuf-net.2.0.0.480/lib/net30/protobuf-net.dll -------------------------------------------------------------------------------- /CommentXMLParser/packages/protobuf-net.2.0.0.480/lib/net35/protobuf-net.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwarren/StackOverflowTagServer/94da1a4876d7e77e94295c11a1be7e595f3ac8bf/CommentXMLParser/packages/protobuf-net.2.0.0.480/lib/net35/protobuf-net.dll -------------------------------------------------------------------------------- /CommentXMLParser/packages/protobuf-net.2.0.0.480/lib/net40/protobuf-net.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwarren/StackOverflowTagServer/94da1a4876d7e77e94295c11a1be7e595f3ac8bf/CommentXMLParser/packages/protobuf-net.2.0.0.480/lib/net40/protobuf-net.dll -------------------------------------------------------------------------------- /CommentXMLParser/packages/protobuf-net.2.0.0.480/lib/sl3-wp/protobuf-net.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwarren/StackOverflowTagServer/94da1a4876d7e77e94295c11a1be7e595f3ac8bf/CommentXMLParser/packages/protobuf-net.2.0.0.480/lib/sl3-wp/protobuf-net.dll -------------------------------------------------------------------------------- /CommentXMLParser/packages/protobuf-net.2.0.0.480/lib/sl4/protobuf-net.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwarren/StackOverflowTagServer/94da1a4876d7e77e94295c11a1be7e595f3ac8bf/CommentXMLParser/packages/protobuf-net.2.0.0.480/lib/sl4/protobuf-net.dll -------------------------------------------------------------------------------- /CommentXMLParser/packages/protobuf-net.2.0.0.480/protobuf-net.2.0.0.480.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwarren/StackOverflowTagServer/94da1a4876d7e77e94295c11a1be7e595f3ac8bf/CommentXMLParser/packages/protobuf-net.2.0.0.480/protobuf-net.2.0.0.480.nupkg -------------------------------------------------------------------------------- /CommentXMLParser/packages/repositories.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Questions.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwarren/StackOverflowTagServer/94da1a4876d7e77e94295c11a1be7e595f3ac8bf/Questions.bin -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | My attempt to replicate the StackOverflow Tag Server, as described [here][1] and [here][2] 2 | 3 | [1]: http://marcgravell.blogspot.co.uk/2011/10/assault-by-gc.html 4 | [2]: http://samsaffron.com/archive/2011/10/28/in-managed-code-we-trust-our-recent-battles-with-the-net-garbage-collector -------------------------------------------------------------------------------- /SO Tag Server - explanation.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwarren/StackOverflowTagServer/94da1a4876d7e77e94295c11a1be7e595f3ac8bf/SO Tag Server - explanation.xlsx -------------------------------------------------------------------------------- /Sample Question - original.xml: -------------------------------------------------------------------------------- 1 |

When I try to build it, I get this error:

" + 10 | "

Cannot implicitly convert type 'decimal' to 'double'

I tried making trans " + 11 | "a double, but then the control doesn't work. This code worked fine for me in VB.NET.

" + 12 | "

What do I need to do differently?

" 13 | OwnerUserId="8" 14 | LastEditorUserId="56555" 15 | LastEditorDisplayName="Rich B" 16 | LastEditDate="2009-07-25T19:33:10.080" 17 | LastActivityDate="2010-02-04T22:33:13.630" 18 | Title="When setting a form's opacity should I use a decimal or double?" 19 | Tags="" 20 | AnswerCount="12" 21 | CommentCount="10" 22 | FavoriteCount="7" 23 | /> -------------------------------------------------------------------------------- /Sample Question - parts used in TagServer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwarren/StackOverflowTagServer/94da1a4876d7e77e94295c11a1be7e595f3ac8bf/Sample Question - parts used in TagServer.png -------------------------------------------------------------------------------- /Sample Question - tidied up.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Sample Question.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwarren/StackOverflowTagServer/94da1a4876d7e77e94295c11a1be7e595f3ac8bf/Sample Question.png -------------------------------------------------------------------------------- /Sample Question.xml: -------------------------------------------------------------------------------- 1 |

When I try to build it, I get this error:

" + 10 | "

Cannot implicitly convert type 'decimal' to 'double'

I tried making trans " + 11 | "a double, but then the control doesn't work. This code worked fine for me in VB.NET.

" + 12 | "

What do I need to do differently?

" 13 | OwnerUserId="8" 14 | LastEditorUserId="56555" 15 | LastEditorDisplayName="Rich B" 16 | LastEditDate="2009-07-25T19:33:10.080" 17 | LastActivityDate="2010-02-04T22:33:13.630" 18 | Title="When setting a form's opacity should I use a decimal or double?" 19 | Tags="" 20 | AnswerCount="12" 21 | CommentCount="10" 22 | FavoriteCount="7" 23 | /> -------------------------------------------------------------------------------- /Shared/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Shared")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("CA, Inc.")] 12 | [assembly: AssemblyProduct("Shared")] 13 | [assembly: AssemblyCopyright("Copyright © CA, Inc. 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("fef9a14e-9501-4247-990b-42f861346c73")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Shared/Question.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ProtoBuf; 4 | using System.Linq; 5 | 6 | namespace Shared 7 | { 8 | [ProtoContract] 9 | public class Question 10 | { 11 | [ProtoMember(1)] 12 | public long Id { get; set; } 13 | [ProtoMember(2)] 14 | public string Title { get; set; } 15 | 16 | [ProtoMember(3)] 17 | public string RawTags { get; set; } 18 | 19 | private IList tagList = null; 20 | public IList Tags 21 | { 22 | get 23 | { 24 | if (tagList == null) 25 | { 26 | tagList = new List( 27 | RawTags.Split(new string[] { " ", "<", ">" }, StringSplitOptions.RemoveEmptyEntries) 28 | .Select(s => String.Intern(s))); 29 | } 30 | 31 | return tagList; 32 | } 33 | set 34 | { 35 | tagList = value; 36 | } 37 | } 38 | 39 | public Uri Url { get { return new Uri("http://stackoverflow.com/questions/" + Id.ToString()); } } 40 | 41 | [ProtoMember(4)] 42 | public DateTime CreationDate { get; set; } 43 | [ProtoMember(5)] 44 | public DateTime LastActivityDate { get; set; } 45 | [ProtoMember(6)] 46 | public int? Score { get; set; } 47 | [ProtoMember(7)] 48 | public int? ViewCount { get; set; } 49 | [ProtoMember(8)] 50 | public int? AnswerCount { get; set; } 51 | [ProtoMember(9)] 52 | public int? AcceptedAnswerId { get; set; } 53 | 54 | public override string ToString() 55 | { 56 | return base.ToString(); 57 | 58 | // Print the full Question for each result (this is harder to read!!) 59 | //Logger.Log("\t{0}", string.Join("\n\t", result.Select(r => new 60 | // { 61 | // r.Id, 62 | // Tags = string.Join(", ", r.Tags), 63 | // r.LastActivityDate, 64 | // r.CreationDate, 65 | // r.Score, 66 | // r.ViewCount, 67 | // r.AnswerCount 68 | // }))); 69 | } 70 | 71 | public override bool Equals(object obj) 72 | { 73 | // If parameter is null return false. 74 | if (obj == null) 75 | { 76 | return false; 77 | } 78 | 79 | // If parameter cannot be cast to Question return false. 80 | Question other = obj as Question; 81 | if (other == null) 82 | { 83 | return false; 84 | } 85 | 86 | // Return true if the fields match: 87 | return this.Equals(other); 88 | } 89 | 90 | public bool Equals(Question other) 91 | { 92 | // If parameter is null return false: 93 | if (other == null) 94 | { 95 | return false; 96 | } 97 | 98 | return Id == other.Id; 99 | } 100 | 101 | 102 | public override int GetHashCode() 103 | { 104 | return Id.GetHashCode(); 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /Shared/Shared.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {267821FF-A334-449D-8DE8-5FDBF588A678} 8 | Library 9 | Properties 10 | Shared 11 | Shared 12 | v4.5 13 | 512 14 | 15 | 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | false 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | false 35 | 36 | 37 | true 38 | bin\x64\Debug\ 39 | DEBUG;TRACE 40 | full 41 | x64 42 | prompt 43 | MinimumRecommendedRules.ruleset 44 | false 45 | 46 | 47 | bin\x64\Release\ 48 | TRACE 49 | true 50 | pdbonly 51 | x64 52 | prompt 53 | MinimumRecommendedRules.ruleset 54 | false 55 | 56 | 57 | true 58 | bin\x86\Debug\ 59 | DEBUG;TRACE 60 | full 61 | x86 62 | prompt 63 | MinimumRecommendedRules.ruleset 64 | false 65 | 66 | 67 | bin\x86\Release\ 68 | TRACE 69 | true 70 | pdbonly 71 | x86 72 | prompt 73 | MinimumRecommendedRules.ruleset 74 | false 75 | 76 | 77 | 78 | ..\packages\protobuf-net.2.0.0.668\lib\net40\protobuf-net.dll 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 100 | -------------------------------------------------------------------------------- /StackOverflowTagServer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.22823.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared", "Shared\Shared.csproj", "{267821FF-A334-449D-8DE8-5FDBF588A678}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebSite", "WebSite\WebSite.csproj", "{A0431F1F-9014-442B-8339-F09ED6F95042}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommentXMLParser", "CommentXMLParser\CommentXMLParser.csproj", "{1D4E5979-FF19-45BF-B260-345178BE5B50}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagServer", "TagServer\TagServer.csproj", "{916C84BB-F73B-4283-9FA1-A5411EE666F6}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{B8719BFE-C1C8-4461-9E65-EF2A92398E05}" 15 | ProjectSection(SolutionItems) = preProject 16 | .nuget\packages.config = .nuget\packages.config 17 | EndProjectSection 18 | EndProject 19 | Global 20 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 21 | Debug|Any CPU = Debug|Any CPU 22 | Debug|x64 = Debug|x64 23 | Debug|x86 = Debug|x86 24 | Release|Any CPU = Release|Any CPU 25 | Release|x64 = Release|x64 26 | Release|x86 = Release|x86 27 | EndGlobalSection 28 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 29 | {267821FF-A334-449D-8DE8-5FDBF588A678}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {267821FF-A334-449D-8DE8-5FDBF588A678}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {267821FF-A334-449D-8DE8-5FDBF588A678}.Debug|x64.ActiveCfg = Debug|x64 32 | {267821FF-A334-449D-8DE8-5FDBF588A678}.Debug|x64.Build.0 = Debug|x64 33 | {267821FF-A334-449D-8DE8-5FDBF588A678}.Debug|x86.ActiveCfg = Debug|x86 34 | {267821FF-A334-449D-8DE8-5FDBF588A678}.Debug|x86.Build.0 = Debug|x86 35 | {267821FF-A334-449D-8DE8-5FDBF588A678}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {267821FF-A334-449D-8DE8-5FDBF588A678}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {267821FF-A334-449D-8DE8-5FDBF588A678}.Release|x64.ActiveCfg = Release|x64 38 | {267821FF-A334-449D-8DE8-5FDBF588A678}.Release|x64.Build.0 = Release|x64 39 | {267821FF-A334-449D-8DE8-5FDBF588A678}.Release|x86.ActiveCfg = Release|x86 40 | {267821FF-A334-449D-8DE8-5FDBF588A678}.Release|x86.Build.0 = Release|x86 41 | {A0431F1F-9014-442B-8339-F09ED6F95042}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {A0431F1F-9014-442B-8339-F09ED6F95042}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {A0431F1F-9014-442B-8339-F09ED6F95042}.Debug|x64.ActiveCfg = Debug|x64 44 | {A0431F1F-9014-442B-8339-F09ED6F95042}.Debug|x64.Build.0 = Debug|x64 45 | {A0431F1F-9014-442B-8339-F09ED6F95042}.Debug|x86.ActiveCfg = Debug|x86 46 | {A0431F1F-9014-442B-8339-F09ED6F95042}.Debug|x86.Build.0 = Debug|x86 47 | {A0431F1F-9014-442B-8339-F09ED6F95042}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {A0431F1F-9014-442B-8339-F09ED6F95042}.Release|Any CPU.Build.0 = Release|Any CPU 49 | {A0431F1F-9014-442B-8339-F09ED6F95042}.Release|x64.ActiveCfg = Release|x64 50 | {A0431F1F-9014-442B-8339-F09ED6F95042}.Release|x64.Build.0 = Release|x64 51 | {A0431F1F-9014-442B-8339-F09ED6F95042}.Release|x86.ActiveCfg = Release|x86 52 | {A0431F1F-9014-442B-8339-F09ED6F95042}.Release|x86.Build.0 = Release|x86 53 | {1D4E5979-FF19-45BF-B260-345178BE5B50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 54 | {1D4E5979-FF19-45BF-B260-345178BE5B50}.Debug|Any CPU.Build.0 = Debug|Any CPU 55 | {1D4E5979-FF19-45BF-B260-345178BE5B50}.Debug|x64.ActiveCfg = Debug|x64 56 | {1D4E5979-FF19-45BF-B260-345178BE5B50}.Debug|x64.Build.0 = Debug|x64 57 | {1D4E5979-FF19-45BF-B260-345178BE5B50}.Debug|x86.ActiveCfg = Debug|x86 58 | {1D4E5979-FF19-45BF-B260-345178BE5B50}.Debug|x86.Build.0 = Debug|x86 59 | {1D4E5979-FF19-45BF-B260-345178BE5B50}.Release|Any CPU.ActiveCfg = Release|Any CPU 60 | {1D4E5979-FF19-45BF-B260-345178BE5B50}.Release|Any CPU.Build.0 = Release|Any CPU 61 | {1D4E5979-FF19-45BF-B260-345178BE5B50}.Release|x64.ActiveCfg = Release|x64 62 | {1D4E5979-FF19-45BF-B260-345178BE5B50}.Release|x64.Build.0 = Release|x64 63 | {1D4E5979-FF19-45BF-B260-345178BE5B50}.Release|x86.ActiveCfg = Release|x86 64 | {1D4E5979-FF19-45BF-B260-345178BE5B50}.Release|x86.Build.0 = Release|x86 65 | {916C84BB-F73B-4283-9FA1-A5411EE666F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 66 | {916C84BB-F73B-4283-9FA1-A5411EE666F6}.Debug|Any CPU.Build.0 = Debug|Any CPU 67 | {916C84BB-F73B-4283-9FA1-A5411EE666F6}.Debug|x64.ActiveCfg = Debug|x64 68 | {916C84BB-F73B-4283-9FA1-A5411EE666F6}.Debug|x64.Build.0 = Debug|x64 69 | {916C84BB-F73B-4283-9FA1-A5411EE666F6}.Debug|x86.ActiveCfg = Debug|x86 70 | {916C84BB-F73B-4283-9FA1-A5411EE666F6}.Debug|x86.Build.0 = Debug|x86 71 | {916C84BB-F73B-4283-9FA1-A5411EE666F6}.Release|Any CPU.ActiveCfg = Release|Any CPU 72 | {916C84BB-F73B-4283-9FA1-A5411EE666F6}.Release|Any CPU.Build.0 = Release|Any CPU 73 | {916C84BB-F73B-4283-9FA1-A5411EE666F6}.Release|x64.ActiveCfg = Release|x64 74 | {916C84BB-F73B-4283-9FA1-A5411EE666F6}.Release|x64.Build.0 = Release|x64 75 | {916C84BB-F73B-4283-9FA1-A5411EE666F6}.Release|x86.ActiveCfg = Release|x86 76 | {916C84BB-F73B-4283-9FA1-A5411EE666F6}.Release|x86.Build.0 = Release|x86 77 | EndGlobalSection 78 | GlobalSection(SolutionProperties) = preSolution 79 | HideSolutionNode = FALSE 80 | EndGlobalSection 81 | EndGlobal 82 | -------------------------------------------------------------------------------- /TagServer/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /TagServer/CLR/HashHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace StackOverflowTagServer.CLR 7 | { 8 | static class HashHelpers 9 | { 10 | // Table of prime numbers to use as hash table sizes. 11 | // A typical resize algorithm would pick the smallest prime number in this array 12 | // that is larger than twice the previous capacity. 13 | // Suppose our Hashtable currently has capacity x and enough elements are added 14 | // such that a resize needs to occur. Resizing first computes 2x then finds the 15 | // first prime in the table greater than 2x, i.e. if primes are ordered 16 | // p_1, p_2, ..., p_i, ..., it finds p_n such that p_n-1 < 2x < p_n. 17 | // Doubling is important for preserving the asymptotic complexity of the 18 | // hashtable operations such as add. Having a prime guarantees that double 19 | // hashing does not lead to infinite loops. IE, your hash function will be 20 | // h1(key) + i*h2(key), 0 <= i < size. h2 and the size must be relatively prime. 21 | public static readonly int[] primes = { 22 | 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919, 23 | 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, 24 | 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437, 25 | 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, 26 | 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369}; 27 | //7,199,369 28 | 29 | // This is the maximum prime smaller than Array.MaxArrayLength 30 | public const int MaxPrimeArrayLength = 0x7FEFFFFD; 31 | 32 | // Returns size of hashtable to grow to. 33 | public static int ExpandPrime(int oldSize) 34 | { 35 | int newSize = 2 * oldSize; 36 | 37 | // Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow. 38 | // Note that this check works even when _items.Length overflowed thanks to the (uint) cast 39 | if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize) 40 | { 41 | //Contract.Assert(MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength"); 42 | return MaxPrimeArrayLength; 43 | } 44 | 45 | return GetPrime(newSize); 46 | } 47 | 48 | public static int GetMinPrime() 49 | { 50 | return primes[0]; 51 | } 52 | 53 | internal const Int32 HashPrime = 101; 54 | public static int GetPrime(int min) 55 | { 56 | if (min < 0) 57 | throw new ArgumentException("Arg_HTCapacityOverflow"); 58 | //Contract.EndContractBlock(); 59 | 60 | for (int i = 0; i < primes.Length; i++) 61 | { 62 | int prime = primes[i]; 63 | if (prime >= min) return prime; 64 | } 65 | 66 | //outside of our predefined table. 67 | //compute the hard way. 68 | for (int i = (min | 1); i < Int32.MaxValue; i += 2) 69 | { 70 | if (IsPrime(i) && ((i - 1) % /*Hashtable.*/HashPrime != 0)) 71 | return i; 72 | } 73 | return min; 74 | } 75 | 76 | //[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 77 | public static bool IsPrime(int candidate) 78 | { 79 | if ((candidate & 1) != 0) 80 | { 81 | int limit = (int)Math.Sqrt(candidate); 82 | for (int divisor = 3; divisor <= limit; divisor += 2) 83 | { 84 | if ((candidate % divisor) == 0) 85 | return false; 86 | } 87 | return true; 88 | } 89 | return (candidate == 2); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /TagServer/CLR/HashSetCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Threading; 5 | 6 | namespace StackOverflowTagServer.CLR 7 | { 8 | internal class HashSetCache 9 | { 10 | private readonly Lazy> LazyHashSetCache; 11 | 12 | internal HashSetCache(int initialSize, IEqualityComparer comparer = null) 13 | { 14 | LazyHashSetCache = new Lazy>(() => 15 | { 16 | HashSet hashSet; 17 | hashSet = comparer == null ? new HashSet() : new HashSet(comparer); 18 | var initialiseMethod = typeof(HashSet).GetMethod("Initialize", BindingFlags.NonPublic | BindingFlags.Instance); 19 | var exclusionCount = initialSize; 20 | initialiseMethod.Invoke(hashSet, new object[] { exclusionCount }); 21 | return hashSet; 22 | }, LazyThreadSafetyMode.ExecutionAndPublication); 23 | } 24 | 25 | internal StackOverflowTagServer.CLR.HashSet GetCachedHashSet(IEnumerable populateHashSet) 26 | { 27 | var hashSet = LazyHashSetCache.Value; 28 | hashSet.Clear(); 29 | hashSet.UnionWith(populateHashSet); 30 | return hashSet; 31 | } 32 | 33 | internal StackOverflowTagServer.CLR.HashSet GetCachedHashSet() 34 | { 35 | var hashSet = LazyHashSetCache.Value; 36 | hashSet.Clear(); 37 | return hashSet; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /TagServer/DataStructures/DisposableAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StackOverflowTagServer.DataStructures 4 | { 5 | // From http://ayende.com/blog/890/the-ultimate-disposable 6 | internal class DisposableAction : IDisposable 7 | { 8 | readonly Action _action; 9 | 10 | public DisposableAction(Action action) 11 | { 12 | if (action == null) 13 | throw new ArgumentNullException("action"); 14 | _action = action; 15 | } 16 | 17 | public void Dispose() 18 | { 19 | _action(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /TagServer/DataStructures/GCCollectionInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StackOverflowTagServer.DataStructures 4 | { 5 | public interface IGCInfo 6 | { 7 | int Gen0 { get; } 8 | int Gen1 { get; } 9 | int Gen2 { get; } 10 | } 11 | 12 | internal class GCCollectionInfo 13 | { 14 | private class GCInfo : IGCInfo 15 | { 16 | public int Gen0 { get; private set; } 17 | public int Gen1 { get; private set; } 18 | public int Gen2 { get; private set; } 19 | 20 | // We want this to be protected, so that instances of GCInfo can't be created directly 21 | protected GCInfo(int gen0, int gen1, int gen2) 22 | { 23 | Gen0 = gen0; 24 | Gen1 = gen1; 25 | Gen2 = gen2; 26 | } 27 | } 28 | 29 | private class GCInfoInstance : GCInfo 30 | { 31 | // This class just exists so we can call the protected ctor of GCInfo 32 | public GCInfoInstance(int gen0, int gen1, int gen2) 33 | : base(gen0, gen1, gen2) 34 | { 35 | } 36 | } 37 | 38 | public IGCInfo InitialValues { get; private set; } 39 | public IGCInfo Count { get; private set; } 40 | 41 | private bool _collected = false; 42 | 43 | public GCCollectionInfo() 44 | { 45 | InitialValues = new GCInfoInstance(GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2)); 46 | //Count = new GCInfo(InitialValues.Gen0, InitialValues.Gen1, InitialValues.Gen2); 47 | } 48 | 49 | public void UpdateCollectionInfo() 50 | { 51 | Count = new GCInfoInstance( 52 | GC.CollectionCount(0) - InitialValues.Gen0, 53 | GC.CollectionCount(1) - InitialValues.Gen1, 54 | GC.CollectionCount(2) - InitialValues.Gen2); 55 | 56 | _collected = true; 57 | } 58 | 59 | public override string ToString() 60 | { 61 | if (_collected == false) 62 | return "UpdateCollectionInfo() has not been called, no intermediateResults to display"; 63 | 64 | return string.Format( 65 | "GC Collections - Gen0: {0}, Gen1: {1}, Gen2: {2}{3}", 66 | Count.Gen0, Count.Gen1, Count.Gen2, 67 | Count.Gen2 > 0 ? " ****" : string.Empty); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /TagServer/DataStructures/Int32Converter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace StackOverflowTagServer.DataStructures 5 | { 6 | // From http://stackoverflow.com/questions/4176653/int-to-byte-array/4176752#4176752 7 | [StructLayout(LayoutKind.Explicit)] 8 | struct Int32Converter 9 | { 10 | [FieldOffset(0)] 11 | public int Value; 12 | [FieldOffset(0)] 13 | public byte Byte1; 14 | [FieldOffset(1)] 15 | public byte Byte2; 16 | [FieldOffset(2)] 17 | public byte Byte3; 18 | [FieldOffset(3)] 19 | public byte Byte4; 20 | 21 | public Int32Converter(int value) 22 | { 23 | Byte1 = Byte2 = Byte3 = Byte4 = 0; 24 | Value = value; 25 | } 26 | 27 | public static implicit operator Int32(Int32Converter value) 28 | { 29 | return value.Value; 30 | } 31 | 32 | public static implicit operator Int32Converter(int value) 33 | { 34 | return new Int32Converter(value); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /TagServer/DataStructures/QueryType.cs: -------------------------------------------------------------------------------- 1 | namespace StackOverflowTagServer.DataStructures 2 | { 3 | public enum QueryType 4 | { 5 | LastActivityDate, 6 | CreationDate, 7 | Score, 8 | ViewCount, 9 | AnswerCount 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /TagServer/DataStructures/SimpleBloomFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | 4 | namespace StackOverflowTagServer.DataStructures 5 | { 6 | // From http://stackoverflow.com/questions/18553961/what-hash-function-should-i-use-for-a-bloom-filter-with-128-bit-keys/23382392#23382392 7 | internal class SimpleBloomFilter 8 | { 9 | public delegate int HashFunction(int input); 10 | 11 | private readonly BitArray bitArray; 12 | private readonly int size; 13 | private long counter; 14 | 15 | public SimpleBloomFilter(int size) 16 | { 17 | bitArray = new BitArray(size); 18 | this.size = size; 19 | } 20 | 21 | public long NumberOfItems { get { return counter; } } 22 | 23 | public double Truthiness { get { return (double)TrueBits() / bitArray.Count; } } 24 | 25 | public void Add(int item) 26 | { 27 | int index1 = Math.Abs(HashThomasWang(item)) % size; 28 | bitArray[index1] = true; 29 | 30 | int index2 = Math.Abs(HashFNV1a(item)) % size; 31 | bitArray[index2] = true; 32 | 33 | //int index3 = Math.Abs(item.GetHashCode()) % size; 34 | //bitArray[index3] = true; 35 | 36 | counter++; 37 | } 38 | 39 | #if DEBUG 40 | public bool PossiblyExists(int item, bool debugInfo = false) 41 | #else 42 | /// 43 | /// A bloom filter is basically a bitvector, where you set bits. 44 | /// If you want to figure out if an item exists, 45 | /// the bloom filter will give you a TRUE if the item possibly exists 46 | /// and a FALSE if the item for sure doesn't exist. 47 | /// 48 | /// 49 | /// 50 | public bool PossiblyExists(int item) 51 | #endif 52 | { 53 | int index1 = Math.Abs(HashThomasWang(item)) % size; 54 | #if DEBUG 55 | if (debugInfo) 56 | Logger.Log("HashThomasWang - {0,8} = {1,8}, bitArray[{1,8}] = {2}", item, index1, bitArray[index1]); 57 | #endif 58 | if (bitArray[index1] == false) 59 | return false; 60 | 61 | int index2 = Math.Abs(HashFNV1a(item)) % size; 62 | #if DEBUG 63 | if (debugInfo) 64 | Logger.Log(" HashFNV1a - {0,8} = {1,8}, bitArray[{1,8}] = {2}", item, index2, bitArray[index2]); 65 | #endif 66 | if (bitArray[index2] == false) 67 | return false; 68 | 69 | //int index3 = Math.Abs(item.GetHashCode()) % size; 70 | //if (bitArray[index3] == false) 71 | // return false; 72 | 73 | return true; // this can be a false-positive 74 | } 75 | 76 | // Taken from https://gist.github.com/richardkundl/8300092#211 77 | // Hashes a 32-bit signed int using Thomas Wang's method v3.1 (http://www.concentric.net/~Ttwang/tech/inthash.htm). 78 | // Runtime is suggested to be 11 cycles. 79 | private static int HashThomasWang(int input) 80 | { 81 | uint x = (uint)input; 82 | unchecked 83 | { 84 | x = ~x + (x << 15); // x = (x << 15) - x- 1, as (~x) + y is equivalent to y - x - 1 in two's complement representation 85 | x = x ^ (x >> 12); 86 | x = x + (x << 2); 87 | x = x ^ (x >> 4); 88 | x = x * 2057; // x = (x + (x << 3)) + (x<< 11); 89 | x = x ^ (x >> 16); 90 | return (int)x; 91 | } 92 | } 93 | 94 | // Taken http://stackoverflow.com/questions/13974443/c-sharp-implementation-of-fnv-hash 95 | private static int HashFNV1a(int value) 96 | { 97 | var intConverter = (Int32Converter)value; 98 | int hash = FNVConstants.OffsetBasis; 99 | hash = (hash ^ intConverter.Byte1) * FNVConstants.Prime; 100 | hash = (hash ^ intConverter.Byte2) * FNVConstants.Prime; 101 | hash = (hash ^ intConverter.Byte3) * FNVConstants.Prime; 102 | hash = (hash ^ intConverter.Byte4) * FNVConstants.Prime; 103 | return hash; 104 | } 105 | 106 | private int TrueBits() 107 | { 108 | int output = 0; 109 | foreach (bool bit in bitArray) 110 | { 111 | if (bit == true) 112 | output++; 113 | } 114 | return output; 115 | } 116 | } 117 | 118 | static class FNVConstants 119 | { 120 | public static readonly int OffsetBasis = unchecked((int)2166136261); 121 | public static readonly int Prime = 16777619; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /TagServer/DataStructures/TrieEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | // From https://github.com/kpol/trie/blob/master/AlgoLib/AlgoLib/TrieEntry.cs 7 | namespace StackOverflowTagServer.DataStructures 8 | { 9 | /// 10 | /// Defines a key/value pair that can be set or retrieved from . 11 | /// 12 | public struct TrieEntry 13 | { 14 | /// 15 | /// Initializes a new instance of the structure with the specified key and value. 16 | /// 17 | /// The object defined in each key/value pair. 18 | /// The definition associated with key. 19 | public TrieEntry(string key, TValue value) 20 | : this() 21 | { 22 | Key = key; 23 | Value = value; 24 | } 25 | 26 | /// 27 | /// Gets the key in the key/value pair. 28 | /// 29 | public string Key { get; private set; } 30 | 31 | /// 32 | /// Gets the value in the key/value pair. 33 | /// 34 | public TValue Value { get; private set; } 35 | 36 | /// 37 | /// Returns the fully qualified type name of this instance. 38 | /// 39 | /// 40 | /// A containing a fully qualified type name. 41 | /// 42 | /// 2 43 | public override string ToString() 44 | { 45 | return string.Format("[{0}, {1}]", Key, Value); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /TagServer/EWAH/BufferedRunningLengthWord.cs: -------------------------------------------------------------------------------- 1 | namespace Ewah 2 | { 3 | /* 4 | * Copyright 2012, Kemal Erdogan, Daniel Lemire and Ciaran Jessup 5 | * Licensed under APL 2.0. 6 | */ 7 | 8 | 9 | /// 10 | /// Mostly for internal use. Similar to RunningLengthWord, but can 11 | /// be modified without access to the array, and has faster access. 12 | /// 13 | internal sealed class BufferedRunningLengthWord 14 | { 15 | #region Fields 16 | 17 | /// 18 | /// how many dirty words have we read so far? 19 | /// 20 | public int DirtyWordOffset; 21 | 22 | /// 23 | /// The Number of literal words 24 | /// 25 | public int NumberOfLiteralWords; 26 | 27 | /// 28 | /// The Running bit 29 | /// 30 | public bool RunningBit; 31 | 32 | /// 33 | /// The Running length 34 | /// 35 | public long RunningLength; 36 | 37 | #endregion 38 | 39 | #region C'tors 40 | 41 | /// 42 | /// Instantiates a new buffered running length word 43 | /// 44 | /// the rlw 45 | public BufferedRunningLengthWord(RunningLengthWord rlw) 46 | : this(rlw.ArrayOfWords[rlw.Position]) 47 | { 48 | } 49 | 50 | /// 51 | /// Instantiates a new buffered running length word 52 | /// 53 | /// the word 54 | public BufferedRunningLengthWord(long a) 55 | { 56 | NumberOfLiteralWords = (int) (((ulong) a) >> (1 + RunningLengthWord.RunningLengthBits)); 57 | RunningBit = (a & 1) != 0; 58 | RunningLength = (int) ((((ulong) a) >> 1) & RunningLengthWord.LargestRunningLengthCount); 59 | } 60 | 61 | #endregion 62 | 63 | #region Instance Properties 64 | 65 | /// 66 | /// Size in uncompressed words 67 | /// 68 | public long Count 69 | { 70 | get { return RunningLength + NumberOfLiteralWords; } 71 | } 72 | 73 | #endregion 74 | 75 | #region Instance Methods 76 | 77 | public override string ToString() 78 | { 79 | return "running bit = " + RunningBit + " running length = " 80 | + RunningLength + " number of lit. words " 81 | + NumberOfLiteralWords; 82 | } 83 | 84 | /// 85 | /// Discard first words 86 | /// 87 | /// 88 | public void DiscardFirstWords(long x) 89 | { 90 | if (RunningLength >= x) 91 | { 92 | RunningLength -= x; 93 | return; 94 | } 95 | x -= RunningLength; 96 | RunningLength = 0; 97 | DirtyWordOffset += (int) x; 98 | NumberOfLiteralWords -= (int) x; 99 | } 100 | 101 | /// 102 | /// Reset the values of this running length word so that it has the same values 103 | /// as the other running length word. 104 | /// 105 | /// the other running length word 106 | public void Reset(RunningLengthWord rlw) 107 | { 108 | Reset(rlw.ArrayOfWords[rlw.Position]); 109 | } 110 | 111 | /// 112 | /// Reset the values using the provided word. 113 | /// 114 | /// the word 115 | public void Reset(long a) 116 | { 117 | NumberOfLiteralWords = (int) (((ulong) a) >> (1 + RunningLengthWord.RunningLengthBits)); 118 | RunningBit = (a & 1) != 0; 119 | RunningLength = (int) ((((ulong) a) >> 1) & RunningLengthWord.LargestRunningLengthCount); 120 | DirtyWordOffset = 0; 121 | } 122 | 123 | #endregion 124 | } 125 | } -------------------------------------------------------------------------------- /TagServer/EWAH/EwahCompressedBitArray.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwarren/StackOverflowTagServer/94da1a4876d7e77e94295c11a1be7e595f3ac8bf/TagServer/EWAH/EwahCompressedBitArray.cs -------------------------------------------------------------------------------- /TagServer/EWAH/EwahCompressedBitArraySerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Ewah 5 | { 6 | /* 7 | * Copyright 2012, Kemal Erdogan, Daniel Lemire and Ciaran Jessup 8 | * Licensed under APL 2.0. 9 | */ 10 | 11 | /// 12 | /// A very simple Serialization schema for serialising and de-serialising instance of . 13 | /// 14 | /// The current implementation lacks serialization version information, and type information, it is rigid, brittle, simplistic but 15 | /// results in less byte bloat than a traditional BinaryFormatter. 16 | /// 17 | /// Consists of a very simple, fixed width header, and abritrary length buffer. 18 | /// 19 | /// Bytes 1-4 : 'SizeInBits' 20 | /// Bytes 5-8 : 'ActualSizeInWords' 21 | /// Bytes 9-12 : 'RunningLengthWordPosition' 22 | /// Bytes 13-End : Contents of the internal long[] buffer 23 | /// 24 | /// The encoding scheme is that of Microsoft's Built in System.BitConverter methods. Unfortunately the endian-ness of these calls 25 | /// is architecture specific, so beware that to be certain of the endian order on the machine serializing this class one must use the 26 | /// BitConverter.IsLittleEndian property. 27 | /// 28 | public class EwahCompressedBitArraySerializer 29 | { 30 | /// 31 | /// Deserializes the specified stream into an instance of 32 | /// 33 | /// The stream containing the data that constructs a valid instance of EwahCompressedBitArray. 34 | /// 35 | public EwahCompressedBitArray Deserialize(Stream serializationStream) 36 | { 37 | byte[] buff= new byte[8]; 38 | serializationStream.Read(buff, 0, 4); 39 | int sizeInBits = BitConverter.ToInt32(buff, 0); 40 | serializationStream.Read(buff, 0, 4); 41 | int actualSizeInWords = BitConverter.ToInt32(buff, 0); 42 | serializationStream.Read(buff, 0, 4); 43 | int runningLengthWordPosition = BitConverter.ToInt32(buff, 0); 44 | long[] buffer = new long[actualSizeInWords]; 45 | for (int i = 0; i < actualSizeInWords; i++) 46 | { 47 | serializationStream.Read(buff, 0, 8); 48 | buffer[i] = BitConverter.ToInt64(buff, 0); 49 | } 50 | return new EwahCompressedBitArray(sizeInBits, actualSizeInWords, buffer, runningLengthWordPosition); 51 | } 52 | 53 | /// 54 | /// Serializes an instance of into the given stream 55 | /// 56 | /// The serialization stream. 57 | /// The bit array. 58 | public void Serialize(Stream serializationStream, EwahCompressedBitArray bitArray) 59 | { 60 | // No actual need to call Shrink with this serialisation strategy, so we can avoid 61 | // mutating the source type (side-effects are bad ;-) ) 62 | serializationStream.Write(BitConverter.GetBytes(bitArray.SizeInBits), 0, 4); 63 | serializationStream.Write(BitConverter.GetBytes(bitArray._ActualSizeInWords), 0, 4); 64 | serializationStream.Write(BitConverter.GetBytes(bitArray._Rlw.Position), 0, 4); 65 | for (int i = 0; i < bitArray._ActualSizeInWords; i++) 66 | { 67 | serializationStream.Write(BitConverter.GetBytes(bitArray._Buffer[i]), 0, 8); 68 | } 69 | return; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /TagServer/EWAH/EwahEnumerator.cs: -------------------------------------------------------------------------------- 1 | namespace Ewah 2 | { 3 | /* 4 | * Copyright 2012, Kemal Erdogan, Daniel Lemire and Ciaran Jessup 5 | * Licensed under APL 2.0. 6 | */ 7 | 8 | 9 | /// 10 | /// The class EwahEnumerator represents a special type of 11 | /// efficient enumerator iterating over (uncompressed) words of bits. 12 | /// 13 | public sealed class EwahEnumerator 14 | { 15 | #region Readonly & Static Fields 16 | 17 | /// 18 | /// current running length word 19 | /// 20 | public readonly RunningLengthWord _Rlw; 21 | 22 | /// 23 | /// The size in words 24 | /// 25 | private readonly int _SizeInWords; 26 | 27 | #endregion 28 | 29 | #region Fields 30 | 31 | /// 32 | /// The pointer represent the location of the current running length 33 | /// word in the array of words (embedded in the rlw attribute). 34 | /// 35 | private int _Pointer; 36 | 37 | #endregion 38 | 39 | #region C'tors 40 | 41 | /// 42 | /// Instantiates a new eWAH enumerator 43 | /// 44 | /// the array of words 45 | /// the number of words that are significant in the array of words 46 | public EwahEnumerator(long[] a, int sizeinwords) 47 | { 48 | _Rlw = new RunningLengthWord(a, 0); 49 | _SizeInWords = sizeinwords; 50 | _Pointer = 0; 51 | } 52 | 53 | #endregion 54 | 55 | #region Instance Properties 56 | 57 | /// 58 | /// Access to the array of words 59 | /// 60 | public long[] Buffer 61 | { 62 | get { return _Rlw.ArrayOfWords; } 63 | } 64 | 65 | /// 66 | /// Position of the dirty words represented by this running length word 67 | /// 68 | public int DirtyWords 69 | { 70 | get { return _Pointer - (int) _Rlw.NumberOfLiteralWords; } 71 | } 72 | 73 | #endregion 74 | 75 | #region Instance Methods 76 | 77 | /// 78 | /// Checks for next 79 | /// 80 | /// true, if successful 81 | public bool HasNext() 82 | { 83 | return _Pointer < _SizeInWords; 84 | } 85 | 86 | /// 87 | /// Next running length word 88 | /// 89 | /// 90 | public RunningLengthWord Next() 91 | { 92 | _Rlw.Position = _Pointer; 93 | _Pointer += (int) _Rlw.NumberOfLiteralWords + 1; 94 | return _Rlw; 95 | } 96 | 97 | /// 98 | /// Reset the enumerator to the beginning 99 | /// 100 | internal void Reset() 101 | { 102 | _Rlw.Position = 0; 103 | _Pointer = 0; 104 | } 105 | 106 | #endregion 107 | } 108 | } -------------------------------------------------------------------------------- /TagServer/EWAH/RunningLengthWord.cs: -------------------------------------------------------------------------------- 1 | [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("EWAH.Tests")] 2 | 3 | namespace Ewah 4 | { 5 | 6 | /* 7 | * Copyright 2012, Kemal Erdogan, Daniel Lemire and Ciaran Jessup 8 | * Licensed under APL 2.0. 9 | */ 10 | 11 | /// 12 | /// Mostly for internal use. 13 | /// 14 | public sealed class RunningLengthWord 15 | { 16 | #region Constants 17 | 18 | /// 19 | /// largest number of dirty words in a run 20 | /// 21 | public const long LargestLiteralCount = (1L << LiteralBits) - 1; 22 | 23 | /// 24 | /// largest number of clean words in a run 25 | /// 26 | public const long LargestRunningLengthCount = (1L << RunningLengthBits) - 1; 27 | 28 | /// 29 | /// number of bits dedicated to marking of the running length of clean words 30 | /// 31 | public const int RunningLengthBits = 32; 32 | 33 | private const int LiteralBits = 64 - 1 - RunningLengthBits; 34 | private const long NotRunningLengthPlusRunningBit = ~RunningLengthPlusRunningBit; 35 | private const long NotShiftedLargestRunningLengthCount = ~ShiftedLargestRunningLengthCount; 36 | 37 | private const long RunningLengthPlusRunningBit = (1L << (RunningLengthBits + 1)) - 1; 38 | private const long ShiftedLargestRunningLengthCount = LargestRunningLengthCount << 1; 39 | 40 | #endregion 41 | 42 | #region Fields 43 | 44 | /// 45 | /// The array of words. 46 | /// 47 | public long[] ArrayOfWords; 48 | 49 | /// 50 | /// The Position in array. 51 | /// 52 | public int Position; 53 | 54 | #endregion 55 | 56 | #region C'tors 57 | 58 | /// 59 | /// Instantiates a new running length word 60 | /// 61 | /// an array of 64-bit words 62 | /// p Position in the array where the running length word is located 63 | internal RunningLengthWord(long[] a, int p) 64 | { 65 | ArrayOfWords = a; 66 | Position = p; 67 | } 68 | 69 | #endregion 70 | 71 | #region Instance Properties 72 | 73 | /// 74 | /// Return the size in uncompressed words represented by 75 | /// this running length word. 76 | /// 77 | /// 78 | public long Count 79 | { 80 | get { return RunningLength + NumberOfLiteralWords; } 81 | } 82 | 83 | /// 84 | /// the number of literal words 85 | /// 86 | public long NumberOfLiteralWords 87 | { 88 | get { return (long) (((ulong) ArrayOfWords[Position]) >> (1 + RunningLengthBits)); } 89 | set 90 | { 91 | ArrayOfWords[Position] |= NotRunningLengthPlusRunningBit; 92 | ArrayOfWords[Position] &= (value << (RunningLengthBits + 1)) 93 | | RunningLengthPlusRunningBit; 94 | } 95 | } 96 | 97 | public bool HaveAddedALiteralWord; 98 | 99 | /// 100 | /// the running bit 101 | /// 102 | public bool RunningBit 103 | { 104 | get { return (ArrayOfWords[Position] & 1) != 0; } 105 | set 106 | { 107 | if (value) 108 | { 109 | ArrayOfWords[Position] |= 1L; 110 | } 111 | else 112 | { 113 | ArrayOfWords[Position] &= ~1L; 114 | } 115 | } 116 | } 117 | 118 | /// 119 | /// the running length 120 | /// 121 | public long RunningLength 122 | { 123 | get { return (long) ((((ulong) ArrayOfWords[Position]) >> 1) & LargestRunningLengthCount); } 124 | set 125 | { 126 | ArrayOfWords[Position] |= ShiftedLargestRunningLengthCount; 127 | ArrayOfWords[Position] &= (value << 1) 128 | | NotShiftedLargestRunningLengthCount; 129 | } 130 | } 131 | 132 | #endregion 133 | 134 | #region ICloneable Members 135 | 136 | public object Clone() 137 | { 138 | var clone = new RunningLengthWord(ArrayOfWords,Position); 139 | return clone; 140 | } 141 | 142 | #endregion 143 | #region Instance Methods 144 | 145 | public override string ToString() 146 | { 147 | return "running bit = " + RunningBit + " running length = " 148 | + RunningLength + " number of lit. words " 149 | + NumberOfLiteralWords; 150 | } 151 | 152 | #endregion 153 | } 154 | } -------------------------------------------------------------------------------- /TagServer/Logger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | 5 | namespace StackOverflowTagServer 6 | { 7 | public static class Logger 8 | { 9 | private static readonly List messages = new List(); 10 | public static List Messages { get { return messages; } } 11 | 12 | internal static void Log(string format, params object[] args) 13 | { 14 | var msg = string.Format(format, args); 15 | Log(msg); 16 | } 17 | 18 | internal static void Log(string msg = "") 19 | { 20 | Console.WriteLine(msg); 21 | Trace.WriteLine(msg); 22 | } 23 | 24 | internal static void LogStartupMessage(string format, params object[] args) 25 | { 26 | var msg = string.Format(format, args); 27 | LogStartupMessage(msg); 28 | } 29 | 30 | internal static void LogStartupMessage(string msg = "") 31 | { 32 | Console.WriteLine(msg); 33 | Trace.WriteLine(msg); 34 | messages.Add(msg); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /TagServer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("StackOverflowTagServer")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("StackOverflowTagServer")] 13 | [assembly: AssemblyCopyright("Copyright © 2012")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("9196fde0-521f-459e-9848-42917e79fc94")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /TagServer/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.0 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace StackOverflowTagServer.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("StackOverflowTagServer.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to vb6 65 | ///vba 66 | ///vb 67 | ///homework 68 | ///grails 69 | ///coldfusion 70 | ///flash 71 | ///iphone 72 | ///air 73 | ///sifr 74 | ///ms-access 75 | ///db2 76 | ///vbscript 77 | ///perl 78 | ///sap 79 | ///jpa 80 | ///gql 81 | ///java-ee 82 | ///magento 83 | ///ipad 84 | ///qt 85 | ///weblogic 86 | ///blackberry 87 | ///gwt 88 | ///pentaho 89 | ///wordpress 90 | ///mac 91 | ///corba 92 | ///intellij-idea 93 | ///lucene 94 | ///safari 95 | ///seo 96 | ///redis 97 | ///itouch 98 | ///ant 99 | ///antlr 100 | ///ada 101 | ///gtk 102 | ///doctrine 103 | ///lotus 104 | ///tomcat 105 | ///jcl 106 | ///mongodb 107 | ///netlogo 108 | ///nosql 109 | ///smalltalk 110 | ///beamer 111 | ///spring 112 | ///symbian 113 | ///agile 114 | ///firebird 115 | ///samba 116 | ///jasper-reports 117 | ///sybase 118 | ///fortran 119 | ///qtp 120 | ///itunes 121 | ///sqlite 122 | ///soapui 123 | ///acrobat 124 | ///actionscript* 125 | ///flex* 126 | ///cocoa* 127 | ///struts* 128 | ///ruby* 129 | ///zen [rest of string was truncated]";. 130 | /// 131 | internal static string leppie___excluded_tags { 132 | get { 133 | return ResourceManager.GetString("leppie___excluded_tags", resourceCulture); 134 | } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /TagServer/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\Resources\leppie - excluded tags.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 123 | 124 | -------------------------------------------------------------------------------- /TagServer/Querying/BaseQueryProcessor.cs: -------------------------------------------------------------------------------- 1 | using Shared; 2 | using StackOverflowTagServer.CLR; 3 | using StackOverflowTagServer.DataStructures; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading; 8 | 9 | using TagByQueryLookup = System.Collections.Generic.Dictionary; 10 | 11 | namespace StackOverflowTagServer.Querying 12 | { 13 | internal class BaseQueryProcessor 14 | { 15 | protected readonly List questions; 16 | protected readonly Func GetTagByQueryLookup; 17 | 18 | internal BaseQueryProcessor(List questions, Func getTagByQueryLookup) 19 | { 20 | this.questions = questions; 21 | this.GetTagByQueryLookup = getTagByQueryLookup; 22 | } 23 | 24 | // 8.5 million is more than enough, our data-set only has 7.9 million questions! 25 | protected ThreadLocal> cache = new ThreadLocal>(() => new HashSetCache(initialSize: 850000, comparer: new IntComparer())); 26 | protected ThreadLocal> secondCache = new ThreadLocal>(() => new HashSetCache(initialSize: 850000, comparer: new IntComparer())); 27 | 28 | protected Func GetFieldSelector(QueryType type) 29 | { 30 | Func fieldSelector; 31 | switch (type) 32 | { 33 | case QueryType.LastActivityDate: 34 | fieldSelector = qu => qu.LastActivityDate.ToString(); 35 | break; 36 | case QueryType.CreationDate: 37 | fieldSelector = qu => qu.CreationDate.ToString(); 38 | break; 39 | case QueryType.Score: 40 | fieldSelector = qu => qu.Score.HasValue ? qu.Score.Value.ToString("N0") : ""; 41 | break; 42 | case QueryType.ViewCount: 43 | fieldSelector = qu => qu.ViewCount.HasValue ? qu.ViewCount.Value.ToString("N0") : ""; 44 | break; 45 | case QueryType.AnswerCount: 46 | fieldSelector = qu => qu.AnswerCount.HasValue ? qu.AnswerCount.Value.ToString("N0") : ""; 47 | break; 48 | default: 49 | throw new InvalidOperationException(string.Format("Invalid query type {0}", (int)type)); 50 | } 51 | return fieldSelector; 52 | } 53 | 54 | protected void ThrowIfInvalidParameters(string tag, int pageSize, TagByQueryLookup queryInfo) 55 | { 56 | if (string.IsNullOrWhiteSpace(tag) || queryInfo.ContainsKey(tag) == false) 57 | throw new InvalidOperationException(string.Format("Invalid tag specified: {0}", tag ?? "")); 58 | 59 | if (pageSize < 1 || pageSize > 250) 60 | throw new InvalidOperationException(string.Format("Invalid page size provided: {0}, only values from 1 to 250 are allowed", pageSize)); 61 | } 62 | 63 | protected void PrintResults(IEnumerable questions, string info, QueryType queryType) 64 | { 65 | Logger.Log("RESULTS for \"{0}\":", info); 66 | var fieldFetcher = GetFieldSelector(queryType); 67 | foreach (var question in questions.Take(10)) 68 | { 69 | Logger.Log(" Qu=[{0,9:N0}], {1}={2,10}, Tags= {3}", 70 | question.Id, queryType, fieldFetcher(question), String.Join(", ", question.Tags)); 71 | } 72 | } 73 | 74 | protected void PrintResults(IEnumerable bits, Func questionLookup, string info, QueryType queryType) 75 | { 76 | Logger.Log("RESULTS for \"{0}\":", info); 77 | var fieldFetcher = GetFieldSelector(queryType); 78 | foreach (var bit in bits.Take(10)) 79 | { 80 | var questionId = questionLookup == null ? bit : questionLookup(bit); 81 | var question = questions[questionId]; 82 | Logger.Log(" Bit=[{0,9:N0}] -> Qu=[{1,9:N0}], {2}={3,10}, Tags= {4}", 83 | bit, questionId, queryType, fieldFetcher(question), String.Join(", ", question.Tags)); 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /TagServer/Querying/IntComparer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace StackOverflowTagServer.Querying 4 | { 5 | class IntComparer : IEqualityComparer 6 | { 7 | public bool Equals(int x, int y) 8 | { 9 | return x.Equals(y); 10 | } 11 | 12 | public int GetHashCode(int obj) 13 | { 14 | return obj.GetHashCode(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /TagServer/Querying/QueryInfo.cs: -------------------------------------------------------------------------------- 1 | using StackOverflowTagServer.DataStructures; 2 | 3 | namespace StackOverflowTagServer.Querying 4 | { 5 | public class QueryInfo 6 | { 7 | public QueryInfo() 8 | { 9 | // Set some sensible defaults 10 | PageSize = 25; 11 | Skip = 0; 12 | Operator = "AND"; 13 | 14 | UseLinq = false; 15 | UseBitMapIndexes = false; 16 | 17 | UseLeppieExclusions = false; 18 | DebugMode = false; 19 | } 20 | 21 | public QueryType Type { get; set; } 22 | public int PageSize { get; set; } 23 | public int Skip { get; set; } 24 | 25 | public string Tag { get; set; } 26 | public string OtherTag { get; set; } 27 | public string Operator { get; set; } 28 | 29 | public bool UseLinq { get; set; } 30 | public bool UseBitMapIndexes { get; set; } 31 | public bool UseLeppieExclusions { get; set; } 32 | public bool DebugMode { get; set; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /TagServer/Querying/QueryResult.cs: -------------------------------------------------------------------------------- 1 | using Shared; 2 | using System.Collections.Generic; 3 | 4 | namespace StackOverflowTagServer.Querying 5 | { 6 | public class QueryResult 7 | { 8 | public List Questions { get; set; } 9 | 10 | public Dictionary Counters { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /TagServer/Querying/QueryTester.cs: -------------------------------------------------------------------------------- 1 | using Shared; 2 | using System.Collections.Generic; 3 | 4 | namespace StackOverflowTagServer.Querying 5 | { 6 | internal class QueryTester 7 | { 8 | private List questions; 9 | 10 | internal QueryTester(List questions) 11 | { 12 | this.questions = questions; 13 | } 14 | 15 | internal void TestAndOrNotQueries() 16 | { 17 | // TODO When it's available, do this via the API 18 | 19 | //var tag1 = "c#"; 20 | //var tag2 = ".net"; // "java" 21 | //var tagGroup1 = tagsByViewCount[tag1]; 22 | //var tagGroup2 = tagsByViewCount[tag2]; 23 | 24 | ////Use Intersect for AND, Union for OR and Except for NOT 25 | //var allIntersect = tagGroup1.Intersect(tagGroup2).Select(i => questions[i]).ToArray(); 26 | //var intersectTimer = Stopwatch.StartNew(); 27 | //var tagIntersect = tagGroup1.Intersect(tagGroup2).Take(10).Select(i => questions[i]).ToArray(); 28 | //intersectTimer.Stop(); 29 | //var allIntersectMatch = tagIntersect.All(qu => qu.Tags.Contains(tag1) && qu.Tags.Contains(tag2)); 30 | //Console.WriteLine("Doing a Intersect (AND) of \"{0}\", \"{1}\" tags took {2:0.00} msecs - all match {3}", 31 | // tag1, tag2, intersectTimer.Elapsed.TotalMilliseconds, allIntersectMatch); 32 | 33 | //intersectTimer = Stopwatch.StartNew(); 34 | //var tagIntersect2 = tagGroup1.Intersect(tagGroup2).Skip(allIntersect.Count() - 10).Take(10).Select(i => questions[i]).ToArray(); 35 | //intersectTimer.Stop(); 36 | //var allIntersectMatch2 = tagIntersect.All(qu => qu.Tags.Contains(tag1) && qu.Tags.Contains(tag2)); 37 | //Console.WriteLine("Doing a Intersect (Skip({0})) (AND) of \"{1}\", \"{2}\" tags took {3:0.00} msecs - all match {4}", 38 | // allIntersect.Count() - 10, tag1, tag2, intersectTimer.Elapsed.TotalMilliseconds, allIntersectMatch2); 39 | 40 | ////Use Intersect for AND, Union for OR and Except for NOT 41 | //var unionTimer = Stopwatch.StartNew(); 42 | //var tagUnion = tagGroup1.Union(tagGroup2).Take(10).Select(i => questions[i]).ToArray(); 43 | //unionTimer.Stop(); 44 | //var allUnionMatch = tagUnion.All(qu => qu.Tags.Contains(tag1) || qu.Tags.Contains(tag2)); 45 | //Console.WriteLine("Doing a Union (OR) of \"{0}\", \"{1}\" tags took {2:0.00} msecs - all match {3}", 46 | // tag1, tag2, unionTimer.Elapsed.TotalMilliseconds, allUnionMatch); 47 | 48 | ////Use Intersect for AND, Union for OR and Except for NOT 49 | //var exceptTimer = Stopwatch.StartNew(); 50 | //var tagExcept = tagGroup1.Except(tagGroup2).Take(10).Select(i => questions[i]).ToArray(); 51 | //exceptTimer.Stop(); 52 | //var allExceptMatch = tagExcept.All(qu => qu.Tags.Contains(tag1) && (qu.Tags.Contains(tag2) == false)); 53 | //Console.WriteLine("Doing a Except (NOT) of \"{0}\", \"{1}\" tags took {2:0.00} msecs - all match {3}\n", 54 | // tag1, tag2, exceptTimer.Elapsed.TotalMilliseconds, allExceptMatch); 55 | } 56 | 57 | internal void TestQueries() 58 | { 59 | // TODO When it's available, do this via the API 60 | 61 | //var csharpTags = tagsByViewCount["c#"]; 62 | //var javaTags = tagsByViewCount["java"]; 63 | 64 | //var mostRecentTimer2 = Stopwatch.StartNew(); 65 | //var startPosn = 0; 66 | //var takeAmt = 10; 67 | //var mostRecentCSharp2 = new List(takeAmt); 68 | //for (int i = startPosn; i < startPosn + takeAmt; i++) 69 | //{ 70 | // mostRecentCSharp2.Add(questions[csharpTags[i]]); 71 | //} 72 | //mostRecentTimer2.Stop(); 73 | //Console.WriteLine("2) Most Recent C# tags took {0}, ({1:0.00} ms) - NOT using LINQ, using foreach instead ({2} items)", 74 | // mostRecentTimer2.Elapsed, mostRecentTimer2.Elapsed.TotalMilliseconds, mostRecentCSharp2.Count()); 75 | 76 | ////Top 10 Most recently updated!! C# questions is just 77 | //var mostRecentTimer = Stopwatch.StartNew(); 78 | //var mostRecentCSharp = csharpTags.Skip(0).Take(10).Select(x => questions[x]).ToList(); 79 | //mostRecentTimer.Stop(); 80 | //Console.WriteLine("1) Most Recent C# tags took {0}, ({1:0.00} ms) - Using LINQ ({2} items)", 81 | // mostRecentTimer.Elapsed, mostRecentTimer.Elapsed.TotalMilliseconds, mostRecentCSharp.Count()); 82 | 83 | //var lessRecentTimer = Stopwatch.StartNew(); 84 | //var lessRecentCSharp = csharpTags.Skip(1000).Take(10).Select(x => questions[x]).ToList(); 85 | //lessRecentTimer.Stop(); 86 | //Console.WriteLine("1) Less Recent C# tags took {0}, ({1:0.00} ms) - Using LINQ ({2} items)", 87 | // lessRecentTimer.Elapsed, lessRecentTimer.Elapsed.TotalMilliseconds, lessRecentCSharp.Count()); 88 | 89 | //mostRecentTimer2 = Stopwatch.StartNew(); 90 | //mostRecentCSharp2 = new List(takeAmt); 91 | //for (int i = startPosn; i < startPosn + takeAmt; i++) 92 | //{ 93 | // mostRecentCSharp2.Add(questions[csharpTags[i]]); 94 | //} 95 | //mostRecentTimer2.Stop(); 96 | //Console.WriteLine("2) Most Recent C# tags took {0}, ({1:0.00} ms) - NOT using LINQ, using foreach instead ({2} items)", 97 | // mostRecentTimer2.Elapsed, mostRecentTimer2.Elapsed.TotalMilliseconds, mostRecentCSharp2.Count()); 98 | 99 | //var areEqual = mostRecentCSharp.ListEquals(mostRecentCSharp2, (qu1, qu2) => 100 | //{ 101 | // if (qu1.CreationDate == qu2.CreationDate && 102 | // qu1.LastActivityDate == qu2.LastActivityDate && 103 | // qu1.Id == qu2.Id && 104 | // qu1.Tags.ListEquals(qu2.Tags, (t1, t2) => t1 == t2)) 105 | // return true; 106 | 107 | // return false; 108 | //}); 109 | //Console.WriteLine("When comparing LINQ with non-LINQ method, results are {0}\n", areEqual ? "the SAME" : "DIFFERENT"); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /TagServer/Results.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | 5 | namespace StackOverflowTagServer 6 | { 7 | internal static class Results 8 | { 9 | private static StreamWriter stream; 10 | 11 | internal static void CreateNewFile(string filename) 12 | { 13 | var directory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 14 | var fileStream = new FileStream(Path.Combine(directory, filename), 15 | mode: FileMode.Create, 16 | access: FileAccess.ReadWrite, 17 | share: FileShare.ReadWrite); 18 | stream = new StreamWriter(fileStream); 19 | } 20 | 21 | internal static void AddHeaders(params string [] headers) 22 | { 23 | try 24 | { 25 | stream.WriteLine(string.Join(", ", headers)); 26 | stream.Flush(); 27 | } 28 | catch (ObjectDisposedException) 29 | { 30 | // swallow 31 | } 32 | } 33 | 34 | internal static void StartNewRow() 35 | { 36 | try 37 | { 38 | stream.WriteLine(); 39 | stream.Flush(); 40 | } 41 | catch (ObjectDisposedException) 42 | { 43 | // swallow 44 | } 45 | } 46 | 47 | internal static void AddData(string data) 48 | { 49 | if (stream == null) 50 | return; 51 | 52 | try 53 | { 54 | stream.Write(data.EndsWith(",") ? data : data + ","); 55 | } 56 | catch (ObjectDisposedException) 57 | { 58 | // swallow 59 | } 60 | } 61 | 62 | internal static void CloseFile() 63 | { 64 | if (stream != null) 65 | { 66 | stream.Flush(); 67 | stream.Close(); 68 | stream.Dispose(); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /TagServer/Utils.cs: -------------------------------------------------------------------------------- 1 | using Shared; 2 | using StackOverflowTagServer.DataStructures; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Reflection; 8 | 9 | namespace StackOverflowTagServer 10 | { 11 | public static class Utils 12 | { 13 | public static List GetLeppieTagsFromResource() 14 | { 15 | var leppieTags = new List(); 16 | var resourceStream = Utils.GetStream("leppie - excluded tags.txt"); 17 | if (resourceStream != null) 18 | { 19 | var fileStream = new StreamReader(resourceStream); 20 | string line; 21 | while ((line = fileStream.ReadLine()) != null) 22 | leppieTags.Add(line); 23 | //Logger.Log(string.Join(", ", tagsToExpand)); 24 | } 25 | return leppieTags; 26 | } 27 | 28 | // From http://stackoverflow.com/questions/11590582/read-text-file-resource-from-net-library/11596483#11596483 29 | internal static Stream GetStream(string resourceName) 30 | { 31 | try 32 | { 33 | Assembly assy = Assembly.GetExecutingAssembly(); 34 | string[] resources = assy.GetManifestResourceNames(); 35 | for (int i = 0; i < resources.Length; i++) 36 | { 37 | if (resources[i].ToLower().IndexOf(resourceName.ToLower()) != -1) 38 | { 39 | // resource found 40 | return assy.GetManifestResourceStream(resources[i]); 41 | } 42 | } 43 | } 44 | catch (Exception) 45 | { 46 | throw; 47 | } 48 | return Stream.Null; 49 | } 50 | 51 | internal static IDisposable SetConsoleColour(ConsoleColor newColour) 52 | { 53 | Console.ForegroundColor = newColour; 54 | return new DisposableAction(() => Console.ResetColor()); 55 | } 56 | 57 | internal static ConsoleColor GetColorForTimespan(TimeSpan elapsed) 58 | { 59 | if (elapsed.TotalMilliseconds > 500) 60 | return ConsoleColor.Red; 61 | else if (elapsed.TotalMilliseconds > 400) 62 | return ConsoleColor.DarkYellow; 63 | return Console.ForegroundColor; 64 | } 65 | 66 | internal static void CompareLists(List listA, string nameA, List listB, string nameB) 67 | { 68 | if (listA.Count != listB.Count) 69 | Logger.LogStartupMessage("ERROR: list have different lengths, {0}: {1}, {2}: {3}", nameA, listA.Count, nameB, listB.Count); 70 | var AExceptB = listA.Select(r => r.Id).Except(listB.Select(r => r.Id)).ToList(); 71 | if (AExceptB.Any()) 72 | { 73 | Logger.LogStartupMessage("ERROR: Items in {0}, but not in {1}: {2}\n", nameA, nameB, 74 | string.Join(", ", AExceptB.Select(r => string.Format("[{0}]={1}", listA.FindIndex(s => s.Id == r), r)))); 75 | } 76 | var BExceptA = listB.Select(r => r.Id).Except(listA.Select(r => r.Id)).ToList(); 77 | if (BExceptA.Any()) 78 | { 79 | Logger.LogStartupMessage("ERROR: Items in {0}, but not in {1}: {2}\n", nameB, nameA, 80 | string.Join(", ", BExceptA.Select(r => string.Format("[{0}]={1}", listB.FindIndex(s => s.Id == r), r)))); 81 | } 82 | 83 | //foreach (var item in Enumerable.Range(0, Math.Min(listA.Count, listB.Count))) 84 | //{ 85 | // if (listA[item].Id != listB[item].Id) 86 | // Logger.LogStartupMessage("ERROR: lists differ at position[{0}], {1} Id: {2}, {3} Id: {4}", 87 | // item, nameA, listA[item].Id, nameB, listB[item].Id); 88 | //} 89 | } 90 | 91 | internal static List SelectNItemsFromList(List expandedTags, int count) 92 | { 93 | if (count == expandedTags.Count) 94 | return expandedTags; 95 | else 96 | { 97 | var result = new List(count); 98 | var step = expandedTags.Count / count; 99 | int i = 0; 100 | while (i < expandedTags.Count && (result.Count < count)) 101 | { 102 | result.Add(expandedTags[i]); 103 | i += step; 104 | } 105 | return result; 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /TagServer/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /WebSite/App_Start/BundleConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Optimization; 2 | 3 | namespace Server 4 | { 5 | public class BundleConfig 6 | { 7 | // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862 8 | public static void RegisterBundles(BundleCollection bundles) 9 | { 10 | bundles.Add(new ScriptBundle("~/bundles/jquery").Include( 11 | "~/Scripts/jquery-{version}.js")); 12 | 13 | // Use the development version of Modernizr to develop with and learn from. Then, when you're 14 | // ready for production, use the build tool at http://modernizr.com to pick only the tests you need. 15 | bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( 16 | "~/Scripts/modernizr-*")); 17 | 18 | bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( 19 | "~/Scripts/bootstrap.js", 20 | "~/Scripts/respond.js")); 21 | 22 | bundles.Add(new StyleBundle("~/Content/css").Include( 23 | "~/Content/bootstrap.css", 24 | "~/Content/site.css")); 25 | 26 | // Set EnableOptimizations to false for debugging. For more information, 27 | // visit http://go.microsoft.com/fwlink/?LinkId=301862 28 | BundleTable.EnableOptimizations = true; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /WebSite/App_Start/FilterConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | 3 | namespace Server 4 | { 5 | public class FilterConfig 6 | { 7 | public static void RegisterGlobalFilters(GlobalFilterCollection filters) 8 | { 9 | filters.Add(new HandleErrorAttribute()); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /WebSite/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | using System.Web.Routing; 3 | 4 | namespace Server 5 | { 6 | public class RouteConfig 7 | { 8 | public static void RegisterRoutes(RouteCollection routes) 9 | { 10 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 11 | 12 | routes.MapRoute( 13 | name: "Default", 14 | url: "{controller}/{action}/{id}", 15 | defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } 16 | ); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /WebSite/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | using Server.Infrastructure; 2 | using System.Web.Http; 3 | 4 | namespace Server 5 | { 6 | public static class WebApiConfig 7 | { 8 | public static void Register(HttpConfiguration config) 9 | { 10 | // Web API configuration and services 11 | 12 | // Web API routes 13 | config.MapHttpAttributeRoutes(); 14 | 15 | //config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html")); 16 | config.Formatters.Add(new BrowserJsonFormatter()); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /WebSite/ApplicationInsights.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 2bcbb0e7-b5ee-4b3e-bc8d-d4fc860c4b6d 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/ApiDescriptionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Web; 4 | using System.Web.Http.Description; 5 | 6 | namespace Server.Areas.HelpPage 7 | { 8 | public static class ApiDescriptionExtensions 9 | { 10 | /// 11 | /// Generates an URI-friendly ID for the . E.g. "Get-Values-id_name" instead of "GetValues/{id}?name={name}" 12 | /// 13 | /// The . 14 | /// The ID as a string. 15 | public static string GetFriendlyId(this ApiDescription description) 16 | { 17 | string path = description.RelativePath; 18 | string[] urlParts = path.Split('?'); 19 | string localPath = urlParts[0]; 20 | string queryKeyString = null; 21 | if (urlParts.Length > 1) 22 | { 23 | string query = urlParts[1]; 24 | string[] queryKeys = HttpUtility.ParseQueryString(query).AllKeys; 25 | queryKeyString = String.Join("_", queryKeys); 26 | } 27 | 28 | StringBuilder friendlyPath = new StringBuilder(); 29 | friendlyPath.AppendFormat("{0}-{1}", 30 | description.HttpMethod.Method, 31 | localPath.Replace("/", "-").Replace("{", String.Empty).Replace("}", String.Empty)); 32 | if (queryKeyString != null) 33 | { 34 | friendlyPath.AppendFormat("_{0}", queryKeyString); 35 | } 36 | return friendlyPath.ToString(); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/App_Start/HelpPageConfig.cs: -------------------------------------------------------------------------------- 1 | // Uncomment the following to provide samples for PageResult. Must also add the Microsoft.AspNet.WebApi.OData 2 | // package to your project. 3 | ////#define Handle_PageResultOfT 4 | 5 | using System; 6 | using System.Collections; 7 | using System.Collections.Generic; 8 | using System.Diagnostics; 9 | using System.Diagnostics.CodeAnalysis; 10 | using System.Linq; 11 | using System.Net.Http.Headers; 12 | using System.Reflection; 13 | using System.Web; 14 | using System.Web.Http; 15 | #if Handle_PageResultOfT 16 | using System.Web.Http.OData; 17 | #endif 18 | 19 | namespace Server.Areas.HelpPage 20 | { 21 | /// 22 | /// Use this class to customize the Help Page. 23 | /// For example you can set a custom to supply the documentation 24 | /// or you can provide the samples for the requests/responses. 25 | /// 26 | public static class HelpPageConfig 27 | { 28 | [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", 29 | MessageId = "Server.Areas.HelpPage.TextSample.#ctor(System.String)", 30 | Justification = "End users may choose to merge this string with existing localized resources.")] 31 | [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", 32 | MessageId = "bsonspec", 33 | Justification = "Part of a URI.")] 34 | public static void Register(HttpConfiguration config) 35 | { 36 | //// Uncomment the following to use the documentation from XML documentation file. 37 | //config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml"))); 38 | 39 | //// Uncomment the following to use "sample string" as the sample for all actions that have string as the body parameter or return type. 40 | //// Also, the string arrays will be used for IEnumerable. The sample objects will be serialized into different media type 41 | //// formats by the available formatters. 42 | //config.SetSampleObjects(new Dictionary 43 | //{ 44 | // {typeof(string), "sample string"}, 45 | // {typeof(IEnumerable), new string[]{"sample 1", "sample 2"}} 46 | //}); 47 | 48 | // Extend the following to provide factories for types not handled automatically (those lacking parameterless 49 | // constructors) or for which you prefer to use non-default property values. Line below provides a fallback 50 | // since automatic handling will fail and GeneratePageResult handles only a single type. 51 | #if Handle_PageResultOfT 52 | config.GetHelpPageSampleGenerator().SampleObjectFactories.Add(GeneratePageResult); 53 | #endif 54 | 55 | // Extend the following to use a preset object directly as the sample for all actions that support a media 56 | // type, regardless of the body parameter or return type. The lines below avoid display of binary content. 57 | // The BsonMediaTypeFormatter (if available) is not used to serialize the TextSample object. 58 | config.SetSampleForMediaType( 59 | new TextSample("Binary JSON content. See http://bsonspec.org for details."), 60 | new MediaTypeHeaderValue("application/bson")); 61 | 62 | //// Uncomment the following to use "[0]=foo&[1]=bar" directly as the sample for all actions that support form URL encoded format 63 | //// and have IEnumerable as the body parameter or return type. 64 | //config.SetSampleForType("[0]=foo&[1]=bar", new MediaTypeHeaderValue("application/x-www-form-urlencoded"), typeof(IEnumerable)); 65 | 66 | //// Uncomment the following to use "1234" directly as the request sample for media type "text/plain" on the controller named "Values" 67 | //// and action named "Put". 68 | //config.SetSampleRequest("1234", new MediaTypeHeaderValue("text/plain"), "Values", "Put"); 69 | 70 | //// Uncomment the following to use the image on "../images/aspNetHome.png" directly as the response sample for media type "image/png" 71 | //// on the controller named "Values" and action named "Get" with parameter "id". 72 | //config.SetSampleResponse(new ImageSample("../images/aspNetHome.png"), new MediaTypeHeaderValue("image/png"), "Values", "Get", "id"); 73 | 74 | //// Uncomment the following to correct the sample request when the action expects an HttpRequestMessage with ObjectContent. 75 | //// The sample will be generated as if the controller named "Values" and action named "Get" were having string as the body parameter. 76 | //config.SetActualRequestType(typeof(string), "Values", "Get"); 77 | 78 | //// Uncomment the following to correct the sample response when the action returns an HttpResponseMessage with ObjectContent. 79 | //// The sample will be generated as if the controller named "Values" and action named "Post" were returning a string. 80 | //config.SetActualResponseType(typeof(string), "Values", "Post"); 81 | } 82 | 83 | #if Handle_PageResultOfT 84 | private static object GeneratePageResult(HelpPageSampleGenerator sampleGenerator, Type type) 85 | { 86 | if (type.IsGenericType) 87 | { 88 | Type openGenericType = type.GetGenericTypeDefinition(); 89 | if (openGenericType == typeof(PageResult<>)) 90 | { 91 | // Get the T in PageResult 92 | Type[] typeParameters = type.GetGenericArguments(); 93 | Debug.Assert(typeParameters.Length == 1); 94 | 95 | // Create an enumeration to pass as the first parameter to the PageResult constuctor 96 | Type itemsType = typeof(List<>).MakeGenericType(typeParameters); 97 | object items = sampleGenerator.GetSampleObject(itemsType); 98 | 99 | // Fill in the other information needed to invoke the PageResult constuctor 100 | Type[] parameterTypes = new Type[] { itemsType, typeof(Uri), typeof(long?), }; 101 | object[] parameters = new object[] { items, null, (long)ObjectGenerator.DefaultCollectionSize, }; 102 | 103 | // Call PageResult(IEnumerable items, Uri nextPageLink, long? count) constructor 104 | ConstructorInfo constructor = type.GetConstructor(parameterTypes); 105 | return constructor.Invoke(parameters); 106 | } 107 | } 108 | 109 | return null; 110 | } 111 | #endif 112 | } 113 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Controllers/HelpController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Http; 3 | using System.Web.Mvc; 4 | using Server.Areas.HelpPage.ModelDescriptions; 5 | using Server.Areas.HelpPage.Models; 6 | 7 | namespace Server.Areas.HelpPage.Controllers 8 | { 9 | /// 10 | /// The controller that will handle requests for the help page. 11 | /// 12 | public class HelpController : Controller 13 | { 14 | private const string ErrorViewName = "Error"; 15 | 16 | public HelpController() 17 | : this(GlobalConfiguration.Configuration) 18 | { 19 | } 20 | 21 | public HelpController(HttpConfiguration config) 22 | { 23 | Configuration = config; 24 | } 25 | 26 | public HttpConfiguration Configuration { get; private set; } 27 | 28 | public ActionResult Index() 29 | { 30 | ViewBag.DocumentationProvider = Configuration.Services.GetDocumentationProvider(); 31 | return View(Configuration.Services.GetApiExplorer().ApiDescriptions); 32 | } 33 | 34 | public ActionResult Api(string apiId) 35 | { 36 | if (!String.IsNullOrEmpty(apiId)) 37 | { 38 | HelpPageApiModel apiModel = Configuration.GetHelpPageApiModel(apiId); 39 | if (apiModel != null) 40 | { 41 | return View(apiModel); 42 | } 43 | } 44 | 45 | return View(ErrorViewName); 46 | } 47 | 48 | public ActionResult ResourceModel(string modelName) 49 | { 50 | if (!String.IsNullOrEmpty(modelName)) 51 | { 52 | ModelDescriptionGenerator modelDescriptionGenerator = Configuration.GetModelDescriptionGenerator(); 53 | ModelDescription modelDescription; 54 | if (modelDescriptionGenerator.GeneratedModels.TryGetValue(modelName, out modelDescription)) 55 | { 56 | return View(modelDescription); 57 | } 58 | } 59 | 60 | return View(ErrorViewName); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/HelpPage.css: -------------------------------------------------------------------------------- 1 | .help-page h1, 2 | .help-page .h1, 3 | .help-page h2, 4 | .help-page .h2, 5 | .help-page h3, 6 | .help-page .h3, 7 | #body.help-page, 8 | .help-page-table th, 9 | .help-page-table pre, 10 | .help-page-table p { 11 | font-family: "Segoe UI Light", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif; 12 | } 13 | 14 | .help-page pre.wrapped { 15 | white-space: -moz-pre-wrap; 16 | white-space: -pre-wrap; 17 | white-space: -o-pre-wrap; 18 | white-space: pre-wrap; 19 | } 20 | 21 | .help-page .warning-message-container { 22 | margin-top: 20px; 23 | padding: 0 10px; 24 | color: #525252; 25 | background: #EFDCA9; 26 | border: 1px solid #CCCCCC; 27 | } 28 | 29 | .help-page-table { 30 | width: 100%; 31 | border-collapse: collapse; 32 | text-align: left; 33 | margin: 0px 0px 20px 0px; 34 | border-top: 1px solid #D4D4D4; 35 | } 36 | 37 | .help-page-table th { 38 | text-align: left; 39 | font-weight: bold; 40 | border-bottom: 1px solid #D4D4D4; 41 | padding: 5px 6px 5px 6px; 42 | } 43 | 44 | .help-page-table td { 45 | border-bottom: 1px solid #D4D4D4; 46 | padding: 10px 8px 10px 8px; 47 | vertical-align: top; 48 | } 49 | 50 | .help-page-table pre, 51 | .help-page-table p { 52 | margin: 0px; 53 | padding: 0px; 54 | font-family: inherit; 55 | font-size: 100%; 56 | } 57 | 58 | .help-page-table tbody tr:hover td { 59 | background-color: #F3F3F3; 60 | } 61 | 62 | .help-page a:hover { 63 | background-color: transparent; 64 | } 65 | 66 | .help-page .sample-header { 67 | border: 2px solid #D4D4D4; 68 | background: #76B8DB; 69 | color: #FFFFFF; 70 | padding: 8px 15px; 71 | border-bottom: none; 72 | display: inline-block; 73 | margin: 10px 0px 0px 0px; 74 | } 75 | 76 | .help-page .sample-content { 77 | display: block; 78 | border-width: 0; 79 | padding: 15px 20px; 80 | background: #FFFFFF; 81 | border: 2px solid #D4D4D4; 82 | margin: 0px 0px 10px 0px; 83 | } 84 | 85 | .help-page .api-name { 86 | width: 40%; 87 | } 88 | 89 | .help-page .api-documentation { 90 | width: 60%; 91 | } 92 | 93 | .help-page .parameter-name { 94 | width: 20%; 95 | } 96 | 97 | .help-page .parameter-documentation { 98 | width: 40%; 99 | } 100 | 101 | .help-page .parameter-type { 102 | width: 20%; 103 | } 104 | 105 | .help-page .parameter-annotations { 106 | width: 20%; 107 | } 108 | 109 | .help-page h1, 110 | .help-page .h1 { 111 | font-size: 36px; 112 | } 113 | 114 | .help-page h2, 115 | .help-page .h2 { 116 | font-size: 24px; 117 | } 118 | 119 | .help-page h3, 120 | .help-page .h3 { 121 | font-size: 20px; 122 | } 123 | 124 | #body.help-page { 125 | font-size: 14px; 126 | line-height: 143%; 127 | color: #333; 128 | } 129 | 130 | .help-page a { 131 | color: #00abec; 132 | text-decoration: none; 133 | } 134 | -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/HelpPageAreaRegistration.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | using System.Web.Mvc; 3 | 4 | namespace Server.Areas.HelpPage 5 | { 6 | public class HelpPageAreaRegistration : AreaRegistration 7 | { 8 | public override string AreaName 9 | { 10 | get 11 | { 12 | return "HelpPage"; 13 | } 14 | } 15 | 16 | public override void RegisterArea(AreaRegistrationContext context) 17 | { 18 | context.MapRoute( 19 | "HelpPage_Default", 20 | "Help/{action}/{apiId}", 21 | new { controller = "Help", action = "Index", apiId = UrlParameter.Optional }); 22 | 23 | HelpPageConfig.Register(GlobalConfiguration.Configuration); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/ModelDescriptions/CollectionModelDescription.cs: -------------------------------------------------------------------------------- 1 | namespace Server.Areas.HelpPage.ModelDescriptions 2 | { 3 | public class CollectionModelDescription : ModelDescription 4 | { 5 | public ModelDescription ElementDescription { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/ModelDescriptions/ComplexTypeModelDescription.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | 3 | namespace Server.Areas.HelpPage.ModelDescriptions 4 | { 5 | public class ComplexTypeModelDescription : ModelDescription 6 | { 7 | public ComplexTypeModelDescription() 8 | { 9 | Properties = new Collection(); 10 | } 11 | 12 | public Collection Properties { get; private set; } 13 | } 14 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/ModelDescriptions/DictionaryModelDescription.cs: -------------------------------------------------------------------------------- 1 | namespace Server.Areas.HelpPage.ModelDescriptions 2 | { 3 | public class DictionaryModelDescription : KeyValuePairModelDescription 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/ModelDescriptions/EnumTypeModelDescription.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | 4 | namespace Server.Areas.HelpPage.ModelDescriptions 5 | { 6 | public class EnumTypeModelDescription : ModelDescription 7 | { 8 | public EnumTypeModelDescription() 9 | { 10 | Values = new Collection(); 11 | } 12 | 13 | public Collection Values { get; private set; } 14 | } 15 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/ModelDescriptions/EnumValueDescription.cs: -------------------------------------------------------------------------------- 1 | namespace Server.Areas.HelpPage.ModelDescriptions 2 | { 3 | public class EnumValueDescription 4 | { 5 | public string Documentation { get; set; } 6 | 7 | public string Name { get; set; } 8 | 9 | public string Value { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/ModelDescriptions/IModelDocumentationProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace Server.Areas.HelpPage.ModelDescriptions 5 | { 6 | public interface IModelDocumentationProvider 7 | { 8 | string GetDocumentation(MemberInfo member); 9 | 10 | string GetDocumentation(Type type); 11 | } 12 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/ModelDescriptions/KeyValuePairModelDescription.cs: -------------------------------------------------------------------------------- 1 | namespace Server.Areas.HelpPage.ModelDescriptions 2 | { 3 | public class KeyValuePairModelDescription : ModelDescription 4 | { 5 | public ModelDescription KeyModelDescription { get; set; } 6 | 7 | public ModelDescription ValueModelDescription { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/ModelDescriptions/ModelDescription.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Server.Areas.HelpPage.ModelDescriptions 4 | { 5 | /// 6 | /// Describes a type model. 7 | /// 8 | public abstract class ModelDescription 9 | { 10 | public string Documentation { get; set; } 11 | 12 | public Type ModelType { get; set; } 13 | 14 | public string Name { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/ModelDescriptions/ModelNameAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Server.Areas.HelpPage.ModelDescriptions 4 | { 5 | /// 6 | /// Use this attribute to change the name of the generated for a type. 7 | /// 8 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum, AllowMultiple = false, Inherited = false)] 9 | public sealed class ModelNameAttribute : Attribute 10 | { 11 | public ModelNameAttribute(string name) 12 | { 13 | Name = name; 14 | } 15 | 16 | public string Name { get; private set; } 17 | } 18 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/ModelDescriptions/ModelNameHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace Server.Areas.HelpPage.ModelDescriptions 7 | { 8 | internal static class ModelNameHelper 9 | { 10 | // Modify this to provide custom model name mapping. 11 | public static string GetModelName(Type type) 12 | { 13 | ModelNameAttribute modelNameAttribute = type.GetCustomAttribute(); 14 | if (modelNameAttribute != null && !String.IsNullOrEmpty(modelNameAttribute.Name)) 15 | { 16 | return modelNameAttribute.Name; 17 | } 18 | 19 | string modelName = type.Name; 20 | if (type.IsGenericType) 21 | { 22 | // Format the generic type name to something like: GenericOfAgurment1AndArgument2 23 | Type genericType = type.GetGenericTypeDefinition(); 24 | Type[] genericArguments = type.GetGenericArguments(); 25 | string genericTypeName = genericType.Name; 26 | 27 | // Trim the generic parameter counts from the name 28 | genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf('`')); 29 | string[] argumentTypeNames = genericArguments.Select(t => GetModelName(t)).ToArray(); 30 | modelName = String.Format(CultureInfo.InvariantCulture, "{0}Of{1}", genericTypeName, String.Join("And", argumentTypeNames)); 31 | } 32 | 33 | return modelName; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/ModelDescriptions/ParameterAnnotation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Server.Areas.HelpPage.ModelDescriptions 4 | { 5 | public class ParameterAnnotation 6 | { 7 | public Attribute AnnotationAttribute { get; set; } 8 | 9 | public string Documentation { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/ModelDescriptions/ParameterDescription.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | 4 | namespace Server.Areas.HelpPage.ModelDescriptions 5 | { 6 | public class ParameterDescription 7 | { 8 | public ParameterDescription() 9 | { 10 | Annotations = new Collection(); 11 | } 12 | 13 | public Collection Annotations { get; private set; } 14 | 15 | public string Documentation { get; set; } 16 | 17 | public string Name { get; set; } 18 | 19 | public ModelDescription TypeDescription { get; set; } 20 | } 21 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/ModelDescriptions/SimpleTypeModelDescription.cs: -------------------------------------------------------------------------------- 1 | namespace Server.Areas.HelpPage.ModelDescriptions 2 | { 3 | public class SimpleTypeModelDescription : ModelDescription 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Models/HelpPageApiModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | using System.Net.Http.Headers; 4 | using System.Web.Http.Description; 5 | using Server.Areas.HelpPage.ModelDescriptions; 6 | 7 | namespace Server.Areas.HelpPage.Models 8 | { 9 | /// 10 | /// The model that represents an API displayed on the help page. 11 | /// 12 | public class HelpPageApiModel 13 | { 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | public HelpPageApiModel() 18 | { 19 | UriParameters = new Collection(); 20 | SampleRequests = new Dictionary(); 21 | SampleResponses = new Dictionary(); 22 | ErrorMessages = new Collection(); 23 | } 24 | 25 | /// 26 | /// Gets or sets the that describes the API. 27 | /// 28 | public ApiDescription ApiDescription { get; set; } 29 | 30 | /// 31 | /// Gets or sets the collection that describes the URI parameters for the API. 32 | /// 33 | public Collection UriParameters { get; private set; } 34 | 35 | /// 36 | /// Gets or sets the documentation for the request. 37 | /// 38 | public string RequestDocumentation { get; set; } 39 | 40 | /// 41 | /// Gets or sets the that describes the request body. 42 | /// 43 | public ModelDescription RequestModelDescription { get; set; } 44 | 45 | /// 46 | /// Gets the request body parameter descriptions. 47 | /// 48 | public IList RequestBodyParameters 49 | { 50 | get 51 | { 52 | return GetParameterDescriptions(RequestModelDescription); 53 | } 54 | } 55 | 56 | /// 57 | /// Gets or sets the that describes the resource. 58 | /// 59 | public ModelDescription ResourceDescription { get; set; } 60 | 61 | /// 62 | /// Gets the resource property descriptions. 63 | /// 64 | public IList ResourceProperties 65 | { 66 | get 67 | { 68 | return GetParameterDescriptions(ResourceDescription); 69 | } 70 | } 71 | 72 | /// 73 | /// Gets the sample requests associated with the API. 74 | /// 75 | public IDictionary SampleRequests { get; private set; } 76 | 77 | /// 78 | /// Gets the sample responses associated with the API. 79 | /// 80 | public IDictionary SampleResponses { get; private set; } 81 | 82 | /// 83 | /// Gets the error messages associated with this model. 84 | /// 85 | public Collection ErrorMessages { get; private set; } 86 | 87 | private static IList GetParameterDescriptions(ModelDescription modelDescription) 88 | { 89 | ComplexTypeModelDescription complexTypeModelDescription = modelDescription as ComplexTypeModelDescription; 90 | if (complexTypeModelDescription != null) 91 | { 92 | return complexTypeModelDescription.Properties; 93 | } 94 | 95 | CollectionModelDescription collectionModelDescription = modelDescription as CollectionModelDescription; 96 | if (collectionModelDescription != null) 97 | { 98 | complexTypeModelDescription = collectionModelDescription.ElementDescription as ComplexTypeModelDescription; 99 | if (complexTypeModelDescription != null) 100 | { 101 | return complexTypeModelDescription.Properties; 102 | } 103 | } 104 | 105 | return null; 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/SampleGeneration/ImageSample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Server.Areas.HelpPage 4 | { 5 | /// 6 | /// This represents an image sample on the help page. There's a display template named ImageSample associated with this class. 7 | /// 8 | public class ImageSample 9 | { 10 | /// 11 | /// Initializes a new instance of the class. 12 | /// 13 | /// The URL of an image. 14 | public ImageSample(string src) 15 | { 16 | if (src == null) 17 | { 18 | throw new ArgumentNullException("src"); 19 | } 20 | Src = src; 21 | } 22 | 23 | public string Src { get; private set; } 24 | 25 | public override bool Equals(object obj) 26 | { 27 | ImageSample other = obj as ImageSample; 28 | return other != null && Src == other.Src; 29 | } 30 | 31 | public override int GetHashCode() 32 | { 33 | return Src.GetHashCode(); 34 | } 35 | 36 | public override string ToString() 37 | { 38 | return Src; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/SampleGeneration/InvalidSample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Server.Areas.HelpPage 4 | { 5 | /// 6 | /// This represents an invalid sample on the help page. There's a display template named InvalidSample associated with this class. 7 | /// 8 | public class InvalidSample 9 | { 10 | public InvalidSample(string errorMessage) 11 | { 12 | if (errorMessage == null) 13 | { 14 | throw new ArgumentNullException("errorMessage"); 15 | } 16 | ErrorMessage = errorMessage; 17 | } 18 | 19 | public string ErrorMessage { get; private set; } 20 | 21 | public override bool Equals(object obj) 22 | { 23 | InvalidSample other = obj as InvalidSample; 24 | return other != null && ErrorMessage == other.ErrorMessage; 25 | } 26 | 27 | public override int GetHashCode() 28 | { 29 | return ErrorMessage.GetHashCode(); 30 | } 31 | 32 | public override string ToString() 33 | { 34 | return ErrorMessage; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/SampleGeneration/SampleDirection.cs: -------------------------------------------------------------------------------- 1 | namespace Server.Areas.HelpPage 2 | { 3 | /// 4 | /// Indicates whether the sample is used for request or response 5 | /// 6 | public enum SampleDirection 7 | { 8 | Request = 0, 9 | Response 10 | } 11 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/SampleGeneration/TextSample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Server.Areas.HelpPage 4 | { 5 | /// 6 | /// This represents a preformatted text sample on the help page. There's a display template named TextSample associated with this class. 7 | /// 8 | public class TextSample 9 | { 10 | public TextSample(string text) 11 | { 12 | if (text == null) 13 | { 14 | throw new ArgumentNullException("text"); 15 | } 16 | Text = text; 17 | } 18 | 19 | public string Text { get; private set; } 20 | 21 | public override bool Equals(object obj) 22 | { 23 | TextSample other = obj as TextSample; 24 | return other != null && Text == other.Text; 25 | } 26 | 27 | public override int GetHashCode() 28 | { 29 | return Text.GetHashCode(); 30 | } 31 | 32 | public override string ToString() 33 | { 34 | return Text; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Views/Help/Api.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web.Http 2 | @using Server.Areas.HelpPage.Models 3 | @model HelpPageApiModel 4 | 5 | @{ 6 | var description = Model.ApiDescription; 7 | ViewBag.Title = description.HttpMethod.Method + " " + description.RelativePath; 8 | } 9 | 10 | 11 |
12 | 19 |
20 | @Html.DisplayForModel() 21 |
22 |
23 | -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Views/Help/DisplayTemplates/ApiGroup.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web.Http 2 | @using System.Web.Http.Controllers 3 | @using System.Web.Http.Description 4 | @using Server.Areas.HelpPage 5 | @using Server.Areas.HelpPage.Models 6 | @model IGrouping 7 | 8 | @{ 9 | var controllerDocumentation = ViewBag.DocumentationProvider != null ? 10 | ViewBag.DocumentationProvider.GetDocumentation(Model.Key) : 11 | null; 12 | } 13 | 14 |

@Model.Key.ControllerName

15 | @if (!String.IsNullOrEmpty(controllerDocumentation)) 16 | { 17 |

@controllerDocumentation

18 | } 19 | 20 | 21 | 22 | 23 | 24 | @foreach (var api in Model) 25 | { 26 | 27 | 28 | 38 | 39 | } 40 | 41 |
APIDescription
@api.HttpMethod.Method @api.RelativePath 29 | @if (api.Documentation != null) 30 | { 31 |

@api.Documentation

32 | } 33 | else 34 | { 35 |

No documentation available.

36 | } 37 |
-------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Views/Help/DisplayTemplates/CollectionModelDescription.cshtml: -------------------------------------------------------------------------------- 1 | @using Server.Areas.HelpPage.ModelDescriptions 2 | @model CollectionModelDescription 3 | @if (Model.ElementDescription is ComplexTypeModelDescription) 4 | { 5 | @Html.DisplayFor(m => m.ElementDescription) 6 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Views/Help/DisplayTemplates/ComplexTypeModelDescription.cshtml: -------------------------------------------------------------------------------- 1 | @using Server.Areas.HelpPage.ModelDescriptions 2 | @model ComplexTypeModelDescription 3 | @Html.DisplayFor(m => m.Properties, "Parameters") -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Views/Help/DisplayTemplates/DictionaryModelDescription.cshtml: -------------------------------------------------------------------------------- 1 | @using Server.Areas.HelpPage.ModelDescriptions 2 | @model DictionaryModelDescription 3 | Dictionary of @Html.DisplayFor(m => Model.KeyModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.KeyModelDescription }) [key] 4 | and @Html.DisplayFor(m => Model.ValueModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.ValueModelDescription }) [value] -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Views/Help/DisplayTemplates/EnumTypeModelDescription.cshtml: -------------------------------------------------------------------------------- 1 | @using Server.Areas.HelpPage.ModelDescriptions 2 | @model EnumTypeModelDescription 3 | 4 |

Possible enumeration values:

5 | 6 | 7 | 8 | 9 | 10 | 11 | @foreach (EnumValueDescription value in Model.Values) 12 | { 13 | 14 | 15 | 18 | 21 | 22 | } 23 | 24 |
NameValueDescription
@value.Name 16 |

@value.Value

17 |
19 |

@value.Documentation

20 |
-------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Views/Help/DisplayTemplates/HelpPageApiModel.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web.Http 2 | @using System.Web.Http.Description 3 | @using Server.Areas.HelpPage.Models 4 | @using Server.Areas.HelpPage.ModelDescriptions 5 | @model HelpPageApiModel 6 | 7 | @{ 8 | ApiDescription description = Model.ApiDescription; 9 | } 10 |

@description.HttpMethod.Method @description.RelativePath

11 |
12 |

@description.Documentation

13 | 14 |

Request Information

15 | 16 |

URI Parameters

17 | @Html.DisplayFor(m => m.UriParameters, "Parameters") 18 | 19 |

Body Parameters

20 | 21 |

@Model.RequestDocumentation

22 | 23 | @if (Model.RequestModelDescription != null) 24 | { 25 | @Html.DisplayFor(m => m.RequestModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.RequestModelDescription }) 26 | if (Model.RequestBodyParameters != null) 27 | { 28 | @Html.DisplayFor(m => m.RequestBodyParameters, "Parameters") 29 | } 30 | } 31 | else 32 | { 33 |

None.

34 | } 35 | 36 | @if (Model.SampleRequests.Count > 0) 37 | { 38 |

Request Formats

39 | @Html.DisplayFor(m => m.SampleRequests, "Samples") 40 | } 41 | 42 |

Response Information

43 | 44 |

Resource Description

45 | 46 |

@description.ResponseDescription.Documentation

47 | 48 | @if (Model.ResourceDescription != null) 49 | { 50 | @Html.DisplayFor(m => m.ResourceDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.ResourceDescription }) 51 | if (Model.ResourceProperties != null) 52 | { 53 | @Html.DisplayFor(m => m.ResourceProperties, "Parameters") 54 | } 55 | } 56 | else 57 | { 58 |

None.

59 | } 60 | 61 | @if (Model.SampleResponses.Count > 0) 62 | { 63 |

Response Formats

64 | @Html.DisplayFor(m => m.SampleResponses, "Samples") 65 | } 66 | 67 |
-------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Views/Help/DisplayTemplates/ImageSample.cshtml: -------------------------------------------------------------------------------- 1 | @using Server.Areas.HelpPage 2 | @model ImageSample 3 | 4 | -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Views/Help/DisplayTemplates/InvalidSample.cshtml: -------------------------------------------------------------------------------- 1 | @using Server.Areas.HelpPage 2 | @model InvalidSample 3 | 4 | @if (HttpContext.Current.IsDebuggingEnabled) 5 | { 6 |
7 |

@Model.ErrorMessage

8 |
9 | } 10 | else 11 | { 12 |

Sample not available.

13 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Views/Help/DisplayTemplates/KeyValuePairModelDescription.cshtml: -------------------------------------------------------------------------------- 1 | @using Server.Areas.HelpPage.ModelDescriptions 2 | @model KeyValuePairModelDescription 3 | Pair of @Html.DisplayFor(m => Model.KeyModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.KeyModelDescription }) [key] 4 | and @Html.DisplayFor(m => Model.ValueModelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = Model.ValueModelDescription }) [value] -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Views/Help/DisplayTemplates/ModelDescriptionLink.cshtml: -------------------------------------------------------------------------------- 1 | @using Server.Areas.HelpPage.ModelDescriptions 2 | @model Type 3 | @{ 4 | ModelDescription modelDescription = ViewBag.modelDescription; 5 | if (modelDescription is ComplexTypeModelDescription || modelDescription is EnumTypeModelDescription) 6 | { 7 | if (Model == typeof(Object)) 8 | { 9 | @:Object 10 | } 11 | else 12 | { 13 | @Html.ActionLink(modelDescription.Name, "ResourceModel", "Help", new { modelName = modelDescription.Name }, null) 14 | } 15 | } 16 | else if (modelDescription is CollectionModelDescription) 17 | { 18 | var collectionDescription = modelDescription as CollectionModelDescription; 19 | var elementDescription = collectionDescription.ElementDescription; 20 | @:Collection of @Html.DisplayFor(m => elementDescription.ModelType, "ModelDescriptionLink", new { modelDescription = elementDescription }) 21 | } 22 | else 23 | { 24 | @Html.DisplayFor(m => modelDescription) 25 | } 26 | } -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Views/Help/DisplayTemplates/Parameters.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Collections.Generic 2 | @using System.Collections.ObjectModel 3 | @using System.Web.Http.Description 4 | @using System.Threading 5 | @using Server.Areas.HelpPage.ModelDescriptions 6 | @model IList 7 | 8 | @if (Model.Count > 0) 9 | { 10 | 11 | 12 | 13 | 14 | 15 | @foreach (ParameterDescription parameter in Model) 16 | { 17 | ModelDescription modelDescription = parameter.TypeDescription; 18 | 19 | 20 | 23 | 26 | 39 | 40 | } 41 | 42 |
NameDescriptionTypeAdditional information
@parameter.Name 21 |

@parameter.Documentation

22 |
24 | @Html.DisplayFor(m => modelDescription.ModelType, "ModelDescriptionLink", new { modelDescription = modelDescription }) 25 | 27 | @if (parameter.Annotations.Count > 0) 28 | { 29 | foreach (var annotation in parameter.Annotations) 30 | { 31 |

@annotation.Documentation

32 | } 33 | } 34 | else 35 | { 36 |

None.

37 | } 38 |
43 | } 44 | else 45 | { 46 |

None.

47 | } 48 | 49 | -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Views/Help/DisplayTemplates/Samples.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Net.Http.Headers 2 | @model Dictionary 3 | 4 | @{ 5 | // Group the samples into a single tab if they are the same. 6 | Dictionary samples = Model.GroupBy(pair => pair.Value).ToDictionary( 7 | pair => String.Join(", ", pair.Select(m => m.Key.ToString()).ToArray()), 8 | pair => pair.Key); 9 | var mediaTypes = samples.Keys; 10 | } 11 |
12 | @foreach (var mediaType in mediaTypes) 13 | { 14 |

@mediaType

15 |
16 | Sample: 17 | @{ 18 | var sample = samples[mediaType]; 19 | if (sample == null) 20 | { 21 |

Sample not available.

22 | } 23 | else 24 | { 25 | @Html.DisplayFor(s => sample); 26 | } 27 | } 28 |
29 | } 30 |
-------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Views/Help/DisplayTemplates/SimpleTypeModelDescription.cshtml: -------------------------------------------------------------------------------- 1 | @using Server.Areas.HelpPage.ModelDescriptions 2 | @model SimpleTypeModelDescription 3 | @Model.Documentation -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Views/Help/DisplayTemplates/TextSample.cshtml: -------------------------------------------------------------------------------- 1 | @using Server.Areas.HelpPage 2 | @model TextSample 3 | 4 |
5 | @Model.Text
6 | 
-------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Views/Help/Index.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web.Http 2 | @using System.Web.Http.Controllers 3 | @using System.Web.Http.Description 4 | @using System.Collections.ObjectModel 5 | @using Server.Areas.HelpPage.Models 6 | @model Collection 7 | 8 | @{ 9 | ViewBag.Title = "ASP.NET Web API Help Page"; 10 | 11 | // Group APIs by controller 12 | ILookup apiGroups = Model.ToLookup(api => api.ActionDescriptor.ControllerDescriptor); 13 | } 14 | 15 | 16 |
17 |
18 |
19 |

@ViewBag.Title

20 |
21 |
22 |
23 |
24 | 32 |
33 | @foreach (var group in apiGroups) 34 | { 35 | @Html.DisplayFor(m => group, "ApiGroup") 36 | } 37 |
38 |
39 | -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Views/Help/ResourceModel.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web.Http 2 | @using Server.Areas.HelpPage.ModelDescriptions 3 | @model ModelDescription 4 | 5 | 6 |
7 | 14 |

@Model.Name

15 |

@Model.Documentation

16 |
17 | @Html.DisplayFor(m => Model) 18 |
19 |
20 | -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @ViewBag.Title 7 | @RenderSection("scripts", required: false) 8 | 9 | 10 | @RenderBody() 11 | 12 | -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Views/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /WebSite/Areas/HelpPage/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | // Change the Layout path below to blend the look and feel of the help page with your existing web pages 3 | Layout = "~/Views/Shared/_Layout.cshtml"; 4 | } -------------------------------------------------------------------------------- /WebSite/Content/Site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Set padding to keep content from hitting the edges */ 7 | .body-content { 8 | padding-left: 15px; 9 | padding-right: 15px; 10 | } 11 | 12 | /* Set width on the form input elements since they're 100% wide by default */ 13 | input, 14 | select, 15 | textarea { 16 | max-width: 280px; 17 | } 18 | -------------------------------------------------------------------------------- /WebSite/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | 3 | namespace Server.Controllers 4 | { 5 | public class HomeController : Controller 6 | { 7 | public ActionResult Index() 8 | { 9 | ViewBag.Title = "Home Page"; 10 | 11 | return View(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /WebSite/Controllers/InfoController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.ApplicationInsights; 2 | using StackOverflowTagServer; 3 | using StackOverflowTagServer.DataStructures; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.Linq; 8 | using System.Web.Http; 9 | 10 | namespace Server.Controllers 11 | { 12 | public class InfoController : ApiController 13 | { 14 | [Route("api")] 15 | [Route("api/Info")] 16 | [HttpGet] 17 | public object Info() 18 | { 19 | var telemetry = new TelemetryClient(); 20 | try 21 | { 22 | //telemetry.TrackEvent("API-Tags-Get()"); 23 | var tagServer = WebApiApplication.TagServer.Value; 24 | return GetAPIInfo(tagServer); 25 | } 26 | catch (Exception ex) 27 | { 28 | Trace.Write(ex); 29 | telemetry.TrackException(ex); //, properties, measurements); 30 | return new { Error = ex.ToString().Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) }; 31 | } 32 | } 33 | 34 | private object GetAPIInfo(TagServer tagServer) 35 | { 36 | var urlRoot = "http://" + Request.RequestUri.Host + ":" + Request.RequestUri.Port + "/api/"; 37 | return new 38 | { 39 | SampleUrls = new 40 | { 41 | BasicQueries = new Dictionary 42 | { 43 | { urlRoot + "Tags/c%23", "c# questions (have to escape \"c#\" -> \"c%23\"), defaults to QueryType.ViewCount, pageSize = 50, skip = 0" }, 44 | { urlRoot + "Tags/c%23?Type=LastActivityDate&PageSize=25", "25 Most Recent c# questions" }, 45 | { urlRoot + "Tags/c%23?Type=Score&PageSize=1&Skip=71993", "Lowest scoring c# question" }, 46 | { urlRoot + "Tags/.net?Type=AnswerCount&PageSize=1", "The .NET question with the most answers" } 47 | }.ToArray(), 48 | AdvancedQueries = new Dictionary 49 | { 50 | { urlRoot + "Query/.net?operator=AND&otherTag=jquery", "AND, i.e. 'C# AND jQuery'" }, 51 | { urlRoot + "Query/.net?operator=OR&otherTag=jquery", "OR, i.e. 'C# OR jQuery'" }, 52 | { urlRoot + "Query/.net?operator=OR-NOT&otherTag=jquery", "OR NOT, i.e. 'C# OR NOT jQuery'" }, 53 | { urlRoot + "Query/.net?operator=OR-NOT&otherTag=jquery&useLinq=true", "OR NOT, i.e. 'C# OR NOT jQuery' BUT using LINQ" }, 54 | }.ToArray(), 55 | AdvancedQueryParameters = new Dictionary 56 | { 57 | { "OtherTag", "i.e. 'c# AND jQuery' (&otherTag=jquery)" }, 58 | { "Type", "Can be " + String.Join(", ", Enum.GetNames(typeof(QueryType))) }, 59 | { "Operator", "Can be 'AND', 'AND-NOT', 'OR', 'OR-NOT', 'NOT'" }, 60 | { "PageSize", "1 to 50" }, 61 | { "Skip", "0 to 'as many as you want!!'" }, 62 | { "UseLinq", "i.e. '&UseLinq=true' (will be slower than the default mode)" }, 63 | { "UseLeppieExclusions", "See " + urlRoot + "/Wildcards/LeppieExpandedTags for the full list" }, 64 | { "DebugMode", "i.e. '&DebugMode=true'" } 65 | }, 66 | //RelatedTagQueries = new Dictionary 67 | //{ 68 | //}.ToArray(), 69 | Questions = new Dictionary 70 | { 71 | { urlRoot + "Questions/472906", "Get an individual question (by Id)" }, 72 | }.ToArray(), 73 | Wildcards = new Dictionary 74 | { 75 | { urlRoot + "/Wildcards/?wildcards=*c%23*,*java*", "Show all the Tags that matches the given wildcards (comma seperated list, starts-with, end-with or contains only)" }, 76 | { urlRoot + "/Wildcards/?wildcards=*c%23,c%23*,.net", "Show all the Tags that matches the given wildcards (comma seperated list, starts-with, end-with or contains only)" }, 77 | { urlRoot + "/Wildcards/?useLeppieWildcards=true", 78 | "Show all the Tags that matches the exclusion list from Leppie (see " + 79 | urlRoot + "Wildcards/LeppieExpandedTags and " + urlRoot + "Wildcards/LeppieWildcards for the full list)" 80 | }, 81 | }.ToArray(), 82 | }, 83 | SetupMessages = Logger.Messages, 84 | Top50Tags = tagServer.AllTags 85 | .Take(50), 86 | Bottom50Tags = tagServer.AllTags 87 | .OrderBy(t => t.Value) 88 | .Take(50) 89 | }; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /WebSite/Controllers/QuestionsController.cs: -------------------------------------------------------------------------------- 1 | using Server.Infrastructure; 2 | using System.Linq; 3 | using System.Web.Http; 4 | 5 | namespace Server.Controllers 6 | { 7 | [ActionWebApiFilter] 8 | public class QuestionsController : ApiController 9 | { 10 | [Route("api/Questions/{id}")] 11 | [HttpGet] 12 | public Shared.Question Get(int id) 13 | { 14 | return WebApiApplication.TagServer.Value.Questions.Where(qu => qu.Id == id).FirstOrDefault(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /WebSite/Controllers/TagsController.cs: -------------------------------------------------------------------------------- 1 | using Server.Infrastructure; 2 | using StackOverflowTagServer.DataStructures; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Web; 6 | using System.Web.Http; 7 | 8 | namespace Server.Controllers 9 | { 10 | [ActionWebApiFilter] 11 | public class TagsController : ApiController 12 | { 13 | [Route("api/Tags/{tag}")] 14 | [HttpGet] 15 | public object Get(string tag) 16 | { 17 | // Couldn't get Web API to play nice with "c%23" ("c#") and query parameters?!? 18 | var queryStringPairs = HttpContext.Current.Request.QueryString.ToPairs(); 19 | var type = QueryStringProcessor.GetEnum(queryStringPairs, "type", QueryType.ViewCount); 20 | var pageSize = QueryStringProcessor.GetInt(queryStringPairs, "pageSize", 50); 21 | var skip = QueryStringProcessor.GetInt(queryStringPairs, "skip", 0); 22 | 23 | // TODO add a "Descending" option 24 | 25 | var timer = Stopwatch.StartNew(); 26 | var results = WebApiApplication.TagServer.Value.Query(type, tag, pageSize, skip); 27 | timer.Stop(); 28 | 29 | return new 30 | { 31 | Statistics = new { 32 | Elapsed = timer.Elapsed, 33 | ElapsedMilliseconds = timer.Elapsed.TotalMilliseconds.ToString("N2"), 34 | Count = results.Count, 35 | TotalQuestionsForTag = WebApiApplication.TagServer.Value.TotalCount(type, tag) 36 | }, 37 | DEBUGGING = new 38 | { 39 | Tag = tag, 40 | QueryType = type.ToString(), 41 | PageSize = pageSize, 42 | Skip = skip, 43 | HttpContext.Current.Request.Path, 44 | HttpContext.Current.Request.RawUrl, 45 | QueryString = HttpContext.Current.Request.QueryString 46 | .ToPairs() 47 | .ToDictionary(p => p.Key, p => p.Value) 48 | }, 49 | Results = results, 50 | }; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /WebSite/Controllers/WildcardController.cs: -------------------------------------------------------------------------------- 1 | using Server.Infrastructure; 2 | using StackOverflowTagServer; 3 | using StackOverflowTagServer.DataStructures; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.Linq; 8 | using System.Web; 9 | using System.Web.Http; 10 | 11 | namespace Server.Controllers 12 | { 13 | public class WildcardController : ApiController 14 | { 15 | [Route("api/Wildcards/")] 16 | [HttpGet] 17 | public object Expand() 18 | { 19 | var queryStringPairs = HttpContext.Current.Request.QueryString.ToPairs(); 20 | var wildcards = QueryStringProcessor.GetString(queryStringPairs, "wildcards", ""); 21 | var useLeppieWildcards = QueryStringProcessor.GetBool(queryStringPairs, "useLeppieWildcards", false); 22 | 23 | var timer = Stopwatch.StartNew(); 24 | 25 | var wildcardExpansionTimer = Stopwatch.StartNew(); 26 | var initialWildcards = useLeppieWildcards ? WebApiApplication.LeppieWildcards.Value : wildcards.Split(',').ToList(); 27 | var allTags = WebApiApplication.TagServer.Value.AllTags; 28 | var nGrams = WebApiApplication.NGrams.Value; 29 | var expandedTags = WildcardProcessor.ExpandTagsNGrams(allTags, initialWildcards, nGrams); 30 | wildcardExpansionTimer.Stop(); 31 | 32 | //var bitMapTimer = Stopwatch.StartNew(); 33 | //var bitMapIndex = WebApiApplication.TagServer.Value.CreateBitMapIndexForExcludedTags(expandedTags, QueryType.Score); 34 | //bitMapTimer.Stop(); 35 | 36 | timer.Stop(); 37 | 38 | return new 39 | { 40 | TotalElapsedMilliseconds = timer.Elapsed.TotalMilliseconds.ToString("N2") + " ms", 41 | WildcardExpansionMilliseconds = wildcardExpansionTimer.Elapsed.TotalMilliseconds.ToString("N2") + " ms", 42 | //BitMapCreationMilliseconds = bitMapTimer.Elapsed.TotalMilliseconds.ToString("N2") + " ms", 43 | //QuestionsIncludingExpandedTags = ((ulong)WebApiApplication.TagServer.Value.Questions.Count - bitMapIndex.GetCardinality()).ToString("N0"), 44 | InitialWildcards = useLeppieWildcards ? "USING Leppie's Wildcards, list to big to print!!!" : String.Join(" - ", initialWildcards), 45 | ExpandedTagsCount = expandedTags.Count.ToString("N0"), 46 | ExpandedWildcardCount = initialWildcards.Where(w => w.Contains('*')) 47 | .ToDictionary(w => w, w => WildcardProcessor.ExpandTagsNGrams(allTags, new List(new[] { w }), nGrams).Count) 48 | .OrderByDescending(g => g.Value) 49 | .ThenBy(g => g.Key) 50 | .ToDictionary(g => g.Key, g => g.Value), 51 | ExpandedWildcard = initialWildcards.Where(w => w.Contains('*')) 52 | .ToDictionary(w => w, w => WildcardProcessor.ExpandTagsNGrams(allTags, new List(new[] { w }), nGrams)) 53 | .OrderBy(g => g.Key) 54 | .ToDictionary(g => g.Key, g => g.Value), 55 | }; 56 | } 57 | 58 | [Route("api/Wildcards/LeppieWildcards")] 59 | [HttpGet] 60 | public object LeppieWildcards() 61 | { 62 | var justWildcards = WebApiApplication.LeppieWildcards.Value 63 | .Where(w => w.Contains("*")) 64 | .OrderBy(t => t) 65 | .ToList(); 66 | return new 67 | { 68 | Count = WebApiApplication.LeppieWildcards.Value.Count, 69 | JustWildcardsCount = justWildcards.Count, 70 | JustWildcards = justWildcards, 71 | FullList = WebApiApplication.LeppieWildcards.Value.OrderBy(t => t) 72 | }; 73 | } 74 | 75 | [Route("api/Wildcards/LeppieExpandedTags")] 76 | [HttpGet] 77 | public object LeppieExpandedWildcards() 78 | { 79 | var allTags = WebApiApplication.TagServer.Value.AllTags; 80 | var leppieWildcards = WebApiApplication.LeppieWildcards.Value; 81 | var nGrams = WebApiApplication.NGrams.Value; 82 | var timer = Stopwatch.StartNew(); 83 | var expandedWildcards = WildcardProcessor.ExpandTagsNGrams(allTags, leppieWildcards, nGrams) 84 | .OrderBy(t => t) 85 | .ToList(); 86 | timer.Stop(); 87 | 88 | return new 89 | { 90 | ElapsedMilliseconds = timer.Elapsed.TotalMilliseconds.ToString("N2"), 91 | CountBeforeExpansion = leppieWildcards.Count, 92 | ExpandedCount = expandedWildcards.Count, 93 | ExpandedWildcards = expandedWildcards 94 | }; 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /WebSite/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="Server.WebApiApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /WebSite/Infrastructure/ActionWebApiFilter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Threading; 7 | using System.Web; 8 | using System.Web.Http.Controllers; 9 | using System.Web.Http.Filters; 10 | 11 | namespace Server.Infrastructure 12 | { 13 | public class ActionWebApiFilter : ActionFilterAttribute 14 | { 15 | ThreadLocal Timer = new ThreadLocal(() => Stopwatch.StartNew()); 16 | 17 | public override void OnActionExecuting(HttpActionContext actionContext) 18 | { 19 | // pre-processing 20 | //Trace.WriteLine("Starting Request: " + actionContext.Request.RequestUri.ToString()); 21 | Timer.Value.Restart(); 22 | } 23 | 24 | public async override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) 25 | { 26 | var now = DateTime.Now.ToString("yyyy_MM_dd@HH_mm_ss"); 27 | var fileName = ""; 28 | if (Timer.IsValueCreated) 29 | fileName = String.Format("Response-{0}ms-{1}-{2}.json", Timer.Value.ElapsedMilliseconds, now, Guid.NewGuid()); 30 | else 31 | fileName = String.Format("Response-{0}-{1}.json", now, Guid.NewGuid()); 32 | var dataFolder = HttpContext.Current.Server.MapPath("~/Data"); 33 | var responseFolder = Path.Combine(dataFolder, "Responses"); 34 | if (Directory.Exists(responseFolder) == false) 35 | Directory.CreateDirectory(responseFolder); 36 | var response = ""; 37 | try 38 | { 39 | Trace.WriteLine(String.Format("Request: {0} -> {1} {2}", 40 | actionExecutedContext.Request.RequestUri.ToString(), 41 | (int)actionExecutedContext.Response.StatusCode, 42 | actionExecutedContext.Response.StatusCode)); 43 | Trace.WriteLine(String.Format(" Took {0} ({1:N2} msecs)", Timer.Value.Elapsed, Timer.Value.Elapsed.TotalMilliseconds)); 44 | if (actionExecutedContext == null) 45 | { 46 | Trace.WriteLine("\"actionExecutedContext\" is null, unable to get the response"); 47 | return; 48 | } 49 | if (actionExecutedContext.Response == null) 50 | { 51 | Trace.WriteLine("\"actionExecutedContext.Response\" is null, unable to get the response"); 52 | return; 53 | } 54 | if (actionExecutedContext.Response.Content == null) 55 | { 56 | Trace.WriteLine("\"actionExecutedContext.Response.Content\" is null, unable to get the response"); 57 | return; 58 | } 59 | 60 | response = await actionExecutedContext.Response.Content.ReadAsStringAsync(); 61 | if (response != null) 62 | { 63 | var headers = actionExecutedContext.Response.Content.Headers; 64 | if (headers != null) 65 | { 66 | var headersAsText = String.Join(", ", headers.Select(h => String.Format("{0}: {1}", h.Key, String.Join(", ", h.Value)))); 67 | Trace.WriteLine(String.Format(" Headers: {0}", headersAsText)); 68 | } 69 | } 70 | 71 | Trace.WriteLine(string.Format(" Contents saved as {0}", fileName)); 72 | 73 | dynamic parsedJson = JsonConvert.DeserializeObject(response); 74 | var formattedJson = JsonConvert.SerializeObject(parsedJson, Formatting.Indented); 75 | File.WriteAllText(Path.Combine(responseFolder, fileName), formattedJson); 76 | } 77 | catch (Exception ex) 78 | { 79 | Trace.WriteLine(ex); 80 | File.WriteAllText(Path.Combine(responseFolder, fileName), response); 81 | } 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /WebSite/Infrastructure/BrowserJsonFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http.Formatting; 3 | using System.Net.Http.Headers; 4 | 5 | namespace Server.Infrastructure 6 | { 7 | //http://stackoverflow.com/questions/9847564/how-do-i-get-asp-net-web-api-to-return-json-instead-of-xml-using-chrome/20556625#20556625 8 | public class BrowserJsonFormatter : JsonMediaTypeFormatter 9 | { 10 | public BrowserJsonFormatter() 11 | { 12 | this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html")); 13 | } 14 | 15 | public override void SetDefaultContentHeaders(Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType) 16 | { 17 | base.SetDefaultContentHeaders(type, headers, mediaType); 18 | headers.ContentType = new MediaTypeHeaderValue("application/json"); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /WebSite/Infrastructure/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Specialized; 4 | using System.Linq; 5 | 6 | namespace Server.Infrastructure 7 | { 8 | public static class Extensions 9 | { 10 | public static IEnumerable> ToPairs(this NameValueCollection collection) 11 | { 12 | if (collection == null) 13 | { 14 | throw new ArgumentNullException("collection"); 15 | } 16 | 17 | return collection.Cast().Select(key => new KeyValuePair(key, collection[key])); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /WebSite/Infrastructure/QueryStringProcessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | 6 | namespace Server.Infrastructure 7 | { 8 | internal static class QueryStringProcessor 9 | { 10 | internal static T GetEnum(IEnumerable> parameters, string name, T defaultValue) 11 | { 12 | if (parameters.Any(p => p.Key.ToLowerInvariant() == name.ToLowerInvariant())) 13 | { 14 | var match = parameters.First(p => p.Key.ToLowerInvariant() == name.ToLowerInvariant()); 15 | return (T)Enum.Parse(typeof(T), match.Value, ignoreCase: true); 16 | } 17 | return defaultValue; 18 | } 19 | 20 | internal static int GetInt(IEnumerable> parameters, string name, int defaultValue) 21 | { 22 | if (parameters.Any(p => p.Key.ToLowerInvariant() == name.ToLowerInvariant())) 23 | { 24 | var match = parameters.First(p => p.Key.ToLowerInvariant() == name.ToLowerInvariant()); 25 | return int.Parse(match.Value, NumberStyles.Integer); 26 | } 27 | return defaultValue; 28 | } 29 | 30 | internal static string GetString(IEnumerable> parameters, string name, string defaultValue) 31 | { 32 | if (parameters.Any(p => p.Key.ToLowerInvariant() == name.ToLowerInvariant())) 33 | { 34 | var match = parameters.First(p => p.Key.ToLowerInvariant() == name.ToLowerInvariant()); 35 | return match.Value; 36 | } 37 | return defaultValue; 38 | } 39 | 40 | internal static bool GetBool(IEnumerable> parameters, string name, bool defaultValue) 41 | { 42 | if (parameters.Any(p => p.Key.ToLowerInvariant() == name.ToLowerInvariant())) 43 | { 44 | var match = parameters.First(p => p.Key.ToLowerInvariant() == name.ToLowerInvariant()); 45 | if (String.IsNullOrEmpty(match.Value)) 46 | return true; 47 | else if (String.Compare(match.Value, "true", ignoreCase: true) == 0) 48 | return true; 49 | else if (String.Compare(match.Value, "false", ignoreCase: true) == 0) 50 | return false; 51 | } 52 | return defaultValue; 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /WebSite/Project_Readme.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Your ASP.NET application 6 | 95 | 96 | 97 | 98 | 102 | 103 |
104 |
105 |

This application consists of:

106 |
    107 |
  • Help Page for documenting your Web APIs
  • 108 |
  • Theming using Bootstrap
  • 109 |
  • Authentication, if selected, shows how to register and sign in
  • 110 |
  • ASP.NET features managed using NuGet
  • 111 |
112 |
113 | 114 | 130 | 131 |
132 |

Deploy

133 | 138 |
139 | 140 |
141 |

Get help

142 | 146 |
147 |
148 | 149 | 150 | -------------------------------------------------------------------------------- /WebSite/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Server")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("CA, Inc.")] 12 | [assembly: AssemblyProduct("Server")] 13 | [assembly: AssemblyCopyright("Copyright © CA, Inc. 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("93f769eb-4229-4005-9903-46c916a4f270")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /WebSite/RunInIISExpress.bat: -------------------------------------------------------------------------------- 1 | "C:\Program Files\IIS Express\iisexpress.exe" /config:"C:\Users\warma11\Downloads\__GitHub__\StackOverflowTagServer\.vs\config\applicationhost.config" /site:"WebSite" /apppool:"Clr4IntegratedAppPool" -------------------------------------------------------------------------------- /WebSite/Scripts/_references.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwarren/StackOverflowTagServer/94da1a4876d7e77e94295c11a1be7e595f3ac8bf/WebSite/Scripts/_references.js -------------------------------------------------------------------------------- /WebSite/Scripts/respond.min.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ 16 | /*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */ 17 | window.matchMedia=window.matchMedia||(function(e,f){var c,a=e.documentElement,b=a.firstElementChild||a.firstChild,d=e.createElement("body"),g=e.createElement("div");g.id="mq-test-1";g.style.cssText="position:absolute;top:-100em";d.style.background="none";d.appendChild(g);return function(h){g.innerHTML='­';a.insertBefore(d,b);c=g.offsetWidth==42;a.removeChild(d);return{matches:c,media:h}}})(document); 18 | 19 | /*! Respond.js v1.2.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ 20 | (function(e){e.respond={};respond.update=function(){};respond.mediaQueriesSupported=e.matchMedia&&e.matchMedia("only all").matches;if(respond.mediaQueriesSupported){return}var w=e.document,s=w.documentElement,i=[],k=[],q=[],o={},h=30,f=w.getElementsByTagName("head")[0]||s,g=w.getElementsByTagName("base")[0],b=f.getElementsByTagName("link"),d=[],a=function(){var D=b,y=D.length,B=0,A,z,C,x;for(;B-1,minw:F.match(/\(min\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:F.match(/\(max\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}}j()},l,r,v=function(){var z,A=w.createElement("div"),x=w.body,y=false;A.style.cssText="position:absolute;font-size:1em;width:1em";if(!x){x=y=w.createElement("body");x.style.background="none"}x.appendChild(A);s.insertBefore(x,s.firstChild);z=A.offsetWidth;if(y){s.removeChild(x)}else{x.removeChild(A)}z=p=parseFloat(z);return z},p,j=function(I){var x="clientWidth",B=s[x],H=w.compatMode==="CSS1Compat"&&B||w.body[x]||B,D={},G=b[b.length-1],z=(new Date()).getTime();if(I&&l&&z-l-1?(p||v()):1)}if(!!J){J=parseFloat(J)*(J.indexOf(y)>-1?(p||v()):1)}if(!K.hasquery||(!A||!L)&&(A||H>=C)&&(L||H<=J)){if(!D[K.media]){D[K.media]=[]}D[K.media].push(k[K.rules])}}for(var E in q){if(q[E]&&q[E].parentNode===f){f.removeChild(q[E])}}for(var E in D){var M=w.createElement("style"),F=D[E].join("\n");M.type="text/css";M.media=E;f.insertBefore(M,G.nextSibling);if(M.styleSheet){M.styleSheet.cssText=F}else{M.appendChild(w.createTextNode(F))}q.push(M)}},n=function(x,z){var y=c();if(!y){return}y.open("GET",x,true);y.onreadystatechange=function(){if(y.readyState!=4||y.status!=200&&y.status!=304){return}z(y.responseText)};if(y.readyState==4){return}y.send(null)},c=(function(){var x=false;try{x=new XMLHttpRequest()}catch(y){x=new ActiveXObject("Microsoft.XMLHTTP")}return function(){return x}})();a();respond.update=a;function t(){j(true)}if(e.addEventListener){e.addEventListener("resize",t,false)}else{if(e.attachEvent){e.attachEvent("onresize",t)}}})(this); -------------------------------------------------------------------------------- /WebSite/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | 
2 |

ASP.NET

3 |

ASP.NET is a free web framework for building great Web sites and Web applications using HTML, CSS, and JavaScript.

4 |

Learn more »

5 |
6 |
7 |
8 |

Getting started

9 |

ASP.NET Web API is a framework that makes it easy to build HTTP services that reach 10 | a broad range of clients, including browsers and mobile devices. ASP.NET Web API 11 | is an ideal platform for building RESTful applications on the .NET Framework.

12 |

Learn more »

13 |
14 |
15 |

Get more libraries

16 |

NuGet is a free Visual Studio extension that makes it easy to add, remove, and update libraries and tools in Visual Studio projects.

17 |

Learn more »

18 |
19 |
20 |

Web Hosting

21 |

You can easily find a web hosting company that offers the right mix of features and price for your applications.

22 |

Learn more »

23 |
24 |
25 | -------------------------------------------------------------------------------- /WebSite/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = null; 3 | } 4 | 5 | 6 | 7 | 8 | 9 | Error 10 | 11 | 12 |
13 |

Error.

14 |

An error occurred while processing your request.

15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /WebSite/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | @ViewBag.Title 7 | @Styles.Render("~/Content/css") 8 | @Scripts.Render("~/bundles/modernizr") 9 | 10 | 17 | 27 | 28 | 29 | 30 | 49 |
50 | @RenderBody() 51 |
52 |
53 |

© @DateTime.Now.Year - My ASP.NET Application

54 |
55 |
56 | 57 | @Scripts.Render("~/bundles/jquery") 58 | @Scripts.Render("~/bundles/bootstrap") 59 | @RenderSection("scripts", required: false) 60 | 61 | 62 | -------------------------------------------------------------------------------- /WebSite/Views/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /WebSite/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } 4 | -------------------------------------------------------------------------------- /WebSite/Web.Debug.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /WebSite/Web.Release.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /WebSite/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /WebSite/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwarren/StackOverflowTagServer/94da1a4876d7e77e94295c11a1be7e595f3ac8bf/WebSite/favicon.ico -------------------------------------------------------------------------------- /WebSite/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwarren/StackOverflowTagServer/94da1a4876d7e77e94295c11a1be7e595f3ac8bf/WebSite/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /WebSite/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwarren/StackOverflowTagServer/94da1a4876d7e77e94295c11a1be7e595f3ac8bf/WebSite/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /WebSite/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwarren/StackOverflowTagServer/94da1a4876d7e77e94295c11a1be7e595f3ac8bf/WebSite/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /WebSite/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/repositories.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | --------------------------------------------------------------------------------