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

com.reandroid.arsc.coder.xml.XmlCoder Maven / Gradle / Ivy

The 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.coder.xml;

import com.reandroid.arsc.array.ResValueMapArray;
import com.reandroid.arsc.chunk.PackageBlock;
import com.reandroid.arsc.chunk.TableBlock;
import com.reandroid.arsc.chunk.TypeBlock;
import com.reandroid.arsc.coder.CoderSetting;
import com.reandroid.arsc.coder.EncodeResult;
import com.reandroid.arsc.coder.ValueCoder;
import com.reandroid.arsc.coder.XmlSanitizer;
import com.reandroid.arsc.container.SpecTypePair;
import com.reandroid.arsc.item.TypeString;
import com.reandroid.arsc.model.ResourceEntry;
import com.reandroid.arsc.value.*;
import com.reandroid.utils.io.IOUtil;
import com.reandroid.xml.*;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Set;
import java.util.function.Predicate;

public class XmlCoder {

    private static XmlCoder sInstance;

    public final ValuesXml VALUES_XML;

    private CoderSetting setting;

    public XmlCoder() {
        VALUES_XML = new ValuesXml(this);
    }

    public CoderSetting getSetting() {
        CoderSetting setting = this.setting;
        if(setting == null) {
            setting = new CoderSetting();
            this.setting = setting;
        }
        return setting;
    }
    public void setSetting(CoderSetting setting) {
        this.setting = setting;
    }

    public XmlStringDecoder getStringDecoder() {
        return getSetting().getStringDecoder();
    }

    /**
     * Use getSetting().setLogger(logger);
     * */
    @Deprecated
    public void setLogger(XmlCoderLogger logger) {
        getSetting().setLogger(logger);
    }
    /**
     * Use getSetting().getLogger();
     * */
    @Deprecated
    public XmlCoderLogger getLogger() {
        return getSetting().getLogger();
    }

    public static class ValuesXml {

        private final BagRootAttribute BAG_ROOT_ATTRIBUTE = new BagRootAttribute();
        private final BagChild BAG_CHILD;
        private final XmlCoder xmlCoder;

        public ValuesXml(XmlCoder xmlCoder){
            this.xmlCoder = xmlCoder;
            this.BAG_CHILD = new BagChild(this);
        }

        public XmlStringDecoder getStringDecoder() {
            return xmlCoder.getStringDecoder();
        }
        public boolean isAapt() {
            return xmlCoder.getSetting().isAapt();
        }

