Skip to content

Commit 47f25f2

Browse files
authored
Merge pull request doxygen#12224 from albert-github/feature/bug_dw_configgen_2
Check and autocorrect translated configuration files
2 parents b4b0bc1 + b3246ba commit 47f25f2

1 file changed

Lines changed: 216 additions & 6 deletions

File tree

src/configgen.py

Lines changed: 216 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -732,19 +732,24 @@ def collectOptions(elem):
732732
options = set()
733733
messages = set()
734734
optionsWithElems = {}
735+
attrib = {}
736+
values = set()
735737
for group in elem.getElementsByTagName('group'):
736738
for option in group.getElementsByTagName('option'):
737739
optionId = option.getAttribute('id')
738740
optionType = option.getAttribute('type')
739741
if optionId and optionType!='obsolete':
740742
options.add(optionId)
741743
optionsWithElems[optionId] = option
744+
attrib[optionId] = sorted(option.attributes.items())
745+
if option.getElementsByTagName('value'):
746+
values.add(optionId)
742747
for generator in elem.getElementsByTagName('generator'):
743748
for message in generator.getElementsByTagName('message'):
744749
messageId = message.getAttribute('name')
745750
messages.add(messageId)
746751

747-
return (options,optionsWithElems,messages)
752+
return (options,optionsWithElems,messages,attrib,values)
748753

749754
def syncWarnings(typ, existing, language):
750755
missing = existing - language
@@ -777,7 +782,7 @@ def syncLocalizedConfig(elem, configFile, translationsDir, autoSync=False):
777782
import os
778783
import shutil
779784

780-
existingOptions, existingOptionsWithElements, existingMessages = collectOptions(elem)
785+
existingOptions, existingOptionsWithElements, existingMessages, existingAttrib, existingValues = collectOptions(elem)
781786
print("Found %d active options in %s" % (len(existingOptions), configFile))
782787
print("Found %d active messages in %s" % (len(existingMessages), configFile))
783788

@@ -801,12 +806,104 @@ def syncLocalizedConfig(elem, configFile, translationsDir, autoSync=False):
801806
print(" Error parsing %s: %s" % (configFile, e))
802807
continue
803808

804-
langOptions, langOptionsWithElements, langMessages = collectOptions(langDoc)
809+
langOptions, langOptionsWithElements, langMessages, langAttrib, langValues = collectOptions(langDoc)
805810

806811
optionsError = syncWarnings('Options', existingOptions, langOptions)
807812
messagesError = syncWarnings('Messages', existingMessages, langMessages)
808813

