From 0ec2b3fe1c32af9fb42000a238078a567196a0ae Mon Sep 17 00:00:00 2001 From: REAndroid Date: Tue, 2 May 2023 16:41:33 +0200 Subject: [PATCH] implement xml pp namespace features #18 --- .../arsc/chunk/xml/ParserEventList.java | 2 +- .../arsc/chunk/xml/ResXmlAttribute.java | 9 +- .../arsc/chunk/xml/ResXmlElement.java | 2008 +++++++++-------- .../arsc/chunk/xml/ResXmlPullParser.java | 103 +- .../arsc/chunk/xml/ResXmlStartNamespace.java | 173 +- .../reandroid/xml/XmlParserToSerializer.java | 34 +- 6 files changed, 1224 insertions(+), 1105 deletions(-) diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/ParserEventList.java b/src/main/java/com/reandroid/arsc/chunk/xml/ParserEventList.java index d9b2a37..f383204 100644 --- a/src/main/java/com/reandroid/arsc/chunk/xml/ParserEventList.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/ParserEventList.java @@ -1,4 +1,4 @@ - /* +/* * Copyright (C) 2022 github.com/REAndroid * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlAttribute.java b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlAttribute.java index 991bcbf..f0c96da 100755 --- a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlAttribute.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlAttribute.java @@ -123,12 +123,13 @@ public class ResXmlAttribute extends ValueItem implements AttributeValue, Compar startNamespace.addAttributeReference(this); } private void unLinkStartNameSpace(){ - ResXmlElement xmlElement=getParentResXmlElement(); - if(xmlElement==null){ + ResXmlElement xmlElement = getParentResXmlElement(); + if(xmlElement == null){ return; } - ResXmlStartNamespace startNamespace=xmlElement.getStartNamespaceByUriRef(getNamespaceReference()); - if(startNamespace==null){ + ResXmlStartNamespace startNamespace = + xmlElement.getStartNamespaceByUriRef(getNamespaceReference()); + if(startNamespace == null){ return; } startNamespace.removeAttributeReference(this); diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlElement.java b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlElement.java index 5e0a3df..06accd8 100755 --- a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlElement.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlElement.java @@ -1,1024 +1,1038 @@ - /* - * 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; +/* + * 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.base.Block; - import com.reandroid.arsc.container.BlockList; - import com.reandroid.arsc.container.SingleBlockContainer; - import com.reandroid.arsc.header.HeaderBlock; - import com.reandroid.arsc.io.BlockReader; - import com.reandroid.arsc.pool.ResXmlStringPool; - import com.reandroid.common.EntryStore; - import com.reandroid.json.JSONConvert; - import com.reandroid.json.JSONArray; - import com.reandroid.json.JSONObject; - import com.reandroid.xml.*; +import com.reandroid.arsc.chunk.ChunkType; +import com.reandroid.arsc.base.Block; +import com.reandroid.arsc.container.BlockList; +import com.reandroid.arsc.container.SingleBlockContainer; +import com.reandroid.arsc.header.HeaderBlock; +import com.reandroid.arsc.io.BlockReader; +import com.reandroid.arsc.pool.ResXmlStringPool; +import com.reandroid.common.EntryStore; +import com.reandroid.json.JSONConvert; +import com.reandroid.json.JSONArray; +import com.reandroid.json.JSONObject; +import com.reandroid.xml.*; - import java.io.IOException; - import java.util.*; +import java.io.IOException; +import java.util.*; - public class ResXmlElement extends ResXmlNode implements JSONConvert, - Comparator { - private final BlockList mStartNamespaceList; - private final SingleBlockContainer mStartElementContainer; - private final BlockList mBody; - private final SingleBlockContainer mEndElementContainer; - private final BlockList mEndNamespaceList; - private int mLevel; - public ResXmlElement() { - super(5); - this.mStartNamespaceList = new BlockList<>(); - this.mStartElementContainer= new SingleBlockContainer<>(); - this.mBody = new BlockList<>(); - this.mEndElementContainer = new SingleBlockContainer<>(); - this.mEndNamespaceList = new BlockList<>(); - addChild(0, mStartNamespaceList); - addChild(1, mStartElementContainer); - addChild(2, mBody); - addChild(3, mEndElementContainer); - addChild(4, mEndNamespaceList); - } - public void changeIndex(ResXmlElement element, int index){ - int i = 0; - for(ResXmlNode xmlNode:mBody.getChildes()){ - if(i == index){ - element.setIndex(i); - i++; - } - if(xmlNode==element){ - continue; - } - xmlNode.setIndex(i); - i++; - } - mBody.sort(this); - } - public int lastIndexOf(String tagName){ - List elementList = listElements(tagName); - int i = elementList.size(); - if(i==0){ - return -1; - } - i--; - return elementList.get(i).getIndex(); - } - public int indexOf(String tagName){ - ResXmlElement element = getElementByTagName(tagName); - if(element!=null){ - return element.getIndex(); - } - return -1; - } - public int indexOf(ResXmlElement element){ - int index = 0; - for(ResXmlNode xmlNode:mBody.getChildes()){ - if(xmlNode==element){ - return index; - } - index++; - } - return -1; - } - public void setAttributesUnitSize(int size, boolean setToAll){ - ResXmlStartElement startElement = getStartElement(); - startElement.setAttributesUnitSize(size); - if(setToAll){ - for(ResXmlElement child:listElements()){ - child.setAttributesUnitSize(size, setToAll); - } - } - } - public String getStartComment(){ - ResXmlStartElement start = getStartElement(); - if(start!=null){ - return start.getComment(); - } - return null; - } - public String getEndComment(){ - ResXmlEndElement end = getEndElement(); - if(end!=null){ - return end.getComment(); - } - return null; - } - public int getStartLineNumber(){ - ResXmlStartElement start = getStartElement(); - if(start!=null){ - return start.getLineNumber(); - } - return 0; - } - public int getEndLineNumber(){ - ResXmlEndElement end = getEndElement(); - if(end!=null){ - return end.getLineNumber(); - } - return 0; - } - public void setComment(String comment){ - getStartElement().setComment(comment); - } - public void calculatePositions(){ - ResXmlStartElement start = getStartElement(); - if(start!=null){ - start.calculatePositions(); - } - } - public ResXmlAttribute newAttribute(){ - return getStartElement().newAttribute(); - } - void onRemoved(){ - for(ResXmlStartNamespace startNamespace:getStartNamespaceList()){ - startNamespace.onRemoved(); - } - ResXmlStartElement start = getStartElement(); - if(start!=null){ - start.onRemoved(); - } - ResXmlText resXmlText=getResXmlText(); - if(resXmlText!=null){ - resXmlText.onRemoved(); - } - for(ResXmlElement child:listElements()){ - child.onRemoved(); - } - } - void linkStringReferences(){ - for(ResXmlStartNamespace startNamespace:getStartNamespaceList()){ - startNamespace.linkStringReferences(); - } - ResXmlStartElement start = getStartElement(); - if(start!=null){ - start.linkStringReferences(); - } - ResXmlText resXmlText=getResXmlText(); - if(resXmlText!=null){ - resXmlText.linkStringReferences(); - } - for(ResXmlElement child:listElements()){ - child.linkStringReferences(); - } - } - public ResXmlElement createChildElement(){ - return createChildElement(null); - } - public ResXmlElement createChildElement(String tag){ - int lineNo=getStartElement().getLineNumber()+1; - ResXmlElement resXmlElement=new ResXmlElement(); - resXmlElement.newStartElement(lineNo); +public class ResXmlElement extends ResXmlNode implements JSONConvert, + Comparator { + private final BlockList mStartNamespaceList; + private final SingleBlockContainer mStartElementContainer; + private final BlockList mBody; + private final SingleBlockContainer mEndElementContainer; + private final BlockList mEndNamespaceList; + private int mLevel; + public ResXmlElement() { + super(5); + this.mStartNamespaceList = new BlockList<>(); + this.mStartElementContainer= new SingleBlockContainer<>(); + this.mBody = new BlockList<>(); + this.mEndElementContainer = new SingleBlockContainer<>(); + this.mEndNamespaceList = new BlockList<>(); + addChild(0, mStartNamespaceList); + addChild(1, mStartElementContainer); + addChild(2, mBody); + addChild(3, mEndElementContainer); + addChild(4, mEndNamespaceList); + } + public void changeIndex(ResXmlElement element, int index){ + int i = 0; + for(ResXmlNode xmlNode:mBody.getChildes()){ + if(i == index){ + element.setIndex(i); + i++; + } + if(xmlNode==element){ + continue; + } + xmlNode.setIndex(i); + i++; + } + mBody.sort(this); + } + public int lastIndexOf(String tagName){ + List elementList = listElements(tagName); + int i = elementList.size(); + if(i==0){ + return -1; + } + i--; + return elementList.get(i).getIndex(); + } + public int indexOf(String tagName){ + ResXmlElement element = getElementByTagName(tagName); + if(element!=null){ + return element.getIndex(); + } + return -1; + } + public int indexOf(ResXmlElement element){ + int index = 0; + for(ResXmlNode xmlNode:mBody.getChildes()){ + if(xmlNode==element){ + return index; + } + index++; + } + return -1; + } + public void setAttributesUnitSize(int size, boolean setToAll){ + ResXmlStartElement startElement = getStartElement(); + startElement.setAttributesUnitSize(size); + if(setToAll){ + for(ResXmlElement child:listElements()){ + child.setAttributesUnitSize(size, setToAll); + } + } + } + public String getStartComment(){ + ResXmlStartElement start = getStartElement(); + if(start!=null){ + return start.getComment(); + } + return null; + } + public String getEndComment(){ + ResXmlEndElement end = getEndElement(); + if(end!=null){ + return end.getComment(); + } + return null; + } + public int getStartLineNumber(){ + ResXmlStartElement start = getStartElement(); + if(start!=null){ + return start.getLineNumber(); + } + return 0; + } + public int getEndLineNumber(){ + ResXmlEndElement end = getEndElement(); + if(end!=null){ + return end.getLineNumber(); + } + return 0; + } + public void setComment(String comment){ + getStartElement().setComment(comment); + } + public void calculatePositions(){ + ResXmlStartElement start = getStartElement(); + if(start!=null){ + start.calculatePositions(); + } + } + public ResXmlAttribute newAttribute(){ + return getStartElement().newAttribute(); + } + void onRemoved(){ + for(ResXmlStartNamespace startNamespace:getStartNamespaceList()){ + startNamespace.onRemoved(); + } + ResXmlStartElement start = getStartElement(); + if(start!=null){ + start.onRemoved(); + } + ResXmlText resXmlText=getResXmlText(); + if(resXmlText!=null){ + resXmlText.onRemoved(); + } + for(ResXmlElement child:listElements()){ + child.onRemoved(); + } + } + void linkStringReferences(){ + for(ResXmlStartNamespace startNamespace:getStartNamespaceList()){ + startNamespace.linkStringReferences(); + } + ResXmlStartElement start = getStartElement(); + if(start!=null){ + start.linkStringReferences(); + } + ResXmlText resXmlText=getResXmlText(); + if(resXmlText!=null){ + resXmlText.linkStringReferences(); + } + for(ResXmlElement child:listElements()){ + child.linkStringReferences(); + } + } + public ResXmlElement createChildElement(){ + return createChildElement(null); + } + public ResXmlElement createChildElement(String tag){ + int lineNo=getStartElement().getLineNumber()+1; + ResXmlElement resXmlElement=new ResXmlElement(); + resXmlElement.newStartElement(lineNo); - addElement(resXmlElement); + addElement(resXmlElement); - if(tag!=null){ - resXmlElement.setTag(tag); - } - return resXmlElement; - } - public ResXmlAttribute getOrCreateAndroidAttribute(String name, int resourceId){ - return getOrCreateAttribute(NS_ANDROID_URI, NS_ANDROID_PREFIX, name, resourceId); - } - public ResXmlAttribute getOrCreateAttribute(String uri, String prefix, String name, int resourceId){ - ResXmlAttribute attribute=searchAttribute(name, resourceId); - if(attribute==null){ - attribute = createAttribute(name, resourceId); - if(uri!=null){ - ResXmlElement root = getRootResXmlElement(); - ResXmlStartNamespace ns = root.getOrCreateNamespace(uri, prefix); - attribute.setNamespaceReference(ns.getUriReference()); - } - } - return attribute; - } - public ResXmlAttribute getOrCreateAttribute(String name, int resourceId){ - ResXmlAttribute attribute=searchAttribute(name, resourceId); - if(attribute==null){ - attribute=createAttribute(name, resourceId); - } - return attribute; - } - public ResXmlAttribute createAndroidAttribute(String name, int resourceId){ - ResXmlAttribute attribute=createAttribute(name, resourceId); - ResXmlStartNamespace ns = getOrCreateNamespace(NS_ANDROID_URI, NS_ANDROID_PREFIX); - attribute.setNamespaceReference(ns.getUriReference()); - return attribute; - } - public ResXmlAttribute createAttribute(String name, int resourceId){ - ResXmlAttribute attribute=new ResXmlAttribute(); - addAttribute(attribute); - attribute.setName(name, resourceId); - return attribute; - } - public void addAttribute(ResXmlAttribute attribute){ - getStartElement().getResXmlAttributeArray().add(attribute); - } - public ResXmlElement getElementByTagName(String name){ - if(name==null){ - return null; - } - for(ResXmlElement child:listElements()){ - if(name.equals(child.getTag())||name.equals(child.getTagName())){ - return child; - } - } - return null; - } - private ResXmlAttribute searchAttribute(String name, int resourceId){ - if(resourceId==0){ - return searchAttributeByName(name); - } - return searchAttributeByResourceId(resourceId); - } - // Searches attribute with resource id = 0 - public ResXmlAttribute searchAttributeByName(String name){ - ResXmlStartElement startElement=getStartElement(); - if(startElement!=null){ - return startElement.searchAttributeByName(name); - } - return null; - } - public ResXmlAttribute searchAttributeByResourceId(int resourceId){ - ResXmlStartElement startElement=getStartElement(); - if(startElement!=null){ - return startElement.searchAttributeByResourceId(resourceId); - } - return null; - } - public void setTag(String tag){ - ResXmlStringPool pool = getStringPool(); - if(pool==null){ - return; - } - ensureStartEndElement(); - ResXmlStartElement start=getStartElement(); - String prefix=null; - String name=tag; - int i=tag.lastIndexOf(':'); - if(i>=0){ - prefix=tag.substring(0,i); - i++; - name=tag.substring(i); - } - start.setName(name); - ResXmlStartNamespace ns = getStartNamespaceByPrefix(prefix); - if(ns!=null){ - start.setNamespaceReference(ns.getUriReference()); - } - } - public String getTagName(){ - ResXmlStartElement startElement=getStartElement(); - if(startElement!=null){ - return startElement.getTagName(); - } - return null; - } - public String getTag(){ - ResXmlStartElement startElement=getStartElement(); - if(startElement!=null){ - return startElement.getName(); - } - return null; - } - public String getTagUri(){ - ResXmlStartElement startElement=getStartElement(); - if(startElement!=null){ - return startElement.getUri(); - } - return null; - } - public String getTagPrefix(){ - ResXmlStartElement startElement=getStartElement(); - if(startElement!=null){ - return startElement.getPrefix(); - } - return null; - } - public int getAttributeCount() { - ResXmlStartElement startElement=getStartElement(); - if(startElement!=null){ - return startElement.getResXmlAttributeArray().childesCount(); - } - return 0; - } - public ResXmlAttribute getAttributeAt(int index){ - ResXmlStartElement startElement=getStartElement(); - if(startElement!=null){ - return startElement.getResXmlAttributeArray().get(index); - } - return null; - } - public Collection listAttributes(){ - ResXmlStartElement startElement=getStartElement(); - if(startElement!=null){ - return startElement.listResXmlAttributes(); - } - return new ArrayList<>(); - } - public ResXmlStringPool getStringPool(){ - Block parent=getParent(); - while (parent!=null){ - if(parent instanceof ResXmlDocument){ - return ((ResXmlDocument)parent).getStringPool(); - } - if(parent instanceof ResXmlElement){ - return ((ResXmlElement)parent).getStringPool(); - } - parent=parent.getParent(); - } - return null; - } - public ResXmlIDMap getResXmlIDMap(){ - ResXmlDocument resXmlDocument = getParentDocument(); - if(resXmlDocument!=null){ - return resXmlDocument.getResXmlIDMap(); - } - return null; - } - public ResXmlDocument getParentDocument(){ - return getParentInstance(ResXmlDocument.class); - } + if(tag!=null){ + resXmlElement.setTag(tag); + } + return resXmlElement; + } + public ResXmlAttribute getOrCreateAndroidAttribute(String name, int resourceId){ + return getOrCreateAttribute(NS_ANDROID_URI, NS_ANDROID_PREFIX, name, resourceId); + } + public ResXmlAttribute getOrCreateAttribute(String uri, String prefix, String name, int resourceId){ + ResXmlAttribute attribute=searchAttribute(name, resourceId); + if(attribute==null){ + attribute = createAttribute(name, resourceId); + if(uri!=null){ + ResXmlElement root = getRootResXmlElement(); + ResXmlStartNamespace ns = root.getOrCreateNamespace(uri, prefix); + attribute.setNamespaceReference(ns.getUriReference()); + } + } + return attribute; + } + public ResXmlAttribute getOrCreateAttribute(String name, int resourceId){ + ResXmlAttribute attribute=searchAttribute(name, resourceId); + if(attribute==null){ + attribute=createAttribute(name, resourceId); + } + return attribute; + } + public ResXmlAttribute createAndroidAttribute(String name, int resourceId){ + ResXmlAttribute attribute=createAttribute(name, resourceId); + ResXmlStartNamespace ns = getOrCreateNamespace(NS_ANDROID_URI, NS_ANDROID_PREFIX); + attribute.setNamespaceReference(ns.getUriReference()); + return attribute; + } + public ResXmlAttribute createAttribute(String name, int resourceId){ + ResXmlAttribute attribute=new ResXmlAttribute(); + addAttribute(attribute); + attribute.setName(name, resourceId); + return attribute; + } + public void addAttribute(ResXmlAttribute attribute){ + getStartElement().getResXmlAttributeArray().add(attribute); + } + public ResXmlElement getElementByTagName(String name){ + if(name==null){ + return null; + } + for(ResXmlElement child:listElements()){ + if(name.equals(child.getTag())||name.equals(child.getTagName())){ + return child; + } + } + return null; + } + private ResXmlAttribute searchAttribute(String name, int resourceId){ + if(resourceId==0){ + return searchAttributeByName(name); + } + return searchAttributeByResourceId(resourceId); + } + // Searches attribute with resource id = 0 + public ResXmlAttribute searchAttributeByName(String name){ + ResXmlStartElement startElement=getStartElement(); + if(startElement!=null){ + return startElement.searchAttributeByName(name); + } + return null; + } + public ResXmlAttribute searchAttributeByResourceId(int resourceId){ + ResXmlStartElement startElement=getStartElement(); + if(startElement!=null){ + return startElement.searchAttributeByResourceId(resourceId); + } + return null; + } + public void setTag(String tag){ + ResXmlStringPool pool = getStringPool(); + if(pool==null){ + return; + } + ensureStartEndElement(); + ResXmlStartElement start=getStartElement(); + String prefix=null; + String name=tag; + int i=tag.lastIndexOf(':'); + if(i>=0){ + prefix=tag.substring(0,i); + i++; + name=tag.substring(i); + } + start.setName(name); + ResXmlStartNamespace ns = getStartNamespaceByPrefix(prefix); + if(ns!=null){ + start.setNamespaceReference(ns.getUriReference()); + } + } + public String getTagName(){ + ResXmlStartElement startElement=getStartElement(); + if(startElement!=null){ + return startElement.getTagName(); + } + return null; + } + public String getTag(){ + ResXmlStartElement startElement=getStartElement(); + if(startElement!=null){ + return startElement.getName(); + } + return null; + } + public String getTagUri(){ + ResXmlStartElement startElement=getStartElement(); + if(startElement!=null){ + return startElement.getUri(); + } + return null; + } + public String getTagPrefix(){ + ResXmlStartElement startElement=getStartElement(); + if(startElement!=null){ + return startElement.getPrefix(); + } + return null; + } + public int getAttributeCount() { + ResXmlStartElement startElement=getStartElement(); + if(startElement!=null){ + return startElement.getResXmlAttributeArray().childesCount(); + } + return 0; + } + public ResXmlAttribute getAttributeAt(int index){ + ResXmlStartElement startElement=getStartElement(); + if(startElement!=null){ + return startElement.getResXmlAttributeArray().get(index); + } + return null; + } + public Collection listAttributes(){ + ResXmlStartElement startElement=getStartElement(); + if(startElement!=null){ + return startElement.listResXmlAttributes(); + } + return new ArrayList<>(); + } + public ResXmlStringPool getStringPool(){ + Block parent=getParent(); + while (parent!=null){ + if(parent instanceof ResXmlDocument){ + return ((ResXmlDocument)parent).getStringPool(); + } + if(parent instanceof ResXmlElement){ + return ((ResXmlElement)parent).getStringPool(); + } + parent=parent.getParent(); + } + return null; + } + public ResXmlIDMap getResXmlIDMap(){ + ResXmlDocument resXmlDocument = getParentDocument(); + if(resXmlDocument!=null){ + return resXmlDocument.getResXmlIDMap(); + } + return null; + } + public ResXmlDocument getParentDocument(){ + return getParentInstance(ResXmlDocument.class); + } - @Override - public int getDepth(){ - int depth = 0; - ResXmlElement parent = getParentResXmlElement(); - while (parent!=null){ - depth++; - parent = parent.getParentResXmlElement(); - } - return depth; - } - @Override - void addEvents(ParserEventList parserEventList){ - String comment = getStartComment(); - if(comment!=null){ - parserEventList.add( - new ParserEvent(ParserEvent.COMMENT, this, comment, false)); - } - parserEventList.add(new ParserEvent(ParserEvent.START_TAG, this)); - for(ResXmlNode xmlNode:getXmlNodes()){ - xmlNode.addEvents(parserEventList); - } - comment = getEndComment(); - if(comment!=null){ - parserEventList.add( - new ParserEvent(ParserEvent.COMMENT, this, comment, true)); - } - parserEventList.add(new ParserEvent(ParserEvent.END_TAG, this)); - } - public int getLevel(){ - return mLevel; - } - private void setLevel(int level){ - mLevel = level; - } - public void addElement(ResXmlElement element){ - mBody.add(element); - } - public boolean removeAttribute(ResXmlAttribute resXmlAttribute){ - if(resXmlAttribute != null){ - resXmlAttribute.onRemoved(); - } - return getStartElement().getResXmlAttributeArray().remove(resXmlAttribute); - } - public boolean removeElement(ResXmlElement element){ - if(element !=null && element.getParent()!=null){ - element.onRemoved(); - } - return mBody.remove(element); - } - public boolean removeNode(ResXmlNode node){ - if(node instanceof ResXmlElement){ - return removeElement((ResXmlElement) node); - } - return mBody.remove(node); - } - public int countElements(){ - int result = 0; - for(ResXmlNode xmlNode: getXmlNodes()){ - if(xmlNode instanceof ResXmlElement){ - result++; - } - } - return result; - } - public void clearChildes(){ - List copyOfNodeList=new ArrayList<>(mBody.getChildes()); - for(ResXmlNode xmlNode:copyOfNodeList){ - if(xmlNode==null){ - continue; - } - xmlNode.onRemove(); - mBody.remove(xmlNode); - } - } - public ResXmlNode getResXmlNode(int position){ - return mBody.get(position); - } - public int countResXmlNodes(){ - return mBody.size(); - } - public List listXmlNodes(){ - return new ArrayList<>(getXmlNodes()); - } - private List getXmlNodes(){ - return mBody.getChildes(); - } - public List listXmlText(){ - List results=new ArrayList<>(); - for(ResXmlNode xmlNode: getXmlNodes()){ - if(xmlNode instanceof ResXmlTextNode){ - results.add(((ResXmlTextNode) xmlNode).getResXmlText()); - } - } - return results; - } - public List listXmlTextNodes(){ - List results=new ArrayList<>(); - for(ResXmlNode xmlNode: getXmlNodes()){ - if(xmlNode instanceof ResXmlTextNode){ - results.add((ResXmlTextNode) xmlNode); - } - } - return results; - } - public List listElements(){ - List results=new ArrayList<>(); - for(ResXmlNode xmlNode: getXmlNodes()){ - if(xmlNode instanceof ResXmlElement){ - results.add((ResXmlElement) xmlNode); - } - } - return results; - } - public List listElements(String name){ - List results=new ArrayList<>(); - if(name==null){ - return results; - } - for(ResXmlElement element:listElements()){ - if(name.equals(element.getTag())||name.equals(element.getTagName())){ - results.add(element); - } - } - return results; - } - public ResXmlElement getRootResXmlElement(){ - ResXmlElement parent=getParentResXmlElement(); - if(parent!=null){ - return parent.getRootResXmlElement(); - } - return this; - } - public ResXmlElement getParentResXmlElement(){ - return getParentInstance(ResXmlElement.class); - } - public ResXmlStartNamespace getStartNamespaceByUriRef(int uriRef){ - if(uriRef<0){ - return null; - } - for(ResXmlStartNamespace ns:mStartNamespaceList.getChildes()){ - if(uriRef==ns.getUriReference()){ - return ns; - } - } - ResXmlElement xmlElement=getParentResXmlElement(); - if(xmlElement!=null){ - return xmlElement.getStartNamespaceByUriRef(uriRef); - } - return null; - } - public ResXmlStartNamespace getOrCreateNamespace(String uri, String prefix){ - ResXmlStartNamespace exist=getStartNamespaceByUri(uri); - if(exist!=null){ - return exist; - } - ResXmlStartNamespace startNamespace=new ResXmlStartNamespace(); - ResXmlEndNamespace endNamespace=new ResXmlEndNamespace(); - startNamespace.setEnd(endNamespace); + @Override + public int getDepth(){ + int depth = 0; + ResXmlElement parent = getParentResXmlElement(); + while (parent!=null){ + depth++; + parent = parent.getParentResXmlElement(); + } + return depth; + } + @Override + void addEvents(ParserEventList parserEventList){ + String comment = getStartComment(); + if(comment!=null){ + parserEventList.add( + new ParserEvent(ParserEvent.COMMENT, this, comment, false)); + } + parserEventList.add(new ParserEvent(ParserEvent.START_TAG, this)); + for(ResXmlNode xmlNode:getXmlNodes()){ + xmlNode.addEvents(parserEventList); + } + comment = getEndComment(); + if(comment!=null){ + parserEventList.add( + new ParserEvent(ParserEvent.COMMENT, this, comment, true)); + } + parserEventList.add(new ParserEvent(ParserEvent.END_TAG, this)); + } + public int getLevel(){ + return mLevel; + } + private void setLevel(int level){ + mLevel = level; + } + public void addElement(ResXmlElement element){ + mBody.add(element); + } + public boolean removeAttribute(ResXmlAttribute resXmlAttribute){ + if(resXmlAttribute != null){ + resXmlAttribute.onRemoved(); + } + return getStartElement().getResXmlAttributeArray().remove(resXmlAttribute); + } + public boolean removeElement(ResXmlElement element){ + if(element !=null && element.getParent()!=null){ + element.onRemoved(); + } + return mBody.remove(element); + } + public boolean removeNode(ResXmlNode node){ + if(node instanceof ResXmlElement){ + return removeElement((ResXmlElement) node); + } + return mBody.remove(node); + } + public int countElements(){ + int result = 0; + for(ResXmlNode xmlNode: getXmlNodes()){ + if(xmlNode instanceof ResXmlElement){ + result++; + } + } + return result; + } + public void clearChildes(){ + List copyOfNodeList=new ArrayList<>(mBody.getChildes()); + for(ResXmlNode xmlNode:copyOfNodeList){ + if(xmlNode==null){ + continue; + } + xmlNode.onRemove(); + mBody.remove(xmlNode); + } + } + public ResXmlNode getResXmlNode(int position){ + return mBody.get(position); + } + public int countResXmlNodes(){ + return mBody.size(); + } + public List listXmlNodes(){ + return new ArrayList<>(getXmlNodes()); + } + private List getXmlNodes(){ + return mBody.getChildes(); + } + public List listXmlText(){ + List results=new ArrayList<>(); + for(ResXmlNode xmlNode: getXmlNodes()){ + if(xmlNode instanceof ResXmlTextNode){ + results.add(((ResXmlTextNode) xmlNode).getResXmlText()); + } + } + return results; + } + public List listXmlTextNodes(){ + List results=new ArrayList<>(); + for(ResXmlNode xmlNode: getXmlNodes()){ + if(xmlNode instanceof ResXmlTextNode){ + results.add((ResXmlTextNode) xmlNode); + } + } + return results; + } + public List listElements(){ + List results=new ArrayList<>(); + for(ResXmlNode xmlNode: getXmlNodes()){ + if(xmlNode instanceof ResXmlElement){ + results.add((ResXmlElement) xmlNode); + } + } + return results; + } + public List listElements(String name){ + List results=new ArrayList<>(); + if(name==null){ + return results; + } + for(ResXmlElement element:listElements()){ + if(name.equals(element.getTag())||name.equals(element.getTagName())){ + results.add(element); + } + } + return results; + } + public ResXmlElement getRootResXmlElement(){ + ResXmlElement parent=getParentResXmlElement(); + if(parent!=null){ + return parent.getRootResXmlElement(); + } + return this; + } + public ResXmlElement getParentResXmlElement(){ + return getParentInstance(ResXmlElement.class); + } + public ResXmlStartNamespace getStartNamespaceByUriRef(int uriRef){ + if(uriRef<0){ + return null; + } + for(ResXmlStartNamespace ns:mStartNamespaceList.getChildes()){ + if(uriRef==ns.getUriReference()){ + return ns; + } + } + ResXmlElement xmlElement=getParentResXmlElement(); + if(xmlElement!=null){ + return xmlElement.getStartNamespaceByUriRef(uriRef); + } + return null; + } + public ResXmlStartNamespace getOrCreateNamespace(String uri, String prefix){ + ResXmlStartNamespace exist=getStartNamespaceByUri(uri); + if(exist!=null){ + return exist; + } + ResXmlStartNamespace startNamespace=new ResXmlStartNamespace(); + ResXmlEndNamespace endNamespace=new ResXmlEndNamespace(); + startNamespace.setEnd(endNamespace); - addStartNamespace(startNamespace); - addEndNamespace(endNamespace); + addStartNamespace(startNamespace); + addEndNamespace(endNamespace); - startNamespace.setUri(uri); - startNamespace.setPrefix(prefix); + startNamespace.setUri(uri); + startNamespace.setPrefix(prefix); - return startNamespace; - } - public ResXmlStartNamespace getStartNamespaceByUri(String uri){ - if(uri==null){ - return null; - } - for(ResXmlStartNamespace ns:mStartNamespaceList.getChildes()){ - if(uri.equals(ns.getUri())){ - return ns; - } - } - ResXmlElement xmlElement=getParentResXmlElement(); - if(xmlElement!=null){ - return xmlElement.getStartNamespaceByUri(uri); - } - return null; - } - public ResXmlStartNamespace getStartNamespaceByPrefix(String prefix){ - if(prefix==null){ - return null; - } - for(ResXmlStartNamespace ns:mStartNamespaceList.getChildes()){ - if(prefix.equals(ns.getPrefix())){ - return ns; - } - } - ResXmlElement xmlElement=getParentResXmlElement(); - if(xmlElement!=null){ - return xmlElement.getStartNamespaceByPrefix(prefix); - } - return null; - } - public List getStartNamespaceList(){ - return mStartNamespaceList.getChildes(); - } - public void addStartNamespace(ResXmlStartNamespace item){ - mStartNamespaceList.add(item); - } - public List getEndNamespaceList(){ - return mEndNamespaceList.getChildes(); - } - public void addEndNamespace(ResXmlEndNamespace item){ - mEndNamespaceList.add(item); - } + return startNamespace; + } + public ResXmlStartNamespace getStartNamespaceByUri(String uri){ + if(uri==null){ + return null; + } + for(ResXmlStartNamespace ns:mStartNamespaceList.getChildes()){ + if(uri.equals(ns.getUri())){ + return ns; + } + } + ResXmlElement xmlElement=getParentResXmlElement(); + if(xmlElement!=null){ + return xmlElement.getStartNamespaceByUri(uri); + } + return null; + } + public ResXmlStartNamespace getStartNamespaceByPrefix(String prefix){ + if(prefix==null){ + return null; + } + for(ResXmlStartNamespace ns:mStartNamespaceList.getChildes()){ + if(prefix.equals(ns.getPrefix())){ + return ns; + } + } + ResXmlElement xmlElement=getParentResXmlElement(); + if(xmlElement!=null){ + return xmlElement.getStartNamespaceByPrefix(prefix); + } + return null; + } + public List getStartNamespaceList(){ + return mStartNamespaceList.getChildes(); + } + public int getNamespaceCount(){ + return mStartNamespaceList.size(); + } + public ResXmlStartNamespace getNamespace(int index){ + return mStartNamespaceList.get(index); + } + public void addStartNamespace(ResXmlStartNamespace item){ + mStartNamespaceList.add(item); + } + public List getEndNamespaceList(){ + return mEndNamespaceList.getChildes(); + } + public void addEndNamespace(ResXmlEndNamespace item){ + mEndNamespaceList.add(item); + } + void removeNamespace(ResXmlStartNamespace startNamespace){ + if(startNamespace == null){ + return; + } + startNamespace.onRemoved(); + mStartNamespaceList.remove(startNamespace); + mEndNamespaceList.remove(startNamespace.getEnd()); + } - ResXmlStartElement newStartElement(int lineNo){ - ResXmlStartElement startElement=new ResXmlStartElement(); - setStartElement(startElement); + ResXmlStartElement newStartElement(int lineNo){ + ResXmlStartElement startElement=new ResXmlStartElement(); + setStartElement(startElement); - ResXmlEndElement endElement=new ResXmlEndElement(); - startElement.setResXmlEndElement(endElement); + ResXmlEndElement endElement=new ResXmlEndElement(); + startElement.setResXmlEndElement(endElement); - setEndElement(endElement); - endElement.setResXmlStartElement(startElement); + setEndElement(endElement); + endElement.setResXmlStartElement(startElement); - startElement.setLineNumber(lineNo); - endElement.setLineNumber(lineNo); + startElement.setLineNumber(lineNo); + endElement.setLineNumber(lineNo); - return startElement; - } + return startElement; + } - public ResXmlStartElement getStartElement(){ - return mStartElementContainer.getItem(); - } - public void setStartElement(ResXmlStartElement item){ - mStartElementContainer.setItem(item); - } + public ResXmlStartElement getStartElement(){ + return mStartElementContainer.getItem(); + } + public void setStartElement(ResXmlStartElement item){ + mStartElementContainer.setItem(item); + } - public ResXmlEndElement getEndElement(){ - return mEndElementContainer.getItem(); - } - public void setEndElement(ResXmlEndElement item){ - mEndElementContainer.setItem(item); - } + public ResXmlEndElement getEndElement(){ + return mEndElementContainer.getItem(); + } + public void setEndElement(ResXmlEndElement item){ + mEndElementContainer.setItem(item); + } - // Use listXmlText() instead to be removed on next version - @Deprecated - public ResXmlText getResXmlText(){ - List 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); - } + // Use listXmlText() instead to be removed on next version + @Deprecated + public ResXmlText getResXmlText(){ + List 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(){ - return isElementBalanced() && isNamespaceBalanced(); - } - private boolean isNamespaceBalanced(){ - return (mStartNamespaceList.size()==mEndNamespaceList.size()); - } - private boolean isElementBalanced(){ - return (hasStartElement() && hasEndElement()); - } - private boolean hasStartElement(){ - return mStartElementContainer.hasItem(); - } - private boolean hasEndElement(){ - return mEndElementContainer.hasItem(); - } + private boolean isBalanced(){ + return isElementBalanced() && isNamespaceBalanced(); + } + private boolean isNamespaceBalanced(){ + return (mStartNamespaceList.size()==mEndNamespaceList.size()); + } + private boolean isElementBalanced(){ + return (hasStartElement() && hasEndElement()); + } + private boolean hasStartElement(){ + return mStartElementContainer.hasItem(); + } + private boolean hasEndElement(){ + return mEndElementContainer.hasItem(); + } - private void linkStartEnd(){ - linkStartEndElement(); - linkStartEndNameSpaces(); - } - private void linkStartEndElement(){ - ResXmlStartElement start=getStartElement(); - ResXmlEndElement end=getEndElement(); - if(start==null || end==null){ - return; - } - start.setResXmlEndElement(end); - end.setResXmlStartElement(start); - } - private void ensureStartEndElement(){ - ResXmlStartElement start=getStartElement(); - ResXmlEndElement end=getEndElement(); - if(start!=null && end!=null){ - return; - } - if(start==null){ - start=new ResXmlStartElement(); - setStartElement(start); - } - if(end==null){ - end=new ResXmlEndElement(); - setEndElement(end); - } - linkStartEndElement(); - } - private void linkStartEndNameSpaces(){ - if(!isNamespaceBalanced()){ - return; - } - int max=mStartNamespaceList.size(); - for(int i=0;i0 && getLevel()==0){ - onFinishedUnexpected(reader); - return; - } - onFinishedSuccess(reader, headerBlock); - } - private void onFinishedSuccess(BlockReader reader, HeaderBlock headerBlock) throws IOException{ + @Override + public void onReadBytes(BlockReader reader) throws IOException { + int pos = reader.getPosition(); + while (readNext(reader)){ + if(pos==reader.getPosition()){ + break; + } + pos=reader.getPosition(); + } + } + private boolean readNext(BlockReader reader) throws IOException { + int pos = reader.getPosition(); + if(isBalanced()){ + return false; + } + HeaderBlock headerBlock=reader.readHeaderBlock(); + if(headerBlock==null){ + return false; + } + ChunkType chunkType=headerBlock.getChunkType(); + if(chunkType==null){ + unknownChunk(reader, headerBlock); + return false; + } + if(chunkType==ChunkType.XML_START_ELEMENT){ + onStartElement(reader); + }else if(chunkType==ChunkType.XML_END_ELEMENT){ + onEndElement(reader); + }else if(chunkType==ChunkType.XML_START_NAMESPACE){ + onStartNamespace(reader); + }else if(chunkType==ChunkType.XML_END_NAMESPACE){ + onEndNamespace(reader); + }else if(chunkType==ChunkType.XML_CDATA){ + onXmlText(reader); + }else{ + unexpectedChunk(reader, headerBlock); + } + if(!isBalanced()){ + if(!reader.isAvailable()){ + unBalancedFinish(reader); + }else if(pos!=reader.getPosition()){ + return true; + } + } + linkStartEnd(); + onFinishedRead(reader, headerBlock); + return false; + } + private void onFinishedRead(BlockReader reader, HeaderBlock headerBlock) throws IOException{ + int avail=reader.available(); + if(avail>0 && getLevel()==0){ + onFinishedUnexpected(reader); + return; + } + onFinishedSuccess(reader, headerBlock); + } + private void onFinishedSuccess(BlockReader reader, HeaderBlock headerBlock) throws IOException{ - } - private void onFinishedUnexpected(BlockReader reader) throws IOException{ - StringBuilder builder=new StringBuilder(); - builder.append("Unexpected finish reading: reader=").append(reader.toString()); - HeaderBlock header = reader.readHeaderBlock(); - if(header!=null){ - builder.append(", next header="); - builder.append(header.toString()); - } - throw new IOException(builder.toString()); - } - private void onStartElement(BlockReader reader) throws IOException{ - if(hasStartElement()){ - ResXmlElement childElement=new ResXmlElement(); - addElement(childElement); - childElement.setLevel(getLevel()+1); - childElement.readBytes(reader); - }else{ - ResXmlStartElement startElement=new ResXmlStartElement(); - setStartElement(startElement); - startElement.readBytes(reader); - } - } - private void onEndElement(BlockReader reader) throws IOException{ - if(hasEndElement()){ - multipleEndElement(reader); - return; - } - ResXmlEndElement endElement=new ResXmlEndElement(); - setEndElement(endElement); - endElement.readBytes(reader); - } - private void onStartNamespace(BlockReader reader) throws IOException{ - ResXmlStartNamespace startNamespace=new ResXmlStartNamespace(); - addStartNamespace(startNamespace); - startNamespace.readBytes(reader); - } - private void onEndNamespace(BlockReader reader) throws IOException{ - ResXmlEndNamespace endNamespace=new ResXmlEndNamespace(); - addEndNamespace(endNamespace); - endNamespace.readBytes(reader); - } - private void onXmlText(BlockReader reader) throws IOException{ - ResXmlText xmlText=new ResXmlText(); - addResXmlText(xmlText); - xmlText.readBytes(reader); - } + } + private void onFinishedUnexpected(BlockReader reader) throws IOException{ + StringBuilder builder=new StringBuilder(); + builder.append("Unexpected finish reading: reader=").append(reader.toString()); + HeaderBlock header = reader.readHeaderBlock(); + if(header!=null){ + builder.append(", next header="); + builder.append(header.toString()); + } + throw new IOException(builder.toString()); + } + private void onStartElement(BlockReader reader) throws IOException{ + if(hasStartElement()){ + ResXmlElement childElement=new ResXmlElement(); + addElement(childElement); + childElement.setLevel(getLevel()+1); + childElement.readBytes(reader); + }else{ + ResXmlStartElement startElement=new ResXmlStartElement(); + setStartElement(startElement); + startElement.readBytes(reader); + } + } + private void onEndElement(BlockReader reader) throws IOException{ + if(hasEndElement()){ + multipleEndElement(reader); + return; + } + ResXmlEndElement endElement=new ResXmlEndElement(); + setEndElement(endElement); + endElement.readBytes(reader); + } + private void onStartNamespace(BlockReader reader) throws IOException{ + ResXmlStartNamespace startNamespace=new ResXmlStartNamespace(); + addStartNamespace(startNamespace); + startNamespace.readBytes(reader); + } + private void onEndNamespace(BlockReader reader) throws IOException{ + ResXmlEndNamespace endNamespace=new ResXmlEndNamespace(); + addEndNamespace(endNamespace); + endNamespace.readBytes(reader); + } + private void onXmlText(BlockReader reader) throws IOException{ + ResXmlText xmlText=new ResXmlText(); + addResXmlText(xmlText); + xmlText.readBytes(reader); + } - private void unknownChunk(BlockReader reader, HeaderBlock headerBlock) throws IOException{ - throw new IOException("Unknown chunk: "+headerBlock.toString()); - } - private void multipleEndElement(BlockReader reader) throws IOException{ - throw new IOException("Multiple end element: "+reader.toString()); - } - private void unexpectedChunk(BlockReader reader, HeaderBlock headerBlock) throws IOException{ - throw new IOException("Unexpected chunk: "+headerBlock.toString()); - } - private void unBalancedFinish(BlockReader reader) throws IOException{ - if(!isNamespaceBalanced()){ - throw new IOException("Unbalanced namespace: start=" - +mStartNamespaceList.size()+", end="+mEndNamespaceList.size()); - } + private void unknownChunk(BlockReader reader, HeaderBlock headerBlock) throws IOException{ + throw new IOException("Unknown chunk: "+headerBlock.toString()); + } + private void multipleEndElement(BlockReader reader) throws IOException{ + throw new IOException("Multiple end element: "+reader.toString()); + } + private void unexpectedChunk(BlockReader reader, HeaderBlock headerBlock) throws IOException{ + throw new IOException("Unexpected chunk: "+headerBlock.toString()); + } + private void unBalancedFinish(BlockReader reader) throws IOException{ + if(!isNamespaceBalanced()){ + throw new IOException("Unbalanced namespace: start=" + +mStartNamespaceList.size()+", end="+mEndNamespaceList.size()); + } - if(!isElementBalanced()){ - // Should not happen unless corrupted file, auto corrected above - StringBuilder builder=new StringBuilder(); - builder.append("Unbalanced element: start="); - ResXmlStartElement startElement=getStartElement(); - if(startElement!=null){ - builder.append(startElement); - }else { - builder.append("null"); - } - builder.append(", end="); - ResXmlEndElement endElement=getEndElement(); - if(endElement!=null){ - builder.append(endElement); - }else { - builder.append("null"); - } - throw new IOException(builder.toString()); - } - } - @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; - JSONArray nsList=new JSONArray(); - for(ResXmlStartNamespace namespace:getStartNamespaceList()){ - JSONObject ns=new JSONObject(); - ns.put(NAME_namespace_uri, namespace.getUri()); - ns.put(NAME_namespace_prefix, namespace.getPrefix()); - nsList.put(i, ns); - i++; - } - if(i>0){ - jsonObject.put(NAME_namespaces, nsList); - } - jsonObject.put(NAME_name, start.getName()); - String comment=start.getComment(); - if(comment!=null){ - jsonObject.put(NAME_comment, comment); - } - String uri=start.getUri(); - if(uri!=null){ - jsonObject.put(NAME_namespace_uri, uri); - } - JSONArray attrArray=start.getResXmlAttributeArray().toJson(); - jsonObject.put(NAME_attributes, attrArray); - i=0; - JSONArray childes=new JSONArray(); - for(ResXmlNode xmlNode: getXmlNodes()){ - childes.put(i, xmlNode.toJson()); - i++; - } - if(i>0){ - jsonObject.put(NAME_childes, childes); - } - return jsonObject; - } - @Override - public void fromJson(JSONObject json) { - ResXmlStartElement start = getStartElement(); - int lineNo=json.optInt(NAME_line, 1); - if(start==null){ - start = newStartElement(lineNo); - }else { - start.setLineNumber(lineNo); - } - JSONArray nsArray = json.optJSONArray(NAME_namespaces); - if(nsArray!=null){ - int length=nsArray.length(); - for(int i=0;i0){ + jsonObject.put(NAME_namespaces, nsList); + } + jsonObject.put(NAME_name, start.getName()); + String comment=start.getComment(); + if(comment!=null){ + jsonObject.put(NAME_comment, comment); + } + String uri=start.getUri(); + if(uri!=null){ + jsonObject.put(NAME_namespace_uri, uri); + } + JSONArray attrArray=start.getResXmlAttributeArray().toJson(); + jsonObject.put(NAME_attributes, attrArray); + i=0; + JSONArray childes=new JSONArray(); + for(ResXmlNode xmlNode: getXmlNodes()){ + childes.put(i, xmlNode.toJson()); + i++; + } + if(i>0){ + jsonObject.put(NAME_childes, childes); + } + return jsonObject; + } + @Override + public void fromJson(JSONObject json) { + ResXmlStartElement start = getStartElement(); + int lineNo=json.optInt(NAME_line, 1); + if(start==null){ + start = newStartElement(lineNo); + }else { + start.setLineNumber(lineNo); + } + JSONArray nsArray = json.optJSONArray(NAME_namespaces); + if(nsArray!=null){ + int length=nsArray.length(); + for(int i=0;i"); - builder.append(text.toString()); - builder.append(""); - }else { - builder.append("/>"); - } - return builder.toString(); - } - return "NULL"; - } - static ResXmlElement newResXmlElement(String tag){ - ResXmlElement resXmlElement=new ResXmlElement(); - ResXmlStartElement startElement=new ResXmlStartElement(); - resXmlElement.setStartElement(startElement); - ResXmlEndElement endElement=new ResXmlEndElement(); - resXmlElement.setEndElement(endElement); - resXmlElement.setTag(tag); - return resXmlElement; - } + /** + * Decodes binary {@link ResXmlElement} to readable {@link XMLElement} + * @param entryStore : used for decoding attribute name and values + * @param currentPackageId : is id of current package defining this xml, used for + * decoding reference names e.g @{package.name}:string/entry_name + * */ + 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()); + } + for(ResXmlAttribute resXmlAttribute:listAttributes()){ + XMLAttribute xmlAttribute = + resXmlAttribute.decodeToXml(entryStore, currentPackageId); + xmlElement.addAttribute(xmlAttribute); + } + String comment=getStartComment(); + if(comment!=null){ + xmlElement.addComment(new XMLComment(comment)); + } + comment=getEndComment(); + if(comment!=null){ + xmlElement.addComment(new XMLComment(comment)); + } + for(ResXmlNode xmlNode: getXmlNodes()){ + 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; + } + @Override + public int compare(ResXmlNode node1, ResXmlNode node2) { + return Integer.compare(node1.getIndex(), node2.getIndex()); + } + @Override + public String toString(){ + ResXmlStartElement start = getStartElement(); + if(start!=null){ + ResXmlText text=getResXmlText(); + StringBuilder builder=new StringBuilder(); + builder.append("<"); + builder.append(start.toString()); + if(text!=null){ + builder.append(">"); + builder.append(text.toString()); + builder.append(""); + }else { + builder.append("/>"); + } + return builder.toString(); + } + return "NULL"; + } + static ResXmlElement newResXmlElement(String tag){ + ResXmlElement resXmlElement=new ResXmlElement(); + ResXmlStartElement startElement=new ResXmlStartElement(); + resXmlElement.setStartElement(startElement); + ResXmlEndElement endElement=new ResXmlEndElement(); + resXmlElement.setEndElement(endElement); + resXmlElement.setTag(tag); + return resXmlElement; + } - 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_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"; - static final String NAME_namespaces = "namespaces"; - static final String NAME_namespace_uri = "namespace_uri"; - static final String NAME_namespace_prefix = "namespace_prefix"; - private static final String NAME_line = "line"; - static final String NAME_attributes = "attributes"; - static final String NAME_childes = "childes"; - } + static final String NAME_element = "element"; + static final String NAME_name = "name"; + static final String NAME_comment = "comment"; + static final String NAME_text = "text"; + static final String NAME_namespaces = "namespaces"; + static final String NAME_namespace_uri = "namespace_uri"; + static final String NAME_namespace_prefix = "namespace_prefix"; + private static final String NAME_line = "line"; + static final String NAME_attributes = "attributes"; + static final String NAME_childes = "childes"; +} diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlPullParser.java b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlPullParser.java index 0731cef..439bf7f 100644 --- a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlPullParser.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlPullParser.java @@ -1,4 +1,4 @@ - /* +/* * Copyright (C) 2022 github.com/REAndroid * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -34,9 +34,13 @@ public class ResXmlPullParser implements XmlResourceParser { private ResXmlDocument mDocument; private boolean mDocumentCreatedHere; private DocumentLoadedListener documentLoadedListener; + private boolean processNamespaces; + private boolean reportNamespaceAttrs; public ResXmlPullParser(Decoder decoder){ this.mDecoder = decoder; + this.processNamespaces = true; + this.reportNamespaceAttrs = true; } public ResXmlPullParser(){ this(null); @@ -54,6 +58,7 @@ public class ResXmlPullParser implements XmlResourceParser { public synchronized void setResXmlDocument(ResXmlDocument xmlDocument){ closeDocument(); this.mDocument = xmlDocument; + initDefaultFeatures(); initializeDecoder(xmlDocument); xmlDocument.addEvents(mEventList); } @@ -109,17 +114,33 @@ public class ResXmlPullParser implements XmlResourceParser { @Override public int getAttributeCount() { ResXmlElement element = getCurrentElement(); - if(element!=null){ - return element.getAttributeCount(); + if(element == null){ + return 0; } - return 0; + int count = element.getAttributeCount(); + if(reportNamespaceAttrs){ + count += element.getNamespaceCount(); + } + return count; } @Override public String getAttributeName(int index) { + if(reportNamespaceAttrs){ + int nsCount = getNamespaceCountInternal(); + if(index < nsCount){ + return getNamespaceAttributeName(index); + } + } return decodeAttributeName(getResXmlAttributeAt(index)); } @Override public String getAttributeValue(int index) { + if(reportNamespaceAttrs){ + int nsCount = getNamespaceCountInternal(); + if(index < nsCount){ + return getNamespaceAttributeValue(index); + } + } return decodeAttributeValue(getResXmlAttributeAt(index)); } @Override @@ -324,9 +345,22 @@ public class ResXmlPullParser implements XmlResourceParser { @Override public void setFeature(String name, boolean state) throws XmlPullParserException { + if(FEATURE_PROCESS_NAMESPACES.equals(name)) { + processNamespaces = state; + }else if(FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name)) { + reportNamespaceAttrs = state; + }else { + throw new XmlPullParserException("Unsupported feature: " + name); + } } + @Override public boolean getFeature(String name) { + if(FEATURE_PROCESS_NAMESPACES.equals(name)) { + return processNamespaces; + }else if(FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name)) { + return reportNamespaceAttrs; + } return false; } @Override @@ -358,12 +392,15 @@ public class ResXmlPullParser implements XmlResourceParser { } @Override public int getNamespaceCount(int depth) throws XmlPullParserException { + if(reportNamespaceAttrs){ + return 0; + } ResXmlElement element = getCurrentElement(); while(element!=null && element.getDepth()>depth){ element=element.getParentResXmlElement(); } if(element!=null){ - return element.getStartNamespaceList().size(); + return element.getNamespaceCount(); } return 0; } @@ -371,7 +408,7 @@ public class ResXmlPullParser implements XmlResourceParser { public String getNamespacePrefix(int pos) throws XmlPullParserException { ResXmlElement element = getCurrentElement(); if(element!=null){ - return element.getStartNamespaceList().get(pos).getPrefix(); + return element.getNamespace(pos).getPrefix(); } return null; } @@ -379,7 +416,7 @@ public class ResXmlPullParser implements XmlResourceParser { public String getNamespaceUri(int pos) throws XmlPullParserException { ResXmlElement element = getCurrentElement(); if(element!=null){ - return element.getStartNamespaceList().get(pos).getUri(); + return element.getNamespace(pos).getUri(); } return null; } @@ -466,10 +503,13 @@ public class ResXmlPullParser implements XmlResourceParser { if(element!=null){ return element.countResXmlNodes() == 0 && element.getAttributeCount()==0; } - return false; + return true; } @Override public String getAttributeNamespace(int index) { + if(processNamespaces){ + return null; + } ResXmlAttribute attribute = getResXmlAttributeAt(index); if(attribute != null){ return attribute.getUri(); @@ -478,6 +518,9 @@ public class ResXmlPullParser implements XmlResourceParser { } @Override public String getAttributePrefix(int index) { + if(processNamespaces){ + return null; + } ResXmlAttribute attribute = getResXmlAttributeAt(index); if(attribute != null){ return attribute.getNamePrefix(); @@ -493,7 +536,7 @@ public class ResXmlPullParser implements XmlResourceParser { return false; } private String decodeAttributeName(ResXmlAttribute attribute){ - if(attribute==null){ + if(attribute == null){ return null; } String name; @@ -502,6 +545,9 @@ public class ResXmlPullParser implements XmlResourceParser { name = attribute.getName(); }else { name = mDecoder.decodeResourceName(attribute.getNameResourceID(), true); + if(processNamespaces){ + name = attribute.getNamePrefix() + ":" + name; + } } return name; } @@ -512,6 +558,7 @@ public class ResXmlPullParser implements XmlResourceParser { return mDecoder.decodeAttributeValue(attribute); } public ResXmlAttribute getResXmlAttributeAt(int index){ + index = getRealAttributeIndex(index); ResXmlElement element = getCurrentElement(); if(element == null){ return null; @@ -545,6 +592,33 @@ public class ResXmlPullParser implements XmlResourceParser { } return null; } + private int getRealAttributeIndex(int index){ + if(reportNamespaceAttrs){ + index = index - getNamespaceCountInternal(); + } + return index; + } + private int getNamespaceCountInternal(){ + ResXmlElement element = getCurrentElement(); + if(element != null){ + return element.getNamespaceCount(); + } + return 0; + } + private String getNamespaceAttributeName(int index){ + ResXmlStartNamespace namespace = getCurrentElement() + .getNamespace(index); + String prefix = namespace.getPrefix(); + if(processNamespaces){ + prefix = "xmlns:" + prefix; + } + return prefix; + } + private String getNamespaceAttributeValue(int index){ + ResXmlStartNamespace namespace = getCurrentElement() + .getNamespace(index); + return namespace.getUri(); + } @Override public int getEventType() throws XmlPullParserException { return mEventList.getType(); @@ -552,6 +626,10 @@ public class ResXmlPullParser implements XmlResourceParser { @Override public int next() throws XmlPullParserException, IOException { mEventList.next(); + int type = mEventList.getType(); + if(type == START_TAG){ + onStartTag(); + } return mEventList.getType(); } @Override @@ -629,6 +707,13 @@ public class ResXmlPullParser implements XmlResourceParser { this.mDocumentCreatedHere = true; } } + private void initDefaultFeatures(){ + processNamespaces = true; + reportNamespaceAttrs = true; + } + private void onStartTag(){ + + } public static interface DocumentLoadedListener{ public ResXmlDocument onDocumentLoaded(ResXmlDocument resXmlDocument); diff --git a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlStartNamespace.java b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlStartNamespace.java index eb5460f..3570537 100755 --- a/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlStartNamespace.java +++ b/src/main/java/com/reandroid/arsc/chunk/xml/ResXmlStartNamespace.java @@ -1,89 +1,90 @@ - /* - * 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; +/* + * 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.xml.SchemaAttr; - import com.reandroid.xml.XMLAttribute; +import com.reandroid.arsc.chunk.ChunkType; +import com.reandroid.xml.SchemaAttr; +import com.reandroid.xml.XMLAttribute; - import java.util.HashSet; - import java.util.Set; +import java.util.HashSet; +import java.util.Set; - public class ResXmlStartNamespace extends ResXmlNamespace { - private final Set mReferencedAttributes; - public ResXmlStartNamespace() { - super(ChunkType.XML_START_NAMESPACE); - this.mReferencedAttributes = new HashSet<>(); - } - public ResXmlEndNamespace getEnd(){ - return (ResXmlEndNamespace) getPair(); - } - public void setEnd(ResXmlEndNamespace namespace){ - setPair(namespace); - } - @Override - void linkStringReferences(){ - super.linkStringReferences(); - ResXmlEndNamespace end = getEnd(); - if(end!=null){ - end.linkStringReferences(); - } - } - @Override - void onRemoved(){ - ResXmlEndNamespace end = getEnd(); - if(end!=null){ - end.onRemoved(); - } - mReferencedAttributes.clear(); - } - public boolean hasReferencedAttributes(){ - return mReferencedAttributes.size()>0; - } - public void clearReferencedAttributes(){ - mReferencedAttributes.clear(); - } - public Set getReferencedAttributes(){ - return mReferencedAttributes; - } - void addAttributeReference(ResXmlAttribute attribute){ - if(attribute!=null){ - mReferencedAttributes.add(attribute); - } - } - void removeAttributeReference(ResXmlAttribute attribute){ - if(attribute!=null){ - mReferencedAttributes.remove(attribute); - } - } - public XMLAttribute decodeToXml(){ - String uri=getUri(); - String prefix=getPrefix(); - if(isEmpty(uri) || isEmpty(prefix)){ - return null; - } - SchemaAttr schemaAttr=new SchemaAttr(prefix, uri); - schemaAttr.setLineNumber(getLineNumber()); - return schemaAttr; - } - private boolean isEmpty(String txt){ - if(txt==null){ - return true; - } - txt=txt.trim(); - return txt.length()==0; - } - } +public class ResXmlStartNamespace extends ResXmlNamespace { + private final Set mReferencedAttributes; + + public ResXmlStartNamespace() { + super(ChunkType.XML_START_NAMESPACE); + this.mReferencedAttributes = new HashSet<>(); + } + public ResXmlEndNamespace getEnd(){ + return (ResXmlEndNamespace) getPair(); + } + public void setEnd(ResXmlEndNamespace namespace){ + setPair(namespace); + } + @Override + void linkStringReferences(){ + super.linkStringReferences(); + ResXmlEndNamespace end = getEnd(); + if(end!=null){ + end.linkStringReferences(); + } + } + @Override + void onRemoved(){ + ResXmlEndNamespace end = getEnd(); + if(end!=null){ + end.onRemoved(); + } + mReferencedAttributes.clear(); + } + public boolean hasReferencedAttributes(){ + return mReferencedAttributes.size()>0; + } + public void clearReferencedAttributes(){ + mReferencedAttributes.clear(); + } + public Set getReferencedAttributes(){ + return mReferencedAttributes; + } + void addAttributeReference(ResXmlAttribute attribute){ + if(attribute!=null){ + mReferencedAttributes.add(attribute); + } + } + void removeAttributeReference(ResXmlAttribute attribute){ + if(attribute!=null){ + mReferencedAttributes.remove(attribute); + } + } + public XMLAttribute decodeToXml(){ + String uri=getUri(); + String prefix=getPrefix(); + if(isEmpty(uri) || isEmpty(prefix)){ + return null; + } + SchemaAttr schemaAttr=new SchemaAttr(prefix, uri); + schemaAttr.setLineNumber(getLineNumber()); + return schemaAttr; + } + private boolean isEmpty(String txt){ + if(txt==null){ + return true; + } + txt=txt.trim(); + return txt.length()==0; + } +} diff --git a/src/main/java/com/reandroid/xml/XmlParserToSerializer.java b/src/main/java/com/reandroid/xml/XmlParserToSerializer.java index 8d1c36f..b4c3862 100644 --- a/src/main/java/com/reandroid/xml/XmlParserToSerializer.java +++ b/src/main/java/com/reandroid/xml/XmlParserToSerializer.java @@ -1,4 +1,4 @@ - /* +/* * Copyright (C) 2022 github.com/REAndroid * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,6 +16,7 @@ package com.reandroid.xml; import android.content.res.XmlResourceParser; +import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -24,10 +25,18 @@ import java.io.IOException; public class XmlParserToSerializer { private final XmlSerializer serializer; private final XmlResourceParser parser; + private boolean enableIndent; + public XmlParserToSerializer(XmlResourceParser parser, XmlSerializer serializer){ this.parser = parser; this.serializer = serializer; + this.enableIndent = true; } + + public void setEnableIndent(boolean enableIndent) { + this.enableIndent = enableIndent; + } + public void write() throws IOException, XmlPullParserException { XmlResourceParser parser = this.parser; int event = parser.next(); @@ -71,17 +80,24 @@ public class XmlParserToSerializer { private void onStartTag() throws IOException, XmlPullParserException { XmlResourceParser parser = this.parser; XmlSerializer serializer = this.serializer; - serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - int nsCount = parser.getNamespaceCount(parser.getDepth()); - for(int i=0; i