root/trunk/Units/unit_Templater.pas

431434
1
{ ****************************************************************************** }
1
(* *****************************************************************************
2
{ }
2
  *
3
{ MyHomeLib }
3
  * MyHomeLib
4
{ }
4
  *
5
{ Version 1 }
5
  * Класс работы с шаблонами
  *
6
{ 31.01.2010 }
6
  * Version 1.0
7
{ Copyright (c) Matvienko Sergei  matv84@mail.ru }
7
  * 31.01.2010
8
{ }
8
  * Copyright (c) Aleksey Penkov  alex.penkov@gmail.com
9
{ Класс работы с шаблонами }
9
  * Author        Matvienko Sergei  matv84@mail.ru
10
{ ****************************************************************************** }
10
  *
11
  ****************************************************************************** *)
11
12
12
unit unit_Templater;
13
unit unit_Templater;
13
14
...
...
25
27
26
  TTemplater = class
28
  TTemplater = class
27
  private
29
  private
28
    Template: string;
30
    FTemplate: string;
29
    ParsedString: string;
31
    FParsedString: string;
30
    BlocksMap: array of TElement;
32
    FBlocksMap: array of TElement;
31
  public
33
  public
32
    constructor Create;
34
    constructor Create;
33
    function ValidateTemplate(Template: string): TErrorType;
35
    function ValidateTemplate(Template: string): TErrorType;
34
    function SetTemplate(Template: String): TErrorType;
36
    function SetTemplate(Template: string): TErrorType;
35
    procedure ParseTemplate(ACollection: TDMCollection);
37
    procedure ParseTemplate(ACollection: TDMCollection);
36
    function GetParsedString: string;
38
    function GetParsedString: string;
37
  end;
39
  end;
38
40
39
implementation
41
implementation
40
42
41
uses SysUtils, unit_Globals, unit_Consts;
43
uses
44
  SysUtils, unit_Globals, unit_Consts;
42
45
43
constructor TTemplater.Create;
46
constructor TTemplater.Create;
44
begin
47
begin
45
  inherited;
48
  inherited;
46
  ParsedString := '';
49
  FParsedString := '';
47
  Template := '';
50
  FTemplate := '';
48
end;
51
end;
49
52
50
function TTemplater.ValidateTemplate(Template: string): TErrorType;
53
function TTemplater.ValidateTemplate(Template: string): TErrorType;
51
const
54
const
52
  mask_elements: array [1 .. 8] of string = ('f', 't', 's', 'n', 'id', 'g',
55
  mask_elements: array [1 .. 8] of string = ('f', 't', 's', 'n', 'id', 'g', 'fl', 'rg');
53
    'fl', 'rg');
54
var
56
var
55
  stack: array of TElement;
57
  stack: array of TElement;
56
  h, k, i, j, StackPos, ElementPos, ColElements, last_char,
58
  h, k, i, j, StackPos, ElementPos, ColElements, last_char, last_col_elements: integer;
57
    last_col_elements: integer;
58
  bol, TemplEnd: boolean;
59
  bol, TemplEnd: boolean;
59
  TemplatePart: string;
60
  TemplatePart: string;
60
begin
61
begin
61
  // Поправка на количество частей пути в карту элементов и блоков (используется при разборе путей)
62
  // Поправка на количество частей пути в карту элементов и блоков (используется при разборе путей)
62
  last_col_elements := 0;
63
  last_col_elements := 0;
63
  last_char := 0;
64
  last_char := 0;
65
64
  // Определение количества элементов в шаблоне
66
  // Определение количества элементов в шаблоне
65
  ColElements := 0;
67
  ColElements := 0;
66
  for i := 1 to Length(Template) do
68
  for i := 1 to Length(Template) do
67
    if Template[i] = '%' then
69
    if Template[i] = '%' then
68
      inc(ColElements);
70
      Inc(ColElements);
71
69
  // Установка необходимой размерности и инициализация массивов
  SetLength(stack, ColElements);
72
  // Установка необходимой размерности и инициализация массивов
  SetLength(stack, ColElements);
70
  SetLength(BlocksMap, ColElements);
73
  SetLength(FBlocksMap, ColElements);
71
  for i := 0 to ColElements - 1 do
74
  for i := 0 to ColElements - 1 do
72
  begin
75
  begin
73
    stack[i].name := '';
76
    stack[i].name := '';
74
    BlocksMap[i].name := '';
77
    FBlocksMap[i].name := '';
75
  end;
78
  end;
