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.