All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.reandroid.arsc.chunk.xml.ResXmlElement Maven / Gradle / Ivy

There is a newer version: 1.3.5
Show newest version
/*
 *  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.array.ResXmlAttributeArray;
import com.reandroid.arsc.base.BlockCounter;
import com.reandroid.arsc.chunk.ChunkType;
import com.reandroid.arsc.base.Block;
import com.reandroid.arsc.model.ResourceLibrary;
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.item.ResXmlString;
import com.reandroid.arsc.pool.ResXmlStringPool;
import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONArray;
import com.reandroid.json.JSONObject;
import com.reandroid.xml.*;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;

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 ResXmlAttribute getIdAttribute(){
        ResXmlStartElement startElement = getStartElement();
        if(startElement != null){
            return startElement.getIdAttribute();
        }
        return null;
    }
    public ResXmlAttribute getClassAttribute(){
        ResXmlStartElement startElement = getStartElement();
        if(startElement != null){
            return startElement.getClassAttribute();
        }
        return null;
    }
    public ResXmlAttribute getStyleAttribute(){
        ResXmlStartElement startElement = getStartElement();
        if(startElement != null){
            return startElement.getStyleAttribute();
        }
        return null;
    }
    public ResXmlNamespace getNamespaceAt(int i){
        return mStartNamespaceList.get(i);
    }
    public int getNamespaceCount(){
        return mStartNamespaceList.size();
    }
    public ResXmlNamespace getNamespace(String uri, String prefix){
        return getXmlStartNamespace(uri, prefix);
    }
    public ResXmlNamespace getOrCreateNamespace(String uri, String prefix){
        return getOrCreateXmlStartNamespace(uri, prefix);
    }
    public ResXmlNamespace newNamespace(String uri, String prefix){
        return createXmlStartNamespace(uri, prefix);
    }
    public ResXmlNamespace getNamespaceByUri(String uri){
        return getStartNamespaceByUri(uri);
    }
    public ResXmlNamespace getNamespaceByPrefix(String prefix){
        return getStartNamespaceByPrefix(prefix, null);
    }
    public ResXmlNamespace getOrCreateNamespaceByPrefix(String prefix){
        if(prefix == null || prefix.trim().length() == 0){
            return null;
        }
        ResXmlNamespace namespace = getNamespaceByPrefix(prefix);
        if(namespace != null){
            return namespace;
        }
        String uri;
        if(ResourceLibrary.PREFIX_ANDROID.equals(prefix)){
            uri = ResourceLibrary.URI_ANDROID;
        }else {
            uri = ResourceLibrary.URI_RES_AUTO;
        }
        return getOrCreateNamespace(uri, prefix);
    }
    public int autoSetAttributeNamespaces(){
        int changedCount = 0;
        for(ResXmlAttribute attribute : listAttributes()){
            boolean changed = attribute.autoSetNamespace();
            if(changed){
                changedCount ++;
            }
        }
        for(ResXmlNode child : getXmlNodes()){
            if(child instanceof ResXmlElement){
                changedCount += ((ResXmlElement)child).autoSetAttributeNamespaces();
            }
        }
        return changedCount;
    }
    public int autoSetAttributeNames(){
        int changedCount = 0;
        for(ResXmlAttribute attribute : listAttributes()){
            boolean changed = attribute.autoSetName();
            if(changed){
                changedCount ++;
            }
        }
        for(ResXmlNode child : getXmlNodes()){
            if(child instanceof ResXmlElement){
                changedCount += ((ResXmlElement)child).autoSetAttributeNames();
            }
        }
        return changedCount;
    }
    public void autoSetLineNumber(){
        int startLineNumber = calculateLineNumber(true);
        setStartLineNumber(startLineNumber);
        for(ResXmlStartNamespace startNamespace : getStartNamespaceList()){
            startNamespace.setLineNumber(startLineNumber);
        }
        for(ResXmlNode child : getXmlNodes()){
            child.autoSetLineNumber();
        }
        setEndLineNumber(calculateLineNumber(false));
    }
    public void clearNullNodes(){
        clearNullNodes(true);
    }
    private void clearNullNodes(boolean recursive){
        for(ResXmlNode node:listXmlNodes()){
            if(node.isNull()){
                removeNode(node);
            }
            if(!recursive || !(node instanceof ResXmlElement)){
                continue;
            }
            ((ResXmlElement)node).clearNullNodes(true);
        }
    }
    int removeUnusedNamespaces(){
        int count = 0;
        List nsList = new ArrayList<>(getStartNamespaceList());
        for(ResXmlStartNamespace ns : nsList){
            boolean removed = ns.removeIfNoReference();
            if(removed){
                count ++;
            }
        }
        for(ResXmlNode node : getXmlNodes()){
            if(node instanceof ResXmlElement){
                ResXmlElement child = (ResXmlElement) node;
                count += child.removeUnusedNamespaces();
            }
        }
        return count;
    }
    public int removeUndefinedAttributes(){
        int count = 0;
        ResXmlStartElement start = getStartElement();
        if(start != null){
            count += start.removeUndefinedAttributes();
        }
        for(ResXmlNode xmlNode : getXmlNodes()){
            if(xmlNode instanceof ResXmlElement){
                count += ((ResXmlElement)xmlNode).removeUndefinedAttributes();
            }
        }
        return count;
    }
    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;
    }
    String getEndComment(){
        ResXmlEndElement end = getEndElement();
        if(end!=null){
            return end.getComment();
        }
        return null;
    }
    private int calculateLineNumber(boolean startLine){
        ResXmlElement root = getRootResXmlElement();
        BlockCounter counter = new BlockCounter(this);
        root.calculateLineNumber(counter, startLine);
        return counter.getCount();
    }
    @Override
    void calculateLineNumber(BlockCounter counter, boolean startLine){
        if(counter.FOUND){
            return;
        }
        counter.addCount(1);
        if(counter.END == this && startLine){
            counter.FOUND = true;
            return;
        }
        int attrCount = getAttributeCount() + getNamespaceCount();
        if(attrCount != 0){
            counter.addCount(attrCount - 1);
        }
        boolean haveElement = false;
        for(ResXmlNode xmlNode : getXmlNodes()){
            if(counter.FOUND){
                return;
            }
            xmlNode.calculateLineNumber(counter, startLine);
            if(xmlNode instanceof ResXmlElement){
                haveElement = true;
            }
        }
        if(haveElement){
            counter.addCount(1);
        }
        if(counter.END == this){
            counter.FOUND = true;
        }
    }
    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 setStartLineNumber(int lineNumber){
        ResXmlStartElement start = getStartElement();
        if(start!=null){
            start.setLineNumber(lineNumber);
        }
    }
    public void setEndLineNumber(int lineNumber){
        ResXmlEndElement end = getEndElement();
        if(end != null){
            end.setLineNumber(lineNumber);
        }
    }
    public void setComment(String comment){
        getStartElement().setComment(comment);
    }
    void calculatePositions(){
        ResXmlStartElement start = getStartElement();
        if(start!=null){
            start.calculatePositions();
        }
    }
    public ResXmlAttribute newAttribute(){
        return getStartElement().newAttribute();
    }
    @Override
    void onRemoved(){
        for(ResXmlStartNamespace startNamespace:getStartNamespaceList()){
            startNamespace.onRemoved();
        }
        ResXmlStartElement start = getStartElement();
        if(start != null){
            start.onRemoved();
        }
        for(ResXmlNode xmlNode : listXmlNodes()){
            xmlNode.onRemoved();
        }
    }
    @Override
    void linkStringReferences(){
        for(ResXmlStartNamespace startNamespace:getStartNamespaceList()){
            startNamespace.linkStringReferences();
        }
        ResXmlStartElement start = getStartElement();
        if(start != null){
            start.linkStringReferences();
        }
        for(ResXmlNode xmlNode : getXmlNodes()){
            xmlNode.linkStringReferences();
        }
    }
    public ResXmlElement createChildElement(){
        return createChildElement(null);
    }
    public ResXmlElement createChildElement(String name){
        int lineNo = getStartElement().getLineNumber()+1;
        ResXmlElement resXmlElement=new ResXmlElement();
        resXmlElement.newStartElement(lineNo);

        addElement(resXmlElement);

        if(name != null){
            resXmlElement.setName(name);
        }
        return resXmlElement;
    }
    public ResXmlAttribute getOrCreateAndroidAttribute(String name, int resourceId){
        return getOrCreateAttribute(
                ResourceLibrary.URI_ANDROID,
                ResourceLibrary.PREFIX_ANDROID,
                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 && resourceId != 0){
                attribute.setNamespace(uri, prefix);
            }
        }
        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 = getOrCreateXmlStartNamespace(ResourceLibrary.URI_ANDROID,
                ResourceLibrary.PREFIX_ANDROID);
        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.getName())
                    || name.equals(child.getName(true))){
                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 String getName(){
        return getName(false);
    }
    public String getName(boolean includePrefix){
        ResXmlStartElement startElement = getStartElement();
        if(startElement != null){
            return startElement.getTagName(includePrefix);
        }
        return null;
    }
    public void setName(String uri, String prefix, String name){
        setName(name);
        setTagNamespace(uri, prefix);
    }
    public void setName(String name){
        ResXmlStringPool stringPool = getStringPool();
        if(stringPool == null){
            return;
        }
        ensureStartEndElement();
        ResXmlStartElement startElement = getStartElement();
        if(name == null){
            startElement.setName(null);
            return;
        }
        String prefix = null;
        int i = name.lastIndexOf(':');
        if(i >= 0){
            prefix = name.substring(0, i);
            i++;
            name = name.substring(i);
        }
        startElement.setName(name);
        if(prefix == null){
            return;
        }
        ResXmlNamespace namespace = getOrCreateNamespaceByPrefix(prefix);
        if(namespace != null){
            startElement.setNamespaceReference(namespace.getUriReference());
        }
    }
    public String getUri(){
        ResXmlStartElement startElement = getStartElement();
        if(startElement != null){
            return startElement.getUri();
        }
        return null;
    }
    public String getPrefix(){
        ResXmlStartElement startElement = getStartElement();
        if(startElement != null){
            return startElement.getPrefix();
        }
        return null;
    }
    /**
     * Use setName(String)
     * */
    @Deprecated
    public void setTag(String tag){
        setName(tag);
    }
    /**
     * Use getName(true)
     * */
    @Deprecated
    public String getTagName(){
        return getName(true);
    }
    /**
     * Use getName()
     * */
    @Deprecated
    public String getTag(){
        return getName();
    }
    /**
     * Use getUri()
     * */
    @Deprecated
    public String getTagUri(){
        return getUri();
    }
    /**
     * Use getPrefix()
     * */
    @Deprecated
    public String getTagPrefix(){
        return getPrefix();
    }
    public void setTagNamespace(String uri, String prefix){
        ResXmlStartElement startElement = getStartElement();
        if(startElement != null){
            startElement.setTagNamespace(uri, prefix);
        }
    }
    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(){
        ResXmlElement parent = getParentResXmlElement();
        if(parent != null){
            return parent.getDepth() + 1;
        }
        return 0;
    }
    @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(){
        ResXmlNode[] copyOfNodeList=mBody.getChildes().toArray(new ResXmlNode[0]);
        for(ResXmlNode xmlNode:copyOfNodeList){
            if(xmlNode==null){
                continue;
            }
            xmlNode.onRemoved();
            mBody.remove(xmlNode);
        }
    }
    public ResXmlNode getResXmlNode(int position){
        return mBody.get(position);
    }
    public int countResXmlNodes(){
        return mBody.size();
    }
    public boolean hasText(){
        for(ResXmlNode xmlNode : getXmlNodes()){
            if(xmlNode instanceof ResXmlTextNode){
                return true;
            }
        }
        return false;
    }
    public boolean hasElement(){
        for(ResXmlNode xmlNode : getXmlNodes()){
            if(xmlNode instanceof ResXmlElement){
                return true;
            }
        }
        return false;
    }
    public List listXmlNodes(){
        return new ArrayList<>(getXmlNodes());
    }
    private List getXmlNodes(){
        return mBody.getChildes();
    }
    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.getName(false))
                    || name.equals(element.getName(true))){
                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);
    }
    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;
    }
    ResXmlStartNamespace getXmlStartNamespace(String uri, String prefix){
        if(uri == null || prefix == null){
            return null;
        }
        for(ResXmlStartNamespace ns : mStartNamespaceList.getChildes()){
            if(uri.equals(ns.getUri()) && prefix.equals(ns.getPrefix())){
                return ns;
            }
        }
        ResXmlElement xmlElement = getParentResXmlElement();
        if(xmlElement != null){
            return xmlElement.getXmlStartNamespace(uri, prefix);
        }
        return null;
    }
    private ResXmlStartNamespace getOrCreateXmlStartNamespace(String uri, String prefix){
        ResXmlStartNamespace exist = getXmlStartNamespace(uri, prefix);
        if(exist != null){
            return exist;
        }
        return getRootResXmlElement().createXmlStartNamespace(uri, prefix);
    }
    private ResXmlStartNamespace createXmlStartNamespace(String uri, String prefix){
        ResXmlStartNamespace startNamespace = new ResXmlStartNamespace();
        ResXmlEndNamespace endNamespace = new ResXmlEndNamespace();
        startNamespace.setEnd(endNamespace);

        addStartNamespace(startNamespace);
        addEndNamespace(endNamespace, true);
        ResXmlStringPool stringPool = getStringPool();
        ResXmlString xmlString = stringPool.createNew(uri);
        startNamespace.setUriReference(xmlString.getIndex());
        startNamespace.setPrefix(prefix);

        return startNamespace;
    }
    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;
    }
    private ResXmlStartNamespace getStartNamespaceByPrefix(String prefix, ResXmlStartNamespace result){
        if(prefix == null){
            return result;
        }
        for(ResXmlStartNamespace ns:getStartNamespaceList()){
            if(!prefix.equals(ns.getPrefix())){
                continue;
            }
            String uri = ns.getUri();
            if(uri != null && uri.length() != 0){
                return ns;
            }
            result = ns;
        }
        ResXmlElement xmlElement = getParentResXmlElement();
        if(xmlElement != null){
            return xmlElement.getStartNamespaceByPrefix(prefix, result);
        }
        return result;
    }
    private List getStartNamespaceList(){
        return mStartNamespaceList.getChildes();
    }
    private void addStartNamespace(ResXmlStartNamespace item){
        mStartNamespaceList.add(item);
    }
    private void addEndNamespace(ResXmlEndNamespace item, boolean at_first){
        if(at_first){
            mEndNamespaceList.add(0, item);
        }else {
            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);

        ResXmlEndElement endElement=new ResXmlEndElement();
        startElement.setResXmlEndElement(endElement);

        setEndElement(endElement);
        endElement.setResXmlStartElement(startElement);

        startElement.setLineNumber(lineNo);
        endElement.setLineNumber(lineNo);

        return startElement;
    }
    private ResXmlAttributeArray getAttributeArray(){
        ResXmlStartElement startElement = getStartElement();
        if(startElement != null){
            return startElement.getResXmlAttributeArray();
        }
        return null;
    }

    private ResXmlStartElement getStartElement(){
        return mStartElementContainer.getItem();
    }
    private void setStartElement(ResXmlStartElement item){
        mStartElementContainer.setItem(item);
    }

    private ResXmlEndElement getEndElement(){
        return mEndElementContainer.getItem();
    }
    private void setEndElement(ResXmlEndElement item){
        mEndElementContainer.setItem(item);
    }

    private void addResXmlTextNode(ResXmlTextNode xmlTextNode){
        mBody.add(xmlTextNode);
    }
    public void addResXmlText(ResXmlText xmlText){
        if(xmlText!=null){
            addResXmlTextNode(new ResXmlTextNode(xmlText));
        }
    }
    public void addResXmlText(String text){
        if(text==null){
            return;
        }
        ResXmlTextNode xmlTextNode = createResXmlText();
        xmlTextNode.setText(text);
    }
    private ResXmlTextNode getOrCreateResXmlText(){
        ResXmlNode last = getResXmlNode(countResXmlNodes() - 1);
        if(last instanceof ResXmlTextNode){
            return (ResXmlTextNode) last;
        }
        return createResXmlText();
    }
    private ResXmlTextNode createResXmlText(){
        ResXmlTextNode xmlTextNode = new ResXmlTextNode();
        addResXmlTextNode(xmlTextNode);
        return xmlTextNode;
    }

    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{

    }
    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, false);
        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());
        }

        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 void serialize(XmlSerializer serializer) throws IOException {
        int count = getNamespaceCount();
        for(int i = 0; i < count; i++){
            ResXmlNamespace namespace = getNamespaceAt(i);
            serializer.setPrefix(namespace.getPrefix(),
                    namespace.getUri());
        }
        String comment = getStartComment();
        if(comment != null){
            serializer.comment(comment);
        }
        boolean indent = getFeatureSafe(serializer, FEATURE_INDENT_OUTPUT);
        setIndent(serializer, indent);
        boolean indentChanged = indent;
        serializer.startTag(getUri(), getName());
        count = getAttributeCount();
        for(int i = 0; i < count; i++){
            ResXmlAttribute attribute = getAttributeAt(i);
            attribute.serialize(serializer);
        }
        for(ResXmlNode xmlNode : getXmlNodes()){
            if(indentChanged && xmlNode instanceof ResXmlTextNode){
                indentChanged = false;
                setIndent(serializer, false);
            }
            xmlNode.serialize(serializer);
        }
        serializer.endTag(getUri(), getName());
        if(indent != indentChanged){
            setIndent(serializer, true);
        }
        serializer.flush();
    }
    @Override
    public void parse(XmlPullParser parser) throws IOException, XmlPullParserException {
        if(parser.getEventType() != XmlPullParser.START_TAG){
            throw new XmlPullParserException("Invalid state START_TAG != "
                    + parser.getEventType());
        }
        setStartLineNumber(parser.getLineNumber());

        String name = parser.getName();
        String prefix = splitPrefix(name);
        name = splitName(name);
        setName(name);
        String uri = parser.getNamespace();
        if(prefix == null){
            prefix = parser.getPrefix();
        }
        parseNamespaces(parser);
        parseAttributes(parser);
        parseChildes(parser);
        if(prefix != null){
            if(uri == null || uri.length() == 0){
                ResXmlNamespace ns = getNamespaceByPrefix(prefix);
                if(ns != null){
                    uri = ns.getUri();
                }
            }
            setTagNamespace(uri, prefix);
        }
        setEndLineNumber(parser.getLineNumber());
        clearNullNodes(false);
        calculatePositions();
    }
    private void parseChildes(XmlPullParser parser) throws IOException, XmlPullParserException {
        ResXmlElement currentElement = this;
        int event = parser.next();
        while (event != XmlPullParser.END_TAG && event != XmlPullParser.END_DOCUMENT){
            if(event == XmlPullParser.START_TAG){
                ResXmlElement element = createChildElement();
                element.parse(parser);
                currentElement = element;
            }else if(ResXmlTextNode.isTextEvent(event)){
                ResXmlTextNode textNode = getOrCreateResXmlText();
                textNode.parse(parser);
            }else if(event == XmlPullParser.COMMENT){
                currentElement.setComment(parser.getText());
            }
            event = parser.next();
        }
    }
    private void parseNamespaces(XmlPullParser parser) throws XmlPullParserException {
        int count = parser.getNamespaceCount(parser.getDepth());
        for(int i = 0; i < count; i++){
            ResXmlStartNamespace namespace = createXmlStartNamespace(
                    parser.getNamespaceUri(i),
                    parser.getNamespacePrefix(i));
            namespace.setLineNumber(parser.getLineNumber());
        }
        count = parser.getAttributeCount();
        for(int i = 0; i < count; i++){
            String name = parser.getAttributeName(i);
            String prefix = splitPrefix(name);
            name = splitName(name);
            String value = parser.getAttributeValue(i);
            if(looksNamespace(value, prefix)){
                getOrCreateNamespace(value, name);
            }
        }
    }
    private void parseAttributes(XmlPullParser parser) throws IOException {
        int count = parser.getAttributeCount();
        for(int i = 0; i < count; i++){
            String name = parser.getAttributeName(i);
            String prefix = splitPrefix(name);
            name = splitName(name);
            String value = parser.getAttributeValue(i);
            if(looksNamespace(value, prefix)){
                continue;
            }
            if(prefix == null){
                prefix = parser.getAttributePrefix(i);
                if(prefix != null && prefix.length() == 0){
                    prefix = null;
                }
            }
            String uri;
            if(prefix != null){
                uri = parser.getAttributeNamespace(i);
                if(uri.length() == 0){
                    ResXmlNamespace ns = getNamespaceByPrefix(prefix);
                    if(ns != null){
                        uri = ns.getUri();
                    }
                }
            }else {
                uri = null;
            }
            ResXmlAttribute attribute = newAttribute();
            attribute.encode(false, uri, prefix, name, value);
        }
    }

    @Override
    public JSONObject toJson() {
        JSONObject jsonObject=new JSONObject();
        jsonObject.put(NAME_node_type, NAME_element);
        jsonObject.put(NAME_name, getName(false));
        jsonObject.put(NAME_namespace_uri, getUri());
        jsonObject.put(NAME_namespace_prefix, getPrefix());
        int lineStart = getStartLineNumber();
        int lineEnd = getEndLineNumber();
        jsonObject.put(NAME_line, lineStart);
        if(lineStart != lineEnd){
            jsonObject.put(NAME_line_end, lineEnd);
        }
        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(ns);
        }
        if(!nsList.isEmpty()){
            jsonObject.put(NAME_namespaces, nsList);
        }
        jsonObject.put(NAME_comment, getStartComment());
        ResXmlAttributeArray attributeArray = getAttributeArray();
        if(attributeArray != null){
            JSONArray attrArray = attributeArray.toJson();
            if(!attrArray.isEmpty()){
                jsonObject.put(NAME_attributes, attrArray);
            }
        }
        JSONArray childes = new JSONArray();
        for(ResXmlNode xmlNode : getXmlNodes()){
            childes.put(xmlNode.toJson());
        }
        if(!childes.isEmpty()){
            jsonObject.put(NAME_childes, childes);
        }
        return jsonObject;
    }
    @Override
    public void fromJson(JSONObject json) {
        ensureStartEndElement();
        int startLineNumber = json.optInt(NAME_line, 0);
        int endLineNo = json.optInt(NAME_line_end, 0);
        if(endLineNo == 0 && startLineNumber != 0){
            endLineNo = startLineNumber;
        }
        setStartLineNumber(startLineNumber);
        setEndLineNumber(endLineNo);
        for(ResXmlStartNamespace startNamespace : getStartNamespaceList()){
            startNamespace.setLineNumber(startLineNumber);
        }
        JSONArray nsArray = json.optJSONArray(NAME_namespaces);
        if(nsArray != null){
            int length = nsArray.length();
            for(int i=0; i");
                for(ResXmlTextNode textNode : listXmlTextNodes()){
                    builder.append(textNode.getText());
                }
                builder.append("");
            }else {
                builder.append("/>");
            }
            return builder.toString();
        }
        return "NULL";
    }

    private static boolean looksNamespace(String uri, String prefix){
        return uri.length() != 0 && "xmlns".equals(prefix);
    }
    private static boolean getFeatureSafe(XmlSerializer serializer, String name){
        try{
            return serializer.getFeature(name);
        }catch (Throwable ignored){
            return false;
        }
    }
    private static String splitPrefix(String name){
        int i = name.indexOf(':');
        if(i >= 0){
            return name.substring(0, i);
        }
        return null;
    }
    private static String splitName(String name){
        int i = name.indexOf(':');
        if(i >= 0){
            return name.substring(i + 1);
        }
        return name;
    }
    static void setIndent(XmlSerializer serializer, boolean state){
        setFeatureSafe(serializer, FEATURE_INDENT_OUTPUT, state);
    }
    private static void setFeatureSafe(XmlSerializer serializer, String name, boolean state){
        try{
            serializer.setFeature(name, state);
        }catch (Throwable ignored){
        }
    }

    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";
    private static final String NAME_line_end = "line_end";
    static final String NAME_attributes = "attributes";
    static final String NAME_childes = "childes";

    private static final String FEATURE_INDENT_OUTPUT = "http://xmlpull.org/v1/doc/features.html#indent-output";

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy