Update ooo320-m1
[ooovba.git] / connectivity / source / drivers / ado / ADatabaseMetaDataImpl.cxx
blob1480b52fd4d6fb40bb673d1ba214296ef5fafe2e
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: ADatabaseMetaDataImpl.cxx,v $
10 * $Revision: 1.11 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_connectivity.hxx"
33 #include "ado/ADatabaseMetaData.hxx"
34 #include "ado/ADatabaseMetaDataResultSetMetaData.hxx"
35 #include "ado/Awrapado.hxx"
36 #include "ado/AGroup.hxx"
37 #include "ado/adoimp.hxx"
38 #include "ado/AIndex.hxx"
39 #include "ado/AKey.hxx"
40 #include "ado/ATable.hxx"
41 #include <com/sun/star/sdbc/DataType.hpp>
42 #include <com/sun/star/sdbc/ProcedureResult.hpp>
43 #include <com/sun/star/sdbc/ColumnValue.hpp>
44 #ifdef DELETE
45 #undef DELETE
46 #endif
47 #include <com/sun/star/sdbcx/Privilege.hpp>
48 #include <com/sun/star/sdbcx/PrivilegeObject.hpp>
49 #include <com/sun/star/sdbc/KeyRule.hpp>
50 #include <com/sun/star/sdbcx/KeyType.hpp>
52 using namespace connectivity::ado;
53 using namespace ::com::sun::star::sdbc;
54 using namespace ::com::sun::star::sdbcx;
55 using namespace ::com::sun::star::uno;
57 // -------------------------------------------------------------------------
58 void ODatabaseMetaData::fillLiterals()
60 ADORecordset *pRecordset = NULL;
61 OLEVariant vtEmpty;
62 vtEmpty.setNoArg();
63 m_pADOConnection->OpenSchema(adSchemaDBInfoLiterals,vtEmpty,vtEmpty,&pRecordset);
65 ADOS::ThrowException(*m_pADOConnection,*this);
67 OSL_ENSURE(pRecordset,"fillLiterals: no resultset!");
68 if ( pRecordset )
70 WpADORecordset aRecordset(pRecordset);
72 aRecordset.MoveFirst();
73 OLEVariant aValue;
74 LiteralInfo aInfo;
75 while(!aRecordset.IsAtEOF())
77 WpOLEAppendCollection<ADOFields, ADOField, WpADOField> aFields(aRecordset.GetFields());
78 WpADOField aField(aFields.GetItem(1));
79 aInfo.pwszLiteralValue = aField.get_Value();
80 aField = aFields.GetItem(5);
81 aInfo.fSupported = aField.get_Value();
82 aField = aFields.GetItem(6);
83 aInfo.cchMaxLen = aField.get_Value().getUInt32();
85 aField = aFields.GetItem(4);
86 sal_uInt32 nId = aField.get_Value().getUInt32();
87 m_aLiteralInfo[nId] = aInfo;
89 aRecordset.MoveNext();
91 aRecordset.Close();
94 // -------------------------------------------------------------------------
95 sal_Int32 ODatabaseMetaData::getMaxSize(sal_uInt32 _nId)
97 if(!m_aLiteralInfo.size())
98 fillLiterals();
100 sal_Int32 nSize = 0;
101 ::std::map<sal_uInt32,LiteralInfo>::const_iterator aIter = m_aLiteralInfo.find(_nId);
102 if(aIter != m_aLiteralInfo.end() && (*aIter).second.fSupported)
103 nSize = ((*aIter).second.cchMaxLen == (-1)) ? 0 : (*aIter).second.cchMaxLen;
104 return nSize;
106 // -------------------------------------------------------------------------
107 sal_Bool ODatabaseMetaData::isCapable(sal_uInt32 _nId)
109 if(!m_aLiteralInfo.size())
110 fillLiterals();
111 sal_Bool bSupported = sal_False;
112 ::std::map<sal_uInt32,LiteralInfo>::const_iterator aIter = m_aLiteralInfo.find(_nId);
113 if(aIter != m_aLiteralInfo.end())
114 bSupported = (*aIter).second.fSupported;
115 return bSupported;
118 // -------------------------------------------------------------------------
119 ::rtl::OUString ODatabaseMetaData::getLiteral(sal_uInt32 _nId)
121 if(!m_aLiteralInfo.size())
122 fillLiterals();
123 ::rtl::OUString sStr;
124 ::std::map<sal_uInt32,LiteralInfo>::const_iterator aIter = m_aLiteralInfo.find(_nId);
125 if(aIter != m_aLiteralInfo.end() && (*aIter).second.fSupported)
126 sStr = (*aIter).second.pwszLiteralValue;
127 return sStr;
129 // -----------------------------------------------------------------------------
130 // -------------------------------------------------------------------------
131 void ODatabaseMetaDataResultSetMetaData::setColumnPrivilegesMap()
133 m_mColumns[8] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("IS_GRANTABLE"),
134 ColumnValue::NULLABLE,
135 3,3,0,
136 DataType::VARCHAR);
138 // -------------------------------------------------------------------------
139 void ODatabaseMetaDataResultSetMetaData::setColumnsMap()
141 m_mColumns[6] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("TYPE_NAME"),
142 ColumnValue::NO_NULLS,
143 0,0,0,
144 DataType::VARCHAR);
145 m_mColumns[11] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("NULLABLE"),
146 ColumnValue::NO_NULLS,
147 1,1,0,
148 DataType::INTEGER);
149 m_mColumns[12] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("REMARKS"),
150 ColumnValue::NULLABLE,
151 0,0,0,
152 DataType::VARCHAR);
153 m_mColumns[13] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("COLUMN_DEF"),
154 ColumnValue::NULLABLE,
155 0,0,0,
156 DataType::VARCHAR);
157 m_mColumns[14] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("SQL_DATA_TYPE"),
158 ColumnValue::NO_NULLS,
159 1,1,0,
160 DataType::INTEGER);
161 m_mColumns[15] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("SQL_DATETIME_SUB"),
162 ColumnValue::NO_NULLS,
163 1,1,0,
164 DataType::INTEGER);
165 m_mColumns[16] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("CHAR_OCTET_LENGTH"),
166 ColumnValue::NO_NULLS,
167 1,1,0,
168 DataType::INTEGER);
170 // -------------------------------------------------------------------------
171 void ODatabaseMetaDataResultSetMetaData::setTablesMap()
173 m_mColumns[5] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("REMARKS"),
174 ColumnValue::NULLABLE,
175 0,0,0,
176 DataType::VARCHAR);
178 // -------------------------------------------------------------------------
179 void ODatabaseMetaDataResultSetMetaData::setProcedureColumnsMap()
181 m_mColumns[12] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("NULLABLE"),
182 ColumnValue::NO_NULLS,
183 1,1,0,
184 DataType::INTEGER);
186 // -------------------------------------------------------------------------
187 void ODatabaseMetaDataResultSetMetaData::setPrimaryKeysMap()
189 m_mColumns[5] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("KEY_SEQ"),
190 ColumnValue::NO_NULLS,
191 1,1,0,
192 DataType::INTEGER);
193 m_mColumns[6] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("PK_NAME"),
194 ColumnValue::NULLABLE,
195 0,0,0,
196 DataType::VARCHAR);
198 // -------------------------------------------------------------------------
199 void ODatabaseMetaDataResultSetMetaData::setIndexInfoMap()
201 m_mColumns[4] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("NON_UNIQUE"),
202 ColumnValue::NO_NULLS,
203 1,1,0,
204 DataType::BIT);
205 m_mColumns[5] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("INDEX_QUALIFIER"),
206 ColumnValue::NULLABLE,
207 0,0,0,
208 DataType::VARCHAR);
209 m_mColumns[10] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("ASC_OR_DESC"),
210 ColumnValue::NULLABLE,
211 0,0,0,
212 DataType::VARCHAR);
214 // -------------------------------------------------------------------------
215 void ODatabaseMetaDataResultSetMetaData::setTablePrivilegesMap()
217 m_mColumns[6] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("PRIVILEGE"),
218 ColumnValue::NULLABLE,
219 0,0,0,
220 DataType::VARCHAR);
221 m_mColumns[7] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("IS_GRANTABLE"),
222 ColumnValue::NULLABLE,
223 0,0,0,
224 DataType::VARCHAR);
226 // -------------------------------------------------------------------------
227 void ODatabaseMetaDataResultSetMetaData::setCrossReferenceMap()
229 m_mColumns[9] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("KEY_SEQ"),
230 ColumnValue::NO_NULLS,
231 1,1,0,
232 DataType::INTEGER);
234 // -------------------------------------------------------------------------
235 void ODatabaseMetaDataResultSetMetaData::setTypeInfoMap()
237 m_mColumns[3] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("PRECISION"),
238 ColumnValue::NO_NULLS,
239 1,1,0,
240 DataType::INTEGER);
241 m_mColumns[7] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("NULLABLE"),
242 ColumnValue::NO_NULLS,
243 1,1,0,
244 DataType::INTEGER);
245 m_mColumns[12] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("AUTO_INCREMENT"),
246 ColumnValue::NO_NULLS,
247 1,1,0,
248 DataType::BIT);
249 m_mColumns[16] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("SQL_DATA_TYPE"),
250 ColumnValue::NO_NULLS,
251 1,1,0,
252 DataType::INTEGER);
253 m_mColumns[17] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("SQL_DATETIME_SUB"),
254 ColumnValue::NO_NULLS,
255 1,1,0,
256 DataType::INTEGER);
257 m_mColumns[18] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("NUM_PREC_RADIX"),
258 ColumnValue::NO_NULLS,
259 1,1,0,
260 DataType::INTEGER);
262 // -------------------------------------------------------------------------
263 void ODatabaseMetaDataResultSetMetaData::setProceduresMap()
265 m_mColumns[7] = OColumn(::rtl::OUString(),::rtl::OUString::createFromAscii("REMARKS"),
266 ColumnValue::NULLABLE,
267 0,0,0,
268 DataType::VARCHAR);
270 // -------------------------------------------------------------------------
271 sal_Bool SAL_CALL ODatabaseMetaDataResultSetMetaData::isSearchable( sal_Int32 column ) throw(SQLException, RuntimeException)
273 if(m_mColumns.size() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end())
274 return (*m_mColumnsIter).second.isSearchable();
275 return sal_True;
277 // -------------------------------------------------------------------------
278 sal_Bool SAL_CALL ODatabaseMetaDataResultSetMetaData::isAutoIncrement( sal_Int32 column ) throw(SQLException, RuntimeException)
280 if(m_mColumns.size() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end())
281 return (*m_mColumnsIter).second.isAutoIncrement();
282 return sal_False;
284 // -------------------------------------------------------------------------
285 ::rtl::OUString SAL_CALL ODatabaseMetaDataResultSetMetaData::getColumnServiceName( sal_Int32 column ) throw(SQLException, RuntimeException)
287 if(m_mColumns.size() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end())
288 return (*m_mColumnsIter).second.getColumnServiceName();
289 return ::rtl::OUString();
291 // -------------------------------------------------------------------------
292 ::rtl::OUString SAL_CALL ODatabaseMetaDataResultSetMetaData::getTableName( sal_Int32 column ) throw(SQLException, RuntimeException)
294 if(m_mColumns.size() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end())
295 return (*m_mColumnsIter).second.getTableName();
296 return ::rtl::OUString();
298 // -------------------------------------------------------------------------
299 ::rtl::OUString SAL_CALL ODatabaseMetaDataResultSetMetaData::getCatalogName( sal_Int32 column ) throw(SQLException, RuntimeException)
301 if(m_mColumns.size() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end())
302 return (*m_mColumnsIter).second.getCatalogName();
303 return ::rtl::OUString();
305 // -------------------------------------------------------------------------
306 ::rtl::OUString SAL_CALL ODatabaseMetaDataResultSetMetaData::getColumnTypeName( sal_Int32 column ) throw(SQLException, RuntimeException)
308 if(m_mColumns.size() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end())
309 return (*m_mColumnsIter).second.getColumnTypeName();
310 return ::rtl::OUString();
312 // -------------------------------------------------------------------------
314 sal_Bool SAL_CALL ODatabaseMetaDataResultSetMetaData::isCaseSensitive( sal_Int32 column ) throw(SQLException, RuntimeException)
316 if(m_mColumns.size() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end())
317 return (*m_mColumnsIter).second.isCaseSensitive();
318 return sal_True;
320 // -------------------------------------------------------------------------
322 ::rtl::OUString SAL_CALL ODatabaseMetaDataResultSetMetaData::getSchemaName( sal_Int32 column ) throw(SQLException, RuntimeException)
324 if(m_mColumns.size() && (m_mColumnsIter = m_mColumns.find(column)) != m_mColumns.end())
325 return (*m_mColumnsIter).second.getSchemaName();
326 return ::rtl::OUString();
328 // -----------------------------------------------------------------------------
329 // -------------------------------------------------------------------------
330 ObjectTypeEnum OAdoGroup::MapObjectType(sal_Int32 _ObjType)
332 ObjectTypeEnum eNumType= adPermObjTable;
333 switch(_ObjType)
335 case PrivilegeObject::TABLE:
336 break;
337 case PrivilegeObject::VIEW:
338 eNumType = adPermObjView;
339 break;
340 case PrivilegeObject::COLUMN:
341 eNumType = adPermObjColumn;
342 break;
344 return eNumType;
346 // -------------------------------------------------------------------------
347 sal_Int32 OAdoGroup::MapRight(RightsEnum _eNum)
349 sal_Int32 nRight = 0;
350 if(_eNum & adRightRead)
351 nRight |= Privilege::SELECT;
352 if(_eNum & adRightInsert)
353 nRight |= Privilege::INSERT;
354 if(_eNum & adRightUpdate)
355 nRight |= Privilege::UPDATE;
356 if(_eNum & adRightDelete)
357 nRight |= Privilege::DELETE;
358 if(_eNum & adRightReadDesign)
359 nRight |= Privilege::READ;
360 if(_eNum & adRightCreate)
361 nRight |= Privilege::CREATE;
362 if(_eNum & adRightWriteDesign)
363 nRight |= Privilege::ALTER;
364 if(_eNum & adRightReference)
365 nRight |= Privilege::REFERENCE;
366 if(_eNum & adRightDrop)
367 nRight |= Privilege::DROP;
369 return nRight;
371 // -------------------------------------------------------------------------
372 RightsEnum OAdoGroup::Map2Right(sal_Int32 _eNum)
374 sal_Int32 nRight = adRightNone;
375 if(_eNum & Privilege::SELECT)
376 nRight |= adRightRead;
378 if(_eNum & Privilege::INSERT)
379 nRight |= adRightInsert;
381 if(_eNum & Privilege::UPDATE)
382 nRight |= adRightUpdate;
384 if(_eNum & Privilege::DELETE)
385 nRight |= adRightDelete;
387 if(_eNum & Privilege::READ)
388 nRight |= adRightReadDesign;
390 if(_eNum & Privilege::CREATE)
391 nRight |= adRightCreate;
393 if(_eNum & Privilege::ALTER)
394 nRight |= adRightWriteDesign;
396 if(_eNum & Privilege::REFERENCE)
397 nRight |= adRightReference;
399 if(_eNum & Privilege::DROP)
400 nRight |= adRightDrop;
402 return (RightsEnum)nRight;
404 // -------------------------------------------------------------------------
405 void WpADOIndex::Create()
407 HRESULT hr = -1;
409 _ADOIndex* pIndex = NULL;
410 hr = CoCreateInstance(ADOS::CLSID_ADOINDEX_25,
411 NULL,
412 CLSCTX_INPROC_SERVER,
413 ADOS::IID_ADOINDEX_25,
414 (void**)&pIndex );
417 if( !FAILED( hr ) )
419 operator=( pIndex );
420 pIndex->Release();
423 // -------------------------------------------------------------------------
424 void OAdoIndex::fillPropertyValues()
426 if(m_aIndex.IsValid())
428 m_Name = m_aIndex.get_Name();
429 m_IsUnique = m_aIndex.get_Unique();
430 m_IsPrimaryKeyIndex = m_aIndex.get_PrimaryKey();
431 m_IsClustered = m_aIndex.get_Clustered();
434 // -----------------------------------------------------------------------------
435 void WpADOKey::Create()
437 HRESULT hr = -1;
438 _ADOKey* pKey = NULL;
439 hr = CoCreateInstance(ADOS::CLSID_ADOKEY_25,
440 NULL,
441 CLSCTX_INPROC_SERVER,
442 ADOS::IID_ADOKEY_25,
443 (void**)&pKey );
446 if( !FAILED( hr ) )
448 operator=( pKey );
449 pKey->Release();
452 // -------------------------------------------------------------------------
453 void OAdoKey::fillPropertyValues()
455 if(m_aKey.IsValid())
457 m_aProps->m_Type = MapKeyRule(m_aKey.get_Type());
458 m_Name = m_aKey.get_Name();
459 m_aProps->m_ReferencedTable = m_aKey.get_RelatedTable();
460 m_aProps->m_UpdateRule = MapRule(m_aKey.get_UpdateRule());
461 m_aProps->m_DeleteRule = MapRule(m_aKey.get_DeleteRule());
464 // -------------------------------------------------------------------------
465 sal_Int32 OAdoKey::MapRule(const RuleEnum& _eNum)
467 sal_Int32 eNum = KeyRule::NO_ACTION;
468 switch(_eNum)
470 case adRICascade:
471 eNum = KeyRule::CASCADE;
472 break;
473 case adRISetNull:
474 eNum = KeyRule::SET_NULL;
475 break;
476 case adRINone:
477 eNum = KeyRule::NO_ACTION;
478 break;
479 case adRISetDefault:
480 eNum = KeyRule::SET_DEFAULT;
481 break;
483 return eNum;
485 // -------------------------------------------------------------------------
486 RuleEnum OAdoKey::Map2Rule(const sal_Int32& _eNum)
488 RuleEnum eNum = adRINone;
489 switch(_eNum)
491 case KeyRule::CASCADE:
492 eNum = adRICascade;
493 break;
494 case KeyRule::SET_NULL:
495 eNum = adRISetNull;
496 break;
497 case KeyRule::NO_ACTION:
498 eNum = adRINone;
499 break;
500 case KeyRule::SET_DEFAULT:
501 eNum = adRISetDefault;
502 break;
504 return eNum;
506 // -------------------------------------------------------------------------
507 sal_Int32 OAdoKey::MapKeyRule(const KeyTypeEnum& _eNum)
509 sal_Int32 nKeyType = KeyType::PRIMARY;
510 switch(_eNum)
512 case adKeyPrimary:
513 nKeyType = KeyType::PRIMARY;
514 break;
515 case adKeyForeign:
516 nKeyType = KeyType::FOREIGN;
517 break;
518 case adKeyUnique:
519 nKeyType = KeyType::UNIQUE;
520 break;
522 return nKeyType;
524 // -------------------------------------------------------------------------
525 KeyTypeEnum OAdoKey::Map2KeyRule(const sal_Int32& _eNum)
527 KeyTypeEnum eNum( adKeyPrimary );
528 switch(_eNum)
530 case KeyType::PRIMARY:
531 eNum = adKeyPrimary;
532 break;
533 case KeyType::FOREIGN:
534 eNum = adKeyForeign;
535 break;
536 case KeyType::UNIQUE:
537 eNum = adKeyUnique;
538 break;
539 default:
540 OSL_ENSURE( false, "OAdoKey::Map2KeyRule: invalid key type!" );
542 return eNum;
544 // -----------------------------------------------------------------------------
545 void WpADOTable::Create()
547 HRESULT hr = -1;
548 _ADOTable* pTable = NULL;
549 hr = CoCreateInstance(ADOS::CLSID_ADOTABLE_25,
550 NULL,
551 CLSCTX_INPROC_SERVER,
552 ADOS::IID_ADOTABLE_25,
553 (void**)&pTable );
556 if( !FAILED( hr ) )
558 operator=( pTable );
559 pTable->Release();
562 // -------------------------------------------------------------------------
563 ::rtl::OUString WpADOCatalog::GetObjectOwner(const ::rtl::OUString& _rName, ObjectTypeEnum _eNum)
565 OLEVariant _rVar;
566 _rVar.setNoArg();
567 OLEString aBSTR;
568 OLEString sStr1(_rName);
569 pInterface->GetObjectOwner(sStr1,_eNum,_rVar,&aBSTR);
570 return aBSTR;
572 // -----------------------------------------------------------------------------
573 void OAdoTable::fillPropertyValues()
575 if(m_aTable.IsValid())
577 m_Name = m_aTable.get_Name();
578 m_Type = m_aTable.get_Type();
580 WpADOCatalog aCat(m_aTable.get_ParentCatalog());
581 if(aCat.IsValid())
582 m_CatalogName = aCat.GetObjectOwner(m_aTable.get_Name(),adPermObjTable);
585 WpADOProperties aProps = m_aTable.get_Properties();
586 if(aProps.IsValid())
587 m_Description = OTools::getValue(aProps,::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Description")));
591 // -----------------------------------------------------------------------------
592 void WpADOUser::Create()
594 HRESULT hr = -1;
595 _ADOUser* pUser = NULL;
596 hr = CoCreateInstance(ADOS::CLSID_ADOUSER_25,
597 NULL,
598 CLSCTX_INPROC_SERVER,
599 ADOS::IID_ADOUSER_25,
600 (void**)&pUser );
603 if( !FAILED( hr ) )
605 operator=( pUser );
606 pUser->Release();
609 // -------------------------------------------------------------------------