/*
 * E57Format.cpp - implementation of public functions of the E57 format
 *   Reference Implementation.
 *
 * Copyright 2009 - 2010 Kevin Ackley (kackley@gwi.net)
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

/*!
@mainpage

@section main_Introduction Introduction
This browser-based document describes the E57 Foundation API (Application Programmer Interface) version 0.51, which is a collection of functions that help a C++ programmer read and write ASTM E57 format files.
The API acts a documented software connector between an application (above the interface) and an implementation of the API (also called an E57 Foundation Implementation, below the interface).
There could (and hopefully will be) more than one E57 Foundation Implementation of this API (perhaps written by a different programmer, or with different performance characteristics), but initially there is a single implementation: the Reference Implementation.

@section main_ReferenceImplemenation The Reference Implementation
There are two main goals for the Reference Implementation.
The first is to provide a kick-start for vendors that want to support the E57 format, lowering the barrier to adoption.
The Foundation API and underlying Reference Implementation hide much of the complexity of using the E57 data format standard.
The second goal of the Reference Implementation is to promote interoperability by being a paragon of functionality.
Test applications linked with the Reference Implementation can attempt to interoperate with a vendor's application.

Vendors are not required to incorporate the Reference Implementation into their own applications (they can roll their own E57 format reader/writer library).
But if they do compile the Reference Implementation into their application, they increase the likelihood that the application will interoperate smoothly.
The rest of this document is not about the Reference Implementation, but about the interface (the Foundation API) that controls it.

@section main_ASTMStandard ASTM standard
The Foundation API and libe57 aren't part of the ASTM standard; they support it.
The Foundation API and libe57 aren't sanctioned or approved in any way by ASTM.
The designer of the API and author of the Reference Implementation (Kevin Ackley) was heavily involved in the drafting of the E57 format standard.
However, the E57 Standard for 3D Imaging Data Exchange is the final word on what is a legal/valid .e57 file.

@section main_NeedStandard Do I need to get the ASTM standard?
Yes. The Foundation API provides the building blocks (the E57 primitive elements) for constructing and interpreting an E57 file, but the data structures that are built with these building blocks are up to the user of the API.
The required names and types of these data structures (not to mention their meanings) are specified in the ASTM standard, so you need a copy.

ASTM owns the standard, which is currently being balloted.
ASTM E57 committee members can get draft versions of standards.
After approval, everyone will need to purchase the standard from the ASTM website.
Only a very few of the 100+ ASTM committees make money, and E57 isn't one of them.
So support your industry and buy a copy.

@section main_IsXml Is the E57 format XML?
Only partially, but it's a highly specialized XML.
It's better think of an E57 file as hierarchical tree of eight data types, that happens to be partly encoded in XML on the disk.
The E57 format is a hybrid of XML (encoding the tree) and binary sections (which efficiently encode the records of point data).
For efficiency, the binary sections must be read/written in large blocks of records.
The XML section, although it holds most of the complexity in the file, is much smaller and objects can be accessed individually.
The binary sections aren't embedded in the XML section.
The XML section makes references to the binary sections, which are stored separately within the E57 file.

@section main_ApiObjects The API objects
The Foundation API is described in C++, and is object-oriented.
There are 15 types of objects, each described in a C++ class.
There is a class that encapsulates the E57 file (ImageFile), a class for each of the eight E57 primitive elements described in the standard (IntegerNode, ScaledIntegerNode, FloatNode, StringNode, BlobNode, StructureNode, VectorNode, and CompressedVectorNode) and a base class that encapsulates the common functionality of all nodes (Node).
There are two classes that keep track of the block reads/writes to the binary sections (CompressedVectorReader, CompressedVectorWriter) and a class to manage buffers for these block transfers (SourceDestBuffer).
Finally, there is a class to organize the reporting of errors (E57Exception), and a class for miscellaneous functions that are not associated with any of the other objects (E57Utilities).

@section main_SetOnce Set-once design
The primary motivation for the E57 file is to be a conduit between the proprietary formats of two different vendors' software (the reader and the writer).
It is not a general-purpose 3D database.
The API design reflects this use case by not allowing modifications of data.
This simplifies the implementation.
For example, it is not possible to change a value of a node, or delete an attached child node, and it is an error to attempt to attach a node to the tree using an already existing name.

@section main_HandleSemantics Handle semantics
All but one of the classes (the exception being E57Exception) have handle semantics, which means that there is a level of indirection.
The Foundation API classes are actually just pointers to an underlying object.
The pointers are smart (they have reference counts), so the user doesn't have to explicitly free them.
If two handles exist to the same underlying object, the object is not deleted until both handles are destroyed.
So the programmer is not burdened with keeping track of who is going to delete an object, the object "deletes itself" when all the references to it are gone.
This simplifies the use of the API, especially in the creation of trees of objects.
After you attach some object into a tree, there will be two references to the object, yours and the parent of the object in the tree.
So when you delete your reference, the parent's reference will keep the object alive (until the parent is destroyed).

A second benefit of smart pointers is that the copy and assignment functions in the classes are very cheap.
They just copy or assign the handle, not the underlying object.
So function call arguments (that are API objects) are always "call-by-reference", and the underlying object is not copied.
It is impossible to assign the underlying state of one object directly into another.
This restriction fits well into the set-once scheme described above.

The bottom line is that you should not use new/delete with the E57 Foundation objects, nor reference types (e.g. ImageFile&).
You don't have to keep track of who will delete the object, nor try to speed up the passing of handles to functions.
And the API objects clean up after themselves when an exception occurs.

@section main_NoDefaultConstructors No default constructors
None of the API classes contain default constructors, so there is no equivalent to a NULL handle (with no underlying object).
So if you have an API handle, you know it points to a valid object.

@section main_VersionNumbers Version numbers
There are three separate entities that have version numbers: the ASTM E57 format standard itself, the E57 Library (libe57), and the Foundation API.

When the E57 standard is approved, its version will start out at version 1.0.
Until that time, and until it is verified that the Reference Implementation writes the correct format, the E57 format version number written in an .e57 file is less than 1.0 (currently 0.5).
The Reference Implementation is currently configured to reject any E57 format version numbers less than the one it was built to write.
So don't invest a lot of time in creating .e57 files that can't be recreated, because the format version number in the file will advance at least once more.

The E57 library (libe57) is a collection of tools, examples, data files, tests, and software libraries that help read and write .e57 files.
The Reference Implementation is a key component of the libe57, but there are other parts as well.
For example, libe57 contains tools that sit on top of the Foundation API, that process E57 files (e.g. las2e57, e57fields).
The entire collection of tools and software libraries is tracked by a single version number: the version of libe57.
The libe57 version identifier has 3 parts, the major version number, the minor version number, and the build number.
The E57 Library version will start at about 0.1.20.
The minor version number will advance with each phase of beta release.
The build number always increases, it is basically the version number from the source code tracking system.
The version will be advanced to 1.0.XX at the end of the beta period.

The Foundation API version is tracked by two numbers: the major version number, and the minor version number.
The minor version number of the Foundation API version will advance when there is any change to the interface (either backwardly compatible or not).
The major version number will advance for changes that are not backward compatible.
Currently the API is at version 0.51.
When the API has completely solidified, the version number will advance to 1.00.
In some situations, a third number (the subversion build number of libe57) may be listed in the Foundation API version identifier.
This number can be ignored.
Changes in the build number do not imply changes in the API.
If the major and minor version number haven't changed, the API hasn't changed.

@section main_WhereStart Where to start learning about the API
To learn about the API, start reading at the HelloWorld.cpp example.
It has a lot of detail about the basics of the API and the basic mechanics of using it.
You might try pasting the HelloWorld.cpp example into a file, compile it, and link with the Reference Implementation.
Then read the Detailed Descriptions at the bottom of each of the 15 class pages listed on the Class/ClassList tab.
Then look at the discussion of each of the example program listings on the Files/FileList tab.
Finally, find DemoWrite01.cpp, included in libe57, which writes a real .e57 file that meets all the specifications of the ASTM standard.

If your existing software already writes LAS format v1.0 to v1.2, look at the utility las2e57.exe.
You may be able to use it to write an LAS file and convert to it E57 without any programming at all.
Two utilities are useful for examining what you have written: e57xmldump.exe and e57fields.exe.

@section main_GetCopy How do I get a copy of the source?
The full source of libe57 is available to anyone on the SourceForge website: http://sourceforge.net/projects/e57-3d-imgfmt/.
The libe57 source is distributed under the very liberal Boost Software License.
Binary distributions may be made available for some compiler/OSes.

@section main_GetHelp How can I get help?
Help is available by email through E57 SourceForge repository.
To get help during the beta phase of the software release you have to agree to be a beta tester (you have to describe what you are doing and hopefully to give a review of your experience).
To get more information about the beta test, contact Roland Schwarz by email at roland_schwarz@users.sourceforge.net.
*/ /*!
@page CopyRightPage Copyright

Copyright 2010 Kevin Ackley (kackley@gwi.net)

Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:

The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

//! @file E57Foundation.cpp

#include "E57FormatImpl.h"

#include "ImageFileImpl.h"
#include "SourceDestBufferImpl.h"

using namespace e57;
using namespace std;

/*!
@file HelloWorld.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno HelloWorld.cpp

This example program writes a very simple E57 file with a single String element in it, then reopens the file for reading, and prints out the String value to the console.
Note that this program does not attempt to create a complete E57 file according to the ASTM standard (which requires many elements to be defined with specific names).
The numbering in the source listing is not quite sequential (e.g. lines 2 and 3 are missing).
This is because some of the lines have documentation directives (for Doxygen) that don't appear in the listings.

<b>Source line 4</b> includes the @c iostream header because the example uses @c cout.
The API does not require @c iostream to be included (@c stdio.h could be used, or nothing at all).
<b>Source line 5</b> includes E57Foundation.h, the E57 Foundation API header file where all the API classes are declared.
All programs that use the E57 Foundation API must include E57Foundation.h
It is OK (but wasteful) to include the E57Foundation.h header twice in the same file, as the header has an include guard.

<b>Source line 6</b> has a @c using directive which allows all the Foundation API classes and functions to be referenced without having to preceed each reference with <tt>e57::</tt> (for example: <tt>e57::StructureNode root = imf.root();</tt>).
The <tt>using namespace e57;</tt> directive must follow the inclusion of the E57Foundation.h header file.
The entire API is declared in the "e57" namespace to eliminate the risk that in a large application a class name conflicts with an existing type.
All preprocessor macros defined in E57Foundation.h (which cannot be in a namespace) begin with the prefix @c "E57", so there is low risk of name clashes.
For convenience, the @c using directive on <b>source line 7</b> allows all names in the @c std namespace to be used without qualification (e.g. @c cout instead of @c std::cout).

In the @c main function there are two @c try blocks, the first creates a new file on disk, and the second reads the file back.
The @c try block is used because the API reports errors by throwing c++ exceptions rather than returning an error code from each function (see E57Exception for more details).
On <b>source line 10</b>, the function argument names @c argc and @c argv are commented out so that the c++ compiler doesn't complain about unused variables when compiler warnings are enabled.

In the first @c try block, the variable @c imf is constructed on <b>source line 12</b>, with the file name to create on the disk and the "w" mode string that indicates that the file is to be written to.
The scope of the first @c imf variable will end at the end of the first @c try block.
It is a good idea to limit the scope of smart handles (such as the ImageFile object), as memory will not be reclaimed until all references to the underlying object are destroyed.
The file name is @c "temp._e57" is used instead of @c "temp.e57", because the file won't be a complete, legal ASTM E57 Format file,  it just exercises some of the primitive E57 element data types.
If the file already exists on the disk, the old copy is deleted before a new, empty one is created.

The @c imf variable is a handle to the ImageFile object, which represents the entire contents of an E57 file.
The state of an ImageFile is stored partially in memory and partially on the disk, so the disk file may grow as elements are added to the ImageFile.
All the objects in the API are implemented as reference counted handles, which means that there is a level of indirection (using smart pointers).
This means that you don't have to manipulate pointers to objects or use the @c new /@c delete operators.
Also the handles are very small, so copying them (as an argument to a function call, for example) is very cheap.
Thirdly, since the handle are "smart" (they do reference counting), you don't have to worry about freeing any objects when you are done with them.
Operationally, this means you probably will never need to create a variable with type ImageFile* or ImageFile&.

An E57 file is conceptually a single hierarchical tree of elements.
After the @c imf object is constructed, the tree is empty, with only the top-most root element defined.
New elements are added to the ImageFile by attaching them into the tree as either children of the root element directly, or indirectly to children of elements that have been already attached into the tree.
It is OK to temporarily have small trees that are not yet attached to an ImageFile.
However it is recommended to limit the extent of these unattached sub-trees, as some of the API operations (that write large quantities of data) require that the object be attached to an ImageFile (e.g. writing to a BlobNode).
It is also OK (but not recommended) to create E57 element nodes that are never attached to an ImageFile.

In <b>source line 13</b>, a handle to the predefined root element of the ImageFile is fetched and stored in variable @c root.
The root element of an ImageFile is always a StructureNode and that is the type that ImageFile::root() returns.

In <b>source line 15</b>, a new StringNode element is created and attached as a child to the @c root StructureNode.
There are three API calls (one is inserted silently by the compiler) in this line.
First a StringNode is created with a value of "Hello world.".
The first argument of the StringNode constructor in <b>source line 15</b> is the handle to the ImageFile where the StringNode will @em eventually be attached.
The StringNode constructor returns a handle, which is used in the call to StructureNode::set().
Since we don't need the StringNode handle for anything else, we don't have to save it in a variable, which would take another line of code.

The second function call in <b>source line 15</b> is inserted automatically by the compiler.
The actual argument passed to StructureNode::set in the second position is a StringNode type.
But StructureNode::set is only defined to take generic Node handles in the second position.
There is a function in the API that can safely convert from a StringNode handle to a generic Node handle.
This function can automatically be applied by the C++ compiler.
This process is called "upcasting", which saves a lot of code duplication (functions that can handle all eight types of Nodes don't need to be defined for each specific type).

The third function call in <b>source line 15</b> attaches the new StringNode element as a child of the @c root StructureNode.
The new child is given the element name "greeting", and after attachment to the ImageFile, the element has an absolute pathname of "/greeting".
It would be an error if the root node already had a child with the element name "greeting", because the design of the API forbids any element from being set twice.

The statement on <b>source line 17</b> explicitly closes the file on the disk and the ImageFile referred to by the handle @c imf enters the @c closed state.
All data in memory is written into the disk file, and the file is closed.
No further input/output operations are possible with the file after it is closed.
It is important to explicitly close the file using the ImageFile::close() function rather than have it closed in the ImageFile destructor since errors are reported using exceptions, which are impossible to throw from a destructor in C++.

The @c catch statement at the end of the first try block will be invoked if an error occurs in one of the API function calls inside the @c try block.
For example, an exception would occur if there was not enough disk space and the file could not be completely written.
In this example program, the error is reported by printing a few lines of helpful explanation to the console, using the E57Exception::report function.
The three arguments to the E57Exception::report function allow the position in the code where the exception is handled to be reported as well as where the exception was thrown.
It is possible that calls to E57 Foundation API functions can produce exceptions other than E57Exception (for example: bad_alloc when there is insufficient memory).
Production code would probably have more handlers for other classes of exceptions.
A non-zero value is returned in <b>source line 20</b> to indicate to the caller that the program did not succeed.

In the second try block, the file is reopened in read mode.
This time the tree of elements in the ImageFile is not empty, since E57 elements have been read in from the disk file.
In <b>source line 25</b> the root element of the ImageFile is fetched.

In <b>source line 27</b>, two API calls are invoked to get a handle on the StringNode that was written in the first @c try block.
The first obvious call is to StructureNode::get(const ustring&) const which fetches a handle of a child element of the @c root node.
If @c root didn't have a child element named "greeting", an exception would be thrown.
But since we just wrote the file in the first @c try block above, we can be sure that child element does exist.
Another way to be sure that a child element exists with a certain name is if it is required by the ASTM standard.
If the element is optional in the ASTM standard, then existence must be queried by using StructureNode::isDefined() before fetching with StructureNode::get(const ustring&) const.

The second, not so obvious, API call in <b>source line 27</b> is to a type conversion: from a Node handle (returned by StructureNode::get(const ustring&) const) to a StringNode handle stored in the variable @c greeting.
This conversion is required since a child of a StructureNode can be any E57 element type.
Here again, we know that the node type must be StringNode since we wrote the file in the previous @c try block.
In an E57 file, the type of a given element may be mandated.
If the type of "greeting" was not StringNode, then the conversion would throw an exception to indicate an error condition.
If the ASTM standard allows several types for a given element, then the actual type in the file may be queried using Node::type() before a type conversion is requested.

The value of the StringNode is printed to the console on <b>source line 28</b>.
As on <b>source line 17</b>, the ImageFile is explicitly closed on <b>source line 30</b>.
After the ImageFile is closed, no further I/O is possible with the @c imf handle.

The following console output is produced:
@includelineno HelloWorld.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno HelloWorld.xml

Here is the source code without line numbers to cut&paste from:
@include HelloWorld.cpp

*/ /*!
@file Cancel.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno Cancel.cpp

This example program shows how to stop the writing of an ImageFile and gracefully remove any partially written files from the disk.
See the HelloWorld.cpp example for discussion of the use of include files, constructing an ImageFile, and the try/catch block to handle exceptions.

The ImageFile is opened for writing in <b>source line 21</b>.
<b>Source line 23</b> calls a function to print out whether ImageFile is open (which it is).
Some error is encountered (illustrated by <b>source line 24</b>), and ImageFile::cancel is called on <b>source line 26</b>.
This call causes the disk file to be unlinked (deleted) and the ImageFile isOpen state changes to false.
The state is printed on the console by function called on <b>source line 27</b>.
At this point, most of the calls to API functions (those that require the ImageFile to be open) will fail with an exception.
So there is not much to do except communicate the abort to the caller.

The following console output is produced:
@includelineno Cancel.out

There is no XML section of the @c temp._e57 E57 file, because it was deleted by the program during the abort.

Here is the source code without line numbers to cut&paste from:
@include Cancel.cpp

*/ /*!
@file Extensions.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno Extensions.cpp

This example program shows how to declare an E57 extension prefix and URI in an ImageFile.
See the HelloWorld.cpp example for discussion of the use of include files, constructing an ImageFile, and the try/catch block to handle exceptions.

In <b>source line 12</b> an write-mode ImageFile is created.
In <b>source line 14</b> an E57 extension is declared in the ImageFile, with prefix "ext1" and a given URI.
The extension must be declared before the prefix is used in any element names.
<b>Source lines 17 and 23</b> show that given a prefix, the corresponding URI can be found (and vice versa).
<b>Source lines 28-30</b> show how to iterate over all extensions defined in the file.
Note that the standardized default ASTM URI does not qualify as an "extension" and is not printed in the output.
Finally, a StringNode is added to the ImageFile root on <b>source line 32</b>, using the "ext1" extension in the element name.
If some unknown extension had been given instead in the element name on <b>source line 32</b> (e.g. "ext2"), then the StructureNode::set function would have thrown an E57_ERROR_BAD_PATH_NAME exception.

In the XML listing below, the "ext1" prefix is declared in <b>XML line 3</b>, and is used in the element name in <b>XML line 5</b>.

The following console output is produced:
@includelineno Extensions.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno Extensions.xml

Here is the source code without line numbers to cut&paste from:
@include Extensions.cpp

*/ /*!
@file NameParse.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno NameParse.cpp

This example program illustrates the element parsing functions of ImageFile.
See the HelloWorld.cpp example for discussion of the use of include files, constructing an ImageFile, and the try/catch block to handle exceptions.

A write-mode ImageFile is created in <b>source line 31</b>.
A test function is called with two example element names in <b>source lines 33-34</b>.
The parsing functions don't actually check to see if there are objects defined with the given path names or element names, they just test whether they are syntactically correct.
The ImageFile::isElementNameExtended functions only checks to see if a prefix is used, and not if it is declared in the ImageFile.

The <b>output lines 2-3</b> show that "bar" can be parsed and it is not extended.
The <b>output lines 5-6</b> show that "foo:bar" can be parsed and it is extended.

The following console output is produced:
@includelineno NameParse.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno NameParse.xml

Here is the source code without line numbers to cut&paste from:
@include NameParse.cpp

*/ /*!
@file ImageFileDump.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno ImageFileDump.cpp

This example program creates an ImageFile, then creates various kinds of nodes, dumps out their internal state to the console, then reads back the ImageFile and dumps out some more objects' state.
See the HelloWorld.cpp example for discussion of the use of include files, constructing an ImageFile, and the try/catch block to handle exceptions.

The format of the output isn't documented and may vary from one API implementation to another.
However several observations can be made about the Reference Implementation output:
The indenting is used to show the nesting of objects and sub-objects or sub-children.
The entire sub-tree is printed recursively, with children being more indented.
The output listing is very comprehensive, but can be very long.
Some of the dump functions (e.g. CompressedVectorWriter::dump) have detail that is only understandable to the implementation author, but others (like Node::dump) show a lot of state that is already visible through the API.

The following console output is produced:
@includelineno ImageFileDump.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno ImageFileDump.xml

Here is the source code without line numbers to cut&paste from:
@include ImageFileDump.cpp

*/ /*!
@file NodeFunctions.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno NodeFunctions.cpp

This example program illustrates the manipulation and interrogation of generic Nodes.
A tree three levels deep is created and printed.
See the HelloWorld.cpp example for discussion of the use of include files, constructing an ImageFile, and the try/catch block to handle exceptions.

In <b>source lines 82-83</b>, an new E57 file is opened for writing, and the predefined root node is fetched.
On <b>source line 86</b>, a StructureNode is created, and a handle to it is saved in @c child.
On <b>source line 87</b>, a StringNode is created, and a handle to it is saved in @c grandchild.
The StructureNode is then attached to the root of the ImageFile, on <b>source line 88</b>, with a resulting absolute path name of "/child".
Then, in <b>source line 89</b>, the StringNode is attached into the ImageFile tree with a resulting absolute path name of "/child/grandchild".

<b>Source line 92</b> calls a routine @c printSpecificInfo that prints out information that is specific to the type of the node.
However the format argument to the function is of type Node, and the type of actual argument provided is type StructureNode in <b>source line 92</b>.
This type mis-match is resolved by the compiler by using the upcast function of StructureNode that can convert the handle into a generic Node.
This conversion is silently by the compiler, as it is always type-safe.
Once inside @c printSpecificInfo, the true type of @c n is temporarily lost.
A Node handle does not have the member function to get how many children the underlying StructureNode object has.
On <b>source line 24</b>, the true type of @c n is fetched, and used in a @c switch statement to protect against attempting to convert a generic Node handle to a handle of the incorrect underlying type (this attempt would fail with an exception).
The generic Node handle is convert back to the correct underlying type (e.g. in <b>source line 26</b>) by calling a handle downcast function.
Note that in <b>source lines 27-29</b>, three alternate syntaxes are given for calling the exact same downcast function.
The specific handle is used to print out some information that can't be gotten using the generic handle.
Then @c printGenericInfo is called to print out the state of seven generic Node attributes that are common to all Node types.

Some state of the three nodes ("/", "/child", and "/child/grandchild") are listed in the <b>output lines 1-27</b>.
The two more nodes are created, but not attached to the root node, and some of their state is printed out in <b>output lines 28-45</b>.
Note that the isAttached attribute is false for both nodes.
When the @c try block ends in <b>source line 111</b>, all the constructed objects will be deleted.
The two unattached nodes will not be saved in the E57 file, because the were not attached directly or indirectly to the ImageFile root node.
This is reflected in the XML listing, where only three elements show up, instead of five.
Also note that the two unattached nodes remember (see <b>output lines 35 and 44</b>) which destination ImageFile was declared during their creation, even though they never get attached to that file.
It is not an error to fail to attach a node to the declared ImageFile (it's just wasteful).

The following console output is produced:
@includelineno NodeFunctions.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno NodeFunctions.xml

Here is the source code without line numbers to cut&paste from:
@include NodeFunctions.cpp

*/ /*!
@file StructureCreate.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno StructureCreate.cpp

This example program writes an ImageFile containing two StructureNodes (/child1, and /child2) and a StringNode (/child1/grandchild).
Some StructureNode functions then interrogate the resulting tree.
See the HelloWorld.cpp example for discussion of the use of include files, constructing an ImageFile, and the try/catch block to handle exceptions.

Two StructureNodes are created in <b>source lines 16-17</b>, and are attached as children to the ImageFile root node in <b>source lines 19-20</b>.
Handles are saved (in @c child1 and @c child2) so that they may be referred to later in the program.
A StringNode (with handle @c grandchild) is created and attached underneath @c child1.
Attaching @c grandchild to @c child1 before it was attached to the ImageFile root would have also been possible.
The number of children of @c root is printed (2) in <b>source line 23</b>.

A for loop, in <b>source lines 24-32</b>, that iterates through the two children of @c root and prints out some information about each.
Each child is fetched by index in <b>source line 25</b>.
The StructureNode::get(int64_t) const function returns a generic Node handle whose type must be checked before it is downcast to StructureNode (also see discussion in the IntegerNode::IntegerNode example concerning downcasting).

<b>Source line 34</b> illustrates a call to see if a structure contains an node using a relative pathname (relative to @c child1).
In contrast, <b>source line 37</b> illustrates a test of existance of an node using an absolute pathname (the path from the root of the tree that contains the @c child1 node).
<b>Source lines 41-42</b> show the testing of existance of an absolute pathName in a tree that is several levels deep
Once the existence of the elements is established in <b>source line 41</b>, it may be safely fetched in <b>source line 42</b>.

The following console output is produced:
@includelineno StructureCreate.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno StructureCreate.xml

Here is the source code without line numbers to cut&paste from:
@include StructureCreate.cpp

*/ /*!
@file VectorCreate.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno VectorCreate.cpp

This example program writes an ImageFile containing two VectorNodes, one VectorNode must contain children of the same type, and the second VectorNode can contain children of differing types.
See the HelloWorld.cpp example for discussion of the use of include files, constructing an ImageFile, and the try/catch block to handle exceptions.

<b>Source lines 16-17</b> create a homogeneous VectorNode (can only contain children of the same type) and attaches it the ImageFile root node.
In <b>source line 18</b> the first StringNode is added to the VectorNode by appending to the end.
Since the VectorNode was initially empty, the first string is put in the 0 child index position.
After the first child is appended to the VectorNode, any further children appended must be the same type as the first.
The second append, on <b>source line 19</b>, is in fact the same type, and it is put in the 1 child index position.

<b>Source lines 22-23</b> create a heterogeneous VectorNode and attach it to the ImageFile root node.
<b>Source lines 24-25</b> show that it is not an error to append child nodes of different types to the heterogeneous VectorNode.
<b>Source lines 27-30</b> show that the heterogeneous attribute can be interrogated.

The following console output is produced:
@includelineno VectorCreate.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno VectorCreate.xml

Here is the source code without line numbers to cut&paste from:
@include VectorCreate.cpp

*/ /*!
@file VectorFunctions.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno VectorFunctions.cpp

This example program writes an ImageFile containing a VectorNode that contains two IntegerNodes.  The VectorNode is interrogated is several ways.
See the HelloWorld.cpp example for discussion of the use of include files, constructing an ImageFile, and the try/catch block to handle exceptions.

In <b>source lines 16-19</b> a homogeneous VectorNode (can only contain children with identical types) is created, attached to the root of the ImageFile, and two IntegerNodes are appended.
The number of children is printed out in <b>source line 21</b>.
In <b>source lines 22-29</b>, a for loop iterates over the children, downcasting them to IntegerNode handles, and then prints out some information about each.
<b>Source lines 31 and 33</b> check to see whether a particular child is defined, but using a ustring element name rather than an integer index.
<b>Source lines 37-40</b> illustrates the downcasting of the VectorNode itself.

The following console output is produced:
@includelineno VectorFunctions.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno VectorFunctions.xml

Here is the source code without line numbers to cut&paste from:
@include VectorFunctions.cpp

*/ /*!
@file IntegerCreate.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno IntegerCreate.cpp

This example program writes an ImageFile containing 4 IntegerNodes.
It then prints out some basic information about the state of each IntegerNode.
See the HelloWorld.cpp example for discussion of the use of include files, constructing an ImageFile, and the try/catch block to handle exceptions.

<b>Source lines 35-38</b> construct the 4 IntegerNodes and attach them with distinct element names to the root node.
In <b>source line 35</b>, the IntegerNode is constructed with a default value, default minimum, and default maximum.
The defaults are specified in the E57Foundation.h include file.
The default values create a zero value IntegerNode with minimum/maximum range set to the largest values that can be represented by a signed 64 bit number (which is the underlying representation in the reference implementation of the E57 Foundation API).
<b>Source line 36</b> attaches an IntegerNode with a given value but default min/max.
<b>Source line 37</b> creates a IntegerNode that can represent all values of an unsigned 10 bit number.
<b>Source line 38</b> creates a IntegerNode that can represent all values of an signed 8 bit number.
The minimum/maximum specified don't have to be representable by an integral number of bits (e.g. minimum=1, maximum=100 is OK).
In <b>source lines 37-38</b>, if the specified value had been chosen outside the given minimum/maximum bounds, an exception would have been thrown (since the ASTM spec requires the value be within the bounds).

Each IntegerNode is then fetched by absolute path name in <b>source line 17</b>.
The call to StructureNode::get(const ustring&) const in <b>source line 17</b> can throw an exception (::E57_ERROR_PATH_UNDEFINED) if the path name is not defined.
<b>Source line 16</b> checks that the path name is actually defined before fetching the child node.
The return type of the StructureNode::get(const ustring&) const function is a generic Node handle (because containers can hold any type of node, not just IntegerNode).
A test on <b>source line 18</b> makes sure we aren't surprised by an exception in <b>source line 19</b>, where the generic Node handle is downcast to a handle to IntegerNode.
This downcast could have been specified in three other equivalent forms: <tt>"IntegerNode iNode(root.get(pathName));"</tt> or <tt>"IntegerNode iNode = IntegerNode(root.get(pathName));"</tt>, <tt>"IntegerNode iNode = (IntegerNode)root.get(pathName);"</tt>, resulting in exactly the same function call.
The static_cast form used in the code makes it clear that a type conversion is being performed.
It is not possible for the iNode variable to refer to any other type of node.
If the downcast could not be performed, an exception would be thrown.

Once we have iNode handle in <b>source line 19</b>, we can then call IntegerNode specific functions (in <b>source lines 20-22</b>) to print out the state of the IntegerNode.
The functions in <b>source lines 20-22</b> can be called even though the ImageFile was opened in write mode.
All read functions for objects stored in a write-mode ImageFile work correctly.
The are some functions that are accessible with a generic Node handle (for example Node::pathName is called in <b>source line 24</b>).
Note that in <b>output listing lines 3-4 and 7-8</b> the minimum and maximum are very large numbers which are the defaults if they aren't explicitly specified.
Also note that on <b>XML line 4</b>, the IntegerNode value, minimum, and maximum aren't given since the ASTM E57 standard specifies default values that match those specified in the API header file.
Only in <b>XML line 6</b> is it necessary to list all three numbers for the IntegerNode.

The following console output is produced:
The following console output is produced:
@includelineno IntegerCreate.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno IntegerCreate.xml

Here is the source code without line numbers to cut&paste from:
@include IntegerCreate.cpp

*/ /*!
@file ScaledIntegerCreate.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno ScaledIntegerCreate.cpp

This example program writes an ImageFile containing 5 ScaledIntegerNodes.
It then prints out some basic information about the state of each ScaledIntegerNode.
See the HelloWorld.cpp example for discussion of the use of include files, constructing an ImageFile, and the try/catch block to handle exceptions.
Also see discussion in the IntegerCreate.cpp example concerning downcasting.

<b>Source lines 38-42</b> illustrate the use of the default arguments in the ScaledIntegerNode constructor.
It only make sense to use a ScaledIntegerNode object when the scale or offset is specified explicitly as in <b>source lines 41-42</b> (otherwise the representation domain is the same as an IntegerNode element).

In <b>source lines 40-42</b>, if the specified raw value had been chosen outside the given minimum/maximum bounds, an exception would have been thrown (since the ASTM spec requires the value be within the bounds).

Note that in <b>output listing lines 23-24</b>, the scaled value is 0.001 times the size of the raw value (using the specified scale factor 0.001).
Also in <b>output line 31</b>, an additional offset of 100.0 has been added.

The following console output is produced:
@includelineno ScaledIntegerCreate.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno ScaledIntegerCreate.xml

Here is the source code without line numbers to cut&paste from:
@include ScaledIntegerCreate.cpp

*/ /*!
@file FloatCreate.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno FloatCreate.cpp

This example program writes an ImageFile containing 7 FloatNodes.
It then prints out some basic information about the state of each FloatNode.
See the HelloWorld.cpp example for discussion of the use of include files, constructing an ImageFile, and the try/catch block to handle exceptions.
Also see discussion in the IntegerCreate.cpp example concerning downcasting.

FloatNodes come in two different precisions: E57_SINGLE and E57_DOUBLE, that are 32 bit or 64 bit IEEE encoding respectively.
<b>Source lines 36-42</b> illustrate the use of the default arguments in the FloatNode constructor.
<b>Source line 36</b> specifies a 0.0 E57_DOUBLE, with largest possible double precision bounds.
Because there is only one FloatNode constructor, <b>source lines 37-38</b> both produce an E57_DOUBLE (the type of the second argument in <b>source line 37</b> is promoted to double precision by the compiler).
In <b>source lines 39-40</b>, the explicit use of the E57_SINGLE argument causes both FloatNodes to have minimum/maximum bounds set to the min/max representable by a single precision IEEE floating point number (see <b>output listing lines 19-20 and 24-25</b>).
<b>Source line 41</b> has same effect as <b>source lines 37-38</b>.
<b>Source line 42</b> produces a FloatNode with all three value/minimum/maximum numbers specified explicitly.
Because of the potential confusion, it is recommended that form in <b>source line 37</b> be avoided.

In <b>source line 42</b>, if the specified value had been chosen outside the given minimum/maximum bounds, an exception would have been thrown (since the ASTM spec requires the value be within the bounds).

On the <b>XML line 5</b>, the number is different than <b>XML line 6</b>, because of the conversion performed by the compiler on <b>source line 37</b>.
Also note that <b>XML lines 4-9</b> don't have to explicitly specify the minimum/maximum attributes since the API default values match those given in the ASTM spec.

The following console output is produced:
@includelineno FloatCreate.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno FloatCreate.xml

Here is the source code without line numbers to cut&paste from:
@include FloatCreate.cpp

*/ /*!
@file StringCreate.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno StringCreate.cpp

This example program writes an ImageFile containing 3 StringNodes.
It then prints out the value of each StringNode.
See the HelloWorld.cpp example for discussion of the use of include files, constructing an ImageFile, and the try/catch block to handle exceptions.
Also see discussion in the IntegerCreate.cpp example concerning downcasting.

<b>Source lines 33-35</b> illustrate the use of the default arguments in the StringNode constructor.
In <b>source line 33</b>, an empty string is created.
In <b>source line 34</b>, a 9 character string is created.
In <b>source line 35</b>, a 15 character string is created that has a problematic "]]>" substring embedded in it.
The ASTM E57 standard specifies special handling for strings that contain a "]]>" substring.
<b>XML line 6</b> shows how the string is encoded in XML.
This is handled automatically by the API implementation, without intervention by the API user.

The following console output is produced:
@includelineno StringCreate.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno StringCreate.xml

Here is the source code without line numbers to cut&paste from:
@include StringCreate.cpp

*/ /*!
@file BlobCreate.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno BlobCreate.cpp

This example program writes an ImageFile containing a BlobNode, then reads it back and prints out the BlobNode contents.
See the HelloWorld.cpp example for discussion of the use of include files, constructing an ImageFile, and the try/catch block to handle exceptions.

In <b>source line 18</b>, an empty 10 byte long BlobNode is created in the ImageFile.
The 10 bytes are initially zero, but this example overwrites the zeroes with some given values from a buffer.
The writing is performed in two pieces of 5 bytes each on <b>source lines 22-23</b>.

The ImageFile is closed, and then reopened for reading on <b>source line 32</b>.
A handle to the BlobNode is fetched on <b>source line 35</b> (with no checking since this program wrote the file).
The byte length (10) of the BlobNode is printed out in <b>source line 37</b>.
The for loop on <b>source lines 38-42</b> reads the blob back one byte at a time and prints the values.
Production code would, of course, read in much larger blocks, or perhaps a single, full length read into an allocated buffer.
Since blobs are a byte sequence, they do not suffer from "endian-ness" (dependency on byte ordering of the host CPU).
The BlobNode vales are stored in the binary section of the E57 file, and hence don't appear in the XML listing below.

The following console output is produced:
@includelineno BlobCreate.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno BlobCreate.xml

Here is the source code without line numbers to cut&paste from:
@include BlobCreate.cpp

*/ /*!
@file CompressedVectorCreate.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno CompressedVectorCreate.cpp

This example program writes a CompressedVectorNode in an ImageFile from three separate arrays, each containing 4 double coordinates.
The CompressedVectorNode is then read back into a single array of structures.
See the HelloWorld.cpp example for discussion of the use of include files, constructing an ImageFile, and the try/catch block to handle exceptions.

In <b>source lines 20-21</b>, a new ImageFile is opened for writing, and its predefined root node is fetched.
In <b>source lines 23-26</b>, a @c prototype structure is created that describes the fields that will be stored in each record in the CompressedVectorNode.
Each record will consist of 3 double precision floating-point numbers, with element names @c cartesianX, @c cartesianY, and @c cartesianZ.

<b>Source line 28</b> creates an empty heterogeneous VectorNode whose handle is stored in variable name @c codecs.
A codec (coder/decoder) is a pair of algorithms that copy the data between main memory and the data file.
The coder/decoder algorithms are implemented internally in an E57 Foundation Implementation.
The coder is utilized during the writing of an E57 file, and the decoder is utilized during the reading of an E57 file.
A coder algorithm gets a data item (a double in this example) from a memory buffer in the writing program, perhaps does some processing on it to make it smaller, and then stores the result in the disk file.
A decoder algorithm gets some data from the previously written disk file, undoes any processing that the coder did, and stores the reconstituted data into a memory buffer in the reading program.
Currently there is only one codec option (bitPackCodec) in the Reference Implementation, and it is the default option if no codecs are specified for a field in the record.
So an empty @c codecs VectorNode requests the bitPackCodec for each of the three fields in the record.

Technically, there are four elements in the prototype tree: three FloatNodes and a StructureNode which contains them.
However, container elements in the @c prototype tree do not need to be encoded in the binary section of the E57 file, so they don't need a codec specified for them.
The values stored in the three FloatNodes are all zero (that's what the default to in the constructors on <b>source lines 24-26</b>).
In a prototype, however, the values are ignored.
It is the type of the node and any specified limits on what value can be stored that are important.
In the example, the type is FloatNode, the precision is E57_DOUBLE, and the default limits are set to the smallest and largest values possible in a double precision float (so effectively there are no limits to the value that can be stored).
Because no compression is used in the coder, each record will require 3*64=192 bits of storage in the file.

<b>source line 30</b> creates the CompressedVectorNode using the two trees @c prototype and @c codecs.
The CompressedVectorNode @c cv is attached into the tree of the ImageFile, under the path name "/points".
The two trees @c prototype and @c codecs don't contain any data values and aren't connected into the data tree of an ImageFile in the same way as nodes that do contain values.
They function more like the arguments to the constructors of other Node types (e.g. the @c precision of FloatNode, or the byteCount of BlobNode).
They don't get path names.
However you can get them back by fetching the CompressedVectorNode by its path name, then call CompressedVectorNode::prototype or CompressedVectorNode::codecs.

Three built-in C++ arrays are specified in <b>source lines 34-36</b>, each holding four coordinates.
Although it doesn't matter in this example, the arrays are declared @c static, which means they are not stored on the stack.
Storing large built-in arrays on the stack risks causing a stack overflow.
In <b>source lines 37-40</b>, a SourceDestBuffer object is created that describes each array, and a vector of the three SourceDestBuffers is created.
The vector function @b push_back appends the SourceDestBuffers one-at-a-time to the vector.
Constructing a three element vector and assigning the SourceDestBuffer to each element (e.g. sourceBuffers[0] = SourceDestBuffer(...)) won't work because SourceDestBuffer has no default constructor (it is impossible to make the equivalent of a NULL handle).
In the construction of each SourceDestBuffer, the address of the buffer is given, along with the number of elements in the buffer, and the pathname in the prototype that identifies the field of the record that will get the values stored in the buffer.
For writers, all the fields specified in the prototype must be written at one time.
The writer cannot write each field separately.
Readers may read any combination of defined fields that suit them.

The types of memory buffers for both writer and reader in this example program match the representation specified in the prototype.
Therefore no representation conversions will be required during writing or reading.
If there were conversions needed, they would be specified with additional arguments during the construction of the SourceDestBuffers.

The local block in <b>source lines 42-46</b> will create an iterator that can write blocks of data into the CompressedVector.
In this example only a single block is written.
A C++ local block (the nested {}) is used to limit the scope of the iterator variable @c writer.
It is good practice to control the scope of reader/writer iterators, so that you can control their lifetimes.
In this example, the local block is not essential.

On <b>source line 43</b>, the CompressedVectorWriter iterator object is created by specifying which buffers the iterator will transfer data from.
The creation of the CompressedVectorWriter doesn't perform the first write.
The first write is requested explicitly as in <b>source line 44</b>.
In programming, iterators often process one item in a collection at a time.
In the Foundation API, however, for efficiency reasons the data should be processed in blocks of 100s or 1000s of points at a time.
The specified buffers can be refilled after they are written, and the iterator can be called for a second write with just the number of points to write (in <b>source line 44</b>), or a different buffers can be filled and handed to the iterator using an overloaded write function that takes a new vector of SourceDestBuffers.

In <b>source line 45</b>, the CompressedVectorWriter::close must be explicitly to signal the end of the write.
This call is required for much the same reason as the ImageFile::close is required: communicating error conditions from a destructor using exceptions is impossible.
If CompressedVectorWriter::close hadn't been called, the points written using the CompressedVectorWriter would have been discarded.

In <b>source lines 55-56</b>, the E57 file is reopened and the root node is fetched.
In <b>source line 58</b> the CompressedVectorNode is fetched by name, and downcast to the type that it is known to be (without checking).
Since this program just wrote the file, the reader routine knows that the "/points" CompressedVectorNode exists and that the prototype has three elements in it that store double precision floating point numbers.
Readers that have no guarantee of the layout of the file must consult the prototype to determine what fields they recognize and what memory representation types are appropriate.
Often the number of possibilities are constrained by the ASTM E57 format standard or by the documentation of an extension.

In <b>source lines 61-68</b>, the SourceDestBuffer objects that describe the memory buffers that will receive the data are created.
In contrast to the writer, the organization of the XYZ coordinates is different in the reader routine.
The XYZ values are no longer stored in separate arrays.
They are stored as a single array of @c XYZStruct.
This is handled by using the stride argument in the SourceDestBuffer constructor.
Three SourceDestBuffers are still needed, but stride value is set to the size of the @c XYZStruct.
When the CompressedVectorReader iterator writes a series of @c x values into the buffer, instead of storing them contiguously, the iterator advances by adding the stride value to the output pointer, thereby skipping over the intervening @c yz fields to the next @c x field.
Note that in <b>source line 63</b> the address of the buffer passed to the SourceDestBuffer constructor is not the start of the whole array, but the address of the first x (the same is true for the y, and z fields).

In <b>source line 72</b>, the CompressedVectorReader iterator is created.
Unlike the writer case, the reader does not have to request all the defined fields in the read.
In the @c while loop on <b>source lines 76-83</b>, blocks of records are read until the read function returns 0.
Alternatively, the number of records defined could have been fetched using CompressedVectorNode::childCount.
Like the CompressedVectorWriter, the CompressedVectorReader is explicitly closed in <b>source line 86</b>.

Note that the FloatNode values written in the file do not appear in the XML listing, as they are stored in the binary section of the file that is not printed by the utility (E57xmldump.exe) that generated the listing.
On <b>XML line 4</b>, you can see the @c fileOffset XML attribute that indicates the binary section that holds the CompressedVectorNode values starts at the physical offset of 40 (decimal) in the disk file.

The following console output is produced:
@includelineno CompressedVectorCreate.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno CompressedVectorCreate.xml

Here is the source code without line numbers to cut&paste from:
@include CompressedVectorCreate.cpp

*/ /*!
@file SourceDestBufferFunctions.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno SourceDestBufferFunctions.cpp

This example program illustrates the state interrogation functions for SourceDestBuffer, CompressedVectorWriter, and CompressedVectorReader.
A very simple CompressedVectorNode is written and then read back in.
See the HelloWorld.cpp example for discussion of the use of include files, constructing an ImageFile, and the try/catch block to handle exceptions.
See CompressedVectorCreate.cpp example for discussion about the writing/reading a CompressedVectorNode.

ImageFile::isWritable is called on <b>source lines 16 and 70</b>.
In the CompressedVectorWriter nested block on <b>source lines 30-47</b>, ImageFile::writerCount is called before, during, and after the use of the CompressedVectorWriter object to show how many writers a open.
Also CompressedVectorWriter::isOpen is called three times to show is behavior after close is called.

<b>Source lines 50-55</b> show the use of the functions that interrogate the state of a SourceDestBuffer.
Note in <b>output lines 17-19</b>, the default values for @c doConversion and @c doScaling are false, and the default value for @c stride with a @c double buffer is sizeof(double) == 8.

In the reader section on <b>source lines 68-103</b>, similar printouts are produced for ImageFile::readerCount, and CompressedVectorReader::isOpen.

The following console output is produced:
@includelineno SourceDestBufferFunctions.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno SourceDestBufferFunctions.xml

Here is the source code without line numbers to cut&paste from:
@include SourceDestBufferFunctions.cpp

*/ /*!
@file SourceDestBufferNumericCreate.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno SourceDestBufferNumericCreate.cpp

This example program illustrates the writing and reading of numbers (IntegerNode, ScaledIntegerNode, and FloatNode) in a CompressedVectorNode.
See the HelloWorld.cpp example for discussion of the use of include files, constructing an ImageFile, and the try/catch block to handle exceptions.
See CompressedVectorCreate.cpp example for discussion about the writing/reading a CompressedVectorNode.

<b>Source lines 18-27</b> create a CompressedVectorNode prototype with eight ScaledIntegerNodes and two FloatNodes.
The ScaledIntegerNode rawValues are constrained to be in interval [-10000, 10000], and are scaled by a factor of 1/1000 with no offset.
One FloatNode is a double precision and the other is single precision, both with no constrain on the value.

<b>Source lines 34-53</b> prepare a vector of eight SourceDestBuffers holding integer of various numbers of bytes and signed/unsigned combinations, as well as two SourceDestBuffers of single and double precision floating point numbers.
Note that the buffer declared in <b>source line 40</b>, @c i64, is read twice: once with no scaling requested (the default value on <b>source line 50</b>), and once with scaling requested (on <b>source line 51</b>).

The CompressedVectorNode is written in <b>source lines 56-59</b>.
The file is then reopened on <b>source line 69</b>.
The CompressedVectorNode is then fetched by name (with no checking) in <b>source line 72</b>.
In <b>source line 78</b>, the prototype is fetched from the CompressedVectorNode (with a little checking on <b>source lines 76-77</b>).

A for loop on <b>source line 84</b> iterates through the prototype children, and reads each field twice, printing the results.
The first read is with no scaling requested in <b>source line 93</b>.
The second read is with scaling requested in <b>source line 111</b>.
Note on both reads, @c doConversion is true, which requests that the file representation be converted automatically to the memory buffer representation.
This is necessary, since the read memory buffer is a @c double, and eight of the ten children are ScaledIntegerNodes.
Requesting conversion is not necessary in the case of the two FloatNode children, but doesn't hurt.

The following observations can be made about the results printed in the output listing below:

The seven ScaledIntegerNodes that were written without any scaling all work about the same (in <b>output lines 5-60</b>).
The raw value is stored in the file, and is read back the same.
The scaled read produces a scaled value that is multiplied by 0.001.

The ScaledIntegerNode that was written with scaling requested, has a raw value that is 1000 times larger than the number stored in the memory buffer (in <b>output lines 77-80</b>).
This is because "scaling" for a writer means dividing by the @c scale factor before storing the result.
Note that the raw value for the last point of the buffer where write scaling was requested had a value of 10000, which was just at the edge of the maximum allowed value.
If a slightly larger value was written with scaling requested in this example, an E57Exception with an errorCode of E57_ERROR_VALUE_OUT_OF_BOUNDS would have been thrown.
Whether you perform the scaling yourself, or request the E57 Foundation to do it, you should make sure that result is within the declared bounds.
This is necessary because the coding schemes that store the representation in the file cannot (and will not) reliably store any value outside of the declared [minimum, maximum] interval.

The scaling options have no effect on the FloatNodes, as reflected by the <b>output lines 79-93</b>.

The following console output is produced:
@includelineno SourceDestBufferNumericCreate.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno SourceDestBufferNumericCreate.xml

Here is the source code without line numbers to cut&paste from:
@include SourceDestBufferNumericCreate.cpp

*/ /*!
@file SourceDestBufferStringCreate.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno SourceDestBufferStringCreate.cpp

This example program illustrates the writing and reading of StringNodes in a CompressedVectorNode.
See the HelloWorld.cpp example for discussion of the use of include files, constructing an ImageFile, and the try/catch block to handle exceptions.
See CompressedVectorCreate.cpp example for discussion about the writing/reading a CompressedVectorNode.
See SourceDestBufferNumericCreate.cpp example for discussion about writing numbers in a CompressedVectorNode.

It is envisioned that for most uses of the E57 format, the fields of a CompressedVectorNode record will be entirely numeric.
However it is possible to store Unicode strings in the record of a CompressedVectorNode (i.e. store strings in the compressed section of an E57 file).
Writing and reading strings in a CompressedVectorNode are a little different than numbers: their values are not a known fixed width.
Rather than using built-in C++ arrays as memory buffers to hold numbers, string use vector<ustring> buffers.
The vector<ustring> has a fixed number of entries, just like the C++ arrays from numbers, but the length of each ustring in the vector can be different (i.e. sbuf[0].length() can be different than sbuf[1].length()).

<b>Source lines 12-21</b> create a CompressedVectorNode that has a record that has a single string element @c s.
<b>Source lines 24-28</b> create a SourceDestBuffer that represents a two element vector that holds two ustrings of different lengths.
Note that the third argument in the SourceDestBuffer constructor on <b>source line 28</b>, is the address of the vector to use, rather than the vector itself.
<b>Source lines 31-33</b> write the 2 strings to the CompressedVectorNode as a block.

In <b>source lines 43-46</b>, the E57 file is reopened for reading, and the CompressedVectorNode is fetched.
An vector of strings is constructed in <b>source line 49</b> which is large enough to hold all the strings from all the records defined (which is two records in this example), and a corresponding SourceDestBuffer is created in <b>source line 50</b>.
The strings are read in one gulp on <b>source line 55</b>.
The string values are printed on <b>source lines 58-59</b>.
Repeated calls to CompressedVectorReader::read with a fixed length vector would be preferable in working code, as the number of records in a CompressedVectorNode is unbounded.
Even with fetching a fixed number of strings on each read call, the amount of memory required is unknown because the strings read can be of any length.

Note that the string values written in the file do not appear in the XML listing, as they are stored in the binary section of the file that is not printed by the utility (E57xmldump.exe) that generated the listing.
On <b>XML line 4</b>, you can see the @c fileOffset XML attribute that indicates the binary section that holds the CompressedVectorNode values starts at the physical offset of 40 (decimal) in the disk file.

The following console output is produced:
@includelineno SourceDestBufferStringCreate.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno SourceDestBufferStringCreate.xml

Here is the source code without line numbers to cut&paste from:
@include SourceDestBufferStringCreate.cpp

*/ /*!
@file E57ExceptionFunctions.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno E57ExceptionFunctions.cpp

This example program demonstrates several functions for extracting information out of an E57Exception object thrown by the API.
See the HelloWorld.cpp example for discussion of the use of include files, constructing an ImageFile, and the try/catch block to handle exceptions.

<b>Source line 13</b> attempts to read a file that doesn't exist, hence causing an E57Exception to be thrown with an errorCode of E57_ERROR_OPEN_FAILED.
Because of the @c throw, <b>source line 14</b> doesn't execute, and execution resumes on <b>source line 16</b>.
<b>Source lines 16-23</b> extract various fields from the E57Exception and print them on the console.
In <b>output line 3</b>, the context string can have some very useful values of variables near where the exception was thrown.
These variable values can be useful in debugging.
The context string format might be useful for a programmer, but not necessarily useful for an end-user of the software.
<b>Output lines 5-7</b> show information about where the exception was thrown in the implementation internal source code.

<b>Source line 26</b> uses a single E57Exception function, E57Exception::report, that prints out all the information to the console shown on <b>output lines 9-15</b>, as well as the source location where the exception @c catch is located in the user's source.
The format of <b>output lines 14-15</b> is chosen so that smart text editors (e.g. GNU emacs) can interpret the lines as error messages and create an automatic link, that when clicked on, opens the given file at the line number there the exception was thrown, or caught.
Different editors may require a different formatting of the lines to create the clickable link.

<b>Source lines 29-30</b> illustrate the printing of a custom error message based on the value of the error code.
The errorCode could also have been tested in a @c switch statement.

The following console output is produced:
@includelineno E57ExceptionFunctions.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno E57ExceptionFunctions.xml

Here is the source code without line numbers to cut&paste from:
@include E57ExceptionFunctions.cpp

*/ /*!
@file RawXML.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno RawXML.cpp

This example program writes a very small ImageFile, then uses the two XML utility functions to read the XML section of the file without opening it as an ImageFile in read-mode.

<b>Source lines 13-18</b> write a simple ImageFile on the disk.
In <b>source line 25</b> a E57Utilities object is created once, to avoid overhead of constructing it multiple times (which might be expensive in some API implementations, see E57Utilities::E57Utilities for more discussion).
<b>Source line 28</b> gets the logical length of the XML section in the file on disk.
The @c for loop on <b>source lines 31-40</b> repeatedly fetch buffers of the XML section.
In a production version, the buffer would be much larger than 8 characters.
The buffers are sent to the @c cout ostream on <b>source line 39</b>, with appropriate casts to keep the compiler happy.
The XML is not parsed, just read in blocks.
If the file is corrupted and has checksum errors, the raw XML utility functions will fail.
There are no E57 Fountation API functions to read a corrupt E57 file (a .e57 file with checksum errors).

The following console output is produced:
@includelineno RawXML.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno RawXML.xml

Here is the source code without line numbers to cut&paste from:
@include RawXML.cpp

*/ /*!
@file Versions.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno Versions.cpp

This example program demonstrates the use of a utility function to fetch version information from the underlying API implementation.

The getVersion function is a member function of an object that is created at run-time, by E57Utilities::E57Utilities.
The reason for this unusual invocation is explained in E57Utilities::E57Utilities.

Once E57Utilities is constructed on <b>source line 14</b>, the E57Utilities::getVersions function is called which returns three variables that are printed on <b>source lines 16-18</b>.

Once the ASTM draft standard is approved and the testing phase of the Reference Implementation is complete, astmMajor=1 and astmMinor=0 will be returned.

The following console output is produced:
@includelineno Versions.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno Versions.xml

Here is the source code without line numbers to cut&paste from:
@include Versions.cpp

*/ /*!
@file CheckInvariant.cpp
Also see listing at end of this page for source without line numbers (to cut&paste from).
@includelineno CheckInvariant.cpp

This example program demonstrates the use of the checkInvariant function of various API classes.
See the HelloWorld.cpp example for discussion of the use of include files, constructing an ImageFile, and the try/catch block to handle exceptions.

A class invariant is a collection of statements that are always true before and after a call to any member function of the class.
The checkInvariant functions are diagnostics that check an object and its access functions for consistency.
Each API call in this example, is followed (with an extra indentation) by checkInvariant calls to the object that was invoked and any objects used as arguments that might have been modified.
This example illustrates that it can become tedious to manually check each object for consistency.
A E57 Foundation Implementation may have a specially-built library version that can perform these checks on all objects used in API functions, before and after the call.
It may be useful to selectively call checkInvariant explicitly from user code when debugging a particular problem.

The following console output is produced:
@includelineno CheckInvariant.out

The XML section of the @c temp._e57 E57 file produced by this example program is as follows:
@includelineno CheckInvariant.xml

Here is the source code without line numbers to cut&paste from:
@include CheckInvariant.cpp
*/


