diff options
Diffstat (limited to 'cpp/src/Freeze/Transform.cpp')
-rw-r--r-- | cpp/src/Freeze/Transform.cpp | 2047 |
1 files changed, 2047 insertions, 0 deletions
diff --git a/cpp/src/Freeze/Transform.cpp b/cpp/src/Freeze/Transform.cpp new file mode 100644 index 00000000000..b86db6a0f2e --- /dev/null +++ b/cpp/src/Freeze/Transform.cpp @@ -0,0 +1,2047 @@ +// ********************************************************************** +// +// Copyright (c) 2001 +// MutableRealms, Inc. +// Huntsville, AL, USA +// +// All Rights Reserved +// +// ********************************************************************** + +#include <Freeze/Transform.h> + +#include <vector> +#include <assert.h> +#include <iostream> +#include <Freeze/Print.h> + +#include <Freeze/ErrorHandler.h> +#include <parsers/DOMParser.hpp> + +using namespace std; + +ostream& +operator<<(ostream& os, const IllegalTransform& ex) +{ + os << "IllegalTransform: file: " << ex.file() << " line: " << ex.line(); + return os; +} + +ostream& +operator<<(ostream& os, const IncompatibleSchema& ex) +{ + os << "IncompatibleSchema: file: " << ex.file() << " line: " << ex.line(); + return os; +} + +ostream& +operator<<(ostream& os, const InvalidSchema& ex) +{ + os << "InvalidSchema: file: " << ex.file() << " line: " << ex.line(); + return os; +} + +ostream& +operator<<(ostream& os, const SchemaViolation& ex) +{ + os << "SchemaViolation: file: " << ex.file() << " line: " << ex.line(); + return os; +} + +static string +toString(const DOMString& s) +{ + char* t = s.transcode(); + string r(t); + delete[] t; + return r; +} + + +typedef ::std::map< ::std::string, DocumentInfo*> DocumentMap; + +typedef ::std::map< ::std::string, ::std::string> PrefixURIMap; + +struct DocumentInfo +{ + DOM_Document document; + PrefixURIMap nsMap; + ::std::string targetNamespace; +}; + +// +// Convert a QName from prefix:local to local@namespaceURI. +// +static string +convertQName(const string& qname, const DocumentInfo* info) +{ + size_t pos = qname.find(':'); + string prefix; + string localName; + if (pos != string::npos) + { + prefix = qname.substr(0, pos); + localName = qname.substr(pos+1); + } + else + { + localName = qname; + } + + PrefixURIMap::const_iterator q = info->nsMap.find(prefix); + if (q == info->nsMap.end()) + { + cout << qname << endl; + // + // No namespace - TODO: not InvalidSchema - it's an + // invalid instance document. + // + throw InvalidSchema(__FILE__, __LINE__); + } + + string n = localName; + n += '@'; + n += q->second; + + return n; +} + +static void +createNSMap(DocumentInfo* info, DOM_Node& root) +{ + static const string targetNamespaceAttrName = "targetNamespace"; + static const string xmlnsAttrName = "xmlns"; + + DOM_NamedNodeMap attributes = root.getAttributes(); + unsigned int max = attributes.getLength(); + for (unsigned int i = 0; i < max; ++i) + { + DOM_Node attribute = attributes.item(i); + string attrName = toString(attribute.getNodeName()); + if (attrName.substr(0, 5) == xmlnsAttrName) + { + string ns; + if (attrName.size() > 5) + { + ns = attrName.substr(6); + } + string uri = toString(attribute.getNodeValue()); + //cout << "adding: " << ns << " " << uri << endl; + info->nsMap.insert(make_pair(ns, uri)); + } + else if (attrName == targetNamespaceAttrName) + { + info->targetNamespace = toString(attribute.getNodeValue()); + } + } +} + +static DOM_Node +findChild(const DOM_Node& parent, const string& namespaceURI, const string& localname) +{ + DOM_NodeList children = parent.getChildNodes(); + for (unsigned int i = 0; i < children.getLength(); ++i) + { + DOM_Node child = children.item(i); + if (toString(child.getNamespaceURI()) == namespaceURI && toString(child.getLocalName()) == localname) + { + return child; + } + } + + return DOM_Node(); +} + +// +// TODO: This is used for abuse. Replace by a real search. +// +static DOM_Node +findChild(const DOM_Node& parent, const string& name) +{ + DOM_NodeList children = parent.getChildNodes(); + for (unsigned int i = 0; i < children.getLength(); ++i) + { + DOM_Node child = children.item(i); + if (toString(child.getNodeName()) == name) + { + return child; + } + } + return DOM_Node(); +} + +static string +getAttributeByName(DOM_Node& node, const string& name) +{ + DOM_NamedNodeMap attributes = node.getAttributes(); + unsigned int max = attributes.getLength(); + for (unsigned int i = 0; i < max; ++i) + { + DOM_Node attribute = attributes.item(i); + string nodeName = toString(attribute.getNodeName()); + if (nodeName == name) + { + return toString(attribute.getNodeValue()); + } + } + + return string(""); +} + +static string +getLengthAttribute(DOM_Node& node) +{ + static const string lengthName = "length"; + return getAttributeByName(node, lengthName); +} + +static string +getTypeAttribute(DOM_Node& node) +{ + static const string typeName = "type"; + return getAttributeByName(node, typeName); +} + +static string +getNameAttribute(DOM_Node& node) +{ + static const string nameName = "name"; + return getAttributeByName(node, nameName); +} + +static string +getValueAttribute(DOM_Node& node) +{ + static const string valueName = "value"; + return getAttributeByName(node, valueName); +} + +static string +findAppinfoType(DOM_Node& node) +{ + DOM_Node child = findChild(node, "xs:annotation"); + if (!child.isNull()) + { + child = findChild(child, "xs:appinfo"); + if (!child.isNull()) + { + child = findChild(child, "type"); + if (!child.isNull()) + { + child = child.getFirstChild(); + if (!child.isNull()) + { + return toString(child.getNodeValue()); + } + } + } + } + + return string(""); +} + +// +// Specific transforms. +// +class NilTransform : public Transform +{ +public: + + NilTransform() + { + } + + virtual void + transform(ostream& os, const DocumentInfo*, const string&, DOM_Node node) + { + if (node.isNull()) + { + throw SchemaViolation(__FILE__, __LINE__); + } + + os << node; + } + + virtual ostream& + print(ostream& os) + { + os << "[nil transform]"; + return os; + } +}; + +// +// This transform is the root transform for marshaled documents. +// +// TODO: complete, rename +// +class InternalTransform : public Transform +{ +public: + + InternalTransform() + { + } + + virtual void + transform(ostream& os, const DocumentInfo*, const string&, DOM_Node node) + { + } + + virtual ostream& + print(ostream& os) + { + os << "[internal transform]"; + return os; + } +}; + +class ElementTransform : public Transform +{ +public: + + ElementTransform(const string& namespaceURI, const string& name, Transform* transform) : + _ns(namespaceURI), + _name(name), + _transform(transform) + { + } + + virtual void + transform(ostream& os, const DocumentInfo* info, const string&, DOM_Node node) + { + _transform->transform(os, info, _name, node); + } + + virtual ostream& + print(ostream& os) + { + os << "[ element: " << name() << ": " << _transform->print(os) << "]"; + return os; + } + + const string& namespaceURI() const { return _ns; } + const string& name() const { return _name; } + +private: + + string _ns; + string _name; + + Transform* _transform; +}; + +class ValidateEnumerationTransform : public Transform +{ +public: + + ValidateEnumerationTransform(const vector<string>& values) : + _values(values) + { + } + + virtual void + transform(ostream& os, const DocumentInfo*, const string&, DOM_Node node) + { + if (node.isNull()) + { + throw SchemaViolation(__FILE__, __LINE__); + } + + DOM_Node child = node.getFirstChild(); + if (child.isNull() || child.getNodeType() != DOM_Node::TEXT_NODE) + { + throw SchemaViolation(__FILE__, __LINE__); + } + string value = toString(child.getNodeValue()); + if (find(_values.begin(), _values.end(), value) == _values.end()) + { + throw IllegalTransform(__FILE__, __LINE__); + } + os << node; + } + + virtual ostream& + print(ostream& os) + { + os << "[validate enumeration]"; + return os; + } + +private: + + vector<string> _values; +}; + +class ValidateIntegerTransform : public Transform +{ +public: + + ValidateIntegerTransform(const string& from, const string& to) : + _from(from), + _to(to) + { + } + + virtual void + transform(ostream& os, const DocumentInfo*, const string&, DOM_Node node) + { + if (node.isNull()) + { + throw SchemaViolation(__FILE__, __LINE__); + } + + DOM_Node child = node.getFirstChild(); + if (child.isNull() || child.getNodeType() != DOM_Node::TEXT_NODE) + { + throw SchemaViolation(__FILE__, __LINE__); + } + string value = toString(child.getNodeValue()); + long v = atol(value.c_str()); + if (_to == "xs:byte") + { + if (v < SCHAR_MIN || v > SCHAR_MAX) + { + throw IllegalTransform(__FILE__, __LINE__); + } + } + else if (_to == "xs:short") + { + if (v < SHRT_MIN || v > SHRT_MAX) + { + throw IllegalTransform(__FILE__, __LINE__); + } + } + else if (_to == "xs:int") + { + if (v < INT_MIN || v > INT_MAX) + { + throw IllegalTransform(__FILE__, __LINE__); + } + } + else if (_to == "xs:long") + { + } + os << node; + } + + virtual ostream& + print(ostream& os) + { + os << "[validate integer: from=" << _from << " to=" << _to << " ]"; + return os; + } + +private: + + string _from; + string _to; +}; + +class ValidateFloatTransform : public Transform +{ +public: + + ValidateFloatTransform(const string& from, const string& to) : + _from(from), + _to(to) + { + } + + virtual void + transform(ostream& os, const DocumentInfo*, const string&, DOM_Node node) + { + if (node.isNull()) + { + throw SchemaViolation(__FILE__, __LINE__); + } + + DOM_Node child = node.getFirstChild(); + if (child.isNull() || child.getNodeType() != DOM_Node::TEXT_NODE) + { + throw SchemaViolation(__FILE__, __LINE__); + } + os << node; + } + + virtual ostream& + print(ostream& os) + { + os << "[validate float: from=" << _from << " to=" << _to << " ]"; + return os; + } + +private: + + string _from; + string _to; +}; + +class SequenceTransform : public Transform +{ +public: + + SequenceTransform(Transform* transform) : + _transform(transform) + { + } + + virtual void + transform(ostream& os, const DocumentInfo* info, const string&, DOM_Node node) + { + if (node.isNull()) + { + throw SchemaViolation(__FILE__, __LINE__); + } + string name = toString(node.getNodeName()); + os << "<" << name; + string length = getLengthAttribute(node); + os << " length=\"" << length << "\""; + // TODO: print attributes + os << ">"; + + long l = atol(length.c_str()); + + DOM_NodeList children = node.getChildNodes(); + for (unsigned int i = 0; i < children.getLength(); ++i) + { + DOM_Node child = children.item(i); + if (child.getNodeType() != DOM_Node::ELEMENT_NODE) + { + continue; + } + + static const string sequenceElementName = "e"; + string nodeName = toString(child.getNodeName()); + if (l == 0 || nodeName != sequenceElementName) + { + throw SchemaViolation(__FILE__, __LINE__); + } + _transform->transform(os, info, nodeName, child); + --l; + } + + os << "</" << name << ">"; + } + + virtual ostream& + print(ostream& os) + { + os << "[sequence]\n"; + os << "\telement:" << _transform->print(os); + os << "[sequence]\n"; + return os; + } + +private: + + Transform* _transform; +}; + +class StructTransform : public Transform +{ +public: + + StructTransform() + { + } + + virtual void + transform(ostream& os, const DocumentInfo* info, const string&, DOM_Node node) + { + if (node.isNull()) + { + throw SchemaViolation(__FILE__, __LINE__); + } + string name = toString(node.getNodeName()); + os << "<" << name; + // TODO: print attributes + os << ">"; + + DOM_NodeList children = node.getChildNodes(); + for (vector<ElementTransform*>::const_iterator p = _transforms.begin(); p != _transforms.end(); ++p) + { + DOM_Node child = findChild(node, (*p)->namespaceURI(), (*p)->name()); + (*p)->transform(os, info, (*p)->name(), child); + } + + os << "</" << name << ">"; + } + + virtual ostream& + print(ostream& os) + { + os << "[struct start]\n"; + for (unsigned int i = 0; i < _transforms.size(); ++i) + { + os << "\t"; + _transforms[i]->print(os); + os << "\n"; + } + os << "[struct end]\n"; + return os; + } + + void + append(ElementTransform* transform) + { + _transforms.push_back(transform); + } + +private: + + vector<ElementTransform*> _transforms; +}; + +class DefaultInitializedStructTransform : public Transform +{ +public: + + DefaultInitializedStructTransform() + { + } + + virtual void + transform(ostream& os, const DocumentInfo* info, const string& name, DOM_Node node) + { + os << "<" << name; + // TODO: print attributes + os << ">"; + + DOM_Node child; // Nil + + for (vector<ElementTransform*>::const_iterator p = _transforms.begin(); p != _transforms.end(); ++p) + { + (*p)->transform(os, info, (*p)->name(), child); + } + + os << "</" << name << ">"; + } + + virtual ostream& + print(ostream& os) + { + os << "[empty struct start]\n"; + for (unsigned int i = 0; i < _transforms.size(); ++i) + { + os << "\t"; + _transforms[i]->print(os); + os << "\n"; + } + os << "[struct end]\n"; + return os; + } + + void + append(ElementTransform* transform) + { + _transforms.push_back(transform); + } + +private: + + vector<ElementTransform*> _transforms; +}; + +class ClassTransform : public Transform +{ +public: + + ClassTransform(TransformMap& transforms) : + _transforms(transforms) + { + } + + virtual void + transform(ostream& os, const DocumentInfo* info, const string& name, DOM_Node node) + { + static const string xsitypeAttrName = "xsi:type"; + string type = getAttributeByName(node, xsitypeAttrName); + if (type.empty()) + { + static const string xsinilAttrName = "xsi:nil"; + string nil = getAttributeByName(node, xsinilAttrName); + if (nil.empty()) + { + throw SchemaViolation(__FILE__, __LINE__); + } + // + // Act as NilTransform + // + os << node; + return; + } + + string n = convertQName(type, info); + + // + // Technically this is only permitted to be a more derived + // type - however, this will not be enforced here. + // + TransformMap::const_iterator p = _transforms.find(n); + if (p == _transforms.end()) + { + cout << n << endl; + throw SchemaViolation(__FILE__, __LINE__); + } + p->second->transform(os, info, name, node); + } + + virtual ostream& + print(ostream& os) + { + os << "[class]\n"; + return os; + } + +private: + + TransformMap& _transforms; + +}; + +class EmitStringTransform : public Transform +{ +public: + + EmitStringTransform(const string& s) : + _s(s) + { + } + + virtual void + transform(ostream& os, const DocumentInfo*, const string& name, DOM_Node) + { + if (!_s.empty()) + { + os << "<" << name << ">" << _s << "</" << name << ">"; + } + else + { + os << "<" << name << "/>"; + } + } + + virtual ostream& + print(ostream& os) + { + os << "[emit string: \"" << _s << "\"]"; + return os; + } + +private: + + string _s; +}; + +class EmitAttributeTransform : public Transform +{ +public: + + EmitAttributeTransform(const string& s) : + _s(s) + { + } + + virtual void + transform(ostream& os, const DocumentInfo*, const string& name, DOM_Node) + { + os << "<" << name << " " << _s << "/>"; + } + + virtual ostream& + print(ostream& os) + { + os << "[emit attribute: \"" << _s << "\"]"; + return os; + } + +private: + + string _s; + +}; + +// +// TODO: all the references are dangerous (DOM_Node&). Should either +// be const DOM_Node& or DOM_Node. +// +class TransformFactory +{ +public: + + TransformFactory(DOM_Document&, DOM_Document&, TransformMap&, TransformMap&); + ~TransformFactory(); + + enum Type + { + TypeInteger, // byte, short, int, long + TypeFloat, // float, double + TypeString, + TypeEnumeration, + TypeStruct, + TypeClass, + TypeException, + TypeDictionary, + TypeDictionaryContent, + TypeSequence, + TypeProxy, + TypeReference, + TypeInternal + }; + +private: + + DOM_Node findTypeInDocument(DOM_Document& doc, const ::std::string& local); + void findType(const DocumentMap&, const DocumentInfo*, const ::std::string&, DOM_Node&, DocumentInfo*&); + + Transform* createTransform(const DocumentInfo*, DOM_Node&, Type, const ::std::string&, + const DocumentInfo*, DOM_Node&, Type, const ::std::string&); + + Transform* createTransformByTypeName(const DocumentInfo*, const ::std::string&, + const DocumentInfo*, const ::std::string&); + + Transform* createComplexTypeTransform(DOM_Node&, DOM_Node&); + Transform* createSimpleTypeTransform(DOM_Node&, DOM_Node&); + + Transform* createStructTransform(const DocumentInfo*, DOM_Node&, const DocumentInfo*, DOM_Node&); + Transform* createStaticClassTransform(DOM_Node&, DOM_Node&); + Transform* createClassTransform(const DocumentInfo*, DOM_Node&, const DocumentInfo*, DOM_Node&); + Transform* createSequenceTransform(const DocumentInfo*, DOM_Node&, const DocumentInfo*, DOM_Node&); + Transform* createDictionaryTransform(const DocumentInfo*, DOM_Node&, const DocumentInfo*, DOM_Node&); + void createEnumValues(DOM_Node&, ::std::vector< ::std::string>&); + Transform* createEnumerationTransform(DOM_Node&); + + void createSequenceElementTransform(const DocumentInfo*, DOM_Node&, const DocumentInfo*, DOM_Node&, + ::std::vector<ElementTransform*>&); + void createClassContentTransform(const DocumentInfo*, DOM_Node&, + const DocumentInfo*, DOM_Node&, + ::std::vector<ElementTransform*>&); + + Transform* createDefaultInitializedStructTransform(const DocumentInfo*, DOM_Node&); + void createDefaultInitializedSequenceElementTransform(const DocumentInfo*, + DOM_Node&, ::std::vector<ElementTransform*>&); + Transform* createDefaultInitializedTransform(const DocumentInfo*, const ::std::string&); + + Type getType(DOM_Node&); + Type getTypeByName(const DocumentMap&, const DocumentInfo*, const ::std::string&, DOM_Node&, DocumentInfo*&); + + DOM_Node findSchemaRoot(const DOM_Document&); + + void import(DocumentMap&, const ::std::string&, const ::std::string&); + void processImport(DOM_Document&, DocumentMap&); + + void processElements(const DocumentInfo* info); + + // + // Set of documents. The set of all documents is the entire + // document set. + // + // The map maps from targetNamespace to DocumentInfo. + // + DocumentMap _fromDocs; + DocumentMap _toDocs; + + // + // Map of local@uri element names to transforms. Needed for actual + // transform. + // + TransformMap& _elements; + + // + // Map of local@uri type names to transform. This information + // cached for creation of the transform. + // + TransformMap _types; + + // + // Map of local@uri class transforms (based on static type). This + // information cached for creation of the transform. + // + TransformMap& _staticClassTransforms; + + // + // Map of local@uri transforms for creating new types. This + // information cached for creation of the transform. + // + TransformMap _defaultInitializedTransforms; +}; + +TransformFactory::TransformFactory(DOM_Document& fromDoc, DOM_Document& toDoc, + TransformMap& elements, + TransformMap& staticClassTransforms) : + _elements(elements), + _staticClassTransforms(staticClassTransforms) +{ + DocumentInfo* fromInfo = new DocumentInfo(); + fromInfo->document = fromDoc; + + DocumentInfo* toInfo = new DocumentInfo(); + toInfo->document = toDoc; + + DOM_Node fromSchema = findSchemaRoot(fromDoc); + assert(!fromSchema.isNull()); + + DOM_Node toSchema = findSchemaRoot(toDoc); + assert(!toSchema.isNull()); + + // + // Create the namespace maps for the two schemas. + // + createNSMap(fromInfo, fromSchema); + createNSMap(toInfo, toSchema); + + // + // Add the root document to the document map. + // + _fromDocs.insert(make_pair(fromInfo->targetNamespace, fromInfo)); + _toDocs.insert(make_pair(toInfo->targetNamespace, toInfo)); + + processImport(fromDoc, _fromDocs); + processImport(toDoc, _toDocs); + + for (DocumentMap::const_iterator p = _fromDocs.begin(); p != _fromDocs.end(); ++p) + { + processElements(p->second); + } +} + +TransformFactory::~TransformFactory() +{ +} + +struct StringTypeTable +{ + string name; + TransformFactory::Type type; + + bool operator==(const string& rhs) const { return name == rhs; } +}; + +TransformFactory::Type +TransformFactory::getType(DOM_Node& node) +{ + // + // Check the appinfo element for the actual type. + // + static const StringTypeTable items[] = + { + { "enumeration", TypeEnumeration }, + { "struct", TypeStruct }, + { "class", TypeClass }, + { "exception", TypeException }, + { "dictionary", TypeDictionary }, + { "dictionaryContent", TypeDictionaryContent }, + { "sequence", TypeSequence }, + { "proxy", TypeProxy }, + { "reference", TypeReference }, + { "internal", TypeInternal } + }; + static const StringTypeTable* begin = &items[0]; + static const StringTypeTable* end = &items[sizeof(items)/sizeof(items[0])]; + + string type = findAppinfoType(node); + + const StringTypeTable* p = find(begin, end, type); + if (p == end) + { + throw InvalidSchema(__FILE__, __LINE__); + } + + return p->type; +} + +TransformFactory::Type +TransformFactory::getTypeByName(const DocumentMap& docs, const DocumentInfo* info, const string& type, + DOM_Node& n, DocumentInfo*& nInfo) +{ + // + // First check to see if the type is a primitive schema type. + // + static const StringTypeTable items[] = + { + { "xs:byte", TypeInteger }, + { "xs:short", TypeInteger }, + { "xs:int", TypeInteger }, + { "xs:long", TypeInteger }, + { "xs:float", TypeFloat }, + { "xs:double", TypeFloat }, + { "xs:string", TypeString }, + }; + static const StringTypeTable* begin = &items[0]; + static const StringTypeTable* end = &items[sizeof(items)/sizeof(items[0])]; + + const StringTypeTable* p = find(begin, end, type); + if (p != end) + { + return p->type; + } + + findType(docs, info, type, n, nInfo); + if (n.isNull()) + { + throw InvalidSchema(__FILE__, __LINE__); + } + + return getType(n); +} + +Transform* +TransformFactory::createTransformByTypeName(const DocumentInfo* fromTypeInfo, const string& fromTypeName, + const DocumentInfo* toTypeInfo, const string& toTypeName) +{ + DOM_Node from; + DocumentInfo* fromInfo; + Type fromType = getTypeByName(_fromDocs, fromTypeInfo, fromTypeName, from, fromInfo); + + DOM_Node to; + DocumentInfo* toInfo; + Type toType = getTypeByName(_toDocs, toTypeInfo, toTypeName, to, toInfo); + + return createTransform(fromInfo, from, fromType, fromTypeName, toInfo, to, toType, toTypeName); +} + +// +// This really needs to do more or less depending on the type of +// transform (element, or dealing with complexType/simpleType. +// +Transform* +TransformFactory::createTransform(const DocumentInfo* fromInfo, DOM_Node& from, + Type fromType, const string& fromTypeName, + const DocumentInfo* toInfo, DOM_Node& to, + Type toType, const string& toTypeName) +{ + + TransformMap::const_iterator p = _types.find(fromTypeName); + if (p != _types.end()) + { + //cout << "returning previously cached transform: " << fromTypeName << endl; + return p->second; + } + + Transform* transform = 0; + + // + // First handle transforms where the types are equivalent. + // + if (fromType == toType) + { + switch(fromType) + { + case TypeInteger: + { + transform = new ValidateIntegerTransform(fromTypeName, toTypeName); + break; + } + + case TypeFloat: + { + transform = new ValidateFloatTransform(fromTypeName, toTypeName); + break; + } + + case TypeString: + case TypeProxy: // Same as string + case TypeReference: // Same as string + { + transform = new NilTransform(); + break; + } + + case TypeEnumeration: + { + // + // If the type names are not the same then it's illegal. + // + // TODO: This doesn't allow the renaming of types. By + // removing this comparison renaming of types would be + // permitted. Should this be permitted? + // + if (fromTypeName != toTypeName) + { + throw IllegalTransform(__FILE__, __LINE__); + } + + transform = createEnumerationTransform(to); + break; + } + + case TypeStruct: + { + // + // If the type names are not the same then it's illegal. + // + // TODO: This doesn't allow the renaming of types. By + // removing this comparison renaming of types would be + // permitted. Should this be permitted? + // + if (fromTypeName != toTypeName) + { + throw IllegalTransform(__FILE__, __LINE__); + } + + transform = createStructTransform(fromInfo, from, toInfo, to); + break; + } + + case TypeDictionaryContent: + { + // + // If the type names are not the same then it's illegal. + // + // TODO: This doesn't allow the renaming of types. By + // removing this comparison renaming of types would be + // permitted. Should this be permitted? + // + if (fromTypeName != toTypeName) + { + throw IllegalTransform(__FILE__, __LINE__); + } + + transform = createStructTransform(fromInfo, from, toInfo, to); + break; + } + + case TypeDictionary: + { + // + // If the type names are not the same then it's illegal. + // + // TODO: This doesn't allow the renaming of types. By + // removing this comparison renaming of types would be + // permitted. Should this be permitted? + // + if (fromTypeName != toTypeName) + { + throw IllegalTransform(__FILE__, __LINE__); + } + + transform = createDictionaryTransform(fromInfo, from, toInfo, to); + break; + } + + case TypeSequence: + { + // + // If the type names are not the same then it's illegal. + // + // TODO: This doesn't allow the renaming of types. By + // removing this comparison renaming of types would be + // permitted. Should this be permitted? + // + if (fromTypeName != toTypeName) + { + throw IllegalTransform(__FILE__, __LINE__); + } + + transform = createSequenceTransform(fromInfo, from, toInfo, to); + break; + } + + case TypeClass: + { + // + // If the type names are not the same then it's illegal. + // + // TODO: This doesn't allow the renaming of types. By + // removing this comparison renaming of types would be + // permitted. Should this be permitted? + // + if (fromTypeName != toTypeName) + { + throw IllegalTransform(__FILE__, __LINE__); + } + + Transform* staticTransform = createClassTransform(fromInfo, from, toInfo, to); + return new ClassTransform(_staticClassTransforms); + break; + } + + case TypeInternal: + { + // + // No transformation created for internal stuff. + // + transform = new InternalTransform(); + break; + } + + case TypeException: + default: + throw IllegalTransform(__FILE__, __LINE__); + } + } + + if (transform == 0) + { + // + // Next we have transforms from type-to-type. + // + if (fromType == TypeString && toType == TypeEnumeration) + { + // + // String to enumeration transform needs to validate the + // string as a member of the enumeration values. + // + transform = createEnumerationTransform(to); + } + else if (fromType == TypeEnumeration && toType == TypeString) + { + // + // Enumeration to string transform is nil transform. + // + transform = new NilTransform(); + } + } + + // + // TODO: struct->class, class->struct. + // + + if (transform == 0) + { + throw IllegalTransform(__FILE__, __LINE__); + } + + _types.insert(make_pair(fromTypeName, transform)); + + return transform; +} + +Transform* +TransformFactory::createStructTransform(const DocumentInfo* fromInfo, DOM_Node& from, + const DocumentInfo* toInfo, DOM_Node& to) +{ + static const string sequenceName = "xs:sequence"; + + DOM_Node fromSeq = findChild(from, sequenceName); + DOM_Node toSeq = findChild(to, sequenceName); + if (fromSeq.isNull() || toSeq.isNull()) + { + throw InvalidSchema(__FILE__, __LINE__); + } + + StructTransform* nodeTransform = new StructTransform(); + + vector<ElementTransform*> v; + createSequenceElementTransform(fromInfo, fromSeq, toInfo, toSeq, v); + for(vector<ElementTransform*>::const_iterator p = v.begin(); p != v.end(); ++p) + { + nodeTransform->append(*p); + } + + return nodeTransform; +} + +void +TransformFactory::createSequenceElementTransform(const DocumentInfo* fromInfo, DOM_Node& from, + const DocumentInfo* toInfo, DOM_Node& to, + vector<ElementTransform*>& v) +{ + // + // Allowable transforms: + // + // * Node added. + // * Node removed. + // * Node moved. + // + + DOM_NodeList fromSeqChildren = from.getChildNodes(); + DOM_NodeList toSeqChildren = to.getChildNodes(); + + // + // First run through the to set. This loop handles the node + // remove, and node changed transforms (plus allowable type + // changes). + // + for (unsigned int i = 0; i < toSeqChildren.getLength(); ++i) + { + ElementTransform* transform = 0; + DOM_Node toChild = toSeqChildren.item(i); + if (toChild.getNodeType() != DOM_Node::ELEMENT_NODE) + { + continue; + } + + static const string element = "xs:element"; + if (toString(toChild.getNodeName()) != element) + { + throw InvalidSchema(__FILE__, __LINE__); + } + + string toElementName = getNameAttribute(toChild); + string toTypeName = getTypeAttribute(toChild); + + // + // Search for the node in the fromSeqChildren list. + // + for (unsigned int j = 0; j < fromSeqChildren.getLength(); ++j) + { + DOM_Node fromChild = fromSeqChildren.item(j); + + if (fromChild.getNodeType() != DOM_Node::ELEMENT_NODE) + { + // Skip non element nodes. + continue; + } + + if (toString(fromChild.getNodeName()) != element) + { + throw InvalidSchema(__FILE__, __LINE__); + } + + string fromElementName = getNameAttribute(fromChild); + + if (fromElementName == toElementName) + { + string fromTypeName = getTypeAttribute(fromChild); + Transform* t = createTransformByTypeName(fromInfo, fromTypeName, toInfo, toTypeName); + + transform = new ElementTransform(toInfo->targetNamespace, toElementName, t); + assert(transform); + } + } + + // + // If there is no transform then this is a new node + // type. Create a transform to add an empty element of the + // appropriate type. + // + if (!transform) + { + Transform* t = createDefaultInitializedTransform(toInfo, toTypeName); + transform = new ElementTransform(toInfo->targetNamespace, toElementName, t); + } + + v.push_back(transform); + } +} + +void +TransformFactory::createClassContentTransform(const DocumentInfo* fromInfo, DOM_Node& from, + const DocumentInfo* toInfo, DOM_Node& to, + vector<ElementTransform*>& v) +{ + static const string complexContentName = "xs:complexContent"; + static const string sequenceName = "xs:sequence"; + static const string extension = "xs:extension"; + static const string baseAttrName = "base"; + + DOM_Node fromContent = findChild(from, complexContentName); + DOM_Node toContent = findChild(to, complexContentName); + + if (fromContent.isNull() && toContent.isNull()) + { + // + // Must be base of a class heirarchy (while this + // implementation is a little more flexible, with Ice it is + // limited to ::Ice::Object). + // + + // + // Look for xs:sequence + // + DOM_Node fromSeq = findChild(from, sequenceName); + DOM_Node toSeq = findChild(from, sequenceName); + + if (fromSeq.isNull() || toSeq.isNull()) + { + throw InvalidSchema(__FILE__, __LINE__); + } + + // + // TODO: Create cache of base class transforms + // + createSequenceElementTransform(fromInfo, fromSeq, toInfo, toSeq, v); + return; + } + + if (fromContent.isNull() || toContent.isNull()) + { + throw InvalidSchema(__FILE__, __LINE__); + } + + DOM_Node fromExtension = findChild(fromContent, extension); + DOM_Node toExtension = findChild(toContent, extension); + + if (fromExtension.isNull() || toExtension.isNull()) + { + throw InvalidSchema(__FILE__, __LINE__); + } + + // TOOD: getAttributeBase? + string fromBaseName = getAttributeByName(fromExtension, baseAttrName); + string toBaseName = getAttributeByName(toExtension, baseAttrName); + + // + // Cannot change base class. + // + if (fromBaseName != toBaseName) + { + throw IllegalTransform(__FILE__, __LINE__); + } + + DOM_Node fromBaseNode; + DocumentInfo* fromBaseInfo; + findType(_fromDocs, fromInfo, fromBaseName, fromBaseNode, fromBaseInfo); + DOM_Node toBaseNode; + DocumentInfo* toBaseInfo; + findType(_toDocs, toInfo, toBaseName, toBaseNode, toBaseInfo); + + if (fromBaseNode.isNull() || toBaseNode.isNull()) + { + throw SchemaViolation(__FILE__, __LINE__); + } + + // + // Find the content transform for the base type. + // + createClassContentTransform(fromBaseInfo, fromBaseNode, toBaseInfo, toBaseNode, v); + + // + // Look for xs:sequence + // + DOM_Node fromSeq = findChild(fromExtension, sequenceName); + DOM_Node toSeq = findChild(toExtension, sequenceName); + + if (fromSeq.isNull() || toSeq.isNull()) + { + throw InvalidSchema(__FILE__, __LINE__); + } + + createSequenceElementTransform(fromInfo, fromSeq, toInfo, toSeq, v); +} + +// +// Schema for class looks like: +// +// <xs:complexContent> +// <xs:extension base=...> +// <xs:sequence> ... +// +Transform* +TransformFactory::createClassTransform(const DocumentInfo* fromInfo, DOM_Node& from, + const DocumentInfo* toInfo, DOM_Node& to) +{ + string type = getNameAttribute(to); + type += '@'; + type += toInfo->targetNamespace; + + TransformMap::const_iterator p = _staticClassTransforms.find(type); + if (p != _staticClassTransforms.end()) + { + cout << "returning cached static class transform: " << type << endl; + return p->second; + } + + vector<ElementTransform*> v; + createClassContentTransform(fromInfo, from, toInfo, to, v); + + StructTransform* nodeTransform = new StructTransform(); + for(vector<ElementTransform*>::const_iterator p = v.begin(); p != v.end(); ++p) + { + nodeTransform->append(*p); + } + + _staticClassTransforms.insert(make_pair(type, nodeTransform)); + + return nodeTransform; +} + +Transform* +TransformFactory::createSequenceTransform(const DocumentInfo* fromInfo, DOM_Node& from, + const DocumentInfo* toInfo, DOM_Node& to) +{ + static const string sequenceName = "xs:sequence"; + + DOM_Node fromSeq = findChild(from, sequenceName); + DOM_Node toSeq = findChild(to, sequenceName); + if (fromSeq.isNull() || toSeq.isNull()) + { + throw InvalidSchema(__FILE__, __LINE__); + } + + // + // Sequences have one element - which contains the type of the sequence. + // + static const string element = "xs:element"; + + DOM_Node fromElement = findChild(fromSeq, element); + DOM_Node toElement = findChild(toSeq, element); + + if (fromElement.isNull() || toElement.isNull()) + { + throw InvalidSchema(__FILE__, __LINE__); + } + + string fromTypeName = getTypeAttribute(fromElement); + string toTypeName = getTypeAttribute(toElement); + + Transform* transform = createTransformByTypeName(fromInfo, fromTypeName, toInfo, toTypeName); + return new SequenceTransform(transform); +} + +Transform* +TransformFactory::createDictionaryTransform(const DocumentInfo* fromInfo, DOM_Node& from, + const DocumentInfo* toInfo, DOM_Node& to) +{ + static const string sequenceName = "xs:sequence"; + + DOM_Node fromSeq = findChild(from, sequenceName); + DOM_Node toSeq = findChild(to, sequenceName); + if (fromSeq.isNull() || toSeq.isNull()) + { + throw InvalidSchema(__FILE__, __LINE__); + } + + // + // Sequences have one element - which contains the type of the sequence. + // + static const string element = "xs:element"; + + DOM_Node fromElement = findChild(fromSeq, element); + DOM_Node toElement = findChild(toSeq, element); + + if (fromElement.isNull() || toElement.isNull()) + { + throw InvalidSchema(__FILE__, __LINE__); + } + + string fromTypeName = getTypeAttribute(fromElement); + string toTypeName = getTypeAttribute(toElement); + + Transform* transform = createTransformByTypeName(fromInfo, fromTypeName, toInfo, toTypeName); + return new SequenceTransform(transform); +} + +void +TransformFactory::createEnumValues(DOM_Node& to, vector<string>& values) +{ + static const string restriction = "xs:restriction"; + + DOM_Node toRes = findChild(to, restriction); + if (toRes.isNull()) + { + throw InvalidSchema(__FILE__, __LINE__); + } + + // + // Gather up a list of allowable values. + // + DOM_NodeList toResChildren = toRes.getChildNodes(); + + for (unsigned int i = 0; i < toResChildren.getLength(); ++i) + { + DOM_Node toChild = toResChildren.item(i); + + if (toChild.getNodeType() != DOM_Node::ELEMENT_NODE) + { + continue; + } + + if (toString(toChild.getNodeName()) != "xs:enumeration") + { + throw InvalidSchema(__FILE__, __LINE__); + } + + string value = getValueAttribute(toChild); + if (value.empty()) + { + throw InvalidSchema(__FILE__, __LINE__); + } + + values.push_back(value); + } +} + +Transform* +TransformFactory::createEnumerationTransform(DOM_Node& to) +{ + vector<string> values; + createEnumValues(to, values); + + return new ValidateEnumerationTransform(values); +} + +void +TransformFactory::createDefaultInitializedSequenceElementTransform(const DocumentInfo* info, DOM_Node& node, + vector<ElementTransform*>& v) +{ + // + // Allowable transforms: + // + // * Node added. + // * Node removed. + // * Node moved. + // + + DOM_NodeList seqChild = node.getChildNodes(); + for (unsigned int i = 0; i < seqChild.getLength(); ++i) + { + DOM_Node child = seqChild.item(i); + if (child.getNodeType() != DOM_Node::ELEMENT_NODE) + { + continue; + } + + static const string element = "xs:element"; + if (toString(child.getNodeName()) != element) + { + throw InvalidSchema(__FILE__, __LINE__); + } + + string elementName = getNameAttribute(child); + string typeName = getTypeAttribute(child); + + Transform* t = createDefaultInitializedTransform(info, typeName); + ElementTransform* transform = new ElementTransform(info->targetNamespace, elementName, t); + + v.push_back(transform); + } +} + +Transform* +TransformFactory::createDefaultInitializedStructTransform(const DocumentInfo* info, DOM_Node& node) +{ + static const string sequenceName = "xs:sequence"; + + DOM_Node seq = findChild(node, sequenceName); + if (seq.isNull()) + { + throw InvalidSchema(__FILE__, __LINE__); + } + + DefaultInitializedStructTransform* transform = new DefaultInitializedStructTransform(); + + vector<ElementTransform*> v; + createDefaultInitializedSequenceElementTransform(info, seq, v); + for(vector<ElementTransform*>::const_iterator p = v.begin(); p != v.end(); ++p) + { + transform->append(*p); + } + + return transform; +} + +Transform* +TransformFactory::createDefaultInitializedTransform(const DocumentInfo* info, const string& typeName) +{ + string fullTypeName = convertQName(typeName, info); + TransformMap::const_iterator p = _defaultInitializedTransforms.find(fullTypeName); + if (p != _defaultInitializedTransforms.end()) + { + // + // Return cached empty transform. + // + return p->second; + } + + DOM_Node n; + DocumentInfo* nInfo; + Type type = getTypeByName(_toDocs, info, typeName, n, nInfo); + + Transform* transform = 0; + switch(type) + { + case TypeInteger: + transform = new EmitStringTransform("0"); + break; + + case TypeFloat: + transform = new EmitStringTransform("0.0"); + break; + + case TypeProxy: + case TypeReference: + case TypeString: + // + // Default string, reference & proxy is empty. + // + transform = new EmitStringTransform(""); + break; + + case TypeEnumeration: + { + vector<string> values; + createEnumValues(n, values); + transform = new EmitStringTransform(values[0]); + break; + } + + case TypeStruct: + { + if (n.isNull()) + { + throw InvalidSchema(__FILE__, __LINE__); + } + transform = createDefaultInitializedStructTransform(info, n); + break; + } + + case TypeDictionary: + case TypeSequence: + { + if (n.isNull()) + { + throw InvalidSchema(__FILE__, __LINE__); + } + transform = new EmitAttributeTransform("length=\"0\""); + break; + } + + case TypeInternal: + case TypeException: + case TypeDictionaryContent: + case TypeClass: + default: + throw IllegalTransform(__FILE__, __LINE__); + } + + if (transform == 0) + { + throw IllegalTransform(__FILE__, __LINE__); + } + + _defaultInitializedTransforms.insert(make_pair(fullTypeName, transform)); + + return transform; +} + +void +print(DOM_Node& node, unsigned int level) +{ + if (node.isNull()) + { + return; + } + unsigned int i; + for (i = 0; i < level; ++i) + { + cout << " "; + } + ++level; + cout << toString(node.getNodeName()) << endl; + + DOM_NodeList children = node.getChildNodes(); + + for (i = 0; i < children.getLength(); ++i) + { + for (unsigned int l = 0; l < level; ++l) + { + cout << " "; + } + DOM_Node child = children.item(i); + print(child, level); + } +} + +DOM_Node +TransformFactory::findTypeInDocument(DOM_Document& doc, const string& local) +{ + DOM_Node schema = findSchemaRoot(doc); + + DOM_NodeList children = schema.getChildNodes(); + unsigned int i; + for (i = 0; i < children.getLength(); ++i) + { + DOM_Node child = children.item(i); + if (child.getNodeType() == DOM_Node::ELEMENT_NODE) + { + string na = getNameAttribute(child); + if (na == local) + { + return child; + } + } + } + + return DOM_Node(); +} + +// +// This find method takes the type QName and then maps the prefix +// portion to a namespace in the current document (info). Next the +// documents which have the given targetNamespace are searched for an +// element node with that name. +// +void +TransformFactory::findType(const DocumentMap& docs, const DocumentInfo* info, const string& type, + DOM_Node& n, DocumentInfo*& nInfo) +{ + string uri; + string local; + + size_t pos = type.find(':'); + if (pos != string::npos) + { + string prefix = type.substr(0, pos); + PrefixURIMap::const_iterator p = info->nsMap.find(prefix); + if (p == info->nsMap.end()) + { + throw InvalidSchema(__FILE__, __LINE__); + } + uri = p->second; + local = type.substr(pos+1); + + //cout << "looking for : " << local << " @ " << uri << endl; + } + else + { + uri = info->targetNamespace; + local = type; + + //cout << "looking for : " << local << " @ " << uri << endl; + } + + // + // Find all documents with targetNamespace == uri + // + // TODO: multimap + // + DocumentMap::const_iterator p = docs.find(uri); + if (p != docs.end()) + { + n = findTypeInDocument(p->second->document, local); + nInfo = p->second; + } +} + +DOM_Node +TransformFactory::findSchemaRoot(const DOM_Document& root) +{ + // + // TODO: Can this be static const? + // + DOMString schemaURI("http://www.w3.org/2001/XMLSchema"); + DOMString schemaLocalName("schema"); + DOM_Node n; + + DOM_NodeList nodes = root.getElementsByTagNameNS(schemaURI, schemaLocalName); + if (nodes.getLength() != 1) + { + throw InvalidSchema(__FILE__, __LINE__); + } + return nodes.item(0); +} + +void +TransformFactory::import(DocumentMap& documents, const string& ns, const string& loc) +{ + DOMTreeErrorReporter errorReporter; + DOMParser parser; + parser.setValidationScheme(DOMParser::Val_Never); + parser.setDoNamespaces(true); + parser.setErrorHandler(&errorReporter); + // Entity resolver? + + try + { + parser.parse(loc.c_str()); + if (errorReporter.getSawErrors()) + { + throw InvalidSchema(__FILE__, __LINE__); + } + } + catch (const XMLException& toCatch) + { + cout << "Exception message is: \n" + << DOMString(toCatch.getMessage()) << "\n" ; + throw InvalidSchema(__FILE__, __LINE__); + } + catch (const SAXParseException& toCatch) + { + cout << "Exception message is: \n" + << DOMString(toCatch.getMessage()) << "\n" ; + throw InvalidSchema(__FILE__, __LINE__); + } + catch (...) + { + cout << "Unexpected Exception \n" ; + throw InvalidSchema(__FILE__, __LINE__); + } + DOM_Document document = parser.getDocument(); + + DOM_Node schema = findSchemaRoot(document); + + DocumentInfo* info = new DocumentInfo(); + info->document = document; + + createNSMap(info, schema); + + // + // Rename the targetNamespace: + // + info->targetNamespace = ns; + + documents.insert(make_pair(info->targetNamespace, info)); + + // + // Process any imports in the imported document. + // + processImport(document, documents); +} + +void +TransformFactory::processImport(DOM_Document& parent, DocumentMap& documents) +{ + DOM_Node schema = findSchemaRoot(parent); + assert(!schema.isNull()); + + DOM_Node child = schema.getFirstChild(); + while (!child.isNull()) + { + static const string importName = "xs:import"; + string nodeName = toString(child.getNodeName()); + if (nodeName == importName) + { + string ns = getAttributeByName(child, "namespace"); + string loc = getAttributeByName(child, "schemaLocation"); + + import(documents, ns, loc); + } + child = child.getNextSibling(); + } +} + +void +TransformFactory::processElements(const DocumentInfo* info) +{ + DOM_Node schema = findSchemaRoot(info->document); + + DOM_NodeList children = schema.getChildNodes(); + unsigned int i; + for (i = 0; i < children.getLength(); ++i) + { + DOM_Node child = children.item(i); + if (child.getNodeType() != DOM_Node::ELEMENT_NODE) + { + continue; + } + string nodeName = toString(child.getNodeName()); + + static const string element = "xs:element"; + + if (nodeName != element) + { + continue; + } + + string nameAttr = getNameAttribute(child); + + DOM_Node to; + DocumentInfo* toNodeInfo; // Overrides the top-level toInfo. + + // + // In this case info is correct since the element name must be + // an unqualified name. + // + assert(nameAttr.find(':') == string::npos); + + findType(_toDocs, info, nameAttr, to, toNodeInfo); + if (to.isNull()) + { + cout << "ignoring " << nameAttr << endl; + // + // No equivalent in the new schema. + // + continue; + } + + string toNodeName = toString(to.getNodeName()); + string toNameAttr = getNameAttribute(to); + + // + // Sanity check. + // + assert(toNameAttr == nameAttr); + + // + // Construct the full element name - local@uri + // + string fullElementName = nameAttr; + fullElementName += '@'; + fullElementName += info->targetNamespace; + + if (_elements.find(fullElementName) != _elements.end()) + { + cout << "redefinition of element: " << fullElementName << endl; + throw InvalidSchema(__FILE__, __LINE__); + } + + string fromTypeName = getTypeAttribute(child); + string toTypeName = getTypeAttribute(to); + + // + // Ignore anonymous elements (operation contents). + // + if (fromTypeName.empty() && toTypeName.empty()) + { + continue; + } + + // + // However, it's not legal for an element to change + // type. + // + if (fromTypeName.empty() || toTypeName.empty()) + { + throw InvalidSchema(__FILE__, __LINE__); + } + + Transform* transform = createTransformByTypeName(info, fromTypeName, toNodeInfo, toTypeName); + //cout << "new element: " << fullElementName << endl; + + // + // Add association between name & transform + // + _elements.insert(make_pair(fullElementName, transform)); + } +} + + +Transformer::Transformer(DOM_Document& fromDoc, DOM_Document& toDoc) +{ + TransformFactory factory(fromDoc, toDoc, _elements, _staticClassTransforms); +} + +Transformer::~Transformer() +{ +} + +void +Transformer::transform(ostream& os, DOM_Document& doc) +{ + DOM_Node root = doc.getFirstChild(); + DocumentInfo info; + info.document = doc; + createNSMap(&info, root); + + DOM_NodeList children = root.getChildNodes(); + for (unsigned int i = 0; i < children.getLength(); ++i) + { + DOM_Node child = children.item(i); + if (child.getNodeType() != DOM_Node::ELEMENT_NODE) + { + continue; + } + + string nodeName = toString(child.getNodeName()); + + // + // Create local@namespace version of the element name. + // + string n = convertQName(nodeName, &info); + + //cout << "looking for : " << n << endl; + + TransformMap::const_iterator p = _elements.find(n); + if (p == _elements.end()) + { + cerr << "cannot find element: " << n << endl; + throw SchemaViolation(__FILE__, __LINE__); + } + + p->second->transform(os, &info, nodeName, child); + } +} + |