diff options
Diffstat (limited to 'sdk_common/src/com/android/ide/common/resources/ResourceResolver.java')
-rw-r--r-- | sdk_common/src/com/android/ide/common/resources/ResourceResolver.java | 590 |
1 files changed, 0 insertions, 590 deletions
diff --git a/sdk_common/src/com/android/ide/common/resources/ResourceResolver.java b/sdk_common/src/com/android/ide/common/resources/ResourceResolver.java deleted file mode 100644 index 219c93f..0000000 --- a/sdk_common/src/com/android/ide/common/resources/ResourceResolver.java +++ /dev/null @@ -1,590 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * 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.android.ide.common.resources; - -import static com.android.SdkConstants.ANDROID_PREFIX; -import static com.android.SdkConstants.ANDROID_THEME_PREFIX; -import static com.android.SdkConstants.PREFIX_ANDROID; -import static com.android.SdkConstants.PREFIX_RESOURCE_REF; -import static com.android.SdkConstants.PREFIX_THEME_REF; -import static com.android.SdkConstants.REFERENCE_STYLE; - -import com.android.ide.common.rendering.api.LayoutLog; -import com.android.ide.common.rendering.api.RenderResources; -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.rendering.api.StyleResourceValue; -import com.android.resources.ResourceType; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -public class ResourceResolver extends RenderResources { - - private final Map<ResourceType, Map<String, ResourceValue>> mProjectResources; - private final Map<ResourceType, Map<String, ResourceValue>> mFrameworkResources; - - private final Map<StyleResourceValue, StyleResourceValue> mStyleInheritanceMap = - new HashMap<StyleResourceValue, StyleResourceValue>(); - - private StyleResourceValue mTheme; - - private FrameworkResourceIdProvider mFrameworkProvider; - private LayoutLog mLogger; - private String mThemeName; - private boolean mIsProjectTheme; - - private ResourceResolver( - Map<ResourceType, Map<String, ResourceValue>> projectResources, - Map<ResourceType, Map<String, ResourceValue>> frameworkResources) { - mProjectResources = projectResources; - mFrameworkResources = frameworkResources; - } - - /** - * Creates a new {@link ResourceResolver} object. - * - * @param projectResources the project resources. - * @param frameworkResources the framework resources. - * @param themeName the name of the current theme. - * @param isProjectTheme Is this a project theme? - * @return a new {@link ResourceResolver} - */ - public static ResourceResolver create( - Map<ResourceType, Map<String, ResourceValue>> projectResources, - Map<ResourceType, Map<String, ResourceValue>> frameworkResources, - String themeName, boolean isProjectTheme) { - - ResourceResolver resolver = new ResourceResolver( - projectResources, frameworkResources); - - resolver.computeStyleMaps(themeName, isProjectTheme); - - return resolver; - } - - // ---- Methods to help dealing with older LayoutLibs. - - public String getThemeName() { - return mThemeName; - } - - public boolean isProjectTheme() { - return mIsProjectTheme; - } - - public Map<ResourceType, Map<String, ResourceValue>> getProjectResources() { - return mProjectResources; - } - - public Map<ResourceType, Map<String, ResourceValue>> getFrameworkResources() { - return mFrameworkResources; - } - - // ---- RenderResources Methods - - @Override - public void setFrameworkResourceIdProvider(FrameworkResourceIdProvider provider) { - mFrameworkProvider = provider; - } - - @Override - public void setLogger(LayoutLog logger) { - mLogger = logger; - } - - @Override - public StyleResourceValue getCurrentTheme() { - return mTheme; - } - - @Override - public StyleResourceValue getTheme(String name, boolean frameworkTheme) { - ResourceValue theme = null; - - if (frameworkTheme) { - Map<String, ResourceValue> frameworkStyleMap = mFrameworkResources.get( - ResourceType.STYLE); - theme = frameworkStyleMap.get(name); - } else { - Map<String, ResourceValue> projectStyleMap = mProjectResources.get(ResourceType.STYLE); - theme = projectStyleMap.get(name); - } - - if (theme instanceof StyleResourceValue) { - return (StyleResourceValue) theme; - } - - return null; - } - - @Override - public boolean themeIsParentOf(StyleResourceValue parentTheme, StyleResourceValue childTheme) { - do { - childTheme = mStyleInheritanceMap.get(childTheme); - if (childTheme == null) { - return false; - } else if (childTheme == parentTheme) { - return true; - } - } while (true); - } - - @Override - public ResourceValue getFrameworkResource(ResourceType resourceType, String resourceName) { - return getResource(resourceType, resourceName, mFrameworkResources); - } - - @Override - public ResourceValue getProjectResource(ResourceType resourceType, String resourceName) { - return getResource(resourceType, resourceName, mProjectResources); - } - - @Override - @Deprecated - public ResourceValue findItemInStyle(StyleResourceValue style, String attrName) { - // this method is deprecated because it doesn't know about the namespace of the - // attribute so we search for the project namespace first and then in the - // android namespace if needed. - ResourceValue item = findItemInStyle(style, attrName, false /*isFrameworkAttr*/); - if (item == null) { - item = findItemInStyle(style, attrName, true /*isFrameworkAttr*/); - } - - return item; - } - - @Override - public ResourceValue findItemInStyle(StyleResourceValue style, String itemName, - boolean isFrameworkAttr) { - ResourceValue item = style.findValue(itemName, isFrameworkAttr); - - // if we didn't find it, we look in the parent style (if applicable) - if (item == null && mStyleInheritanceMap != null) { - StyleResourceValue parentStyle = mStyleInheritanceMap.get(style); - if (parentStyle != null) { - return findItemInStyle(parentStyle, itemName, isFrameworkAttr); - } - } - - return item; - } - - @Override - public ResourceValue findResValue(String reference, boolean forceFrameworkOnly) { - if (reference == null) { - return null; - } - if (reference.startsWith(PREFIX_THEME_REF) - && reference.length() > PREFIX_THEME_REF.length()) { - // no theme? no need to go further! - if (mTheme == null) { - return null; - } - - boolean frameworkOnly = false; - - // eliminate the prefix from the string - String originalReference = reference; - if (reference.startsWith(ANDROID_THEME_PREFIX)) { - frameworkOnly = true; - reference = reference.substring(ANDROID_THEME_PREFIX.length()); - } else { - reference = reference.substring(PREFIX_THEME_REF.length()); - } - - // at this point, value can contain type/name (drawable/foo for instance). - // split it to make sure. - String[] segments = reference.split("/"); - - // we look for the referenced item name. - String referenceName = null; - - if (segments.length == 2) { - // there was a resType in the reference. If it's attr, we ignore it - // else, we assert for now. - if (ResourceType.ATTR.getName().equals(segments[0])) { - referenceName = segments[1]; - } else { - // At this time, no support for ?type/name where type is not "attr" - return null; - } - } else { - // it's just an item name. - referenceName = segments[0]; - - // Make sure it looks like a resource name; if not, it could just be a string - // which starts with a ? - if (!Character.isJavaIdentifierStart(referenceName.charAt(0))) { - return null; - } - for (int i = 1, n = referenceName.length(); i < n; i++) { - char c = referenceName.charAt(i); - if (!Character.isJavaIdentifierPart(c) && c != '.') { - return null; - } - } - } - - // now we look for android: in the referenceName in order to support format - // such as: ?attr/android:name - if (referenceName.startsWith(PREFIX_ANDROID)) { - frameworkOnly = true; - referenceName = referenceName.substring(PREFIX_ANDROID.length()); - } - - // Now look for the item in the theme, starting with the current one. - ResourceValue item = findItemInStyle(mTheme, referenceName, - forceFrameworkOnly || frameworkOnly); - - 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, originalReference, frameworkOnly)); - } - - return item; - } else if (reference.startsWith(PREFIX_RESOURCE_REF)) { - boolean frameworkOnly = false; - - // check for the specific null reference value. - if (REFERENCE_NULL.equals(reference)) { - return null; - } - - // Eliminate the prefix from the string. - if (reference.startsWith(ANDROID_PREFIX)) { - frameworkOnly = true; - reference = reference.substring(ANDROID_PREFIX.length()); - } else { - reference = reference.substring(PREFIX_RESOURCE_REF.length()); - } - - // at this point, value contains type/[android:]name (drawable/foo for instance) - String[] segments = reference.split("/"); - if (segments.length != 2) { - return null; - } - - // now we look for android: in the resource name in order to support format - // such as: @drawable/android:name - String referenceName = segments[1]; - if (referenceName.startsWith(PREFIX_ANDROID)) { - frameworkOnly = true; - referenceName = referenceName.substring(PREFIX_ANDROID.length()); - } - - ResourceType type = ResourceType.getEnum(segments[0]); - - // unknown type? - if (type == null) { - return null; - } - - // Make sure it looks like a resource name; if not, it could just be a string - // which starts with a ? - if (!Character.isJavaIdentifierStart(referenceName.charAt(0))) { - return null; - } - for (int i = 1, n = referenceName.length(); i < n; i++) { - char c = referenceName.charAt(i); - if (!Character.isJavaIdentifierPart(c) && c != '.') { - return null; - } - } - - return findResValue(type, referenceName, - forceFrameworkOnly ? true :frameworkOnly); - } - - // Looks like the value didn't reference anything. Return null. - return null; - } - - @Override - public ResourceValue resolveValue(ResourceType type, String name, String value, - boolean isFrameworkValue) { - if (value == null) { - return null; - } - - // get the ResourceValue referenced by this value - ResourceValue resValue = findResValue(value, isFrameworkValue); - - // if resValue is null, but value is not null, this means it was not a reference. - // we return the name/value wrapper in a ResourceValue. the isFramework flag doesn't - // matter. - if (resValue == null) { - return new ResourceValue(type, name, value, isFrameworkValue); - } - - // we resolved a first reference, but we need to make sure this isn't a reference also. - return resolveResValue(resValue); - } - - @Override - public ResourceValue resolveResValue(ResourceValue resValue) { - 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) { - if (mLogger != null) { - mLogger.error(LayoutLog.TAG_BROKEN, - String.format("Potential stackoverflow trying to resolve '%s'. Render may not be accurate.", value), - null); - } - return resValue; - } - - // otherwise, we attempt to resolve this new value as well - return resolveResValue(resolvedResValue); - } - - // ---- Private helper methods. - - /** - * 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 ResouceValue for the given type - Map<String, ResourceValue> typeMap; - - // if allowed, search in the project resources first. - if (frameworkOnly == false) { - 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; - } - - private ResourceValue getResource(ResourceType resourceType, String resourceName, - Map<ResourceType, Map<String, ResourceValue>> resourceRepository) { - Map<String, ResourceValue> typeMap = resourceRepository.get(resourceType); - if (typeMap != null) { - ResourceValue item = typeMap.get(resourceName); - if (item != null) { - item = resolveResValue(item); - return item; - } - } - - // didn't find the resource anywhere. - return null; - - } - - /** - * Compute style information from the given list of style for the project and framework. - * @param themeName the name of the current theme. - * @param isProjectTheme Is this a project theme? - */ - private void computeStyleMaps(String themeName, boolean isProjectTheme) { - mThemeName = themeName; - mIsProjectTheme = isProjectTheme; - Map<String, ResourceValue> projectStyleMap = mProjectResources.get(ResourceType.STYLE); - Map<String, ResourceValue> frameworkStyleMap = mFrameworkResources.get(ResourceType.STYLE); - - // first, get the theme - ResourceValue theme = null; - - // project theme names have been prepended with a * - if (isProjectTheme) { - theme = projectStyleMap.get(themeName); - } else { - theme = frameworkStyleMap.get(themeName); - } - - if (theme instanceof StyleResourceValue) { - // compute the inheritance map for both the project and framework styles - computeStyleInheritance(projectStyleMap.values(), projectStyleMap, - frameworkStyleMap); - - // Compute the style inheritance for the framework styles/themes. - // Since, for those, the style parent values do not contain 'android:' - // we want to force looking in the framework style only to avoid using - // similarly named styles from the project. - // To do this, we pass null in lieu of the project style map. - computeStyleInheritance(frameworkStyleMap.values(), null /*inProjectStyleMap */, - frameworkStyleMap); - - mTheme = (StyleResourceValue) theme; - } - } - - - - /** - * Compute the parent style for all the styles in a given list. - * @param styles the styles for which we compute the parent. - * @param inProjectStyleMap the map of project styles. - * @param inFrameworkStyleMap the map of framework styles. - * @param outInheritanceMap the map of style inheritance. This is filled by the method. - */ - private void computeStyleInheritance(Collection<ResourceValue> styles, - Map<String, ResourceValue> inProjectStyleMap, - Map<String, ResourceValue> inFrameworkStyleMap) { - for (ResourceValue value : styles) { - if (value instanceof StyleResourceValue) { - StyleResourceValue style = (StyleResourceValue)value; - StyleResourceValue parentStyle = null; - - // first look for a specified parent. - String parentName = style.getParentStyle(); - - // no specified parent? try to infer it from the name of the style. - if (parentName == null) { - parentName = getParentName(value.getName()); - } - - if (parentName != null) { - parentStyle = getStyle(parentName, inProjectStyleMap, inFrameworkStyleMap); - - if (parentStyle != null) { - mStyleInheritanceMap.put(style, parentStyle); - } - } - } - } - } - - - /** - * Computes the name of the parent style, or <code>null</code> if the style is a root style. - */ - private String getParentName(String styleName) { - int index = styleName.lastIndexOf('.'); - if (index != -1) { - return styleName.substring(0, index); - } - - return null; - } - - /** - * Searches for and returns the {@link StyleResourceValue} from a given name. - * <p/>The format of the name can be: - * <ul> - * <li>[android:]<name></li> - * <li>[android:]style/<name></li> - * <li>@[android:]style/<name></li> - * </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 == false && inProjectStyleMap != null) { - parent = inProjectStyleMap.get(name); - } - - // if not found, then look in the framework resources. - if (parent == 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; - } -} |