/*!
@brief Check whether Node class invariant is true
@param   [in] doRecurse     If true, also check invariants of all children or sub-objects recursively.
@param   [in] doDowncast    If true, also check any invariants of the actual derived type in addition to the generic node invariants.
@details
This function checks at least the assertions in the documented class invariant description (see class reference page for this object).
Other internal invariants that are implementation-dependent may also be checked.
If any invariant clause is violated, an E57Exception with errorCode of E57_ERROR_INVARIANCE_VIOLATION is thrown.

Specifying doRecurse=true only makes sense if doDowncast=true is also specified (the generic Node has no way to access any children).
Checking the invariant recursively may be expensive if the tree is large, so should be used judiciously, in debug versions of the application.
@post    No visible state is modified.
@throw   ::E57_ERROR_INVARIANCE_VIOLATION or any other E57 ErrorCode
@see     CheckInvariant.cpp example, Class Invariant section in Node, IntegerNode::checkInvariant, ScaledIntegerNode::checkInvariant, FloatNode::checkInvariant, BlobNode::checkInvariant, StructureNode::checkInvariant, VectorNode::checkInvariant, CompressedVectorNode::checkInvariant
*/
void Node::checkInvariant(bool doRecurse, bool doDowncast) {
    ImageFile imf = destImageFile();

    // If destImageFile not open, can't test invariant (almost every call would throw)
    if (!imf.isOpen())
        return;

    // Parent attachment state is same as this attachment state
    if (isAttached() != parent().isAttached())
        throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    // Parent destination ImageFile is same as this
    if (imf != parent().destImageFile())
        throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    // If this is the ImageFile root node
    if (*this == imf.root()) {
        // Must be attached
        if (!isAttached())
           throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

        // Must be is a root node
        if(!isRoot())
           throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
    }

    // If this is a root node
    if (isRoot()) {
        // Absolute pathName is "/"
        if (pathName() != "/")
           throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

        // parent() returns this node
        if (*this != parent())
           throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
    } else {
        // Non-root can't be own parent
        if (*this == parent())
           throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

        // pathName is concatenation of parent pathName and this elementName
        if (parent().isRoot()) {
            if (pathName() != "/" + elementName())
                throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
        } else {
            if (pathName() != parent().pathName() + "/" + elementName())
                throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
        }

        // Non-root nodes must be children of either a VectorNode or StructureNode
        if (parent().type() == E57_VECTOR) {
            VectorNode v = static_cast<VectorNode>(parent());

            // Must be defined in parent VectorNode with this elementName
            if (!v.isDefined(elementName()))
                throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

            // Getting child of parent with this elementName must return this
            if (v.get(elementName()) != *this)
                throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
        } else if (parent().type() == E57_STRUCTURE) {
            StructureNode s = static_cast<StructureNode>(parent());

            // Must be defined in parent VectorNode with this elementName
            if (!s.isDefined(elementName()))
                throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

            // Getting child of parent with this elementName must return this
            if (s.get(elementName()) != *this)
                throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
        } else
            throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
    }

    // If this is attached
    if (isAttached()) {
        // Get root of this
        Node n = *this;
        while (!n.isRoot())
            n = n.parent();

        // If in tree of ImageFile (could be in a prototype instead)
        if (n == imf.root()) {
            // pathName must be defined
            if (!imf.root().isDefined(pathName()))
                throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

            // Getting by absolute pathName must be this
            if (imf.root().get(pathName()) != *this)
                throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
        }
    }

    // If requested, check invariants of derived types:
    if (doDowncast) {
        switch (type()) {
            case E57_STRUCTURE:         {StructureNode          s(*this);   s.checkInvariant(doRecurse, false); } break;
            case E57_VECTOR:            {VectorNode             v(*this);   v.checkInvariant(doRecurse, false); } break;
            case E57_COMPRESSED_VECTOR: {CompressedVectorNode   cv(*this);  cv.checkInvariant(doRecurse, false);} break;
            case E57_INTEGER:           {IntegerNode            i(*this);   i.checkInvariant(doRecurse, false); } break;
            case E57_SCALED_INTEGER:    {ScaledIntegerNode      si(*this);  si.checkInvariant(doRecurse, false);} break;
            case E57_FLOAT:             {FloatNode              f(*this);   f.checkInvariant(doRecurse, false); } break;
            case E57_STRING:            {StringNode             s(*this);   s.checkInvariant(doRecurse, false); } break;
            case E57_BLOB:              {BlobNode               b(*this);   b.checkInvariant(doRecurse, false); } break;
            default: break;
        }
    }
}

