diff --git a/src/.gitignore b/src/submodules/OPMLCore.NET/.gitignore
similarity index 69%
rename from src/.gitignore
rename to src/submodules/OPMLCore.NET/.gitignore
index c779b69..6edac65 100644
--- a/src/.gitignore
+++ b/src/submodules/OPMLCore.NET/.gitignore
@@ -1,10 +1,21 @@
+
+# Created by https://www.gitignore.io/api/visualstudio,visualstudiocode
+
+### VisualStudioCode ###
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+.history
+
+### VisualStudio ###
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
-*.rsuser
*.suo
*.user
*.userosscache
@@ -13,9 +24,6 @@
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
-# Mono auto generated files
-mono_crash.*
-
# Build results
[Dd]ebug/
[Dd]ebugPublic/
@@ -23,62 +31,43 @@ mono_crash.*
[Rr]eleases/
x64/
x86/
-[Ww][Ii][Nn]32/
-[Aa][Rr][Mm]/
-[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
-[Ll]ogs/
-# Visual Studio 2015/2017 cache/options directory
+# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
-# Visual Studio 2017 auto generated files
-Generated\ Files/
-
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
-# NUnit
+# NUNIT
*.VisualState.xml
TestResult.xml
-nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
-# Benchmark Results
-BenchmarkDotNet.Artifacts/
-
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
+**/Properties/launchSettings.json
-# ASP.NET Scaffolding
-ScaffoldingReadMe.txt
-
-# StyleCop
-StyleCopReport.xml
-
-# Files built by Visual Studio
*_i.c
*_p.c
-*_h.h
+*_i.h
*.ilk
*.meta
*.obj
-*.iobj
*.pch
*.pdb
-*.ipdb
*.pgc
*.pgd
*.rsp
@@ -88,7 +77,6 @@ StyleCopReport.xml
*.tlh
*.tmp
*.tmp_proj
-*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
@@ -117,9 +105,6 @@ ipch/
*.vspx
*.sap
-# Visual Studio Trace Files
-*.e2e
-
# TFS 2012 Local Workspace
$tf/
@@ -131,21 +116,15 @@ _ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
+# JustCode is a .NET coding add-in
+.JustCode
+
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
-# AxoCover is a Code Coverage Tool
-.axoCover/*
-!.axoCover/settings.json
-
-# Coverlet is a free, cross platform Code Coverage Tool
-coverage*.json
-coverage*.xml
-coverage*.info
-
# Visual Studio code coverage results
*.coverage
*.coveragexml
@@ -181,9 +160,11 @@ publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
-# Note: Comment the next line if you want to checkin your web deploy settings,
-# but database connection strings (with potential passwords) will be unencrypted
-*.pubxml
+# TODO: Uncomment the next line to ignore your web deploy settings.
+# By default, sensitive information, such as encrypted password
+# should be stored in the .pubxml.user file.
+#*.pubxml
+*.pubxml.user
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
@@ -193,14 +174,12 @@ PublishScripts/
# NuGet Packages
*.nupkg
-# NuGet Symbol Packages
-*.snupkg
# The packages folder can be ignored because of Package Restore
-**/[Pp]ackages/*
+**/packages/*
# except build/, which is used as an MSBuild target.
-!**/[Pp]ackages/build/
+!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
-#!**/[Pp]ackages/repositories.config
+#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
@@ -218,15 +197,12 @@ AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
-*.appx
-*.appxbundle
-*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
-!?*.[Cc]ache/
+!*.[Cc]ache/
# Others
ClientBin/
@@ -239,10 +215,6 @@ ClientBin/
*.publishsettings
orleans.codegen.cs
-# Including strong name files can present a security risk
-# (https://github.com/github/gitignore/pull/2483#issue-259490424)
-#*.snk
-
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
@@ -257,8 +229,6 @@ _UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
-ServiceFabricBackup/
-*.rptproj.bak
# SQL Server files
*.mdf
@@ -269,10 +239,6 @@ ServiceFabricBackup/
*.rdl.data
*.bim.layout
*.bim_*.settings
-*.rptproj.rsuser
-*- [Bb]ackup.rdl
-*- [Bb]ackup ([0-9]).rdl
-*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
@@ -284,6 +250,9 @@ FakesAssemblies/
.ntvs_analysis.dat
node_modules/
+# Typescript v1 declaration files
+typings/
+
# Visual Studio 6 build log
*.plg
@@ -308,8 +277,12 @@ paket-files/
# FAKE - F# Make
.fake/
-# CodeRush personal settings
-.cr/personal
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
@@ -319,9 +292,6 @@ __pycache__/
# tools/**
# !tools/packages.config
-# Tabs Studio
-*.tss
-
# Telerik's JustMock configuration file
*.jmconfig
@@ -331,34 +301,12 @@ __pycache__/
*.odx.cs
*.xsd.cs
-# OpenCover UI analysis results
-OpenCover/
-
-# Azure Stream Analytics local run output
-ASALocalRun/
-
-# MSBuild Binary and Structured Log
-*.binlog
-
-# NVidia Nsight GPU debugger configuration file
-*.nvuser
-
-# MFractors (Xamarin productivity tool) working folder
-.mfractor/
-
-# Local History for Visual Studio
-.localhistory/
-
-# BeatPulse healthcheck temp database
-healthchecksdb
-
-# Backup folder for Package Reference Convert tool in Visual Studio 2017
-MigrationBackup/
+### VisualStudio Patch ###
+# By default, sensitive information, such as encrypted password
+# should be stored in the .pubxml.user file.
-# Ionide (cross platform F# VS Code tools) working folder
-.ionide/
-# Fody - auto-generated XML schema
-FodyWeavers.xsd
+# End of https://www.gitignore.io/api/visualstudio,visualstudiocode
-[Ss]ubmodules/*
\ No newline at end of file
+# Mac
+.DS_Store
diff --git a/src/submodules/OPMLCore.NET/.vscode/launch.json b/src/submodules/OPMLCore.NET/.vscode/launch.json
new file mode 100644
index 0000000..3451294
--- /dev/null
+++ b/src/submodules/OPMLCore.NET/.vscode/launch.json
@@ -0,0 +1,29 @@
+{
+ // Use IntelliSense to find out which attributes exist for C# debugging
+ // Use hover for the description of the existing attributes
+ // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
+ "version": "0.2.0",
+ "configurations": [
+
+ {
+ "name": ".NET Core Launch (console)",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ // If you have changed target frameworks, make sure to update the program path.
+ "program": "${workspaceFolder}/test.xunit/bin/Debug/netcoreapp2.0/test.xunit.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/test.xunit",
+ // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window
+ "console": "internalConsole",
+ "stopAtEntry": false,
+ "internalConsoleOptions": "openOnSessionStart"
+ },
+ {
+ "name": ".NET Core Attach",
+ "type": "coreclr",
+ "request": "attach",
+ "processId": "${command:pickProcess}"
+ }
+ ,]
+}
\ No newline at end of file
diff --git a/src/submodules/OPMLCore.NET/.vscode/tasks.json b/src/submodules/OPMLCore.NET/.vscode/tasks.json
new file mode 100644
index 0000000..3724a42
--- /dev/null
+++ b/src/submodules/OPMLCore.NET/.vscode/tasks.json
@@ -0,0 +1,15 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "build",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/test.xunit/test.xunit.csproj"
+ ],
+ "problemMatcher": "$msCompile"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/submodules/OPMLCore.NET/LICENSE b/src/submodules/OPMLCore.NET/LICENSE
new file mode 100644
index 0000000..0d1fa8d
--- /dev/null
+++ b/src/submodules/OPMLCore.NET/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 fnya
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/src/submodules/OPMLCore.NET/OPMLCoredotNET.sln b/src/submodules/OPMLCore.NET/OPMLCoredotNET.sln
new file mode 100644
index 0000000..3bdd659
--- /dev/null
+++ b/src/submodules/OPMLCore.NET/OPMLCoredotNET.sln
@@ -0,0 +1,53 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26124.0
+MinimumVisualStudioVersion = 15.0.26124.0
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0DA6DFA3-972E-4B42-AF2C-67D226DCDD89}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OPMLCore.NET", "src\OPMLCore.NET\OPMLCore.NET.csproj", "{74D51452-48A8-4F0F-82B5-F47826B4F1C9}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "test", "test\test.csproj", "{15E07BD7-75A1-46F8-8697-F146915FC23F}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {74D51452-48A8-4F0F-82B5-F47826B4F1C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {74D51452-48A8-4F0F-82B5-F47826B4F1C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {74D51452-48A8-4F0F-82B5-F47826B4F1C9}.Debug|x64.ActiveCfg = Debug|x64
+ {74D51452-48A8-4F0F-82B5-F47826B4F1C9}.Debug|x64.Build.0 = Debug|x64
+ {74D51452-48A8-4F0F-82B5-F47826B4F1C9}.Debug|x86.ActiveCfg = Debug|x86
+ {74D51452-48A8-4F0F-82B5-F47826B4F1C9}.Debug|x86.Build.0 = Debug|x86
+ {74D51452-48A8-4F0F-82B5-F47826B4F1C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {74D51452-48A8-4F0F-82B5-F47826B4F1C9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {74D51452-48A8-4F0F-82B5-F47826B4F1C9}.Release|x64.ActiveCfg = Release|x64
+ {74D51452-48A8-4F0F-82B5-F47826B4F1C9}.Release|x64.Build.0 = Release|x64
+ {74D51452-48A8-4F0F-82B5-F47826B4F1C9}.Release|x86.ActiveCfg = Release|x86
+ {74D51452-48A8-4F0F-82B5-F47826B4F1C9}.Release|x86.Build.0 = Release|x86
+ {15E07BD7-75A1-46F8-8697-F146915FC23F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {15E07BD7-75A1-46F8-8697-F146915FC23F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {15E07BD7-75A1-46F8-8697-F146915FC23F}.Debug|x64.ActiveCfg = Debug|x64
+ {15E07BD7-75A1-46F8-8697-F146915FC23F}.Debug|x64.Build.0 = Debug|x64
+ {15E07BD7-75A1-46F8-8697-F146915FC23F}.Debug|x86.ActiveCfg = Debug|x86
+ {15E07BD7-75A1-46F8-8697-F146915FC23F}.Debug|x86.Build.0 = Debug|x86
+ {15E07BD7-75A1-46F8-8697-F146915FC23F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {15E07BD7-75A1-46F8-8697-F146915FC23F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {15E07BD7-75A1-46F8-8697-F146915FC23F}.Release|x64.ActiveCfg = Release|x64
+ {15E07BD7-75A1-46F8-8697-F146915FC23F}.Release|x64.Build.0 = Release|x64
+ {15E07BD7-75A1-46F8-8697-F146915FC23F}.Release|x86.ActiveCfg = Release|x86
+ {15E07BD7-75A1-46F8-8697-F146915FC23F}.Release|x86.Build.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {74D51452-48A8-4F0F-82B5-F47826B4F1C9} = {0DA6DFA3-972E-4B42-AF2C-67D226DCDD89}
+ EndGlobalSection
+EndGlobal
diff --git a/src/submodules/OPMLCore.NET/README.md b/src/submodules/OPMLCore.NET/README.md
new file mode 100644
index 0000000..38b2afc
--- /dev/null
+++ b/src/submodules/OPMLCore.NET/README.md
@@ -0,0 +1,48 @@
+# OPMLCore.NET
+OPMLCore.NET is OPML Parser which easy to use for .NET Core Applications.
+
+It is written in C# and with Visual Studio Code.
+
+It supports .NET Core 2.0.
+
+# Setting
+
+You shoud add project refference to your project.
+
+The below is sample `.csproj` which is added project refference.
+
+```
+
+
+
+```
+And then `dotnet restore`.
+
+# Usage
+ How to use is below.
+
+```
+using OPMLCore.NET; //Add
+
+Opml opml = new Opml("opmlFilePath");
+
+foreach (Outline outline in opml.Body.Outlines)
+{
+ //Output outline node
+ Console.WriteLine(outline.Text);
+ Console.WriteLine(outline.XMLUrl);
+
+ //output child's output node
+ foreach (Outline childOutline in outline.Outlines)
+ {
+ Console.WriteLine(childOutline.Text);
+ Console.WriteLine(childOutline.XMLUrl);
+ }
+}
+
+```
+
+For more detail, show test code or source code.
+
+# License
+Lisense is MIT License.
diff --git a/src/submodules/OPMLCore.NET/src/OPMLCore.NET/Body.cs b/src/submodules/OPMLCore.NET/src/OPMLCore.NET/Body.cs
new file mode 100644
index 0000000..bbcce35
--- /dev/null
+++ b/src/submodules/OPMLCore.NET/src/OPMLCore.NET/Body.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Text;
+using System.Xml;
+using System.Collections.Generic;
+
+namespace OPMLCore.NET {
+ public class Body
+ {
+ ///
+ /// Outline list
+ ///
+ public List Outlines { get; set; } = new List();
+
+ ///
+ /// Constructor
+ ///
+ public Body()
+ {
+
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// element of Body
+ public Body(XmlElement element)
+ {
+ if (element.Name.Equals("body", StringComparison.CurrentCultureIgnoreCase))
+ {
+ foreach (XmlNode node in element.ChildNodes)
+ {
+ if (node.Name.Equals("outline", StringComparison.CurrentCultureIgnoreCase))
+ {
+ Outlines.Add(new Outline((XmlElement)node));
+ }
+ }
+ }
+ }
+
+ public override string ToString() {
+ StringBuilder buf = new StringBuilder();
+ buf.Append("\r\n");
+ foreach (Outline outline in Outlines)
+ {
+ buf.Append(outline.ToString());
+ }
+ buf.Append("\r\n");
+
+ return buf.ToString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/submodules/OPMLCore.NET/src/OPMLCore.NET/Head.cs b/src/submodules/OPMLCore.NET/src/OPMLCore.NET/Head.cs
new file mode 100644
index 0000000..7cb72f0
--- /dev/null
+++ b/src/submodules/OPMLCore.NET/src/OPMLCore.NET/Head.cs
@@ -0,0 +1,213 @@
+using System;
+using System.Text;
+using System.Xml;
+using System.Collections.Generic;
+
+namespace OPMLCore.NET {
+ public class Head
+ {
+ ///
+ /// title
+ ///
+ public string Title { get; set; }
+
+ ///
+ /// Created date
+ ///
+ public DateTime? DateCreated { get; set; } = null;
+
+ ///
+ /// Modified date
+ ///
+ public DateTime? DateModified { get; set; } = null;
+
+ ///
+ /// ownerName
+ ///
+ public string OwnerName { get; set; }
+
+ ///
+ /// ownerEmail
+ ///
+ public string OwnerEmail { get; set; }
+
+ ///
+ /// ownerId
+ ///
+ public string OwnerId { get; set;}
+
+ ///
+ /// docs
+ ///
+ public string Docs { get; set;}
+
+ ///
+ /// expansionState
+ ///
+ public List ExpansionState { get; set; } = new List();
+
+ ///
+ /// vertScrollState
+ ///
+ public string VertScrollState { get; set; }
+
+ ///
+ /// windowTop
+ ///
+ public string WindowTop { get; set; }
+
+ ///
+ /// windowLeft
+ ///
+ public string WindowLeft { get; set; }
+
+ ///
+ /// windowBottom
+ ///
+ public string WindowBottom { get; set; }
+
+ ///
+ /// windowRight
+ ///
+ public string WindowRight { get; set; }
+
+ ///
+ /// Constructor
+ ///
+ public Head()
+ {
+
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// element of Head
+ public Head(XmlElement element)
+ {
+ if (element.Name.Equals("head", StringComparison.CurrentCultureIgnoreCase))
+ {
+ foreach (XmlNode node in element.ChildNodes)
+ {
+ Title = GetStringValue(node, "title", Title);
+ DateCreated = GetDateTimeValue(node, "dateCreated", DateCreated);
+ DateModified = GetDateTimeValue(node, "dateModified", DateModified);
+ OwnerName = GetStringValue(node, "ownerName", OwnerName);
+ OwnerEmail = GetStringValue(node, "ownerEmail", OwnerEmail);
+ OwnerId = GetStringValue(node, "ownerId", OwnerId);
+ Docs = GetStringValue(node, "docs", Docs);
+ ExpansionState = GetExpansionState(node, "expansionState", ExpansionState);
+ VertScrollState = GetStringValue(node, "vertScrollState", VertScrollState);
+ WindowTop = GetStringValue(node, "windowTop", WindowTop);
+ WindowLeft = GetStringValue(node, "windowLeft", WindowLeft);
+ WindowBottom = GetStringValue(node, "windowBottom", WindowBottom);
+ WindowRight = GetStringValue(node, "windowRight", WindowRight);
+ }
+ }
+ }
+
+ private string GetStringValue(XmlNode node, string name, string value)
+ {
+ if (node.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase))
+ {
+ return node.InnerText;
+ } else if (!node.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase)) {
+ return value;
+ } else {
+ return string.Empty;
+ }
+ }
+
+ private DateTime? GetDateTimeValue(XmlNode node, string name, DateTime? value)
+ {
+ if (node.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase))
+ {
+ try {
+ return DateTime.Parse(node.InnerText);
+ } catch {
+ return null;
+ }
+ } else if (!node.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase)){
+ return value;
+ } else {
+ return null;
+ }
+ }
+
+ private List GetExpansionState(XmlNode node, string name, List value)
+ {
+ if (node.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase))
+ {
+ List list = new List();
+ var items = node.InnerText.Split(',');
+ foreach(var item in items)
+ {
+ list.Add(item.Trim());
+ }
+ return list;
+
+ } else if (!node.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase))
+ {
+ return value;
+ } else {
+ return new List();
+ }
+ }
+
+ public override string ToString()
+ {
+ StringBuilder buf = new StringBuilder();
+ buf.Append("\r\n");
+ buf.Append(GetNodeString("title", Title));
+ buf.Append(GetNodeString("dateCreated", DateCreated));
+ buf.Append(GetNodeString("dateModified", DateModified));
+ buf.Append(GetNodeString("ownerName", OwnerName));
+ buf.Append(GetNodeString("ownerEmail", OwnerEmail));
+ buf.Append(GetNodeString("ownerId", OwnerId));
+ buf.Append(GetNodeString("docs", Docs));
+ buf.Append(GetNodeString("expansionState", ExpansionState));
+ buf.Append(GetNodeString("vertScrollState", VertScrollState));
+ buf.Append(GetNodeString("windowTop", WindowTop));
+ buf.Append(GetNodeString("windowLeft", WindowLeft));
+ buf.Append(GetNodeString("windowBottom", WindowBottom));
+ buf.Append(GetNodeString("windowRight", WindowRight));
+ buf.Append("\r\n");
+ return buf.ToString();
+ }
+
+ private string GetNodeString(string name, string value)
+ {
+ if (string.IsNullOrEmpty(value))
+ {
+ return string.Empty;
+ } else {
+ return $"<{name}>{value}{name}>\r\n";
+ }
+ }
+ private string GetNodeString(string name, DateTime? value)
+ {
+ if (value == null)
+ {
+ return string.Empty;
+ } else {
+ return $"<{name}>{value?.ToString("R")}{name}>\r\n";
+ }
+ }
+
+ private string GetNodeString(string name, List value)
+ {
+ if (value.Count == 0) {
+ return string.Empty;
+ }
+
+ StringBuilder buf = new StringBuilder();
+ foreach (var item in value)
+ {
+ buf.Append(item);
+ buf.Append(",");
+ }
+
+ return $"<{name}>{buf.Remove(buf.Length - 1, 1).ToString()}{name}>\r\n";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/submodules/OPMLCore.NET/src/OPMLCore.NET/OPMLCore.NET.csproj b/src/submodules/OPMLCore.NET/src/OPMLCore.NET/OPMLCore.NET.csproj
new file mode 100644
index 0000000..9f5c4f4
--- /dev/null
+++ b/src/submodules/OPMLCore.NET/src/OPMLCore.NET/OPMLCore.NET.csproj
@@ -0,0 +1,7 @@
+
+
+
+ netstandard2.0
+
+
+
diff --git a/src/submodules/OPMLCore.NET/src/OPMLCore.NET/Opml.cs b/src/submodules/OPMLCore.NET/src/OPMLCore.NET/Opml.cs
new file mode 100644
index 0000000..d82be9a
--- /dev/null
+++ b/src/submodules/OPMLCore.NET/src/OPMLCore.NET/Opml.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Text;
+using System.Xml;
+
+namespace OPMLCore.NET {
+ public class Opml {
+ ///
+ /// Version of OPML
+ ///
+ public string Version { get; set;}
+
+ ///
+ /// Encoding of OPML
+ ///
+ public string Encoding { get; set;}
+
+ ///
+ /// Head of OPML
+ ///
+ public Head Head { get; set;} = new Head();
+
+ ///
+ /// Body of OPML
+ ///
+ public Body Body { get; set;} = new Body();
+
+ ///
+ /// Constructor
+ ///
+ public Opml()
+ {
+
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// Location of the OPML file
+ public Opml(string location)
+ {
+ XmlDocument doc = new XmlDocument();
+ doc.Load(location);
+ readOpmlNodes(doc);
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// XMLDocument of the OPML
+ public Opml(XmlDocument doc)
+ {
+ readOpmlNodes(doc);
+ }
+
+
+ private void readOpmlNodes(XmlDocument doc) {
+ foreach (XmlNode nodes in doc)
+ {
+ if (nodes.Name.Equals("opml", StringComparison.CurrentCultureIgnoreCase))
+ {
+ foreach (XmlNode childNode in nodes)
+ {
+
+ if (childNode.Name.Equals("head", StringComparison.CurrentCultureIgnoreCase))
+ {
+ Head = new Head((XmlElement) childNode);
+ }
+
+ if (childNode.Name.Equals("body", StringComparison.CurrentCultureIgnoreCase))
+ {
+ Body = new Body((XmlElement) childNode);
+ }
+ }
+ }
+ }
+ }
+
+ public override string ToString()
+ {
+ StringBuilder buf = new StringBuilder();
+ String ecoding = string.IsNullOrEmpty(Encoding)?"UTF-8":Encoding;
+ buf.Append($"\r\n");
+ String version = string.IsNullOrEmpty(Version)?"2.0":Version;
+ buf.Append($"\r\n");
+ buf.Append(Head.ToString());
+ buf.Append(Body.ToString());
+ buf.Append("");
+
+ return buf.ToString();
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/submodules/OPMLCore.NET/src/OPMLCore.NET/Outline.cs b/src/submodules/OPMLCore.NET/src/OPMLCore.NET/Outline.cs
new file mode 100644
index 0000000..99ceebe
--- /dev/null
+++ b/src/submodules/OPMLCore.NET/src/OPMLCore.NET/Outline.cs
@@ -0,0 +1,203 @@
+using System;
+using System.Text;
+using System.Xml;
+using System.Collections.Generic;
+
+namespace OPMLCore.NET {
+ public class Outline
+ {
+ ///
+ /// Text of the XML file (required)
+ ///
+ public string Text { get; set; }
+
+ ///
+ /// true / false
+ ///
+ public string IsComment { get; set; }
+
+ ///
+ /// true / false
+ ///
+ public string IsBreakpoint { get; set; }
+
+ ///
+ /// outline node was created
+ ///
+ public DateTime? Created { get; set; } = null;
+
+ ///
+ /// Categories
+ ///
+ public List Category { get; set; } = new List();
+
+ ///
+ /// Description
+ ///
+ public string Description { get; set; }
+
+ ///
+ /// HTML URL
+ ///
+ public string HTMLUrl { get; set; }
+
+ ///
+ /// Language
+ ///
+ public string Language { get; set; }
+
+ ///
+ /// Title
+ ///
+ public string Title { get; set; }
+
+ ///
+ /// Type (rss/atom)
+ ///
+ public string Type { get; set; }
+
+ ///
+ /// Version of RSS.
+ /// RSS1 for RSS1.0. RSS for 0.91, 0.92 or 2.0.
+ ///
+ public string Version { get; set; }
+
+ ///
+ /// URL of the XML file
+ ///
+ public string XMLUrl { get; set; }
+
+ ///
+ /// Outline list
+ ///
+ public List Outlines { get; set; } = new List();
+
+ ///
+ /// Constructor
+ ///
+ public Outline()
+ {
+
+ }
+
+
+ ///
+ /// Constructor
+ ///
+ /// element of Head
+ public Outline(XmlElement element)
+ {
+ Text = element.GetAttribute("text");
+ IsComment = element.GetAttribute("isComment");
+ IsBreakpoint = element.GetAttribute("isBreakpoint");
+ Created = GetDateTimeAttribute(element, "created");
+ Category = GetCategoriesAtrribute(element, "category");
+ Description = element.GetAttribute("description");
+ HTMLUrl = element.GetAttribute("htmlUrl");
+ Language = element.GetAttribute("language");
+ Title = element.GetAttribute("title");
+ Type = element.GetAttribute("type");
+ Version = element.GetAttribute("version");
+ XMLUrl = element.GetAttribute("xmlUrl");
+
+ if (element.HasChildNodes) {
+ foreach (XmlNode child in element.ChildNodes)
+ {
+ if (child.Name.Equals("outline", StringComparison.CurrentCultureIgnoreCase))
+ {
+ Outlines.Add(new Outline((XmlElement) child));
+ }
+ }
+ }
+ }
+
+ private DateTime? GetDateTimeAttribute(XmlElement element, string name)
+ {
+ string dt = element.GetAttribute(name);
+
+ try {
+ return DateTime.Parse(dt);
+ } catch {
+ return null;
+ }
+ }
+
+ private List GetCategoriesAtrribute(XmlElement element, string name)
+ {
+ List list = new List();
+ var items = element.GetAttribute(name).Split(',');
+ foreach(var item in items)
+ {
+ list.Add(item.Trim());
+ }
+ return list;
+ }
+
+ public override string ToString()
+ {
+ StringBuilder buf = new StringBuilder();
+ buf.Append(" 0)
+ {
+ buf.Append(">\r\n");
+ foreach (Outline outline in Outlines)
+ {
+ buf.Append(outline.ToString());
+ }
+ buf.Append("\r\n");
+ } else {
+ buf.Append(" />\r\n");
+ }
+ return buf.ToString();
+ }
+
+ private string GetAtrributeString(string name, string value)
+ {
+ if (string.IsNullOrEmpty(value))
+ {
+ return string.Empty;
+ } else {
+ return $" {name}=\"{value}\"";
+ }
+ }
+
+ private string GetAtrributeString(string name, DateTime? value)
+ {
+ if (value == null)
+ {
+ return string.Empty;
+ } else {
+ return $" {name}=\"{value?.ToString("R")}\"";
+ }
+ }
+
+ private string GetAtrributeString(string name, List value)
+ {
+ if (value.Count == 0) {
+ return string.Empty;
+ }
+
+ StringBuilder buf = new StringBuilder();
+ foreach (var item in value)
+ {
+ buf.Append(item);
+ buf.Append(",");
+ }
+
+ return $" {name}=\"{buf.Remove(buf.Length - 1, 1).ToString()}\"";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/submodules/OPMLCore.NET/test/UnitTest1.cs b/src/submodules/OPMLCore.NET/test/UnitTest1.cs
new file mode 100644
index 0000000..3dba2d7
--- /dev/null
+++ b/src/submodules/OPMLCore.NET/test/UnitTest1.cs
@@ -0,0 +1,251 @@
+using System;
+using System.Linq;
+using System.Text;
+using System.Xml;
+using Xunit;
+using OPMLCore.NET;
+
+namespace test
+{
+ public class UnitTest1
+ {
+ [Fact]
+ public void NormalTest()
+ {
+ StringBuilder xml = new StringBuilder();
+ xml.Append("");
+ xml.Append("");
+ xml.Append("");
+ xml.Append("mySubscriptions.opml");
+ xml.Append("Sat, 18 Jun 2005 12:11:52 GMT");
+ xml.Append("Tue, 02 Aug 2005 21:42:48 GMT");
+ xml.Append("fnya");
+ xml.Append("fnya@example.com");
+ xml.Append("http://news.com.com/");
+ xml.Append("http://news.com.com/");
+ xml.Append("1, 6, 13, 16, 18, 20");
+ xml.Append("1");
+ xml.Append("106");
+ xml.Append("106");
+ xml.Append("558");
+ xml.Append("479");
+ xml.Append("");
+ xml.Append("");
+ xml.Append("");
+ xml.Append("");
+ xml.Append("");
+
+ XmlDocument doc = new XmlDocument();
+ doc.LoadXml(xml.ToString());
+ Opml opml = new Opml(doc);
+
+ Assert.True(opml.Head.Title == "mySubscriptions.opml");
+ Assert.True(opml.Head.DateCreated == DateTime.Parse("Sat, 18 Jun 2005 12:11:52 GMT"));
+ Assert.True(opml.Head.DateModified == DateTime.Parse("Tue, 02 Aug 2005 21:42:48 GMT"));
+ Assert.True(opml.Head.OwnerName == "fnya");
+ Assert.True(opml.Head.OwnerEmail == "fnya@example.com");
+ Assert.True(opml.Head.OwnerId == "http://news.com.com/");
+ Assert.True(opml.Head.Docs == "http://news.com.com/");
+ Assert.True(opml.Head.ExpansionState.ToArray().SequenceEqual("1,6,13,16,18,20".Split(',')));
+ Assert.True(opml.Head.VertScrollState == "1");
+ Assert.True(opml.Head.WindowTop == "106");
+ Assert.True(opml.Head.WindowLeft == "106");
+ Assert.True(opml.Head.WindowBottom == "558");
+ Assert.True(opml.Head.WindowRight == "479");
+
+ foreach (var outline in opml.Body.Outlines)
+ {
+ Assert.True(outline.Text == "CNET News.com");
+ Assert.True(outline.IsComment == "true");
+ Assert.True(outline.IsBreakpoint == "true");
+ Assert.True(outline.Created == DateTime.Parse("Tue, 02 Aug 2005 21:42:48 GMT"));
+ Assert.True(outline.Category.ToArray().SequenceEqual("/Harvard/Berkman,/Politics".Split(',')));
+ Assert.True(outline.Description == "Tech news and business reports by CNET News.com.");
+ Assert.True(outline.HTMLUrl == "http://news.com.com/");
+ Assert.True(outline.Language == "unknown");
+ Assert.True(outline.Title == "CNET News.com");
+ Assert.True(outline.Type == "rss");
+ Assert.True(outline.Version == "RSS2");
+ Assert.True(outline.XMLUrl == "http://news.com.com/2547-1_3-0-5.xml");
+ }
+ }
+
+ [Fact]
+ public void ChildNodeTest()
+ {
+ StringBuilder xml = new StringBuilder();
+ xml.Append("");
+ xml.Append("");
+ xml.Append("");
+ xml.Append("mySubscriptions.opml");
+ xml.Append("");
+ xml.Append("");
+ xml.Append("");
+ xml.Append("");
+ xml.Append("");
+ xml.Append("");
+ xml.Append("");
+
+ XmlDocument doc = new XmlDocument();
+ doc.LoadXml(xml.ToString());
+ Opml opml = new Opml(doc);
+
+ foreach (var outline in opml.Body.Outlines)
+ {
+ foreach(var childOutline in outline.Outlines) {
+ Assert.True(childOutline.Text == "washingtonpost.com");
+ Assert.True(childOutline.HTMLUrl == "http://www.washingtonpost.com");
+ Assert.True(childOutline.XMLUrl == "http://www.washingtonpost.com/rss.xml");
+ }
+ }
+ }
+
+ [Fact]
+ public void CreateNormalOpmlTest()
+ {
+ Opml opml = new Opml();
+ opml.Encoding = "UTF-8";
+ opml.Version = "2.0";
+
+ Head head = new Head();
+ head.Title = "mySubscriptions.opml";
+ head.DateCreated = DateTime.Parse("Sat, 18 Jun 2005 12:11:52 GMT").ToUniversalTime();
+ head.DateModified = DateTime.Parse("Tue, 02 Aug 2005 21:42:48 GMT").ToUniversalTime();
+ head.OwnerName = "fnya";
+ head.OwnerEmail = "fnya@example.com";
+ head.OwnerId = "http://news.com.com/";
+ head.Docs = "http://news.com.com/";
+ head.ExpansionState.Add("1");
+ head.ExpansionState.Add("6");
+ head.ExpansionState.Add("13");
+ head.ExpansionState.Add("16");
+ head.ExpansionState.Add("18");
+ head.ExpansionState.Add("20");
+ head.VertScrollState = "1";
+ head.WindowTop = "106";
+ head.WindowLeft = "106";
+ head.WindowBottom = "558";
+ head.WindowRight = "479";
+ opml.Head = head;
+
+ Outline outline = new Outline();
+ outline.Text = "CNET News.com";
+ outline.IsComment = "true";
+ outline.IsBreakpoint = "true";
+ outline.Created = DateTime.Parse("Tue, 02 Aug 2005 21:42:48 GMT").ToUniversalTime();
+ outline.Category.Add("/Harvard/Berkman");
+ outline.Category.Add("/Politics");
+ outline.Description = "Tech news and business reports by CNET News.com.";
+ outline.HTMLUrl = "http://news.com.com/";
+ outline.Language = "unknown";
+ outline.Title = "CNET News.com";
+ outline.Type = "rss";
+ outline.Version = "RSS2";
+ outline.XMLUrl = "http://news.com.com/2547-1_3-0-5.xml";
+
+ Body body = new Body();
+ body.Outlines.Add(outline);
+ opml.Body = body;
+
+ StringBuilder xml = new StringBuilder();
+ xml.Append("\r\n");
+ xml.Append("\r\n");
+ xml.Append("\r\n");
+ xml.Append("mySubscriptions.opml\r\n");
+ xml.Append("Sat, 18 Jun 2005 12:11:52 GMT\r\n");
+ xml.Append("Tue, 02 Aug 2005 21:42:48 GMT\r\n");
+ xml.Append("fnya\r\n");
+ xml.Append("fnya@example.com\r\n");
+ xml.Append("http://news.com.com/\r\n");
+ xml.Append("http://news.com.com/\r\n");
+ xml.Append("1,6,13,16,18,20\r\n");
+ xml.Append("1\r\n");
+ xml.Append("106\r\n");
+ xml.Append("106\r\n");
+ xml.Append("558\r\n");
+ xml.Append("479\r\n");
+ xml.Append("\r\n");
+ xml.Append("\r\n");
+ xml.Append("\r\n");
+ xml.Append("\r\n");
+ xml.Append("");
+
+ Assert.True(opml.ToString() == xml.ToString());
+
+ }
+
+ [Fact]
+ public void CreateChildOpmlTest()
+ {
+ Opml opml = new Opml();
+ opml.Encoding = "UTF-8";
+ opml.Version = "2.0";
+
+ Head head = new Head();
+ head.Title = "mySubscriptions.opml";
+ opml.Head = head;
+
+ Outline outline = new Outline();
+ outline.Text = "IT";
+
+ Outline childOutline = new Outline();
+ childOutline.Text = "CNET News.com";
+ childOutline.HTMLUrl = "http://news.com.com/";
+ childOutline.XMLUrl = "http://news.com.com/2547-1_3-0-5.xml";
+
+ outline.Outlines.Add(childOutline);
+
+ Body body = new Body();
+ body.Outlines.Add(outline);
+ opml.Body = body;
+
+ StringBuilder xml = new StringBuilder();
+ xml.Append("\r\n");
+ xml.Append("\r\n");
+ xml.Append("\r\n");
+ xml.Append("mySubscriptions.opml\r\n");
+ xml.Append("\r\n");
+ xml.Append("\r\n");
+ xml.Append("\r\n");
+ xml.Append("\r\n");
+ xml.Append("\r\n");
+ xml.Append("\r\n");
+ xml.Append("");
+
+ Assert.True(opml.ToString() == xml.ToString());
+ }
+ }
+}
diff --git a/src/submodules/OPMLCore.NET/test/test.csproj b/src/submodules/OPMLCore.NET/test/test.csproj
new file mode 100644
index 0000000..37ebc6e
--- /dev/null
+++ b/src/submodules/OPMLCore.NET/test/test.csproj
@@ -0,0 +1,20 @@
+
+
+
+ netcoreapp2.0
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+