Update of /cvsroot/objecthandler/ObjectHandler/ohxl
In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv2303/ohxl
Modified Files:
conversions.cpp functioncall.cpp functioncall.hpp
functions.cpp objecthandlerxl.cpp objecthandlerxl.hpp
Added Files:
callingrange.cpp callingrange.hpp
Removed Files:
instancenamexl.cpp instancenamexl.hpp
Log Message:
revise processing for instance names
Index: objecthandlerxl.cpp
===================================================================
RCS file: /cvsroot/objecthandler/ObjectHandler/ohxl/objecthandlerxl.cpp,v
retrieving revision 1.2
retrieving revision 1.3
diff -C2 -d -r1.2 -r1.3
*** objecthandlerxl.cpp 25 May 2006 17:01:57 -0000 1.2
--- objecthandlerxl.cpp 1 Jun 2006 10:52:08 -0000 1.3
***************
*** 18,23 ****
#include <oh/exception.hpp>
#include <ohxl/objecthandlerxl.hpp>
! #include <ohxl/instancenamexl.hpp>
#include <ohxl/conversions.hpp>
/* Use BOOST_MSVC instead of _MSC_VER since some other vendors (Metrowerks,
for example) also #define _MSC_VER
--- 18,24 ----
#include <oh/exception.hpp>
#include <ohxl/objecthandlerxl.hpp>
! #include <ohxl/callingrange.hpp>
#include <ohxl/conversions.hpp>
+ #include <ohxl/functioncall.hpp>
/* Use BOOST_MSVC instead of _MSC_VER since some other vendors (Metrowerks,
for example) also #define _MSC_VER
***************
*** 32,114 ****
#include <sstream>
#include <cmath>
- #include <boost/regex.hpp>
namespace ObjHandler {
! std::string ObjectHandlerXL::storeObject(const std::string &instanceName,
! const obj_ptr &object) {
! object->setInstanceName(boost::shared_ptr < Object::InstanceName > (new InstanceNameXL(instanceName)));
! std::string fullName = object->getFullName();
! objectList_[fullName] = object;
! return fullName;
! }
! void ObjectHandlerXL::collectGarbage() {
! ObjectList::iterator iter_current, iter_previous;
! iter_current = objectList_.begin();
! while (iter_current != objectList_.end()) {
! iter_previous = iter_current;
! iter_current++;
! std::string nameFull = iter_previous->first;
! obj_ptr object = iter_previous->second;
! if (!object->isValid())
! objectList_.erase(nameFull);
! }
! }
! void ObjectHandlerXL::deleteKey(const std::string &key) {
! for (ObjectList::iterator iter = objectList_.begin();
! iter != objectList_.end(); iter++) {
! std::string nameFull = iter->first;
! obj_ptr object = iter->second;
! if (object->getKey() == key) {
! objectList_.erase(nameFull);
! break;
! }
! }
! }
! bool ObjectHandlerXL::nameIsFull(const std::string &name) const {
! static const boost::regex NAME_REGEX(".*~_[\\da-f]{5}");
! return regex_match(name, NAME_REGEX);
! }
! obj_ptr ObjectHandlerXL::retrieveObjectNameStub(const std::string &nameStub) const {
! bool objectFound = false;
! std::string nameFullFound;
! obj_ptr retrievedObject;
! ObjectList::const_iterator i;
! for (i=objectList_.begin(); i!=objectList_.end(); i++) {
! std::string nameFull = i->first;
! obj_ptr object = i->second;
! if (nameStub == object->getStubName()) {
! if (objectFound) {
std::ostringstream msg;
! msg << "error retrieving object with instance name '" << nameStub
! << "': two (& maybe more) objects exist with that instance name -"
! << " #1: '" << nameFullFound
! << "' #2: '" << nameFull << "'";
throw Exception(msg.str());
} else {
! retrievedObject = object;
! objectFound = true;
! nameFullFound = nameFull;
}
}
}
if (i != objectList_.end()) {
std::ostringstream msg;
! msg << "ObjectHandler error: attempt to retrieve object "
! "with unknown instance name '" << nameStub << "'";
throw Exception(msg.str());
! } else
! return retrievedObject;
}
! obj_ptr ObjectHandlerXL::retrieveObject(const std::string &name) const {
! if (nameIsFull(name))
! return ObjectHandlerBase::retrieveObject(name);
else
! return retrieveObjectNameStub(name);
}
--- 33,164 ----
#include <sstream>
#include <cmath>
namespace ObjHandler {
! int ObjectHandlerXL::instanceNameCount_ = 0;
! boost::shared_ptr < CallingRange > ObjectHandlerXL::getCallingRange() {
!
! // XLOPERs which might need freeing in the event of an exception
! XLOPER xOldName;
! XLOPER xValue;
! try {
! boost::shared_ptr < CallingRange > callingRange;
! // get name if any
!
! Excel(xlfGetDef, &xOldName, 1, FunctionCall::instance().getCallerAddress());
!
! // if name - return associated CallingRange object
!
! if (xOldName.xltype == xltypeStr) {
! std::string oldKey;
! operToScalar(oldKey, xOldName);
! Excel(xlFree, 0, 1, &xOldName);
! std::map < std::string, boost::shared_ptr < CallingRange > >::const_iterator i;
! i = callingRanges_.find(oldKey);
! if (i == callingRanges_.end()) {
std::ostringstream msg;
! msg << "no calling range named " << oldKey;
throw Exception(msg.str());
} else {
! callingRange = i->second;
! callingRange->update();
}
}
+
+ return callingRange;
+
+ } catch (const std::exception &e) {
+
+ // free any memory that may have been allocated
+
+ Excel(xlFree, 0, 2, &xOldName, &xValue);
+
+ // propagate the exception
+
+ std::ostringstream err;
+ err << "ObjectHandlerXL::getCallingRange(): " << e.what();
+ throw Exception(err.str());
}
+
+ }
+
+ void ObjectHandlerXL::resetCaller() {
+ getCallingRange();
+ }
+
+ std::string ObjectHandlerXL::storeObject(
+ const std::string &instanceName,
+ const obj_ptr &object) {
+ boost::shared_ptr < CallingRange > callingRange = getCallingRange();
+
+ if (!callingRange) {
+ callingRange = boost::shared_ptr < CallingRange >(new CallingRange);
+ callingRanges_[callingRange->getKey()] = callingRange;
+ }
+
+ std::string instanceNameDerived;
+ if (instanceName.empty())
+ instanceNameDerived = generateInstanceName();
+ else
+ instanceNameDerived = instanceName;
+
+ ObjectList::const_iterator i = objectList_.find(instanceNameDerived);
if (i != objectList_.end()) {
std::ostringstream msg;
! msg << "unable to instantiate object with instance name '" << instanceNameDerived
! << "' because another object with that instance name already exists.";
throw Exception(msg.str());
! }
!
! callingRange->registerObject(instanceNameDerived);
!
! std::string instanceNameCounter = ObjectHandlerBase::storeObject(instanceNameDerived, object);
! return instanceNameCounter + "#" + callingRange->updateCount();
}
! obj_ptr ObjectHandlerXL::retrieveObject(const std::string &instanceName) const {
! std::string instanceNameStub;
! int counterOffset = instanceName.length() - 5;
! if (counterOffset >= 0 && instanceName[counterOffset] == '#')
! instanceNameStub = instanceName.substr(0, counterOffset);
else
! instanceNameStub = instanceName;
! return ObjectHandlerBase::retrieveObject(instanceNameStub);
! }
!
! std::string ObjectHandlerXL::generateInstanceName() {
! static const int COUNT_WIDTH = 5;
! static const int COUNT_BASE = 16;
! static const double COUNT_MAX = pow((double)COUNT_BASE, COUNT_WIDTH);
!
! if (instanceNameCount_ > COUNT_MAX)
! throw Exception("ObjectHandlerXL::generateInstanceName: max count value exceeded");
! std::ostringstream s;
! s << "obj_" << std::setw(COUNT_WIDTH) << std::setfill('0') << std::setbase(COUNT_BASE) << instanceNameCount_++;
! return s.str();
! }
!
! void ObjectHandlerXL::collectGarbage() {
! std::map < std::string, boost::shared_ptr < CallingRange > >::const_iterator iter_current, iter_previous;
! iter_current = callingRanges_.begin();
! while (iter_current != callingRanges_.end()) {
! iter_previous = iter_current;
! iter_current++;
! std::string key = iter_previous->first;
! boost::shared_ptr < CallingRange > callingRange = iter_previous->second;
! if (!callingRange->isValid()) {
! callingRange->clear();
! callingRanges_.erase(key);
! }
! }
! }
!
! void ObjectHandlerXL::deleteAllObjects() {
! callingRanges_.clear();
! ObjectHandlerBase::deleteAllObjects();
}
Index: objecthandlerxl.hpp
===================================================================
RCS file: /cvsroot/objecthandler/ObjectHandler/ohxl/objecthandlerxl.hpp,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** objecthandlerxl.hpp 19 May 2006 15:12:41 -0000 1.1
--- objecthandlerxl.hpp 1 Jun 2006 10:52:08 -0000 1.2
***************
*** 20,25 ****
*/
! #ifndef oh_objecthandlerxl_hpp
! #define oh_objecthandlerxl_hpp
#include <oh/objecthandlerbase.hpp>
--- 20,25 ----
*/
! #ifndef ohxl_objecthandlerxl_hpp
! #define ohxl_objecthandlerxl_hpp
#include <oh/objecthandlerbase.hpp>
***************
*** 29,32 ****
--- 29,34 ----
*/
namespace ObjHandler {
+ class CallingRange;
+
//! Global Object repository.
/*! Maintains a repository of Objects.
***************
*** 36,47 ****
class ObjectHandlerXL : public ObjectHandlerBase {
public:
virtual std::string storeObject(const std::string &instanceName,
const obj_ptr &object);
! virtual obj_ptr retrieveObject(const std::string &name) const;
virtual void collectGarbage();
! void deleteKey(const std::string &key);
private:
! bool nameIsFull(const std::string &name) const;
! obj_ptr retrieveObjectNameStub(const std::string &nameStub) const;
};
--- 38,71 ----
class ObjectHandlerXL : public ObjectHandlerBase {
public:
+ //! Store Object with given handle.
+ /*! This function is optimized for the base case where the calling range
+ contains a single formula.
+
+ Additional behavior is supported:
+ - multiple functions nested within a single calling range - with the
+ possibility that functions to construct Objects are intermixed with
+ functions from another source e.g. Excel or some other user addin
+ - "anonymous objects" i.e. the user has not supplied an instance name
+ and a default value is generated
+
+ Performance in such special cases is sub-optimal.
+ */
virtual std::string storeObject(const std::string &instanceName,
const obj_ptr &object);
! virtual obj_ptr retrieveObject(const std::string &instanceName) const;
virtual void collectGarbage();
! virtual void deleteAllObjects();
! //! Reset the calling cell.
! /*! This function resets the status of the calling cell to "not busy" and
! should be called by non-constructor functions in case they wrap constructors
! which set the caller's status to "busy".
! */
! virtual void resetCaller();
private:
! std::map < std::string, boost::shared_ptr < CallingRange > > callingRanges_;
! boost::shared_ptr < CallingRange > getCallingRange();
! virtual std::string generateInstanceName();
! void clearCallingRange(boost::shared_ptr < CallingRange > callingRange);
! static int instanceNameCount_;
};
--- NEW FILE: callingrange.hpp ---
/*
Copyright (C) 2006 Eric Ehlers
This file is part of QuantLib, a free-software/open-source library
for financial quantitative analysts and developers - http://quantlib.org/
QuantLib is free software: you can redistribute it and/or modify it under the
terms of the QuantLib license. You should have received a copy of the
license along with this program; if not, please email qua...@li...
The license is also available online at http://quantlib.org/html/license.html
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the license for more details.
*/
/*! \file
\brief CallingRange class
*/
#ifndef ohxl_callingrange_hpp
#define ohxl_callingrange_hpp
#include <oh/object.hpp>
namespace ObjHandler {
class CallingRange {
public:
CallingRange();
~CallingRange();
bool isValid();
const std::string &getKey() {
return key_;
}
void registerObject(const std::string &instanceName);
void clear();
void update();
std::string updateCount();
private:
static int keyCount_;
static std::string getKeyCount();
std::string key_;
std::vector < std::string > residentObjects_;
bool busy_;
int updateCount_;
};
}
#endif
--- instancenamexl.cpp DELETED ---
Index: conversions.cpp
===================================================================
RCS file: /cvsroot/objecthandler/ObjectHandler/ohxl/conversions.cpp,v
retrieving revision 1.2
retrieving revision 1.3
diff -C2 -d -r1.2 -r1.3
*** conversions.cpp 25 May 2006 16:59:51 -0000 1.2
--- conversions.cpp 1 Jun 2006 10:52:08 -0000 1.3
***************
*** 252,257 ****
}
! if (xString->val.str[0])
! ret.assign(xString->val.str + 1, xString->val.str[0]);
if (needToFree)
--- 252,265 ----
}
! //if (xString->val.str[0])
! // ret.assign(xString->val.str + 1, xString->val.str[0]);
!
! // expirimental workaround for apparent bug in Excel API
! // where the value for the string length wraps around the byte
!
! int stringLength = xString->val.str[0];
! if (stringLength < 0) stringLength += 257;
! if (stringLength)
! ret.assign(xString->val.str + 1, x);
if (needToFree)
Index: functions.cpp
===================================================================
RCS file: /cvsroot/objecthandler/ObjectHandler/ohxl/functions.cpp,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** functions.cpp 19 May 2006 15:12:41 -0000 1.1
--- functions.cpp 1 Jun 2006 10:52:08 -0000 1.2
***************
*** 129,133 ****
OPER *dummy9) {
try {
! ObjHandler::FunctionCall functionCall;
static std::map < std::string, long > iterators;
static long ret;
--- 129,133 ----
OPER *dummy9) {
try {
! ObjHandler::FunctionCall functionCall("ohDependsOn");
static std::map < std::string, long > iterators;
static long ret;
Index: functioncall.cpp
===================================================================
RCS file: /cvsroot/objecthandler/ObjectHandler/ohxl/functioncall.cpp,v
retrieving revision 1.2
retrieving revision 1.3
diff -C2 -d -r1.2 -r1.3
*** functioncall.cpp 25 May 2006 16:59:51 -0000 1.2
--- functioncall.cpp 1 Jun 2006 10:52:08 -0000 1.3
***************
*** 21,30 ****
#include <ohxl/conversions.hpp>
#include <sstream>
namespace ObjHandler {
! FunctionCall *FunctionCall::instance_ = 0;
! FunctionCall::FunctionCall() {
if (instance_)
throw Exception("Multiple attempts to initialize global FunctionCall object");
--- 21,36 ----
#include <ohxl/conversions.hpp>
#include <sstream>
+ #include <string>
namespace ObjHandler {
! FunctionCall *FunctionCall::instance_ = 0;
! // FIXME these values need to be configured at runtime
! const std::string libFunctionSignature = "=ql";
! const std::string libName = "QuantLib";
!
! FunctionCall::FunctionCall(const std::string functionName)
! : functionName_(functionName) {
if (instance_)
throw Exception("Multiple attempts to initialize global FunctionCall object");
***************
*** 64,67 ****
--- 70,74 ----
Excel(xlfGetCell, &xAddress, 2, TempNum(1), getCallerReference());
operToScalar(address_, xAddress);
+ Excel(xlFree, 0, 1, &xAddress);
} catch (const std::exception &e) {
Excel(xlFree, 0, 1, &xAddress);
***************
*** 74,120 ****
}
! void FunctionCall::clearCell() {
!
! // XLOPERs which might need freeing in the event of an exception
!
! XLOPER xOldName;
! XLOPER xValue;
!
! try {
!
! // exit if calling cell is #VALUE
!
! Excel(xlfGetCell, &xValue, 2, TempNum(5), getCallerReference());
! if (xValue.xltype & xltypeErr) return;
! Excel(xlFree, 0, 1, &xValue);
!
! // get name if any
!
! Excel(xlfGetDef, &xOldName, 1, getCallerAddress());
!
! // if name - delete associated object
!
! if (xOldName.xltype == xltypeStr) {
! std::string oldKey;
! operToScalar(oldKey, xOldName);
! ObjectHandler::instance().deleteKey(oldKey);
}
! Excel(xlFree, 0, 1, &xOldName);
!
! } catch (const std::exception &e) {
!
! // free any memory that may have been allocated
! Excel(xlFree, 0, 2, &xOldName, &xValue);
! // propagate the exception
! std::ostringstream err;
! err << "FunctionCall::clearCell(): " << e.what();
! throw Exception(err.str());
}
-
}
}
-
--- 81,136 ----
}
! const std::string &FunctionCall::getFormula() {
! if (formula_.empty()) {
! XLOPER xFormula;
! try {
! Excel(xlfGetFormula, &xFormula, 1, getCallerReference());
! operToScalar(formula_, xFormula);
! Excel(xlFree, 0, 1, &xFormula);
! } catch (const std::exception &e) {
! Excel(xlFree, 0, 1, &xFormula);
! std::ostringstream err;
! err << "FunctionCall::getFormula(): " << e.what();
! throw Exception(err.str());
}
! }
! return formula_;
! }
! bool FunctionCall::outerFunction() {
! // exit if calling cell is #VALUE
! //XLOPER xValue;
! //Excel(xlfGetCell, &xValue, 2, TempNum(5), getCallerReference());
! //if (xValue.xltype & (xltypeErr | xltypeMissing)) {
! // return false;
! //}
! //Excel(xlFree, 0, 1, &xValue);
! std::string formula = getFormula();
! std::string functionNameCompare = "=" + functionName_ + "(";
! if (formula.substr(0, functionNameCompare.length()) == functionNameCompare) {
! if (formula.find(functionName_, functionNameCompare.length()) != std::string::npos) {
! std::ostringstream err;
! err << "Function '" << functionName_ << "' appears in formula '" << formula
! << "' as both the outermost function and as an inner function. "
! << "ObjectHandler cannot handle cell formulas in which a given function "
! << "is nested within itself as this circumvents garbage collection. "
! << "Please split this formula into separate cells.";
! throw Exception(err.str());
! }
! return true;
! } else {
! if (formula.substr(0, libFunctionSignature.length()) != libFunctionSignature) {
! std::ostringstream err;
! err << "in formula '" << formula << "' - " << libName << " function '" << functionName_
! << "' is nested within a non-" << libName << " function. ObjectHandler cannot handle "
! << "library functions nested within non-library functions as this circumvents garbage "
! << "collection. Please split this formula into separate cells.";
! throw Exception(err.str());
! }
! return false;
}
}
}
--- NEW FILE: callingrange.cpp ---
/*
Copyright (C) 2006 Eric Ehlers
This file is part of QuantLib, a free-software/open-source library
for financial quantitative analysts and developers - http://quantlib.org/
QuantLib is free software: you can redistribute it and/or modify it under the
terms of the QuantLib license. You should have received a copy of the
license along with this program; if not, please email qua...@li...
The license is also available online at http://quantlib.org/html/license.html
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the license for more details.
*/
#include <ohxl/callingrange.hpp>
#include <ohxl/conversions.hpp>
#include <ohxl/functioncall.hpp>
#include <oh/objecthandler.hpp>
#include <oh/exception.hpp>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <cmath>
namespace ObjHandler {
int CallingRange::keyCount_ = 0;
std::string CallingRange::getKeyCount() {
static const int KEY_WIDTH = 5;
static const int KEY_BASE = 16;
static const double KEY_MAX = pow((double)KEY_BASE, KEY_WIDTH);
if (keyCount_ > KEY_MAX)
throw Exception("CallingRange::getKeyCount: max key value exceeded");
std::ostringstream s;
s << '_' << std::setw(KEY_WIDTH) << std::setfill('0') << std::setbase(KEY_BASE) << keyCount_++;
return s.str();
}
CallingRange::CallingRange() : busy_(false), updateCount_(0) {
// name the calling range
key_ = getKeyCount();
const XLOPER *xCaller = FunctionCall::instance().getCallerReference();
XLOPER xRet;
Excel(xlfSetName, &xRet, 2, TempStrStl(key_), xCaller);
if (xRet.xltype != xltypeBool || !xRet.val.boolean)
throw Exception("error on call to xlfSetName");
}
CallingRange::~CallingRange() {
// unname the calling range
Excel(xlfSetName, 0, 1, TempStrStl(key_));
}
void CallingRange::clear() {
std::vector < std::string >::const_iterator i;
for (i = residentObjects_.begin(); i != residentObjects_.end(); i++) {
std::string instanceName = *i;
ObjectHandler::instance().deleteObject(instanceName);
}
residentObjects_.clear();
}
void CallingRange::registerObject(const std::string &instanceName) {
residentObjects_.push_back(instanceName);
}
bool CallingRange::isValid() {
// XLOPERs which might need freeing in the event of an exception
XLOPER xDef;
XLOPER xRef;
try {
Excel(xlfGetName, &xDef, 1, TempStrStl(key_));
std::string address;
operToScalar(address, xDef);
Excel(xlfTextref, &xRef, 1, TempStrStl(address.substr(1)));
bool ret = (xRef.xltype & (xltypeRef | xltypeSRef)) != 0;
Excel(xlFree, 0, 2, &xDef, &xRef);
return ret;
} catch (const std::exception &e) {
// free any memory that may have been allocated
Excel(xlFree, 0, 2, &xDef, &xRef);
// propagate the exception
std::ostringstream err;
err << "CallingRange::isValid: " << e.what();
throw Exception(err.str());
}
}
void CallingRange::update() {
/*
support for nested formulas e.g. "=qlFunc1(qlFunc2(), SUM(), qlFunc3(), xxx)"
implement the following algorithm:
- on first call to this formula - mark calling range as "busy" - clear()
- intermediate calls - take no action
- final call - mark calling range as "not busy"
*/
if (FunctionCall::instance().outerFunction()) {
if (busy_) {
busy_ = false;
} else {
clear();
}
} else {
if (busy_) {
return;
} else {
busy_ = true;
clear();
}
}
}
std::string CallingRange::updateCount() {
if (updateCount_ > 9999) updateCount_ = 0;
std::ostringstream s;
s << std::setw(4) << std::setfill('0') << updateCount_++;
return s.str();
}
}
--- instancenamexl.hpp DELETED ---
Index: functioncall.hpp
===================================================================
RCS file: /cvsroot/objecthandler/ObjectHandler/ohxl/functioncall.hpp,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** functioncall.hpp 19 May 2006 15:12:41 -0000 1.1
--- functioncall.hpp 1 Jun 2006 10:52:08 -0000 1.2
***************
*** 20,25 ****
*/
! #ifndef oh_functioncall_hpp
! #define oh_functioncall_hpp
#include <xlsdk/xlsdkdefines.hpp>
--- 20,25 ----
*/
! #ifndef ohxl_functioncall_hpp
! #define ohxl_functioncall_hpp
#include <xlsdk/xlsdkdefines.hpp>
***************
*** 38,48 ****
class DLL_API FunctionCall {
public:
! FunctionCall();
~FunctionCall();
static FunctionCall &instance();
- void clearCell();
const XLOPER *getCallerReference();
const XLOPER *getCallerAddress();
const std::string &getAddressString();
private:
static FunctionCall *instance_;
--- 38,49 ----
class DLL_API FunctionCall {
public:
! FunctionCall(const std::string functionName);
~FunctionCall();
static FunctionCall &instance();
const XLOPER *getCallerReference();
const XLOPER *getCallerAddress();
const std::string &getAddressString();
+ const std::string &getFormula();
+ bool outerFunction();
private:
static FunctionCall *instance_;
***************
*** 50,53 ****
--- 51,56 ----
XLOPER xReftext;
std::string address_;
+ std::string formula_;
+ std::string functionName_;
};
|