// 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 }
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) }
/// <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}"); } }
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 }