001/* 002 * Copyright 2018 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.runtime.config; 017 018import java.io.File; 019import java.io.FileOutputStream; 020import java.io.OutputStream; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.Iterator; 026import java.util.List; 027import java.util.Map; 028import java.util.Properties; 029 030import javax.xml.transform.OutputKeys; 031import javax.xml.transform.TransformerFactory; 032import javax.xml.transform.TransformerFactoryConfigurationError; 033import javax.xml.transform.sax.SAXTransformerFactory; 034import javax.xml.transform.sax.TransformerHandler; 035import javax.xml.transform.stream.StreamResult; 036 037import org.apache.avalon.framework.activity.Initializable; 038import org.apache.avalon.framework.configuration.ConfigurationException; 039import org.apache.avalon.framework.context.Context; 040import org.apache.avalon.framework.context.Contextualizable; 041import org.apache.avalon.framework.service.ServiceException; 042import org.apache.avalon.framework.service.ServiceManager; 043import org.apache.avalon.framework.service.Serviceable; 044import org.apache.cocoon.xml.XMLUtils; 045import org.apache.commons.lang3.StringUtils; 046import org.apache.xml.serializer.OutputPropertiesFactory; 047import org.slf4j.Logger; 048import org.slf4j.LoggerFactory; 049import org.xml.sax.SAXException; 050 051import org.ametys.runtime.i18n.I18nizableText; 052import org.ametys.runtime.model.CategorizedElementDefinitionHelper; 053import org.ametys.runtime.model.CategorizedElementDefinitionProxy; 054import org.ametys.runtime.model.DefinitionAndValue; 055import org.ametys.runtime.model.ElementDefinition; 056import org.ametys.runtime.model.Enumerator; 057import org.ametys.runtime.model.Model; 058import org.ametys.runtime.model.ModelHelper; 059import org.ametys.runtime.model.ModelItem; 060import org.ametys.runtime.model.ModelItemGroup; 061import org.ametys.runtime.model.checker.ItemChecker; 062import org.ametys.runtime.model.exception.UndefinedItemPathException; 063import org.ametys.runtime.model.type.ElementType; 064import org.ametys.runtime.model.type.ModelItemTypeConstants; 065import org.ametys.runtime.model.type.xml.XMLElementType; 066import org.ametys.runtime.parameter.Validator; 067import org.ametys.runtime.plugin.component.ThreadSafeComponentManager; 068 069/** 070 * This manager handle the parameters of the application that have to be stored by the plugins. 071 */ 072public final class ConfigManager implements Model, Contextualizable, Serviceable, Initializable 073{ 074 // shared instance 075 private static ConfigManager __manager; 076 077 // Logger for traces 078 private Logger _logger = LoggerFactory.getLogger(ConfigManager.class); 079 private Logger _threadSafeComponentLogger = LoggerFactory.getLogger("runtime.plugin.threadsafecomponent"); 080 081 // Avalon stuff 082 private ServiceManager _serviceManager; 083 private Context _context; 084 085 // ComponentManager for the validators 086 private ThreadSafeComponentManager<Validator> _validatorManager; 087 // ComponentManager for the enumerators 088 private ThreadSafeComponentManager<Enumerator> _enumeratorManager; 089 // ComponentManager for the parameter checkers 090 private ThreadSafeComponentManager<ItemChecker> _parameterCheckerManager; 091 092 private Map<String, ConfigParameterInfo> _declaredParams; 093 private Map<String, ConfigParameterInfo> _declaredParamCheckers; 094 private Collection<String> _usedParamIds; 095 private Map<String, CategorizedElementDefinitionProxy> _categorizedParameterProxies; 096 private Map<String, ConfigParameterCheckerDescriptor> _parameterCheckers; 097 098 // The configuration model items 099 private List<ModelItem> _categorizedDefinitions; 100 private Map<String, ElementDefinition> _flatDefinitions; 101 102 // Determines if the extension point is initialized 103 private boolean _isInitialized; 104 // Determines if all parameters are valued 105 private boolean _isComplete; 106 107 private ConfigParameterTypeExtensionPoint _configParameterTypeEP; 108 109 private ConfigManager() 110 { 111 // empty constructor 112 } 113 114 /** 115 * Returns the shared instance of the ConfigManager 116 * @return the shared instance of the ConfigManager 117 */ 118 public static ConfigManager getInstance() 119 { 120 if (__manager == null) 121 { 122 __manager = new ConfigManager(); 123 } 124 125 return __manager; 126 } 127 128 @Override 129 public void contextualize(Context context) 130 { 131 _context = context; 132 } 133 134 @Override 135 public void service(ServiceManager manager) throws ServiceException 136 { 137 _serviceManager = manager; 138 _configParameterTypeEP = (ConfigParameterTypeExtensionPoint) _serviceManager.lookup(ConfigParameterTypeExtensionPoint.ROLE); 139 } 140 141 @Override 142 public void initialize() 143 { 144 _usedParamIds = new ArrayList<>(); 145 _declaredParams = new HashMap<>(); 146 _categorizedParameterProxies = new HashMap<>(); 147 _declaredParamCheckers = new HashMap<>(); 148 _parameterCheckers = new HashMap<>(); 149 _flatDefinitions = new HashMap<>(); 150 151 _validatorManager = new ThreadSafeComponentManager<>(); 152 _validatorManager.setLogger(_threadSafeComponentLogger); 153 _validatorManager.contextualize(_context); 154 _validatorManager.service(_serviceManager); 155 156 _enumeratorManager = new ThreadSafeComponentManager<>(); 157 _enumeratorManager.setLogger(_threadSafeComponentLogger); 158 _enumeratorManager.contextualize(_context); 159 _enumeratorManager.service(_serviceManager); 160 161 _parameterCheckerManager = new ThreadSafeComponentManager<>(); 162 _parameterCheckerManager.setLogger(_threadSafeComponentLogger); 163 _parameterCheckerManager.contextualize(_context); 164 _parameterCheckerManager.service(_serviceManager); 165 } 166 167 /** 168 * Registers new available parameters. 169 * The addFeatureConfig() method allows to select which ones are actually useful. 170 * @param pluginName the name of the plugin defining the parameters 171 * @param parameters the configuration parameters definition 172 * @param parameterCheckers the parameters checkers definition 173 */ 174 public void addPluginConfig(String pluginName, Map<String, ConfigParameterInfo> parameters, Map<String, ConfigParameterInfo> parameterCheckers) 175 { 176 _logger.debug("Adding parameters and parameters checkers for plugin {}.", pluginName); 177 178 // declare parameters and parameter checkers configured in the plugin 179 _declareParameters(parameters); 180 _declareParameterCheckers(parameterCheckers); 181 } 182 183 /** 184 * Registers a new parameter or references a globalConfig parameter.<br> 185 * @param featureId the id of the feature defining the parameters 186 * @param parameters the configuration parameters definition 187 * @param parameterCheckers the parameters checkers definition 188 * @param parameterReferences references to already defined parameters 189 */ 190 public void addFeatureConfig(String featureId, Map<String, ConfigParameterInfo> parameters, Map<String, ConfigParameterInfo> parameterCheckers, Collection<String> parameterReferences) 191 { 192 _logger.debug("Selecting parameters for feature {}.", featureId); 193 194 // declare parameters and parameter checkers configured in the feature 195 _declareParameters(parameters); 196 _declareParameterCheckers(parameterCheckers); 197 198 // Add parameters declared in feature to the list of used parameters 199 _usedParamIds.addAll(parameters.keySet()); 200 201 // Add referenced parameters to the list of used parameters 202 _usedParamIds.addAll(parameterReferences); 203 } 204 205 private void _declareParameters(Map<String, ConfigParameterInfo> parameters) 206 { 207 for (String id : parameters.keySet()) 208 { 209 ConfigParameterInfo info = parameters.get(id); 210 211 // Check if the parameter is not already declared 212 if (_declaredParams.containsKey(id)) 213 { 214 throw new IllegalArgumentException("The config parameter '" + id + "' is already declared. Parameters ids must be unique"); 215 } 216 217 // Add the new parameter to the list of declared ones 218 _declaredParams.put(id, info); 219 220 _logger.debug("Parameter added: {}", id); 221 } 222 223 _logger.debug("{} parameter(s) added", parameters.size()); 224 } 225 226 private void _declareParameterCheckers(Map<String, ConfigParameterInfo> parameterCheckers) 227 { 228 for (String id : parameterCheckers.keySet()) 229 { 230 ConfigParameterInfo info = parameterCheckers.get(id); 231 232 // Check if the parameter checker is not already declared 233 if (_declaredParamCheckers.containsKey(id)) 234 { 235 throw new IllegalArgumentException("The config parameter checker '" + id + "' is already declared. Parameter checkers ids must be unique."); 236 } 237 238 // Add the new parameter checker to the list of declared ones 239 _declaredParamCheckers.put(id, info); 240 241 _logger.debug("Parameter checker added: {}", id); 242 } 243 244 _logger.debug("{} parameter checker(s) added", parameterCheckers.size()); 245 } 246 247 /** 248 * Ends the initialization of the config parameters, by checking against the 249 * already valued parameters.<br> 250 * If at least one parameter has no value, the application won't start. 251 */ 252 public void parseAndValidate() 253 { 254 _logger.debug("Initialization"); 255 256 _isInitialized = false; 257 _isComplete = true; 258 259 ConfigParameterDefinitionParser parser = new ConfigParameterDefinitionParser(_configParameterTypeEP, _enumeratorManager, _validatorManager); 260 _parseParameters(parser); 261 262 ConfigParameterCheckerParser parameterCheckerParser = new ConfigParameterCheckerParser(_parameterCheckerManager); 263 _parseParameterCheckers(parameterCheckerParser); 264 265 _categorizeParameters(); 266 267 try 268 { 269 parser.lookupComponents(); 270 parameterCheckerParser.lookupComponents(); 271 } 272 catch (Exception e) 273 { 274 throw new RuntimeException("Unable to lookup parameter local components", e); 275 } 276 277 _validateParametersForReading(); 278 279 _usedParamIds.clear(); 280 _declaredParams.clear(); 281 _declaredParamCheckers.clear(); 282 283 _isInitialized = true; 284 285 Config.setInitialized(_isComplete); 286 287 _logger.debug("Initialization ended"); 288 } 289 290 private void _parseParameters(ConfigParameterDefinitionParser parser) 291 { 292 for (String id : _usedParamIds) 293 { 294 // Check if the parameter is not already used 295 if (_categorizedParameterProxies.get(id) == null) 296 { 297 // Move the parameter from the unused list, to the used list 298 ConfigParameterInfo parameterInfo = _declaredParams.get(id); 299 300 if (parameterInfo == null) 301 { 302 throw new RuntimeException("The parameter '" + id + "' is used but not declared"); 303 } 304 305 CategorizedElementDefinitionProxy categorizedDefinitionProxy = null; 306 307 try 308 { 309 categorizedDefinitionProxy = parser.parse(_serviceManager, parameterInfo.getPluginName(), parameterInfo.getConfiguration(), getInstance(), null); 310 } 311 catch (ConfigurationException ex) 312 { 313 throw new RuntimeException("Unable to configure the config parameter : " + id, ex); 314 } 315 316 _categorizedParameterProxies.put(id, categorizedDefinitionProxy); 317 } 318 } 319 } 320 321 private void _parseParameterCheckers(ConfigParameterCheckerParser parameterCheckerParser) 322 { 323 for (String id : _declaredParamCheckers.keySet()) 324 { 325 boolean invalidParameters = false; 326 327 // Check if the parameter checker is not already used 328 if (_parameterCheckers.get(id) == null) 329 { 330 ConfigParameterInfo info = _declaredParamCheckers.get(id); 331 332 ConfigParameterCheckerDescriptor parameterChecker = null; 333 try 334 { 335 336 parameterChecker = parameterCheckerParser.parseParameterChecker(info.getPluginName(), info.getConfiguration()); 337 } 338 catch (ConfigurationException ex) 339 { 340 throw new RuntimeException("Unable to configure the parameter checker: " + id, ex); 341 } 342 343 for (String linkedParameterPath : parameterChecker.getLinkedParamsPaths()) 344 { 345 CategorizedElementDefinitionProxy linkedParameter = null; 346 347 // Linked parameters can be declared with an absolute path, in which case they are prefixed with '/ 348 if (linkedParameterPath.startsWith(ModelItem.ITEM_PATH_SEPARATOR)) 349 { 350 linkedParameter = _categorizedParameterProxies.get(linkedParameterPath.substring(ModelItem.ITEM_PATH_SEPARATOR.length())); 351 } 352 else 353 { 354 linkedParameter = _categorizedParameterProxies.get(linkedParameterPath); 355 } 356 357 // If at least one parameter used is invalid, the parameter checker is invalidated 358 if (linkedParameter == null) 359 { 360 invalidParameters = true; 361 break; 362 } 363 } 364 365 if (invalidParameters) 366 { 367 _logger.debug("All the configuration parameters associated to the parameter checker '{}' are not used.\nThis parameter checker will be ignored.", parameterChecker.getName()); 368 } 369 else 370 { 371 _parameterCheckers.put(id, parameterChecker); 372 } 373 } 374 } 375 } 376 377 private void _categorizeParameters() 378 { 379 Collection<CategorizedElementDefinitionProxy> categorizedParameterProxiesValues = _categorizedParameterProxies.values(); 380 _categorizedDefinitions = CategorizedElementDefinitionHelper.categorize(categorizedParameterProxiesValues); 381 _flatDefinitions = CategorizedElementDefinitionHelper.getFlatDefinitions(categorizedParameterProxiesValues); 382 383 // Add parameter checkers to categories, groups and element definitions 384 _addParameterCheckersToModelItems(); 385 } 386 387 private void _addParameterCheckersToModelItems() 388 { 389 for (ConfigParameterCheckerDescriptor parameterChecker: _parameterCheckers.values()) 390 { 391 I18nizableText uiCategory = parameterChecker.getUiRefCategory(); 392 if (uiCategory != null) 393 { 394 ModelItemGroup category = _getModelItemGroup(_categorizedDefinitions, uiCategory); 395 if (category == null) 396 { 397 _logger.warn("The category {} doesn't exist, thus the parameter checker {} will not be added.", uiCategory, parameterChecker.getName()); 398 } 399 else 400 { 401 I18nizableText uiGroup = parameterChecker.getUiRefGroup(); 402 if (uiGroup == null) 403 { 404 category.addItemChecker(parameterChecker); 405 } 406 else 407 { 408 ModelItemGroup group = _getModelItemGroup(category.getChildren(), uiGroup); 409 if (group == null) 410 { 411 _logger.warn("The group {} doesn't exist, thus the parameter checker {} will not be added.", uiGroup, parameterChecker.getName()); 412 } 413 else 414 { 415 group.addItemChecker(parameterChecker); 416 } 417 } 418 } 419 } 420 else 421 { 422 String uiParameterId = parameterChecker.getUiRefParamId(); 423 if (uiParameterId != null) 424 { 425 ElementDefinition definition = _flatDefinitions.get(uiParameterId); 426 if (definition == null) 427 { 428 _logger.warn("The parameter {} doesn't exist, thus the parameter checker {} will not be added.", uiParameterId, parameterChecker.getName()); 429 } 430 else 431 { 432 definition.addItemChecker(parameterChecker); 433 } 434 } 435 } 436 } 437 } 438 439 private ModelItemGroup _getModelItemGroup(List<ModelItem> items, I18nizableText itemGroupLabel) 440 { 441 for (ModelItem item : items) 442 { 443 if (itemGroupLabel.equals(item.getLabel()) && item instanceof ModelItemGroup) 444 { 445 return (ModelItemGroup) item; 446 } 447 } 448 449 return null; 450 } 451 452 private void _validateParametersForReading() 453 { 454 // Dispose potential previous parameters 455 Config.dispose(); 456 457 // Get configuration values 458 Map<String, DefinitionAndValue> definitionAndValues = null; 459 try 460 { 461 Config.setModel(this); 462 definitionAndValues = Config.__read(); 463 } 464 catch (Exception e) 465 { 466 _logger.error("Cannot read the configuration file.", e); 467 _isComplete = false; 468 } 469 470 if (_isComplete && definitionAndValues != null) 471 { 472 for (ElementDefinition definition : _flatDefinitions.values()) 473 { 474 boolean isGroupSwitchOn = ModelHelper.isGroupSwitchOn(definition, Config.__extractValues(definitionAndValues)); 475 boolean isDisabled = ModelHelper.evaluateDisableConditions(definition.getDisableConditions(), definitionAndValues, _logger); 476 477 if (isGroupSwitchOn && !isDisabled) 478 { 479 DefinitionAndValue definitionAndValue = definitionAndValues.get(definition.getName()); 480 if (definitionAndValue == null) 481 { 482 _logger.warn("The parameter '" + definition.getName() + "' is not valued. Configuration is not initialized."); 483 _isComplete = false; 484 } 485 else 486 { 487 Object value = definitionAndValue.getValue(); 488 List<I18nizableText> errors = ModelHelper.validateValue(definition, value); 489 if (!errors.isEmpty()) 490 { 491 if (_logger.isWarnEnabled()) 492 { 493 StringBuilder sb = new StringBuilder("The parameter '" + definition.getName() + "' is not valid with value '" + value + "' :"); 494 495 for (I18nizableText error : errors) 496 { 497 sb.append("\n* " + error.toString()); 498 } 499 sb.append("\nConfiguration is not initialized"); 500 501 _logger.warn(sb.toString()); 502 } 503 504 _isComplete = false; 505 } 506 } 507 508 } 509 } 510 511 } 512 } 513 514 /** 515 * Update the configuration file with the given values<br> 516 * Values are untyped (all are of type String) and might be null. 517 * @param values A map (key, value). 518 * @param fileName the configuration file absolute path 519 * @return errors The fields in error 520 * @throws Exception If an error occurred while saving values 521 */ 522 public Map<String, List<I18nizableText>> save(Map<String, Object> values, String fileName) throws Exception 523 { 524 // Retrieve the old values for password purposes 525 Map<String, DefinitionAndValue> oldDefinitionAndValues = getOldDefinitionAndValues(); 526 527 // Resolve each value and associate it to its definition 528 Map<String, DefinitionAndValue> definitionAndValues = _getDefinitionAndResolvedValues(values, oldDefinitionAndValues); 529 530 //TODO NEWATTRIBUTEAPI_CONFIG RUNTIME-2851 when definitionAndValues list will be complete (now it contains only the values sent, not all the definition + values/null), the call with _flatDefinitions can be switched to a call without _flatDefinitions 531 // Validate parameters 532 Map<String, List<I18nizableText>> errorFields = CategorizedElementDefinitionHelper.validateValuesForWriting(definitionAndValues, _flatDefinitions, _logger); 533 if (!errorFields.isEmpty()) 534 { 535 return errorFields; 536 } 537 538 // SAX 539 _saxConfigurationFile(fileName, definitionAndValues); 540 541 return Collections.EMPTY_MAP; 542 } 543 544 /** 545 * Retrieves old definition and values if the configuration is not well initialized 546 * @return the old definition and values pairs 547 */ 548 public Map<String, DefinitionAndValue> getOldDefinitionAndValues() 549 { 550 Map<String, DefinitionAndValue> oldDefinitionAndValues = null; 551 if (Config.getInstance() == null) 552 { 553 try 554 { 555 oldDefinitionAndValues = Config.__read(); 556 } 557 catch (Exception e) 558 { 559 oldDefinitionAndValues = new HashMap<>(); 560 } 561 } 562 return oldDefinitionAndValues; 563 } 564 565 private Map<String, DefinitionAndValue> _getDefinitionAndResolvedValues(Map<String, Object> values, Map<String, DefinitionAndValue> oldDefinitionAndValues) 566 { 567 Map<String, DefinitionAndValue> definitionAndValues = new HashMap<>(); 568 for (Map.Entry<String, Object> value : values.entrySet()) 569 { 570 ElementDefinition definition = _flatDefinitions.get(value.getKey()); 571 if (definition != null) 572 { 573 Object resolvedValue = _resolveValue(definition, value.getValue(), oldDefinitionAndValues); 574 DefinitionAndValue definitionAndValue = new DefinitionAndValue<>(null, definition, resolvedValue); 575 definitionAndValues.put(value.getKey(), definitionAndValue); 576 } 577 } 578 return definitionAndValues; 579 } 580 581 /** 582 * Resolve the given value for the parameter check 583 * @param parameterName name of the parameter to resolve 584 * @param value the value to resolve 585 * @param oldDefinitionAndValues the old definition and values if the configuration is not initialized 586 * @return the resolved value as a String 587 */ 588 public String resolveValueForParameterChecker(String parameterName, Object value, Map<String, DefinitionAndValue> oldDefinitionAndValues) 589 { 590 ElementDefinition definition = _flatDefinitions.get(parameterName); 591 592 if (definition == null) 593 { 594 return null; 595 } 596 597 Object resolvedValue = _resolveValue(definition, value, oldDefinitionAndValues); 598 return definition.getType().toString(resolvedValue); 599 } 600 601 private Object _resolveValue(ElementDefinition definition, Object value, Map<String, DefinitionAndValue> oldDefinitionAndValues) 602 { 603 String parameterId = definition.getName(); 604 ElementType type = definition.getType(); 605 Object resolvedValue = value; 606 607 // keeps the value of an empty password field 608 if (value == null && ModelItemTypeConstants.PASSWORD_ELEMENT_TYPE_ID.equals(type.getId())) 609 { 610 if (Config.getInstance() != null) 611 { 612 resolvedValue = Config.getInstance().getValue(parameterId); 613 } 614 else if (oldDefinitionAndValues != null) 615 { 616 Object oldValue = null; 617 DefinitionAndValue oldDefinitionAdValue = oldDefinitionAndValues.get(definition.getName()); 618 if (oldDefinitionAdValue != null) 619 { 620 oldValue = oldDefinitionAdValue.getValue(); 621 } 622 resolvedValue = oldValue; 623 } 624 } 625 626 return resolvedValue; 627 } 628 629 private void _saxConfigurationFile(String fileName, Map<String, DefinitionAndValue> definitionAndValues) throws TransformerFactoryConfigurationError, Exception 630 { 631 // create the result where to write 632 File outputFile = new File(fileName); 633 outputFile.getParentFile().mkdirs(); 634 635 try (OutputStream os = new FileOutputStream(fileName)) 636 { 637 // create a transformer for saving sax into a file 638 TransformerHandler th = ((SAXTransformerFactory) TransformerFactory.newInstance()).newTransformerHandler(); 639 640 StreamResult result = new StreamResult(os); 641 th.setResult(result); 642 643 // create the format of result 644 Properties format = new Properties(); 645 format.put(OutputKeys.METHOD, "xml"); 646 format.put(OutputKeys.INDENT, "yes"); 647 format.put(OutputKeys.ENCODING, "UTF-8"); 648 format.put(OutputPropertiesFactory.S_KEY_INDENT_AMOUNT, "2"); 649 th.getTransformer().setOutputProperties(format); 650 651 // sax the configuration into the transformer 652 _saxParameters(th, Config.__extractValues(definitionAndValues)); 653 } 654 catch (Exception e) 655 { 656 throw new Exception("An error occured while saving the config values.", e); 657 } 658 } 659 660 private void _saxParameters(TransformerHandler handler, Map<String, Object> values) throws SAXException 661 { 662 handler.startDocument(); 663 XMLUtils.startElement(handler, "config"); 664 665 666 // Iterate over categorized parameters 667 for (ModelItem category : _categorizedDefinitions) 668 { 669 if (!(category instanceof ModelItemGroup)) 670 { 671 // Should not happen, categories are created in categorizeParameters method 672 continue; 673 } 674 675 StringBuilder categoryLabel = new StringBuilder(); 676 categoryLabel.append("+\n | "); 677 categoryLabel.append(category.getLabel().toString()); 678 categoryLabel.append("\n +"); 679 680 // Comment on current category 681 XMLUtils.data(handler, "\n "); 682 handler.comment(categoryLabel.toString().toCharArray(), 0, categoryLabel.length()); 683 XMLUtils.data(handler, "\n"); 684 XMLUtils.data(handler, "\n"); 685 686 Iterator<ModelItem> groups = ((ModelItemGroup) category).getChildren().iterator(); 687 while (groups.hasNext()) 688 { 689 ModelItem group = groups.next(); 690 if (!(group instanceof ModelItemGroup)) 691 { 692 // Should not happen, groups are created in categorizeParameters method 693 continue; 694 } 695 696 StringBuilder groupLabel = new StringBuilder(); 697 groupLabel.append(" "); 698 groupLabel.append(group.getLabel().toString()); 699 groupLabel.append(" "); 700 701 // Comment on current group 702 XMLUtils.data(handler, " "); 703 handler.comment(groupLabel.toString().toCharArray(), 0, groupLabel.length()); 704 XMLUtils.data(handler, "\n "); 705 706 for (ModelItem definition : ((ModelItemGroup) group).getChildren()) 707 { 708 if (!(definition instanceof ElementDefinition)) 709 { 710 // Should not happen, parameter references are created in categorizeParameters method 711 continue; 712 } 713 714 Object value = values.get(definition.getName()); 715 ElementType<Object> type = ((ElementDefinition) definition).getType(); 716 717 if (!(type instanceof XMLElementType)) 718 { 719 // Should not happen, configuration parameters only work with XML element types 720 continue; 721 } 722 723 ((XMLElementType<Object>) type).write(handler, definition.getName(), value); 724 } 725 726 if (groups.hasNext()) 727 { 728 XMLUtils.data(handler, "\n"); 729 } 730 } 731 732 XMLUtils.data(handler, "\n"); 733 } 734 735 XMLUtils.endElement(handler, "config"); 736 handler.endDocument(); 737 } 738 739 /** 740 * Recursively evaluate the {@link DisableConditions} against the configuration values 741 * @param disableConditions the disable conditions to evaluate 742 * @param values the configuration values 743 * @return true if the disable conditions are true, false otherwise 744 */ 745 public boolean evaluateDisableConditions(DisableConditions disableConditions, Map<String, Object> values) 746 { 747 Map<String, DefinitionAndValue> definitionAndValues = _getDefinitionAndValues(values); 748 return ModelHelper.evaluateDisableConditions(disableConditions, definitionAndValues, _logger); 749 } 750 751 private Map<String, DefinitionAndValue> _getDefinitionAndValues(Map<String, Object> values) 752 { 753 Map<String, DefinitionAndValue> definitionAndValues = new HashMap<>(); 754 for (Map.Entry<String, Object> value : values.entrySet()) 755 { 756 ElementDefinition definition = _flatDefinitions.get(value.getKey()); 757 if (definition != null) 758 { 759 DefinitionAndValue definitionAndValue = new DefinitionAndValue<>(null, definition, value.getValue()); 760 definitionAndValues.put(value.getKey(), definitionAndValue); 761 } 762 } 763 return definitionAndValues; 764 } 765 766 public ModelItem getModelItem(String itemPath) throws UndefinedItemPathException 767 { 768 ModelItem item = _flatDefinitions.get(itemPath); 769 if (item != null) 770 { 771 return item; 772 } 773 else 774 { 775 throw new UndefinedItemPathException("The parameter '" + itemPath + "' is not defined in the configuration."); 776 } 777 } 778 779 /** 780 * Retrieves all configuration parameters 781 * @return all configuration parameters 782 */ 783 public Collection<ElementDefinition> getConfigurationParameters() 784 { 785 return _flatDefinitions.values(); 786 } 787 788 public Collection<ModelItem> getModelItems() 789 { 790 return _categorizedDefinitions; 791 } 792 793 /** 794 * Gets the parameter checker with its id 795 * @param id the id of the parameter checker to get 796 * @return the associated parameter checker descriptor 797 */ 798 public ConfigParameterCheckerDescriptor getParameterChecker(String id) 799 { 800 return _parameterCheckers.get(id); 801 } 802 803 /** 804 * Returns true if the model is initialized and all parameters are valued 805 * @return true if the model is initialized and all parameters are valued 806 */ 807 public boolean isComplete() 808 { 809 return _isInitialized && _isComplete; 810 } 811 812 /** 813 * Returns true if the config file does not exist 814 * @return true if the config file does not exist 815 */ 816 public boolean isEmpty() 817 { 818 return !Config.fileExists(); 819 } 820 821 /** 822 * Dispose the manager before restarting it 823 */ 824 public void dispose() 825 { 826 _isInitialized = false; 827 _isComplete = true; 828 829 _usedParamIds = null; 830 _declaredParams = null; 831 _categorizedParameterProxies = null; 832 _declaredParamCheckers = null; 833 _parameterCheckers = null; 834 _flatDefinitions = null; 835 836 if (_validatorManager != null) 837 { 838 _validatorManager.dispose(); 839 _validatorManager = null; 840 } 841 if (_enumeratorManager != null) 842 { 843 _enumeratorManager.dispose(); 844 _enumeratorManager = null; 845 } 846 if (_parameterCheckerManager != null) 847 { 848 _parameterCheckerManager.dispose(); 849 _parameterCheckerManager = null; 850 } 851 } 852 853 public String getId() 854 { 855 return StringUtils.EMPTY; 856 } 857 858 public String getFamilyId() 859 { 860 return this.getClass().getName(); 861 } 862}