예제 #1
0
 // reference policies do not hold information, thus functions and sys-pol can just link the original policy
 // see "explanation wrt new handling of reference policies" in Explanations.cs
 private void GatherRefPol(EM2Item pol, EM2Country.Content ctryContent)
 {
     if (pol.properties.ContainsKey(EM2TAGS.REFPOL_ID) && !string.IsNullOrEmpty(pol.properties[EM2TAGS.REFPOL_ID]))
     {
         ctryContent.referencePolicies.Add(pol.id, pol.properties[EM2TAGS.REFPOL_ID]);
     }
 }
        public static EM2Item Copy(EM2Item orig, List <EM2Country.SysItem> allSysVals, out List <EM2Country.SysItem> copiedSysVals)
        {
            EM2Item copy = new EM2Item()
            {
                id = Guid.NewGuid().ToString(), name = orig.name, order = orig.order, partentId = orig.partentId
            };

            foreach (var p in orig.properties)
            {
                copy.properties.Add(p.Key, p.Value);
            }

            List <EM2Country.SysItem> origSysVals = (from sv in allSysVals where sv.itemID == orig.id select sv).ToList();

            copiedSysVals = new List <EM2Country.SysItem>();
            foreach (EM2Country.SysItem origSysVal in origSysVals) // dublicate the values of the policy/function/parameter (order, off/on/value)
            {
                copiedSysVals.Add(new EM2Country.SysItem()
                {
                    sysID  = origSysVal.sysID,
                    itemID = copy.id,
                    order  = origSysVal.order,
                    value  = origSysVal.value
                });
            }

            return(copy);
        }
        // merge Agg_Var and Agg_IL to Agg (can have parameter type var-or-il, which is more handy for executable)
        private void AdaptTotals_Par(EM2Item par, EM2Item fun)
        {
            if (fun.name.ToLower() != DefFun.Totals.ToLower())
            {
                return;
            }

            if (par.name.ToLower() == AGG_VAR ||
                par.name.ToLower() == AGG_IL)
            {
                par.name = DefPar.Totals.Agg;
            }
        }
 private void AdaptFootnotes_Par(EM2Item par)
 {
     if (par.name.ToLower() == "#_lowlim_amount")
     {
         par.name = DefPar.Footnote.LowLim;
     }
     if (par.name.ToLower() == "#_uplim_amount")
     {
         par.name = DefPar.Footnote.UpLim;
     }
     if (par.name.ToLower() == "#_means")
     {
         par.name = DefQuery.Par.Val;
     }
 }
 // put year-values from string (2006|102.49°2007|104.93°2008|109.83°2009...) into own elements
 private void AdaptYearValues(EM2Item em2, List <Dictionary <string, string> > em3, string ID)
 {
     foreach (var val in em2.properties)
     {
         if (val.Key != EM2TAGS.YEAR_VALUES)
         {
             continue;
         }
         foreach (string yearValPair in val.Value.Split('°'))
         {
             string[] yearVal = yearValPair.Split('|');
             em3.Add(new Dictionary <string, string>()
             {
                 { ID, em2.id },
                 { EM_XmlHandler.TAGS.YEAR, yearVal[0] },
                 { EM_XmlHandler.TAGS.VALUE, yearVal[1] }
             });
         }
     }
     em2.properties.Remove(EM2TAGS.YEAR_VALUES); // after putting into own elements, remove the string
 }
예제 #6
0
 private void AdaptPolProp(EM2Item pol)
 {
     pol.properties.Remove(EM2TAGS.SYSTEM_ID); // dispensable due to new structure
     pol.properties.Remove(EM2TAGS.TYPE);      // dispensable (sic/ben/tac/etc. is not even used in current UI)
     pol.properties.Remove(EM2TAGS.COLOR);     // only relevant for UI (rethink for future UI)
 }
