private static Map<String, String> processProperties(Map<String, String> previousStackProperties,
                                                       Map<String, String> newStackProperties,
                                                       Map<String, String> userProperties) {

    if ((previousStackProperties == null) && (newStackProperties == null)) {
      return userProperties;
    } else {
      Map<String, String> updatedProperties = new TreeMap<>();
      if (userProperties != null) {
        updatedProperties.putAll(userProperties);
      }

      if (previousStackProperties == null) {
        previousStackProperties = Collections.emptyMap();
      }

      if (newStackProperties == null) {
        newStackProperties = Collections.emptyMap();
      }

      // Find properties to modify and remove
      for (Map.Entry<String, String> entry : previousStackProperties.entrySet()) {
        String name = entry.getKey();

        if (newStackProperties.containsKey(name)) {
          String previousValue = entry.getValue();
          String newValue = newStackProperties.get(name);
          String userValue = updatedProperties.get(name);

          // See if the user property should be modified...
          // Test if the old property value is different than the new property value and that the user did
          // not update the value from the old default. If the user updated the value, then it would be
          // risky to change it to the new stack default value.
          if (((previousValue == null) ? (newValue != null) : !previousValue.equals(newValue)) &&
              ((previousValue == null) ? (userValue == null) : previousValue.equals(userValue))) {
            LOG.debug("Modifying property named {} from user-specified Kerberos Descriptor", name);
            updatedProperties.put(name, newValue);
          }
        } else {
          LOG.debug("Removing property named {} from user-specified Kerberos Descriptor", name);
          updatedProperties.remove(name);
        }
      }

      // Find properties to add
      for (Map.Entry<String, String> entry : newStackProperties.entrySet()) {
        String name = entry.getKey();

        if (!previousStackProperties.containsKey(name) && !updatedProperties.containsKey(name)) {
          // A new property was found, add it...
          LOG.debug("Adding property named {} to user-specified Kerberos Descriptor", name);
          updatedProperties.put(name, entry.getValue());
        }
      }

      return updatedProperties;
    }
  }
Processes a map of global or configuration properties to change the user-supplied data based on
the changes observed between the previous stack version's data and the new stack version's data.
<p>
If a property exists in both the previous and new stacks, and the user has not changed it; then
the value of the property will be updated to the new stack version's value.  Else, if the user
changed the value, the changed value will be left as-is.
<p>
If a property exists only in the previous stack, then it will be removed.
<p>
If a property exists only in the new stack, then it will be added.

@param previousStackProperties properties from the previous stack version's Kerberos descriptor
@param newStackProperties      properties from the new stack version's Kerberos descriptor
@param userProperties          properties from the user-specified Kerberos descriptor
@return a new map of updated properties
ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptorUpdateHelper.java