private static int mapFromUpdated(
            Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem workItem,
            EnterpriseChangeRequest newEcr, String fieldName)
        {
            int noOfMappedChanged = 0;

            // TODO: For the "calculated" fields we have defined ourselves we know the
            // dependencies, but should be expressed more elegant than this. Also the
            // Ericsson.Defect.User.DisplayName is dependent on System.AssignedTo but
            // will not be updated - we "know" that ...
            if (fieldName.Equals(TFSMapper.TFS_OWNER))
            {
                fieldName = TFSMapper.ERICSSON_DEFECT_USER_SIGNUM;
            }

            // Can be multiple ECR properties updated by one TFS field
            List <Property> props = props = AttributesMapper.getInstance().getNotifyProperties(fieldName);

            if (props != null && props.Count > 0)
            {
                foreach (Property prop in props)
                {
                    if (prop.getNotifyChange() && TFSMapper.getInstance().setEcrValues(newEcr, prop.getKey(), workItem))
                    {
                        noOfMappedChanged++;
                    }
                }
            }

            return(noOfMappedChanged);
        }
        // ===============================================

        static public String GetTfsValueForEcrKey(String propertyName, WorkItem workItem)
        {
            List <String> values = TFSMapper.getInstance().mapToEcr(propertyName, workItem);

            if (values.Count == 0)
            {
                HandlerSettings.LogMessage(
                    "Missing map entry for 'key': " + propertyName,
                    HandlerSettings.LoggingLevel.INFO);
                return("");
            }
            else if (values.Count > 1)
            {
                HandlerSettings.LogMessage(
                    "More than one value for 'key': " + propertyName,
                    HandlerSettings.LoggingLevel.WARN);
            }

            return(values[0]);
        }
        // Return an EnterpriseChangeRequest with mapped values for the update
        public static EnterpriseChangeRequest mapFromUpdatedWorkitem(
            Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem workItem,
            Uri about,
            WorkItemChangedEvent notification)
        {
            EnterpriseChangeRequest newEcr = new EnterpriseChangeRequest();

            newEcr.SetAbout(about);

            // The notification contain all changed fields. To understand what to
            // propagate to the client, we need to check which ecm fields that are
            // affected by the changes and are configured for notifyChange

            int noOfMappedChanged = 0;

            if (notification.ChangedFields != null)
            {
                // Most fields are String fields
                StringField[] changedStrFields = notification.ChangedFields.StringFields;
                if (changedStrFields != null)
                {
                    for (int i = 0; i < changedStrFields.Length; i++)
                    {
                        String fieldName = changedStrFields[i].ReferenceName;
                        noOfMappedChanged += mapFromUpdated(workItem, newEcr, fieldName);
                    }
                }

                // For example Priority is an Integer field
                IntegerField[] changedIntFields = notification.ChangedFields.IntegerFields;
                if (changedIntFields != null)
                {
                    for (int i = 0; i < changedIntFields.Length; i++)
                    {
                        String fieldName = changedIntFields[i].ReferenceName;
                        noOfMappedChanged += mapFromUpdated(workItem, newEcr, fieldName);
                    }
                }
            }

            // For example the Description is a Text field
            TextField[] changedTextFields = notification.TextFields;
            if (changedTextFields != null)
            {
                for (int i = 0; i < changedTextFields.Length; i++)
                {
                    String fieldName = changedTextFields[i].ReferenceName;
                    noOfMappedChanged += mapFromUpdated(workItem, newEcr, fieldName);
                }
            }

            // To find a change in the Comment/History one need to look at revision - 1.
            noOfMappedChanged += mapFromUpdated(workItem, newEcr, TFSMapper.TFS_HISTORY);

            // Need to send list of attachments in all cases when we have another update. So if we already have
            // an update (noOfMappedChanged > 0), send - otherwise, check if changed then send.
            if ((noOfMappedChanged > 0 || TFSMapper.getInstance().hasLinksChanged(workItem)))
            {
                noOfMappedChanged += mapFromUpdated(workItem, newEcr, TFSMapper.ERICSSON_DEFECT_HYPERLINK);
            }

            if (noOfMappedChanged > 0)
            {
                // More than 1 field that was mapped changed
                return(newEcr);
            }
            else
            {
                // No field of interest was changed
                HandlerSettings.LogMessage(
                    String.Format("No mapped fields was updated for: {0}", workItem.Title),
                    HandlerSettings.LoggingLevel.INFO);
                return(null);
            }
        }
        /// <summary>
        /// Initiatie application - if fatal error, return false.
        /// </summary>
        static public Boolean initFromFile()
        {
            if (initiated)
            {
                return(initiatedOK);
            }
            initiated   = true;
            initiatedOK = false;

            // =====================================================================
            // Get adapterHome and config file - fatal error if not found, report and exit

            adapterHome = Environment.GetEnvironmentVariable("ADAPTER_HOME_TFS");
            if (adapterHome == null || !Directory.Exists(adapterHome))
            {
                // TODO: Is there a reasonable deafult to use here? For now; C:\ + "tfs_adapter".
                // Note: Can't use the user home as the TFS process normally can't read there
                adapterHome = "C:\\adapter_home_tfs";
            }
            if (!Directory.Exists(adapterHome))
            {
                throw new DirectoryNotFoundException(
                          String.Format("Failed to find adapter home at: {0}. Exit.", adapterHome));
            }

            // Note: Using the recommended file construct Path.Combine(adapterHome + "properties.xml")
            // resulted in "C:\\adapter_home_tfsproperties.xml" i.e. not correct. So resort to basic.
            String configFile = adapterHome + "\\properties.xml";

            // Handle re-read of files without need of server reboot. So if missing file is logged, if
            // adding a file at that location it should be picked up.
            handleConfigFileChange(ConfigFile.PROPERTIES, configFile);

            if (!File.Exists(configFile))
            {
                throw new FileNotFoundException(
                          String.Format("Failed to find properties.xml at: {0}. Exit.", configFile));
            }


            // =====================================================================
            // Read the configuration file

            XElement config = null;

            try
            {
                XDocument doc = XDocument.Load(configFile);
                config = doc.Element("configuration");
            }
            catch (Exception e)
            {
                throw new Exception(
                          String.Format("Exception when loading property file {0}. ", configFile), e);
            }

            // =====================================================================
            // Initiate logging

            // For logging, log4net is recommended, available e.g. at http://www.nuget.org/packages/log4net/2.0.3
            // BUT introduces another dependency, and found no clear examples of using this on server side. So
            // will use a simpler approach for now.

            initLogging(config, adapterHome);

            LogMessage(String.Format("Reading properties from file: {0}", configFile), LoggingLevel.INFO);

            // =====================================================================
            // Get connection settings

            initiatedOK = initConnectionSettings(config);
            if (!initiatedOK)
            {
                return(initiatedOK);
            }

            // =====================================================================
            // Read list of Friends

            initiatedOK = initFriends(config);
            if (!initiatedOK)
            {
                return(initiatedOK);
            }

            // =====================================================================
            // Read list of Headers for REST call

            initiatedOK = initHeaders(config);
            if (!initiatedOK)
            {
                return(initiatedOK);
            }

            // =====================================================================
            // Configure mapping - if mapping not found, fatal error

            initiatedOK = TFSMapper.initTFSFieldNames(config);
            if (!initiatedOK)
            {
                return(initiatedOK);
            }

            initiatedOK = initMapping(config, adapterHome);
            if (!initiatedOK)
            {
                return(initiatedOK);
            }

            // ======================================
            // Read optional sync status messages

            XElement noSyncMessageAttr = config.Element("noSyncMessage");

            if (noSyncMessageAttr != null)
            {
                noSyncMessage = noSyncMessageAttr.Value;
            }
            XElement successMessageAttr = config.Element("successMessage");

            if (successMessageAttr != null)
            {
                successMessage = successMessageAttr.Value;
            }
            XElement warningMessageAttr = config.Element("warningMessage");

            if (warningMessageAttr != null)
            {
                warningMessage = warningMessageAttr.Value;
            }
            XElement errorMessageAttr = config.Element("errorMessage");

            if (errorMessageAttr != null)
            {
                errorMessage = errorMessageAttr.Value;
            }
            LogMessage("Read optional sync status messages.", LoggingLevel.INFO);

            LogMessage(
                String.Format("Read of properties from file {0} successful.", configFile),
                LoggingLevel.INFO);

            initiatedOK = true;
            return(initiatedOK);
        }