Line data Source code
1 :
2 : /*
3 : * ALMA - Atacama Large Millimeter Array
4 : * (c) European Southern Observatory, 2002
5 : * (c) Associated Universities Inc., 2002
6 : * Copyright by ESO (in the framework of the ALMA collaboration),
7 : * Copyright by AUI (in the framework of the ALMA collaboration),
8 : * All rights reserved.
9 : *
10 : * This library is free software; you can redistribute it and/or
11 : * modify it under the terms of the GNU Lesser General Public
12 : * License as published by the Free software Foundation; either
13 : * version 2.1 of the License, or (at your option) any later version.
14 : *
15 : * This library is distributed in the hope that it will be useful,
16 : * but WITHOUT ANY WARRANTY, without even the implied warranty of
17 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 : * Lesser General Public License for more details.
19 : *
20 : * You should have received a copy of the GNU Lesser General Public
21 : * License along with this library; if not, write to the Free Software
22 : * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23 : * MA 02111-1307 USA
24 : *
25 : * Warning!
26 : * --------------------------------------------------------------------
27 : * | This is generated code! Do not modify this file. |
28 : * | If you do, all changes will be lost when the file is re-generated. |
29 : * --------------------------------------------------------------------
30 : *
31 : * File HistoryTable.cpp
32 : */
33 : #include <alma/ASDM/ConversionException.h>
34 : #include <alma/ASDM/DuplicateKey.h>
35 : #include <alma/ASDM/OutOfBoundsException.h>
36 :
37 : using asdm::ConversionException;
38 : using asdm::DuplicateKey;
39 : using asdm::OutOfBoundsException;
40 :
41 : #include <alma/ASDM/ASDM.h>
42 : #include <alma/ASDM/HistoryTable.h>
43 : #include <alma/ASDM/HistoryRow.h>
44 : #include <alma/ASDM/Parser.h>
45 :
46 : using asdm::ASDM;
47 : using asdm::HistoryTable;
48 : using asdm::HistoryRow;
49 : using asdm::Parser;
50 :
51 : #include <iostream>
52 : #include <fstream>
53 : #include <iterator>
54 : #include <sstream>
55 : #include <set>
56 : #include <algorithm>
57 : using namespace std;
58 :
59 : #include <alma/ASDM/Misc.h>
60 : using namespace asdm;
61 :
62 : #include <libxml/parser.h>
63 : #include <libxml/tree.h>
64 :
65 : #ifndef WITHOUT_BOOST
66 : #include "boost/filesystem/operations.hpp"
67 : #include <boost/algorithm/string.hpp>
68 : #else
69 : #include <sys/stat.h>
70 : #endif
71 :
72 : namespace asdm {
73 : // The name of the entity we will store in this table.
74 : static string entityNameOfHistory = "History";
75 :
76 : // An array of string containing the names of the columns of this table.
77 : // The array is filled in the order : key, required value, optional value.
78 : //
79 : static string attributesNamesOfHistory_a[] = {
80 :
81 : "execBlockId"
82 : ,
83 : "time"
84 :
85 :
86 : , "message"
87 :
88 : , "priority"
89 :
90 : , "origin"
91 :
92 : , "objectId"
93 :
94 : , "application"
95 :
96 : , "cliCommand"
97 :
98 : , "appParms"
99 :
100 :
101 : };
102 :
103 : // A vector of string whose content is a copy of the strings in the array above.
104 : //
105 : static vector<string> attributesNamesOfHistory_v (attributesNamesOfHistory_a, attributesNamesOfHistory_a + sizeof(attributesNamesOfHistory_a) / sizeof(attributesNamesOfHistory_a[0]));
106 :
107 : // An array of string containing the names of the columns of this table.
108 : // The array is filled in the order where the names would be read by default in the XML header of a file containing
109 : // the table exported in binary mode.
110 : //
111 : static string attributesNamesInBinOfHistory_a[] = {
112 :
113 : "execBlockId" , "time" , "message" , "priority" , "origin" , "objectId" , "application" , "cliCommand" , "appParms"
114 : ,
115 :
116 :
117 : };
118 :
119 : // A vector of string whose content is a copy of the strings in the array above.
120 : //
121 : static vector<string> attributesNamesInBinOfHistory_v(attributesNamesInBinOfHistory_a, attributesNamesInBinOfHistory_a + sizeof(attributesNamesInBinOfHistory_a) / sizeof(attributesNamesInBinOfHistory_a[0]));
122 :
123 :
124 : // The array of attributes (or column) names that make up key key.
125 : //
126 : string keyOfHistory_a[] = {
127 :
128 : "execBlockId"
129 : ,
130 : "time"
131 :
132 : };
133 :
134 : // A vector of strings which are copies of those stored in the array above.
135 : vector<string> keyOfHistory_v(keyOfHistory_a, keyOfHistory_a + sizeof(keyOfHistory_a) / sizeof(keyOfHistory_a[0]));
136 :
137 : /**
138 : * Return the list of field names that make up key key
139 : * as a const reference to a vector of strings.
140 : */
141 0 : const vector<string>& HistoryTable::getKeyName() {
142 0 : return keyOfHistory_v;
143 : }
144 :
145 :
146 4 : HistoryTable::HistoryTable(ASDM &c) : container(c) {
147 :
148 : // Define a default entity.
149 4 : entity.setEntityId(EntityId("uid://X0/X0/X0"));
150 4 : entity.setEntityIdEncrypted("na");
151 4 : entity.setEntityTypeName("HistoryTable");
152 4 : entity.setEntityVersion("1");
153 4 : entity.setInstanceVersion("1");
154 :
155 : // Archive XML
156 4 : archiveAsBin = false;
157 :
158 : // File XML
159 4 : fileAsBin = false;
160 :
161 : // By default the table is considered as present in memory
162 4 : presentInMemory = true;
163 :
164 : // By default there is no load in progress
165 4 : loadInProgress = false;
166 4 : }
167 :
168 : /**
169 : * A destructor for HistoryTable.
170 : */
171 8 : HistoryTable::~HistoryTable() {
172 4 : for (unsigned int i = 0; i < privateRows.size(); i++)
173 0 : delete(privateRows.at(i));
174 8 : }
175 :
176 : /**
177 : * Container to which this table belongs.
178 : */
179 0 : ASDM &HistoryTable::getContainer() const {
180 0 : return container;
181 : }
182 :
183 : /**
184 : * Return the number of rows in the table.
185 : */
186 0 : unsigned int HistoryTable::size() const {
187 0 : if (presentInMemory)
188 0 : return privateRows.size();
189 : else
190 0 : return declaredSize;
191 : }
192 :
193 : /**
194 : * Return the name of this table.
195 : */
196 87 : string HistoryTable::getName() const {
197 87 : return entityNameOfHistory;
198 : }
199 :
200 : /**
201 : * Return the name of this table.
202 : */
203 0 : string HistoryTable::name() {
204 0 : return entityNameOfHistory;
205 : }
206 :
207 : /**
208 : * Return the the names of the attributes (or columns) of this table.
209 : */
210 0 : const vector<string>& HistoryTable::getAttributesNames() { return attributesNamesOfHistory_v; }
211 :
212 : /**
213 : * Return the the names of the attributes (or columns) of this table as they appear by default
214 : * in an binary export of this table.
215 : */
216 0 : const vector<string>& HistoryTable::defaultAttributesNamesInBin() { return attributesNamesInBinOfHistory_v; }
217 :
218 : /**
219 : * Return this table's Entity.
220 : */
221 0 : Entity HistoryTable::getEntity() const {
222 0 : return entity;
223 : }
224 :
225 : /**
226 : * Set this table's Entity.
227 : */
228 0 : void HistoryTable::setEntity(Entity e) {
229 0 : this->entity = e;
230 0 : }
231 :
232 : //
233 : // ====> Row creation.
234 : //
235 :
236 : /**
237 : * Create a new row.
238 : */
239 0 : HistoryRow *HistoryTable::newRow() {
240 0 : return new HistoryRow (*this);
241 : }
242 :
243 :
244 : /**
245 : * Create a new row initialized to the specified values.
246 : * @return a pointer on the created and initialized row.
247 :
248 : * @param execBlockId
249 :
250 : * @param time
251 :
252 : * @param message
253 :
254 : * @param priority
255 :
256 : * @param origin
257 :
258 : * @param objectId
259 :
260 : * @param application
261 :
262 : * @param cliCommand
263 :
264 : * @param appParms
265 :
266 : */
267 0 : HistoryRow* HistoryTable::newRow(Tag execBlockId, ArrayTime time, std::string message, std::string priority, std::string origin, std::string objectId, std::string application, std::string cliCommand, std::string appParms){
268 0 : HistoryRow *row = new HistoryRow(*this);
269 :
270 0 : row->setExecBlockId(execBlockId);
271 :
272 0 : row->setTime(time);
273 :
274 0 : row->setMessage(message);
275 :
276 0 : row->setPriority(priority);
277 :
278 0 : row->setOrigin(origin);
279 :
280 0 : row->setObjectId(objectId);
281 :
282 0 : row->setApplication(application);
283 :
284 0 : row->setCliCommand(cliCommand);
285 :
286 0 : row->setAppParms(appParms);
287 :
288 0 : return row;
289 : }
290 :
291 :
292 :
293 0 : HistoryRow* HistoryTable::newRow(HistoryRow* row) {
294 0 : return new HistoryRow(*this, row);
295 : }
296 :
297 : //
298 : // Append a row to its table.
299 : //
300 :
301 :
302 :
303 :
304 :
305 : /**
306 : * Returns a string built by concatenating the ascii representation of the
307 : * parameters values suffixed with a "_" character.
308 : */
309 0 : string HistoryTable::Key(Tag execBlockId) {
310 0 : ostringstream ostrstr;
311 : ostrstr
312 :
313 0 : << execBlockId.toString() << "_"
314 :
315 : ;
316 0 : return ostrstr.str();
317 0 : }
318 :
319 :
320 :
321 0 : HistoryRow* HistoryTable::add(HistoryRow* x) {
322 : string keystr = Key(
323 0 : x->getExecBlockId()
324 0 : );
325 0 : if (context.find(keystr) == context.end()) {
326 0 : vector<HistoryRow *> v;
327 0 : context[keystr] = v;
328 0 : }
329 0 : return insertByTime(x, context[keystr]);
330 0 : }
331 :
332 :
333 :
334 :
335 0 : void HistoryTable::addWithoutCheckingUnique(HistoryRow * x) {
336 0 : HistoryRow * dummy = checkAndAdd(x, true); // We require the check for uniqueness to be skipped.
337 : // by passing true in the second parameter
338 : // whose value by default is false.
339 : // this statement is never executed, but it hides the unused return value from the compiler to silence that warning.
340 : if (false) cout << (unsigned long long) dummy;
341 0 : }
342 :
343 :
344 :
345 :
346 : //
347 : // A private method to append a row to its table, used by input conversion
348 : // methods, with row uniqueness.
349 : //
350 :
351 :
352 :
353 :
354 :
355 :
356 :
357 :
358 :
359 0 : HistoryRow* HistoryTable::checkAndAdd(HistoryRow* x, bool /* skipCheckUniqueness */ ) {
360 : string keystr = Key(
361 0 : x->getExecBlockId()
362 0 : );
363 0 : if (context.find(keystr) == context.end()) {
364 0 : vector<HistoryRow *> v;
365 0 : context[keystr] = v;
366 0 : }
367 :
368 0 : vector<HistoryRow*>& found = context.find(keystr)->second;
369 0 : return insertByTime(x, found);
370 0 : }
371 :
372 :
373 :
374 :
375 :
376 :
377 : //
378 : // A private method to brutally append a row to its table, without checking for row uniqueness.
379 : //
380 :
381 0 : void HistoryTable::append(HistoryRow *x) {
382 0 : privateRows.push_back(x);
383 0 : x->isAdded(true);
384 0 : }
385 :
386 :
387 :
388 :
389 :
390 0 : vector<HistoryRow *> HistoryTable::get() {
391 0 : checkPresenceInMemory();
392 0 : return privateRows;
393 : }
394 :
395 0 : const vector<HistoryRow *>& HistoryTable::get() const {
396 0 : const_cast<HistoryTable&>(*this).checkPresenceInMemory();
397 0 : return privateRows;
398 : }
399 :
400 :
401 :
402 :
403 :
404 :
405 :
406 :
407 :
408 :
409 0 : vector<HistoryRow *> *HistoryTable::getByContext(Tag execBlockId) {
410 : //if (getContainer().checkRowUniqueness() == false)
411 : //throw IllegalAccessException ("The method 'getByContext' can't be called because the dataset has been built without checking the row uniqueness.", "HistoryTable");
412 :
413 0 : checkPresenceInMemory();
414 0 : string k = Key(execBlockId);
415 :
416 0 : if (context.find(k) == context.end()) return 0;
417 0 : else return &(context[k]);
418 0 : }
419 :
420 :
421 :
422 :
423 :
424 :
425 :
426 :
427 :
428 :
429 : /*
430 : ** Returns a HistoryRow* given a key.
431 : ** @return a pointer to the row having the key whose values are passed as parameters, or 0 if
432 : ** no row exists for that key.
433 : **
434 : */
435 :
436 :
437 0 : HistoryRow* HistoryTable::getRowByKey(Tag execBlockId, ArrayTime time) {
438 0 : checkPresenceInMemory();
439 0 : string keystr = Key(execBlockId);
440 :
441 0 : if (context.find(keystr) == context.end()) return 0;
442 :
443 0 : vector<HistoryRow* > row = context[keystr];
444 :
445 : // Is the vector empty...impossible in principle !
446 0 : if (row.size() == 0) return 0;
447 :
448 : // Only one element in the vector
449 0 : if (row.size() == 1) {
450 0 : if (time.get() == row.at(0)->getTime().get())
451 0 : return row.at(0);
452 : else
453 0 : return 0;
454 : }
455 :
456 : // Optimizations
457 0 : HistoryRow* last = row.at(row.size()-1);
458 0 : if (time.get() > last->getTime().get()) return 0;
459 0 : HistoryRow* first = row.at(0);
460 0 : if (time.get() < first->getTime().get()) return 0;
461 :
462 : // More than one row
463 : // let's use a dichotomy method for the general case..
464 0 : int k0 = 0;
465 0 : int k1 = row.size() - 1;
466 0 : while (k0 != k1 ) {
467 0 : if (time.get() == row.at(k0)->getTime().get()) {
468 0 : return row.at(k0);
469 : }
470 0 : else if (time.get() == row.at(k1)->getTime().get()) {
471 0 : return row.at(k1);
472 : }
473 : else {
474 0 : if (time.get() <= row.at((k0+k1)/2)->getTime().get())
475 0 : k1 = (k0 + k1) / 2;
476 : else
477 0 : k0 = (k0 + k1) / 2;
478 : }
479 : }
480 0 : return 0;
481 0 : }
482 :
483 :
484 :
485 :
486 :
487 :
488 :
489 :
490 :
491 : #ifndef WITHOUT_ACS
492 : using asdmIDL::HistoryTableIDL;
493 : #endif
494 :
495 : #ifndef WITHOUT_ACS
496 : // Conversion Methods
497 :
498 : HistoryTableIDL *HistoryTable::toIDL() {
499 : HistoryTableIDL *x = new HistoryTableIDL ();
500 : unsigned int nrow = size();
501 : x->row.length(nrow);
502 : vector<HistoryRow*> v = get();
503 : for (unsigned int i = 0; i < nrow; ++i) {
504 : //x->row[i] = *(v[i]->toIDL());
505 : v[i]->toIDL(x->row[i]);
506 : }
507 : return x;
508 : }
509 :
510 : void HistoryTable::toIDL(asdmIDL::HistoryTableIDL& x) const {
511 : unsigned int nrow = size();
512 : x.row.length(nrow);
513 : vector<HistoryRow*> v = get();
514 : for (unsigned int i = 0; i < nrow; ++i) {
515 : v[i]->toIDL(x.row[i]);
516 : }
517 : }
518 : #endif
519 :
520 : #ifndef WITHOUT_ACS
521 : void HistoryTable::fromIDL(HistoryTableIDL x) {
522 : unsigned int nrow = x.row.length();
523 : for (unsigned int i = 0; i < nrow; ++i) {
524 : HistoryRow *tmp = newRow();
525 : tmp->setFromIDL(x.row[i]);
526 : // checkAndAdd(tmp);
527 : add(tmp);
528 : }
529 : }
530 : #endif
531 :
532 :
533 0 : string HistoryTable::toXML() {
534 0 : string buf;
535 :
536 0 : buf.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?> ");
537 0 : buf.append("<HistoryTable xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:histry=\"http://Alma/XASDM/HistoryTable\" xsi:schemaLocation=\"http://Alma/XASDM/HistoryTable http://almaobservatory.org/XML/XASDM/4/HistoryTable.xsd\" schemaVersion=\"4\" schemaRevision=\"-1\">\n");
538 :
539 0 : buf.append(entity.toXML());
540 0 : string s = container.getEntity().toXML();
541 : // Change the "Entity" tag to "ContainerEntity".
542 0 : buf.append("<Container" + s.substr(1,s.length() - 1)+" ");
543 0 : vector<HistoryRow*> v = get();
544 0 : for (unsigned int i = 0; i < v.size(); ++i) {
545 : try {
546 0 : buf.append(v[i]->toXML());
547 0 : } catch (const NoSuchRow &e) {
548 0 : }
549 0 : buf.append(" ");
550 : }
551 0 : buf.append("</HistoryTable> ");
552 0 : return buf;
553 0 : }
554 :
555 :
556 0 : string HistoryTable::getVersion() const {
557 0 : return version;
558 : }
559 :
560 :
561 0 : void HistoryTable::fromXML(string& tableInXML) {
562 : //
563 : // Look for a version information in the schemaVersion of the XML
564 : //
565 : xmlDoc *doc;
566 : #if LIBXML_VERSION >= 20703
567 0 : doc = xmlReadMemory(tableInXML.data(), tableInXML.size(), "XMLTableHeader.xml", NULL, XML_PARSE_NOBLANKS|XML_PARSE_HUGE);
568 : #else
569 : doc = xmlReadMemory(tableInXML.data(), tableInXML.size(), "XMLTableHeader.xml", NULL, XML_PARSE_NOBLANKS);
570 : #endif
571 0 : if ( doc == NULL )
572 0 : throw ConversionException("Failed to parse the xmlHeader into a DOM structure.", "History");
573 :
574 0 : xmlNode* root_element = xmlDocGetRootElement(doc);
575 0 : if ( root_element == NULL || root_element->type != XML_ELEMENT_NODE )
576 0 : throw ConversionException("Failed to retrieve the root element in the DOM structure.", "History");
577 :
578 0 : xmlChar * propValue = xmlGetProp(root_element, (const xmlChar *) "schemaVersion");
579 0 : if ( propValue != 0 ) {
580 0 : version = string( (const char*) propValue);
581 0 : xmlFree(propValue);
582 : }
583 :
584 0 : Parser xml(tableInXML);
585 0 : if (!xml.isStr("<HistoryTable"))
586 0 : error();
587 : // cout << "Parsing a HistoryTable" << endl;
588 0 : string s = xml.getElement("<Entity","/>");
589 0 : if (s.length() == 0)
590 0 : error();
591 0 : Entity e;
592 0 : e.setFromXML(s);
593 0 : if (e.getEntityTypeName() != "HistoryTable")
594 0 : error();
595 0 : setEntity(e);
596 : // Skip the container's entity; but, it has to be there.
597 0 : s = xml.getElement("<ContainerEntity","/>");
598 0 : if (s.length() == 0)
599 0 : error();
600 :
601 : // Get each row in the table.
602 0 : s = xml.getElementContent("<row>","</row>");
603 : HistoryRow *row;
604 0 : if (getContainer().checkRowUniqueness()) {
605 : try {
606 0 : while (s.length() != 0) {
607 0 : row = newRow();
608 0 : row->setFromXML(s);
609 0 : checkAndAdd(row);
610 0 : s = xml.getElementContent("<row>","</row>");
611 : }
612 :
613 : }
614 0 : catch (const DuplicateKey &e1) {
615 0 : throw ConversionException(e1.getMessage(),"HistoryTable");
616 0 : }
617 0 : catch (const UniquenessViolationException &e1) {
618 0 : throw ConversionException(e1.getMessage(),"HistoryTable");
619 0 : }
620 0 : catch (...) {
621 : // cout << "Unexpected error in HistoryTable::checkAndAdd called from HistoryTable::fromXML " << endl;
622 0 : }
623 : }
624 : else {
625 : try {
626 0 : while (s.length() != 0) {
627 0 : row = newRow();
628 0 : row->setFromXML(s);
629 0 : addWithoutCheckingUnique(row);
630 0 : s = xml.getElementContent("<row>","</row>");
631 : }
632 : }
633 0 : catch (const DuplicateKey &e1) {
634 0 : throw ConversionException(e1.getMessage(),"HistoryTable");
635 0 : }
636 0 : catch (...) {
637 : // cout << "Unexpected error in HistoryTable::addWithoutCheckingUnique called from HistoryTable::fromXML " << endl;
638 0 : }
639 : }
640 :
641 :
642 0 : if (!xml.isStr("</HistoryTable>"))
643 0 : error();
644 :
645 : //Does not change the convention defined in the model.
646 : //archiveAsBin = false;
647 : //fileAsBin = false;
648 :
649 : // clean up the xmlDoc pointer
650 0 : if ( doc != NULL ) xmlFreeDoc(doc);
651 :
652 0 : }
653 :
654 :
655 0 : void HistoryTable::error() {
656 0 : throw ConversionException("Invalid xml document","History");
657 : }
658 :
659 :
660 0 : string HistoryTable::MIMEXMLPart(const asdm::ByteOrder* byteOrder) {
661 0 : string UID = getEntity().getEntityId().toString();
662 0 : string withoutUID = UID.substr(6);
663 0 : string containerUID = getContainer().getEntity().getEntityId().toString();
664 0 : ostringstream oss;
665 0 : oss << "<?xml version='1.0' encoding='ISO-8859-1'?>";
666 0 : oss << "\n";
667 0 : oss << "<HistoryTable xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:histry=\"http://Alma/XASDM/HistoryTable\" xsi:schemaLocation=\"http://Alma/XASDM/HistoryTable http://almaobservatory.org/XML/XASDM/4/HistoryTable.xsd\" schemaVersion=\"4\" schemaRevision=\"-1\">\n";
668 0 : oss<< "<Entity entityId='"<<UID<<"' entityIdEncrypted='na' entityTypeName='HistoryTable' schemaVersion='1' documentVersion='1'/>\n";
669 0 : oss<< "<ContainerEntity entityId='"<<containerUID<<"' entityIdEncrypted='na' entityTypeName='ASDM' schemaVersion='1' documentVersion='1'/>\n";
670 0 : oss << "<BulkStoreRef file_id='"<<withoutUID<<"' byteOrder='"<<byteOrder->toString()<<"' />\n";
671 0 : oss << "<Attributes>\n";
672 :
673 0 : oss << "<execBlockId/>\n";
674 0 : oss << "<time/>\n";
675 0 : oss << "<message/>\n";
676 0 : oss << "<priority/>\n";
677 0 : oss << "<origin/>\n";
678 0 : oss << "<objectId/>\n";
679 0 : oss << "<application/>\n";
680 0 : oss << "<cliCommand/>\n";
681 0 : oss << "<appParms/>\n";
682 :
683 0 : oss << "</Attributes>\n";
684 0 : oss << "</HistoryTable>\n";
685 :
686 0 : return oss.str();
687 0 : }
688 :
689 0 : string HistoryTable::toMIME(const asdm::ByteOrder* byteOrder) {
690 0 : EndianOSStream eoss(byteOrder);
691 :
692 0 : string UID = getEntity().getEntityId().toString();
693 :
694 : // The MIME Header
695 0 : eoss <<"MIME-Version: 1.0";
696 0 : eoss << "\n";
697 0 : eoss << "Content-Type: Multipart/Related; boundary='MIME_boundary'; type='text/xml'; start= '<header.xml>'";
698 0 : eoss <<"\n";
699 0 : eoss <<"Content-Description: Correlator";
700 0 : eoss <<"\n";
701 0 : eoss <<"alma-uid:" << UID;
702 0 : eoss <<"\n";
703 0 : eoss <<"\n";
704 :
705 : // The MIME XML part header.
706 0 : eoss <<"--MIME_boundary";
707 0 : eoss <<"\n";
708 0 : eoss <<"Content-Type: text/xml; charset='ISO-8859-1'";
709 0 : eoss <<"\n";
710 0 : eoss <<"Content-Transfer-Encoding: 8bit";
711 0 : eoss <<"\n";
712 0 : eoss <<"Content-ID: <header.xml>";
713 0 : eoss <<"\n";
714 0 : eoss <<"\n";
715 :
716 : // The MIME XML part content.
717 0 : eoss << MIMEXMLPart(byteOrder);
718 :
719 : // The MIME binary part header
720 0 : eoss <<"--MIME_boundary";
721 0 : eoss <<"\n";
722 0 : eoss <<"Content-Type: binary/octet-stream";
723 0 : eoss <<"\n";
724 0 : eoss <<"Content-ID: <content.bin>";
725 0 : eoss <<"\n";
726 0 : eoss <<"\n";
727 :
728 : // The MIME binary content
729 0 : entity.toBin(eoss);
730 0 : container.getEntity().toBin(eoss);
731 0 : eoss.writeInt((int) privateRows.size());
732 0 : for (unsigned int i = 0; i < privateRows.size(); i++) {
733 0 : privateRows.at(i)->toBin(eoss);
734 : }
735 :
736 : // The closing MIME boundary
737 0 : eoss << "\n--MIME_boundary--";
738 0 : eoss << "\n";
739 :
740 0 : return eoss.str();
741 0 : }
742 :
743 :
744 0 : void HistoryTable::setFromMIME(const string & mimeMsg) {
745 0 : string xmlPartMIMEHeader = "Content-ID: <header.xml>\n\n";
746 :
747 0 : string binPartMIMEHeader = "--MIME_boundary\nContent-Type: binary/octet-stream\nContent-ID: <content.bin>\n\n";
748 :
749 : // Detect the XML header.
750 0 : string::size_type loc0 = mimeMsg.find(xmlPartMIMEHeader, 0);
751 0 : if ( loc0 == string::npos) {
752 : // let's try with CRLFs
753 0 : xmlPartMIMEHeader = "Content-ID: <header.xml>\r\n\r\n";
754 0 : loc0 = mimeMsg.find(xmlPartMIMEHeader, 0);
755 0 : if ( loc0 == string::npos )
756 0 : throw ConversionException("Failed to detect the beginning of the XML header", "History");
757 : }
758 :
759 0 : loc0 += xmlPartMIMEHeader.size();
760 :
761 : // Look for the string announcing the binary part.
762 0 : string::size_type loc1 = mimeMsg.find( binPartMIMEHeader, loc0 );
763 :
764 0 : if ( loc1 == string::npos ) {
765 0 : throw ConversionException("Failed to detect the beginning of the binary part", "History");
766 : }
767 :
768 : //
769 : // Extract the xmlHeader and analyze it to find out what is the byte order and the sequence
770 : // of attribute names.
771 : //
772 0 : string xmlHeader = mimeMsg.substr(loc0, loc1-loc0);
773 : xmlDoc *doc;
774 0 : doc = xmlReadMemory(xmlHeader.data(), xmlHeader.size(), "BinaryTableHeader.xml", NULL, XML_PARSE_NOBLANKS);
775 0 : if ( doc == NULL )
776 0 : throw ConversionException("Failed to parse the xmlHeader into a DOM structure.", "History");
777 :
778 : // This vector will be filled by the names of all the attributes of the table
779 : // in the order in which they are expected to be found in the binary representation.
780 : //
781 0 : vector<string> attributesSeq;
782 :
783 0 : xmlNode* root_element = xmlDocGetRootElement(doc);
784 0 : if ( root_element == NULL || root_element->type != XML_ELEMENT_NODE )
785 0 : throw ConversionException("Failed to parse the xmlHeader into a DOM structure.", "History");
786 :
787 0 : const ByteOrder* byteOrder=0;
788 0 : if ( string("ASDMBinaryTable").compare((const char*) root_element->name) == 0) {
789 : // Then it's an "old fashioned" MIME file for tables.
790 : // Just try to deserialize it with Big_Endian for the bytes ordering.
791 0 : byteOrder = asdm::ByteOrder::Big_Endian;
792 :
793 : //
794 : // Let's consider a default order for the sequence of attributes.
795 : //
796 :
797 :
798 0 : attributesSeq.push_back("execBlockId") ;
799 :
800 0 : attributesSeq.push_back("time") ;
801 :
802 0 : attributesSeq.push_back("message") ;
803 :
804 0 : attributesSeq.push_back("priority") ;
805 :
806 0 : attributesSeq.push_back("origin") ;
807 :
808 0 : attributesSeq.push_back("objectId") ;
809 :
810 0 : attributesSeq.push_back("application") ;
811 :
812 0 : attributesSeq.push_back("cliCommand") ;
813 :
814 0 : attributesSeq.push_back("appParms") ;
815 :
816 :
817 :
818 :
819 :
820 : // And decide that it has version == "2"
821 0 : version = "2";
822 : }
823 0 : else if (string("HistoryTable").compare((const char*) root_element->name) == 0) {
824 : // It's a new (and correct) MIME file for tables.
825 : //
826 : // 1st ) Look for a BulkStoreRef element with an attribute byteOrder.
827 : //
828 0 : xmlNode* bulkStoreRef = 0;
829 0 : xmlNode* child = root_element->children;
830 :
831 0 : if (xmlHasProp(root_element, (const xmlChar*) "schemaVersion")) {
832 0 : xmlChar * value = xmlGetProp(root_element, (const xmlChar *) "schemaVersion");
833 0 : version = string ((const char *) value);
834 0 : xmlFree(value);
835 : }
836 :
837 : // Skip the two first children (Entity and ContainerEntity).
838 0 : bulkStoreRef = (child == 0) ? 0 : ( (child->next) == 0 ? 0 : child->next->next );
839 :
840 0 : if ( bulkStoreRef == 0 || (bulkStoreRef->type != XML_ELEMENT_NODE) || (string("BulkStoreRef").compare((const char*) bulkStoreRef->name) != 0))
841 0 : throw ConversionException ("Could not find the element '/HistoryTable/BulkStoreRef'. Invalid XML header '"+ xmlHeader + "'.", "History");
842 :
843 : // We found BulkStoreRef, now look for its attribute byteOrder.
844 0 : _xmlAttr* byteOrderAttr = 0;
845 0 : for (struct _xmlAttr* attr = bulkStoreRef->properties; attr; attr = attr->next)
846 0 : if (string("byteOrder").compare((const char*) attr->name) == 0) {
847 0 : byteOrderAttr = attr;
848 0 : break;
849 : }
850 :
851 0 : if (byteOrderAttr == 0)
852 0 : throw ConversionException("Could not find the element '/HistoryTable/BulkStoreRef/@byteOrder'. Invalid XML header '" + xmlHeader +"'.", "History");
853 :
854 0 : string byteOrderValue = string((const char*) byteOrderAttr->children->content);
855 0 : if (!(byteOrder = asdm::ByteOrder::fromString(byteOrderValue)))
856 0 : throw ConversionException("No valid value retrieved for the element '/HistoryTable/BulkStoreRef/@byteOrder'. Invalid XML header '" + xmlHeader + "'.", "History");
857 :
858 : //
859 : // 2nd) Look for the Attributes element and grab the names of the elements it contains.
860 : //
861 0 : xmlNode* attributes = bulkStoreRef->next;
862 0 : if ( attributes == 0 || (attributes->type != XML_ELEMENT_NODE) || (string("Attributes").compare((const char*) attributes->name) != 0))
863 0 : throw ConversionException ("Could not find the element '/HistoryTable/Attributes'. Invalid XML header '"+ xmlHeader + "'.", "History");
864 :
865 0 : xmlNode* childOfAttributes = attributes->children;
866 :
867 0 : while ( childOfAttributes != 0 && (childOfAttributes->type == XML_ELEMENT_NODE) ) {
868 0 : attributesSeq.push_back(string((const char*) childOfAttributes->name));
869 0 : childOfAttributes = childOfAttributes->next;
870 : }
871 0 : }
872 : // Create an EndianISStream from the substring containing the binary part.
873 0 : EndianISStream eiss(mimeMsg.substr(loc1+binPartMIMEHeader.size()), byteOrder);
874 :
875 0 : entity = Entity::fromBin((EndianIStream&) eiss);
876 :
877 : // We do nothing with that but we have to read it.
878 0 : Entity containerEntity = Entity::fromBin((EndianIStream&) eiss);
879 :
880 : // Let's read numRows but ignore it and rely on the value specified in the ASDM.xml file.
881 0 : int numRows = ((EndianIStream&) eiss).readInt();
882 0 : if ((numRows != -1) // Then these are *not* data produced at the EVLA.
883 0 : && ((unsigned int) numRows != this->declaredSize )) { // Then the declared size (in ASDM.xml) is not equal to the one
884 : // written into the binary representation of the table.
885 0 : cout << "The a number of rows ('"
886 : << numRows
887 0 : << "') declared in the binary representation of the table is different from the one declared in ASDM.xml ('"
888 0 : << this->declaredSize
889 0 : << "'). I'll proceed with the value declared in ASDM.xml"
890 0 : << endl;
891 : }
892 :
893 0 : if (getContainer().checkRowUniqueness()) {
894 : try {
895 0 : for (uint32_t i = 0; i < this->declaredSize; i++) {
896 0 : HistoryRow* aRow = HistoryRow::fromBin((EndianIStream&) eiss, *this, attributesSeq);
897 0 : checkAndAdd(aRow);
898 : }
899 : }
900 0 : catch (const DuplicateKey &e) {
901 0 : throw ConversionException("Error while writing binary data , the message was "
902 0 : + e.getMessage(), "History");
903 0 : }
904 0 : catch (const TagFormatException &e) {
905 0 : throw ConversionException("Error while reading binary data , the message was "
906 0 : + e.getMessage(), "History");
907 0 : }
908 : }
909 : else {
910 0 : for (uint32_t i = 0; i < this->declaredSize; i++) {
911 0 : HistoryRow* aRow = HistoryRow::fromBin((EndianIStream&) eiss, *this, attributesSeq);
912 0 : append(aRow);
913 : }
914 : }
915 : //Does not change the convention defined in the model.
916 : //archiveAsBin = true;
917 : //fileAsBin = true;
918 0 : if ( doc != NULL ) xmlFreeDoc(doc);
919 :
920 0 : }
921 :
922 0 : void HistoryTable::setUnknownAttributeBinaryReader(const string& attributeName, BinaryAttributeReaderFunctor* barFctr) {
923 : //
924 : // Is this attribute really unknown ?
925 : //
926 0 : for (vector<string>::const_iterator iter = attributesNamesOfHistory_v.begin(); iter != attributesNamesOfHistory_v.end(); iter++) {
927 0 : if ((*iter).compare(attributeName) == 0)
928 0 : throw ConversionException("the attribute '"+attributeName+"' is known you can't override the way it's read in the MIME binary file containing the table.", "History");
929 : }
930 :
931 : // Ok then register the functor to activate when an unknown attribute is met during the reading of a binary table?
932 0 : unknownAttributes2Functors[attributeName] = barFctr;
933 0 : }
934 :
935 0 : BinaryAttributeReaderFunctor* HistoryTable::getUnknownAttributeBinaryReader(const string& attributeName) const {
936 0 : map<string, BinaryAttributeReaderFunctor*>::const_iterator iter = unknownAttributes2Functors.find(attributeName);
937 0 : return (iter == unknownAttributes2Functors.end()) ? 0 : iter->second;
938 : }
939 :
940 :
941 0 : void HistoryTable::toFile(string directory) {
942 0 : if (!directoryExists(directory.c_str()) &&
943 0 : !createPath(directory.c_str())) {
944 0 : throw ConversionException("Could not create directory " , directory);
945 : }
946 :
947 0 : string fileName = directory + "/History.xml";
948 0 : ofstream tableout(fileName.c_str(),ios::out|ios::trunc);
949 0 : if (tableout.rdstate() == ostream::failbit)
950 0 : throw ConversionException("Could not open file " + fileName + " to write ", "History");
951 0 : if (fileAsBin)
952 0 : tableout << MIMEXMLPart();
953 : else
954 0 : tableout << toXML() << endl;
955 0 : tableout.close();
956 0 : if (tableout.rdstate() == ostream::failbit)
957 0 : throw ConversionException("Could not close file " + fileName, "History");
958 :
959 0 : if (fileAsBin) {
960 : // write the bin serialized
961 0 : string fileName = directory + "/History.bin";
962 0 : ofstream tableout(fileName.c_str(),ios::out|ios::trunc);
963 0 : if (tableout.rdstate() == ostream::failbit)
964 0 : throw ConversionException("Could not open file " + fileName + " to write ", "History");
965 0 : tableout << toMIME() << endl;
966 0 : tableout.close();
967 0 : if (tableout.rdstate() == ostream::failbit)
968 0 : throw ConversionException("Could not close file " + fileName, "History");
969 0 : }
970 0 : }
971 :
972 :
973 0 : void HistoryTable::setFromFile(const string& directory) {
974 : #ifndef WITHOUT_BOOST
975 : if (boost::filesystem::exists(boost::filesystem::path(uniqSlashes(directory + "/History.xml"))))
976 : setFromXMLFile(directory);
977 : else if (boost::filesystem::exists(boost::filesystem::path(uniqSlashes(directory + "/History.bin"))))
978 : setFromMIMEFile(directory);
979 : #else
980 : // alternative in Misc.h
981 0 : if (file_exists(uniqSlashes(directory + "/History.xml")))
982 0 : setFromXMLFile(directory);
983 0 : else if (file_exists(uniqSlashes(directory + "/History.bin")))
984 0 : setFromMIMEFile(directory);
985 : #endif
986 : else
987 0 : throw ConversionException("No file found for the History table", "History");
988 0 : }
989 :
990 :
991 0 : void HistoryTable::setFromMIMEFile(const string& directory) {
992 0 : string tablePath ;
993 :
994 0 : tablePath = directory + "/History.bin";
995 0 : ifstream tablefile(tablePath.c_str(), ios::in|ios::binary);
996 0 : if (!tablefile.is_open()) {
997 0 : throw ConversionException("Could not open file " + tablePath, "History");
998 : }
999 : // Read in a stringstream.
1000 0 : stringstream ss; ss << tablefile.rdbuf();
1001 :
1002 0 : if (tablefile.rdstate() == istream::failbit || tablefile.rdstate() == istream::badbit) {
1003 0 : throw ConversionException("Error reading file " + tablePath,"History");
1004 : }
1005 :
1006 : // And close.
1007 0 : tablefile.close();
1008 0 : if (tablefile.rdstate() == istream::failbit)
1009 0 : throw ConversionException("Could not close file " + tablePath,"History");
1010 :
1011 0 : setFromMIME(ss.str());
1012 0 : }
1013 : /*
1014 : void HistoryTable::openMIMEFile (const string& directory) {
1015 :
1016 : // Open the file.
1017 : string tablePath ;
1018 : tablePath = directory + "/History.bin";
1019 : ifstream tablefile(tablePath.c_str(), ios::in|ios::binary);
1020 : if (!tablefile.is_open())
1021 : throw ConversionException("Could not open file " + tablePath, "History");
1022 :
1023 : // Locate the xmlPartMIMEHeader.
1024 : string xmlPartMIMEHeader = "CONTENT-ID: <HEADER.XML>\n\n";
1025 : CharComparator comparator;
1026 : istreambuf_iterator<char> BEGIN(tablefile.rdbuf());
1027 : istreambuf_iterator<char> END;
1028 : istreambuf_iterator<char> it = search(BEGIN, END, xmlPartMIMEHeader.begin(), xmlPartMIMEHeader.end(), comparator);
1029 : if (it == END)
1030 : throw ConversionException("failed to detect the beginning of the XML header", "History");
1031 :
1032 : // Locate the binaryPartMIMEHeader while accumulating the characters of the xml header.
1033 : string binPartMIMEHeader = "--MIME_BOUNDARY\nCONTENT-TYPE: BINARY/OCTET-STREAM\nCONTENT-ID: <CONTENT.BIN>\n\n";
1034 : string xmlHeader;
1035 : CharCompAccumulator compaccumulator(&xmlHeader, 100000);
1036 : ++it;
1037 : it = search(it, END, binPartMIMEHeader.begin(), binPartMIMEHeader.end(), compaccumulator);
1038 : if (it == END)
1039 : throw ConversionException("failed to detect the beginning of the binary part", "History");
1040 :
1041 : cout << xmlHeader << endl;
1042 : //
1043 : // We have the xmlHeader , let's parse it.
1044 : //
1045 : xmlDoc *doc;
1046 : doc = xmlReadMemory(xmlHeader.data(), xmlHeader.size(), "BinaryTableHeader.xml", NULL, XML_PARSE_NOBLANKS);
1047 : if ( doc == NULL )
1048 : throw ConversionException("Failed to parse the xmlHeader into a DOM structure.", "History");
1049 :
1050 : // This vector will be filled by the names of all the attributes of the table
1051 : // in the order in which they are expected to be found in the binary representation.
1052 : //
1053 : vector<string> attributesSeq(attributesNamesInBinOfHistory_v);
1054 :
1055 : xmlNode* root_element = xmlDocGetRootElement(doc);
1056 : if ( root_element == NULL || root_element->type != XML_ELEMENT_NODE )
1057 : throw ConversionException("Failed to parse the xmlHeader into a DOM structure.", "History");
1058 :
1059 : const ByteOrder* byteOrder=0;
1060 : if ( string("ASDMBinaryTable").compare((const char*) root_element->name) == 0) {
1061 : // Then it's an "old fashioned" MIME file for tables.
1062 : // Just try to deserialize it with Big_Endian for the bytes ordering.
1063 : byteOrder = asdm::ByteOrder::Big_Endian;
1064 :
1065 : // And decide that it has version == "2"
1066 : version = "2";
1067 : }
1068 : else if (string("HistoryTable").compare((const char*) root_element->name) == 0) {
1069 : // It's a new (and correct) MIME file for tables.
1070 : //
1071 : // 1st ) Look for a BulkStoreRef element with an attribute byteOrder.
1072 : //
1073 : xmlNode* bulkStoreRef = 0;
1074 : xmlNode* child = root_element->children;
1075 :
1076 : if (xmlHasProp(root_element, (const xmlChar*) "schemaVersion")) {
1077 : xmlChar * value = xmlGetProp(root_element, (const xmlChar *) "schemaVersion");
1078 : version = string ((const char *) value);
1079 : xmlFree(value);
1080 : }
1081 :
1082 : // Skip the two first children (Entity and ContainerEntity).
1083 : bulkStoreRef = (child == 0) ? 0 : ( (child->next) == 0 ? 0 : child->next->next );
1084 :
1085 : if ( bulkStoreRef == 0 || (bulkStoreRef->type != XML_ELEMENT_NODE) || (string("BulkStoreRef").compare((const char*) bulkStoreRef->name) != 0))
1086 : throw ConversionException ("Could not find the element '/HistoryTable/BulkStoreRef'. Invalid XML header '"+ xmlHeader + "'.", "History");
1087 :
1088 : // We found BulkStoreRef, now look for its attribute byteOrder.
1089 : _xmlAttr* byteOrderAttr = 0;
1090 : for (struct _xmlAttr* attr = bulkStoreRef->properties; attr; attr = attr->next)
1091 : if (string("byteOrder").compare((const char*) attr->name) == 0) {
1092 : byteOrderAttr = attr;
1093 : break;
1094 : }
1095 :
1096 : if (byteOrderAttr == 0)
1097 : throw ConversionException("Could not find the element '/HistoryTable/BulkStoreRef/@byteOrder'. Invalid XML header '" + xmlHeader +"'.", "History");
1098 :
1099 : string byteOrderValue = string((const char*) byteOrderAttr->children->content);
1100 : if (!(byteOrder = asdm::ByteOrder::fromString(byteOrderValue)))
1101 : throw ConversionException("No valid value retrieved for the element '/HistoryTable/BulkStoreRef/@byteOrder'. Invalid XML header '" + xmlHeader + "'.", "History");
1102 :
1103 : //
1104 : // 2nd) Look for the Attributes element and grab the names of the elements it contains.
1105 : //
1106 : xmlNode* attributes = bulkStoreRef->next;
1107 : if ( attributes == 0 || (attributes->type != XML_ELEMENT_NODE) || (string("Attributes").compare((const char*) attributes->name) != 0))
1108 : throw ConversionException ("Could not find the element '/HistoryTable/Attributes'. Invalid XML header '"+ xmlHeader + "'.", "History");
1109 :
1110 : xmlNode* childOfAttributes = attributes->children;
1111 :
1112 : while ( childOfAttributes != 0 && (childOfAttributes->type == XML_ELEMENT_NODE) ) {
1113 : attributesSeq.push_back(string((const char*) childOfAttributes->name));
1114 : childOfAttributes = childOfAttributes->next;
1115 : }
1116 : }
1117 : // Create an EndianISStream from the substring containing the binary part.
1118 : EndianIFStream eifs(&tablefile, byteOrder);
1119 :
1120 : entity = Entity::fromBin((EndianIStream &) eifs);
1121 :
1122 : // We do nothing with that but we have to read it.
1123 : Entity containerEntity = Entity::fromBin((EndianIStream &) eifs);
1124 :
1125 : // Let's read numRows but ignore it and rely on the value specified in the ASDM.xml file.
1126 : int numRows = eifs.readInt();
1127 : if ((numRows != -1) // Then these are *not* data produced at the EVLA.
1128 : && ((unsigned int) numRows != this->declaredSize )) { // Then the declared size (in ASDM.xml) is not equal to the one
1129 : // written into the binary representation of the table.
1130 : cout << "The a number of rows ('"
1131 : << numRows
1132 : << "') declared in the binary representation of the table is different from the one declared in ASDM.xml ('"
1133 : << this->declaredSize
1134 : << "'). I'll proceed with the value declared in ASDM.xml"
1135 : << endl;
1136 : }
1137 : // clean up xmlDoc pointer
1138 : if ( doc != NULL ) xmlFreeDoc(doc);
1139 : }
1140 : */
1141 :
1142 :
1143 0 : void HistoryTable::setFromXMLFile(const string& directory) {
1144 0 : string tablePath ;
1145 :
1146 0 : tablePath = directory + "/History.xml";
1147 :
1148 : /*
1149 : ifstream tablefile(tablePath.c_str(), ios::in|ios::binary);
1150 : if (!tablefile.is_open()) {
1151 : throw ConversionException("Could not open file " + tablePath, "History");
1152 : }
1153 : // Read in a stringstream.
1154 : stringstream ss;
1155 : ss << tablefile.rdbuf();
1156 :
1157 : if (tablefile.rdstate() == istream::failbit || tablefile.rdstate() == istream::badbit) {
1158 : throw ConversionException("Error reading file '" + tablePath + "'", "History");
1159 : }
1160 :
1161 : // And close
1162 : tablefile.close();
1163 : if (tablefile.rdstate() == istream::failbit)
1164 : throw ConversionException("Could not close file '" + tablePath + "'", "History");
1165 :
1166 : // Let's make a string out of the stringstream content and empty the stringstream.
1167 : string xmlDocument = ss.str(); ss.str("");
1168 :
1169 : // Let's make a very primitive check to decide
1170 : // whether the XML content represents the table
1171 : // or refers to it via a <BulkStoreRef element.
1172 : */
1173 :
1174 0 : string xmlDocument;
1175 : try {
1176 0 : xmlDocument = getContainer().getXSLTransformer()(tablePath);
1177 0 : if (getenv("ASDM_DEBUG")) cout << "About to read " << tablePath << endl;
1178 : }
1179 0 : catch (const XSLTransformerException &e) {
1180 0 : throw ConversionException("Caugth an exception whose message is '" + e.getMessage() + "'.", "History");
1181 0 : }
1182 :
1183 0 : if (xmlDocument.find("<BulkStoreRef") != string::npos)
1184 0 : setFromMIMEFile(directory);
1185 : else
1186 0 : fromXML(xmlDocument);
1187 0 : }
1188 :
1189 :
1190 :
1191 :
1192 :
1193 :
1194 :
1195 :
1196 :
1197 0 : HistoryRow * HistoryTable::insertByTime(HistoryRow* x, vector<HistoryRow *>&row ) {
1198 0 : ArrayTime start = x->getTime();
1199 :
1200 : // Is the vector empty ?
1201 0 : if (row.size() == 0) {
1202 0 : row.push_back(x);
1203 0 : privateRows.push_back(x);
1204 0 : x->isAdded(true);
1205 0 : return x;
1206 : }
1207 :
1208 : // Optimization for the case of insertion by ascending time.
1209 0 : HistoryRow* last = row.at(row.size()-1);
1210 :
1211 0 : if (start.get() > last->getTime().get()) {
1212 0 : row.push_back(x);
1213 0 : privateRows.push_back(x);
1214 0 : x->isAdded(true);
1215 0 : return x;
1216 : }
1217 :
1218 : // Optimization for the case of insertion by descending time.
1219 0 : HistoryRow* first = row.at(0);
1220 :
1221 0 : if (start.get() < first->getTime().get()) {
1222 0 : row.insert(row.begin(), x);
1223 0 : privateRows.push_back(x);
1224 0 : x->isAdded(true);
1225 0 : return x;
1226 : }
1227 :
1228 : // Case where x has to be inserted inside row; let's use a dichotomy
1229 : // method to find the insertion index.
1230 0 : int k0 = 0;
1231 0 : int k1 = row.size() - 1;
1232 0 : while (k0 != (k1 - 1)) {
1233 0 : if (start.get() == row.at(k0)->getTime().get()) {
1234 0 : if (row.at(k0)->equalByRequiredValue(x))
1235 0 : return row.at(k0);
1236 : else
1237 0 : throw DuplicateKey("DuplicateKey exception in ", "HistoryTable");
1238 : }
1239 0 : else if (start.get() == row.at(k1)->getTime().get()) {
1240 0 : if (row.at(k1)->equalByRequiredValue(x))
1241 0 : return row.at(k1);
1242 : else
1243 0 : throw DuplicateKey("DuplicateKey exception in ", "HistoryTable");
1244 : }
1245 : else {
1246 0 : if (start.get() <= row.at((k0+k1)/2)->getTime().get())
1247 0 : k1 = (k0 + k1) / 2;
1248 : else
1249 0 : k0 = (k0 + k1) / 2;
1250 : }
1251 : }
1252 :
1253 0 : if (start.get() == row.at(k0)->getTime().get()) {
1254 0 : if (row.at(k0)->equalByRequiredValue(x))
1255 0 : return row.at(k0);
1256 : else
1257 0 : throw DuplicateKey("DuplicateKey exception in ", "HistoryTable");
1258 : }
1259 0 : else if (start.get() == row.at(k1)->getTime().get()) {
1260 0 : if (row.at(k1)->equalByRequiredValue(x))
1261 0 : return row.at(k1);
1262 : else
1263 0 : throw DuplicateKey("DuplicateKey exception in ", "HistoryTable");
1264 : }
1265 :
1266 0 : row.insert(row.begin()+(k0+1), x);
1267 0 : privateRows.push_back(x);
1268 0 : x->isAdded(true);
1269 0 : return x;
1270 0 : }
1271 :
1272 :
1273 :
1274 :
1275 :
1276 : } // End namespace asdm
1277 :
|