예제 #7
0
        /// <summary>
        /// performs all EM2->EM3 content (see above) adaptations for country- and AddOn-files
        /// *!*!*!*!*!*!*  NOTE: ALL "explanation wrt ..." ARE FOUND IN explanations.cs  *!*!*!*!*!*!*
        /// </summary>
        /// <param name="ctryContent"> content of country-XML-file as read by EM2Country.Read </param>
        /// <param name="dataContent"> content of country-dataconfig-XML-file as read by EM2Data.Read </param>
        /// <param name="extensionInfo"> content of global policy-switches-XML-file (provides info for adaptation, see below) </param>
        internal void AdaptCountry(EM2Country.Content ctryContent, EM2Data.Content dataContent,
                                   List <List <MultiProp> > extensionInfo,
                                   out List <string> errors)
        {
            errors = new List <string>();

            // ***  C O U N T R Y  ***
            ctryContent.general.id = null; // remove country-id (which never had any use)

            // ***  A D A P T   O R D E R   T O   V I S U A L   O R D E R  ***
            // ***  S E T   F U N C T I O N ' S   R U N - O P T I O N   ***
            AdaptOrder(ctryContent);

            // ***  S Y S T E M S  ***
            foreach (var sys in ctryContent.systems)
            {
                AdaptSysProp(sys.Value);                             // remove outdated properties (ExchangeRateEuro, Private, ...)
            }
            // ***  P O L I C I E S  ***
            foreach (var pol in ctryContent.policies)
            {
                AdaptPolProp(pol.Value);                             // remove outdated properties (Type, Color, Private, ...)
                GatherRefPol(pol.Value, ctryContent);                // see "explanation wrt to new handling of reference policies"
            }
            RemoveRefPol(ctryContent);                               // see "explanation wrt to new handling of reference policies"

            // ***  F U N C T I O N S  ***
            List <string> funIds = (from f in ctryContent.functions select f.Key).ToList();

            foreach (string funId in funIds)                    // it is necessary to run over the ids instead of the functions themselves
            {                                                   // because we may add functions and thus change the collection
                EM2Item fun = ctryContent.functions[funId];
                AdaptFunProp(fun);                              // remove outdated properties (Color, Private, ...)
                AdaptChangeParam_Fun(fun, ctryContent, errors); // see "explanation wrt to new handling of ChangeParam"
            }

            // ***  P A R A M E T E R S  ***
            foreach (var par in ctryContent.parameters)
            {
                AdaptParProp(par.Value);         // remove outdated properties (Color, ValueType, Private, ...)
                EM2Item fun = ctryContent.functions[par.Value.properties[EM2TAGS.FUNCTION_ID]];
                AdaptTotals_Par(par.Value, fun); // merge Agg_Var and Agg_IL
                AdaptCZDefConfig_Par(par.Value); // solve a special case for CZ (actually a syntax error due to very old notation)
                AdaptFootnotes_Par(par.Value);   // replace #_LowLim_Amount by #_LowLim, #_Income by #_Info, etc.
            }

            // ***  S Y S T E M   V A L U E S   O F   P O L I C I E S  ***
            foreach (var sysPol in ctryContent.sysPol)
            {
                ReplaceSwitchAndToggle(sysPol); // see "explanation wrt to toggle and switch"
            }

            // ***  S Y S T E M   V A L U E S   O F   F U N C T I O N S  ***
            foreach (var sysFun in ctryContent.sysFun)
            {
                ReplaceSwitchAndToggle(sysFun); // see "explanation wrt to toggle and switch"
            }

            // ***  S Y S T E M   V A L U E S   O F   P A R A M E T E R S  ***
            foreach (var sysPar in ctryContent.sysPar)
            {
                ReplaceEmptyByNA(sysPar);            // it seems that there are (faulty!) parameter values set to "<![CDATA[]]>" (i.e. empty CDATA)
                ReplaceWhoMustBeEligAliases(sysPar); // replace 'one_member' by 'one', 'all_members' and 'taxunit' by 'all' and 'all_adult' by 'all_adults'
            }


            // ***  U P R A T I N G   I N D I C E S  ***
            foreach (var upIndex in ctryContent.upInd)
            {
                AdaptYearValues(upIndex.Value, ctryContent.upIndVal, EM_XmlHandler.TAGS.UPIND_ID); // put year-values in own elements (see comment in function)
            }

            // ***  I N D I R E C T   T A X E S  ***
            foreach (var indTax in ctryContent.indTax)
            {
                AdaptYearValues(indTax.Value, ctryContent.indTaxVal, EM_XmlHandler.TAGS.INDTAX_ID); // put year-values in own elements (see above)
            }

            if (dataContent == null)
            {
                return;
            }
            // BREAK HERE FOR ADD-ONS !!! the rest only concerns countries
            //---------------------------------------------------------------------------------------

            // ***  D A T A S E T S  ***
            //foreach (var dataSet in dataContent.dataSets) { } // nothing to do currently

            // ***  S Y S T E M - D A T A - C O M B I N A T I O N S  ***
            foreach (var sysData in dataContent.sysData)
            {
                AdaptSysDataProp(sysData); // remove outdated properties (UseCommonDefault, ...)
            }
            // ***  S W I T C H - G R O U P S  ***
            for (int i = dataContent.policySwitches.Count - 1; i >= 0; --i) // see "explanation wrt new handling of policy switches"
            {
                AdaptExtensionSwitchProp(dataContent.policySwitches[i], extensionInfo, ctryContent.policies, out bool remove, ctryContent);
                if (remove) // remove if no respective switch policy found (maybe outdated switch) or if set to n/a
                {
                    dataContent.policySwitches.RemoveAt(i);
                }
            }
        }
        // also see "explanation wrt new handling of ChangeParam" in Explanations.cs
        private void AdaptChangeParam_Fun(EM2Item fun, EM2Country.Content ctryContent, List <string> errors)
        {
            if (fun.name.ToLower() != DefFun.ChangeParam.ToLower())
            {
                return;
            }

            // === G A T H E R  I N F O R M A T I O N ===
            // get all parameters of the ChangeParam ...
            // i.e. Param_Id, Param_NewVal, Param_CondVal, RunCond, #_DataBasename, #_xxx (i.e. other footnote-parameters)
            var allPar = from p in ctryContent.parameters
                         where p.Value.properties.ContainsKey(EM2TAGS.FUNCTION_ID) && p.Value.properties[EM2TAGS.FUNCTION_ID] == fun.id
                         select p;
            // ... and put them into lists that allow deciding on what to do with them
            string        idRunCond               = null;                // id of Run_Cond-parameter
            List <string> idDataBaseFootnotes     = new List <string>(); // ids of footnote-parameters #_DataBase (referred by Run_Cond)
            List <string> idsParamId_ChangeSwitch = new List <string>(); // ids of Param_Id-parameters, which refer to a function or policy
            List <string> idsVal_ChangeSwitch     = new List <string>(); // ids of Param_New/CondVal-parameters with value on/off/toggle
            List <string> idsParamId_ChangeParam  = new List <string>(); // ids of Param_Id-parameters, which refer to a parameter
            List <string> idsVal_ChangeParam      = new List <string>(); // ids of Param_CondVal-parameters with another value than on/off/toggle
            List <string> idsOtherFootnotes       = new List <string>(); // ids of other footnote-parameters than #_DataBase (rather unlikely)

            bool hasChangeParam_Par  = false;                            // has parameters that belong into a ChangeParam function
            bool hasChangeSwitch_Par = false;                            // has parameters that belong into a ChangeSwitch function

            foreach (var par in allPar)
            {
                // --- Param_Id ---
                if (par.Value.name.ToLower() == DefPar.ChangeParam.Param_Id.ToLower())
                {   // Param_Id refers to a policy or function -> belongs into a ChangeSwitch function
                    bool?refersToPolFun = RefersToPolFun(par.Value.id);
                    if (refersToPolFun == true)
                    {
                        idsParamId_ChangeSwitch.Add(par.Value.id); hasChangeSwitch_Par = true;
                    }
                    // Param_Id refers to a parameter -> belongs into a ChangeParam function
                    else if (refersToPolFun == false)
                    {
                        idsParamId_ChangeParam.Add(par.Value.id); hasChangeParam_Par = true;
                    }
                    //else: parameter is n/a in all systems
                }

                // --- Run_Cond ---
                else if (par.Value.name.ToLower() == DefPar.Common.Run_Cond.ToLower())
                {
                    idRunCond = par.Key;
                }

                // --- #_DataBase ---
                else if (par.Value.name.ToLower() == DefQuery.Par.DataBasename.ToLower())
                {
                    idDataBaseFootnotes.Add(par.Key);
                }

                // --- Param_CondVal or Param_NewVal ---
                else if (par.Value.name.ToLower() == DefPar.ChangeParam.Param_NewVal.ToLower() ||
                         par.Value.name.ToLower() == DefPar.ChangeParam.Param_CondVal.ToLower())
                {
                    // Param_Cond/NewVal's value = off/on/toggle -> belongs into a ChangeSwitch function
                    bool?isSwitchChange = IsSwitchChange(par.Value.id);
                    if (isSwitchChange == true)
                    {
                        idsVal_ChangeSwitch.Add(par.Value.id); hasChangeSwitch_Par = true;
                    }
                    // Param_Cond/NewVal's value = some parameter-change -> belongs into a ChangeParam function
                    else if (isSwitchChange == false)
                    {
                        idsVal_ChangeParam.Add(par.Value.id); hasChangeParam_Par = true;
                    }
                    //else: parameter is n/a in all systems
                }

                // --- other (valid) parameter can only be another footnote parameter than #_DataBase ---
                else
                {
                    idsOtherFootnotes.Add(par.Key);
                }
            }

            // === T R A N S F O R M   A C T I O N S ===
            if (hasChangeSwitch_Par && hasChangeParam_Par) // splitting the function in two is complicated (must invent ids ...) and is not
            {                                              // necessary for existing countries/add-ons, thus do not forsee before necessary ...
                // AddError("impossible to assign all parameters to either a ChangeParam or ChangeSwitch function");
                // return;

                // unfortunately it seems that we need a solution for this a bit complicated case, thus:
                SeparateSwitchFun(); hasChangeSwitch_Par = false;
            } // note (!hasChangeSwitch_Par && !hasChangeParam_Par) is theoretically possible, if all pars are n/a, but that wouldn't do any harm

            // --- change to ChangeSwitch function ---
            if (hasChangeSwitch_Par)
            {
                fun.name = DefFun.ChangeSwitch;
                foreach (string id in idsParamId_ChangeSwitch) // rename 'Param_Id' to 'PolFun'
                {
                    ctryContent.parameters[id].name = DefPar.ChangeSwitch.PolFun;
                }
                foreach (string id in idsVal_ChangeSwitch) // rename 'Param_CondVal'/'Param_NewVal' to 'SwitchNewVal'
                {                                          // and change possible 'toggle'-value to 'off'
                    ctryContent.parameters[id].name = DefPar.ChangeSwitch.Switch_NewVal;
                    foreach (var sysPar in GetSysPar(id))
                    {
                        if (XmlHelpers.RemoveCData(sysPar.value).ToLower() == DefPar.Value.TOGGLE)
                        {
                            sysPar.value = DefPar.Value.OFF;
                        }
                    }
                    // else: no change necessary for on / off / n/a
                }
                // no change necessary for other parameters, i.e. Run_Cond and associated footnotes
            }

            // --- adapt ChangeParam function ---
            if (hasChangeParam_Par)
            {
                // if 'RunCond={IsUsedDatabase#1}' transform to 'Database=xxx', otherwise error (e.g. an actual run-time RunCond or a more complicated RunCond)
                if (idRunCond != null && !TransformRunCond())
                {
                    return;
                }

                // rename from Param_CondVal to Param_NewVal if necessary
                foreach (string id in idsVal_ChangeParam)
                {
                    ctryContent.parameters[id].name = DefPar.ChangeParam.Param_NewVal;
                }

                // no change necessary for Param_Id (keeps Guid or symbolic id)
                // ignore other parameters - if there are e.g. unnecessary footnotes, this is also wrong for the old executable
            }

            // === H E L P E R   F U N C T I O N S ===
            void AddError(string error)
            {
                errors.Add($"{ctryContent.general.name}: Failed to transform {fun.name} ({fun.id}): {error} (hint: case not yet handled by transformer)");
            }

            bool TransformRunCond()
            {
                bool runCondDefined = false;
                List <EM2Country.SysItem> runCondVals = GetSysPar(idRunCond).ToList();

                foreach (EM2Country.SysItem runCondVal in runCondVals)
                {
                    string runCond = XmlHelpers.RemoveCData(runCondVal.value).Replace(" ", "").ToLower();
                    if (runCond == DefPar.Value.NA)
                    {
                        continue;
                    }
                    // remove everything that is allowed in a simple enough run-cond, to then check for empty string
                    for (int i = 999; i >= 1; --i)
                    {
                        runCond = runCond.Replace($"isuseddatabase#{i}", "");
                    }
                    runCond = runCond.Replace("{}", "").Replace("|", "");
                    if (runCond != string.Empty)
                    {
                        AddError("too complex Run_Cond");                          // still continue, so the country still runs
                    }
                    runCondDefined = true;
                }
                if (runCondDefined) // rename '#_Database' to 'Dataset' if RunCond is actually {IsUsedDatabase#1}
                {                   // (system values are ok, i.e. contain the respective dataset-name)
                    if (idDataBaseFootnotes.Count() == 0)
                    {
                        AddError("missing database specification"); return(false);
                    }
                    foreach (string idDataBaseFootnote in idDataBaseFootnotes)
                    {
                        ctryContent.parameters[idDataBaseFootnote].name = DefPar.ChangeParam.Dataset;
                    }
                }
                // remove the RunCond and its system values
                ctryContent.parameters.Remove(idRunCond);
                foreach (EM2Country.SysItem runCondVal in runCondVals)
                {
                    ctryContent.sysPar.Remove(runCondVal);
                }

                return(true);
            }

            bool?RefersToPolFun(string id_Param_Id)
            {
                bool allNa = true;

                foreach (EM2Country.SysItem sysPar in GetSysPar(id_Param_Id))
                {
                    string id = (XmlHelpers.RemoveCData(sysPar.value));
                    if (id == DefPar.Value.NA)
                    {
                        continue;
                    }
                    allNa = false;
                    if (EM_Helpers.IsGuid(id)) // i.e. most likely this is a country-file (and not an add-on)
                    {
                        if (!ctryContent.parameters.ContainsKey(id))
                        {
                            return(true);                                         // a bit unsafe, as only "most likely" a country-file
                        }
                    }                                                             // i.e. could still be a country-par-guid in an add-on
                    else // i.e. must be an add-on (unless this is an invalid use of symbolic-id in a country file or otherwise invalid)
                    {
                        if (!AddOn.IsParSymbolicID(id))
                        {
                            return(true);
                        }
                    } // note: a maybe "safer" way to assess whether the id refers to a parameter or to a policy or function
                }     // would be to analyse the corresponding ParamCond/NewVal (on/off/toggle->Pol/Fun), but that's complex to code
                if (allNa)
                {
                    return(null);
                }
                return(false);
            }

            bool?IsSwitchChange(string id_Param_NewVal)
            {
                bool allNa = true;

                foreach (EM2Country.SysItem sysPar in GetSysPar(id_Param_NewVal))
                {
                    string newVal = (XmlHelpers.RemoveCData(sysPar.value)).ToLower();
                    if (newVal == DefPar.Value.OFF || newVal == DefPar.Value.ON || newVal == DefPar.Value.TOGGLE)
                    {
                        return(true);
                    }
                    if (newVal != DefPar.Value.NA)
                    {
                        allNa = false;
                    }
                }
                if (allNa)
                {
                    return(null);
                }
                return(false);
            }

            IEnumerable <EM2Country.SysItem> GetSysPar(string parId)
            {
                return(from sp in ctryContent.sysPar where sp.itemID == parId select sp);
            }

            void SeparateSwitchFun()
            {
                // first generate a separate ChangeSwitch function ...
                EM2Item switchFun = EM2Item.Copy(fun, ctryContent.sysFun, out List <EM2Country.SysItem> switchFunSysVals);

                switchFun.name  = DefFun.ChangeSwitch;
                switchFun.order = 1234567; // unimportant for a ChangeSwitch
                ctryContent.functions.Add(switchFun.id, switchFun);
                ctryContent.sysFun.AddRange(switchFunSysVals);

                // ... then "move" parameters from existing ChangeParam to new ChangeSwitch ...
                foreach (string id in idsParamId_ChangeSwitch)
                {
                    ctryContent.parameters[id].partentId = switchFun.id;          // move ...
                    ctryContent.parameters[id].properties[EM2TAGS.FUNCTION_ID] = switchFun.id;
                    ctryContent.parameters[id].name = DefPar.ChangeSwitch.PolFun; // ... and rename 'Param_Id' to 'PolFun'

                    foreach (var x in ctryContent.parameters[id].properties)
                    {
                        RandColor.WriteLine($"{x.Key}={x.Value}");
                    }
                }
                foreach (string id in idsVal_ChangeSwitch)
                {
                    ctryContent.parameters[id].partentId = switchFun.id;                 // move ...
                    ctryContent.parameters[id].properties[EM2TAGS.FUNCTION_ID] = switchFun.id;
                    ctryContent.parameters[id].name = DefPar.ChangeSwitch.Switch_NewVal; // ... rename 'Param_CondVal'/'Param_NewVal' to 'SwitchNewVal' ...
                    foreach (var sysPar in GetSysPar(id))                                // ... and change possible 'toggle'-value to 'off'
                    {
                        if (XmlHelpers.RemoveCData(sysPar.value).ToLower() == DefPar.Value.TOGGLE)
                        {
                            sysPar.value = DefPar.Value.OFF;
                        }
                    }
                }

                // ... finally copy possible run-condition ...
                if (idRunCond == null)
                {
                    return;
                }
                EM2Item runCond = EM2Item.Copy(ctryContent.parameters[idRunCond], ctryContent.sysPar, out List <EM2Country.SysItem> runCondSysVals);

                runCond.partentId = switchFun.id; runCond.properties[EM2TAGS.FUNCTION_ID] = switchFun.id;
                ctryContent.parameters.Add(runCond.id, runCond);
                ctryContent.sysPar.AddRange(runCondSysVals);

                // ... and its footnotes
                foreach (string footNoteId in idDataBaseFootnotes)
                {
                    EM2Item footNote = EM2Item.Copy(ctryContent.parameters[footNoteId], ctryContent.sysPar, out List <EM2Country.SysItem> footNoteSysVals);
                    footNote.partentId = switchFun.id; footNote.properties[EM2TAGS.FUNCTION_ID] = switchFun.id;
                    ctryContent.parameters.Add(footNote.id, footNote);
                    ctryContent.sysPar.AddRange(footNoteSysVals);
                }
            }
        }
        /// <summary> reads an EM2 country-XML-file into an EM2Country.Content structure </summary>
        /// <param name="fileName"> full path to the country-XML-file </param>
        /// <param name="errors"> stores critical and non-critical erros during the read-process, empty structure for no errors </param>
        /// <returns> Content structure on success, null on failure </returns>
        public static Content Read(string fileName, out List <string> errors)
        {
            Content content = new Content();

            errors = new List <string>();
            try
            {
                using (StreamReader streamReader = new StreamReader(fileName, DefGeneral.DEFAULT_ENCODING))
                    using (XmlReader mainReader = XmlReader.Create(streamReader))
                    {
                        bool firstSystem = true;
                        // structures which allow assigning the system-dependent values (order, switch/value) of
                        // EM2-policies/functions/parameters to the single remaining EM3-policy/function/parameter
                        // dic-key: policy-order[°function-order][°parameter-order]
                        // dic-value: the id of the remaining policy/function/parameter
                        Dictionary <string, string> polRefs = new Dictionary <string, string>();
                        Dictionary <string, string> funRefs = new Dictionary <string, string>();
                        Dictionary <string, string> parRefs = new Dictionary <string, string>();
                        // reference for all (old) ids, i.e. key = old id, value = new id,
                        // for (after all reading is done) adapting parameter values which contain ids (e.g. Loop/First_Func, ChangeParam/Param_Id)
                        Dictionary <string, string> guidRef = new Dictionary <string, string>();

                        // *** READ COUNTRY ***
                        mainReader.ReadToDescendant(EM2TAGS.COUNTRY);
                        XmlReader countryReader = mainReader.ReadSubtree(); countryReader.ReadToDescendant(EM2TAGS.ID);
                        do
                        {
                            switch (GetElementType(countryReader))                                                  // analyse the current tag: does it still belong to country
                            {                                                                                       // or is the the first system
                            case ELEMENT_TYPE.NO: continue;                                                         // only XmlNodeType.Element is relevant

                            case ELEMENT_TYPE.PROPERTY: ReadProperty(countryReader, ref content.general); continue; // read country-properties (name, shortname)

                            case ELEMENT_TYPE.SUBTREE: break;                                                       // system-tag (for better readability not coded inside the switch-structure)

                            default: continue;
                            }

                            // *** READ SYSTEM ***
                            EM2Item   sysProperties = new EM2Item(); // takes the system-properties (name, year, currency, ...)
                            XmlReader sysReader     = countryReader.ReadSubtree(); sysReader.ReadToDescendant(EM2TAGS.ID);
                            do
                            {
                                switch (GetElementType(sysReader, true))
                                {
                                case ELEMENT_TYPE.NO: continue;

                                case ELEMENT_TYPE.PROPERTY: ReadProperty(sysReader, ref sysProperties); continue; // reads system-properties (name, year, currency, ...)

                                case ELEMENT_TYPE.SUBTREE: break;                                                 // policy-tag (for better readability not coded inside the switch-structure)

                                default: continue;
                                }

                                // *** READ POLICY ***
                                EM2Item   polProperties = new EM2Item(); // takes the policy-properties (name, reference-policy, ...), is stored only for the first system
                                SysItem   polSysVal     = new SysItem(); // takes the system-dependent policy-values (switch, order), is stored for all systems
                                XmlReader polReader     = sysReader.ReadSubtree(); polReader.ReadToDescendant(EM2TAGS.ID);
                                do
                                {
                                    switch (GetElementType(polReader))
                                    {
                                    case ELEMENT_TYPE.NO: continue;

                                    case ELEMENT_TYPE.PROPERTY: ReadProperty(polReader, ref polProperties); continue; // reads policy-properties (name, reference-policy, ...)

                                    case ELEMENT_TYPE.SYSVAL: ReadSysVal(polReader, ref polSysVal); continue;         // reads system-dependent policy-values (switch, order)

                                    case ELEMENT_TYPE.SUBTREE: break;                                                 // function-tag (for better readability not coded inside the switch-structure)

                                    default: continue;
                                    }

                                    // *** READ FUNCTION *** (see descriptions for policy above)
                                    EM2Item   funProperties = new EM2Item(); SysItem funSysVal = new SysItem();
                                    XmlReader funReader = polReader.ReadSubtree(); funReader.ReadToDescendant(EM2TAGS.ID);
                                    do
                                    {
                                        switch (GetElementType(funReader))
                                        {
                                        case ELEMENT_TYPE.NO: continue;

                                        case ELEMENT_TYPE.PROPERTY: ReadProperty(funReader, ref funProperties); continue;

                                        case ELEMENT_TYPE.SYSVAL: ReadSysVal(funReader, ref funSysVal); continue;

                                        case ELEMENT_TYPE.SUBTREE: break;

                                        default: continue;
                                        }

                                        // *** READ PARAMETER *** (see descriptions for policy above)
                                        EM2Item   parProperties = new EM2Item(); SysItem parSysVal = new SysItem();
                                        XmlReader parReader = funReader.ReadSubtree(); parReader.ReadToDescendant(EM2TAGS.ID);
                                        do
                                        {
                                            switch (GetElementType(parReader))
                                            {
                                            case ELEMENT_TYPE.NO: continue;

                                            case ELEMENT_TYPE.PROPERTY: ReadProperty(parReader, ref parProperties); continue;

                                            case ELEMENT_TYPE.SYSVAL: ReadSysVal(parReader, ref parSysVal); continue;

                                            default: continue;
                                            }
                                        } while (parReader.Read());

                                        // *** STORE PARAMETER ***
                                        string parRef = polSysVal.order + "°" + funSysVal.order + "°" + parSysVal.order;
                                        if (firstSystem)                                           // ... for 1st system: the properties
                                        {
                                            parProperties.order     = long.Parse(parSysVal.order); // order and parentId are necessary for
                                            parProperties.partentId = funProperties.id;            // adapting to visual order (see EM23Adapt_Order.cs)
                                            content.parameters.Add(parProperties.id, parProperties);
                                            if (!parRefs.TryAdd(parRef, parProperties.id))         // add reference-item, to allow parameters of following systems to target this now unique parameter
                                            {
                                                errors.Add(GenerateError($"Failed to add parameter to reference-list: {sysProperties.name}/{polProperties.name}/{funProperties.name}/{parProperties.name} (order: {parRef})"));
                                            }
                                        }
                                        // ... for all systems: value and order
                                        if (!parRefs.TryGetValue(parRef, out parSysVal.itemID)) // get the id of the now unique parameter stored in firstSystem-branch
                                        {
                                            errors.Add(GenerateError($"Failed to assign parameter: {sysProperties.name}/{polProperties.name}/{funProperties.name}/{parProperties.name} (order: {parRef}, id: {parProperties.id})"));
                                        }
                                        parSysVal.sysID = sysProperties.id;
                                        if (parSysVal.itemID != null) // adding this request avoids a crash in order-setting and thus allows translating the country
                                        {
                                            content.sysPar.Add(parSysVal);
                                        }
                                        parReader.Close();
                                        // finally, store id-change-info (i.e. old id -> new id) for (below) adapting parameter values which contain ids (e.g. ChangeParam/Param_Id)
                                        guidRef.Add(parProperties.id, parSysVal.itemID);
                                    } while (funReader.Read());

                                    // *** STORE FUNCTION ***
                                    string funRef = polSysVal.order + "°" + funSysVal.order;
                                    if (firstSystem)                                           // ... for 1st system: the properties
                                    {
                                        funProperties.order     = long.Parse(funSysVal.order); // order and parentId are necessary for
                                        funProperties.partentId = polProperties.id;            // adapting to visual order (see EM23Adapt_Order.cs)
                                        content.functions.Add(funProperties.id, funProperties);
                                        if (!funRefs.TryAdd(funRef, funProperties.id))         // see description parameter above
                                        {
                                            errors.Add(GenerateError($"Failed to add function to reference-list: {sysProperties.name}/{polProperties.name}/{funProperties.name} (order: {funRef})"));
                                        }
                                    }
                                    // ... for all systems: switch and order
                                    if (!funRefs.TryGetValue(funRef, out funSysVal.itemID)) // see description parameter above
                                    {
                                        errors.Add(GenerateError($"Failed to assign function {sysProperties.name}/{polProperties.name}/{funProperties.name} (order: {funRef}, id: {funProperties.id})"));
                                    }
                                    funSysVal.sysID = sysProperties.id;
                                    if (funSysVal.itemID != null) // see comment on adding request above
                                    {
                                        content.sysFun.Add(funSysVal);
                                    }
                                    funReader.Close();
                                    // finally, store id-change-info (i.e. old id -> new id) for (below) adapting parameter values which contain ids (e.g. ChangeParam/Param_Id)
                                    guidRef.Add(funProperties.id, funSysVal.itemID);
                                } while (polReader.Read());

                                // *** STORE POLICY ***
                                string polRef = polSysVal.order;
                                if (firstSystem)                                       // ... for 1st system: the properties
                                {
                                    polProperties.order = long.Parse(polSysVal.order); // order is necessary for adapting to visual order (see EM23Adapt_Order.cs)
                                    content.policies.Add(polProperties.id, polProperties);
                                    if (!polRefs.TryAdd(polRef, polProperties.id))     // see description parameter above
                                    {
                                        errors.Add(GenerateError($"Failed to add policy to reference-list: {sysProperties.name}/{polProperties.name} (order: {polRef})"));
                                    }
                                }
                                // ... for all systems: switch and order
                                if (!polRefs.TryGetValue(polRef, out polSysVal.itemID)) // see description parameter above
                                {
                                    errors.Add(GenerateError($"Failed to assign policy {sysProperties.name}/{polProperties.name} (order: {polRef}, id: {polProperties.id})"));
                                }
                                polSysVal.sysID = sysProperties.id;
                                if (polSysVal.itemID != null) // see comment on adding request above
                                {
                                    content.sysPol.Add(polSysVal);
                                }
                                polReader.Close();
                                // finally, store id-change-info (i.e. old id -> new id) for (below) adapting parameter values which contain ids (e.g. ChangeParam/Param_Id)
                                guidRef.Add(polProperties.id, polSysVal.itemID);
                            } while (sysReader.Read());

                            // *** STORE SYSTEM ***
                            content.systems.Add(sysProperties.id, sysProperties);
                            sysReader.Close();
                            firstSystem = false;
                        } while (countryReader.Read());

                        countryReader.Close();

                        // adapt parameter values, which contain ids, e.g. ChangeParam/Param_Id, Loop/First_Func, ...
                        foreach (SysItem sysPar in content.sysPar)
                        {
                            if (EM_Helpers.IsGuid(XmlHelpers.RemoveCData(sysPar.value)))
                            { // note: do not use 'guid.ToString' because for some reason that's lowercase(?)
                                if (guidRef.TryGetValue(XmlHelpers.RemoveCData(sysPar.value), out string newGuid))
                                {
                                    sysPar.value = newGuid;
                                }
                                else if (content.parameters[sysPar.itemID].name.ToLower() != DefPar.AddOn_ExtensionSwitch.Extension_Id.ToLower())
                                {
                                    errors.Add(GenerateError($"Unknown id found: {sysPar.value} as value of parameter with id {sysPar.itemID}"));
                                }
                            }
                        }

                        do
                        {
                            if (mainReader.NodeType != XmlNodeType.Element)
                            {
                                continue;
                            }

                            // *** READ UPRATING INDICES ***
                            if (mainReader.Name == EM2TAGS.UPINDEX)
                            {
                                XmlReader upiReader = mainReader.ReadSubtree(); upiReader.ReadToDescendant(EM2TAGS.ID);
                                EM2Item   upi       = new EM2Item();
                                do
                                {
                                    if (upiReader.NodeType == XmlNodeType.Element)
                                    {
                                        ReadProperty(upiReader, ref upi);
                                    }
                                } while (upiReader.Read());
                                upiReader.Close();
                                content.upInd.Add(upi.id, upi);
                            }
                            // *** READ LOOK GROUPS ***
                            else if (mainReader.Name == EM2TAGS.LOOKGROUP)
                            {
                                XmlReader lgReader = mainReader.ReadSubtree(); lgReader.ReadToDescendant(EM2TAGS.ID);
                                EM2Item   lg       = new EM2Item();
                                do
                                {
                                    if (lgReader.NodeType != XmlNodeType.Element)
                                    {
                                        continue;
                                    }
                                    if (lgReader.Name == EM2TAGS.LOOKGROUP_POLICY)
                                    {
                                        XmlReader lgPReader = lgReader.ReadSubtree(); lgPReader.ReadToDescendant(EM2TAGS.LOOKGROUP_ID);
                                        string    lgId = null, polId = null;
                                        do
                                        {
                                            if (lgPReader.NodeType != XmlNodeType.Element)
                                            {
                                                continue;
                                            }
                                            if (lgPReader.Name == EM2TAGS.LOOKGROUP_ID)
                                            {
                                                lgId = lgPReader.ReadInnerXml();
                                            }
                                            if (lgPReader.Name == EM2TAGS.POLICY_ID)
                                            {
                                                polId = lgPReader.ReadInnerXml();
                                            }
                                        } while (lgPReader.Read());
                                        if (lgId != null && polId != null &&
                                            content.policies.ContainsKey(polId)) // only add the content once, i.e. for the single kept policy
                                        {
                                            content.lookGroupPol.Add(new Tuple <string, string>(lgId, polId));
                                        }
                                        lgPReader.Close();
                                    }
                                    else if (lgReader.Name == EM2TAGS.LOOKGROUP_FUNCTION)
                                    {
                                        XmlReader lgFReader = lgReader.ReadSubtree(); lgFReader.ReadToDescendant(EM2TAGS.LOOKGROUP_ID);
                                        string    lgId = null, funId = null;
                                        do
                                        {
                                            if (lgFReader.NodeType != XmlNodeType.Element)
                                            {
                                                continue;
                                            }
                                            if (lgFReader.Name == EM2TAGS.LOOKGROUP_ID)
                                            {
                                                lgId = lgFReader.ReadInnerXml();
                                            }
                                            if (lgFReader.Name == EM2TAGS.FUNCTION_ID)
                                            {
                                                funId = lgFReader.ReadInnerXml();
                                            }
                                        } while (lgFReader.Read());
                                        if (lgId != null && funId != null &&
                                            content.functions.ContainsKey(funId)) // only add the content once, i.e. for the single kept function
                                        {
                                            content.lookGroupFun.Add(new Tuple <string, string>(lgId, funId));
                                        }
                                        lgFReader.Close();
                                    }
                                    else if (lgReader.Name == EM2TAGS.LOOKGROUP_PARAMETER)
                                    {
                                        XmlReader lgPReader = lgReader.ReadSubtree(); lgPReader.ReadToDescendant(EM2TAGS.LOOKGROUP_ID);
                                        string    lgId = null, parId = null;
                                        do
                                        {
                                            if (lgPReader.NodeType != XmlNodeType.Element)
                                            {
                                                continue;
                                            }
                                            if (lgPReader.Name == EM2TAGS.LOOKGROUP_ID)
                                            {
                                                lgId = lgPReader.ReadInnerXml();
                                            }
                                            if (lgPReader.Name == EM2TAGS.PARAMETER_ID)
                                            {
                                                parId = lgPReader.ReadInnerXml();
                                            }
                                        } while (lgPReader.Read());
                                        if (lgId != null && parId != null &&
                                            content.parameters.ContainsKey(parId)) // only add the content once, i.e. for the single kept parameter
                                        {
                                            content.lookGroupPar.Add(new Tuple <string, string>(lgId, parId));
                                        }
                                        lgPReader.Close();
                                    }
                                    else
                                    {
                                        ReadProperty(lgReader, ref lg);
                                    }
                                } while (lgReader.Read());
                                lgReader.Close();
                                if (lg.id != null)
                                {
                                    content.lookGroup.Add(lg.id, lg);
                                }
                            }
                            // *** READ EXTENSIONS ***
                            else if (mainReader.Name == EM2TAGS.EXTENSION_POLICY)
                            {
                                XmlReader exReader = mainReader.ReadSubtree(); exReader.ReadToDescendant(EM2TAGS.EXTENSION_ID);
                                string    exId = null, polId = null, baseoff = null;
                                do
                                {
                                    if (exReader.NodeType != XmlNodeType.Element)
                                    {
                                        continue;
                                    }
                                    if (exReader.Name == EM2TAGS.EXTENSION_ID)
                                    {
                                        exId = exReader.ReadInnerXml();
                                    }
                                    if (exReader.Name == EM2TAGS.POLICY_ID)
                                    {
                                        polId = exReader.ReadInnerXml();
                                    }
                                    if (exReader.Name == EM2TAGS.BASEOFF)
                                    {
                                        baseoff = exReader.ReadInnerXml();
                                    }
                                } while (exReader.Read());
                                if (exId != null && polId != null && baseoff != null &&
                                    content.policies.ContainsKey(polId)) // only add the content once, i.e. for the single kept policy
                                {
                                    content.extensionPol.Add(new Tuple <string, string, string>(exId, polId, baseoff));
                                }
                                exReader.Close();
                            }
                            else if (mainReader.Name == EM2TAGS.EXTENSION_FUNCTION)
                            {
                                XmlReader exReader = mainReader.ReadSubtree(); exReader.ReadToDescendant(EM2TAGS.EXTENSION_ID);
                                string    exId = null, funId = null, baseoff = null;
                                do
                                {
                                    if (exReader.NodeType != XmlNodeType.Element)
                                    {
                                        continue;
                                    }
                                    if (exReader.Name == EM2TAGS.EXTENSION_ID)
                                    {
                                        exId = exReader.ReadInnerXml();
                                    }
                                    if (exReader.Name == EM2TAGS.FUNCTION_ID)
                                    {
                                        funId = exReader.ReadInnerXml();
                                    }
                                    if (exReader.Name == EM2TAGS.BASEOFF)
                                    {
                                        baseoff = exReader.ReadInnerXml();
                                    }
                                } while (exReader.Read());
                                if (exId != null && funId != null && baseoff != null &&
                                    content.functions.ContainsKey(funId)) // only add the content once, i.e. for the single kept function
                                {
                                    content.extensionFun.Add(new Tuple <string, string, string>(exId, funId, baseoff));
                                }
                                exReader.Close();
                            }
                            else if (mainReader.Name == EM2TAGS.EXTENSION_PARAMETER)
                            {
                                XmlReader exReader = mainReader.ReadSubtree(); exReader.ReadToDescendant(EM2TAGS.EXTENSION_ID);
                                string    exId = null, parId = null, baseoff = null;
                                do
                                {
                                    if (exReader.NodeType != XmlNodeType.Element)
                                    {
                                        continue;
                                    }
                                    if (exReader.Name == EM2TAGS.EXTENSION_ID)
                                    {
                                        exId = exReader.ReadInnerXml();
                                    }
                                    if (exReader.Name == EM2TAGS.PARAMETER_ID)
                                    {
                                        parId = exReader.ReadInnerXml();
                                    }
                                    if (exReader.Name == EM2TAGS.BASEOFF)
                                    {
                                        baseoff = exReader.ReadInnerXml();
                                    }
                                } while (exReader.Read());
                                if (exId != null && parId != null && baseoff != null &&
                                    content.parameters.ContainsKey(parId)) // only add the content once, i.e. for the single kept parameter
                                {
                                    content.extensionPar.Add(new Tuple <string, string, string>(exId, parId, baseoff));
                                }
                                exReader.Close();
                            }
                            else if (mainReader.Name == EM2TAGS.INDTAX)
                            {
                                XmlReader itReader = mainReader.ReadSubtree(); itReader.ReadToDescendant(EM2TAGS.ID);
                                EM2Item   it       = new EM2Item();
                                do
                                {
                                    if (itReader.NodeType == XmlNodeType.Element)
                                    {
                                        ReadProperty(itReader, ref it);
                                    }
                                } while (itReader.Read());
                                itReader.Close();
                                content.indTax.Add(it.id, it);
                            }
                        } while (mainReader.Read());
                    }
                return(content);
            }
            catch (Exception exception)
            {
                errors.Add(GenerateError(exception.Message));
                return(null);
            }

            ELEMENT_TYPE GetElementType(XmlReader reader, bool forSystem = false) // helping function: see usage above
            {
                if (reader.NodeType != XmlNodeType.Element)
                {
                    return(ELEMENT_TYPE.NO);
                }
                switch (reader.Name)
                {
                case EM2TAGS.SYSTEM:
                case EM2TAGS.POLICY:
                case EM2TAGS.FUNCTION:
                case EM2TAGS.PARAMETER: return(ELEMENT_TYPE.SUBTREE);

                case EM2TAGS.SWITCH:
                case EM2TAGS.VALUE: return(ELEMENT_TYPE.SYSVAL);

                case EM2TAGS.ORDER: return(forSystem ? ELEMENT_TYPE.PROPERTY : ELEMENT_TYPE.SYSVAL);

                default: return(ELEMENT_TYPE.PROPERTY);
                }
            }

            void ReadProperty(XmlReader reader, ref EM2Item item) // helping function: see usage above
            {
                switch (reader.Name)
                {
                case EM2TAGS.ID: item.id = reader.ReadInnerXml(); break;

                case EM2TAGS.NAME:
                case EM2TAGS.REFERENCE:     // uprating-factor-name is for some reason called reference
                    item.name = reader.ReadInnerXml(); break;

                default: item.properties.Add(reader.Name, reader.ReadInnerXml()); break;
                }
            }

            void ReadSysVal(XmlReader reader, ref SysItem item) // helping function: see usage above
            {
                switch (reader.Name)
                {
                case EM2TAGS.SWITCH:
                case EM2TAGS.VALUE: item.value = reader.ReadInnerXml(); break;

                case EM2TAGS.ORDER: item.order = reader.ReadInnerXml(); break;
                }
            }

            string GenerateError(string error)
            {
                return($"{(content.general.name != string.Empty ? content.general.name : fileName)}: {error}");
            }
        }
        /// <summary> reads an EM2 country-dataconfig-XML-file into an EM2Data.Content structure </summary>
        /// <param name="fileName"> full path to the country-dataconfig-XML-file </param>
        /// <param name="errors"> stores critical and non-critical erros during the read-process, empty structure for no errors </param>
        /// <returns> Content structure on success, null on failure </returns>
        public static Content Read(string fileName, out List <string> errors)
        {
            Content content = new Content();

            errors = new List <string>();
            try
            {
                using (StreamReader sr = new StreamReader(fileName, Encoding.UTF8))
                    using (XmlReader mainReader = XmlReader.Create(sr))
                    {
                        mainReader.ReadToDescendant(EM2TAGS.DATABASE);
                        do
                        {
                            if (mainReader.NodeType != XmlNodeType.Element)
                            {
                                continue;
                            }
                            if (mainReader.Name == EM2TAGS.DATABASE)
                            {
                                // *** READ DATABASE ELEMENT ***
                                EM2Item   dataProperties = new EM2Item(); // takes the data-properties (name, yearinc, currency, ...)
                                XmlReader dataReader     = mainReader.ReadSubtree(); dataReader.ReadToDescendant(EM2TAGS.ID);
                                do
                                {
                                    if (dataReader.NodeType != XmlNodeType.Element)
                                    {
                                        continue;
                                    }
                                    switch (dataReader.Name)
                                    {
                                    case EM2TAGS.ID: dataProperties.id = dataReader.ReadInnerXml(); continue;

                                    case EM2TAGS.NAME: dataProperties.name = dataReader.ReadInnerXml(); continue;

                                    case EM2TAGS.SYSCONFIG: break; // DBSystemConfig-tag (for better readability not coded inside the switch-structure)

                                    default: dataProperties.properties.Add(dataReader.Name, dataReader.ReadInnerXml()); continue;
                                    }

                                    // *** READ DBSYSTEMCONFIG ELEMENT ***
                                    SysDataItem dsProperties = new SysDataItem();
                                    XmlReader   dsReader     = dataReader.ReadSubtree(); dsReader.ReadToDescendant(EM2TAGS.SYSTEM_ID);
                                    do
                                    {
                                        if (dsReader.NodeType != XmlNodeType.Element)
                                        {
                                            continue;
                                        }
                                        switch (dsReader.Name)
                                        {
                                        case EM2TAGS.DATAID: dsProperties.dataID = dsReader.ReadInnerXml(); continue;

                                        case EM2TAGS.SYSTEM_ID: dsProperties.sysID = dsReader.ReadInnerXml(); continue;

                                        case EM2TAGS.SYSNAME: dsProperties.sysName = dsReader.ReadInnerXml(); continue;

                                        case EM2TAGS.POLSWITCH: break; // PolicySwitch-tag (for better readability not coded inside the switch-structure)

                                        default: dsProperties.properties.Add(dsReader.Name, dsReader.ReadInnerXml()); continue;
                                        }

                                        // *** READ POLICYSWITCH ELEMENT ***
                                        PolSwitchItem switchProperties = new PolSwitchItem();
                                        XmlReader     switchReader     = dsReader.ReadSubtree(); switchReader.ReadToDescendant(EM2TAGS.SWITCHPOLID);
                                        do
                                        {
                                            if (switchReader.NodeType != XmlNodeType.Element)
                                            {
                                                continue;
                                            }
                                            switch (switchReader.Name)
                                            {
                                            case EM2TAGS.SWITCHPOLID: switchProperties.switchPolID = switchReader.ReadInnerXml(); continue;

                                            case EM2TAGS.SYSTEM_ID: switchProperties.sysID = switchReader.ReadInnerXml(); continue;

                                            case EM2TAGS.DATAID: switchProperties.dataID = switchReader.ReadInnerXml(); continue;

                                            case EM2TAGS.VALUE: switchProperties.value = switchReader.ReadInnerXml(); continue;
                                            }
                                        } while (switchReader.Read());

                                        // *** STORE POLICYSWITCH ELEMENT ***
                                        content.policySwitches.Add(switchProperties);
                                        switchReader.Close();
                                    } while (dsReader.Read());

                                    // *** STORE DBSYSTEMCONFIG ELEMENT ***
                                    content.sysData.Add(dsProperties);
                                    dsReader.Close();
                                } while (dataReader.Read());

                                // *** STORE DATABASE ELEMENT ***
                                content.dataSets.Add(dataProperties.id, dataProperties);
                                dataReader.Close();
                            }
                            else if (mainReader.Name == EM2TAGS.EXTENSION)
                            {
                                XmlReader extReader = mainReader.ReadSubtree(); extReader.ReadToDescendant(EM2TAGS.ID);
                                EM2Item   extension = new EM2Item();
                                do
                                {
                                    if (extReader.NodeType != XmlNodeType.Element)
                                    {
                                        continue;
                                    }
                                    switch (extReader.Name)
                                    {
                                    case EM2TAGS.ID: extension.id = extReader.ReadInnerXml(); break;

                                    case EM2TAGS.NAME: extension.name = extReader.ReadInnerXml(); break;

                                    case EM2TAGS.SHORTNAME: extension.properties.Add(EM2TAGS.SHORTNAME, extReader.ReadInnerXml()); break;

                                    case EM2TAGS.LOOK: extension.properties.Add(EM2TAGS.LOOK, extReader.ReadInnerXml()); break;
                                    }
                                } while (extReader.Read());
                                content.localExtensions.Add(extension);
                                extReader.Close();
                            }
                        } while (mainReader.Read());
                    }
                return(content);
            }
            catch (Exception exception)
            {
                errors.Add(GenerateError(exception.Message));
                return(null);
            }

            string GenerateError(string error)
            {
                FileInfo fi = new FileInfo(fileName);

                return($"{(fi == null ? string.Empty : fi.Name + ": ")}{error}");
            }
        }
예제 #11
0
 private void AdaptSysProp(EM2Item sys)
 {
     sys.properties.Remove(EM2TAGS.COUNTRY_ID);         // dispensable
     sys.properties.Remove(EM2TAGS.EXCHANGE_RATE_EURO); // moved to global file ExchangeRates.xml
 }