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  }