detect circular references and throw exception

This commit is contained in:
jez 2008-08-02 21:41:44 +00:00
parent 216cb5602a
commit d357f70405

View file

@ -35,14 +35,14 @@ private:
class VariableStack : public Arabica::XPath::VariableResolver<std::string> class VariableStack : public Arabica::XPath::VariableResolver<std::string>
{ {
public: public:
VariableStack() VariableStack()
{ {
stack_.push_back(Scope()); stack_.push_back(Scope());
params_.push_back(Scope()); params_.push_back(Scope());
params_.push_back(Scope()); params_.push_back(Scope());
} // VariableStack } // VariableStack
VariableStack(const VariableStack& rhs) : VariableStack(const VariableStack& rhs) :
stack_(rhs.stack_), stack_(rhs.stack_),
params_(rhs.params_) params_(rhs.params_)
@ -50,10 +50,10 @@ public:
} // VariableStack } // VariableStack
void pushScope() void pushScope()
{ {
stack_.push_back(Scope()); stack_.push_back(Scope());
params_.push_back(Scope()); params_.push_back(Scope());
} // pushScope } // pushScope
void chainScope() void chainScope()
{ {
@ -61,12 +61,12 @@ public:
params_.push_back(Scope(params_.back())); params_.push_back(Scope(params_.back()));
} // chainsScope } // chainsScope
void popScope() void popScope()
{ {
params_.pop_back(); params_.pop_back();
stack_.pop_back(); stack_.pop_back();
} // popScope } // popScope
void topLevelParam(Variable_instance_ptr param) void topLevelParam(Variable_instance_ptr param)
{ {
params_.front()[clarkName(param)] = param; params_.front()[clarkName(param)] = param;
@ -91,69 +91,76 @@ public:
void declareParam(Variable_instance_ptr param) void declareParam(Variable_instance_ptr param)
{ {
ScopeStack::reverse_iterator p = params_.rbegin()+1; ScopeStack::reverse_iterator p = params_.rbegin()+1;
Scope::iterator i = p->find(clarkName(param)); Scope::iterator i = p->find(clarkName(param));
if(i == p->end()) if(i == p->end())
declareVariable(param); declareVariable(param);
else else
declareVariable(i->second); declareVariable(i->second);
} // declareParam } // declareParam
void declareVariable(Variable_instance_ptr var) void declareVariable(Variable_instance_ptr var)
{ {
std::string name = clarkName(var); std::string name = clarkName(var);
Scope& stack = stack_.back(); Scope& stack = stack_.back();
if(stack.find(name) != stack.end()) if(stack.find(name) != stack.end())
throw std::runtime_error("Duplicate variable name : " + name); throw std::runtime_error("Duplicate variable name : " + name);
stack[clarkName(var)] = var; stack[clarkName(var)] = var;
} // declareVariable } // declareVariable
void freezeTopLevel() void freezeTopLevel()
{ {
const Scope& top = stack_.front(); const Scope& top = stack_.front();
for(Scope::const_iterator v = top.begin(), ve = top.end(); v != ve; ++v) for(Scope::const_iterator v = top.begin(), ve = top.end(); v != ve; ++v)
v->second->injectGlobalScope(top); v->second->injectGlobalScope(top);
} // freezeTopLevel } // freezeTopLevel
void injectGlobalScope(const Scope& scope) void injectGlobalScope(const Scope& scope)
{ {
stack_.front() = scope; stack_.front() = scope;
} // injectGlobalScope } // injectGlobalScope
virtual Arabica::XPath::XPathValue<std::string> resolveVariable(const std::string& namespace_uri, virtual Arabica::XPath::XPathValue<std::string> resolveVariable(const std::string& namespace_uri,
const std::string& name) const const std::string& name) const
{ {
std::string clarkName = "{" + namespace_uri + "}" + name; std::string clarkName = "{" + namespace_uri + "}" + name;
Arabica::XPath::XPathValue<std::string> val = lookup(stack_.back(), clarkName); if(std::find(resolutionStack_.begin(), resolutionStack_.end(), clarkName) != resolutionStack_.end())
if(val != 0) throw std::runtime_error("Circular dependency: " + clarkName + " refers to itself directly or indirectly.");
return val;
val = lookup(stack_.front(), clarkName); // try our "global" scope resolutionStack_.push_back(clarkName);
if(val == 0) Arabica::XPath::XPathValue<std::string> val = lookup(stack_.back(), clarkName);
throw Arabica::XPath::UnboundVariableException(clarkName); resolutionStack_.pop_back();
return val; if(val != 0)
return val;
val = lookup(stack_.front(), clarkName); // try our "global" scope
if(val == 0)
throw Arabica::XPath::UnboundVariableException(clarkName);
return val;
} // resolveVariable } // resolveVariable
private: private:
typedef std::vector<Scope> ScopeStack; typedef std::vector<Scope> ScopeStack;
std::string clarkName(Variable_instance_ptr var) std::string clarkName(Variable_instance_ptr var)
{ {
return "{" + var->namespace_uri() + "}" + var->name(); return "{" + var->namespace_uri() + "}" + var->name();
} // clarkName } // clarkName
Arabica::XPath::XPathValue<std::string> lookup(const Scope& scope, const std::string& name) const Arabica::XPath::XPathValue<std::string> lookup(const Scope& scope, const std::string& name) const
{ {
Scope::const_iterator i = scope.find(name); Scope::const_iterator i = scope.find(name);
if(i == scope.end()) if(i == scope.end())
return Arabica::XPath::XPathValue<std::string>(0); return Arabica::XPath::XPathValue<std::string>(0);
return i->second->value(); return i->second->value();
} // lookup } // lookup
ScopeStack stack_; ScopeStack stack_;
ScopeStack params_; ScopeStack params_;
mutable std::vector<std::string> resolutionStack_;
}; // class VariableStack }; // class VariableStack
} // namespace XSLT } // namespace XSLT