        public void decodeTable(File resourcesDir,
                                TableBlock tableBlock,
                                Predicate decodedEntries) throws IOException {
            logMessage("Resource table ...");
            ValuesDirectorySerializer directorySerializer =
                    new ValuesDirectorySerializer(resourcesDir);
            decodeTable(directorySerializer, tableBlock, decodedEntries);
            logMessage("Finished resource table");
        }
        public void decodeTable(ValuesSerializerFactory serializerFactory,
                                  TableBlock tableBlock,
                                  Predicate decodedEntries) throws IOException {
            for (PackageBlock packageBlock : tableBlock.listPackages()){
                decodePackage(serializerFactory, packageBlock, decodedEntries);
            }
        }
        public void decodePackage(ValuesSerializerFactory serializerFactory,
                           PackageBlock packageBlock,
                           Predicate decodedEntries) throws IOException {

            packageBlock.sortTypes();

            Iterable specs = packageBlock.listSpecTypePairs();

            for (SpecTypePair specTypePair : specs){
                Set configs = specTypePair.listResConfig();
                int size = configs.size();
                int i = 0;
                for(ResConfig resConfig : configs){
                    i ++;
                    TypeBlock typeBlock = specTypePair.getTypeBlock(resConfig);
                    logVerbose(i + "/" + size + " " + packageBlock.getName()
                            + ":" + typeBlock.getTypeName() + resConfig.getQualifiers());
                    XmlSerializer serializer = serializerFactory.createSerializer(typeBlock);
                    int entriesCount = decode(serializer, specTypePair, resConfig, decodedEntries);
                    serializerFactory.onFinish(serializer, entriesCount);
                }
            }
        }
        public int decode(XmlSerializer serializer,
                           SpecTypePair specTypePair,
                           ResConfig resConfig,
                           Predicate decodedEntries) throws IOException {
            Iterator resources = specTypePair.getResources();
            return decode(serializer, resources, resConfig, decodedEntries);
        }
        public int decode(XmlSerializer serializer,
                           Iterator resources,
                           ResConfig resConfig,
                           Predicate decodedEntries) throws IOException {
            int entriesCount = 0;
            while (resources.hasNext()){
                ResourceEntry resourceEntry = resources.next();
                boolean decoded = decode(serializer,
                        resourceEntry,
                        resConfig,
                        decodedEntries);
                if(decoded){
                    entriesCount++;
                }
            }
            return entriesCount;
        }
        public boolean decode(XmlSerializer serializer,
                           ResourceEntry resourceEntry,
                           ResConfig resConfig,
                           Predicate decodedEntries) throws IOException {
            Entry entry = resourceEntry.get(resConfig);
            if(entry == null || decodedEntries.test(entry)){
                return false;
            }
            if(entry.isComplex()){
                decodeBag(serializer, entry);
            }else if (!entry.isNull()){
                decodeScalar(serializer, entry);
            }else {
                return false;
            }
            return true;
        }
        private void decodeBag(XmlSerializer serializer, Entry entry) throws IOException {
            String tag = startEntry(serializer, entry);
            BAG_ROOT_ATTRIBUTE.decode(serializer, entry);
            int childCount = BAG_CHILD.decode(serializer, entry);
            endEntry(serializer, tag, childCount != 0);
        }
        private void decodeScalar(XmlSerializer serializer, Entry entry) throws IOException {
            String tag = startEntry(serializer, entry);
            if(ignoreIdValue(entry)){
                endEntry(serializer, tag);
                return;
            }
            ResValue resValue = entry.getResValue();
            if(resValue.getValueType() == ValueType.STRING){
                getStringDecoder().serializeText(resValue.getDataAsPoolString(), serializer);
            }else {
                resValue.serializeText(serializer, false);
            }
            endEntry(serializer, tag);
        }
        private String startEntry(XmlSerializer serializer, Entry entry) throws IOException {
            String tag = entry.getXmlTag();
            XmlDecodeUtil.entryIndent(serializer);
            serializer.startTag(null, tag);
            serializer.attribute(null, ATTR_name, entry.getName());
            return tag;
        }
        private void endEntry(XmlSerializer serializer, String tag) throws IOException {
            endEntry(serializer, tag, false);
        }
        private void endEntry(XmlSerializer serializer, String tag, boolean indent) throws IOException {
            if(indent){
                XmlDecodeUtil.entryIndent(serializer);
            }
            serializer.endTag(null, tag);
        }
        private boolean ignoreIdValue(Entry entry){
            if(!TypeString.isTypeId(entry.getTypeName())){
                return false;
            }
            if(isAapt()) {
                return true;
            }
            ResValue resValue = entry.getResValue();
            ValueType valueType = resValue.getValueType();
            if(valueType == ValueType.BOOLEAN){
                return true;
            }
            if(valueType == ValueType.STRING){
                String value = resValue.getValueAsString();
                return value == null || value.length() == 0;
            }
            return false;
        }
        public void encode(File valuesXmlFile, PackageBlock packageBlock) throws IOException, XmlPullParserException {
            XmlPullParser parser = XMLFactory.newPullParser(valuesXmlFile);

            TypeBlock typeBlock = packageBlock.getOrCreateTypeBlock(
                    XmlEncodeUtil.getQualifiersFromValuesXml(valuesXmlFile),
                    XmlEncodeUtil.getTypeFromValuesXml(valuesXmlFile));

            encode(parser, typeBlock);
        }
        public void encode(XmlPullParser parser, TypeBlock typeBlock) throws IOException, XmlPullParserException {
            int event = parser.getEventType();
            boolean documentStarted = false;
            if(event == XmlPullParser.START_DOCUMENT){
                documentStarted = true;
                parser.next();
            }
            event = XMLUtil.ensureStartTag(parser);
            if(event != XmlPullParser.START_TAG){
                throw new XmlEncodeException("Expecting xml state START_TAG but found: "
                        + XMLUtil.toEventName(parser.getEventType()));
            }
            if(PackageBlock.TAG_resources.equals(parser.getName())){
                parser.next();
            }else if(documentStarted){
                throw new XmlEncodeException("Expecting  tag but found: " + parser.getName());
            }
            while (XMLUtil.ensureStartTag(parser) == XmlPullParser.START_TAG){
                XMLElement element = XMLElement.parseElement(parser);
                encodeEntry(element, typeBlock);
            }
            IOUtil.close(parser);
        }
        public void encodeEntry(XMLElement entryElement, TypeBlock typeBlock) throws IOException{
            Entry entry = typeBlock.getOrCreateDefinedEntry(
                    entryElement.getAttributeValue(ATTR_name));
            if(entry == null){
                throw new XmlEncodeException("Undefined entry name: " + entryElement.getDebugText());
            }
            if(isBag(entryElement)){
                encodeBag(entryElement, entry);
            }else {
                encodeScalar(entryElement, entry);
            }
        }
        public void encodeScalar(XMLElement element, Entry entry) throws IOException{
            entry.ensureComplex(false);
            if(isTypeId(element)){
                encodeScalarId(element, entry);
            }else {
                encodeScalarAny(element, entry);
            }
            checkVisibility(entry);
        }

