ARSCLib/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlStartElement.java
2023-01-22 15:27:26 -05:00

293 lines
10 KiB
Java
Executable File

/*
* Copyright (C) 2022 github.com/REAndroid
*
* 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 com.reandroid.arsc.chunk.xml;
import com.reandroid.arsc.chunk.ChunkType;
import com.reandroid.arsc.array.ResXmlAttributeArray;
import com.reandroid.arsc.item.ResXmlString;
import com.reandroid.arsc.item.ShortItem;
import java.util.Collection;
import java.util.Set;
public class ResXmlStartElement extends BaseXmlChunk {
private final ShortItem mAttributeStart;
private final ShortItem mAttributeUnitSize;
private final ShortItem mAttributeCount;
private final ShortItem mIdAttributePosition;
private final ShortItem mClassAttributePosition;
private final ShortItem mStyleAttributePosition;
private final ResXmlAttributeArray mAttributeArray;
private ResXmlEndElement mResXmlEndElement;
public ResXmlStartElement() {
super(ChunkType.XML_START_ELEMENT, 7);
mAttributeStart = new ShortItem(ATTRIBUTES_DEFAULT_START);
mAttributeUnitSize = new ShortItem(ATTRIBUTES_UNIT_SIZE);
mAttributeCount = new ShortItem();
mIdAttributePosition = new ShortItem();
mClassAttributePosition = new ShortItem();
mStyleAttributePosition = new ShortItem();
mAttributeArray = new ResXmlAttributeArray(getHeaderBlock(),
mAttributeStart,
mAttributeCount,
mAttributeUnitSize);
addChild(mAttributeStart);
addChild(mAttributeUnitSize);
addChild(mAttributeCount);
addChild(mIdAttributePosition);
addChild(mClassAttributePosition);
addChild(mStyleAttributePosition);
addChild(mAttributeArray);
}
void setAttributesUnitSize(int size){
mAttributeArray.setAttributesUnitSize(size);
}
public ResXmlAttribute newAttribute(){
ResXmlAttributeArray attributeArray = getResXmlAttributeArray();
return attributeArray.createNext();
}
@Override
void linkStringReferences(){
super.linkStringReferences();
ResXmlEndElement end = getResXmlEndElement();
if(end!=null){
end.linkStringReferences();
}
for(ResXmlAttribute attr:listResXmlAttributes()){
attr.linkStringReferences();
}
}
@Override
Set<ResXmlString> clearStringReferences(){
Set<ResXmlString> results=super.clearStringReferences();
ResXmlEndElement end = getResXmlEndElement();
if(end!=null){
results.addAll(end.clearStringReferences());
}
for(ResXmlAttribute attr:listResXmlAttributes()){
results.addAll(attr.clearStringReferences());
}
return results;
}
@Override
protected void onPreRefreshRefresh(){
sortAttributes();
}
private void sortAttributes(){
ResXmlAttributeArray array = getResXmlAttributeArray();
ResXmlAttribute idAttribute=array.get(mIdAttributePosition.get()-1);
ResXmlAttribute classAttribute=array.get(mClassAttributePosition.get()-1);
ResXmlAttribute styleAttribute=array.get(mStyleAttributePosition.get()-1);
array.sortAttributes();
if(idAttribute!=null){
mIdAttributePosition.set((short) (idAttribute.getIndex()+1));
}
if(classAttribute!=null){
mClassAttributePosition.set((short) (classAttribute.getIndex()+1));
// In case obfuscation
if(!ATTRIBUTE_NAME_CLASS.equals(classAttribute.getName())){
classAttribute.setName(ATTRIBUTE_NAME_CLASS, 0);
}
}
if(styleAttribute!=null){
mStyleAttributePosition.set((short) (styleAttribute.getIndex()+1));
// In case obfuscation
if(!ATTRIBUTE_NAME_STYLE.equals(styleAttribute.getName())){
styleAttribute.setName(ATTRIBUTE_NAME_STYLE, 0);
}
}
}
void calculatePositions(){
ResXmlAttribute idAttribute=getAttribute(ATTRIBUTE_RESOURCE_ID_id);
ResXmlAttribute classAttribute=getNoIdAttribute(ATTRIBUTE_NAME_CLASS);
ResXmlAttribute styleAttribute=getNoIdAttribute(ATTRIBUTE_NAME_STYLE);
if(idAttribute!=null){
mIdAttributePosition.set((short) (idAttribute.getIndex()+1));
}
if(classAttribute!=null){
mClassAttributePosition.set((short) (classAttribute.getIndex()+1));
}
if(styleAttribute!=null){
mStyleAttributePosition.set((short) (styleAttribute.getIndex()+1));
}
}
public ResXmlAttribute getAttribute(int resourceId){
for(ResXmlAttribute attribute:listResXmlAttributes()){
if(resourceId==attribute.getNameResourceID()){
return attribute;
}
}
return null;
}
private ResXmlAttribute getNoIdAttribute(String name){
for(ResXmlAttribute attribute:listResXmlAttributes()){
if(attribute.getNameResourceID()!=0){
continue;
}
if(name.equals(attribute.getName())){
return attribute;
}
}
return null;
}
public ResXmlAttribute getAttribute(String uri, String name){
if(name==null){
return null;
}
for(ResXmlAttribute attribute:listResXmlAttributes()){
if(name.equals(attribute.getName())||name.equals(attribute.getFullName())){
if(uri!=null){
if(uri.equals(attribute.getUri())){
return attribute;
}
continue;
}
return attribute;
}
}
return null;
}
public ResXmlAttribute searchAttributeByName(String name){
if(name==null){
return null;
}
for(ResXmlAttribute attribute:listResXmlAttributes()){
if(name.equals(attribute.getFullName()) || name.equals(attribute.getName())){
return attribute;
}
}
return null;
}
public ResXmlAttribute searchAttributeByResourceId(int resourceId){
if(resourceId==0){
return null;
}
for(ResXmlAttribute attribute:listResXmlAttributes()){
if(resourceId==attribute.getNameResourceID()){
return attribute;
}
}
return null;
}
public String getTagName(){
String prefix=getPrefix();
String name=getName();
if(prefix==null){
return name;
}
return prefix+":"+name;
}
public void setName(String name){
setString(name);
ResXmlEndElement endElement = getResXmlEndElement();
if(endElement!=null){
endElement.setString(name);
}
}
public Collection<ResXmlAttribute> listResXmlAttributes(){
return getResXmlAttributeArray().listItems();
}
public ResXmlAttributeArray getResXmlAttributeArray(){
return mAttributeArray;
}
public String getUri(){
int uriRef=getNamespaceReference();
if(uriRef<0){
return null;
}
ResXmlElement parentElement=getParentResXmlElement();
ResXmlStartNamespace startNamespace=parentElement.getStartNamespaceByUriRef(uriRef);
if(startNamespace!=null){
return startNamespace.getUri();
}
return null;
}
public String getPrefix(){
int uriRef=getNamespaceReference();
if(uriRef<0){
return null;
}
ResXmlElement parentElement=getParentResXmlElement();
ResXmlStartNamespace startNamespace=parentElement.getStartNamespaceByUriRef(uriRef);
if(startNamespace!=null){
return startNamespace.getPrefix();
}
return null;
}
public void setResXmlEndElement(ResXmlEndElement element){
mResXmlEndElement=element;
}
public ResXmlEndElement getResXmlEndElement(){
return mResXmlEndElement;
}
@Override
protected void onChunkRefreshed() {
refreshAttributeStart();
refreshAttributeCount();
}
private void refreshAttributeStart(){
int start=countUpTo(mAttributeArray);
start=start-getHeaderBlock().getHeaderSize();
mAttributeStart.set((short)start);
}
private void refreshAttributeCount(){
int count=mAttributeArray.childesCount();
mAttributeCount.set((short)count);
}
@Override
public String toString(){
String txt=getTagName();
if(txt==null){
return super.toString();
}
StringBuilder builder=new StringBuilder();
builder.append(txt);
ResXmlAttribute[] allAttr=mAttributeArray.getChildes();
if(allAttr!=null){
for(int i=0;i<allAttr.length;i++){
if(i>10){
break;
}
builder.append(" ");
builder.append(allAttr[i].toString());
}
}
return builder.toString();
}
private static final short ATTRIBUTES_UNIT_SIZE=20;
private static final short ATTRIBUTES_DEFAULT_START=20;
/*
* Find another way to mark an attribute is class, device actually relies on
* value of mClassAttributePosition */
private static final String ATTRIBUTE_NAME_CLASS="class";
/*
* Find another way to mark an attribute is style, device actually relies on
* value of mStyleAttributePosition */
private static final String ATTRIBUTE_NAME_STYLE="style";
/*
* Resource id value of attribute 'android:id'
* instead of relying on hardcoded value, we should find another way to
* mark an attribute is 'id' */
private static final int ATTRIBUTE_RESOURCE_ID_id =0x010100d0;
}