]>
git.p6c8.net - template-class.git/blob - template.py
3 # Template (Version 2.5)
5 # Klasse zum Parsen von Templates
7 # Autor: Patrick Canterino <patrick@patshaping.de>
8 # Letzte Aenderung: 25.11.2011
10 # Copyright (C) 2002-2011 Patrick Canterino
12 # Diese Datei kann unter den Bedingungen der "Artistic License 2.0"
13 # weitergegeben und / oder veraendert werden.
15 # http://www.opensource.org/licenses/artistic-license-2.0
37 self
.defined_vars
= []
42 # Kompletten Vorlagentext zurueckgeben
46 # Rueckgabe: Kompletter Vorlagentext (String)
48 def get_template(self
):
49 return str(self
.template
)
53 # Kompletten Vorlagentext aendern
55 # Parameter: Vorlagentext
57 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
59 def set_template(self
,template
):
60 self
.template
= str(template
)
64 # Vorlagentext ans Template-Objekt anhaengen
66 # Parameter: Vorlagentext
68 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
70 def add_text(self
,text
):
71 self
.set_template(self
.get_template()+str(text
))
75 # Einlesen einer Vorlagendatei und {INCLUDE}-Anweisungen ggf. verarbeiten
76 # (Text wird an bereits vorhandenen Text angehaengt)
78 # Parameter: 1. Datei zum Einlesen
79 # 2. Status-Code (Boolean):
80 # true => {INCLUDE}-Anweisungen nicht verarbeiten
81 # false => {INCLUDE}-Anweisungen verarbeiten (Standard)
83 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
85 def read_file(self
,file,not_include
=0):
92 self
.add_text(content
)
95 if not not_include
: self
.parse_includes()
99 # Wert einer Variable setzen
101 # Parameter: 1. Name der Variable
102 # 2. Wert, den die Variable erhalten soll
104 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
106 def set_var(self
,var
,content
):
107 self
.vars[var
] = content
111 # Wert einer Variable zurueckgeben
113 # Parameter: (optional) Variablenname
115 # Rueckgabe: Wert der Variable;
116 # wenn die Variable nicht existiert, false;
117 # wenn kein Variablenname angegeben wurde, wird ein
118 # Array mit den Variablennamen zurueckgegeben
120 def get_var(self
,var
=None):
122 if self
.vars.has_key(var
):
123 return self
.vars[var
]
127 return self
.vars.keys()
131 # Komplettes Variablen-Array mit einem anderen Array ueberschreiben
135 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
137 def set_vars(self
,vars):
142 # Zum bestehenden Variablen-Array weitere Variablen in Form eines Arrays
147 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
149 def add_vars(self
,vars):
150 self
.vars.update(vars)
154 # Daten fuer eine Schleife setzen
156 # Parameter: 1. Name der Schleife
157 # 2. Array mit den Dictionaries mit den Variablen fuer
158 # die Schleifendurchgaenge
160 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
162 def set_loop_data(self
,loop
,data
):
163 self
.loop_vars
[loop
] = data
167 # Daten fuer einen Schleifendurchgang hinzufuegen
169 # Parameter: 1. Name der Schleife
170 # 2. Dictionary mit den Variablen fuer den
173 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
175 def add_loop_data(self
,loop
,data
):
176 if self
.loop_vars
.has_key(loop
) and type(self
.loop_vars
[loop
]) is list:
177 self
.loop_vars
[loop
].append(data
)
179 self
.loop_vars
[loop
] = [data
]
183 # In der Template definierte Variablen auslesen, Variablen
184 # ersetzen, {IF}- und {TRIM}-Bloecke parsen
186 # Parameter: -nichts-
188 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
191 if self
.old_parsing
: return self
.parse_old()
193 # Zuerst die Schleifen parsen
195 if self
.loop_vars
and self
.loop_vars
.keys():
196 loops
= self
.loop_vars
.keys()
199 self
.parse_loop(loop
)
201 # In Template-Datei definierte Variablen auslesen
203 self
.get_defined_vars()
207 vars = self
.get_var()
209 if vars is not None and type(vars) is list:
210 self
.parse_if_blocks()
213 # {TRIM}-Bloecke entfernen
215 self
.parse_trim_blocks()
219 # In der Template definierte Variablen auslesen, Variablen
220 # ersetzen, {IF}- und {TRIM}-Bloecke parsen
223 # Parameter: -nichts-
225 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
228 # Zuerst die Schleifen parsen
230 if self
.loop_vars
and self
.loop_vars
.keys():
231 loops
= self
.loop_vars
.keys()
234 self
.parse_loop(loop
)
236 # Normale Variablen durchgehen
239 vars = self
.get_var()
242 val
= self
.get_var(var
)
244 self
.parse_if_block(var
,val
)
246 if type(val
) is list:
247 self
.fillin_array(var
,val
)
251 # Jetzt dasselbe mit denen, die direkt in der Template-Datei definiert
252 # sind, machen. Ich weiss, dass das eine ziemlich unsaubere Loesung ist,
253 # aber es funktioniert
255 self
.get_defined_vars()
257 for var
in self
.defined_vars
:
258 val
= self
.get_var(var
)
260 self
.parse_if_block(var
,val
)
263 # {TRIM}-Bloecke entfernen
265 self
.parse_trim_blocks()
269 # Variablen durch Text ersetzen
271 # Parameter: 1. Variable zum Ersetzen
272 # 2. Text, durch den die Variable ersetzt werden soll
274 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
276 def fillin(self
,var
,text
):
277 if text
is None: text
= ''
279 template
= self
.get_template();
280 template
= template
.replace('{'+str(var
)+'}',str(text
));
282 self
.set_template(template
);
286 # Variable durch Array ersetzen
288 # Parameter: 1. Variable zum Ersetzen
289 # 2. Array, durch das die Variable ersetzt werden soll
290 # 3. Zeichenkette, mit der das Array verbunden werden soll
293 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
295 def fillin_array(self
,var
,array
,glue
=''):
296 self
.fillin(var
,str(glue
).join(array
))
300 # Variablen eine nach der anderen ersetzen. Sollte in einer Variable eine
301 # andere Variable auftauchen, so wird diese nicht ersetzt.
303 # Parameter: Array mit zu parsenden Variablen (optional)
305 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
307 def replace_vars(self
,valid
=None):
308 template
= self
.get_template()
311 valid_vars
= self
.get_var()
317 while x
< len(template
):
318 if template
[x
] == '{':
319 for var
in valid_vars
:
320 # Pruefen, ob hier eine gueltige Variable beginnt
322 if template
[x
+1:x
+len(var
)+2] == var
+ '}':
323 if type(self
.get_var(var
)) is list:
324 content
= ''.join(self
.get_var(var
))
326 # Muss es nochmal zum String machen
327 # Hilft gegen den "ordinal not in range(128)"-Fehler
328 # Habe aber keine Ahnung, welche neuen Probleme das verursachen koennte
330 content
= str(self
.get_var(var
))
332 if content
is None: content
= ''
334 # Daten vor und nach der Variable
337 post
= template
[len(pre
)+2+len(var
):]
339 # Alles neu zusammensetzen
341 template
= pre
+ content
+ post
345 x
= len(pre
+ content
) - 1
349 self
.set_template(template
)
353 # Template in Datei schreiben
355 # Parameter: Datei-Handle
357 # Rueckgabe: Status-Code (Boolean)
359 def to_file(self
,handle
):
360 return handle
.write(self
.get_template())
364 # Den gesicherten Stand des Template-Textes wiederherstellen
366 # Parameter: -nichts-
368 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
371 self
.template
= self
.original
375 # Aktuellen Stand des Template-Textes sichern
376 # (alte Sicherung wird ueberschrieben)
378 # Parameter: -nichts-
380 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
382 def save_state(self
):
383 self
.original
= self
.template
387 # Eine Schleife parsen
389 # Parameter: Name der Schleife
391 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
393 def parse_loop(self
,name
):
394 template
= self
.get_template()
395 if template
.find('{LOOP '+name
+'}') == -1: return
400 while template
.find('{LOOP '+name
+'}',offset
) != -1:
401 begin
= template
.find('{LOOP '+name
+'}',offset
)
403 if template
.find('{ENDLOOP}',begin
+6+name_len
) != -1:
404 end
= template
.find('{ENDLOOP}',begin
+6+name_len
)
406 block
= template
[begin
:end
+9]
407 content
= block
[name_len
+7:-9]
413 while x
< len(self
.loop_vars
[name
]):
414 loop_data
= self
.loop_vars
[name
][x
]
415 loop_vars
= loop_data
.keys()
418 ctpl
.set_template(content
)
420 for loop_var
in loop_vars
:
421 ctpl
.set_var(name
+'.'+loop_var
,loop_data
[loop_var
])
428 parsed_block
+= ctpl
.get_template()
433 template
= template
.replace(block
,parsed_block
)
434 offset
= begin
+len(parsed_block
)
439 self
.set_template(template
)
443 # In der Template-Datei definierte Variablen auslesen
445 # Parameter: -nichts-
447 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
449 def get_defined_vars(self
):
450 template
= self
.get_template()
451 if template
.find('{DEFINE ') == -1: return
455 while template
.find('{DEFINE ',offset
) != -1:
456 begin
= template
.find('{DEFINE ',offset
)+8
468 while x
< len(template
):
469 if template
[x
] == '\012' or template
[x
] == '\015':
470 # Wenn in einem {DEFINE}-Block ein Zeilenumbruch gefunden wird,
471 # brechen wir mit dem Parsen des Blockes ab
476 if template
[x
] == '"':
477 # Der Inhalt der Variable ist hier zu Ende
481 if template
[x
+1] == '}':
482 # Hier ist der Block zu Ende
484 if self
.get_var(name
) is None:
485 # Die Variable wird nur gesetzt, wenn sie nicht bereits gesetzt ist
487 self
.set_var(name
,content
)
488 self
.defined_vars
.append(name
)
490 # {DEFINE}-Block entfernen
492 pre
= template
[0:begin
-8]
493 post
= template
[x
+2:]
502 elif template
[x
] == '\\':
503 # Ein Backslash wurde gefunden, er dient zum Escapen von Zeichen
505 if template
[x
+1] == 'n':
506 # "\n" in Zeilenumbrueche umwandeln
510 content
+= template
[x
+1]
515 content
+= template
[x
]
520 if template
[x
] == '"':
526 # Variablennamen auslesen
528 if template
[x
] == '}' and name
!= '':
529 # Wir haben einen {DEFINE}-Block
534 # Alles ab hier sollte mit dem Teil verbunden werden, der das
535 # {DEFINE} in einer Zeile verarbeitet
537 # Der Parser fuer {DEFINE}-Bloecke ist nicht rekursiv, was auch
538 # nicht noetig sein sollte
540 if template
.find('{ENDDEFINE}',x
) != -1:
541 end
= template
.find('{ENDDEFINE}',x
)
544 content
= template
[x
:end
]
546 if self
.get_var(name
) is None:
547 # Die Variable wird nur gesetzt, wenn sie nicht bereits gesetzt ist
549 self
.set_var(name
,content
)
550 self
.defined_vars
.append(name
)
552 pre
= template
[0:begin
-8]
553 post
= template
[end
+11:]
555 template
= pre
+ post
565 elif template
[x
] != ' ':
568 elif template
[x
] != '':
576 self
.set_template(template
)
580 # IF-Bloecke verarbeiten
582 # Parameter: 1. Name des IF-Blocks (das, was nach dem IF steht)
583 # 2. Status-Code (true => Inhalt anzeigen
584 # false => Inhalt nicht anzeigen
586 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
588 def parse_if_block(self
,name
,state
,no_negate
=0):
590 template
= self
.get_template()
594 while template
.find('{IF '+name
+'}') >= 0:
597 start
= template
.find('{IF '+name
+'}')
598 tpl_tmp
= template
[start
:]
599 splitted
= tpl_tmp
.split('{ENDIF}')
601 block
= '' # Kompletter bedingter Block
602 ifs
= 0 # IF-Zaehler (wird fuer jedes IF erhoeht und fuer jedes ENDIF erniedrigt)
608 while x
< len(splitted
):
609 # Verschachtelungsfehler abfangen
610 if x
== len(splitted
)-1: raise TplClassIFNestingError(self
.file,name
,count
)
612 ifs
+= splitted
[x
].count('{IF ') # Zum Zaehler jedes Vorkommen von IF hinzuzaehlen
613 ifs
-= 1 # Zaehler um 1 erniedrigen
614 block
+= splitted
[x
]+'{ENDIF}' # Daten zum Block hinzufuegen
619 # Zaehler wieder 0, also haben wir das Ende des IF-Blocks gefunden :-))
622 if_block
= block
[len(name
)+5:-7] # Alles zwischen {IF} und {ENDIF}
626 else_block
= '' # Alles ab {ELSE}
629 splitted
= if_block
.split('{ELSE}');
633 while x
< len(splitted
):
634 ifs
+= splitted
[x
].count('{IF ') # Zum Zaehler jedes Vorkommen von IF hinzuzaehlen
635 ifs
-= splitted
[x
].count('{ENDIF}') # Vom Zaehler jedes Vorkommen von ENDIF abziehen
640 # Zaehler 0, also haben wir das Ende des IF-Abschnitts gefunden
642 # Aus dem Rest den ELSE-Block zusammenbauen
646 while y
< len(splitted
):
647 else_block
+= '{ELSE}'+splitted
[y
]
651 if_block
= if_block
[0:len(if_block
)-len(else_block
)]
652 else_block
= else_block
[6:]
657 replacement
= if_block
659 replacement
= else_block
661 template
= template
.replace(block
,replacement
)
663 self
.set_template(template
)
665 # Evtl. verneinte Form parsen
668 self
.parse_if_block('!'+name
,not state
,1)
672 # IF-Bloecke zu allen definierten Variablen verarbeiten
674 # Parameter: Array mit zu verarbeitenden IF-Bloecken (optional)
676 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
678 def parse_if_blocks(self
,valid
=None):
680 valid_vars
= self
.get_var()
684 for valid_var
in valid_vars
:
685 self
.parse_if_block(valid_var
,self
.get_var(valid_var
))
687 # parse_trim_blocks()
689 # {TRIM}-Bloecke parsen
691 # Dieser Parser ist nicht rekursiv, was auch nicht
692 # noetig sein sollte.
694 # Parameter: -nichts-
696 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
698 def parse_trim_blocks(self
):
699 template
= self
.get_template()
700 if template
.find('{TRIM}') == -1: return
704 while template
.find('{TRIM}',offset
) >= 0:
705 begin
= template
.find('{TRIM}',offset
)
707 if template
.find('{ENDTRIM}',begin
+6) >= 0:
708 end
= template
.find('{ENDTRIM}',begin
+6)
710 block
= template
[begin
:end
+9]
711 content
= block
[6:-9]
713 trimmed
= content
.strip()
715 template
= template
.replace(block
,trimmed
)
717 offset
= begin
+len(trimmed
)
721 self
.set_template(template
)
725 # Bedingungstags in einem Vorlagentext verarbeiten
727 # Parameter: 1. Tagname
728 # 2. Status-Code (true => Tag-Inhalt anzeigen
729 # false => Tag-Inhalt nicht anzeigen
731 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
733 def parse_condtag(self
,condtag
,state
):
734 condtag
= str(condtag
)
735 template
= self
.get_template()
737 while template
.find('<'+condtag
+'>') >= 0:
738 start
= template
.find('<'+condtag
+'>') # Beginn des Blocks
739 end
= template
.find('</'+condtag
+'>')+len(condtag
)+3 # Ende des Blocks
741 extract
= template
[start
:end
] # Kompletten Bedingungsblock extrahieren...
744 replacement
= extract
[len(condtag
)+2:0-len(condtag
)-3]
748 template
= template
.replace(extract
,replacement
) # Block durch neue Daten ersetzen
750 self
.set_template(template
)
754 # {INCLUDE}-Anweisungen verarbeiten
756 # Parameter: -nichts-
758 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
760 def parse_includes(self
):
761 template
= self
.get_template()
762 if template
.find('{INCLUDE ') == -1: return
766 while template
.find('{INCLUDE ',offset
) >= 0:
767 begin
= template
.find('{INCLUDE ',offset
)
773 if template
[start
] == '"':
782 while x
< len(template
):
783 if template
[x
] == '\012' or template
[x
] == '\015':
786 elif long == 0 and template
[x
] == ' ':
789 elif long == 1 and template
[x
] == '"':
790 if template
[x
+1] != '}': skip
= 1
792 elif long == 0 and template
[x
] == '}':
799 if skip
== 1: continue
804 if not os
.path
.isabs(file):
805 dir = os
.path
.dirname(self
.file)
806 if not dir: dir = '.'
807 filepath
= os
.path
.normpath(dir+'/'+file)
809 if os
.path
.isfile(filepath
):
813 if long == 1: end
= start
+ len(file) + 2
814 else: end
= start
+ len(file) + 1
816 pre
= template
[0:begin
]
817 post
= template
[end
:]
819 template
= pre
+inc
.get_template()+post
820 offset
= len(pre
)+len(inc
.get_template())
824 self
.set_template(template
)
826 # Klasse zum Erzeugen des Fehlers bei falsch verschachtelten
829 class TplClassIFNestingError
:
831 def __init__(self
,file,name
,count
):
837 return 'Nesting error found while parsing IF block "'+self
.name
+'" nr. '+str(self
.count
)+' in template file "'+self
.file+'"'
patrick-canterino.de