00001 #include "XmlObjectDefinitionReader.h"
00002 #include "exception/FileIOException.h"
00003 #include "exception/XmlParseException.h"
00004 #include "Util.h"
00005 #include "DefaultObjectDefinition.h"
00006 #include "PropertyParser.h"
00007 #include "DefaultWireDefinition.h"
00008 #include "ReferenceDefinition.h"
00009
00010 #include <QFile>
00011 #include <QDir>
00012
00013 #include <log4cxx/logger.h>
00014
00015 using namespace log4cxx;
00016
00017 using namespace qic;
00018
00019 LoggerPtr XmlObjectDefinitionReader::logger(Logger::getLogger("org.qic.XmlObjectDefinitionReader"));
00020
00021 const char * XmlObjectDefinitionReader::QIC_BASE_DIR = "QIC_DIR";
00022 const char * XmlObjectDefinitionReader::OBJECTS_TAG = "objects";
00023 const char * XmlObjectDefinitionReader::OBJECT_TAG = "object";
00024 const char * XmlObjectDefinitionReader::PARSER_TAG = "parser";
00025 const char * XmlObjectDefinitionReader::INJECTOR_TAG = "injector";
00026
00027 const char * XmlObjectDefinitionReader::WIRE_TAG = "wire";
00028 const char * XmlObjectDefinitionReader::WIRE_TYPE_ATTR = "type";
00029 const char * XmlObjectDefinitionReader::SENDER_TAG = "sender";
00030 const char * XmlObjectDefinitionReader::RECEIVER_TAG = "receiver";
00031 const char * XmlObjectDefinitionReader::SENDER_ID_ATTR = "id";
00032 const char * XmlObjectDefinitionReader::RECEIVER_ID_ATTR = "id";
00033 const char * XmlObjectDefinitionReader::SENDER_SIGNAL_ATTR = "signal";
00034 const char * XmlObjectDefinitionReader::RECEIVER_METHOD_ATTR = "method";
00035
00036 const char * XmlObjectDefinitionReader::TAG_ATTR = "tag";
00037 const char * XmlObjectDefinitionReader::PLUGIN_ATTR = "plugin";
00038 const char * XmlObjectDefinitionReader::ID_ATTR = "id";
00039 const char * XmlObjectDefinitionReader::FACTORY_ATTR = "factory-object";
00040 const char * XmlObjectDefinitionReader::FACTORY_METHOD_ATTR = "factory-method";
00041 const char * XmlObjectDefinitionReader::INIT_METHOD_ATTR = "init-method";
00042 const char * XmlObjectDefinitionReader::DESTROY_METHOD_ATTR = "destroy-method";
00043 const char * XmlObjectDefinitionReader::PROPERTY_TAG = "property";
00044 const char * XmlObjectDefinitionReader::NAME_ATTR = "name";
00045 const char * XmlObjectDefinitionReader::REF_ATTR = "ref";
00046
00047 const char * XmlObjectDefinitionReader::INJECTOR_TYPE_ATTR = "type";
00048
00049
00050 XmlObjectDefinitionReader::XmlObjectDefinitionReader(ObjectDefinitionRegistry * objectRegistry)
00051 {
00052 _objectRegistry = objectRegistry;
00053
00054 loadParserPlugins();
00055 loadInjectorPlugins();
00056 }
00057
00058 XmlObjectDefinitionReader::~ XmlObjectDefinitionReader()
00059 {
00060 unloadParserPlugins();
00061 }
00062
00063 int
00064 XmlObjectDefinitionReader::loadObjectDefinitions (QString path)
00065 {
00066 LOG4CXX_INFO(logger, QString("Loading object definitions: ").append(path).toStdString());
00067
00068 _currentFile = path;
00069
00070 QFile file(path);
00071 try
00072 {
00073 if (!file.open(QIODevice::ReadOnly))
00074 {
00075 throw FileIOException (path + " - " + file.errorString());
00076 }
00077 int count = loadObjectDefinitions(file);
00078
00079 file.close();
00080 return count;
00081 }
00082 catch (std::exception & )
00083 {
00084 if (file.isOpen())
00085 {
00086 file.close();
00087 }
00088 throw;
00089 }
00090 }
00091
00092 int
00093 XmlObjectDefinitionReader::loadObjectDefinitions (QIODevice & source)
00094 {
00095 QDomDocument doc;
00096 QString errorMsg;
00097 int errorLine;
00098 int errorCol;
00099 if (!doc.setContent(&source, &errorMsg, &errorLine, &errorCol))
00100 {
00101 throw XmlParseException (QString("File %1 (Line %2, Column %3): %4")
00102 .arg(_currentFile).arg(errorLine).arg(errorCol).arg(errorMsg));
00103 }
00104 return loadObjectDefinitions (doc);
00105 }
00106
00107 void
00108 XmlObjectDefinitionReader::loadInjectorPlugins ()
00109 {
00110 QString path = Util::getEnvVariable(QIC_BASE_DIR);
00111 QDir parser_dir;
00112
00113 QString file = parser_dir.absoluteFilePath(path + "/injector_plugins.xml");
00114 LOG4CXX_INFO(logger, "Loading injector plugins: " + file.toStdString());
00115
00116 loadObjectDefinitions(file);
00117 }
00118
00119 void
00120 XmlObjectDefinitionReader::loadParserPlugins ()
00121 {
00122 QString path = Util::getEnvVariable(QIC_BASE_DIR);
00123 QDir parser_dir;
00124
00125 QString file = parser_dir.absoluteFilePath(path + "/parser_plugins.xml");
00126 LOG4CXX_INFO(logger, "Loading parser plugins: " + file.toStdString());
00127
00128 loadObjectDefinitions(file);
00129 }
00130
00131 void
00132 XmlObjectDefinitionReader::unloadParserPlugins ()
00133 {
00134 LOG4CXX_INFO(logger, "Unloading parser plugins. ");
00135
00136 foreach (QString id, _parser_loader_map.keys())
00137 {
00138 QPluginLoader * loader = _parser_loader_map[id];
00139 if (loader)
00140 {
00141 loader->unload();
00142 delete loader;
00143 }
00144 else
00145 {
00146 LOG4CXX_WARN(logger, "Inconsistent application context. NULL plugin loader. id: " + id.toStdString());
00147 }
00148 }
00149 }
00150
00151 int
00152 XmlObjectDefinitionReader::loadObjectDefinitions (QDomDocument & doc)
00153 {
00154 int count = 0;
00155 QDomElement docElem = doc.documentElement();
00156 if (docElem.isNull())
00157 {
00158 throw XmlParseException (QString("File: %1 Root element NULL.").arg(_currentFile));
00159 }
00160 QString rootTag = docElem.tagName();
00161
00162 LOG4CXX_DEBUG(logger, "File: " + _currentFile.toStdString() + " Root tag: " + rootTag.toStdString());
00163
00164 if ( rootTag != OBJECTS_TAG)
00165 {
00166 throw XmlParseException (QString("File: %1 Root element tag name should be '%2'").arg(_currentFile).arg(OBJECTS_TAG));
00167 }
00168 QDomNode n = docElem.firstChild();
00169 while(!n.isNull())
00170 {
00171 QDomElement e = n.toElement();
00172 if (e.isNull())
00173 {
00174 throw XmlParseException (QString ("File: %1 %2 is not Element.").arg(_currentFile).arg(n.nodeName()));
00175 }
00176 QString tag = e.tagName ();
00177 if(tag == OBJECT_TAG)
00178 {
00179 parseObjectTag (e);
00180 count ++;
00181 }
00182 else if(tag == PARSER_TAG)
00183 {
00184 parseParserTag (e);
00185 }
00186 else if(tag == WIRE_TAG)
00187 {
00188 parseWireTag (e);
00189 }
00190 else if(tag == INJECTOR_TAG)
00191 {
00192 parseInjectorTag (e);
00193 }
00194 else
00195 {
00196 throw XmlParseException (QString("File %1: Expecting %2 or %3, %4 encountered.")
00197 .arg(_currentFile).arg(OBJECT_TAG).arg(PARSER_TAG).arg(tag));
00198 }
00199 n = n.nextSibling();
00200 }
00201
00202 return count;
00203 }
00204
00205 void
00206 XmlObjectDefinitionReader::parseWireTag( QDomElement & e)
00207 {
00208 DefaultWireDefinition * def = new DefaultWireDefinition();
00209 try
00210 {
00211 QString type = e.attribute(WIRE_TYPE_ATTR);
00212 if (type.isEmpty())
00213 {
00214 type = "auto";
00215 }
00216 def->setWireType( type);
00217
00218 for(QDomNode objNode = e.firstChild(); !objNode.isNull(); objNode = objNode.nextSibling())
00219 {
00220 QDomElement objEl = objNode.toElement();
00221 if (objEl.isNull())
00222 {
00223 throw XmlParseException (QString ("File: %1 %2 is not Element.").arg(_currentFile).arg(objNode.nodeName()));
00224 }
00225 if (objEl.tagName() == SENDER_TAG)
00226 {
00227 QString id = objEl.attribute(SENDER_ID_ATTR);
00228 if(id.isEmpty())
00229 {
00230 throw XmlParseException (QString("File: %1 sender element must have an '%2' attribute")
00231 .arg(_currentFile).arg(SENDER_ID_ATTR));
00232 }
00233 def->setSenderObjectId( id);
00234
00235 QString signal = objEl.attribute(SENDER_SIGNAL_ATTR);
00236 if(signal.isEmpty())
00237 {
00238 throw XmlParseException (QString("File: %1 sender element must have an '%2' attribute")
00239 .arg(_currentFile).arg(SENDER_SIGNAL_ATTR));
00240 }
00241 def->setSenderSignal( signal);
00242 }
00243 else if (objEl.tagName() == RECEIVER_TAG)
00244 {
00245 QString id = objEl.attribute(RECEIVER_ID_ATTR);
00246 if(id.isEmpty())
00247 {
00248 throw XmlParseException (QString("File: %1 receiver element must have an '%2' attribute")
00249 .arg(_currentFile).arg(RECEIVER_ID_ATTR));
00250 }
00251 def->setReceiverObjectId( id);
00252
00253 QString method = objEl.attribute(RECEIVER_METHOD_ATTR);
00254 if(method.isEmpty())
00255 {
00256 throw XmlParseException (QString("File: %1 receiver element must have an '%2' attribute")
00257 .arg(_currentFile).arg(RECEIVER_METHOD_ATTR));
00258 }
00259 def->setReceiverMethod( method);
00260 }
00261 else
00262 {
00263 throw XmlParseException (QString("File %1: Expecting %2 and %3, %4 encountered.")
00264 .arg(_currentFile).arg(SENDER_TAG).arg(RECEIVER_TAG).arg(objEl.tagName()));
00265 }
00266 }
00267 _objectRegistry->registerWireDefinition( def);
00268 }
00269 catch (std::exception &)
00270 {
00271 delete def;
00272 throw;
00273 }
00274 }
00275
00276 void
00277 XmlObjectDefinitionReader::parseObjectPropertySubTag (QString id, QString name, QDomNode & objNode, ObjectDefinition * def)
00278 {
00279 QDomNode valueNode = objNode.firstChild();
00280 QString xml = Util::translateVariable(Util::asXml(valueNode));
00281
00282 QDomDocument tmpDoc;
00283 tmpDoc.setContent(xml);
00284 QDomElement docElem = tmpDoc.documentElement();
00285 QString tag = docElem.tagName();
00286
00287 if (_parser_map.contains(tag))
00288 {
00289 QObject * parser_obj = _parser_map[tag];
00290 PropertyParser * parser = qobject_cast<PropertyParser *>(parser_obj);
00291 QVariant propVal = parser->parseProperty(docElem, _parser_map);
00292
00293 def->setPropertyValue(name, propVal);
00294
00295 if(logger->isDebugEnabled())
00296 {
00297 logger->forcedLog(Level::DEBUG, "Object " + id.toStdString() + "Property: name: " + name.toStdString() + " value: " + xml.replace("\n", "").toStdString(), __FILE__, __LINE__);
00298 }
00299 }
00300 else
00301 {
00302 throw XmlParseException (QString("File %1: Object: %2, Property: %3: Unexpected tag name %4.")
00303 .arg(_currentFile).arg(id).arg(name).arg(tag));
00304 }
00305 }
00306
00307 void
00308 XmlObjectDefinitionReader::parseObjectPropertyTag (QString id, QDomElement & e, ObjectDefinition * def)
00309 {
00310 for(QDomNode objNode = e.firstChild(); !objNode.isNull(); objNode = objNode.nextSibling())
00311 {
00312 QDomElement objEl = objNode.toElement();
00313 if (objEl.isNull())
00314 {
00315 throw XmlParseException (QString ("File: %1 %2 is not Element.").arg(_currentFile).arg(objNode.nodeName()));
00316 }
00317 if (objEl.tagName() == PROPERTY_TAG)
00318 {
00319 QString name = objEl.attribute(NAME_ATTR);
00320 if(name.isEmpty())
00321 {
00322 throw XmlParseException (QString("File: %1 Property element must have an '%2' attribute")
00323 .arg(_currentFile).arg(NAME_ATTR));
00324 }
00325
00326 QString refId = objEl.attribute(REF_ATTR);
00327
00328 if (refId.isEmpty())
00329 {
00330 parseObjectPropertySubTag(id, name, objNode, def);
00331 }
00332 else
00333 {
00334
00335 ReferenceDefinition refDef;
00336 refDef.refId = refId;
00337 refDef.isWeak = false;
00338
00339 def->setPropertyValue( name, QVariant::fromValue(refDef));
00340
00341 LOG4CXX_DEBUG(logger, "Object " + id.toStdString() + " property name: " + name.toStdString() + " ref: " + refId.toStdString());
00342 }
00343
00344 }
00345 else
00346 {
00347 throw XmlParseException (QString("File %1: Expecting %2, %3 encountered.")
00348 .arg(_currentFile).arg(PROPERTY_TAG).arg(objEl.tagName()));
00349 }
00350 }
00351 }
00352
00353 void
00354 XmlObjectDefinitionReader::parseFactoryObject (QString id, QDomElement & e, ObjectDefinition * def)
00355 {
00356
00357
00358 QString factory = e.attribute(FACTORY_ATTR);
00359 if (factory.isEmpty())
00360 {
00361 throw XmlParseException (QString("File: %1 Object element must have an '%2' attribute or '%3' attribute") .arg(_currentFile).arg(PLUGIN_ATTR).arg(FACTORY_ATTR));
00362 }
00363 else
00364 {
00365 def->setFactoryObjectId(factory);
00366 }
00367
00368 QString factory_method = e.attribute(FACTORY_METHOD_ATTR);
00369 if (factory_method.isEmpty())
00370 {
00371 throw XmlParseException (QString("File: %1 Factory object element must have an '%2' attribute") .arg(_currentFile).arg(FACTORY_METHOD_ATTR));
00372 }
00373 else
00374 {
00375 def->setFactoryMethodName(factory_method);
00376 }
00377 LOG4CXX_DEBUG(logger, "Object: " + std::string(ID_ATTR) + ": " + id.toStdString() + " " + std::string(FACTORY_ATTR) + ": " + factory.toStdString() + " " + std::string(FACTORY_METHOD_ATTR) + ": " + factory_method.toStdString() );
00378 }
00379
00380 void
00381 XmlObjectDefinitionReader::parseObjectTag (QDomElement & e)
00382 {
00383 DefaultObjectDefinition * def = new DefaultObjectDefinition();
00384 try
00385 {
00386 QString id = e.attribute(ID_ATTR);
00387 if (id.isEmpty())
00388 {
00389 throw XmlParseException (QString("File: %1 Object element must have an '%2' attribute").arg(_currentFile).arg(ID_ATTR));
00390 }
00391 def->setId(id);
00392
00393 QString init_method = e.attribute(INIT_METHOD_ATTR);
00394 if (!init_method.isEmpty())
00395 {
00396 def->setInitMethodName(init_method);
00397 }
00398
00399 QString destroy_method = e.attribute(DESTROY_METHOD_ATTR);
00400 if (!destroy_method.isEmpty())
00401 {
00402 def->setDestroyMethodName(destroy_method);
00403 }
00404
00405 LOG4CXX_DEBUG(logger, "Object: " + std::string(ID_ATTR) + ": " + id.toStdString() + " " + std::string(INIT_METHOD_ATTR) + ": " + init_method.toStdString() + " " + std::string(DESTROY_METHOD_ATTR) + ": " + destroy_method.toStdString());
00406
00407 QString plugin_path = Util::translateVariable(e.attribute(PLUGIN_ATTR));
00408 if (plugin_path.isEmpty())
00409 {
00410 parseFactoryObject (id, e, def);
00411 }
00412 else
00413 {
00414 LOG4CXX_DEBUG(logger, "Object: " + std::string(ID_ATTR) + ": " + id.toStdString() + " " + std::string(PLUGIN_ATTR) + ": " + plugin_path.toStdString());
00415 def->setPluginPath(plugin_path);
00416 }
00417
00418 parseObjectPropertyTag (id, e, def);
00419
00420 _objectRegistry->registerObjectDefinition(id, def);
00421 }
00422 catch (std::exception &)
00423 {
00424 delete def;
00425 throw;
00426 }
00427
00428 }
00429
00430 void
00431 XmlObjectDefinitionReader::parseParserTag(QDomElement & e)
00432 {
00433 QString tag = e.attribute(TAG_ATTR);
00434 if (tag.isEmpty())
00435 {
00436 throw XmlParseException (QString("File: %1 parser element must have an '%2' attribute").arg(_currentFile).arg(TAG_ATTR));
00437 }
00438 QString plugin_path = Util::translateVariable(e.attribute(PLUGIN_ATTR));
00439 if (plugin_path.isEmpty())
00440 {
00441 throw XmlParseException (QString("File: %1 parser element must have an '%2' attribute").arg(_currentFile).arg(PLUGIN_ATTR));
00442 }
00443 else
00444 {
00445 LOG4CXX_DEBUG(logger, "Load parser plugin: tag: " + tag.toStdString() + " path: " + plugin_path.toStdString());
00446
00447 Util::loadPlugin (tag, plugin_path, _parser_loader_map, _parser_map);
00448 }
00449
00450 }
00451
00452 void
00453 XmlObjectDefinitionReader::parseInjectorTag(QDomElement & e)
00454 {
00455 QString type = e.attribute(INJECTOR_TYPE_ATTR);
00456 if (type.isEmpty())
00457 {
00458 throw XmlParseException (QString("File: %1 injector element must have an '%2' attribute").arg(_currentFile).arg(INJECTOR_TYPE_ATTR));
00459 }
00460 QString plugin_path = Util::translateVariable(e.attribute(PLUGIN_ATTR));
00461 if (plugin_path.isEmpty())
00462 {
00463 throw XmlParseException (QString("File: %1 injector element must have an '%2' attribute").arg(_currentFile).arg(PLUGIN_ATTR));
00464 }
00465 else
00466 {
00467 _objectRegistry->registerReferenceInjector( type, plugin_path);
00468 }
00469 }