This tutorial shows how to use content classes in a simple program. The program can be compiled using the Cω compiler.
To run this tutorial, you may use the following project and source files:
These files are located in the \samples\Basics\Email subdirectory under the path where you installed Cω, which by default is C:\Program Files\Comega.
Content classes are classes that can be used to specify the schema or structure
of a document type. To an XML programmer, they might be thought of as a DTD
<!ELEMENT> rule or an XSD sequence. To a SQL programmer, the
closest approximation here might be the subschema for a particular table in the
database.
In this tutorial, we will create a short content class Email
that describes the structure of simple e-mail messages, such as the following:
public class Email {
struct{
struct{
string From;
string To;
string? Subject;
} Header;
struct{
string P;
}+ Body;
}
}
For the Email class, we can see that it contains a
Header and a Body. A Header
is field whose type is an anonymous struct containing
a From field of type string, a
To field of type string and an optional Subject
of type string?. A Body is a
field whose type is a stream of structs, each consisting of a single field
P of type string.
Alternately, this same structure of the Email class
could be expressed in XSD schema using the following content rules:
<element name="Email">
<complexType>
<sequence>
<element name="Header">
<complexType>
<sequence>
<element name="From" type="string"/>
<element name="To" type="string"/>
<element name="Subject" type="string" minOccurs="0" maxOccurs="1"/>
</sequence>
</complexType>
</element>
<element name="Body">
<complexType>
<sequence minOccurs="1">
<element name="P" type="string"/>
</sequence>
</complexType>
</element>
</sequence>
</complexType>
</element>
In the XSD representation, each of the <sequence> groupings
with local <element> declarations corresponds to
anonymous structs with field declarations in Cω. Also, the XSD uses explicit
occurrence constraints (minOccurs, maxOccurs)
instead of the ? and + operators.
For those who know DTDs the Email class in Cω will
possibly look more familiar. The main differences for the DTD version are the
limits inherent in how DTDs work. DTDs require all elements to be defined at
the top level; they use commas as separators instead of semicolons as
terminators, and for practical purposes they support only a single primitive
type of #PCData which corresponds to string.
<!ELEMENT Email (Header, Body)> <!ELEMENT Header (From, To, Subject?)> <!ELEMENT Body (P+)> <!ELEMENT From (#PCDATA)> <!ELEMENT To (#PCDATA)> <!ELEMENT Subject (#PCDATA)> <!ELEMENT P (#PCDATA)>
This simple example already highlights several features of the Cω type
system such as anonymous struct types and various forms of stream types. Anonymous structs,
such as struct{ string From; string To; string? Subject;}
are essentially tuples. The stream types used in this
example are an optional string written string?,
where null represents non-existence, and struct{
string P; }+, which stands for one or more struct{
string P; } values.
The content class Email is similar to a normal class, which means
that we can add, properties, methods and that we can use instances of
Email just like any other CLR object. So let us add two more methods
to our class; their definitions will be provided in the examples below:
static Email Vacation (DateTime d, TimeSpan s, string To){...}
virtual bool MustRead () {...}
Instances of content classes can be created using normal constructors or with XML object creation expressions, the content can be accessed using member access or queries.
The following is a complete Cω program that declares the content class
Email and then uses it to print output based upon the rules the
content class provides for handling various types of email messages.
using System;
public class Email {
//content of Email message
struct{
struct{
string From;
string To;
string? Subject;
} Header;
struct{
string P;
}+ Body;
}
// methods of Email message
public static Email Vacation(DateTime d, TimeSpan s, string to){
return <Email>
<Header>
<From>John Doe</From>
<To>{to}</To>
<Subject>OOF</Subject>
</Header>
<Body><P>I am OOF from {d} until {d+s}.</P></Body>
</Email>;
}
public virtual bool MustRead(){
bool IsInteresting(string s){
return
( s.IndexOf("URGENT") >= 0
|| s.IndexOf("YOUR ASSISTANCE") >= 0
|| s.IndexOf("MILLION") >= 0
|| s.IndexOf("100% RISK FREE") >= 0
);
};
return this...string::*[IsInteresting(it)] != null;
}
}
public class Demo {
public static void Main() {
Email hello =
<Email>
<Header>
<From>Bill</From>
<To>Steve</To>
</Header>
<Body>
<P>Hi Steve,</P>
<P>It's time for coffee.</P>
</Body>
</Email>;
// print all members of header of the hello message
foreach(it in hello.Header.*){ Console.WriteLine(it); };
// print all paragraphs in the body of the hello message
foreach(it in hello.Body.P){ Console.WriteLine(it); };
// print the body of a vacation message using transitive query
vacation = Email.Vacation(DateTime.Now, new TimeSpan(5,0,0),"Larry");
foreach(it in vacation...P){ Console.WriteLine(it); };
// check if the spam message must be read
Console.WriteLine("Must read = {0}", Spam.Sample.MustRead());
Console.ReadLine();
}
}
public class Spam {
public static Email Sample =
<Email>
<Header>
<From>spammer@spamcentral.com</From>
<To>undisclosed recipients</To>
<Subject>Great Deals on Cars</Subject>
</Header>
<Body>
<P>
FABICAM CAR DEALERSHIP HAS THE RIGHT CAR
DEAL FOR YOU. PLEASE STOP BY AT ANY OF OUR 24 HOUR STORES
TO SEE FOR YOURSELF.
</P>
<P>
WE WILL GIVE YOU A PRICE BREAK. THE PRICE FOR YOUR FAVORITE
CAR WILL BE LESS THAN THE DEALER INVOICE.
</P>
<P>THIS TRANSACTION IS 100% RISK FREE.</P>
</Body>
</Email>;
}
Bill Steve Hi Steve, It is time for Cofee. I am OOF from 05/01/2003 13:10:12 until 05/01/2003 18:10:12. Must read = True