Beispiel #1
0
  private ResourceValue resolveResValue(ResourceValue resValue, int depth) {
    if (resValue == null) {
      return null;
    }

    // if the resource value is null, we simply return it.
    String value = resValue.getValue();
    if (value == null) {
      return resValue;
    }

    // else attempt to find another ResourceValue referenced by this one.
    ResourceValue resolvedResValue = findResValue(value, resValue.isFramework());

    // if the value did not reference anything, then we simply return the input value
    if (resolvedResValue == null) {
      return resValue;
    }

    // detect potential loop due to mishandled namespace in attributes
    if (resValue == resolvedResValue || depth >= MAX_RESOURCE_INDIRECTION) {
      if (mLogger != null) {
        mLogger.error(
            LayoutLog.TAG_BROKEN,
            String.format(
                "Potential stack overflow trying to resolve '%s': cyclic resource definitions? Render may not be accurate.",
                value),
            null);
      }
      return resValue;
    }

    // otherwise, we attempt to resolve this new value as well
    return resolveResValue(resolvedResValue, depth + 1);
  }
Beispiel #2
0
  private ResourceValue findItemInStyle(
      StyleResourceValue style, String itemName, boolean isFrameworkAttr, int depth) {
    ResourceValue item = style.findValue(itemName, isFrameworkAttr);

    // if we didn't find it, we look in the parent style (if applicable)
    //noinspection VariableNotUsedInsideIf
    if (item == null) {
      StyleResourceValue parentStyle = mStyleInheritanceMap.get(style);
      if (parentStyle != null) {
        if (depth >= MAX_RESOURCE_INDIRECTION) {
          if (mLogger != null) {
            mLogger.error(
                LayoutLog.TAG_BROKEN,
                String.format(
                    "Cyclic style parent definitions: %1$s", computeCyclicStyleChain(style)),
                null);
          }

          return null;
        }

        return findItemInStyle(parentStyle, itemName, isFrameworkAttr, depth + 1);
      }
    }

    return item;
  }
Beispiel #3
0
  /**
   * Searches for and returns the {@link StyleResourceValue} from a given name.
   *
   * <p>The format of the name can be:
   *
   * <ul>
   *   <li>[android:]&lt;name&gt;
   *   <li>[android:]style/&lt;name&gt;
   *   <li>@[android:]style/&lt;name&gt;
   * </ul>
   *
   * @param parentName the name of the style.
   * @param inProjectStyleMap the project style map. Can be <code>null</code>
   * @param inFrameworkStyleMap the framework style map.
   * @return The matching {@link StyleResourceValue} object or <code>null</code> if not found.
   */
  private StyleResourceValue getStyle(
      String parentName,
      Map<String, ResourceValue> inProjectStyleMap,
      Map<String, ResourceValue> inFrameworkStyleMap) {
    boolean frameworkOnly = false;

    String name = parentName;

    // remove the useless @ if it's there
    if (name.startsWith(PREFIX_RESOURCE_REF)) {
      name = name.substring(PREFIX_RESOURCE_REF.length());
    }

    // check for framework identifier.
    if (name.startsWith(PREFIX_ANDROID)) {
      frameworkOnly = true;
      name = name.substring(PREFIX_ANDROID.length());
    }

    // at this point we could have the format <type>/<name>. we want only the name as long as
    // the type is style.
    if (name.startsWith(REFERENCE_STYLE)) {
      name = name.substring(REFERENCE_STYLE.length());
    } else if (name.indexOf('/') != -1) {
      return null;
    }

    ResourceValue parent = null;

    // if allowed, search in the project resources.
    if (!frameworkOnly && inProjectStyleMap != null) {
      parent = inProjectStyleMap.get(name);
    }

    // if not found, then look in the framework resources.
    if (parent == null) {
      if (inFrameworkStyleMap == null) {
        return null;
      }
      parent = inFrameworkStyleMap.get(name);
    }

    // make sure the result is the proper class type and return it.
    if (parent instanceof StyleResourceValue) {
      return (StyleResourceValue) parent;
    }

    if (mLogger != null) {
      mLogger.error(
          LayoutLog.TAG_RESOURCES_RESOLVE,
          String.format("Unable to resolve parent style name: %s", parentName),
          null /*data*/);
    }

    return null;
  }
