blob: b2e7f573f9cff1ce79a67fa1e8bc2ee2ebfabf8f [file] [log] [blame]
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* 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 org.jetbrains.android.dom;
import com.android.resources.ResourceType;
import com.android.SdkConstants;
import com.intellij.openapi.module.Module;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiReference;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.util.xml.*;
import org.jetbrains.android.dom.attrs.AttributeDefinition;
import org.jetbrains.android.dom.attrs.AttributeDefinitions;
import org.jetbrains.android.dom.attrs.AttributeFormat;
import org.jetbrains.android.dom.converters.*;
import org.jetbrains.android.dom.layout.LayoutElement;
import org.jetbrains.android.dom.layout.LayoutViewElement;
import org.jetbrains.android.dom.manifest.*;
import org.jetbrains.android.dom.menu.Group;
import org.jetbrains.android.dom.menu.Item;
import org.jetbrains.android.dom.menu.Menu;
import org.jetbrains.android.dom.resources.*;
import org.jetbrains.android.dom.xml.PreferenceElement;
import org.jetbrains.android.dom.xml.XmlResourceElement;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.resourceManagers.ResourceManager;
import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import static org.jetbrains.android.util.AndroidUtils.SYSTEM_RESOURCE_PACKAGE;
/**
* @author Eugene.Kudelevsky
*/
@SuppressWarnings({"EnumSwitchStatementWhichMissesCases"})
public class AndroidDomUtil {
public static final StaticEnumConverter BOOLEAN_CONVERTER = new StaticEnumConverter("true", "false");
public static final Map<String, String> SPECIAL_RESOURCE_TYPES = new HashMap<String, String>();
private static final PackageClassConverter ACTIVITY_CONVERTER = new PackageClassConverter(AndroidUtils.ACTIVITY_BASE_CLASS_NAME);
private static final OnClickConverter ON_CLICK_CONVERTER = new OnClickConverter();
private static final FragmentClassConverter FRAGMENT_CLASS_CONVERTER = new FragmentClassConverter();
@NonNls public static final String ATTR_ID = "id";
@NonNls public static final String ATTR_STYLE = "style";
@NonNls public static final String ATTR_LAYOUT_PREFIX = "layout_";
@NonNls public static final String ATTR_LAYOUT_MARGIN = "layout_margin";
@NonNls public static final String ATTR_TEXT = "text";
@NonNls public static final String ATTR_HINT = "hint";
@NonNls public static final String ATTR_SRC = "src";
@NonNls public static final String ATTR_ON_CLICK = "onClick";
static {
addSpecialResourceType(ResourceType.STRING.getName(), "label", "description", "title");
addSpecialResourceType(ResourceType.DRAWABLE.getName(), "icon");
addSpecialResourceType(ResourceType.STYLE.getName(), "theme");
addSpecialResourceType(ResourceType.ANIM.getName(), "animation");
addSpecialResourceType(ResourceType.ID.getName(), "id", "layout_toRightOf", "layout_toLeftOf", "layout_above", "layout_below",
"layout_alignBaseLine", "layout_alignLeft", "layout_alignTop", "layout_alignRight", "layout_alignBottom");
}
private AndroidDomUtil() {
}
@Nullable
public static String getResourceType(@NotNull AttributeFormat format) {
switch (format) {
case Color:
return ResourceType.COLOR.getName();
case Dimension:
return ResourceType.DIMEN.getName();
case String:
return ResourceType.STRING.getName();
case Integer:
return ResourceType.INTEGER.getName();
case Boolean:
return ResourceType.BOOL.getName();
default:
return null;
}
}
@Nullable
public static ResolvingConverter<String> getStringConverter(@NotNull AttributeFormat format, @NotNull String[] values) {
switch (format) {
case Enum:
return new StaticEnumConverter(values);
case Boolean:
return BOOLEAN_CONVERTER;
case Integer:
return IntegerConverter.INSTANCE;
default:
return null;
}
}
@Nullable
public static ResourceReferenceConverter getResourceReferenceConverter(@NotNull AttributeDefinition attr) {
boolean containsReference = false;
Set<String> resourceTypes = new HashSet<String>();
Set<AttributeFormat> formats = attr.getFormats();
for (AttributeFormat format : formats) {
if (format == AttributeFormat.Reference) {
containsReference = true;
}
String type = getResourceType(format);
if (type != null) {
resourceTypes.add(type);
}
}
String specialResourceType = getSpecialResourceType(attr.getName());
if (specialResourceType != null) {
resourceTypes.add(specialResourceType);
}
if (containsReference) {
if (resourceTypes.contains(ResourceType.COLOR.getName())) {
resourceTypes.add(ResourceType.DRAWABLE.getName());
}
if (resourceTypes.size() == 0) {
resourceTypes.addAll(AndroidResourceUtil.getNames(AndroidResourceUtil.REFERRABLE_RESOURCE_TYPES));
}
}
if (resourceTypes.size() > 0) {
return new ResourceReferenceConverter(resourceTypes);
}
return null;
}
@Nullable
public static ResolvingConverter<String> simplify(CompositeConverter composite) {
switch (composite.size()) {
case 0:
return null;
case 1:
return composite.getConverters().get(0);
default:
return composite;
}
}
@Nullable
public static Converter getSpecificConverter(@NotNull XmlName attrName, DomElement context) {
if (context == null) {
return null;
}
if (!SdkConstants.NS_RESOURCES.equals(attrName.getNamespaceKey())) {
return null;
}
final XmlTag xmlTag = context.getXmlTag();
if (xmlTag == null) {
return null;
}
final String localName = attrName.getLocalName();
final String tagName = xmlTag.getName();
if (context instanceof XmlResourceElement) {
if ("configure".equals(localName) && "appwidget-provider".equals(tagName)) {
return ACTIVITY_CONVERTER;
}
else if ("fragment".equals(localName)) {
return FRAGMENT_CLASS_CONVERTER;
}
}
else if (context instanceof LayoutViewElement) {
if ("onClick".equals(localName)) {
return ON_CLICK_CONVERTER;
}
}
return null;
}
@Nullable
public static ResolvingConverter getConverter(@NotNull AttributeDefinition attr) {
Set<AttributeFormat> formats = attr.getFormats();
CompositeConverter composite = new CompositeConverter();
String[] values = attr.getValues();
boolean containsUnsupportedFormats = false;
for (AttributeFormat format : formats) {
ResolvingConverter<String> converter = getStringConverter(format, values);
if (converter != null) {
composite.addConverter(converter);
}
else {
containsUnsupportedFormats = true;
}
}
ResourceReferenceConverter resConverter = getResourceReferenceConverter(attr);
if (formats.contains(AttributeFormat.Flag)) {
if (resConverter != null) {
composite.addConverter(new LightFlagConverter(values));
}
return new FlagConverter(simplify(composite), values);
}
if (resConverter == null && formats.contains(AttributeFormat.Enum)) {
resConverter = new ResourceReferenceConverter(Arrays.asList(ResourceType.INTEGER.getName()));
resConverter.setQuiet(true);
}
ResolvingConverter<String> stringConverter = simplify(composite);
if (resConverter != null) {
resConverter.setAdditionalConverter(simplify(composite), containsUnsupportedFormats);
return resConverter;
}
return stringConverter;
}
@Nullable
public static String getSpecialResourceType(String attrName) {
String type = SPECIAL_RESOURCE_TYPES.get(attrName);
if (type != null) return type;
if (attrName.endsWith("Animation")) return "anim";
return null;
}
// for special cases
static void addSpecialResourceType(String type, String... attrs) {
for (String attr : attrs) {
SPECIAL_RESOURCE_TYPES.put(attr, type);
}
}
public static boolean containsAction(@NotNull IntentFilter filter, @NotNull String name) {
for (Action action : filter.getActions()) {
if (name.equals(action.getName().getValue())) {
return true;
}
}
return false;
}
public static boolean containsCategory(@NotNull IntentFilter filter, @NotNull String name) {
for (Category category : filter.getCategories()) {
if (name.equals(category.getName().getValue())) {
return true;
}
}
return false;
}
@Nullable
public static Activity getActivityDomElementByClass(@NotNull Module module, PsiClass c) {
AndroidFacet facet = AndroidFacet.getInstance(module);
if (facet != null) {
Manifest manifest = facet.getManifest();
if (manifest != null) {
Application application = manifest.getApplication();
if (application != null) {
for (Activity activity : application.getActivities()) {
PsiClass activityClass = activity.getActivityClass().getValue();
if (c.getManager().areElementsEquivalent(c, activityClass)) {
return activity;
}
}
}
}
}
return null;
}
public static String[] getStaticallyDefinedSubtags(@NotNull AndroidDomElement element) {
if (element instanceof ManifestElement) {
return AndroidManifestUtils.getStaticallyDefinedSubtags((ManifestElement)element);
}
if (element instanceof LayoutViewElement) {
return new String[]{"include", "requestFocus"};
}
if (element instanceof LayoutElement) {
return new String[]{"requestFocus"};
}
if (element instanceof Group || element instanceof StringArray || element instanceof IntegerArray || element instanceof Style) {
return new String[]{"item"};
}
if (element instanceof Item) {
return new String[]{"menu"};
}
if (element instanceof Menu) {
return new String[]{"item", "group"};
}
if (element instanceof Attr) {
return new String[]{"enum", "flag"};
}
if (element instanceof DeclareStyleable) {
return new String[]{"attr"};
}
if (element instanceof Resources) {
return new String[]{"string", "drawable", "dimen", "color", "style", "string-array", "integer-array", "array", "plurals",
"declare-styleable", "integer", "bool", "attr", "item", "eat-comment"};
}
if (element instanceof StyledText) {
return new String[]{"b", "i", "u"};
}
if (element instanceof PreferenceElement) {
return new String[]{"intent", "extra"};
}
return ArrayUtil.EMPTY_STRING_ARRAY;
}
@Nullable
public static AttributeDefinition getAttributeDefinition(@NotNull AndroidFacet facet, @NotNull XmlAttribute attribute) {
String localName = attribute.getLocalName();
ResourceManager manager =
facet.getResourceManager(attribute.getNamespace().equals(SdkConstants.NS_RESOURCES) ? SYSTEM_RESOURCE_PACKAGE : null);
if (manager != null) {
AttributeDefinitions attrDefs = manager.getAttributeDefinitions();
if (attrDefs != null) {
return attrDefs.getAttrDefByName(localName);
}
}
return null;
}
@NotNull
public static Collection<String> removeUnambigiousNames(@NotNull Map<String, PsiClass> viewClassMap) {
final Map<String, String> class2Name = new HashMap<String, String>();
for (String tagName : viewClassMap.keySet()) {
final PsiClass viewClass = viewClassMap.get(tagName);
if (!AndroidUtils.isAbstract(viewClass)) {
final String qName = viewClass.getQualifiedName();
final String prevTagName = class2Name.get(qName);
if (prevTagName == null || tagName.indexOf('.') == -1) {
class2Name.put(qName, tagName);
}
}
}
return class2Name.values();
}
@Nullable
public static AndroidResourceReferenceBase getAndroidResourceReference(@Nullable GenericAttributeValue<ResourceValue> attribute,
boolean localOnly) {
if (attribute == null) {
return null;
}
final ResourceValue resValue = attribute.getValue();
if (resValue == null || (localOnly && resValue.getPackage() != null)) {
return null;
}
final XmlAttributeValue value = attribute.getXmlAttributeValue();
if (value == null) {
return null;
}
for (PsiReference reference : value.getReferences()) {
if (reference instanceof AndroidResourceReferenceBase) {
return (AndroidResourceReferenceBase)reference;
}
}
return null;
}
}