tclxml-3.2/examples/tclxslt/common.tcl

Go to the documentation of this file.
00001 /*  common.tcl --*/
00002 /* */
00003 /*  Common code shared between tkxmllint and tkxsltproc.*/
00004 /* */
00005 /*  The master version of this file is in the TclDOM project.*/
00006 /* */
00007 /*  Copyright (c) 2005 Explain*/
00008 /*  http://www.explain.com.au*/
00009 /*  Copyright (c) 2004 Zveno*/
00010 /*  http://www.zveno.com/*/
00011 /* */
00012 /*  See the file "LICENSE" in this distribution for information on usage and*/
00013 /*  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.*/
00014 /* */
00015 /*  $Id: common.tcl,v 1.1 2005/05/21 11:21:01 balls Exp $*/
00016 
00017 package require http
00018 
00019 /*  SetProperties --*/
00020 /* */
00021 /*  Setup tag properties*/
00022 /* */
00023 /*  Arguments:*/
00024 /*  win toplevel window*/
00025 /*  log log window*/
00026 /* */
00027 /*  Results:*/
00028 /*  Tag properties set*/
00029 
00030 ret  SetProperties (type win , type log) {
00031 
00032     $log tag configure timing -background #bafdff
00033     $log tag configure error -background #ff9d8d
00034     $log tag configure errorhighlight -background #cd3030
00035     $log tag configure related -background #ffe59f
00036     $log tag configure relatedhighlight -background #e8b417
00037     $log tag configure message -background #ffe59f
00038     $log tag configure log -background #b9ffd4
00039 
00040     return {}
00041 }
00042 
00043 /*  Browse --*/
00044 /* */
00045 /*  Choose a file*/
00046 /* */
00047 /*  Arguments:*/
00048 /*  win toplevel window*/
00049 /*  field   name of state variable field to update*/
00050 /*  args    configuration options*/
00051 /* */
00052 /*  Results:*/
00053 /*  Current file is set*/
00054 
00055 ret  Browse (type win , type field , type args) {
00056     upvar \#0 State$win state
00057 
00058     set w [expr {$win == "." ? {} : $win}]
00059 
00060     array set opts {
00061     -title {Select Document}
00062     -type open
00063     -command {}
00064     }
00065     array set opts $args
00066 
00067     set cwd [pwd]
00068     if {$state(cwd) != {}} {
00069     set cwd $state(cwd)
00070     }
00071 
00072     switch -- $opts(-type) {
00073     save {
00074         set fname [tk_getSaveFile -parent $win -title [mc $opts(-title)] -initialdir $cwd]
00075     }
00076     open -
00077     default {
00078         set fname [tk_getOpenFile -parent $win -title [mc $opts(-title)] -initialdir $cwd]
00079     }
00080     }
00081 
00082     if {![string length $fname]} {
00083     return {}
00084     }
00085 
00086     set state($field) file:///$fname
00087     set state(cwd) [file dirname $fname]
00088 
00089     if {[string length $fname] && [string length $opts(-command)]} {
00090     uplevel #0 $opts(-command)
00091     }
00092 
00093     return {}
00094 }
00095 
00096 /*  ReadAndParseXML --*/
00097 /* */
00098 /*  Helper procedure to read an XML document from a file*/
00099 /*  and parse it into a DOM tree.*/
00100 /* */
00101 /*  Arguments:*/
00102 /*  win toplevel window*/
00103 /*  label   description of the document*/
00104 /*  fname   filename of document to parse*/
00105 /*  baseuri base URI for document*/
00106 /*  timearr name of array for timing information,*/
00107 /*      "start" entry must exist.*/
00108 /*  args    additional options*/
00109 /* */
00110 /*  Results:*/
00111 /*  Document read into memory.  Log messages provide feedback.*/
00112 /*  Returns DOM document token.*/
00113 
00114 ret  ReadAndParseXML (type win , type label , type fname , type baseuri , optional timearr =time , type args) {
00115     upvar 1 $timearr time
00116     upvar \#0 State$win state
00117 
00118     array set opts {
00119     -noent 0
00120     -nonet 0
00121     }
00122     array set opts $args
00123 
00124     set state(externalentities) 0
00125 
00126     Feedback $win [mc "Opening $label document \"$fname\""]
00127     if {[string match http://* $fname]} {
00128     FeedbackProgress $win 0
00129     set state(start_download) [clock clicks -milliseconds]
00130     if {[catch {::http::geturl $fname \
00131             -command [list HTTPComplete $win] \
00132             -progress [list HTTPProgress $win] \
00133             -timeout 30000} token]} {
00134         tk_messageBox -message "unable to retrieve $label document \"$fname\" due to \"$token\"" -parent $win -type ok -icon error
00135         return -code error {}
00136     }
00137     ::http::wait $token
00138     if {[::http::status $token] != "ok"} {
00139         return -code error {}
00140     }
00141     set xml [::http::data $token]
00142     ::http::cleanup $token
00143     set time(read) [clock clicks -milliseconds]
00144     } else {
00145     if {[catch {open $fname} ch]} {
00146         tk_messageBox -message "unable to open $label document \"$fname\" due to \"$ch\"" -parent $win -type ok -icon error
00147         return -code error {}
00148     }
00149     set time(open) [clock clicks -milliseconds]
00150     Log timing $win "Opening $label document took [expr $time(open) - $time(start)]ms\n"
00151 
00152     Feedback $win [mc "Reading $label document"]
00153     # Take note of encoding information
00154     set encoding {}
00155     gets $ch xmldecl
00156     set re ^[::sgml::cl $::xml::Wsp]*<\\?xml[::sgml::cl $::xml::Wsp]+(version[::sgml::cl $::xml::Wsp]*=[::sgml::cl $::xml::Wsp]*("|')[::sgml::cl ^"']+\\2)?[::sgml::cl $::xml::Wsp]*(encoding[::sgml::cl $::xml::Wsp]*=[::sgml::cl $::xml::Wsp]*("|')([::sgml::cl ^"']+)\\4)?[::sgml::cl $::xml::Wsp]*(standalone[::sgml::cl $::xml::Wsp]*=[::sgml::cl $::xml::Wsp]*("|')(yes|no)\\7)?[::sgml::cl $::xml::Wsp]*\\?>
00157     if {[regexp $re $xmldecl discard allversion delimiter allencoding delimiter encoding allstandalone delimiter]} {
00158         if {[catch {fconfigure $ch -encoding $encoding} msg]} {
00159         if {[catch {fconfigure $ch -encoding [string tolower $encoding]} msg]} {
00160             tk_messageBox -message "unable to read $label document \"$fname\" due to \"$msg\"" -parent $win -type ok -icon error
00161             return -code error {}
00162         }
00163         }
00164     }
00165     set xml $xmldecl\n[read $ch]
00166     close $ch
00167     # Watch out for UTF-16 documents
00168     if {[regexp "^(\xFF\xFE)|(\xFE\xFF)" $xml]} {
00169         set xml [encoding convertfrom unicode $xml]
00170     }
00171     set time(read) [clock clicks -milliseconds]
00172     Log timing $win "Reading $label document took [expr $time(read) - $time(open)]ms\n"
00173     }
00174 
00175     Feedback $win [mc "Parsing $label XML"]
00176     if {[catch {dom::parse $xml \
00177         -baseuri [uri_escape $baseuri] \
00178         -defaultexpandinternalentities $opts(-noent) \
00179         -externalentitycommand [list External $win]} doc]} {
00180 
00181     if {[string match "unable to*" $doc]} {
00182         Log add $win $doc
00183     } else {
00184         Log addXMLError $win $xml $doc
00185     }
00186     Feedback $win [mc "Parsing $label document failed"]
00187     after 2000 [list Feedback $win {}]
00188     return -code error {}
00189     }
00190     set time(parse) [clock clicks -milliseconds]
00191     Log timing $win "Parsing $label document took [expr $time(parse) - $time(read)]ms\n"
00192     set time(last) $time(parse)
00193 
00194     if {$state(xinclude)} {
00195     Feedback $win [mc "$label document XInclude processing"]
00196     # TODO: handle doc in slave interp
00197     if {[catch {dom::xinclude $doc} msg]} {
00198         Log addDocError $win $doc $msg
00199         Feedback $win [mc "$label document XInclude processing failed"]
00200         after 2000 [list Feedback $win {}]
00201     }
00202     set time(xinclude) [clock clicks -milliseconds]
00203     Log timing $win "$label document XInclude took [expr $time(xinclude) - $time(last)]ms\n"
00204     set time(last) $time(xinclude)
00205     }
00206 
00207     return $doc
00208 }
00209 
00210 /*  External --*/
00211 /* */
00212 /*  Handle external entity references*/
00213 /* */
00214 /*  Arguments:*/
00215 /*  win toplevel window*/
00216 /*  name    current parser*/
00217 /*  baseuri base URI of document*/
00218 /*  uri system identifier of referenced entity*/
00219 /*  id  public identifier of referenced entity*/
00220 /* */
00221 /*  Results:*/
00222 /*  This reference is logged.*/
00223 /*  If loading of external entities is enabled then the entity is laoded as usual,*/
00224 /*  otherwise an empty entity is returned.*/
00225 
00226 ret  External (type win , type name , type baseuri , type uri , type id) {
00227     upvar \#0 State$win state
00228 
00229     if {$state(nonet) &&
00230     ([string match http:* $uri] || [string match ftp:* $uri])} {
00231     Log entity $win "external entity not loaded, network access not permitted: system ID \"$uri\" public ID \"$id\""
00232     return {}
00233     }
00234 
00235     Log entity $win "external entity reference: system ID \"$uri\" public ID \"$id\""
00236 
00237     incr state(externalentities)
00238     # resume normal loading of external entity
00239     return -code continue {}
00240 }
00241 
00242 /*  GetFilename --*/
00243 /* */
00244 /*  Helper routine to retrieve resource filename*/
00245 /* */
00246 /*  Arguments:*/
00247 /*  win toplevel window*/
00248 /*  entry   entry widget containing filename value*/
00249 /*  field   member of state array containing URI*/
00250 /* */
00251 /*  Results:*/
00252 /*  Returns filename.  If URI is not a valid file: URL,*/
00253 /*  returns empty string and displays message.*/
00254 
00255 ret  GetFilename (type win , type entry , type field) {
00256     upvar \#0 State$win state
00257 
00258     set state($field) [$entry get]
00259 
00260     if {[catch {uri::split $state($field)} spliturl]} {
00261     # Try the URL as a pathname
00262     set fname $state($field)
00263     set state($field) file:///$state(field)
00264     } else {
00265     array set urlarray $spliturl
00266     switch -- $urlarray(scheme) {
00267         http {
00268         set fname $state($field)
00269         }
00270         file {
00271         set fname $urlarray(path)
00272         }
00273         default {
00274         tk_messageBox -message "\"$urlarray(scheme)\" type URLs are not supported" -parent $win -type ok -icon warning
00275         return {}
00276         }
00277     }
00278     }
00279 
00280     return $fname
00281 }
00282 
00283 /*  HTTPComplete --*/
00284 /* */
00285 /*  HTTP download is finished*/
00286 /* */
00287 /*  Arguments:*/
00288 /*  win toplevel window*/
00289 /*  token   http token*/
00290 /* */
00291 /*  Results:*/
00292 /*  Set progress to completion*/
00293 
00294 ret  HTTPComplete (type win , type token) {
00295     upvar \#0 State$win state
00296 
00297     $state(progress) itemconfigure $state(progressbar) -state disabled
00298     Log timing $win "Downloading document took [expr [clock clicks -milliseconds] - $state(start_download)]ms\n"
00299 
00300     return {}
00301 }
00302 
00303 /*  HTTPProgress --*/
00304 /* */
00305 /*  HTTP download is in progress*/
00306 /* */
00307 /*  Arguments:*/
00308 /*  win toplevel window*/
00309 /*  token   http token*/
00310 /*  total   total number of bytes to download*/
00311 /*  current number of bytes downloaded so far*/
00312 /* */
00313 /*  Results:*/
00314 /*  Set progress bar*/
00315 
00316 ret  HTTPProgress (type win , type token , type total , type current) {
00317     upvar \#0 State$win state
00318 
00319     FeedbackProgress $win [expr ($current * 100) / $total]
00320 
00321     return {}
00322 }
00323 
00324 /*  Log --*/
00325 /* */
00326 /*  Manage the log window*/
00327 /* */
00328 /*  Arguments:*/
00329 /*  win toplevel window*/
00330 /*  args    messages to display*/
00331 /* */
00332 /*  Results:*/
00333 /*  Log window updated.*/
00334 
00335 ret  Log (type method , type win , type args) {
00336     upvar \#0 State$win state
00337 
00338     set w [expr {$win == "." ? {} : $win}]
00339 
00340     switch -- $method {
00341     clear {
00342         $state(messages).log configure -state normal
00343         $state(messages).log delete 1.0 end
00344         $state(messages).log configure -state disabled
00345     }
00346     view {
00347         set what [lindex $args 0]
00348         switch -- $what {
00349         start {
00350             $state(messages).log see 1.0
00351         }
00352         end {
00353             $state(messages).log see end
00354         }
00355         default {
00356             return -code error "don't know how to view \"$what\""
00357         }
00358         }
00359     }
00360     add {
00361         $state(messages).log configure -state normal
00362         $state(messages).log insert end [lindex $args 0]
00363         $state(messages).log configure -state disabled
00364         $state(messages).log see end
00365     }
00366     addXMLError {
00367         $state(messages).log configure -state normal
00368 
00369         set xml [lindex $args 0]
00370         set id 0
00371         $state(messages).log insert end [mc "Problems detected in document:\n"]
00372         foreach errormsg [lindex $args 1] {
00373         foreach {domain level code node line message relatedLine dummy related1 related2} $errormsg break
00374         lappend error($line) error$id
00375         lappend related($relatedLine) related$id
00376         $state(messages).log insert end $message [list error error$id]
00377         if {[string index $message end] != "\n"} {
00378             $state(messages).log insert end \n
00379         }
00380         $state(messages).log tag bind error$id <Enter> [list ErrorHighlight $w $state(messages).log error$id $line $relatedLine]
00381         $state(messages).log tag bind error$id <Leave> [list ErrorRemoveHighlight $w $state(messages).log error$id $line $relatedLine]
00382         incr id
00383         }
00384         $state(messages).log insert end \n
00385 
00386         set linenum 1
00387         foreach line [split $xml \n] {
00388 
00389         if {[info exists error($linenum)]} {
00390             $state(messages).log insert end $line "error errorline$linenum"
00391         } elseif {[info exists related($linenum)]} {
00392             $state(messages).log insert end $line "related relatedline$linenum"
00393         } else {
00394             $state(messages).log insert end $line
00395         }
00396         $state(messages).log insert end \n
00397 
00398         incr linenum
00399         }
00400 
00401         $state(messages).log configure -state disabled
00402         $state(messages).log see end
00403     }
00404     addDocError {
00405         $state(messages).log configure -state normal
00406 
00407         set doc [lindex $args 0]
00408         foreach errormsg [lindex $args 1] {
00409         foreach {domain level code node line message relatedLine dummy related1 related2} $errormsg break
00410         $state(messages).log insert end $message
00411         if {[string index $message end] != "\n"} {
00412             $state(messages).log insert end \n
00413         }
00414         }
00415 
00416         $state(messages).log configure -state disabled
00417         $state(messages).log see end
00418     }
00419     addMessage {
00420         $state(messages).log configure -state normal
00421         $state(messages).log insert end [lindex $args 0] message
00422         $state(messages).log configure -state disabled
00423         $state(messages).log see end
00424     }
00425     timing {
00426         if {$state(timing)} {
00427         $state(messages).log configure -state normal
00428         $state(messages).log insert end [lindex $args 0] timing
00429         $state(messages).log configure -state disabled
00430         $state(messages).log see end
00431         }
00432     }
00433     entity {
00434         if {$state(display:entrefs)} {
00435         $state(messages).log configure -state normal
00436         $state(messages).log insert end [lindex $args 0] log \n
00437         $state(messages).log configure -state disabled
00438         $state(messages).log see end
00439         }
00440     }
00441     default {
00442         return -code error "unknown method \"$method\""
00443     }
00444     }
00445 
00446     return {}
00447 }
00448 
00449 /*  ErrorHighlight -- Highlight an error*/
00450 
00451 ret  ErrorHighlight (type win , type log , type tag , type line , type related) {
00452     $log tag configure $tag -background [$log tag cget errorhighlight -background]
00453     $log tag configure errorline$line -background [$log tag cget errorhighlight -background]
00454     $log tag raise errorline$line error
00455     $log tag configure relatedline$related -background [$log tag cget relatedhighlight -background]
00456     $log tag raise relatedline$related related
00457 
00458     return {}
00459 }
00460 ret  ErrorRemoveHighlight (type win , type log , type tag , type line , type related) {
00461     Feedback $win {}
00462 
00463     $log tag configure $tag -background {}
00464     $log tag configure errorline$line -background {}
00465     $log tag configure relatedline$related -background {}
00466 
00467     return {}
00468 }
00469 
00470 /*  Feedback -- Manage the feedback widget*/
00471 
00472 ret  Feedback (type win , type msg) {
00473     upvar \#0 State$win state
00474 
00475     set state(feedback) $msg
00476     update
00477 
00478     return {}
00479 }
00480 ret  FeedbackProgress (type win , type percent) {
00481     upvar \#0 State$win state
00482 
00483     $state(progress) coords $state(progressbar) 0 0 $percent 25
00484     update
00485 
00486     return {}
00487 }
00488 
00489 /*  Incr -- utility to increment a variable, handling non-existance*/
00490 
00491 ret  Incr var (
00492     type upvar $, type var , type v
00493     , type if , optional [info =exists v] , optional 
00494     incr =v
00495      , type else , optional 
00496     set =v 1
00497     
00498 
00499     , type return $, type v
00500 )
00501 
00502 # This should be part of the uri package
00503 
00504 proc uri_escape uri {
00505     # TODO: other characters must also be escaped
00506     regsub -all { } $uri {%20} uri
00507 
00508     return $uri
00509 }
00510 
00511 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1