/*!
@class Node
@brief   Generic handle to any of the 8 types of E57 element objects.
@details
A Node is a generic handle to an underlying object that is any of the eight type of E57 element objects.
Each of the eight node types support the all the functions of the Node class.
A Node is a vertex in a tree (acyclic graph), which is a hierarchical organization of nodes.
At the top of the hierarchy is a single root Node.
If a Node is a container type (StructureNode, VectorNode, CompressedVectorNode) it may have child nodes.
The following are non-container type nodes (also known as terminal nodes): IntegerNode, ScaledIntegerNode, FloatNode, StringNode, BlobNode.
Terminal nodes store various types of values and cannot have children.
Each Node has an elementName, which is a string that uniquely identifies it within the children of its parent.
Children of a StructureNode have elementNames that are explicitly given by the API user.
Children of a VectorNode or CompressedVectorNode have element names that are string reorientations of the Node's positional index, starting at "0".
A path name is a sequence elementNames (divided by "/") that must be traversed to get from a Node to one of its descendents.

Data is organized in an E57 format file (an ImageFile) hierarchically.
Each ImageFile has a predefined root node that other nodes can be attached to as children (either directly or indirectly).
A Node can exist temporarily without being attached to an ImageFile, however the state will not be saved in the associated file, and the state will be lost if the program exits.

A handle to a generic Node may be safely be converted to and from a handle to the Node's true underlying type.
Since an attempt to convert a generic Node to a incorrect handle type will fail with an exception, the true type should be interrogated beforehand.

Due to the set-once design of the Foundation API, terminal nodes are immutable (i.e. their values and attributes can't change after creation).
Once a parent-child relationship has been established, it cannot be changed.

Only generic operations are available for a Node, to access more specific operations (e.g. StructureNode::childCount) the generic handle must be converted to the node type of the underlying object.
This conversion is done in a type-safe way using "downcasting" (see discussion below).

@section node_Downcasting Downcasting
The conversion from a general handle type to a specific handle type is called "downcasting".
Each of the 8 specific node types have a downcast function (see IntegerNode::IntegerNode(const Node&) for example).
If a downcast is requested to an incorrect type (e.g. taking a Node handle that is actually a FloatNode and trying to downcast it to a IntegerNode), an E57Exception is thrown with an ErrorCode of E57_ERROR_BAD_NODE_DOWNCAST.
Depending on the program design, throwing a bad downcast exception might be acceptable, if an element must be a specific type and no recovery is possible.
If a standard requires an element be one several types, then Node::type() should be used to interrogate the type in an @c if or @c switch statement.
Downcasting is "dangerous" (can fail with an exception) so the API requires the programmer to explicitly call the downcast functions rather than have the c++ compiler insert them automatically.

@section node_Upcasting Upcasting
The conversion of a specific node handle (e.g. IntegerNode) to a general Node handle is called "upcasting".
Each of the 8 specific node types have an upcast function (see IntegerNode::operator Node() for example).
Upcasting is "safe" (can't cause an exception) so the API allows the c++ compiler to insert them automatically.
Upcasting is useful if you have a specific node handle and want to call a function that takes a generic Node handle argument.
In this case, the function can be called with the specific handle and the compiler will automatically insert the upcast conversion.
This implicit conversion allows one function, with an argument of type Node, to handle operations that apply to all 8 types of nodes (e.g. StructureNode::set()).

@section node_invariant Class Invariant
A class invariant is a list of statements about an object that are always true before and after any operation on the object.
An invariant is useful for testing correct operation of an implementation.
Statements in an invariant can involve only externally visible state, or can refer to internal implementation-specific state that is not visible to the API user.
The following C++ code checks externally visible state for consistency and throws an exception if the invariant is violated:
@dontinclude E57Foundation.cpp
@skip begin Node::checkInvariant
@skip checkInvariant(
@until end Node::checkInvariant

@see     StructureNode, VectorNode, CompressedVectorNode, IntegerNode, ScaledIntegerNode, FloatNode, StringNode, BlobNode
*/

//! @brief Check whether StructureNode class invariant is true
//! @copydetails IntegerNode::checkInvariant()
void StructureNode::checkInvariant(bool doRecurse, bool doUpcast) {
    // If destImageFile not open, can't test invariant (almost every call would throw)
    if (!destImageFile().isOpen())
        return;

    // If requested, call Node::checkInvariant
    if (doUpcast)
        static_cast<Node>(*this).checkInvariant(false, false);

    // Check each child
    for (int64_t i = 0; i < childCount(); i++) {
        Node child = get(i);

        // If requested, check children recursively
        if (doRecurse)
            child.checkInvariant(doRecurse, true);

        // Child's parent must be this
        if (static_cast<Node>(*this) != child.parent())
           throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

        // Child's elementName must be defined
        if (!isDefined(child.elementName()))
           throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

        // Getting child by element name must yield same child
        Node n = get(child.elementName());
        if (n != child)
           throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
    }
}

//! @brief Check whether VectorNode class invariant is true
//! @copydetails IntegerNode::checkInvariant()
void VectorNode::checkInvariant(bool doRecurse, bool doUpcast) {
    // If destImageFile not open, can't test invariant (almost every call would throw)
    if (!destImageFile().isOpen())
        return;

    // If requested, call Node::checkInvariant
    if (doUpcast)
        static_cast<Node>(*this).checkInvariant(false, false);

    // Check each child
    for (int64_t i = 0; i < childCount(); i++) {
        Node child = get(i);

        // If requested, check children recursively
        if (doRecurse)
            child.checkInvariant(doRecurse, true);

        // Child's parent must be this
        if (static_cast<Node>(*this) != child.parent())
           throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

        // Child's elementName must be defined
        if (!isDefined(child.elementName()))
           throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

        // Getting child by element name must yield same child
        Node n = get(child.elementName());
        if (n != child)
           throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
    }
}

//! @brief Check whether CompressedVectorNode class invariant is true
//! @copydetails IntegerNode::checkInvariant()
void CompressedVectorNode::checkInvariant(bool doRecurse, bool doUpcast) {
    // If destImageFile not open, can't test invariant (almost every call would throw)
    if (!destImageFile().isOpen())
        return;

    // If requested, call Node::checkInvariant
    if (doUpcast)
        static_cast<Node>(*this).checkInvariant(false, false);

    // Check prototype is good Node
    prototype().checkInvariant(doRecurse);

    // prototype attached state not same as this attached state
    if (prototype().isAttached() != isAttached())
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    // prototype not root
    if (!prototype().isRoot())
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    // prototype dest ImageFile not same as this dest ImageFile
    if (prototype().destImageFile() != destImageFile())
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    // Check codecs is good Node
    codecs().checkInvariant(doRecurse);

    // codecs attached state not same as this attached state
    if (codecs().isAttached() != isAttached())
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    // codecs not root
    if (!codecs().isRoot())
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    // codecs dest ImageFile not same as this dest ImageFile
    if (codecs().destImageFile() != destImageFile())
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
}
/*!
@brief Check whether IntegerNode class invariant is true
@param   [in] doRecurse   If true, also check invariants of all children or sub-objects recursively.
@param   [in] doUpcast    If true, also check invariants of the generic Node class.
@details
This function checks at least the assertions in the documented class invariant description (see class reference page for this object).
Other internal invariants that are implementation-dependent may also be checked.
If any invariant clause is violated, an E57Exception with errorCode of E57_ERROR_INVARIANCE_VIOLATION is thrown.

Checking the invariant recursively may be expensive if the tree is large, so should be used judiciously, in debug versions of the application.
@post    No visible state is modified.
@throw   ::E57_ERROR_INVARIANCE_VIOLATION or any other E57 ErrorCode
@see     CheckInvariant.cpp example
*/
void IntegerNode::checkInvariant(bool /*doRecurse*/, bool doUpcast) {
    // If destImageFile not open, can't test invariant (almost every call would throw)
    if (!destImageFile().isOpen())
        return;

    // If requested, call Node::checkInvariant
    if (doUpcast)
        static_cast<Node>(*this).checkInvariant(false, false);

    if (value() < minimum() || value() > maximum())
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
}

//! @brief Check whether ScaledIntegerNode class invariant is true
//! @copydetails IntegerNode::checkInvariant()
void ScaledIntegerNode::checkInvariant(bool /*doRecurse*/, bool doUpcast) {
    // If destImageFile not open, can't test invariant (almost every call would throw)
    if (!destImageFile().isOpen())
        return;

    // If requested, call Node::checkInvariant
    if (doUpcast)
        static_cast<Node>(*this).checkInvariant(false, false);

    // If value is out of bounds
    if (rawValue() < minimum() || rawValue() > maximum())
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    // If scale is zero
    if (scale() == 0)
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    // If scaled value is not calculated correctly
    if (scaledValue() != rawValue() * scale() + offset())
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
}
//! @brief Check whether FloatNode class invariant is true
//! @copydetails IntegerNode::checkInvariant()
void FloatNode::checkInvariant(bool /*doRecurse*/, bool doUpcast) {
    // If destImageFile not open, can't test invariant (almost every call would throw)
    if (!destImageFile().isOpen())
        return;

    // If requested, call Node::checkInvariant
    if (doUpcast)
        static_cast<Node>(*this).checkInvariant(false, false);

    if (precision() == E57_SINGLE) {
        if (minimum() < E57_FLOAT_MIN || maximum() > E57_FLOAT_MAX)
           throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
    }

    // If value is out of bounds
    if (value() < minimum() || value() > maximum())
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
}
//! @brief Check whether StringNode class invariant is true
//! @copydetails IntegerNode::checkInvariant()
void StringNode::checkInvariant(bool /*doRecurse*/, bool doUpcast) {
    // If destImageFile not open, can't test invariant (almost every call would throw)
    if (!destImageFile().isOpen())
        return;

    // If requested, call Node::checkInvariant
    if (doUpcast)
        static_cast<Node>(*this).checkInvariant(false, false);
    /// ? check legal UTF-8
}
//! @brief Check whether BlobNode class invariant is true
//! @copydetails IntegerNode::checkInvariant()
void BlobNode::checkInvariant(bool /*doRecurse*/, bool doUpcast) {
    // If destImageFile not open, can't test invariant (almost every call would throw)
    if (!destImageFile().isOpen())
        return;

    // If requested, call Node::checkInvariant
    if (doUpcast)
        static_cast<Node>(*this).checkInvariant(false, false);

    if (byteCount() < 0)
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
}
/*!
@brief Check whether CompressedVectorReader class invariant is true
@param   [in] doRecurse   If true, also check invariants of all children or sub-objects recursively.
@details
This function checks at least the assertions in the documented class invariant description (see class reference page for this object).
Other internal invariants that are implementation-dependent may also be checked.
If any invariant clause is violated, an E57Exception with errorCode of E57_ERROR_INVARIANCE_VIOLATION is thrown.
@post    No visible state is modified.
@see     CheckInvariant.cpp example
*/
void CompressedVectorReader::checkInvariant(bool /*doRecurse*/) {
    // If this CompressedVectorReader is not open, can't test invariant (almost every call would throw)
    if (!isOpen())
        return;

    CompressedVectorNode cv = compressedVectorNode();
    ImageFile imf = cv.destImageFile();

    // If destImageFile not open, can't test invariant (almost every call would throw)
    if (!imf.isOpen())
        return;

    // Associated CompressedVectorNode must be attached to ImageFile
    if (!cv.isAttached())
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    // Dest ImageFile must have at least 1 reader (this one)
    if (imf.readerCount() < 1)
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    // Dest ImageFile can't have any writers
    if (imf.writerCount() != 0)
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
}

//! @brief Check whether CompressedVectorWriter class invariant is true
//! @copydetails CompressedVectorReader::checkInvariant
void CompressedVectorWriter::checkInvariant(bool /*doRecurse*/) {
    // If this CompressedVectorWriter is not open, can't test invariant (almost every call would throw)
    if (!isOpen())
        return;

    CompressedVectorNode cv = compressedVectorNode();
    ImageFile imf = cv.destImageFile();

    // If destImageFile not open, can't test invariant (almost every call would throw)
    if (!imf.isOpen())
        return;

    // Associated CompressedVectorNode must be attached to ImageFile
    if (!cv.isAttached())
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    // Dest ImageFile must be writable
    if (!imf.isWritable())
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    // Dest ImageFile must have exactly 1 writer (this one)
    if (imf.writerCount() != 1)
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    // Dest ImageFile can't have any readers
    if (imf.readerCount() != 0)
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
}

/*!
@brief Check whether ImageFile class invariant is true
@param   [in] doRecurse   If true, also check invariants of all children or sub-objects recursively.
@details
This function checks at least the assertions in the documented class invariant description (see class reference page for this object).
Other internal invariants that are implementation-dependent may also be checked.
If any invariant clause is violated, an E57Exception with errorCode of E57_ERROR_INVARIANCE_VIOLATION is thrown.

Checking the invariant recursively may be expensive if the tree is large, so should be used judiciously, in debug versions of the application.
@post    No visible state is modified.
@throw   ::E57_ERROR_INVARIANCE_VIOLATION or any other E57 ErrorCode
@see     CheckInvariant.cpp example, Node::checkInvariant
*/
void ImageFile::checkInvariant(bool doRecurse) const
{
    // If this ImageFile is not open, can't test invariant (almost every call would throw)
    if (!isOpen())
        return;

    // root() node must be a root node
    if (!root().isRoot())
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    // Can't have empty fileName
    if (fileName().empty())
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    int wCount = writerCount();
    int rCount = readerCount();

    // Can't have negative number of readers
    if (rCount < 0)
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    // Can't have negative number of writers
    if (wCount < 0)
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    // Can't have more than one writer
    if (1 < wCount)
       throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

    // If have writer
    if (wCount > 0) {
        // Must be in write-mode
        if (!isWritable())
           throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);

        // Can't have any readers
        if (rCount > 0)
           throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
    }

    // Extension prefixes and URIs are unique
    const size_t eCount = extensionsCount();
    for (size_t i = 0; i < eCount; i++) {
        for (size_t j = i+1; j < eCount; j++) {
            if (extensionsPrefix(i) == extensionsPrefix(j))
                throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
            if (extensionsUri(i) == extensionsUri(j))
                throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
        }
    }

    // Verify lookup functions are correct
    for (size_t i = 0; i < eCount; i++) {
        ustring goodPrefix = extensionsPrefix(i);
        ustring goodUri    = extensionsUri(i);
        ustring prefix, uri;
        if (!extensionsLookupPrefix(goodPrefix, uri))
            throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
        if (uri != goodUri)
            throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
        if (!extensionsLookupUri(goodUri, prefix))
            throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
        if (prefix != goodPrefix)
            throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
    }

    // If requested, check all objects "below" this one
    if (doRecurse)
        root().checkInvariant(doRecurse);
}

//! @brief Check whether SourceDestBuffer class invariant is true
void SourceDestBuffer::checkInvariant(bool /*doRecurse*/) const
{
    // Stride must be >= a memory type dependent value
    size_t min_stride = 0;
    switch (memoryRepresentation()) {
        case E57_INT8:      min_stride = 1; break;
        case E57_UINT8:     min_stride = 1; break;
        case E57_INT16:     min_stride = 2; break;
        case E57_UINT16:    min_stride = 2; break;
        case E57_INT32:     min_stride = 4; break;
        case E57_UINT32:    min_stride = 4; break;
        case E57_INT64:     min_stride = 8; break;
        case E57_BOOL:      min_stride = 1; break;
        case E57_REAL32:    min_stride = 4; break;
        case E57_REAL64:    min_stride = 8; break;
        case E57_USTRING:   min_stride = sizeof(ustring); break;
        default:
            throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
    }
    if (stride() < min_stride)
        throw E57_EXCEPTION1(E57_ERROR_INVARIANCE_VIOLATION);
}

/*!
@brief   Return the NodeType of a generic Node.
@details This function allows the actual node type to be interrogated before upcasting the handle to the actual node type (see Upcasting and Dowcasting section in Node).
@return  The NodeType of a generic Node, which may be one of the following NodeType enumeration values:
::E57_STRUCTURE, ::E57_VECTOR, ::E57_COMPRESSED_VECTOR, ::E57_INTEGER, ::E57_SCALED_INTEGER, ::E57_FLOAT, ::E57_STRING, ::E57_BLOB.
@post    No visible state is modified.
@see     NodeFunctions.cpp example, NodeType, StructureCreate.cpp example, upcast/dowcast discussion in Node
*/
NodeType Node::type() const
{
    return impl_->type();
}

/*!
@brief   Is this a root node.
@details A root node has itself as a parent (it is not a child of any node).
Newly constructed nodes (before they are inserted into an ImageFile tree) start out as root nodes.
It is possible to temporarily create small trees that are unattached to any ImageFile.
In these temporary trees, the top-most node will be a root node.
After the tree is attached to the ImageFile tree, the only root node will be the pre-created one of the ImageTree (the one returned by ImageFile::root).
The concept of @em attachement is slightly larger than that of the parent-child relationship (see Node::isAttached and CompressedVectorNode::CompressedVectorNode for more details).
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  true if this node is a root node.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     NodeFunctions.cpp example, Node::parent, Node::isAttached, CompressedVectorNode::CompressedVectorNode
*/
bool Node::isRoot() const
{
    return impl_->isRoot();
}

/*!
@brief   Return parent of node, or self if a root node.
@details Nodes are organized into trees (acyclic graphs) with a distinguished node (the "top-most" node) called the root node.
A parent-child relationship is established between nodes to form a tree.
Nodes can have zero or one parent.
Nodes with zero parents are called root nodes.
In the API, if a node has zero parents it is represented by having itself as a parent.
Due to the set-once design of the API, a parent-child relationship cannot be modified once established.
A child node can be any of the 8 node types, but a parent node can only be one of the 3 container node types (E57_STRUCTURE, E57_VECTOR, and E57_COMPRESSED_VECTOR).
Each parent-child link has a string name (the elementName) associated with it (See Node::elementName for more details).
More than one tree can be formed at any given time.
Typically small trees are temporarily constructed before attachement to an ImageFile so that they will be written to the disk.

@b Warning: user algorithms that use this function to walk the tree must take care to handle the case where a node is its own parent (it is a root node).
Use Node::isRoot to avoid infinite loops or infinite recursion.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  A smart Node handle referencing the parent node or this node if is a root node.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     NodeFunctions.cpp example, Node::isRoot, Node::isAttached, CompressedVectorNode::CompressedVectorNode, Node::elementName
*/
Node Node::parent() const
{
    return Node(impl_->parent());
}

/*!
@brief   Get absolute pathname of node.
@details
Nodes are organized into trees (acyclic graphs) by a parent-child relationship between nodes.
Each parent-child relationship has an associated elementName string that is unique for a given parent.
Any node in a given tree can be identified by a sequence of elementNames of how to get to the node from the root of the tree.
An absolute pathname string that is formed by arranging this sequence of elementNames separated by the "/" character with a leading "/" prepended.

Some example absolute pathNames: "/data3D/0/points/153/cartesianX", "/data3D/0/points", "/cameraImages/1/pose/rotation/w", and "/".
These examples have probably been attached to an ImageFile.
Here is an example absolute pathName of a node in a pose tree that has not yet been attached to an ImageFile: "/pose/rotation/w".

A technical aside: the elementName of a root node does not appear in absolute pathnames, since the "path" is between the staring node (the root) and the ending node.
By convention, in this API, a root node has the empty string ("") as its elementName.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The absolute path name of the node.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     NodeFunctions.cpp example, Node::elementName, Node::parent, Node::isRoot
*/
ustring Node::pathName() const
{
    return impl_->pathName();
}

/*!
@brief   Get element name of node.
@details
The elementName is a string associated with each parent-child link between nodes.
For a given parent, the elementName uniquely identifies each of its children.
Thus, any node in a tree can be identified by a sequence of elementNames that form a path from the tree's root node (see Node::pathName for more details).

Three types of nodes (the container node types) can be parents: StructureNode, VectorNode, and CompressedVectorNode.
The children of a StructureNode are explicitly given unique elementNames when they are attached to the parent (using StructureNode::set).
The children of VectorNode and CompressedVectorNode are implicitly given elementNames based on their position in the list (starting at "0").
In a CompressedVectorNode, the elementName can become quite large: "1000000000" or more.
However in a CompressedVectorNode, the elementName string is not stored in the file and is deduced by the position of the child.

@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The element name of the node, or "" if a root node.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     NodeFunctions.cpp example, Node::pathName, Node::parent, Node::isRoot
*/
ustring Node::elementName() const
{
    return impl_->elementName();
}

