
Introduction
Welcome to XMLFoundation June 15 2013 update:
The most major update has been in the area of Android. A new Android sample program was added that displays a simple GUI from Java that uses ServerCore.cpp to build an HTTP server application. You will find this documentation in the source download. https://skydrive.live.com/redir.aspx?resid=D7EC275E76D295CF!560
iPad/Mac/iPhone development is underway.
I have always respected the content editors at codeproject.com for allowing me the freedom to post about my tribulations with the American justice system. Concerned strangers came to my aid, their phone calls to the authorities has helped. It caused my release from prison, and although I still have a warrant for my arrest - I am free and on the run.
If you will be working with the Android source, you will likely want to see XMLFoundation in action. Yesterday I wrote to MajorGeeks.com , Slashdot.org , TechCrunch.com, Tucows.com about my work on Android during this port of the XMLFoundation: https://skydrive.live.com/redir.aspx?resid=D7EC275E76D295CF!941
This is what's new right now. There is more to come. Everyone who read the "disturbing" folder is aged to call the FBI and ask about how they are handling this case and the warrant for my arrest. Thanks for your concern.
=========
The December 21, 2012 build extended and widened the library interfaces with emphasis on the future in
software design, and even style. XMLFoundation maps the data
in raw XML to lists,
arrays, strings, ints, and even int64's in the application layer. All
of these data types have been
supported for over a decade already. New interfaces in the 2012 version
support mapping char(1 byte), short(2 bytes), and char buf[n bytes] to fully
complete the mapping to every native C++ data type. The foundational GString is now indexed by 64 bit addressing which pushes all xml document size
limitations into almost infinity. This long addressing scheme has been
added in such a way that 32 bit applications will still use 64 bit addressing
granting them the bounds of infinity as well. Benchmark tests confirm that XMLFoundation is the fastest approach for moving XML into the application layer.
The overhead of pushing an extra 4 bytes on the call stack during tokenization is
measurable but insignificant in light of all the stack operations eliminated by using a custom non
SAX interface to the XML parser (Read details in the 'Faster than Fast' section). On 64 bit systems there is no performance
penalty to pay at
all since the registers are 64 bits wide already.
The GString is such a sexy article of engineering that it gets
used to hold all types of streamed data in an application, not just XML. By design, the GString replaced
ostream which the tokenizer (aka -the lexical analyzer or the XML Parser) was initially built with.
By overloading the << operator it was a very simple task to port this work
to a better
stream class. In the times of 2012 we now deal with file
sizes and offsets that require 64bit indexing on the average or above average home computer.
Granted it will be many years before the average home computer allocates
contiguous regions of memory that large - but high end servers do it already and
they have registers that are 64 bits wide. To keep GString positioned to
serve mankind in ALL situations it now uses a 64 bit index. Target the
future.
A 32 bit test parsed xml containing element and attribute tags
mapped to various lists, string and integers. This was executed while counting
cpu cycles using the assembly code in GPerformanceProfile.cpp for these results
running in a native 32 bit operating system that is not under WOW or
virtualization:
Tokenizing with a 64
bit index in the new XMLFoundation 32 bit build on a 32 bit OS:
(176,121) CPU cycles
Tokenizing with a 32 bit index in the
old XMLFoundation 32
bit build on a 32 bit OS: (170,144)
CPU cycles
By merely widening the integer index at the lowest level of the
XML parser it caused the machine code produced by the C++ compiler to PUSH and
POP more data onto the stack, hence it now takes more CPU cycles to process the
same amount of XML. If you understand what caused the difference, then you
can understand why the XMLFoundation is preferable to SAX if you want the
fastest solution. Truly this is the fastest solution on earth for processing XML
in 32 bit, even though it is now optimized for 64 bit.
The fastest solution will be the one selected to process the largest XML data
sets in the world because the decision will be made by an engineer not a
politician. Those data sets will need this very large indexing scheme.
Smaller data sets no longer need to worry that some freak occurrence (an
exception) might (however unlikely) surpass 32 bit indexing thresholds.
English words fail to express what raw numbers so emphatically and eloquently
assert is the fastest way to process XML. Aside from all this raw
horsepower produced through efficient algorithmic design, the application source
code that uses XML is organized and simple.
The 2012 version also supports a new interface to access unparsed CDATA
from the object directly in the memory buffer passed into the lexical analyzer
that algorithmically instantiates objects for you. Several new examples
have been added in December advancing the example documentation to include
Inheritance of Mapping explaining how to
use base object mappings. Another Sample explains custom
Parsed and Unparsed Object Data handlers.
If a picture is worth a thousand words, an example is worth ten thousand.
"Open Source" projects are frequently unsupported and undocumented, however you
will find that the ongoing commentating of source code is continuously being
maintained and developed to make the toolkit more useful and productive in the
hands of people who are new to using it. The detailed documentation is all
in the source code, right where you need it. For example a comment was
just recently added into ListAbstraction.h just above the
StringCollectionAbstraction class that explains how that base class is used to
store ANY data type into ANY data structure. That comment links to a new
class called CDoubleArrayAbstraction in AbstractionsMFC.h that stores the data
type "double" into MFC's array implementation called CArray. While the
implementation was the point of interest of one person, the comments added will
be the point of interest to even more people that need some other data type in
some other kind of data structure.
As many of you know this project had no updates for 2 years
while I was in prison serving time for "Escape".
The details of my case should disturb you. I would have
been killed if some authorities had their way but I lived to code another day so
you can be certain that I am going to make the most of every keystroke now.
I added a folder called "Disturbing" to the root folder of the source code
distribution that contains some serious arrays of 1's and 0's in various
formats.
Welcome to XMLFoundation 2012.
As the name suggests it provides a foundation for XML
support in an application, however this is much more than just another XML
parser. It applies a unique approach to handling XML that allows your
application code to focus on the application rather than traversing DOM or
subscribing to SAX events. The most unique feature of the
XMLFoundation is the object oriented encapsulation that provides XML support in
the application layer. XMLFoundation allows you to easily integrate XML
with your GUI, or with your server objects, and it natively supports COM, DCOM,
and CORBA objects.
XMLFoundation contains a small, fast, and portable XML
tokenizer that has been refined and optimized in many large software projects. My involvement with XML
pre-dates the finalization of the XML 1.0 recommendation by W3C. For years,
the only XML Parser that could match XMLFoundation tokenization performance was
"Xpat" by James Clark - but as you will see the unique ability to
bypass DOM and SAX altogether makes XMLFoundation the fastest solution available for
moving XML to and from application layer objects - and it requires far less
lines of code to do it.
The performance of the stack based XML parser is at the top
of its class for non-validating parsers. Parsing and tokenization is only
half the task, the other half is getting the results into the member variables,
lists, and objects that they need to be in to be useful in the application layer
- it is in that task that XMLFoundation is in a class of its own. The
performance is unparalleled because the memory buffer that contains the source
XML parses directly into your custom class objects without ever being copied or
temporarily stored in a DOM tree. It parses directly into your lists,
objects, arrays, indexed data structures, and all native C++ data types.
It even has support for common containers of element data such as MFC CStrings.
It's been used in Java too.
That said, speed of execution is less impressive than the speed of development
and overall reduction in lines of code required to effectively use XML in your
application.
XML is in the Foundation, but the foundation does much more
than just XML. It is also a web services framework implemented in
ServerCore.cpp. It can be extended several ways for HTTP as well as for
other protocols. This allows you to to build your application on a
multi-threaded server blueprint that has been used on many platforms and it has
been used to build servers that are not even XML based, but needless to say it
works great for building an XML based server. The services framework
supports a unique design approach for both static and dynamic server extensions
and examples of both - but XMLFoundation does even more than XML and Web
Services.
If you are building an application that does not use XML
and never will..... XMLFoundation is still a very valuable tool available to
solve many very common development tasks. The data structure classes alone
( List, Hash, Stack, Tree, Array, QSort ) are very useful. They
all have "Iterator"
objects so that data structures can be
read-referenced by multiple threads at the same time without blocking. The interface is
standard to all data structures. If you
find MFC or Rogue Wave Standard C++ library data structures useful, you will likely find XMLFoundation data
structures even more so.
XMLFoundation also has standard algorithm implementations
( Encryption, Compression, Data hash, Encoding ). These are based on the
works of other authors. They have been included into the XMLFoundation in
a simplified build format. They all compile under C++, so if you are using
them on AS/400, AIX, Solaris, Linux, or other like platforms - you do not even
need to reference a C compiler from the makefile, only your C++
compiler. They are also organized into single .CPP files for each
implementation - often a consolidation of many individual C source files in the
original authors publications.
XMLFoundation also has a plethora of application utilities
including ( Sorts, Performance Timers, Disk Directory, Exceptions,
INI Profiles,
Caching, String, Stream ). XMLFoundation has many utilities that MFC does not.
They are complete, comment documented with examples, and thoroughly tested on
many software projects.
XMLFoundation is
very portable. It builds on all versions of Windows (Win95 through
Windows8 and Windows Mobile). Portions were initially developed on a RISC machine, and
it was used in Solaris and Linux as early as 2001. Some of the compilers that have been used to build XMLFoundation
include: CC5.0, Xlc, IntelC++, KAIc++,
ForteC++, Visual C++, Borland C++, and eMc++. However I believe it works with any C++
compiler found
here, because it does not use namespaces, iostreams, or STL - all areas that
are prone to porting problems from my experience. It does have template
classes but their inclusion is optional as part of the implementation rather
than part of the foundation. XMLFoundation and all the sample
applications have recently been built and verified on Ubuntu and Fedora. The
source is distributed with VC6 makefiles so that the source can be imported into
projects using every version of the Microsoft compilers from 1998 through Visual
Studio 2010. Now the source includes a Visual Studio 2012 project file with 32 and 64 bit
targets defined.
The build dependencies are meticulously correct. Smart linkers leave out
everything you don't use, so don't expect to see code bloat as a
punishment for using XMLFoundation. Other development libraries were
not designed as well from a build perspective. Your application will not
load any DLL's as a result of using the XMLFoundation. Xfer
is another project I manage that is built on the XMLFoundation for the
platform independence - the code is tight and the product(s) built on
XMLFoundation reflect that.
I suppose an entire article could be written about each of
the foundational classes, and I'm certain that they will be written. They
are all well commented and coded with a highly experienced approach. The
String class uses stack space when possible to avoid heap allocations.
It's the best string implementation I've ever seen. The INI Profile class
uses triggers that allows your application to pick up real-time configuration
changes much like RegNotifyChangeKeyValue() in the Windows SDK. Exceptions
can be configured to unwind the call stack to a memory buffer like Java's printStackTrace(). The Tree has an iterator. The Directory can
delete recursively - on all platforms. The Stack is entirely inline, with
standard and macro methods - It could not be any faster if it was coded directly
in assembly. The StringList puts MFC's CStringList to shame, just look at
the interfaces. The GHash puts Microsoft's CMapStringToPtr to shame.
It is unspeakably faster. Look at the "MFCTypesFromXML" example and see for
yourself.
You cannot build a house on foundation of wet cement that
has not cured yet. With cement, minimizing stress prior to curing
minimizes cracking in your foundation. The same is true of software.
The XMLFoundation is solid and completely cured. It would be too bold to say that the
XMLFoundation has no bugs in, but it has none that I am aware of and the code
has been heavily used. It is a complete foundation. Building an application on
any foundation like Java 1.0 or .NET 1.0 or anything 1.0 means that if you don't
get slowed down by the bugs, you will be slowed down when you find all the
missing functionality. This code was first released to the public July 4,
2002 - the XMLFoundation was already very mature for it's age - it came from a
good family - it's mother had already been used on the largest software project
in the world. Since then I have built several complex applications on
it and many others have as well. It was completely stress tested with SMP hardware during a recent
Fortune 50 proof of concept implementation. XMLFoundation interfaces are
well established constants, no longer a curing foundation that is still forming.
The mother of the XMLFoundation was "The XML Object
Framework", born in 1998 & 1999(it was a long labor) for a client of mine. The XMLFoundation was
born the following year. The XMLFoundation sported a completely new
implementation of the xml parser based on the custom GString stream class that
was also born in 2000. XML Journal Magazine reviewed a product built on
the early XMLFoundation object factorization and called it "5 Star / World
Class" in XMLJournal Magazine Volume 2 Issue 7 (note: they did not review XMLFoundation they reviewed TransactXML).
XMLFoundation was heavily developed the following two years before it became
public in 2002. This project is mature and stable.
XMLFoundation absolutely IS the future in certain technology subsets. It is a gift to the world of engineering, and
it comes with all the source code. Universities that want to teach
algorithms, applications, or OO Design will find the XMLFoundation to be a
great source code to base a curriculum on. Independent authors who want to
write about cutting edge technology will find XMLFoundation a worthy
subject. The future was written in the past.
XML is data. “Objects from data” is not a new concept.
Programmers have been doing that for years, even before they were called
objects. We still need to get data into objects today. The data
can be XML or a result set, and the object might be a CDialog, a CORBA Object, a
COM object, or your own invention. You still need to get the same thing done.
Programmers have been doing this as long as there have been programmers.
If you apply enough force you can make the cube fit into the round
hole. If you apply enough force you can do anything - even police
California. The brute force approach is
to parse the XML into a DOM tree, and traverse the tree to gather the data
required by application/object variables. This approach causes volumes of
“simple” source code to move data from XML into Structured Objects, a
poor approach with respect to implementation time and long term maintenance.
Alternatively, the OO approach generalizes this process into reusable
functionality that enables objects to serialize to and from XML directly.
OO is pronounced ohhh-ohhh - and it is short for Object Oriented (incase you
didn't know) - it's poetic tech lingo - a code of it's own.
Software developers of every language have a similar need. They must
either:
- Write their own Object-XML tools,
- Find some production quality framework ready to use or,
- Use brute force and budget for maintenance programmers.
We can mostly rule out option A because it takes a lot of
time and the purpose of the project is to build product not tools. Option
C is also unwise if you have any long term plans for your product or want to be
able to quickly add new features. Option B leaves several paths and it
wouldn't be right for me to toot my own horn and tell you that XMLFoundation is
the best option available in the entire software industry to accomplish this
fundamental task - so I encourage you to research this yourself and I expect
that you will agree XMLFoundation is not just the best free solution, it's the
best solution.
It's difficult to directly compare XMLFoundation to other
solutions because the utilities in XMLFoundation, and many of the features in
XMLFoundation are not found in other solutions. That said, here is a
starting point for your own research:
Microsoft developed the "Xml.Serialization.XmlSerializer",
for C# but it only supports shallow serialization(no nested or complex objects)
and it lacks many other features found in the XMLFoundation. I would wager that
even the limited support it does provide is slower than XMLFoundation but I have
not put the two technologies to a speed comparison.
Liquid Technologies has a nice XML Serialization library( 'XML Data Binding' in
Liquid XML Studio $246) that is mostly fast, but I am aware that when
serializing small strings it's about 5 times slower than MSXML. Most likely this
is due to heap allocations. XMLFoundation avoids most heap allocations for small
strings. Once again I've never compared the two in a performance race.
A product called CMarkup is a C++ library that sells for $249 and has more
features than what Microsoft put together (enough that it can actually be used
for real world development). It does not support parsing directly into the kinds
of data structures that XMLFoundation does (tree, hash, etc) so a performance
comparison would need to include that. I have never put it to a side by side
performance test with XMLFoundation but if anyone reading this did it, I would
like to hear the results.
IBM Developer Works has posted 2 or more XML Serialization libraries but they are based on an external XML Parser - so by nature of their design
they must be slower.
XMLFoundation is free. No strings attached. There is not even a 'professional' version that costs money - this is the professional version.
The list goes on, and on, and on, and on of likeminded solutions.
From a procedural perspective we put data in square sets just to make programming simple. Consider this example data that is a "Customer"
with a list of "Orders" where each order has a list of "LineItems". That is not a square dataset - but for
the sake of the application layer we have forced it to be square for the last 4 decades.
| CUSTOMER |
CUST_ID |
ORDER_ID |
ORDER_DATE |
LINEITEM_ID |
LINEITEM_DESC |
PRICE |
| Brian |
777 |
1 |
July 4, 1777 |
7 |
Firecrackers |
$111 |
| Brian |
777 |
1 |
July 4, 1777 |
14 |
Ariel Shells |
$222 |
| Brian |
777 |
1 |
July 4, 1777 |
21 |
Party Favors |
$444 |
| Brian |
777 |
2 |
July 4, 2009 |
28 |
Attorney Fees |
$222 |
| Brian |
777 |
2 |
July 4, 2009 |
35 |
State Fines |
$555 |
The repetition in red filled the hole in to make non-square data be
square. The data in red is normally a pointer reference to the last sort
break at the DBMS kernel level, but various toolsets often expand it long form
so that 1 instance of a "Row" object does not rely on the data in
another instance. It's a terrible situation that has plagued applications
for as long as I can remember. The problem is that in reality there is no
such thing as a "Row" object - it was more of a temporary/tool-object
to get the data into real objects like Customers, Orders and LineItems. Countless data access
products in the form of VBX, OCX, ActiveX and various frameworks and libraries
serve up square data sets to applications that MANUALLY code the transfer of
data into their application objects with volumes of code that looked something
like this:
Customer::Load()
{
Tool.GetData()
while( Tool.MoreRows() )
{
Row = Tool.GetNextRow();
if (Row.GetColumn("Order_ID") != LastOrderID)
{
LastOrderID = Row.GetColumn("Order_ID")
Order = new OrderObject;
Order.date = Tool.GetColumn("Order_Date")
Order.id = Tool.GetColumn("Order_ID")
AddOrderToCustomer(Order)
}
LineItem = new LineItemObject;
LineItem.id = Tool.GetColumn("LineItem_ID")
LineItem.desc = Tool.GetColumn("LineItem_Desc")
LineItem.price = Tool.GetColumn("Price")
Order.AddLineItem( LineItem )
}
}
Notice all the use of Tool that
represents some sort of data set tool, class or library. Building software
to accomplish this task of copying data into objects without such a tool would
dramatically increase the lines of code required to move the square dataset into
your application objects. Knowing what tools to use can be the difference
between the success or failure of an entire project. One bad tool, or one
missing tool can make all the difference in the world to a software developer.
This is the same example data from the square result set represented in XML:
<Customer id=777>
<Name>Brian</Name>
<Order id=1>
<Date>July 4, 1777</Date>
<LineItem id=7>
<Desc>Firecrackers</Desc>
<Price>111</Price>
</LineItem>
<LineItem id=14>
<Desc>Ariel Shells</Desc>
<Price>222</Price>
</LineItem>
<LineItem id=21>
<Desc>Party Favors</Desc>
<Price>444</Price>
</LineItem>
</Order>
<Order id=2>
<Date>July 4, 2009</Date>
<LineItem id=72>
<Desc>Attorney Fees</Desc>
<Price>222</Price>
</LineItem>
<LineItem id=42>
<Desc>State Fines</Desc>
<Price>555</Price>
</LineItem>
</Order>
</Customers>
There is nothing square about XML. XML is an N-airy
tree. That's why we naturally use DOM (Document Object Model) to traverse
the data. For 1000's of years we thought that the world was flat.
Engineers made it be square because that was easier for them to cope with and
now we live in the days where it begins to take it's true shape.
Unfortunately as of 2010, the opportunity of the paradigm data shape shift has
not been harnessed by most programmers that grew up in the square world and are
only familiar with square tools. They take the most obvious development
path. If you presented the problem of sorting to someone who has no tools,
they will likely build a "bubble sort" - because that is the most
obvious and immediate solution.
Typically the XML is parsed into a tree structure. This means that the
linear and contiguous memory buffer of source XML is copied into many
fragmented pieces of memory across the heap - each element and in many cases
each token gets it's own heap space. This makes the XML elements
and attributes programmatically accessible with loops and recursion, just like Tool
did for square datasets. The XML parser puts the Elements and
Attributes into this temporary fragmented memory tree structure so that the
application programmer can get at the information to copy it once more
into a final structure that can be displayed on the GUI or used by the
application. It is likely going to take as much or more code to get from
the temporary DOM tree into the objects as it did to get from the square result set
into the objects. In many cases it will require recursion that is
difficult to debug - much more difficult than the old fashioned iterative code
required to copy from square result sets. Below is a code sample of some common tasks:
-------------------------------------------------------
MSXML::IXMLDOMNamedNodeMapPtr pAttrList = m_pCurNode->Getattributes();
_bstr_t bstrAttrName = (_bstr_t)(LPCTSTR)m_strName;
MSXML::IXMLDOMAttributePtr pNewAttr = m_pDOMDoc->createAttribute(bstrAttrName);
_bstr_t bstrAttrValue = (LPCTSTR)m_strValue;
pNewAttr->PutnodeValue((_variant_t)bstrAttrValue);
pAttrList->setNamedItem(pNewAttr);
AddNodeToTree(pNewAttr, m_hCurItem);
The square world is becoming part of history like the flat world. I remember back in the early 90's we tried to rid ourselves of the square world with something
called "The Object Database". It was a great concept and the only reason square prevailed against it is because nobody could implement an Object
Database that was fast enough. Who cares how clean the code is if the application is dysfunctional because it is too slow? This is why
XMLFoundation is so performance oriented - that's what it takes to change the world. The clean code alone is not enough.
Now I'll explain how to accomplish the task of loading up your object with the information in the XML using a fully object oriented
approach to data handling. Customer, Order, and LineItem are derived from XMLObject. They must implement 1 virtual method called
MapMembers()
that would look like this:
void Customer::MapXMLTagsToMembers()
{
MapMember(&m_OrderList, Order::GetStaticTag());
MapAttribute(&m_nCustomerID, "id");
MapMember(&m_strName, "Name");
}
void Order::MapXMLTagsToMembers()
{
MapAttribute(&m_nOrderID, "id");
MapMember(&m_LineItemList, LineItem::GetStaticTag());
MapMember(&m_strDate, "Date"); }
void LineItem::MapXMLTagsToMembers()
{
MapAttribute(&m_nLineItemID, "id");
MapMember(&m_strDesc, "Desc");
MapMember(&m_strPrice, "Price");
}
Now all the object assignment and creation code is summed up into this one line.
Customers.FromXML( pzXML )
If you had a trace statement in the constructor of the Order, you would see that it was called for every appearance of an Order in the XML.
Now suppose you wanted to manipulate some member variables then regenerate
the XML: just assign your member variables normally then regenerate your XML - that's just as easy.
char *pzXML = Order.ToXML()
MapXMLTagsToMembers() defines everything needed for your objects to read
or write XML as a base method. Without the XMLFoundation you
would have to code all that looping and mapping 2 times if you wanted both
reading and writing XML. Without the XMLFoundation you will have a larger
maintenance issue if any XML document structure changes because you'll have to
hunt through your looping and recursion routines to find the Element name to
change. XMLFoundation provides countless other niceties such as mapping
any number of XML tags to the same member, and conditional inclusion of members
in the output XML based on tag name or the member state such as DIRTY indicating
that the member was updated and you only want ToXML() to generate a delta of the
data rather than the entire set. You can specify element order or have
them output alphabetically. Common needs that can all be accomplished in 1
line of code rather than pages of code.
-------------------------------------------------------
MapMember(&m_nVersion,"VersionNumber");
MapMember(&m_nVersion,"ProtocolVersion");
SetMemberSerialize("VersionNumber", false );
void AddAttribute( const char * pzName, const char * pzValue, int nUpdate=0 );
It's fun to compare the differences between DOM and XMFoundation, but much of the functionality in the XMLFoundation cannot be compared to anything in
DOM. For example, the XMLFoundation maintains a bit flag field for each member that it manages. These are the values that can be managed:
#define DATA_DIRTY 0x01
#define DATA_NOT_NULL 0x02
#define DATA_CACHED 0x04
#define DATA_NULL 0x08
#define DATA_SERIALIZE 0x10
The following interface uses some of the member state flags:
bool setMemberDirty(void *pAddressOfMemberToSet, int bDirty = 1);
bool setMemberDirty(char *pzTagNameOfMemberToSet, int bDirty = 1);
bool isMemberDirty(void *pAddressOfMemberToCheck);
bool isMemberDirty(char *pzTagNameOfMemberToCheck);
bool isMemberNull(void *pAddressOfMemberToCheck);
bool isMemberNull(char *pzTagNameOfMemberToCheck);
bool isMemberCached(void *pAddressOfMemberToCheck);
bool isMemberCached(char *pzTagNameOfMemberToCheck);
XMLFoundation also has many options available during the creation of the XML. DOM has nothing that compares.
#define ORDER_MEMBERS_ALPHABETICALLY 0x01
#define RECURSE_OBJECTS_DEEP 0x02
#define INCLUDE_ALL_CACHED_MEMBERS 0x04
#define EXCLUDE_SHORT_TERMINATION 0x08
#define EXCLUDE_MAPPED_ATTRIBUTES 0x10
#define EXCLUDE_UNMAPPED_ATTRIBUTES 0x20
#define INCLUDE_DOCTYPE_DECLARATION 0x40
#define FULL_SERIALIZE 0x80
#define USE_OBJECT_MARKERS 0x100
#define NO_WHITESPACE 0x200
#define NO_EMPTY_STRINGS 0x400
#define EXCLUDE_OBJECT_VALUE 0x800
It also has a SAX like (but faster and far simpler) way to subscribe to notifications.
virtual MemberDescriptor *HandleUnmappedMember( const char *pzTag );
virtual void *ObjectMessage( int nCase, char *pzArg1, char *pzArg2, unsigned int nArg3, void *pArg4)
The approach used by XMLFoundation is faster than SAX. Since the object factory and the XML tokenizer were built for each other they did some unusual
tricks for each other. The tokenizer uses a unique approach to begin with. It's purely pointer based. Tokens are structures that point
into the source XML, except for entities that get expanded into a special memory region. Tokens do not hold copies of any data. During object
factorization it becomes necessary to have the token data in a null terminated string format. The big performance boosting hack is that to obtain null
terminated strings, the tokenizer actually plunks a null down over the first byte past the end of the token data. It keeps track of the data it
clobbers and restores it before parsing out the next token. There are no event calls that needlessly push data on the stack just to immediately
pop it back off. Performance profilers showed that call stack pushes and pops were the single largest consumer of CPU cycles in the tokenization process.
XMLFoundation eliminates them by "pulling" the data through a call to [void getToken(token *tok)], rather than the SAX approach that gets the data
"pushed" into the application events with between 2 and 7 arguments depending on the token type. SAX would be the fastest approach if the XMLFoundation did not exist. The XMLFoundation is the only XML parser that uses this approach. It is non-standard,
and not in compliance with W3C interfaces to an XML Parser - For our uses, It's better than any W3C standard.
I realize that the vast majority of people who use XMLFoundation would never care about these grungy technical details. To say that it is very fast is
enough for most people, but I am also writing to the people at the Apache Foundation, and Microsoft, and IBM, W3C, and the many other people who have
built their own XML Parser implementations. Fast is an understatement. Performance is a prevailing design pattern found throughout the
XMLFoundation. For example the XMLObject class is carefully designed to add minimal CPU cycles during construction because it is to the XMLFoundation
what CObject is to MFC. It has been carefully designed to add minimal entries to the virtual method table. In many cases virtual calls were consolidated
for that purpose.
The Object Factory is the part of the XMLFoundation that instantiates objects for you based on certain element tags in the source XML. It is based on the same
principle as DECLARE_DYNCREATE() that allows MFC to instantiate CView derived classes for you. In the XMLFoundation it is called
DECLARE_FACTORY().
The XMLFoundation uses this macro to instantiate COM and CORBA objects as well.
Every object that derives from XMLObject must have 1 macro in the class definition, the DECLARE macro, normally in your .h source
file. It must also have one macro at global space, often in the .cpp file matching the .h file - or you may choose to consolidate all of your IMPLEMENT
macros in a single .cpp file. These macro's supply the XML tag and 'this' object's name, aka the class name. Terminology Note: Within the
XMLFoundation the term 'tag' is 'Element Name' and sometimes 'Attribute Name'.
These macros write a method that return new instances of 'this' object type. The address of this global static function is stored
in a structure keyed by tag name. As the tags are encountered - during the xml parsing - objects are created to contain the data that they expect to follow.
If a tag is mapped to an object in a list or tree structure, then every time that tag is encountered at the level it is mapped it will create a
new instance for you and put it in the data structure (list, tree, etc.) you specified with all its member variables already
assigned from the source XML as you have them mapped.
XMLFoundation has support for mapping to all native C++ data types. It also has support for mapping into data container
objects. It has specific support for RWCString, CString, and
GString, and it's very easy to add support for others by deriving from the class
"StringAbstraction" and supplying the pure virtual methods that will enable any kind of data container class to interoperate with the Object Factory
for automatic member assignments. These are the MemberMap methods in
XMLObject:
void MapMember(int *pValue,const char *pTag);
void MapMember(long *pValue,const char *pTag);
void MapMember(__int64 *pValue, const char *pTag);
void MapMember(void *pValue,const char *pTag,StringAbstraction *pHandler);
void MapMember(void *pDataStructure,
KeyedDataStructureAbstraction *pHandler,
const char *pzObjectName,
const char *pNestedInTag = 0);
void MapMember(void *pStringCollection, const char *pzElementName,
StringCollectionAbstraction *pHandler,
const char *pNestedInTag = 0);
void MapMember(void *pIntegerArray, const char *pzElementName,
IntegerArrayAbstraction *pHandler,
const char *pNestedInTag = 0);
void MapMember(XMLObject *pObj, const char *pDefaultTagOverride = 0,
const char *pzWrapper = 0 );
void MapMember(void *pList,const char *pObjectTag,
ListAbstraction *pHandler,const char *pNestedInTag = 0,
ObjectFactory pFactory = 0 );
void MapMember(XMLObject **pObj,const char *pzTag,
const char *pNestedInTag= 0, ObjectFactory pFactory=0);
The following code can be found in the example programs. Inheritance of XML maps works intuitively and enables you to organize and manage your code efficiently.
class CMatter : public XMLObject
{
public:
GString m_strWeight;
virtual void MapXMLTagsToMembers()
{
MapMember(&m_strWeight, "Weight");
}
DECLARE_FACTORY(CMatter, Matter)
CMatter(){}
~CMatter(){};
};
IMPLEMENT_FACTORY(CMatter, Matter)
class CLife : public
<span style="background-color: rgb(255, 0, 0);">CMatter
{
public:
GString m_strDNA;
virtual void MapXMLTagsToMembers()
{
MapMember( &m_strDNA, "DNA");
CMatter::MapXMLTagsToMembers(); }
DECLARE_FACTORY(CLife, Life)
CLife(){ }
~CLife(){};
};
IMPLEMENT_FACTORY(CLife, Life)
class CHuman : public
CLife
{
public:
GString m_strFingerPrint;
GString m_strGender;
virtual void MapXMLTagsToMembers()
{
MapMember(&m_strFingerPrint,"FingerPrint");
MapMember(&m_strGender,"Gender");
CLife::MapXMLTagsToMembers(); }
DECLARE_FACTORY(CHuman, Human)
CHuman(){}
~CHuman(){};
};
IMPLEMENT_FACTORY(CHuman, Human)
char pzXML3[] =
"<Human>"
"<Gender>Male</Gender>"
"<DNA>1101010001010101101011000010101010</DNA>"
"<FingerPrint>Unique</FingerPrint>"
"<Weight>777</Weight>"
"</Human>";
void Main()
{
CHuman O;
O.FromXMLX(pzXML3);
GString strDebug;
strDebug << "\n\n\nGender:" << O.m_strGender << " FingerPrint:"
<< O.m_strFingerPrint << "\n" << "DNA:" << O.m_strDNA
<< " Weight:" << O.m_strWeight << "\n\n";
printf(strDebug);
printf(O.ToXML());
CLife life;
life.FromXML(pzXML3);strDebug.Empty();
strDebug << "\n\nDNA:" << life.m_strDNA << " "
<< "Weight:" << life.m_strWeight << "\n\n";
printf(strDebug);
printf(life.ToXML());
So- for example, you may create an object CPlant that like the CHuman is derived from CLife. A CPlant would contain the elements of CLife (DNA) and of CMatter (Weight) by inheritance.
If each XML message represents a transaction it is wise to map the commonalities of all transactions, or groups of
transactions into a base class that allows derivatives to inherit the base elements of the transaction that will only be maintained in one place.
By using the XMLFoundation you inherit some powerful navigation features that can be used to help you debug your application with the
Dump() member. Because the factory manages all the object relationships, a new kind of object navigation arises: objects know their creators so an
"Order" can know at runtime if it resides inside a list in a
"Customer", or some other kind of object, or if it is not contained by another
object at all. This is what a full Dump() output looks like:
----------------------------------------------------------------------------------
Object Dump My comments
----------------------------------------------------------------------------------
Object Instance name: MyOrder Dump of Order Object
{
string OID =
string UpdateTime =
References = 1
--------------------------------
Type :string
Tag :OrderDate
Value :1776-07-04 The Order Date is July 4 1776
State :(Clean | Valid | Cached)
Kind :Element
--------------------------------
Type :string
Tag :ShippedDate
Value :2010-07-04 The Ship Date is July 4 2010
State :(Clean | Valid | Cached)
Kind :Element
--------------------------------
Type :List<XMLObject *>
Tag :LineItem contains a list of 3 LineItem objects
Contains:3 items
Object Instance name: MyOrderLineItem
{
string OID = 1121.0000 The 1st begins here
string UpdateTime =
References = 26
--------------------------------
Type :string
Tag :Description
Value : Description is empty
State :(Clean | Null | Uncached) here we can see that it was never
assigned, it was not set to ""
Kind :Element
--------------------------------
Type :int
Tag :ProductID
Value :11 ProductID is 11
State :(Clean | Valid | Cached)
Kind :Element
--------------------------------
Type :string
Tag :UnitPrice
Value :21.0000 Unit Price is 21.0000
State :(Clean | Valid | Cached)
Kind :Element
}
Object Instance name: MyOrderLineItem <--- here begins the 2nd of 3 line items
{
string OID = 332.5000
string UpdateTime =
References = 21
--------------------------------
Type :string
Tag :Description
Value :
State :(Clean | Null | Uncached)
Kind :Element
--------------------------------
Type :int
Tag :ProductID
Value :33
State :(Clean | Valid | Cached)
Kind :Element
--------------------------------
Type :string
Tag :UnitPrice
Value :2.5000
State :(Clean | Valid | Cached)
Kind :Element
}
Object Instance name: MyOrderLineItem
{
string OID = 7234.8000
string UpdateTime =
References = 23
--------------------------------
Type :string
Tag :Description
Value :
State :(Clean | Null | Uncached)
Kind :Element
--------------------------------
Type :int
Tag :ProductID
Value :72
State :(Clean | Valid | Cached)
Kind :Element
--------------------------------
Type :string
Tag :UnitPrice
Value :34.8000
State :(Clean | Valid | Cached)
Kind :Element
}
}
This is an example of what is involved to get XML to the GUI. The XML is somewhat complex to show how simple the code will
be. The XML is a "Customer" with a list of "Orders" where each order has a list of "LineItems". This is the XML:
<Customer>
<ContactName>New Dude</ContactName>
<City>Antioch</City>
<Country>All of them</Country>
<Order>
<ShippedDate>1997-09-02</ShippedDate>
<OrderDate>1997-08-25</OrderDate>
<LineItem>
<UnitPrice>45.6000</UnitPrice>
<ProductID>28</ProductID>
<Description/>
</LineItem>
<LineItem>
<UnitPrice>18.0000</UnitPrice>
<ProductID>39</ProductID>
<Description/>
</LineItem>
</Order>
<Order>
<ShippedDate>Futuristic</ShippedDate>
<OrderDate>Tomorrow</OrderDate>
<LineItem>
<UnitPrice>1234567.77</UnitPrice>
<ProductID>1234567</ProductID>
<Description/>
</LineItem>
</Order>
</Customer>
Notice that the XML foundation will parse directly in to the CStrings that are already DDX bound to
MFC's UpdateData(). This is accomplished through Multiple Inheritance. Our Dialog class derives from both MFC's CDialog, and XMLFoundation's XMLObject.
The sample application reads XML and displays it in the GUI where it can be changed by the user, then saved back out to XML that reflects
the users changes.
The complete code for this example is in "XMLDialog", but for the purpose of understanding what it takes to integrate XMLFoundation
with an MFC Dialog this shows you ALL the code of interest.
#include "xmlObject.h"
#include "GList.h"
class CXMLDialogDlg : public CDialog, public XMLObject
{
GList m_lstOrders;
virtual void MapXMLTagsToMembers();
virtual void *ObjectMessage( int nCase, char *pzArg1, char *pzArg2,
unsigned int nArg3 = 0, void *pArg4 = 0 );
DECLARE_FACTORY(CXMLDialogDlg, Customer);
CString m_strCity;
CString m_strCountry;
CString m_strName;
CString m_strRichEditXML;
}
IMPLEMENT_FACTORY(CXMLDialogDlg, Customer)
void CXMLDialogDlg::MapXMLTagsToMembers()
{
MapMember(&m_strName, "ContactName", &gC);
MapMember(&m_strCity, "City", &gC);
MapMember(&m_strCountry, "Country", &gC);
MapMember(&m_lstOrders, MyOrder::GetStaticTag(), &gGListHandler, 0 );
}
void CXMLDialogDlg::OnBtnMakeXML()
{
UpdateData(TRUE); m_strRichEditXML = ToXML(); UpdateData(FALSE); }
void CXMLDialogDlg::OnBtnLoadGUI()
{
FromXML(m_strRichEditXML); UpdateData(FALSE);
}
void *CXMLDialogDlg::ObjectMessage( int nCase, char *pzArg1, char *pzArg2, int nArg3, void *pArg4 )
{
if(nCase == MSG_SUBOBJECT_UPDATE)
{
MyOrder *pO = (MyOrder *)pArg4;
int nItemIndex = m_List.InsertItem(LVIF_TEXT|LVIF_PARAM, 0, pO->m_strOrderDate,
0, 0, 0, (long)pO);
m_List.SetItemText(nItemIndex, 1, pO->m_strShippedDate);
}
return 0;
}
The XMLFoundation was designed and built for CORBA before
it ever added any support for MFC. If you have a pre-existing CORBA system
that needs some XML tools you have come to the right place. If you are
building a new CORBA system - this is best tool available for XML support.
If you have read this document all the way to this point then you will likely understand how the XMLFoundation works for CORBA by showing
you this tiny piece of code:
class CustomerImpl : public virtual CustomerBOAImpl, public virtual XMLObject
along with the IMPLEMENT_ORB_FACTORY() macro defined in XMLObject.h, this is how CORBA can natively support the FromXML() and
ToXML() by using the XMLFoundation. The Object Factory can instantiate your interface objects for you based on the XML.
CORBA implementations can be done in Java or C++. The XMLFoundation supports both. CORBA breaks down the language barrier allowing Java
applications to easily, and natively deal with C++ objects. This example details the creation of C++ CORBA objects - The Java implementation is nearly
identical further blurring the lines between Java/C++ within the same project.
The C++ CORBA implementation will bridge into J2EE Application servers everywhere, it will work for any ORB
but a few of the most popular ones have been tested, and the makefiles are included with the CORBA sample that ships with the XMLFoundation.
The three makefiles included are for:
- Borland/Enterprise Studio - Visibroker
- IONA/iPortal Enterprise - Orbix
- BEA/Weblogic Enterprise - ObjectBroker (works great with Tuxedo implementations)
This example extends the ORB to provide native XML accessors. The sample CORBA application is based around 1 very simple object type.
It has a unique integer we call a CustomerID and a string we call a CustomerName. Each customer may contain 0 to n references to another object of the same type
as it's self, a MyCORBAObject. This would model something like a list of Customers that were referred by 'this' customer.
The IDL Looks Like This
module ExCORBA
{
interface MyCORBAObject
{
void getXMLState(out string s);
void setXMLState(in string s);
void setState(in string s, in long l);
void addSubObject(in string s, in long l);
void delSubObjects();
MyCORBAObject getSubObjectIOR(in long l);
void dumpState(out string s);
};
};
Follow this 12 Step Program
This is a very simple application. The client application makes 12 calls to the server. Every even numbered call is exactly the same - it is
a call to getXMLState() to see what's going on in the server. The client obtains an initial IOR from a server serialized IOR upon server startup.
- Assign some state in a native CORBA call. This is a typical CORBA data assignment operation. Two values are set in the object. The client
assigns two members on the server. The code looks like this on the client:
CustObject1->setState("Root",777);
- View the state of the object in XML. This uses the XML accessor to return the state of the object. The code looks like this on the client:
CORBA::String_var s;
CustObject1->getXMLState(s);
and looks like this on the server:
void ExCORBAImpl::getXMLState( CORBA::String_out s)
{
const char *p = ToXML();
s = CORBA::string_dup(p);
}
and the result XML is this:
<MyCORBAImpl>
<CustomerID>777</CustomerID>
<CustomerName>Root</CustomerName>
</MyCORBAImpl>
The tag names are configured by the ExCORBAImpl object like this:
void ExCORBAImpl::MapXMLTagsToMembers()
{
MapMember(&_nCustID, "CustomerID");
MapMember(&_strCustName, "CustomerName",&gGenericStrHandler);
MapMember(&m_lstCMyImplObjs, "MyCORBAImpl",&gGListHandler,0);
}
- Update the state of the object through XML. Step 1 used a typical object accessor to assign the state. Step 3 accomplishes the same through XML.
The code on the client looks like this:
CustObject1->setXMLState("<MyCorbaImpl><CustomerName>SuperUser</CustomerName>
</MyCorbaImpl>");
On the server the code looks like this:
void ExCORBAImpl::setXMLState( const char* pzXML )
{
FromXML( pzXML );
}
- View the modified object state in XML. This is the exact same code (client and server) as Step 2. We're calling
getXMLState() again,
and the result is:
<MyCORBAImpl>
<CustomerID>777</CustomerID>
<CustomerName>SuperUser</CustomerName>
</MyCORBAImpl>
- Add CORBA Sub-Objects through XML. Step 5 is a lot like step 3 where we updated the name "root"
to "SuperUser" through an XML assignment. This time we'll add an object reference. The client code looks like this:
CustObject1->setXMLState(
"<MyCORBAImpl>"
"<MyCORBAImpl>"
"<CustomerID>123</CustomerID>"
"<CustomerName>Al Gore</CustomerName>"
"</MyCORBAImpl>"
"<MyCORBAImpl>"
"<CustomerID>456</CustomerID>"
"<CustomerName>George Bush Jr.</CustomerName>"
"</MyCORBAImpl>"
"</MyCORBAImpl>");
and this is the code on the server:
void ExCORBAImpl::setXMLState( const char* pzXML )
{
FromXML( pzXML );
}
- (exactly like steps 2 & 4) - View the object's XML state.
<MyCORBAImpl>
<CustomerID>777</CustomerID>
<CustomerName>SuperUser</CustomerName>
<MyCORBAImpl>
<CustomerID>123</CustomerID>
<CustomerName>Al Gore</CustomerName>
</MyCORBAImpl>
<MyCORBAImpl>
<CustomerID>456</CustomerID>
<CustomerName>George Bush Jr.</CustomerName>
</MyCORBAImpl>
</MyCORBAImpl>
- Get a CORBA object reference for object instance 456. On the client the code looks like this:
ExCORBA::MyCORBAObject_var CustObject2;
CustObject2 = CustObject1->getSubObjectIOR(456);
and on the server we walk the list of objects and return the first one that matches the supplied CustomerID like this:
ExCORBA::MyCORBAObject_ptr ExCORBAImpl::getSubObjectIOR(CORBA::Long CustomerID)
{
GListIterator it(&m_lstCMyImplObjs);
while(it()) {
XMLObject *pO = (XMLObject *)it++; ExCORBAImpl*pIO = (ExCORBAImpl*)pO->GetInterfaceObject();
if (pIO->GetCustomerID() == CustomerID)
{
return pIO->_this();
break;
}
}
return 0;
}
- Exactly like steps (2, 4 & 6) EXCEPT we are using the Object ref returned by step 7.
<MyCORBAImpl>
<CustomerID>456</CustomerID>
<CustomerName>GeorgeBush Jr.</CustomerName>
</MyCORBAImpl>
- Add a Sub-Object without using XML. In the same way we used a traditional member assignment in step 1, we can create a new object reference
to demonstrate the two models seamlessly working together. On the client:
CustObject1->addSubObject("Michelangelo",1475);
and on the server the code looks like this:
void ExCORBAImpl::addSubObject( const char* s, CORBA::Long l )
{
ExCORBAImpl *p = new ExCORBAImpl;
p->_nCustID = l;
p->_strCustName = s;
m_lstCMyImplObjs.AddLast((XMLObject *)p);
}
- Get an object reference to the object created in step 9 and display it's state in XML. This is the client code:
CustObject2 = CustObject1->getSubObjectIOR(1475); CustObject2->getXMLState(s);
and the result is:
<MyCORBAImpl>
<CustomerID>1475</CustomerID>
<CustomerName>Michelangelo</CustomerName>
</MyCORBAImpl>
- Deleting Sub objects. All objects, no matter how they were created, are destroyed the same. The list contains both Factory created objects
and Objects created the tradional way. Once again this shows how seamlessly the ORB fits together with the XMLFoundations's Object Factory.
This the CORBA Implementation/Interface and XMLObject are all one in the same. This cleans up the whole mess.
CustObject1->delSubObjects();
on the server:
void ExCORBAImpl::delSubObjects() IT_THROW_DECL((CORBA::SystemException))
{
GListIterator it(&m_lstCMyImplObjs);
while(it())
{
XMLObject *pO = (XMLObject *)it++;
pO->DecRef();
}
m_lstCMyImplObjs.RemoveAll();
}
- To see that step 11 worked, view the XML state like we did in 2,4,6,8, & 10. Now all the contained objects are gone, and
"SuperUser" is alone.
<MyCORBAImpl>
<CustomerID>777</CustomerID>
<CustomerName>SuperUser</CustomerName>
</MyCORBAImpl>
Create a basic ATL COM project with Visual Studio.
Visual Studio will write your IDL, and implementation header files. The following code sample is
the standard implementation header file with a few small additions (highlighted in yellow) required for XML support.
class ATL_NO_VTABLE CMyATLObj :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CMyATLObj, &CLSID_MyATLObj>,
public IDispatchImpl<IMyATLObj, &IID_IMyATLObj, &LIBID_EXATLCOMLib>,
public XMLObject
{
~CMyATLObj();
DECLARE_REGISTRY_RESOURCEID(IDR_MYATLOBJ)
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP(CMyATLObj)
COM_INTERFACE_ENTRY(IMyATLObj)
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()
void MapXMLTagsToMembers();
public:
DECLARE_FACTORY(CMyATLObj, Container)
};
In your implementation file you'll need to add the macro at a global scope and implement
MapXMLTagsToMembers() to define the Object to XML mappings. This example maps an integer,
a string, and a list of COM objects.
void
CMyATLObj::MapXMLTagsToMembers()
{
MapObjectID("CustomerID",1);
MapMember(&m_nInteger, "CustomerID");
MapMember(&m_strString, "CustomerName", &gGenericStrHandler);
MapMember(&m_lstCMyATLObj, CMyATLObj::GetStaticTag(),
&gGListHandler,0);
}
In the ExATLCOM sample application several additional methods have been added to the COM Object. Most notably
put_XMLState() that has the ability to
assign members variables and create COM objects when supplied well-formed XML as input.
STDMETHODIMP
CMyATLObj::put_XMLState(BSTR newVal)
{
_bstr_t b(newVal);
FromXML((const char *)b);
return S_OK;
}
XMLFoundation has been serving up the XML related needs of the application layer for nearly a decade. It has been used to build a
wide variety of application types. A common recurring need in the application layer has to do with "data updates". Any application
that receives XML updates might consider the performance advantages and reduction in development labor by using the XMLFoundation to solve the problem for them.
For example, suppose you had some large dump of XML data. In your application layer you need to quickly access individual
pieces of that information. In just a few lines of code, the XML can be mapped to a keyed data structure for fast indexed reads by your application. If the initial
XML dataset was 100+million records - you will want to provide updates to your indexed information rather than rebuilding the entire index. You could
write the code to search for the data to update, or allow the XMLFoundation to manage it for you.
Another common example in distributed systems: Data is often cached at a middle tier or in the application itself.
Efficiently designed systems only update an "Address" rather than a whole "Customer" and all his "Orders" when an
"Address" changes. XMLFoundation can greatly simplify this task.
At the core of caching is something XMLFoundation calls the OID, or Object ID. It is a unique key to the object, and any object that
participates in XMLFoundation caching must have one. The definition of the OID can come from 2 places. It can be defined in the XML data, or it can
be defined by the object that mapped the data.
This is how a "'MyOrderLineItem" object might define the OID. It uses a combination of the "ProductID" and
"UnitPrice" so in this example a price change constitutes a different object. Normally an OID has a direct correlation to DBMS indexes in
properly normalized data. ObjectID's can be made to work well over poor data models
too. This example code uses two XML Elements ("ProductID" and "UnitPrice") to build the unique object ID.
MapObjectID() also allows you to use Attributes to define the OID.
void MyOrderLineItem::MapXMLTagsToMembers()
{
MapObjectID("ProductID",1,"UnitPrice",1);
}
Alternatively the OID can be directly defined by the data itself with a special attribute named "OID" - so that NO CODE needs to be written.
<MyOrderLineItem oid='777'>
<ProductID>123</ProductID>
<UnitPrice>7.77</UnitPrice>
</MyOrderLineItem>
The sample application "ObjectCache" provides over 30 test cases that detail the usage of object caching.
For the most part - objects contain members. Members are mapped to attributes and elements in the XML. For the most part - that is how most
XML documents are arranged but as you will see here there are yet still two other forms of markup for getting untagged data into the object.
As a prerequisite to reading the following paragraph you must know a little bit about what the XML specification refers to as CDATA, so go plug that into your
favorite search engine if you are not familiar with unparsed data then when you pop your stack of things to do you will find yourself ready to continue reading this:
<Thing Color='Red White and Blue'>
<![CDATA[]]>-Object Data-=
<---Pay special attention to this line
<String>Capitol Capital G</String>
<Number>777</Number>
<Wrapper>
<StringList>one</StringList>
<StringList>two</StringList>
</Wrapper>=-More Object Data-
<---and this line (Parsed Data)
</Thing>
The class declaration below has Maps for all of the elements and attributes in the XML above.
It makes no provisions for Object Data - Parsed or Unparsed.
class MyCustomObject : public XMLObject
{
public:
GString m_strString; GString m_strColor; int m_nInteger; GStringList m_strList;
virtual void MapXMLTagsToMembers()
{
MapMember( &m_strList, "StringList", "Wrapper");
MapMember( &m_nInteger, "Number");
MapMember( &m_strString, "String");
MapAttribute(&m_strColor, "Color");
}
DECLARE_FACTORY(MyCustomObject, Thing)
MyCustomObject(){}
~MyCustomObject(){};
};
IMPLEMENT_FACTORY(MyCustomObject, Thing)
This is how your code will obtain this "unmapped" object data.
void ObjectDataAndCDataExample()
{
MyCustomObject O;
O.FromXMLX(pzXML);
GString *pG = O.GetCDataStorage();
printf(*pG); printf("\n\n");
int nOffsetFromStartofXML = pG->Buf() - pzXML;
pG = O.GetObjectDataStorage();
nOffsetFromStartofXML = pG->Buf() - pzXML; printf(*pG); printf("\n\n");
printf(O.ToXML());
printf("\n\n");
MyCustomObject O2;
O2.m_nInteger = 777;
O2.m_strString = "G.G.G.Guru";
O2.m_strColor = "Gold, Green and White";
*(O2.GetCDataStorage()) << "x<data>x";
O2.SetObjectDataValue("-- object <data> is parsed --");
printf(O2.ToXML());
}
5Loaves is included with the XMLFoundation, but not in the foundation.
It is a tool that uses XMLFoundation. It needs the XMLFoundation but the
XMLFoundation does not need 5Loaves. 5Loaves is implemented in a single
file called ServerCore.cpp. It is a unique piece of code and it is very simple to use. There is no
header file for ServerCore.cpp and it causes no DLL's to be loaded by your application.
It is a portable, properly threaded, and well written server core that can be
applied to countless custom server implementations. It is a Proxy.
It is an HTTP Server. It is a 'connection joiner'. ServerCore.cpp is
currently used to accept TCP connections for an advanced networking
product called Xfer. (note: Xfer is not open
source). The ServerCore has a good portable threading
model with some unique features that allow you to limit such things as, number
of connections per IP/Subnet and Connections per second. The 5Loaves HTTP
Server works faster than IIS or Apache in some cases. It has been designed to be fast and includes unique features such as
'content caching' to serve up prebuilt HTTP headers and data from memory rather
than from disk.
The Core of 5Loaves is a ground up POSIX threaded TCP server. This
server template can be applied to build many types of applications that service
TCP connections. The sample programs are server applications that use the
5Loaves ServerCore. One example is the 5Loaves shell console. It is
a command line interpreter like the DOS prompt or a Unix shell written from
scratch. It is a useful application starting point if you ever need a
simple shell that has been tested in Linux, Solaris, AIX, HPUX, and
Windows. There is also a Windows Service application. It contains
the proper implementation for integrating 5Loaves with the Windows Service
Control Manager. There is also an example program with all the source to
an ActiveX implementation of 5Loaves so you can see that this server core can be
embed just about anywhere in any development language.
The following code sample shows you how to start the HTTP service from inside
your own process. You can't do that with IIS or Apache. The HTTP
server does support binary plugins like ISAPI, and there is an example program
that creates plugins. It's an advanced HTTP server.
#include "../Core/ServerCore.cpp"
char *pzBoundStartupConfig =
"[System]\r\n" "Pool=20\r\n"
"ProxyPool=0\r\n"
"\r\n"
"[HTTP]\r\n" "Enable=yes\r\n"
"Index=Index.html\r\n"
"Home=%s\r\n"
"Port=%s\r\n";
void CMyClass::StartHTTPServer(const char *pzHomeDirectory,const char *pzPort)
{
GString strCfgData;
strCfgData.Format(pzBoundStartupConfig,pzHomeDirectory,pzPort);
SetProfile(new GProfile( strCfgData, strCfgData.Length()) );
server_start();Building a Custom HTTP Web Service
Approach #1 - "Low Level Static Code"
Adding on to the rather trivial amount of code shown above for integrating
ServerCore, you may want to develop
a custom "dynamic content" server based on this HTTP server
implementation that can be easily integrated into YOUR process (unlike IIS or
Apache). There are several ways to go about accomplishing this task and
depending on your situation one way may be more appropriate than another.
One way of implementing a custom ServerCore extension can be accomplished via
"Low Level Static Code". The advantage to this form of
integration is that no DLL's are loaded. Another possible advantage is that no
form of integration could possibly be faster. Using this approach ServerCore will manage nothing except for multi-threading
the connections for you and the initial TCP network read.
This can be done by adding this one line of code:
#define SERVERCORE_CUSTOM_HTTP
prior to adding this line of code:
#include "../Core/ServerCore.cpp"
You will also have to create a file called ServerCoreCustomHTTP.cpp, a
sample implementation has been provided in the Server/Core folder. To see it work in the
"5Loaves" example project add the #define SERVERCORE_CUSTOM_HTTP into the file Servers/5loaves/Console.cpp then create a
text file called 5Loaves.txt that you can place in the same folder as the binary
or at "C:\\" with this contents.
[System]
Pool=20
ProxyPool=0
[HTTP]
Enable=yes
This sets the thread pool to 20 setting the limit of your server to 20 concurrent client connections and supports no proxy connections, you may set the
thread pool at any value that you have enough hardware resources to support.
Making this example work in the Windows Service is equally as simple by adding #define SERVERCORE_CUSTOM_HTTP
into Servers/WebServerService/WebServerService.cpp (directly above the inclusion of ServerCore.cpp) This will extend
the service application to run a custom 'low level - static code' extension that is implemented in ServerCoreCustomHTTP.cpp
exactly like the console application.
I have two products that both use this "Low Level Static Code" approach to extending ServerCore.cpp. It is my preferred approach when
building an "application" based on ServerCore.cpp because the integration is "tight" beyond the definition
of "tight". You should search ServerCore.cpp for SERVERCORE_CUSTOM_HTTP to see for yourself that this is not function
call - but a true inline implementation into the very first call stack frame of the servicing thread. Since the code is being added directly
into the lowest level possible there is no function call dispatch, it executes the extension without even adding a new frame on the call stack
(you can't do that with IIS or Apache - much less in YOUR process space) - this makes the extension code look strange because there will
be no open scope { or close of scope } - and you will exit with a GOTO rather than a return. For example consider this complete
example found in Servers/Core/ServerCoreCustomHTTP.cpp:
if (memcmp(sockBuffer,"GET",3) == 0)
{
GString strRequest;
strRequest.SetFromUpTo(&sockBuffer[4]," ");
GString strResponse("Server Response: Hello World");
HTTPSend(td->sockfd, strResponse, strResponse.Length());
goto KEEP_ALIVE;
}
else if (memcmp(sockBuffer,"POST /",6) == 0)
{
}Now - to see this example work... Run 5Loaves.exe with the 5Loaves.txt in the same directory
Put this URL into your browser: http://127.0.0.1/ Your browser will display "Server Response: Hello World"
Approach #2 - "Binary Plugin"
This next approach to extending an HTTP service is more typical. Both IIS and Apache support both CGI and ISAPI to support user developed web server
extensions via "Binary Plugin's". This approach allows you to rebuild the extension without rebuilding the HTTP server. You will create
a DLL (under Windows) or an SO (under Unix) that works a lot like ISAPI. The HTTP service will load and execute your extension giving you access to
everything necessary to build any kind of custom extension. The ServerCore adds one additional layer of abstraction by invoking the "Plugin"
through the "Language Driver" described in the next section.
Plugins and Language Drivers
The XMLFoundation supports "Language Drivers". 5Loaves is
among the applications that implements them. Language
drivers allow user developed extensions to be invoked programmatically.
Just about every programming language can have a language driver developed for
it. Several complete Language Driver implementations come with the XMLFoundation
source code.
The programmer's code is "the plugin"
that is executed by the "Language Driver". The majority of
people who use this technology will probably be developing "plugins", but you
also may develop an application that allows users to develop their own plugins.
All of the source
code for everything I speak of is included in the XMLFoundation, see [IntegrationBase.h/cpp][IntegrationLanguages.h/cpp],
using DynamicLibrary.h to load the DLL/SO's on many platforms.
I have used the Language Driver functionality in several
applications. For example, I have an XSLT that allows user extensions in
addition to the built-in XSL keywords. This allows my application to pass a
string value into a user defined method in a plugin that might need to do a
database lookup to translate a code - a task that is too complex for any XSL
keyword. Loading the "Language Driver" and executing a plugin
does not require much code. This is the code to pack 4 arguments and
invoke a plugin called "Test1" inside "PluginExample.dll"
through the CStdCall Language Driver:
char pzArgBuff[256];
sprintf(pzArgBuff,"aaa%cbbb%c....fast%c",0,0,0); unsigned long *pL = (unsigned long *)&pzArgBuff[8]; *pL = 777; char pzArgSizes[16];
strcpy(pzArgSizes,"4|4|4|5");
InterfaceInstance *pII = GetInterfaceInstance("CStdCall");
int nOutResultSize;
const char *pzResults = pII->InvokeEx( "PluginExample.dll", "PluginExample", "Test1",
pzArgBuff,pzArgSizes,&nOutResultSize, "anonymous",
"password" );The code shown above would be in the hosting application, or the application that supports custom extensions.
It will then execute the plugin that is even easier to develop (code shown below):
ExposedMethods() should define the arguments like this:
Test1&&One&&char *&&Two&&char *&&Three&&Packed Unsigned Long&&Four&&char *
extern "C" __declspec(dllexport) void Test1(void *pHandle, DriverExec Exec,
const char *One, const char *Two, const char *Three, const char *Four)
{
PlugInController PIC(pHandle, Exec);
(*(unsigned long *)Three)++;
unsigned long uThree = *(unsigned long *)Three);
uThree++;
PIC.AppendResults("Argument 3 is Native Packed Binary in a Plugin");
}One sample application titled "PluginExample" is devoted to C++ plugins. It shows how to write several types of POST handler plugins for
the HTTP server. You will find the utility class CMultiPartForm handy if you need to write a handler for a Multipart HTTP POST, other plugin utilities
like PlugInController found in PluginBuilder.h make the job of building a plugin as easy as possible.
ServerCore.cpp has the implementation using (InterfaceInstance *)pII->InvokeEx()described above already complete so that you
may extend the HTTP service via your own DLL(or COM object or Perl/Python script). This is how to build a custom dynamic web page from an HTTP server plugin:
- Compile the CStdCall Language Driver
- Compile the PluginExample
- Setup
the 5Loaves.txt Configuration File like this (but set the [TXML]Drivers=
and [CStdCall]Path= to where steps 1 and 2 compiled to):
[System]
Pool=20
ProxyPool=0
[HTTP]
Enable=yes
EnableServerExtensions=yes
ServerPlugin1=/Test2WWWPage|CStdCall|PluginExample.dll|PluginExample|Test2
ServerPlugin2=/Test3WWWPage|CStdCall|PluginExample.dll|PluginExample|Test3
ServerPlugin3=/Test4WWWPage|CStdCall|PluginExample.dll|PluginExample|Test4
ServerPlugin4=/Test5WWWPage|CStdCall|PluginExample.dll|PluginExample|Test5
ServerPlugin5=/Test7WWWPage|CStdCall|PluginExample.dll|PluginExample|Test7
ServerPlugin6=/Page1|CStdCall|PluginExample.dll|PluginExample|Page1
ServerPlugin7=/Page2|CStdCall|PluginExample.dll|PluginExample|Page2
ServerPlugin8=/Page3|CStdCall|PluginExample.dll|PluginExample|Page3
[TXML]
Drivers=C:\XMLFoundation\Drivers\Debug
[CStdCall]
Path=C:\XMLFoundation\Examples\C++\HTTP.Xfer.Messaging-PluginExample
The HTTP service will loop through the ServerPluginN entries and dispatch the
plugin calls for you. You must configure the ServerPluginN entries in
numerical order and the HTTP service will load each entry upto the first
numerical break. You may also manage the dispatch partially yourself by
using a wildcard * in the first argument - for example.
ServerPlugin1=/PluginPage*|CStdCall|MyDLL.dll|MyDLL|DoIt
This would map the following URL's to DoIt() in:
MyDLL.dll
http://127.0.0.1/PluginPageFoo.html
http://127.0.0.1/PluginPageBar%20Argument1%20Argument2
http://127.0.0.1/PluginPageHello
- Put
the 5Loaves.txt file into the project directory if you run 5Loaves.exe under a
debugger or put 5Loaves.txt in the same directory as 5Loaves.exe if you run
5Loaves.exe outside the debugger - either way when it starts 5Loaves will
display this message:
5Loaves>Windows Console using [5Loaves.txt]
Listening on port[80]
All bound ports are now being serviced
started
5Loaves>
- Now
execute the most basic plugin that takes 0 arguments and is simply a static
bound html page - put this URL into your browser: http://127.0.0.1/Page1
The browser will show a simple web page with 3 edit fields and a
"Submit" button. If you see that page - the plugin
executed properly through an HTTP GET which loaded an HTML page that will POST
back the 3 arguments into another handler that exists in that same plugin.
The information POST'ed by the HTML form calls a method with with 3
arguments. So you supply the values 1, 2, and 3 to the three edit fields
and press submit your browser will display this:
5Loaves HTTP Server Invoked me with [1] and [2] and [3]!
- Now try this URL: http://127.0.0.1/test2WWWPage&root&777&superuser and this will be the result:
5Loaves HTTP Server Invoked me with [root] and [777] and [superuser]!
- Look at the Plugin example to see the details of implementing a plugin.
You will see that this is the plugin handler that was called in step 5 and 6:
extern "C" __declspec(dllexport) void Test2(void *pHandle, DriverExec Exec,
const char *One, const char *Two, const char *Three)
{
PlugInController PIC(pHandle, Exec);
PIC.AppendResults("5Loaves HTTP Server Invoked me with [");
PIC.AppendResults(One);
PIC.AppendResults("] and [");
PIC.AppendResults(Two);
PIC.AppendResults("] and [");
PIC.AppendResults(Three);
PIC.AppendResults("]!");
}
5Loaves provides tunneling with encryption and compression as a base
service. It can be used to secure internet connections much like SSH.
It works by running some form of server with the 5Loaves engine on two
machines. The data passed between those machines can be compressed or
encrypted or simply logged. Starting the server is done exactly the same
no matter if you are running the HTTP service or the Tunneling service - only
the configuration startup string changes. The following configuration
example will open a listener on port 1972, anything it receives will be
encrypted and compressed and sent to [www.ExampleServer.com] on port 2009.
The server will decrypt and decompress the data from port 2009 and forward it to
port 1972, so the data on port 1972 at the server will be as if the client had
directly sent it. To open a tunnel entry point on the client side use
this:
[Tunnel1]
Enable=yes
LocalPort=1972
RemotePort=2009
RemoteMachine=www.ExampleServer.com
Timeout=30
CompressEnabled=yes
CipherPass=Tiger
RawPacketProxy=no
To exit the tunnel on the server:
[Proxy1]
Enable=yes
LocalPort=2009
RemotePort=1972
RemoteMachine=127.0.0.1 (we could also use an internal resource like
192.168.*)
Timeout=60
RawPacketProxy=no
CompressEnabled=yes
CipherPass=Tiger
Note: you can start any number of tunnels, simply increment the section
[Tunnel2] and [Proxy2]. ServerCore will stop loading tunnels at the first
break of numeric order of the [sectionN].
This type of usage may be of
interest especially to web developers or people who are just curious.
Sometimes it's interesting to see the data between the HTTP server and the
browser. Redirects and HTML frames and Javascript can make that difficult.
This section sets up a clear text proxy just for the purpose of seeing the
transmission log between the browser and the web-server. Once this is
running, connect with a web browser to 127.0.0.1 and you in fact be
connecting to the endpoint configured under the RemoteMachine= entry.
[Tunnel2]
Enable=yes
LocalPort=80
RemotePort=80
RemoteMachine=www.SampleWebSite.com
Timeout=65000
RawPacketProxy=yes
LogPath=c:\HTMLSpy
LogBinary=no
LogEnabled=yes
In the folder HTMLSpy you will see a log file of the communication between
the browser and the web server. I used Firefox as a browser, and the
5Loaves HTTP server for this example. I loaded the page twice in the
browser so that you can see the caching mechanism working between the browser
and the web server in this log file: The header is defined like this:
"tx->s" means "transmit to server" so that is information
that came from the browser, it is followed by the time, then the total bytes
transmitted, 470 in this case. The reply is "tx->c" or
"transmit to client", you can see that the HTTP server responded with
181 bytes that instructed the browser to use the version it has cached.
You can also see that the "Server:" name was set to "MyWebServer",
that is a variable in the 5Loaves HTTP server unlike the static names IIS and
Apache use.
tx->s00:51:42-000470>
GET / HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.11)
Gecko/2009060215 Firefox/3.0.11
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*
By changing the log binary to yes, now we can see what a small gif file looks
like over the wire from the web server.
[Tunnel2]
LogBinary=yes
tx->c01:05:29-000317>
48 54 54 50 2F 31 2E 31 20 32 30 30 20 4F 4B 0D 0A 44 61 74 65 3A 20 57
65 HTTP/1.1 200 OK..Date: We
64 2C 20 30 31 20 4A 75 6C 20 32 30 30 39 20 30 31 3A 30 35 3A 32 39 20
47 d, 01 Jul 2009 01:05:29 G
4D 54 0D 0A 53 65 72 76 65 72 3A 20 4D 79 57 65 62 53 65 72 76 65 72 0D
0A MT..Server: MyWebServer..
43 6F 6E 6E 65 63 74 69 6F 6E 3A 20 6B 65 65 70 2D 61 6C 69 76 65 0D 0A
4B Connection: keep-alive..K
65 65 70 2D 41 6C 69 76 65 3A 20 74 69 6D 65 6F 75 74 3D 32 30 2C 20 6D
61 eep-Alive: timeout=20, ma
78 3D 31 34 39 0D 0A 4C 61 73 74 2D 6D 6F 64 69 66 69 65 64 3A 20 53 75
6E x=149..Last-modified: Sun
2C 20 31 33 20 4D 61 72 20 32 30 30 35 20 32 32 3A 33 32 3A 33 36 20 47
4D , 13 Mar 2005 22:32:36 GM
54 0D 0A 45 54 61 67 3A 20 31 31 31 30 37 34 39 35 35 36 0D 0A 43 6F 6E
74 T..ETag: 1110749556..Cont
65 6E 74 2D 74 79 70 65 3A 20 69 6D 61 67 65 2F 67 69 66 0D 0A 43 6F 6E
74 ent-type: image/gif..Cont
65 6E 74 2D 6C 65 6E 67 74 68 3A 20 37 34 0D 0A 0D 0A 47 49 46 38 39 61
10 ent-length: 74....GIF89a.
00 10 00 91 00 00 00 00 00 FF FF FF FF FF FF 00 00 00 21 F9 04 01 00 00
02 ..................!......
00 2C 00 00 00 00 10 00 10 00 00 02 1B 94 8F A9 CB 07 AD C0 83 4E 52 23
2D .,...................NR#-
CD BA F1 BE 7C 5B 76 91 E5 54 5E EA CA 1A 05 00
3B
....|[v..T^.....;
Messaging
5loaves has a unique network connectivity utility built in. It allows
machines behind a firewall that cannot "listen" for connections
outside the network to accept connections from anywhere without any firewall
configuration changes. There is a complete example called "FilePoster"
that puts a file on a machine behind a firewall. This is a bare bones
'proof of concept' implementation that gives you a working model to customize
for your own purposes. It requires 3 machines to see it work as designed.
Machine 1 - (the switchboard) should be located on the
internet. You must run the HTTP service along with the "Switchboard
Service", you can see that this example runs it on port 81 just incase you
have IIS or Apache already on port 80.
[SwitchBoardServer]
Enable=yes
Name=/PublicPath/
[HTTP]
Enable=yes
Port=81
ContentCache=0
UseKeepAlives=1
HTTPHeaderServerName=5Loaves
KeepAliveTimeOut=20
ShowIPAddressPageName=ShowIP
Home=d:\home
[Trace]
HTTPHeaderTrace=0
ThreadTrace=0
ConnectTrace=1
Machine 2 - (the server) this is the machine behind the
firewall that you want to open up a connection path to. It will poll the
switchboard server looking for connections. It should run 5oaves with the
configuration below. This will accept remote data, and write it to disk in
a file at "c:\5LMessages\UBTsAccountForYou". You could change
the application logic that writes the file - you can do anything with the data
that may contain commands, database queries, or custom logic.
[Messaging]
Enable=yes
AcceptFrom=UBTsAccountForYou
DefaultSwitchBoardServer=10.20.30.40
DefaultSwitchBoardPort=81
UseBrowserProxy=no
[MsgFrom-UBTsAccountForYou]
Enable=yes
CheckAtSwitchBoard=yes
Name=/PublicPath/UBTsAccountForYou
DiskLocation=c:\5LMessages\UBTsAccountForYou
LetSenderPlaceFile=No
PollIntervalSeconds=20
Machine 3- (the client) runs the FilePoster sample application. Machine 3 can reach "the switchboard (machine 1)"
but not "the server (machine 2)". We will send the data to Machine 2 and get a response back from that machine. In this case we are
simply writing the data we send to a file, but the data could just as easily have been an SQL statement and the return data could be the result set rather
than just a confirmation that the file was written.
How it works:
The "server" polls into the "switchboard" with an HTTP GET. The "client" pushes a multipart HTTP POST to the
"switchboard". The switchboard joins the connections and proxies the data. An HTTP GET needs an HTTP "200 OK" so the
"switchboard" server rips off the POST headers from the data sent up by the "client" and replaces them with an HTTP 200 followed by the
POST data that gets proxied straight through. Once this initial message proxy is complete, the client connection that POSTed it
waits in the switchboard, for the server to POST back a response. Then the Switchboard goes through the same process of ripping off the POST HTTP header and replacing it with a 200 OK
before sending the response back to the client. Lastly, the switchboard server replies with an empty HTTP 200 to the servers response POST to complete the normal HTTP request/response
design for both the client and the server. This allows it to pass through HTTP proxy servers and direct support for them is included. Technically
this is a loophole through most networks that only allow HTTP, because as you see we invented a new protocol that looks like HTTP, but in fact it is not.
XMLFoundation supports Java too, it supports all Java data types like (string byte bool double long short) as well as Java data structures
such as (Vector Stack ArrayList LinkedList TreeSet HashSet ). The XMLFoundation for Java is binary. If you want to build it yourself,
you can because the source code to the entire JavaXMLFoundation is public and included in this release.
There is no need to build it, but it's nice to be able to. I can hear some uneducated Java programmer already saying "I only want a 'Pure' Java
solution". This is Pure. It's as pure as the JVM, because if you look close you'll see that it is actually an enhancement to the JVM.
The JVM (Java Virtual Machine) is written in C and C++. Your Java code runs anywhere the JVM can compile, and the
XMLFoundation works the same way. The good news is that Java programmers don't have to deal with C++ just because their JVM is written in C++.
The same is true of the XMLFoundation for Java.
The XMLFoundation uses JNI (Java Native Interface). It parses the XML in the native binary (that was created by a C++ compiler just
like the JVM) and instantiates 'pure' Java Objects through JNI, then it assigns all the member variables just like the C++ XMLFoundation does. Can you
think of a faster way to get the job done?
This is some sample code that uses the JavaXMLFoundation. A much more detailed example is included in the source code:
import java.util.Iterator;
import java.util.Vector;
class MyLineItem extends XMLObject
{
private String item;
private String quantity;
private int ItemID;
MyLineItem()
{
super("LineItem");
}; }
MyLineItem(int nID, String itm, String qty)
{
super("LineItem");
item = itm;
quantity = qty;
ItemID = nID;
}
void MapXMLTagsToMembers()
{
MapMember(quantity, "quantity", "Quantity");
MapMember(item, "item", "Description");
MapMember(ItemID, "ItemID", "SKU");
MapObjectId(this, "ItemID"); }
}
class Customer2
{
public String name;
private int CustID;
private XMLObject ContainedXMLObj;
public MyOrder objOrder;
private Vector vecStrings;
long l;
short s;
double d;
byte b;
boolean z;
public void XMLDump()
{
MyExchange("out");
System.err.println( ContainedXMLObj.toXML() );
}
void MyExchange(String inOut)
{
ContainedXMLObj.Member(this, inOut, b, "b","byte","", 1);
ContainedXMLObj.Member(this, inOut, z, "z","bool","", 1);
ContainedXMLObj.Member(this, inOut, l, "l","long","", 1);
ContainedXMLObj.Member(this, inOut, d, "d","double","", 1);
ContainedXMLObj.Member(this, inOut, s, "s","short","", 1);
sp; "s","short","", 1);
ContainedXMLObj.Member(this, inOut, name, "name","FirstName","", 1);
ContainedXMLObj.Member(this, inOut, CustID, "CustID","CustomerID","", 1);
ContainedXMLObj.Member(this, inOut, objOrder, "objOrder", "Order","MyOrder","");
ContainedXMLObj.Member(this, inOut, vecStrings,"vecStrings",
"StringItem","String", "StringList Level2Wrapper");
}
public Customer2( String strXML )
{
ContainedXMLObj = new XMLObject("Customer", false );
ContainedXMLObj.fromXML(strXML);
MyExchange("in");
}
public void ApplyXML( String strXML )
{
ContainedXMLObj.fromXML(strXML);
MyExchange("in");
}
}
This is some of the code I developed inside the JavaXMLFoundation that interacts with the JVM. (Don't worry you'll never have to work with this code.)
jobject MakeObjectInstance(JNIEnv *env,const char *pzObjectType,
DynamicXMLObject *pDO,DynamicXMLObject *pDXOOOwner)
{
if (env->ExceptionOccurred())
{
env->ExceptionClear();
}
jclass clazzA = env->FindClass(pzObjectType);
jobject objReturnValue = 0;
GString strType("Ljava/lang/String;");
jmethodID midctor = env->GetMethodID(clazzA, "", "(LXMLObject;)V");
if (env->ExceptionOccurred())
{
env->ExceptionClear();
}
if (midctor)
{
jclass clazzX = env->FindClass("XMLObject");
jmethodID midX =
env->GetMethodID(clazzX, "", "(Ljava/lang/String;)V");
jstring tagX = env->NewStringUTF(pDO->GetObjectTag());
jobject objX = env->NewObject(clazzA, midX, tagX );
jclass clazz = env->GetObjectClass(objX);
jfieldID fid = env->GetFieldID(clazz, "oH", "I");
env->SetIntField(objX, fid, CastDXMLO(pDO));
objReturnValue = env->NewObject(clazzA, midctor, objX );
}
else
{
objReturnValue = env->AllocObject(clazzA);
jclass clazz = env->GetObjectClass(objReturnValue);
jfieldID fid = env->GetFieldID(clazz, "oH", "I");
if (env->ExceptionOccurred())
{
env->ExceptionClear();
}
if (fid)
{
env->SetIntField(objReturnValue, fid, CastDXMLO(pDO));
union CAST_THIS_TYPE_SAFE_COMPILERS
{
jobject mbrObj;
void * mbrVoid;
}Member;
Member.mbrObj = env->NewGlobalRef(objReturnValue);
pDXOOOwner->addSubUserLanguageObject(Member.mbrVoid);
pDO->setUserLanguageObject(Member.mbrVoid);
cacheManager.addAlternate( pDO );
}
else if (strType.CompareNoCase(pzObjectType) != 0)
{
GString Err;
Err.Format("Object type [%s] must either be derived from XMLObject\n"
"or supply a constructor %s(XMLObject o)",pzObjectType,pzObjectType);
}
}
pDO->SetObjectType(pzObjectType);
return objReturnValue;
}Java programmers will derive from this class - then the use is nearly identical to the C++ XMLFoundation:
import java.util.Vector;
import java.util.Stack;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.TreeSet;
import java.util.HashSet;
public class XMLObject {
static { System.loadLibrary("JavaXMLFoundation"); }
private static int InstanceId = 0;
private native void JavaMap(int oH, int DataType,String strName,String xmlTag,
String strWrapper, String ObjType, String strContainerType, int nSource);
private native void JavaExchange(Object o, int oH, String inout,int nType,
String b,String c,String d,String strObjectType, String strContainerType,
int nSource);
private native int JavaConstruct(int n, String strXMLTag, int bAutoDataSync);
private native void JavaDestruct(int oH);
private native void JavaMapCacheDisable(int oH);
private native void JavaMapOID(int oH, Object o, String Key1, String Key2,
String Key3, String Key4, String Key5);
private native XMLObject JavaGetSubObj(int oH);
private native void JavaFromXML(int oH, String strXML);
private native String JavaToXML(int oH);
private native void JavaRemoveAll(int oH);
private int oH;
public void MemberRemoveAll()
{
JavaRemoveAll(oH);
}
protected void finalize()
{
JavaDestruct(oH);
}
protected void freedom()
{
JavaDestruct(oH);
}
public XMLObject(String strXMLTag)
{
oH = ++InstanceId;
oH = JavaConstruct(oH, strXMLTag, 1);
}
public XMLObject(String strXMLTag, boolean bAutoDataSync)
{
oH = ++InstanceId;
if (bAutoDataSync)
oH = JavaConstruct(oH, strXMLTag, 1);
else
oH = JavaConstruct(oH, strXMLTag, 0);
}
private void MapXMLTagsToMembers()
{
}
void DontCacheMemberMaps()
{
JavaMapCacheDisable(oH);
}
void fromXML(String strXML)
{
JavaFromXML(oH, strXML);
}
String toXML()
{
return JavaToXML(oH);
}
private String MakeObjectName(String strIn)
{
if (strIn.compareToIgnoreCase("String") == 0)
{
strIn = "Ljava/lang/String;";
}; }
else
{
String sTemp = "L" + strIn + ";";
strIn = sTemp;
}
return strIn;
}
public void MapAttrib(long z, String pzName, String pzXMLTag)
{
JavaMap(oH,0,pzName,pzXMLTag,"","", "", 2);
}
public void MapAttrib(double z, String pzName, String pzXMLTag )
{
JavaMap(oH,1,pzName,pzXMLTag,"","", "", 2);
}
public void MapAttrib(short z, String pzName, String pzXMLTag )
{
JavaMap(oH,2,pzName,pzXMLTag,"","", "", 2);
}
public void MapAttrib(byte z, String pzName, String pzXMLTag)
{
JavaMap(oH,3,pzName,pzXMLTag,"","", "", 2);
}
public void MapAttrib(String z, String pzName, String pzXMLTag)
{
JavaMap(oH,4,pzName,pzXMLTag,"","", "", 2);
}
public void MapAttrib(int z, String pzName, String pzXMLTag )
{
JavaMap(oH,5,pzName,pzXMLTag,"","", "", 2);
}
public void MapAttrib(boolean z, String pzName, String pzXMLTag )
{
JavaMap(oH,6,pzName,pzXMLTag,"","", "", 2);
}
public void MapAttrib(long z, String pzName, String pzXMLTag, String pzNestedInTag)
{
JavaMap(oH,0,pzName,pzXMLTag,pzNestedInTag,"", "", 2);
}
public void MapAttrib(double z, String pzName, String pzXMLTag, String pzNestedInTag )
{
JavaMap(oH,1,pzName,pzXMLTag,pzNestedInTag,"", "", 2);
}
public void MapAttrib(short z, String pzName, String pzXMLTag, String pzNestedInTag )
{
JavaMap(oH,2,pzName,pzXMLTag,pzNestedInTag,"", "", 2);
}
public void MapAttrib(byte z, String pzName, String pzXMLTag, String pzNestedInTag)
{
JavaMap(oH,3,pzName,pzXMLTag,pzNestedInTag,"", "", 2);
}
public void MapAttrib(String z, String pzName, String pzXMLTag, String pzNestedInTag )
{
JavaMap(oH,4,pzName,pzXMLTag,pzNestedInTag,"", "", 2);
}
public void MapAttrib(int z, String pzName, String pzXMLTag, String pzNestedInTag )
{
JavaMap(oH,5,pzName,pzXMLTag,pzNestedInTag,"", "", 2);
}
public void MapAttrib(boolean z, String pzName, String pzXMLTag, String pzNestedInTag )
{
JavaMap(oH,6,pzName,pzXMLTag,pzNestedInTag,"", "", 2);
}
public void MapMember(long z, String pzName, String pzXMLTag)
{
JavaMap(oH,0,pzName,pzXMLTag,"","", "", 1);
}
public void MapMember(double z, String pzName, String pzXMLTag )
{
JavaMap(oH,1,pzName,pzXMLTag,"","", "", 1);
}
public void MapMember(short z, String pzName, String pzXMLTag )
{
JavaMap(oH,2,pzName,pzXMLTag,"","", "", 1);
}
public void MapMember(byte z, String pzName, String pzXMLTag)
{
JavaMap(oH,3,pzName,pzXMLTag,"","", "", 1);
}
public void MapMember(String z, String pzName, String pzXMLTag )
{
JavaMap(oH,4,pzName,pzXMLTag,"","", "", 1);
}
public void MapMember(int z, String pzName, String pzXMLTag )
{
JavaMap(oH,5,pzName,pzXMLTag,"","", "", 1);
}
public void MapMember(boolean z, String pzName, String pzXMLTag )
{
JavaMap(oH,6,pzName,pzXMLTag,"","", "", 1);
}
public void MapMember(Object ob, String pzName, String pzXMLTag, String strObjectType )
{
JavaMap(oH,7,pzName,pzXMLTag,"",MakeObjectName(strObjectType), "", 1);
}
public void MapMember(Vector a, String pzName, String pzXMLTag, String strObjectType )
{
JavaMap(oH,8,pzName,pzXMLTag,"",MakeObjectName(strObjectType),"Ljava/util/Vector;", 1);
}
public void MapMember(Stack a, String pzName, String pzXMLTag, String strObjectType )
{
JavaMap(oH,8,pzName,pzXMLTag,"",MakeObjectName(strObjectType),"Ljava/util/Stack;", 1);
}
public void MapMember(ArrayList a, String pzName, String pzXMLTag, String strObjectType )
{
JavaMap(oH,8,pzName,pzXMLTag,"",MakeObjectName(strObjectType),"Ljava/util/ArrayList;", 1);
}
public void MapMember(LinkedList a, String pzName, String pzXMLTag, String strObjectType )
{
JavaMap(oH,8,pzName,pzXMLTag,"",MakeObjectName(strObjectType),"Ljava/util/LinkedList;", 1);
}
public void MapMember(TreeSet a, String pzName, String pzXMLTag, String strObjectType )
{
JavaMap(oH,8,pzName,pzXMLTag,"",MakeObjectName(strObjectType),"Ljava/util/TreeSet;", 1);
}
public void MapMember(HashSet a, String pzName, String pzXMLTag, String strObjectType )
{
JavaMap(oH,8,pzName,pzXMLTag,"",MakeObjectName(strObjectType),"Ljava/util/HashSet;", 1);
}
public void MapMember(long z, String pzName, String pzXMLTag, String pzNestedInTag)
{
JavaMap(oH,0,pzName,pzXMLTag,pzNestedInTag,"", "", 1);
}
public void MapMember(double z, String pzName, String pzXMLTag, String pzNestedInTag )
{
JavaMap(oH,1,pzName,pzXMLTag,pzNestedInTag,"", "", 1);
}
public void MapMember(short z, String pzName, String pzXMLTag, String pzNestedInTag )
{
JavaMap(oH,2,pzName,pzXMLTag,pzNestedInTag,"", "", 1);
}
public void MapMember(byte z, String pzName, String pzXMLTag, String pzNestedInTag)
{
JavaMap(oH,3,pzName,pzXMLTag,pzNestedInTag,"", "", 1);
}
public void MapMember(String z, String pzName, String pzXMLTag, String pzNestedInTag )
{
JavaMap(oH,4,pzName,pzXMLTag,pzNestedInTag,"", "", 1);
}
public void MapMember(int z, String pzName, String pzXMLTag, String pzNestedInTag )
{
JavaMap(oH,5,pzName,pzXMLTag,pzNestedInTag,"", "", 1);
}
public void MapMember(boolean z, String pzName, String pzXMLTag, String pzNestedInTag )
{
JavaMap(oH,6,pzName,pzXMLTag,pzNestedInTag,"", "", 1);
}
public void MapMember(Object ob, String pzName, String pzXMLTag,
String strObjectType, String pzNestedInTag )
{
JavaMap(oH,7,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(strObjectType), "", 1);
}
public void MapMember(Vector a, String pzName, String pzXMLTag,
String strObjectType, String pzNestedInTag )
{
JavaMap(oH,8,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(strObjectType),
"Ljava/util/Vector;", 1);
}
public void MapMember(Stack a, String pzName, String pzXMLTag,
String strObjectType, String pzNestedInTag )
{
JavaMap(oH,8,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(strObjectType),
"Ljava/util/Stack;", 1);
}
public void MapMember(ArrayList a, String pzName, String pzXMLTag,
String strObjectType, String pzNestedInTag )
{
JavaMap(oH,8,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(strObjectType),
"Ljava/util/ArrayList;", 1);
}
public void MapMember(LinkedList a, String pzName, String pzXMLTag,
String strObjectType, String pzNestedInTag )
{
JavaMap(oH,8,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(strObjectType),
"Ljava/util/LinkedList;", 1);
}
public void MapMember(TreeSet a, String pzName, String pzXMLTag,
String strObjectType, String pzNestedInTag )
{
JavaMap(oH,8,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(strObjectType),
"Ljava/util/TreeSet;", 1);
}
public void MapMember(HashSet a, String pzName, String pzXMLTag,
String strObjectType, String pzNestedInTag )
{
JavaMap(oH,8,pzName,pzXMLTag,pzNestedInTag,MakeObjectName(strObjectType),
"Ljava/util/HashSet;", 1);
}
public void MapObjectId(Object o, String Key1)
{
JavaMapOID(oH, o, Key1, "", "", "", "");
}
public void MapObjectId(Object o, String Key1, String Key2)
{
JavaMapOID(oH, o, Key1, Key2, "", "", "");
}
public void MapObjectId(Object o, String Key1, String Key2, String Key3)
{
JavaMapOID(oH, o, Key1, Key2, Key3, "", "");
}
public void MapObjectId(Object o, String Key1, String Key2, String Key3, String Key4)
{
JavaMapOID(oH, o, Key1, Key2, Key3, Key4, "");
}
public void MapObjectId(Object o, String Key1, String Key2, String Key3,
String Key4, String Key5)
{
JavaMapOID(oH, o, Key1, Key2, Key3, Key4, Key5);
}
public void Member(Object o, String xfer, long z, String pzName, String pzXMLTag,
String pzNestedInTag, int nSource )
{
JavaExchange(o,oH,xfer,0,pzName,pzXMLTag,pzNestedInTag,"","", nSource);
}
public void Member(Object o, String xfer, double z, String pzName, String pzXMLTag,
String pzNestedInTag, int nSource )
{
JavaExchange(o,oH,xfer,1,pzName,pzXMLTag,pzNestedInTag,"","",nSource);
}
public void Member(Object o, String xfer, short z, String pzName, String pzXMLTag,
String pzNestedInTag, int nSource)
{
JavaExchange(o,oH,xfer,2,pzName,pzXMLTag,pzNestedInTag,"","",nSource);
}
public void Member(Object o, String xfer, byte z, String pzName, String pzXMLTag,
String pzNestedInTag, int nSource )
{
JavaExchange(o,oH,xfer,3,pzName,pzXMLTag,pzNestedInTag,"","",nSource);
}
public void Member(Object o, String xfer, String z, String pzName, String pzXMLTag,
String pzNestedInTag , int nSource)
{
JavaExchange(o,oH,xfer,4,pzName,pzXMLTag,pzNestedInTag,"","",nSource);
}
public void Member(Object o, String xfer, int z, String pzName, String pzXMLTag,
String pzNestedInTag, int nSource )
{
JavaExchange(o,oH,xfer,5,pzName,pzXMLTag,pzNestedInTag,"","",nSource);
}
public void Member(Object o, String xfer, boolean z, String pzName,
String pzXMLTag, String pzNestedInTag, int nSource )
{
JavaExchange(o,oH,xfer,6,pzName,pzXMLTag,pzNestedInTag,"","",nSource);
}
public void Member(Object o, String xfer, Object o2, String pzName,
String pzXMLTag, String pzObjectType, String pzNestedInTag )
{
JavaExchange(o,oH,xfer,7,pzName,pzXMLTag,pzNestedInTag,
MakeObjectName(pzObjectType),"",1);
}
public void Member(Object o, String xfer, Vector a, String pzName,
String pzXMLTag, String strObjectType, String pzNestedInTag )
{
JavaExchange(o,oH,xfer,8,pzName,pzXMLTag,pzNestedInTag,
MakeObjectName(strObjectType),"Ljava/util/Vector;",1);
}
public void Member(Object o, String xfer, Stack a, String pzName,
String pzXMLTag, String strObjectType, String pzNestedInTag)
{
JavaExchange(o,oH,xfer,8,pzName,pzXMLTag,pzNestedInTag,
MakeObjectName(strObjectType),"Ljava/util/Stack;",1);
}
public void Member(Object o, String xfer, ArrayList a, String pzName,
String pzXMLTag, String strObjectType, String pzNestedInTag )
{
JavaExchange(o,oH,xfer,8,pzName,pzXMLTag,pzNestedInTag,
MakeObjectName(strObjectType),"Ljava/util/ArrayList;",1);
}
public void Member(Object o, String xfer, LinkedList a,
String pzName, String pzXMLTag, String strObjectType,
String pzNestedInTag )
{
JavaExchange(o,oH,xfer,8,pzName,pzXMLTag,pzNestedInTag,
MakeObjectName(strObjectType),"Ljava/util/LinkedList;",1);
}
public void Member(Object o, String xfer, TreeSet a, String pzName,
String pzXMLTag, String strObjectType, String pzNestedInTag )
{
JavaExchange(o,oH,xfer,8,pzName,pzXMLTag,pzNestedInTag,
MakeObjectName(strObjectType),"Ljava/util/TreeSet;",1);
}
public void Member(Object o, String xfer, HashSet a,
String pzName, String pzXMLTag, String strObjectType, String pzNestedInTag )
{
JavaExchange(o,oH,xfer,8,pzName,pzXMLTag,pzNestedInTag,
MakeObjectName(strObjectType),"Ljava/util/HashSet;",1);
}
}The XMLFoundation was designed and developed mostly by myself,
however I had help from co-designers on the NCIS (National Clinical Information System) Project, and mostly only 1 other co-developer. Special thanks to the SAIC management who
believed in me and gave me the opportunity to develop the mother product. Thanks to everyone who stood beside me during the long and expensive labor of
putting this work together. "I told you so" to everyone else. In the end technology prevails over politics and ignorance. The XMLFoundation contains
several independent algorithms that were developed by other authors - and their supporting communities - their source code contains more information about
them. Historically it has been the solo engineers that set the direction for the masses. Bjarne Stroustrup incremented the work of
Brian Kernighan and Dennis Ritchie. There are countless contributors from the purest perspective of technology advancement and they could not
possibly all be named here.
In conclusion, this started a long time ago and it's not going away anytime soon. It's a masterpiece conspiracy to end times of high crimes.