gitee.com/wgliang/goreporter@v0.0.0-20180902115603-df1b20f7c5d0/linters/copycheck/output/text.go (about)

     1  package output
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"log"
     7  	"sort"
     8  
     9  	"github.com/360EntSecGroup-Skylar/goreporter/linters/copycheck/syntax"
    10  )
    11  
    12  type FileReader interface {
    13  	ReadFile(node *syntax.Node) ([]byte, error)
    14  }
    15  
    16  type Printer interface {
    17  	Print(dups [][]*syntax.Node)
    18  	SPrint(dups [][]*syntax.Node) []string
    19  	Finish()
    20  }
    21  
    22  type TextPrinter struct {
    23  	writer  io.Writer
    24  	freader FileReader
    25  	cnt     int
    26  }
    27  
    28  func NewTextPrinter(w io.Writer, fr FileReader) *TextPrinter {
    29  	return &TextPrinter{
    30  		writer:  w,
    31  		freader: fr,
    32  	}
    33  }
    34  
    35  func (p *TextPrinter) Print(dups [][]*syntax.Node) {
    36  	p.cnt++
    37  	fmt.Fprintf(p.writer, "found %d clones:\n", len(dups))
    38  	clones := p.prepareClonesInfo(dups)
    39  	sort.Sort(byNameAndLine(clones))
    40  	for _, cl := range clones {
    41  		fmt.Fprintf(p.writer, "  %s:%d,%d\n", cl.filename, cl.lineStart, cl.lineEnd)
    42  	}
    43  }
    44  
    45  func (p *TextPrinter) SPrint(dups [][]*syntax.Node) (copys []string) {
    46  	p.cnt++
    47  	clones := p.prepareClonesInfo(dups)
    48  	sort.Sort(byNameAndLine(clones))
    49  	for _, cl := range clones {
    50  		copys = append(copys, fmt.Sprintf("%s:%d,%d\n", cl.filename, cl.lineStart, cl.lineEnd))
    51  	}
    52  	return copys
    53  }
    54  
    55  func (p *TextPrinter) prepareClonesInfo(dups [][]*syntax.Node) []clone {
    56  	clones := make([]clone, len(dups))
    57  	for i, dup := range dups {
    58  		cnt := len(dup)
    59  		if cnt == 0 {
    60  			log.Fatal("zero length dup")
    61  		}
    62  		nstart := dup[0]
    63  		nend := dup[cnt-1]
    64  
    65  		file, err := p.freader.ReadFile(nstart)
    66  		if err != nil {
    67  			log.Fatal(err)
    68  		}
    69  
    70  		cl := clone{filename: nstart.Filename}
    71  		cl.lineStart, cl.lineEnd = blockLines(file, nstart.Pos, nend.End)
    72  		clones[i] = cl
    73  	}
    74  	return clones
    75  }
    76  
    77  func (p *TextPrinter) Finish() {
    78  	fmt.Fprintf(p.writer, "\nFound total %d clone groups.\n", p.cnt)
    79  }
    80  
    81  func blockLines(file []byte, from, to int) (int, int) {
    82  	line := 1
    83  	lineStart, lineEnd := 0, 0
    84  	for offset, b := range file {
    85  		if b == '\n' {
    86  			line++
    87  		}
    88  		if offset == from {
    89  			lineStart = line
    90  		}
    91  		if offset == to-1 {
    92  			lineEnd = line
    93  			break
    94  		}
    95  	}
    96  	return lineStart, lineEnd
    97  }
    98  
    99  type clone struct {
   100  	filename  string
   101  	lineStart int
   102  	lineEnd   int
   103  	fragment  []byte
   104  }
   105  
   106  type byNameAndLine []clone
   107  
   108  func (c byNameAndLine) Len() int { return len(c) }
   109  
   110  func (c byNameAndLine) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
   111  
   112  func (c byNameAndLine) Less(i, j int) bool {
   113  	if c[i].filename == c[j].filename {
   114  		return c[i].lineStart < c[j].lineStart
   115  	}
   116  	return c[i].filename < c[j].filename
   117  }