encode XML source correctly

* add 's' suffix on type directories
* write uncompressed files at decoding
This commit is contained in:
REAndroid 2023-01-12 13:46:58 -05:00
parent 7651efae55
commit bb46abb499
20 changed files with 444 additions and 154 deletions

View File

@ -53,6 +53,7 @@ import java.util.*;
throws IOException, XMLException { throws IOException, XMLException {
this.decodedEntries.clear(); this.decodedEntries.clear();
logMessage("Decoding ..."); logMessage("Decoding ...");
decodeUncompressedFiles(outDir);
TableEntryStore entryStore=new TableEntryStore(); TableEntryStore entryStore=new TableEntryStore();
entryStore.add(Frameworks.getAndroid()); entryStore.add(Frameworks.getAndroid());
TableBlock tableBlock=apkModule.getTableBlock(); TableBlock tableBlock=apkModule.getTableBlock();
@ -74,6 +75,12 @@ import java.util.*;
extractRootFiles(outDir); extractRootFiles(outDir);
} }
private void decodeUncompressedFiles(File outDir)
throws IOException {
File file=new File(outDir, UncompressedFiles.JSON_FILE);
UncompressedFiles uncompressedFiles = apkModule.getUncompressedFiles();
uncompressedFiles.toJson().write(file);
}
private void decodeResFile(EntryStore entryStore, File outDir, ResFile resFile) private void decodeResFile(EntryStore entryStore, File outDir, ResFile resFile)
throws IOException, XMLException { throws IOException, XMLException {
if(resFile.isBinaryXml()){ if(resFile.isBinaryXml()){

View File

@ -19,6 +19,7 @@ package com.reandroid.lib.apk;
import com.reandroid.archive.FileInputSource; import com.reandroid.archive.FileInputSource;
import com.reandroid.lib.apk.xmlencoder.RESEncoder; import com.reandroid.lib.apk.xmlencoder.RESEncoder;
import com.reandroid.lib.arsc.chunk.TableBlock; import com.reandroid.lib.arsc.chunk.TableBlock;
import com.reandroid.lib.json.JSONObject;
import com.reandroid.xml.XMLException; import com.reandroid.xml.XMLException;
import java.io.File; import java.io.File;
@ -34,6 +35,7 @@ package com.reandroid.lib.apk;
this.resEncoder = new RESEncoder(module, tableBlock); this.resEncoder = new RESEncoder(module, tableBlock);
} }
public void scanDirectory(File mainDirectory) throws IOException, XMLException { public void scanDirectory(File mainDirectory) throws IOException, XMLException {
loadUncompressedFiles(mainDirectory);
resEncoder.scanDirectory(mainDirectory); resEncoder.scanDirectory(mainDirectory);
File rootDir=new File(mainDirectory, "root"); File rootDir=new File(mainDirectory, "root");
scanRootDir(rootDir); scanRootDir(rootDir);
@ -51,6 +53,11 @@ package com.reandroid.lib.apk;
archive.add(inputSource); archive.add(inputSource);
} }
} }
private void loadUncompressedFiles(File mainDirectory) throws IOException, XMLException {
File file=new File(mainDirectory, UncompressedFiles.JSON_FILE);
UncompressedFiles uncompressedFiles = getApkModule().getUncompressedFiles();
uncompressedFiles.fromJson(file);
}
public void setApkLogger(APKLogger apkLogger) { public void setApkLogger(APKLogger apkLogger) {
this.resEncoder.setAPKLogger(apkLogger); this.resEncoder.setAPKLogger(apkLogger);
} }

View File

@ -139,7 +139,11 @@ public class ResFile {
EntryBlock entryBlock=pickOne(); EntryBlock entryBlock=pickOne();
TypeBlock typeBlock=entryBlock.getTypeBlock(); TypeBlock typeBlock=entryBlock.getTypeBlock();
StringBuilder builder=new StringBuilder(); StringBuilder builder=new StringBuilder();
builder.append(typeBlock.getTypeName()); String type=typeBlock.getTypeName();
builder.append(type);
if(!type.equals("plurals") && !type.endsWith("s")){
builder.append('s');
}
builder.append(typeBlock.getQualifiers()); builder.append(typeBlock.getQualifiers());
builder.append('/'); builder.append('/');
builder.append(entryBlock.getName()); builder.append(entryBlock.getName());

View File

@ -114,6 +114,9 @@ package com.reandroid.lib.apk.xmlencoder;
if(i>0){ if(i>0){
name=name.substring(0, i); name=name.substring(0, i);
} }
if(!name.equals("plurals") && name.endsWith("s")){
name=name.substring(0, name.length()-1);
}
return name; return name;
} }
public static String getTypeNameFromValuesXml(File valuesXml){ public static String getTypeNameFromValuesXml(File valuesXml){

View File

@ -83,13 +83,18 @@ public class XMLFileEncoder {
ensureNamespaces(element, resXmlElement); ensureNamespaces(element, resXmlElement);
resXmlElement.setTag(element.getTagName()); resXmlElement.setTag(element.getTagName());
buildAttributes(element, resXmlElement); buildAttributes(element, resXmlElement);
int count=element.getChildesCount(); for(XMLNode node:element.getChildNodes()){
for(int i=0;i<count;i++){ if(node instanceof XMLText){
XMLElement child=element.getChildAt(i); resXmlElement.addResXmlText(((XMLText)node).getText(true));
}else if(node instanceof XMLComment){
resXmlElement.setComment(((XMLComment)node).getCommentText());
}else if(node instanceof XMLElement){
XMLElement child=(XMLElement) node;
ResXmlElement childXml=resXmlElement.createChildElement(); ResXmlElement childXml=resXmlElement.createChildElement();
buildElement(child, childXml); buildElement(child, childXml);
} }
} }
}
private void buildAttributes(XMLElement element, ResXmlElement resXmlElement){ private void buildAttributes(XMLElement element, ResXmlElement resXmlElement){
int count=element.getAttributeCount(); int count=element.getAttributeCount();
for(int i=0;i<count;i++){ for(int i=0;i<count;i++){

View File

@ -43,7 +43,7 @@ class XMLValuesEncoderAttr extends XMLValuesEncoderBag{
encodeAttributes(parentElement, resValueBag); encodeAttributes(parentElement, resValueBag);
encodeEnumOrFlag(parentElement, resValueBag); encodeEnumOrFlag(parentElement, resValueBag);
// TODO: re-check if this is necessary // TODO: re-check if this is necessary
resValueBag.getEntryBlock().setEntryTypeShared(true); resValueBag.getEntryBlock().setPublic(true);
} }
private void encodeAttributes(XMLElement parentElement, ResValueBag resValueBag){ private void encodeAttributes(XMLElement parentElement, ResValueBag resValueBag){
int count=parentElement.getAttributeCount(); int count=parentElement.getAttributeCount();

View File

@ -37,7 +37,7 @@ class XMLValuesEncoderId extends XMLValuesEncoder{
setVisibility(entryBlock); setVisibility(entryBlock);
} }
private void setVisibility(EntryBlock entryBlock){ private void setVisibility(EntryBlock entryBlock){
entryBlock.setEntryTypePublic(true); entryBlock.setWeak(true);
entryBlock.setEntryTypeShared(true); entryBlock.setPublic(true);
} }
} }

View File

@ -18,7 +18,6 @@ package com.reandroid.lib.arsc.chunk.xml;
import com.reandroid.lib.arsc.chunk.ChunkType; import com.reandroid.lib.arsc.chunk.ChunkType;
import com.reandroid.lib.arsc.base.Block; import com.reandroid.lib.arsc.base.Block;
import com.reandroid.lib.arsc.container.BlockList; import com.reandroid.lib.arsc.container.BlockList;
import com.reandroid.lib.arsc.container.FixedBlockContainer;
import com.reandroid.lib.arsc.container.SingleBlockContainer; import com.reandroid.lib.arsc.container.SingleBlockContainer;
import com.reandroid.lib.arsc.decoder.ValueDecoder; import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.header.HeaderBlock; import com.reandroid.lib.arsc.header.HeaderBlock;
@ -29,37 +28,37 @@ import com.reandroid.lib.common.EntryStore;
import com.reandroid.lib.json.JSONConvert; import com.reandroid.lib.json.JSONConvert;
import com.reandroid.lib.json.JSONArray; import com.reandroid.lib.json.JSONArray;
import com.reandroid.lib.json.JSONObject; import com.reandroid.lib.json.JSONObject;
import com.reandroid.xml.NameSpaceItem; import com.reandroid.xml.*;
import com.reandroid.xml.XMLAttribute;
import com.reandroid.xml.XMLElement;
import com.reandroid.xml.XMLException;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
public class ResXmlElement extends FixedBlockContainer implements JSONConvert<JSONObject> { public class ResXmlElement extends ResXmlNode implements JSONConvert<JSONObject> {
private final BlockList<ResXmlStartNamespace> mStartNamespaceList; private final BlockList<ResXmlStartNamespace> mStartNamespaceList;
private final SingleBlockContainer<ResXmlStartElement> mStartElementContainer; private final SingleBlockContainer<ResXmlStartElement> mStartElementContainer;
private final BlockList<ResXmlElement> mBody; private final BlockList<ResXmlNode> mBody;
private final SingleBlockContainer<ResXmlText> mResXmlTextContainer;
private final SingleBlockContainer<ResXmlEndElement> mEndElementContainer; private final SingleBlockContainer<ResXmlEndElement> mEndElementContainer;
private final BlockList<ResXmlEndNamespace> mEndNamespaceList; private final BlockList<ResXmlEndNamespace> mEndNamespaceList;
private int mDepth; private int mDepth;
public ResXmlElement() { public ResXmlElement() {
super(6); super(5);
this.mStartNamespaceList = new BlockList<>(); this.mStartNamespaceList = new BlockList<>();
this.mStartElementContainer= new SingleBlockContainer<>(); this.mStartElementContainer= new SingleBlockContainer<>();
this.mBody = new BlockList<>(); this.mBody = new BlockList<>();
this.mResXmlTextContainer = new SingleBlockContainer<>();
this.mEndElementContainer = new SingleBlockContainer<>(); this.mEndElementContainer = new SingleBlockContainer<>();
this.mEndNamespaceList = new BlockList<>(); this.mEndNamespaceList = new BlockList<>();
addChild(0, mStartNamespaceList); addChild(0, mStartNamespaceList);
addChild(1, mStartElementContainer); addChild(1, mStartElementContainer);
addChild(2, mBody); addChild(2, mBody);
addChild(3, mResXmlTextContainer); addChild(3, mEndElementContainer);
addChild(4, mEndElementContainer); addChild(4, mEndNamespaceList);
addChild(5, mEndNamespaceList); }
public String getComment(){
return getStartElement().getComment();
}
public void setComment(String comment){
getStartElement().setComment(comment);
} }
public void calculatePositions(){ public void calculatePositions(){
ResXmlStartElement start = getStartElement(); ResXmlStartElement start = getStartElement();
@ -293,10 +292,53 @@ import java.util.*;
return mBody.remove(element); return mBody.remove(element);
} }
public int countElements(){ public int countElements(){
return mBody.size(); int result = 0;
for(ResXmlNode xmlNode:listXmlNodes()){
if(xmlNode instanceof ResXmlElement){
result++;
}
}
return result;
}
public void clearChildes(){
List<ResXmlNode> copyOfNodeList=new ArrayList<>(mBody.getChildes());
for(ResXmlNode xmlNode:copyOfNodeList){
if(xmlNode==null){
continue;
}
xmlNode.onRemove();
mBody.remove(xmlNode);
}
}
public List<ResXmlNode> listXmlNodes(){
return mBody.getChildes();
}
public List<ResXmlText> listXmlText(){
List<ResXmlText> results=new ArrayList<>();
for(ResXmlNode xmlNode:listXmlNodes()){
if(xmlNode instanceof ResXmlTextNode){
results.add(((ResXmlTextNode) xmlNode).getResXmlText());
}
}
return results;
}
public List<ResXmlTextNode> listXmlTextNodes(){
List<ResXmlTextNode> results=new ArrayList<>();
for(ResXmlNode xmlNode:listXmlNodes()){
if(xmlNode instanceof ResXmlTextNode){
results.add((ResXmlTextNode) xmlNode);
}
}
return results;
} }
public List<ResXmlElement> listElements(){ public List<ResXmlElement> listElements(){
return mBody.getChildes(); List<ResXmlElement> results=new ArrayList<>();
for(ResXmlNode xmlNode:listXmlNodes()){
if(xmlNode instanceof ResXmlElement){
results.add((ResXmlElement) xmlNode);
}
}
return results;
} }
public List<ResXmlElement> listElements(String name){ public List<ResXmlElement> listElements(String name){
List<ResXmlElement> results=new ArrayList<>(); List<ResXmlElement> results=new ArrayList<>();
@ -432,25 +474,40 @@ import java.util.*;
mEndElementContainer.setItem(item); mEndElementContainer.setItem(item);
} }
// Use listXmlText() instead to be removed on next version
@Deprecated
public ResXmlText getResXmlText(){ public ResXmlText getResXmlText(){
return mResXmlTextContainer.getItem(); List<ResXmlText> xmlTextList=listXmlText();
if(xmlTextList.size()==0){
return null;
} }
return xmlTextList.get(0);
}
public void addResXmlTextNode(ResXmlTextNode xmlTextNode){
mBody.add(xmlTextNode);
}
public void addResXmlText(ResXmlText xmlText){
if(xmlText!=null){
addResXmlTextNode(new ResXmlTextNode(xmlText));
}
}
// Use addResXmlText()
@Deprecated
public void setResXmlText(ResXmlText xmlText){ public void setResXmlText(ResXmlText xmlText){
mResXmlTextContainer.setItem(xmlText); addResXmlText(xmlText);
} }
@Deprecated
public void setResXmlText(String text){ public void setResXmlText(String text){
clearChildes();
addResXmlText(text);
}
public void addResXmlText(String text){
if(text==null){ if(text==null){
mResXmlTextContainer.setItem(null); return;
}else {
ResXmlText xmlText=mResXmlTextContainer.getItem();
if(xmlText==null){
xmlText=new ResXmlText();
mResXmlTextContainer.setItem(xmlText);
ResXmlStartElement start = getStartElement();
xmlText.setLineNumber(start.getLineNumber());
}
xmlText.setText(text);
} }
ResXmlTextNode xmlTextNode=new ResXmlTextNode();
addResXmlTextNode(xmlTextNode);
xmlTextNode.setText(text);
} }
private boolean isBalanced(){ private boolean isBalanced(){
@ -602,7 +659,7 @@ import java.util.*;
} }
private void onXmlText(BlockReader reader) throws IOException{ private void onXmlText(BlockReader reader) throws IOException{
ResXmlText xmlText=new ResXmlText(); ResXmlText xmlText=new ResXmlText();
setResXmlText(xmlText); addResXmlText(xmlText);
xmlText.readBytes(reader); xmlText.readBytes(reader);
} }
@ -644,6 +701,7 @@ import java.util.*;
@Override @Override
public JSONObject toJson() { public JSONObject toJson() {
JSONObject jsonObject=new JSONObject(); JSONObject jsonObject=new JSONObject();
jsonObject.put(NAME_node_type, NAME_element);
ResXmlStartElement start = getStartElement(); ResXmlStartElement start = getStartElement();
jsonObject.put(NAME_line, start.getLineNumber()); jsonObject.put(NAME_line, start.getLineNumber());
int i=0; int i=0;
@ -663,13 +721,6 @@ import java.util.*;
if(comment!=null){ if(comment!=null){
jsonObject.put(NAME_comment, comment); jsonObject.put(NAME_comment, comment);
} }
ResXmlText xmlText=getResXmlText();
if(xmlText!=null){
String text=xmlText.getText();
if(text!=null){
jsonObject.put(NAME_text, text);
}
}
String uri=start.getUri(); String uri=start.getUri();
if(uri!=null){ if(uri!=null){
jsonObject.put(NAME_namespace_uri, uri); jsonObject.put(NAME_namespace_uri, uri);
@ -678,8 +729,8 @@ import java.util.*;
jsonObject.put(NAME_attributes, attrArray); jsonObject.put(NAME_attributes, attrArray);
i=0; i=0;
JSONArray childes=new JSONArray(); JSONArray childes=new JSONArray();
for(ResXmlElement element:listElements()){ for(ResXmlNode xmlNode:listXmlNodes()){
childes.put(i, element.toJson()); childes.put(i, xmlNode.toJson());
i++; i++;
} }
if(i>0){ if(i>0){
@ -730,12 +781,29 @@ import java.util.*;
int length=childArray.length(); int length=childArray.length();
for(int i=0;i<length;i++){ for(int i=0;i<length;i++){
JSONObject childObject=childArray.getJSONObject(i); JSONObject childObject=childArray.getJSONObject(i);
ResXmlElement child = createChildElement(); if(isTextNode(childObject)){
child.fromJson(childObject); ResXmlTextNode xmlTextNode=new ResXmlTextNode();
addResXmlTextNode(xmlTextNode);
xmlTextNode.fromJson(childObject);
}else {
ResXmlElement childElement = createChildElement();
childElement.fromJson(childObject);
}
} }
} }
start.calculatePositions(); start.calculatePositions();
} }
private boolean isTextNode(JSONObject childObject){
String type=childObject.optString(NAME_node_type, null);
if(ResXmlTextNode.NAME_text.equals(type)){
return true;
}
if(NAME_element.equals(type)){
return false;
}
// support older ARSCLib versions
return childObject.has(NAME_text);
}
/** /**
* Decodes binary {@link ResXmlElement} to readable {@link XMLElement} * Decodes binary {@link ResXmlElement} to readable {@link XMLElement}
@ -745,6 +813,7 @@ import java.util.*;
* */ * */
public XMLElement decodeToXml(EntryStore entryStore, int currentPackageId) throws XMLException { public XMLElement decodeToXml(EntryStore entryStore, int currentPackageId) throws XMLException {
XMLElement xmlElement = new XMLElement(getTagName()); XMLElement xmlElement = new XMLElement(getTagName());
xmlElement.setLineNumber(getStartElement().getLineNumber());
for(ResXmlStartNamespace startNamespace:getStartNamespaceList()){ for(ResXmlStartNamespace startNamespace:getStartNamespaceList()){
xmlElement.addAttribute(startNamespace.decodeToXml()); xmlElement.addAttribute(startNamespace.decodeToXml());
} }
@ -753,15 +822,21 @@ import java.util.*;
resXmlAttribute.decodeToXml(entryStore, currentPackageId); resXmlAttribute.decodeToXml(entryStore, currentPackageId);
xmlElement.addAttribute(xmlAttribute); xmlElement.addAttribute(xmlAttribute);
} }
for(ResXmlElement childResXmlElement:listElements()){ String comment=getComment();
if(comment!=null){
xmlElement.addComment(new XMLComment(comment));
}
for(ResXmlNode xmlNode:listXmlNodes()){
if(xmlNode instanceof ResXmlElement){
ResXmlElement childResXmlElement=(ResXmlElement)xmlNode;
XMLElement childXMLElement = XMLElement childXMLElement =
childResXmlElement.decodeToXml(entryStore, currentPackageId); childResXmlElement.decodeToXml(entryStore, currentPackageId);
xmlElement.addChild(childXMLElement); xmlElement.addChild(childXMLElement);
}else if(xmlNode instanceof ResXmlTextNode){
ResXmlTextNode childResXmlTextNode=(ResXmlTextNode)xmlNode;
XMLText xmlText = childResXmlTextNode.decodeToXml();
xmlElement.addText(xmlText);
} }
ResXmlText resXmlText = getResXmlText();
if(resXmlText!=null){
xmlElement.setTextContent(
ValueDecoder.escapeSpecialCharacter(resXmlText.getText()));
} }
return xmlElement; return xmlElement;
} }
@ -799,6 +874,7 @@ import java.util.*;
public static final String NS_ANDROID_URI = "http://schemas.android.com/apk/res/android"; public static final String NS_ANDROID_URI = "http://schemas.android.com/apk/res/android";
public static final String NS_ANDROID_PREFIX = "android"; public static final String NS_ANDROID_PREFIX = "android";
static final String NAME_element = "element";
static final String NAME_name = "name"; static final String NAME_name = "name";
static final String NAME_comment = "comment"; static final String NAME_comment = "comment";
static final String NAME_text = "text"; static final String NAME_text = "text";

View File

@ -0,0 +1,30 @@
/*
* 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.lib.arsc.chunk.xml;
import com.reandroid.lib.arsc.container.FixedBlockContainer;
import com.reandroid.lib.json.JSONConvert;
import com.reandroid.lib.json.JSONObject;
public abstract class ResXmlNode extends FixedBlockContainer implements JSONConvert<JSONObject> {
ResXmlNode(int childesCount) {
super(childesCount);
}
void onRemove(){
}
public static final String NAME_node_type="node_type";
}

View File

@ -56,7 +56,9 @@ import java.util.Set;
if(isEmpty(uri) || isEmpty(prefix)){ if(isEmpty(uri) || isEmpty(prefix)){
return null; return null;
} }
return new SchemaAttr(prefix, uri); SchemaAttr schemaAttr=new SchemaAttr(prefix, uri);
schemaAttr.setLineNumber(getLineNumber());
return schemaAttr;
} }
private boolean isEmpty(String txt){ private boolean isEmpty(String txt){
if(txt==null){ if(txt==null){

View File

@ -0,0 +1,79 @@
/*
* 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.lib.arsc.chunk.xml;
import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.json.JSONObject;
import com.reandroid.xml.XMLText;
public class ResXmlTextNode extends ResXmlNode {
private final ResXmlText resXmlText;
public ResXmlTextNode(ResXmlText resXmlText) {
super(1);
this.resXmlText = resXmlText;
addChild(0, resXmlText);
}
public ResXmlTextNode() {
this(new ResXmlText());
}
public ResXmlText getResXmlText() {
return resXmlText;
}
public int getLineNumber(){
return getResXmlText().getLineNumber();
}
public void setLineNumber(int lineNumber){
getResXmlText().setLineNumber(lineNumber);
}
public String getText(){
return getResXmlText().getText();
}
public void setText(String text){
getResXmlText().setText(text);
}
public int getTextReference(){
return getResXmlText().getTextReference();
}
public void setTextReference(int ref){
getResXmlText().setTextReference(ref);
}
@Override
public String toString(){
String txt=getText();
if(txt!=null){
return txt;
}
return super.toString();
}
@Override
public JSONObject toJson() {
JSONObject jsonObject=new JSONObject();
jsonObject.put(NAME_node_type, NAME_text);
jsonObject.put(NAME_text, getText());
return jsonObject;
}
@Override
public void fromJson(JSONObject json) {
setText(json.optString(NAME_text, null));
}
public XMLText decodeToXml() {
XMLText xmlText=new XMLText(ValueDecoder.escapeSpecialCharacter(getText()));
xmlText.setLineNumber(getLineNumber());
return xmlText;
}
public static final String NAME_text="text";
}

View File

@ -77,6 +77,23 @@ public class ByteArray extends BlockItem {
byte[] bts = getBytesInternal(); byte[] bts = getBytesInternal();
bts[index]=value; bts[index]=value;
} }
public boolean getBit(int byteOffset, int bitIndex){
return ((get(byteOffset)>>bitIndex) & 0x1) == 1;
}
public void putBit(int byteOffset, int bitIndex, boolean bit){
int val=get(byteOffset);
int left=val>>bitIndex;
if(bit){
left=left|0x1;
}else {
left=left & 0xFE;
}
left=left<<bitIndex;
bitIndex=8-bitIndex;
int right=(0xFF>>bitIndex) & val;
val=left|right;
put(byteOffset, (byte) val);
}
public final void putShort(int offset, short val){ public final void putShort(int offset, short val){
byte[] bts = getBytesInternal(); byte[] bts = getBytesInternal();
bts[offset+1]= (byte) (val >>> 8 & 0xff); bts[offset+1]= (byte) (val >>> 8 & 0xff);

View File

@ -57,7 +57,7 @@ public abstract class BaseResValueItem extends BaseResValue implements ResValueI
return null; return null;
} }
public ReferenceItem getTableStringReference(){ public ReferenceItem getTableStringReference(){
if(getValueType()!=ValueType.STRING){ if(getValueType()!=ValueType.STRING || getEntryBlock()==null){
return null; return null;
} }
if(mReferenceItem==null){ if(mReferenceItem==null){

View File

@ -35,9 +35,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class EntryBlock extends Block implements JSONConvert<JSONObject> { public class EntryBlock extends Block implements JSONConvert<JSONObject> {
private ShortItem mHeaderSize; private ByteArray entryHeader;
private ByteItem mFlagEntryType;
private ByteItem mByteFlagsB;
private IntegerItem mSpecReference; private IntegerItem mSpecReference;
private BaseResValue mResValue; private BaseResValue mResValue;
private boolean mUnLocked; private boolean mUnLocked;
@ -127,20 +125,16 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
return tableString.getHtml(); return tableString.getHtml();
} }
public TableString getValueAsTableString(){ public TableString getValueAsTableString(){
TableStringPool stringPool=getTableStringPool(); TableStringPool stringPool = getTableStringPool();
if(stringPool==null){ if(stringPool==null){
return null; return null;
} }
BaseResValue res = getResValue(); BaseResValue baseResValue = getResValue();
if(!(res instanceof ResValueInt)){ if(!(baseResValue instanceof ResValueInt)){
return null; return null;
} }
ResValueInt resValueInt=(ResValueInt)res; ResValueInt resValueInt = (ResValueInt)baseResValue;
TableString tableString= stringPool.get(resValueInt.getData()); return stringPool.get(resValueInt.getData());
if(tableString==null){
return null;
}
return tableString;
} }
private TableStringPool getTableStringPool(){ private TableStringPool getTableStringPool(){
PackageBlock pkg=getPackageBlock(); PackageBlock pkg=getPackageBlock();
@ -256,27 +250,24 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
removeTableReferences(); removeTableReferences();
removeSpecReferences(); removeSpecReferences();
} }
public void setEntryTypeBag(boolean b){ private void setEntryTypeBag(boolean b){
mFlagEntryType.putBit(0, b); entryHeader.putBit(OFFSET_FLAGS, 0, b);
refreshHeaderSize(); refreshHeaderSize();
} }
public boolean isEntryTypeBag(){ public boolean isEntryTypeBag(){
return mFlagEntryType.getBit(0); return entryHeader.getBit(OFFSET_FLAGS,0);
} }
public void setEntryTypeShared(boolean b){ public void setPublic(boolean b){
mFlagEntryType.putBit(1, b); entryHeader.putBit(OFFSET_FLAGS,1, b);
} }
public boolean isEntryTypeShared(){ public boolean isPublic(){
return mFlagEntryType.getBit(1); return entryHeader.getBit(OFFSET_FLAGS,1);
} }
public void setEntryTypePublic(boolean b){ public void setWeak(boolean b){
mFlagEntryType.putBit(2, b); entryHeader.putBit(OFFSET_FLAGS, 2, b);
} }
public boolean isEntryTypePublic(){ public boolean isWeak(){
return mFlagEntryType.getBit(2); return entryHeader.getBit(OFFSET_FLAGS,2);
}
private void setByteFlagsB(byte b){
mByteFlagsB.set(b);
} }
private IntegerItem getSpecReferenceBlock(){ private IntegerItem getSpecReferenceBlock(){
return mSpecReference; return mSpecReference;
@ -341,7 +332,7 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
return; return;
} }
if(resValue!=null){ if(resValue!=null){
resValue.setIndex(4); resValue.setIndex(2);
resValue.setParent(this); resValue.setParent(this);
} }
if(mResValue!=null){ if(mResValue!=null){
@ -509,47 +500,32 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
if(mUnLocked){ if(mUnLocked){
return; return;
} }
mUnLocked =true; mUnLocked = true;
this.mHeaderSize =new ShortItem(); entryHeader = new ByteArray(4);
this.mFlagEntryType =new ByteItem();
this.mByteFlagsB=new ByteItem();
if(mSpecReference==null){ if(mSpecReference==null){
this.mSpecReference = new IntegerItem(); this.mSpecReference = new IntegerItem();
}else if(mSpecReference.isNull()){ }else if(mSpecReference.isNull()){
mSpecReference.setNull(false); mSpecReference.setNull(false);
} }
entryHeader.setIndex(0);
mHeaderSize.setIndex(0); mSpecReference.setIndex(1);
mFlagEntryType.setIndex(1); entryHeader.setParent(this);
mByteFlagsB.setIndex(2);
mSpecReference.setIndex(3);
mHeaderSize.setParent(this);
mFlagEntryType.setParent(this);
mByteFlagsB.setParent(this);
mSpecReference.setParent(this); mSpecReference.setParent(this);
} }
private void lockEntry(){ private void lockEntry(){
if(!mUnLocked){ if(!mUnLocked){
return; return;
} }
removeAllReferences(); removeAllReferences();
mUnLocked =false; mUnLocked = false;
mHeaderSize.setParent(null); entryHeader.setParent(null);
mFlagEntryType.setParent(null);
mByteFlagsB.setParent(null);
mSpecReference.setParent(null); mSpecReference.setParent(null);
mHeaderSize.setIndex(-1); entryHeader.setIndex(-1);
mFlagEntryType.setIndex(-1);
mByteFlagsB.setIndex(-1);
mSpecReference.setIndex(-1); mSpecReference.setIndex(-1);
removeResValue(); removeResValue();
this.mHeaderSize =null; this.entryHeader = null;
this.mFlagEntryType =null;
this.mByteFlagsB =null;
this.mSpecReference =null; this.mSpecReference =null;
} }
private void removeResValue(){ private void removeResValue(){
@ -560,11 +536,13 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
} }
} }
private void refreshHeaderSize(){ private void refreshHeaderSize(){
short size;
if(isEntryTypeBag()){ if(isEntryTypeBag()){
mHeaderSize.set(HEADER_SIZE_BAG); size=HEADER_SIZE_BAG;
}else { }else {
mHeaderSize.set(HEADER_SIZE_INT); size=HEADER_SIZE_INT;
} }
entryHeader.putShort(OFFSET_SIZE, size);
} }
private void createResValue(){ private void createResValue(){
if(getResValue()!=null){ if(getResValue()!=null){
@ -599,9 +577,7 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
if(isNull()){ if(isNull()){
return null; return null;
} }
byte[] results=mHeaderSize.getBytes(); byte[] results=entryHeader.getBytes();
results=addBytes(results, mFlagEntryType.getBytes());
results=addBytes(results, mByteFlagsB.getBytes());
results=addBytes(results, mSpecReference.getBytes()); results=addBytes(results, mSpecReference.getBytes());
results=addBytes(results, mResValue.getBytes()); results=addBytes(results, mResValue.getBytes());
return results; return results;
@ -612,8 +588,7 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
return 0; return 0;
} }
/* /*
mHeaderSize -> 2 bytes entryHeader -> 4 bytes
mFlags -> 2 bytes
mSpecReference -> 4 bytes mSpecReference -> 4 bytes
------- -------
Total = 8 bytes, thus this value is always fixed no need to re-count Total = 8 bytes, thus this value is always fixed no need to re-count
@ -633,9 +608,7 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
return; return;
} }
counter.addCount(countBytes()); counter.addCount(countBytes());
mHeaderSize.onCountUpTo(counter); entryHeader.onCountUpTo(counter);
mFlagEntryType.onCountUpTo(counter);
mByteFlagsB.onCountUpTo(counter);
mSpecReference.onCountUpTo(counter); mSpecReference.onCountUpTo(counter);
mResValue.onCountUpTo(counter); mResValue.onCountUpTo(counter);
} }
@ -644,9 +617,7 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
if(isNull()){ if(isNull()){
return 0; return 0;
} }
int result=mHeaderSize.writeBytes(stream); int result=entryHeader.writeBytes(stream);
result+= mFlagEntryType.writeBytes(stream);
result+=mByteFlagsB.writeBytes(stream);
result+= mSpecReference.writeBytes(stream); result+= mSpecReference.writeBytes(stream);
result+=mResValue.writeBytes(stream); result+=mResValue.writeBytes(stream);
return result; return result;
@ -695,15 +666,12 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
public void onReadBytes(BlockReader reader) throws IOException{ public void onReadBytes(BlockReader reader) throws IOException{
setNull(false); setNull(false);
removeResValue(); removeResValue();
mHeaderSize.readBytes(reader); entryHeader.readBytes(reader);
mFlagEntryType.readBytes(reader);
mByteFlagsB.readBytes(reader);
mSpecReference.readBytes(reader); mSpecReference.readBytes(reader);
createResValue(); createResValue();
mResValue.readBytes(reader); mResValue.readBytes(reader);
updatePackage(); updatePackage();
updateSpecRef(); updateSpecRef();
mResValue.onDataLoaded();
} }
@Override @Override
public JSONObject toJson() { public JSONObject toJson() {
@ -715,10 +683,10 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
if(isEntryTypeBag()){ if(isEntryTypeBag()){
jsonObject.put(NAME_is_bag, true); jsonObject.put(NAME_is_bag, true);
} }
if(isEntryTypePublic()){ if(isWeak()){
jsonObject.put(NAME_is_public, true); jsonObject.put(NAME_is_weak, true);
} }
if(isEntryTypeShared()){ if(isPublic()){
jsonObject.put(NAME_is_shared, true); jsonObject.put(NAME_is_shared, true);
} }
jsonObject.put(NAME_value, getResValue().toJson()); jsonObject.put(NAME_value, getResValue().toJson());
@ -737,8 +705,8 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
baseResValue=new ResValueInt(); baseResValue=new ResValueInt();
} }
setResValue(baseResValue); setResValue(baseResValue);
setEntryTypeShared(json.optBoolean(NAME_is_shared, false)); setPublic(json.optBoolean(NAME_is_shared, false));
setEntryTypePublic(json.optBoolean(NAME_is_public, false)); setWeak(json.optBoolean(NAME_is_weak, false));
setName(json.getString(NAME_entry_name)); setName(json.getString(NAME_entry_name));
baseResValue.fromJson(json.getJSONObject(NAME_value)); baseResValue.fromJson(json.getJSONObject(NAME_value));
mResValue.onDataLoaded(); mResValue.onDataLoaded();
@ -765,8 +733,8 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
SpecString spec = getPackageBlock() SpecString spec = getPackageBlock()
.getSpecStringPool().getOrCreate(name); .getSpecStringPool().getOrCreate(name);
setSpecReference(spec.getIndex()); setSpecReference(spec.getIndex());
setEntryTypeShared(entryBlock.isEntryTypeShared()); setPublic(entryBlock.isPublic());
setEntryTypePublic(entryBlock.isEntryTypePublic()); setWeak(entryBlock.isWeak());
} }
private ResValueBag getOrCreateResValueBag(){ private ResValueBag getOrCreateResValueBag(){
if(mResValue instanceof ResValueBag){ if(mResValue instanceof ResValueBag){
@ -833,13 +801,15 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
return builder.toString(); return builder.toString();
} }
private final static short HEADER_SIZE_BAG = 0x0010; private static final int OFFSET_SIZE = 0;
private final static short HEADER_SIZE_INT = 0x0008; private static final int OFFSET_FLAGS = 2;
private static final short HEADER_SIZE_BAG = 0x0010;
private static final short HEADER_SIZE_INT = 0x0008;
public static final String NAME_entry_name ="entry_name"; public static final String NAME_entry_name ="entry_name";
private static final String NAME_is_bag="is_bag"; private static final String NAME_is_bag="is_bag";
private static final String NAME_is_shared="is_shared"; private static final String NAME_is_shared="is_shared";
private static final String NAME_is_public="is_public"; private static final String NAME_is_weak = "is_weak";
private static final String NAME_value="value"; private static final String NAME_value="value";
} }

View File

@ -139,7 +139,9 @@ public class ResValueBagItem extends BaseResValueItem{
} }
void refreshTableReference(){ void refreshTableReference(){
if(getValueType()==ValueType.STRING){ if(getValueType()==ValueType.STRING){
if(!hasTableReference()){
addTableReference(getTableStringReference()); addTableReference(getTableStringReference());
}
}else { }else {
removeTableReference(); removeTableReference();
} }

View File

@ -48,11 +48,16 @@ public class XMLElement extends XMLNode{
} }
public void addText(XMLText text){ public void addText(XMLText text){
addTextInternal(text, true);
}
private void addTextInternal(XMLText text, boolean addSupper){
if(text==null){ if(text==null){
return; return;
} }
mTexts.add(text); mTexts.add(text);
super.addChildNode(text); if(addSupper){
super.addChildNodeInternal(text);
}
} }
private void appendText(String text){ private void appendText(String text){
if(text==null || text.length()==0){ if(text==null || text.length()==0){
@ -171,7 +176,7 @@ public class XMLElement extends XMLNode{
} }
public XMLElement createElement(String tag) { public XMLElement createElement(String tag) {
XMLElement baseElement=new XMLElement(tag); XMLElement baseElement=new XMLElement(tag);
addChildNoCheck(baseElement); addChildNoCheck(baseElement, true);
return baseElement; return baseElement;
} }
public void addChild(Collection<XMLElement> elements) { public void addChild(Collection<XMLElement> elements) {
@ -183,7 +188,7 @@ public class XMLElement extends XMLNode{
} }
} }
public void addChild(XMLElement child) { public void addChild(XMLElement child) {
addChildNoCheck(child); addChildNoCheck(child, true);
} }
private void clearChildElements(){ private void clearChildElements(){
mChildes.clear(); mChildes.clear();
@ -238,6 +243,9 @@ public class XMLElement extends XMLNode{
mComments=null; mComments=null;
} }
public void addComment(XMLComment commentElement) { public void addComment(XMLComment commentElement) {
addCommentInternal(commentElement, true);
}
void addCommentInternal(XMLComment commentElement, boolean addSuper) {
if(commentElement==null){ if(commentElement==null){
return; return;
} }
@ -247,7 +255,9 @@ public class XMLElement extends XMLNode{
mComments.add(commentElement); mComments.add(commentElement);
commentElement.setIndent(getIndent()); commentElement.setIndent(getIndent());
commentElement.setParent(this); commentElement.setParent(this);
super.addChildNode(commentElement); if(addSuper){
super.addChildNodeInternal(commentElement);
}
} }
public void removeChildElements(){ public void removeChildElements(){
mChildes.clear(); mChildes.clear();
@ -413,14 +423,26 @@ public class XMLElement extends XMLNode{
void setParent(XMLElement baseElement){ void setParent(XMLElement baseElement){
mParent=baseElement; mParent=baseElement;
} }
private void addChildNoCheck(XMLElement child){ @Override
void onChildAdded(XMLNode xmlNode){
if(xmlNode instanceof XMLComment){
addCommentInternal((XMLComment) xmlNode, false);
}else if(xmlNode instanceof XMLElement){
addChildNoCheck((XMLElement) xmlNode, false);
}else if(xmlNode instanceof XMLText){
addTextInternal((XMLText) xmlNode, false);
}
}
private void addChildNoCheck(XMLElement child, boolean addSupper){
if(child==null || child == this){ if(child==null || child == this){
return; return;
} }
child.setParent(this); child.setParent(this);
child.setIndent(getChildIndent()); child.setIndent(getChildIndent());
mChildes.add(child); mChildes.add(child);
super.addChildNode(child); if(addSupper){
super.addChildNodeInternal(child);
}
} }
public int getLevel(){ public int getLevel(){
int rs=0; int rs=0;
@ -579,12 +601,19 @@ public class XMLElement extends XMLNode{
public boolean hasTextContent() { public boolean hasTextContent() {
return mTexts.size()>0; return mTexts.size()>0;
} }
public String getText(){
if(mTexts.size()==0){
return null;
}
return mTexts.get(0).getText();
}
public void setTextContent(String text){ public void setTextContent(String text){
setTextContent(text, true); setTextContent(text, true);
} }
public void setTextContent(String text, boolean escape){ public void setTextContent(String text, boolean escape){
clearChildElements(); clearChildElements();
clearTexts(); clearTexts();
super.getChildNodes().clear();
if(escape){ if(escape){
text=XMLUtil.escapeXmlChars(text); text=XMLUtil.escapeXmlChars(text);
} }

View File

@ -21,12 +21,37 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public abstract class XMLNode { public abstract class XMLNode {
private int mLineNumber;
private int mColumnNumber;
private final List<XMLNode> mChildNodes = new ArrayList<>(); private final List<XMLNode> mChildNodes = new ArrayList<>();
void addChildNode(XMLNode xmlNode){ public int getColumnNumber() {
if(xmlNode!=null && canAdd(xmlNode)){ return mColumnNumber;
mChildNodes.add(xmlNode);
} }
public void setColumnNumber(int columnNumber) {
this.mColumnNumber = columnNumber;
}
public int getLineNumber() {
return mLineNumber;
}
public void setLineNumber(int lineNumber) {
this.mLineNumber = lineNumber;
}
public void addChildNode(XMLNode xmlNode){
boolean addOk=addChildNodeInternal(xmlNode);
if(addOk){
onChildAdded(xmlNode);
}
}
boolean addChildNodeInternal(XMLNode xmlNode){
if(xmlNode!=null && canAdd(xmlNode)){
return mChildNodes.add(xmlNode);
}
return false;
}
void onChildAdded(XMLNode xmlNode){
} }
boolean canAdd(XMLNode xmlNode){ boolean canAdd(XMLNode xmlNode){
return !mChildNodes.contains(xmlNode); return !mChildNodes.contains(xmlNode);

View File

@ -26,6 +26,11 @@ public class XMLText extends XMLNode{
public XMLText(){ public XMLText(){
this(null); this(null);
} }
@Override
public void addChildNode(XMLNode xmlNode){
throw new IllegalArgumentException("Can not add xml node on text: "+xmlNode);
}
public String getText(){ public String getText(){
return getText(true); return getText(true);
} }

View File

@ -425,6 +425,10 @@ public class MXParser implements XmlPullParser
public InputStream getInputStream(){ public InputStream getInputStream(){
return inputStream; return inputStream;
} }
public Reader getReader(){
reset();
return reader;
}
public String getInputEncoding() { public String getInputEncoding() {
return inputEncoding; return inputEncoding;

View File

@ -45,7 +45,7 @@ public class XMLDocumentParser {
public XMLDocument parse() throws XMLParseException { public XMLDocument parse() throws XMLParseException {
try { try {
XMLDocument document= parseDocument(); XMLDocument document= parseDocument();
closeFileInputStream(); close();
return document; return document;
} catch (XmlPullParserException | IOException e) { } catch (XmlPullParserException | IOException e) {
XMLParseException ex=new XMLParseException(e.getMessage()); XMLParseException ex=new XMLParseException(e.getMessage());
@ -53,12 +53,20 @@ public class XMLDocumentParser {
throw ex; throw ex;
} }
} }
private void close(){
closeReader();
closeFileInputStream();
mResDocument=null;
mCurrentElement=null;
mCurrentText=null;
mComments=null;
}
private void closeFileInputStream(){ private void closeFileInputStream(){
if(!(mParser instanceof MXParser)){ if(!(mParser instanceof MXParser)){
return; return;
} }
MXParser mxParser=(MXParser) mParser; MXParser parser=(MXParser) mParser;
InputStream inputStream = mxParser.getInputStream(); InputStream inputStream = parser.getInputStream();
if(!(inputStream instanceof FileInputStream)){ if(!(inputStream instanceof FileInputStream)){
return; return;
} }
@ -67,6 +75,19 @@ public class XMLDocumentParser {
} catch (IOException ignored) { } catch (IOException ignored) {
} }
} }
private void closeReader(){
if(!(mParser instanceof MXParser)){
return;
}
MXParser parser=(MXParser) mParser;
Reader reader = parser.getReader();
if(reader!=null){
try {
reader.close();
} catch (IOException ignored) {
}
}
}
private XMLDocument parseDocument() throws XmlPullParserException, IOException { private XMLDocument parseDocument() throws XmlPullParserException, IOException {
mResDocument=null; mResDocument=null;
@ -121,6 +142,8 @@ public class XMLDocumentParser {
}else { }else {
mCurrentElement=mCurrentElement.createElement(name); mCurrentElement=mCurrentElement.createElement(name);
} }
mCurrentElement.setColumnNumber(mParser.getColumnNumber());
mCurrentElement.setLineNumber(mParser.getLineNumber());
checkIndent(); checkIndent();
flushComments(mCurrentElement); flushComments(mCurrentElement);
String ns=mParser.getNamespace(); String ns=mParser.getNamespace();
@ -284,6 +307,8 @@ public class XMLDocumentParser {
} }
XMLComment commentElement=new XMLComment(); XMLComment commentElement=new XMLComment();
commentElement.setCommentText(commentText); commentElement.setCommentText(commentText);
commentElement.setColumnNumber(mParser.getColumnNumber());
commentElement.setLineNumber(mParser.getLineNumber());
addComment(commentElement); addComment(commentElement);
} }
private void addComment(XMLComment ce){ private void addComment(XMLComment ce){