/*!
@brief   Get the ImageFile that was declared as the destination for the node when it was created.
@details The first argument of the constructors of each of the 8 types of nodes is an ImageFile that indicates which ImageFile the node will eventually be attached to.
This function returns that constructor argument.
It is an error to attempt to attach the node to a different ImageFile.
However it is not an error to not attach the node to any ImageFile (it's just wasteful).
Use Node::isAttached to check if the node actually did get attached.
@post    No visible object state is modified.
@return  The ImageFile that was declared as the destination for the node when it was created.
@see     NodeFunctions.cpp example, Node::isAttached, StructureNode::StructureNode(), VectorNode::VectorNode(), CompressedVectorNode::CompressedVectorNode(), IntegerNode::IntegerNode(), ScaledIntegerNode::ScaledIntegerNode(), FloatNode::FloatNode(), StringNode::StringNode(), BlobNode::BlobNode()
*/
ImageFile Node::destImageFile() const
{
    return ImageFile(impl_->destImageFile());
}

/*!
@brief   Has node been attached into the tree of an ImageFile.
@details Nodes are attached into an ImageFile tree by inserting them as children (directly or indirectly) of the ImageFile's root node.
Nodes can also be attached to an ImageFile if they are used in the @c codecs or @c prototype trees of an CompressedVectorNode that is attached.
Attached nodes will be saved to disk when the ImageFile is closed, and restored when the ImageFile is read back in from disk.
Unattached nodes will not be saved to disk.
It is not recommended to create nodes that are not eventually attached to the ImageFile.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible object state is modified.
@return  @c true if node is child of (or in codecs or prototype of a child CompressedVectorNode of) the root node of an ImageFile.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     NodeFunctions.cpp example, Node::destImageFile, ImageFile::root
*/
bool Node::isAttached() const
{
    return impl_->isAttached();
}

/*!
@brief   Diagnostic function to print internal state of object to output stream in an indented format.
@param   [in] indent    Number of spaces to indent all the printed lines of this object.
@param   [in] os        Output stream to print on.
@details
All objects in the E57 Foundation API (with exception of E57Exception) support a dump() function.
These functions print out to the console a detailed listing of the internal state of objects.
The content of these printouts is not documented, and is really of interest only to implementation developers/maintainers or the really adventurous users.
In implementations of the API other than the Reference Implementation, the dump() functions may produce no output (although the functions should still be defined).
The output format may change from version to version.
@post    No visible object state is modified.
@throw   No E57Exceptions
@see     ImageFileDump.cpp example
*/
#ifdef E57_DEBUG
void Node::dump(int indent, std::ostream& os) const
{
    impl_->dump(indent, os);
}
#else
void Node::dump(int indent, std::ostream& os) const
{}
#endif

/*!
@brief   Test if two node handles refer to the same underlying node
@param   [in] n2        The node to compare this node with
@post    No visible object state is modified.
@return  @c true if node handles refer to the same underlying node.
@throw   No E57Exceptions
*/
bool Node::operator==(Node n2) const
{
    return(impl_ == n2.impl_);
}

/*!
@brief   Test if two node handles refer to different underlying nodes
@param   [in] n2        The node to compare this node with
@post    No visible object state is modified.
@return  @c true if node handles refer to different underlying nodes.
@throw   No E57Exceptions
*/
bool Node::operator!=(Node n2) const
{
    return(impl_ != n2.impl_);
}

//! @cond documentNonPublic   The following isn't part of the API, and isn't documented.
Node::Node(NodeImplSharedPtr ni)
: impl_(ni)
{}
//! @endcond

//=====================================================================================
/*!
@class StructureNode
@brief   An E57 element containing named child nodes.
@details
A StructureNode is a container of named child nodes, which may be any of the eight node types.
The children of a structure node must have unique elementNames.
Once a child node is set with a particular elementName, it may not be modified.

See Node class discussion for discussion of the common functions that StructureNode supports.
@section structurenode_invariant Class Invariant
A class invariant is a list of statements about an object that are always true before and after any operation on the object.
An invariant is useful for testing correct operation of an implementation.
Statements in an invariant can involve only externally visible state, or can refer to internal implementation-specific state that is not visible to the API user.
The following C++ code checks externally visible state for consistancy and throws an exception if the invariant is violated:
@dontinclude E57Format.cpp
@skip begin StructureNode::checkInvariant
@skip checkInvariant(
@until end StructureNode::checkInvariant

@see     Node
*/

/*!
@brief   Create an empty StructureNode.
@param   [in] destImageFile   The ImageFile where the new node will eventually be stored.
@details
A StructureNode is a container for collections of named E57 nodes.
The @a destImageFile indicates which ImageFile the StructureNode will eventually be attached to.
A node is attached to an ImageFile by adding it underneath the predefined root of the ImageFile (gotten from ImageFile::root).
It is not an error to fail to attach the StructureNode to the @a destImageFile.
It is an error to attempt to attach the StructureNode to a different ImageFile.
@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be true).
@pre     The @a destImageFile must have been opened in write mode (i.e. destImageFile.isWritable() must be true).
@return  A smart StructureNode handle referencing the underlying object.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     StructureCreate.cpp example, NodeFunctions.cpp example, Node
*/
StructureNode::StructureNode(ImageFile destImageFile)
: impl_(new StructureNodeImpl(destImageFile.impl()))
{
}

//! @brief   Is this a root node.
//! @copydetails Node::isRoot()
bool StructureNode::isRoot() const
{
    return impl_->isRoot();
}

//! @brief   Return parent of node, or self if a root node.
//! @copydetails Node::parent()
Node StructureNode::parent() const
{
    return Node(impl_->parent());
}

//! @brief   Get absolute pathname of node.
//! @copydetails Node::pathName()
ustring StructureNode::pathName() const
{
    return impl_->pathName();
}

//! @brief   Get elementName string, that identifies the node in its parent.
//! @copydetails Node::elementName()
ustring StructureNode::elementName() const
{
    return impl_->elementName();
}

//! @brief   Get the ImageFile that was declared as the destination for the node when it was created.
//! @copydetails Node::destImageFile()
ImageFile StructureNode::destImageFile() const
{
    return ImageFile(impl_->destImageFile());
}

//! @brief   Has node been attached into the tree of an ImageFile.
//! @copydetails Node::isAttached()
bool StructureNode::isAttached() const
{
    return impl_->isAttached();
}

/*!
@brief   Return number of child nodes contained by this StructureNode.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  Number of child nodes contained by this StructureNode.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     StructureCreate.cpp example, StructureNode::get(int64_t) const, StructureNode::set, VectorNode::childCount
*/
int64_t StructureNode::childCount() const
{
    return impl_->childCount();
}

/*!
@brief   Is the given pathName defined relative to this node.
@param   [in] pathName   The absolute pathname, or pathname relative to this object, to check.
@details
The @a pathName may be relative to this node, or absolute (starting with a "/").
The origin of the absolute path name is the root of the tree that contains this StructureNode.
If this StructureNode is not attached to an ImageFile, the @a pathName origin root will not the root node of an ImageFile.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  true if pathName is currently defined.
@throw   ::E57_ERROR_BAD_PATH_NAME
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     StructureCreate.cpp example, ImageFile::root, VectorNode::isDefined
*/
bool StructureNode::isDefined(const ustring& pathName) const
{
    return impl_->isDefined(pathName);
}

/*!
@brief   Get a child element by positional index.
@param   [in] index   The index of child element to get, starting at 0.
@details
The order of children is not specified, and may be different than order the children were added to the StructureNode.
The order of children may change if more children are added to the StructureNode.
@pre     0 <= @a index < childCount()
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  A smart Node handle referencing the child node.
@throw   ::E57_ERROR_CHILD_INDEX_OUT_OF_BOUNDS
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     StructureCreate.cpp example, StructureNode::childCount, StructureNode::get(const ustring&) const, VectorNode::get
*/
Node StructureNode::get(int64_t index) const
{
    return Node(impl_->get(index));
}

/*!
@brief   Get a child by path name.
@param   [in] pathName   The absolute pathname, or pathname relative to this object, of the object to get.
The @a pathName may be relative to this node, or absolute (starting with a "/").
The origin of the absolute path name is the root of the tree that contains this StructureNode.
If this StructureNode is not attached to an ImageFile, the @a pathName origin root will not the root node of an ImageFile.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@pre     The @a pathName must be defined (i.e. isDefined(pathName)).
@post    No visible state is modified.
@return  A smart Node handle referencing the child node.
@throw   ::E57_ERROR_BAD_PATH_NAME
@throw   ::E57_ERROR_PATH_UNDEFINED
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     StructureCreate.cpp example, StructureNode::get(int64_t) const
*/
Node StructureNode::get(const ustring& pathName) const
{
    return Node(impl_->get(pathName));
}

/*!
@brief   Add a new child at a given path
    @param   [in] pathName  The absolute pathname, or pathname relative to this object, that the child object @a n will be given.
@param   [in] n         The node to be added to the tree with given @a pathName.
@details
The @a pathName may be relative to this node, or absolute (starting with a "/").
The origin of the absolute path name is the root of the tree that contains this StructureNode.
If this StructureNode is not attached to an ImageFile, the @a pathName origin root will not the root node of an ImageFile.

The path name formed from all element names in @a pathName except the last must exist.
If the @a pathName identifies the child of a VectorNode, then the last element name in @a pathName must be numeric, and be equal to the childCount of that VectorNode (the request is equivalent to VectorNode::append).
The StructureNode must not be a descendent of a homogeneous VectorNode with more than one child.

The element naming grammar specified by the ASTM E57 format standard are not enforced in this function.
This would be very difficult to do dynamically, as some of the naming rules involve combinations of names.
@pre     The new child node @a n must be a root node (i.e. n.isRoot()).
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@pre     The associated destImageFile must have been opened in write mode (i.e. destImageFile().isWritable()).
@pre     The @a pathName must not already be defined (i.e. !isDefined(pathName)).
@pre     The associated destImageFile of this StructureNode and of @a n must be same (i.e. destImageFile() == n.destImageFile()).
@post    The @a pathName will be defined (i.e. isDefined(pathName)).
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_BAD_PATH_NAME
@throw   ::E57_ERROR_PATH_UNDEFINED
@throw   ::E57_ERROR_SET_TWICE
@throw   ::E57_ERROR_ALREADY_HAS_PARENT
@throw   ::E57_ERROR_DIFFERENT_DEST_IMAGEFILE
@throw   ::E57_ERROR_HOMOGENEOUS_VIOLATION
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     StructureCreate.cpp example, VectorNode::append
*/
void StructureNode::set(const ustring& pathName, const Node &n)
{
    impl_->set(pathName, n.impl(), false);
}

//! @brief   Diagnostic function to print internal state of object to output stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void StructureNode::dump(int indent, std::ostream& os) const
{
    impl_->dump(indent, os);
}
#else
void StructureNode::dump(int indent, std::ostream& os) const
{}
#endif

/*!
@brief   Upcast a StructureNode handle to a generic Node handle.
@details An upcast is always safe, and the compiler can automatically insert it for initializations of Node variables and Node function arguments.
@return  A smart Node handle referencing the underlying object.
@throw   No E57Exceptions.
@see     NodeFunctions.cpp example, StructureCreate.cpp example, explanation in Node, Node::type(), StructureNode(const Node&)
*/
StructureNode::operator Node() const
{
    /// Implicitly upcast from shared_ptr<StructureNodeImpl> to SharedNodeImplPtr and construct a Node object
    return Node(impl_);
}

/*!
@brief   Downcast a generic Node handle to a StructureNode handle.
@param   [in] n The generic handle to downcast.
@details The handle @a n must be for an underlying StructureNode, otherwise an exception is thrown.
In designs that need to avoid the exception, use Node::type() to determine the actual type of the @a n before downcasting.
This function must be explicitly called (c++ compiler cannot insert it automatically).
@return  A smart StructureNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_NODE_DOWNCAST
@see     NodeFunctions.cpp example, StructureCreate.cpp example, Node::type(), StructureNode::operator Node()
*/
StructureNode::StructureNode(const Node& n)
{
    /// Downcast from SharedNodeImplPtr to shared_ptr<StructureNodeImpl>
    shared_ptr<StructureNodeImpl> ni(dynamic_pointer_cast<StructureNodeImpl>(n.impl()));
    if (!ni)
        throw E57_EXCEPTION2(E57_ERROR_BAD_NODE_DOWNCAST, "nodeType=" + toString(n.type()));

    /// Set our shared_ptr to the downcast shared_ptr
    impl_ = ni;
}

//! @cond documentNonPublic   The following isn't part of the API, and isn't documented.
StructureNode::StructureNode(std::weak_ptr<ImageFileImpl> fileParent)
: impl_(new StructureNodeImpl(fileParent))
{}

StructureNode::StructureNode(shared_ptr<StructureNodeImpl> ni)
: impl_(ni)
{}
//! @endcond

//=====================================================================================
/*!
@class VectorNode
@brief   An E57 element containing ordered vector of child nodes.
@details
A VectorNode is a container of ordered child nodes.
The child nodes are automatically assigned an elementName, which is a string version of the positional index of the child starting at "0".
Child nodes may only be appended onto the end of a VectorNode.

A VectorNode that is created with a restriction that its children must have the same type is called a "homogeneous VectorNode".
A VectorNode without such a restriction is called a "heterogeneous VectorNode".

See Node class discussion for discussion of the common functions that StructureNode supports.
@section vectornode_invariant Class Invariant
A class invariant is a list of statements about an object that are always true before and after any operation on the object.
An invariant is useful for testing correct operation of an implementation.
Statements in an invariant can involve only externally visible state, or can refer to internal implementation-specific state that is not visible to the API user.
The following C++ code checks externally visible state for consistency and throws an exception if the invariant is violated:
@dontinclude E57Format.cpp
@skip begin VectorNode::checkInvariant
@skip checkInvariant(
@until end VectorNode::checkInvariant

@see     Node
*/

/*!
@brief   Create a new empty Vector node.
@param   [in] destImageFile         The ImageFile where the new node will eventually be stored.
@param   [in] allowHeteroChildren   Will child elements of differing types be allowed in this VectorNode.
@details
A VectorNode is a ordered container of E57 nodes.
The @a destImageFile indicates which ImageFile the VectorNode will eventually be attached to.
A node is attached to an ImageFile by adding it underneath the predefined root of the ImageFile (gotten from ImageFile::root).
It is not an error to fail to attach the VectorNode to the @a destImageFile.
It is an error to attempt to attach the VectorNode to a different ImageFile.

If @a allowHeteroChildren is false, then the children that are appended to the VectorNode must be identical in every visible characteristic except the stored values.
These visible characteristics include number of children (for StructureNode and VectorNode descendents), number of records/prototypes/codecs (for CompressedVectorNode), minimum/maximum attributes (for IntegerNode, ScaledIntegerNode, FloatNode), byteCount (for BlobNode), scale/offset (for ScaledIntegerNode), and all elementNames.
The enforcement of this homogeneity rule begins when the second child is appended to the VectorNode, thus it is not an error to modify a child of a homogeneous VectorNode containing only one child.

If @a allowHeteroChildren is true, then the types of the children of the VectorNode are completely unconstrained.
@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be true).
@pre     The @a destImageFile must have been opened in write mode (i.e. destImageFile.isWritable() must be true).
@return  A smart VectorNode handle referencing the underlying object.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     VectorCreate.cpp example, VectorFunctions.cpp example, Node, VectorNode::allowHeteroChildren, ::E57_ERROR_HOMOGENEOUS_VIOLATION
*/
VectorNode::VectorNode(ImageFile destImageFile, bool allowHeteroChildren)
: impl_(new VectorNodeImpl(destImageFile.impl(), allowHeteroChildren))
{
}

//! @brief   Is this a root node.
//! @copydetails Node::isRoot()
bool VectorNode::isRoot() const
{
    return impl_->isRoot();
}

//! @brief   Return parent of node, or self if a root node.
//! @copydetails Node::parent()
Node VectorNode::parent() const
{
    return Node(impl_->parent());
}

//! @brief   Get absolute pathname of node.
//! @copydetails Node::pathName()
ustring VectorNode::pathName() const
{
    return impl_->pathName();
}

//! @brief   Get elementName string, that identifies the node in its parent..
//! @copydetails Node::elementName()
ustring VectorNode::elementName() const
{
    return impl_->elementName();
}

//! @brief   Get the ImageFile that was declared as the destination for the node when it was created.
//! @copydetails Node::destImageFile()
ImageFile VectorNode::destImageFile() const
{
    return ImageFile(impl_->destImageFile());
}

//! @brief   Has node been attached into the tree of an ImageFile.
//! @copydetails Node::isAttached()
bool VectorNode::isAttached() const
{
    return impl_->isAttached();
}

/*!
@brief   Get whether child elements are allowed to be different types?
@details
See the class discussion at bottom of VectorNode page for details of homogeneous/heterogeneous VectorNode.
The returned attribute is determined when the VectorNode is created, and cannot be changed.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  True if child elements can be different types.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     VectorCreate.cpp example, ::E57_ERROR_HOMOGENEOUS_VIOLATION
*/
bool VectorNode::allowHeteroChildren() const
{
    return impl_->allowHeteroChildren();
}

/*!
@brief   Get number of child elements in this VectorNode.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  Number of child elements in this VectorNode.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     VectorFunctions.cpp example, VectorNode::get(int64_t), VectorNode::append, StructureNode::childCount
*/
int64_t VectorNode::childCount() const
{
    return impl_->childCount();
}

/*!
@brief   Is the given pathName defined relative to this node.
@param   [in] pathName   The absolute pathname, or pathname relative to this object, to check.
@details
The @a pathName may be relative to this node, or absolute (starting with a "/").
The origin of the absolute path name is the root of the tree that contains this VectorNode.
If this VectorNode is not attached to an ImageFile, the @a pathName origin root will not the root node of an ImageFile.

The element names of child elements of VectorNodes are numbers, encoded as strings, starting at "0".
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  true if pathName is currently defined.
@throw   ::E57_ERROR_BAD_PATH_NAME
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     VectorFunctions.cpp example, StructureNode::isDefined
*/
bool VectorNode::isDefined(const ustring& pathName) const
{
    return impl_->isDefined(pathName);
}

/*!
@brief   Get a child element by positional index.
@param   [in] index   The index of child element to get, starting at 0.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@pre     0 <= @a index < childCount()
@post    No visible state is modified.
@return  A smart Node handle referencing the child node.
@throw   ::E57_ERROR_CHILD_INDEX_OUT_OF_BOUNDS
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     VectorFunctions.cpp example, VectorNode::childCount, VectorNode::append, StructureNode::get(int64_t) const
*/
Node VectorNode::get(int64_t index) const
{
    return Node(impl_->get(index));
}

/*!
@brief   Get a child element by string path name
@param   [in] pathName   The pathname, either absolute or relative, of the object to get.
@details
The @a pathName may be relative to this node, or absolute (starting with a "/").
The origin of the absolute path name is the root of the tree that contains this VectorNode.
If this VectorNode is not attached to an ImageFile, the @a pathName origin root will not the root node of an ImageFile.

The element names of child elements of VectorNodes are numbers, encoded as strings, starting at "0".
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@pre     The @a pathName must be defined (i.e. isDefined(pathName)).
@post    No visible state is modified.
@return  A smart Node handle referencing the child node.
@throw   ::E57_ERROR_BAD_PATH_NAME
@throw   ::E57_ERROR_PATH_UNDEFINED
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     VectorFunctions.cpp example, VectorNode::childCount, VectorNode::append, StructureNode::get(int64_t) const
*/
Node VectorNode::get(const ustring& pathName) const
{
    return Node(impl_->get(pathName));
}

/*!
@brief   Append a child element to end of VectorNode.
@param   [in] n   The node to be added as a child at end of the VectorNode.
@details
If the VectorNode is homogeneous and already has at least one child, then @a n must be identical to the existing children in every visible characteristic except the stored values.
These visible characteristics include number of children (for StructureNode and VectorNode descendents), number of records/prototypes/codecs (for CompressedVectorNode), minimum/maximum attributes (for IntegerNode, ScaledIntegerNode, FloatNode), byteCount (for BlobNode), scale/offset (for ScaledIntegerNode), and all elementNames.

The VectorNode must not be a descendent of a homogeneous VectorNode with more than one child.
@pre     The new child node @a n must be a root node (not already having a parent).
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@pre     The associated destImageFile must have been opened in write mode (i.e. destImageFile().isWritable()).
@post    the childCount is incremented.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_HOMOGENEOUS_VIOLATION
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_ALREADY_HAS_PARENT
@throw   ::E57_ERROR_DIFFERENT_DEST_IMAGEFILE
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     VectorFunctions.cpp example, VectorNode::childCount, VectorNode::get(int64_t), StructureNode::set
*/
void VectorNode::append(Node n)
{
    impl_->append(n.impl());
}

//! @brief   Diagnostic function to print internal state of object to output stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void VectorNode::dump(int indent, std::ostream& os) const
{
    impl_->dump(indent, os);
}
#else
void VectorNode::dump(int indent, std::ostream& os) const
{}
#endif


/*!
@brief   Upcast a VectorNode handle to a generic Node handle.
@details An upcast is always safe, and the compiler can automatically insert it for initializations of Node variables and Node function arguments.
@return  A smart Node handle referencing the underlying object.
@throw   No E57Exceptions.
@see     VectorFunctions.cpp example, NodeFunctions.cpp example, VectorNode(const Node&) example, explanation in Node, Node::type(), VectorNode(const Node&)
*/
VectorNode::operator Node() const
{
    /// Implicitly upcast from shared_ptr<VectorNodeImpl> to SharedNodeImplPtr and construct a Node object
    return Node(impl_);
}

/*!
@brief   Downcast a generic Node handle to a VectorNode handle.
@param   [in] n The generic handle to downcast.
@details The handle @a n must be for an underlying VectorNode, otherwise an exception is thrown.
In designs that need to avoid the exception, use Node::type() to determine the actual type of the @a n before downcasting.
This function must be explicitly called (c++ compiler cannot insert it automatically).
@return  A smart VectorNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_NODE_DOWNCAST
@see     VectorFunctions.cpp example, NodeFunctions.cpp example, Node::type(), VectorNode::operator Node()
*/
VectorNode::VectorNode(const Node& n)
{
    /// Downcast from SharedNodeImplPtr to shared_ptr<VectorNodeImpl>
    shared_ptr<VectorNodeImpl> ni(dynamic_pointer_cast<VectorNodeImpl>(n.impl()));
    if (!ni)
        throw E57_EXCEPTION2(E57_ERROR_BAD_NODE_DOWNCAST, "nodeType=" + toString(n.type()));

    /// Set our shared_ptr to the downcast shared_ptr
    impl_ = ni;
}

//! @cond documentNonPublic   The following isn't part of the API, and isn't documented.
VectorNode::VectorNode(shared_ptr<VectorNodeImpl> ni)
: impl_(ni)
{}
//! @endcond

//=====================================================================================
/*!
@class SourceDestBuffer
@brief   A memory buffer to transfer data to/from a CompressedVectorNode in a block.
@details
The SourceDestBuffer is an encapsulation of a buffer in memory that will transfer data to/from a field in a CompressedVectorNode.
The API user is responsible for creating the actual memory buffer, describing it correctly to the API, making sure it exists during the transfer period, and destroying it after the transfer is complete.
Additionally, the SourceDestBuffer has information that specifies the connection to the CompressedVectorNode field (i.e. the field's path name in the prototype).

The type of buffer element may be an assortment of built-in C++ memory types.
There are all combinations of signed/unsigned and 8/16/32/64 bit integers (except unsigned 64bit integer, which is not supported in the ASTM standard), bool, float, double, as well as a vector of variable length unicode strings.
The compiler selects the appropriate constructor automatically based on the type of the buffer array.
However, the API user is responsible for reporting the correct length and stride options (otherwise unspecified behavior can occur).

The connection of the SourceDestBuffer to a CompressedVectorNode field is established by specifying the pathName.
There are several options to this connection: doConversion and doScaling, which are described in the constructor documentation.

@section sourcedestbuffer_invariant Class Invariant
A class invariant is a list of statements about an object that are always true before and after any operation on the object.
An invariant is useful for testing correct operation of an implementation.
Statements in an invariant can involve only externally visible state, or can refer to internal implementation-specific state that is not visible to the API user.
The following C++ code checks externally visible state for consistency and throws an exception if the invariant is violated:
@dontinclude E57Format.cpp
@skip begin SourceDestBuffer::checkInvariant
@skip checkInvariant(
@until end SourceDestBuffer::checkInvariant

@see     Node
*/

/*!
@brief   Designate buffers to transfer data to/from a CompressedVectorNode in a block.
@param   [in] destImageFile The ImageFile where the new node will eventually be stored.
@param   [in] pathName      The pathname of the field in CompressedVectorNode that will transfer data to/from.
@param   [in] b             The caller allocated memory buffer.
@param   [in] capacity      The total number of memory elements in buffer @a b.
@param   [in] doConversion  Will a conversion be attempted between memory and ImageFile representations.
@param   [in] doScaling     In a ScaledInteger field, do memory elements hold scaled values, if false they hold raw values.
@param   [in] stride        The number of bytes between memory elements.  If zero, defaults to sizeof memory element.
@details
This overloaded form of the SourceDestBuffer constructor declares a buffer @a b to be the source/destination of a transfer of values stored in a CompressedVectorNode.\n\n

The @a pathName will be used to identify a Node in the prototype that will get/receive data from this buffer.
The @a pathName may be an absolute path name (e.g. "/cartesianX") or a path name relative to the root of the prototype (i.e. the absolute path name without the leading "/", for example: "cartesianX").\n\n

The type of @a b is used to determine the MemoryRepresentation of the SourceDestBuffer.
The buffer @a b may be used for multiple block transfers.
See discussions of operation of SourceDestBuffer attributes in SourceDestBuffer::memoryRepresentation, SourceDestBuffer::capacity, SourceDestBuffer::doConversion, and SourceDestBuffer::doScaling, and SourceDestBuffer::stride.\n\n

The API user is responsible for ensuring that the lifetime of the @a b memory buffer exceeds the time that it is used in transfers (i.e. the E57 Foundation Implementation cannot detect that the buffer been destroyed).\n\n

The @a capacity must match the capacity of all other SourceDestBuffers that will participate in a transfer with a CompressedVectorNode.

@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be true).
@pre     The stride must be >= sizeof(*b)
@return  A smart SourceDestBuffer handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_BAD_PATH_NAME
@throw   ::E57_ERROR_BAD_BUFFER
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferNumericCreate.cpp example, SourceDestVufferStringCreate.cpp example, ImageFile::reader, ImageFile::writer, CompressedVectorReader::read(std::vector<SourceDestBuffer>&), CompressedVectorWriter::write(std::vector<SourceDestBuffer>&)
*/
SourceDestBuffer::SourceDestBuffer(ImageFile destImageFile, const ustring &pathName, int8_t* b, const size_t capacity, bool doConversion, bool doScaling, size_t stride)
: impl_(new SourceDestBufferImpl(destImageFile.impl(), pathName, capacity, doConversion, doScaling))
{
   impl_->setTypeInfo<int8_t>( b, stride );
}

