github.com/xml-comp/xml-comp@v0.0.42-0.20180719122048-292fe466b944/comparer/comparer.go (about) 1 package comparer 2 3 import ( 4 "bufio" 5 "fmt" 6 "os" 7 "path/filepath" 8 "reflect" 9 "strings" 10 ) 11 12 const ( 13 pathSep = string(os.PathSeparator) 14 ) 15 16 var ( 17 // DocType is required If you want to use the package, so don't 18 // forget to instantiate It before using the Compare function 19 DocType string 20 // Docs , Lines and InNeed are `metrics` of how the program is running 21 Docs int 22 Lines int 23 InNeed int 24 ) 25 26 // Compare is the function that takes two comparable paths to 27 // directories and writes It's differences into the translation's 28 // directories files 29 func Compare(original, translation string) (err error) { 30 originalDir, err := ReadDir(original) 31 if err != nil { 32 return 33 } 34 for _, f := range originalDir { 35 if f.IsDir() { 36 err = checkTransDirExists(f.Name(), translation) 37 if err != nil { 38 return 39 } 40 err = Compare(filepath.Join(original, f.Name()), filepath.Join(translation, f.Name())) 41 if err != nil { 42 return 43 } 44 } else { 45 Docs += 2 46 err = readFiles(filepath.Join(original, f.Name()), filepath.Join(translation, f.Name())) 47 if err != nil { 48 return 49 } 50 } 51 } 52 return 53 } 54 55 func ReadDir(path string) (file []os.FileInfo, err error) { 56 err = os.Chdir(path) 57 if err != nil { 58 return 59 } 60 fi, err := os.Open(path) 61 if err != nil { 62 return 63 } 64 defer fi.Close() 65 file, err = fi.Readdir(0) 66 if err != nil { 67 return 68 } 69 return 70 } 71 72 func readFiles(originalFile, translationFile string) (err error) { 73 if (len(originalFile) == 0) || (len(translationFile) == 0) { 74 return errEmptyFileOrPathName 75 } 76 err = os.Chdir(filepath.Dir(originalFile)) 77 if err != nil { 78 return 79 } 80 fName := strings.Split(originalFile, pathSep) 81 fileName := fName[len(fName)-1] 82 orgTags, err := readFile(fileName, filepath.Dir(originalFile)) 83 if err != nil { 84 return 85 } 86 fName = strings.Split(translationFile, pathSep) 87 fileName = fName[len(fName)-1] 88 trltTags, err := readFile(fileName, filepath.Dir(translationFile)) 89 if err != nil { 90 err = os.Chdir(filepath.Dir(translationFile)) 91 if err != nil { 92 return 93 } 94 file, errCreate := os.Create(fileName) 95 if errCreate != nil { 96 return errCreate 97 } 98 defer file.Close() 99 } 100 missingTags := findMissing(orgTags, trltTags) 101 if missingTags == nil { 102 return 103 } 104 outdatedTags := findMissing(trltTags, orgTags) 105 err = writeToFileMissingTags(translationFile, outdatedTags, true) 106 if err != nil { 107 return 108 } 109 err = writeToFileMissingTags(translationFile, missingTags, false) 110 if err != nil { 111 return 112 } 113 return 114 } 115 116 func writeToFileMissingTags(translationFilePath string, missingTags map[string]string, outdated bool) (err error) { 117 f, err := os.OpenFile(translationFilePath, os.O_APPEND|os.O_WRONLY, 0600) 118 if err != nil { 119 return 120 } 121 defer f.Close() 122 for missingKey, missingValue := range missingTags { 123 if len(missingKey) == 0 { 124 continue 125 } 126 if string(missingKey[1]) == pathSep { 127 continue 128 } 129 InNeed++ 130 if isCommentaryOrDocType(missingKey) { 131 _, err = f.WriteString(fmt.Sprintf("\n%s", missingKey)) 132 if err != nil { 133 return 134 } 135 continue 136 } 137 if outdated { 138 _, err = f.WriteString(fmt.Sprintf("\n[OUTDATED]%s", missingKey)) 139 if err != nil { 140 return 141 } 142 continue 143 } 144 _, err = f.WriteString(fmt.Sprintf("\n%s%s</%s", missingKey, missingValue, missingKey[1:])) 145 if err != nil { 146 return 147 } 148 } 149 return 150 } 151 152 func isCommentaryOrDocType(key string) bool { 153 if (hasSubstring(key, "<!-")) || (hasSubstring(key, "<--")) || hasSubstring(key, "<?"+DocType) { 154 return true 155 } 156 return false 157 } 158 159 func hasSubstring(str, s string) bool { 160 return strings.Contains(s, str) 161 } 162 163 func readFile(fileName, filePath string) (map[string]string, error) { 164 if (len(fileName) == 0) || (len(filePath) == 0) { 165 return nil, errEmptyFileOrPathName 166 } 167 splittedFileName := strings.Split(fileName, ".") 168 if splittedFileName[len(splittedFileName)-1] != DocType { 169 return nil, nil 170 } 171 inFile, err := os.Open(filepath.Join(filePath, fileName)) 172 if err != nil { 173 return nil, err 174 } 175 defer inFile.Close() 176 tags := map[string]string{} 177 scanner := bufio.NewScanner(inFile) 178 for scanner.Scan() { 179 Lines++ 180 line := scanner.Text() 181 indexStart := strings.Index(line, "<") 182 indexEnd := strings.Index(line, ">") 183 if (len(line) == 0) || indexStart < 0 || indexEnd < 0 { 184 continue 185 } 186 tag := line[indexStart : indexEnd+1] 187 if string(tag[0]) == pathSep { 188 continue 189 } 190 markers := strings.Split(tag, " ") 191 tag = markers[0] 192 valEnd := strings.LastIndex(line, "<") 193 if valEnd < indexEnd { 194 continue 195 } 196 translationValue := line[indexEnd+1 : valEnd] 197 if (indexStart != -1) && (indexEnd != -1) { 198 tags[tag] = translationValue 199 } 200 } 201 return tags, nil 202 } 203 204 func findMissing(original, translation map[string]string) map[string]string { 205 missing := make(map[string]string) 206 if reflect.DeepEqual(original, translation) { 207 return nil 208 } 209 for k, v := range original { 210 if _, ok := translation[k]; !ok { 211 missing[k] = v 212 } 213 } 214 return missing 215 } 216 217 // TODO: refactor 218 // this should return a bool and an error, 219 // this way the upper func can handle dir creation 220 func checkTransDirExists(dir, translation string) (err error) { 221 splitDir := strings.Split(dir, pathSep) 222 dir = filepath.Join(translation, splitDir[len(splitDir)-1]) 223 _, err = os.Open(dir) 224 if err != nil { 225 splitedDirectory := strings.Split(dir, pathSep) 226 parentDirFromSplit := dir[:len(dir)-len(splitedDirectory[len(splitedDirectory)-1])-1] 227 os.Chdir(parentDirFromSplit) 228 err = os.Mkdir(splitedDirectory[len(splitedDirectory)-1], 0700) 229 if err != nil { 230 return 231 } 232 } 233 return 234 }