Exemple #1
0
// The output is not guaranteed to be well-formed XML, so the
// serialized string is returned. Consideration is being given
// to returning a slice of bytes and encoding information.
func (style *Stylesheet) Process(doc *xml.XmlDocument, options StylesheetOptions) (out string, err error) {
	// lookup output method, doctypes, encoding
	// create output document with appropriate values
	output := xml.CreateEmptyDocument(doc.InputEncoding(), doc.OutputEncoding())
	// init context node/document
	context := &ExecutionContext{Output: output.Me, OutputNode: output, Style: style, Source: doc}
	context.Current = doc
	context.XPathContext = doc.DocXPathCtx()
	// when evaluating keys/global vars position is always 1
	context.XPathContext.SetContextPosition(1, 1)
	start := doc
	style.populateKeys(start, context)
	// eval global params
	// eval global variables
	for _, val := range style.Variables {
		val.Apply(doc, context)
	}
	// set xpath context
	// process nodes
	style.processNode(start, context, nil)

	out, err = style.constructOutput(output, options)
	// reset anything required for re-use
	return
}
Exemple #2
0
// ParseStylesheet compiles the stylesheet's XML representation
// and returns a Stylesheet instance.
//
// The fileuri argument is used to resolve relative paths for xsl:import and xsl:include
// instructions and should generally be the filename of the stylesheet. If you pass
// an empty string, the working directory will be used for path resolution.
func ParseStylesheet(doc *xml.XmlDocument, fileuri string) (style *Stylesheet, err error) {
	style = &Stylesheet{Doc: doc,
		NamespaceMapping: make(map[string]string),
		NamespaceAlias:   make(map[string]string),
		ElementMatches:   make(map[string]*list.List),
		AttrMatches:      make(map[string]*list.List),
		PIMatches:        list.New(),
		CommentMatches:   list.New(),
		IdKeyMatches:     list.New(),
		NodeMatches:      list.New(),
		TextMatches:      list.New(),
		Imports:          list.New(),
		NamedTemplates:   make(map[string]*Template),
		AttributeSets:    make(map[string]CompiledStep),
		includes:         make(map[string]bool),
		Keys:             make(map[string]*Key),
		Functions:        make(map[string]xpath.XPathFunction),
		Variables:        make(map[string]*Variable)}

	// register the built-in XSLT functions
	style.RegisterXsltFunctions()

	//XsltParseStylesheetProcess
	cur := xml.Node(doc.Root())

	// get all the namespace mappings
	for _, ns := range cur.DeclaredNamespaces() {
		style.NamespaceMapping[ns.Uri] = ns.Prefix
	}

	//get xsl:version, should be 1.0 or 2.0
	version := cur.Attr("version")
	if version != "1.0" {
		log.Println("VERSION 1.0 expected")
	}

	//record excluded prefixes
	excl := cur.Attr("exclude-result-prefixes")
	if excl != "" {
		style.ExcludePrefixes = strings.Fields(excl)
	}
	//record extension prefixes
	ext := cur.Attr("extension-element-prefixes")
	if ext != "" {
		style.ExtensionPrefixes = strings.Fields(ext)
	}

	//if the root is an LRE, this is an simplified stylesheet
	if !IsXsltName(cur, "stylesheet") && !IsXsltName(cur, "transform") {
		template := &Template{Match: "/", Priority: 0}
		template.CompileContent(doc)
		style.compilePattern(template, "")
		return
	}

	//optionally optimize by removing blank nodes, combining adjacent text nodes, etc
	err = style.parseChildren(cur, fileuri)

	//xsl:import (must be first)
	//flag non-empty text nodes, non XSL-namespaced nodes
	//  actually registered extension namspaces are good!
	//warn unknown XSLT element (forwards-compatible mode)

	return
}
Exemple #3
0
// actually produce (and possibly write) the final output
func (style *Stylesheet) constructOutput(output *xml.XmlDocument, options StylesheetOptions) (out string, err error) {
	//if not explicitly set, spec requires us to check for html
	outputType := style.OutputMethod
	if outputType == "" {
		outputType = "xml"
		root := output.Root()
		if root != nil && root.Name() == "html" && root.Namespace() == "" {
			outputType = "html"
		}
	}

	// construct DTD declaration depending on xsl:output settings
	docType := ""
	if style.doctypeSystem != "" {
		docType = "<!DOCTYPE "
		docType = docType + output.Root().Name()
		if style.doctypePublic != "" {
			docType = docType + fmt.Sprintf(" PUBLIC \"%s\"", style.doctypePublic)
		} else {
			docType = docType + " SYSTEM"
		}
		docType = docType + fmt.Sprintf(" \"%s\"", style.doctypeSystem)
		docType = docType + ">\n"
	}

	// create the XML declaration depending on xsl:output settings
	decl := ""
	if outputType == "xml" {
		if !style.OmitXmlDeclaration {
			decl = style.constructXmlDeclaration()
		}
		format := xml.XML_SAVE_NO_DECL | xml.XML_SAVE_AS_XML
		if options.IndentOutput || style.IndentOutput {
			format = format | xml.XML_SAVE_FORMAT
		}
		// we get slightly incorrect output if we call out.SerializeWithFormat directly
		// this seems to be a libxml bug; we work around it the same way libxslt does

		//TODO: honor desired encoding
		//  this involves decisions about supported encodings, strings vs byte slices
		//  we can sidestep a little if we enable option to write directly to file
		for cur := output.FirstChild(); cur != nil; cur = cur.NextSibling() {
			b, size := cur.SerializeWithFormat(format, nil, nil)
			if b != nil {
				out = out + string(b[:size])
			}
		}
		if out != "" {
			out = decl + docType + out + "\n"
		}
	}
	if outputType == "html" {
		out = docType
		b, size := output.ToHtml(nil, nil)
		out = out + string(b[:size])
	}
	if outputType == "text" {
		format := xml.XML_SAVE_NO_DECL
		for cur := output.FirstChild(); cur != nil; cur = cur.NextSibling() {
			b, size := cur.SerializeWithFormat(format, nil, nil)
			if b != nil {
				out = out + string(b[:size])
			}
		}
	}
	return
}