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); }
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; }
/** * Searches for and returns the {@link StyleResourceValue} from a given name. * * <p>The format of the name can be: * * <ul> * <li>[android:]<name> * <li>[android:]style/<name> * <li>@[android:]style/<name> * </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; }
/** * 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; }
@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); }