Skip to content

Commit

Permalink
SetVariable execute="later"
Browse files Browse the repository at this point in the history
This fixes #5
  • Loading branch information
pgundlach committed Apr 29, 2023
1 parent f153fc8 commit f40abe1
Show file tree
Hide file tree
Showing 5 changed files with 374 additions and 50 deletions.
59 changes: 40 additions & 19 deletions core/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func cmdA(xd *xtsDocument, layoutelt *goxml.Element) (xpath.Sequence, error) {
frontend.SettingHyperlink: hl,
},
}
getTextvalues(te, seq, "cmdA", layoutelt.Line)
err = getTextvalues(te, seq, "cmdA", layoutelt.Line)

return xpath.Sequence{te}, err
}
Expand Down Expand Up @@ -187,13 +187,15 @@ func cmdAttribute(xd *xtsDocument, layoutelt *goxml.Element) (xpath.Sequence, er

func cmdB(xd *xtsDocument, layoutelt *goxml.Element) (xpath.Sequence, error) {
seq, err := dispatch(xd, layoutelt, xd.data)

if err != nil {
return nil, err
}
te := &frontend.Text{
Settings: frontend.TypesettingSettings{
frontend.SettingFontWeight: frontend.FontWeight700,
},
}
getTextvalues(te, seq, "cmdBold", layoutelt.Line)
err = getTextvalues(te, seq, "cmdBold", layoutelt.Line)

return xpath.Sequence{te}, err
}
Expand Down Expand Up @@ -731,16 +733,16 @@ func cmdForall(xd *xtsDocument, layoutelt *goxml.Element) (xpath.Sequence, error
return nil, newTypesettingErrorFromStringf("ForAll (line %d): error parsing select XPath expression %s", layoutelt.Line, err)
}

oldContext := xd.data.Ctx.SetContext(xpath.Sequence{})
oldContext := xd.data.Ctx.SetContextSequence(xpath.Sequence{})
for i, itm := range eval {
xd.data.Ctx.SetContext(xpath.Sequence{itm})
xd.data.Ctx.SetContextSequence(xpath.Sequence{itm})
xd.data.Ctx.Pos = i + 1
eval, err = dispatch(xd, layoutelt, xd.data)
if err != nil {
return nil, err
}
}
xd.data.Ctx.SetContext(xpath.Sequence{oldContext})
xd.data.Ctx.SetContextSequence(xpath.Sequence{oldContext})

return nil, nil
}
Expand All @@ -767,13 +769,15 @@ func cmdGroup(xd *xtsDocument, layoutelt *goxml.Element) (xpath.Sequence, error)

func cmdI(xd *xtsDocument, layoutelt *goxml.Element) (xpath.Sequence, error) {
seq, err := dispatch(xd, layoutelt, xd.data)

if err != nil {
return nil, err
}
te := &frontend.Text{
Settings: frontend.TypesettingSettings{
frontend.SettingStyle: frontend.FontStyleItalic,
},
}
getTextvalues(te, seq, "cmdBold", layoutelt.Line)
err = getTextvalues(te, seq, "cmdBold", layoutelt.Line)

return xpath.Sequence{te}, err
}
Expand Down Expand Up @@ -856,7 +860,7 @@ func cmdLoadDataset(xd *xtsDocument, layoutelt *goxml.Element) (xpath.Sequence,
if err != nil {
return nil, err
}
oldContext := xd.data.Ctx.SetContext(xpath.Sequence{})
oldContext := xd.data.Ctx.SetContextSequence(xpath.Sequence{})
xd.data.Ctx.Store = map[any]any{
"xd": xd,
}
Expand All @@ -865,7 +869,7 @@ func cmdLoadDataset(xd *xtsDocument, layoutelt *goxml.Element) (xpath.Sequence,
_, err = dispatch(xd, rec, xd.data)
}
}
xd.data.Ctx.SetContext(xpath.Sequence{oldContext})
xd.data.Ctx.SetContextSequence(xpath.Sequence{oldContext})
xd.data = saveData

return nil, nil
Expand Down Expand Up @@ -914,12 +918,12 @@ func cmdProcessNode(xd *xtsDocument, layoutelt *goxml.Element) (xpath.Sequence,
return nil, err
}

oldContext := xd.data.Ctx.SetContext(xpath.Sequence{})
oldContext := xd.data.Ctx.SetContextSequence(xpath.Sequence{})

for i, itm := range eval {
xd.data.Ctx.Pos = i + 1
if elt, ok := itm.(*goxml.Element); ok {
xd.data.Ctx.SetContext(xpath.Sequence{elt})
xd.data.Ctx.SetContextSequence(xpath.Sequence{elt})
if dd, ok := dataDispatcher[elt.Name]; ok {
if rec, ok := dd[attValues.Mode]; ok {
_, err = dispatch(xd, rec, xd.data)
Expand All @@ -928,7 +932,7 @@ func cmdProcessNode(xd *xtsDocument, layoutelt *goxml.Element) (xpath.Sequence,
}
}

xd.data.Ctx.SetContext(xpath.Sequence{oldContext})
xd.data.Ctx.SetContextSequence(xpath.Sequence{oldContext})
return nil, nil
}

Expand Down Expand Up @@ -1217,8 +1221,8 @@ func cmdParagraph(xd *xtsDocument, layoutelt *goxml.Element) (xpath.Sequence, er
bag.Logger.Warnf("text format %q not found", *name)
}
}
getTextvalues(te, seq, "cmdParagraph", layoutelt.Line)
return xpath.Sequence{te}, nil
err = getTextvalues(te, seq, "cmdParagraph", layoutelt.Line)
return xpath.Sequence{te}, err
}

func cmdPlaceObject(xd *xtsDocument, layoutelt *goxml.Element) (xpath.Sequence, error) {
Expand All @@ -1242,7 +1246,7 @@ func cmdPlaceObject(xd *xtsDocument, layoutelt *goxml.Element) (xpath.Sequence,
var area *area
var ok bool
if area, ok = xd.currentGrid.areas[attValues.Area]; !ok {
return nil, fmt.Errorf("area %s not found", attValues.Area)
return nil, newTypesettingErrorFromStringf(fmt.Sprintf("area %s not found", attValues.Area))
}

pos := positioningUnknown
Expand Down Expand Up @@ -1473,12 +1477,24 @@ func cmdSetVariable(xd *xtsDocument, layoutelt *goxml.Element) (xpath.Sequence,
attValues := &struct {
Select *string `sdxml:"noescape"`
Variable string `sdxml:"mustexist"`
Execute *string
Trace bool
}{}
if err := getXMLAttributes(xd, layoutelt, attValues); err != nil {
return nil, err
}

if ex := attValues.Execute; ex != nil {
if *ex == "later" {
if sel := attValues.Select; sel != nil {
xd.data.SetVariable(attValues.Variable, xpath.Sequence{returnEvalSelectLater(*sel, xd)})
} else {
xd.data.SetVariable(attValues.Variable, xpath.Sequence{returnEvalBodyLater(layoutelt, xd)})
}
}
return nil, nil
}

var eval xpath.Sequence
var err error
if attValues.Select != nil {
Expand Down Expand Up @@ -1517,6 +1533,9 @@ func cmdSpan(xd *xtsDocument, layoutelt *goxml.Element) (xpath.Sequence, error)
}

seq, err := dispatch(xd, layoutelt, xd.data)
if err != nil {
return nil, err
}

te := &frontend.Text{
Settings: frontend.TypesettingSettings{},
Expand All @@ -1532,7 +1551,7 @@ func cmdSpan(xd *xtsDocument, layoutelt *goxml.Element) (xpath.Sequence, error)
te.Settings[frontend.SettingStyle] = frontend.ResolveFontStyle(val)
}

getTextvalues(te, seq, "cmdSpan", layoutelt.Line)
err = getTextvalues(te, seq, "cmdSpan", layoutelt.Line)

return xpath.Sequence{te}, err
}
Expand Down Expand Up @@ -2023,13 +2042,15 @@ func cmdTrace(xd *xtsDocument, layoutelt *goxml.Element) (xpath.Sequence, error)

func cmdU(xd *xtsDocument, layoutelt *goxml.Element) (xpath.Sequence, error) {
seq, err := dispatch(xd, layoutelt, xd.data)

if err != nil {
return nil, err
}
te := &frontend.Text{
Settings: frontend.TypesettingSettings{
frontend.SettingTextDecorationLine: frontend.TextDecorationUnderline,
},
}
getTextvalues(te, seq, "cmdUnderline", layoutelt.Line)
err = getTextvalues(te, seq, "cmdUnderline", layoutelt.Line)

return xpath.Sequence{te}, err
}
Expand Down
46 changes: 42 additions & 4 deletions core/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,35 @@ import (
xpath "github.com/speedata/goxpath"
)

// A seqfunc is used to defer evaluation of SetVariable
type seqfunc func() (xpath.Sequence, error)

// The next two functions store the contents of a SetVariable selection (either
// with the attribute select or the body of the SetVariable element) and return
// a function that evaluate with the context that is valid during the creation
// time.
func returnEvalSelectLater(selection string, xd *xtsDocument) seqfunc {
ctx := xpath.CopyContext(xd.data.Ctx)
return func() (xpath.Sequence, error) {
oldContext := xpath.CopyContext(xd.data.Ctx)
xd.data.Ctx = ctx
eval, err := xd.data.Evaluate(selection)
xd.data.Ctx = oldContext
return eval, err
}
}

func returnEvalBodyLater(layoutelt *goxml.Element, xd *xtsDocument) seqfunc {
ctx := xpath.CopyContext(xd.data.Ctx)
return func() (xpath.Sequence, error) {
oldContext := xpath.CopyContext(xd.data.Ctx)
xd.data.Ctx = ctx
eval, err := dispatch(xd, layoutelt, xd.data)
xd.data.Ctx = oldContext
return eval, err
}
}

// applyLayoutStylesheet creates an HTML fragment, applies CSS and reads the
// attributes from the fragment. This is handy when styling layout elements with
// CSS.
Expand Down Expand Up @@ -85,10 +114,10 @@ func (xd *xtsDocument) getFontSizeLeading(size string) (bag.ScaledPoint, bag.Sca

// Get the values from the child elements of B, Paragraph and its ilk and fill
// the provided Text struct to get a recursive data structure.
func getTextvalues(te *frontend.Text, seq xpath.Sequence, cmdname string, line int) {
func getTextvalues(te *frontend.Text, seq xpath.Sequence, cmdname string, line int) error {
if len(seq) == 0 {
te.Items = append(te.Items, "\u200B")
return
return nil
}
for _, itm := range seq {
switch t := itm.(type) {
Expand Down Expand Up @@ -118,6 +147,7 @@ func getTextvalues(te *frontend.Text, seq xpath.Sequence, cmdname string, line i
bag.Logger.DPanicf("%s (line %d): unknown type %T (getTextvalues)", cmdname, line, t)
}
}
return nil
}

func getStructTag(f reflect.StructField, tagName string) string {
Expand Down Expand Up @@ -390,10 +420,18 @@ func (xd *xtsDocument) getAttributeHeight(name string, element *goxml.Element, m
// evaluateXPath runs an XPath expression. It saves and restores the current
// context.
func evaluateXPath(xd *xtsDocument, namespaces map[string]string, xpath string) (xpath.Sequence, error) {
oldContext := xd.data.Ctx.GetContext()
oldContext := xd.data.Ctx.GetContextSequence()
xd.data.Ctx.Namespaces = namespaces
seq, err := xd.data.Evaluate(xpath)
xd.data.Ctx.SetContext(oldContext)
for _, itm := range seq {
if f, ok := itm.(seqfunc); ok {
// we assume that f() re-sets the old context
seq, err = f()
return seq, err
}
}

xd.data.Ctx.SetContextSequence(oldContext)
return seq, err
}

Expand Down
Loading

0 comments on commit f40abe1

Please sign in to comment.