blob: fd7c5987405ef68333cec21bb922e8cf0ee96760 [file] [log] [blame]
// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package results contains the result type returned by the classifier backend.
// Placing the type into a separate module allows us to swap out backends and
// still use the same datatype.
package results
import (
"bufio"
"fmt"
"os"
"sort"
)
// LicenseType is the assumed type of the unknown license.
type LicenseType struct {
Filename string
Name string
MatchType string
Variant string
Confidence float64
StartLine int
EndLine int
}
// LicenseTypes is a list of LicenseType objects.
type LicenseTypes []*LicenseType
func (lt LicenseTypes) Len() int { return len(lt) }
func (lt LicenseTypes) Swap(i, j int) { lt[i], lt[j] = lt[j], lt[i] }
func (lt LicenseTypes) Less(i, j int) bool {
if lt[i].Confidence > lt[j].Confidence {
return true
}
if lt[i].Confidence < lt[j].Confidence {
return false
}
if lt[i].Filename < lt[j].Filename {
return true
}
if lt[i].Filename > lt[j].Filename {
return false
}
return lt[i].EndLine < lt[j].EndLine
}
// Classification is the license classification for a segment of a file.
type Classification struct {
Name string
Confidence float64
StartLine int
EndLine int
Text string `json:",omitempty"`
}
// Classifications contains all license classifications for a file
type Classifications []*Classification
// FileClassifications contains the license classifications for a particular file.
type FileClassifications struct {
Filepath string
Classifications Classifications
}
//JSONResult is the format for the jr JSON file
type JSONResult []*FileClassifications
func (jr JSONResult) Len() int { return len(jr) }
func (jr JSONResult) Swap(i, j int) { jr[i], jr[j] = jr[j], jr[i] }
func (jr JSONResult) Less(i, j int) bool { return jr[i].Filepath < jr[j].Filepath }
// readFileLines will read a specified range of lines of a file
func readFileLines(filename string, startLine, endLine int) (string, error) {
f, err := os.Open(filename)
if err != nil {
return "", err
}
defer f.Close()
scanner := bufio.NewScanner(f)
lines := ""
i := 0
for scanner.Scan() {
i++ // lines are 1-indexed
if i < startLine {
continue
} else if i > endLine {
break
}
lines += scanner.Text() + "\n"
}
if i < endLine {
return "", fmt.Errorf(
"line %d was the last line read from file %s, but endLine was set to %d", i, filename, endLine)
}
return lines, nil
}
// NewJSONResult creates a new JSONResult object from a LicenseTypes object.
func NewJSONResult(licenses LicenseTypes, includeText bool) (JSONResult, error) {
fMap := map[string]*FileClassifications{}
for _, l := range licenses {
currF, ok := fMap[l.Filename]
if !ok {
currF = &FileClassifications{Filepath: l.Filename}
fMap[l.Filename] = currF
}
c := &Classification{
Name: l.Name,
Confidence: l.Confidence,
StartLine: l.StartLine,
EndLine: l.EndLine,
}
if includeText {
text, err := readFileLines(l.Filename, l.StartLine, l.EndLine)
if err != nil {
return nil, err
}
c.Text = text
}
currF.Classifications = append(currF.Classifications, c)
}
jr := JSONResult{}
for _, fc := range fMap {
jr = append(jr, fc)
}
sort.Sort(jr)
return jr, nil
}