        public void encodeBag(XMLElement element, Entry entry) throws IOException{
            entry.ensureComplex(true);
            BAG_ROOT_ATTRIBUTE.encode(element, entry);
            Iterator childes = element.getElements();
            while (childes.hasNext()){
                BAG_CHILD.encode(childes.next(), entry);
            }
            checkVisibility(entry);
        }
        private boolean isBag(XMLElement element){
            String tag = element.getName();
            if("string".equals(tag)){
                return false;
            }
            if(element.hasChildElements()){
                return true;
            }
            if(element.hasTextNode()){
                return false;
            }
            return element.hasAttribute(ATTR_parent)
                    || element.hasAttribute(ATTR_formats)
                    || TypeString.isTypeArray(tag);
        }
        private void encodeScalarId(XMLElement element, Entry entry) throws IOException {
            if(!element.hasTextNode()){
                entry.setValueAsBoolean(false);
            }else {
                encodeScalarAny(element, entry);
            }
            ValueHeader header = entry.getHeader();
            header.setPublic(true);
            header.setWeak(true);
        }
        private void encodeScalarAny(XMLElement element, Entry entry) throws IOException{
            ResValue resValue = entry.getResValue();
            if(element.hasChildElements()){
                resValue.setValueAsString(StyleDocument.copyInner(element));
                return;
            }
            String text = element.getTextContent();
            EncodeResult encodeResult = ValueCoder.encodeReference(entry.getPackageBlock(), text);
            if(encodeResult == null){
                AttributeDataFormat dataFormat = AttributeDataFormat.fromValueTypeName(
                        element.getAttributeValue(ATTR_type));
                if(dataFormat == null){
                    dataFormat = AttributeDataFormat.fromValueTypeName(element.getName());
                    if(dataFormat != AttributeDataFormat.STRING){
                        dataFormat = null;
                    }
                }
                encodeResult = ValueCoder.encode(text, dataFormat);
                if(encodeResult == null && dataFormat != null && !dataFormat.contains(ValueType.STRING)){
                    throw new XmlEncodeException("Invalid value: " + element);
                }
            }
            if(encodeResult != null){
                if(encodeResult.isError()){
                    throw new XmlEncodeException(encodeResult.getError()+ ": " + element.getDebugText());
                }
                resValue.setValue(encodeResult);
            }else {
                resValue.setValueAsString(StyleDocument.copyInner(element));
            }
        }
        private boolean isTypeId(XMLElement element){
            if(element.hasChildElements()){
                return false;
            }
            return TypeString.isTypeId(element.getName());
        }
        private void checkVisibility(Entry entry){
            ValueHeader valueHeader = entry.getHeader();
            if(valueHeader == null){
                return;
            }
            TypeBlock typeBlock = entry.getTypeBlock();
            if(typeBlock == null){
                return;
            }
            if(typeBlock.isTypeAttr() || typeBlock.isTypeId()){
                valueHeader.setPublic(true);
            }
        }

        public XmlCoderLogger getLogger() {
            return xmlCoder.getSetting().getLogger();
        }
        private void logMessage(String message){
            XmlCoderLogger logger = getLogger();
            if(logger != null){
                logger.logMessage("Decoding", message);
            }
        }
        private void logVerbose(String message){
            XmlCoderLogger logger = getLogger();
            if(logger != null){
                logger.logMessage("Decoding", message);
            }
        }
    }
    public static class BagChild {

        private final ValuesXml valuesXml;

        public BagChild(ValuesXml valuesXml) {
            this.valuesXml = valuesXml;
        }
        public XmlStringDecoder getStringDecoder() {
            return valuesXml.getStringDecoder();
        }
        public void encode(XMLElement child, Entry entry) throws IOException{
            ChildType childType = ChildType.getType(child);
            if(childType == null){
                throw new XmlEncodeException("Unknown child bag: " + child.getDebugText());
            }
            switch (childType){
                case ATTR:
                    encodeAttr(child, entry);
                    break;
                case ARRAY:
                    encodeArray(child, entry);
                    break;
                case PLURAL:
                    encodePlural(child, entry);
                    break;
                case STYLE:
                    encodeStyle(child, entry);
                    break;
            }
        }
        public int decode(XmlSerializer serializer, Entry entry) throws IOException{
            ChildType childType = ChildType.getType(entry);
            switch (childType){
                case ATTR:
                    return decodeAttr(serializer, entry);
                case ARRAY:
                    return decodeArray(serializer, entry);
                case PLURAL:
                    return decodePlural(serializer, entry);
                case STYLE:
                    return decodeStyle(serializer, entry);
            }
            return 0;
        }
        public int decodeAttr(XmlSerializer serializer, Entry entry) throws IOException {
            ResTableMapEntry mapEntry = entry.getResTableMapEntry();

            ResValueMap formatsMap = mapEntry.getByType(AttributeType.FORMATS);
            AttributeDataFormat bagType = AttributeDataFormat.typeOfBag(formatsMap.getData());

            int childCount = 0;
            for (ResValueMap valueMap : mapEntry) {
                AttributeType attributeType = valueMap.getAttributeType();
                if (attributeType != null) {
                    continue;
                }
                assert bagType != null;
                startTag(serializer, bagType.getName());
                serializer.attribute(null, ATTR_name, valueMap.decodeName());
                serializer.attribute(null, ATTR_value, valueMap.decodeValue());
                endTag(serializer, bagType.getName());
                childCount++;
            }
            return childCount;
        }
        public int decodePlural(XmlSerializer serializer, Entry entry) throws IOException {
            ResTableMapEntry mapEntry = entry.getResTableMapEntry();
            ResValueMapArray mapArray = mapEntry.getValue();
            int childCount = 0;
            Iterator iterator = mapArray.iterator();
            while (iterator.hasNext()){
                ResValueMap valueMap = iterator.next();
                AttributeType attributeType = valueMap.getAttributeType();
                if(attributeType == null){
                    continue;
                }
                startTag(serializer, TAG_item);
                serializer.attribute(null, ATTR_quantity, attributeType.getName());
                if(valueMap.getValueType() == ValueType.STRING) {
                    getStringDecoder().serializeText(valueMap.getDataAsPoolString(), serializer);
                } else {
                    valueMap.serializeText(serializer);
                }
                endTag(serializer, TAG_item);
                childCount++;
            }
            return childCount;
        }
        public int decodeStyle(XmlSerializer serializer, Entry entry) throws IOException {
            ResTableMapEntry mapEntry = entry.getResTableMapEntry();
            ResValueMapArray mapArray = mapEntry.getValue();
            int childCount = 0;
            Iterator iterator = mapArray.iterator();
            while (iterator.hasNext()){
                ResValueMap valueMap = iterator.next();
                startTag(serializer, TAG_item);
                String name = valueMap.decodeName(true);
                boolean escapeValue = false;
                if(name == null){
                    name = ValueCoder.decodeUnknownNameId(valueMap.getNameId());
                    escapeValue = true;
                }
                serializer.attribute(null, ATTR_name, name);

                if(valueMap.getValueType() == ValueType.STRING) {
                    getStringDecoder().serializeText(valueMap.getDataAsPoolString(), serializer);
                } else {
                    valueMap.serializeText(serializer, escapeValue);
                }

                endTag(serializer, TAG_item);
                childCount ++;
            }
            return childCount;
        }
        public int decodeArray(XmlSerializer serializer, Entry entry) throws IOException {
            boolean escapeValues = "array".equals(entry.getXmlTag());
            ResTableMapEntry mapEntry = entry.getResTableMapEntry();
            ResValueMapArray mapArray = mapEntry.getValue();
            int childCount = 0;
            Iterator iterator = mapArray.iterator();
            while (iterator.hasNext()){
                ResValueMap valueMap = iterator.next();
                startTag(serializer, TAG_item);
                if(valueMap.getValueType() == ValueType.STRING) {
                    getStringDecoder().serializeText(valueMap.getDataAsPoolString(), serializer);
                } else {
                    valueMap.serializeText(serializer, escapeValues);
                }
                endTag(serializer, TAG_item);
                childCount ++;
            }
            return childCount;
        }
        public void encodeAttr(XMLElement child, Entry entry) throws IOException{
            AttributeDataFormat dataFormat = AttributeDataFormat.fromBagTypeName(child.getName());
            entry.ensureComplex(true);
            ResValueMapArray mapArray = entry.getResValueMapArray();
            ResValueMap formats = mapArray.getOrCreateType(AttributeType.FORMATS);
            formats.addAttributeTypeFormat(dataFormat);
            ResValueMap valueMap = mapArray.createNext();
            XMLAttribute attribute = child.getAttribute(ATTR_name);

            ResourceEntry resourceEntry = valueMap.encodeIdName(attribute.getPrefix(),
                    attribute.getValueAsString());
            if(resourceEntry == null){
                throw new XmlEncodeException("Unknown " + dataFormat.getName()
                        + " name: " + child.getDebugText());
            }

            EncodeResult encodeResult = ValueCoder.encode(getValue(child));
            if(encodeResult == null){
                // TODO: unbelievable! we always expect INT/HEX on enum/flags lets throw exception to see
                throw new XmlEncodeException("Unexpected value: " + child.getDebugText());
            }
            if(encodeResult.isError()){
                throw new XmlEncodeException(encodeResult.getError() + ": " + child.getDebugText());
            }
            valueMap.setValue(encodeResult);
            entry.getHeader().setPublic(true);
        }
        public void encodeArray(XMLElement child, Entry entry) throws IOException{
            entry.ensureComplex(true);
            ResValueMapArray mapArray = entry.getResValueMapArray();

            ResValueMap valueMap = mapArray.createNext();
            valueMap.setArrayIndex();

            String text = child.getTextContent();
            EncodeResult encodeResult = ValueCoder.encodeReference(entry.getPackageBlock(), text);
            if(encodeResult == null){
                AttributeDataFormat dataFormat = getParentArrayType(child);
                encodeResult = ValueCoder.encode(text, dataFormat);
            }
            if(encodeResult != null){
                if(encodeResult.isError()){
                    throw new XmlEncodeException("Unexpected array value: " + child.getDebugText());
                }
                valueMap.setValue(encodeResult);
                return;
            }
            valueMap.setValueAsString(StyleDocument.copyInner(child));
        }
        private void startTag(XmlSerializer serializer, String name) throws IOException {
            XmlDecodeUtil.bagIndent(serializer);
            serializer.startTag(null, name);
        }
        private void endTag(XmlSerializer serializer, String name) throws IOException {
            serializer.endTag(null, name);
        }
        private AttributeDataFormat getParentArrayType(XMLElement child){
            XMLElement parent = child.getParentElement();
            if(parent == null){
                return null;
            }
            String name = parent.getName();
            int i = name.indexOf('-');
            if(i < 0){
                return null;
            }
            name = name.substring(0, i);
            return AttributeDataFormat.fromValueTypeName(name);
        }
        public void encodePlural(XMLElement child, Entry entry) throws IOException{
            AttributeType attributeType = AttributeType.fromName(
                    child.getAttributeValue(ATTR_quantity));
            if(attributeType == null){
                throw new XmlEncodeException("Failed to get attribute '"
                        + ATTR_quantity + "'" + child.getDebugText());
            }
            entry.ensureComplex(true);
            ResValueMapArray mapArray = entry.getResValueMapArray();
            ResValueMap valueMap = mapArray.createNext();
            valueMap.setAttributeType(attributeType);

            String text = child.getTextContent();
            EncodeResult encodeResult = ValueCoder.encodeReference(entry.getPackageBlock(), text);
            if(encodeResult == null){
                encodeResult = ValueCoder.encode(text);
            }
            if(encodeResult != null){
                if(encodeResult.isError()){
                    throw new XmlEncodeException(encodeResult.getError() + ": " + child.getDebugText());
                }
                valueMap.setValue(encodeResult);
                return;
            }
            valueMap.setValueAsString(StyleDocument.copyInner(child));
        }
        public void encodeStyle(XMLElement child, Entry entry) throws IOException{
            entry.ensureComplex(true);
            ResValueMapArray mapArray = entry.getResValueMapArray();

            ResValueMap valueMap = mapArray.createNext();
            EncodeResult encodeResult = valueMap.encodeStyle(child);
            if(encodeResult.isError()){
                throw new XmlEncodeException(encodeResult.getError() + ": " + child.getDebugText());
            }
        }
        private String getValue(XMLElement element) {
            String value = element.getAttributeValue(ATTR_value);
            if(value == null) {
                value = element.getTextContent();
                if(value != null) {
                    value = value.trim();
                }
            }
            return value;
        }
    }
    public static class BagRootAttribute {
        public void decode(XmlSerializer serializer, Entry entry) throws IOException {
            decodeParent(serializer, entry);
            decodeAttrTypes(serializer, entry);
        }
        public void decodeAttrTypes(XmlSerializer serializer, Entry entry) throws IOException {
            if(!entry.getTypeBlock().isTypeAttr()){
                return;
            }
            ResTableMapEntry mapEntry = entry.getResTableMapEntry();
            ResValueMapArray mapArray = mapEntry.getValue();
            Iterator iterator = mapArray.iterator();
            while (iterator.hasNext()){
                ResValueMap valueMap = iterator.next();
                AttributeType attributeType = valueMap.getAttributeType();
                if(attributeType == null){
                    continue;
                }
                boolean ignore_empty = mapArray.size() > 1;
                valueMap.serializeAttribute(serializer,
                        attributeType.getName(), ignore_empty);
            }
        }
        public void decodeParent(XmlSerializer serializer, Entry entry) throws IOException {
            ResTableMapEntry mapEntry = entry.getResTableMapEntry();
            String parent = mapEntry.decodeParentId();
            if(parent != null){
                serializer.attribute(null, ATTR_parent, parent);
                return;
            }
            if(mapEntry.isStyle()) {
                if(mapEntry.childesCount() == 0 ||
                        entry.getName().indexOf('.') > 0 ||
                        entry.getResourceEntry().getConfigsCount() > 1) {
                    serializer.attribute(null, ATTR_parent, "");
                }
            }
        }
        public void encode(XMLElement element, Entry entry) throws IOException{
            Iterator attributes = element.getAttributes();
            while (attributes.hasNext()){
                XMLAttribute xmlAttribute = attributes.next();
                encode(xmlAttribute, entry);
            }
        }
        public void encode(XMLAttribute xmlAttribute, Entry entry) throws IOException{
            if(xmlAttribute.getPrefix() != null){
                throw new XmlEncodeException("Unknown root attribute: " + xmlAttribute.getDebugText());
            }
            encode(xmlAttribute.getName(false),
                    xmlAttribute.getValueAsString(false), entry);
        }
        public void encode(String name, String value, Entry entry) throws IOException{
            if(name.equals(ATTR_name)){
                encodeName(value, entry);
            }else if(name.equals(ATTR_parent)){
                encodeParent(value, entry);
            }else if(name.equals(ATTR_formats)){
                encodeFormats(value, entry);
            }else {
                AttributeType attributeType = AttributeType.fromName(name);
                if(attributeType != null){
                    encodeType(attributeType, value, entry);
                }else{
                    // TODO: what could be ?
                }
            }
        }
        public void encodeType(AttributeType attributeType, String value, Entry entry) throws IOException{
            entry.ensureComplex(true);
            ResValueMapArray mapArray = entry.getResValueMapArray();
            ResValueMap valueMap = mapArray.getOrCreateType(attributeType);
            EncodeResult encodeResult = ValueCoder.encodeReference(entry.getPackageBlock(), value);
            if(encodeResult != null){
                if(encodeResult.isError()){
                    throw new XmlEncodeException(encodeResult.getError());
                }
                valueMap.setValue(encodeResult);
                return;
            }
            encodeResult = ValueCoder.encode(value);
            if(encodeResult != null){
                if(encodeResult.isError()){
                    throw new XmlEncodeException(encodeResult.getError());
                }
                valueMap.setValue(encodeResult);
                return;
            }
            // TODO: will never reach here, we are expecting min, max and l10n only
            value = XmlSanitizer.unEscapeUnQuote(value);
            valueMap.setValueAsString(value);
        }
        public void encodeFormats(String value, Entry entry) {
            entry.ensureComplex(true);
            ResValueMapArray mapArray = entry.getResValueMapArray();
            ResValueMap valueMap = mapArray.getOrCreateType(AttributeType.FORMATS);
            // TODO: validate unknown formats
            AttributeDataFormat[] dataFormats = AttributeDataFormat.parseValueTypes(value);
            valueMap.setData(AttributeDataFormat.sum(dataFormats));
        }
        public void encodeParent(String value, Entry entry) throws IOException{
            PackageBlock packageBlock = entry.getPackageBlock();
            EncodeResult parent = ValueCoder.encodeReference(packageBlock,
                    value);
            if(parent != null){
                if(parent.isError()){
                    throw new XmlEncodeException(parent.getError());
                }
                entry.ensureComplex(true);
                entry.getResTableMapEntry().setParentId(parent.value);
            }
        }
        public void encodeName(String value, Entry entry) {
            if(entry.isDefined()){
                return;
            }
            // TODO: confirm will never reach here

            if(value == null || value.length() == 0){
                return;
            }
            entry.setName(value);
        }
    }
    enum ChildType{
        ATTR,
        ARRAY,
        PLURAL,
        STYLE;
        public static ChildType getType(XMLElement child){
            String tag = child.getName(false);
            if(AttributeDataFormat.fromBagTypeName(tag) != null){
                return ATTR;
            }
            if(!TAG_item.equals(tag)){
                return null;
            }
            if(child.getAttribute(ATTR_name) != null){
                return STYLE;
            }
            int count = child.getAttributeCount();
            if(count == 0 || (count == 1 && child.getAttribute(ATTR_type) != null)){
                return ARRAY;
            }

            AttributeType attributeType = AttributeType.fromName(
                    child.getAttributeValue(ATTR_quantity));
            if(attributeType != null && attributeType.isPlural()){
                return PLURAL;
            }
            return STYLE;
        }
        public static ChildType getType(Entry entry){
            ResTableMapEntry mapEntry = entry.getResTableMapEntry();
            if(mapEntry.isAttr()){
                return ATTR;
            }
            if(mapEntry.isArray()){
                return ARRAY;
            }
            if(mapEntry.isPlural()){
                return PLURAL;
            }
            return STYLE;
        }
    }

    public static XmlCoder getInstance(){
        if(sInstance != null){
            return sInstance;
        }
        synchronized (XmlCoder.class){
            if(sInstance == null){
                sInstance = new XmlCoder();
            }
            return sInstance;
        }
    }

    static final String ATTR_name = "name";
    static final String ATTR_formats = "formats";
    static final String ATTR_parent = "parent";
    static final String ATTR_quantity = "quantity";
    static final String ATTR_type = "type";
    static final String ATTR_value = "value";

    static final String TAG_item = "item";
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy