summaryrefslogtreecommitdiff
path: root/cpp/src/Transform/Transformer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/Transform/Transformer.cpp')
-rw-r--r--cpp/src/Transform/Transformer.cpp2156
1 files changed, 2156 insertions, 0 deletions
diff --git a/cpp/src/Transform/Transformer.cpp b/cpp/src/Transform/Transformer.cpp
new file mode 100644
index 00000000000..f6f37a790f2
--- /dev/null
+++ b/cpp/src/Transform/Transformer.cpp
@@ -0,0 +1,2156 @@
+// **********************************************************************
+//
+// Copyright (c) 2003
+// ZeroC, Inc.
+// Billerica, MA, USA
+//
+// All Rights Reserved.
+//
+// Ice is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License version 2 as published by
+// the Free Software Foundation.
+//
+// **********************************************************************
+
+#include <Transform/Transformer.h>
+#include <Transform/TransformUtil.h>
+#include <Transform/Data.h>
+#include <Transform/Parser.h>
+#include <Transform/Error.h>
+#include <Transform/Exception.h>
+#include <Transform/Analyzer.h>
+#include <IceXML/Parser.h>
+#include <db_cxx.h>
+#include <stack>
+
+using namespace std;
+
+namespace Transform
+{
+
+class Descriptor;
+typedef IceUtil::Handle<Descriptor> DescriptorPtr;
+
+class TransformDescriptor;
+typedef IceUtil::Handle<TransformDescriptor> TransformDescriptorPtr;
+
+typedef map<string, TransformDescriptorPtr> TransformMap;
+
+class DeleteRecordException {};
+
+class TransformSymbolTable : public SymbolTable
+{
+public:
+
+ TransformSymbolTable(const DataFactoryPtr&, const Slice::UnitPtr&, const Slice::UnitPtr&, const ErrorReporterPtr&,
+ TransformSymbolTable* = 0);
+
+ virtual DataPtr getValue(const Identifier&) const;
+ virtual DataPtr getConstantValue(const string&) const;
+
+ void add(const string&, const DataPtr&);
+
+private:
+
+ DataPtr findValue(const string&);
+
+ DataFactoryPtr _factory;
+ Slice::UnitPtr _old;
+ Slice::UnitPtr _new;
+ ErrorReporterPtr _errorReporter;
+ TransformSymbolTable* _parent;
+ typedef map<string, DataPtr> DataMap;
+ DataMap _dataMap;
+ DataMap _constantCache;
+};
+
+class Descriptor : virtual public IceUtil::SimpleShared
+{
+public:
+
+ virtual ~Descriptor();
+
+ virtual DescriptorPtr parent() const;
+ virtual void addChild(const DescriptorPtr&) = 0;
+ virtual void validate() = 0;
+ virtual void execute(TransformSymbolTable&, DataInterceptor&) = 0;
+
+ virtual DataFactoryPtr factory() const;
+ virtual Slice::UnitPtr oldUnit() const;
+ virtual Slice::UnitPtr newUnit() const;
+ virtual ErrorReporterPtr errorReporter() const;
+
+protected:
+
+ Descriptor(const DescriptorPtr&, int);
+
+ NodePtr parse(const string&) const;
+
+ Slice::TypePtr findType(const Slice::UnitPtr&, const string&);
+
+ DescriptorPtr _parent;
+ int _line;
+};
+
+class SetDescriptor : public Descriptor
+{
+public:
+
+ SetDescriptor(const DescriptorPtr&, int, const string&, const string&, const string&, const string&, bool);
+
+ virtual void addChild(const DescriptorPtr&);
+ virtual void validate();
+ virtual void execute(TransformSymbolTable&, DataInterceptor&);
+
+private:
+
+ IdentNodePtr _target;
+ NodePtr _key;
+ string _keyStr;
+ NodePtr _value;
+ string _valueStr;
+ string _type;
+ bool _convert;
+};
+
+class RemoveDescriptor : public Descriptor
+{
+public:
+
+ RemoveDescriptor(const DescriptorPtr&, int, const string&, const string&);
+
+ virtual void addChild(const DescriptorPtr&);
+ virtual void validate();
+ virtual void execute(TransformSymbolTable&, DataInterceptor&);
+
+private:
+
+ IdentNodePtr _target;
+ NodePtr _key;
+ string _keyStr;
+};
+
+class DeleteDescriptor : public Descriptor
+{
+public:
+
+ DeleteDescriptor(const DescriptorPtr&, int);
+
+ virtual void addChild(const DescriptorPtr&);
+ virtual void validate();
+ virtual void execute(TransformSymbolTable&, DataInterceptor&);
+};
+
+class FailDescriptor : public Descriptor
+{
+public:
+
+ FailDescriptor(const DescriptorPtr&, int, const string&);
+
+ virtual void addChild(const DescriptorPtr&);
+ virtual void validate();
+ virtual void execute(TransformSymbolTable&, DataInterceptor&);
+
+private:
+
+ string _message;
+};
+
+class EchoDescriptor : public Descriptor
+{
+public:
+
+ EchoDescriptor(const DescriptorPtr&, int, const string&, const string&);
+
+ virtual void addChild(const DescriptorPtr&);
+ virtual void validate();
+ virtual void execute(TransformSymbolTable&, DataInterceptor&);
+
+private:
+
+ string _message;
+ string _value;
+ NodePtr _node;
+};
+
+class ExecutableContainerDescriptor : virtual public Descriptor
+{
+public:
+
+ ExecutableContainerDescriptor(const DescriptorPtr&, int, const string&);
+
+ virtual void addChild(const DescriptorPtr&);
+ virtual void validate();
+ virtual void execute(TransformSymbolTable&, DataInterceptor&);
+
+private:
+
+ string _name;
+ vector<DescriptorPtr> _children;
+};
+
+class ConditionalDescriptor : public ExecutableContainerDescriptor
+{
+public:
+
+ ConditionalDescriptor(const DescriptorPtr&, int, const string&);
+
+ virtual void execute(TransformSymbolTable&, DataInterceptor&);
+
+private:
+
+ string _test;
+ NodePtr _node;
+};
+
+class IterateDescriptor : public ExecutableContainerDescriptor
+{
+public:
+
+ IterateDescriptor(const DescriptorPtr&, int, const string&, const string&, const string&, const string&,
+ const string&);
+
+ virtual void execute(TransformSymbolTable&, DataInterceptor&);
+
+private:
+
+ IdentNodePtr _target;
+ string _key;
+ string _value;
+ string _element;
+ string _index;
+};
+
+class TransformDescriptor : public ExecutableContainerDescriptor
+{
+public:
+
+ TransformDescriptor(const DescriptorPtr&, int, const string&, bool);
+
+ string type() const;
+ bool doDefaultTransform() const;
+
+private:
+
+ bool _default;
+ Slice::TypePtr _oldType;
+ Slice::TypePtr _newType;
+};
+
+class InitDescriptor : virtual public ExecutableContainerDescriptor, virtual public DataInitializer
+{
+public:
+
+ InitDescriptor(const DescriptorPtr&, int, const string&);
+
+ virtual void initialize(const DataFactoryPtr&, const DataPtr&, const Ice::CommunicatorPtr&);
+
+ string type() const;
+
+private:
+
+ Slice::TypePtr _type;
+};
+typedef IceUtil::Handle<InitDescriptor> InitDescriptorPtr;
+
+// TODO: Is this class necessary?
+class RecordDescriptor : public ExecutableContainerDescriptor
+{
+public:
+
+ RecordDescriptor(const DescriptorPtr&, int);
+};
+typedef IceUtil::Handle<RecordDescriptor> RecordDescriptorPtr;
+
+class DatabaseDescriptor : public Descriptor
+{
+public:
+
+ DatabaseDescriptor(const DescriptorPtr&, int, const string&, const string&);
+
+ virtual void addChild(const DescriptorPtr&);
+ virtual void validate();
+ virtual void execute(TransformSymbolTable&, DataInterceptor&);
+
+ bool transform(IceInternal::BasicStream&, IceInternal::BasicStream&, IceInternal::BasicStream&,
+ IceInternal::BasicStream&, const TransformMap&);
+
+private:
+
+ Slice::TypePtr _oldKey;
+ Slice::TypePtr _newKey;
+ Slice::TypePtr _oldValue;
+ Slice::TypePtr _newValue;
+ RecordDescriptorPtr _record;
+};
+typedef IceUtil::Handle<DatabaseDescriptor> DatabaseDescriptorPtr;
+
+class TransformerDescriptor : public Descriptor
+{
+public:
+
+ TransformerDescriptor(const DataFactoryPtr&, const Slice::UnitPtr&, const Slice::UnitPtr&, const ErrorReporterPtr&,
+ int);
+
+ virtual void addChild(const DescriptorPtr&);
+ virtual void validate();
+ virtual void execute(TransformSymbolTable&, DataInterceptor&);
+
+ virtual DataFactoryPtr factory() const;
+ virtual Slice::UnitPtr oldUnit() const;
+ virtual Slice::UnitPtr newUnit() const;
+ virtual ErrorReporterPtr errorReporter() const;
+
+ void transform(const Ice::CommunicatorPtr&, Db*, Db*);
+
+private:
+
+ DataFactoryPtr _factory;
+ Slice::UnitPtr _old;
+ Slice::UnitPtr _new;
+ ErrorReporterPtr _errorReporter;
+ DatabaseDescriptorPtr _database;
+ TransformMap _transforms;
+ vector<DescriptorPtr> _children;
+};
+typedef IceUtil::Handle<TransformerDescriptor> TransformerDescriptorPtr;
+
+class DescriptorHandler : public IceXML::Handler
+{
+public:
+
+ DescriptorHandler(const DataFactoryPtr&, const Slice::UnitPtr&, const Slice::UnitPtr&, const ErrorReporterPtr&);
+
+ virtual void startElement(const std::string&, const IceXML::Attributes&, int, int);
+ virtual void endElement(const std::string&, int, int);
+ virtual void characters(const std::string&, int, int);
+ virtual void error(const std::string&, int, int);
+
+ TransformerDescriptorPtr descriptor() const;
+
+private:
+
+ DataFactoryPtr _factory;
+ Slice::UnitPtr _old;
+ Slice::UnitPtr _new;
+ ErrorReporterPtr _errorReporter;
+ DescriptorPtr _current;
+ TransformerDescriptorPtr _transformer;
+};
+
+class TransformInterceptor : public DataInterceptor
+{
+public:
+
+ TransformInterceptor(const DataFactoryPtr&, const ErrorReporterPtr&, const Slice::UnitPtr&,
+ const Slice::UnitPtr&, const TransformMap&);
+
+ virtual bool preTransform(const DataPtr&, const DataPtr&);
+ virtual void postTransform(const DataPtr&, const DataPtr&);
+ virtual ObjectDataMap& getObjectMap();
+
+private:
+
+ DataFactoryPtr _factory;
+ ErrorReporterPtr _errorReporter;
+ Slice::UnitPtr _old;
+ Slice::UnitPtr _new;
+ const TransformMap& _transformMap;
+ ObjectDataMap _objectMap;
+};
+
+} // End of namespace Transform
+
+//
+// TransformSymbolTable
+//
+Transform::TransformSymbolTable::TransformSymbolTable(const DataFactoryPtr& factory, const Slice::UnitPtr& oldUnit,
+ const Slice::UnitPtr& newUnit,
+ const ErrorReporterPtr& errorReporter,
+ TransformSymbolTable* parent) :
+ _factory(factory), _old(oldUnit), _new(newUnit), _errorReporter(errorReporter), _parent(parent)
+{
+}
+
+Transform::DataPtr
+Transform::TransformSymbolTable::getValue(const Identifier& id) const
+{
+ DataPtr result;
+
+ Identifier tmpid = id;
+ string s = tmpid.front();
+ tmpid.erase(tmpid.begin());
+
+ DataMap::const_iterator p = _dataMap.find(s);
+ if(p != _dataMap.end())
+ {
+ result = p->second;
+ }
+
+ if(!result && _parent)
+ {
+ result = _parent->findValue(s);
+ }
+
+ if(!result)
+ {
+ ostringstream ostr;
+ ostr << "invalid identifier `" << id << "'";
+ throw TransformException(__FILE__, __LINE__, ostr.str());
+ }
+
+ for(Identifier::const_iterator p = tmpid.begin(); p != tmpid.end(); ++p)
+ {
+ DataPtr d = result->getMember(*p);
+ if(!d)
+ {
+ ostringstream ostr;
+ ostr << "invalid identifier `" << id << "'";
+ throw TransformException(__FILE__, __LINE__, ostr.str());
+ }
+ result = d;
+ }
+
+ return result;
+}
+
+Transform::DataPtr
+Transform::TransformSymbolTable::getConstantValue(const string& name) const
+{
+ string::size_type pos;
+ Slice::UnitPtr unit;
+ string key;
+
+ DataMap::const_iterator p = _constantCache.find(name);
+ if(p != _constantCache.end())
+ {
+ return p->second;
+ }
+
+ if(_parent)
+ {
+ return _parent->getConstantValue(name);
+ }
+
+ //
+ // Name must start with "::Old" or "::New" to indicate the Slice unit.
+ //
+ pos = name.find("::Old");
+ if(pos == 0)
+ {
+ if(name.length() > 5)
+ {
+ key = name.substr(5);
+ unit = _old;
+ }
+ }
+ else
+ {
+ pos = name.find("::New");
+ if(pos == 0)
+ {
+ if(name.length() > 5)
+ {
+ key = name.substr(5);
+ unit = _new;
+ }
+ }
+ }
+
+ if(key.empty())
+ {
+ throw TransformException(__FILE__, __LINE__, "invalid constant name `" + name + "'");
+ }
+
+ Slice::ContainedList l = unit->findContents(key);
+ if(l.empty())
+ {
+ throw TransformException(__FILE__, __LINE__, "unknown constant `" + name + "'");
+ }
+
+ Slice::EnumeratorPtr e = Slice::EnumeratorPtr::dynamicCast(l.front());
+ Slice::ConstPtr c = Slice::ConstPtr::dynamicCast(l.front());
+ if(!e && !c)
+ {
+ throw TransformException(__FILE__, __LINE__, "`" + name + "' does not refer to a Slice constant or enumerator");
+ }
+
+ DataPtr result;
+
+ if(c)
+ {
+ Slice::TypePtr type = c->type();
+ string value = c->value();
+ Slice::BuiltinPtr b = Slice::BuiltinPtr::dynamicCast(type);
+ if(b)
+ {
+ switch(b->kind())
+ {
+ case Slice::Builtin::KindByte:
+ case Slice::Builtin::KindShort:
+ case Slice::Builtin::KindInt:
+ case Slice::Builtin::KindLong:
+ {
+ string::size_type pos;
+ Ice::Long n;
+ bool success = IceUtil::stringToInt64(value, n, pos);
+ assert(success);
+ result = _factory->createInteger(n, true);
+ break;
+ }
+
+ case Slice::Builtin::KindBool:
+ {
+ result = _factory->createBoolean(value == "true", true);
+ break;
+ }
+
+ case Slice::Builtin::KindFloat:
+ case Slice::Builtin::KindDouble:
+ {
+ double v = strtod(value.c_str(), 0);
+ result = _factory->createDouble(v, true);
+ break;
+ }
+
+ case Slice::Builtin::KindString:
+ {
+ result = _factory->createString(value, true);
+ break;
+ }
+
+ case Slice::Builtin::KindObject:
+ case Slice::Builtin::KindObjectProxy:
+ case Slice::Builtin::KindLocalObject:
+ assert(false);
+ }
+ }
+ else
+ {
+ Slice::EnumPtr en = Slice::EnumPtr::dynamicCast(type);
+ assert(en);
+ Slice::EnumeratorList el = en->getEnumerators();
+ for(Slice::EnumeratorList::iterator p = el.begin(); p != el.end(); ++p)
+ {
+ if((*p)->name() == value)
+ {
+ e = *p;
+ break;
+ }
+ }
+ assert(e);
+ }
+ }
+
+ if(!result)
+ {
+ assert(e);
+ result = _factory->create(e->type(), true);
+ EnumDataPtr ed = EnumDataPtr::dynamicCast(result);
+ assert(ed);
+ ed->setValueAsString(e->name());
+ }
+
+ //
+ // Cache the value.
+ //
+ const_cast<DataMap&>(_constantCache).insert(DataMap::value_type(name, result));
+
+ return result;
+}
+
+void
+Transform::TransformSymbolTable::add(const string& name, const DataPtr& data)
+{
+ DataPtr d = findValue(name);
+ if(d)
+ {
+ throw TransformException(__FILE__, __LINE__, "`" + name + "' is already defined");
+ }
+
+ _dataMap.insert(DataMap::value_type(name, data));
+}
+
+Transform::DataPtr
+Transform::TransformSymbolTable::findValue(const string& name)
+{
+ DataMap::const_iterator p = _dataMap.find(name);
+ if(p != _dataMap.end())
+ {
+ return p->second;
+ }
+
+ if(_parent)
+ {
+ return _parent->findValue(name);
+ }
+
+ return 0;
+}
+
+//
+// Descriptor
+//
+Transform::Descriptor::Descriptor(const DescriptorPtr& parent, int line) :
+ _parent(parent), _line(line)
+{
+}
+
+Transform::Descriptor::~Descriptor()
+{
+}
+
+Transform::DescriptorPtr
+Transform::Descriptor::parent() const
+{
+ return _parent;
+}
+
+Transform::DataFactoryPtr
+Transform::Descriptor::factory() const
+{
+ assert(_parent);
+ return _parent->factory();
+}
+
+Slice::UnitPtr
+Transform::Descriptor::oldUnit() const
+{
+ assert(_parent);
+ return _parent->oldUnit();
+}
+
+Slice::UnitPtr
+Transform::Descriptor::newUnit() const
+{
+ assert(_parent);
+ return _parent->newUnit();
+}
+
+Transform::ErrorReporterPtr
+Transform::Descriptor::errorReporter() const
+{
+ assert(_parent);
+ return _parent->errorReporter();
+}
+
+Transform::NodePtr
+Transform::Descriptor::parse(const string& expr) const
+{
+ return Parser::parse(expr, factory(), errorReporter());
+}
+
+Slice::TypePtr
+Transform::Descriptor::findType(const Slice::UnitPtr& u, const string& type)
+{
+ Slice::TypeList l;
+
+ l = u->lookupType(type, false);
+ if(l.empty())
+ {
+ errorReporter()->error("unknown type `" + type + "'");
+ }
+
+ return l.front();
+}
+
+//
+// SetDescriptor
+//
+Transform::SetDescriptor::SetDescriptor(const DescriptorPtr& parent, int line, const string& target, const string& key,
+ const string& value, const string& type, bool convert) :
+ Descriptor(parent, line), _keyStr(key), _valueStr(value), _type(type), _convert(convert)
+{
+ DescriptorErrorContext ctx(errorReporter(), "set", _line);
+
+ NodePtr node = parse(target);
+ _target = IdentNodePtr::dynamicCast(node);
+ if(!_target)
+ {
+ errorReporter()->error("`target' attribute is not an identifier: `" + target + "'");
+ }
+
+ if(!key.empty())
+ {
+ _key = parse(key);
+ }
+
+ if(!value.empty())
+ {
+ _value = parse(value);
+ }
+}
+
+void
+Transform::SetDescriptor::addChild(const DescriptorPtr&)
+{
+ DescriptorErrorContext ctx(errorReporter(), "set", _line);
+ errorReporter()->error("<set> cannot have child elements");
+}
+
+void
+Transform::SetDescriptor::validate()
+{
+}
+
+void
+Transform::SetDescriptor::execute(TransformSymbolTable& sym, DataInterceptor& interceptor)
+{
+ DescriptorErrorContext ctx(errorReporter(), "set", _line);
+
+ DataPtr data = sym.getValue(_target->getValue());
+ if(data->readOnly())
+ {
+ ostringstream ostr;
+ ostr << _target->getValue();
+ errorReporter()->error("target `" + ostr.str() + "' cannot be modified");
+ }
+
+ DataPtr key;
+ if(_key)
+ {
+ try
+ {
+ key = _key->evaluate(sym);
+ }
+ catch(const EvaluateException& ex)
+ {
+ errorReporter()->error("evaluation of key `" + _keyStr + "' failed:\n" + ex.reason());
+ }
+ }
+
+ DataPtr value;
+ if(_value)
+ {
+ try
+ {
+ value = _value->evaluate(sym);
+ }
+ catch(const EvaluateException& ex)
+ {
+ errorReporter()->error("evaluation of value `" + _valueStr + "' failed:\n" + ex.reason());
+ }
+ }
+
+ Destroyer<DataPtr> valueDestroyer;
+ if(!_type.empty())
+ {
+ assert(!value);
+ Slice::TypeList l = newUnit()->lookupType(_type, false);
+ if(l.empty())
+ {
+ errorReporter()->error("type `" + _type + "' not found");
+ }
+ value = factory()->create(l.front(), false);
+ valueDestroyer.set(value);
+ ObjectRefPtr ref = ObjectRefPtr::dynamicCast(value);
+ if(!ref)
+ {
+ errorReporter()->error("type `" + _type + "' is not a class");
+ }
+ ref->instantiate();
+ }
+
+ if(_key)
+ {
+ DictionaryDataPtr dict = DictionaryDataPtr::dynamicCast(data);
+ if(!dict)
+ {
+ ostringstream ostr;
+ ostr << _target->getValue();
+ errorReporter()->error("target `" + ostr.str() + "' is not a dictionary");
+ }
+ dict->put(key, value, interceptor, _convert);
+ }
+ else
+ {
+ data->set(value, interceptor, _convert);
+ }
+}
+
+//
+// RemoveDescriptor
+//
+Transform::RemoveDescriptor::RemoveDescriptor(const DescriptorPtr& parent, int line, const string& target,
+ const string& key) :
+ Descriptor(parent, line), _keyStr(key)
+{
+ DescriptorErrorContext ctx(errorReporter(), "remove", _line);
+
+ NodePtr node = parse(target);
+ _target = IdentNodePtr::dynamicCast(node);
+ if(!_target)
+ {
+ errorReporter()->error("`target' attribute is not an identifier: `" + target + "'");
+ }
+
+ _key = parse(key);
+}
+
+void
+Transform::RemoveDescriptor::addChild(const DescriptorPtr&)
+{
+ DescriptorErrorContext ctx(errorReporter(), "remove", _line);
+ errorReporter()->error("<remove> cannot have child elements");
+}
+
+void
+Transform::RemoveDescriptor::validate()
+{
+}
+
+void
+Transform::RemoveDescriptor::execute(TransformSymbolTable& sym, DataInterceptor& interceptor)
+{
+ DescriptorErrorContext ctx(errorReporter(), "remove", _line);
+
+ DataPtr key;
+ try
+ {
+ key = _key->evaluate(sym);
+ }
+ catch(const EvaluateException& ex)
+ {
+ errorReporter()->error("evaluation of key `" + _keyStr + "' failed:\n" + ex.reason());
+ }
+
+ DataPtr data = sym.getValue(_target->getValue());
+ if(data->readOnly())
+ {
+ ostringstream ostr;
+ ostr << _target->getValue();
+ errorReporter()->error("target `" + ostr.str() + "' cannot be modified");
+ }
+
+ DictionaryDataPtr dict = DictionaryDataPtr::dynamicCast(data);
+ if(!dict)
+ {
+ ostringstream ostr;
+ ostr << _target->getValue();
+ errorReporter()->error("target `" + ostr.str() + "' is not a dictionary");
+ }
+ dict->remove(key);
+}
+
+//
+// DeleteDescriptor
+//
+Transform::DeleteDescriptor::DeleteDescriptor(const DescriptorPtr& parent, int line) :
+ Descriptor(parent, line)
+{
+}
+
+void
+Transform::DeleteDescriptor::addChild(const DescriptorPtr&)
+{
+ DescriptorErrorContext ctx(errorReporter(), "delete", _line);
+ errorReporter()->error("<delete> cannot have child elements");
+}
+
+void
+Transform::DeleteDescriptor::validate()
+{
+}
+
+void
+Transform::DeleteDescriptor::execute(TransformSymbolTable&, DataInterceptor&)
+{
+ throw DeleteRecordException();
+}
+
+//
+// FailDescriptor
+//
+Transform::FailDescriptor::FailDescriptor(const DescriptorPtr& parent, int line, const string& message) :
+ Descriptor(parent, line), _message(message)
+{
+}
+
+void
+Transform::FailDescriptor::addChild(const DescriptorPtr&)
+{
+ DescriptorErrorContext ctx(errorReporter(), "fail", _line);
+ errorReporter()->error("<fail> cannot have child elements");
+}
+
+void
+Transform::FailDescriptor::validate()
+{
+}
+
+void
+Transform::FailDescriptor::execute(TransformSymbolTable&, DataInterceptor&)
+{
+ throw TransformException(__FILE__, __LINE__, _message);
+}
+
+//
+// EchoDescriptor
+//
+Transform::EchoDescriptor::EchoDescriptor(const DescriptorPtr& parent, int line, const string& message,
+ const string& value) :
+ Descriptor(parent, line), _message(message), _value(value)
+{
+ if(!value.empty())
+ {
+ _node = parse(value);
+ }
+}
+
+void
+Transform::EchoDescriptor::addChild(const DescriptorPtr&)
+{
+ DescriptorErrorContext ctx(errorReporter(), "echo", _line);
+ errorReporter()->error("<echo> cannot have child elements");
+}
+
+void
+Transform::EchoDescriptor::validate()
+{
+}
+
+void
+Transform::EchoDescriptor::execute(TransformSymbolTable& sym, DataInterceptor&)
+{
+ DescriptorErrorContext ctx(errorReporter(), "echo", _line);
+
+ ostream& out = errorReporter()->stream();
+
+ if(!_message.empty())
+ {
+ out << _message;
+ }
+
+ if(_node)
+ {
+ try
+ {
+ DataPtr v = _node->evaluate(sym);
+ v->print(out);
+ }
+ catch(const EvaluateException& ex)
+ {
+ errorReporter()->error("evaluation of value `" + _value + "' failed:\n" + ex.reason());
+ }
+ }
+
+ out << endl;
+}
+
+//
+// ExecutableContainerDescriptor
+//
+Transform::ExecutableContainerDescriptor::ExecutableContainerDescriptor(const DescriptorPtr& parent, int line,
+ const string& name) :
+ Descriptor(parent, line), _name(name)
+{
+}
+
+void
+Transform::ExecutableContainerDescriptor::addChild(const DescriptorPtr& child)
+{
+ _children.push_back(child);
+}
+
+void
+Transform::ExecutableContainerDescriptor::validate()
+{
+ for(vector<DescriptorPtr>::iterator p = _children.begin(); p != _children.end(); ++p)
+ {
+ (*p)->validate();
+ }
+}
+
+void
+Transform::ExecutableContainerDescriptor::execute(TransformSymbolTable& sym, DataInterceptor& interceptor)
+{
+ for(vector<DescriptorPtr>::iterator p = _children.begin(); p != _children.end(); ++p)
+ {
+ (*p)->execute(sym, interceptor);
+ }
+}
+
+//
+// ConditionalDescriptor
+//
+Transform::ConditionalDescriptor::ConditionalDescriptor(const DescriptorPtr& parent, int line, const string& test) :
+ ExecutableContainerDescriptor(parent, line, "if"), Descriptor(parent, line), _test(test)
+{
+ DescriptorErrorContext ctx(errorReporter(), "if", _line);
+ _node = parse(test);
+}
+
+void
+Transform::ConditionalDescriptor::execute(TransformSymbolTable& sym, DataInterceptor& interceptor)
+{
+ DescriptorErrorContext ctx(errorReporter(), "if", _line);
+
+ try
+ {
+ DataPtr b = _node->evaluate(sym);
+ BooleanDataPtr bd = BooleanDataPtr::dynamicCast(b);
+ if(!bd)
+ {
+ errorReporter()->error("expression `" + _test + "' does not evaluate to a boolean");
+ }
+ if(bd->booleanValue())
+ {
+ ExecutableContainerDescriptor::execute(sym, interceptor);
+ }
+ }
+ catch(const EvaluateException& ex)
+ {
+ errorReporter()->error("evaluation of conditional expression `" + _test + "' failed:\n" + ex.reason());
+ }
+}
+
+//
+// IterateDescriptor
+//
+Transform::IterateDescriptor::IterateDescriptor(const DescriptorPtr& parent, int line, const string& target,
+ const string& key, const string& value, const string& element,
+ const string& index) :
+ ExecutableContainerDescriptor(parent, line, "iterate"), Descriptor(parent, line), _key(key), _value(value),
+ _element(element), _index(index)
+{
+ DescriptorErrorContext ctx(errorReporter(), "iterate", _line);
+
+ NodePtr node = parse(target);
+ _target = IdentNodePtr::dynamicCast(node);
+ if(!_target)
+ {
+ errorReporter()->error("`target' attribute is not an identifier: `" + target + "'");
+ }
+}
+
+void
+Transform::IterateDescriptor::execute(TransformSymbolTable& sym, DataInterceptor& interceptor)
+{
+ DescriptorErrorContext ctx(errorReporter(), "iterate", _line);
+
+ DataPtr data = sym.getValue(_target->getValue());
+
+ DictionaryDataPtr dict = DictionaryDataPtr::dynamicCast(data);
+ SequenceDataPtr seq = SequenceDataPtr::dynamicCast(data);
+ if(!dict && !seq)
+ {
+ ostringstream ostr;
+ ostr << _target->getValue();
+ errorReporter()->error("target `" + ostr.str() + "' is not a dictionary or sequence");
+ }
+
+ if(dict)
+ {
+ if(!_element.empty())
+ {
+ errorReporter()->error("attribute `element' specified for dictionary target");
+ }
+ if(!_index.empty())
+ {
+ errorReporter()->error("attribute `index' specified for dictionary target");
+ }
+
+ string key = _key;
+ if(key.empty())
+ {
+ key = "key";
+ }
+
+ string value = _value;
+ if(value.empty())
+ {
+ value = "value";
+ }
+
+ DataMap map = dict->getElements();
+ for(DataMap::iterator p = map.begin(); p != map.end(); ++p)
+ {
+ TransformSymbolTable elemSym(factory(), oldUnit(), newUnit(), errorReporter(), &sym);
+ elemSym.add(key, p->first);
+ elemSym.add(value, p->second);
+ ExecutableContainerDescriptor::execute(elemSym, interceptor);
+ }
+ }
+ else
+ {
+ if(!_key.empty())
+ {
+ errorReporter()->error("attribute `key' specified for sequence target");
+ }
+ if(!_value.empty())
+ {
+ errorReporter()->error("attribute `value' specified for sequence target");
+ }
+
+ string element = _element;
+ if(element.empty())
+ {
+ element = "elem";
+ }
+
+ string index = _index;
+ if(index.empty())
+ {
+ index = "i";
+ }
+
+ DataList l = seq->getElements();
+ Ice::Long i = 0;
+ for(DataList::iterator p = l.begin(); p != l.end(); ++p, ++i)
+ {
+ TransformSymbolTable elemSym(factory(), oldUnit(), newUnit(), errorReporter(), &sym);
+ elemSym.add(element, *p);
+ elemSym.add(index, factory()->createInteger(i, true));
+ ExecutableContainerDescriptor::execute(elemSym, interceptor);
+ }
+ }
+}
+
+//
+// TransformDescriptor
+//
+Transform::TransformDescriptor::TransformDescriptor(const DescriptorPtr& parent, int line, const string& type,
+ bool def) :
+ ExecutableContainerDescriptor(parent, line, "transform"), Descriptor(parent, line), _default(def)
+{
+ DescriptorErrorContext ctx(errorReporter(), "transform", _line);
+
+ Slice::TypeList l;
+
+ l = oldUnit()->lookupType(type, false);
+ if(!l.empty())
+ {
+ _oldType = l.front();
+ }
+
+ l = newUnit()->lookupType(type, false);
+ if(l.empty())
+ {
+ errorReporter()->error("unable to find type `" + type + "' in new Slice definitions");
+ }
+ else
+ {
+ _newType = l.front();
+ }
+}
+
+string
+Transform::TransformDescriptor::type() const
+{
+ return typeName(_newType);
+}
+
+bool
+Transform::TransformDescriptor::doDefaultTransform() const
+{
+ return _default;
+}
+
+//
+// InitDescriptor
+//
+Transform::InitDescriptor::InitDescriptor(const DescriptorPtr& parent, int line, const string& type) :
+ ExecutableContainerDescriptor(parent, line, "init"), Descriptor(parent, line)
+{
+ DescriptorErrorContext ctx(errorReporter(), "init", _line);
+
+ Slice::TypeList l = newUnit()->lookupType(type, false);
+ if(l.empty())
+ {
+ errorReporter()->error("unable to find type `" + type + "' in new Slice definitions");
+ }
+ else
+ {
+ _type = l.front();
+ }
+}
+
+void
+Transform::InitDescriptor::initialize(const DataFactoryPtr& factory, const DataPtr& data,
+ const Ice::CommunicatorPtr& communicator)
+{
+ DescriptorErrorContext ctx(errorReporter(), "init", _line);
+
+#if 0 // TODO
+ // Is interceptor really necessary?
+ TransformSymbolTable sym(factory, _old, _new);
+ sym.add("value", data);
+ eh->raise(true);
+ execute(sym, interceptor);
+#endif
+}
+
+string
+Transform::InitDescriptor::type() const
+{
+ return typeName(_type);
+}
+
+//
+// RecordDescriptor
+//
+Transform::RecordDescriptor::RecordDescriptor(const DescriptorPtr& parent, int line) :
+ ExecutableContainerDescriptor(parent, line, "record"), Descriptor(parent, line)
+{
+}
+
+//
+// DatabaseDescriptor
+//
+Transform::DatabaseDescriptor::DatabaseDescriptor(const DescriptorPtr& parent, int line, const string& keyTypes,
+ const string& valueTypes) :
+ Descriptor(parent, line)
+{
+ DescriptorErrorContext ctx(errorReporter(), "database", _line);
+
+ string oldKeyName, newKeyName;
+ string oldValueName, newValueName;
+ string::size_type pos;
+
+ pos = keyTypes.find(',');
+ if(pos == 0 || pos == keyTypes.size())
+ {
+ errorReporter()->error("invalid key type specification `" + keyTypes +"'");
+ }
+ if(pos == string::npos)
+ {
+ oldKeyName = keyTypes;
+ newKeyName = keyTypes;
+ }
+ else
+ {
+ oldKeyName = keyTypes.substr(0, pos);
+ newKeyName = keyTypes.substr(pos + 1);
+ }
+
+ pos = valueTypes.find(',');
+ if(pos == 0 || pos == valueTypes.size())
+ {
+ errorReporter()->error("invalid value type specification `" + valueTypes +"'");
+ }
+ if(pos == string::npos)
+ {
+ oldValueName = valueTypes;
+ newValueName = valueTypes;
+ }
+ else
+ {
+ oldValueName = valueTypes.substr(0, pos);
+ newValueName = valueTypes.substr(pos + 1);
+ }
+
+ //
+ // Look up the Slice definitions for the key and value types.
+ //
+ _oldKey = findType(oldUnit(), oldKeyName);
+ _newKey = findType(newUnit(), newKeyName);
+ _oldValue = findType(oldUnit(), oldValueName);
+ _newValue = findType(newUnit(), newValueName);
+}
+
+void
+Transform::DatabaseDescriptor::addChild(const DescriptorPtr& child)
+{
+ DescriptorErrorContext ctx(errorReporter(), "database", _line);
+
+ RecordDescriptorPtr rec = RecordDescriptorPtr::dynamicCast(child);
+ if(!rec)
+ {
+ errorReporter()->error("invalid child for <database> descriptor");
+ }
+ if(_record)
+ {
+ errorReporter()->error("only one <record> element can be specified");
+ }
+ _record = rec;
+}
+
+void
+Transform::DatabaseDescriptor::validate()
+{
+ if(!_record)
+ {
+ _record = new RecordDescriptor(this, _line);
+ }
+ _record->validate();
+}
+
+void
+Transform::DatabaseDescriptor::execute(TransformSymbolTable&, DataInterceptor&)
+{
+ assert(false);
+}
+
+bool
+Transform::DatabaseDescriptor::transform(IceInternal::BasicStream& inKey, IceInternal::BasicStream& inValue,
+ IceInternal::BasicStream& outKey, IceInternal::BasicStream& outValue,
+ const TransformMap& transforms)
+{
+ errorReporter()->raise(false);
+
+ TransformInterceptor interceptor(factory(), errorReporter(), oldUnit(), newUnit(), transforms);
+
+ //
+ // Create data representations of the old key and value types.
+ //
+ factory()->disableInitializers();
+ DataPtr oldKeyData = factory()->create(_oldKey, true);
+ Destroyer<DataPtr> oldKeyDataDestroyer(oldKeyData);
+ DataPtr oldValueData = factory()->create(_oldValue, true);
+ Destroyer<DataPtr> oldValueDataDestroyer(oldValueData);
+
+ //
+ // Unmarshal the old key and value.
+ //
+ oldKeyData->unmarshal(inKey);
+ oldValueData->unmarshal(inValue);
+ if(_oldValue->usesClasses())
+ {
+ inValue.readPendingObjects();
+ oldValueData->registerObjects(interceptor.getObjectMap());
+ }
+ factory()->enableInitializers();
+
+ //
+ // Create data representations of the new key and value types.
+ //
+ DataPtr newKeyData = factory()->create(_newKey, false);
+ Destroyer<DataPtr> newKeyDataDestroyer(newKeyData);
+ DataPtr newValueData = factory()->create(_newValue, false);
+ Destroyer<DataPtr> newValueDataDestroyer(newValueData);
+
+ //
+ // Copy the data from the old key and value to the new key and value, if possible.
+ //
+ newKeyData->transform(oldKeyData, interceptor);
+ newValueData->transform(oldValueData, interceptor);
+
+ //
+ // Execute the <record> descriptor.
+ //
+ // TODO: Revisit identifiers.
+ //
+ TransformSymbolTable sym(factory(), oldUnit(), newUnit(), errorReporter());
+ sym.add("oldkey", oldKeyData);
+ sym.add("newkey", newKeyData);
+ sym.add("oldvalue", oldValueData);
+ sym.add("newvalue", newValueData);
+ errorReporter()->raise(true);
+ _record->execute(sym, interceptor);
+
+ newKeyData->marshal(outKey);
+ newValueData->marshal(outValue);
+ if(_newValue->usesClasses())
+ {
+ outValue.writePendingObjects();
+ }
+
+ return true;
+}
+
+//
+// TransformerDescriptor
+//
+Transform::TransformerDescriptor::TransformerDescriptor(const DataFactoryPtr& factory, const Slice::UnitPtr& oldUnit,
+ const Slice::UnitPtr& newUnit,
+ const ErrorReporterPtr& errorReporter, int line) :
+ Descriptor(0, line), _factory(factory), _old(oldUnit), _new(newUnit), _errorReporter(errorReporter)
+{
+}
+
+void
+Transform::TransformerDescriptor::addChild(const DescriptorPtr& child)
+{
+ DescriptorErrorContext ctx(errorReporter(), "transformer", _line);
+
+ DatabaseDescriptorPtr db = DatabaseDescriptorPtr::dynamicCast(child);
+ TransformDescriptorPtr transform = TransformDescriptorPtr::dynamicCast(child);
+ InitDescriptorPtr init = InitDescriptorPtr::dynamicCast(child);
+
+ if(db)
+ {
+ if(_database)
+ {
+ errorReporter()->error("only one <database> element can be specified");
+ }
+ else
+ {
+ _database = db;
+ _children.push_back(db);
+ }
+ }
+ else if(transform)
+ {
+ string name = transform->type();
+ TransformMap::iterator p = _transforms.find(name);
+ if(p != _transforms.end())
+ {
+ errorReporter()->error("transform `" + name + "' specified more than once");
+ }
+ _transforms.insert(TransformMap::value_type(name, transform));
+ _children.push_back(transform);
+ }
+ else if(init)
+ {
+ string name = init->type();
+ _factory->addInitializer(init->type(), init);
+ _children.push_back(init);
+ }
+ else
+ {
+ errorReporter()->error("invalid child element");
+ }
+}
+
+void
+Transform::TransformerDescriptor::validate()
+{
+ DescriptorErrorContext ctx(errorReporter(), "transformer", _line);
+
+ if(!_database)
+ {
+ errorReporter()->error("no <database> element specified");
+ }
+
+ for(vector<DescriptorPtr>::iterator p = _children.begin(); p != _children.end(); ++p)
+ {
+ (*p)->validate();
+ }
+}
+
+void
+Transform::TransformerDescriptor::execute(TransformSymbolTable&, DataInterceptor&)
+{
+ assert(false);
+}
+
+Transform::DataFactoryPtr
+Transform::TransformerDescriptor::factory() const
+{
+ return _factory;
+}
+
+Slice::UnitPtr
+Transform::TransformerDescriptor::oldUnit() const
+{
+ return _old;
+}
+
+Slice::UnitPtr
+Transform::TransformerDescriptor::newUnit() const
+{
+ return _new;
+}
+
+Transform::ErrorReporterPtr
+Transform::TransformerDescriptor::errorReporter() const
+{
+ return _errorReporter;
+}
+
+void
+Transform::TransformerDescriptor::transform(const Ice::CommunicatorPtr& communicator, Db* db, Db* dbNew)
+{
+ DescriptorErrorContext ctx(errorReporter(), "transformer", _line);
+
+ Dbc* dbc = 0;
+
+ IceInternal::InstancePtr instance = IceInternal::getInstance(communicator);
+ db->cursor(0, &dbc, 0);
+
+ communicator->addObjectFactory(new Transform::ObjectFactory(_factory, _old), "");
+
+ try
+ {
+ Dbt dbKey, dbValue;
+ while(dbc->get(&dbKey, &dbValue, DB_NEXT) == 0)
+ {
+ IceInternal::BasicStream inKey(instance.get());
+ inKey.b.resize(dbKey.get_size());
+ memcpy(&inKey.b[0], dbKey.get_data(), dbKey.get_size());
+ inKey.i = inKey.b.begin();
+
+ IceInternal::BasicStream inValue(instance.get());
+ inValue.b.resize(dbValue.get_size());
+ memcpy(&inValue.b[0], dbValue.get_data(), dbValue.get_size());
+ inValue.i = inValue.b.begin();
+ inValue.startReadEncaps();
+
+ IceInternal::BasicStream outKey(instance.get());
+ IceInternal::BasicStream outValue(instance.get());
+ outValue.startWriteEncaps();
+ try
+ {
+ if(_database->transform(inKey, inValue, outKey, outValue, _transforms))
+ {
+ outValue.endWriteEncaps();
+ Dbt dbNewKey(&outKey.b[0], outKey.b.size()), dbNewValue(&outValue.b[0], outValue.b.size());
+ if(dbNew->put(0, &dbNewKey, &dbNewValue, DB_NOOVERWRITE) == DB_KEYEXIST)
+ {
+ errorReporter()->error("duplicate key encountered");
+ }
+ }
+ }
+ catch(const DeleteRecordException&)
+ {
+ // The record is deleted simply by not adding it to the new database.
+ }
+ }
+ }
+ catch(...)
+ {
+ if(dbc)
+ {
+ dbc->close();
+ }
+ communicator->removeObjectFactory("");
+ throw;
+ }
+
+ communicator->removeObjectFactory("");
+
+ if(dbc)
+ {
+ dbc->close();
+ }
+}
+
+//
+// DescriptorHandler
+//
+Transform::DescriptorHandler::DescriptorHandler(const DataFactoryPtr& factory, const Slice::UnitPtr& oldUnit,
+ const Slice::UnitPtr& newUnit, const ErrorReporterPtr& errorReporter) :
+ _factory(factory), _old(oldUnit), _new(newUnit), _errorReporter(errorReporter)
+{
+}
+
+void
+Transform::DescriptorHandler::startElement(const string& name, const IceXML::Attributes& attributes, int line,
+ int column)
+{
+ DescriptorPtr d;
+
+ if(name == "transformer")
+ {
+ if(_current)
+ {
+ _errorReporter->descriptorError("<transformer> must be the top-level element", line);
+ }
+
+ _transformer = new TransformerDescriptor(_factory, _old, _new, _errorReporter, line);
+ d = _transformer;
+ }
+ else if(name == "database")
+ {
+ if(!_current)
+ {
+ _errorReporter->descriptorError("<database> must be a child of <transformer>", line);
+ }
+
+ IceXML::Attributes::const_iterator p = attributes.find("key");
+ if(p == attributes.end())
+ {
+ _errorReporter->descriptorError("required attribute `key' is missing from <database>", line);
+ }
+ string keyTypes = p->second;
+
+ p = attributes.find("value");
+ if(p == attributes.end())
+ {
+ _errorReporter->descriptorError("required attribute `value' is missing from <database>", line);
+ }
+ string valueTypes = p->second;
+
+ d = new DatabaseDescriptor(_current, line, keyTypes, valueTypes);
+ }
+ else if(name == "record")
+ {
+ if(!_current)
+ {
+ _errorReporter->descriptorError("<record> must be a child of <database>", line);
+ }
+
+ d = new RecordDescriptor(_current, line);
+ }
+ else if(name == "transform")
+ {
+ if(!_current)
+ {
+ _errorReporter->descriptorError("<transform> must be a child of <transformer>", line);
+ }
+
+ IceXML::Attributes::const_iterator p;
+
+ string type;
+ p = attributes.find("type");
+ if(p == attributes.end())
+ {
+ _errorReporter->descriptorError("required attribute `type' is missing from <transform>", line);
+ }
+ type = p->second;
+
+ bool def = true;
+ p = attributes.find("default");
+ if(p != attributes.end())
+ {
+ if(p->second == "false")
+ {
+ def = false;
+ }
+ }
+
+ d = new TransformDescriptor(_current, line, type, def);
+ }
+ else if(name == "init")
+ {
+ if(!_current)
+ {
+ _errorReporter->descriptorError("<init> must be a child of <transformer>", line);
+ }
+
+ IceXML::Attributes::const_iterator p = attributes.find("type");
+ if(p == attributes.end())
+ {
+ _errorReporter->descriptorError("required attribute `type' is missing from <init>", line);
+ }
+
+ d = new InitDescriptor(_current, line, p->second);
+ }
+ else if(name == "set")
+ {
+ if(!_current)
+ {
+ _errorReporter->descriptorError("<set> cannot be a top-level element", line);
+ }
+
+ IceXML::Attributes::const_iterator p;
+
+ string target, key, value, type;
+ bool convert = false;
+ p = attributes.find("target");
+ if(p == attributes.end())
+ {
+ _errorReporter->descriptorError("required attribute `target' is missing from <set>", line);
+ }
+ target = p->second;
+
+ p = attributes.find("key");
+ if(p != attributes.end())
+ {
+ key = p->second;
+ }
+
+ p = attributes.find("value");
+ if(p != attributes.end())
+ {
+ value = p->second;
+ }
+
+ p = attributes.find("type");
+ if(p != attributes.end())
+ {
+ type = p->second;
+ }
+
+ p = attributes.find("convert");
+ if(p != attributes.end())
+ {
+ convert = p->second == "true";
+ }
+
+ if((value.empty() && type.empty()) || (!value.empty() && !type.empty()))
+ {
+ _errorReporter->descriptorError("<set> requires one of the attributes `value' or 'type'", line);
+ }
+
+ d = new SetDescriptor(_current, line, target, key, value, type, convert);
+ }
+ else if(name == "remove")
+ {
+ if(!_current)
+ {
+ _errorReporter->descriptorError("<remove> cannot be a top-level element", line);
+ }
+
+ IceXML::Attributes::const_iterator p;
+
+ string target, key;
+ p = attributes.find("target");
+ if(p == attributes.end())
+ {
+ _errorReporter->descriptorError("required attribute `target' is missing from <remove>", line);
+ }
+ target = p->second;
+
+ p = attributes.find("key");
+ if(p == attributes.end())
+ {
+ _errorReporter->descriptorError("required attribute `key' is missing from <remove>", line);
+ }
+ key = p->second;
+
+ d = new RemoveDescriptor(_current, line, target, key);
+ }
+ else if(name == "delete")
+ {
+ if(!_current)
+ {
+ _errorReporter->descriptorError("<delete> cannot be a top-level element", line);
+ }
+
+ d = new DeleteDescriptor(_current, line);
+ }
+ else if(name == "fail")
+ {
+ if(!_current)
+ {
+ _errorReporter->descriptorError("<fail> cannot be a top-level element", line);
+ }
+
+ IceXML::Attributes::const_iterator p = attributes.find("message");
+ string message;
+ if(p != attributes.end())
+ {
+ message = p->second;
+ }
+
+ d = new FailDescriptor(_current, line, message);
+ }
+ else if(name == "echo")
+ {
+ if(!_current)
+ {
+ _errorReporter->descriptorError("<echo> cannot be a top-level element", line);
+ }
+
+ IceXML::Attributes::const_iterator p;
+ string message, value;
+
+ p = attributes.find("message");
+ if(p != attributes.end())
+ {
+ message = p->second;
+ }
+
+ p = attributes.find("value");
+ if(p != attributes.end())
+ {
+ value = p->second;
+ }
+
+ d = new EchoDescriptor(_current, line, message, value);
+ }
+ else if(name == "if")
+ {
+ if(!_current)
+ {
+ _errorReporter->descriptorError("<if> cannot be a top-level element", line);
+ }
+
+ IceXML::Attributes::const_iterator p = attributes.find("test");
+ if(p == attributes.end())
+ {
+ _errorReporter->descriptorError("required attribute `test' is missing from <if>", line);
+ }
+
+ d = new ConditionalDescriptor(_current, line, p->second);
+ }
+ else if(name == "iterate")
+ {
+ if(!_current)
+ {
+ _errorReporter->descriptorError("<iterate> cannot be a top-level element", line);
+ }
+
+ IceXML::Attributes::const_iterator p;
+ string target, key, value, element, index;
+
+ p = attributes.find("target");
+ if(p == attributes.end())
+ {
+ _errorReporter->descriptorError("required attribute `target' is missing from <iterate>", line);
+ }
+ target = p->second;
+
+ p = attributes.find("key");
+ if(p != attributes.end())
+ {
+ key = p->second;
+ }
+
+ p = attributes.find("value");
+ if(p != attributes.end())
+ {
+ value = p->second;
+ }
+
+ p = attributes.find("element");
+ if(p != attributes.end())
+ {
+ element = p->second;
+ }
+
+ p = attributes.find("index");
+ if(p != attributes.end())
+ {
+ index = p->second;
+ }
+
+ d = new IterateDescriptor(_current, line, target, key, value, element, index);
+ }
+ else
+ {
+ _errorReporter->descriptorError("unknown descriptor `" + name + "'", line);
+ }
+
+ if(_current)
+ {
+ _current->addChild(d);
+ }
+ _current = d;
+}
+
+void
+Transform::DescriptorHandler::endElement(const std::string& name, int, int)
+{
+ assert(_current);
+ _current = _current->parent();
+}
+
+void
+Transform::DescriptorHandler::characters(const std::string&, int, int)
+{
+}
+
+void
+Transform::DescriptorHandler::error(const std::string& msg, int line, int col)
+{
+ _errorReporter->descriptorError(msg, line);
+}
+
+Transform::TransformerDescriptorPtr
+Transform::DescriptorHandler::descriptor() const
+{
+ return _transformer;
+}
+
+//
+// TransformInterceptor
+//
+Transform::TransformInterceptor::TransformInterceptor(const DataFactoryPtr& factory,
+ const ErrorReporterPtr& errorReporter,
+ const Slice::UnitPtr& oldUnit,
+ const Slice::UnitPtr& newUnit,
+ const TransformMap& transformMap) :
+ _factory(factory), _errorReporter(errorReporter), _old(oldUnit), _new(newUnit), _transformMap(transformMap)
+{
+}
+
+bool
+Transform::TransformInterceptor::preTransform(const DataPtr& dest, const DataPtr&)
+{
+ //
+ // Allow a transform descriptor to disable the default transformation.
+ //
+ string type = typeName(dest->getType());
+ TransformMap::const_iterator p = _transformMap.find(type);
+ if(p != _transformMap.end())
+ {
+ return p->second->doDefaultTransform();
+ }
+
+ return true;
+}
+
+void
+Transform::TransformInterceptor::postTransform(const DataPtr& dest, const DataPtr& src)
+{
+ //
+ // Execute the type's transform (if any).
+ //
+ string type = typeName(dest->getType());
+ TransformMap::const_iterator p = _transformMap.find(type);
+ if(p != _transformMap.end())
+ {
+ bool raise = _errorReporter->raise();
+ _errorReporter->raise(true);
+ TransformSymbolTable sym(_factory, _old, _new, _errorReporter);
+ sym.add("new", dest);
+ sym.add("old", src);
+ try
+ {
+ p->second->execute(sym, *this);
+ _errorReporter->raise(raise);
+ }
+ catch(...)
+ {
+ _errorReporter->raise(raise);
+ throw;
+ }
+ }
+}
+
+Transform::ObjectDataMap&
+Transform::TransformInterceptor::getObjectMap()
+{
+ return _objectMap;
+}
+
+//
+// Transformer
+//
+Transform::Transformer::Transformer(const Ice::CommunicatorPtr& communicator, const Slice::UnitPtr& oldUnit,
+ const Slice::UnitPtr& newUnit, bool ignoreTypeChanges) :
+ _communicator(communicator), _old(oldUnit), _new(newUnit), _ignoreTypeChanges(ignoreTypeChanges)
+{
+}
+
+void
+Transform::Transformer::analyze(const string& oldKey, const string& newKey, const string& oldValue,
+ const string& newValue, ostream& descriptors, Ice::StringSeq& missingTypes,
+ Ice::StringSeq& errors)
+{
+ //
+ // Look up the Slice definitions for the key and value types.
+ //
+ Slice::TypePtr oldKeyType = findType(_old, oldKey, errors);
+ Slice::TypePtr newKeyType = findType(_new, newKey, errors);
+ Slice::TypePtr oldValueType = findType(_old, oldValue, errors);
+ Slice::TypePtr newValueType = findType(_new, newValue, errors);
+ if(!oldKeyType || !newKeyType || !oldValueType || !newValueType)
+ {
+ return;
+ }
+
+ Analyzer analyzer(_old, _new, _ignoreTypeChanges);
+ analyzer.analyze(oldKeyType, newKeyType, oldValueType, newValueType, descriptors, missingTypes, errors);
+}
+
+void
+Transform::Transformer::analyze(ostream& descriptors, Ice::StringSeq& missingTypes, Ice::StringSeq& errors)
+{
+ createEvictorSliceTypes(_old);
+ createEvictorSliceTypes(_new);
+
+ const string keyType = "::Freeze::EvictorStorageKey";
+ const string valueType = "::Freeze::ObjectRecord";
+ analyze(keyType, keyType, valueType, valueType, descriptors, missingTypes, errors);
+}
+
+void
+Transform::Transformer::transform(istream& is, Db* db, Db* dbNew, ostream& errors)
+{
+ createCoreSliceTypes(_old);
+ createCoreSliceTypes(_new);
+
+ ErrorReporterPtr errorReporter = new ErrorReporter(errors);
+
+ try
+ {
+ DataFactoryPtr factory = new DataFactory(_communicator, _new, errorReporter);
+ DescriptorHandler dh(factory, _old, _new, errorReporter);
+ IceXML::Parser::parse(is, dh);
+
+ TransformerDescriptorPtr descriptor = dh.descriptor();
+ descriptor->validate();
+ descriptor->transform(_communicator, db, dbNew);
+ }
+ catch(const IceXML::ParserException& ex)
+ {
+ errorReporter->error(ex.reason());
+ }
+}
+
+void
+Transform::Transformer::createCoreSliceTypes(const Slice::UnitPtr& unit)
+{
+ string scoped;
+ Slice::TypeList l;
+ Slice::ContainedList c;
+
+ //
+ // Create the Slice definition for _FacetMap if it doesn't exist. This type is
+ // necessary for marshaling an object's facet map.
+ //
+ l = unit->lookupTypeNoBuiltin("::_FacetMap", false);
+ if(l.empty())
+ {
+ Slice::TypePtr str = unit->builtin(Slice::Builtin::KindString);
+ Slice::TypePtr obj = unit->builtin(Slice::Builtin::KindObject);
+ unit->createDictionary("_FacetMap", str, obj, false);
+ }
+ else
+ {
+ assert(Slice::DictionaryPtr::dynamicCast(l.front()));
+ }
+}
+
+void
+Transform::Transformer::createEvictorSliceTypes(const Slice::UnitPtr& unit)
+{
+ string scoped;
+ Slice::TypeList l;
+ Slice::ContainedList c;
+
+ //
+ // Create the Ice module if necessary.
+ //
+ c = unit->lookupContained("Ice", false);
+ Slice::ModulePtr ice;
+ if(c.empty())
+ {
+ ice = unit->createModule("Ice");
+ }
+ else
+ {
+ ice = Slice::ModulePtr::dynamicCast(c.front());
+ if(!ice)
+ {
+ throw TransformException(__FILE__, __LINE__,
+ "the symbol `::Ice' is defined in Slice but is not a module");
+ }
+ }
+
+ //
+ // Create the Slice definition for Ice::Identity if it doesn't exist.
+ //
+ scoped = "::Ice::Identity";
+ l = unit->lookupTypeNoBuiltin(scoped, false);
+ Slice::StructPtr identity;
+ if(l.empty())
+ {
+ identity = ice->createStruct("Identity", false);
+ Slice::TypePtr str = unit->builtin(Slice::Builtin::KindString);
+ identity->createDataMember("category", str);
+ identity->createDataMember("name", str);
+ }
+ else
+ {
+ identity = Slice::StructPtr::dynamicCast(l.front());
+ if(!identity)
+ {
+ throw TransformException(__FILE__, __LINE__,
+ "the symbol `::Ice::Identity' is defined in Slice but is not a struct");
+ }
+ }
+
+ //
+ // Create the Slice definition for Ice::FacetPath if it doesn't exist.
+ //
+ scoped = "::Ice::FacetPath";
+ l = unit->lookupTypeNoBuiltin(scoped, false);
+ Slice::SequencePtr facetPath;
+ if(l.empty())
+ {
+ Slice::TypePtr str = unit->builtin(Slice::Builtin::KindString);
+ facetPath = ice->createSequence("FacetPath", str, false);
+ }
+ else
+ {
+ facetPath = Slice::SequencePtr::dynamicCast(l.front());
+ if(!facetPath)
+ {
+ throw TransformException(__FILE__, __LINE__,
+ "the symbol `::Ice::FacetPath' is defined in Slice but is not a sequence");
+ }
+ }
+
+ //
+ // Create the Freeze module if necessary.
+ //
+ c = unit->lookupContained("Freeze", false);
+ Slice::ModulePtr freeze;
+ if(c.empty())
+ {
+ freeze = unit->createModule("Freeze");
+ }
+ else
+ {
+ freeze = Slice::ModulePtr::dynamicCast(c.front());
+ if(!freeze)
+ {
+ throw TransformException(__FILE__, __LINE__,
+ "the symbol `::Freeze' is defined in Slice but is not a module");
+ }
+ }
+
+ //
+ // Create the Slice definition for Freeze::EvictorStorageKey if it doesn't exist.
+ //
+ scoped = "::Freeze::EvictorStorageKey";
+ l = unit->lookupTypeNoBuiltin(scoped, false);
+ if(l.empty())
+ {
+ Slice::StructPtr esk = freeze->createStruct("EvictorStorageKey", false);
+ esk->createDataMember("identity", identity);
+ esk->createDataMember("facet", facetPath);
+ }
+ else
+ {
+ if(!Slice::StructPtr::dynamicCast(l.front()))
+ {
+ throw TransformException(__FILE__, __LINE__, "the symbol `::Freeze::EvictorStorageKey' is defined in "
+ "Slice but is not a struct");
+ }
+ }
+
+ //
+ // Create the Slice definition for Freeze::Statistics if it doesn't exist.
+ //
+ scoped = "::Freeze::Statistics";
+ l = unit->lookupTypeNoBuiltin(scoped, false);
+ Slice::StructPtr stats;
+ if(l.empty())
+ {
+ stats = freeze->createStruct("Statistics", false);
+ Slice::TypePtr tl = unit->builtin(Slice::Builtin::KindLong);
+ stats->createDataMember("creationTime", tl);
+ stats->createDataMember("lastSaveTime", tl);
+ stats->createDataMember("avgSaveTime", tl);
+ }
+ else
+ {
+ stats = Slice::StructPtr::dynamicCast(l.front());
+ if(!stats)
+ {
+ throw TransformException(__FILE__, __LINE__, "the symbol `::Freeze::Statistics' is defined in "
+ "Slice but is not a struct");
+ }
+ }
+
+ //
+ // Create the Slice definition for Freeze::ObjectRecord if it doesn't exist.
+ //
+ scoped = "::Freeze::ObjectRecord";
+ l = unit->lookupTypeNoBuiltin(scoped, false);
+ if(l.empty())
+ {
+ Slice::StructPtr rec = freeze->createStruct("ObjectRecord", false);
+ Slice::TypePtr obj = unit->builtin(Slice::Builtin::KindObject);
+ rec->createDataMember("servant", obj);
+ rec->createDataMember("stats", stats);
+ }
+ else
+ {
+ if(!Slice::StructPtr::dynamicCast(l.front()))
+ {
+ throw TransformException(__FILE__, __LINE__, "the symbol `::Freeze::ObjectRecord' is defined in "
+ "Slice but is not a struct");
+ }
+ }
+}
+
+Slice::TypePtr
+Transform::Transformer::findType(const Slice::UnitPtr& u, const string& type, Ice::StringSeq& errors)
+{
+ Slice::TypeList l;
+
+ l = u->lookupType(type, false);
+ if(l.empty())
+ {
+ errors.push_back("error: unknown type `" + type + "'");
+ return 0;
+ }
+
+ return l.front();
+}