@@ -12,15 +12,14 @@ import (
12
12
"errors"
13
13
"fmt"
14
14
"go/ast"
15
- "go/build"
16
15
"go/parser"
17
16
"go/scanner"
18
17
"go/token"
19
18
"os"
20
19
"path/filepath"
20
+ "reflect"
21
21
22
22
"aahframework.org/essentials.v0"
23
- "aahframework.org/log.v0"
24
23
)
25
24
26
25
var (
@@ -61,17 +60,17 @@ var (
61
60
62
61
// Inspect method processes the Go source code for the given directory and its
63
62
// sub-directories.
64
- func Inspect (path string , excludes ess.Excludes , registeredActions map [string ]map [string ]uint8 ) (* program , []error ) {
65
- if err := validateInput (path ); err != nil {
66
- return nil , append ([]error {}, err )
67
- }
68
-
69
- prg := & program {
63
+ func Inspect (path string , excludes ess.Excludes , registeredActions map [string ]map [string ]uint8 ) (* Program , []error ) {
64
+ prg := & Program {
70
65
Path : path ,
71
66
Packages : []* packageInfo {},
72
67
RegisteredActions : registeredActions ,
73
68
}
74
69
70
+ if err := validateInput (path ); err != nil {
71
+ return prg , append ([]error {}, err )
72
+ }
73
+
75
74
var (
76
75
pkgs map [string ]* ast.Package
77
76
errs []error
@@ -138,281 +137,54 @@ func Inspect(path string, excludes ess.Excludes, registeredActions map[string]ma
138
137
return prg , errs
139
138
}
140
139
141
- //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
142
- // Program struct and its methods
143
- //______________________________________________________________________________
144
-
145
- // Program holds all details loaded from the Go source code for given Path.
146
- type program struct {
147
- Path string
148
- Packages []* packageInfo
149
- RegisteredActions map [string ]map [string ]uint8
150
- }
151
-
152
- // Process method processes all packages in the program for `Type`,
153
- // `Embedded Type`, `Method`, etc.
154
- func (prg * program ) Process () {
155
- for _ , pkgInfo := range prg .Packages {
156
- pkgInfo .Types = map [string ]* typeInfo {}
157
-
158
- // Each source file
159
- for name , file := range pkgInfo .Pkg .Files {
160
- pkgInfo .Files = append (pkgInfo .Files , filepath .Base (name ))
161
- fileImports := make (map [string ]string )
162
-
163
- for _ , decl := range file .Decls {
164
- // Processing imports
165
- pkgInfo .processImports (decl , fileImports )
166
-
167
- // Processing types
168
- pkgInfo .processTypes (decl , fileImports )
169
-
170
- // Processing methods
171
- processMethods (pkgInfo , prg .RegisteredActions , decl , fileImports )
172
- }
173
- }
140
+ // FindFieldIndexes method does breadth-first search on struct
141
+ // anonymous field to find given type `struct` discover index positions.
142
+ //
143
+ // For e.g.: `aah.Context`, `ws.Context`, etc.
144
+ func FindFieldIndexes (targetTyp reflect.Type , searchTyp reflect.Type ) [][]int {
145
+ var indexes [][]int
146
+ type nodeType struct {
147
+ val reflect.Value
148
+ index []int
174
149
}
175
- }
176
150
177
- // FindTypeByEmbeddedType method returns all the typeInfo that has directly or
178
- // indirectly embedded by given type name. Type name must be fully qualified
179
- // type name. E.g.: aahframework.org/aah.Controller
180
- func (prg * program ) FindTypeByEmbeddedType (qualifiedTypeName string ) []* typeInfo {
181
- var (
182
- queue = []string {qualifiedTypeName }
183
- processed []string
184
- result []* typeInfo
185
- )
151
+ queue := []nodeType {{reflect .New (targetTyp ), []int {}}}
186
152
187
153
for len (queue ) > 0 {
188
- typeName := queue [0 ]
189
- queue = queue [1 :]
190
- processed = append (processed , typeName )
191
-
192
- // search within all packages in the program
193
- for _ , p := range prg .Packages {
194
- // search within all struct type in the package
195
- for _ , t := range p .Types {
196
- // If this one has been processed or is already in queue, then move on.
197
- if ess .IsSliceContainsString (processed , t .FullyQualifiedName ()) ||
198
- ess .IsSliceContainsString (queue , t .FullyQualifiedName ()) {
199
- continue
200
- }
201
-
202
- // search through the embedded types to see if the current type is among them.
203
- for _ , et := range t .EmbeddedTypes {
204
- // If so, add this type's FullyQualifiedName into queue,
205
- // and it's typeInfo into result.
206
- if typeName == et .FullyQualifiedName () {
207
- queue = append (queue , t .FullyQualifiedName ())
208
- result = append (result , t )
209
- break
210
- }
211
- }
212
- }
213
- }
214
- }
215
-
216
- return result
217
- }
218
-
219
- // CreateImportPaths method returns unique package alias with import path.
220
- func (prg * program ) CreateImportPaths (types []* typeInfo ) map [string ]string {
221
- importPaths := map [string ]string {}
222
- for _ , t := range types {
223
- createAlias (t .PackageName (), t .ImportPath , importPaths )
224
- for _ , m := range t .Methods {
225
- for _ , p := range m .Parameters {
226
- if ! p .Type .IsBuiltIn {
227
- createAlias (p .Type .PackageName , p .ImportPath , importPaths )
228
- }
229
- }
230
- }
231
- }
232
-
233
- return importPaths
234
- }
154
+ var (
155
+ node = queue [0 ]
156
+ elem = node .val
157
+ elemType = elem .Type ()
158
+ )
235
159
236
- func createAlias (packageName , importPath string , importPaths map [string ]string ) {
237
- importPath = filepath .ToSlash (importPath )
238
- if _ , found := importPaths [importPath ]; ! found {
239
- cnt := 0
240
- pkgAlias := packageName
241
-
242
- for isPkgAliasExists (importPaths , pkgAlias ) {
243
- pkgAlias = fmt .Sprintf ("%s%d" , packageName , cnt )
244
- cnt ++
245
- }
246
-
247
- if ! ess .IsStrEmpty (pkgAlias ) && ! ess .IsStrEmpty (importPath ) {
248
- importPaths [importPath ] = pkgAlias
160
+ if elemType .Kind () == reflect .Ptr {
161
+ elem = elem .Elem ()
162
+ elemType = elem .Type ()
249
163
}
250
- }
251
- }
252
164
253
- //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
254
- // PackageInfo struct and its methods
255
- //______________________________________________________________________________
256
-
257
- // PackageInfo holds the single paackge information.
258
- type packageInfo struct {
259
- Fset * token.FileSet
260
- Pkg * ast.Package
261
- Types map [string ]* typeInfo
262
- ImportPath string
263
- FilePath string
264
- Files []string
265
- }
266
-
267
- // Name method return package name
268
- func (p * packageInfo ) Name () string {
269
- return filepath .Base (p .ImportPath )
270
- }
271
-
272
- func (p * packageInfo ) processTypes (decl ast.Decl , imports map [string ]string ) {
273
- genDecl , ok := decl .(* ast.GenDecl )
274
- if ! ok || ! isTypeTok (genDecl ) || len (genDecl .Specs ) == 0 {
275
- return
276
- }
277
-
278
- spec := genDecl .Specs [0 ].(* ast.TypeSpec )
279
- st , ok := spec .Type .(* ast.StructType )
280
- if ! ok {
281
- // Not a struct type
282
- return
283
- }
284
-
285
- typeName := spec .Name .Name
286
- ty := & typeInfo {
287
- Name : typeName ,
288
- ImportPath : filepath .ToSlash (p .ImportPath ),
289
- Methods : make ([]* methodInfo , 0 ),
290
- EmbeddedTypes : make ([]* typeInfo , 0 ),
291
- }
292
-
293
- for _ , field := range st .Fields .List {
294
- // If field.Names is set, it's not an embedded type.
295
- if field .Names != nil && len (field .Names ) > 0 {
296
- continue
297
- }
298
-
299
- fPkgName , fTypeName := parseStructFieldExpr (field .Type )
300
- if ess .IsStrEmpty (fTypeName ) {
165
+ queue = queue [1 :]
166
+ if elemType .Kind () != reflect .Struct {
301
167
continue
302
168
}
303
169
304
- // Find the import path for embedded type. If it was referenced without
305
- // a package name, use the current package import path otherwise
306
- // get the import path by package name.
307
- var eTypeImportPath string
308
- if ess .IsStrEmpty (fPkgName ) {
309
- eTypeImportPath = ty .ImportPath
310
- } else {
311
- var found bool
312
- if eTypeImportPath , found = imports [fPkgName ]; ! found {
313
- log .Errorf ("AST: Unable to find import path for %s.%s" , fPkgName , fTypeName )
170
+ for i := 0 ; i < elem .NumField (); i ++ {
171
+ // skip non-anonymous fields
172
+ field := elemType .Field (i )
173
+ if ! field .Anonymous {
314
174
continue
315
175
}
316
- }
317
-
318
- ty .EmbeddedTypes = append (ty .EmbeddedTypes , & typeInfo {Name : fTypeName , ImportPath : eTypeImportPath })
319
- }
320
176
321
- p .Types [typeName ] = ty
322
- }
323
-
324
- func (p * packageInfo ) processImports (decl ast.Decl , imports map [string ]string ) {
325
- genDecl , ok := decl .(* ast.GenDecl )
326
- if ! ok || ! isImportTok (genDecl ) {
327
- return
328
- }
329
-
330
- for _ , dspec := range genDecl .Specs {
331
- spec := dspec .(* ast.ImportSpec )
332
- var pkgAlias string
333
- if spec .Name != nil {
334
- if spec .Name .Name == "_" {
177
+ // If it's a search type then record the field index and move on
178
+ if field .Type == searchTyp {
179
+ indexes = append (indexes , append (node .index , i ))
335
180
continue
336
181
}
337
182
338
- pkgAlias = spec .Name .Name
183
+ fieldValue := elem .Field (i )
184
+ queue = append (queue ,
185
+ nodeType {fieldValue , append (append ([]int {}, node .index ... ), i )})
339
186
}
340
-
341
- importPath := spec .Path .Value [1 : len (spec .Path .Value )- 1 ]
342
- if ess .IsStrEmpty (pkgAlias ) {
343
- if alias , found := buildImportCache [importPath ]; found {
344
- pkgAlias = alias
345
- } else { // build cache
346
- pkg , err := build .Import (importPath , p .FilePath , 0 )
347
- if err != nil {
348
- log .Errorf ("AST: Unable to find import path: %s" , importPath )
349
- continue
350
- }
351
- pkgAlias = pkg .Name
352
- buildImportCache [importPath ] = pkg .Name
353
- }
354
- }
355
-
356
- imports [pkgAlias ] = importPath
357
- }
358
- }
359
-
360
- //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
361
- // TypeInfo struct and its methods
362
- //______________________________________________________________________________
363
-
364
- // TypeInfo holds the information about Controller Name, Methods,
365
- // Embedded types etc.
366
- type typeInfo struct {
367
- Name string
368
- ImportPath string
369
- Methods []* methodInfo
370
- EmbeddedTypes []* typeInfo
371
- }
372
-
373
- // MethodInfo holds the information of single method and it's Parameters.
374
- type methodInfo struct {
375
- Name string
376
- StructName string
377
- Parameters []* parameterInfo
378
- }
379
-
380
- // ParameterInfo holds the information of single Parameter in the method.
381
- type parameterInfo struct {
382
- Name string
383
- ImportPath string
384
- Type * typeExpr
385
- }
386
-
387
- // FullyQualifiedName method returns the fully qualified type name.
388
- func (t * typeInfo ) FullyQualifiedName () string {
389
- return fmt .Sprintf ("%s.%s" , t .ImportPath , t .Name )
390
- }
391
-
392
- // PackageName method returns types package name from import path.
393
- func (t * typeInfo ) PackageName () string {
394
- return filepath .Base (t .ImportPath )
395
- }
396
-
397
- //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
398
- // TypeExpr struct and its methods
399
- //______________________________________________________________________________
400
-
401
- // TypeExpr holds the information of single parameter data type.
402
- type typeExpr struct {
403
- Expr string
404
- IsBuiltIn bool
405
- PackageName string
406
- ImportPath string
407
- PackageIndex uint8
408
- Valid bool
409
- }
410
-
411
- // Name method returns type name for expression.
412
- func (te * typeExpr ) Name () string {
413
- if te .IsBuiltIn || ess .IsStrEmpty (te .PackageName ) {
414
- return te .Expr
415
187
}
416
188
417
- return fmt . Sprintf ( "%s%s.%s" , te . Expr [: te . PackageIndex ], te . PackageName , te . Expr [ te . PackageIndex :])
189
+ return indexes
418
190
}
0 commit comments