_xml.tcl

Go to the documentation of this file.
00001 /*  -*- tcl -*-*/
00002 /* */
00003 /*  $Id: _xml.tcl,v 1.9 2004/04/22 21:16:46 jenglish Exp $*/
00004 /* */
00005 /*  [expand] utilities for generating XML.*/
00006 /* */
00007 /*  Copyright (C) 2001 Joe English <jenglish@sourceforge.net>.*/
00008 /*  Freely redistributable.*/
00009 /* */
00010 /* */
00011 
00012 
00013 /*  Handling XML delimiters in content:*/
00014 /* */
00015 /*  Plain text is initially passed through unescaped;*/
00016 /*  internally-generated markup is protected by preceding it with \1.*/
00017 /*  The final PostProcess step strips the escape character from*/
00018 /*  real markup and replaces markup characters from content*/
00019 /*  with entity references.*/
00020 /* */
00021 
00022 variable attvalMap { {&} &amp;  {<} &lt;  {>} &gt; {"} &quot; {'} &apos; } ; /*  "*/
00023 variable markupMap { {&} {\1&}  {<} {\1<}  {>} {\1>} }
00024 variable finalMap  { {\1&} {&}  {\1<} {<}  {\1>} {>}
00025              {&} &amp;  {<} &lt;   {>} &gt; }
00026 
00027 ret  fmt_postprocess (type text)    {
00028     variable finalMap
00029     return [string map $finalMap $text]
00030 }
00031 
00032 /*  markup text --*/
00033 /*  Protect markup characters in $text with \1.*/
00034 /*  These will be stripped out in PostProcess.*/
00035 /* */
00036 ret  markup (type text) {
00037     variable markupMap
00038     return [string map $markupMap $text]
00039 }
00040 
00041 /*  attlist { n1 v1 n2 v2 ... } --*/
00042 /*  Return XML-formatted attribute list.*/
00043 /*  Does *not* escape markup -- the result must be passed through*/
00044 /*  [markup] before returning it to the expander.*/
00045 /* */
00046 ret  attlist (type nvpairs) {
00047     variable attvalMap
00048     if {[llength $nvpairs] == 1} { set nvpairs [lindex $nvpairs 0] }
00049     set attlist ""
00050     foreach {name value} $nvpairs {
00051         append attlist " $name='[string map $attvalMap $value]'"
00052     }
00053     return $attlist
00054 }
00055 
00056 /*  startTag gi ?attname attval ... ? --*/
00057 /*  Return start-tag for element $gi with specified attributes.*/
00058 /* */
00059 ret  startTag (type gi , type args) {
00060     return [markup "<$gi[attlist $args]>"]
00061 }
00062 
00063 /*  endTag gi --*/
00064 /*  Return end-tag for element $gi.*/
00065 /* */
00066 ret  endTag (type gi) {
00067     return [markup "</$gi>"]
00068 }
00069 
00070 /*  emptyElement gi ?attribute  value ... ?*/
00071 /*  Return empty-element tag.*/
00072 /* */
00073 ret  emptyElement (type gi , type args) {
00074     return [markup "<$gi[attlist $args]/>"]
00075 }
00076 
00077 /*  xmlComment text --*/
00078 /*  Return XML comment declaration containing $text.*/
00079 /*  NB: if $text includes the sequence "--", it will be mangled.*/
00080 /* */
00081 ret  xmlComment (type text) {
00082     return [markup "<!-- [string map {-- { - - }} $text] -->"]
00083 }
00084 
00085 /*  wrap content gi --*/
00086 /*  Returns $content wrapped inside <$gi> ... </$gi> tags.*/
00087 /* */
00088 ret  wrap (type content , type gi) {
00089     return "[startTag $gi]${content}[endTag $gi]"
00090 }
00091 
00092 /*  wrap? content gi --*/
00093 /*  Same as [wrap], but returns an empty string if $content is empty.*/
00094 /* */
00095 ret  wrap? (type content , type gi) {
00096     if {![string length [string trim $content]]} { return "" }
00097     return "[startTag $gi]${content}[endTag $gi]"
00098 }
00099 
00100 /*  wrapLines? content gi ? gi... ?*/
00101 /*  Same as [wrap?], but separates entries with newlines*/
00102 /*        and supports multiple nesting levels.*/
00103 /* */
00104 ret  wrapLines? (type content , type args) {
00105     if {![string length $content]} { return "" }
00106     foreach gi $args {
00107     set content [join [list [startTag $gi] $content [endTag $gi]] "\n"]
00108     }
00109     return $content
00110 }
00111 
00112 /*  sequence args --*/
00113 /*  Handy combinator.*/
00114 /* */
00115 ret  sequence (type args) { join $args "\n" }
00116 
00117 /* */
00118 /*  XML context management.*/
00119 /* */
00120 
00121 variable elementStack [list]
00122 
00123 /*  start gi ?attribute value ... ? --*/
00124 /*  Return start-tag for element $gi*/
00125 /*  As a side-effect, pushes $gi onto the element stack.*/
00126 /* */
00127 ret  start (type gi , type args) {
00128     if {[llength $args] == 1} { set args [lindex $args 0] }
00129     variable elementStack
00130     lappend elementStack $gi
00131     return [startTag $gi $args]
00132 }
00133 
00134 /*  xmlContext {gi1 ... giN} ?default?  --*/
00135 /*  Pops elements off the element stack until one of*/
00136 /*  the specified element types is found.*/
00137 /* */
00138 /*  Returns: sequence of end-tags for each element popped.*/
00139 /* */
00140 /*  If none of the specified elements are found, returns*/
00141 /*      a start-tag for $default.*/
00142 /* */
00143 ret  xmlContext (type gis , optional default ={)} {
00144     variable elementStack
00145     set origStack $elementStack
00146     set endTags [list]
00147     while {[llength $elementStack]} {
00148      current =  [lindex $elementStack end]
00149     if {[lsearch $gis $current] >= 0} {
00150         return [join $endTags \n]
00151     }
00152     lappend endTags [endTag $current]
00153      elementStack =  [lreplace $elementStack end end]
00154     }
00155     /*  Not found:*/
00156      elementStack =  $origStack
00157     if {![string length $default]} {
00158          where =  "[join $elementStack /] - [info level 1]"
00159     puts_stderr "Warning: Cannot start context $gis ($where)"
00160          default =  [lindex $gis 0] 
00161     }
00162     lappend elementStack $default
00163     return [startTag $default]
00164 }
00165 
00166 /*  end ? gi ? --*/
00167 /*  Generate markup to close element $gi, including end-tags*/
00168 /*  for any elements above it on the element stack.*/
00169 /* */
00170 /*  If element name is omitted, closes the current element.*/
00171 /* */
00172 ret  end (optional gi ={)} {
00173     variable elementStack
00174     if {![string length $gi]} {
00175          gi =  [lindex $elementStack end]
00176     }
00177      prefix =  [xmlContext $gi]
00178      elementStack =  [lreplace $elementStack end end]
00179     return [join [list $prefix [endTag $gi]] "\n"]
00180 }
00181 
00182 /* */
00183 /*  Utilities for multi-pass processing.*/
00184 /* */
00185 /*  Not really XML-related, but I find them handy.*/
00186 /* */
00187 
00188 variable PassProcs
00189 variable Buffers
00190 
00191 /*  pass $passNo procName procArgs { body  } --*/
00192 /*  Specifies procedure definition for pass $n.*/
00193 /* */
00194 ret  pass (type pass , type proc , type arguments , type body) {
00195     variable PassProcs
00196     lappend PassProcs($pass) $proc $arguments $body
00197 }
00198 
00199 ret  setPassProcs (type pass) {
00200     variable PassProcs
00201     foreach {proc args body} $PassProcs($pass) {
00202     proc $proc $args $body
00203     }
00204 }
00205 
00206 /*  holdBuffers buffer ? buffer ...? --*/
00207 /*  Declare a list of hold buffers,*/
00208 /*  to collect data in one pass and output it later.*/
00209 /* */
00210 ret  holdBuffers (type args) {
00211     variable Buffers
00212     foreach arg $args {
00213     set Buffers($arg) [list]
00214     }
00215 }
00216 
00217 /*  hold buffer text --*/
00218 /*  Append text to named buffer*/
00219 /* */
00220 ret  hold (type buffer , type entry) {
00221     variable Buffers
00222     lappend Buffers($buffer) $entry
00223     return
00224 }
00225 
00226 /*  held buffer --*/
00227 /*  Returns current contents of named buffer and empty the buffer.*/
00228 /* */
00229 ret  held (type buffer) {
00230     variable Buffers
00231     set content [join $Buffers($buffer) "\n"]
00232     set Buffers($buffer) [list]
00233     return $content
00234 }
00235 
00236 /* *EOF**/
00237 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1