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

com.reandroid.arsc.header.HeaderBlock 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.header;

import com.reandroid.arsc.base.BlockContainer;
import com.reandroid.arsc.chunk.ChunkType;
import com.reandroid.arsc.base.Block;
import com.reandroid.arsc.container.BlockList;
import com.reandroid.arsc.container.ExpandableBlockContainer;
import com.reandroid.arsc.io.BlockLoad;
import com.reandroid.arsc.io.BlockReader;
import com.reandroid.arsc.item.BlockItem;
import com.reandroid.arsc.item.ByteArray;
import com.reandroid.arsc.item.IntegerItem;
import com.reandroid.arsc.item.ShortItem;
import com.reandroid.utils.HexBytesWriter;
import com.reandroid.utils.HexUtil;

import java.io.*;
import java.util.List;

public class HeaderBlock extends ExpandableBlockContainer implements BlockLoad {
    private final ShortItem mType;
    private final ShortItem mHeaderSize;
    private final IntegerItem mChunkSize;
    private HeaderLoaded mHeaderLoaded;
    private final ByteArray extraBytes;
    public HeaderBlock(short type){
        super(3);
        this.mType=new ShortItem(type);
        this.mHeaderSize=new ShortItem();
        this.mChunkSize=new IntegerItem();
        this.extraBytes=new ByteArray();
        addChild(mType);
        addChild(mHeaderSize);
        addChild(mChunkSize);
        this.mType.setBlockLoad(this);
        this.mHeaderSize.setBlockLoad(this);
        this.mChunkSize.setBlockLoad(this);
    }
    public HeaderBlock(ChunkType chunkType){
        this(chunkType.ID);
    }
    public int getMinimumSize(){
        return countBytes();
    }
    public ByteArray getExtraBytes() {
        return extraBytes;
    }
    public void setHeaderLoaded(HeaderLoaded headerLoaded){
        this.mHeaderLoaded=headerLoaded;
    }
    public ChunkType getChunkType(){
        return ChunkType.get(mType.get());
    }
    public short getType(){
        return mType.get();
    }
    public void setType(ChunkType chunkType){
        short type;
        if(chunkType==null){
            type=0;
        }else {
            type=chunkType.ID;
        }
        setType(type);
    }
    public void setType(short type){
        mType.set(type);
    }

    public int getHeaderSize(){
        return mHeaderSize.unsignedInt();
    }
    public void setHeaderSize(short headerSize){
        mHeaderSize.set(headerSize);
    }
    public int getChunkSize(){
        return mChunkSize.get();
    }
    public void setChunkSize(int chunkSize){
        mChunkSize.set(chunkSize);
    }

    public final void refreshHeader(){
        refreshHeaderSize();
        refreshChunkSize();
    }
    private void refreshHeaderSize(){
        setHeaderSize((short)countBytes());
    }
    private void refreshChunkSize(){
        Block parent=getParent();
        if(parent==null){
            return;
        }
        int count=parent.countBytes();
        setChunkSize(count);
    }
    /**Non buffering reader*/
    public int readBytes(InputStream inputStream) throws IOException{
        int result = onReadBytes(inputStream);
        super.notifyBlockLoad();
        return result;
    }
    private int onReadBytes(InputStream inputStream) throws IOException {
        int readCount = readBytes(inputStream, this);
        int difference = getHeaderSize() - readCount;
        initExtraBytes(this.extraBytes, difference);
        if(this.extraBytes.size()>0){
            readCount += extraBytes.readBytes(inputStream);
        }
        return readCount;
    }
    private int readBytes(InputStream inputStream, Block block) throws IOException{
        int result=0;
        if(block instanceof BlockItem){
            result = ((BlockItem)block).readBytes(inputStream);
        }else if(block instanceof BlockList){
            List childes=
                    ((BlockList) block).getChildes();
            for(Block child:childes){
                result+=readBytes(inputStream, child);
            }
        }else if(block instanceof BlockContainer){
            Block[] childes =
                    ((BlockContainer) block).getChildes();
            for(Block child:childes){
                result+=readBytes(inputStream, child);
            }
        }else {
            throw new IOException("Can not read block type: "+block.getClass());
        }
        return result;
    }
    @Override
    public void onReadBytes(BlockReader reader) throws IOException {
        int start=reader.getPosition();
        super.onReadBytes(reader);
        int readActual=reader.getPosition() - start;
        int difference=getHeaderSize()-readActual;
        initExtraBytes(this.extraBytes, difference);
        if(this.extraBytes.size()>0){
            this.extraBytes.readBytes(reader);
        }
    }
    @Override
    public void onBlockLoaded(BlockReader reader, Block sender) throws IOException {
        if(sender==this.mType){
            onChunkTypeLoaded(mType.get());
        }else if(sender==this.mHeaderSize){
            onHeaderSizeLoaded(mHeaderSize.unsignedInt());
        }else if(sender==this.mChunkSize){
            onChunkSizeLoaded(mHeaderSize.unsignedInt(),
                    mChunkSize.get());
        }
    }

    @Override
    protected void onRefreshed() {
        // Not required, the parent should call refreshHeader()
    }
    @Override
    protected void refreshChildes(){
        // Not required
    }
    void initExtraBytes(ByteArray extraBytes, int difference){
        if(difference==0){
            return;
        }
        if(extraBytes.getParent()==null){
            addChild(extraBytes);
        }
        extraBytes.setSize(difference);
    }
    void onChunkTypeLoaded(short chunkType){
        HeaderLoaded headerLoaded = mHeaderLoaded;
        if(headerLoaded!=null){
            headerLoaded.onChunkTypeLoaded(chunkType);
        }
    }
    void onHeaderSizeLoaded(int size){
        HeaderLoaded headerLoaded = mHeaderLoaded;
        if(headerLoaded!=null){
            headerLoaded.onHeaderSizeLoaded(size);
        }
    }
    void onChunkSizeLoaded(int headerSize, int chunkSize){
        HeaderLoaded headerLoaded = mHeaderLoaded;
        if(headerLoaded!=null){
            headerLoaded.onChunkSizeLoaded(headerSize, chunkSize);
        }
    }
    /**
     * Prints bytes in hex for debug/testing
     * */
    public String toHex(){
        return HexBytesWriter.toHex(getBytes());
    }
    @Override
    public String toString(){
        short t = getType();
        ChunkType type = ChunkType.get(t);
        StringBuilder builder = new StringBuilder();
        if(type!=null){
            builder.append(type.toString());
        }else {
            builder.append("Unknown type=");
            builder.append(HexUtil.toHex4(t));
        }
        builder.append("{ValueHeader=");
        builder.append(getHeaderSize());
        builder.append(", Chunk=");
        builder.append(getChunkSize());
        builder.append("}");
        return builder.toString();
    }

    public interface HeaderLoaded{
        void onChunkTypeLoaded(short type);
        void onHeaderSizeLoaded(int headerSize);
        void onChunkSizeLoaded(int headerSize, int chunkSize);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy