using System using System Collections using System Collections Generic

   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
  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
  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
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Net;
using System.Web;
using System.Xml;
namespace RSSBoard
{
class MainClass
{
public static void Main()
{
RssChannel rss = new RssChannel(@"http://www.yaplakal.com/news.xml");
}
}
/// <summary>
/// Канал сообщений.
/// </summary>
public class RssChannel
{
/// <summary>
/// Процедура предназначеная для записи логов.
/// </summary>
//private void WriteLog(string text)
//{
// StreamWriter fs = new StreamWriter(@"Log.txt", true, Encoding.Unicode);
// fs.WriteLine("(" + DateTime.Now.ToString("G") + ") " + text);
// fs.Close();
//}
/// <summary>
/// Версия RSS
/// </summary>
private Version rssVersion;
/// <summary>
/// Версия RSS
/// </summary>
public Version RssVersion { get { return rssVersion; } }
/// <summary>
/// Адрес канала
/// </summary>
readonly Uri rssBoardUrlAddress;
/// <summary>
/// Адрес канала
/// </summary>
public Uri RssBoardUrlAddress { get { return rssBoardUrlAddress; } }
#region Обязательные элементы
/// <summary>
/// Название канала, по которому люди будут ссылаться на сервис. Если у вас есть веб-сайт, который содержит ту же информацию, что и RSS-файл, элемент &lt;title&gt; канала должен совпадать с &lt;title&gt; заглавной веб-страницы.
///<example>
/// (Заголовки новостей GoUpstate.com)
/// </example>
/// </summary>
private string title;
/// <summary>
/// URL веб-сайта, связанного с каналом.
///<example>
/// (http://www.goupstate.com/)
/// </example>
/// </summary>
private Uri link;
/// <summary>
/// Фраза или предложение для описания канала.
/// <example>
/// (Последние новости GoUpstate.com, веб-сайта Spartanburg Herald-Journal.)
/// </example>
/// </summary>
private string description;
/// <summary>
/// Название канала, по которому люди будут ссылаться на сервис. Если у вас есть веб-сайт, который содержит ту же информацию, что и RSS-файл, элемент &lt;title&gt; канала должен совпадать с &lt;title&gt; заглавной веб-страницы.
///<example>
/// (Заголовки новостей GoUpstate.com)
/// </example>
/// </summary>
public string Title { get { return title; } }
/// <summary>
/// URL веб-сайта, связанного с каналом.
///<example>
/// (http://www.goupstate.com/)
/// </example>
/// </summary>
public Uri Link { get { return link; } }
/// <summary>
/// Фраза или предложение для описания канала.
/// <example>
/// (Последние новости GoUpstate.com, веб-сайта Spartanburg Herald-Journal.)
/// </example>
/// </summary>
public string Description { get { return description; } }
#endregion
#region Необязательные элементы
/// <summary>
/// Язык, на котором написан канал. Позволяет сборщикам, к примеру, объединить на одной странице все сайты на итальянском. Перечень допустимых значений элемента, предоставленный Netscape — http://www.rssboard.org/rss-language-codes. Также можно использовать значения, определенные W3C.
/// <example>
/// (en-us)
/// </example>
/// </summary>
private string language;
/// <summary>
/// Язык, на котором написан канал. Позволяет сборщикам, к примеру, объединить на одной странице все сайты на итальянском. Перечень допустимых значений элемента, предоставленный Netscape — http://www.rssboard.org/rss-language-codes. Также можно использовать значения, определенные W3C.
/// <example>
/// (en-us)
/// </example>
/// </summary>
public string Language { get { return language; } }
/// <summary>
/// Информация об авторском праве на канал.
/// <example>
/// (Copyright 2002, Spartanburg Herald-Journal)
/// </example>
/// </summary>
private string copyright;
/// <summary>
/// Информация об авторском праве на канал.
/// <example>
/// (Copyright 2002, Spartanburg Herald-Journal)
/// </example>
/// </summary>
public string Copyright { get { return copyright; } }
/// <summary>
/// Адрес электронной почты человека, ответственного за редакторский текст.
/// <example>
/// (geo@herald.com (Джордж Матеский))
/// </example>
/// </summary>
private string managingEditor;
/// <summary>
/// Адрес электронной почты человека, ответственного за редакторский текст.
/// <example>
/// (geo@herald.com (Джордж Матеский))
/// </example>
/// </summary>
public string ManagingEditor { get { return managingEditor; } }
/// <summary>
/// Адрес электронной почты человека, ответственного за технические аспекты канала.
/// <example>
/// (betty@herald.com (Бетти Гернси))
/// </example>
/// </summary>
private string webMaster;
/// <summary>
/// Адрес электронной почты человека, ответственного за технические аспекты канала.
/// <example>
/// (betty@herald.com (Бетти Гернси))
/// </example>
/// </summary>
public string WebMaster { get { return webMaster; } }
/// <summary>
/// Дата публикации текста в канале. Например, «New York Times» выходит ежедневно и дата публикации меняется каждые 24 часа, тогда и меняется pubDate канала. Дата и время в RSS соответствуют спецификации RFC 822, за исключением того, что год может представляться двумя или четырьмя цифрами (последнее предпочтительно).
/// <example>
/// (Sat, 07 Sep 2002 00:00:01 GMT)
/// </example>
/// </summary>
private DateTime pubDate;
/// <summary>
/// Дата публикации текста в канале. Например, «New York Times» выходит ежедневно и дата публикации меняется каждые 24 часа, тогда и меняется pubDate канала. Дата и время в RSS соответствуют спецификации RFC 822, за исключением того, что год может представляться двумя или четырьмя цифрами (последнее предпочтительно).
/// <example>
/// (Sat, 07 Sep 2002 00:00:01 GMT)
/// </example>
/// </summary>
public DateTime PubDate { get { return pubDate; } }
/// <summary>
/// Время Последнего изменения содержимого канала.
/// <example>
/// (Sat, 07 Sep 2002 09:42:31 GMT)
/// </example>
/// </summary>
private DateTime lastBuildDate;
/// <summary>
/// Время Последнего изменения содержимого канала.
/// <example>
/// (Sat, 07 Sep 2002 09:42:31 GMT)
/// </example>
/// </summary>
public DateTime LastBuildDate { get { return lastBuildDate; } }
/// <summary>
/// Указывает одну и более категорию, к которой относится канал. Следует тем же правилам, что и элемент category в &lt;item&gt;.
/// <example>
/// (&lt;category&gt;Newspapers&lt;/category&gt;)
/// </example>
/// </summary>
private List<RssCategory> category;
/// <summary>
/// Указывает одну и более категорию, к которой относится канал. Следует тем же правилам, что и элемент category в &lt;item&gt;.
/// <example>
/// (&lt;category&gt;Newspapers&lt;/category&gt;)
/// </example>
/// </summary>
public List<RssCategory> Category { get { return category; } }
/// <summary>
/// Строка определяет использованную для создания канала программу.
/// <example>
/// (MightyInHouse Content System v2.3)
/// </example>
/// </summary>
private string generator;
/// <summary>
/// Строка определяет использованную для создания канала программу.
/// <example>
/// (MightyInHouse Content System v2.3)
/// </example>
/// </summary>
public string Generator { get { return generator; } }
/// <summary>
/// URL, указывающий на документацию для использованного в файле RSS формата (например, на данную страницу). Это для людей, которые могут столкнуться через 25 лет с RSS-файлом на веб-сервере и захотят узнать, что же это такое.
/// <example>
/// (http://www.rssboard.org/rss-specification)
/// </example>
/// </summary>
private Uri docs;
/// <summary>
/// URL, указывающий на документацию для использованного в файле RSS формата (например, на данную страницу). Это для людей, которые могут столкнуться через 25 лет с RSS-файлом на веб-сервере и захотят узнать, что же это такое.
/// <example>
/// (http://www.rssboard.org/rss-specification)
/// </example>
/// </summary>
public Uri Docs { get { return docs; } }
/// <summary>
/// Указывает веб-сервис, поддерживающий интерфейс rssCloud.
/// <example>
/// (&lt;cloud domain="rpc.sys.com" port="80" path="/RPC2" registerProcedure="pingMe" protocol="soap"/&gt;)
/// </example>
/// </summary>
private ChanelCloud cloud;
/// <summary>
/// Указывает веб-сервис, поддерживающий интерфейс rssCloud.
/// <example>
/// (&lt;cloud domain="rpc.sys.com" port="80" path="/RPC2" registerProcedure="pingMe" protocol="soap"/&gt;)
/// </example>
/// </summary>
public ChanelCloud Cloud { get { return cloud; } }
/// <summary>
/// Время жизни; количество минут, на которые канал может кешироваться перед обновлением с ресурса.
/// <example>
/// (&lt;ttl&gt;60&lt;/ttl&gt;)
/// </example>
/// </summary>
private TimeSpan ttl;
/// <summary>
/// Время жизни; количество минут, на которые канал может кешироваться перед обновлением с ресурса.
/// <example>
/// (&lt;ttl&gt;60&lt;/ttl&gt;)
/// </example>
/// </summary>
public TimeSpan Ttl { get { return ttl; } }
/// <summary>
/// Изображение GIF, JPEG или PNG, которое может отображаться с каналом.
/// </summary>
private ChanelImage image;
/// <summary>
/// Изображение GIF, JPEG или PNG, которое может отображаться с каналом.
/// </summary>
public ChanelImage Image { get { return image; } }
/// <summary>
/// Рейтинг канала PICS.
/// </summary>
private string rating;
/// <summary>
/// Рейтинг канала PICS.
/// </summary>
public string Rating { get { return rating; } }
/// <summary>
/// Поле текстового ввода, которое может отображаться с каналом.
/// </summary>
private ChaneltextInput textInput;
/// <summary>
/// Поле текстового ввода, которое может отображаться с каналом.
/// </summary>
public ChaneltextInput TextInput { get { return textInput; } }
/// <summary>
/// Совет сборщикам каналов о том, какие часы можно пропустить.
/// </summary>
private string skipHours;
/// <summary>
/// Совет сборщикам каналов о том, какие часы можно пропустить.
/// </summary>
public string SkipHours { get { return skipHours; } }
/// <summary>
/// Совет сборщикам каналов о том, какие дни можно пропустить.
/// </summary>
private string skipDays;
/// <summary>
/// Совет сборщикам каналов о том, какие дни можно пропустить.
/// </summary>
public string SkipDays { get { return skipDays; } }
#endregion
/// <summary>
/// Сообщения канала.
/// </summary>
private List<RssItem> items;
/// <summary>
/// Сообщения канала.
/// </summary>
public IList<RssItem> Items { get { return items.AsReadOnly(); } }
public RssChannel(string UrlAddress):this(new Uri(UrlAddress))
{
}
public RssChannel(Uri UrlAddress)
{
rssBoardUrlAddress = UrlAddress;
Refresh();
}
public void Refresh()
{
try
{
XmlDocument xmlDoc;
WebClient webClient = new WebClient();
Stream rssStream = webClient.OpenRead(rssBoardUrlAddress);
XmlTextReader reader = new XmlTextReader(rssStream);
reader.Namespaces = false;
xmlDoc = new XmlDocument();
xmlDoc.Load(reader);
XmlNode rssNode = xmlDoc.SelectSingleNode("rss");
if (rssNode != null)
{
if (rssNode.Attributes["version"] != null) rssVersion = new Version(rssNode.Attributes["version"].Value);
else throw new Exception(@"Неуказана версия rss.");
XmlNodeList channelNodes = rssNode.ChildNodes;
if (channelNodes.Count > 0) LoadChannel(channelNodes[0]);
else throw new Exception(@"Нет rss каналов.");
if (channelNodes.Count > 1) new Exception(@"Количество rss каналов больше 1.");
}
else new Exception(@"Нет ни одного rss канала.");
}
catch (Exception e)
{
System.Windows.Forms.MessageBox.Show("Ошибка при загрузке rss из \"" + rssBoardUrlAddress + "\"\r\n" + e.Message + ".","Ошибка!",System.Windows.Forms.MessageBoxButtons.OK,System.Windows.Forms.MessageBoxIcon.Warning,System.Windows.Forms.MessageBoxDefaultButton.Button1);
return;
}
}
private void LoadChannel(XmlNode xmlNode)
{
items = new List<RssItem>();
category = new List<RssCategory>();
XmlNode selectedNode;
selectedNode = xmlNode.SelectSingleNode("title");
if (selectedNode != null) title = HttpUtility.HtmlDecode(selectedNode.InnerText);
selectedNode = xmlNode.SelectSingleNode("link");
if (selectedNode != null) link = new Uri(selectedNode.InnerText);
selectedNode = xmlNode.SelectSingleNode("description");
if (selectedNode != null) description = HttpUtility.HtmlDecode(selectedNode.InnerText);
selectedNode = xmlNode.SelectSingleNode("language");
if (selectedNode != null) language = selectedNode.InnerText;
selectedNode = xmlNode.SelectSingleNode("copyright");
if (selectedNode != null) copyright = selectedNode.InnerText;
selectedNode = xmlNode.SelectSingleNode("managingEditor");
if (selectedNode != null) managingEditor = selectedNode.InnerText;
selectedNode = xmlNode.SelectSingleNode("webMaster");
if (selectedNode != null) webMaster = selectedNode.InnerText;
selectedNode = xmlNode.SelectSingleNode("pubDate");
if (selectedNode != null) DateTime.TryParse(selectedNode.InnerText, out pubDate);
selectedNode = xmlNode.SelectSingleNode("lastBuildDate");
if (selectedNode != null) DateTime.TryParse(selectedNode.InnerText, out lastBuildDate);
XmlNodeList selectedNodes = xmlNode.SelectNodes("category");
if (selectedNode != null)
{
foreach (XmlNode selectednode in selectedNodes)
category.Add(new RssCategory(selectednode));
}
selectedNode = xmlNode.SelectSingleNode("generator");
if (selectedNode != null) generator = selectedNode.InnerText;
selectedNode = xmlNode.SelectSingleNode("docs");
if (selectedNode != null) docs = new Uri(selectedNode.InnerText);
selectedNode = xmlNode.SelectSingleNode("cloud");
if (selectedNode != null) cloud = new ChanelCloud(selectedNode);
selectedNode = xmlNode.SelectSingleNode("ttl");
if (selectedNode != null) TimeSpan.TryParse(selectedNode.InnerText, out ttl);
selectedNode = xmlNode.SelectSingleNode("image");
if (selectedNode != null) image = new ChanelImage(selectedNode);
selectedNode = xmlNode.SelectSingleNode("rating");
if (selectedNode != null) rating = selectedNode.InnerText;
selectedNode = xmlNode.SelectSingleNode("textInput");
if (selectedNode != null) textInput = new ChaneltextInput(selectedNode);
selectedNode = xmlNode.SelectSingleNode("skipHours");
if (selectedNode != null) skipHours = selectedNode.InnerText;
selectedNode = xmlNode.SelectSingleNode("skipDays");
if (selectedNode != null) skipDays = selectedNode.InnerText;
foreach (XmlNode xmlItem in xmlNode.SelectNodes("item"))
{
RssItem rssItem = new RssItem(xmlItem, this);
items.Add(rssItem);
}
if (title == null) new Exception("У канала не запонен элемент \"title\"");
if (link == null) new Exception("У канала не запонен элемент \"link\"");
if (description == null) new Exception("У канала не запонен элемент \"description\"");
}
/// <summary>
/// Изображение GIF, JPEG или PNG, которое может отображаться с каналом.
/// </summary>
public class ChanelImage
{
#region Обязательные элементы
/// <summary>
/// Описание изображения, которое будет использовано в атрибуте alt элемента HTML img, когда канал будет показан в гипертексте.
/// </summary>
private string title;
/// <summary>
/// URL сайта; изображение канала будет служить ссылкой на этот сайт. (Как правило, &lt;title&gt; и &lt;link&gt; изображения должны совпадать с &lt;title&gt; и &lt;link&gt; канала).
/// </summary>
private Uri link;
/// <summary>
/// URL изображения GIF, JPEG или PNG, представляющего канал.
/// </summary>
private Uri url;
/// <summary>
/// Описание изображения, которое будет использовано в атрибуте alt элемента HTML img, когда канал будет показан в гипертексте.
/// </summary>
public string Title { get { return title; } }
/// <summary>
/// URL сайта; изображение канала будет служить ссылкой на этот сайт. (Как правило, &lt;title&gt; и &lt;link&gt; изображения должны совпадать с &lt;title&gt; и &lt;link&gt; канала).
/// </summary>
public Uri Link { get { return link; } }
/// <summary>
/// URL изображения GIF, JPEG или PNG, представляющего канал.
/// </summary>
public Uri URL { get { return url; } }
#endregion
#region Необязательные элементы
/// <summary>
/// задающие ширину изображения в пикселях.
/// </summary>
private short width = 88;
/// <summary>
/// задающие высоту изображения в пикселях.
/// </summary>
private short height = 31;
/// <summary>
/// содержит текст, включаемый в атрибут title ссылки, сформированной вокруг изображения в HTML-отображении.
/// </summary>
private string description;
/// <summary>
/// задающие ширину изображения в пикселях.
/// </summary>
public short Width { get { return width; } }
/// <summary>
/// задающие высоту изображения в пикселях.
/// </summary>
public short Height { get { return height; } }
/// <summary>
/// содержит текст, включаемый в атрибут title ссылки, сформированной вокруг изображения в HTML-отображении.
/// </summary>
private string Description { get { return description; } }
#endregion
public ChanelImage(XmlNode xmlNode)
{
XmlNode selectedNode;
selectedNode = xmlNode.SelectSingleNode("title");
if (selectedNode != null) title = HttpUtility.HtmlDecode(selectedNode.InnerText);
selectedNode = xmlNode.SelectSingleNode("description");
if (selectedNode != null) description = HttpUtility.HtmlDecode(selectedNode.InnerText);
selectedNode = xmlNode.SelectSingleNode("link");
if (selectedNode != null) link = new Uri(selectedNode.InnerText);
selectedNode = xmlNode.SelectSingleNode("url");
if (selectedNode != null) url = new Uri(selectedNode.InnerText);
selectedNode = xmlNode.SelectSingleNode("width");
if (selectedNode != null) short.TryParse(selectedNode.InnerText, out width);
selectedNode = xmlNode.SelectSingleNode("height");
if (selectedNode != null) short.TryParse(selectedNode.InnerText, out height);
if (title == null) throw new Exception("У значка канала не запонен элемент \"title\"");
if (link == null) throw new Exception("У значка канала не запонен элемент \"link\"");
if (url == null) throw new Exception("У значка канала не запонен элемент \"url\"");
if (width > 144) throw new Exception("У значка канала значение элемента \"width\" больше 144");
if (height > 400) throw new Exception("У значка канала не запонен элемент \"height\" 400");
}
public static bool operator ==(ChanelImage i1, ChanelImage i2)
{
if (!(i1 is ChanelImage) && !(i2 is ChanelImage)) return true;
else if (!(i1 is ChanelImage) || !(i2 is ChanelImage)) return false;
else if (i1.description == i2.description &&
i1.height == i2.height &&
i1.link == i2.link &&
i1.title == i2.title &&
i1.url == i2.url &&
i1.width == i2.width) return true;
else return false;
}
public static bool operator !=(ChanelImage i1, ChanelImage i2)
{
return !(i1 == i2);
}
public override bool Equals(object obj)
{
ChanelImage i1 = obj as ChanelImage;
if (i1 == null) return false;
if (i1.description == this.description &&
i1.height == this.height &&
i1.link == this.link &&
i1.title == this.title &&
i1.url == this.url &&
i1.width == this.width) return true;
else return false;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
/// <summary>
/// &lt;cloud&gt; указывает веб-сервис, поддерживающий интерфейс rssCloud, который может реализовываться через HTTP-POST, XML-RPC или SOAP 1.1 и реализует подписку на обновления канала.
/// </summary>
public class ChanelCloud
{
private string domain;
private string port;
private string path;
private string registerProcedure;
private string protocol;
public string Domain { get { return domain; } }
public string Port { get { return Port; } }
public string Path { get { return Path; } }
public string RegisterProcedure { get { return RegisterProcedure; } }
public string Protocol { get { return Protocol; } }
public ChanelCloud(XmlNode xmlNode)
{
if (xmlNode.Attributes["domain"] != null) domain = xmlNode.Attributes["domain"].Value;
if (xmlNode.Attributes["port"] != null) port = xmlNode.Attributes["port"].Value;
if (xmlNode.Attributes["path"] != null) path = xmlNode.Attributes["path"].Value;
if (xmlNode.Attributes["registerProcedure"] != null) registerProcedure = xmlNode.Attributes["registerProcedure"].Value;
if (xmlNode.Attributes["protocol"] != null) protocol = xmlNode.Attributes["protocol"].Value;
}
public static bool operator ==(ChanelCloud cc1, ChanelCloud cc2)
{
if (!(cc1 is ChanelCloud) && !(cc2 is ChanelCloud)) return true;
else if (!(cc1 is ChanelCloud) || !(cc2 is ChanelCloud)) return false;
else if (cc1.domain == cc2.domain &&
cc1.path == cc2.path &&
cc1.port == cc2.port &&
cc1.protocol == cc2.protocol &&
cc1.registerProcedure == cc2.registerProcedure) return true;
else return false;
}
public static bool operator !=(ChanelCloud cc1, ChanelCloud cc2)
{
return !(cc1 == cc2);
}
public override bool Equals(object obj)
{
ChanelCloud cc1 = obj as ChanelCloud;
if (cc1 == null) return false;
if (cc1.domain == this.domain &&
cc1.path == this.path &&
cc1.port == this.port &&
cc1.protocol == this.protocol &&
cc1.registerProcedure == this.registerProcedure) return true;
else return false;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
/// <summary>
/// Поле текстового ввода, которое может отображаться с каналом.
/// </summary>
public class ChaneltextInput
{
#region Обязательные элементы
/// <summary>
/// Заголовок кнопки отправки текста из поля ввода.
/// </summary>
private string title;
/// <summary>
/// URL CGI-скрипта, который обрабатывает запросы текстового ввода.
/// </summary>
private Uri link;
/// <summary>
/// Описание поля ввода.
/// </summary>
private string description;
/// <summary>
/// Название текстового объекта в поле ввода.
/// </summary>
private string name;
/// <summary>
/// Заголовок кнопки отправки текста из поля ввода.
/// </summary>
public string Title { get { return title; } }
/// <summary>
/// URL CGI-скрипта, который обрабатывает запросы текстового ввода.
/// </summary>
public Uri Link { get { return link; } }
/// <summary>
/// Описание поля ввода.
/// </summary>
public string Description { get { return description; } }
/// <summary>
/// Название текстового объекта в поле ввода.
/// </summary>
private string Name { get { return name; } }
#endregion
public ChaneltextInput(XmlNode xmlNode)
{
XmlNode selectedNode;
selectedNode = xmlNode.SelectSingleNode("title");
if (selectedNode != null) title = HttpUtility.HtmlDecode(selectedNode.InnerText);
selectedNode = xmlNode.SelectSingleNode("link");
if (selectedNode != null) link = new Uri(selectedNode.InnerText);
selectedNode = xmlNode.SelectSingleNode("description");
if (selectedNode != null) description = HttpUtility.HtmlDecode(selectedNode.InnerText);
selectedNode = xmlNode.SelectSingleNode("name");
if (selectedNode != null) name = HttpUtility.HtmlDecode(selectedNode.InnerText);
if (title == null) throw new Exception("У поля текстового ввода канала не запонен элемент \"title\"");
if (link == null) throw new Exception("У поля текстового ввода канала не запонен элемент \"link\"");
if (description == null) throw new Exception("У поля текстового ввода канала не запонен элемент \"description\"");
if (name == null) throw new Exception("У поля текстового ввода канала не запонен элемент \"name\"");
}
public static bool operator ==(ChaneltextInput cii1, ChaneltextInput cii2)
{
if (!(cii1 is ChaneltextInput) && !(cii2 is ChaneltextInput)) return true;
else if (!(cii1 is ChaneltextInput) || !(cii2 is ChaneltextInput)) return false;
else if (cii1.description == cii2.description &&
cii1.link == cii2.link &&
cii1.name == cii2.name &&
cii1.title == cii2.title) return true;
else return false;
}
public static bool operator !=(ChaneltextInput cii1, ChaneltextInput cii2)
{
return !(cii1 == cii2);
}
public override bool Equals(object obj)
{
ChaneltextInput cii1 = obj as ChaneltextInput;
if (cii1 == null) return false;
if (cii1.description == this.description &&
cii1.link == this.link &&
cii1.name == this.name &&
cii1.title == this.title) return true;
else return false;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
public static bool operator ==(RssChannel c1, RssChannel c2)
{
if (!(c1 is RssChannel) && !(c2 is RssChannel)) return true;
else if (!(c1 is RssChannel) || !(c2 is RssChannel)) return false;
else if (c1.category == c2.category &&
c1.cloud == c2.cloud &&
c1.copyright == c2.copyright &&
c1.description == c2.description &&
c1.docs == c2.docs &&
c1.generator == c2.generator &&
c1.image == c2.image &&
c1.items == c2.items &&
c1.language == c2.language &&
c1.lastBuildDate == c2.lastBuildDate &&
c1.link == c2.link &&
c1.managingEditor == c2.managingEditor &&
c1.pubDate == c2.pubDate &&
c1.rating == c2.rating &&
c1.skipDays == c2.skipDays &&
c1.skipHours == c2.skipHours &&
c1.textInput == c2.textInput &&
c1.title == c2.title &&
c1.ttl == c2.ttl &&
c1.webMaster == c2.webMaster) return true;
else return false;
}
public static bool operator !=(RssChannel c1, RssChannel c2)
{
return !(c1 == c2);
}
public override bool Equals(object obj)
{
RssChannel c1 = obj as RssChannel;
if (c1 == null) return false;
if (c1.category == this.category &&
c1.cloud == this.cloud &&
c1.copyright == this.copyright &&
c1.description == this.description &&
c1.docs == this.docs &&
c1.generator == this.generator &&
c1.image == this.image &&
c1.items == this.items &&
c1.language == this.language &&
c1.lastBuildDate == this.lastBuildDate &&
c1.link == this.link &&
c1.managingEditor == this.managingEditor &&
c1.pubDate == this.pubDate &&
c1.rating == this.rating &&
c1.skipDays == this.skipDays &&
c1.skipHours == this.skipHours &&
c1.textInput == this.textInput &&
c1.title == this.title &&
c1.ttl == this.ttl &&
c1.webMaster == this.webMaster) return true;
else return false;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
/// <summary>
/// Канал может содержать любое количество статей, элементов &lt;item&gt;. Статья во многом похожа на публикацию в газете или журнале, &lt;description&gt; — это ее краткое изложение, а &lt;link&gt; указывает на полный текст. Статья может быть самодостаточной, в таком случае &lt;description&gt; содержит текст (можно использовать экранированную сущностями HTML-разметку, см. примеры), а ссылка (&lt;link&gt;) и заголовок (&lt;title&gt;) могут опускаться. Все элементы &lt;item&gt; являются необязательными, однако по крайней мере &lt;title&gt; или &lt;description&gt; должен существовать.
/// </summary>
public class RssItem
{
#region Обязательные элементы
/// <summary>
/// Заголовок сообщение
/// </summary>
protected string title;
/// <summary>
/// Ссылка на сообщение
/// </summary>
protected readonly Uri link;
/// <summary>
/// Описание сообщение
/// </summary>
protected string description;
/// <summary>
/// Заголовок сообщение
/// </summary>
public string Title { get { return title; } }
/// <summary>
/// Ссылка на сообщение
/// </summary>
public Uri Link { get { return link; } }
/// <summary>
/// Описание сообщение
/// </summary>
public string Description { get { return description; } }
#endregion
#region Необязательные элементы
/// <summary>
/// Адрес электронной почты автора сообщения.
/// </summary>
protected string author;
/// <summary>
/// Адрес электронной почты автора сообщения.
/// </summary>
public string Author { get { return author; } }
/// <summary>
/// Включает сообщение в одну или более категорий.
/// </summary>
protected List<RssCategory> category;
/// <summary>
/// Включает сообщение в одну или более категорий.
/// </summary>
public List<RssCategory> Category { get { return category; } }
/// <summary>
/// URL страницы для комментариев, относящихся к сообщению.
/// </summary>
protected Uri comments;
/// <summary>
/// Описывает медиа-объект, прикрепленный к сообщению.
/// </summary>
public Uri Comments { get { return comments; } }
/// <summary>
/// Описывает медиа-объект, прикрепленный к сообщению.
/// </summary>
protected ItemEnclosure enclosure;
/// <summary>
/// Описывает медиа-объект, прикрепленный к сообщению.
/// </summary>
public ItemEnclosure Enclosure { get { return enclosure; } }
/// <summary>
/// Строка, уникальным образом идентифицирующая сообщение.
/// </summary>
protected ItemGuid guid;
/// <summary>
/// Строка, уникальным образом идентифицирующая сообщение.
/// </summary>
public ItemGuid Guid { get { return guid; } }
/// <summary>
/// Показывает, когда сообщение было опубликовано.
/// </summary>
protected DateTime pubDate;
/// <summary>
/// Показывает, когда сообщение было опубликовано.
/// </summary>
public DateTime PubDate { get { return pubDate; } }
/// <summary>
/// RSS-канал, из которого получено сообщение.
/// </summary>
protected ItemSource source;
/// <summary>
/// RSS-канал, из которого получено сообщение.
/// </summary>
public ItemSource Source { get { return source; } }
/// <summary>
/// Параметр для поиска сообщеня в яндексе.
/// </summary>
private string yandexFullText;
/// <summary>
/// Параметр для поиска сообщеня в яндексе.
/// </summary>
public string YandexFullText { get { return yandexFullText; } }
/// <summary>
/// RSS-канал, из которого получено сообщение.
/// </summary>
private RssChannel parent;
/// <summary>
/// RSS-канал, из которого получено сообщение.
/// </summary>
public RssChannel Parent { get { return parent; } }
#endregion
public RssItem(XmlNode xmlNode, RssChannel rssChannel)
{
category = new List<RssCategory>();
parent = rssChannel;
XmlNode selectedNode;
selectedNode = xmlNode.SelectSingleNode("title");
if (selectedNode != null) title = HttpUtility.HtmlDecode(selectedNode.InnerText);
selectedNode = xmlNode.SelectSingleNode("link");
if (selectedNode != null) link = new Uri(selectedNode.InnerText);
selectedNode = xmlNode.SelectSingleNode("description");
if (selectedNode != null) description = HttpUtility.HtmlDecode(selectedNode.InnerText);
selectedNode = xmlNode.SelectSingleNode("author");
if (selectedNode != null) author = selectedNode.InnerText;
XmlNodeList selectedNodes = xmlNode.SelectNodes("category");
if (selectedNode != null)
{
foreach (XmlNode selectednode in selectedNodes)
category.Add(new RssCategory(selectednode));
}
selectedNode = xmlNode.SelectSingleNode("comments");
if (selectedNode != null) comments = new Uri(selectedNode.InnerText);
selectedNode = xmlNode.SelectSingleNode("enclosure");
if (selectedNode != null) enclosure = new ItemEnclosure(selectedNode);
selectedNode = xmlNode.SelectSingleNode("guid");
if (selectedNode != null) guid = new ItemGuid(selectedNode);
else if (link != null) guid = new ItemGuid(link.OriginalString, true);
selectedNode = xmlNode.SelectSingleNode("pubDate");
if (selectedNode != null) pubDate = DateTime.Parse(selectedNode.InnerText);
selectedNode = xmlNode.SelectSingleNode("source");
if (selectedNode != null) source = new ItemSource(selectedNode);
//selectedNode = xmlNode.SelectSingleNode("yandex:full-text");
//if (selectedNode != null) yandexFullText = selectedNode.InnerText;
if (title == null) throw new Exception("У статьи канала не запонен элемент \"title\"");
if (link == null) throw new Exception("У статьи канала не запонен элемент \"link\"");
if (description == null) throw new Exception("У статьи канала не запонен элемент \"description\"");
}
/// <summary>
/// Этот элемент должен передавать источники ссылок и новостей. Может использоваться в команде отправки сообщений в канал и должен автоматически генерироваться, когда сообщение пересылается от сборщика к средству редактирования блога.
/// </summary>
public class ItemSource
{
/// <summary>
/// Название источника.
/// </summary>
protected string title;
/// <summary>
/// содержит название канала-источника сообщения, полученное из его 'title'. Имеет обязательный атрибут URL, ссылку на источник.
/// </summary>
protected readonly Uri url;
/// <summary>
/// Название источника.
/// </summary>
public string Title { get { return title; } }
/// <summary>
/// содержит название канала-источника сообщения, полученное из его 'title'. Имеет обязательный атрибут URL, ссылку на источник.
/// </summary>
public Uri URL { get { return url; } }
public ItemSource(XmlNode xmlNode)
{
if (xmlNode.Attributes["url"] != null) url = new Uri(xmlNode.Attributes["url"].Value);
title = xmlNode.InnerText;
}
public static bool operator ==(ItemSource is1, ItemSource is2)
{
if (!(is1 is ItemSource) && !(is2 is ItemSource)) return true;
else if (!(is1 is ItemSource) || !(is2 is ItemSource)) return false;
else if (is1.title == is2.title &&
is1.url == is2.url) return true;
else return false;
}
public static bool operator !=(ItemSource is1, ItemSource is2)
{
return !(is1 == is2);
}
public override bool Equals(object obj)
{
ItemSource is1 = obj as ItemSource;
if (is1 == null) return false;
if (is1.title == this.title &&
is1.url == this.url) return true;
else return false;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
/// <summary>
/// Описывает медиа-объект, прикрепленный к сообщению.
/// </summary>
public class ItemEnclosure
{
/// <summary>
/// Адрес приложения, обязательно HTTP URL
/// </summary>
private Uri url;
/// <summary>
/// Размер приложения в байтах
/// </summary>
private int length;
/// <summary>
/// Тип приложения MIME
/// </summary>
private string type;
/// <summary>
/// Адрес приложения, обязательно HTTP URL
/// </summary>
private Uri URL { get { return url; } }
/// <summary>
/// Размер приложения в байтах
/// </summary>
private int Length { get { return length; } }
/// <summary>
/// Тип приложения MIME
/// </summary>
private string Type { get { return type; } }
public ItemEnclosure(XmlNode xmlNode)
{
XmlNode selectedNode;
selectedNode = xmlNode.SelectSingleNode("url");
if (selectedNode != null) url = new Uri(selectedNode.InnerText);
selectedNode = xmlNode.SelectSingleNode("length");
if (selectedNode != null) int.TryParse(selectedNode.InnerText, out length);
selectedNode = xmlNode.SelectSingleNode("type");
if (selectedNode != null) type = HttpUtility.HtmlDecode(selectedNode.InnerText);
}
public static bool operator ==(ItemEnclosure ie1, ItemEnclosure ie2)
{
if (!(ie1 is ItemEnclosure) && !(ie2 is ItemEnclosure)) return true;
else if (!(ie1 is ItemEnclosure) || !(ie2 is ItemEnclosure)) return false;
else if (ie1.length == ie2.length &&
ie1.type == ie2.type &&
ie1.url == ie2.url) return true;
else return false;
}
public static bool operator !=(ItemEnclosure ie1, ItemEnclosure ie2)
{
return !(ie1 == ie2);
}
public override bool Equals(object obj)
{
ItemEnclosure ie1 = obj as ItemEnclosure;
if (ie1 == null) return false;
if (ie1.length == this.length &&
ie1.type == this.type &&
ie1.url == this.url) return true;
else return false;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
/// <summary>
/// Строка, уникальным образом идентифицирующая сообщение.
/// </summary>
public class ItemGuid
{
/// <summary>
/// Уникальный
/// </summary>
protected string title;
/// <summary>
/// Если необязательный атрибут isPermaLink имеет значение true, то идентификатор рассматривается как постоянная ссылка на полную статью, описанную в элементе
/// </summary>
protected bool isPermaLink;
public string Title { get { return title; } }
/// <summary>
/// Если необязательный атрибут isPermaLink имеет значение true, то идентификатор рассматривается как постоянная ссылка на полную статью, описанную в элементе
/// </summary>
public bool IsPermaLink { get { return isPermaLink; } }
public ItemGuid(string guidTitle, bool guidIsPermaLink)
{
isPermaLink = guidIsPermaLink;
title = guidTitle;
}
public ItemGuid(XmlNode xmlNode)
{
if (xmlNode.Attributes["isPermaLink"] != null) bool.TryParse(xmlNode.Attributes["isPermaLink"].Value, out isPermaLink);
title = xmlNode.InnerText;
}
public static bool operator ==(ItemGuid ig1, ItemGuid ig2)
{
if (!(ig1 is ItemGuid) && !(ig2 is ItemGuid)) return true;
else if (!(ig1 is ItemGuid) || !(ig2 is ItemGuid)) return false;
else if (ig1.isPermaLink == ig2.isPermaLink &&
ig1.title == ig2.title) return true;
else return false;
}
public static bool operator !=(ItemGuid ig1, ItemGuid ig2)
{
return !(ig1 == ig2);
}
public override bool Equals(object obj)
{
ItemGuid ig1 = obj as ItemGuid;
if (ig1 == null) return false;
if (ig1.isPermaLink == this.isPermaLink &&
ig1.title == this.title) return true;
else return false;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
public static bool operator ==(RssItem i1, RssItem i2)
{
if (!(i1 is RssItem) && !(i2 is RssItem)) return true;
else if (!(i1 is RssItem) || !(i2 is RssItem)) return false;
else if (i1.author == i2.author &&
i1.category == i2.category &&
i1.comments == i2.comments &&
i1.description == i2.description &&
i1.enclosure == i2.enclosure &&
i1.guid == i2.guid &&
i1.link == i2.link &&
i1.pubDate == i2.pubDate &&
i1.source == i2.source &&
i1.title == i2.title &&
i1.yandexFullText == i2.yandexFullText) return true;
else return false;
}
public static bool operator !=(RssItem i1, RssItem i2)
{
return !(i1 == i2);
}
public override bool Equals(object obj)
{
RssItem i1 = obj as RssItem;
if (i1 == null) return false;
if (i1.author == this.author &&
i1.category == this.category &&
i1.comments == this.comments &&
i1.description == this.description &&
i1.enclosure == this.enclosure &&
i1.guid == this.guid &&
i1.link == this.link &&
i1.pubDate == this.pubDate &&
i1.source == this.source &&
i1.title == this.title &&
i1.yandexFullText == this.yandexFullText) return true;
else return false;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
/// <summary>
/// Включает сообщение в одну или более категорий.
/// </summary>
public class RssCategory
{
/// <summary>
/// Название категории.
/// </summary>
protected string title;
/// <summary>
/// Домен категории. Предназначен для каталогизации.
/// </summary>
protected string domain;
/// <summary>
/// Название категории.
/// </summary>
public string Title { get { return title; } }
/// <summary>
/// Домен категории. Предназначен для каталогизации.
/// </summary>
public string Domain { get { return domain; } }
public RssCategory(XmlNode xmlNode)
{
if (xmlNode.Attributes["domain"] != null) domain = xmlNode.Attributes["domain"].Value;
title = xmlNode.InnerText;
}
public static bool operator ==(RssCategory c1, RssCategory c2)
{
if (!(c1 is RssCategory) && !(c2 is RssCategory)) return true;
else if (!(c1 is RssCategory) || !(c2 is RssCategory)) return false;
else if (c1.domain == c2.domain &&
c1.title == c2.title) return true;
else return false;
}
public static bool operator !=(RssCategory c1, RssCategory c2)
{
return !(c1 == c2);
}
public override bool Equals(object obj)
{
RssCategory c1 = obj as RssCategory;
if (c1 == null) return false;
if (c1.domain == this.domain &&
c1.title == this.title) return true;
else return false;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
}