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

com.reandroid.arsc.value.ValueHeader Maven / Gradle / Ivy

/*
  *  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.value;

import com.reandroid.arsc.base.Block;
import com.reandroid.arsc.chunk.ParentChunk;
import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.*;
import com.reandroid.arsc.pool.SpecStringPool;
import com.reandroid.arsc.pool.StringPool;
import com.reandroid.arsc.refactor.ResourceMergeOption;
import com.reandroid.json.JSONConvert;
import com.reandroid.json.JSONObject;

import java.io.IOException;

public class ValueHeader extends BlockItem implements JSONConvert {
    private ReferenceItem mStringReference;
    public ValueHeader(int size){
        super(size);
        writeSize();
        putInteger(getBytesInternal(), OFFSET_SPEC_REFERENCE, -1);
    }

    void linkSpecStringsInternal(SpecStringPool specStringPool){
        int key = getKey();
        SpecString specString = specStringPool.get(key);
        if(specString == null){
            mStringReference = null;
            return;
        }
        if(mStringReference != null){
            specString.removeReference(mStringReference);
        }
        ReferenceItem stringReference = new ValueHeaderReference(this);
        mStringReference = stringReference;
        specString.addReference(stringReference);
    }
    public void onRemoved(){
        unLinkStringReference();
    }
    public String getName(){
        StringItem stringItem = getNameString();
        if(stringItem!=null){
            return stringItem.get();
        }
        return null;
    }
    public boolean isComplex(){
        return getBit(getBytesInternal(), OFFSET_FLAGS,0);
    }
    public void setComplex(boolean complex){
        putBit(getBytesInternal(), OFFSET_FLAGS, 0, complex);
    }
    public void setPublic(boolean b){
        putBit(getBytesInternal(), OFFSET_FLAGS,1, b);
    }
    public boolean isPublic(){
        return getBit(getBytesInternal(), OFFSET_FLAGS,1);
    }
    public void setWeak(boolean b){
        putBit(getBytesInternal(), OFFSET_FLAGS, 2, b);
    }
    public boolean isWeak(){
        return getBit(getBytesInternal(), OFFSET_FLAGS,2);
    }
    // Intentionally made accessible internal, use ResValue#setCompact
    void setCompact(boolean b){
        if(b == isCompact()){
            return;
        }
        int key = getKey();
        putBit(getBytesInternal(), OFFSET_FLAGS, 3, b);
        writeKey(key, b);
    }
    public boolean isCompact(){
        return getBit(getBytesInternal(), OFFSET_FLAGS,3);
    }

    public int getKey(){
        if(isCompact()){
            return getShortUnsigned(getBytesInternal(), 0);
        }
        return getData();
    }
    public void setKey(int key){
        if(key == getKey()){
            return;
        }
        unLinkStringReference();
        writeKey(key);
        linkStringReference();
    }
    void writeKey(int key) {
        writeKey(key, isCompact());
    }
    private void writeKey(int key, boolean compact){
        if(compact){
            putShort(getBytesInternal(), 0, key);
        }else {
            setData(key);
        }
    }
    int getData(){
        return getInteger(getBytesInternal(), 4);
    }
    void setData(int data){
        putInteger(getBytesInternal(), 4, data);
    }
    byte getType(){
        return getBytesInternal()[OFFSET_DATA_TYPE];
    }
    void setType(byte type){
        getBytesInternal()[OFFSET_DATA_TYPE] = type;
    }
    public void setKey(StringItem stringItem){
        if(ignoreUpdateKey(stringItem)){
            return;
        }
        unLinkStringReference();
        int key = -1;
        if(stringItem != null){
            key = stringItem.getIndex();
        }
        writeKey(key);
        linkStringReference(stringItem);
    }
    private boolean ignoreUpdateKey(StringItem stringItem){
        int key = getKey();
        ReferenceItem referenceItem = this.mStringReference;
        if(stringItem == null){
            return referenceItem == null && key == -1;
        }
        if(referenceItem == null || key != stringItem.getIndex()){
            return false;
        }
        return getSpecString(key) == stringItem;
    }
    public void setSize(int size){
        if(!isCompact()){
            super.setBytesLength(size, false);
            writeSize();
        }
    }
    public int getSize(){
        return getBytesInternal().length;
    }
    int readSize(){
        if(getSize()<2){
            return 0;
        }
        return 0xffff & getShort(getBytesInternal(), OFFSET_SIZE);
    }
    private void writeSize(){
        int size = getSize();
        if(size>1){
            putShort(getBytesInternal(), OFFSET_SIZE, (short) size);
        }
    }

    private void linkStringReference(){
        StringPool specStringPool = getSpecStringPool();
        if(specStringPool == null || specStringPool.isStringLinkLocked()){
            return;
        }
        linkStringReference(specStringPool.get(getKey()));
    }
    private void linkStringReference(StringItem stringItem){
        unLinkStringReference();
        if(stringItem==null){
            return;
        }
        ReferenceItem stringReference = new ValueHeaderReference(this);
        mStringReference = stringReference;
        stringItem.addReference(stringReference);
    }
    private void unLinkStringReference(){
        ReferenceItem stringReference = mStringReference;
        if(stringReference==null){
            return;
        }
        mStringReference = null;
        StringItem stringItem = getNameString();
        if(stringItem == null){
            return;
        }
        stringItem.removeReference(stringReference);
    }
    public StringItem getNameString(){
        return getSpecString(getKey());
    }
    private StringItem getSpecString(int key){
        if(key < 0){
            return null;
        }
        StringPool specStringPool = getSpecStringPool();
        if(specStringPool==null){
            return null;
        }
        return specStringPool.get(key);
    }
    private StringPool getSpecStringPool(){
        Block parent = getParent();
        while (parent!=null){
            if(parent instanceof ParentChunk){
                return ((ParentChunk) parent).getSpecStringPool();
            }
            parent = parent.getParent();
        }
        return null;
    }
    @Override
    public void onReadBytes(BlockReader reader) throws IOException {
        int position = reader.getPosition();
        reader.readFully(getBytesInternal());
        if(!isCompact()){
            reader.seek(position);
            int size = reader.readUnsignedShort();
            setBytesLength(size, false);
            reader.readFully(getBytesInternal());
        }
    }
    private void setName(String name){
        if(name==null){
            name = "";
        }
        StringPool stringPool = getSpecStringPool();
        if(stringPool==null){
            return;
        }
        StringItem stringItem = stringPool.getOrCreate(name);
        setKey(stringItem);
    }
    public void merge(ValueHeader valueHeader){
        if(valueHeader == null || valueHeader ==this){
            return;
        }
        setComplex(valueHeader.isComplex());
        setWeak(valueHeader.isWeak());
        setPublic(valueHeader.isPublic());
        setName(valueHeader.getName());
    }
    public void mergeWithName(ResourceMergeOption mergeOption, ValueHeader valueHeader){
        this.merge(valueHeader);
    }
    public void toJson(JSONObject jsonObject) {
        jsonObject.put(NAME_entry_name, getName());
        if(isWeak()){
            jsonObject.put(NAME_is_weak, true);
        }
        if(isPublic()){
            jsonObject.put(NAME_is_public, true);
        }
    }
    @Override
    public JSONObject toJson() {
        JSONObject jsonObject = new JSONObject();
        toJson(jsonObject);
        return jsonObject;
    }
    @Override
    public void fromJson(JSONObject json) {
        setWeak(json.optBoolean(NAME_is_weak, false));
        setPublic(json.optBoolean(NAME_is_public, false));
        setName(json.optString(NAME_entry_name));
    }
    @Override
    public String toString(){
        if(isNull()){
            return "null";
        }
        StringBuilder builder=new StringBuilder();
        int byte_size = getSize();
        int read_size = readSize();
        if(byte_size!=8){
            builder.append("size=").append(byte_size);
        }
        if(byte_size!=read_size){
            builder.append(", readSize=").append(read_size);
        }
        if(isComplex()){
            builder.append(", complex");
        }
        if(isPublic()){
            builder.append(", public");
        }
        if(isWeak()){
            builder.append(", weak");
        }
        if(isCompact()){
            builder.append(", compact");
        }
        String name = getName();
        if(name!=null){
            builder.append(", name=").append(name);
        }else {
            builder.append(", key=").append(getKey());
        }
        return builder.toString();
    }


    static class ValueHeaderReference implements ReferenceItem {

        private final ValueHeader valueHeader;

        ValueHeaderReference(ValueHeader valueHeader){
            this.valueHeader = valueHeader;
        }
        @Override
        public int get() {
            return valueHeader.getKey();
        }
        @Override
        public void set(int value) {
            valueHeader.writeKey(value);
        }
        @SuppressWarnings("unchecked")
        @Override
        public  T1 getReferredParent(Class parentClass) {
            ValueHeader block = this.valueHeader;
            if(parentClass.isInstance(block)){
                return (T1) block;
            }
            return block.getParentInstance(parentClass);
        }
    }

    private static final int OFFSET_SIZE = 0;
    private static final int OFFSET_FLAGS = 2;
    private static final int OFFSET_DATA_TYPE = 3;
    private static final int OFFSET_SPEC_REFERENCE = 4;


    public static final String NAME_is_complex = "is_complex";
    public static final String NAME_is_public = "is_public";
    public static final String NAME_is_weak = "is_weak";

    public static final String NAME_entry_name = "entry_name";
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy