[oXygen-user] oXygen Users Meetup Prague 2014 - Recordings Available

Radu Pisoi radu_pisoi at sync.ro
Wed Mar 19 09:50:44 CDT 2014


Hi Florent,

To make my response more clear for anyone who reads this post, I want to mention that this discussion is about the presentation "How to Develop XSLT Stylesheets for Saxon CE" from the oXygen users meetup at the XML Prague conference.

Also, I will copy your question left on youtube.com here:
> Thanks for this video (that I already saw live :-p) This is very useful. 
> Is the source code of the add-on available anywhere? Maybe with a bit of explanation about how to build it? 
> So I can try to create my own add-on with other completion in XSLT. Is it available for XQuery as well?

Unfortunately, the framework 'XSLT with Saxon-CE support' was not developed as a separate project and the process to extract it is not immediately.

On the other hand, I wouldn't like to make this framework public because:
* we have plans to add built-in support for editing Saxon-CE stylesheets in a future version. This way, the user wouldn't have to import the fake stylesheet in order to avoid the validation problems related to the unknown Saxon-CE extension functions. Also, this solution will not use the additional Schematron module to validate the names of the Saxon-CE extension elements and attributes.
* the rules from the Schematron module cannot validates the XPath expressions from additional Saxon-CE attributes, by example from ixslt:set-attribute/@select. 

In conclusion, this add-on represents a temporary solution that is possible to be deprecated in the next version. 

However, the sources for almost all components are already available in the add-on installation folder:
- the XSLT module that contains declaration of all Saxon-CE extension functions may be found in 
$addOnInstallDir\saxon\ce\resources\extension_functions.xsl;

- the Schematron module used to verify the names of Saxon-CE extension elements and atributes can be found here:
$addOnInstallDir\saxon\ce\resources\saxon-ce.sch

- a Java implementation of the interface SchemaManagerFilter used to improve the content completion proposals by adding the Saxon-CE elements and attributes. The source of this class may be found in the attachments. I attached the sources of the XSLT and Schematron modules.

The path for $addOnInstallDir is $applicationsPreferencesDir/com.oxygenxml.com/extensions/v15.2/frameworks/$updateSiteURL/xslt-saxon-ce.

> Maybe with a bit of explanation about how to build it? So I can try to create my own add-on with other completion in XSLT.

If you need the sources as a framework example, an alternate solution is to download the Author SDK from:
http://oxygenxml.com/oxygen_sdk.html#XML_Editor_Authoring_SDK.
It also contains a sample framework: Simple Documentation Framework - SDF. 

> Is it available for XQuery as well?

Unfortunately, the 'Document Type' support is available only for XML documents, so it cannot be used to extend the content completion for XQuery documents. 

Do not hesitate to contact us if you need further assistance. 

Regards,
Radu
-- 
Radu Pisoi
<oXygen/>  XML Editor, Schema Editor and XSLT Editor/Debugger
http://www.oxygenxml.com

On 3/19/2014 13:24, Florent Georges wrote:
>    Thanks for this, this is very useful!
> 
>    By the way, I left a question on the page for one video last week. 
> That looked like the most sensible place to leave that comment (on 
> http://youtube.com/watch?v=7PzsZWDBZtc).  Just to be sure you did not 
> miss it.
> 
>    Regards,
> 
> -- 
> Florent Georges
> http://fgeorges.org/
> http://h2oconsulting.be/
> 
> 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: extension_functions.xsl
Type: text/xml
Size: 8110 bytes
Desc: not available
Url : http://www.oxygenxml.com/pipermail/oxygen-user/attachments/20140319/315386e7/attachment.xsl 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: saxon-ce.sch
Type: text/xml
Size: 4421 bytes
Desc: not available
Url : http://www.oxygenxml.com/pipermail/oxygen-user/attachments/20140319/315386e7/attachment.xml 
-------------- next part --------------
/*
 *  The Syncro Soft SRL License
 *
 *  Copyright (c) 1998-2012 Syncro Soft SRL, Romania.  All rights
 *  reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *  1. Redistribution of source or in binary form is allowed only with
 *  the prior written permission of Syncro Soft SRL.
 *
 *  2. Redistributions of source code must retain the above copyright
 *  notice, this list of conditions and the following disclaimer.
 *
 *  3. Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in
 *  the documentation and/or other materials provided with the
 *  distribution.
 *
 *  4. The end-user documentation included with the redistribution,
 *  if any, must include the following acknowledgment:
 *  "This product includes software developed by the
 *  Syncro Soft SRL (http://www.sync.ro/)."
 *  Alternately, this acknowledgment may appear in the software itself,
 *  if and wherever such third-party acknowledgments normally appear.
 *
 *  5. The names "Oxygen" and "Syncro Soft SRL" must
 *  not be used to endorse or promote products derived from this
 *  software without prior written permission. For written
 *  permission, please contact support at oxygenxml.com.
 *
 *  6. Products derived from this software may not be called "Oxygen",
 *  nor may "Oxygen" appear in their name, without prior written
 *  permission of the Syncro Soft SRL.
 *
 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED.  IN NO EVENT SHALL THE SYNCRO SOFT SRL OR
 *  ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 *  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 *  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 *  SUCH DAMAGE.
 */
package ro.sync.ecss.extensions.xslt;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Stack;

import ro.sync.annotations.api.API;
import ro.sync.annotations.api.APIType;
import ro.sync.annotations.api.SourceType;
import ro.sync.contentcompletion.xml.CIAttribute;
import ro.sync.contentcompletion.xml.CIElement;
import ro.sync.contentcompletion.xml.CIElementAdapter;
import ro.sync.contentcompletion.xml.CIValue;
import ro.sync.contentcompletion.xml.Context;
import ro.sync.contentcompletion.xml.ContextElement;
import ro.sync.contentcompletion.xml.SchemaManagerFilter;
import ro.sync.contentcompletion.xml.WhatAttributesCanGoHereContext;
import ro.sync.contentcompletion.xml.WhatElementsCanGoHereContext;
import ro.sync.contentcompletion.xml.WhatPossibleValuesHasAttributeContext;
import ro.sync.outline.xml.Attribute;
import ro.sync.xml.ProxyNamespaceMapping;

/**
 * Extends XSLT schema manager to add extension elements for Saxon CE.
 * 
 * @author radu_pisoi
 */
@API(type=APIType.INTERNAL, src=SourceType.PUBLIC)
public class SaxonCESchemaManagerFilter implements SchemaManagerFilter {
  /**
   * Namespace for Saxon CE extension elements and functions.
   */
  private static final String IXSL_NAMESPACE = "http://saxonica.com/ns/interactiveXSLT";
  
  /**
   * XSL namespace.
   */
  private static final String XSLT_NAMESPACE = "http://www.w3.org/1999/XSL/Transform";
  
  /**
   * Annotation for set-attribute extension element.
   */
  private static final String SET_ATTRIBUTE_ANNOTATION = 
      "Sets an attribute or psuedo attribute with a given name and value in the HTML page for the current node. "
      + "The current node must be an element in the HTML page. "
      + "The instruction has attributes name, namespace, and select, and works in exactly the same way as "
      + "xsl:attribute, except that the attribute is written directly to the current element.\n"
      + "Like xsl:result-document, the writing of the attribute is delayed until the end of the transformation phase. "
      + "The instruction also follows the same restrictions as xsl:result-document in that it should "
      + "not be used while evaluating a variable or a function.";
  
  /**
   * Annotation for set-property extension element.
   */
  private static final String SET_PROPERTY_ANNOTATION = 
      "Sets the value of properties for client objects that are not part of the DOM. "
      + "The instruction has attributes name, object, and select. "
      + "The name attribute is either a property name or a dot (character '.') separated list of names. "
      + "The object attribute is the object the property belong to ? when no object attribute is present, the window object is used. "
      + "The select attribute provides the new property value.";
  
  /**
   * Annotation for remove-attribute extension element.
   */
  private static final String REMOVE_ATTRIBUTE_ANNOTATION = 
      "Removes the attribute with the given name argument at the current element node. "
      + "The optional namespace argument is used to specify the namespace of the attribute to be removed. "
      + "A typical use for this is to enable a disabled HTML element by removing the disabled attribute (setting the value has no effect).";
  
  /**
   * Annotation for schedule-action extension element.
   */
  private static final String SCHEDULE_ACTION_ANNOTATION = 
      "Makes an asynchronous call to the template named in an xsl:call-template instruction that is the "
      + "only permitted body content of ixsl:schedule-action. "
      + "The wait attribute of ixsl:schedule-action is an integer used to specify the delay in miliseconds "
      + "before the call is invoked. See the Animation section for further detail.";
  
  /**
   * Element name for set-attribute.
   */
  private static final String SET_ATTRIBUTE_ELEMENT_NAME = "set-attribute";
  
  /**
   * Element name for set-property.
   */
  private static final String SET_PROPERTY_ELEMENT_NAME = "set-property";

  /**
   * Element name for remove-attribute.
   */
  private static final String REMOVE_ATTRIBUTE_ELEMENT_NAME = "remove-attribute";

  /**
   * Element name for schedule-action.
   */
  private static final String SCHEDULE_ACTION_ELEMENT_NAME = "schedule-action";
  
  /**
   * CI element implementation for Saxon CE extension elements.
   */
  public static class SaxonCECIElement extends CIElementAdapter {
    /**
     * Element local name.
     */
    private String name;
    
    /**
     * The detected prefix for IXSL namespace.
     */
    private String prefix;

    /**
     * Extension element annotation.
     */
    private String annotation;
    
    /**
     * The list with required attributes.
     */
    private List<CIAttribute> attributes;

    /**
     * Constructor.
     * 
     * @param name The element name. 
     * @param prefix  The detected prefix for IXSL namespace.
     * @param annotation Extension element annotation.
     */
    public SaxonCECIElement(String name, String prefix, String annotation) {
      this.name = name;
      this.prefix = prefix;
      this.annotation = annotation;
    }
    
    /**
     * @see ro.sync.contentcompletion.xml.CIElementAdapter#getAnnotation()
     */
    @Override
    public String getAnnotation() {
      return annotation;
    }
    
    /**
     * @see ro.sync.contentcompletion.xml.CIElementAdapter#getName()
     */
    @Override
    public String getName() {
      return name;
    }
    
    /**
     * @see ro.sync.contentcompletion.xml.CIElementAdapter#setAttributes(java.util.List)
     */
    @Override
    public void setAttributes(List<CIAttribute> attributes) {
      this.attributes = attributes;
    }
    
    /**
     * @see ro.sync.contentcompletion.xml.CIElementAdapter#getAttributes()
     */
    @Override
    public List<CIAttribute> getAttributes() {
      return attributes;
    }
    
    /**
     * @see ro.sync.contentcompletion.xml.CIElementAdapter#getNamespace()
     */
    @Override
    public String getNamespace() {
      return IXSL_NAMESPACE;
    }
    
    /**
     * @see ro.sync.contentcompletion.xml.CIElementAdapter#getQName()
     */
    @Override
    public String getQName() {
      String qname = name;
      if (prefix.length() > 0) {
        qname = prefix + ":" + name;
      }
      return qname;
    }
    
    /**
     * @see ro.sync.contentcompletion.xml.CIElementAdapter#getPrefix()
     */
    @Override
    public String getPrefix() {
      return prefix;
    }
  }
  
  /**
   * @see ro.sync.ecss.extensions.api.Extension#getDescription()
   */
  @Override
  public String getDescription() {
    return "Saxon CE Schema manager filter";
  }

  /**
   * Verify if IXSL namespace is declared.
   * 
   * @param context The current context. 
   * @return True if IXSL namespace is declared.
   */
  private boolean isIXSLNamespaceDeclared(Context context) {
    boolean isIXSLNamespaceDeclared = false;
    
    // Test if IXSL namespace is declared.
    ProxyNamespaceMapping pnm = context.getPrefixNamespaceMapping();
    Set<String> namespaces = pnm.getNamespaces();
    isIXSLNamespaceDeclared = namespaces != null && namespaces.contains(IXSL_NAMESPACE);
    
    return isIXSLNamespaceDeclared;
  }
  
  /**
   * @see ro.sync.contentcompletion.xml.SchemaManagerFilter#filterElements(java.util.List, ro.sync.contentcompletion.xml.WhatElementsCanGoHereContext)
   */
  @Override
  public List<CIElement> filterElements(
      List<CIElement> elements,
      WhatElementsCanGoHereContext context) {
    if (context == null) {
      return elements;
    }
    
    // Add Saxon CE extension elements only inside the xsl:template elements 
    Stack<ContextElement> elementStack = context.getElementStack();
    if (elementStack != null && elementStack.size() > 1) {
      ContextElement contextElement = elementStack.get(1);
      if (XSLT_NAMESPACE.equals(contextElement.getNamespace()) && isIXSLNamespaceDeclared(context)) {
        // Add Saxon CE extension elements
        ProxyNamespaceMapping pnm = context.getPrefixNamespaceMapping();
        String prefix = pnm.getPrefixForNamespace(IXSL_NAMESPACE);
        
        // ixsl:set-attribute
        SaxonCECIElement setAttributeElement = new SaxonCECIElement(
            SET_ATTRIBUTE_ELEMENT_NAME, 
            prefix,
            SET_ATTRIBUTE_ANNOTATION);
        List<CIAttribute> reqAttributes = new ArrayList<CIAttribute>();
        
        // Name attribute
        CIAttribute nameAttr = new CIAttribute("name", true, false, null, null);
        reqAttributes.add(nameAttr);

        // select attribute
        CIAttribute selectAttr = new CIAttribute("select", true, false, null, null);
        reqAttributes.add(selectAttr);
        
        setAttributeElement.setAttributes(reqAttributes);          
        elements.add(setAttributeElement);
        
        // ixsl:set-property
        SaxonCECIElement setPropertyElement = new SaxonCECIElement(
            SET_PROPERTY_ELEMENT_NAME, 
            prefix, 
            SET_PROPERTY_ANNOTATION);
        reqAttributes = new ArrayList<CIAttribute>();
        // Name attribute
        nameAttr = new CIAttribute("name", true, false, null, null);
        reqAttributes.add(nameAttr);

        // select attribute
        selectAttr = new CIAttribute("select", true, false, null, null);
        reqAttributes.add(selectAttr);
        setPropertyElement.setAttributes(reqAttributes);          
        elements.add(setPropertyElement);

        // ixsl:remove-attribute
        SaxonCECIElement removeAttributeElement = new SaxonCECIElement(REMOVE_ATTRIBUTE_ELEMENT_NAME, prefix, REMOVE_ATTRIBUTE_ANNOTATION);

        reqAttributes = new ArrayList<CIAttribute>();
        // Name attribute
        nameAttr = new CIAttribute("name", true, false, null, null);
        reqAttributes.add(nameAttr);
        removeAttributeElement.setAttributes(reqAttributes);   
        elements.add(removeAttributeElement);

        SaxonCECIElement scheduleActionElement = new SaxonCECIElement(SCHEDULE_ACTION_ELEMENT_NAME, prefix, SCHEDULE_ACTION_ANNOTATION);
        elements.add(scheduleActionElement);
      }
    }
    
    return elements;
  }

  /**
   * @see ro.sync.contentcompletion.xml.SchemaManagerFilter#filterAttributes(java.util.List, ro.sync.contentcompletion.xml.WhatAttributesCanGoHereContext)
   */
  @Override
  public List<CIAttribute> filterAttributes(
      List<CIAttribute> attributes,
      WhatAttributesCanGoHereContext context) {
    if (context == null) {
      return attributes;
    }

    
    ContextElement parentElement = context.getParentElement();
    
    if (parentElement != null && IXSL_NAMESPACE.equals(parentElement.getNamespace())) {
      // Elements from IXSL namespace
      String elementName = parentElement.getQName();
      int idx = elementName.indexOf(":");
      if (idx >= 0) {
        elementName = elementName.substring(idx + 1);
      }
      
      if (attributes == null) {
        attributes = new ArrayList<CIAttribute>();
      }
      
      Attribute[] previousAttributes = context.getPreviousAttributes();
      List<Attribute> prevAttributesList = null;
      if (previousAttributes != null) {
        prevAttributesList = Arrays.asList(previousAttributes);
      }
      
      if (SET_ATTRIBUTE_ELEMENT_NAME.equals(elementName)) {
        // Name attribute
        CIAttribute nameAttr = new CIAttribute("name", true, false, null, null);
        if (!isAttributeSet(prevAttributesList, nameAttr)) {
          attributes.add(nameAttr);
        }
        
        // Namespace attribute
        CIAttribute namespaceAttr = new CIAttribute("namespace", true, false, null, null);
        if (!isAttributeSet(prevAttributesList, namespaceAttr)) {
          attributes.add(namespaceAttr);
        }
        
        // select attribute
        CIAttribute selectAttr = new CIAttribute("select", true, false, null, null);
        if (!isAttributeSet(prevAttributesList, selectAttr)) {
          attributes.add(selectAttr);
        }
      } else if (SET_PROPERTY_ELEMENT_NAME.equals(elementName)) {
        // Name attribute
        CIAttribute nameAttr = new CIAttribute("name", true, false, null, null);
        if (!isAttributeSet(prevAttributesList, nameAttr)) {
          attributes.add(nameAttr);
        }
        
        // object attribute
        CIAttribute objectAttr = new CIAttribute("object", true, false, null, null);
        if (!isAttributeSet(prevAttributesList, objectAttr)) {
          attributes.add(objectAttr);
        }
        
        // select attribute
        CIAttribute selectAttr = new CIAttribute("select", true, false, null, null);
        if (!isAttributeSet(prevAttributesList, selectAttr)) {
          attributes.add(selectAttr);
        }
      } else if (REMOVE_ATTRIBUTE_ELEMENT_NAME.equals(elementName)) {
        // Name attribute
        CIAttribute nameAttr = new CIAttribute("name", true, false, null, null);
        if (!isAttributeSet(prevAttributesList, nameAttr)) {
          attributes.add(nameAttr);
        }
        
        // object attribute
        CIAttribute namespaceAttr = new CIAttribute("namespace", true, false, null, null);
        if (!isAttributeSet(prevAttributesList, namespaceAttr)) {
          attributes.add(namespaceAttr);
        }
        
      } else if (SCHEDULE_ACTION_ELEMENT_NAME.equals(elementName)) {
        // wait attribute
        CIAttribute waitAttr = new CIAttribute("wait", true, false, null, null);
        if (!isAttributeSet(prevAttributesList, waitAttr)) {
          attributes.add(waitAttr);
        }
      }
    }
    return attributes;
  }
  
  /**
   * Test if an attribute is already set.
   * 
   * @param prevAttributesList The list with attributes.
   * @param attr  The attribute to verify.
   * @return True if attribute is already set.
   */
  private boolean isAttributeSet(List<Attribute> prevAttributesList, CIAttribute attr) {
    boolean isAttributeSet = false;
    if (prevAttributesList != null) {
      for (Iterator iterator = prevAttributesList.iterator(); iterator.hasNext();) {
        Attribute attribute = (Attribute) iterator.next();
        if (attribute.getLocalName() != null && attribute.getLocalName().equals(attr.getName())) {
          isAttributeSet = true;
          break;
        }
      }
    }
    return isAttributeSet;
  }

  /**
   * @see ro.sync.contentcompletion.xml.SchemaManagerFilter#filterAttributeValues(java.util.List, ro.sync.contentcompletion.xml.WhatPossibleValuesHasAttributeContext)
   */
  @Override
  public List<CIValue> filterAttributeValues(List<CIValue> attributeValues,
      WhatPossibleValuesHasAttributeContext context) {
    if (context == null) {
      return attributeValues;
    }
    
    if(isIXSLNamespaceDeclared(context)) {
      ContextElement parentElement = context.getParentElement();
      
      if (parentElement != null && 
          XSLT_NAMESPACE.equals(parentElement.getNamespace())) {
        
        // Elements from XSL namespace
        String elementName = parentElement.getQName();
        int idx = elementName.indexOf(":");
        if (idx >= 0) {
          elementName = elementName.substring(idx + 1);
        }
        
        if (attributeValues == null) {
          attributeValues = new ArrayList<CIValue>();
        }
        
        String attributeName = context.getAttributeName();
        
        if ("result-document".equals(elementName) && "method".equals(attributeName)) {
          // Method attribute
          ProxyNamespaceMapping namespaceMapping = parentElement.getPrefixNamespaceMapping();
          String ixslPrefix = namespaceMapping.getPrefixForNamespace(IXSL_NAMESPACE);
          
          String value = "replace-content";
          if (ixslPrefix != null && ixslPrefix.length() > 0) {
            value = ixslPrefix + ":" + value;
          }
          
          CIValue val = new CIValue(value);
          attributeValues.add(val);
          
          value = "append-content";
          if (ixslPrefix != null && ixslPrefix.length() > 0) {
            value = ixslPrefix + ":" + value;
          }
          
          val = new CIValue(value);
          attributeValues.add(val);
        }
      }
    }
    
    return attributeValues;
  }

  /**
   * @see ro.sync.contentcompletion.xml.SchemaManagerFilter#filterElementValues(java.util.List, ro.sync.contentcompletion.xml.Context)
   */
  @Override
  public List<CIValue> filterElementValues(List<CIValue> elementValues, Context context) {
    return elementValues;
  }

}


More information about the oXygen-user mailing list