809-
if autoSync and (optionsError or messagesError):
814+
# attributes handling
815+
extraOptions = langOptions - existingOptions
816+
langMatch = langOptions - extraOptions
817+
attribError = set()
818+
for optionId in langMatch:
819+
missingAttrib = set(existingAttrib[optionId]) - set(langAttrib[optionId])
820+
extraAttrib = set(langAttrib[optionId]) - set(existingAttrib[optionId])
821+
if missingAttrib:
822+
attribError.add(optionId)
823+
print(" Missing %d %s for %s" % (len(missingAttrib), "attributes", optionId))
824+
825+
if extraAttrib:
826+
attribError.add(optionId)
827+
print(" Extra %d %s for %s" % (len(extraAttrib), "attributes", optionId))
828+
if not attribError:
829+
print(" OK - all %s are synchronized" % "attributes")
830+
831+
# values handling
832+
valuesError = False
833+
# we only need options that are also in the original
834+
langValues = langValues - extraOptions
835+
# language options that should have values
836+
missingAllValues = existingValues - langValues
837+
if missingAllValues:
838+
print(" Missing %d %s: %s" % (len(missingAllValues), "all values", ', '.join(sorted(list(missingAllValues))[:5])))
839+
if len(missingAllValues) > 5:
840+
print(" ... and %d more" % (len(missingAllValues) - 5))
841+
valuesError = True
842+
# language options that should have no values
843+
extraAllValues = langValues - existingValues
844+
if extraAllValues:
845+
print(" Extra %d %s: %s" % (len(extraAllValues), "all values", ', '.join(sorted(list(extraAllValues))[:5])))
846+
if len(extraAllValues) > 5:
847+
print(" ... and %d more" % (len(extraAllValues) - 5))
848+
valuesError = True
849+
850+
# both have values, some furter investigations
851+
bothValues = existingValues - missingAllValues
852+
# partial extra / missing attr
853+
bothError = set()
854+
for optionId in bothValues:
855+
optionElem = existingOptionsWithElements[optionId]
856+
langElem = langOptionsWithElements[optionId]
857+
optValues = set()
858+
langValues = set()
859+
for optValue in optionElem.getElementsByTagName('value'):
860+
optValues.add(optValue.getAttribute('name'))
861+
for langValue in langElem.getElementsByTagName('value'):
862+
langValues.add(langValue.getAttribute('name'))
863+
missingValues = optValues - langValues
864+
missingLen = len(missingValues)
865+
extraLen = len(langValues - optValues)
866+
if missingLen:
867+
print(" Missing %d %s for %s" % (missingLen, "values", optionId))
868+
bothError.add(optionId)
869+
if extraLen:
870+
print(" Extra %d %s for %s" % (extraLen, "values", optionId))
871+
bothError.add(optionId)
872+
# both have value elements
873+
bothName = optValues - missingValues
874+
for name in bothName:
875+
toCorrect = False
876+
for optValue in optionElem.getElementsByTagName('value'):
877+
if optValue.getAttribute('name') == name:
878+
optAttr = set(optValue.attributes.items())
879+
break
880+
for langValue in langElem.getElementsByTagName('value'):
881+
if langValue.getAttribute('name') == name:
882+
langAttr = set(langValue.attributes.items())
883+
break
884+
missing = optAttr - langAttr
885+
extra = langAttr - optAttr
886+
if missing or extra:
887+
if len(missing) == 1 and len(extra) == 1:
888+
for attr, dummy in missing:
889+
missAttr = attr
890+
break
891+
for attr, dummy in extra:
892+
extraAttr = attr
893+
break
894+
if missAttr != 'desc' or extraAttr != 'desc':
895+
bothError.add(optionId)
896+
toCorrect = True
897+
else:
898+
bothError.add(optionId)
899+
toCorrect = True
900+
if toCorrect:
901+
print(" Differences in attributes for %s with name %s for %s" % ("value", name, optionId))
902+
903+
if not (valuesError or bothError):
904+
print(" OK - all %s are synchronized" % "values")
905+
906+
if autoSync and (optionsError or messagesError or attribError or valuesError or bothError):
810907
print(" Auto-syncing...")
811908

812909
rootElement = langDoc.documentElement
@@ -838,6 +935,118 @@ def syncLocalizedConfig(elem, configFile, translationsDir, autoSync=False):
838935
parentGroupNew.appendChild(importedElem)
839936
print(" Added: %s" % optionId)
840937

938+
# handle option attributes
939+
for optionId in attribError:
940+
existingElem = existingOptionsWithElements[optionId]
941+
langElem = langOptionsWithElements[optionId]
942+
for attr,val in langElem.attributes.items():
943+
langElem.removeAttribute(attr)
944+
for attr,val in existingElem.attributes.items():
945+
langElem.setAttribute(attr,val)
946+
947+
# handle values
948+
for optionId in extraAllValues:
949+
optionElem = langOptionsWithElements[optionId]
950+
for valueElem in optionElem.getElementsByTagName('value'):
951+
optionElem.removeChild(valueElem)
952+
print(" Removed all values of: %s" % optionId)
953+
954+
for optionId in missingAllValues:
955+
existingElem = existingOptionsWithElements[optionId]
956+
langElem = langOptionsWithElements[optionId]
957+
for valueElem in existingElem.getElementsByTagName('value'):
958+
importedElem = langDoc.importNode(valueElem, True)
959+
langElem.appendChild(importedElem)
960+
print(" Added all values for: %s" % optionId)
961+
962+
# handle bothValue errors
963+
for optionId in bothError:
964+
optionElem = existingOptionsWithElements[optionId]
965+
langElem = langOptionsWithElements[optionId]
966+
optValues = set()
967+
langValues = set()
968+
for optValue in optionElem.getElementsByTagName('value'):
969+
optValues.add(optValue.getAttribute('name'))
970+
for langValue in langElem.getElementsByTagName('value'):
971+
langValues.add(langValue.getAttribute('name'))
972+
missing = optValues - langValues
973+
extra = langValues - optValues
974+
existingElem = existingOptionsWithElements[optionId]
975+
langElem = langOptionsWithElements[optionId]
976+
if missing:
977+
for miss in missing:
978+
for allVal in existingElem.getElementsByTagName('value'):
979+
if allVal.getAttribute('name') == miss:
980+
importedElem = langDoc.importNode(allVal, True)
981+
langElem.appendChild(importedElem)
982+
print(" Added: value %s of %s" % (miss,optionId))
983+
if extra:
984+
for extr in extra:
985+
for allVal in langElem.getElementsByTagName('value'):
986+
if allVal.getAttribute('name') == extr:
987+
langElem.removeChild(allVal)
988+
print(" Removed: value %s of %s" % (extr,optionId))
989+
990+
# both have value elements
991+
bothName = optValues - missing
992+
for name in bothName:
993+
toCorrect = False
994+
for optValue in optionElem.getElementsByTagName('value'):
995+
if optValue.getAttribute('name') == name:
996+
optAttr = set(optValue.attributes.items())
997+
break
998+
for langValue in langElem.getElementsByTagName('value'):
999+
if langValue.getAttribute('name') == name:
1000+
langAttr = set(langValue.attributes.items())
1001+
break
1002+
missing = optAttr - langAttr
1003+
extra = langAttr - optAttr
1004+
if missing or extra:
1005+
if len(missing) == 1 and len(extra) == 1:
1006+
for attr, dummy in missing:
1007+
missAttr = attr
1008+
break
1009+
for attr, dummy in extra:
1010+
extraAttr = attr
1011+
break
1012+
if missAttr != 'desc' or extraAttr != 'desc':
1013+
toCorrect = True
1014+
else:
1015+
toCorrect = True
1016+
1017+
if toCorrect:
1018+
optHasDesc = False
1019+
for optValue in optionElem.getElementsByTagName('value'):
1020+
if optValue.getAttribute('name') == name:
1021+
for attr, dummy in optValue.attributes.items():
1022+
if attr == 'desc':
1023+
optHasDesc = True
1024+
break
1025+
break
1026+
langHasDesc = False
1027+
for langValue in langElem.getElementsByTagName('value'):
1028+
if langValue.getAttribute('name') == name:
1029+
for attr, dummy in langValue.attributes.items():
1030+
if attr == 'desc':
1031+
langHasDesc = True
1032+
break
1033+
break
1034+
for langValue in langElem.getElementsByTagName('value'):
1035+
if langValue.getAttribute('name') == name:
1036+
for attr, dummy in langValue.attributes.items():
1037+
if attr != 'desc' or not optHasDesc:
1038+
langValue.removeAttribute(attr)
1039+
lang = langValue
1040+
break
1041+
for optValue in optionElem.getElementsByTagName('value'):
1042+
if optValue.getAttribute('name') == name:
1043+
for attr, val in optValue.attributes.items():
1044+
if attr != 'desc' or not langHasDesc:
1045+
lang.setAttribute(attr,val)
1046+
break
1047+
1048+
1049+
# handle generator / messages
8411050
missingMessages = existingMessages - langMessages
8421051
extraMessages = langMessages - existingMessages
8431052

@@ -856,7 +1065,8 @@ def syncLocalizedConfig(elem, configFile, translationsDir, autoSync=False):
8561065
for messageId in missingMessages:
8571066
for message in parentGenerator.getElementsByTagName('message'):
8581067
if messageId == message.getAttribute('name'):
859-
parentGeneratorNew.appendChild(message)
1068+
importedElem = langDoc.importNode(message, True)
1069+
parentGeneratorNew.appendChild(importedElem)
8601070
print(" Added: %s" % messageId)
8611071

8621072
backupFile = configFile + ".bak"
@@ -1110,7 +1320,7 @@ def main():
11101320
print("#include \"configdoc.h\"")
11111321
print("#include \"docintf.h\"")
11121322
print("")
1113-
##
1323+
11141324
print("void addConfigDocs{0}(DocIntf *doc)".format(locale))
11151325
print("{")
11161326
for n in elem.childNodes:

0 commit comments

Comments
 (0)