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 StateTable.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/StateTable.h>
43 : #include <alma/ASDM/StateRow.h>
44 : #include <alma/ASDM/Parser.h>
45 :
46 : using asdm::ASDM;
47 : using asdm::StateTable;
48 : using asdm::StateRow;
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 entityNameOfState = "State";
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 attributesNamesOfState_a[] = {
80 :
81 : "stateId"
82 :
83 :
84 : , "calDeviceName"
85 :
86 : , "sig"
87 :
88 : , "ref"
89 :
90 : , "onSky"
91 :
92 :
93 : , "weight"
94 :
95 : };
96 :
97 : // A vector of string whose content is a copy of the strings in the array above.
98 : //
99 : static vector<string> attributesNamesOfState_v (attributesNamesOfState_a, attributesNamesOfState_a + sizeof(attributesNamesOfState_a) / sizeof(attributesNamesOfState_a[0]));
100 :
101 : // An array of string containing the names of the columns of this table.
102 : // The array is filled in the order where the names would be read by default in the XML header of a file containing
103 : // the table exported in binary mode.
104 : //
105 : static string attributesNamesInBinOfState_a[] = {
106 :
107 : "stateId" , "calDeviceName" , "sig" , "ref" , "onSky"
108 : ,
109 : "weight"
110 :
111 : };
112 :
113 : // A vector of string whose content is a copy of the strings in the array above.
114 : //
115 : static vector<string> attributesNamesInBinOfState_v(attributesNamesInBinOfState_a, attributesNamesInBinOfState_a + sizeof(attributesNamesInBinOfState_a) / sizeof(attributesNamesInBinOfState_a[0]));
116 :
117 :
118 : // The array of attributes (or column) names that make up key key.
119 : //
120 : string keyOfState_a[] = {
121 :
122 : "stateId"
123 :
124 : };
125 :
126 : // A vector of strings which are copies of those stored in the array above.
127 : vector<string> keyOfState_v(keyOfState_a, keyOfState_a + sizeof(keyOfState_a) / sizeof(keyOfState_a[0]));
128 :
129 : /**
130 : * Return the list of field names that make up key key
131 : * as a const reference to a vector of strings.
132 : */
133 0 : const vector<string>& StateTable::getKeyName() {
134 0 : return keyOfState_v;
135 : }
136 :
137 :
138 118 : StateTable::StateTable(ASDM &c) : container(c) {
139 :
140 : // Define a default entity.
141 118 : entity.setEntityId(EntityId("uid://X0/X0/X0"));
142 118 : entity.setEntityIdEncrypted("na");
143 118 : entity.setEntityTypeName("StateTable");
144 118 : entity.setEntityVersion("1");
145 118 : entity.setInstanceVersion("1");
146 :
147 : // Archive XML
148 118 : archiveAsBin = false;
149 :
150 : // File XML
151 118 : fileAsBin = false;
152 :
153 : // By default the table is considered as present in memory
154 118 : presentInMemory = true;
155 :
156 : // By default there is no load in progress
157 118 : loadInProgress = false;
158 118 : }
159 :
160 : /**
161 : * A destructor for StateTable.
162 : */
163 236 : StateTable::~StateTable() {
164 289 : for (unsigned int i = 0; i < privateRows.size(); i++)
165 171 : delete(privateRows.at(i));
166 236 : }
167 :
168 : /**
169 : * Container to which this table belongs.
170 : */
171 234 : ASDM &StateTable::getContainer() const {
172 234 : return container;
173 : }
174 :
175 : /**
176 : * Return the number of rows in the table.
177 : */
178 164 : unsigned int StateTable::size() const {
179 164 : if (presentInMemory)
180 164 : return privateRows.size();
181 : else
182 0 : return declaredSize;
183 : }
184 :
185 : /**
186 : * Return the name of this table.
187 : */
188 797 : string StateTable::getName() const {
189 797 : return entityNameOfState;
190 : }
191 :
192 : /**
193 : * Return the name of this table.
194 : */
195 0 : string StateTable::name() {
196 0 : return entityNameOfState;
197 : }
198 :
199 : /**
200 : * Return the the names of the attributes (or columns) of this table.
201 : */
202 0 : const vector<string>& StateTable::getAttributesNames() { return attributesNamesOfState_v; }
203 :
204 : /**
205 : * Return the the names of the attributes (or columns) of this table as they appear by default
206 : * in an binary export of this table.
207 : */
208 0 : const vector<string>& StateTable::defaultAttributesNamesInBin() { return attributesNamesInBinOfState_v; }
209 :
210 : /**
211 : * Return this table's Entity.
212 : */
213 40 : Entity StateTable::getEntity() const {
214 40 : return entity;
215 : }
216 :
217 : /**
218 : * Set this table's Entity.
219 : */
220 92 : void StateTable::setEntity(Entity e) {
221 92 : this->entity = e;
222 92 : }
223 :
224 : //
225 : // ====> Row creation.
226 : //
227 :
228 : /**
229 : * Create a new row.
230 : */
231 154 : StateRow *StateTable::newRow() {
232 154 : return new StateRow (*this);
233 : }
234 :
235 :
236 : /**
237 : * Create a new row initialized to the specified values.
238 : * @return a pointer on the created and initialized row.
239 :
240 : * @param calDeviceName
241 :
242 : * @param sig
243 :
244 : * @param ref
245 :
246 : * @param onSky
247 :
248 : */
249 23 : StateRow* StateTable::newRow(CalibrationDeviceMod::CalibrationDevice calDeviceName, bool sig, bool ref, bool onSky){
250 23 : StateRow *row = new StateRow(*this);
251 :
252 23 : row->setCalDeviceName(calDeviceName);
253 :
254 23 : row->setSig(sig);
255 :
256 23 : row->setRef(ref);
257 :
258 23 : row->setOnSky(onSky);
259 :
260 23 : return row;
261 : }
262 :
263 :
264 :
265 0 : StateRow* StateTable::newRow(StateRow* row) {
266 0 : return new StateRow(*this, row);
267 : }
268 :
269 : //
270 : // Append a row to its table.
271 : //
272 :
273 :
274 :
275 :
276 : /**
277 : * Look up the table for a row whose noautoincrementable attributes are matching their
278 : * homologues in *x. If a row is found this row else autoincrement *x.stateId,
279 : * add x to its table and returns x.
280 : *
281 : * @returns a pointer on a StateRow.
282 : * @param x. A pointer on the row to be added.
283 : */
284 :
285 :
286 23 : StateRow* StateTable::add(StateRow* x) {
287 :
288 23 : StateRow* aRow = lookup(
289 :
290 : x->getCalDeviceName()
291 : ,
292 23 : x->getSig()
293 : ,
294 23 : x->getRef()
295 : ,
296 23 : x->getOnSky()
297 :
298 : );
299 23 : if (aRow) return aRow;
300 :
301 :
302 :
303 : // Autoincrement stateId
304 17 : x->setStateId(Tag(size(), TagType::State));
305 :
306 17 : row.push_back(x);
307 17 : privateRows.push_back(x);
308 17 : x->isAdded(true);
309 17 : return x;
310 : }
311 :
312 :
313 :
314 154 : void StateTable::addWithoutCheckingUnique(StateRow * x) {
315 154 : if (getRowByKey(
316 308 : x->getStateId()
317 154 : ) != (StateRow *) 0)
318 0 : throw DuplicateKey("Dupicate key exception in ", "StateTable");
319 154 : row.push_back(x);
320 154 : privateRows.push_back(x);
321 154 : x->isAdded(true);
322 154 : }
323 :
324 :
325 :
326 :
327 : //
328 : // A private method to append a row to its table, used by input conversion
329 : // methods, with row uniqueness.
330 : //
331 :
332 :
333 : /**
334 : * If this table has an autoincrementable attribute then check if *x verifies the rule of uniqueness and throw exception if not.
335 : * Check if *x verifies the key uniqueness rule and throw an exception if not.
336 : * Append x to its table.
337 : * @param x a pointer on the row to be appended.
338 : * @returns a pointer on x.
339 : * @throws DuplicateKey
340 :
341 : * @throws UniquenessViolationException
342 :
343 : */
344 0 : StateRow* StateTable::checkAndAdd(StateRow* x, bool skipCheckUniqueness) {
345 0 : if (!skipCheckUniqueness) {
346 :
347 :
348 0 : if (lookup(
349 :
350 : x->getCalDeviceName()
351 : ,
352 0 : x->getSig()
353 : ,
354 0 : x->getRef()
355 : ,
356 0 : x->getOnSky()
357 :
358 0 : )) throw UniquenessViolationException();
359 :
360 :
361 : }
362 :
363 0 : if (getRowByKey(
364 :
365 0 : x->getStateId()
366 :
367 0 : )) throw DuplicateKey("Duplicate key exception in ", "StateTable");
368 :
369 0 : row.push_back(x);
370 0 : privateRows.push_back(x);
371 0 : x->isAdded(true);
372 0 : return x;
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 StateTable::append(StateRow *x) {
382 0 : privateRows.push_back(x);
383 0 : x->isAdded(true);
384 0 : }
385 :
386 :
387 :
388 :
389 :
390 1855 : vector<StateRow *> StateTable::get() {
391 1855 : checkPresenceInMemory();
392 1855 : return privateRows;
393 : }
394 :
395 0 : const vector<StateRow *>& StateTable::get() const {
396 0 : const_cast<StateTable&>(*this).checkPresenceInMemory();
397 0 : return privateRows;
398 : }
399 :
400 :
401 :
402 :
403 :
404 :
405 :
406 :
407 : /*
408 : ** Returns a StateRow* given a key.
409 : ** @return a pointer to the row having the key whose values are passed as parameters, or 0 if
410 : ** no row exists for that key.
411 : **
412 : */
413 11604 : StateRow* StateTable::getRowByKey(Tag stateId) {
414 11604 : checkPresenceInMemory();
415 11604 : StateRow* aRow = 0;
416 16082 : for (unsigned int i = 0; i < privateRows.size(); i++) {
417 15928 : aRow = row.at(i);
418 :
419 :
420 15928 : if (aRow->stateId != stateId) continue;
421 :
422 :
423 11450 : return aRow;
424 : }
425 154 : return 0;
426 : }
427 :
428 :
429 :
430 : /**
431 : * Look up the table for a row whose all attributes except the autoincrementable one
432 : * are equal to the corresponding parameters of the method.
433 : * @return a pointer on this row if any, 0 otherwise.
434 : *
435 :
436 : * @param calDeviceName.
437 :
438 : * @param sig.
439 :
440 : * @param ref.
441 :
442 : * @param onSky.
443 :
444 : */
445 23 : StateRow* StateTable::lookup(CalibrationDeviceMod::CalibrationDevice calDeviceName, bool sig, bool ref, bool onSky) {
446 : StateRow* aRow;
447 29 : for (unsigned int i = 0; i < privateRows.size(); i++) {
448 12 : aRow = privateRows.at(i);
449 12 : if (aRow->compareNoAutoInc(calDeviceName, sig, ref, onSky)) return aRow;
450 : }
451 17 : return 0;
452 : }
453 :
454 :
455 :
456 :
457 :
458 :
459 :
460 : #ifndef WITHOUT_ACS
461 : using asdmIDL::StateTableIDL;
462 : #endif
463 :
464 : #ifndef WITHOUT_ACS
465 : // Conversion Methods
466 :
467 : StateTableIDL *StateTable::toIDL() {
468 : StateTableIDL *x = new StateTableIDL ();
469 : unsigned int nrow = size();
470 : x->row.length(nrow);
471 : vector<StateRow*> v = get();
472 : for (unsigned int i = 0; i < nrow; ++i) {
473 : //x->row[i] = *(v[i]->toIDL());
474 : v[i]->toIDL(x->row[i]);
475 : }
476 : return x;
477 : }
478 :
479 : void StateTable::toIDL(asdmIDL::StateTableIDL& x) const {
480 : unsigned int nrow = size();
481 : x.row.length(nrow);
482 : vector<StateRow*> v = get();
483 : for (unsigned int i = 0; i < nrow; ++i) {
484 : v[i]->toIDL(x.row[i]);
485 : }
486 : }
487 : #endif
488 :
489 : #ifndef WITHOUT_ACS
490 : void StateTable::fromIDL(StateTableIDL x) {
491 : unsigned int nrow = x.row.length();
492 : for (unsigned int i = 0; i < nrow; ++i) {
493 : StateRow *tmp = newRow();
494 : tmp->setFromIDL(x.row[i]);
495 : // checkAndAdd(tmp);
496 : add(tmp);
497 : }
498 : }
499 : #endif
500 :
501 :
502 13 : string StateTable::toXML() {
503 13 : string buf;
504 :
505 13 : buf.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?> ");
506 13 : buf.append("<StateTable xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:state=\"http://Alma/XASDM/StateTable\" xsi:schemaLocation=\"http://Alma/XASDM/StateTable http://almaobservatory.org/XML/XASDM/4/StateTable.xsd\" schemaVersion=\"4\" schemaRevision=\"-1\">\n");
507 :
508 13 : buf.append(entity.toXML());
509 13 : string s = container.getEntity().toXML();
510 : // Change the "Entity" tag to "ContainerEntity".
511 13 : buf.append("<Container" + s.substr(1,s.length() - 1)+" ");
512 13 : vector<StateRow*> v = get();
513 29 : for (unsigned int i = 0; i < v.size(); ++i) {
514 : try {
515 16 : buf.append(v[i]->toXML());
516 0 : } catch (const NoSuchRow &e) {
517 0 : }
518 16 : buf.append(" ");
519 : }
520 13 : buf.append("</StateTable> ");
521 26 : return buf;
522 13 : }
523 :
524 :
525 0 : string StateTable::getVersion() const {
526 0 : return version;
527 : }
528 :
529 :
530 78 : void StateTable::fromXML(string& tableInXML) {
531 : //
532 : // Look for a version information in the schemaVersion of the XML
533 : //
534 : xmlDoc *doc;
535 : #if LIBXML_VERSION >= 20703
536 78 : doc = xmlReadMemory(tableInXML.data(), tableInXML.size(), "XMLTableHeader.xml", NULL, XML_PARSE_NOBLANKS|XML_PARSE_HUGE);
537 : #else
538 : doc = xmlReadMemory(tableInXML.data(), tableInXML.size(), "XMLTableHeader.xml", NULL, XML_PARSE_NOBLANKS);
539 : #endif
540 78 : if ( doc == NULL )
541 0 : throw ConversionException("Failed to parse the xmlHeader into a DOM structure.", "State");
542 :
543 78 : xmlNode* root_element = xmlDocGetRootElement(doc);
544 78 : if ( root_element == NULL || root_element->type != XML_ELEMENT_NODE )
545 0 : throw ConversionException("Failed to retrieve the root element in the DOM structure.", "State");
546 :
547 78 : xmlChar * propValue = xmlGetProp(root_element, (const xmlChar *) "schemaVersion");
548 78 : if ( propValue != 0 ) {
549 51 : version = string( (const char*) propValue);
550 51 : xmlFree(propValue);
551 : }
552 :
553 78 : Parser xml(tableInXML);
554 78 : if (!xml.isStr("<StateTable"))
555 0 : error();
556 : // cout << "Parsing a StateTable" << endl;
557 156 : string s = xml.getElement("<Entity","/>");
558 78 : if (s.length() == 0)
559 0 : error();
560 78 : Entity e;
561 78 : e.setFromXML(s);
562 78 : if (e.getEntityTypeName() != "StateTable")
563 0 : error();
564 78 : setEntity(e);
565 : // Skip the container's entity; but, it has to be there.
566 78 : s = xml.getElement("<ContainerEntity","/>");
567 78 : if (s.length() == 0)
568 0 : error();
569 :
570 : // Get each row in the table.
571 78 : s = xml.getElementContent("<row>","</row>");
572 : StateRow *row;
573 78 : if (getContainer().checkRowUniqueness()) {
574 : try {
575 0 : while (s.length() != 0) {
576 0 : row = newRow();
577 0 : row->setFromXML(s);
578 0 : checkAndAdd(row);
579 0 : s = xml.getElementContent("<row>","</row>");
580 : }
581 :
582 : }
583 0 : catch (const DuplicateKey &e1) {
584 0 : throw ConversionException(e1.getMessage(),"StateTable");
585 0 : }
586 0 : catch (const UniquenessViolationException &e1) {
587 0 : throw ConversionException(e1.getMessage(),"StateTable");
588 0 : }
589 0 : catch (...) {
590 : // cout << "Unexpected error in StateTable::checkAndAdd called from StateTable::fromXML " << endl;
591 0 : }
592 : }
593 : else {
594 : try {
595 232 : while (s.length() != 0) {
596 154 : row = newRow();
597 154 : row->setFromXML(s);
598 154 : addWithoutCheckingUnique(row);
599 154 : s = xml.getElementContent("<row>","</row>");
600 : }
601 : }
602 0 : catch (const DuplicateKey &e1) {
603 0 : throw ConversionException(e1.getMessage(),"StateTable");
604 0 : }
605 0 : catch (...) {
606 : // cout << "Unexpected error in StateTable::addWithoutCheckingUnique called from StateTable::fromXML " << endl;
607 0 : }
608 : }
609 :
610 :
611 78 : if (!xml.isStr("</StateTable>"))
612 0 : error();
613 :
614 : //Does not change the convention defined in the model.
615 : //archiveAsBin = false;
616 : //fileAsBin = false;
617 :
618 : // clean up the xmlDoc pointer
619 78 : if ( doc != NULL ) xmlFreeDoc(doc);
620 :
621 78 : }
622 :
623 :
624 0 : void StateTable::error() {
625 0 : throw ConversionException("Invalid xml document","State");
626 : }
627 :
628 :
629 0 : string StateTable::MIMEXMLPart(const asdm::ByteOrder* byteOrder) {
630 0 : string UID = getEntity().getEntityId().toString();
631 0 : string withoutUID = UID.substr(6);
632 0 : string containerUID = getContainer().getEntity().getEntityId().toString();
633 0 : ostringstream oss;
634 0 : oss << "<?xml version='1.0' encoding='ISO-8859-1'?>";
635 0 : oss << "\n";
636 0 : oss << "<StateTable xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:state=\"http://Alma/XASDM/StateTable\" xsi:schemaLocation=\"http://Alma/XASDM/StateTable http://almaobservatory.org/XML/XASDM/4/StateTable.xsd\" schemaVersion=\"4\" schemaRevision=\"-1\">\n";
637 0 : oss<< "<Entity entityId='"<<UID<<"' entityIdEncrypted='na' entityTypeName='StateTable' schemaVersion='1' documentVersion='1'/>\n";
638 0 : oss<< "<ContainerEntity entityId='"<<containerUID<<"' entityIdEncrypted='na' entityTypeName='ASDM' schemaVersion='1' documentVersion='1'/>\n";
639 0 : oss << "<BulkStoreRef file_id='"<<withoutUID<<"' byteOrder='"<<byteOrder->toString()<<"' />\n";
640 0 : oss << "<Attributes>\n";
641 :
642 0 : oss << "<stateId/>\n";
643 0 : oss << "<calDeviceName/>\n";
644 0 : oss << "<sig/>\n";
645 0 : oss << "<ref/>\n";
646 0 : oss << "<onSky/>\n";
647 :
648 0 : oss << "<weight/>\n";
649 0 : oss << "</Attributes>\n";
650 0 : oss << "</StateTable>\n";
651 :
652 0 : return oss.str();
653 0 : }
654 :
655 0 : string StateTable::toMIME(const asdm::ByteOrder* byteOrder) {
656 0 : EndianOSStream eoss(byteOrder);
657 :
658 0 : string UID = getEntity().getEntityId().toString();
659 :
660 : // The MIME Header
661 0 : eoss <<"MIME-Version: 1.0";
662 0 : eoss << "\n";
663 0 : eoss << "Content-Type: Multipart/Related; boundary='MIME_boundary'; type='text/xml'; start= '<header.xml>'";
664 0 : eoss <<"\n";
665 0 : eoss <<"Content-Description: Correlator";
666 0 : eoss <<"\n";
667 0 : eoss <<"alma-uid:" << UID;
668 0 : eoss <<"\n";
669 0 : eoss <<"\n";
670 :
671 : // The MIME XML part header.
672 0 : eoss <<"--MIME_boundary";
673 0 : eoss <<"\n";
674 0 : eoss <<"Content-Type: text/xml; charset='ISO-8859-1'";
675 0 : eoss <<"\n";
676 0 : eoss <<"Content-Transfer-Encoding: 8bit";
677 0 : eoss <<"\n";
678 0 : eoss <<"Content-ID: <header.xml>";
679 0 : eoss <<"\n";
680 0 : eoss <<"\n";
681 :
682 : // The MIME XML part content.
683 0 : eoss << MIMEXMLPart(byteOrder);
684 :
685 : // The MIME binary part header
686 0 : eoss <<"--MIME_boundary";
687 0 : eoss <<"\n";
688 0 : eoss <<"Content-Type: binary/octet-stream";
689 0 : eoss <<"\n";
690 0 : eoss <<"Content-ID: <content.bin>";
691 0 : eoss <<"\n";
692 0 : eoss <<"\n";
693 :
694 : // The MIME binary content
695 0 : entity.toBin(eoss);
696 0 : container.getEntity().toBin(eoss);
697 0 : eoss.writeInt((int) privateRows.size());
698 0 : for (unsigned int i = 0; i < privateRows.size(); i++) {
699 0 : privateRows.at(i)->toBin(eoss);
700 : }
701 :
702 : // The closing MIME boundary
703 0 : eoss << "\n--MIME_boundary--";
704 0 : eoss << "\n";
705 :
706 0 : return eoss.str();
707 0 : }
708 :
709 :
710 0 : void StateTable::setFromMIME(const string & mimeMsg) {
711 0 : string xmlPartMIMEHeader = "Content-ID: <header.xml>\n\n";
712 :
713 0 : string binPartMIMEHeader = "--MIME_boundary\nContent-Type: binary/octet-stream\nContent-ID: <content.bin>\n\n";
714 :
715 : // Detect the XML header.
716 0 : string::size_type loc0 = mimeMsg.find(xmlPartMIMEHeader, 0);
717 0 : if ( loc0 == string::npos) {
718 : // let's try with CRLFs
719 0 : xmlPartMIMEHeader = "Content-ID: <header.xml>\r\n\r\n";
720 0 : loc0 = mimeMsg.find(xmlPartMIMEHeader, 0);
721 0 : if ( loc0 == string::npos )
722 0 : throw ConversionException("Failed to detect the beginning of the XML header", "State");
723 : }
724 :
725 0 : loc0 += xmlPartMIMEHeader.size();
726 :
727 : // Look for the string announcing the binary part.
728 0 : string::size_type loc1 = mimeMsg.find( binPartMIMEHeader, loc0 );
729 :
730 0 : if ( loc1 == string::npos ) {
731 0 : throw ConversionException("Failed to detect the beginning of the binary part", "State");
732 : }
733 :
734 : //
735 : // Extract the xmlHeader and analyze it to find out what is the byte order and the sequence
736 : // of attribute names.
737 : //
738 0 : string xmlHeader = mimeMsg.substr(loc0, loc1-loc0);
739 : xmlDoc *doc;
740 0 : doc = xmlReadMemory(xmlHeader.data(), xmlHeader.size(), "BinaryTableHeader.xml", NULL, XML_PARSE_NOBLANKS);
741 0 : if ( doc == NULL )
742 0 : throw ConversionException("Failed to parse the xmlHeader into a DOM structure.", "State");
743 :
744 : // This vector will be filled by the names of all the attributes of the table
745 : // in the order in which they are expected to be found in the binary representation.
746 : //
747 0 : vector<string> attributesSeq;
748 :
749 0 : xmlNode* root_element = xmlDocGetRootElement(doc);
750 0 : if ( root_element == NULL || root_element->type != XML_ELEMENT_NODE )
751 0 : throw ConversionException("Failed to parse the xmlHeader into a DOM structure.", "State");
752 :
753 0 : const ByteOrder* byteOrder=0;
754 0 : if ( string("ASDMBinaryTable").compare((const char*) root_element->name) == 0) {
755 : // Then it's an "old fashioned" MIME file for tables.
756 : // Just try to deserialize it with Big_Endian for the bytes ordering.
757 0 : byteOrder = asdm::ByteOrder::Big_Endian;
758 :
759 : //
760 : // Let's consider a default order for the sequence of attributes.
761 : //
762 :
763 :
764 0 : attributesSeq.push_back("stateId") ;
765 :
766 0 : attributesSeq.push_back("calDeviceName") ;
767 :
768 0 : attributesSeq.push_back("sig") ;
769 :
770 0 : attributesSeq.push_back("ref") ;
771 :
772 0 : attributesSeq.push_back("onSky") ;
773 :
774 :
775 0 : attributesSeq.push_back("weight") ;
776 :
777 :
778 :
779 :
780 : // And decide that it has version == "2"
781 0 : version = "2";
782 : }
783 0 : else if (string("StateTable").compare((const char*) root_element->name) == 0) {
784 : // It's a new (and correct) MIME file for tables.
785 : //
786 : // 1st ) Look for a BulkStoreRef element with an attribute byteOrder.
787 : //
788 0 : xmlNode* bulkStoreRef = 0;
789 0 : xmlNode* child = root_element->children;
790 :
791 0 : if (xmlHasProp(root_element, (const xmlChar*) "schemaVersion")) {
792 0 : xmlChar * value = xmlGetProp(root_element, (const xmlChar *) "schemaVersion");
793 0 : version = string ((const char *) value);
794 0 : xmlFree(value);
795 : }
796 :
797 : // Skip the two first children (Entity and ContainerEntity).
798 0 : bulkStoreRef = (child == 0) ? 0 : ( (child->next) == 0 ? 0 : child->next->next );
799 :
800 0 : if ( bulkStoreRef == 0 || (bulkStoreRef->type != XML_ELEMENT_NODE) || (string("BulkStoreRef").compare((const char*) bulkStoreRef->name) != 0))
801 0 : throw ConversionException ("Could not find the element '/StateTable/BulkStoreRef'. Invalid XML header '"+ xmlHeader + "'.", "State");
802 :
803 : // We found BulkStoreRef, now look for its attribute byteOrder.
804 0 : _xmlAttr* byteOrderAttr = 0;
805 0 : for (struct _xmlAttr* attr = bulkStoreRef->properties; attr; attr = attr->next)
806 0 : if (string("byteOrder").compare((const char*) attr->name) == 0) {
807 0 : byteOrderAttr = attr;
808 0 : break;
809 : }
810 :
811 0 : if (byteOrderAttr == 0)
812 0 : throw ConversionException("Could not find the element '/StateTable/BulkStoreRef/@byteOrder'. Invalid XML header '" + xmlHeader +"'.", "State");
813 :
814 0 : string byteOrderValue = string((const char*) byteOrderAttr->children->content);
815 0 : if (!(byteOrder = asdm::ByteOrder::fromString(byteOrderValue)))
816 0 : throw ConversionException("No valid value retrieved for the element '/StateTable/BulkStoreRef/@byteOrder'. Invalid XML header '" + xmlHeader + "'.", "State");
817 :
818 : //
819 : // 2nd) Look for the Attributes element and grab the names of the elements it contains.
820 : //
821 0 : xmlNode* attributes = bulkStoreRef->next;
822 0 : if ( attributes == 0 || (attributes->type != XML_ELEMENT_NODE) || (string("Attributes").compare((const char*) attributes->name) != 0))
823 0 : throw ConversionException ("Could not find the element '/StateTable/Attributes'. Invalid XML header '"+ xmlHeader + "'.", "State");
824 :
825 0 : xmlNode* childOfAttributes = attributes->children;
826 :
827 0 : while ( childOfAttributes != 0 && (childOfAttributes->type == XML_ELEMENT_NODE) ) {
828 0 : attributesSeq.push_back(string((const char*) childOfAttributes->name));
829 0 : childOfAttributes = childOfAttributes->next;
830 : }
831 0 : }
832 : // Create an EndianISStream from the substring containing the binary part.
833 0 : EndianISStream eiss(mimeMsg.substr(loc1+binPartMIMEHeader.size()), byteOrder);
834 :
835 0 : entity = Entity::fromBin((EndianIStream&) eiss);
836 :
837 : // We do nothing with that but we have to read it.
838 0 : Entity containerEntity = Entity::fromBin((EndianIStream&) eiss);
839 :
840 : // Let's read numRows but ignore it and rely on the value specified in the ASDM.xml file.
841 0 : int numRows = ((EndianIStream&) eiss).readInt();
842 0 : if ((numRows != -1) // Then these are *not* data produced at the EVLA.
843 0 : && ((unsigned int) numRows != this->declaredSize )) { // Then the declared size (in ASDM.xml) is not equal to the one
844 : // written into the binary representation of the table.
845 0 : cout << "The a number of rows ('"
846 : << numRows
847 0 : << "') declared in the binary representation of the table is different from the one declared in ASDM.xml ('"
848 0 : << this->declaredSize
849 0 : << "'). I'll proceed with the value declared in ASDM.xml"
850 0 : << endl;
851 : }
852 :
853 0 : if (getContainer().checkRowUniqueness()) {
854 : try {
855 0 : for (uint32_t i = 0; i < this->declaredSize; i++) {
856 0 : StateRow* aRow = StateRow::fromBin((EndianIStream&) eiss, *this, attributesSeq);
857 0 : checkAndAdd(aRow);
858 : }
859 : }
860 0 : catch (const DuplicateKey &e) {
861 0 : throw ConversionException("Error while writing binary data , the message was "
862 0 : + e.getMessage(), "State");
863 0 : }
864 0 : catch (const TagFormatException &e) {
865 0 : throw ConversionException("Error while reading binary data , the message was "
866 0 : + e.getMessage(), "State");
867 0 : }
868 : }
869 : else {
870 0 : for (uint32_t i = 0; i < this->declaredSize; i++) {
871 0 : StateRow* aRow = StateRow::fromBin((EndianIStream&) eiss, *this, attributesSeq);
872 0 : append(aRow);
873 : }
874 : }
875 : //Does not change the convention defined in the model.
876 : //archiveAsBin = true;
877 : //fileAsBin = true;
878 0 : if ( doc != NULL ) xmlFreeDoc(doc);
879 :
880 0 : }
881 :
882 0 : void StateTable::setUnknownAttributeBinaryReader(const string& attributeName, BinaryAttributeReaderFunctor* barFctr) {
883 : //
884 : // Is this attribute really unknown ?
885 : //
886 0 : for (vector<string>::const_iterator iter = attributesNamesOfState_v.begin(); iter != attributesNamesOfState_v.end(); iter++) {
887 0 : if ((*iter).compare(attributeName) == 0)
888 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.", "State");
889 : }
890 :
891 : // Ok then register the functor to activate when an unknown attribute is met during the reading of a binary table?
892 0 : unknownAttributes2Functors[attributeName] = barFctr;
893 0 : }
894 :
895 0 : BinaryAttributeReaderFunctor* StateTable::getUnknownAttributeBinaryReader(const string& attributeName) const {
896 0 : map<string, BinaryAttributeReaderFunctor*>::const_iterator iter = unknownAttributes2Functors.find(attributeName);
897 0 : return (iter == unknownAttributes2Functors.end()) ? 0 : iter->second;
898 : }
899 :
900 :
901 13 : void StateTable::toFile(string directory) {
902 13 : if (!directoryExists(directory.c_str()) &&
903 0 : !createPath(directory.c_str())) {
904 0 : throw ConversionException("Could not create directory " , directory);
905 : }
906 :
907 13 : string fileName = directory + "/State.xml";
908 13 : ofstream tableout(fileName.c_str(),ios::out|ios::trunc);
909 13 : if (tableout.rdstate() == ostream::failbit)
910 0 : throw ConversionException("Could not open file " + fileName + " to write ", "State");
911 13 : if (fileAsBin)
912 0 : tableout << MIMEXMLPart();
913 : else
914 13 : tableout << toXML() << endl;
915 13 : tableout.close();
916 13 : if (tableout.rdstate() == ostream::failbit)
917 0 : throw ConversionException("Could not close file " + fileName, "State");
918 :
919 13 : if (fileAsBin) {
920 : // write the bin serialized
921 0 : string fileName = directory + "/State.bin";
922 0 : ofstream tableout(fileName.c_str(),ios::out|ios::trunc);
923 0 : if (tableout.rdstate() == ostream::failbit)
924 0 : throw ConversionException("Could not open file " + fileName + " to write ", "State");
925 0 : tableout << toMIME() << endl;
926 0 : tableout.close();
927 0 : if (tableout.rdstate() == ostream::failbit)
928 0 : throw ConversionException("Could not close file " + fileName, "State");
929 0 : }
930 13 : }
931 :
932 :
933 78 : void StateTable::setFromFile(const string& directory) {
934 : #ifndef WITHOUT_BOOST
935 : if (boost::filesystem::exists(boost::filesystem::path(uniqSlashes(directory + "/State.xml"))))
936 : setFromXMLFile(directory);
937 : else if (boost::filesystem::exists(boost::filesystem::path(uniqSlashes(directory + "/State.bin"))))
938 : setFromMIMEFile(directory);
939 : #else
940 : // alternative in Misc.h
941 78 : if (file_exists(uniqSlashes(directory + "/State.xml")))
942 78 : setFromXMLFile(directory);
943 0 : else if (file_exists(uniqSlashes(directory + "/State.bin")))
944 0 : setFromMIMEFile(directory);
945 : #endif
946 : else
947 0 : throw ConversionException("No file found for the State table", "State");
948 78 : }
949 :
950 :
951 0 : void StateTable::setFromMIMEFile(const string& directory) {
952 0 : string tablePath ;
953 :
954 0 : tablePath = directory + "/State.bin";
955 0 : ifstream tablefile(tablePath.c_str(), ios::in|ios::binary);
956 0 : if (!tablefile.is_open()) {
957 0 : throw ConversionException("Could not open file " + tablePath, "State");
958 : }
959 : // Read in a stringstream.
960 0 : stringstream ss; ss << tablefile.rdbuf();
961 :
962 0 : if (tablefile.rdstate() == istream::failbit || tablefile.rdstate() == istream::badbit) {
963 0 : throw ConversionException("Error reading file " + tablePath,"State");
964 : }
965 :
966 : // And close.
967 0 : tablefile.close();
968 0 : if (tablefile.rdstate() == istream::failbit)
969 0 : throw ConversionException("Could not close file " + tablePath,"State");
970 :
971 0 : setFromMIME(ss.str());
972 0 : }
973 : /*
974 : void StateTable::openMIMEFile (const string& directory) {
975 :
976 : // Open the file.
977 : string tablePath ;
978 : tablePath = directory + "/State.bin";
979 : ifstream tablefile(tablePath.c_str(), ios::in|ios::binary);
980 : if (!tablefile.is_open())
981 : throw ConversionException("Could not open file " + tablePath, "State");
982 :
983 : // Locate the xmlPartMIMEHeader.
984 : string xmlPartMIMEHeader = "CONTENT-ID: <HEADER.XML>\n\n";
985 : CharComparator comparator;
986 : istreambuf_iterator<char> BEGIN(tablefile.rdbuf());
987 : istreambuf_iterator<char> END;
988 : istreambuf_iterator<char> it = search(BEGIN, END, xmlPartMIMEHeader.begin(), xmlPartMIMEHeader.end(), comparator);
989 : if (it == END)
990 : throw ConversionException("failed to detect the beginning of the XML header", "State");
991 :
992 : // Locate the binaryPartMIMEHeader while accumulating the characters of the xml header.
993 : string binPartMIMEHeader = "--MIME_BOUNDARY\nCONTENT-TYPE: BINARY/OCTET-STREAM\nCONTENT-ID: <CONTENT.BIN>\n\n";
994 : string xmlHeader;
995 : CharCompAccumulator compaccumulator(&xmlHeader, 100000);
996 : ++it;
997 : it = search(it, END, binPartMIMEHeader.begin(), binPartMIMEHeader.end(), compaccumulator);
998 : if (it == END)
999 : throw ConversionException("failed to detect the beginning of the binary part", "State");
1000 :
1001 : cout << xmlHeader << endl;
1002 : //
1003 : // We have the xmlHeader , let's parse it.
1004 : //
1005 : xmlDoc *doc;
1006 : doc = xmlReadMemory(xmlHeader.data(), xmlHeader.size(), "BinaryTableHeader.xml", NULL, XML_PARSE_NOBLANKS);
1007 : if ( doc == NULL )
1008 : throw ConversionException("Failed to parse the xmlHeader into a DOM structure.", "State");
1009 :
1010 : // This vector will be filled by the names of all the attributes of the table
1011 : // in the order in which they are expected to be found in the binary representation.
1012 : //
1013 : vector<string> attributesSeq(attributesNamesInBinOfState_v);
1014 :
1015 : xmlNode* root_element = xmlDocGetRootElement(doc);
1016 : if ( root_element == NULL || root_element->type != XML_ELEMENT_NODE )
1017 : throw ConversionException("Failed to parse the xmlHeader into a DOM structure.", "State");
1018 :
1019 : const ByteOrder* byteOrder=0;
1020 : if ( string("ASDMBinaryTable").compare((const char*) root_element->name) == 0) {
1021 : // Then it's an "old fashioned" MIME file for tables.
1022 : // Just try to deserialize it with Big_Endian for the bytes ordering.
1023 : byteOrder = asdm::ByteOrder::Big_Endian;
1024 :
1025 : // And decide that it has version == "2"
1026 : version = "2";
1027 : }
1028 : else if (string("StateTable").compare((const char*) root_element->name) == 0) {
1029 : // It's a new (and correct) MIME file for tables.
1030 : //
1031 : // 1st ) Look for a BulkStoreRef element with an attribute byteOrder.
1032 : //
1033 : xmlNode* bulkStoreRef = 0;
1034 : xmlNode* child = root_element->children;
1035 :
1036 : if (xmlHasProp(root_element, (const xmlChar*) "schemaVersion")) {
1037 : xmlChar * value = xmlGetProp(root_element, (const xmlChar *) "schemaVersion");
1038 : version = string ((const char *) value);
1039 : xmlFree(value);
1040 : }
1041 :
1042 : // Skip the two first children (Entity and ContainerEntity).
1043 : bulkStoreRef = (child == 0) ? 0 : ( (child->next) == 0 ? 0 : child->next->next );
1044 :
1045 : if ( bulkStoreRef == 0 || (bulkStoreRef->type != XML_ELEMENT_NODE) || (string("BulkStoreRef").compare((const char*) bulkStoreRef->name) != 0))
1046 : throw ConversionException ("Could not find the element '/StateTable/BulkStoreRef'. Invalid XML header '"+ xmlHeader + "'.", "State");
1047 :
1048 : // We found BulkStoreRef, now look for its attribute byteOrder.
1049 : _xmlAttr* byteOrderAttr = 0;
1050 : for (struct _xmlAttr* attr = bulkStoreRef->properties; attr; attr = attr->next)
1051 : if (string("byteOrder").compare((const char*) attr->name) == 0) {
1052 : byteOrderAttr = attr;
1053 : break;
1054 : }
1055 :
1056 : if (byteOrderAttr == 0)
1057 : throw ConversionException("Could not find the element '/StateTable/BulkStoreRef/@byteOrder'. Invalid XML header '" + xmlHeader +"'.", "State");
1058 :
1059 : string byteOrderValue = string((const char*) byteOrderAttr->children->content);
1060 : if (!(byteOrder = asdm::ByteOrder::fromString(byteOrderValue)))
1061 : throw ConversionException("No valid value retrieved for the element '/StateTable/BulkStoreRef/@byteOrder'. Invalid XML header '" + xmlHeader + "'.", "State");
1062 :
1063 : //
1064 : // 2nd) Look for the Attributes element and grab the names of the elements it contains.
1065 : //
1066 : xmlNode* attributes = bulkStoreRef->next;
1067 : if ( attributes == 0 || (attributes->type != XML_ELEMENT_NODE) || (string("Attributes").compare((const char*) attributes->name) != 0))
1068 : throw ConversionException ("Could not find the element '/StateTable/Attributes'. Invalid XML header '"+ xmlHeader + "'.", "State");
1069 :
1070 : xmlNode* childOfAttributes = attributes->children;
1071 :
1072 : while ( childOfAttributes != 0 && (childOfAttributes->type == XML_ELEMENT_NODE) ) {
1073 : attributesSeq.push_back(string((const char*) childOfAttributes->name));
1074 : childOfAttributes = childOfAttributes->next;
1075 : }
1076 : }
1077 : // Create an EndianISStream from the substring containing the binary part.
1078 : EndianIFStream eifs(&tablefile, byteOrder);
1079 :
1080 : entity = Entity::fromBin((EndianIStream &) eifs);
1081 :
1082 : // We do nothing with that but we have to read it.
1083 : Entity containerEntity = Entity::fromBin((EndianIStream &) eifs);
1084 :
1085 : // Let's read numRows but ignore it and rely on the value specified in the ASDM.xml file.
1086 : int numRows = eifs.readInt();
1087 : if ((numRows != -1) // Then these are *not* data produced at the EVLA.
1088 : && ((unsigned int) numRows != this->declaredSize )) { // Then the declared size (in ASDM.xml) is not equal to the one
1089 : // written into the binary representation of the table.
1090 : cout << "The a number of rows ('"
1091 : << numRows
1092 : << "') declared in the binary representation of the table is different from the one declared in ASDM.xml ('"
1093 : << this->declaredSize
1094 : << "'). I'll proceed with the value declared in ASDM.xml"
1095 : << endl;
1096 : }
1097 : // clean up xmlDoc pointer
1098 : if ( doc != NULL ) xmlFreeDoc(doc);
1099 : }
1100 : */
1101 :
1102 :
1103 78 : void StateTable::setFromXMLFile(const string& directory) {
1104 78 : string tablePath ;
1105 :
1106 78 : tablePath = directory + "/State.xml";
1107 :
1108 : /*
1109 : ifstream tablefile(tablePath.c_str(), ios::in|ios::binary);
1110 : if (!tablefile.is_open()) {
1111 : throw ConversionException("Could not open file " + tablePath, "State");
1112 : }
1113 : // Read in a stringstream.
1114 : stringstream ss;
1115 : ss << tablefile.rdbuf();
1116 :
1117 : if (tablefile.rdstate() == istream::failbit || tablefile.rdstate() == istream::badbit) {
1118 : throw ConversionException("Error reading file '" + tablePath + "'", "State");
1119 : }
1120 :
1121 : // And close
1122 : tablefile.close();
1123 : if (tablefile.rdstate() == istream::failbit)
1124 : throw ConversionException("Could not close file '" + tablePath + "'", "State");
1125 :
1126 : // Let's make a string out of the stringstream content and empty the stringstream.
1127 : string xmlDocument = ss.str(); ss.str("");
1128 :
1129 : // Let's make a very primitive check to decide
1130 : // whether the XML content represents the table
1131 : // or refers to it via a <BulkStoreRef element.
1132 : */
1133 :
1134 78 : string xmlDocument;
1135 : try {
1136 78 : xmlDocument = getContainer().getXSLTransformer()(tablePath);
1137 78 : if (getenv("ASDM_DEBUG")) cout << "About to read " << tablePath << endl;
1138 : }
1139 0 : catch (const XSLTransformerException &e) {
1140 0 : throw ConversionException("Caugth an exception whose message is '" + e.getMessage() + "'.", "State");
1141 0 : }
1142 :
1143 78 : if (xmlDocument.find("<BulkStoreRef") != string::npos)
1144 0 : setFromMIMEFile(directory);
1145 : else
1146 78 : fromXML(xmlDocument);
1147 78 : }
1148 :
1149 :
1150 :
1151 :
1152 :
1153 :
1154 :
1155 :
1156 :
1157 :
1158 0 : void StateTable::autoIncrement(string key, StateRow* x) {
1159 0 : map<string, int>::iterator iter;
1160 0 : if ((iter=noAutoIncIds.find(key)) == noAutoIncIds.end()) {
1161 : // There is not yet a combination of the non autoinc attributes values in the hashtable
1162 :
1163 : // Initialize stateId to Tag(0).
1164 0 : x->setStateId(Tag(0, TagType::State));
1165 :
1166 : // Record it in the map.
1167 0 : noAutoIncIds.insert(make_pair(key, 0));
1168 : }
1169 : else {
1170 : // There is already a combination of the non autoinc attributes values in the hashtable
1171 : // Increment its value.
1172 0 : int n = iter->second + 1;
1173 :
1174 : // Initialize stateId to Tag(n).
1175 0 : x->setStateId(Tag(n, TagType::State));
1176 :
1177 : // Record it in the map.
1178 0 : noAutoIncIds.insert(make_pair(key, n));
1179 : }
1180 0 : }
1181 :
1182 : } // End namespace asdm
1183 :
|