]>
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: 26.4.2013
10 # Copyright (C) 2002-2013 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 if content
is None: content
= ''
108 self
.vars[var
] = content
112 # Wert einer Variable zurueckgeben
114 # Parameter: (optional) Variablenname
116 # Rueckgabe: Wert der Variable;
117 # wenn die Variable nicht existiert, false;
118 # wenn kein Variablenname angegeben wurde, wird ein
119 # Array mit den Variablennamen zurueckgegeben
121 def get_var(self
,var
=None):
124 return self
.vars[var
]
128 return self
.vars.keys()
132 # Komplettes Variablen-Array mit einem anderen Array ueberschreiben
136 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
138 def set_vars(self
,vars):
143 # Zum bestehenden Variablen-Array weitere Variablen in Form eines Arrays
148 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
150 def add_vars(self
,vars):
151 self
.vars.update(vars)
155 # Daten fuer eine Schleife setzen
157 # Parameter: 1. Name der Schleife
158 # 2. Array mit den Dictionaries mit den Variablen fuer
159 # die Schleifendurchgaenge
161 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
163 def set_loop_data(self
,loop
,data
):
164 self
.loop_vars
[loop
] = data
168 # Daten fuer einen Schleifendurchgang hinzufuegen
170 # Parameter: 1. Name der Schleife
171 # 2. Dictionary mit den Variablen fuer den
174 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
176 def add_loop_data(self
,loop
,data
):
177 if loop
in self
.loop_vars
and type(self
.loop_vars
[loop
]) is list:
178 self
.loop_vars
[loop
].append(data
)
180 self
.loop_vars
[loop
] = [data
]
184 # In der Template definierte Variablen auslesen, Variablen
185 # ersetzen, {IF}- und {TRIM}-Bloecke parsen
187 # Parameter: -nichts-
189 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
192 if self
.old_parsing
: return self
.parse_old()
194 # Zuerst die Schleifen parsen
196 if self
.loop_vars
and self
.loop_vars
.keys():
197 loops
= self
.loop_vars
.keys()
200 self
.parse_loop(loop
)
202 # In Template-Datei definierte Variablen auslesen
204 self
.get_defined_vars()
208 vars = self
.get_var()
210 if vars is not None and len(vars) > 0:
211 self
.parse_if_blocks()
214 # {TRIM}-Bloecke entfernen
216 self
.parse_trim_blocks()
220 # In der Template definierte Variablen auslesen, Variablen
221 # ersetzen, {IF}- und {TRIM}-Bloecke parsen
224 # Parameter: -nichts-
226 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
229 # Zuerst die Schleifen parsen
231 if self
.loop_vars
and self
.loop_vars
.keys():
232 loops
= self
.loop_vars
.keys()
235 self
.parse_loop(loop
)
237 # Normale Variablen durchgehen
240 vars = self
.get_var()
243 val
= self
.get_var(var
)
245 self
.parse_if_block(var
,val
)
247 if type(val
) is list:
248 self
.fillin_array(var
,val
)
252 # Jetzt dasselbe mit denen, die direkt in der Template-Datei definiert
253 # sind, machen. Ich weiss, dass das eine ziemlich unsaubere Loesung ist,
254 # aber es funktioniert
256 self
.get_defined_vars()
258 for var
in self
.defined_vars
:
259 val
= self
.get_var(var
)
261 self
.parse_if_block(var
,val
)
264 # {TRIM}-Bloecke entfernen
266 self
.parse_trim_blocks()
270 # Variablen durch Text ersetzen
272 # Parameter: 1. Variable zum Ersetzen
273 # 2. Text, durch den die Variable ersetzt werden soll
275 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
277 def fillin(self
,var
,text
):
278 if text
is None: text
= ''
280 template
= self
.get_template();
281 template
= template
.replace('{'+str(var
)+'}',str(text
));
283 self
.set_template(template
);
287 # Variable durch Array ersetzen
289 # Parameter: 1. Variable zum Ersetzen
290 # 2. Array, durch das die Variable ersetzt werden soll
291 # 3. Zeichenkette, mit der das Array verbunden werden soll
294 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
296 def fillin_array(self
,var
,array
,glue
=''):
297 self
.fillin(var
,str(glue
).join(array
))
301 # Variablen eine nach der anderen ersetzen. Sollte in einer Variable eine
302 # andere Variable auftauchen, so wird diese nicht ersetzt.
304 # Parameter: Array mit zu parsenden Variablen (optional)
306 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
308 def replace_vars(self
,valid
=None):
309 template
= self
.get_template()
312 valid_vars
= self
.get_var()
318 while x
< len(template
):
319 if template
[x
] == '{':
320 for var
in valid_vars
:
321 # Pruefen, ob hier eine gueltige Variable beginnt
323 if template
[x
+1:x
+len(var
)+2] == var
+ '}':
324 if type(self
.get_var(var
)) is list:
325 content
= ''.join(self
.get_var(var
))
327 # Muss es nochmal zum String machen
328 # Hilft gegen den "ordinal not in range(128)"-Fehler
329 # Habe aber keine Ahnung, welche neuen Probleme das verursachen koennte
331 content
= str(self
.get_var(var
))
333 if content
is None: content
= ''
335 # Daten vor und nach der Variable
338 post
= template
[len(pre
)+2+len(var
):]
340 # Alles neu zusammensetzen
342 template
= pre
+ content
+ post
346 x
= len(pre
+ content
) - 1
350 self
.set_template(template
)
354 # Template in Datei schreiben
356 # Parameter: Datei-Handle
358 # Rueckgabe: Status-Code (Boolean)
360 def to_file(self
,handle
):
361 return handle
.write(self
.get_template())
365 # Den gesicherten Stand des Template-Textes wiederherstellen
367 # Parameter: -nichts-
369 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
372 self
.template
= self
.original
376 # Aktuellen Stand des Template-Textes sichern
377 # (alte Sicherung wird ueberschrieben)
379 # Parameter: -nichts-
381 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
383 def save_state(self
):
384 self
.original
= self
.template
388 # Eine Schleife parsen
390 # Parameter: Name der Schleife
392 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
394 def parse_loop(self
,name
):
395 template
= self
.get_template()
396 if template
.find('{LOOP '+name
+'}') == -1: return
401 while template
.find('{LOOP '+name
+'}',offset
) != -1:
402 begin
= template
.find('{LOOP '+name
+'}',offset
)
404 if template
.find('{ENDLOOP}',begin
+6+name_len
) != -1:
405 end
= template
.find('{ENDLOOP}',begin
+6+name_len
)
407 block
= template
[begin
:end
+9]
408 content
= block
[name_len
+7:-9]
414 while x
< len(self
.loop_vars
[name
]):
415 loop_data
= self
.loop_vars
[name
][x
]
416 loop_vars
= loop_data
.keys()
419 ctpl
.set_template(content
)
421 for loop_var
in loop_vars
:
422 ctpl
.set_var(name
+'.'+loop_var
,loop_data
[loop_var
])
429 parsed_block
+= ctpl
.get_template()
434 template
= template
.replace(block
,parsed_block
)
435 offset
= begin
+len(parsed_block
)
440 self
.set_template(template
)
444 # In der Template-Datei definierte Variablen auslesen
446 # Parameter: -nichts-
448 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
450 def get_defined_vars(self
):
451 template
= self
.get_template()
452 if template
.find('{DEFINE ') == -1: return
456 while template
.find('{DEFINE ',offset
) != -1:
457 begin
= template
.find('{DEFINE ',offset
)+8
469 while x
< len(template
):
470 if template
[x
] == '\012' or template
[x
] == '\015':
471 # Wenn in einem {DEFINE}-Block ein Zeilenumbruch gefunden wird,
472 # brechen wir mit dem Parsen des Blockes ab
477 if template
[x
] == '"':
478 # Der Inhalt der Variable ist hier zu Ende
482 if template
[x
+1] == '}':
483 # Hier ist der Block zu Ende
485 if self
.get_var(name
) is None:
486 # Die Variable wird nur gesetzt, wenn sie nicht bereits gesetzt ist
488 self
.set_var(name
,content
)
489 self
.defined_vars
.append(name
)
491 # {DEFINE}-Block entfernen
493 pre
= template
[0:begin
-8]
494 post
= template
[x
+2:]
503 elif template
[x
] == '\\':
504 # Ein Backslash wurde gefunden, er dient zum Escapen von Zeichen
506 if template
[x
+1] == 'n':
507 # "\n" in Zeilenumbrueche umwandeln
511 content
+= template
[x
+1]
516 content
+= template
[x
]
521 if template
[x
] == '"':
527 # Variablennamen auslesen
529 if template
[x
] == '}' and name
!= '':
530 # Wir haben einen {DEFINE}-Block
535 # Alles ab hier sollte mit dem Teil verbunden werden, der das
536 # {DEFINE} in einer Zeile verarbeitet
538 # Der Parser fuer {DEFINE}-Bloecke ist nicht rekursiv, was auch
539 # nicht noetig sein sollte
541 if template
.find('{ENDDEFINE}',x
) != -1:
542 end
= template
.find('{ENDDEFINE}',x
)
545 content
= template
[x
:end
]
547 if self
.get_var(name
) is None:
548 # Die Variable wird nur gesetzt, wenn sie nicht bereits gesetzt ist
550 self
.set_var(name
,content
)
551 self
.defined_vars
.append(name
)
553 pre
= template
[0:begin
-8]
554 post
= template
[end
+11:]
556 template
= pre
+ post
566 elif template
[x
] != ' ':
569 elif template
[x
] != '':
577 self
.set_template(template
)
581 # IF-Bloecke verarbeiten
583 # Parameter: 1. Name des IF-Blocks (das, was nach dem IF steht)
584 # 2. Status-Code (true => Inhalt anzeigen
585 # false => Inhalt nicht anzeigen
587 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
589 def parse_if_block(self
,name
,state
,no_negate
=0):
591 template
= self
.get_template()
595 while template
.find('{IF '+name
+'}') >= 0:
598 start
= template
.find('{IF '+name
+'}')
599 tpl_tmp
= template
[start
:]
600 splitted
= tpl_tmp
.split('{ENDIF}')
602 block
= '' # Kompletter bedingter Block
603 ifs
= 0 # IF-Zaehler (wird fuer jedes IF erhoeht und fuer jedes ENDIF erniedrigt)
609 while x
< len(splitted
):
610 # Verschachtelungsfehler abfangen
611 if x
== len(splitted
)-1: raise TplClassIFNestingError(self
.file,name
,count
)
613 ifs
+= splitted
[x
].count('{IF ') # Zum Zaehler jedes Vorkommen von IF hinzuzaehlen
614 ifs
-= 1 # Zaehler um 1 erniedrigen
615 block
+= splitted
[x
]+'{ENDIF}' # Daten zum Block hinzufuegen
620 # Zaehler wieder 0, also haben wir das Ende des IF-Blocks gefunden :-))
623 if_block
= block
[len(name
)+5:-7] # Alles zwischen {IF} und {ENDIF}
627 else_block
= '' # Alles ab {ELSE}
630 splitted
= if_block
.split('{ELSE}');
634 while x
< len(splitted
):
635 ifs
+= splitted
[x
].count('{IF ') # Zum Zaehler jedes Vorkommen von IF hinzuzaehlen
636 ifs
-= splitted
[x
].count('{ENDIF}') # Vom Zaehler jedes Vorkommen von ENDIF abziehen
641 # Zaehler 0, also haben wir das Ende des IF-Abschnitts gefunden
643 # Aus dem Rest den ELSE-Block zusammenbauen
647 while y
< len(splitted
):
648 else_block
+= '{ELSE}'+splitted
[y
]
652 if_block
= if_block
[0:len(if_block
)-len(else_block
)]
653 else_block
= else_block
[6:]
658 replacement
= if_block
660 replacement
= else_block
662 template
= template
.replace(block
,replacement
)
664 self
.set_template(template
)
666 # Evtl. verneinte Form parsen
669 self
.parse_if_block('!'+name
,not state
,1)
673 # IF-Bloecke zu allen definierten Variablen verarbeiten
675 # Parameter: Array mit zu verarbeitenden IF-Bloecken (optional)
677 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
679 def parse_if_blocks(self
,valid
=None):
681 valid_vars
= self
.get_var()
685 for valid_var
in valid_vars
:
686 self
.parse_if_block(valid_var
,self
.get_var(valid_var
))
688 # parse_trim_blocks()
690 # {TRIM}-Bloecke parsen
692 # Dieser Parser ist nicht rekursiv, was auch nicht
693 # noetig sein sollte.
695 # Parameter: -nichts-
697 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
699 def parse_trim_blocks(self
):
700 template
= self
.get_template()
701 if template
.find('{TRIM}') == -1: return
705 while template
.find('{TRIM}',offset
) >= 0:
706 begin
= template
.find('{TRIM}',offset
)
708 if template
.find('{ENDTRIM}',begin
+6) >= 0:
709 end
= template
.find('{ENDTRIM}',begin
+6)
711 block
= template
[begin
:end
+9]
712 content
= block
[6:-9]
714 trimmed
= content
.strip()
716 template
= template
.replace(block
,trimmed
)
718 offset
= begin
+len(trimmed
)
722 self
.set_template(template
)
726 # Bedingungstags in einem Vorlagentext verarbeiten
728 # Parameter: 1. Tagname
729 # 2. Status-Code (true => Tag-Inhalt anzeigen
730 # false => Tag-Inhalt nicht anzeigen
732 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
734 def parse_condtag(self
,condtag
,state
):
735 condtag
= str(condtag
)
736 template
= self
.get_template()
738 while template
.find('<'+condtag
+'>') >= 0:
739 start
= template
.find('<'+condtag
+'>') # Beginn des Blocks
740 end
= template
.find('</'+condtag
+'>')+len(condtag
)+3 # Ende des Blocks
742 extract
= template
[start
:end
] # Kompletten Bedingungsblock extrahieren...
745 replacement
= extract
[len(condtag
)+2:0-len(condtag
)-3]
749 template
= template
.replace(extract
,replacement
) # Block durch neue Daten ersetzen
751 self
.set_template(template
)
755 # {INCLUDE}-Anweisungen verarbeiten
757 # Parameter: -nichts-
759 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
761 def parse_includes(self
):
762 template
= self
.get_template()
763 if template
.find('{INCLUDE ') == -1: return
767 while template
.find('{INCLUDE ',offset
) >= 0:
768 begin
= template
.find('{INCLUDE ',offset
)
774 if template
[start
] == '"':
783 while x
< len(template
):
784 if template
[x
] == '\012' or template
[x
] == '\015':
787 elif long == 0 and template
[x
] == ' ':
790 elif long == 1 and template
[x
] == '"':
791 if template
[x
+1] != '}': skip
= 1
793 elif long == 0 and template
[x
] == '}':
800 if skip
== 1: continue
805 if not os
.path
.isabs(file):
806 dir = os
.path
.dirname(self
.file)
807 if not dir: dir = '.'
808 filepath
= os
.path
.normpath(dir+'/'+file)
810 if os
.path
.isfile(filepath
):
814 if long == 1: end
= start
+ len(file) + 2
815 else: end
= start
+ len(file) + 1
817 pre
= template
[0:begin
]
818 post
= template
[end
:]
820 template
= pre
+inc
.get_template()+post
821 offset
= len(pre
)+len(inc
.get_template())
825 self
.set_template(template
)
827 # Klasse zum Erzeugen des Fehlers bei falsch verschachtelten
830 class TplClassIFNestingError
:
832 def __init__(self
,file,name
,count
):
838 return 'Nesting error found while parsing IF block "'+self
.name
+'" nr. '+str(self
.count
)+' in template file "'+self
.file+'"'
patrick-canterino.de