Guest User

Untitled

a guest
Jul 22nd, 2018
98
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Pascal 8.96 KB | None | 0 0
  1. unit Parser;
  2.  
  3.  
  4. {$mode objfpc}
  5. {$h+}
  6.  
  7.  
  8. interface
  9.  
  10.  
  11. uses
  12.   Classes, Contnrs, Drupal;
  13.  
  14.  
  15. function ParseXML(Strings: TStrings): TObjectList;
  16.  
  17.  
  18. implementation
  19.  
  20.  
  21. uses
  22.  
  23.   SysUtils;
  24.  
  25.  
  26. type
  27.  
  28.   TSpan = record
  29.     Content: string;
  30.     X: Integer;
  31.   end;
  32.  
  33.   TLine = array of TSpan;
  34.  
  35.  
  36. const
  37.  
  38.   BLOCK_JUNK_LENGTH = 2;
  39.   BLOCK_HEAD_LENGTH = 21;
  40.   DOLLAR_RATE = 8;
  41.  
  42.  
  43. var
  44.  
  45.   Data: array of TLine;
  46.   Offset: Integer;
  47.   Strings: TStrings;
  48.  
  49.  
  50. function CreateStruct(const Line: TLine): TStringList; forward;
  51.  
  52. function IsLineEnded(X: Integer): Boolean; forward;
  53. function IsSpanCollapsible(X: Integer): Boolean; forward;
  54. function IsSpanEnded(): Boolean; forward;
  55.  
  56. function ParseLine(): TLine; forward;
  57. function ParseLine(Count: Integer): TLine; forward;
  58.  
  59. function ParseSpan(): TSpan; forward;
  60.  
  61. procedure Print(const Line: TLine); forward;
  62.  
  63. procedure PushLine(const Line: TLine); forward;
  64.  
  65. function ReadC(Node: string): Char; forward;
  66. function ReadX(Node: string): Integer; forward;
  67.  
  68. function Translate(const Line: TLine): TContent; forward;
  69.  
  70.  
  71. { CreateStruct }
  72.  
  73.  
  74. function CreateStruct(const Line: TLine): TStringList;
  75. var
  76.   Diff0, Diff1: Integer;
  77.   Head: TLine;
  78.   I, J: Integer;
  79.   Match: Integer;
  80. begin
  81.  
  82.   Result := TStringList.Create();
  83.  
  84.   Head := Parser.Data[1];
  85.  
  86.   for I := 0 to Length(Line) - 1 do
  87.   begin
  88.     // <hardcode>
  89.     if Line[I].X = 117 then
  90.     begin
  91.       Result.Values['Район'] := Line[I].Content;
  92.       continue;
  93.     end;
  94.     if (Line[I].X > 430) and (Line[I].X < 470) then
  95.     begin
  96.       Result.Values['Номер'] := Line[I].Content;
  97.       continue;
  98.     end;
  99.     // </hardcode>
  100.     Match := 0;
  101.     Diff0 := Abs(Line[I].X - Head[Match].X);
  102.     for J := 1 to Length(Head) - 1 do
  103.     begin
  104.       Diff1 := Abs(Line[I].X - Head[J].X);
  105.       if (Diff1 < Diff0) then
  106.       begin
  107.         Diff0 := Diff1;
  108.         Match := J;
  109.       end;
  110.     end;
  111.     Result.Values[Head[Match].Content] := Line[I].Content;
  112.   end;
  113.  
  114. end;
  115.  
  116.  
  117. { IsLineEnded }
  118.  
  119.  
  120. function IsLineEnded(X: Integer): Boolean;
  121. var
  122.   I: Integer absolute Parser.Offset;
  123.   S: TStrings absolute Parser.Strings;
  124. begin
  125.   if (S[I][2] = 's') then
  126.     Result := (ReadX(S[I]) = X)
  127.   else
  128.     Result := (S[I][2] = '/') and (S[I][3] = 'p');
  129. end;
  130.  
  131.  
  132. { IsSpanCollapsible }
  133.  
  134.  
  135. function IsSpanCollapsible(X: Integer): Boolean;
  136. var
  137.   I: Integer absolute Parser.Offset;
  138.   S: TStrings absolute Parser.Strings;
  139. begin
  140.   if (I + 1 < S.Count) and (S[I + 1][2] = 's') then
  141.     Result := ReadX(S[I + 1]) = X
  142.   else
  143.     Result := false;
  144. end;
  145.  
  146.  
  147. { IsSpanEnded }
  148.  
  149.  
  150. function IsSpanEnded(): Boolean;
  151. var
  152.   I: Integer absolute Parser.Offset;
  153.   S: TStrings absolute Parser.Strings;
  154. begin
  155.   Result := (S[I][2] = '/') and (S[I][3] = 's');
  156. end;
  157.  
  158.  
  159. { ParseXML }
  160.  
  161.  
  162. function ParseXML(Strings: TStrings): TObjectList;
  163. var
  164.   I: Integer;
  165. begin
  166.  
  167.   Parser.Offset := 0;
  168.   Parser.Strings := Strings;
  169.  
  170.   PushLine(ParseLine(BLOCK_JUNK_LENGTH));
  171.  
  172.   PushLine(ParseLine(BLOCK_HEAD_LENGTH));
  173.  
  174.   while Parser.Offset < Parser.Strings.Count {hack to skip last page tag} - 1 {/hack} do
  175.     PushLine(ParseLine());
  176.  
  177.   Result := TObjectList.Create();
  178.   for I := 2 to Length(Parser.Data) - 1 do
  179.     Result.Add(Translate(Parser.Data[I]));
  180.  
  181. end;
  182.  
  183.  
  184. { ParseLine }
  185.  
  186.  
  187. function ParseLine(): TLine;
  188. var
  189.   I: Integer absolute Parser.Offset;
  190.   S: TStrings absolute Parser.Strings;
  191.   J: Integer = 1;
  192. begin
  193.  
  194.   SetLength(Result, 1);
  195.   Result[0] := ParseSpan();
  196.  
  197.   while I < S.Count do
  198.   begin
  199.     SetLength(Result, J + 1);
  200.     Result[J] := ParseSpan();
  201.     if (J = 20) or IsLineEnded(Result[0].X) then
  202.       break;
  203.     Inc(J);
  204.   end;
  205.  
  206. end;
  207.  
  208.  
  209. function ParseLine(Count: Integer): TLine;
  210. var
  211.   I: Integer;
  212. begin
  213.   SetLength(Result, Count);
  214.   for I := 0 to Count - 1 do
  215.     Result[I] := ParseSpan();
  216. end;
  217.  
  218.  
  219. {
  220.   ParseSpan
  221.  
  222.   Считывает спан из документа, а затем перемещает курсор
  223.   к следующему спану или в конец документа.
  224. }
  225.  
  226.  
  227. function ParseSpan(): TSpan;
  228. var
  229.   I: Integer absolute Parser.Offset;
  230.   S: TStrings absolute Parser.Strings;
  231. begin
  232.  
  233.   Result.Content := '';
  234.  
  235.   while I < S.Count do
  236.   begin
  237.     if S[I][2] = 'c' then
  238.       Result.Content := Result.Content + ReadC(S[I])
  239.     else
  240.       if S[I][2] = 's' then
  241.         Result.X := ReadX(S[I])
  242.       else
  243.         if IsSpanEnded() then
  244.           if IsSpanCollapsible(Result.X) then
  245.             Inc(I)
  246.           else
  247.             break;
  248.     Inc(I);
  249.   end;
  250.   Inc(I);
  251. end;
  252.  
  253.  
  254. { Print }
  255.  
  256.  
  257. procedure Print(const Line: TLine);
  258. var
  259.   I: Integer;
  260. begin
  261.   WriteLn('line {');
  262.   for I := 0 to Length(Line) - 1 do
  263.   begin
  264.     WriteLn('  span {');
  265.     WriteLn('    content: ''', Line[I].Content, ''';');
  266.     WriteLn('    x: ', Line[I].X, ';');
  267.     WriteLn('  }');
  268.   end;
  269.   WriteLn('}');
  270.   WriteLn();
  271. end;
  272.  
  273.  
  274. { PushLine }
  275.  
  276.  
  277. procedure PushLine(const Line: TLine);
  278. var
  279.   I: Integer;
  280. begin
  281.   I := Length(Data);
  282.   if (I <= 2) or ((I > 2) and (Data[1][0].X <> Line[0].X)) then
  283.   begin
  284.     SetLength(Data, I + 1);
  285.     Data[I] := Line;
  286.   end;
  287. end;
  288.  
  289.  
  290. { ReadC }
  291.  
  292.  
  293. function ReadC(Node: string): Char;
  294. begin
  295.   Delete(Node, 1, Pos('c=', Node) + 2);
  296.   Node := Copy(Node, 1, Pos('"', Node) - 1);
  297.   if Length(Node) > 1 then
  298.     Result := WideChar(StrToInt('$' + Copy(Node, 4, Length(Node) - 4)))
  299.   else
  300.     Result := WideChar(Node[1]);
  301. end;
  302.  
  303.  
  304. { ReadX }
  305.  
  306.  
  307. function ReadX(Node: string): Integer;
  308. begin
  309.   Delete(Node, 1, Pos('bbox', Node) + 5);
  310.   Result := StrToInt(Copy(Node, 1, Pos(' ', Node) - 1));
  311. end;
  312.  
  313.  
  314. { Translate }
  315.  
  316.  
  317. function GetNumber(S: string): Integer;
  318. begin
  319.   Result := StrToIntDef(S, 0);
  320.   if Result = 0 then
  321.     Result := -1;
  322. end;
  323.  
  324.  
  325. function GetDistrict(S: TStrings): TContentDistrict;
  326. var
  327.   D: string;
  328. begin
  329.   D := S.Values['Район'];
  330.   if D = 'Голосеевский' then
  331.     exit(cdGol);
  332.   if D = 'Дарницкий' then
  333.     exit(cdDar);
  334.   if D = 'Деснянский' then
  335.     exit(cdDes);
  336.   if D = 'Днепровский' then
  337.     exit(cdDne);
  338.   if D = 'Оболонский' then
  339.     exit(cdObo);
  340.   if D = 'Печерский' then
  341.     exit(cdPec);
  342.   if D = 'Подольский' then
  343.     exit(cdPod);
  344.   if D = 'Святошинский' then
  345.     exit(cdSvy);
  346.   if D = 'Соломенский' then
  347.     exit(cdSol);
  348.   if D = 'Шевченковский' then
  349.     exit(cdShe);
  350.   Result := cdOther;
  351. end;
  352.  
  353.  
  354. function GetAddress(S: TStrings): string;
  355. var
  356.   A, B: string;
  357. begin
  358.  
  359.   Result := '';
  360.  
  361.   if GetDistrict(S) = cdOther then
  362.     Result := S.Values['Район'];
  363.  
  364.   A := S.Values['Улица'];
  365.  
  366.   if A <> '' then
  367.     if Result = '' then
  368.       Result := A
  369.     else
  370.       Result := Result + ', ' + A;
  371.  
  372.   B := S.Values['№ дома'];
  373.  
  374.   if B <> '' then
  375.     if Result = '' then
  376.       Result := B
  377.     else
  378.       Result := Result + ', ' + B;
  379.  
  380. end;
  381.  
  382.  
  383. function GetKind(S: TStrings): TContentKind;
  384. begin
  385.   if S.Values['Комнат'] = 'д' then
  386.     exit(ckHouse);
  387.   if S.Values['Планировка'] = 'к' then
  388.     exit(ckRoom);
  389.   Result := ckFlat;
  390. end;
  391.  
  392.  
  393. function GetPhone(S: TStrings): string;
  394. begin
  395.   Result := S.Values['Номер'];
  396.   if Length(Result) = 7 then
  397.      Result := '44' + Result;
  398.   if Length(Result) > 9 then
  399.      Result := Copy(Result, Length(Result) - 8, 9);
  400.   Insert('-', Result, 3);
  401.   Insert('-', Result, 7);
  402.   Insert('-', Result, 10);
  403. end;
  404.  
  405. function GetPrice(S: TStrings): Integer;
  406. begin
  407.   Result := StrToIntDef(S.Values['Цена'], -1);
  408.   if (Result <> -1) and (S.Values['Валюта'] = '$') then
  409.     Result := Result * DOLLAR_RATE;
  410. end;
  411.  
  412.  
  413. function GetPriceKind(S: TStrings): TContentPriceKind;
  414. begin
  415.   Result := cpkMonthly; // уточнить!
  416. end;
  417.  
  418.  
  419. function GetRooms(S: TStrings): Integer;
  420. begin
  421.   if GetKind(S) = ckFlat then
  422.     Result := GetNumber(S.Values['Комнат'])
  423.   else
  424.     Result := -1;
  425. end;
  426.  
  427.  
  428.  
  429. function Translate(const Line: TLine): TContent;
  430. var
  431.   Struct: TStringList;
  432. begin
  433.   Struct := CreateStruct(Line);
  434.  
  435.   Result := TContent.Create();
  436.   Result.Address := UTF8Encode(GetAddress(Struct));
  437.   Result.AreaKitchen := GetNumber(Struct.Values['Кухня']);
  438.   Result.AreaResidential := GetNumber(Struct.Values['Жилая']);
  439.   Result.Body := UTF8Encode(Struct.Values['Примечание']);
  440.   Result.District := GetDistrict(Struct);
  441.   Result.Kind := GetKind(Struct);
  442.   Result.Level := GetNumber(Struct.Values['Этаж']);
  443.   Result.Levels := GetNumber(Struct.Values['Этажность']);
  444.   Result.MiscFridge := (Struct.Values['Холодильник'] = '+');
  445.   Result.MiscFurniture := (Struct.Values['Мебель'] = '+');
  446.   Result.MiscPhone := (Struct.Values['Телефон'] = '+');
  447.   Result.MiscTV := (Struct.Values['Телевизор'] = '+');
  448.   Result.Phone := GetPhone(Struct);
  449.   Result.Price := GetPrice(Struct);
  450.   Result.PriceKind := GetPriceKind(Struct);
  451.   Result.Rooms := GetRooms(Struct);
  452.  
  453.   Struct.Free();
  454. end;
  455.  
  456.  
  457. initialization
  458.  
  459.  
  460. SetLength(Data, 0);
  461.  
  462.  
  463. end.
Add Comment
Please, Sign In to add comment