aboutsummaryrefslogtreecommitdiff
path: root/src/tools/ak/liteparse/values_parse.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/ak/liteparse/values_parse.go')
-rw-r--r--src/tools/ak/liteparse/values_parse.go226
1 files changed, 226 insertions, 0 deletions
diff --git a/src/tools/ak/liteparse/values_parse.go b/src/tools/ak/liteparse/values_parse.go
new file mode 100644
index 0000000..048133b
--- /dev/null
+++ b/src/tools/ak/liteparse/values_parse.go
@@ -0,0 +1,226 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// 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 liteparse
+
+import (
+ "context"
+ "encoding/xml"
+ "fmt"
+
+ rdpb "src/tools/ak/res/proto/res_data_go_proto"
+ rmpb "src/tools/ak/res/proto/res_meta_go_proto"
+ "src/tools/ak/res/res"
+ "src/tools/ak/res/respipe/respipe"
+ "src/tools/ak/res/resxml/resxml"
+)
+
+// valuesParse handles all tags beneath <resources> and extracts the associated
+// ResourceType/names. Any encountered resources or errors are passed back on the returned channels.
+func valuesParse(ctx context.Context, xmlC <-chan resxml.XMLEvent) (<-chan *rdpb.Resource, <-chan error) {
+ resC := make(chan *rdpb.Resource)
+ errC := make(chan error)
+ go func() {
+ defer close(resC)
+ defer close(errC)
+ for {
+ xe, ok := resxml.ConsumeUntil(res.ResourcesTagName, xmlC)
+ if !ok {
+ return
+ }
+ resChildrenC := resxml.ForwardChildren(ctx, xe, xmlC)
+ for xe := range resChildrenC {
+ se, ok := xe.Token.(xml.StartElement)
+ if !ok {
+ // we ignore all non-start elements during a mini-parse.
+ continue
+ }
+
+ tagChildrenC := resxml.ForwardChildren(ctx, xe, resChildrenC)
+ ctx := respipe.PrefixErr(ctx, fmt.Sprintf("tag-name: %s at: %d: ", se.Name, xe.Offset))
+ if t, ok := res.ResourcesTagToType[se.Name.Local]; ok {
+ if !minResChildParse(ctx, xe, t, tagChildrenC, resC, errC) {
+ return
+ }
+ } else if resxml.SloppyMatches(se.Name, res.ItemTagName) {
+ if !itemParse(ctx, xe, tagChildrenC, resC, errC) {
+ return
+ }
+ }
+ for range tagChildrenC {
+ // exhaust any children beneath this tag, we did not need them in the mini-parse.
+ }
+ }
+ }
+ }()
+ return resC, errC
+}
+
+// itemParse handles <item name="xxxx" type="yyy"></item> tags that are children of <resources/>
+func itemParse(ctx context.Context, xe resxml.XMLEvent, childC <-chan resxml.XMLEvent, resC chan<- *rdpb.Resource, errC chan<- error) bool {
+ name, err := extractName(xe)
+ if err != nil {
+ return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%v: expected to encounter name attribute: %v", xe, err))
+ }
+ var tv string
+ for _, a := range resxml.Attrs(xe) {
+ if resxml.SloppyMatches(res.TypeAttrName, a.Name) {
+ tv = a.Value
+ }
+ }
+ if tv == "" {
+ return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%v: needs type atttribute", xe))
+ }
+ t, err := res.ParseType(tv)
+ if err != nil {
+ return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%q: cannot convert to type: %v", tv, err))
+ }
+ fqn, err := res.ParseName(name, t)
+ if err != nil {
+ return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%q / type: %s: convert to fqn: %v", name, t, err))
+ }
+ r := new(rdpb.Resource)
+ if err := fqn.SetResource(r); err != nil {
+ return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%v: name->proto failed: %v", fqn, err))
+ }
+ return respipe.SendRes(ctx, resC, r)
+}
+
+// Returns the value of the name attribute or an error.
+func extractName(xe resxml.XMLEvent) (string, error) {
+ for _, a := range resxml.Attrs(xe) {
+ if resxml.SloppyMatches(res.NameAttrName, a.Name) {
+ return a.Value, nil
+ }
+ }
+ return "", fmt.Errorf("Expected to encounter name attribute within: %v", resxml.Attrs(xe))
+}
+
+// minResChildParse handles a single top-level tag beneath <resources> and extracts all ResourceTypes/Names beneath it. It returns false if it detects that the context is done.
+func minResChildParse(ctx context.Context, xe resxml.XMLEvent, t res.Type, childC <-chan resxml.XMLEvent, resC chan<- *rdpb.Resource, errC chan<- error) bool {
+ name, err := extractName(xe)
+ if err != nil {
+ return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%#v: needs name attribute: %v", xe, err))
+ }
+
+ fqn, err := res.ParseName(name, t)
+ if err != nil {
+ return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%s: invalid name: %v", name, err))
+ }
+
+ r := new(rdpb.Resource)
+ if err := fqn.SetResource(r); err != nil {
+ return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%v: name->proto failed: %v", fqn, err))
+ }
+ if fqn.Type == res.Styleable {
+ md, ok := parseStyleableChildren(ctx, childC, resC, errC)
+ if !ok {
+ return false
+ }
+ if err := fqn.SetMetaData(md); err != nil {
+ return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%v: could not set stylablemeta: %v", fqn, err))
+ }
+ r.StyleableValue = md
+ }
+ if fqn.Type == res.Attr && !parseAttrChildren(ctx, childC, resC, errC) {
+ return false
+ }
+
+ return respipe.SendRes(ctx, resC, r)
+}
+
+// parseAttrChildren looks at the children of an <attr> tag and determines if any of them creates resources.
+// If it realizes that the provided ctx is canceled, it returns true, otherwise false.
+func parseAttrChildren(ctx context.Context, xmlC <-chan resxml.XMLEvent, resC chan<- *rdpb.Resource, errC chan<- error) bool {
+ for c := range xmlC {
+ ce, ok := c.Token.(xml.StartElement)
+ if !ok {
+ // do not care about non-start element events.
+ continue
+ }
+ if !resxml.SloppyMatches(res.EnumTagName, ce.Name) && !resxml.SloppyMatches(res.FlagTagName, ce.Name) {
+ // only want <enum> or <flag> elements
+ continue
+ }
+
+ enumFlagName, err := extractName(c)
+ if err != nil {
+ return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%v: flag / enum should have had a name attribute: %v", ce, err))
+ }
+ cFqn, err := res.ParseName(enumFlagName, res.ID)
+ if err != nil {
+ return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%v: could not parse child of <attr>: %v", ce, err))
+ }
+ cr := new(rdpb.Resource)
+ if err := cFqn.SetResource(cr); err != nil {
+ return respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%v: name->proto failed: %v", ce, err))
+ }
+ if !respipe.SendRes(ctx, resC, cr) {
+ return false
+ }
+ }
+ return true
+}
+
+// parseStyleableChildren looks at the children of a <declare-stylable> tag and determines what resources they create.
+func parseStyleableChildren(ctx context.Context, xmlC <-chan resxml.XMLEvent, resC chan<- *rdpb.Resource, errC chan<- error) (*rmpb.StyleableMetaData, bool) {
+ var attrNames []string
+ for c := range xmlC {
+ if _, ok := c.Token.(xml.StartElement); !ok {
+ // skip events besides start element.
+ continue
+ }
+ name, err := extractName(c)
+ if err != nil {
+ // being liberal with what we can encounter under a <declare-styleable> tag.
+ continue
+ }
+ attrFqn, err := res.ParseName(name, res.Attr)
+ if err != nil {
+ return nil, respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%q: could not parse name to fqn: %v", name, err))
+ }
+ if attrFqn.Type != res.Attr {
+ return nil, respipe.SendErr(
+ ctx, errC, respipe.Errorf(ctx, "%v: name->nameid proto failed: %v", attrFqn, res.ErrWrongType))
+ }
+
+ attrNames = append(attrNames, attrFqn.String())
+ if attrFqn.Package == "android" {
+ // since we're not generating android attributes (they already exist already)
+ // omit the resource proto for these attrs.
+ continue
+ }
+
+ if attrFqn.Type == res.Attr {
+ ctx := respipe.PrefixErr(ctx, fmt.Sprintf("%q: <attr> child: ", name))
+ childC := resxml.ForwardChildren(ctx, c, xmlC)
+ if !parseAttrChildren(ctx, childC, resC, errC) {
+ return nil, false
+ }
+ }
+
+ attrR := new(rdpb.Resource)
+ if err := attrFqn.SetResource(attrR); err != nil {
+ return nil, respipe.SendErr(ctx, errC, respipe.Errorf(ctx, "%v: name->proto failed: %v", attrFqn, err))
+ }
+
+ if !respipe.SendRes(ctx, resC, attrR) {
+ return nil, false
+ }
+
+ }
+ return &rmpb.StyleableMetaData{
+ FqnAttributes: attrNames,
+ }, true
+}