//! @brief   Designate buffers to transfer data to/from a CompressedVectorNode in a block.
//! @copydetails SourceDestBuffer::SourceDestBuffer(ImageFile,const ustring,int8_t*,size_t,bool,bool,size_t)
SourceDestBuffer::SourceDestBuffer(ImageFile destImageFile, const ustring &pathName, uint8_t* b, const size_t capacity, bool doConversion, bool doScaling, size_t stride)
: impl_(new SourceDestBufferImpl(destImageFile.impl(), pathName, capacity, doConversion, doScaling))
{
   impl_->setTypeInfo<uint8_t>( b, stride );
}

//! @brief   Designate buffers to transfer data to/from a CompressedVectorNode in a block.
//! @copydetails SourceDestBuffer::SourceDestBuffer(ImageFile,const ustring,int8_t*,size_t,bool,bool,size_t)
SourceDestBuffer::SourceDestBuffer(ImageFile destImageFile, const ustring &pathName, int16_t* b, const size_t capacity, bool doConversion, bool doScaling, size_t stride)
: impl_(new SourceDestBufferImpl(destImageFile.impl(), pathName, capacity, doConversion, doScaling))
{
   impl_->setTypeInfo<int16_t>( b, stride );
}

//! @brief   Designate buffers to transfer data to/from a CompressedVectorNode in a block.
//! @copydetails SourceDestBuffer::SourceDestBuffer(ImageFile,const ustring,int8_t*,size_t,bool,bool,size_t)
SourceDestBuffer::SourceDestBuffer(ImageFile destImageFile, const ustring &pathName, uint16_t* b, const size_t capacity, bool doConversion, bool doScaling, size_t stride)
: impl_(new SourceDestBufferImpl(destImageFile.impl(), pathName, capacity, doConversion, doScaling))
{
   impl_->setTypeInfo<uint16_t>( b, stride );
}

//! @brief   Designate buffers to transfer data to/from a CompressedVectorNode in a block.
//! @copydetails SourceDestBuffer::SourceDestBuffer(ImageFile,const ustring,int8_t*,size_t,bool,bool,size_t)
SourceDestBuffer::SourceDestBuffer(ImageFile destImageFile, const ustring &pathName, int32_t* b, const size_t capacity, bool doConversion, bool doScaling, size_t stride)
: impl_(new SourceDestBufferImpl(destImageFile.impl(), pathName, capacity, doConversion, doScaling))
{
   impl_->setTypeInfo<int32_t>( b, stride );
}

//! @brief   Designate buffers to transfer data to/from a CompressedVectorNode in a block.
//! @copydetails SourceDestBuffer::SourceDestBuffer(ImageFile,const ustring,int8_t*,size_t,bool,bool,size_t)
SourceDestBuffer::SourceDestBuffer(ImageFile destImageFile, const ustring &pathName, uint32_t* b, const size_t capacity, bool doConversion, bool doScaling, size_t stride)
: impl_(new SourceDestBufferImpl(destImageFile.impl(), pathName, capacity, doConversion, doScaling))
{
   impl_->setTypeInfo<uint32_t>( b, stride );
}

//! @brief   Designate buffers to transfer data to/from a CompressedVectorNode in a block.
//! @copydetails SourceDestBuffer::SourceDestBuffer(ImageFile,const ustring,int8_t*,size_t,bool,bool,size_t)
SourceDestBuffer::SourceDestBuffer(ImageFile destImageFile, const ustring &pathName, int64_t* b, const size_t capacity, bool doConversion, bool doScaling, size_t stride)
: impl_(new SourceDestBufferImpl(destImageFile.impl(), pathName, capacity, doConversion, doScaling))
{
   impl_->setTypeInfo<int64_t>( b, stride );
}

//! @brief   Designate buffers to transfer data to/from a CompressedVectorNode in a block.
//! @copydetails SourceDestBuffer::SourceDestBuffer(ImageFile,const ustring,int8_t*,size_t,bool,bool,size_t)
SourceDestBuffer::SourceDestBuffer(ImageFile destImageFile, const ustring &pathName, bool* b, const size_t capacity, bool doConversion, bool doScaling, size_t stride)
: impl_(new SourceDestBufferImpl(destImageFile.impl(), pathName, capacity, doConversion, doScaling))
{
   impl_->setTypeInfo<bool>( b, stride );
}

//! @brief   Designate buffers to transfer data to/from a CompressedVectorNode in a block.
//! @copydetails SourceDestBuffer::SourceDestBuffer(ImageFile,const ustring,int8_t*,size_t,bool,bool,size_t)
SourceDestBuffer::SourceDestBuffer(ImageFile destImageFile, const ustring &pathName, float* b, const size_t capacity, bool doConversion, bool doScaling, size_t stride)
: impl_(new SourceDestBufferImpl(destImageFile.impl(), pathName, capacity, doConversion, doScaling))
{
   impl_->setTypeInfo<float>( b, stride );
}

//! @brief   Designate buffers to transfer data to/from a CompressedVectorNode in a block.
//! @copydetails SourceDestBuffer::SourceDestBuffer(ImageFile,const ustring,int8_t*,size_t,bool,bool,size_t)
SourceDestBuffer::SourceDestBuffer(ImageFile destImageFile, const ustring &pathName, double* b, const size_t capacity, bool doConversion, bool doScaling, size_t stride)
: impl_(new SourceDestBufferImpl(destImageFile.impl(), pathName, capacity, doConversion, doScaling))
{
   impl_->setTypeInfo<double>( b, stride );
}

/*!
@brief   Designate vector of strings to transfer data to/from a CompressedVector as a block.
@param   [in] destImageFile The ImageFile where the new node will eventually be stored.
@param   [in] pathName      The pathname of the field in CompressedVectorNode that will transfer data to/from.
@param   [in] b             The caller created vector of ustrings to transfer from/to.
@details
This overloaded form of the SourceDestBuffer constructor declares a vector<ustring> to be the source/destination of a transfer of StringNode values stored in a CompressedVectorNode.

The @a pathName will be used to identify a Node in the prototype that will get/receive data from this buffer.
The @a pathName may be an absolute path name (e.g. "/cartesianX") or a path name relative to the root of the prototype (i.e. the absolute path name without the leading "/", for example: "cartesianX").

The @a b->size() must match capacity of all other SourceDestBuffers that will participate in a transfer with a CompressedVectorNode (string or any other type of buffer).
In a read into the SourceDestBuffer, the previous contents of the strings in the vector are lost, and the memory space is potentially freed.
The @a b->size() of the vector will not be changed.
It is an error to request a read/write of more records that @a b->size() (just as it would be for buffers of integer types).
The API user is responsible for ensuring that the lifetime of the @a b vector exceeds the time that it is used in transfers (i.e. the E57 Foundation Implementation cannot detect that the buffer been destroyed).

@pre     b.size() must be > 0.
@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be true).
@return  A smart SourceDestBuffer handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_BAD_PATH_NAME
@throw   ::E57_ERROR_BAD_BUFFER
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferStringCreate.cpp example, SourceDestBufferNumericCreate.cpp example, SourceDestBuffer::doConversion for discussion on representations compatible with string SourceDestBuffers.
*/
SourceDestBuffer::SourceDestBuffer(ImageFile destImageFile, const ustring &pathName, StringList *b)
: impl_(new SourceDestBufferImpl(destImageFile.impl(), pathName, b))
{
}

/*!
@brief   Get path name in prototype that this SourceDestBuffer will transfer data to/from.
@details
The prototype of a CompressedVectorNode describes the fields that are in each record.
This function returns the path name of the node in the prototype tree that this SourceDestBuffer will write/read.
The correctness of this path name is checked when this SourceDestBuffer is associated with a CompressedVectorNode (either in CompressedVectorNode::writer, CompressedVectorWriter::write(std::vector<SourceDestBuffer>&, unsigned), CompressedVectorNode::reader, CompressedVectorReader::read(std::vector<SourceDestBuffer>&)).

@post    No visible state is modified.
@return  Path name in prototype that this SourceDestBuffer will transfer data to/from.
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferFunctions.cpp example, CompressedVector, CompressedVectorNode::prototype
*/
ustring SourceDestBuffer::pathName() const
{
    return impl_->pathName();
}

/*!
@brief   Get memory representation of the elements in this SourceDestBuffer.
@details
The memory representation is deduced from which overloaded SourceDestBuffer constructor was used.
The memory representation is independent of the type and minimum/maximum bounds of the node in the prototype that the SourceDestBuffer will transfer to/from.
However, some combinations will result in an error if doConversion is not requested (e.g. E57_INT16 and FloatNode).

Some combinations risk an error occurring during a write, if a value is too large (e.g. writing a E57_INT16 memory representation to an IntegerNode with minimum=-1024 maximum=1023).
Some combinations risk an error occurring during a read, if a value is too large (e.g. reading an IntegerNode with minimum=-1024 maximum=1023 int an E57_INT8 memory representation).
Some combinations are never possible (e.g. E57_INT16 and StringNode).
@post    No visible state is modified.
@return  Memory representation of the elements in buffer.
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferFunctions.cpp example, MemoryRepresentation, NodeType
*/
MemoryRepresentation SourceDestBuffer::memoryRepresentation() const
{
    return impl_->memoryRepresentation();
}

/*!
@brief   Get total capacity of buffer.
@details
The API programmer is responsible for correctly specifying the length of a buffer.
This function returns that declared length.
If the length is incorrect (in particular, too long) memory may be corrupted or erroneous values written.
@post    No visible state is modified.
@return  Total capacity of buffer.
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferFunctions.cpp example
*/
size_t SourceDestBuffer::capacity() const
{
    return impl_->capacity();
}

/*!
@brief   Get whether conversions will be performed to match the memory type of buffer.
@details
The API user must explicitly request conversion between basic representation groups in memory and on the disk.
The four basic representation groups are: integer, boolean, floating point, and string.
There is no distinction between integer and boolean groups on the disk (they both use IntegerNode).
A explicit request for conversion between single and double precision floating point representations is not required.

The most useful conversion is between integer and floating point representation groups.
Conversion from integer to floating point representations cannot result in an overflow, and is usually loss-less (except for extremely large integers).
Conversion from floating point to integer representations can result in an overflow, and can be lossy.

Conversion between any of the integer, boolean, and floating point representation groups is supported.
No conversion from the string to any other representation group is possible.
Missing or unsupported conversions are detected when the first transfer is attempted (i.e. not when the CompressedVectorReader or CompressedVectorWriter is created).

@post    No visible state is modified.
@return  true if conversions will be performed to match the memory type of buffer.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferFunctions.cpp example
*/
bool SourceDestBuffer::doConversion() const
{
    return impl_->doConversion();
}

/*!
@brief   Get whether scaling will be performed for ScaledIntegerNode transfers.
@details
The doScaling option only applies to ScaledIntegerNodes stored in a CompressedVectorNode on the disk (it is ignored if a ScaledIntegerNode is not involved).

As a convenience, an E57 Foundation Implementation can perform scaling of data so that the API user can manipulate scaledValues rather than rawValues.
For a reader, the scaling process is: scaledValue = (rawValue * scale) + offset.
For a writer, the scaling process is reversed: rawValue = (scaledValue - offset) / scale.
The result performing a scaling in a reader (or "unscaling" in a writer) is always a floating point number.
This floating point number may have to be converted to be compatible with the destination representation.
If the destination representation is not floating point, there is a risk of violating declared min/max bounds.
Because of this risk, it is recommended that scaling only be requested for reading scaledValues from ScaledIntegerNodes into floating point numbers in memory.

It is also possible (and perhaps safest of all) to never request that scaling be performed, and always deal with rawValues outside the API.
Note this does not mean that ScaledIntegerNodes should be avoided.
ScaledIntgerNodes are essential for encoding numeric data with fractional parts in CompressedVectorNodes.
Because the ASTM E57 format recommends that SI units without prefix be used (i.e. meters, not milli-meters or micro-furlongs), almost every measured value will have a fractional part.

@post    No visible state is modified.
@return  true if scaling will be performed for ScaledInteger transfers.
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferFunctions.cpp example, SourceDestBufferNumericCreate.cpp example, ScaledIntegerNode
*/
bool SourceDestBuffer::doScaling() const
{
    return impl_->doScaling();
}

/*!
@brief   Get number of bytes between consecutive memory elements in buffer
@details
Elements in a memory buffer do not have to be consecutive.
They can also be spaced at regular intervals.
This allows a value to be picked out of an array of C++ structures (the stride would be the size of the structure).
In the case that the element values are stored consecutively in memory, the stride equals the size of the memory representation of the element.
@post    No visible state is modified.
@return  Number of bytes between consecutive memory elements in buffer
@see     SourceDestBufferNumericCreate.cpp example
*/
size_t SourceDestBuffer::stride() const
{
    return impl_->stride();
}

//! @brief   Diagnostic function to print internal state of object to output stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void SourceDestBuffer::dump(int indent, std::ostream& os) const
{
    impl_->dump(indent, os);
}
#else
void SourceDestBuffer::dump(int indent, std::ostream& os) const
{}
#endif

//=====================================================================================
/*!
@class CompressedVectorReader
@brief   An iterator object keeping track of a read in progress from a CompressedVectorNode.
@details
A CompressedVectorReader object is a block iterator that reads blocks of records from a CompressedVectorNode and stores them in memory buffers (SourceDestBuffers).
Blocks of records are processed rather than a single record-at-a-time for efficiency reasons.
The CompressedVectorReader class encapsulates all the state that must be saved in between the processing of one record block and the next (e.g. partially read disk pages, or data decompression state).
New memory buffers can be used for each record block read, or the previous buffers can be reused.

CompressedVectorReader objects have an open/closed state.
Initially a newly created CompressedVectorReader is in the open state.
After the API user calls CompressedVectorReader::close, the object will be in the closed state and no more data transfers will be possible.

There is no CompressedVectorReader constructor in the API.
The function CompressedVectorNode::reader returns an already constructed CompressedVectorReader object with given memory buffers (SourceDestBuffers) already associated.

It is recommended to call CompressedVectorReader::close to gracefully end the transfer.
Unlike the CompressedVectorWriter, not all fields in the record of the CompressedVectorNode are required to be read at one time.

@section CompressedVectorReader_invariant Class Invariant
A class invariant is a list of statements about an object that are always true before and after any operation on the object.
An invariant is useful for testing correct operation of an implementation.
Statements in an invariant can involve only externally visible state, or can refer to internal implementation-specific state that is not visible to the API user.
The following C++ code checks externally visible state for consistency and throws an exception if the invariant is violated:
@dontinclude E57Format.cpp
@skip begin CompressedVectorReader::checkInvariant
@skip checkInvariant(
@until end CompressedVectorReader::checkInvariant

@see     CompressedVectorNode, CompressedVectorWriter
*/

//! @cond documentNonPublic   The following isn't part of the API, and isn't documented.
CompressedVectorReader::CompressedVectorReader(shared_ptr<CompressedVectorReaderImpl> ni)
: impl_(ni)
{}
//! @endcond

/*!
@brief   Request transfer of blocks of data from CompressedVectorNode into previously designated destination buffers.
@details
The SourceDestBuffers used are previously designated either in CompressedVectorNode::reader where this object was created, or in the last call to CompressedVectorReader::read(std::vector<SourceDestBuffer>&) where new buffers were designated.
The function will always return the full number of records requested (the capacity of the SourceDestBuffers) unless it has reached the end of the CompressedVectorNode, in which case it will return less than the capacity of the SourceDestBuffers.
Partial reads will store the records at the beginning of the SourceDestBuffers.
It is not an error to call this function after all records in the CompressedVectorNode have been read (the function returns 0).

If a conversion or bounds error occurs during the transfer, the CompressedVectorReader is left in an undocumented state (it can't be used any further).
If a file I/O or checksum error occurs during the transfer, both the CompressedVectorReader and the associated ImageFile are left in an undocumented state (they can't be used any further).

The API user is responsible for ensuring that the underlying memory buffers represented in the SourceDestBuffers still exist when this function is called.
The E57 Foundation Implementation cannot detect that a memory buffer been destroyed.

@pre     The associated ImageFile must be open.
@pre     This CompressedVectorReader must be open (i.e isOpen())
@return  The number of records read.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_READER_NOT_OPEN
@throw   ::E57_ERROR_CONVERSION_REQUIRED            This CompressedVectorReader in undocumented state
@throw   ::E57_ERROR_VALUE_NOT_REPRESENTABLE        This CompressedVectorReader in undocumented state
@throw   ::E57_ERROR_SCALED_VALUE_NOT_REPRESENTABLE This CompressedVectorReader in undocumented state
@throw   ::E57_ERROR_REAL64_TOO_LARGE               This CompressedVectorReader in undocumented state
@throw   ::E57_ERROR_EXPECTING_NUMERIC              This CompressedVectorReader in undocumented state
@throw   ::E57_ERROR_EXPECTING_USTRING              This CompressedVectorReader in undocumented state
@throw   ::E57_ERROR_BAD_CV_PACKET      This CompressedVectorReader, associated ImageFile in undocumented state
@throw   ::E57_ERROR_LSEEK_FAILED       This CompressedVectorReader, associated ImageFile in undocumented state
@throw   ::E57_ERROR_READ_FAILED        This CompressedVectorReader, associated ImageFile in undocumented state
@throw   ::E57_ERROR_BAD_CHECKSUM       This CompressedVectorReader, associated ImageFile in undocumented state
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferNumericCreate.cpp example, CompressedVectorReader::read(std::vector<SourceDestBuffer>&), CompressedVectorNode::reader, SourceDestBuffer, CompressedVectorReader::read(std::vector<SourceDestBuffer>&)
*/
unsigned CompressedVectorReader::read()
{
    return impl_->read();
}

/*!
@brief   Request transfer of block of data from CompressedVectorNode into given destination buffers.
@param   [in] dbufs     Vector of memory buffers that will receive data read from a CompressedVectorNode.
@details
The @a dbufs must all have the same capacity.
The specified @a dbufs must have same number of elements as previously designated SourceDestBuffer vector.
The each SourceDestBuffer within @a dbufs must be identical to the previously designated SourceDestBuffer except for capacity and buffer address.

The @a dbufs locations are saved so that a later call to CompressedVectorReader::read() can be used without having to re-specify the SourceDestBuffers.

The function will always return the full number of records requested (the capacity of the SourceDestBuffers) unless it has reached the end of the CompressedVectorNode, in which case it will return less than the capacity of the SourceDestBuffers.
Partial reads will store the records at the beginning of the SourceDestBuffers.
It is not an error to call this function after all records in the CompressedVectorNode have been read (the function returns 0).

If a conversion or bounds error occurs during the transfer, the CompressedVectorReader is left in an undocumented state (it can't be used any further).
If a file I/O or checksum error occurs during the transfer, both the CompressedVectorReader and the associated ImageFile are left in an undocumented state (they can't be used any further).

The API user is responsible for ensuring that the underlying memory buffers represented in the SourceDestBuffers still exist when this function is called.
The E57 Foundation Implementation cannot detect that a memory buffer been destroyed.

@pre     The associated ImageFile must be open.
@pre     This CompressedVectorReader must be open (i.e isOpen())
@return  The number of records read.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_READER_NOT_OPEN
@throw   ::E57_ERROR_PATH_UNDEFINED
@throw   ::E57_ERROR_BUFFER_SIZE_MISMATCH
@throw   ::E57_ERROR_BUFFER_DUPLICATE_PATHNAME
@throw   ::E57_ERROR_CONVERSION_REQUIRED            This CompressedVectorReader in undocumented state
@throw   ::E57_ERROR_VALUE_NOT_REPRESENTABLE        This CompressedVectorReader in undocumented state
@throw   ::E57_ERROR_SCALED_VALUE_NOT_REPRESENTABLE This CompressedVectorReader in undocumented state
@throw   ::E57_ERROR_REAL64_TOO_LARGE               This CompressedVectorReader in undocumented state
@throw   ::E57_ERROR_EXPECTING_NUMERIC              This CompressedVectorReader in undocumented state
@throw   ::E57_ERROR_EXPECTING_USTRING              This CompressedVectorReader in undocumented state
@throw   ::E57_ERROR_BAD_CV_PACKET      This CompressedVectorReader, associated ImageFile in undocumented state
@throw   ::E57_ERROR_LSEEK_FAILED       This CompressedVectorReader, associated ImageFile in undocumented state
@throw   ::E57_ERROR_READ_FAILED        This CompressedVectorReader, associated ImageFile in undocumented state
@throw   ::E57_ERROR_BAD_CHECKSUM       This CompressedVectorReader, associated ImageFile in undocumented state
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferNumericCreate.cpp example, CompressedVectorReader::read(), CompressedVectorNode::reader, SourceDestBuffer
*/
unsigned CompressedVectorReader::read(std::vector<SourceDestBuffer>& dbufs)
{
    return impl_->read(dbufs);
}

/*!
@brief   Set record number of CompressedVectorNode where next read will start.
@param   [in] recordNumber   The index of record in ComressedVectorNode where next read using this CompressedVectorReader will start.
@details
This function may be called at any time (as long as ImageFile and CompressedVectorReader are open).
The next read will start at the given recordNumber.
It is not an error to seek to recordNumber = childCount() (i.e. to one record past end of CompressedVectorNode).

@pre     @a recordNumber <= childCount() of CompressedVectorNode.
@pre     The associated ImageFile must be open.
@pre     This CompressedVectorReader must be open (i.e isOpen())
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_READER_NOT_OPEN
@throw   ::E57_ERROR_BAD_CV_PACKET
@throw   ::E57_ERROR_LSEEK_FAILED
@throw   ::E57_ERROR_READ_FAILED
@throw   ::E57_ERROR_BAD_CHECKSUM
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferNumericCreate.cpp example, CompressedVectorNode::reader
*/
void CompressedVectorReader::seek(int64_t recordNumber)
{
    impl_->seek(recordNumber);
}

/*!
@brief   End the read operation.
@details
It is recommended that this function be called to gracefully end a transfer to a CompressedVectorNode.
It is not an error to call this function if the CompressedVectorReader is already closed.
This function will cause the CompressedVectorReader to enter the closed state, and any further transfers requests will fail.

@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferNumericCreate.cpp example, CompressedVectorReader::isOpen, CompressedVectorNode::reader
*/
void CompressedVectorReader::close()
{
    impl_->close();
}

/*!
@brief   Test whether CompressedVectorReader is still open for reading.
@pre     The associated ImageFile must be open.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferNumericCreate.cpp example, CompressedVectorReader::close, CompressedVectorNode::reader
*/
bool CompressedVectorReader::isOpen()
{
    return impl_->isOpen();
}

/*!
@brief   Return the CompressedVectorNode being read.
@details
It is not an error if this CompressedVectorReader is closed.
@pre     The associated ImageFile must be open.
@return  A smart CompressedVectorNode handle referencing the underlying object being read from.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferNumericCreate.cpp example, CompressedVectorReader::close, CompressedVectorNode::reader
*/
CompressedVectorNode CompressedVectorReader::compressedVectorNode() const
{
    return impl_->compressedVectorNode();
}

//! @brief   Diagnostic function to print internal state of object to output stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void CompressedVectorReader::dump(int indent, std::ostream& os) const
{
    impl_->dump(indent, os);
}
#else
void CompressedVectorReader::dump(int indent, std::ostream& os) const
{}
#endif

//=====================================================================================
/*!
@class CompressedVectorWriter
@brief   An iterator object keeping track of a write in progress to a CompressedVectorNode.
@details
A CompressedVectorWriter object is a block iterator that reads blocks of records from memory and stores them in a CompressedVectorNode.
Blocks of records are processed rather than a single record-at-a-time for efficiency reasons.
The CompressedVectorWriter class encapsulates all the state that must be saved in between the processing of one record block and the next (e.g. partially written disk pages, partially filled bytes in a bytestream, or data compression state).
New memory buffers can be used for each record block write, or the previous buffers can be reused.

CompressedVectorWriter objects have an open/closed state.
Initially a newly created CompressedVectorWriter is in the open state.
After the API user calls CompressedVectorWriter::close, the object will be in the closed state and no more data transfers will be possible.

There is no CompressedVectorWriter constructor in the API.
The function CompressedVectorNode::writer returns an already constructed CompressedVectorWriter object with given memory buffers (SourceDestBuffers) already associated.
CompressedVectorWriter::close must explicitly be called to safely and gracefully end the transfer.

@b Warning: If CompressedVectorWriter::close is not called before the CompressedVectorWriter destructor is invoked, all writes to the CompressedVectorNode will be lost (it will have zero children).

@section CompressedVectorWriter_invariant Class Invariant
A class invariant is a list of statements about an object that are always true before and after any operation on the object.
An invariant is useful for testing correct operation of an implementation.
Statements in an invariant can involve only externally visible state, or can refer to internal implementation-specific state that is not visible to the API user.
The following C++ code checks externally visible state for consistency and throws an exception if the invariant is violated:
@dontinclude E57Format.cpp
@skip begin CompressedVectorWriter::checkInvariant
@skip checkInvariant(
@until end CompressedVectorWriter::checkInvariant

@see     CompressedVectorNode, CompressedVectorReader
*/

//! @cond documentNonPublic   The following isn't part of the API, and isn't documented.
CompressedVectorWriter::CompressedVectorWriter(shared_ptr<CompressedVectorWriterImpl> ni)
: impl_(ni)
{}
//! @endcond

/*!
@brief   Request transfer of blocks of data to CompressedVectorNode from previously designated source buffers.
@param   [in] recordCount   Number of records to write.
@details
The SourceDestBuffers used are previously designated either in CompressedVectorNode::writer where this object was created, or in the last call to CompressedVectorWriter::write(std::vector<SourceDestBuffer>&, unsigned) where new buffers were designated.

If a conversion or bounds error occurs during the transfer, the CompressedVectorWriter is left in an undocumented state (it can't be used any further), and all previously written records are deleted from the associated CompressedVectorNode which will then have zero children.
If a file I/O or checksum error occurs during the transfer, both this CompressedVectorWriter and the associated ImageFile are left in an undocumented state (they can't be used any further).
If CompressedVectorWriter::close is not called before the CompressedVectorWriter destructor is invoked, all writes to the CompressedVectorNode will be lost (it will have zero children).

@pre     The associated ImageFile must be open.
@pre     This CompressedVectorWriter must be open (i.e isOpen())
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_WRITER_NOT_OPEN
@throw   ::E57_ERROR_PATH_UNDEFINED
@throw   ::E57_ERROR_NO_BUFFER_FOR_ELEMENT
@throw   ::E57_ERROR_BUFFER_SIZE_MISMATCH
@throw   ::E57_ERROR_BUFFER_DUPLICATE_PATHNAME
@throw   ::E57_ERROR_CONVERSION_REQUIRED     This CompressedVectorWriter in undocumented state, associated CompressedVectorNode modified but consistent, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_VALUE_OUT_OF_BOUNDS     This CompressedVectorWriter in undocumented state, associated CompressedVectorNode modified but consistent, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_VALUE_NOT_REPRESENTABLE This CompressedVectorWriter in undocumented state, associated CompressedVectorNode modified but consistent, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_SCALED_VALUE_NOT_REPRESENTABLE This CompressedVectorWriter in undocumented state, associated CompressedVectorNode modified but consistent, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_REAL64_TOO_LARGE   This CompressedVectorWriter in undocumented state, associated CompressedVectorNode modified but consistent, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_EXPECTING_NUMERIC  This CompressedVectorWriter in undocumented state, associated CompressedVectorNode modified but consistent, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_EXPECTING_USTRING  This CompressedVectorWriter in undocumented state, associated CompressedVectorNode modified but consistent, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_LSEEK_FAILED       This CompressedVectorWriter, associated ImageFile in undocumented state
@throw   ::E57_ERROR_READ_FAILED        This CompressedVectorWriter, associated ImageFile in undocumented state
@throw   ::E57_ERROR_WRITE_FAILED       This CompressedVectorWriter, associated ImageFile in undocumented state
@throw   ::E57_ERROR_BAD_CHECKSUM       This CompressedVectorWriter, associated ImageFile in undocumented state
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferNumericCreate.cpp example, CompressedVectorWriter::write(std::vector<SourceDestBuffer>&,unsigned), CompressedVectorNode::writer, CompressedVectorWriter::close, SourceDestBuffer, E57Exception
*/
void CompressedVectorWriter::write(const size_t recordCount)
{
    impl_->write(recordCount);
}