Beispiel #4
0
  /**
   * Searches for, and returns a {@link ResourceValue} by its name, and type.
   *
   * @param resType the type of the resource
   * @param resName the name of the resource
   * @param frameworkOnly if <code>true</code>, the method does not search in the project resources
   */
  private ResourceValue findResValue(ResourceType resType, String resName, boolean frameworkOnly) {
    // map of ResourceValue for the given type
    Map<String, ResourceValue> typeMap;

    // if allowed, search in the project resources first.
    if (!frameworkOnly) {
      typeMap = mProjectResources.get(resType);
      ResourceValue item = typeMap.get(resName);
      if (item != null) {
        return item;
      }
    }

    // now search in the framework resources.
    typeMap = mFrameworkResources.get(resType);
    ResourceValue item = typeMap.get(resName);
    if (item != null) {
      return item;
    }

    // if it was not found and the type is an id, it is possible that the ID was
    // generated dynamically when compiling the framework resources.
    // Look for it in the R map.
    if (mFrameworkProvider != null && resType == ResourceType.ID) {
      if (mFrameworkProvider.getId(resType, resName) != null) {
        return new ResourceValue(resType, resName, true);
      }
    }

    // didn't find the resource anywhere.
    if (mLogger != null) {
      mLogger.warning(
          LayoutLog.TAG_RESOURCES_RESOLVE,
          "Couldn't resolve resource @"
              + (frameworkOnly ? "android:" : "")
              + resType
              + "/"
              + resName,
          new ResourceValue(resType, resName, frameworkOnly));
    }
    return null;
  }
Beispiel #5
0
  @Override
  public ResourceValue findResValue(String reference, boolean forceFrameworkOnly) {
    if (reference == null) {
      return null;
    }

    ResourceUrl resource = ResourceUrl.parse(reference);
    if (resource != null && resource.hasValidName()) {
      if (resource.theme) {
        // no theme? no need to go further!
        if (mDefaultTheme == null) {
          return null;
        }

        if (resource.type != ResourceType.ATTR) {
          // At this time, no support for ?type/name where type is not "attr"
          return null;
        }

        // Now look for the item in the theme, starting with the current one.
        ResourceValue item =
            findItemInTheme(resource.name, forceFrameworkOnly || resource.framework);
        if (item == null && mLogger != null) {
          mLogger.warning(
              LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR,
              String.format("Couldn't find theme resource %1$s for the current theme", reference),
              new ResourceValue(ResourceType.ATTR, reference, resource.framework));
        }

        return item;
      } else {
        return findResValue(resource.type, resource.name, forceFrameworkOnly || resource.framework);
      }
    }

    // Looks like the value didn't reference anything. Return null.
    return null;
  }
  /**
   * Instantiate a class object, using a specific constructor and parameters.
   *
   * @param clazz the class to instantiate
   * @param constructorSignature the signature of the constructor to use
   * @param constructorParameters the parameters to use in the constructor.
   * @return A new class object, created using a specific constructor and parameters.
   * @throws Exception
   */
  @SuppressWarnings("unchecked")
  private Object instantiateClass(
      Class<?> clazz, Class[] constructorSignature, Object[] constructorParameters)
      throws Exception {
    Constructor<?> constructor = null;

    try {
      constructor = clazz.getConstructor(constructorSignature);

    } catch (NoSuchMethodException e) {
      // Custom views can either implement a 3-parameter, 2-parameter or a
      // 1-parameter. Let's synthetically build and try all the alternatives.
      // That's kind of like switching to the other box.
      //
      // The 3-parameter constructor takes the following arguments:
      // ...(Context context, AttributeSet attrs, int defStyle)

      int n = constructorSignature.length;
      if (n == 0) {
        // There is no parameter-less constructor. Nobody should ask for one.
        throw e;
      }

      for (int i = 3; i >= 1; i--) {
        if (i == n) {
          // Let's skip the one we know already fails
          continue;
        }
        Class[] sig = new Class[i];
        Object[] params = new Object[i];

        int k = i;
        if (n < k) {
          k = n;
        }
        System.arraycopy(constructorSignature, 0, sig, 0, k);
        System.arraycopy(constructorParameters, 0, params, 0, k);

        for (k++; k <= i; k++) {
          if (k == 2) {
            // Parameter 2 is the AttributeSet
            sig[k - 1] = clazz.getClassLoader().loadClass("android.util.AttributeSet");
            params[k - 1] = null;

          } else if (k == 3) {
            // Parameter 3 is the int defstyle
            sig[k - 1] = int.class;
            params[k - 1] = 0;
          }
        }

        constructorSignature = sig;
        constructorParameters = params;

        try {
          // Try again...
          constructor = clazz.getConstructor(constructorSignature);
          if (constructor != null) {
            // Found a suitable constructor, now let's use it.
            // (But let's warn the user if the simple View constructor was found
            // since Unexpected Things may happen if the attribute set constructors
            // are not found)
            if (constructorSignature.length < 2 && mLogger != null) {
              mLogger.warning(
                  "wrongconstructor", //$NON-NLS-1$
                  String.format(
                      "Custom view %1$s is not using the 2- or 3-argument "
                          + "View constructors; XML attributes will not work",
                      clazz.getSimpleName()),
                  null /*data*/);
            }
            break;
          }
        } catch (NoSuchMethodException e1) {
          // pass
        }
      }

      // If all the alternatives failed, throw the initial exception.
      if (constructor == null) {
        throw e;
      }
    }

    constructor.setAccessible(true);
    return constructor.newInstance(constructorParameters);
  }