/* * 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.base.BlockCounter; import com.reandroid.arsc.chunk.PackageBlock; import com.reandroid.arsc.chunk.TableBlock; import com.reandroid.arsc.chunk.TypeBlock; import com.reandroid.arsc.group.EntryGroup; import com.reandroid.arsc.io.BlockReader; import com.reandroid.arsc.item.*; import com.reandroid.json.JSONConvert; import com.reandroid.json.JSONObject; import java.io.IOException; import java.io.OutputStream; public class Entry extends Block implements JSONConvert { private TableEntry mTableEntry; public Entry(){ super(); } public int getId(){ return getIndex(); } public String getName(){ SpecString specString = getSpecString(); if(specString!=null){ return specString.get(); } return null; } public String getTypeName(){ TypeBlock typeBlock = getTypeBlock(); if(typeBlock!=null){ return typeBlock.getTypeName(); } return null; } public int getResourceId(){ PackageBlock packageBlock = getPackageBlock(); if(packageBlock==null){ return 0; } TypeBlock typeBlock = getTypeBlock(); if(typeBlock==null){ return 0; } return (packageBlock.getId()<<24) | (typeBlock.getId() << 16) | getId(); } public int getSpecReference(){ TableEntry tableEntry = getTableEntry(); if(tableEntry == null){ return 0; } return tableEntry.getHeader().getKey(); } public TypeString getTypeString(){ TypeBlock typeBlock = getTypeBlock(); if(typeBlock!=null){ return typeBlock.getTypeString(); } return null; } public boolean isDefault(){ ResConfig resConfig = getResConfig(); if(resConfig!=null){ return resConfig.isDefault(); } return false; } public void setSpecReference(StringItem specReference){ TableEntry tableEntry = getTableEntry(); if(tableEntry == null){ return; } tableEntry.getHeader().setKey(specReference); } public void setSpecReference(int ref){ TableEntry tableEntry = getTableEntry(); if(tableEntry == null){ return; } tableEntry.getHeader().setKey(ref); } private Entry searchEntry(int resourceId){ if(resourceId==getResourceId()){ return this; } PackageBlock packageBlock= getPackageBlock(); if(packageBlock==null){ return null; } TableBlock tableBlock = packageBlock.getTableBlock(); if(tableBlock==null){ return null; } EntryGroup entryGroup = tableBlock.search(resourceId); if(entryGroup!=null){ return entryGroup.pickOne(); } return null; } public ResValue setValueAsRaw(ValueType valueType, int data){ TableEntry tableEntry = ensureTableEntry(false); ResValue resValue = (ResValue) tableEntry.getValue(); resValue.setTypeAndData(valueType, data); return resValue; } public ResValue setValueAsBoolean(boolean val){ int data = val?0xffffffff:0; return setValueAsRaw(ValueType.INT_BOOLEAN, data); } public ResValue setValueAsReference(int resourceId){ return setValueAsRaw(ValueType.REFERENCE, resourceId); } public ResValue setValueAsString(String str){ TableEntry tableEntry = ensureTableEntry(false); ResValue resValue = (ResValue) tableEntry.getValue(); resValue.setValueAsString(str); return resValue; } public SpecString getSpecString(){ TableEntry tableEntry = getTableEntry(); if(tableEntry == null){ return null; } PackageBlock packageBlock = getPackageBlock(); if(packageBlock == null){ return null; } return packageBlock.getSpecStringPool() .get(tableEntry.getHeader().getKey()); } public ResConfig getResConfig(){ TypeBlock typeBlock = getTypeBlock(); if(typeBlock!=null){ return typeBlock.getResConfig(); } return null; } public TypeBlock getTypeBlock(){ return getParent(TypeBlock.class); } private String getPackageName(){ PackageBlock packageBlock = getPackageBlock(); if(packageBlock!=null){ return packageBlock.getName(); } return null; } public PackageBlock getPackageBlock(){ return getParent(PackageBlock.class); } private TableEntry ensureTableEntry(boolean is_complex){ TableEntry tableEntry = getTableEntry(); if(tableEntry == null){ tableEntry = createTableEntry(is_complex); setTableEntry(tableEntry); return tableEntry; } if(is_complex){ if(tableEntry instanceof ResTableMapEntry){ return tableEntry; } tableEntry = createTableEntry(true); setTableEntry(tableEntry); return tableEntry; } tableEntry = createTableEntry(false); setTableEntry(tableEntry); return tableEntry; } public TableEntry getTableEntry(){ return mTableEntry; } public ValueHeader getHeader(){ TableEntry tableEntry = getTableEntry(); if(tableEntry!=null){ return tableEntry.getHeader(); } return null; } @Override public boolean isNull(){ return getTableEntry()==null; } @Override public void setNull(boolean is_null){ if(is_null){ setTableEntry(null); } } @Override public byte[] getBytes() { if(isNull()){ return null; } return getTableEntry().getBytes(); } @Override public int countBytes() { if(isNull()){ return 0; } return getTableEntry().countBytes(); } @Override public void onCountUpTo(BlockCounter counter) { if(counter.FOUND){ return; } if(counter.END==this){ counter.FOUND=true; return; } if(isNull()){ return; } counter.addCount(getTableEntry().countBytes()); } @Override protected int onWriteBytes(OutputStream stream) throws IOException { if(isNull()){ return 0; } return getTableEntry().writeBytes(stream); } @Override public void onReadBytes(BlockReader reader) throws IOException { TableEntry tableEntry = createTableEntry(reader); setTableEntry(tableEntry); tableEntry.readBytes(reader); } public boolean isComplex(){ return getTableEntry() instanceof ResTableMapEntry; } public void setTableEntry(TableEntry tableEntry){ if(tableEntry==this.mTableEntry){ return; } onTableEntryRemoved(); if(tableEntry==null){ return; } tableEntry.setIndex(0); tableEntry.setParent(this); this.mTableEntry = tableEntry; onTableEntryAdded(); } private void onTableEntryAdded(){ PackageBlock packageBlock = getPackageBlock(); if(packageBlock!=null){ packageBlock.onEntryAdded(this); } } private void onTableEntryRemoved(){ TableEntry exist = this.mTableEntry; if(exist == null){ return; } PackageBlock packageBlock = getPackageBlock(); if(packageBlock!=null){ packageBlock.removeEntryGroup(this); } exist.onRemoved(); exist.setIndex(-1); exist.setParent(null); this.mTableEntry = null; } private TableEntry createTableEntry(BlockReader reader) throws IOException { int startPosition = reader.getPosition(); reader.offset(2); boolean is_complex = (0x0001 & reader.readShort()) == 0x0001; reader.seek(startPosition); return createTableEntry(is_complex); } private TableEntry createTableEntry(boolean is_complex) { if(is_complex){ return new ResTableMapEntry(); }else { return new ResTableEntry(); } } @Override public JSONObject toJson() { if(isNull()){ return null; } return getTableEntry().toJson(); } @Override public void fromJson(JSONObject json) { if(json==null){ setNull(true); return; } boolean is_complex = json.optBoolean(ValueHeader.NAME_is_complex, false); TableEntry entry = createTableEntry(is_complex); setTableEntry(entry); entry.fromJson(json); } public void merge(Entry entry){ if(!shouldMerge(entry)){ return; } TableEntry tableEntry = entry.getTableEntry(); TableEntry existEntry = ensureTableEntry(tableEntry instanceof ResTableMapEntry); existEntry.merge(tableEntry); } private boolean shouldMerge(Entry coming){ if(coming == null || coming == this || coming.isNull()){ return false; } if(this.isNull()){ return true; } return coming.getTableEntry().shouldMerge(coming.getTableEntry()); } public String buildResourceName(int resourceId, char prefix, boolean includeType){ if(resourceId==0){ return null; } Entry entry=searchEntry(resourceId); return buildResourceName(entry, prefix, includeType); } public String buildResourceName(Entry entry, char prefix, boolean includeType){ if(entry==null){ return null; } String pkgName=entry.getPackageName(); if(getResourceId()==entry.getResourceId()){ pkgName=null; }else if(pkgName!=null){ if(pkgName.equals(this.getPackageName())){ pkgName=null; } } String type=null; if(includeType){ type=entry.getTypeName(); } String name=entry.getName(); return buildResourceName(prefix, pkgName, type, name); } public String getResourceName(){ return buildResourceName('@',null, getTypeName(), getName()); } public String getResourceName(char prefix){ return getResourceName(prefix, false, true); } public String getResourceName(char prefix, boolean includePackage, boolean includeType){ String pkg=includePackage?getPackageName():null; String type=includeType?getTypeName():null; return buildResourceName(prefix,pkg, type, getName()); } @Override public String toString(){ StringBuilder builder = new StringBuilder(); builder.append(String.format("0x%08x", getResourceId())); builder.append(' '); ResConfig resConfig = getResConfig(); if(resConfig!=null){ builder.append(resConfig); builder.append(' '); } if(isNull()){ builder.append("NULL"); return builder.toString(); } builder.append('@'); builder.append(getTypeName()); builder.append('/'); builder.append(getName()); return builder.toString(); } public static String buildResourceName(char prefix, String packageName, String type, String name){ if(name==null){ return null; } StringBuilder builder=new StringBuilder(); if(prefix!=0){ builder.append(prefix); } if(packageName!=null){ builder.append(packageName); builder.append(':'); } if(type!=null){ builder.append(type); builder.append('/'); } builder.append(name); return builder.toString(); } }