Thursday, August 21, 2008

Using Emacs and nXML to edit XAML files

In this post I'll show how to use Emacs and nXML to edit XAML files.

The powerful nXML mode allows the use a RELAX NG schema to validate and to assist the edition of XML documents.

The RELAX NG schema generated in the previous post is used to provide XAML code completion.

As described in the previous post, not all elements of XAML could be mapped to RELAX NG, so validation errors will be displayed on valid documents.

Once you have Emacs and nXML installed the following line could be added to the .emacs file to activate nXML when a XAML file is opened.


(setq auto-mode-alist (cons '("\.xaml$" . nxml-mode) auto-mode-alist))


Also the mode could be activated by typing M-x nxml-mode.

Once a file is opened with the nXML mode, the schema must be specified for that file. The XML -> Set Schema -> File... option is used to specify the silverlight.rnc file created for the previous post.

Setting the XAML schema

Once the schema is load we can start modifying the file. For example the following screenshot shows the result of pressing Control+Enter (C-Return) inside a DoubleAnimation tag.

Presenting available attributes

All the available attributes are presented as possible options.

In several scenarios the schema presents only the valid options for a given context. For example the following screenshot shows the result of pressing Control+Enter after an opening angle bracket inside a StackPanel tag:

Emacs displaying possible options for a StackPanel child

Properties elements are also available, for example the following screenshot shows, available options by pressing Control+Enter after the "<Line." text:

Property Element completion


Code for this post can be found here.

Wednesday, August 20, 2008

Creating a RELAX NG schema from classes using XAML rules with IronRuby

In this post I'm going to show a little IronRuby program that generates a RELAX NG Schema Definition for XAML documents using some of the rules to map XML to classes.

This program is was created by modifiying the code of the
previous post to also generate RELAX NG Compact Syntax.

My goal with this post is to use the generated schema with a tool that supports RELAX NG to assist the creation of XAML document.

As with the previous post mentions, not all XAML rules can be expressed in XSD or in this case in RELAX NG. However I hope that the generated schema at least provides some help for editing XAML files.

Changes to RootClassNode and ClassNode classes

The RootClassNode and ClassNode contains information for classes that need an schema definition.

Here's the method to create the definition for one element:


def write_schema_definition_rlx(file)
ctype_name = @the_type.Name.to_s+"Atts"
file.print("#{ctype_name} = ")

write_properties_definition_rlx(file)

file.puts("")

if (!is_abstract)
file.print("#{@the_type.Name} = element #{@the_type.Name} { #{ctype_name} ,")
write_inner_elements_definition_rlx(file) unless is_abstract
file.puts("}")
end


@children.values.each {|c| c.write_schema_definition_rlx(file)}
end


What this code dos is to create a grammar definition for the attributes of the current class. This definition will be used in the definition of the current element (if not abstract) and in the definition of the descendants of the current element.

The write_properties_definition_rlx method writes the definition of the attributes using the defined properties.


def write_properties_definition_rlx(file)
atts = get_type_properties

file.print "("
file.print atts.map {|p| "attribute #{p.Name} { text } ?"}.join(" , ")
file.print ")*"
end


The write_inner_elements_definition_rlx method adds the definition of the content property if it exists. The add_group_base_type creates a group with all the descendants of the of the type of the content property.


def write_inner_elements_definition_rlx(file)
write_element_properties_definition_rlx(file)
if is_container
file.puts(",")
property_type = get_content_property_type
element_type = get_collection_element_type

if (element_type != nil)

group = @registry.add_group_base_type(element_type.FullName)
file.puts "(#{group} *)"
elsif (@registry.descends_from_base_type(property_type))

group = @registry.add_group_base_type(property_type.FullName)
file.puts(group)
else

file.puts(" text ")
end

end
end


The write_schema_definition_rlx creates the definition of a ClassNode which represents a class that inherits from another class.


def write_schema_definition_rlx(file)
if @the_type.contains_generic_parameters
ctype_name = @the_type.Name.to_s.gsub(/`/,'')+"Atts"
else
ctype_name = @the_type.Name.to_s + "Atts"
end

file.print("#{ctype_name} = #{@the_type.BaseType.Name}Atts")
if get_type_properties.length > 0
file.print(" , ")
write_properties_definition_rlx(file)
end

file.puts("")

if (!is_abstract)
file.puts(" #{@the_type.Name} = element #{@the_type.Name} {")
file.print("#{ctype_name},")
write_inner_elements_definition_rlx(file)
file.puts("}")

end

@children.values.each {|c| c.write_schema_definition_rlx(file)}
end


The generated schema

An example of the generated schema is the following:


TextBlockAtts = FrameworkElementAtts | (attribute FontSize { text } ? , attribute FontFamily { text } ? , attribute FontWeight { text } ? , attribute FontStyle { text } ? , attribute TextAlignment { text } ? , attribute Text { text } ?
... )*
TextBlock = element TextBlock {
TextBlockAtts,(element TextBlock.FontSize {AnyGenElement} | element TextBlock.FontFamily {AnyGenElement} | element TextBlock.FontWeight {AnyGenElement} | element TextBlock.FontStyle {AnyGenElement} | element TextBlock.TextAlignment {AnyGenElement} | element TextBlock.Text {AnyGenElement}
...)*,
(InlineElementGroup *)
}
...
InlineElementGroup = Run | LineBreak


Code for this post and the generated schema can be found here.

Monday, August 11, 2008

Creating an XSD schema from classes using XAML rules with IronRuby

This post presents a little unfinished experiment for creating an XSD Xml Schema definition from classes based on some of the rules to map XAML documents to objects. The program is written in IronRuby and it uses reflection to inspect classes and generate the schema definition.

The goal is to have an XML Schema that could be used in conjunction with an XML editor to create XAML documents (which is useful for those of us who don't have a full Visual Studio version). Although as the XAML Overview document says, there are elements that could not be completely mapped to an schema definition, some of them are mentioned below.

The Silverlight Visual Studio integration already includes a very nice XAML editing capabilities.

I think this experiment is a great way to learn more about IronRuby and how to use it to call .NET Libraries.

The strategy

What the program will do is to navigate all the classes inheriting from System.Windows.DependencyObject and generate and XML element and a complex type definition with all the properties included in the definition. For this experiment only two mappings are implemented: properties and content properties.

Properties

As the XAML Overview document describes two ways for specifying properties:


  1. By using XML attributes

  2. By using a class.property-name element



This means that:

This


<Button Background="Blue" >
...
</Button>


and


<Button>
<Button.Background>
<SolidColorBrush Color="Blue">
</Button.Background>
...
</Button>


are equivalent.

So the alternative is to create both the attribute and the property element definitions in the schema.

Content properties

For elements that contain the ContentPropertyAttribute a special child element will be created with a reference to a sequence of all identified concrete elements that in inherit from the property type. As discussed below, this definition is not complete for content properties that accept basic types such as a string.


The program

Some constant and variable definitions


require 'mscorlib'
require 'System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL'
include System::Xml
include System::Reflection

SILVERLIGHT_FOLDER = "c:\\Program files\\Microsoft Silverlight\\2.0.30523.8\\"
BASE_TYPE_NAME = "System.Windows.DependencyObject"
CONTENT_PROPERTY_ATTRIBUTE = "System.Windows.Markup.ContentPropertyAttribute"

SILVERLIGHT_NAMESPACE = "http://schemas.microsoft.com/client/2007"
EXTRA_ATTRIBUTES_NAMESPACE = "http://schemas.microsoft.com/winfx/2006/xaml"
XSD_NAMESPACE = "http://www.w3.org/2001/XMLSchema"
CONCRETE_ELEMENTS_GROUP_NAME = "UIElementsGroup"

PRESENTATION_FRAMEWORK_COLLECTION_BASE_TYPE = "PresentationFrameworkCollection`1"



The main program

The main program looks like this:


begin

silveright_system_assembly = Assembly.reflection_only_load_from(SILVERLIGHT_FOLDER + "system.dll")
silveright_windows_assembly = Assembly.reflection_only_load_from(SILVERLIGHT_FOLDER + "System.Windows.dll")
silveright_core_assembly = Assembly.reflection_only_load_from(SILVERLIGHT_FOLDER + "System.Core.dll")
silveright_net_assembly = Assembly.reflection_only_load_from(SILVERLIGHT_FOLDER + "System.Net.dll")
silveright_xml_assembly = Assembly.reflection_only_load_from(SILVERLIGHT_FOLDER + "System.Xml.dll")


registry = Registry.new(silveright_windows_assembly)

registry.collect_data

registry.generate_xsd_schema
puts 'Done!'

rescue System::Reflection::ReflectionTypeLoadException => tl
puts tl
puts tl.LoaderExceptions
rescue System::IO::FileLoadException => e
puts e
puts "-----"
puts e.FusionLog
end


As presented here, the program first collects data about the classes stored in the System.Windows.dll Silverlight library and then generate the XSD schema definition.

The Registry class

The Registry class stores information on the identified classes and keeps track of element groups to be generated.

The collect_data method of the Registry class looks like this:


class Registry

def initialize(types_assembly)
@groups = {}
@types_assembly = types_assembly
@additional_types = {}
@classes = {}
end

...

def get_or_create(name,registry)
if @classes.has_key? name
return @classes[name]
else
return (@classes[name] = ClassNode.new(name,nil,registry))
end
end



...

def collect_data
base_type = @types_assembly.GetType(BASE_TYPE_NAME)
@classes[base_type.FullName] = RootClassNode.new(BASE_TYPE_NAME,base_type,self)

@types_assembly.get_types.each do |a_type|
if (base_type.is_assignable_from a_type and base_type.FullName != a_type.FullName )
node = get_or_create(a_type.full_name,self)
node.the_type = a_type
parent = get_or_create(a_type.BaseType.full_name,self)
parent.add_child(node)

puts "Adding #{a_type.FullName}"
end
end
end

end


As shown here the collect_data method iterates all the classes in the assembly, asking for elements that descend from System.Windows.DependencyObject. For each of these classes an instance of the ClassNode class is created.
If you are familiar with the .NET Reflection API you will recognize some of the names presented here such as is_assignable_from which is a call to the IsAssignableFrom method. As described here, IronRuby allows you to call existing .NET method names using Ruby naming convention .

Generating the Schema

The XSD schema is generated in the generate_xsd_schema Registry method which looks like this:


def generate_xsd_schema
base_type = @types_assembly.GetType(BASE_TYPE_NAME)
swriter = System::IO::StreamWriter.new("silveright.xsd")
writer_settings = XmlWriterSettings.new()
writer_settings.Indent = true
w = XmlWriter.Create(swriter,writer_settings)
w.write_start_document
w.write_start_element("schema",XSD_NAMESPACE)
w.write_attribute_string("targetNamespace",SILVERLIGHT_NAMESPACE)
w.write_attribute_string("elementFormDefault","qualified")
w.write_attribute_string("xmlns","sl",nil,SILVERLIGHT_NAMESPACE)
w.write_attribute_string("xmlns","x",nil,EXTRA_ATTRIBUTES_NAMESPACE)

w.write_start_element("import",XSD_NAMESPACE)
w.write_attribute_string("namespace",EXTRA_ATTRIBUTES_NAMESPACE)
w.write_attribute_string("schemaLocation","extraxamldefs.xsd")
w.write_end_element()

@classes[base_type.FullName].write_schema_definition(w)


create_additional_type_definitions(w)
create_concrete_elements_group(w)

w.write_end_element
w.write_end_document

w.Close
swriter.Close
end


As shown here a .NET XmlWriter class is used to generate the schema.

The write_schema_definition of the RootClassNode and ClassNode classes generates all the appropriate definitions for each class.


For the RootClassNode which represents classes that don't inherit from the DependencyObject the code looks like this:


class RootClassNode
attr_accessor :name,:the_type,:children

def initialize(name,the_type,registry)
@the_type = the_type
@name = name
@children = {}
@registry = registry
end

...

def write_schema_definition(writer)
ctype_name = @the_type.Name.to_s+"Type"
writer.write_start_element("complexType",XSD_NAMESPACE)
writer.write_attribute_string("name",ctype_name )

write_inner_elements_definition(writer) unless is_abstract

write_properties_definition(writer)
if (@the_type.FullName.to_s == BASE_TYPE_NAME)
writer.write_start_element("attributeGroup",XSD_NAMESPACE)
writer.write_attribute_string("ref","x:extraAttributes")
writer.write_end_element
end

writer.write_end_element

write_element_definition(writer,ctype_name) unless is_abstract

@children.values.each {|c| c.write_schema_definition(writer)}
end

end


A complex type is generated with the content of the current class. The write_inner_elements_definition method writes all the description of the child nodes for this complexType, for example it writes the property/element definitions and child node references. The write_properties_definition
method writes the attribute definitions for all the properties.

Also for all base types, a reference to an attribute group of "extraAttributes" is generated. This attribute group contains reference to definitions for some of the XAML attributes such as x:Name. More information about these attributes can be found in XAML Namespace (x:) Language Features.

Finally an element definition is created if the class is not abstract.

For classes inheriting from DependencyObject, a ClassNode instance is created.


class ClassNode < RootClassNode

def write_schema_definition(writer)

if @the_type.contains_generic_parameters
ctype_name = @the_type.Name.to_s.gsub(/`/,'')+"Type"
else
ctype_name = @the_type.Name.to_s + "Type"
end

writer.write_start_element("complexType",XSD_NAMESPACE)
writer.write_attribute_string("name",ctype_name )

writer.write_start_element("complexContent",XSD_NAMESPACE)
writer.write_start_element("extension",XSD_NAMESPACE)
writer.write_attribute_string("base","sl:#{@the_type.BaseType.Name}Type")
write_inner_elements_definition(writer) unless is_abstract
write_properties_definition(writer)
writer.write_end_element
writer.write_end_element
writer.write_end_element


write_element_definition(writer,ctype_name) unless is_abstract

@children.values.each {|c| c.write_schema_definition(writer)}
end
end


The main difference with BaseClassNode is that a complex type extension to the base type is generated. This will reduce the number of attribute definitions of each complex type.

Writing child node references

In order to allow sequences of heterogeneous elements as child nodes of XAML elements, a group definition is created with a choice that references every concrete type.

For example for elements that have a content property of type UIElement the following group is generated:


<group name="UIElementGroup">
<choice>
<element ref="sl:Path" />
<element ref="sl:Ellipse" />
<element ref="sl:Line" />
<element ref="sl:Polygon" />
<element ref="sl:Polyline" />
<element ref="sl:Rectangle" />
...
</choice>
</group>


Combining Enum values

The only way, that I could find, for combining .NET Enum values was to use a combination of Convert.ToInt32 and Enum.ToObject. The following function was used to do that.


def combine(enum_type,enum_values)
System::Enum.ToObject(
enum_type.to_clr_type,
((enum_values.map {|e_value|
System::Convert.ToInt32(e_value)}).inject {|i,j| (j | i)}))
end


A use of this function for combining BindingFlags looks like this:


p = @the_type.get_property(
c.to_string,
combine(BindingFlags,
[BindingFlags.Instance,
BindingFlags.Public,
BindingFlags.NonPublic]))

result_type = p.PropertyType


Elements not mapped

As mentioned at the beginning of the document, not all XAML document features can be accurately represented using XSD. Some of the things that I noticed:


  • Couldn't find a way to define attached properties. A possible workaround is to generate all possible attached property definitions

  • Content properties that allow strings are not represented. This is difficult since it has conflicts with the property/element definitions. Mixed content could be a possible workaround.

  • Extensibility: no easy way to represent things outside of System.Windows. This is a very difficult problem, maybe things like substitution groups could help to represent future child nodes.



Using the generated schema

With the schema generated, an XML editor with XSD Schema aware completion can be used. For example here it is used in Eclipse with XML editor tools included in WTP.




I tried to use the schema with the Netbeans 6.1 IDE (which was used as the Ruby editor for this code) however for schema completion it requires you to specify the schemaLocation attribute. Using this attribute or declaring the xsi namespace generates an error when the XAML is loaded at runtime!.


Code and generated schema for this post can be found here.