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