/*========================================================================= 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 /* unlink */ #else # include /* unlink */ #endif #include typedef vtkstd::vector 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 << "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; }