/*!
@brief   Request transfer of block of data to CompressedVectorNode from given source buffers.
@param   [in] sbufs         Vector of memory buffers that hold data to be written to a CompressedVectorNode.
@param   [in] recordCount   Number of records to write.
@details
The @a sbufs must all have the same capacity.
The @a sbufs capacity must be >= @a recordCount.
The specified @a sbufs must have same number of elements as previously designated SourceDestBuffer vector.
The each SourceDestBuffer within @a sbufs must be identical to the previously designated SourceDestBuffer except for capacity and buffer address.

The @a sbufs locations are saved so that a later call to CompressedVectorWriter::write(unsigned) can be used without having to re-specify the SourceDestBuffers.

If a conversion or bounds error occurs during the transfer, the CompressedVectorWriter is left in an undocumented state (it can't be used any further), and all previously written records are deleted from the the associated CompressedVectorNode which will then have zero children.
If a file I/O or checksum error occurs during the transfer, both this CompressedVectorWriter and the associated ImageFile are left in an undocumented state (they can't be used any further).

@b Warning: If CompressedVectorWriter::close is not called before the CompressedVectorWriter destructor is invoked, all writes to the CompressedVectorNode will be lost (it will have zero children).

@pre     The associated ImageFile must be open.
@pre     This CompressedVectorWriter must be open (i.e isOpen())
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_WRITER_NOT_OPEN
@throw   ::E57_ERROR_PATH_UNDEFINED
@throw   ::E57_ERROR_NO_BUFFER_FOR_ELEMENT
@throw   ::E57_ERROR_BUFFER_SIZE_MISMATCH
@throw   ::E57_ERROR_BUFFER_DUPLICATE_PATHNAME
@throw   ::E57_ERROR_CONVERSION_REQUIRED     This CompressedVectorWriter in undocumented state, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_VALUE_OUT_OF_BOUNDS     This CompressedVectorWriter in undocumented state, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_VALUE_NOT_REPRESENTABLE This CompressedVectorWriter in undocumented state, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_SCALED_VALUE_NOT_REPRESENTABLE This CompressedVectorWriter in undocumented state, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_REAL64_TOO_LARGE   This CompressedVectorWriter in undocumented state, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_EXPECTING_NUMERIC  This CompressedVectorWriter in undocumented state, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_EXPECTING_USTRING  This CompressedVectorWriter in undocumented state, associated ImageFile modified but consistent.
@throw   ::E57_ERROR_LSEEK_FAILED       This CompressedVectorWriter, associated ImageFile in undocumented state
@throw   ::E57_ERROR_READ_FAILED        This CompressedVectorWriter, associated ImageFile in undocumented state
@throw   ::E57_ERROR_WRITE_FAILED       This CompressedVectorWriter, associated ImageFile in undocumented state
@throw   ::E57_ERROR_BAD_CHECKSUM       This CompressedVectorWriter, associated ImageFile in undocumented state
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferNumericCreate.cpp example, CompressedVectorWriter::write(unsigned), CompressedVectorNode::writer, CompressedVectorWriter::close, SourceDestBuffer, E57Exception
*/
void CompressedVectorWriter::write(std::vector<SourceDestBuffer>& sbufs, const size_t recordCount)
{
    impl_->write(sbufs, recordCount);
}

/*!
@brief   End the write operation.
@details
This function must be called to safely and gracefully end a transfer to a CompressedVectorNode.
This is required because errors cannot be communicated from the CompressedVectorNode destructor (in C++ destructors can't throw exceptions).
It is not an error to call this function if the CompressedVectorWriter is already closed.
This function will cause the CompressedVectorWriter to enter the closed state, and any further transfers requests will fail.

@b Warning: If this function is not called before the CompressedVectorWriter destructor is invoked, all writes to the CompressedVectorNode will be lost (it will have zero children).
@pre     The associated ImageFile must be open.
@post    This CompressedVectorWriter is closed (i.e !isOpen())
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_LSEEK_FAILED       This CompressedVectorWriter, associated ImageFile in undocumented state
@throw   ::E57_ERROR_READ_FAILED        This CompressedVectorWriter, associated ImageFile in undocumented state
@throw   ::E57_ERROR_WRITE_FAILED       This CompressedVectorWriter, associated ImageFile in undocumented state
@throw   ::E57_ERROR_BAD_CHECKSUM       This CompressedVectorWriter, associated ImageFile in undocumented state
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferNumericCreate.cpp example, CompressedVectorWriter::isOpen
*/
void CompressedVectorWriter::close()
{
    impl_->close();
}

/*!
@brief   Test whether CompressedVectorWriter is still open for writing.
@pre     The associated ImageFile must be open.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferFunctions.cpp example, CompressedVectorWriter::close, CompressedVectorNode::writer
*/
bool CompressedVectorWriter::isOpen()
{
    return impl_->isOpen();
}

/*!
@brief   Return the CompressedVectorNode being written to.
@pre     The associated ImageFile must be open.
@return  A smart CompressedVectorNode handle referencing the underlying object being written to.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferNumericCreate.cpp example, CompressedVectorNode::writer
*/
CompressedVectorNode CompressedVectorWriter::compressedVectorNode() const
{
    return impl_->compressedVectorNode();
}

//! @brief   Diagnostic function to print internal state of object to output stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void CompressedVectorWriter::dump(int indent, std::ostream& os) const
{
    impl_->dump(indent, os);
}
#else
void CompressedVectorWriter::dump(int indent, std::ostream& os) const
{}
#endif

//=====================================================================================
/*!
@class CompressedVectorNode
@brief   An E57 element containing ordered vector of child nodes, stored in an efficient binary format.
@details
The CompressedVectorNode encodes very long sequences of identically typed records.
In an E57 file, the per-point information (coordinates, intensity, color, time stamp etc.) are stored in a CompressedVectorNode.
For time and space efficiency, the CompressedVectorNode data is stored in a binary section of the E57 file.

Conceptually, the CompressedVectorNode encodes a structure that looks very much like a homogeneous VectorNode object.
However because of the huge volume of data (E57 files can store more than 10 billion points) within a CompressedVectorNode, the functions for accessing the data are dramatically different.
CompressedVectorNode data is accessed in large blocks of records (100s to 1000s at a time).

Two attributes are required to create a new CompressedVectorNode.
The first attribute describes the shape of the record that will be stored.
This record type description is called the @c prototype of the CompressedVectorNode.
Often the @c prototype will be a StructNode with a single level of named child elements.
However, the prototype can be a tree of any depth consisting of the following node types: IntegerNode, ScaledIntegerNode, FloatNode, StringNode, StructureNode, or VectorNode (i.e. CompressedVectorNode and BlobNode are not allowed).
Only the node types and attributes are used in the prototype, the values stored are ignored.
For example, if the prototype contains an IntegerNode, with a value=0, minimum=0, maximum=1023, then this means that each record will contain an integer that can take any value in the interval [0,1023].
As a second example, if the prototype contains an ScaledIntegerNode, with a value=0, minimum=0, maximum=1023, scale=.001, offset=0 then this means that each record will contain an integer that can take any value in the interval [0,1023] and if a reader requests the scaledValue of the field, the rawValue should be multiplied by 0.001.

The second attribute needed to describe a new CompressedVectorNode is the @c codecs description of how the values of the records are to be represented on the disk.
The codec object is a VectorNode of a particular format that describes the encoding for each field in the record, which codec will be used to transfer the values to and from the disk.
Currently only one codec is defined for E57 files, the bitPackCodec, which copies the numbers from memory, removes any unused bit positions, and stores the without additional spaces on the disk.
The bitPackCodec has no configuration options or parameters to tune.
In the ASTM standard, if no codec is specified, the bitPackCodec is assumed.
So specifying the @c codecs as an empty VectorNode is equivalent to requesting at all fields in the record be encoded with the bitPackCodec.

Other than the @c prototype and @c codecs attributes, the only other state directly accessible is the number of children (records) in the CompressedVectorNode.
The read/write access to the contents of the CompressedVectorNode is coordinated by two other Foundation API objects: CompressedVectorReader and CompressedVectorWriter.

@section CompressedVectorNode_invariant Class Invariant
A class invariant is a list of statements about an object that are always true before and after any operation on the object.
An invariant is useful for testing correct operation of an implementation.
Statements in an invariant can involve only externally visible state, or can refer to internal implementation-specific state that is not visible to the API user.
The following C++ code checks externally visible state for consistency and throws an exception if the invariant is violated:
@dontinclude E57Format.cpp
@skip begin CompressedVectorNode::checkInvariant
@skip checkInvariant(
@until end CompressedVectorNode::checkInvariant

@see     CompressedVectorReader, CompressedVectorWriter, Node
*/

/*!
@brief   Create an empty CompressedVectorNode, for writing, that will store records specified by the prototype.
@param   [in] destImageFile The ImageFile where the new node will eventually be stored.
@param   [in] prototype     A tree that describes the fields in each record of the CompressedVectorNode.
@param   [in] codecs        A VectorNode describing which codecs should be used for each field described in the prototype.
@details
The @a destImageFile indicates which ImageFile the CompressedVectorNode will eventually be attached to.
A node is attached to an ImageFile by adding it underneath the predefined root of the ImageFile (gotten from ImageFile::root).
It is not an error to fail to attach the CompressedVectorNode to the @a destImageFile.
It is an error to attempt to attach the CompressedVectorNode to a different ImageFile.
The CompressedVectorNode may not be written to until it is attached to the destImageFile tree.

The @a prototype may be any tree consisting of only the following node types: IntegerNode, ScaledIntegerNode, FloatNode, StringNode, StructureNode, or VectorNode (i.e. CompressedVectorNode and BlobNode are not allowed).
See CompressedVectorNode for discussion about the @a prototype argument.

The @a codecs must be a heterogeneous VectorNode with children as specified in the ASTM E57 data format standard.
Since currently only one codec is supported (bitPackCodec), and it is the default, passing an empty VectorNode will specify that all record fields will be encoded with bitPackCodec.

@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be true).
@pre     The @a destImageFile must have been opened in write mode (i.e. destImageFile.isWritable() must be true).
@pre     @a prototype must be an unattached root node (i.e. !prototype.isAttached() && prototype.isRoot())
@pre     @a prototype cannot contain BlobNodes or CompressedVectorNodes.
@pre     @a codecs must be an unattached root node (i.e. !codecs.isAttached() && codecs.isRoot())
@post    prototype.isAttached()
@post    codecs.isAttached()
@return  A smart CompressedVectorNode handle referencing the underlying object.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_BAD_PROTOTYPE
@throw   ::E57_ERROR_BAD_CODECS
@throw   ::E57_ERROR_ALREADY_HAS_PARENT
@throw   ::E57_ERROR_DIFFERENT_DEST_IMAGEFILE
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVectorCreate.cpp example, SourceDestBuffer, Node, CompressedVectorNode::reader, CompressedVectorNode::writer
*/
CompressedVectorNode::CompressedVectorNode(ImageFile destImageFile, const Node &prototype, const VectorNode &codecs)
: impl_(new CompressedVectorNodeImpl(destImageFile.impl()))
{
    /// Because of shared_ptr quirks, can't set prototype,codecs in CompressedVectorNodeImpl(), so set it afterwards
    impl_->setPrototype(prototype.impl());
    impl_->setCodecs(codecs.impl());
}

//! @brief   Is this a root node.
//! @copydetails Node::isRoot()
bool CompressedVectorNode::isRoot() const
{
    return impl_->isRoot();
}

//! @brief   Return parent of node, or self if a root node.
//! @copydetails Node::parent()
Node CompressedVectorNode::parent() const
{
    return Node(impl_->parent());
}

//! @brief   Get absolute pathname of node.
//! @copydetails Node::pathName()
ustring CompressedVectorNode::pathName() const
{
    return impl_->pathName();
}

//! @brief   Get elementName string, that identifies the node in its parent..
//! @copydetails Node::elementName()
ustring CompressedVectorNode::elementName() const
{
    return impl_->elementName();
}

//! @brief   Get the ImageFile that was declared as the destination for the node when it was created.
//! @copydetails Node::destImageFile()
ImageFile CompressedVectorNode::destImageFile() const
{
    return ImageFile(impl_->destImageFile());
}

//! @brief   Has node been attached into the tree of an ImageFile.
//! @copydetails Node::isAttached()
bool CompressedVectorNode::isAttached() const
{
    return impl_->isAttached();
}

/*!
@brief   Get current number of records in a CompressedVectorNode.
@details
For a CompressedVectorNode with an active CompressedVectorWriter, the returned number will reflect any writes completed.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  Current number of records in CompressedVectorNode.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     CompressedVectorCreate.cpp example, CompressedVectorNode::reader, CompressedVectorNode::writer
*/
int64_t CompressedVectorNode::childCount() const
{
    return impl_->childCount();
}

/*!
@brief   Get the prototype tree that describes the types in the record.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  A smart Node handle referencing the root of the prototype tree.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferNumericCreate.cpp example, CompressedVectorNode::CompressedVectorNode, SourceDestBuffer, CompressedVectorNode::reader, CompressedVectorNode::writer
*/
Node CompressedVectorNode::prototype() const
{
    return Node(impl_->getPrototype());
}

/*!
@brief   Get the codecs tree that describes the encoder/decoder configuration of the CompressedVectorNode.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  A smart VectorNode handle referencing the root of the codecs tree.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferNumericCreate.cpp example, CompressedVectorNode::CompressedVectorNode, SourceDestBuffer, CompressedVectorNode::reader, CompressedVectorNode::writer
*/
VectorNode CompressedVectorNode::codecs() const
{
    return VectorNode(impl_->getCodecs());
}

//! @brief   Diagnostic function to print internal state of object to output stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void CompressedVectorNode::dump(int indent, std::ostream& os) const
{
    impl_->dump(indent, os);
}
#else
void CompressedVectorNode::dump(int indent, std::ostream& os) const
{}
#endif

/*!
@brief   Upcast a CompressedVectorNode handle to a generic Node handle.
@details An upcast is always safe, and the compiler can automatically insert it for initializations of Node variables and Node function arguments.
@return  A smart Node handle referencing the underlying object.
@throw   No E57Exceptions.
@see     NodeFunctions.cpp example, CompressedVectorNode(const Node&) example, explanation in Node, Node::type(), CompressedVectorNode(const Node&)
*/
CompressedVectorNode::operator Node() const
{
    /// Implicitly upcast from shared_ptr<CompressedVectorNodeImpl> to SharedNodeImplPtr and construct a Node object
    return Node(impl_);
}

/*!
@brief   Downcast a generic Node handle to a CompressedVectorNode handle.
@param   [in] n The generic handle to downcast.
@details The handle @a n must be for an underlying CompressedVectorNode, otherwise an exception is thrown.
In designs that need to avoid the exception, use Node::type() to determine the actual type of the @a n before downcasting.
This function must be explicitly called (c++ compiler cannot insert it automatically).
@return  A smart CompressedVectorNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_NODE_DOWNCAST
@see     NodeFunctions.cpp example, Node::type(), CompressedVectorNode::operator Node()
*/
CompressedVectorNode::CompressedVectorNode(const Node& n)
{
    /// Downcast from SharedNodeImplPtr to shared_ptr<CompressedVectorNodeImpl>
    shared_ptr<CompressedVectorNodeImpl> ni(dynamic_pointer_cast<CompressedVectorNodeImpl>(n.impl()));
    if (!ni)
        throw E57_EXCEPTION2(E57_ERROR_BAD_NODE_DOWNCAST, "nodeType=" + toString(n.type()));

    /// Set our shared_ptr to the downcast shared_ptr
    impl_ = ni;
}

//! @cond documentNonPublic   The following isn't part of the API, and isn't documented.
CompressedVectorNode::CompressedVectorNode(shared_ptr<CompressedVectorNodeImpl> ni)
: impl_(ni)
{}
//! @endcond

/*!
@brief   Create an iterator object for writing a series of blocks of data to a CompressedVectorNode.
@param   [in] sbufs         Vector of memory buffers that will hold data to be written to a CompressedVectorNode.
@details
See CompressedVectorWriter::write(std::vector<SourceDestBuffer>&, unsigned) for discussion about restrictions on @a sbufs.

The pathNames in the @a sbufs must match one-to-one with the terminal nodes (i.e. nodes that can have no children: IntegerNode, ScaledIntegerNode, FloatNode, StringNode) in this CompressedVectorNode's prototype.
It is an error for two SourceDestBuffers in @a dbufs to identify the same terminal node in the prototype.


It is an error to call this function if the CompressedVectorNode already has any records (i.e. a CompressedVectorNode cannot be set twice).

@pre     @a sbufs can't be empty (i.e. sbufs.length() > 0).
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@pre     The @a destImageFile must have been opened in write mode (i.e. destImageFile.isWritable()).
@pre     The destination ImageFile can't have any readers or writers open (destImageFile().readerCount()==0 && destImageFile().writerCount()==0)
@pre     This CompressedVectorNode must be attached (i.e. isAttached()).
@pre     This CompressedVectorNode must have no records (i.e. childCount() == 0).
@return  A smart CompressedVectorWriter handle referencing the underlying iterator object.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_SET_TWICE
@throw   ::E57_ERROR_TOO_MANY_WRITERS
@throw   ::E57_ERROR_TOO_MANY_READERS
@throw   ::E57_ERROR_NODE_UNATTACHED
@throw   ::E57_ERROR_PATH_UNDEFINED
@throw   ::E57_ERROR_BUFFER_SIZE_MISMATCH
@throw   ::E57_ERROR_BUFFER_DUPLICATE_PATHNAME
@throw   ::E57_ERROR_NO_BUFFER_FOR_ELEMENT
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferFunctions.cpp example, CompressedVectorWriter, SourceDestBuffer, CompressedVectorNode::CompressedVectorNode, CompressedVectorNode::prototype
*/
CompressedVectorWriter CompressedVectorNode::writer(std::vector<SourceDestBuffer>& sbufs)
{
    return CompressedVectorWriter(impl_->writer(sbufs));
}

/*!
@brief   Create an iterator object for reading a series of blocks of data from a CompressedVectorNode.
@param   [in] dbufs     Vector of memory buffers that will receive data read from a CompressedVectorNode.
@details
The pathNames in the @a dbufs must identify terminal nodes (i.e. node that can have no children: IntegerNode, ScaledIntegerNode, FloatNode, StringNode) in this CompressedVectorNode's prototype.
It is an error for two SourceDestBuffers in @a dbufs to identify the same terminal node in the prototype.
It is not an error to create a CompressedVectorReader for an empty CompressedVectorNode.

@pre     @a dbufs can't be empty
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@pre     The destination ImageFile can't have any writers open (destImageFile().writerCount()==0)
@pre     This CompressedVectorNode must be attached (i.e. isAttached()).
@return  A smart CompressedVectorReader handle referencing the underlying iterator object.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_TOO_MANY_WRITERS
@throw   ::E57_ERROR_NODE_UNATTACHED
@throw   ::E57_ERROR_PATH_UNDEFINED
@throw   ::E57_ERROR_BUFFER_SIZE_MISMATCH
@throw   ::E57_ERROR_BUFFER_DUPLICATE_PATHNAME
@throw   ::E57_ERROR_BAD_CV_HEADER
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferFunctions.cpp example, CompressedVectorReader, SourceDestBuffer, CompressedVectorNode::CompressedVectorNode, CompressedVectorNode::prototype
*/
CompressedVectorReader CompressedVectorNode::reader(const std::vector<SourceDestBuffer>& dbufs)
{
    return CompressedVectorReader(impl_->reader(dbufs));
}

//=====================================================================================
/*!
@class IntegerNode
@brief   An E57 element encoding an integer value.
@details
An IntegerNode is a terminal node (i.e. having no children) that holds an integer value, and minimum/maximum bounds.
Once the IntegerNode value and attributes are set at creation, they may not be modified.

The minimum attribute may be a number in the interval [-2^63, 2^63).
The maximum attribute may be a number in the interval [minimum, 2^63).
The value may be a number in the interval [minimum, maximum].

See Node class discussion for discussion of the common functions that StructureNode supports.

@section IntegerNode_invariant Class Invariant
A class invariant is a list of statements about an object that are always true before and after any operation on the object.
An invariant is useful for testing correct operation of an implementation.
Statements in an invariant can involve only externally visible state, or can refer to internal implementation-specific state that is not visible to the API user.
The following C++ code checks externally visible state for consistency and throws an exception if the invariant is violated:
@dontinclude E57Format.cpp
@skip begin IntegerNode::checkInvariant
@skip checkInvariant(
@until end IntegerNode::checkInvariant

@see     Node, CompressedVector
*/

/*!
@brief   Create an E57 element for storing a integer value.
@param   [in] destImageFile   The ImageFile where the new node will eventually be stored.
@param   [in] value     The integer value of the element.
@param   [in] minimum   The smallest value that the element may take.
@param   [in] maximum   The largest value that the element may take.
@details
An IntegerNode stores an integer value, and a lower and upper bound.
The IntegerNode class corresponds to the ASTM E57 standard Integer element.
See the class discussion at bottom of IntegerNode page for more details.

The @a destImageFile indicates which ImageFile the IntegerNode will eventually be attached to.
A node is attached to an ImageFile by adding it underneath the predefined root of the ImageFile (gotten from ImageFile::root).
It is not an error to fail to attach the IntegerNode to the @a destImageFile.
It is an error to attempt to attach the IntegerNode to a different ImageFile.

@b Warning: it is an error to give an @a value outside the @a minimum / @a maximum bounds, even if the IntegerNode is destined to be used in a CompressedVectorNode prototype (where the @a value will be ignored).
If the IntegerNode is to be used in a prototype, it is recommended to specify a @a value = 0 if 0 is within bounds, or a @a value = @a minimum if 0 is not within bounds.
@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be true).
@pre     The @a destImageFile must have been opened in write mode (i.e. destImageFile.isWritable() must be true).
@pre     minimum <= value <= maximum
@return  A smart IntegerNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_VALUE_OUT_OF_BOUNDS
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     IntegerCreate.cpp example, IntegerNode::value, Node, CompressedVectorNode, CompressedVectorNode::prototype
*/
IntegerNode::IntegerNode(ImageFile destImageFile, int64_t value, int64_t  minimum, int64_t  maximum)
: impl_(new IntegerNodeImpl(destImageFile.impl(), value, minimum, maximum))
{
}

//! @brief   Is this a root node.
//! @copydetails Node::isRoot()
bool IntegerNode::isRoot() const
{
    return impl_->isRoot();
}

//! @brief   Return parent of node, or self if a root node.
//! @copydetails Node::parent()
Node IntegerNode::parent() const
{
    return Node(impl_->parent());
}

//! @brief   Get absolute pathname of node.
//! @copydetails Node::pathName()
ustring IntegerNode::pathName() const
{
    return impl_->pathName();
}

//! @brief   Get elementName string, that identifies the node in its parent..
//! @copydetails Node::elementName()
ustring IntegerNode::elementName() const
{
    return impl_->elementName();
}

//! @brief   Get the ImageFile that was declared as the destination for the node when it was created.
//! @copydetails Node::destImageFile()
ImageFile IntegerNode::destImageFile() const
{
    return ImageFile(impl_->destImageFile());
}

//! @brief   Has node been attached into the tree of an ImageFile.
//! @copydetails Node::isAttached()
bool IntegerNode::isAttached() const
{
    return impl_->isAttached();
}

/*!
@brief   Get integer value stored.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  integer value stored.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     IntegerCreate.cpp example, IntegerNode::minimum, IntegerNode::maximum
*/
int64_t IntegerNode::value() const
{
    return impl_->value();
}

/*!
@brief   Get the declared minimum that the value may take.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The declared minimum that the value may take.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     IntegerCreate.cpp example, IntegerNode::value
*/
int64_t IntegerNode::minimum() const
{
    return impl_->minimum();
}

/*!
@brief   Get the declared maximum that the value may take.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The declared maximum that the value may take.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     IntegerCreate.cpp example, IntegerNode::value
*/
int64_t IntegerNode::maximum() const
{
    return impl_->maximum();
}

//! @brief   Diagnostic function to print internal state of object to output stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void IntegerNode::dump(int indent, std::ostream& os) const
{
    impl_->dump(indent, os);
}
#else
void IntegerNode::dump(int indent, std::ostream& os) const
{}
#endif

/*!
@brief   Upcast a IntegerNode handle to a generic Node handle.
@details An upcast is always safe, and the compiler can automatically insert it for initializations of Node variables and Node function arguments.
@return  A smart Node handle referencing the underlying object.
@throw   No E57Exceptions.
@see     NodeFunctions.cpp example, IntegerNode(const Node&) example, explanation in Node, Node::type(), IntegerNode(const Node&)
*/
IntegerNode::operator Node() const
{
    /// Upcast from shared_ptr<IntegerNodeImpl> to SharedNodeImplPtr and construct a Node object
    return Node(impl_);
}

/*!
@brief   Downcast a generic Node handle to a IntegerNode handle.
@param   [in] n The generic handle to downcast.
@details The handle @a n must be for an underlying IntegerNode, otherwise an exception is thrown.
In designs that need to avoid the exception, use Node::type() to determine the actual type of the @a n before downcasting.
This function must be explicitly called (c++ compiler cannot insert it automatically).
@return  A smart IntegerNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_NODE_DOWNCAST
@see     NodeFunctions.cpp example, Node::type(), IntegerNode::operator Node()
*/
IntegerNode::IntegerNode(const Node& n)
{
    /// Downcast from SharedNodeImplPtr to shared_ptr<IntegerNodeImpl>
    shared_ptr<IntegerNodeImpl> ni(dynamic_pointer_cast<IntegerNodeImpl>(n.impl()));
    if (!ni)
        throw E57_EXCEPTION2(E57_ERROR_BAD_NODE_DOWNCAST, "nodeType=" + toString(n.type()));

    /// Set our shared_ptr to the downcast shared_ptr
    impl_ = ni;
}

//! @cond documentNonPublic   The following isn't part of the API, and isn't documented.
IntegerNode::IntegerNode(shared_ptr<IntegerNodeImpl> ni)
: impl_(ni)
{}
//! @endcond

//=====================================================================================
/*!
@class ScaledIntegerNode
@brief   An E57 element encoding a fixed point number.
@details
An ScaledIntegerNode is a terminal node (i.e. having no children) that holds a fixed point number encoded by an integer @c rawValue, a double precision floating point @c scale, an double precision floating point @c offset, and integer minimum/maximum bounds.

The @c minimum attribute may be a number in the interval [-2^63, 2^63).
The @c maximum attribute may be a number in the interval [minimum, 2^63).
The @c rawValue may be a number in the interval [minimum, maximum].
The @c scaledValue is a calculated double precision floating point number derived from: scaledValue = rawValue*scale + offset.

See Node class discussion for discussion of the common functions that StructureNode supports.

@section ScaledIntegerNode_invariant Class Invariant
A class invariant is a list of statements about an object that are always true before and after any operation on the object.
An invariant is useful for testing correct operation of an implementation.
Statements in an invariant can involve only externally visible state, or can refer to internal implementation-specific state that is not visible to the API user.
The following C++ code checks externally visible state for consistancy and throws an exception if the invariant is violated:
@dontinclude E57Format.cpp
@skip begin ScaledIntegerNode::checkInvariant
@skip checkInvariant(
@until end ScaledIntegerNode::checkInvariant

@see     Node
*/

/*!
@brief   Create an E57 element for storing a fixed point number.
@param   [in] destImageFile   The ImageFile where the new node will eventually be stored.
@param   [in] rawValue  The raw integer value of the element.
@param   [in] minimum   The smallest rawValue that the element may take.
@param   [in] maximum   The largest rawWalue that the element may take.
@param   [in] scale     The scaling factor used to compute scaledValue from rawValue.
@param   [in] offset    The offset factor used to compute scaledValue from rawValue.
@details
An ScaledIntegerNode stores an integer value, a lower and upper bound, and two conversion factors.
The ScaledIntegerNode class corresponds to the ASTM E57 standard ScaledInteger element.
See the class discussion at bottom of ScaledIntegerNode page for more details.

The @a destImageFile indicates which ImageFile the ScaledIntegerNode will eventually be attached to.
A node is attached to an ImageFile by adding it underneath the predefined root of the ImageFile (gotten from ImageFile::root).
It is not an error to fail to attach the ScaledIntegerNode to the @a destImageFile.
It is an error to attempt to attach the ScaledIntegerNode to a different ImageFile.

@b Warning: it is an error to give an @a rawValue outside the @a minimum / @a maximum bounds, even if the ScaledIntegerNode is destined to be used in a CompressedVectorNode prototype (where the @a rawValue will be ignored).
If the ScaledIntegerNode is to be used in a prototype, it is recommended to specify a @a rawValue = 0 if 0 is within bounds, or a @a rawValue = @a minimum if 0 is not within bounds.
@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be true).
@pre     The @a destImageFile must have been opened in write mode (i.e. destImageFile.isWritable() must be true).
@pre     minimum <= rawValue <= maximum
@pre     scale != 0
@return  A smart ScaledIntegerNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_VALUE_OUT_OF_BOUNDS
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerCreate.cpp example, ScaledIntegerNode::rawValue, Node, CompressedVectorNode, CompressedVectorNode::prototype
*/
ScaledIntegerNode::ScaledIntegerNode(ImageFile destImageFile, int64_t rawValue, int64_t  minimum, int64_t  maximum, double scale, double offset)
: impl_(new ScaledIntegerNodeImpl(destImageFile.impl(), rawValue, minimum, maximum, scale, offset))
{
}
ScaledIntegerNode::ScaledIntegerNode(ImageFile destImageFile, int rawValue, int64_t  minimum, int64_t  maximum, double scale, double offset)
: impl_(new ScaledIntegerNodeImpl(destImageFile.impl(), static_cast<int64_t>(rawValue), minimum, maximum, scale, offset))
{
}
ScaledIntegerNode::ScaledIntegerNode(ImageFile destImageFile, int rawValue, int  minimum, int  maximum, double scale, double offset)
: impl_(new ScaledIntegerNodeImpl(destImageFile.impl(), static_cast<int64_t>(rawValue), static_cast<int64_t>(minimum), static_cast<int64_t>(maximum), scale, offset))
{
}
/*!
@brief   This second constructor create an E57 element for storing a fixed point number but does the scaling for you.
@param   [in] destImageFile   The ImageFile where the new node will eventually be stored.
@param   [in] scaledValue     The scaled integer value of the element.
@param   [in] scaledMinimum   The smallest scaledValue that the element may take.
@param   [in] scaledMaximum   The largest scaledValue that the element may take.
@param   [in] scale     The scaling factor used to compute scaledValue from rawValue.
@param   [in] offset    The offset factor used to compute scaledValue from rawValue.
@details
An ScaledIntegerNode stores an integer value, a lower and upper bound, and two conversion factors.
This ScaledIntegerNode constructor calculates the rawValue, minimum, and maximum by doing the floor((scaledValue - offset)/scale + .5) on each scaled parameters.
@b Warning: it is an error to give an @a rawValue outside the @a minimum / @a maximum bounds, even if the ScaledIntegerNode is destined to be used in a CompressedVectorNode prototype (where the @a rawValue will be ignored).
If the ScaledIntegerNode is to be used in a prototype, it is recommended to specify a @a rawValue = 0 if 0 is within bounds, or a @a rawValue = @a minimum if 0 is not within bounds.
@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be true).
@pre     The @a destImageFile must have been opened in write mode (i.e. destImageFile.isWritable() must be true).
@pre     scaledMinimum <= scaledValue <= scaledMaximum
@pre     scale != 0
@return  A smart ScaledIntegerNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_VALUE_OUT_OF_BOUNDS
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerCreate.cpp example, ScaledIntegerNode::scaledValue, Node, CompressedVectorNode, CompressedVectorNode::prototype
*/
ScaledIntegerNode::ScaledIntegerNode(ImageFile destImageFile, double scaledValue, double  scaledMinimum, double  scaledMaximum, double scale, double offset)
: impl_(new ScaledIntegerNodeImpl(destImageFile.impl(), scaledValue, scaledMinimum, scaledMaximum, scale, offset))
{
}
//! @brief   Is this a root node.
//! @copydetails Node::isRoot()
bool ScaledIntegerNode::isRoot() const
{
    return impl_->isRoot();
}

//! @brief   Return parent of node, or self if a root node.
//! @copydetails Node::parent()
Node ScaledIntegerNode::parent() const
{
    return Node(impl_->parent());
}

//! @brief   Get absolute pathname of node.
//! @copydetails Node::pathName()
ustring ScaledIntegerNode::pathName() const
{
    return impl_->pathName();
}

//! @brief   Get elementName string, that identifies the node in its parent..
//! @copydetails Node::elementName()
ustring ScaledIntegerNode::elementName() const
{
    return impl_->elementName();
}

//! @brief   Get the ImageFile that was declared as the destination for the node when it was created.
//! @copydetails Node::destImageFile()
ImageFile ScaledIntegerNode::destImageFile() const
{
    return ImageFile(impl_->destImageFile());
}

//! @brief   Has node been attached into the tree of an ImageFile.
//! @copydetails Node::isAttached()
bool ScaledIntegerNode::isAttached() const
{
    return impl_->isAttached();
}

/*!
@brief   Get raw unscaled integer value of element.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The raw unscaled integer value stored.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerCreate.cpp example, ScaledIntegerNode::scaledValue, ScaledIntegerNode::minimum, ScaledIntegerNode::maximum
*/
int64_t ScaledIntegerNode::rawValue() const
{
    return impl_->rawValue();
}

/*!
@brief   Get scaled value of element.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The scaled value (rawValue*scale + offset) calculated from the rawValue stored.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerCreate.cpp example, ScaledIntegerNode::rawValue
*/
double  ScaledIntegerNode::scaledValue() const
{
    return impl_->scaledValue();
}

/*!
@brief   Get the declared minimum that the raw value may take.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The declared minimum that the rawValue may take.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerCreate.cpp example, ScaledIntegerNode::maximum, ScaledIntegerNode::rawValue
*/
int64_t ScaledIntegerNode::minimum() const
{
    return impl_->minimum();
}
/*!
@brief   Get the declared scaled minimum that the scaled value may take.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The declared minimum that the rawValue may take.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerCreate.cpp example, ScaledIntegerNode::scaledMaximum, ScaledIntegerNode::scaledValue
*/
double ScaledIntegerNode::scaledMinimum() const
{
    return impl_->scaledMinimum();
}
/*!
@brief   Get the declared maximum that the raw value may take.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The declared maximum that the rawValue may take.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerCreate.cpp example, ScaledIntegerNode::minimum, ScaledIntegerNode::rawValue
*/
int64_t ScaledIntegerNode::maximum() const
{
    return impl_->maximum();
}
/*!
@brief   Get the declared scaled maximum that the scaled value may take.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The declared maximum that the rawValue may take.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerCreate.cpp example, ScaledIntegerNode::scaledMinimum, ScaledIntegerNode::scaledValue
*/
double ScaledIntegerNode::scaledMaximum() const     //Added by SC
{
    return impl_->scaledMaximum();
}
/*!
@brief   Get declared scaling factor.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The scaling factor used to compute scaledValue from rawValue.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerCreate.cpp example, ScaledIntegerNode::scaledValue
*/
double  ScaledIntegerNode::scale() const
{
    return impl_->scale();
}

/*!
@brief   Get declared offset.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The offset used to compute scaledValue from rawValue.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     ScaledIntegerCreate.cpp example, ScaledIntegerNode::scaledValue
*/
double  ScaledIntegerNode::offset() const
{
    return impl_->offset();
}

//! @brief   Diagnostic function to print internal state of object to output stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void ScaledIntegerNode::dump(int indent, std::ostream& os) const
{
    impl_->dump(indent, os);
}
#else
void ScaledIntegerNode::dump(int indent, std::ostream& os) const
{}
#endif

/*!
@brief   Upcast a ScaledIntegerNode handle to a generic Node handle.
@details An upcast is always safe, and the compiler can automatically insert it for initializations of Node variables and Node function arguments.
@return  A smart Node handle referencing the underlying object.
@throw   No E57Exceptions.
@see     NodeFunctions.cpp example, ScaledIntegerNode(const Node&) example, explanation in Node, Node::type(), ScaledIntegerNode(const Node&)
*/
ScaledIntegerNode::operator Node() const
{
    /// Upcast from shared_ptr<ScaledIntegerNodeImpl> to SharedNodeImplPtr and construct a Node object
    return Node(impl_);
}

/*!
@brief   Downcast a generic Node handle to an ScaledIntegerNode handle.
@param   [in] n The generic handle to downcast.
@details The handle @a n must be for an underlying ScaledIntegerNode, otherwise an exception is thrown.
In designs that need to avoid the exception, use Node::type() to determine the actual type of the @a n before downcasting.
This function must be explicitly called (c++ compiler cannot insert it automatically).
@return  A smart ScaledIntegerNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_NODE_DOWNCAST
@see     NodeFunctions.cpp example, Node::type(), ScaledIntegerNode::operator Node()
*/
ScaledIntegerNode::ScaledIntegerNode(const Node& n)
{
    /// Downcast from SharedNodeImplPtr to shared_ptr<ScaledIntegerNodeImpl>
    shared_ptr<ScaledIntegerNodeImpl> ni(dynamic_pointer_cast<ScaledIntegerNodeImpl>(n.impl()));
    if (!ni)
        throw E57_EXCEPTION2(E57_ERROR_BAD_NODE_DOWNCAST, "nodeType=" + toString(n.type()));

    /// Set our shared_ptr to the downcast shared_ptr
    impl_ = ni;
}

//! @cond documentNonPublic   The following isn't part of the API, and isn't documented.
ScaledIntegerNode::ScaledIntegerNode(shared_ptr<ScaledIntegerNodeImpl> ni)
: impl_(ni)
{}
//! @endcond

//=====================================================================================
/*!
@class FloatNode
@brief   An E57 element encoding a single or double precision IEEE floating point number.
@details
An FloatNode is a terminal node (i.e. having no children) that holds an IEEE floating point value, and minimum/maximum bounds.
The precision of the floating point value and attributes may be either single or double precision.
Once the FloatNode value and attributes are set at creation, they may not be modified.

If the precision option of the FloatNode is E57_SINGLE:
The minimum attribute may be a number in the interval [-3.402823466e+38, 3.402823466e+38].
The maximum attribute may be a number in the interval [maximum, 3.402823466e+38].
The value may be a number in the interval [minimum, maximum].

If the precision option of the FloatNode is E57_DOUBLE:
The minimum attribute may be a number in the interval [-1.7976931348623158e+308, 1.7976931348623158e+308].
The maximum attribute may be a number in the interval [maximum, 1.7976931348623158e+308].
The value may be a number in the interval [minimum, maximum].

See Node class discussion for discussion of the common functions that StructureNode supports.

@section FloatNode_invariant Class Invariant
A class invariant is a list of statements about an object that are always true before and after any operation on the object.
An invariant is useful for testing correct operation of an implementation.
Statements in an invariant can involve only externally visible state, or can refer to internal implementation-specific state that is not visible to the API user.
The following C++ code checks externally visible state for consistency and throws an exception if the invariant is violated:
@dontinclude E57Format.cpp
@skip begin FloatNode::checkInvariant
@skip checkInvariant(
@until end FloatNode::checkInvariant

@see     Node
*/

/*!
@brief   Create an E57 element for storing an double precision IEEE floating point number.
@param   [in] destImageFile   The ImageFile where the new node will eventually be stored.
@param   [in] value     The double precision IEEE floating point value of the element.
@param   [in] precision The precision of IEEE floating point to use. May be E57_SINGLE or E57_DOUBLE.
@param   [in] minimum   The smallest value that the value may take.
@param   [in] maximum   The largest value that the value may take.
@details
An FloatNode stores an IEEE floating point number and a lower and upper bound.
The FloatNode class corresponds to the ASTM E57 standard Float element.
See the class discussion at bottom of FloatNode page for more details.

The @a destImageFile indicates which ImageFile the FloatNode will eventually be attached to.
A node is attached to an ImageFile by adding it underneath the predefined root of the ImageFile (gotten from ImageFile::root).
It is not an error to fail to attach the FloatNode to the @a destImageFile.
It is an error to attempt to attach the FloatNode to a different ImageFile.

There is only one FloatNode constructor that handles both E57_SINGLE and E57_DOUBLE precision cases.
If @a precision = E57_SINGLE, then the object will silently round the double precision @a value to the nearest representable single precision value.
In this case, the lower bits will be lost, and if the value is outside the representable range of a single precision number, the exponent may be changed.
The same is true for the @a minimum and @a maximum arguments.

@b Warning: it is an error to give an @a value outside the @a minimum / @a maximum bounds, even if the FloatNode is destined to be used in a CompressedVectorNode prototype (where the @a value will be ignored).
If the FloatNode is to be used in a prototype, it is recommended to specify a @a value = 0 if 0 is within bounds, or a @a value = @a minimum if 0 is not within bounds.
@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be true).
@pre     The @a destImageFile must have been opened in write mode (i.e. destImageFile.isWritable() must be true).
@pre     minimum <= value <= maximum
@return  A smart FloatNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_VALUE_OUT_OF_BOUNDS
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     FloatCreate.cpp example, FloatPrecision, FloatNode::value, Node, CompressedVectorNode, CompressedVectorNode::prototype
*/
FloatNode::FloatNode(ImageFile destImageFile, double value, FloatPrecision precision, double minimum, double maximum)
: impl_(new FloatNodeImpl(destImageFile.impl(), value, precision, minimum, maximum))
{
}

//! @brief   Is this a root node.
//! @copydetails Node::isRoot()
bool FloatNode::isRoot() const
{
    return impl_->isRoot();
}

//! @brief   Return parent of node, or self if a root node.
//! @copydetails Node::parent()
Node FloatNode::parent() const
{
    return Node(impl_->parent());
}

//! @brief   Get absolute pathname of node.
//! @copydetails Node::pathName()
ustring FloatNode::pathName() const
{
    return impl_->pathName();
}

//! @brief   Get elementName string, that identifies the node in its parent..
//! @copydetails Node::elementName()
ustring FloatNode::elementName() const
{
    return impl_->elementName();
}

//! @brief   Get the ImageFile that was declared as the destination for the node when it was created.
//! @copydetails Node::destImageFile()
ImageFile FloatNode::destImageFile() const
{
    return ImageFile(impl_->destImageFile());
}

//! @brief   Has node been attached into the tree of an ImageFile.
//! @copydetails Node::isAttached()
bool FloatNode::isAttached() const
{
    return impl_->isAttached();
}

/*!
@brief   Get IEEE floating point value stored.
@details
If precision is E57_SINGLE, the single precision value is returned as a double.
If precision is E57_DOUBLE, the double precision value is returned as a double.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The IEEE floating point value stored, represented as a double.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     FloatCreate.cpp example, FloatNode::minimum, FloatNode::maximum
*/
double FloatNode::value() const
{
    return impl_->value();
}

/*!
@brief   Get declared precision of the floating point number.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The declared precision of the floating point number, either ::E57_SINGLE or ::E57_DOUBLE.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     FloatCreate.cpp example, FloatPrecision
*/
FloatPrecision FloatNode::precision() const
{
    return impl_->precision();
}

/*!
@brief   Get the declared minimum that the value may take.
@details
If precision is E57_SINGLE, the single precision minimum is returned as a double.
If precision is E57_DOUBLE, the double precision minimum is returned as a double.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The declared minimum that the value may take.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     FloatCreate.cpp example, FloatNode::maximum, FloatNode::value
*/
double FloatNode::minimum() const
{
    return impl_->minimum();
}

/*!
@brief   Get the declared maximum that the value may take.
@details
If precision is E57_SINGLE, the single precision maximum is returned as a double.
If precision is E57_DOUBLE, the double precision maximum is returned as a double.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The declared maximum that the value may take.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     FloatCreate.cpp example, FloatNode::minimum, FloatNode::value
*/
double FloatNode::maximum() const
{
    return impl_->maximum();
}

//! @brief   Diagnostic function to print internal state of object to output stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void FloatNode::dump(int indent, std::ostream& os) const
{
    impl_->dump(indent, os);
}
#else
void FloatNode::dump(int indent, std::ostream& os) const
{}
#endif

/*!
@brief   Upcast a FloatNode handle to a generic Node handle.
@details An upcast is always safe, and the compiler can automatically insert it for initializations of Node variables and Node function arguments.
@return  A smart Node handle referencing the underlying object.
@throw   No E57Exceptions.
@see     NodeFunctions.cpp example, FloatNode(const Node&) example, explanation in Node, Node::type()
*/
FloatNode::operator Node() const
{
    /// Upcast from shared_ptr<FloatNodeImpl> to SharedNodeImplPtr and construct a Node object
    return Node(impl_);
}

/*!
@brief   Downcast a generic Node handle to a FloatNode handle.
@param   [in] n The generic handle to downcast.
@details The handle @a n must be for an underlying FloatNode, otherwise an exception is thrown.
In designs that need to avoid the exception, use Node::type() to determine the actual type of the @a n before downcasting.
This function must be explicitly called (c++ compiler cannot insert it automatically).
@return  A smart FloatNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_NODE_DOWNCAST
@see     NodeFunctions.cpp example, Node::type(), FloatNode::operator Node()
*/
FloatNode::FloatNode(const Node& n)
{
    /// Downcast from SharedNodeImplPtr to shared_ptr<FloatNodeImpl>
    shared_ptr<FloatNodeImpl> ni(dynamic_pointer_cast<FloatNodeImpl>(n.impl()));
    if (!ni)
        throw E57_EXCEPTION2(E57_ERROR_BAD_NODE_DOWNCAST, "nodeType=" + toString(n.type()));

    /// Set our shared_ptr to the downcast shared_ptr
    impl_ = ni;
}

//! @cond documentNonPublic   The following isn't part of the API, and isn't documented.
FloatNode::FloatNode(shared_ptr<FloatNodeImpl> ni)
: impl_(ni)
{}
//! @endcond

//=====================================================================================
/*!
@class StringNode
@brief   An E57 element encoding a Unicode character string value.
@details
A StringNode is a terminal node (i.e. having no children) that holds an Unicode character string encoded in UTF-8.
Once the StringNode value is set at creation, it may not be modified.

See Node class discussion for discussion of the common functions that StructureNode supports.

@section StringNode_invariant Class Invariant
A class invariant is a list of statements about an object that are always true before and after any operation on the object.
An invariant is useful for testing correct operation of an implementation.
Statements in an invariant can involve only externally visible state, or can refer to internal implementation-specific state that is not visible to the API user.
The following C++ code checks externally visible state for consistency and throws an exception if the invariant is violated:
@dontinclude E57Format.cpp
@skip begin StringNode::checkInvariant
@skip checkInvariant(
@until end StringNode::checkInvariant

@see     Node
*/

/*!
@brief   Create an element storing a Unicode character string.
@param   [in] destImageFile The ImageFile where the new node will eventually be stored.
@param   [in] value         The Unicode character string value of the element, in UTF-8 encoding.
@details
The StringNode class corresponds to the ASTM E57 standard String element.
See the class discussion at bottom of StringNode page for more details.

The @a destImageFile indicates which ImageFile the StringNode will eventually be attached to.
A node is attached to an ImageFile by adding it underneath the predefined root of the ImageFile (gotten from ImageFile::root).
It is not an error to fail to attach the StringNode to the @a destImageFile.
It is an error to attempt to attach the StringNode to a different ImageFile.

If the StringNode is to be used in a CompressedVectorNode prototype, it is recommended to specify a @a value = "" (the default value).
@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be true).
@pre     The @a destImageFile must have been opened in write mode (i.e. destImageFile.isWritable() must be true).
@return  A smart StringNode handle referencing the underlying object.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     StringCreate.cpp example, StringNode::value, Node, CompressedVectorNode, CompressedVectorNode::prototype
*/
StringNode::StringNode(ImageFile destImageFile, const ustring &value)
: impl_(new StringNodeImpl(destImageFile.impl(), value))
{
}

//! @brief   Is this a root node.
//! @copydetails Node::isRoot()
bool StringNode::isRoot() const
{
    return impl_->isRoot();
}

//! @brief   Return parent of node, or self if a root node.
//! @copydetails Node::parent()
Node StringNode::parent() const
{
    return Node(impl_->parent());
}

//! @brief   Get absolute pathname of node.
//! @copydetails Node::pathName()
ustring StringNode::pathName() const
{
    return impl_->pathName();
}

//! @brief   Get elementName string, that identifies the node in its parent..
//! @copydetails Node::elementName()
ustring StringNode::elementName() const
{
   return impl_->elementName();
}

//! @brief   Get the ImageFile that was declared as the destination for the node when it was created.
//! @copydetails Node::destImageFile()
ImageFile StringNode::destImageFile() const
{
    return ImageFile(impl_->destImageFile());
}

//! @brief   Has node been attached into the tree of an ImageFile.
//! @copydetails Node::isAttached()
bool StringNode::isAttached() const
{
    return impl_->isAttached();
}

/*!
@brief   Get Unicode character string value stored.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The Unicode character string value stored.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     StringCreate.cpp example
*/
ustring StringNode::value() const
{
    return impl_->value();
}

//! @brief   Diagnostic function to print internal state of object to output stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void StringNode::dump(int indent, std::ostream& os) const
{
    impl_->dump(indent, os);
}
#else
void StringNode::dump(int indent, std::ostream& os) const
{}
#endif

/*!
@brief   Upcast a StringNode handle to a generic Node handle.
@details An upcast is always safe, and the compiler can automatically insert it for initializations of Node variables and Node function arguments.
@return  A smart Node handle referencing the underlying object.
@throw   No E57Exceptions.
@see     NodeFunctions.cpp example, StringNode(const Node&) example, explanation in Node, Node::type(), StringNode(const Node&)
*/
StringNode::operator Node() const
{
    /// Upcast from shared_ptr<StringNodeImpl> to SharedNodeImplPtr and construct a Node object
    return Node(impl_);
}

/*!
@brief   Downcast a generic Node handle to a StringNode handle.
@param   [in] n The generic handle to downcast.
@details The handle @a n must be for an underlying StringNode, otherwise an exception is thrown.
In designs that need to avoid the exception, use Node::type() to determine the actual type of the @a n before downcasting.
This function must be explicitly called (c++ compiler cannot insert it automatically).
@return  A smart StringNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_NODE_DOWNCAST
@see     NodeFunctions.cpp example, Node::type(), StringNode::operator Node()
*/
StringNode::StringNode(const Node& n)
{
    /// Downcast from SharedNodeImplPtr to shared_ptr<StringNodeImpl>
    shared_ptr<StringNodeImpl> ni(dynamic_pointer_cast<StringNodeImpl>(n.impl()));
    if (!ni)
        throw E57_EXCEPTION2(E57_ERROR_BAD_NODE_DOWNCAST, "nodeType=" + toString(n.type()));

    /// Set our shared_ptr to the downcast shared_ptr
    impl_ = ni;
}

//! @cond documentNonPublic   The following isn't part of the API, and isn't documented.
StringNode::StringNode(shared_ptr<StringNodeImpl> ni)
: impl_(ni)
{}
//! @endcond

//=====================================================================================
/*!
@class BlobNode
@brief   An E57 element encoding an fixed-length sequence of bytes with an opaque format.
@details
A BlobNode is a terminal node (i.e. having no children) that holds an opaque, fixed-length sequence of bytes.
The number of bytes in the BlobNode is declared at creation time.
The content of the blob is stored within the E57 file in an efficient binary format (but not compressed).
The BlobNode cannot grow after it is created.
There is no ordering constraints on how content of a BlobNode may be accessed (i.e. it is random access).
BlobNodes in an ImageFile opened for reading are read-only.

There are two categories of BlobNodes, distinguished by their usage: private BlobNodes and public BlobNodes.
In a private BlobNode, the format of its content bytes is not published.
This is useful for storing proprietary data that a writer does not wish to share with all readers.
Rather than put this information in a separate file, the writer can embed the file inside the E57 file so it cannot be lost.

In a public BlobNode, the format is published or follows some industry standard format (e.g. JPEG).
Rather than reinvent the wheel in applications that are already well-served by an existing format standard, an E57 file writer can just embed an existing file as an "attachment" in a BlobNode.
The internal format of a public BlobNode is not enforced by the Foundation API.
It is recommended that there be some mechanism for a reader to know ahead of time which format the BlobNode content adheres to (either specified by a document, or encoded by some scheme in the E57 Element tree).

The BlobNode is the one node type where the set-once policy is not strictly enforced.
It is possible to write the same byte location in a BlobNode several times.
However it is not recommended.

See Node class discussion for discussion of the common functions that StructureNode supports.

@section BlobNode_invariant Class Invariant
A class invariant is a list of statements about an object that are always true before and after any operation on the object.
An invariant is useful for testing correct operation of an implementation.
Statements in an invariant can involve only externally visible state, or, can refer to internal implementation-specific state that is not visible to the API user.
The following C++ code checks externally visible state for consistency and throws an exception if the invariant is violated:
@dontinclude E57Format.cpp
@skip begin BlobNode::checkInvariant
@skip checkInvariant(
@until end BlobNode::checkInvariant

@see     Node
*/

