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

org.robolectric.res.android.AttributeResolution Maven / Gradle / Ivy

There is a newer version: 4.14.1
Show newest version
package org.robolectric.res.android;

import static org.robolectric.res.android.Errors.BAD_INDEX;
import static org.robolectric.res.android.Util.ALOGI;

import org.robolectric.res.android.ResourceTypes.Res_value;
import org.robolectric.util.Logger;

public class AttributeResolution {
  public static final boolean kThrowOnBadId = false;
  private static final boolean kDebugStyles = false;

  public static final int STYLE_NUM_ENTRIES = 6;
  public static final int STYLE_TYPE = 0;
  public static final int STYLE_DATA = 1;
  public static final int STYLE_ASSET_COOKIE = 2;
  public static final int STYLE_RESOURCE_ID = 3;
  public static final int STYLE_CHANGING_CONFIGURATIONS = 4;
  public static final int STYLE_DENSITY = 5;

  public static class BagAttributeFinder {

    private final ResTable.bag_entry[] bag_entries;
    private final int bagEndIndex;

    public BagAttributeFinder(ResTable.bag_entry[] bag_entries, int bagEndIndex) {
      this.bag_entries = bag_entries;
      this.bagEndIndex = bagEndIndex;
    }

    public ResTable.bag_entry find(int curIdent) {
      for (int curIndex = bagEndIndex - 1; curIndex >= 0; curIndex--) {
        if (bag_entries[curIndex].map.name.ident == curIdent) {
          return bag_entries[curIndex];
        }
      }
      return null;
    }
  }

  public static class XmlAttributeFinder {

    private ResXMLParser xmlParser;

    public XmlAttributeFinder(ResXMLParser xmlParser) {
      this.xmlParser = xmlParser;
    }

    public int find(int curIdent) {
      if (xmlParser == null) {
        return 0;
      }

      int attributeCount = xmlParser.getAttributeCount();
      for (int i = 0; i < attributeCount; i++) {
        if (xmlParser.getAttributeNameResID(i) == curIdent) {
          return i;
        }
      }
      return attributeCount;
    }
  }

  public static boolean ResolveAttrs(ResTableTheme theme, int defStyleAttr,
                                     int defStyleRes, int[] srcValues,
                                     int srcValuesLength, int[] attrs,
                                     int attrsLength, int[] outValues, int[] outIndices) {
    if (kDebugStyles) {
      ALOGI(
          "APPLY STYLE: theme=%s defStyleAttr=0x%x defStyleRes=0x%x",
          theme, defStyleAttr, defStyleRes);
    }

    final ResTable res = theme.getResTable();
    ResTable_config config = new ResTable_config();
    Res_value value;

    int indicesIdx = 0;

    // Load default style from attribute, if specified...
    Ref defStyleBagTypeSetFlags = new Ref<>(0);
    if (defStyleAttr != 0) {
      Ref valueRef = new Ref<>(null);
      if (theme.GetAttribute(defStyleAttr, valueRef, defStyleBagTypeSetFlags) >= 0) {
        value = valueRef.get();
        if (value.dataType == Res_value.TYPE_REFERENCE) {
          defStyleRes = value.data;
        }
      }
    }

    // Now lock down the resource object and start pulling stuff from it.
    res.lock();

    // Retrieve the default style bag, if requested.
    final Ref defStyleStart = new Ref<>(null);
    Ref defStyleTypeSetFlags = new Ref<>(0);
    int bagOff = defStyleRes != 0
        ? res.getBagLocked(defStyleRes, defStyleStart, defStyleTypeSetFlags) : -1;
    defStyleTypeSetFlags.set(defStyleTypeSetFlags.get() | defStyleBagTypeSetFlags.get());
//    const ResTable::bag_entry* const defStyleEnd = defStyleStart + (bagOff >= 0 ? bagOff : 0);
    final int defStyleEnd = (bagOff >= 0 ? bagOff : 0);
    BagAttributeFinder defStyleAttrFinder = new BagAttributeFinder(defStyleStart.get(), defStyleEnd);

    // Now iterate through all of the attributes that the client has requested,
    // filling in each with whatever data we can find.
    int destOffset = 0;
    for (int ii=0; ii 0 && srcValues[ii] != 0) {
        value = new Res_value((byte) Res_value.TYPE_ATTRIBUTE, srcValues[ii]);
        if (kDebugStyles) {
          ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data);
        }
      } else {
        final ResTable.bag_entry defStyleEntry = defStyleAttrFinder.find(curIdent);
        if (defStyleEntry != null) {
          block = defStyleEntry.stringBlock;
          typeSetFlags = defStyleTypeSetFlags.get();
          value = defStyleEntry.map.value;
          if (kDebugStyles) {
            ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
          }
        }
      }

      int resid = 0;
      Ref valueRef = new Ref<>(value);
      Ref residRef = new Ref<>(resid);
      Ref typeSetFlagsRef = new Ref<>(typeSetFlags);
      Ref configRef = new Ref<>(config);
      if (value.dataType != Res_value.TYPE_NULL) {
        // Take care of resolving the found resource to its final value.
        int newBlock = theme.resolveAttributeReference(valueRef, block,
            residRef, typeSetFlagsRef, configRef);
        value = valueRef.get();
        resid = residRef.get();
        typeSetFlags = typeSetFlagsRef.get();
        config = configRef.get();
        if (newBlock >= 0) block = newBlock;
        if (kDebugStyles) {
          ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
        }
      } else {
        // If we still don't have a value for this attribute, try to find
        // it in the theme!
        int newBlock = theme.GetAttribute(curIdent, valueRef, typeSetFlagsRef);
        value = valueRef.get();
        typeSetFlags = typeSetFlagsRef.get();

        if (newBlock >= 0) {
          if (kDebugStyles) {
            ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
          }
          newBlock = res.resolveReference(valueRef, newBlock, residRef, typeSetFlagsRef, configRef);
          value = valueRef.get();
          resid = residRef.get();
          typeSetFlags = typeSetFlagsRef.get();
          config = configRef.get();
          if (kThrowOnBadId) {
            if (newBlock == BAD_INDEX) {
              throw new IllegalStateException("Bad resource!");
            }
          }
          if (newBlock >= 0) block = newBlock;
          if (kDebugStyles) {
            ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
          }
        }
      }

      // Deal with the special @null value -- it turns back to TYPE_NULL.
      if (value.dataType == Res_value.TYPE_REFERENCE && value.data == 0) {
        if (kDebugStyles) {
          ALOGI("-> Setting to @null!");
        }
        value = Res_value.NULL_VALUE;
        block = -1;
      }

      if (kDebugStyles) {
        ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", curIdent, value.dataType,
            value.data);
      }

      // Write the final value back to Java.
      outValues[destOffset + STYLE_TYPE] = value.dataType;
      outValues[destOffset + STYLE_DATA] = value.data;
      outValues[destOffset + STYLE_ASSET_COOKIE] =
          block != -1 ? res.getTableCookie(block) : -1;
      outValues[destOffset + STYLE_RESOURCE_ID] = resid;
      outValues[destOffset + STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
      outValues[destOffset + STYLE_DENSITY] = config.density;

      if (outIndices != null && value.dataType != Res_value.TYPE_NULL) {
        indicesIdx++;
        outIndices[indicesIdx] = ii;
      }

      destOffset += STYLE_NUM_ENTRIES;
    }

    res.unlock();

    if (outIndices != null) {
      outIndices[0] = indicesIdx;
    }
    return true;
  }

  public static void ApplyStyle(ResTableTheme theme, ResXMLParser xmlParser, int defStyleAttr, int defStyleRes,
                                int[] attrs, int attrsLength, int[] outValues, int[] outIndices) {
    if (kDebugStyles) {
      ALOGI("APPLY STYLE: theme=%s defStyleAttr=0x%x defStyleRes=0x%x xml=%s",
          theme, defStyleAttr, defStyleRes, xmlParser);
    }

    final ResTable res = theme.getResTable();
    Ref config = new Ref<>(new ResTable_config());
    Ref value = new Ref<>(new Res_value());

    int indices_idx = 0;

    // Load default style from attribute, if specified...
    Ref defStyleBagTypeSetFlags = new Ref<>(0);
    if (defStyleAttr != 0) {
      if (theme.GetAttribute(defStyleAttr, value, defStyleBagTypeSetFlags) >= 0) {
        if (value.get().dataType == DataType.REFERENCE.code()) {
          defStyleRes = value.get().data;
        }
      }
    }

    // Retrieve the style class associated with the current XML tag.
    int style = 0;
    Ref styleBagTypeSetFlags = new Ref<>(0);
    if (xmlParser != null) {
      int idx = xmlParser.indexOfStyle();
      if (idx >= 0 && xmlParser.getAttributeValue(idx, value) >= 0) {
        if (value.get().dataType == DataType.ATTRIBUTE.code()) {
          if (theme.GetAttribute(value.get().data, value, styleBagTypeSetFlags) < 0) {
            value.set(value.get().withType(DataType.NULL.code()));
          }
        }
        if (value.get().dataType == DataType.REFERENCE.code()) {
          style = value.get().data;
        }
      }
    }

    // Now lock down the resource object and start pulling stuff from it.
    res.lock();

    // Retrieve the default style bag, if requested.
    final Ref defStyleAttrStart = new Ref<>(null);
    Ref defStyleTypeSetFlags = new Ref<>(0);
    int bagOff = defStyleRes != 0
        ? res.getBagLocked(defStyleRes, defStyleAttrStart, defStyleTypeSetFlags)
        : -1;
    defStyleTypeSetFlags.set(defStyleTypeSetFlags.get() | defStyleBagTypeSetFlags.get());
    // const ResTable::bag_entry* defStyleAttrEnd = defStyleAttrStart + (bagOff >= 0 ? bagOff : 0);
    final ResTable.bag_entry defStyleAttrEnd = null;
    // BagAttributeFinder defStyleAttrFinder = new BagAttributeFinder(defStyleAttrStart, defStyleAttrEnd);
    BagAttributeFinder defStyleAttrFinder = new BagAttributeFinder(defStyleAttrStart.get(), bagOff);

    // Retrieve the style class bag, if requested.
    final Ref styleAttrStart = new Ref<>(null);
    Ref styleTypeSetFlags = new Ref<>(0);
    bagOff = style != 0
        ? res.getBagLocked(style, styleAttrStart, styleTypeSetFlags)
        : -1;
    styleTypeSetFlags.set(styleTypeSetFlags.get() | styleBagTypeSetFlags.get());
    // final ResTable::bag_entry* final styleAttrEnd = styleAttrStart + (bagOff >= 0 ? bagOff : 0);
    final ResTable.bag_entry styleAttrEnd = null;
    //BagAttributeFinder styleAttrFinder = new BagAttributeFinder(styleAttrStart, styleAttrEnd);
    BagAttributeFinder styleAttrFinder = new BagAttributeFinder(styleAttrStart.get(), bagOff);

    // Retrieve the XML attributes, if requested.
    final int kXmlBlock = 0x10000000;
    XmlAttributeFinder xmlAttrFinder = new XmlAttributeFinder(xmlParser);
    final int xmlAttrEnd = xmlParser != null ? xmlParser.getAttributeCount() : 0;

    // Now iterate through all of the attributes that the client has requested,
    // filling in each with whatever data we can find.
    for (int ii = 0; ii < attrsLength; ii++) {
      final int curIdent = attrs[ii];

      if (kDebugStyles) {
        ALOGI("RETRIEVING ATTR 0x%08x...", curIdent);
      }

      int block = kXmlBlock;
      Ref typeSetFlags = new Ref<>(0);

      value.set(Res_value.NULL_VALUE);
      config.get().density = 0;

      // Try to find a value for this attribute...  we prioritize values
      // coming from, first XML attributes, then XML style, then default
      // style, and finally the theme.

      // Walk through the xml attributes looking for the requested attribute.
      final int xmlAttrIdx = xmlAttrFinder.find(curIdent);
      if (xmlAttrIdx != xmlAttrEnd) {
        // We found the attribute we were looking for.
        xmlParser.getAttributeValue(xmlAttrIdx, value);
        if (kDebugStyles) {
          ALOGI("-> From XML: type=0x%x, data=0x%08x", value.get().dataType, value.get().data);
        }
      }

      if (value.get().dataType == DataType.NULL.code() && value.get().data != Res_value.DATA_NULL_EMPTY) {
        // Walk through the style class values looking for the requested attribute.
        final ResTable.bag_entry styleAttrEntry = styleAttrFinder.find(curIdent);
        if (styleAttrEntry != styleAttrEnd) {
          // We found the attribute we were looking for.
          block = styleAttrEntry.stringBlock;
          typeSetFlags.set(styleTypeSetFlags.get());
          value.set(styleAttrEntry.map.value);
          if (kDebugStyles) {
            ALOGI("-> From style: type=0x%x, data=0x%08x", value.get().dataType, value.get().data);
          }
        }
      }

      if (value.get().dataType == DataType.NULL.code() && value.get().data != Res_value.DATA_NULL_EMPTY) {
        // Walk through the default style values looking for the requested attribute.
        final ResTable.bag_entry defStyleAttrEntry = defStyleAttrFinder.find(curIdent);
        if (defStyleAttrEntry != defStyleAttrEnd) {
          // We found the attribute we were looking for.
          block = defStyleAttrEntry.stringBlock;
          typeSetFlags.set(styleTypeSetFlags.get());
          value.set(defStyleAttrEntry.map.value);
          if (kDebugStyles) {
            ALOGI(
                "-> From def style: type=0x%x, data=0x%08x",
                value.get().dataType, value.get().data);
          }
        }
      }

      Ref resid = new Ref<>(0);
      if (value.get().dataType != DataType.NULL.code()) {
        // Take care of resolving the found resource to its final value.
        int newBlock = theme.resolveAttributeReference(value, block,
            resid, typeSetFlags, config);
        if (newBlock >= 0) {
          block = newBlock;
        }

        if (kDebugStyles) {
          ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.get().dataType, value.get().data);
        }
      } else if (value.get().data != Res_value.DATA_NULL_EMPTY) {
        // If we still don't have a value for this attribute, try to find it in the theme!
        int newBlock = theme.GetAttribute(curIdent, value, typeSetFlags);
        if (newBlock >= 0) {
          if (kDebugStyles) {
            ALOGI("-> From theme: type=0x%x, data=0x%08x", value.get().dataType, value.get().data);
          }
          newBlock = res.resolveReference(value, newBlock, resid, typeSetFlags, config);
          if (newBlock >= 0) {
            block = newBlock;
          }

          if (kDebugStyles) {
            ALOGI(
                "-> Resolved theme: type=0x%x, data=0x%08x",
                value.get().dataType, value.get().data);
          }
        }
      }

      // Deal with the special @null value -- it turns back to TYPE_NULL.
      if (value.get().dataType == DataType.REFERENCE.code() && value.get().data == 0) {
        if (kDebugStyles) {
          ALOGI(". Setting to @null!");
        }
        value.set(Res_value.NULL_VALUE);
        block = kXmlBlock;
      }

      if (kDebugStyles) {
        ALOGI(
            "Attribute 0x%08x: type=0x%x, data=0x%08x",
            curIdent, value.get().dataType, value.get().data);
      }

      // Write the final value back to Java.
      int destIndex = ii * STYLE_NUM_ENTRIES;
      Res_value res_value = value.get();
      outValues[destIndex + STYLE_TYPE] = res_value.dataType;
      outValues[destIndex + STYLE_DATA] = res_value.data;
      outValues[destIndex + STYLE_ASSET_COOKIE] =
          block != kXmlBlock ? res.getTableCookie(block) : -1;
      outValues[destIndex + STYLE_RESOURCE_ID] = resid.get();
      outValues[destIndex + STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags.get();
      outValues[destIndex + STYLE_DENSITY] = config.get().density;

      if (res_value.dataType != DataType.NULL.code() || res_value.data == Res_value.DATA_NULL_EMPTY) {
        indices_idx++;

        // out_indices must NOT be nullptr.
        outIndices[indices_idx] = ii;
      }

      if (res_value.dataType == DataType.ATTRIBUTE.code()) {
        ResTable.ResourceName attrName = new ResTable.ResourceName();
        ResTable.ResourceName attrRefName = new ResTable.ResourceName();
        boolean gotName = res.getResourceName(curIdent, true, attrName);
        boolean gotRefName = res.getResourceName(res_value.data, true, attrRefName);
        Logger.warn(
            "Failed to resolve attribute lookup: %s=\"?%s\"; theme: %s",
            gotName ? attrName : "unknown", gotRefName ? attrRefName : "unknown",
            theme);
      }

//      out_values += STYLE_NUM_ENTRIES;
    }

    res.unlock();

    // out_indices must NOT be nullptr.
    outIndices[0] = indices_idx;
  }

  public static boolean RetrieveAttributes(ResTable res, ResXMLParser xmlParser, int[] attrs, int attrsLength, int[] outValues, int[] outIndices) {
    Ref config = new Ref<>(new ResTable_config());
    Ref value = new Ref<>(null);

    int indices_idx = 0;

    // Now lock down the resource object and start pulling stuff from it.
    res.lock();

    // Retrieve the XML attributes, if requested.
    final int xmlAttrCount = xmlParser.getAttributeCount();
    int ix=0;
    int curXmlAttr = xmlParser.getAttributeNameResID(ix);

    final int kXmlBlock = 0x10000000;

    // Now iterate through all of the attributes that the client has requested,
    // filling in each with whatever data we can find.
    int baseDest = 0;
    for (int ii=0; ii typeSetFlags = new Ref<>(0);

      value.set(Res_value.NULL_VALUE);
      config.get().density = 0;

      // Try to find a value for this attribute...
      // Skip through XML attributes until the end or the next possible match.
      while (ix < xmlAttrCount && curIdent > curXmlAttr) {
        ix++;
        curXmlAttr = xmlParser.getAttributeNameResID(ix);
      }
      // Retrieve the current XML attribute if it matches, and step to next.
      if (ix < xmlAttrCount && curIdent == curXmlAttr) {
        block = kXmlBlock;
        xmlParser.getAttributeValue(ix, value);
        ix++;
        curXmlAttr = xmlParser.getAttributeNameResID(ix);
      }

      //printf("Attribute 0x%08x: type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
      Ref resid = new Ref<>(0);
      if (value.get().dataType != Res_value.TYPE_NULL) {
        // Take care of resolving the found resource to its final value.
        //printf("Resolving attribute reference\n");
        int newBlock = res.resolveReference(value, block, resid,
            typeSetFlags, config);
        if (newBlock >= 0) block = newBlock;
      }

      // Deal with the special @null value -- it turns back to TYPE_NULL.
      if (value.get().dataType == Res_value.TYPE_REFERENCE && value.get().data == 0) {
        value.set(Res_value.NULL_VALUE);
        block = kXmlBlock;
      }

      //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);

      // Write the final value back to Java.
      outValues[baseDest + STYLE_TYPE] = value.get().dataType;
      outValues[baseDest + STYLE_DATA] = value.get().data;
      outValues[baseDest + STYLE_ASSET_COOKIE] =
          block != kXmlBlock ? res.getTableCookie(block) : -1;
      outValues[baseDest + STYLE_RESOURCE_ID] = resid.get();
      outValues[baseDest + STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags.get();
      outValues[baseDest + STYLE_DENSITY] = config.get().density;

      if (outIndices != null &&
          (value.get().dataType != Res_value.TYPE_NULL || value.get().data == Res_value.DATA_NULL_EMPTY)) {
        indices_idx++;
        outIndices[indices_idx] = ii;
      }

//      dest += STYLE_NUM_ENTRIES;
      baseDest += STYLE_NUM_ENTRIES;
    }

    res.unlock();

    if (outIndices != null) {
      outIndices[0] = indices_idx;
    }

    return true;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy