]>
git.p6c8.net - template-class.git/blob - template27.py
3 # Template (Version 2.5)
6 # Klasse zum Parsen von Templates
8 # Autor: Patrick Canterino <patrick@patshaping.de>
9 # Letzte Aenderung: 18.4.2013
11 # Copyright (C) 2002-2013 Patrick Canterino
13 # Diese Datei kann unter den Bedingungen der "Artistic License 2.0"
14 # weitergegeben und / oder veraendert werden.
16 # http://www.opensource.org/licenses/artistic-license-2.0
38 self
.defined_vars
= []
43 # Kompletten Vorlagentext zurueckgeben
47 # Rueckgabe: Kompletter Vorlagentext (String)
49 def get_template(self
):
50 return str(self
.template
)
54 # Kompletten Vorlagentext aendern
56 # Parameter: Vorlagentext
58 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
60 def set_template(self
,template
):
61 self
.template
= str(template
)
65 # Vorlagentext ans Template-Objekt anhaengen
67 # Parameter: Vorlagentext
69 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
71 def add_text(self
,text
):
72 self
.set_template(self
.get_template()+str(text
))
76 # Einlesen einer Vorlagendatei und {INCLUDE}-Anweisungen ggf. verarbeiten
77 # (Text wird an bereits vorhandenen Text angehaengt)
79 # Parameter: 1. Datei zum Einlesen
80 # 2. Status-Code (Boolean):
81 # true => {INCLUDE}-Anweisungen nicht verarbeiten
82 # false => {INCLUDE}-Anweisungen verarbeiten (Standard)
84 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
86 def read_file(self
,file,not_include
=0):
93 self
.add_text(content
)
96 if not not_include
: self
.parse_includes()
100 # Wert einer Variable setzen
102 # Parameter: 1. Name der Variable
103 # 2. Wert, den die Variable erhalten soll
105 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
107 def set_var(self
,var
,content
):
108 if content
is None: content
= ''
109 self
.vars[var
] = content
113 # Wert einer Variable zurueckgeben
115 # Parameter: (optional) Variablenname
117 # Rueckgabe: Wert der Variable;
118 # wenn die Variable nicht existiert, false;
119 # wenn kein Variablenname angegeben wurde, wird ein
120 # Array mit den Variablennamen zurueckgegeben
122 def get_var(self
,var
=None):
124 if self
.vars.has_key(var
):
125 return self
.vars[var
]
129 return self
.vars.keys()
133 # Komplettes Variablen-Array mit einem anderen Array ueberschreiben
137 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
139 def set_vars(self
,vars):
144 # Zum bestehenden Variablen-Array weitere Variablen in Form eines Arrays
149 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
151 def add_vars(self
,vars):
152 self
.vars.update(vars)
156 # Daten fuer eine Schleife setzen
158 # Parameter: 1. Name der Schleife
159 # 2. Array mit den Dictionaries mit den Variablen fuer
160 # die Schleifendurchgaenge
162 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
164 def set_loop_data(self
,loop
,data
):
165 self
.loop_vars
[loop
] = data
169 # Daten fuer einen Schleifendurchgang hinzufuegen
171 # Parameter: 1. Name der Schleife
172 # 2. Dictionary mit den Variablen fuer den
175 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
177 def add_loop_data(self
,loop
,data
):
178 if self
.loop_vars
.has_key(loop
) and type(self
.loop_vars
[loop
]) is list:
179 self
.loop_vars
[loop
].append(data
)
181 self
.loop_vars
[loop
] = [data
]
185 # In der Template definierte Variablen auslesen, Variablen
186 # ersetzen, {IF}- und {TRIM}-Bloecke parsen
188 # Parameter: -nichts-
190 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
193 if self
.old_parsing
: return self
.parse_old()
195 # Zuerst die Schleifen parsen
197 if self
.loop_vars
and self
.loop_vars
.keys():
198 loops
= self
.loop_vars
.keys()
201 self
.parse_loop(loop
)
203 # In Template-Datei definierte Variablen auslesen
205 self
.get_defined_vars()
209 vars = self
.get_var()
211 if vars is not None and type(vars) is list:
212 self
.parse_if_blocks()
215 # {TRIM}-Bloecke entfernen
217 self
.parse_trim_blocks()
221 # In der Template definierte Variablen auslesen, Variablen
222 # ersetzen, {IF}- und {TRIM}-Bloecke parsen
225 # Parameter: -nichts-
227 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
230 # Zuerst die Schleifen parsen
232 if self
.loop_vars
and self
.loop_vars
.keys():
233 loops
= self
.loop_vars
.keys()
236 self
.parse_loop(loop
)
238 # Normale Variablen durchgehen
241 vars = self
.get_var()
244 val
= self
.get_var(var
)
246 self
.parse_if_block(var
,val
)
248 if type(val
) is list:
249 self
.fillin_array(var
,val
)
253 # Jetzt dasselbe mit denen, die direkt in der Template-Datei definiert
254 # sind, machen. Ich weiss, dass das eine ziemlich unsaubere Loesung ist,
255 # aber es funktioniert
257 self
.get_defined_vars()
259 for var
in self
.defined_vars
:
260 val
= self
.get_var(var
)
262 self
.parse_if_block(var
,val
)
265 # {TRIM}-Bloecke entfernen
267 self
.parse_trim_blocks()
271 # Variablen durch Text ersetzen
273 # Parameter: 1. Variable zum Ersetzen
274 # 2. Text, durch den die Variable ersetzt werden soll
276 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
278 def fillin(self
,var
,text
):
279 if text
is None: text
= ''
281 template
= self
.get_template();
282 template
= template
.replace('{'+str(var
)+'}',str(text
));
284 self
.set_template(template
);
288 # Variable durch Array ersetzen
290 # Parameter: 1. Variable zum Ersetzen
291 # 2. Array, durch das die Variable ersetzt werden soll
292 # 3. Zeichenkette, mit der das Array verbunden werden soll
295 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
297 def fillin_array(self
,var
,array
,glue
=''):
298 self
.fillin(var
,str(glue
).join(array
))
302 # Variablen eine nach der anderen ersetzen. Sollte in einer Variable eine
303 # andere Variable auftauchen, so wird diese nicht ersetzt.
305 # Parameter: Array mit zu parsenden Variablen (optional)
307 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
309 def replace_vars(self
,valid
=None):
310 template
= self
.get_template()
313 valid_vars
= self
.get_var()
319 while x
< len(template
):
320 if template
[x
] == '{':
321 for var
in valid_vars
:
322 # Pruefen, ob hier eine gueltige Variable beginnt
324 if template
[x
+1:x
+len(var
)+2] == var
+ '}':
325 if type(self
.get_var(var
)) is list:
326 content
= ''.join(self
.get_var(var
))
328 # Muss es nochmal zum String machen
329 # Hilft gegen den "ordinal not in range(128)"-Fehler
330 # Habe aber keine Ahnung, welche neuen Probleme das verursachen koennte
332 content
= str(self
.get_var(var
))
334 if content
is None: content
= ''
336 # Daten vor und nach der Variable
339 post
= template
[len(pre
)+2+len(var
):]
341 # Alles neu zusammensetzen
343 template
= pre
+ content
+ post
347 x
= len(pre
+ content
) - 1
351 self
.set_template(template
)
355 # Template in Datei schreiben
357 # Parameter: Datei-Handle
359 # Rueckgabe: Status-Code (Boolean)
361 def to_file(self
,handle
):
362 return handle
.write(self
.get_template())
366 # Den gesicherten Stand des Template-Textes wiederherstellen
368 # Parameter: -nichts-
370 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
373 self
.template
= self
.original
377 # Aktuellen Stand des Template-Textes sichern
378 # (alte Sicherung wird ueberschrieben)
380 # Parameter: -nichts-
382 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
384 def save_state(self
):
385 self
.original
= self
.template
389 # Eine Schleife parsen
391 # Parameter: Name der Schleife
393 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
395 def parse_loop(self
,name
):
396 template
= self
.get_template()
397 if template
.find('{LOOP '+name
+'}') == -1: return
402 while template
.find('{LOOP '+name
+'}',offset
) != -1:
403 begin
= template
.find('{LOOP '+name
+'}',offset
)
405 if template
.find('{ENDLOOP}',begin
+6+name_len
) != -1:
406 end
= template
.find('{ENDLOOP}',begin
+6+name_len
)
408 block
= template
[begin
:end
+9]
409 content
= block
[name_len
+7:-9]
415 while x
< len(self
.loop_vars
[name
]):
416 loop_data
= self
.loop_vars
[name
][x
]
417 loop_vars
= loop_data
.keys()
420 ctpl
.set_template(content
)
422 for loop_var
in loop_vars
:
423 ctpl
.set_var(name
+'.'+loop_var
,loop_data
[loop_var
])
430 parsed_block
+= ctpl
.get_template()
435 template
= template
.replace(block
,parsed_block
)
436 offset
= begin
+len(parsed_block
)
441 self
.set_template(template
)
445 # In der Template-Datei definierte Variablen auslesen
447 # Parameter: -nichts-
449 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
451 def get_defined_vars(self
):
452 template
= self
.get_template()
453 if template
.find('{DEFINE ') == -1: return
457 while template
.find('{DEFINE ',offset
) != -1:
458 begin
= template
.find('{DEFINE ',offset
)+8
470 while x
< len(template
):
471 if template
[x
] == '\012' or template
[x
] == '\015':
472 # Wenn in einem {DEFINE}-Block ein Zeilenumbruch gefunden wird,
473 # brechen wir mit dem Parsen des Blockes ab
478 if template
[x
] == '"':
479 # Der Inhalt der Variable ist hier zu Ende
483 if template
[x
+1] == '}':
484 # Hier ist der Block zu Ende
486 if self
.get_var(name
) is None:
487 # Die Variable wird nur gesetzt, wenn sie nicht bereits gesetzt ist
489 self
.set_var(name
,content
)
490 self
.defined_vars
.append(name
)
492 # {DEFINE}-Block entfernen
494 pre
= template
[0:begin
-8]
495 post
= template
[x
+2:]
504 elif template
[x
] == '\\':
505 # Ein Backslash wurde gefunden, er dient zum Escapen von Zeichen
507 if template
[x
+1] == 'n':
508 # "\n" in Zeilenumbrueche umwandeln
512 content
+= template
[x
+1]
517 content
+= template
[x
]
522 if template
[x
] == '"':
528 # Variablennamen auslesen
530 if template
[x
] == '}' and name
!= '':
531 # Wir haben einen {DEFINE}-Block
536 # Alles ab hier sollte mit dem Teil verbunden werden, der das
537 # {DEFINE} in einer Zeile verarbeitet
539 # Der Parser fuer {DEFINE}-Bloecke ist nicht rekursiv, was auch
540 # nicht noetig sein sollte
542 if template
.find('{ENDDEFINE}',x
) != -1:
543 end
= template
.find('{ENDDEFINE}',x
)
546 content
= template
[x
:end
]
548 if self
.get_var(name
) is None:
549 # Die Variable wird nur gesetzt, wenn sie nicht bereits gesetzt ist
551 self
.set_var(name
,content
)
552 self
.defined_vars
.append(name
)
554 pre
= template
[0:begin
-8]
555 post
= template
[end
+11:]
557 template
= pre
+ post
567 elif template
[x
] != ' ':
570 elif template
[x
] != '':
578 self
.set_template(template
)
582 # IF-Bloecke verarbeiten
584 # Parameter: 1. Name des IF-Blocks (das, was nach dem IF steht)
585 # 2. Status-Code (true => Inhalt anzeigen
586 # false => Inhalt nicht anzeigen
588 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
590 def parse_if_block(self
,name
,state
,no_negate
=0):
592 template
= self
.get_template()
596 while template
.find('{IF '+name
+'}') >= 0:
599 start
= template
.find('{IF '+name
+'}')
600 tpl_tmp
= template
[start
:]
601 splitted
= tpl_tmp
.split('{ENDIF}')
603 block
= '' # Kompletter bedingter Block
604 ifs
= 0 # IF-Zaehler (wird fuer jedes IF erhoeht und fuer jedes ENDIF erniedrigt)
610 while x
< len(splitted
):
611 # Verschachtelungsfehler abfangen
612 if x
== len(splitted
)-1: raise TplClassIFNestingError(self
.file,name
,count
)
614 ifs
+= splitted
[x
].count('{IF ') # Zum Zaehler jedes Vorkommen von IF hinzuzaehlen
615 ifs
-= 1 # Zaehler um 1 erniedrigen
616 block
+= splitted
[x
]+'{ENDIF}' # Daten zum Block hinzufuegen
621 # Zaehler wieder 0, also haben wir das Ende des IF-Blocks gefunden :-))
624 if_block
= block
[len(name
)+5:-7] # Alles zwischen {IF} und {ENDIF}
628 else_block
= '' # Alles ab {ELSE}
631 splitted
= if_block
.split('{ELSE}');
635 while x
< len(splitted
):
636 ifs
+= splitted
[x
].count('{IF ') # Zum Zaehler jedes Vorkommen von IF hinzuzaehlen
637 ifs
-= splitted
[x
].count('{ENDIF}') # Vom Zaehler jedes Vorkommen von ENDIF abziehen
642 # Zaehler 0, also haben wir das Ende des IF-Abschnitts gefunden
644 # Aus dem Rest den ELSE-Block zusammenbauen
648 while y
< len(splitted
):
649 else_block
+= '{ELSE}'+splitted
[y
]
653 if_block
= if_block
[0:len(if_block
)-len(else_block
)]
654 else_block
= else_block
[6:]
659 replacement
= if_block
661 replacement
= else_block
663 template
= template
.replace(block
,replacement
)
665 self
.set_template(template
)
667 # Evtl. verneinte Form parsen
670 self
.parse_if_block('!'+name
,not state
,1)
674 # IF-Bloecke zu allen definierten Variablen verarbeiten
676 # Parameter: Array mit zu verarbeitenden IF-Bloecken (optional)
678 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
680 def parse_if_blocks(self
,valid
=None):
682 valid_vars
= self
.get_var()
686 for valid_var
in valid_vars
:
687 self
.parse_if_block(valid_var
,self
.get_var(valid_var
))
689 # parse_trim_blocks()
691 # {TRIM}-Bloecke parsen
693 # Dieser Parser ist nicht rekursiv, was auch nicht
694 # noetig sein sollte.
696 # Parameter: -nichts-
698 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
700 def parse_trim_blocks(self
):
701 template
= self
.get_template()
702 if template
.find('{TRIM}') == -1: return
706 while template
.find('{TRIM}',offset
) >= 0:
707 begin
= template
.find('{TRIM}',offset
)
709 if template
.find('{ENDTRIM}',begin
+6) >= 0:
710 end
= template
.find('{ENDTRIM}',begin
+6)
712 block
= template
[begin
:end
+9]
713 content
= block
[6:-9]
715 trimmed
= content
.strip()
717 template
= template
.replace(block
,trimmed
)
719 offset
= begin
+len(trimmed
)
723 self
.set_template(template
)
727 # Bedingungstags in einem Vorlagentext verarbeiten
729 # Parameter: 1. Tagname
730 # 2. Status-Code (true => Tag-Inhalt anzeigen
731 # false => Tag-Inhalt nicht anzeigen
733 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
735 def parse_condtag(self
,condtag
,state
):
736 condtag
= str(condtag
)
737 template
= self
.get_template()
739 while template
.find('<'+condtag
+'>') >= 0:
740 start
= template
.find('<'+condtag
+'>') # Beginn des Blocks
741 end
= template
.find('</'+condtag
+'>')+len(condtag
)+3 # Ende des Blocks
743 extract
= template
[start
:end
] # Kompletten Bedingungsblock extrahieren...
746 replacement
= extract
[len(condtag
)+2:0-len(condtag
)-3]
750 template
= template
.replace(extract
,replacement
) # Block durch neue Daten ersetzen
752 self
.set_template(template
)
756 # {INCLUDE}-Anweisungen verarbeiten
758 # Parameter: -nichts-
760 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
762 def parse_includes(self
):
763 template
= self
.get_template()
764 if template
.find('{INCLUDE ') == -1: return
768 while template
.find('{INCLUDE ',offset
) >= 0:
769 begin
= template
.find('{INCLUDE ',offset
)
775 if template
[start
] == '"':
784 while x
< len(template
):
785 if template
[x
] == '\012' or template
[x
] == '\015':
788 elif long == 0 and template
[x
] == ' ':
791 elif long == 1 and template
[x
] == '"':
792 if template
[x
+1] != '}': skip
= 1
794 elif long == 0 and template
[x
] == '}':
801 if skip
== 1: continue
806 if not os
.path
.isabs(file):
807 dir = os
.path
.dirname(self
.file)
808 if not dir: dir = '.'
809 filepath
= os
.path
.normpath(dir+'/'+file)
811 if os
.path
.isfile(filepath
):
815 if long == 1: end
= start
+ len(file) + 2
816 else: end
= start
+ len(file) + 1
818 pre
= template
[0:begin
]
819 post
= template
[end
:]
821 template
= pre
+inc
.get_template()+post
822 offset
= len(pre
)+len(inc
.get_template())
826 self
.set_template(template
)
828 # Klasse zum Erzeugen des Fehlers bei falsch verschachtelten
831 class TplClassIFNestingError
:
833 def __init__(self
,file,name
,count
):
839 return 'Nesting error found while parsing IF block "'+self
.name
+'" nr. '+str(self
.count
)+' in template file "'+self
.file+'"'
patrick-canterino.de