/*!
@brief   Create an element for storing a sequence of bytes with an opaque format.
@param   [in] destImageFile The ImageFile where the new node will eventually be stored.
@param   [in] byteCount     The number of bytes reserved in the ImageFile for holding the blob.
@details
The BlobNode class corresponds to the ASTM E57 standard Blob element.
See the class discussion at bottom of BlobNode page for more details.

The E57 Foundation Implementation may pre-allocate disk space in the ImageFile to store the declared length of the blob.
The disk must have enough free space to store @a byteCount bytes of data.
The data of a newly created BlobNode is initialized to zero.

The @a destImageFile indicates which ImageFile the BlobNode will eventually be attached to.
A node is attached to an ImageFile by adding it underneath the predefined root of the ImageFile (gotten from ImageFile::root).
It is not an error to fail to attach the BlobNode to the @a destImageFile.
It is an error to attempt to attach the BlobNode to a different ImageFile.
@pre     The @a destImageFile must be open (i.e. destImageFile.isOpen() must be true).
@pre     The @a destImageFile must have been opened in write mode (i.e. destImageFile.isWritable() must be true).
@pre     byteCount >= 0
@return  A smart BlobNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     BlobCreate.cpp example, Node, BlobNode::read, BlobNode::write
*/
BlobNode::BlobNode(ImageFile destImageFile, int64_t byteCount)
: impl_(new BlobNodeImpl(destImageFile.impl(), byteCount))
{
}

//! @brief   Is this a root node.
//! @copydetails Node::isRoot()
bool BlobNode::isRoot() const
{
    return impl_->isRoot();
}

//! @brief   Return parent of node, or self if a root node.
//! @copydetails Node::parent()
Node BlobNode::parent() const
{
    return Node(impl_->parent());
}

//! @brief   Get absolute pathname of node.
//! @copydetails Node::pathName()
ustring BlobNode::pathName() const
{
    return impl_->pathName();
}

//! @brief   Get elementName string, that identifies the node in its parent..
//! @copydetails Node::elementName()
ustring BlobNode::elementName() const
{
    return impl_->elementName();
}

//! @brief   Get the ImageFile that was declared as the destination for the node when it was created.
//! @copydetails Node::destImageFile()
ImageFile BlobNode::destImageFile() const
{
    return ImageFile(impl_->destImageFile());
}

//! @brief   Has node been attached into the tree of an ImageFile.
//! @copydetails Node::isAttached()
bool BlobNode::isAttached() const
{
    return impl_->isAttached();
}

/*!
@brief   Get size of blob declared when it was created.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@post    No visible state is modified.
@return  The declared size of the blob when it was created.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     BlobCreate.cpp example, BlobNode::read, BlobNode::write
*/
int64_t BlobNode::byteCount() const
{
    return impl_->byteCount();
}

/*!
@brief   Read a buffer of bytes from a blob.
@param   [in] buf   A memory buffer to store bytes read from the blob.
@param   [in] start The index of the first byte in blob to read.
@param   [in] count The number of bytes to read.
@details
The memory buffer @a buf must be able to store at least @a count bytes.
The data is stored in a binary section of the ImageFile with checksum protection, so undetected corruption is very unlikely.
It is an error to attempt to read outside the declared size of the Blob.
The format of the data read is opaque (unspecified by the ASTM E57 data format standard).
Since @a buf is a byte buffer, byte ordering is irrelevant (it will come out in the same order that it went in).
There is no constraint on the ordering of reads.
Any part of the Blob data can be read zero or more times.
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@pre     buf != NULL
@pre     0 <= @a start < byteCount()
@pre     0 <= count
@pre     (@a start + @a count) < byteCount()
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_LSEEK_FAILED
@throw   ::E57_ERROR_READ_FAILED
@throw   ::E57_ERROR_BAD_CHECKSUM
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     BlobCreate.cpp example, BlobNode::byteCount, BlobNode::write
*/
void BlobNode::read(uint8_t* buf, int64_t start, size_t count)
{
    impl_->read(buf, start, count);
}

/*!
@brief   Write a buffer of bytes to a blob.
@param   [in] buf   A memory buffer of bytes to write to the blob.
@param   [in] start The index of the first byte in blob to write to.
@param   [in] count The number of bytes to write.
@details
The memory buffer @a buf must store at least @a count bytes.
The data is stored in a binary section of the ImageFile with checksum protection, so undetected corruption is very unlikely.
It is an error to attempt to write outside the declared size of the Blob.
The format of the data written is opaque (unspecified by the ASTM E57 data format standard).
Since @a buf is a byte buffer, byte ordering is irrelevant (it will come out in the same order that it went in).
There is no constraint on the ordering of writes.
It is not an error to write a portion of the BlobNode data more than once, or not at all.
Initially all the BlobNode data is zero, so if a portion is not written, it will remain zero.
The BlobNode is one of the two node types that must be attached to the root of a write mode ImageFile before write operations can be performed (the other type is CompressedVectorNode).
@pre     The destination ImageFile must be open (i.e. destImageFile().isOpen()).
@pre     The associated destImageFile must have been opened in write mode (i.e. destImageFile().isWritable()).
@pre     The BlobNode must be attached to an ImageFile (i.e. isAttached()).
@pre     buf != NULL
@pre     0 <= @a start < byteCount()
@pre     0 <= count
@pre     (@a start + @a count) < byteCount()
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_NODE_UNATTACHED
@throw   ::E57_ERROR_LSEEK_FAILED
@throw   ::E57_ERROR_READ_FAILED
@throw   ::E57_ERROR_WRITE_FAILED
@throw   ::E57_ERROR_BAD_CHECKSUM
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     BlobCreate.cpp example, BlobNode::byteCount, BlobNode::read
*/
void BlobNode::write(uint8_t* buf, int64_t start, size_t count)
{
    impl_->write(buf, start, count);
}

//! @brief   Diagnostic function to print internal state of object to output stream in an indented format.
//! @copydetails Node::dump()
#ifdef E57_DEBUG
void BlobNode::dump(int indent, std::ostream& os) const
{
    impl_->dump(indent, os);
}
#else
void BlobNode::dump(int indent, std::ostream& os) const
{}
#endif

/*!
@brief   Upcast a BlobNode handle to a generic Node handle.
@details An upcast is always safe, and the compiler can automatically insert it for initializations of Node variables and Node function arguments.
@return  A smart Node handle referencing the underlying object.
@throw   No E57Exceptions.
@see     NodeFunctions.cpp example, BlobNode(const Node&) example, explanation in Node, Node::type(), BlobNode(const Node&)
*/
BlobNode::operator Node() const
{
    /// Upcast from shared_ptr<StringNodeImpl> to SharedNodeImplPtr and construct a Node object
    return Node(impl_);
}

/*!
@brief   Downcast a generic Node handle to a BlobNode handle.
@param   [in] n The generic handle to downcast.
@details The handle @a n must be for an underlying BlobNode, otherwise an exception is thrown.
In designs that need to avoid the exception, use Node::type() to determine the actual type of the @a n before downcasting.
This function must be explicitly called (c++ compiler cannot insert it automatically).
@return  A smart BlobNode handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_NODE_DOWNCAST
@see     NodeFunctions.cpp example, Node::type(), BlobNode::operator Node()
*/
BlobNode::BlobNode(const Node& n)
{
    /// Downcast from SharedNodeImplPtr to shared_ptr<BlobNodeImpl>
    shared_ptr<BlobNodeImpl> ni(dynamic_pointer_cast<BlobNodeImpl>(n.impl()));
    if (!ni)
        throw E57_EXCEPTION2(E57_ERROR_BAD_NODE_DOWNCAST, "nodeType=" + toString(n.type()));

    /// Set our shared_ptr to the downcast shared_ptr
    impl_ = ni;
}

//! @cond documentNonPublic   The following isn't part of the API, and isn't documented.
BlobNode::BlobNode(ImageFile destImageFile, int64_t fileOffset, int64_t length)
: impl_(new BlobNodeImpl(destImageFile.impl(), fileOffset, length))
{}

BlobNode::BlobNode(shared_ptr<BlobNodeImpl> ni)
: impl_(ni)
{}
//! @endcond

//=====================================================================================
/*!
@class ImageFile
@brief   An ASTM E57 3D format file object.
@details
@section imagefile_ClassOverview Class overview
The ImageFile class represents the state of an ASTM E57 format data file.
An ImageFile may be created from an E57 file on the disk (read mode).
An new ImageFile may be created to write an E57 file to disk (write mode).

E57 files are organized in a tree structure.
Each ImageFile object has a predefined root node (of type StructureNode).
In a write mode ImageFile, the root node is initially empty.
In a read mode ImageFile, the root node is populated by the tree stored in the .e57 file on disk.

@section imagefile_OpenClose The open/close state
An ImageFile object, opened in either mode (read/write), can be in one of two states: open or closed.
An ImageFile in the open state is ready to perform transfers of data and to be interrogated.
An ImageFile in the closed state cannot perform any further transfers, and has very limited ability to be interrogated.
Note entering the closed state is different than destroying the ImageFile object.
An ImageFile object can still exist and be in the closed state.
When created, the ImageFile is initially open.

The ImageFile state can transition to the closed state in two ways.
The programmer can call ImageFile::close after all required processing has completed.
The programmer can call ImageFile::cancel if it is determined that the ImageFile is no longer needed.

@section imagefile_Extensions Extensions

Basically in an E57 file, "extension = namespace + rules + meaning".
The "namespace" ensures that element names don't collide.
The "rules" may be written on paper, or partly codified in a computer grammar.
The "meaning" is a definition of what was measured, what the numbers in the file mean.

Extensions are identified by URIs.
Extensions are not identified by prefixes.
Prefixes are a shorthand, used in a particular file, to make the element names more palatable for humans.
When thinking about a prefixed element name, in your mind you should immediately substitute the URI for the prefix.
For example, think "http://www.example.com/DemoExtension:extra2" rather than "demo:extra2", if the prefix "demo" is declared in the file to be a shorthand for the URI "http://www.example.com/DemoExtension".

The rules are statements of: what is valid, what element names are possible, what values are possible.
The rules establish the answer to the following yes/no question: "Is this extended E57 file valid?".
The rules divide all possible files into two sets: valid files and invalid files.

The "meanings" part of the above equation defines what the files in the first set, the valid files, actually mean.
This definition usually comes in the form of documentation of the content of each new element in the format and how they relate to the other elements.

An element name in an E57 file is a member of exactly one namespace (either the default namespace defined in the ASTM standard, or an extension namespace).
Rules about the structure of an E57 extension (what element names can appear where), are implicitly assumed only to govern the element names within the namespace of the extension.
Element names in other namespaces are unconstrained.
This is because a reader is required to ignore elements in namespaces that are unfamiliar (to treat them as if they didn't exist).
This enables a writer to "tack on" new elements into pre-defined structures (e.g. structures defined in the ASTM standard), without fear that it will confuse a reader that is only familiar with the old format.
This allows an extension designer to communicate to two sets of readers: the old readers that will understand the information in the old base format, and the new-fangled readers that will be able to read the base format and the extra information stored in element names in the extended namespace.

@section ImageFile_invariant Class Invariant
A class invariant is a list of statements about an object that are always true before and after any operation on the object.
An invariant is useful for testing correct operation of an implementation.
Statements in an invariant can involve only externally visible state, or can refer to internal implementation-specific state that is not visible to the API user.
The following C++ code checks externally visible state for consistency and throws an exception if the invariant is violated:
@dontinclude E57Format.cpp
@skip begin ImageFile::checkInvariant
@skip checkInvariant(
@until end ImageFile::checkInvariant
*/

/*!
@brief   Open an ASTM E57 imaging data file for reading/writing.
@param   [in] fname File name to open.
Support of '\' as a directory separating character is system dependent.
For maximum portability, it is recommended that '/' be used as a directory separator in file names.
Special device file name support are implementation dependent (e.g. "\\.\PhysicalDrive3" or "/dev/hd3").
It is recommended that files that meet all of the requirements for a legal ASTM E57 file format use the extension @c ".e57".
It is recommended that files that utilize the low-level E57 element data types, but do not have all the required element names required by ASTM E57 file format standard use the file extension @c "._e57".
@param   [in] mode Either "w" for writing or "r" for reading.
@param   [in] checksumPolicy The percentage of checksums we compute and verify as an int. Clamped to 0-100.
@details

@par Write Mode
In write mode, the file cannot be already open.
A file with name given by @a fname is immediately created on the disk.
This file may grow as a result of operations on the ImageFile.
Which API functions write data to the file are implementation dependent.
Thus any API operation that stores data may fail as a result of insufficient free disk space.
Read API operations are legal for an ImageFile opened in write mode.

@par Read Mode
Read mode files may be shared.
Write API operations are not legal for an ImageFile opened in read mode (i.e. the ImageFile is read-only).
There is no API support for appending data onto an existing E57 data file.

@post    Resulting ImageFile is in @c open state if constructor succeeds (no exception thrown).
@return  A smart ImageFile handle referencing the underlying object.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_OPEN_FAILED
@throw   ::E57_ERROR_LSEEK_FAILED
@throw   ::E57_ERROR_READ_FAILED
@throw   ::E57_ERROR_WRITE_FAILED
@throw   ::E57_ERROR_BAD_CHECKSUM
@throw   ::E57_ERROR_BAD_FILE_SIGNATURE
@throw   ::E57_ERROR_UNKNOWN_FILE_VERSION
@throw   ::E57_ERROR_BAD_FILE_LENGTH
@throw   ::E57_ERROR_XML_PARSER_INIT
@throw   ::E57_ERROR_XML_PARSER
@throw   ::E57_ERROR_BAD_XML_FORMAT
@throw   ::E57_ERROR_BAD_CONFIGURATION
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     HelloWorld.cpp example, IntegerNode, ScaledIntegerNode, FloatNode, StringNode, BlobNode, StructureNode, VectorNode, CompressedVectorNode, E57Exception, E57Utilities::E57Utilities
*/
ImageFile::ImageFile(const ustring& fname, const ustring& mode, ReadChecksumPolicy checksumPolicy)
: impl_( new ImageFileImpl( checksumPolicy ) )
{
    /// Do second phase of construction, now that ImageFile object is complete.
    impl_->construct2(fname, mode);
}

/*!
@brief   Get the pre-established root StructureNode of the E57 ImageFile.
@details The root node of an ImageFile always exists and is always type StructureNode.
The root node is empty in a newly created write mode ImageFile.
@pre     This ImageFile must be open (i.e. isOpen()).
@return  A smart StructureNode handle referencing the underlying object.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     HelloWorld.cpp example, StructureNode.
*/
StructureNode ImageFile::root() const
{
    return StructureNode(impl_->root());
}

/*!
@brief   Complete any write operations on an ImageFile, and close the file on the disk.
@details
Completes the writing of the state of the ImageFile to the disk.
Some API implementations may store significant portions of the state of the ImageFile in memory.
This state is moved into the disk file before it is closed.
Any errors in finishing the writing are reported by throwing an exception.
If an exception is thrown, depending on the error code, the ImageFile may enter the closed state.
If no exception is thrown, then the file on disk will be an accurate representation of the ImageFile.

@b Warning: if the ImageFile::close function is not called, and the ImageFile destructor is invoked with the ImageFile in the open state, the associated disk file will be deleted and the ImageFile will @em not be saved to the disk (the same outcome as calling ImageFile::cancel).
The reason for this is that any error conditions can't be reported from a destructor, so the user can't be assured that the destruction/close completed successfully.
It is strongly recommended that this close function be called before the ImageFile is destroyed.

It is not an error if ImageFile is already closed.
@post    ImageFile is in @c closed state.
@throw   ::E57_ERROR_LSEEK_FAILED
@throw   ::E57_ERROR_READ_FAILED
@throw   ::E57_ERROR_WRITE_FAILED
@throw   ::E57_ERROR_CLOSE_FAILED
@throw   ::E57_ERROR_BAD_CHECKSUM
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     HelloWorld.cpp example, ImageFile::cancel, ImageFile::isOpen
*/
void ImageFile::close()
{
    impl_->close();
}

/*!
@brief   Stop I/O operations and delete a partially written ImageFile on the disk.
@details
If the ImageFile is write mode, the associated file on the disk is closed and deleted, and the ImageFile goes to the closed state.
If the ImageFile is read mode, the behavior is same as calling ImageFile::close, but no exceptions are thrown.
It is not an error if ImageFile is already closed.
@post    ImageFile is in @c closed state.
@throw   No E57Exceptions.
@see     Cancel.cpp example, ImageFile::ImageFile, ImageFile::close, ImageFile::isOpen
*/
void ImageFile::cancel()
{
    impl_->cancel();
}

/*!
@brief   Test whether ImageFile is still open for accessing.
@post    No visible state is modified.
@return  true if ImageFile is in @c open state.
@throw   No E57Exceptions.
@see     Cancel.cpp example, ImageFile::ImageFile, ImageFile::close
*/
bool ImageFile::isOpen() const
{
    return impl_->isOpen();
}

/*!
@brief   Test whether ImageFile was opened in write mode.
@post    No visible state is modified.
@return  true if ImageFile was opened in write mode.
@throw   No E57Exceptions.
@see     SourceDestBufferFunctions.cpp example, ImageFile::ImageFile, ImageFile::isOpen
*/
bool ImageFile::isWritable() const
{
    return impl_->isWriter();
}

/*!
@brief   Get the file name the ImageFile was created with.
@post    No visible state is modified.
@return  The file name the ImageFile was created with.
@throw   No E57Exceptions.
@see     NodeFunctions.cpp example, Cancel.cpp example, ImageFile::ImageFile
*/
ustring ImageFile::fileName() const
{
    return impl_->fileName();
}

/*!
@brief   Get current number of open CompressedVectorWriter objects writing to ImageFile.
@details
CompressedVectorWriter objects that still exist, but are in the closed state aren't counted.
CompressedVectorWriter objects are created by the CompressedVectorNode::writer function.
@pre     This ImageFile must be open (i.e. isOpen()).
@post    No visible state is modified.
@return  The current number of open CompressedVectorWriter objects writing to ImageFile.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBufferFunctions.cpp example, CompressedVectorNode::writer, CompressedVectorWriter
*/
int ImageFile::writerCount() const
{
    return impl_->writerCount();
}

/*!
@brief   Get current number of open CompressedVectorReader objects reading from ImageFile.
@details
CompressedVectorReader objects that still exist, but are in the closed state aren't counted.
CompressedVectorReader objects are created by the CompressedVectorNode::reader function.
@pre     This ImageFile must be open (i.e. isOpen()).
@post    No visible state is modified.
@return  The current number of open CompressedVectorReader objects reading from ImageFile.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     SourceDestBuffer::pathName() example, CompressedVectorNode::reader, CompressedVectorReader
*/
int ImageFile::readerCount() const
{
    return impl_->readerCount();
}

/*!
@brief   Declare the use of an E57 extension in an ImageFile being written.
@param   [in] prefix    The shorthand name of the extension to use in element names.
@param   [in] uri       The Uniform Resource Identifier string to associate with the prefix in the ImageFile.
@details
The (@a prefix, @a uri) pair is registered in the known extensions of the ImageFile.
Both @a prefix and @a uri must be unique in the ImageFile.
It is not legal to declare a URI associated with the default namespace (@a prefix = "").
It is not an error to declare a namespace and not use it in an element name.
It is an error to use a namespace prefix in an element name that is not declared beforehand.

A writer is free to "hard code" the prefix names in the element name strings that it uses (since it established the prefix declarations in the file).
A reader cannot assume that any given prefix is always mapped to the same URI or vice versa.
A reader might check an ImageFile, and if the prefixes aren't the way it likes, the reader could give up.

A better scheme would be to lookup the URI that the reader is familiar with, and store the prefix that the particular file uses in a variable.
Then every time the reader needs to form a prefixed element name, it can assemble the full element name from the stored prefix variable and the constant documented base name string.
This is less convenient than using a single "hard coded" string constant for an element name, but it is robust against any choice of prefix/URI combination.

See the class discussion at bottom of ImageFile page for more details about namespaces.
@pre     This ImageFile must be open (i.e. isOpen()).
@pre     ImageFile must have been opened in write mode (i.e. isWritable()).
@pre     prefix != ""
@pre     uri != ""
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_FILE_IS_READ_ONLY
@throw   ::E57_ERROR_DUPLICATE_NAMESPACE_PREFIX
@throw   ::E57_ERROR_DUPLICATE_NAMESPACE_URI
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     Extensions.cpp example, ImageFile::extensionsCount, ImageFile::extensionsLookupPrefix, ImageFile::extensionsLookupUri
*/
void ImageFile::extensionsAdd(const ustring& prefix, const ustring& uri)
{
    impl_->extensionsAdd(prefix, uri);
}

/*!
@brief   Get URI associated with an E57 extension prefix in the ImageFile.
@param   [in] prefix    The shorthand name of the extension to look up.
@param   [out] uri      The URI that was associated with the given @a prefix.
@details
If @a prefix = "", then @a uri is set to the default namespace URI, and the function returns true.
if @a prefix is declared in the ImageFile, then @a uri is set the corresponding URI, and the function returns true.
It is an error if @a prefix contains an illegal character combination for E57 namespace prefixes.
It is not an error if @a prefix is well-formed, but not defined in the ImageFile (the function just returns false).
@pre     This ImageFile must be open (i.e. isOpen()).
@post    No visible state is modified.
@return  true if prefix is declared in the ImageFile.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     Extensions.cpp example, ImageFile::extensionsLookupUri
*/
bool ImageFile::extensionsLookupPrefix(const ustring& prefix, ustring& uri) const
{
    return impl_->extensionsLookupPrefix(prefix, uri);
}

/*!
@brief   Get an E57 extension prefix associated with a URI in the ImageFile.
@param   [in] uri       The URI of the extension to look up.
@param   [out] prefix   The shorthand prefix that was associated with the given @a uri.
@details
If @a uri is declared in the ImageFile, then @a prefix is set the corresponding prefix, and the function returns true.
It is an error if @a uri contains an illegal character combination for E57 namespace URIs.
It is not an error if @a uri is well-formed, but not defined in the ImageFile (the function just returns false).
@pre     This ImageFile must be open (i.e. isOpen()).
@pre     uri != ""
@post    No visible state is modified.
@return  true if URI is declared in the ImageFile.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     Extensions.cpp example, ImageFile::extensionsLookupPrefix
*/
bool ImageFile::extensionsLookupUri(const ustring& uri, ustring& prefix) const
{
    return impl_->extensionsLookupUri(uri, prefix);
}

/*!
@brief   Get number of E57 extensions declared in the ImageFile.
@details
The default E57 namespace does not count as an extension.
@pre     This ImageFile must be open (i.e. isOpen()).
@post    No visible state is modified.
@return  The number of E57 extensions defined in the ImageFile.
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     Extensions.cpp example, ImageFile::extensionsPrefix, ImageFile::extensionsUri
*/
size_t ImageFile::extensionsCount() const
{
    return impl_->extensionsCount();
}

/*!
@brief   Get an E57 extension prefix declared in an ImageFile by index.
@param   [in] index The index of the prefix to get, starting at 0.
@details
The order that the prefixes are stored in is not necessarily the same as the order they were created.
However the prefix order will correspond to the URI order.
The default E57 namespace is not counted as an extension.
@pre     This ImageFile must be open (i.e. isOpen()).
@pre     0 <= index < extensionsCount()
@post    No visible state is modified.
@return  The E57 extension prefix at the given index.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     Extensions.cpp example, ImageFile::extensionsCount, ImageFile::extensionsUri
*/
ustring ImageFile::extensionsPrefix(const size_t index) const
{
    return impl_->extensionsPrefix(index);
}

/*!
@brief   Get an E57 extension URI declared in an ImageFile by index.
@param   [in] index The index of the URI to get, starting at 0.
@details
The order that the URIs are stored is not necessarily the same as the order they were created.
However the URI order will correspond to the prefix order.
The default E57 namespace is not counted as an extension.
@pre     This ImageFile must be open (i.e. isOpen()).
@pre     0 <= index < extensionsCount()
@post    No visible state is modified.
@return  The E57 extension URI at the given index.
@throw   ::E57_ERROR_BAD_API_ARGUMENT
@throw   ::E57_ERROR_IMAGEFILE_NOT_OPEN
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     Extensions.cpp example, ImageFile::extensionsCount, ImageFile::extensionsPrefix
*/
ustring ImageFile::extensionsUri(const size_t index) const
{
    return impl_->extensionsUri(index);
}

/*!
@brief   Test whether an E57 element name has an extension prefix.
@details
The element name has a prefix if the function elementNameParse(elementName,prefix,dummy) would succeed, and returned prefix != "".
@param   [in] elementName   The string element name to test.
@post    No visible state is modified.
@return  True if the E57 element name has an extension prefix.
@throw   No E57Exceptions.
@see     NameParse.cpp example
*/
bool ImageFile::isElementNameExtended(const ustring& elementName) const
{
    return impl_->isElementNameExtended(elementName);
}

/*!
@brief   Parse element name into prefix and localPart substrings.
@param   [in] elementName   The string element name to parse into prefix and local parts.
@param   [out] prefix       The prefix (if any) in the @a elementName.
@param   [out] localPart    The part of the element name after the prefix.
@details
A legal element name may be in prefixed (ID:ID) or unprefixed (ID) form,
where ID is a string whose first character is in {a-z,A-Z,_} followed by zero or more characters in {a-z,A-Z,_,0-9,-,.}.
If in prefixed form, the prefix does not have to be declared in the ImageFile.
@post    No visible state is modified.
@throw   ::E57_ERROR_BAD_PATH_NAME
@throw   ::E57_ERROR_INTERNAL           All objects in undocumented state
@see     NameParse.cpp example, ImageFile::isElementNameExtended
*/
void ImageFile::elementNameParse(const ustring& elementName, ustring& prefix, ustring& localPart) const
{
    impl_->elementNameParse(elementName, prefix, localPart);
}

/*!
@brief   Diagnostic function to print internal state of object to output stream in an indented format.
@copydetails Node::dump()
*/
#ifdef E57_DEBUG
void ImageFile::dump(int indent, std::ostream& os) const
{
    impl_->dump(indent, os);
}
#else
void ImageFile::dump(int indent, std::ostream& os) const
{}
#endif

/*!
@brief   Test if two ImageFile handles refer to the same underlying ImageFile
@param   [in] imf2        The ImageFile to compare this ImageFile with
@post    No visible object state is modified.
@return  @c true if ImageFile handles refer to the same underlying ImageFile.
@throw   No E57Exceptions
*/
bool ImageFile::operator==(ImageFile imf2) const
{
    return(impl_ == imf2.impl_);
}

/*!
@brief   Test if two ImageFile handles refer to different underlying ImageFile
@param   [in] imf2        The ImageFile to compare this ImageFile with
@post    No visible object state is modified.
@return  @c true if ImageFile handles refer to different underlying ImageFiles.
@throw   No E57Exceptions
*/
bool ImageFile::operator!=(ImageFile imf2) const
{
    return(impl_ != imf2.impl_);
}

//! @cond documentNonPublic   The following isn't part of the API, and isn't documented.
ImageFile::ImageFile(ImageFileImplSharedPtr imfi)
: impl_(imfi)
{}
//! @endcond