79
76
  TemplEnd := false;
80
  TemplEnd := false;
77
  k := 1;
81
  k := 1;
78
  while not(TemplEnd) do
82
  while not(TemplEnd) do
...
...
85
    while (not(Template[k] in ['/', '\'])) and (k <= Length(Template)) do
89
    while (not(Template[k] in ['/', '\'])) and (k <= Length(Template)) do
86
    begin
90
    begin
87
      TemplatePart := TemplatePart + Template[k];
91
      TemplatePart := TemplatePart + Template[k];
88
      inc(k);
92
      Inc(k);
89
    end;
93
    end;
90
    inc(k);
94
    Inc(k);
91
    // Если больше нет элементов пути, то итеррация крайняя
95
    // Если больше нет элементов пути, то итеррация крайняя
92
    if k > Length(Template) then
96
    if k > Length(Template) then
93
      TemplEnd := true;
97
      TemplEnd := true;
...
...
100
      // Поиск открывающей скобки блока элемента
      if TemplatePart[i] = '[' then
104
      // Поиск открывающей скобки блока элемента
      if TemplatePart[i] = '[' then
101
      begin
105
      begin
102
        inc(StackPos);
106
        Inc(StackPos);
103
        stack[StackPos].BegBlock := i;
107
        stack[StackPos].BegBlock := i;
104
        stack[StackPos].name := '';
108
        stack[StackPos].name := '';
105
      end;
109
      end;
...
...
112
        if (stack[StackPos].name <> '') and (StackPos > 0) then
116
        if (stack[StackPos].name <> '') and (StackPos > 0) then
113
        begin
117
        begin
114
          Result := ErTemplate; // В блоке не может быть более одного элемента
          exit;
118
          Result := ErTemplate; // В блоке не может быть более одного элемента
          exit;
119
          Exit;
115
        end;
120
        end;
116
121
117
        // Выделяем название элемента
        inc(i);
122
        // Выделяем название элемента
        inc(i);
123
        Inc(i);
118
        stack[StackPos].name := '';
124
        stack[StackPos].name := '';
119
        while CharInSet(TemplatePart[i], ['a' .. 'z', 'A' .. 'Z']) do
125
        while CharInSet(TemplatePart[i], ['a' .. 'z', 'A' .. 'Z']) do
120
        begin
126
        begin
121
          stack[StackPos].name := stack[StackPos].name + TemplatePart[i];
127
          stack[StackPos].name := stack[StackPos].name + TemplatePart[i];
122
          inc(i);
128
          Inc(i);
123
        end;
129
        end;
124
        dec(i);
130
        Dec(i);
125
131
126
        // Добавляем элемент в общий список элементов
132
        // Добавляем элемент в общий список элементов
127
        if StackPos = 0 then
133
        if StackPos = 0 then
128
        begin
134
        begin
129
          BlocksMap[ElementPos + last_col_elements].name := stack[StackPos]
135
          FBlocksMap[ElementPos + last_col_elements].name := stack[StackPos].name;
130
            .name;
136
          FBlocksMap[ElementPos + last_col_elements].BegBlock := 0;
131
          BlocksMap[ElementPos + last_col_elements].BegBlock := 0;
137
          FBlocksMap[ElementPos + last_col_elements].EndBlock := 0;
132
          BlocksMap[ElementPos + last_col_elements].EndBlock := 0;
138
          Inc(ElementPos);
133
          inc(ElementPos);
134
        end;
139
        end;
135
136
      end;
140
      end;
137
141
138
      // Поиск окончания блока элемента
142
      // Поиск окончания блока элемента
...
...
145
        if (stack[StackPos].name = '') or (StackPos <= 0) then
147
        if (stack[StackPos].name = '') or (StackPos <= 0) then
146
        begin
148
        begin
147
          Result := ErBlocks; // Проверьте соответствие открывающих и закрывающих скобок блоков элементов
149
          Result := ErBlocks; // Проверьте соответствие открывающих и закрывающих скобок блоков элементов
148
          exit;
150
          Exit;
149
        end;
151
        end;
150
        stack[StackPos].EndBlock := i;
152
        stack[StackPos].EndBlock := i;
151
153
152
        // Добавляем элемент в общий список элементов
154
        // Добавляем элемент в общий список элементов
153
        BlocksMap[ElementPos + last_col_elements].name := stack[StackPos].name;
155
        FBlocksMap[ElementPos + last_col_elements].name := stack[StackPos].name;
154
        BlocksMap[ElementPos + last_col_elements].BegBlock := stack[StackPos]
156
        FBlocksMap[ElementPos + last_col_elements].BegBlock := stack[StackPos].BegBlock + last_char;
155
          .BegBlock + last_char;
157
        FBlocksMap[ElementPos + last_col_elements].EndBlock := stack[StackPos].EndBlock + last_char;
156
        BlocksMap[ElementPos + last_col_elements].EndBlock := stack[StackPos]
158
        Inc(ElementPos);
157
          .EndBlock + last_char;
158
        inc(ElementPos);
159
159
160
        dec(StackPos);
160
        Dec(StackPos);
161
      end;
161
      end;
162
162
163
      // Переход к очередному символу в шаблоне
163
      // Переход к очередному символу в шаблоне
164
      inc(i);
164
      Inc(i);
165
    end;
165
    end;
166
166
167
    // Имеются незакрытые скобки блоков
168
    if StackPos > 0 then
169
    begin
170
      Result := ErBlocks; // Проверьте соответствие открывающих и закрывающих скобок блоков элементов
171
      Exit;
172
    end;
173
167
    // Проверка всех элементов на правильность написания
174
    // Проверка всех элементов на правильность написания
168
    for h := Low(BlocksMap) to High(BlocksMap) do
175
    for h := Low(FBlocksMap) to High(FBlocksMap) do
169
    begin
176
    begin
170
      if BlocksMap[h].name <> '' then
177
      if FBlocksMap[h].name <> '' then
171
      begin
178
      begin
172
        bol := false;
179
        bol := False;
173
        for j := 1 to High(mask_elements) do
180
        for j := 1 to High(mask_elements) do
174
          if BlocksMap[h].name = mask_elements[j] then
181
          if FBlocksMap[h].name = mask_elements[j] then
175
            bol := true;
182
          begin
183
            bol := True;
184
            Break;
185
          end;
176
186
177
        if not(bol) then
187
        if not(bol) then
178
          break;
188
          Break;
179
      end;
189
      end;
180
    end;
190
    end;
181
191
182
    // Имеются незакрытые скобки блоков
183
    if StackPos > 0 then
184
    begin
185
      Result := ErBlocks; // Проверьте соответствие открывающих и закрывающих скобок блоков элементов
186
      exit;
187
    end;
188
    // Имеются неверние элементы шаблона
192
    // Имеются неверние элементы шаблона
189
    if not(bol) then
193
    if not(bol) then
190
    begin
194
    begin
191
      Result := ErElements; // Неверные элементы шаблона
195
      Result := ErElements; // Неверные элементы шаблона
192
      exit;
196
      Exit;
193
    end;
197
    end;
194
198
195
    inc(last_col_elements, ElementPos);
199
    Inc(last_col_elements, ElementPos);
196
200
197
    // Поправка на количество символов с начала строки шаблона в
    // карту элементов и блоков (используется при разборе путей)
201
    // Поправка на количество символов с начала строки шаблона в
    // карту элементов и блоков (используется при разборе путей)
198
    last_char := last_char + k - 1;
202
    last_char := last_char + k - 1;
199
203
200
    // Переход к очередному символу в шаблоне с целью обработки следующей части пути к файлу
    inc(i);
204
    // Переход к очередному символу в шаблоне с целью обработки следующей части пути к файлу
    inc(i);
205
    Inc(i);
201
  end;
206
  end;
202
207
203
  // Если замечаний нет, то шаблон валиден
208
  // Если замечаний нет, то шаблон валиден
...
...
210
begin
214
begin
211
  Result := ValidateTemplate(Template);
215
  Result := ValidateTemplate(Template);
212
  if Result = ErFine then
216
  if Result = ErFine then
213
    Self.Template := Template;
217
    FTemplate := Template;
214
end;
218
end;
215
219
216
procedure TTemplater.ParseTemplate(ACollection: TDMCollection);
220
procedure TTemplater.ParseTemplate(ACollection: TDMCollection);
...
...
226
  i, j: integer;
230
  i, j: integer;
227
  R: TBookRecord;
231
  R: TBookRecord;
228
begin
232
begin
229
  ParsedString := Self.Template;
233
  FParsedString := FTemplate;
230
234
231
  // Получение текущей книги
  ACollection.GetCurrentBook(R);
235
  // Получение текущей книги
  ACollection.GetCurrentBook(R);
...
...
249
253
250
  MaskElements[4].templ := 'f';
254
  MaskElements[4].templ := 'f';
251
  AuthorName := '';
255
  AuthorName := '';
252
  for i := low(R.Authors) to high(R.Authors) do
256
  for i := Low(R.Authors) to High(R.Authors) do
253
  begin
257
  begin
254
    LastName := R.Authors[i].FLastName;
258
    LastName := R.Authors[i].FLastName;
255
    if R.Authors[i].FFirstName <> '' then
259
    if R.Authors[i].FFirstName <> '' then
...
...
257
    if R.Authors[i].FMiddleName <> '' then
261
    if R.Authors[i].FMiddleName <> '' then
258
      MiddleName := ' ' + R.Authors[i].FMiddleName[1] + '.';
262
      MiddleName := ' ' + R.Authors[i].FMiddleName[1] + '.';
259
    AuthorName := AuthorName + LastName + FirstName + MiddleName;
263
    AuthorName := AuthorName + LastName + FirstName + MiddleName;
260
    if i < high(R.Authors) then
264
    if i < High(R.Authors) then
261
      AuthorName := AuthorName + ', ';
265
      AuthorName := AuthorName + ', ';
262
  end;
266
  end;
263
  MaskElements[4].value := AuthorName;
267
  MaskElements[4].value := AuthorName;
...
...
274
  end;
278
  end;
275
279
276
  MaskElements[7].templ := 'fl';
280
  MaskElements[7].templ := 'fl';
277
  MaskElements[7].value := R.Authors[ low(R.Authors)].FLastName[1];
281
  MaskElements[7].value := R.Authors[Low(R.Authors)].FLastName[1];
278
282
279
  MaskElements[8].templ := 'rg';
283
  MaskElements[8].templ := 'rg';
280
  MaskElements[8].value := ACollection.GetRootGenre(R.LibID);
284
  MaskElements[8].value := ACollection.GetRootGenre(R.LibID);
281
285
282
  // Цикл удаления "пустых" блоков
286
  // Цикл удаления "пустых" блоков
283
  for i := Low(MaskElements) to High(MaskElements) do
287
  for i := Low(MaskElements) to High(MaskElements) do
284
    for j := Low(BlocksMap) to High(BlocksMap) do
288
    for j := Low(FBlocksMap) to High(FBlocksMap) do
285
      if (MaskElements[i].templ = BlocksMap[j].name) and
289
      if (MaskElements[i].templ = FBlocksMap[j].name) and (MaskElements[i].value = '') then
286
        (MaskElements[i].value = '') then
290
        if (FBlocksMap[j].BegBlock <> 0) and (FBlocksMap[j].EndBlock <> 0) then
287
        if (BlocksMap[j].BegBlock <> 0) and (BlocksMap[j].EndBlock <> 0) then
288
        begin
291
        begin
289
          Delete(ParsedString, BlocksMap[j].BegBlock,
292
          Delete(FParsedString, FBlocksMap[j].BegBlock, FBlocksMap[j].EndBlock - FBlocksMap[j].BegBlock + 1);
290
            BlocksMap[j].EndBlock - BlocksMap[j].BegBlock + 1);
291
          // Здесь ещё продумаю вариант удаления записей о вложенных элементах без валидации
293
          // Здесь ещё продумаю вариант удаления записей о вложенных элементах без валидации
292
          ValidateTemplate(ParsedString);
294
          ValidateTemplate(FParsedString);
293
        end;
295
        end;
294
296
295
  // Цикл удаления квадратных скобок
297
  // Цикл удаления квадратных скобок
296
  for i := Length(ParsedString) downto 1 do
298
  for i := Length(FParsedString) downto 1 do
297
    if CharInSet(ParsedString[i], ['[', ']']) then
299
    if CharInSet(FParsedString[i], ['[', ']']) then
298
      Delete(ParsedString, i, 1);
300
      Delete(FParsedString, i, 1);
299
301
300
  // Цикл замены элементов шаблона их значениями
302
  // Цикл замены элементов шаблона их значениями
301
  for i := 1 to mask_elements do
303
  for i := 1 to mask_elements do
302
    StrReplace('%' + MaskElements[i].templ, MaskElements[i].value,
304
    StrReplace('%' + MaskElements[i].templ, MaskElements[i].value, FParsedString);
303
      ParsedString);
304
end;
305
end;
305
306
306
function TTemplater.GetParsedString: string;
307
function TTemplater.GetParsedString: string;
307
begin
308
begin
308
  Result := ParsedString;
309
  Result := FParsedString;
309
end;
310
end;
310
311
311
end.
312
end.