blob: 56693ef60b230b3b4e073ca0504c25231834d6cf [file] [log] [blame]
Kevin Rocard93250d12012-07-19 17:48:30 +02001/*
Patrick Benavoli68a91282011-08-31 11:23:23 +02002 * INTEL CONFIDENTIAL
3 * Copyright © 2011 Intel
4 * Corporation All Rights Reserved.
5 *
6 * The source code contained or described herein and all documents related to
7 * the source code ("Material") are owned by Intel Corporation or its suppliers
8 * or licensors. Title to the Material remains with Intel Corporation or its
9 * suppliers and licensors. The Material contains trade secrets and proprietary
10 * and confidential information of Intel or its suppliers and licensors. The
11 * Material is protected by worldwide copyright and trade secret laws and
12 * treaty provisions. No part of the Material may be used, copied, reproduced,
13 * modified, published, uploaded, posted, transmitted, distributed, or
14 * disclosed in any way without Intel’s prior express written permission.
15 *
16 * No license under any patent, copyright, trade secret or other intellectual
17 * property right is granted to or conferred upon you by disclosure or delivery
18 * of the Materials, either expressly, by implication, inducement, estoppel or
19 * otherwise. Any license under such intellectual property rights must be
20 * express and approved by Intel in writing.
21 *
Patrick Benavoli68a91282011-08-31 11:23:23 +020022 * CREATED: 2011-06-01
23 * UPDATED: 2011-07-27
Patrick Benavoli68a91282011-08-31 11:23:23 +020024 */
25#include "ConfigurableDomains.h"
26#include "ConfigurableDomain.h"
27#include "ConfigurableElement.h"
28#include "BinaryStream.h"
Patrick Benavoli6ba361d2011-08-31 11:23:24 +020029#include "AutoLog.h"
Patrick Benavoli68a91282011-08-31 11:23:23 +020030
31#define base CBinarySerializableElement
32
Patrick Benavoli95ac0342011-11-07 20:32:51 +010033CConfigurableDomains::CConfigurableDomains()
Patrick Benavoli68a91282011-08-31 11:23:23 +020034{
35}
36
37string CConfigurableDomains::getKind() const
38{
39 return "ConfigurableDomains";
40}
41
42bool CConfigurableDomains::childrenAreDynamic() const
43{
44 return true;
45}
46
47// Ensure validity on whole domains from main blackboard
48void CConfigurableDomains::validate(const CParameterBlackboard* pMainBlackboard)
49{
50 // Delegate to domains
51 uint32_t uiChild;
52 uint32_t uiNbConfigurableDomains = getNbChildren();
53
54 for (uiChild = 0; uiChild < uiNbConfigurableDomains; uiChild++) {
55
56 CConfigurableDomain* pChildConfigurableDomain = static_cast<CConfigurableDomain*>(getChild(uiChild));
57
58 pChildConfigurableDomain->validate(pMainBlackboard);
59 }
60}
61
62// Configuration application if required
Patrick Benavoli63499d42011-10-24 18:50:03 +020063bool CConfigurableDomains::apply(CParameterBlackboard* pParameterBlackboard, bool bForce, string& strError) const
Patrick Benavoli68a91282011-08-31 11:23:23 +020064{
Patrick Benavoli6ba361d2011-08-31 11:23:24 +020065 CAutoLog autoLog(this, "Applying configurations");
Patrick Benavoli68a91282011-08-31 11:23:23 +020066
67 // Syncer set
68 CSyncerSet syncerSet;
69
70 // Delegate to domains
Patrick Benavoli63499d42011-10-24 18:50:03 +020071
72 // Start with sequence unaware domains
Patrick Benavoli68a91282011-08-31 11:23:23 +020073 uint32_t uiChild;
74 uint32_t uiNbConfigurableDomains = getNbChildren();
75
76 for (uiChild = 0; uiChild < uiNbConfigurableDomains; uiChild++) {
77
Patrick Benavoli63499d42011-10-24 18:50:03 +020078 const CConfigurableDomain* pChildConfigurableDomain = static_cast<const CConfigurableDomain*>(getChild(uiChild));
Patrick Benavoli68a91282011-08-31 11:23:23 +020079
Patrick Benavoli63499d42011-10-24 18:50:03 +020080 if (!pChildConfigurableDomain->getSequenceAwareness() && !pChildConfigurableDomain->apply(pParameterBlackboard, syncerSet, bForce, strError)) {
81
82 return false;
83 }
Patrick Benavoli68a91282011-08-31 11:23:23 +020084 }
85 // Synchronize
Patrick Benavoli63499d42011-10-24 18:50:03 +020086 if (!syncerSet.sync(*pParameterBlackboard, false, strError)) {
87
88 return false;
89 }
90
91 // Then deal with sequence aware domains
92 for (uiChild = 0; uiChild < uiNbConfigurableDomains; uiChild++) {
93
94 const CConfigurableDomain* pChildConfigurableDomain = static_cast<const CConfigurableDomain*>(getChild(uiChild));
95
96 if (pChildConfigurableDomain->getSequenceAwareness() && !pChildConfigurableDomain->apply(pParameterBlackboard, syncerSet, bForce, strError)) {
97
98 return false;
99 }
100 }
101
102 return true;
Patrick Benavoli68a91282011-08-31 11:23:23 +0200103}
104
105// From IXmlSource
106void CConfigurableDomains::toXml(CXmlElement& xmlElement, CXmlSerializingContext& serializingContext) const
107{
108 // Set attribute
109 xmlElement.setAttributeString("SystemClassName", getName());
110
111 base::toXml(xmlElement, serializingContext);
112}
113
114// Configuration/Domains handling
115/// Domains
116bool CConfigurableDomains::createDomain(const string& strName, string& strError)
117{
118 // Already exists?
119 if (findChild(strName)) {
120
121 strError = "Already existing configurable domain";
122
123 return false;
124 }
125
126 log("Creating configurable domain \"%s\"", strName.c_str());
127
128 // Creation/Hierarchy
129 addChild(new CConfigurableDomain(strName));
130
131 return true;
132}
133
134bool CConfigurableDomains::deleteDomain(const string& strName, string& strError)
135{
Patrick Benavoli0bd50542011-11-29 11:10:27 +0100136 CConfigurableDomain* pConfigurableDomain = findConfigurableDomain(strName, strError);
Patrick Benavoli68a91282011-08-31 11:23:23 +0200137
138 if (!pConfigurableDomain) {
139
Patrick Benavoli68a91282011-08-31 11:23:23 +0200140 return false;
141 }
142
143 log("Deleting configurable domain \"%s\"", strName.c_str());
144
145 // Hierarchy
146 removeChild(pConfigurableDomain);
147
148 // Destroy
149 delete pConfigurableDomain;
150
151 return true;
152}
153
Kevin Rocard170f0a42012-06-18 13:56:05 +0200154void CConfigurableDomains::deleteAllDomains()
155{
156 log("Deleting all configurable domains");
157
158 //remove Children
159 clean();
160}
161
Patrick Benavoli68a91282011-08-31 11:23:23 +0200162bool CConfigurableDomains::renameDomain(const string& strName, const string& strNewName, string& strError)
163{
Patrick Benavoli0bd50542011-11-29 11:10:27 +0100164 CConfigurableDomain* pConfigurableDomain = findConfigurableDomain(strName, strError);
Patrick Benavoli68a91282011-08-31 11:23:23 +0200165
166 if (!pConfigurableDomain) {
167
Patrick Benavoli68a91282011-08-31 11:23:23 +0200168 return false;
169 }
170
171 log("Renaming configurable domain \"%s\" to \"%s\"", strName.c_str(), strNewName.c_str());
172
173 // Rename
174 return pConfigurableDomain->rename(strNewName, strError);
175}
176
Patrick Benavoli63499d42011-10-24 18:50:03 +0200177bool CConfigurableDomains::setSequenceAwareness(const string& strDomain, bool bSequenceAware, string& strError)
178{
Patrick Benavoli0bd50542011-11-29 11:10:27 +0100179 CConfigurableDomain* pConfigurableDomain = findConfigurableDomain(strDomain, strError);
Patrick Benavoli63499d42011-10-24 18:50:03 +0200180
181 if (!pConfigurableDomain) {
182
Patrick Benavoli63499d42011-10-24 18:50:03 +0200183 return false;
184 }
185
186 pConfigurableDomain->setSequenceAwareness(bSequenceAware);
187
188 return true;
189}
190
191bool CConfigurableDomains::getSequenceAwareness(const string& strDomain, bool& bSequenceAware, string& strError) const
192{
Patrick Benavoli0bd50542011-11-29 11:10:27 +0100193 const CConfigurableDomain* pConfigurableDomain = findConfigurableDomain(strDomain, strError);
Patrick Benavoli63499d42011-10-24 18:50:03 +0200194
195 if (!pConfigurableDomain) {
196
Patrick Benavoli63499d42011-10-24 18:50:03 +0200197 return false;
198 }
199
200 bSequenceAware = pConfigurableDomain->getSequenceAwareness();
201
202 return true;
203}
204
Patrick Benavoli68a91282011-08-31 11:23:23 +0200205/// Configurations
206bool CConfigurableDomains::listConfigurations(const string& strDomain, string& strResult) const
207{
Patrick Benavoli0bd50542011-11-29 11:10:27 +0100208 const CConfigurableDomain* pConfigurableDomain = findConfigurableDomain(strDomain, strResult);
Patrick Benavoli68a91282011-08-31 11:23:23 +0200209
210 if (!pConfigurableDomain) {
211
Patrick Benavoli68a91282011-08-31 11:23:23 +0200212 return false;
213 }
214 // delegate
215 pConfigurableDomain->listChildren(strResult);
216
217 return true;
218}
219
220bool CConfigurableDomains::createConfiguration(const string& strDomain, const string& strConfiguration, const CParameterBlackboard* pMainBlackboard, string& strError)
221{
222 // Find domain
Patrick Benavoli0bd50542011-11-29 11:10:27 +0100223 CConfigurableDomain* pConfigurableDomain = findConfigurableDomain(strDomain, strError);
Patrick Benavoli68a91282011-08-31 11:23:23 +0200224
225 if (!pConfigurableDomain) {
226
Patrick Benavoli68a91282011-08-31 11:23:23 +0200227 return false;
228 }
229 // Delegate
230 return pConfigurableDomain->createConfiguration(strConfiguration, pMainBlackboard, strError);
231}
232
233bool CConfigurableDomains::deleteConfiguration(const string& strDomain, const string& strConfiguration, string& strError)
234{
235 // Find domain
Patrick Benavoli0bd50542011-11-29 11:10:27 +0100236 CConfigurableDomain* pConfigurableDomain = findConfigurableDomain(strDomain, strError);
Patrick Benavoli68a91282011-08-31 11:23:23 +0200237
238 if (!pConfigurableDomain) {
239
Patrick Benavoli68a91282011-08-31 11:23:23 +0200240 return false;
241 }
242 // Delegate
243 return pConfigurableDomain->deleteConfiguration(strConfiguration, strError);
244}
245
246bool CConfigurableDomains::renameConfiguration(const string& strDomain, const string& strConfigurationName, const string& strNewConfigurationName, string& strError)
247{
248 // Find domain
Patrick Benavoli0bd50542011-11-29 11:10:27 +0100249 CConfigurableDomain* pConfigurableDomain = findConfigurableDomain(strDomain, strError);
Patrick Benavoli68a91282011-08-31 11:23:23 +0200250
251 if (!pConfigurableDomain) {
252
Patrick Benavoli68a91282011-08-31 11:23:23 +0200253 return false;
254 }
255 // Delegate
256 return pConfigurableDomain->renameConfiguration(strConfigurationName, strNewConfigurationName, strError);
257}
258
259bool CConfigurableDomains::listDomainElements(const string& strDomain, string& strResult) const
260{
261 // Find domain
Patrick Benavoli0bd50542011-11-29 11:10:27 +0100262 const CConfigurableDomain* pConfigurableDomain = findConfigurableDomain(strDomain, strResult);
Patrick Benavoli68a91282011-08-31 11:23:23 +0200263
264 if (!pConfigurableDomain) {
265
Patrick Benavoli68a91282011-08-31 11:23:23 +0200266 return false;
267 }
268 // Delegate
269 pConfigurableDomain->listAssociatedToElements(strResult);
270
271 return true;
272}
273
274bool CConfigurableDomains::split(const string& strDomain, CConfigurableElement* pConfigurableElement, string& strError)
275{
276 // Find domain
Patrick Benavoli0bd50542011-11-29 11:10:27 +0100277 CConfigurableDomain* pConfigurableDomain = findConfigurableDomain(strDomain, strError);
Patrick Benavoli68a91282011-08-31 11:23:23 +0200278
279 if (!pConfigurableDomain) {
280
Patrick Benavoli68a91282011-08-31 11:23:23 +0200281 return false;
282 }
283 // Delegate
284 pConfigurableDomain->split(pConfigurableElement, strError);
285
286 return true;
287}
288
289void CConfigurableDomains::listAssociatedElements(string& strResult) const
290{
291 strResult = "\n";
292
293 set<const CConfigurableElement*> configurableElementSet;
294
295 // Get all owned configurable elements
296 gatherAllOwnedConfigurableElements(configurableElementSet);
297
298 // Fill result
299 set<const CConfigurableElement*>::const_iterator it;
300
301 for (it = configurableElementSet.begin(); it != configurableElementSet.end(); ++it) {
302
303 const CConfigurableElement* pConfigurableElement = *it;
304
305 string strAssociatedDomainList;
306
307 pConfigurableElement->listAssociatedDomains(strAssociatedDomainList, false);
308
309 strResult += pConfigurableElement->getPath() + " [" + strAssociatedDomainList + "]\n";
310 }
311}
312
313void CConfigurableDomains::listConflictingElements(string& strResult) const
314{
315 strResult = "\n";
316
317 set<const CConfigurableElement*> configurableElementSet;
318
319 // Get all owned configurable elements
320 gatherAllOwnedConfigurableElements(configurableElementSet);
321
322 // Fill result
323 set<const CConfigurableElement*>::const_iterator it;
324
325 for (it = configurableElementSet.begin(); it != configurableElementSet.end(); ++it) {
326
327 const CConfigurableElement* pConfigurableElement = *it;
328
329 if (pConfigurableElement->getBelongingDomainCount() > 1) {
330
331 string strBelongingDomainList;
332
333 pConfigurableElement->listBelongingDomains(strBelongingDomainList, false);
334
335 strResult += pConfigurableElement->getPath() + " contained in multiple domains: " + strBelongingDomainList + "\n";
336 }
337 }
338}
339
Patrick Benavoli63499d42011-10-24 18:50:03 +0200340void CConfigurableDomains::listDomains(string& strResult) const
341{
342 strResult = "\n";
343
344 // List domains
345 uint32_t uiChild;
346 uint32_t uiNbConfigurableDomains = getNbChildren();
347
348 for (uiChild = 0; uiChild < uiNbConfigurableDomains; uiChild++) {
349
350 const CConfigurableDomain* pChildConfigurableDomain = static_cast<const CConfigurableDomain*>(getChild(uiChild));
351
352 // Name
353 strResult += pChildConfigurableDomain->getName();
354
355 // Sequence awareness
356 if (pChildConfigurableDomain->getSequenceAwareness()) {
357
Patrick Benavoli9bed7ce2011-11-20 20:04:35 +0100358 strResult += " [sequence aware]";
Patrick Benavoli63499d42011-10-24 18:50:03 +0200359 }
Patrick Benavoli9bed7ce2011-11-20 20:04:35 +0100360 strResult += "\n";
Patrick Benavoli63499d42011-10-24 18:50:03 +0200361 }
362}
363
Patrick Benavoli68a91282011-08-31 11:23:23 +0200364// Gather configurable elements owned by any domain
365void CConfigurableDomains::gatherAllOwnedConfigurableElements(set<const CConfigurableElement*>& configurableElementSet) const
366{
367 // Delegate to domains
368 uint32_t uiChild;
369 uint32_t uiNbConfigurableDomains = getNbChildren();
370
371 for (uiChild = 0; uiChild < uiNbConfigurableDomains; uiChild++) {
372
373 const CConfigurableDomain* pChildConfigurableDomain = static_cast<const CConfigurableDomain*>(getChild(uiChild));
374
375 pChildConfigurableDomain->gatherConfigurableElements(configurableElementSet);
376 }
377}
378
379// Config restore
Patrick Benavoli0bd50542011-11-29 11:10:27 +0100380bool CConfigurableDomains::restoreConfiguration(const string& strDomain, const string& strConfiguration, CParameterBlackboard* pMainBlackboard, bool bAutoSync, string& strError) const
Patrick Benavoli68a91282011-08-31 11:23:23 +0200381{
382 // Find domain
Patrick Benavoli0bd50542011-11-29 11:10:27 +0100383 const CConfigurableDomain* pConfigurableDomain = findConfigurableDomain(strDomain, strError);
Patrick Benavoli68a91282011-08-31 11:23:23 +0200384
385 if (!pConfigurableDomain) {
386
Patrick Benavoli68a91282011-08-31 11:23:23 +0200387 return false;
388 }
389 // Delegate
390 return pConfigurableDomain->restoreConfiguration(strConfiguration, pMainBlackboard, bAutoSync, strError);
391}
392
393// Config save
394bool CConfigurableDomains::saveConfiguration(const string& strDomain, const string& strConfiguration, const CParameterBlackboard* pMainBlackboard, string& strError)
395{
396 // Find domain
Patrick Benavoli0bd50542011-11-29 11:10:27 +0100397 CConfigurableDomain* pConfigurableDomain = findConfigurableDomain(strDomain, strError);
Patrick Benavoli68a91282011-08-31 11:23:23 +0200398
399 if (!pConfigurableDomain) {
400
Patrick Benavoli68a91282011-08-31 11:23:23 +0200401 return false;
402 }
403 // Delegate
404 return pConfigurableDomain->saveConfiguration(strConfiguration, pMainBlackboard, strError);
405}
406
Patrick Benavoli63499d42011-10-24 18:50:03 +0200407bool CConfigurableDomains::setElementSequence(const string& strDomain, const string& strConfiguration, const vector<string>& astrNewElementSequence, string& strError)
408{
409 // Find domain
Patrick Benavoli0bd50542011-11-29 11:10:27 +0100410 CConfigurableDomain* pConfigurableDomain = findConfigurableDomain(strDomain, strError);
Patrick Benavoli63499d42011-10-24 18:50:03 +0200411
412 if (!pConfigurableDomain) {
413
Patrick Benavoli63499d42011-10-24 18:50:03 +0200414 return false;
415 }
416
417 // Delegate to domain
418 return pConfigurableDomain->setElementSequence(strConfiguration, astrNewElementSequence, strError);
419}
420
421bool CConfigurableDomains::getElementSequence(const string& strDomain, const string& strConfiguration, string& strResult) const
422{
423 // Find domain
Patrick Benavoli0bd50542011-11-29 11:10:27 +0100424 const CConfigurableDomain* pConfigurableDomain = findConfigurableDomain(strDomain, strResult);
Patrick Benavoli63499d42011-10-24 18:50:03 +0200425
426 if (!pConfigurableDomain) {
427
Patrick Benavoli63499d42011-10-24 18:50:03 +0200428 return false;
429 }
430 // Delegate to domain
431 return pConfigurableDomain->getElementSequence(strConfiguration, strResult);
432}
433
Patrick Benavoli0bd50542011-11-29 11:10:27 +0100434bool CConfigurableDomains::setApplicationRule(const string& strDomain, const string& strConfiguration, const string& strApplicationRule, const CSelectionCriteriaDefinition* pSelectionCriteriaDefinition, string& strError)
435{
436 CConfigurableDomain* pConfigurableDomain = findConfigurableDomain(strDomain, strError);
437
438 if (!pConfigurableDomain) {
439
440 return false;
441 }
442
443 // Delegate to domain
444 return pConfigurableDomain->setApplicationRule(strConfiguration, strApplicationRule, pSelectionCriteriaDefinition, strError);
445}
446
447bool CConfigurableDomains::clearApplicationRule(const string& strDomain, const string& strConfiguration, string& strError)
448{
449 CConfigurableDomain* pConfigurableDomain = findConfigurableDomain(strDomain, strError);
450
451 if (!pConfigurableDomain) {
452
453 return false;
454 }
455
456 // Delegate to domain
457 return pConfigurableDomain->clearApplicationRule(strConfiguration, strError);
458}
459
460bool CConfigurableDomains::getApplicationRule(const string& strDomain, const string& strConfiguration, string& strResult) const
461{
462 const CConfigurableDomain* pConfigurableDomain = findConfigurableDomain(strDomain, strResult);
463
464 if (!pConfigurableDomain) {
465
466 return false;
467 }
468
469 // Delegate to domain
470 return pConfigurableDomain->getApplicationRule(strConfiguration, strResult);
471}
472
Patrick Benavoli68a91282011-08-31 11:23:23 +0200473// Last applied configurations
474void CConfigurableDomains::listLastAppliedConfigurations(string& strResult) const
475{
Patrick Benavoli68a91282011-08-31 11:23:23 +0200476 // Browse domains
477 uint32_t uiChild;
478 uint32_t uiNbConfigurableDomains = getNbChildren();
479
480 for (uiChild = 0; uiChild < uiNbConfigurableDomains; uiChild++) {
481
482 const CConfigurableDomain* pChildConfigurableDomain = static_cast<const CConfigurableDomain*>(getChild(uiChild));
483
Frédéric Boisnard8b243f52012-09-06 18:03:20 +0200484 strResult += pChildConfigurableDomain->getName() + ": " + pChildConfigurableDomain->getLastAppliedConfigurationName() + " [" + pChildConfigurableDomain->getPendingConfigurationName() + "]\n";
Patrick Benavoli68a91282011-08-31 11:23:23 +0200485 }
486}
487
488// Configurable element - domain association
489bool CConfigurableDomains::addConfigurableElementToDomain(const string& strDomain, CConfigurableElement* pConfigurableElement, const CParameterBlackboard* pMainBlackboard, string& strError)
490{
491 // Find domain
Patrick Benavoli0bd50542011-11-29 11:10:27 +0100492 CConfigurableDomain* pConfigurableDomain = findConfigurableDomain(strDomain, strError);
Patrick Benavoli68a91282011-08-31 11:23:23 +0200493
494 if (!pConfigurableDomain) {
495
Patrick Benavoli68a91282011-08-31 11:23:23 +0200496 return false;
497 }
498 // Delegate
499 return pConfigurableDomain->addConfigurableElement(pConfigurableElement, pMainBlackboard, strError);
500}
501
502bool CConfigurableDomains::removeConfigurableElementFromDomain(const string& strDomain, CConfigurableElement* pConfigurableElement, string& strError)
503{
504 // Find domain
Patrick Benavoli0bd50542011-11-29 11:10:27 +0100505 CConfigurableDomain* pConfigurableDomain = findConfigurableDomain(strDomain, strError);
Patrick Benavoli68a91282011-08-31 11:23:23 +0200506
507 if (!pConfigurableDomain) {
508
Patrick Benavoli68a91282011-08-31 11:23:23 +0200509 return false;
510 }
511 // Delegate
512 return pConfigurableDomain->removeConfigurableElement(pConfigurableElement, strError);
513}
514
515// Binary settings load/store
516bool CConfigurableDomains::serializeSettings(const string& strBinarySettingsFilePath, bool bOut, uint8_t uiStructureChecksum, string& strError)
517{
518 // Instantiate byte stream
519 CBinaryStream binarySettingsStream(strBinarySettingsFilePath, bOut, getDataSize(), uiStructureChecksum);
520
521 // Open file
522 if (!binarySettingsStream.open(strError)) {
523
524 strError = "Unable to open binary settings file " + strBinarySettingsFilePath + ": " + strError;
525
526 return false;
527 }
528
529 // Serialize
530 binarySerialize(binarySettingsStream);
531
532 // Close stream
533 binarySettingsStream.close();
534
535 return true;
536}
Patrick Benavoli0bd50542011-11-29 11:10:27 +0100537
538// Domain retrieval
539CConfigurableDomain* CConfigurableDomains::findConfigurableDomain(const string& strDomain, string& strError)
540{
541 // Find domain
542 CConfigurableDomain* pConfigurableDomain = static_cast<CConfigurableDomain*>(findChild(strDomain));
543
544 if (!pConfigurableDomain) {
545
546 strError = "Configurable domain " + strDomain + " not found";
547
548 return NULL;
549 }
550
551 return pConfigurableDomain;
552}
553
554const CConfigurableDomain* CConfigurableDomains::findConfigurableDomain(const string& strDomain, string& strError) const
555{
556 // Find domain
557 const CConfigurableDomain* pConfigurableDomain = static_cast<const CConfigurableDomain*>(findChild(strDomain));
558
559 if (!pConfigurableDomain) {
560
561 strError = "Configurable domain " + strDomain + " not found";
562
563 return NULL;
564 }
565
566 return pConfigurableDomain;
567}