mirror of
https://github.com/revanced/ARSCLib.git
synced 2025-04-30 14:24:25 +02:00
encode XML source correctly
* add 's' suffix on type directories * write uncompressed files at decoding
This commit is contained in:
parent
7651efae55
commit
bb46abb499
@ -53,6 +53,7 @@ import java.util.*;
|
||||
throws IOException, XMLException {
|
||||
this.decodedEntries.clear();
|
||||
logMessage("Decoding ...");
|
||||
decodeUncompressedFiles(outDir);
|
||||
TableEntryStore entryStore=new TableEntryStore();
|
||||
entryStore.add(Frameworks.getAndroid());
|
||||
TableBlock tableBlock=apkModule.getTableBlock();
|
||||
@ -74,6 +75,12 @@ import java.util.*;
|
||||
|
||||
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)
|
||||
throws IOException, XMLException {
|
||||
if(resFile.isBinaryXml()){
|
||||
|
@ -19,6 +19,7 @@ package com.reandroid.lib.apk;
|
||||
import com.reandroid.archive.FileInputSource;
|
||||
import com.reandroid.lib.apk.xmlencoder.RESEncoder;
|
||||
import com.reandroid.lib.arsc.chunk.TableBlock;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
import com.reandroid.xml.XMLException;
|
||||
|
||||
import java.io.File;
|
||||
@ -34,6 +35,7 @@ package com.reandroid.lib.apk;
|
||||
this.resEncoder = new RESEncoder(module, tableBlock);
|
||||
}
|
||||
public void scanDirectory(File mainDirectory) throws IOException, XMLException {
|
||||
loadUncompressedFiles(mainDirectory);
|
||||
resEncoder.scanDirectory(mainDirectory);
|
||||
File rootDir=new File(mainDirectory, "root");
|
||||
scanRootDir(rootDir);
|
||||
@ -51,6 +53,11 @@ package com.reandroid.lib.apk;
|
||||
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) {
|
||||
this.resEncoder.setAPKLogger(apkLogger);
|
||||
}
|
||||
|
@ -139,7 +139,11 @@ public class ResFile {
|
||||
EntryBlock entryBlock=pickOne();
|
||||
TypeBlock typeBlock=entryBlock.getTypeBlock();
|
||||
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('/');
|
||||
builder.append(entryBlock.getName());
|
||||
|
@ -114,6 +114,9 @@ package com.reandroid.lib.apk.xmlencoder;
|
||||
if(i>0){
|
||||
name=name.substring(0, i);
|
||||
}
|
||||
if(!name.equals("plurals") && name.endsWith("s")){
|
||||
name=name.substring(0, name.length()-1);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
public static String getTypeNameFromValuesXml(File valuesXml){
|
||||
|
@ -83,11 +83,16 @@ public class XMLFileEncoder {
|
||||
ensureNamespaces(element, resXmlElement);
|
||||
resXmlElement.setTag(element.getTagName());
|
||||
buildAttributes(element, resXmlElement);
|
||||
int count=element.getChildesCount();
|
||||
for(int i=0;i<count;i++){
|
||||
XMLElement child=element.getChildAt(i);
|
||||
ResXmlElement childXml=resXmlElement.createChildElement();
|
||||
buildElement(child, childXml);
|
||||
for(XMLNode node:element.getChildNodes()){
|
||||
if(node instanceof XMLText){
|
||||
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();
|
||||
buildElement(child, childXml);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void buildAttributes(XMLElement element, ResXmlElement resXmlElement){
|
||||
|
@ -43,7 +43,7 @@ class XMLValuesEncoderAttr extends XMLValuesEncoderBag{
|
||||
encodeAttributes(parentElement, resValueBag);
|
||||
encodeEnumOrFlag(parentElement, resValueBag);
|
||||
// TODO: re-check if this is necessary
|
||||
resValueBag.getEntryBlock().setEntryTypeShared(true);
|
||||
resValueBag.getEntryBlock().setPublic(true);
|
||||
}
|
||||
private void encodeAttributes(XMLElement parentElement, ResValueBag resValueBag){
|
||||
int count=parentElement.getAttributeCount();
|
||||
|
@ -37,7 +37,7 @@ class XMLValuesEncoderId extends XMLValuesEncoder{
|
||||
setVisibility(entryBlock);
|
||||
}
|
||||
private void setVisibility(EntryBlock entryBlock){
|
||||
entryBlock.setEntryTypePublic(true);
|
||||
entryBlock.setEntryTypeShared(true);
|
||||
entryBlock.setWeak(true);
|
||||
entryBlock.setPublic(true);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ package com.reandroid.lib.arsc.chunk.xml;
|
||||
import com.reandroid.lib.arsc.chunk.ChunkType;
|
||||
import com.reandroid.lib.arsc.base.Block;
|
||||
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.decoder.ValueDecoder;
|
||||
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.JSONArray;
|
||||
import com.reandroid.lib.json.JSONObject;
|
||||
import com.reandroid.xml.NameSpaceItem;
|
||||
import com.reandroid.xml.XMLAttribute;
|
||||
import com.reandroid.xml.XMLElement;
|
||||
import com.reandroid.xml.XMLException;
|
||||
import com.reandroid.xml.*;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
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 SingleBlockContainer<ResXmlStartElement> mStartElementContainer;
|
||||
private final BlockList<ResXmlElement> mBody;
|
||||
private final SingleBlockContainer<ResXmlText> mResXmlTextContainer;
|
||||
private final BlockList<ResXmlNode> mBody;
|
||||
private final SingleBlockContainer<ResXmlEndElement> mEndElementContainer;
|
||||
private final BlockList<ResXmlEndNamespace> mEndNamespaceList;
|
||||
private int mDepth;
|
||||
public ResXmlElement() {
|
||||
super(6);
|
||||
super(5);
|
||||
this.mStartNamespaceList = new BlockList<>();
|
||||
this.mStartElementContainer= new SingleBlockContainer<>();
|
||||
this.mBody = new BlockList<>();
|
||||
this.mResXmlTextContainer = new SingleBlockContainer<>();
|
||||
this.mEndElementContainer = new SingleBlockContainer<>();
|
||||
this.mEndNamespaceList = new BlockList<>();
|
||||
addChild(0, mStartNamespaceList);
|
||||
addChild(1, mStartElementContainer);
|
||||
addChild(2, mBody);
|
||||
addChild(3, mResXmlTextContainer);
|
||||
addChild(4, mEndElementContainer);
|
||||
addChild(5, mEndNamespaceList);
|
||||
addChild(3, mEndElementContainer);
|
||||
addChild(4, mEndNamespaceList);
|
||||
}
|
||||
public String getComment(){
|
||||
return getStartElement().getComment();
|
||||
}
|
||||
public void setComment(String comment){
|
||||
getStartElement().setComment(comment);
|
||||
}
|
||||
public void calculatePositions(){
|
||||
ResXmlStartElement start = getStartElement();
|
||||
@ -293,10 +292,53 @@ import java.util.*;
|
||||
return mBody.remove(element);
|
||||
}
|
||||
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(){
|
||||
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){
|
||||
List<ResXmlElement> results=new ArrayList<>();
|
||||
@ -432,25 +474,40 @@ import java.util.*;
|
||||
mEndElementContainer.setItem(item);
|
||||
}
|
||||
|
||||
// Use listXmlText() instead to be removed on next version
|
||||
@Deprecated
|
||||
public ResXmlText getResXmlText(){
|
||||
return mResXmlTextContainer.getItem();
|
||||
}
|
||||
public void setResXmlText(ResXmlText xmlText){
|
||||
mResXmlTextContainer.setItem(xmlText);
|
||||
}
|
||||
public void setResXmlText(String text){
|
||||
if(text==null){
|
||||
mResXmlTextContainer.setItem(null);
|
||||
}else {
|
||||
ResXmlText xmlText=mResXmlTextContainer.getItem();
|
||||
if(xmlText==null){
|
||||
xmlText=new ResXmlText();
|
||||
mResXmlTextContainer.setItem(xmlText);
|
||||
ResXmlStartElement start = getStartElement();
|
||||
xmlText.setLineNumber(start.getLineNumber());
|
||||
}
|
||||
xmlText.setText(text);
|
||||
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){
|
||||
addResXmlText(xmlText);
|
||||
}
|
||||
@Deprecated
|
||||
public void setResXmlText(String text){
|
||||
clearChildes();
|
||||
addResXmlText(text);
|
||||
}
|
||||
public void addResXmlText(String text){
|
||||
if(text==null){
|
||||
return;
|
||||
}
|
||||
ResXmlTextNode xmlTextNode=new ResXmlTextNode();
|
||||
addResXmlTextNode(xmlTextNode);
|
||||
xmlTextNode.setText(text);
|
||||
}
|
||||
|
||||
private boolean isBalanced(){
|
||||
@ -602,7 +659,7 @@ import java.util.*;
|
||||
}
|
||||
private void onXmlText(BlockReader reader) throws IOException{
|
||||
ResXmlText xmlText=new ResXmlText();
|
||||
setResXmlText(xmlText);
|
||||
addResXmlText(xmlText);
|
||||
xmlText.readBytes(reader);
|
||||
}
|
||||
|
||||
@ -644,6 +701,7 @@ import java.util.*;
|
||||
@Override
|
||||
public JSONObject toJson() {
|
||||
JSONObject jsonObject=new JSONObject();
|
||||
jsonObject.put(NAME_node_type, NAME_element);
|
||||
ResXmlStartElement start = getStartElement();
|
||||
jsonObject.put(NAME_line, start.getLineNumber());
|
||||
int i=0;
|
||||
@ -663,13 +721,6 @@ import java.util.*;
|
||||
if(comment!=null){
|
||||
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();
|
||||
if(uri!=null){
|
||||
jsonObject.put(NAME_namespace_uri, uri);
|
||||
@ -678,8 +729,8 @@ import java.util.*;
|
||||
jsonObject.put(NAME_attributes, attrArray);
|
||||
i=0;
|
||||
JSONArray childes=new JSONArray();
|
||||
for(ResXmlElement element:listElements()){
|
||||
childes.put(i, element.toJson());
|
||||
for(ResXmlNode xmlNode:listXmlNodes()){
|
||||
childes.put(i, xmlNode.toJson());
|
||||
i++;
|
||||
}
|
||||
if(i>0){
|
||||
@ -730,12 +781,29 @@ import java.util.*;
|
||||
int length=childArray.length();
|
||||
for(int i=0;i<length;i++){
|
||||
JSONObject childObject=childArray.getJSONObject(i);
|
||||
ResXmlElement child = createChildElement();
|
||||
child.fromJson(childObject);
|
||||
if(isTextNode(childObject)){
|
||||
ResXmlTextNode xmlTextNode=new ResXmlTextNode();
|
||||
addResXmlTextNode(xmlTextNode);
|
||||
xmlTextNode.fromJson(childObject);
|
||||
}else {
|
||||
ResXmlElement childElement = createChildElement();
|
||||
childElement.fromJson(childObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
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}
|
||||
@ -745,6 +813,7 @@ import java.util.*;
|
||||
* */
|
||||
public XMLElement decodeToXml(EntryStore entryStore, int currentPackageId) throws XMLException {
|
||||
XMLElement xmlElement = new XMLElement(getTagName());
|
||||
xmlElement.setLineNumber(getStartElement().getLineNumber());
|
||||
for(ResXmlStartNamespace startNamespace:getStartNamespaceList()){
|
||||
xmlElement.addAttribute(startNamespace.decodeToXml());
|
||||
}
|
||||
@ -753,15 +822,21 @@ import java.util.*;
|
||||
resXmlAttribute.decodeToXml(entryStore, currentPackageId);
|
||||
xmlElement.addAttribute(xmlAttribute);
|
||||
}
|
||||
for(ResXmlElement childResXmlElement:listElements()){
|
||||
XMLElement childXMLElement =
|
||||
childResXmlElement.decodeToXml(entryStore, currentPackageId);
|
||||
xmlElement.addChild(childXMLElement);
|
||||
String comment=getComment();
|
||||
if(comment!=null){
|
||||
xmlElement.addComment(new XMLComment(comment));
|
||||
}
|
||||
ResXmlText resXmlText = getResXmlText();
|
||||
if(resXmlText!=null){
|
||||
xmlElement.setTextContent(
|
||||
ValueDecoder.escapeSpecialCharacter(resXmlText.getText()));
|
||||
for(ResXmlNode xmlNode:listXmlNodes()){
|
||||
if(xmlNode instanceof ResXmlElement){
|
||||
ResXmlElement childResXmlElement=(ResXmlElement)xmlNode;
|
||||
XMLElement childXMLElement =
|
||||
childResXmlElement.decodeToXml(entryStore, currentPackageId);
|
||||
xmlElement.addChild(childXMLElement);
|
||||
}else if(xmlNode instanceof ResXmlTextNode){
|
||||
ResXmlTextNode childResXmlTextNode=(ResXmlTextNode)xmlNode;
|
||||
XMLText xmlText = childResXmlTextNode.decodeToXml();
|
||||
xmlElement.addText(xmlText);
|
||||
}
|
||||
}
|
||||
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_PREFIX = "android";
|
||||
|
||||
static final String NAME_element = "element";
|
||||
static final String NAME_name = "name";
|
||||
static final String NAME_comment = "comment";
|
||||
static final String NAME_text = "text";
|
||||
|
@ -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";
|
||||
}
|
@ -56,7 +56,9 @@ import java.util.Set;
|
||||
if(isEmpty(uri) || isEmpty(prefix)){
|
||||
return null;
|
||||
}
|
||||
return new SchemaAttr(prefix, uri);
|
||||
SchemaAttr schemaAttr=new SchemaAttr(prefix, uri);
|
||||
schemaAttr.setLineNumber(getLineNumber());
|
||||
return schemaAttr;
|
||||
}
|
||||
private boolean isEmpty(String txt){
|
||||
if(txt==null){
|
||||
|
@ -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";
|
||||
}
|
@ -77,6 +77,23 @@ public class ByteArray extends BlockItem {
|
||||
byte[] bts = getBytesInternal();
|
||||
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){
|
||||
byte[] bts = getBytesInternal();
|
||||
bts[offset+1]= (byte) (val >>> 8 & 0xff);
|
||||
|
@ -57,7 +57,7 @@ public abstract class BaseResValueItem extends BaseResValue implements ResValueI
|
||||
return null;
|
||||
}
|
||||
public ReferenceItem getTableStringReference(){
|
||||
if(getValueType()!=ValueType.STRING){
|
||||
if(getValueType()!=ValueType.STRING || getEntryBlock()==null){
|
||||
return null;
|
||||
}
|
||||
if(mReferenceItem==null){
|
||||
|
@ -35,9 +35,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class EntryBlock extends Block implements JSONConvert<JSONObject> {
|
||||
private ShortItem mHeaderSize;
|
||||
private ByteItem mFlagEntryType;
|
||||
private ByteItem mByteFlagsB;
|
||||
private ByteArray entryHeader;
|
||||
private IntegerItem mSpecReference;
|
||||
private BaseResValue mResValue;
|
||||
private boolean mUnLocked;
|
||||
@ -127,20 +125,16 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
|
||||
return tableString.getHtml();
|
||||
}
|
||||
public TableString getValueAsTableString(){
|
||||
TableStringPool stringPool=getTableStringPool();
|
||||
TableStringPool stringPool = getTableStringPool();
|
||||
if(stringPool==null){
|
||||
return null;
|
||||
}
|
||||
BaseResValue res = getResValue();
|
||||
if(!(res instanceof ResValueInt)){
|
||||
BaseResValue baseResValue = getResValue();
|
||||
if(!(baseResValue instanceof ResValueInt)){
|
||||
return null;
|
||||
}
|
||||
ResValueInt resValueInt=(ResValueInt)res;
|
||||
TableString tableString= stringPool.get(resValueInt.getData());
|
||||
if(tableString==null){
|
||||
return null;
|
||||
}
|
||||
return tableString;
|
||||
ResValueInt resValueInt = (ResValueInt)baseResValue;
|
||||
return stringPool.get(resValueInt.getData());
|
||||
}
|
||||
private TableStringPool getTableStringPool(){
|
||||
PackageBlock pkg=getPackageBlock();
|
||||
@ -256,27 +250,24 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
|
||||
removeTableReferences();
|
||||
removeSpecReferences();
|
||||
}
|
||||
public void setEntryTypeBag(boolean b){
|
||||
mFlagEntryType.putBit(0, b);
|
||||
private void setEntryTypeBag(boolean b){
|
||||
entryHeader.putBit(OFFSET_FLAGS, 0, b);
|
||||
refreshHeaderSize();
|
||||
}
|
||||
public boolean isEntryTypeBag(){
|
||||
return mFlagEntryType.getBit(0);
|
||||
return entryHeader.getBit(OFFSET_FLAGS,0);
|
||||
}
|
||||
public void setEntryTypeShared(boolean b){
|
||||
mFlagEntryType.putBit(1, b);
|
||||
public void setPublic(boolean b){
|
||||
entryHeader.putBit(OFFSET_FLAGS,1, b);
|
||||
}
|
||||
public boolean isEntryTypeShared(){
|
||||
return mFlagEntryType.getBit(1);
|
||||
public boolean isPublic(){
|
||||
return entryHeader.getBit(OFFSET_FLAGS,1);
|
||||
}
|
||||
public void setEntryTypePublic(boolean b){
|
||||
mFlagEntryType.putBit(2, b);
|
||||
public void setWeak(boolean b){
|
||||
entryHeader.putBit(OFFSET_FLAGS, 2, b);
|
||||
}
|
||||
public boolean isEntryTypePublic(){
|
||||
return mFlagEntryType.getBit(2);
|
||||
}
|
||||
private void setByteFlagsB(byte b){
|
||||
mByteFlagsB.set(b);
|
||||
public boolean isWeak(){
|
||||
return entryHeader.getBit(OFFSET_FLAGS,2);
|
||||
}
|
||||
private IntegerItem getSpecReferenceBlock(){
|
||||
return mSpecReference;
|
||||
@ -341,7 +332,7 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
|
||||
return;
|
||||
}
|
||||
if(resValue!=null){
|
||||
resValue.setIndex(4);
|
||||
resValue.setIndex(2);
|
||||
resValue.setParent(this);
|
||||
}
|
||||
if(mResValue!=null){
|
||||
@ -509,47 +500,32 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
|
||||
if(mUnLocked){
|
||||
return;
|
||||
}
|
||||
mUnLocked =true;
|
||||
this.mHeaderSize =new ShortItem();
|
||||
this.mFlagEntryType =new ByteItem();
|
||||
this.mByteFlagsB=new ByteItem();
|
||||
mUnLocked = true;
|
||||
entryHeader = new ByteArray(4);
|
||||
if(mSpecReference==null){
|
||||
this.mSpecReference = new IntegerItem();
|
||||
}else if(mSpecReference.isNull()){
|
||||
mSpecReference.setNull(false);
|
||||
}
|
||||
|
||||
mHeaderSize.setIndex(0);
|
||||
mFlagEntryType.setIndex(1);
|
||||
mByteFlagsB.setIndex(2);
|
||||
mSpecReference.setIndex(3);
|
||||
|
||||
mHeaderSize.setParent(this);
|
||||
mFlagEntryType.setParent(this);
|
||||
mByteFlagsB.setParent(this);
|
||||
entryHeader.setIndex(0);
|
||||
mSpecReference.setIndex(1);
|
||||
entryHeader.setParent(this);
|
||||
mSpecReference.setParent(this);
|
||||
|
||||
}
|
||||
private void lockEntry(){
|
||||
if(!mUnLocked){
|
||||
return;
|
||||
}
|
||||
removeAllReferences();
|
||||
mUnLocked =false;
|
||||
mHeaderSize.setParent(null);
|
||||
mFlagEntryType.setParent(null);
|
||||
mByteFlagsB.setParent(null);
|
||||
mUnLocked = false;
|
||||
entryHeader.setParent(null);
|
||||
mSpecReference.setParent(null);
|
||||
|
||||
mHeaderSize.setIndex(-1);
|
||||
mFlagEntryType.setIndex(-1);
|
||||
mByteFlagsB.setIndex(-1);
|
||||
entryHeader.setIndex(-1);
|
||||
mSpecReference.setIndex(-1);
|
||||
removeResValue();
|
||||
|
||||
this.mHeaderSize =null;
|
||||
this.mFlagEntryType =null;
|
||||
this.mByteFlagsB =null;
|
||||
this.entryHeader = null;
|
||||
this.mSpecReference =null;
|
||||
}
|
||||
private void removeResValue(){
|
||||
@ -560,11 +536,13 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
|
||||
}
|
||||
}
|
||||
private void refreshHeaderSize(){
|
||||
short size;
|
||||
if(isEntryTypeBag()){
|
||||
mHeaderSize.set(HEADER_SIZE_BAG);
|
||||
size=HEADER_SIZE_BAG;
|
||||
}else {
|
||||
mHeaderSize.set(HEADER_SIZE_INT);
|
||||
size=HEADER_SIZE_INT;
|
||||
}
|
||||
entryHeader.putShort(OFFSET_SIZE, size);
|
||||
}
|
||||
private void createResValue(){
|
||||
if(getResValue()!=null){
|
||||
@ -599,9 +577,7 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
|
||||
if(isNull()){
|
||||
return null;
|
||||
}
|
||||
byte[] results=mHeaderSize.getBytes();
|
||||
results=addBytes(results, mFlagEntryType.getBytes());
|
||||
results=addBytes(results, mByteFlagsB.getBytes());
|
||||
byte[] results=entryHeader.getBytes();
|
||||
results=addBytes(results, mSpecReference.getBytes());
|
||||
results=addBytes(results, mResValue.getBytes());
|
||||
return results;
|
||||
@ -612,8 +588,7 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
mHeaderSize -> 2 bytes
|
||||
mFlags -> 2 bytes
|
||||
entryHeader -> 4 bytes
|
||||
mSpecReference -> 4 bytes
|
||||
-------
|
||||
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;
|
||||
}
|
||||
counter.addCount(countBytes());
|
||||
mHeaderSize.onCountUpTo(counter);
|
||||
mFlagEntryType.onCountUpTo(counter);
|
||||
mByteFlagsB.onCountUpTo(counter);
|
||||
entryHeader.onCountUpTo(counter);
|
||||
mSpecReference.onCountUpTo(counter);
|
||||
mResValue.onCountUpTo(counter);
|
||||
}
|
||||
@ -644,9 +617,7 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
|
||||
if(isNull()){
|
||||
return 0;
|
||||
}
|
||||
int result=mHeaderSize.writeBytes(stream);
|
||||
result+= mFlagEntryType.writeBytes(stream);
|
||||
result+=mByteFlagsB.writeBytes(stream);
|
||||
int result=entryHeader.writeBytes(stream);
|
||||
result+= mSpecReference.writeBytes(stream);
|
||||
result+=mResValue.writeBytes(stream);
|
||||
return result;
|
||||
@ -695,15 +666,12 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
|
||||
public void onReadBytes(BlockReader reader) throws IOException{
|
||||
setNull(false);
|
||||
removeResValue();
|
||||
mHeaderSize.readBytes(reader);
|
||||
mFlagEntryType.readBytes(reader);
|
||||
mByteFlagsB.readBytes(reader);
|
||||
entryHeader.readBytes(reader);
|
||||
mSpecReference.readBytes(reader);
|
||||
createResValue();
|
||||
mResValue.readBytes(reader);
|
||||
updatePackage();
|
||||
updateSpecRef();
|
||||
mResValue.onDataLoaded();
|
||||
}
|
||||
@Override
|
||||
public JSONObject toJson() {
|
||||
@ -715,10 +683,10 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
|
||||
if(isEntryTypeBag()){
|
||||
jsonObject.put(NAME_is_bag, true);
|
||||
}
|
||||
if(isEntryTypePublic()){
|
||||
jsonObject.put(NAME_is_public, true);
|
||||
if(isWeak()){
|
||||
jsonObject.put(NAME_is_weak, true);
|
||||
}
|
||||
if(isEntryTypeShared()){
|
||||
if(isPublic()){
|
||||
jsonObject.put(NAME_is_shared, true);
|
||||
}
|
||||
jsonObject.put(NAME_value, getResValue().toJson());
|
||||
@ -737,8 +705,8 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
|
||||
baseResValue=new ResValueInt();
|
||||
}
|
||||
setResValue(baseResValue);
|
||||
setEntryTypeShared(json.optBoolean(NAME_is_shared, false));
|
||||
setEntryTypePublic(json.optBoolean(NAME_is_public, false));
|
||||
setPublic(json.optBoolean(NAME_is_shared, false));
|
||||
setWeak(json.optBoolean(NAME_is_weak, false));
|
||||
setName(json.getString(NAME_entry_name));
|
||||
baseResValue.fromJson(json.getJSONObject(NAME_value));
|
||||
mResValue.onDataLoaded();
|
||||
@ -765,8 +733,8 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
|
||||
SpecString spec = getPackageBlock()
|
||||
.getSpecStringPool().getOrCreate(name);
|
||||
setSpecReference(spec.getIndex());
|
||||
setEntryTypeShared(entryBlock.isEntryTypeShared());
|
||||
setEntryTypePublic(entryBlock.isEntryTypePublic());
|
||||
setPublic(entryBlock.isPublic());
|
||||
setWeak(entryBlock.isWeak());
|
||||
}
|
||||
private ResValueBag getOrCreateResValueBag(){
|
||||
if(mResValue instanceof ResValueBag){
|
||||
@ -833,13 +801,15 @@ public class EntryBlock extends Block implements JSONConvert<JSONObject> {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private final static short HEADER_SIZE_BAG = 0x0010;
|
||||
private final static short HEADER_SIZE_INT = 0x0008;
|
||||
private static final int OFFSET_SIZE = 0;
|
||||
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";
|
||||
private static final String NAME_is_bag="is_bag";
|
||||
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";
|
||||
|
||||
}
|
||||
|
@ -139,7 +139,9 @@ public class ResValueBagItem extends BaseResValueItem{
|
||||
}
|
||||
void refreshTableReference(){
|
||||
if(getValueType()==ValueType.STRING){
|
||||
addTableReference(getTableStringReference());
|
||||
if(!hasTableReference()){
|
||||
addTableReference(getTableStringReference());
|
||||
}
|
||||
}else {
|
||||
removeTableReference();
|
||||
}
|
||||
|
@ -48,11 +48,16 @@ public class XMLElement extends XMLNode{
|
||||
}
|
||||
|
||||
public void addText(XMLText text){
|
||||
addTextInternal(text, true);
|
||||
}
|
||||
private void addTextInternal(XMLText text, boolean addSupper){
|
||||
if(text==null){
|
||||
return;
|
||||
}
|
||||
mTexts.add(text);
|
||||
super.addChildNode(text);
|
||||
if(addSupper){
|
||||
super.addChildNodeInternal(text);
|
||||
}
|
||||
}
|
||||
private void appendText(String text){
|
||||
if(text==null || text.length()==0){
|
||||
@ -171,7 +176,7 @@ public class XMLElement extends XMLNode{
|
||||
}
|
||||
public XMLElement createElement(String tag) {
|
||||
XMLElement baseElement=new XMLElement(tag);
|
||||
addChildNoCheck(baseElement);
|
||||
addChildNoCheck(baseElement, true);
|
||||
return baseElement;
|
||||
}
|
||||
public void addChild(Collection<XMLElement> elements) {
|
||||
@ -183,7 +188,7 @@ public class XMLElement extends XMLNode{
|
||||
}
|
||||
}
|
||||
public void addChild(XMLElement child) {
|
||||
addChildNoCheck(child);
|
||||
addChildNoCheck(child, true);
|
||||
}
|
||||
private void clearChildElements(){
|
||||
mChildes.clear();
|
||||
@ -238,6 +243,9 @@ public class XMLElement extends XMLNode{
|
||||
mComments=null;
|
||||
}
|
||||
public void addComment(XMLComment commentElement) {
|
||||
addCommentInternal(commentElement, true);
|
||||
}
|
||||
void addCommentInternal(XMLComment commentElement, boolean addSuper) {
|
||||
if(commentElement==null){
|
||||
return;
|
||||
}
|
||||
@ -247,7 +255,9 @@ public class XMLElement extends XMLNode{
|
||||
mComments.add(commentElement);
|
||||
commentElement.setIndent(getIndent());
|
||||
commentElement.setParent(this);
|
||||
super.addChildNode(commentElement);
|
||||
if(addSuper){
|
||||
super.addChildNodeInternal(commentElement);
|
||||
}
|
||||
}
|
||||
public void removeChildElements(){
|
||||
mChildes.clear();
|
||||
@ -413,14 +423,26 @@ public class XMLElement extends XMLNode{
|
||||
void setParent(XMLElement 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){
|
||||
return;
|
||||
}
|
||||
child.setParent(this);
|
||||
child.setIndent(getChildIndent());
|
||||
mChildes.add(child);
|
||||
super.addChildNode(child);
|
||||
if(addSupper){
|
||||
super.addChildNodeInternal(child);
|
||||
}
|
||||
}
|
||||
public int getLevel(){
|
||||
int rs=0;
|
||||
@ -579,12 +601,19 @@ public class XMLElement extends XMLNode{
|
||||
public boolean hasTextContent() {
|
||||
return mTexts.size()>0;
|
||||
}
|
||||
public String getText(){
|
||||
if(mTexts.size()==0){
|
||||
return null;
|
||||
}
|
||||
return mTexts.get(0).getText();
|
||||
}
|
||||
public void setTextContent(String text){
|
||||
setTextContent(text, true);
|
||||
}
|
||||
public void setTextContent(String text, boolean escape){
|
||||
clearChildElements();
|
||||
clearTexts();
|
||||
super.getChildNodes().clear();
|
||||
if(escape){
|
||||
text=XMLUtil.escapeXmlChars(text);
|
||||
}
|
||||
|
@ -21,12 +21,37 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class XMLNode {
|
||||
private int mLineNumber;
|
||||
private int mColumnNumber;
|
||||
private final List<XMLNode> mChildNodes = new ArrayList<>();
|
||||
|
||||
void addChildNode(XMLNode xmlNode){
|
||||
if(xmlNode!=null && canAdd(xmlNode)){
|
||||
mChildNodes.add(xmlNode);
|
||||
public int getColumnNumber() {
|
||||
return mColumnNumber;
|
||||
}
|
||||
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){
|
||||
return !mChildNodes.contains(xmlNode);
|
||||
|
@ -26,6 +26,11 @@ public class XMLText extends XMLNode{
|
||||
public XMLText(){
|
||||
this(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addChildNode(XMLNode xmlNode){
|
||||
throw new IllegalArgumentException("Can not add xml node on text: "+xmlNode);
|
||||
}
|
||||
public String getText(){
|
||||
return getText(true);
|
||||
}
|
||||
|
@ -425,6 +425,10 @@ public class MXParser implements XmlPullParser
|
||||
public InputStream getInputStream(){
|
||||
return inputStream;
|
||||
}
|
||||
public Reader getReader(){
|
||||
reset();
|
||||
return reader;
|
||||
}
|
||||
|
||||
public String getInputEncoding() {
|
||||
return inputEncoding;
|
||||
|
@ -45,7 +45,7 @@ public class XMLDocumentParser {
|
||||
public XMLDocument parse() throws XMLParseException {
|
||||
try {
|
||||
XMLDocument document= parseDocument();
|
||||
closeFileInputStream();
|
||||
close();
|
||||
return document;
|
||||
} catch (XmlPullParserException | IOException e) {
|
||||
XMLParseException ex=new XMLParseException(e.getMessage());
|
||||
@ -53,12 +53,20 @@ public class XMLDocumentParser {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
private void close(){
|
||||
closeReader();
|
||||
closeFileInputStream();
|
||||
mResDocument=null;
|
||||
mCurrentElement=null;
|
||||
mCurrentText=null;
|
||||
mComments=null;
|
||||
}
|
||||
private void closeFileInputStream(){
|
||||
if(!(mParser instanceof MXParser)){
|
||||
return;
|
||||
}
|
||||
MXParser mxParser=(MXParser) mParser;
|
||||
InputStream inputStream = mxParser.getInputStream();
|
||||
MXParser parser=(MXParser) mParser;
|
||||
InputStream inputStream = parser.getInputStream();
|
||||
if(!(inputStream instanceof FileInputStream)){
|
||||
return;
|
||||
}
|
||||
@ -67,6 +75,19 @@ public class XMLDocumentParser {
|
||||
} 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 {
|
||||
mResDocument=null;
|
||||
@ -121,6 +142,8 @@ public class XMLDocumentParser {
|
||||
}else {
|
||||
mCurrentElement=mCurrentElement.createElement(name);
|
||||
}
|
||||
mCurrentElement.setColumnNumber(mParser.getColumnNumber());
|
||||
mCurrentElement.setLineNumber(mParser.getLineNumber());
|
||||
checkIndent();
|
||||
flushComments(mCurrentElement);
|
||||
String ns=mParser.getNamespace();
|
||||
@ -284,6 +307,8 @@ public class XMLDocumentParser {
|
||||
}
|
||||
XMLComment commentElement=new XMLComment();
|
||||
commentElement.setCommentText(commentText);
|
||||
commentElement.setColumnNumber(mParser.getColumnNumber());
|
||||
commentElement.setLineNumber(mParser.getLineNumber());
|
||||
addComment(commentElement);
|
||||
}
|
||||
private void addComment(XMLComment ce){
|
||||
|
Loading…
x
Reference in New Issue
Block a user