Source code for leo.plugins.xsltWithNodes

#@+leo-ver=5-thin
#@+node:mork.20041010095009: * @file xsltWithNodes.py
#@+<< docstring >>
#@+node:ekr.20050226120104: ** << docstring >>
""" Adds the Outline:XSLT menu containing XSLT-related commands.

This menu contains the following items:

- Set StyleSheet Node:
    - Selects the current node as the xsl stylesheet the plugin will use.

- Process Node with Stylesheet Node:
    - Processes the current node as an xml document,
      resolving section references and Leo directives.
    - Creates a sibling containing the results.

Requires 4Suite 1.0a3 or better, downloadable from http://4Suite.org.

"""
#@-<< docstring >>

#@@language python
#@@tabwidth -4

#@+<< imports >>
#@+node:mork.20041025113509: ** << imports >>
import leo.core.leoGlobals as g

from xml.dom import minidom

if g.isPython3:
    import io
    StringIO = io.StringIO
else:
    import cStringIO
    StringIO = cStringIO.StringIO

try:
    import Ft
    from Ft.Xml import InputSource
    from Ft.Xml.Xslt.Processor import Processor
except ImportError:
    g.cantImport("Ft",__name__)
    Ft = None

import weakref
#@-<< imports >>
#@+<<parser problems>>
#@+node:mork.20041024091024: ** <<parser problems>>
#@@killcolor

#@+at
# 1. Having space before the start of the document caused it not to work. I fixed
#    this by striping the whitespace from the start and end of the data at xslt
#    time.
# 
# 2. having a @ right before a tag causes it to not process.
#     It appears to be safe to follow this pattern:
#     @ </end>
#     but not:
#     @</end>
# 
#     I dont know at this point if its just illegal xml, or its a problem in the parser. ??
#@-<<parser problems>>
#@+<<future directions>>
#@+node:mork.20041025101943: ** <<future directions>>
#@+at
# 1. Add more XSLT boilerplate insertions.( done in .3 )
# 2. Maybe add a well-formedness check. (done in .3, test with minidom )
#@-<<future directions>>
__version__ = '0.6'
#@+<< version history >>
#@+node:mork.20041025113211: ** << version history >>
#@@killcolor

#@+at
# 
# 0.1: Original code.
# 
# 0.2 EKR: Converted to outline.
# 
# 0.3: Added more XSLT boilerplate. Added Test with Minidom Discovered parser problem(?).
# 
# 0.4 EKR:
#     - Added init function.
# 0.5 EKR:
#     - Remove 'start2' hook & haveseen dict.
#     - Use keywords.get('c') instead of g.top().
# 0.6 EKR:
#     - Removed g.top from example code.
#@-<< version history >>

