1// Copyright 2017 Google Inc. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15// Package results contains the result type returned by the classifier backend. 16// Placing the type into a separate module allows us to swap out backends and 17// still use the same datatype. 18package results 19 20import ( 21 "bufio" 22 "fmt" 23 "os" 24 "sort" 25) 26 27// LicenseType is the assumed type of the unknown license. 28type LicenseType struct { 29 Filename string 30 Name string 31 MatchType string 32 Variant string 33 Confidence float64 34 StartLine int 35 EndLine int 36} 37 38// LicenseTypes is a list of LicenseType objects. 39type LicenseTypes []*LicenseType 40 41func (lt LicenseTypes) Len() int { return len(lt) } 42func (lt LicenseTypes) Swap(i, j int) { lt[i], lt[j] = lt[j], lt[i] } 43func (lt LicenseTypes) Less(i, j int) bool { 44 if lt[i].Confidence > lt[j].Confidence { 45 return true 46 } 47 if lt[i].Confidence < lt[j].Confidence { 48 return false 49 } 50 if lt[i].Filename < lt[j].Filename { 51 return true 52 } 53 if lt[i].Filename > lt[j].Filename { 54 return false 55 } 56 return lt[i].EndLine < lt[j].EndLine 57} 58 59// Classification is the license classification for a segment of a file. 60type Classification struct { 61 Name string 62 Confidence float64 63 StartLine int 64 EndLine int 65 Text string `json:",omitempty"` 66} 67 68// Classifications contains all license classifications for a file 69type Classifications []*Classification 70 71// FileClassifications contains the license classifications for a particular file. 72type FileClassifications struct { 73 Filepath string 74 Classifications Classifications 75} 76 77// JSONResult is the format for the jr JSON file 78type JSONResult []*FileClassifications 79 80func (jr JSONResult) Len() int { return len(jr) } 81func (jr JSONResult) Swap(i, j int) { jr[i], jr[j] = jr[j], jr[i] } 82func (jr JSONResult) Less(i, j int) bool { return jr[i].Filepath < jr[j].Filepath } 83 84// readFileLines will read a specified range of lines of a file 85func readFileLines(filename string, startLine, endLine int) (string, error) { 86 f, err := os.Open(filename) 87 if err != nil { 88 return "", err 89 } 90 defer f.Close() 91 92 scanner := bufio.NewScanner(f) 93 lines := "" 94 i := 0 95 for scanner.Scan() { 96 i++ // lines are 1-indexed 97 if i < startLine { 98 continue 99 } else if i > endLine { 100 break 101 } 102 lines += scanner.Text() + "\n" 103 } 104 if i < endLine { 105 return "", fmt.Errorf( 106 "line %d was the last line read from file %s, but endLine was set to %d", i, filename, endLine) 107 } 108 return lines, nil 109} 110 111// NewJSONResult creates a new JSONResult object from a LicenseTypes object. 112func NewJSONResult(licenses LicenseTypes, includeText bool) (JSONResult, error) { 113 fMap := map[string]*FileClassifications{} 114 for _, l := range licenses { 115 currF, ok := fMap[l.Filename] 116 if !ok { 117 currF = &FileClassifications{Filepath: l.Filename} 118 fMap[l.Filename] = currF 119 } 120 c := &Classification{ 121 Name: l.Name, 122 Confidence: l.Confidence, 123 StartLine: l.StartLine, 124 EndLine: l.EndLine, 125 } 126 if includeText { 127 text, err := readFileLines(l.Filename, l.StartLine, l.EndLine) 128 if err != nil { 129 return nil, err 130 } 131 c.Text = text 132 } 133 currF.Classifications = append(currF.Classifications, c) 134 } 135 136 jr := JSONResult{} 137 for _, fc := range fMap { 138 jr = append(jr, fc) 139 } 140 sort.Sort(jr) 141 return jr, nil 142} 143