-
Notifications
You must be signed in to change notification settings - Fork 263
Using Dom Programming Discussion
The ATF Using Dom Sample is a simple and direct example of how to use the ATF DOM in an application. It contains an XML Schema, a schema metadata class file generated by DomGen, DOM adapters for types, and a schema loader. It also shows saving application data in an XML file.
This sample application has no UI. You can run it in a Windows® Command Prompt to see its output text. It saves the application data it creates in an XML file game.xml
.
UsingDom has no UI and does not use MEF, and it mainly demonstrates how to create and handle application data with the ATF DOM.
It has a very simple data model with one base type that game objects are based on, and the model is defined in the XML Schema file game.xsd
. Similarly to many of the other samples, UsingDom has a metadata class created by DomGen. It uses a simple schema loader based on XmlSchemaTypeLoader
that mainly loads the schema and defines DOM adapters on data types. These DOM adapters primarily define properties for type attributes.
The Main()
function loads the XML Schema, which also results in defining the DOM adapters. It creates the application data by building a tree of DomNode
s and setting their attributes. It shows two ways to do this, one by directly setting DomNode
attributes and the other by setting DOM adapted object's properties. This first method is sufficient, so this sample does not even need to define DOM adapters.
The sample also prints out the application data to a Windows® Command Prompt.
UsingDom persists its application data to an XML file with the DomXmlWriter
class, which uses the loaded XML Schema.
UsingDom specifies its data model in the XML Schema file game.xsd
. The "gameObjectType" is for game objects, and the other game objects are based on it: "dwarfType", "ogreType", and "treeType". The type "gameType" is the root type and contains "gameObjectType" type objects:
<xs:complexType name="gameType">
<xs:sequence>
<xs:element name="gameObject" type="gameObjectType" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute name="name" type="xs:ID"/>
</xs:complexType>
...
<xs:element name="game" type="gameType"/>
The entire data schema is shown in this view of the Visual Studio XML Schema Explorer, with all types opened to show their attributes and contained objects:
Note that "treeType" has no attributes of its own, only the attributes of its base type "gameObjectType":
<xs:complexType name="treeType">
<xs:complexContent>
<xs:extension base="gameObjectType">
</xs:extension>
</xs:complexContent>
</xs:complexType>
This shows that a derived type does not have to define additional attributes. This definition does accomplish making "treeType" a "gameObjectType", and that's its only purpose. This allows "treeType" objects to be contained in the "gameType" object.
The command file GenSchemaDef.bat
runs DomGen on the XML Schema file game.xsd
:
{source} ..\..\..\DevTools\DomGen\bin\DomGen.exe game.xsd GameSchema.cs Game.UsingDom UsingDom {source}
DomGen creates the metadata class file GameSchema.cs
defining the class GameSchema
, which shows how the metadata classes are constructed. For instance, here's what is generated from the schema definition for the root type "gameType", shown previously:
public static class gameType
{
public static DomNodeType Type;
public static AttributeInfo nameAttribute;
public static ChildInfo gameObjectChild;
}
The three class members are the following:
-
Type
:DomNodeType
for the type of theDomNode
associated with an object of "gameType". -
nameAttribute
:AttributeInfo
object for the attribute "name". -
gameObjectChild
:ChildInfo
object for the list of objects this type can contain, of type "gameObjectType".
GameSchema
class's Initialize()
method initializes the gameType
class this way:
gameType.Type = typeCollection.GetNodeType("gameType");
gameType.nameAttribute = gameType.Type.GetAttributeInfo("name");
gameType.gameObjectChild = gameType.Type.GetChildInfo("gameObject");
This accomplishes the following:
- Defines the node type
Type
using theXmlSchemaTypeCollection.GetNodeType()
method to the type of "gameType". - Sets
nameAttribute
withDomNodeType.GetAttributeInfo()
to the attribute type for the "name" attribute. - Initializes
gameObjectChild
using theDomNodeType.GetChildInfo()
method to the child info for "gameObject" elements, which are the objects that thegameType
object can contain.
The GameSchemaLoader
class derives from XmlSchemaTypeLoader
, the ATF base schema loader class. Its constructor loads the schema file:
public GameSchemaLoader()
{
// set resolver to locate embedded .xsd file
SchemaResolver = new ResourceStreamResolver(Assembly.GetExecutingAssembly(), "UsingDom/Schemas");
Load("game.xsd");
}
The XmlSchemaTypeLoader.Load()
method calls OnSchemaSetLoaded()
after the schema is loaded to perform any desired initialization:
protected override void OnSchemaSetLoaded(XmlSchemaSet schemaSet)
{
foreach (XmlSchemaTypeCollection typeCollection in GetTypeCollections())
{
m_namespace = typeCollection.TargetNamespace;
m_typeCollection = typeCollection;
GameSchema.Initialize(typeCollection);
// register extensions
GameSchema.gameType.Type.Define(new ExtensionInfo<Game>());
GameSchema.gameType.Type.Define(new ExtensionInfo<ReferenceValidator>());
GameSchema.gameType.Type.Define(new ExtensionInfo<UniqueIdValidator>());
GameSchema.gameObjectType.Type.Define(new ExtensionInfo<GameObject>());
GameSchema.dwarfType.Type.Define(new ExtensionInfo<Dwarf>());
GameSchema.ogreType.Type.Define(new ExtensionInfo<Ogre>());
break;
}
}
The foreach
executes only once, because there's only one collection of types here. After setting field values used by a couple of properties, NameSpace
and TypeCollection
, the loop defines DOM adapters.
The first three are for the root type "gameType", so they apply to the root DomNode
:
-
Game
is for the type "gameType" itself. -
UniqueIdValidator
makes sure that IDs for nodes are unique. Note that the schema for "gameType" specified an ID for its "name" attribute:<xs:attribute name="name" type="xs:ID"/>
. -
ReferenceValidator
verifies that references in the DOM data are valid. This is usually used for references to IDs, and is not needed in this sample, because there are no data references in the data model.
For details on what all the DOM adapters do, see DOM Adapters in UsingDom.
A DOM adapter class provides the capabilities desired for a DomNode
that can be adapted to the DOM adapter. In UsingDom, the DOM adapters provide properties for each attribute of the type the DOM adapter is defined for.
For example, here's the DOM adapter GameObject
for the "gameObjectType" type, which is about as simple as it gets:
public class GameObject : DomNodeAdapter
{
/// <summary>
/// Gets or sets game object name</summary>
public string Name
{
get { return GetAttribute<string>(GameSchema.gameObjectType.nameAttribute); }
set { SetAttribute(GameSchema.gameObjectType.nameAttribute, value); }
}
}
The "gameObjectType" type has just one attribute, "name". GameObject
's Name
property uses the DomNodeAdapter
methods GetAttribute()
and SetAttribute()
on the nameAttribute
for the property's getter and setter. Recall that nameAttribute
is defined as an AttributeInfo
in GameSchema
:
public static AttributeInfo nameAttribute;
And DomNodeAdapter.GetAttribute()
takes an AttributeInfo
parameter:
protected T GetAttribute<T>(AttributeInfo attributeInfo)
Name
's property definition is typical for attribute properties in the sample applications that use the ATF DOM.
The DOM adapter Game
defined for "gameType" also defines a property for its "name" attribute, which is the name of the game. It also defines the GameObjects
property for the game objects that an object of this type contains:
public class Game : DomNodeAdapter
{
/// <summary>
/// Performs initialization when the adapter is connected to the game's DomNode.
/// Raises the DomNodeAdapter NodeSet event and performs custom processing.</summary>
protected override void OnNodeSet()
{
base.OnNodeSet();
m_gameObjects = GetChildList<GameObject>(GameSchema.gameType.gameObjectChild);
}
/// <summary>
/// Gets or sets the game's name</summary>
public string Name
{
get { return GetAttribute<string>(GameSchema.gameType.nameAttribute); }
set { SetAttribute(GameSchema.gameType.nameAttribute, value); }
}
/// <summary>
/// Gets a list of the game objects</summary>
public IList<GameObject> GameObjects
{
get { return m_gameObjects; }
}
private IList<GameObject> m_gameObjects;
}
GameObjects
simply returns the value in the m_gameObjects
field for getting its value. This DOM adapter also has an OnNodeSet()
method, which sets the value of m_gameObjects
. The DomNodeAdapter.GetChildList()
method used here takes a ChildInfo
parameter:
protected IList<T> GetChildList<T>(ChildInfo childInfo)
Thus getting the value of GameObjects
returns an IList<GameObject>
: a list of the DOM adapter objects for the game objects contained in the DomNode
. DOM adapters adapt DomNode
s, so this is essentially a list of the child DomNode
s of the "gameType" node, the root DomNode
.
The DOM adapters Dwarf
and Ogre
both derive from the GameObject
DOM adapter, because their types are based on "gameObjectType":
public class Dwarf : GameObject
In general, if data type A
is based on type B
, the DOM adapter defined on A
should derive from the DOM adapter defined on the type B
.
Main()
puts all the pieces together.
Main()
creates a schema loader:
var gameSchemaLoader = new GameSchemaLoader();
As noted in UsingDom Schema Loader, GameSchemaLoader
's constructor loads the schema, and that also calls OnSchemaSetLoaded()
that defines the DOM adapters, so the application can use them.
UsingDom offers two ways to create application data, one of which is commented out:
DomNode game = null;
// create game either using DomNode or DomNodeAdapter.
game = CreateGameUsingDomNode();
//game = CreateGameUsingDomNodeAdapter();
The DomNode
game
is the root DomNode
for the tree of DomNode
s that contain the application data. UsingDom creates application data by creating a tree of DomNode
s and putting data into each DomNode
by setting its attributes. The code here allows for two approaches to setting up the DomNode
s:
-
CreateGameUsingDomNode()
: CreateDomNode
s and set their attributes directly. -
CreateGameUsingDomNodeAdapter()
: CreateDomNode
s and set their attributes by using DOM adapters on the nodes.
DomNode
. CreateGameUsingDomNode()
does it this way:
// create Dom node of the root type defined by the schema
DomNode game = new DomNode(GameSchema.gameType.Type, GameSchema.gameRootElement);
game.SetAttribute(GameSchema.gameType.nameAttribute, "Ogre Adventure II");
IList<DomNode> childList = game.GetChildList(GameSchema.gameType.gameObjectChild);
This method creates a new DomNode
of the type GameSchema.gameType.Type
with ChildInfo
from GameSchema.gameRootElement
, using this version of the DomNode
constructor:
public DomNode(DomNodeType nodeType, ChildInfo childInfo)
Next, it sets the "name" attribute with the DomNode.SetAttribute()
method. (Note that the DOM adapters described in DOM Adapters in UsingDom set attributes this way.) Finally, it gets a list of the root's child DomNode
s with the DomNode.GetChildList()
method (just as the DOM adapter did).
Contrast this to the approach of CreateGameUsingDomNodeAdapter()
, which simply sets properties of the DOM adapter:
DomNode root = new DomNode(GameSchema.gameType.Type, GameSchema.gameRootElement);
Game game = root.As<Game>();
game.Name = "Ogre Adventure II";
It creates the root DomNode
exactly the same way. However, it then initializes a Game
object by adapting the root DomNode
to the Game
DOM adapter with the Adapters.As()
method:
public static T As<T>(this IAdaptable adaptable)
This adaptation works, because DomNode
implements IAdaptable
:
public sealed class DomNode : IAdaptable, IDecoratable
The last line sets the Name
property to "Ogre Adventure II". By the definition of the Game
DOM adapter, setting this property sets the "name" attribute of its associated DomNode
to the given value. So the result is exactly the same for both methods.
It is not necessary to get the list of the root's child DomNode
s as in the previous approach, because this list is already available in the Game
DOM adapter's property Game.GameObjects
, as previously shown in DOM Adapters in UsingDom.
Although both approaches accomplish the same result, using DOM adapters makes the code simpler and easier to read.
The "ogreType" is also handled differently in the two methods. Here's CreateGameUsingDomNode()
:
// Add an ogre
DomNode ogre = new DomNode(GameSchema.ogreType.Type);
ogre.SetAttribute(GameSchema.ogreType.nameAttribute, "Bill");
ogre.SetAttribute(GameSchema.ogreType.sizeAttribute, 12);
ogre.SetAttribute(GameSchema.ogreType.strengthAttribute, 100);
childList.Add(ogre);
As before, this sets the ogre's attributes using the DomNode.SetAttribute()
method. At the end, it adds the new DomNode
as a child of the root DomNode
using the childList
obtained earlier from the root DomNode
.
Here's CreateGameUsingDomNodeAdapter()
's approach to doing the same thing:
// Add an ogre
DomNode ogreNode = new DomNode(GameSchema.ogreType.Type);
Ogre orge = ogreNode.As<Ogre>();
orge.Name = "Bill";
orge.Size = 12;
orge.Strength = 100;
game.GameObjects.Add(orge);
Again, it creates the ogre DomNode
exactly the same as in CreateGameUsingDomNode()
. However, it then adapts this node to an Ogre
DOM adapter object. It sets attribute values using Ogre
's Name
, Size
, and Strength
properties. The Name
property actually belongs to the GameObject
DOM adapter that Ogre
derives from:
public class Ogre : GameObject
The last difference is that the new DomNode
is added as a child to the GameObjects
property of the Game
object game
, because this property holds the list of child DomNode
s.
At its end, CreateGameUsingDomNode()
returns the DomNode
it created in the beginning:
return game;
On the other hand, CreateGameUsingDomNodeAdapter()
returns the DomNodeAdapter.DomNode
property of the Game
DOM adapter, which contains the adapted DomNode
:
return game.DomNode;
The CreateGameUsingDomNode()
method creates application data without DOM adapters, so the DOM adapters are not really required, except to illustrate this alternate approach to setting up data. However, using DOM adapters makes the code simpler and easier to read.
After application data is created, it is printed:
Print(game);
The Print()
method prints a subtree of DomNode
s:
private static void Print(DomNode game)
{
Console.WriteLine("Game: {0}", game.GetAttribute(game.Type.GetAttributeInfo("name")));
foreach (DomNode child in game.Children)
{
Console.WriteLine();
Console.WriteLine(" {0}", child.Type.Name);
foreach (AttributeInfo attr in child.Type.Attributes)
Console.WriteLine(" {0}: {1}",
attr.Name,
child.GetAttribute(attr));
}
Console.WriteLine();
}
The first WriteLine()
call prints data from the root DomNode
's attributes obtained with DomNodeType.GetAttribute()
. The subsequent outer loop prints information on each child of the root, obtained from the child list in the DomNode.Children
property. The inner loop iterates through all the attributes of each child, obtained from the list of attributes in the DomNodeType.Attributes
property.
This is the output you get when running UsingDom in a Windows® Command Prompt:
{source} Game.UsingDom:ogreType
name: Bill size: 12 strength: 100
Game.UsingDom:dwarfType name: Sally age: 32 experience: 55
Game.UsingDom:treeType name: Mr. Oak
{source}
It's easy to persist application data when you use the ATF DOM and an XML Schema for type definitions. The last section of Main()
sets up the file path for the saved data file and then saves the data:
// create directory for data files
Directory.CreateDirectory(Path.Combine(ExecutablePath, @"data"));
string filePath = Path.Combine(ExecutablePath, "data\\game.xml");
var gameUri = new Uri(filePath);
// save game.
FileMode fileMode = FileMode.Create;
using (FileStream stream = new FileStream(filePath, fileMode))
{
DomXmlWriter writer = new DomXmlWriter(gameSchemaLoader.TypeCollection);
writer.Write(game, stream, gameUri);
}
The most interesting part is the ATF specific line that creates a DomXmlWriter
using this constructor:
public DomXmlWriter(XmlSchemaTypeCollection typeCollection)
The constructor needs an XmlSchemaTypeCollection
, which contains the loaded schema — all the data model's definitions, plus names that can be used in the XML. The DomXmlWriter.Write()
method writes the node tree, given its root, to the given stream and URI:
public virtual void Write(DomNode root, Stream stream, Uri uri)
The resulting file game.xml
looks like this:
<?xml version="1.0" encoding="utf-8"?>
<game xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" name="Ogre Adventure II" xmlns="Game.UsingDom">
<gameObject xsi:type="ogreType" name="Bill" size="12" strength="100" />
<gameObject xsi:type="dwarfType" name="Sally" age="32" experience="55" />
<gameObject xsi:type="treeType" name="Mr. Oak" />
</game>
First, notice that the XML tag names for the objects, <game>
and <gameObject>
, are the element names of the root type and the items contained in a "gameType" object.
Next, the XML attribute names are the attribute names defined in the types. For example, the <game>
tag, of type "gameType", has the attribute "name", which is defined in the XML Schema. The first <gameObject>
tag has an "xsi:type" of "ogreType", which is the type name of an ogre. Its other attributes, "name", "size", and "strength", come from the type definitions for "gameObjectType" and "ogreType".
In other words, the XML in the Schema nicely parallels the XML in the saved file.
- Circuit Editor Programming Discussion: Learn how ATF handles graphs, and provides editors for kinds of graphs, such as circuits.
- Code Editor Programming Discussion: Shows how to interface third party software to an ATF application: the ActiproSoftware SyntaxEditor.
- Diagram Editor Programming Discussion: Very simply combines components from the CircuitEditor, FsmEditor, and StateChartEditor samples into one application, with the abilities of all three, showing the power of components.
-
DOM Property Editor Programming Discussion: Shows how to use the ATF DOM with an XML Schema to define application data with a large variety of attribute types, whose values can be viewed and edited using the ATF
PropertyEditor
component, using various value editors to view and edit attributes. - DOM Tree Editor Programming Discussion: Shows how to edit DOM data using a tree control and display properties in a variety of value editors.
- File Explorer Programming Discussion: Discusses the ATF File Explorer Sample using list and tree controls with adapters.
- FSM Editor Programming Discussion: Tells you about how the ATF FSM Editor Sample edits simple graphs for state machines, using DOM adapters for contexts and validation.
-
Model Viewer Programming Discussion: Shows how the ATF Model Viewer Sample is written, discussing how ATGI and Collada model data is handled, using rendering components, and using a
DesignControl
as a canvas for rendering. -
Simple DOM Editor Programming Discussion: Programming the ATF Simple DOM Editor Sample, creating a palette, using DOM adapters and contexts, editing application data, and searching
DomNode
s. - Simple DOM Editor WPF Programming Discussion: Programming the ATF Simple DOM Editor WPF Sample, which is similar to ATF Simple DOM Editor Sample, but implemented using ATF's WPF framework.
- Simple DOM No XML Editor Programming Discussion: Programming the ATF Simple DOM No XML Editor Sample, which is very similar to ATF Simple DOM Editor Sample, except that it doesn't use XML for either its data model or persisting application data.
- State Chart Editor Programming Discussion: Shows using ATF graph and other classes to create a statechart editor, using DOM adapters, documents, contexts, and validators.
- Target Manager Programming Discussion: Description of how a target manager is implemented using ATF components to manage target devices, such as PlayStation®Vita or PS3™ consoles. A target manager is used in other tools, such as the StateMachine tool.
- Timeline Editor Programming Discussion: Discusses how to create a fairly full-featured timeline editor using the ATF timeline facilities, such as the timeline renderer and the timeline control and its manipulators.
-
Tree List Control Programming Discussion: Demonstrates using the
TreeListControl
andTreeListItemRenderer
classes to display and edit hierarchical data in a tree view with details in columns. -
Tree List Editor Programming Discussion: Demonstrates how to use the ATF tree controls
TreeListView
and its enhancement,TreeListViewEditor
.TreeListView
usesTreeListViewAdapter
, which adaptsTreeListView
to display data in a tree. - Using Dom Programming Discussion: Shows how to use the various parts of the ATF DOM: an XML Schema, a schema metadata class file generated by DomGen, DOM adapters for the data types, a schema loader, and saving application data to an XML file.
- Home
- Getting Started
- Features & Benefits
- Requirements & Dependencies
- Gallery
- Technology & Samples
- Adoption
- News
- Release Notes
- ATF Community
- Searching Documentation
- Using Documentation
- Videos
- Tutorials
- How To
- Programmer's Guide
- Reference
- Code Samples
- Documentation Files
© 2014-2015, Sony Computer Entertainment America LLC