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