mirror of
https://github.com/netbirdio/gomobile-tvos-fork.git
synced 2026-05-22 18:43:29 -07:00
internal/binres: sort element attributes
Change-Id: I3ff6b98aa1c729600f154272b8250f673ae2778f Reviewed-on: https://go-review.googlesource.com/19040 Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
committed by
David Crawshaw
parent
789dd7a15a
commit
002f07f8be
+140
-99
@@ -176,27 +176,12 @@ const (
|
||||
toolsSchema = "http://schemas.android.com/tools"
|
||||
)
|
||||
|
||||
type xattr struct {
|
||||
xml.Attr
|
||||
bool
|
||||
}
|
||||
|
||||
// TODO remove once table can be sorted correctly
|
||||
type xnode struct {
|
||||
line uint32
|
||||
name xml.Name
|
||||
attrs []xattr
|
||||
cdata []string
|
||||
}
|
||||
|
||||
func UnmarshalXML(r io.Reader) (*XML, error) {
|
||||
tbl, err := OpenTable()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var nodes []xnode
|
||||
|
||||
lr := &lineReader{r: r}
|
||||
dec := xml.NewDecoder(lr)
|
||||
bx := new(XML)
|
||||
@@ -217,8 +202,6 @@ func UnmarshalXML(r io.Reader) (*XML, error) {
|
||||
|
||||
switch tkn := tkn.(type) {
|
||||
case xml.StartElement:
|
||||
nodes = append(nodes, xnode{line: uint32(line), name: tkn.Name})
|
||||
|
||||
el := &Element{
|
||||
NodeHeader: NodeHeader{
|
||||
LineNumber: uint32(line),
|
||||
@@ -283,13 +266,9 @@ func UnmarshalXML(r io.Reader) (*XML, error) {
|
||||
}
|
||||
el.attrs = append(el.attrs, nattr)
|
||||
|
||||
att := xattr{attr, false}
|
||||
if attr.Name.Space == "" {
|
||||
// TODO it's unclear how to query these
|
||||
switch attr.Name.Local {
|
||||
case "package", "platformBuildVersionName":
|
||||
nattr.RawValue = pool.ref(attr.Value)
|
||||
nattr.TypedValue.Type = DataString
|
||||
case "platformBuildVersionCode":
|
||||
nattr.TypedValue.Type = DataIntDec
|
||||
i, err := strconv.Atoi(attr.Value)
|
||||
@@ -297,8 +276,9 @@ func UnmarshalXML(r io.Reader) (*XML, error) {
|
||||
return nil, err
|
||||
}
|
||||
nattr.TypedValue.Value = uint32(i)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown attribute lacking namespace %q", attr.Name.Local)
|
||||
default: // "package", "platformBuildVersionName", and any invalid
|
||||
nattr.RawValue = pool.ref(attr.Value)
|
||||
nattr.TypedValue.Type = DataString
|
||||
}
|
||||
} else {
|
||||
// get type spec and value data type
|
||||
@@ -324,7 +304,6 @@ func UnmarshalXML(r io.Reader) (*XML, error) {
|
||||
switch t {
|
||||
case DataString, DataAttribute, DataType(0x3e):
|
||||
// TODO identify 0x3e, in bootstrap.xml this is the native lib name
|
||||
att.bool = true
|
||||
nattr.RawValue = pool.ref(attr.Value)
|
||||
nattr.TypedValue.Type = DataString
|
||||
case DataIntBool, DataType(0x08):
|
||||
@@ -399,11 +378,24 @@ func UnmarshalXML(r io.Reader) (*XML, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
nodes[len(nodes)-1].attrs = append(nodes[len(nodes)-1].attrs, att)
|
||||
}
|
||||
case xml.CharData:
|
||||
if s := poolTrim(string(tkn)); s != "" {
|
||||
nodes[len(nodes)-1].cdata = append(nodes[len(nodes)-1].cdata, s)
|
||||
cdt := &CharData{
|
||||
NodeHeader: NodeHeader{
|
||||
LineNumber: uint32(line),
|
||||
Comment: NoEntry,
|
||||
},
|
||||
RawData: pool.ref(s),
|
||||
}
|
||||
el := bx.stack[len(bx.stack)-1]
|
||||
if el.head == nil {
|
||||
el.head = cdt
|
||||
} else if el.tail == nil {
|
||||
el.tail = cdt
|
||||
} else {
|
||||
return nil, fmt.Errorf("element head and tail already contain chardata")
|
||||
}
|
||||
}
|
||||
case xml.EndElement:
|
||||
n := len(bx.stack)
|
||||
@@ -420,22 +412,6 @@ func UnmarshalXML(r io.Reader) (*XML, error) {
|
||||
}
|
||||
}
|
||||
|
||||
bvc := xml.Attr{
|
||||
Name: xml.Name{
|
||||
Space: "",
|
||||
Local: "platformBuildVersionCode",
|
||||
},
|
||||
Value: "15",
|
||||
}
|
||||
bvn := xml.Attr{
|
||||
Name: xml.Name{
|
||||
Space: "",
|
||||
Local: "platformBuildVersionName",
|
||||
},
|
||||
Value: "4.0.3",
|
||||
}
|
||||
nodes[0].attrs = append(nodes[0].attrs, xattr{bvc, true}, xattr{bvn, true})
|
||||
|
||||
// pools appear to be sorted as follows:
|
||||
// * attribute names prefixed with android:
|
||||
// * "android", [schema-url], [empty-string]
|
||||
@@ -445,12 +421,19 @@ func UnmarshalXML(r io.Reader) (*XML, error) {
|
||||
// * attribute value if data type of name is DataString, DataAttribute, or 0x3e (an unknown)
|
||||
bx.Pool = new(Pool)
|
||||
|
||||
for _, node := range nodes {
|
||||
for _, attr := range node.attrs {
|
||||
if attr.Name.Space == androidSchema {
|
||||
bx.Pool.strings = append(bx.Pool.strings, attr.Name.Local)
|
||||
var arecurse func(*Element)
|
||||
arecurse = func(el *Element) {
|
||||
for _, attr := range el.attrs {
|
||||
if attr.NS.Resolve(pool) == androidSchema {
|
||||
bx.Pool.strings = append(bx.Pool.strings, attr.Name.Resolve(pool))
|
||||
}
|
||||
}
|
||||
for _, child := range el.Children {
|
||||
arecurse(child)
|
||||
}
|
||||
}
|
||||
for _, el := range bx.Children {
|
||||
arecurse(el)
|
||||
}
|
||||
|
||||
// TODO encoding/xml does not enforce namespace prefix and manifest encoding in aapt
|
||||
@@ -462,21 +445,37 @@ func UnmarshalXML(r io.Reader) (*XML, error) {
|
||||
// not present in manifest.
|
||||
bx.Pool.strings = append(bx.Pool.strings, "")
|
||||
|
||||
for _, node := range nodes {
|
||||
for _, attr := range node.attrs {
|
||||
if attr.Name.Space == "" {
|
||||
bx.Pool.strings = append(bx.Pool.strings, attr.Name.Local)
|
||||
var brecurse func(*Element)
|
||||
brecurse = func(el *Element) {
|
||||
for _, attr := range el.attrs {
|
||||
if attr.NS.Resolve(pool) == "" {
|
||||
bx.Pool.strings = append(bx.Pool.strings, attr.Name.Resolve(pool))
|
||||
}
|
||||
}
|
||||
bx.Pool.strings = append(bx.Pool.strings, node.name.Local)
|
||||
for _, attr := range node.attrs {
|
||||
if attr.bool {
|
||||
bx.Pool.strings = append(bx.Pool.strings, attr.Value)
|
||||
|
||||
bx.Pool.strings = append(bx.Pool.strings, el.Name.Resolve(pool))
|
||||
|
||||
for _, attr := range el.attrs {
|
||||
if attr.RawValue != NoEntry {
|
||||
bx.Pool.strings = append(bx.Pool.strings, attr.RawValue.Resolve(pool))
|
||||
} else if attr.NS.Resolve(pool) == "" {
|
||||
bx.Pool.strings = append(bx.Pool.strings, fmt.Sprintf("%+v", attr.TypedValue.Value))
|
||||
}
|
||||
}
|
||||
for _, x := range node.cdata {
|
||||
bx.Pool.strings = append(bx.Pool.strings, x)
|
||||
|
||||
if el.head != nil {
|
||||
bx.Pool.strings = append(bx.Pool.strings, el.head.RawData.Resolve(pool))
|
||||
}
|
||||
if el.tail != nil {
|
||||
bx.Pool.strings = append(bx.Pool.strings, el.tail.RawData.Resolve(pool))
|
||||
}
|
||||
|
||||
for _, child := range el.Children {
|
||||
brecurse(child)
|
||||
}
|
||||
}
|
||||
for _, el := range bx.Children {
|
||||
brecurse(el)
|
||||
}
|
||||
|
||||
// do not eliminate duplicates until the entire slice has been composed.
|
||||
@@ -522,52 +521,22 @@ func UnmarshalXML(r io.Reader) (*XML, error) {
|
||||
resolve(el)
|
||||
}
|
||||
|
||||
var asort func(*Element)
|
||||
asort = func(el *Element) {
|
||||
sort.Sort(byType(el.attrs))
|
||||
// sort.Sort(byName(el.attrs))
|
||||
sort.Sort(byNamespace(el.attrs))
|
||||
for _, child := range el.Children {
|
||||
asort(child)
|
||||
}
|
||||
}
|
||||
for _, el := range bx.Children {
|
||||
asort(el)
|
||||
}
|
||||
|
||||
return bx, nil
|
||||
}
|
||||
|
||||
// asSet returns a set from a slice of strings.
|
||||
func asSet(xs []string) []string {
|
||||
m := make(map[string]bool)
|
||||
fo := xs[:0]
|
||||
for _, x := range xs {
|
||||
if !m[x] {
|
||||
m[x] = true
|
||||
fo = append(fo, x)
|
||||
}
|
||||
}
|
||||
return fo
|
||||
}
|
||||
|
||||
// poolTrim trims all but immediately surrounding space.
|
||||
// \n\t\tfoobar\n\t\t becomes \tfoobar\n
|
||||
func poolTrim(s string) string {
|
||||
var start, end int
|
||||
for i, r := range s {
|
||||
if !unicode.IsSpace(r) {
|
||||
if i != 0 {
|
||||
start = i - 1 // preserve preceding space
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
r := rune(s[i])
|
||||
if !unicode.IsSpace(r) {
|
||||
if i != len(s)-1 {
|
||||
end = i + 2
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if start == 0 && end == 0 {
|
||||
return "" // every char was a space
|
||||
}
|
||||
|
||||
return s[start:end]
|
||||
}
|
||||
|
||||
func (bx *XML) UnmarshalBinary(bin []byte) error {
|
||||
buf := bin
|
||||
if err := (&bx.chunkHeader).UnmarshalBinary(bin); err != nil {
|
||||
@@ -751,6 +720,78 @@ func iterElementsRecurse(el *Element, ch chan *Element) {
|
||||
}
|
||||
}
|
||||
|
||||
// asSet returns a set from a slice of strings.
|
||||
func asSet(xs []string) []string {
|
||||
m := make(map[string]bool)
|
||||
fo := xs[:0]
|
||||
for _, x := range xs {
|
||||
if !m[x] {
|
||||
m[x] = true
|
||||
fo = append(fo, x)
|
||||
}
|
||||
}
|
||||
return fo
|
||||
}
|
||||
|
||||
// poolTrim trims all but immediately surrounding space.
|
||||
// \n\t\tfoobar\n\t\t becomes \tfoobar\n
|
||||
func poolTrim(s string) string {
|
||||
var start, end int
|
||||
for i, r := range s {
|
||||
if !unicode.IsSpace(r) {
|
||||
if i != 0 {
|
||||
start = i - 1 // preserve preceding space
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
r := rune(s[i])
|
||||
if !unicode.IsSpace(r) {
|
||||
if i != len(s)-1 {
|
||||
end = i + 2
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if start == 0 && end == 0 {
|
||||
return "" // every char was a space
|
||||
}
|
||||
|
||||
return s[start:end]
|
||||
}
|
||||
|
||||
// byNamespace sorts attributes based on string pool position of namespace.
|
||||
// Given that "android" always preceeds "" in the pool, this results in the
|
||||
// correct ordering of attributes.
|
||||
type byNamespace []*Attribute
|
||||
|
||||
func (a byNamespace) Len() int { return len(a) }
|
||||
func (a byNamespace) Less(i, j int) bool {
|
||||
return a[i].NS < a[j].NS
|
||||
}
|
||||
func (a byNamespace) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
// byType sorts attributes by the uint8 value of the type.
|
||||
type byType []*Attribute
|
||||
|
||||
func (a byType) Len() int { return len(a) }
|
||||
func (a byType) Less(i, j int) bool {
|
||||
return a[i].TypedValue.Type < a[j].TypedValue.Type
|
||||
}
|
||||
func (a byType) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
// byName sorts attributes that have matching types based on string pool position of name.
|
||||
type byName []*Attribute
|
||||
|
||||
func (a byName) Len() int { return len(a) }
|
||||
func (a byName) Less(i, j int) bool {
|
||||
return a[i].TypedValue.Type == a[j].TypedValue.Type && a[i].Name < a[j].Name
|
||||
}
|
||||
func (a byName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
type lineReader struct {
|
||||
off int64
|
||||
lines []int64
|
||||
|
||||
BIN
Binary file not shown.
+6
@@ -12,10 +12,16 @@ license that can be found in the LICENSE file.
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-sdk android:minSdkVersion="9" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:label="Balloon世界"
|
||||
android:allowBackup="true"
|
||||
android:hasCode="false"
|
||||
foo="bar"
|
||||
android:debuggable="true"
|
||||
baz="bar"
|
||||
tools:strict="label">
|
||||
<activity android:name="android.app.NativeActivity"
|
||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
|
||||
|
||||
Reference in New Issue
Block a user