content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package org.apache.ambari.server.stack;
  
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Map;
  import java.util.Set;
  
  import org.apache.ambari.server.state.PropertyInfo;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  
  /**
   * Encapsulates configuration properties and attributes for a single type.
   */
  public class ConfigurationInfo implements Validable{
    /**
     * Collection of properties
     */
    private Collection<PropertyInfo> properties;
  
    /**
     * Map of attribute category to a map of attribute name/value
     */
    private Map<String, Map<String, String>> attributes;
  
    /**
     * Logger instance
     */
    private final static Logger LOG = LoggerFactory.getLogger(ConfigurationInfo.class);
  
    /**
     * validity flag
     */
    protected boolean valid = true;
    
    /**
     * Constructor.
     *
     * @param properties  configuration properties
     * @param attributes  configuration attributes
     */
    public ConfigurationInfo(Collection<PropertyInfo> properties, Map<String, String> attributes) {
      this.properties = properties;
      setAttributes(attributes);
    }
  
    /**
     * Obtain the configuration properties.
     *
     * @return collection of properties
     */
    public Collection<PropertyInfo> getProperties() {
      return properties;
    }
  
    /**
     * Obtain the configuration attributes.
     *
     * @return map of attribute category to a map of attribute names/values
     */
    public  Map<String, Map<String, String>> getAttributes() {
      return attributes;
    }
  
    /**
     * Set the default value of all type attributes which are not already specified.
     */
    public void ensureDefaultAttributes() {
      Map<String, String> supportsAttributes = attributes.get(Supports.KEYWORD);
      for (Supports supportsProperty : Supports.values()) {
        String propertyName = supportsProperty.getPropertyName();
        if (! supportsAttributes.containsKey(propertyName)) {
          supportsAttributes.put(propertyName, supportsProperty.getDefaultValue());
        }
      }
    }
  
    /**
     *
     * Set the specified configuration type attributes.
     *
     * @param specifiedAttributes attributes that have been specified in configuration
     */
    private void setAttributes(Map<String, String> specifiedAttributes) {
      Map<String, Map<String, String>> attributes = new HashMap<>();
      Map<String, String> supportsAttributes = new HashMap<>();
      attributes.put(Supports.KEYWORD, supportsAttributes);
  
      for (Map.Entry<String, String> entry : specifiedAttributes.entrySet()) {
        String attributeName = entry.getKey();
        Supports s = Supports.attributeNameValueOf(attributeName);
        if (s != null) {
          supportsAttributes.put(s.getPropertyName(),
              Boolean.valueOf(entry.getValue()).toString());
        } else {
          LOG.warn("Unknown configuration type attribute is specified: {}={}", attributeName, entry.getValue());
        }
      }
      this.attributes = attributes;
    }
  
    @Override
    public boolean isValid() {
      return valid;
    }
  
    @Override
    public void setValid(boolean valid) {
      this.valid = valid;
    }
  
    private Set<String> errorSet = new HashSet<>();
    
    @Override
    public void addError(String error) {
      errorSet.add(error);
    }
  
    @Override
    public Collection<String> getErrors() {
      return errorSet;
    }   
  
    @Override
    public void addErrors(Collection<String> errors) {
      this.errorSet.addAll(errors);
    }  
    
    /**
     * Service configuration-types can support different abilities. This
     * enumerates the various abilities that configuration-types can support.
     *
     * For example, Hadoop configuration types like 'core-site' and 'hdfs-site'
     * can support the ability to define certain configs as 'final'.
     */
    public enum Supports {
  
      FINAL("supports_final"),
      ADDING_FORBIDDEN("supports_adding_forbidden"),
      DO_NOT_EXTEND("supports_do_not_extend");
  
      public static final String KEYWORD = "supports";
  
      private String defaultValue;
      private String xmlAttributeName;
  
      Supports(String xmlAttributeName) {
        this(xmlAttributeName, Boolean.FALSE.toString());
      }
  
      Supports(String xmlAttributeName, String defaultValue) {
        this.defaultValue = defaultValue;
        this.xmlAttributeName = xmlAttributeName;
      }
  
      public String getDefaultValue() {
        return defaultValue;
      }
  
      public String getXmlAttributeName() {
        return xmlAttributeName;
      }
  
      public String getPropertyName() {
        return name().toLowerCase();
      }
  
      public static Supports attributeNameValueOf(String attributeName) {
        for (Supports s : values()) {
          if (s.getXmlAttributeName().equals(attributeName)) {
            return s;
          }
        }
        return null;
      }
    }
  }
  