#@+others
#@+node:ekr.20050226120104.1: ** init
[docs]def init(): '''Return True if the plugin has loaded successfully.''' ok = Ft if ok: g.registerHandler(('menu2',"new"),addMenu) g.plugin_signon(__name__) return ok
#@+node:mork.20041025115037: ** xslt elements #This dict contains elements that go into a stylesheet xslt = { 'apply-imports': '<xsl:apply-imports/>', 'apply-templates': "<xsl:apply-templates select ='' mode='' />", 'attribute': "<xsl:attribute name=''> </xsl:attribute>", 'attribute-set': "<xsl:attribute-set name=''> </xsl:attribute-set>", 'call-template': "<xsl:call-template name=''> </xsl:call-template>", 'choose': "<xsl:choose> </xsl:choose>", 'comment': "<xsl:comment> </xsl:comment>", 'copy': "<xsl:copy> </xsl:copy>", 'copy-of': "<xsl:copy-of select='' />", 'decimal-format' : "<xsl:decimal-format />", 'element': "<xsl:element name='' > </xsl:element>", 'fallback': "<xsl:fallback> </xsl:fallback>", 'for-each': "<xsl:for-each select='' > </xsl:for-each>", 'if': "<xsl:if test=''> </xsl:if>", 'import': "<xsl:import href='' />", 'include': "<xsl:include href='' />", 'key': "<xsl:key name='' match='' use='' />", 'message': "<xsl:message> </xsl:message>", 'namespace-alias': "<xsl:namespace-alias stylesheet-prefix='' result-prefix='' />", 'number': "<xsl:number />", 'otherwise': "<xsl:otherwise> </xsl:otherwise>", 'output': "<xsl:output />", 'param': "<xsl:param name='' > </xsl:param>", 'preserve-space': "<xsl:preserve-space elements='' />", 'processing-instruction': "<xsl:processing-instruction name='' > </xsl:processing-instruction>", 'sort': "<xsl:sort />", 'strip-space': "<xsl:strip-space elements='' />", 'stylesheet': "<xsl:stylesheet xmlns:xsl='' version='' > </xsl:stylesheet>", 'template': "<xsl:template > </xsl:template>", 'text': "<xsl:text > </xsl:text>", 'transform': "<xsl:transform > </xsl:transform>", 'value-of': "<xsl:value-of select='' />", 'variable': "<xsl:variable name=''> </xsl:variable>", 'when': "<xsl:when text='' > </xsl:when>", 'with-param': "<xsl:with-param name=''> </xsl:with-param>", } #@+node:mork.20041010095202: ** setStyleNode stylenodes = weakref.WeakKeyDictionary()
[docs]def setStyleNode( c ): '''this command sets what the current style node is''' position = c.p stylenodes[ c ] = position
#@+node:mork.20041010095202.1: ** processDocumentNode
[docs]def processDocumentNode( c ): '''this executes the stylesheet node against the current node''' try: if not styleNodeSelected( c ): return proc = Processor() stylenode = stylenodes[ c ] pos = c.p c.selectPosition( stylenode ) sIO = getString( c ) mdom1 = minidom.parseString( sIO ) sIO = str( mdom1.toxml() ) hstring = str( stylenode.h ) if hstring == "": hstring = "no headline" stylesource = InputSource.DefaultFactory.fromString( sIO, uri = hstring) proc.appendStylesheet( stylesource ) c.selectPosition( pos ) xmlnode = pos.v xIO = getString( c ) mdom2 = minidom.parseString( xIO ) xIO = str( mdom2.toxml()) xhead = str( xmlnode.headString ) if xhead == "": xhead = "no headline" xmlsource = InputSource.DefaultFactory.fromString( xIO, uri = xhead ) result = proc.run( xmlsource ) nhline = "xsl:transform of " + str( xmlnode.headString ) p2 = pos.insertAfter() # tnode ) p2.setBodyString(result) p2.setHeadString(nhline) c.redraw() except Exception as x: g.es( 'exception ' + str( x )) c.redraw()
#@+node:mork.20041025121608: ** addXSLTNode
[docs]def addXSLTNode (c): '''creates a node and inserts some xslt boilerplate''' pos = c.p #body = '''<?xml version="1.0"?>''' # body = '''<?xml version="1.0"?> #<xsl:transform xmlns:xsl="http:///www.w3.org/1999/XSL/Transform" version="1.0">''' body = '''<?xml version="1.0"?> <xsl:transform xmlns:xsl="http:///www.w3.org/1999/XSL/Transform" version="1.0"> </xsl:transform>''' p2 = pos.insertAfter() # tnode) p2.setBodyString(body) p2.setHeadString("xslt stylesheet") c.redraw()
#@+node:mork.20041010110121: ** addXSLTElement
[docs]def addXSLTElement( c , element): '''adds some xslt to the text node''' w = c.frame.body.wrapper w.insert( 'insert', element )
### w.event_generate( '<Key>' ) ### w.update_idletasks() #@+node:mork.20041025113021: ** getString (xsltWithNodes.py)
[docs]def getString (c): ''' This def turns a node into a string using Leo's file-nosent write logic. ''' at = c.atFileCommands # EKR: 2017/04/10: needs testing. at.writeOpenFile(c.p, nosentinels=True, toString=True) return cleanString(at.stringOutput)
#@+node:mork.20041025120706: ** doMinidomTest
[docs]def doMinidomTest( c ): ''' This def performs a simple test on a node. Can the data be successfully parsed by minidom or not? Results are output to the log. ''' s = getString( c ) try: minidom.parseString( s ) except Exception as x: g.error("Minidom could not parse node because of:\n %s" % x) return g.blue("Minidom could parse the node")
#@+node:mork.20041025090303: ** cleanString
[docs]def cleanString( data ): '''This method cleans a string up for the processor. It currently just removes leading and trailing whitespace''' val = data.strip() return val
#@+node:mork.20041010125444: ** jumpToStyleNode
[docs]def jumpToStyleNode( c ): '''Simple method that jumps us to the current XSLT node''' if not styleNodeSelected( c ): return pos = stylenodes[ c ] c.selectPosition( pos ) c.redraw()
#@+node:mork.20041010125444.1: ** styleNodeSelected
[docs]def styleNodeSelected( c ): '''Determines if a XSLT Style node has not been selected''' if c not in stylenodes: g.es( "No Style Node selected" ) return False return True
#@+node:mork.20041010100633: ** addMenu
[docs]def addMenu( tag, keywords ): # pylint: disable=undefined-variable # c *is* defined. c = keywords.get('c') if not c: return mc = c.frame.menu # men = men.getMenu( 'Outline' ) # xmen = Tk.Menu(men,tearoff = False) xmen = mc.createNewMenu ('XSLT',"Outline") c.add_command(xmen, label = "Set Stylesheet Node", command = lambda c = c : setStyleNode(c)) c.add_command(xmen, label = "Jump To Style Node", command = lambda c = c: jumpToStyleNode(c)) c.add_command(xmen, label = "Process Node with Stylesheet Node", command = lambda c=c : processDocumentNode(c)) xmen.add_separator(xmen) c.add_command(xmen, label = "Create Stylesheet Node", command = lambda c = c : addXSLTNode(c)) # elmen= Tk.Menu( xmen, tearoff = False ) # xmen.add_cascade( label = "Insert XSL Element", menu = elmen ) m2 = mc.createNewMenu ('Insert XSL Element','XSLT') xsltkeys = list(xslt.keys()) xsltkeys.sort() for z in xsltkeys: # pylint: disable=cell-var-from-loop c.add_command(m2, label = z, command = lambda c=c,element=xslt[ z ]: addXSLTElement(c,element)) # men.add_cascade(menu = xmen, label = "XSLT-Node Commands") m3 = mc.createNewMenu('XSLT-Node Commands','XSLT') c.add_command(m3, label = 'Test Node with Minidom', command = lambda c=c: doMinidomTest(c))
#@+node:mork.20041025100716: ** examples/tests #@+at # table.leo contains the xml. xslt is in the other node. # # To test this plugin, set the xslt node to be the xslt node. # # Process it against the table.leo node. #@@c # pylint: disable=pointless-string-statement r''' #@+others #@+node:ekr.20140906065955.18786: *3* table.leo #@@path /boboo/leo-4.2-final/plugins #@+node:ekr.20140906065955.18787: *4* @@nosent table.py if g.isPython3: import io StringIO = io.StringIO else: import cStringIO StringIO = cStringIO.StringIO import Tkinter as Tk import tktable as tktab import leo.core.leoGlobals as g import csv import weakref import Pmw class CSVVisualizer(object): arrays = [] #@+others #@+node:ekr.20140906065955.18788: *5* init def __init__( self, c ): self.c = c self.arr = tktab.ArrayVar() CSVVisualizer.arrays.append( self.arr ) self.rows = 0 self.columns = 0 self.type = 'excel' #@+node:ekr.20140906065955.18789: *5* addData def addData( self ): arr = self.arr reader = self.readData() hc = False for n, d in enumerate( reader ): for n1, d2 in enumerate( d ): arr.set( "%s,%s" %( n, n1 ), str(d2) ) self.columns = n1 + 1 self.rows = n + 1 return self.columns, self.rows #@+node:ekr.20140906065955.18790: *5* readData def readData( self ): c = self.c pos = c.p data = pos.b cS = StringIO() cS.write( data ) cS.seek( 0 ) sniff = csv.Sniffer() self.type = sniff.sniff( data ) reader = csv.reader( cS, self.type ) return reader #@+node:ekr.20140906065955.18791: *5* writeData def writeData( self, save ): pos = self.c.p n2 = self.rows n = self.columns data = [] for z in range( n2 ): ndata = [] for z2 in range( n ): ndata.append( self.arr.get( "%s,%s" % ( z, z2 ) ) ) data.append( ndata ) cS = StringIO() csv_write = csv.writer( cS, self.type ) for z in data: csv_write.writerow( z ) cS.seek( 0 ) if not save: p2 = pos.insertAfter() # tnd ) p2.setBodyString(cS.getvalue()) p2.setHeadString("Save of Edited " + str( pos.h)) else: # pos.setTnodeText( cS.getvalue() ) pos.setBodyString(cS.getvalue()) self.c.redraw() #@+node:ekr.20140906065955.18792: *5* addColumn def addColumn( self, tab ): self.columns = self.columns + 1 tab.configure( cols = self.columns ) for z in range( self.rows ): self.arr.set( '%s,%s' %( z , self.columns -1 ), "" ) #@+node:ekr.20140906065955.18793: *5* deleteColumn def deleteColumn( self, tab ): i = tab.index( 'active' ) if i: tab.delete_cols( i[ 1 ], 1 ) self.columns = self.columns - 1 #@+node:ekr.20140906065955.18794: *5* addRow def addRow( self , tab ): self.rows = self.rows + 1 tab.configure( rows = self.rows ) rc = '%s,0' % (self.rows -1 ) for z in range( self.columns ): self.arr.set( '%s,%s' %( self.rows - 1, z ), "" ) tab.activate( rc ) tab.focus_set() #@+node:ekr.20140906065955.18795: *5* deleteRow def deleteRow( self, tab ): i = tab.index( 'active' ) if i: tab.delete_rows( i[ 0 ], 1 ) self.rows = self.rows - 1 #@+node:ekr.20140906065955.18796: *5* createDefaultRecord def createDefaultRecord( self, rows, columns ): self.rows = rows self.columns = columns for z in range( rows ): for z1 in range( columns ): self.arr.set( '%s,%s' %( z, z1 ), "" ) #@+node:ekr.20140906065955.18797: *5* newTable def newTable( c ): pos = c.p npos = pos.insertAfter() # tnd ) npos.setHeadString('New Table') c.redraw() c.selectPosition( npos ) viewTable( c , True ) #@+node:ekr.20140906065955.18798: *5* viewTable def viewTable( c, new = False ): pos = c.p dialog = createDialog( pos ) csvv = CSVVisualizer( c ) sframe = Pmw.ScrolledFrame( dialog.interior() ) sframe.pack() tab = createTable( sframe.interior(), csvv.arr ) createBBox( dialog.interior(), csvv, tab ) if not new: n = csvv.addData() else: n = ( 4, 1 ) csvv.createDefaultRecord( n[ 1 ], n[ 0 ] ) tab.configure( cols = n[ 0 ], rows = n[ 1 ] ) dialog.configure( command = lambda name, d = dialog, csvv = csvv: fireButton( name, d, csvv ) ) dialog.activate() #@+node:ekr.20140906065955.18799: *5* fireButton def fireButton( name, dialog, csvv ): if name == "Close": dialog.deactivate() dialog.destroy() elif name == "Write To New": csvv.writeData( False ) elif name == "Save To Current": csvv.writeData( True ) #@+node:ekr.20140906065955.18800: *5* createDialog def createDialog( pos ): dialog = Pmw.Dialog( title = "Table Editor for " + str( pos.h), buttons = [ 'Save To Current', 'Write To New', 'Close' ] ) dbbox = dialog.component( 'buttonbox' ) for z in range( dbbox.numbuttons() ): dbbox.button( z ).configure( background = 'white', foreground = 'blue' ) return dialog #@+node:ekr.20140906065955.18801: *5* createTable def createTable( parent , arr ): tab = tktab.Table( parent , rows = 0, cols = 0, variable = arr, sparsearray=1, background = 'white', foreground = 'blue', selecttype = 'row' ) tab.tag_configure( 'active', background = '#FFE7C6', foreground = 'blue' ) tab.tag_configure( 'sel', background = '#FFE7C6', foreground = 'blue', bd =2 ) tab.pack() return tab #@+node:ekr.20140906065955.18802: *5* createBBox def createBBox( parent, csvv, tab ): bbox = Pmw.ButtonBox( parent ) bconfig = ( ( "Add Row", lambda tab = tab : csvv.addRow( tab ) ), ( "Delete Row", lambda tab = tab: csvv.deleteRow( tab ) ), ( "Add Column", lambda tab = tab: csvv.addColumn( tab ) ), ( "Delete Column", lambda tab = tab: csvv.deleteColumn( tab ) ) ) for z in bconfig: bbox.add( z[ 0 ], command = z[ 1 ], background = 'white', foreground = 'blue' ) bbox.pack() #@+node:ekr.20140906065955.18803: *5* addMenu haveseen = weakref.WeakKeyDictionary() def addMenu( tag, keywords ): c = keywords.get('c') or keywords.get('new_c') if c in haveseen: return haveseen[ c ] = None men = c.frame.menu men = men.getMenu( 'Outline' ) tmen = Tk.Menu( men, tearoff = 0 ) men.add_cascade( menu = tmen, label = "Table Commands" ) c.add_command(tmen, label = "Edit Node With Table", command = lambda c = c: viewTable( c ) ) c.add_command(tmen, label = "Create New Table", command = lambda c = c: newTable( c ) ) #@+node:ekr.20140906065955.18804: *5* if 1: if 1: registerHandler( ('start2' , 'open2', "new") , addMenu ) __version__ = ".125" g.plugin_signon( __name__ ) #@-others #@+node:mork.20041025100851.1: *3* xslt to turn leo file into html <?xml version="1.0"?> <xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method = 'xml' /> <xsl:preserve-space elements='leo_file/tnodes/t'/> <xsl:template match='v'> <ul type='square'> <xsl:variable name ='t' select ='@t' /> <h1><xsl:value-of select='vh'/></h1> <xsl:for-each select='ancestor::leo_file/tnodes/t'> <xsl:if test="./attribute::tx=$t"> <li> <pre> <xsl:value-of select='.' /> </pre> </li> </xsl:if> </xsl:for-each> <xsl:if test ='./v' > <xsl:apply-templates select = 'v'/> </xsl:if> </ul> </xsl:template> <xsl:template match ='leo_file'> <html><head> <style> ul{ position:relative;right=25; border:thin ridge blue} li{ position:relative;right=25} pre{ background:#FFE7C6 } </style> </head> <body> <xsl:apply-templates select='vnodes'/> </body> </html> </xsl:template> <xsl:template match = 'vnodes'> <xsl:for-each select = 'v'> <frame> <xsl:apply-templates select ='.'/> </frame> </xsl:for-each> </xsl:template> </xsl:transform> #@-others ''' #@-others #@-leo