diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/AttributeInfo.java')
-rwxr-xr-x | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/AttributeInfo.java | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/AttributeInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/AttributeInfo.java new file mode 100755 index 000000000..e246975bb --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/resources/platform/AttributeInfo.java @@ -0,0 +1,355 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.platform; + +import static com.android.SdkConstants.ANDROID_PREFIX; +import static com.android.SdkConstants.ANDROID_THEME_PREFIX; +import static com.android.SdkConstants.ID_PREFIX; +import static com.android.SdkConstants.NEW_ID_PREFIX; +import static com.android.SdkConstants.PREFIX_THEME_REF; +import static com.android.SdkConstants.VALUE_FALSE; +import static com.android.SdkConstants.VALUE_TRUE; +import static com.android.ide.common.api.IAttributeInfo.Format.BOOLEAN; +import static com.android.ide.common.api.IAttributeInfo.Format.COLOR; +import static com.android.ide.common.api.IAttributeInfo.Format.DIMENSION; +import static com.android.ide.common.api.IAttributeInfo.Format.ENUM; +import static com.android.ide.common.api.IAttributeInfo.Format.FLAG; +import static com.android.ide.common.api.IAttributeInfo.Format.FLOAT; +import static com.android.ide.common.api.IAttributeInfo.Format.FRACTION; +import static com.android.ide.common.api.IAttributeInfo.Format.INTEGER; +import static com.android.ide.common.api.IAttributeInfo.Format.STRING; + +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; +import com.android.ide.common.api.IAttributeInfo; +import com.android.ide.common.resources.ResourceRepository; +import com.android.resources.ResourceType; +import com.google.common.base.Splitter; + +import java.util.EnumSet; +import java.util.regex.Pattern; + + +/** + * Information about an attribute as gathered from the attrs.xml file where + * the attribute was declared. This must include a format (string, reference, float, etc.), + * possible flag or enum values, whether it's deprecated and its javadoc. + */ +public class AttributeInfo implements IAttributeInfo { + /** XML Name of the attribute */ + private String mName; + + /** Formats of the attribute. Cannot be null. Should have at least one format. */ + private EnumSet<Format> mFormats; + /** Values for enum. null for other types. */ + private String[] mEnumValues; + /** Values for flag. null for other types. */ + private String[] mFlagValues; + /** Short javadoc (i.e. the first sentence). */ + private String mJavaDoc; + /** Documentation for deprecated attributes. Null if not deprecated. */ + private String mDeprecatedDoc; + /** The source class defining this attribute */ + private String mDefinedBy; + + /** + * @param name The XML Name of the attribute + * @param formats The formats of the attribute. Cannot be null. + * Should have at least one format. + */ + public AttributeInfo(@NonNull String name, @NonNull EnumSet<Format> formats) { + mName = name; + mFormats = formats; + } + + /** + * @param name The XML Name of the attribute + * @param formats The formats of the attribute. Cannot be null. + * Should have at least one format. + * @param javadoc Short javadoc (i.e. the first sentence). + */ + public AttributeInfo(@NonNull String name, @NonNull EnumSet<Format> formats, String javadoc) { + mName = name; + mFormats = formats; + mJavaDoc = javadoc; + } + + public AttributeInfo(AttributeInfo info) { + mName = info.mName; + mFormats = info.mFormats; + mEnumValues = info.mEnumValues; + mFlagValues = info.mFlagValues; + mJavaDoc = info.mJavaDoc; + mDeprecatedDoc = info.mDeprecatedDoc; + } + + /** + * Sets the XML Name of the attribute + * + * @param name the new name to assign + */ + public void setName(String name) { + mName = name; + } + + /** Returns the XML Name of the attribute */ + @Override + public @NonNull String getName() { + return mName; + } + /** Returns the formats of the attribute. Cannot be null. + * Should have at least one format. */ + @Override + public @NonNull EnumSet<Format> getFormats() { + return mFormats; + } + /** Returns the values for enums. null for other types. */ + @Override + public String[] getEnumValues() { + return mEnumValues; + } + /** Returns the values for flags. null for other types. */ + @Override + public String[] getFlagValues() { + return mFlagValues; + } + /** Returns a short javadoc, .i.e. the first sentence. */ + @Override + public @NonNull String getJavaDoc() { + return mJavaDoc; + } + /** Returns the documentation for deprecated attributes. Null if not deprecated. */ + @Override + public String getDeprecatedDoc() { + return mDeprecatedDoc; + } + + /** Sets the values for enums. null for other types. */ + public AttributeInfo setEnumValues(String[] values) { + mEnumValues = values; + return this; + } + + /** Sets the values for flags. null for other types. */ + public AttributeInfo setFlagValues(String[] values) { + mFlagValues = values; + return this; + } + + /** Sets a short javadoc, .i.e. the first sentence. */ + public void setJavaDoc(String javaDoc) { + mJavaDoc = javaDoc; + } + + /** Sets the documentation for deprecated attributes. Null if not deprecated. */ + public void setDeprecatedDoc(String deprecatedDoc) { + mDeprecatedDoc = deprecatedDoc; + } + + /** + * Sets the name of the class (fully qualified class name) which defined + * this attribute + * + * @param definedBy the name of the class (fully qualified class name) which + * defined this attribute + */ + public void setDefinedBy(String definedBy) { + mDefinedBy = definedBy; + } + + /** + * Returns the name of the class (fully qualified class name) which defined + * this attribute + * + * @return the name of the class (fully qualified class name) which defined + * this attribute + */ + @Override + public @NonNull String getDefinedBy() { + return mDefinedBy; + } + + private final static Pattern INTEGER_PATTERN = Pattern.compile("-?[0-9]+"); //$NON-NLS-1$ + private final static Pattern FLOAT_PATTERN = + Pattern.compile("-?[0-9]?(\\.[0-9]+)?"); //$NON-NLS-1$ + private final static Pattern DIMENSION_PATTERN = + Pattern.compile("-?[0-9]+(\\.[0-9]+)?(dp|dip|sp|px|pt|in|mm)"); //$NON-NLS-1$ + + /** + * Checks the given value and returns true only if it is a valid XML value + * for this attribute. + * + * @param value the XML value to check + * @param projectResources project resources to validate resource URLs with, + * if any + * @param frameworkResources framework resources to validate resource URLs + * with, if any + * @return true if the value is valid, false otherwise + */ + public boolean isValid( + @NonNull String value, + @Nullable ResourceRepository projectResources, + @Nullable ResourceRepository frameworkResources) { + + if (mFormats.contains(STRING) || mFormats.isEmpty()) { + // Anything is allowed + return true; + } + + // All other formats require a nonempty string + if (value.isEmpty()) { + // Except for flags + if (mFormats.contains(FLAG)) { + return true; + } + + return false; + } + char first = value.charAt(0); + + // There are many attributes which are incorrectly marked in the attrs.xml + // file, such as "duration", "minHeight", etc. These are marked as only + // accepting "integer", but also appear to accept "reference". Therefore, + // in these cases, be more lenient. (This happens for theme references too, + // such as ?android:attr/listPreferredItemHeight) + if ((first == '@' || first == '?') /* && mFormats.contains(REFERENCE)*/) { + if (value.equals("@null")) { + return true; + } + + if (value.startsWith(NEW_ID_PREFIX) || value.startsWith(ID_PREFIX)) { + // These are handled in the IdGeneratingResourceFile; we shouldn't + // complain about not finding ids in the repository yet since they may + // not yet have been defined (@+id's can be defined in the same layout, + // later on.) + return true; + } + + if (value.startsWith(ANDROID_PREFIX) || value.startsWith(ANDROID_THEME_PREFIX)) { + if (frameworkResources != null) { + return frameworkResources.hasResourceItem(value); + } + } else if (projectResources != null) { + return projectResources.hasResourceItem(value); + } + + // Validate resource string + String url = value; + int typeEnd = url.indexOf('/', 1); + if (typeEnd != -1) { + int typeBegin = url.startsWith("@+") ? 2 : 1; //$NON-NLS-1$ + int colon = url.lastIndexOf(':', typeEnd); + if (colon != -1) { + typeBegin = colon + 1; + } + String typeName = url.substring(typeBegin, typeEnd); + ResourceType type = ResourceType.getEnum(typeName); + if (type != null) { + // TODO: Validate that the name portion conforms to the rules + // (is an identifier but not a keyword, etc.) + // Also validate that the prefix before the colon is either + // not there or is "android" + + //int nameBegin = typeEnd + 1; + //String name = url.substring(nameBegin); + return true; + } + } else if (value.startsWith(PREFIX_THEME_REF)) { + if (projectResources != null) { + return projectResources.hasResourceItem(ResourceType.ATTR, + value.substring(PREFIX_THEME_REF.length())); + } else { + // Until proven otherwise + return true; + } + } + } + + if (mFormats.contains(ENUM) && mEnumValues != null) { + for (String e : mEnumValues) { + if (value.equals(e)) { + return true; + } + } + } + + if (mFormats.contains(FLAG) && mFlagValues != null) { + for (String v : Splitter.on('|').split(value)) { + for (String e : mFlagValues) { + if (v.equals(e)) { + return true; + } + } + } + } + + if (mFormats.contains(DIMENSION)) { + if (DIMENSION_PATTERN.matcher(value).matches()) { + return true; + } + } + + if (mFormats.contains(BOOLEAN)) { + if (value.equalsIgnoreCase(VALUE_TRUE) || value.equalsIgnoreCase(VALUE_FALSE)) { + return true; + } + } + + if (mFormats.contains(FLOAT)) { + if (Character.isDigit(first) || first == '-' || first == '.') { + if (FLOAT_PATTERN.matcher(value).matches()) { + return true; + } + // AAPT accepts more general floats, such as ".1", + try { + Float.parseFloat(value); + return true; + } catch (NumberFormatException nufe) { + // Not a float + } + } + } + + if (mFormats.contains(INTEGER)) { + if (Character.isDigit(first) || first == '-') { + if (INTEGER_PATTERN.matcher(value).matches()) { + return true; + } + } + } + + if (mFormats.contains(COLOR)) { + if (first == '#' && value.length() <= 9) { // Only allowed 32 bit ARGB + try { + // Use Long.parseLong rather than Integer.parseInt to not overflow on + // 32 big hex values like "ff191919" + Long.parseLong(value.substring(1), 16); + return true; + } catch (NumberFormatException nufe) { + // Not a valid color number + } + } + } + + if (mFormats.contains(FRACTION)) { + // should end with % or %p + return true; + } + + return false; + } +} |