-
Notifications
You must be signed in to change notification settings - Fork 2
/
java_provider.go
179 lines (151 loc) · 4.95 KB
/
java_provider.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package provider
import (
"flag"
"fmt"
"log"
"path/filepath"
"sort"
"github.com/bazelbuild/bazel-gazelle/config"
"github.com/bazelbuild/bazel-gazelle/label"
"github.com/bazelbuild/bazel-gazelle/rule"
jipb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/jarindex"
sppb "github.com/stackb/scala-gazelle/build/stack/gazelle/scala/parse"
"github.com/stackb/scala-gazelle/pkg/collections"
"github.com/stackb/scala-gazelle/pkg/protobuf"
"github.com/stackb/scala-gazelle/pkg/resolver"
)
const debugUnresolvedSuperclass = false
// JavaProvider is a provider of symbols for a set of jarindex protos.
type JavaProvider struct {
jarindexFiles collections.StringSlice
scope resolver.Scope
byLabel map[label.Label]*jipb.JarFile
// classSymbols is map a *.ClassFile to it's symbol
classSymbols map[*jipb.ClassFile]*resolver.Symbol
}
// NewJavaProvider constructs a new provider.
func NewJavaProvider() *JavaProvider {
return &JavaProvider{
byLabel: make(map[label.Label]*jipb.JarFile),
jarindexFiles: make(collections.StringSlice, 0),
classSymbols: make(map[*jipb.ClassFile]*resolver.Symbol),
}
}
// Name implements part of the resolver.SymbolProvider interface.
func (p *JavaProvider) Name() string {
return "java"
}
// RegisterFlags implements part of the resolver.SymbolProvider interface.
func (p *JavaProvider) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config) {
fs.Var(&p.jarindexFiles, "javaindex_file", "path to javaindex.pb or javaindex.json file")
}
// CheckFlags implements part of the resolver.SymbolProvider interface.
func (p *JavaProvider) CheckFlags(fs *flag.FlagSet, c *config.Config, scope resolver.Scope) error {
p.scope = scope
for _, filename := range p.jarindexFiles {
if !filepath.IsAbs(filename) {
filename = filepath.Join(c.WorkDir, filename)
}
if err := p.readJarIndex(filename); err != nil {
return err
}
}
return nil
}
// OnResolve implements part of the resolver.SymbolProvider interface.
func (p *JavaProvider) OnResolve() error {
classFiles := sortedSymbolClassfiles(p.classSymbols)
for _, classFile := range classFiles {
symbol := p.classSymbols[classFile]
for _, superclass := range append(classFile.Superclasses, classFile.Interfaces...) {
if resolved, ok := p.scope.GetSymbol(superclass); ok {
symbol.Require(resolved)
} else if debugUnresolvedSuperclass {
log.Printf("Unresolved superclass %s of %s", superclass, classFile.Name)
}
}
}
return nil
}
// OnEnd implements part of the resolver.SymbolProvider interface.
func (p *JavaProvider) OnEnd() error {
return nil
}
// CanProvide implements part of the resolver.SymbolProvider interface.
func (p *JavaProvider) CanProvide(dep label.Label, knownRule func(from label.Label) (*rule.Rule, bool)) bool {
if _, ok := p.byLabel[dep]; ok {
return true
}
return false
}
func (p *JavaProvider) readJarIndex(filename string) error {
var index jipb.JarIndex
if err := protobuf.ReadFile(filename, &index); err != nil {
return fmt.Errorf("reading %s: %v", filename, err)
}
isPredefined := make(map[label.Label]bool)
for _, v := range index.Predefined {
lbl, err := label.Parse(v)
if err != nil {
return fmt.Errorf("bad predefined label %q: %v", v, err)
}
isPredefined[lbl] = true
}
for _, jarFile := range index.JarFile {
if err := p.readJarFile(jarFile, isPredefined); err != nil {
return err
}
}
return nil
}
func (p *JavaProvider) readJarFile(jarFile *jipb.JarFile, isPredefined map[label.Label]bool) error {
if jarFile.Filename == "" {
log.Panicf("jarFile must have a name: %+v", jarFile)
}
var from label.Label
if jarFile.Label != "" {
var err error
from, err = label.Parse(jarFile.Label)
if err != nil {
return fmt.Errorf("%s: parsing label %q: %v", jarFile.Filename, jarFile.Label, err)
}
}
p.byLabel[from] = jarFile
if isPredefined[from] {
from = label.NoLabel
}
for _, pkg := range jarFile.PackageName {
p.putSymbol(sppb.ImportType_PACKAGE, pkg, from)
}
for _, classFile := range jarFile.ClassFile {
p.readClassFile(classFile, from)
}
return nil
}
func (p *JavaProvider) readClassFile(classFile *jipb.ClassFile, from label.Label) {
impType := sppb.ImportType_CLASS
if classFile.IsInterface {
impType = sppb.ImportType_INTERFACE
}
symbol := p.putSymbol(impType, classFile.Name, from)
if len(classFile.Superclasses) > 0 || len(classFile.Interfaces) > 0 {
p.classSymbols[classFile] = symbol
}
}
func (p *JavaProvider) putSymbol(impType sppb.ImportType, imp string, from label.Label) *resolver.Symbol {
symbol := resolver.NewSymbol(impType, imp, p.Name(), from)
p.scope.PutSymbol(symbol)
return symbol
}
func sortedSymbolClassfiles(classSymbols map[*jipb.ClassFile]*resolver.Symbol) []*jipb.ClassFile {
classFiles := make([]*jipb.ClassFile, 0, len(classSymbols))
for classFile := range classSymbols {
classFiles = append(classFiles, classFile)
}
sort.Slice(classFiles, func(i, j int) bool {
a := classFiles[i]
b := classFiles[j]
return a.Name < b.Name
})
return classFiles
}