You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
667 lines
16 KiB
667 lines
16 KiB
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: $RCSfile: vtkXMLUtilities.cxx,v $
|
|
|
|
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
|
|
All rights reserved.
|
|
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
|
|
|
|
This software is distributed WITHOUT ANY WARRANTY; without even
|
|
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
PURPOSE. See the above copyright notice for more information.
|
|
|
|
=========================================================================*/
|
|
#include "vtkXMLUtilities.h"
|
|
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkXMLDataElement.h"
|
|
#include "vtkXMLDataParser.h"
|
|
|
|
#if !defined(_WIN32) || defined(__CYGWIN__)
|
|
# include <unistd.h> /* unlink */
|
|
#else
|
|
# include <io.h> /* unlink */
|
|
#endif
|
|
|
|
#include <vtkstd/vector>
|
|
|
|
typedef vtkstd::vector<vtkXMLDataElement*> vtkXMLUtilitiesDataElementContainer;
|
|
|
|
vtkStandardNewMacro(vtkXMLUtilities);
|
|
vtkCxxRevisionMacro(vtkXMLUtilities, "$Revision: 1.6 $");
|
|
|
|
#define VTK_XML_UTILITIES_FACTORED_POOL_NAME "FactoredPool"
|
|
#define VTK_XML_UTILITIES_FACTORED_NAME "Factored"
|
|
#define VTK_XML_UTILITIES_FACTORED_REF_NAME "FactoredRef"
|
|
|
|
//----------------------------------------------------------------------------
|
|
inline int vtkXMLUtilitiesEncodeEntities(unsigned char c, ostream &output)
|
|
{
|
|
switch (c)
|
|
{
|
|
case '&':
|
|
output << "&";
|
|
return 1;
|
|
|
|
case '"':
|
|
output << """;
|
|
return 1;
|
|
|
|
case '\'':
|
|
output << "'";
|
|
return 1;
|
|
|
|
case '<':
|
|
output << "<";
|
|
return 1;
|
|
|
|
case '>':
|
|
output << ">";
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLUtilities::EncodeString(const char *input, int input_encoding,
|
|
ostream &output, int output_encoding,
|
|
int special_entities)
|
|
{
|
|
// No string
|
|
|
|
if (!input)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// If either the input or output encoding is not specified,
|
|
// or they are the same, dump as is (if no entites had to be converted)
|
|
|
|
int no_input_encoding = (input_encoding <= VTK_ENCODING_NONE ||
|
|
input_encoding >= VTK_ENCODING_UNKNOWN);
|
|
|
|
int no_output_encoding = (output_encoding <= VTK_ENCODING_NONE ||
|
|
output_encoding >= VTK_ENCODING_UNKNOWN);
|
|
|
|
if (!special_entities &&
|
|
(no_input_encoding || no_output_encoding ||
|
|
input_encoding == output_encoding))
|
|
{
|
|
output << input;
|
|
return;
|
|
}
|
|
|
|
// Convert
|
|
|
|
const unsigned char *str = (const unsigned char*)input;
|
|
|
|
// If either the input or output encoding is not specified, just process
|
|
// the entities
|
|
|
|
if (no_input_encoding || no_output_encoding)
|
|
{
|
|
while (*str)
|
|
{
|
|
if (!vtkXMLUtilitiesEncodeEntities(*str, output))
|
|
{
|
|
output << *str;
|
|
}
|
|
str++;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// To VTK_UTF_8...
|
|
|
|
if (output_encoding == VTK_ENCODING_UTF_8)
|
|
{
|
|
int from_iso_8859 = (input_encoding >= VTK_ENCODING_ISO_8859_1 &&
|
|
input_encoding <= VTK_ENCODING_ISO_8859_16);
|
|
|
|
// From ISO-8859 or US-ASCII
|
|
|
|
if (input_encoding == VTK_ENCODING_US_ASCII || from_iso_8859)
|
|
{
|
|
while (*str)
|
|
{
|
|
if (!special_entities || !vtkXMLUtilitiesEncodeEntities(*str, output))
|
|
{
|
|
if (*str > 0x7F)
|
|
{
|
|
#if 0
|
|
// This should be the right implementation, but it seems that
|
|
// it just does not work for Expat. Brad and I should dig into
|
|
// that later, but it seems weird. In the meantime, just
|
|
// output the hex representation.
|
|
|
|
output << "&#x"
|
|
<< hex << (0xC0 | (*str >> 6))
|
|
<< hex << (0x80 | (*str & 0x3F))
|
|
<< ';';
|
|
#else
|
|
output << "&#x" << hex << (int)(*str) << ';';
|
|
#endif
|
|
}
|
|
else if (*str < 30)
|
|
{
|
|
output << "&#x" << hex << (int)(*str) << ';';
|
|
}
|
|
else
|
|
{
|
|
output << *str;
|
|
}
|
|
}
|
|
str++;
|
|
}
|
|
}
|
|
|
|
// From VTK_ENCODING_UTF_8 (i.e. just encode the entities)
|
|
// To be completed (need the whole &#x)
|
|
|
|
else if (input_encoding == VTK_ENCODING_UTF_8)
|
|
{
|
|
while (*str)
|
|
{
|
|
if (!vtkXMLUtilitiesEncodeEntities(*str, output))
|
|
{
|
|
output << *str;
|
|
}
|
|
str++;
|
|
}
|
|
}
|
|
|
|
// Unsupported input encoding
|
|
|
|
else
|
|
{
|
|
vtkGenericWarningMacro(
|
|
<< "Input encoding not supported (" << input_encoding << ")");
|
|
}
|
|
}
|
|
|
|
// From VTK_ENCODING_UTF_8...
|
|
|
|
else if (input_encoding == VTK_ENCODING_UTF_8)
|
|
{
|
|
int to_iso_8859 = (output_encoding >= VTK_ENCODING_ISO_8859_1 &&
|
|
output_encoding <=VTK_ENCODING_ISO_8859_16);
|
|
|
|
// To US-ASCII or ISO 8859
|
|
|
|
if (output_encoding == VTK_ENCODING_US_ASCII || to_iso_8859)
|
|
{
|
|
while (*str)
|
|
{
|
|
if (!special_entities || !vtkXMLUtilitiesEncodeEntities(*str, output))
|
|
{
|
|
// Multi-byte 2-chars converted into one char
|
|
|
|
if (*str > 0x7F)
|
|
{
|
|
output << (unsigned char)((*str << 6) | (str[1] & 0x3F));
|
|
str++;
|
|
}
|
|
else
|
|
{
|
|
output << *str;
|
|
}
|
|
}
|
|
str++;
|
|
}
|
|
}
|
|
|
|
// Unsupported output encoding
|
|
|
|
else
|
|
{
|
|
vtkGenericWarningMacro(
|
|
<< "Output encoding not supported (" << input_encoding << ")");
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLUtilities::CollateAttributes(vtkXMLDataElement *elem,
|
|
ostream &os,
|
|
const char *sep)
|
|
{
|
|
if (!elem)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int i, nb = elem->GetNumberOfAttributes();
|
|
for (i = 0; i < nb; i++)
|
|
{
|
|
const char *name = elem->GetAttributeName(i);
|
|
if (name)
|
|
{
|
|
const char *value = elem->GetAttribute(name);
|
|
if (value)
|
|
{
|
|
if (i)
|
|
{
|
|
os << (sep ? sep : " ");
|
|
}
|
|
os << name << "=\"";
|
|
vtkXMLUtilities::EncodeString(
|
|
value, elem->GetAttributeEncoding(), os, VTK_ENCODING_UTF_8, 1);
|
|
os << '\"';
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLUtilities::FlattenElement(vtkXMLDataElement *elem,
|
|
ostream &os,
|
|
vtkIndent *indent,
|
|
int indent_attributes)
|
|
{
|
|
if (!elem)
|
|
{
|
|
return;
|
|
}
|
|
|
|
unsigned long pos = os.tellp();
|
|
|
|
// Name
|
|
|
|
if (indent)
|
|
{
|
|
os << *indent;
|
|
}
|
|
|
|
os << '<' << elem->GetName();
|
|
|
|
// Attributes
|
|
|
|
if (elem->GetNumberOfAttributes())
|
|
{
|
|
os << ' ';
|
|
if (indent && indent_attributes)
|
|
{
|
|
unsigned long len = (unsigned long)os.tellp() - pos;
|
|
if (os.fail())
|
|
{
|
|
return;
|
|
}
|
|
char *sep = new char [1 + len + 1];
|
|
sep[0] = '\n';
|
|
memset(sep + 1, ' ', len);
|
|
sep[len + 1] = '\0';
|
|
vtkXMLUtilities::CollateAttributes(elem, os, sep);
|
|
delete [] sep;
|
|
}
|
|
else
|
|
{
|
|
vtkXMLUtilities::CollateAttributes(elem, os);
|
|
}
|
|
}
|
|
|
|
// Nested elements and close
|
|
|
|
int nb_nested = elem->GetNumberOfNestedElements();
|
|
if (!nb_nested)
|
|
{
|
|
os << "/>";
|
|
}
|
|
else
|
|
{
|
|
os << '>';
|
|
if (indent)
|
|
{
|
|
os << '\n';
|
|
}
|
|
for (int i = 0; i < nb_nested; i++)
|
|
{
|
|
if (indent)
|
|
{
|
|
vtkIndent next_indent = indent->GetNextIndent();
|
|
vtkXMLUtilities::FlattenElement(elem->GetNestedElement(i),
|
|
os, &next_indent);
|
|
}
|
|
else
|
|
{
|
|
vtkXMLUtilities::FlattenElement(elem->GetNestedElement(i), os);
|
|
}
|
|
}
|
|
if (indent)
|
|
{
|
|
os << *indent;
|
|
}
|
|
os << "</" << elem->GetName() << '>';
|
|
}
|
|
if (indent)
|
|
{
|
|
os << '\n';
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkXMLUtilities::WriteElementToFile(vtkXMLDataElement *elem,
|
|
const char *filename,
|
|
vtkIndent *indent)
|
|
{
|
|
if (!elem || !filename)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
ofstream os(filename, ios::out);
|
|
vtkXMLUtilities::FlattenElement(elem, os, indent);
|
|
|
|
os.flush();
|
|
if (os.fail())
|
|
{
|
|
os.close();
|
|
unlink(filename);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkXMLDataElement*
|
|
vtkXMLUtilities::ReadElementFromStream(istream &is, int encoding)
|
|
{
|
|
vtkXMLDataElement *res = NULL;
|
|
vtkXMLDataParser* xml_parser = vtkXMLDataParser::New();
|
|
xml_parser->SetAttributesEncoding(encoding);
|
|
|
|
xml_parser->SetStream(&is);
|
|
if (xml_parser->Parse())
|
|
{
|
|
res = xml_parser->GetRootElement();
|
|
// Bump up the ref count since we are going to delete the parser
|
|
// which actually owns the element
|
|
res->SetReferenceCount(res->GetReferenceCount() + 1);
|
|
vtkXMLUtilities::UnFactorElements(res);
|
|
}
|
|
|
|
xml_parser->Delete();
|
|
return res;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkXMLDataElement*
|
|
vtkXMLUtilities::ReadElementFromString(const char *str, int encoding)
|
|
{
|
|
if (!str)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
strstream strstr;
|
|
strstr << str;
|
|
vtkXMLDataElement *res =
|
|
vtkXMLUtilities::ReadElementFromStream(strstr, encoding);
|
|
strstr.rdbuf()->freeze(0);
|
|
return res;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkXMLDataElement*
|
|
vtkXMLUtilities::ReadElementFromFile(const char *filename, int encoding)
|
|
{
|
|
if (!filename)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ifstream is(filename);
|
|
return vtkXMLUtilities::ReadElementFromStream(is, encoding);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLUtilitiesFindSimilarElementsInternal(
|
|
vtkXMLDataElement *elem,
|
|
vtkXMLDataElement *tree,
|
|
vtkXMLUtilitiesDataElementContainer *results)
|
|
{
|
|
if (!elem || !tree || !results || elem == tree)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// If the element is equal to the current tree, append it to the
|
|
// results, otherwise check the sub-trees
|
|
|
|
if (elem->IsEqualTo(tree))
|
|
{
|
|
results->push_back(tree);
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < tree->GetNumberOfNestedElements(); i++)
|
|
{
|
|
vtkXMLUtilitiesFindSimilarElementsInternal(
|
|
elem, tree->GetNestedElement(i), results);
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkXMLUtilities::FindSimilarElements(vtkXMLDataElement *elem,
|
|
vtkXMLDataElement *tree,
|
|
vtkXMLDataElement ***results)
|
|
{
|
|
if (!elem || ! tree)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Create a data element container, and find all similar elements
|
|
|
|
vtkXMLUtilitiesDataElementContainer *container =
|
|
new vtkXMLUtilitiesDataElementContainer;
|
|
|
|
vtkXMLUtilitiesFindSimilarElementsInternal(elem, tree, container);
|
|
|
|
// If nothing was found, exit now
|
|
|
|
int size = (int)container->size();
|
|
if (size)
|
|
{
|
|
// Allocate an array of element and copy the contents of the container
|
|
// to this flat structure
|
|
|
|
*results = new vtkXMLDataElement* [size];
|
|
|
|
size = 0;
|
|
for (vtkXMLUtilitiesDataElementContainer::const_iterator
|
|
it = container->begin(); it != container->end(); ++it)
|
|
{
|
|
if (*it)
|
|
{
|
|
(*results)[size++] = *it;
|
|
}
|
|
}
|
|
}
|
|
|
|
delete container;
|
|
|
|
return size;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLUtilities::FactorElements(vtkXMLDataElement *tree)
|
|
{
|
|
if (!tree)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Create the factored pool, and add it to the tree so that it can
|
|
// factor itself too
|
|
|
|
vtkXMLDataElement *pool = vtkXMLDataElement::New();
|
|
pool->SetName(VTK_XML_UTILITIES_FACTORED_POOL_NAME);
|
|
pool->SetAttributeEncoding(tree->GetAttributeEncoding());
|
|
tree->AddNestedElement(pool);
|
|
|
|
// Factor the tree, as long as some factorization has occured
|
|
// (multiple pass might be needed because larger trees are factored
|
|
// first)
|
|
|
|
while (vtkXMLUtilities::FactorElementsInternal(tree, tree, pool)) {};
|
|
|
|
// Nothing factored, remove the useless pool
|
|
|
|
if (!pool->GetNumberOfNestedElements())
|
|
{
|
|
tree->RemoveNestedElement(pool);
|
|
}
|
|
|
|
pool->Delete();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkXMLUtilities::FactorElementsInternal(vtkXMLDataElement *tree,
|
|
vtkXMLDataElement *root,
|
|
vtkXMLDataElement *pool)
|
|
{
|
|
if (!tree || !root || !pool)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Do not bother factoring something already factored
|
|
|
|
if (tree->GetName() &&
|
|
!strcmp(tree->GetName(), VTK_XML_UTILITIES_FACTORED_REF_NAME))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Try to find all trees similar to the current tree
|
|
|
|
vtkXMLDataElement **similar_trees;
|
|
int nb_of_similar_trees = vtkXMLUtilities::FindSimilarElements(
|
|
tree, root, &similar_trees);
|
|
|
|
// None was found, try to factor the sub-trees
|
|
|
|
if (!nb_of_similar_trees)
|
|
{
|
|
int res = 0;
|
|
for (int i = 0; i < tree->GetNumberOfNestedElements(); i++)
|
|
{
|
|
res += vtkXMLUtilities::FactorElementsInternal(
|
|
tree->GetNestedElement(i), root, pool);
|
|
}
|
|
return res ? 1 : 0;
|
|
}
|
|
|
|
// Otherwise replace those trees with factored refs
|
|
|
|
char buffer[5];
|
|
sprintf(buffer, "%02d_", pool->GetNumberOfNestedElements());
|
|
|
|
ostrstream id;
|
|
id << buffer << tree->GetName() << ends;
|
|
|
|
vtkXMLDataElement *factored = vtkXMLDataElement::New();
|
|
factored->SetName(VTK_XML_UTILITIES_FACTORED_NAME);
|
|
factored->SetAttributeEncoding(pool->GetAttributeEncoding());
|
|
factored->SetAttribute("Id", id.str());
|
|
pool->AddNestedElement(factored);
|
|
factored->Delete();
|
|
|
|
vtkXMLDataElement *tree_copy = vtkXMLDataElement::New();
|
|
tree_copy->DeepCopy(tree);
|
|
factored->AddNestedElement(tree_copy);
|
|
tree_copy->Delete();
|
|
|
|
for (int i = 0; i < nb_of_similar_trees; i++)
|
|
{
|
|
similar_trees[i]->RemoveAllAttributes();
|
|
similar_trees[i]->RemoveAllNestedElements();
|
|
similar_trees[i]->SetName(VTK_XML_UTILITIES_FACTORED_REF_NAME);
|
|
similar_trees[i]->SetAttribute("Id", id.str());
|
|
}
|
|
|
|
tree->RemoveAllAttributes();
|
|
tree->RemoveAllNestedElements();
|
|
tree->SetName(VTK_XML_UTILITIES_FACTORED_REF_NAME);
|
|
tree->SetAttribute("Id", id.str());
|
|
|
|
id.rdbuf()->freeze(0);
|
|
|
|
delete [] similar_trees;
|
|
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkXMLUtilities::UnFactorElements(vtkXMLDataElement *tree)
|
|
{
|
|
if (!tree)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Search for the factored pool, if not found, we are done
|
|
|
|
vtkXMLDataElement *pool = tree->FindNestedElementWithName(
|
|
VTK_XML_UTILITIES_FACTORED_POOL_NAME);
|
|
if (!pool)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Remove the pool from the tree, because it makes no sense
|
|
// unfactoring it too
|
|
|
|
pool->Register(tree);
|
|
tree->RemoveNestedElement(pool);
|
|
|
|
// Unfactor the tree
|
|
|
|
vtkXMLUtilities::UnFactorElementsInternal(tree, pool);
|
|
|
|
// Remove the useless empty pool
|
|
|
|
pool->UnRegister(tree);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkXMLUtilities::UnFactorElementsInternal(vtkXMLDataElement *tree,
|
|
vtkXMLDataElement *pool)
|
|
{
|
|
if (!tree || !pool)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int res = 0;
|
|
|
|
// We found a factor, replace it with the corresponding sub-tree
|
|
|
|
if (tree->GetName() &&
|
|
!strcmp(tree->GetName(), VTK_XML_UTILITIES_FACTORED_REF_NAME))
|
|
{
|
|
vtkXMLDataElement *original_tree =
|
|
pool->FindNestedElementWithNameAndAttribute(
|
|
VTK_XML_UTILITIES_FACTORED_NAME, "Id", tree->GetAttribute("Id"));
|
|
if (original_tree && original_tree->GetNumberOfNestedElements())
|
|
{
|
|
tree->DeepCopy(original_tree->GetNestedElement(0));
|
|
res++;
|
|
}
|
|
}
|
|
|
|
// Now try to unfactor the sub-trees
|
|
|
|
for (int i = 0; i < tree->GetNumberOfNestedElements(); i++)
|
|
{
|
|
res += vtkXMLUtilities::UnFactorElementsInternal(
|
|
tree->GetNestedElement(i), pool);
|
|
}
|
|
|
|
return res ? 1 : 0;
|
|
}
|
|
|