aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/descriptors/AndroidManifestDescriptors.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/AttrsXmlParser.java112
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/DeclareStyleableInfo.java28
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/AttrsXmlParserManifestTest.java32
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/AttrsXmlParserTest.java2
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/mock_manifest_attrs.xml4
6 files changed, 163 insertions, 21 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/descriptors/AndroidManifestDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/descriptors/AndroidManifestDescriptors.java
index 423d967..1de2906 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/descriptors/AndroidManifestDescriptors.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/descriptors/AndroidManifestDescriptors.java
@@ -53,7 +53,7 @@ import java.util.Map.Entry;
public final class AndroidManifestDescriptors implements IDescriptorProvider {
private static final String MANIFEST_NODE_NAME = "manifest"; //$NON-NLS-1$
- private static final String ANDROID_MANIFEST_STYLEABLE = "AndroidManifest"; //$NON-NLS-1$
+ public static final String ANDROID_MANIFEST_STYLEABLE = "AndroidManifest"; //$NON-NLS-1$
// Public attributes names, attributes descriptors and elements descriptors
@@ -345,7 +345,7 @@ public final class AndroidManifestDescriptors implements IDescriptorProvider {
* "Inflates" the properties of an {@link ElementDescriptor} from the styleable declaration.
* <p/>
* This first creates all the attributes for the given ElementDescriptor.
- * It then finds all children of the descriptor, inflate them recursively and set them
+ * It then finds all children of the descriptor, inflates them recursively and sets them
* as child to this ElementDescriptor.
*
* @param styleMap The input styleable map for manifest elements & attributes.
@@ -552,7 +552,7 @@ public final class AndroidManifestDescriptors implements IDescriptorProvider {
if (ANDROID_MANIFEST_STYLEABLE.equals(name)) {
sb.append(MANIFEST_NODE_NAME);
} else {
- name = name.replace(ANDROID_MANIFEST_STYLEABLE, ""); //$NON-NLS-1$
+ name = name.replace(ANDROID_MANIFEST_STYLEABLE, ""); //$NON-NLS-1$
boolean first_char = true;
for (char c : name.toCharArray()) {
if (c >= 'A' && c <= 'Z') {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/AttrsXmlParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/AttrsXmlParser.java
index c3cd89f..9b6675e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/AttrsXmlParser.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/AttrsXmlParser.java
@@ -19,9 +19,8 @@ package com.android.ide.eclipse.adt.internal.resources;
import com.android.ide.common.api.IAttributeInfo.Format;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
+import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.AndroidManifestDescriptors;
import com.android.ide.eclipse.adt.internal.resources.ViewClassInfo.LayoutParamsInfo;
-import com.android.sdklib.annotations.VisibleForTesting;
-import com.android.sdklib.annotations.VisibleForTesting.Visibility;
import org.eclipse.core.runtime.IStatus;
import org.w3c.dom.Document;
@@ -33,8 +32,10 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
import java.util.TreeSet;
+import java.util.Map.Entry;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -52,7 +53,8 @@ public final class AttrsXmlParser {
private HashMap<String, AttributeInfo> mAttributeMap;
/** Map of all attribute names for a given element */
- private HashMap<String, DeclareStyleableInfo> mStyleMap;
+ private final Map<String, DeclareStyleableInfo> mStyleMap =
+ new HashMap<String, DeclareStyleableInfo>();
/** Map of all (constant, value) pairs for attributes of format enum or flag.
* E.g. for attribute name=gravity, this tells us there's an enum/flag called "center"
@@ -79,9 +81,6 @@ public final class AttrsXmlParser {
public AttrsXmlParser(String osAttrsXmlPath, AttrsXmlParser inheritableAttributes) {
mOsAttrsXmlPath = osAttrsXmlPath;
- // styles are not inheritable.
- mStyleMap = new HashMap<String, DeclareStyleableInfo>();
-
if (inheritableAttributes == null) {
mAttributeMap = new HashMap<String, AttributeInfo>();
mEnumFlagValues = new HashMap<String, Map<String,Integer>>();
@@ -208,6 +207,9 @@ public final class AttrsXmlParser {
* Finds all the <declare-styleable> and <attr> nodes in the top <resources> node.
*/
private void parseResources(Node res) {
+
+ Map<String, String> unknownParents = new HashMap<String, String>();
+
Node lastComment = null;
for (Node node = res.getFirstChild(); node != null; node = node.getNextSibling()) {
switch (node.getNodeType()) {
@@ -226,9 +228,12 @@ public final class AttrsXmlParser {
if (name != null && !mStyleMap.containsKey(name)) {
DeclareStyleableInfo style = parseDeclaredStyleable(name, node);
if (parents != null) {
- style.setParents(parents.split("[ ,|]")); //$NON-NLS-1$
+ String[] parentsArray =
+ parseStyleableParents(parents, mStyleMap, unknownParents);
+ style.setParents(parentsArray); //$NON-NLS-1$
}
mStyleMap.put(name, style);
+ unknownParents.remove(name);
if (lastComment != null) {
style.setJavaDoc(parseJavadoc(lastComment.getNodeValue()));
}
@@ -241,10 +246,101 @@ public final class AttrsXmlParser {
break;
}
}
+
+ // If we have any unknown parent, re-create synthetic styleable for them.
+ for (Entry<String, String> entry : unknownParents.entrySet()) {
+ String name = entry.getKey();
+ String parent = entry.getValue();
+
+ DeclareStyleableInfo style = new DeclareStyleableInfo(name, (AttributeInfo[])null);
+ if (parent != null) {
+ style.setParents(new String[] { parent });
+ }
+ mStyleMap.put(name, style);
+
+ // Simplify parents names. See SDK Bug 3125910.
+ // Implementation detail: that since we want to delete and add to the map,
+ // we can't just use an iterator.
+ for (String key : new ArrayList<String>(mStyleMap.keySet())) {
+ if (key.startsWith(name) && !key.equals(name)) {
+ // We found a child which name starts with the full name of the
+ // parent. Simplify the children name.
+ String newName = AndroidManifestDescriptors.ANDROID_MANIFEST_STYLEABLE +
+ key.substring(name.length());
+
+ DeclareStyleableInfo newStyle =
+ new DeclareStyleableInfo(newName, mStyleMap.get(key));
+ mStyleMap.remove(key);
+ mStyleMap.put(newName, newStyle);
+ }
+ }
+ }
+ }
+
+ /**
+ * Parses the "parents" attribute from a &lt;declare-styleable&gt;.
+ * <p/>
+ * The syntax is the following:
+ * <pre>
+ * parent[.parent]* [[space|,] parent[.parent]* ]
+ * </pre>
+ * <p/>
+ * In English: </br>
+ * - There can be one or more parents, separated by whitespace or commas. </br>
+ * - Whitespace is ignored and trimmed. </br>
+ * - A parent name is actually composed of one or more identifiers joined by a dot.
+ * <p/>
+ * Styleables do not usually need to declare their parent chain (e.g. the grand-parents
+ * of a parent.) Parent names are unique, so in most cases a styleable will only declare
+ * its immediate parent.
+ * <p/>
+ * However it is possible for a styleable's parent to not exist, e.g. if you have a
+ * styleable "A" that is the root and then styleable "C" declares its parent to be "A.B".
+ * In this case we record "B" as the parent, even though it is unknown and will never be
+ * known. Any parent that is currently not in the knownParent map is thus added to the
+ * unknownParent set. The caller will remove the name from the unknownParent set when it
+ * sees a declaration for it.
+ *
+ * @param parents The parents string to parse. Must not be null or empty.
+ * @param knownParents The map of all declared styles known so far.
+ * @param unknownParents A map of all unknown parents collected here.
+ * @return The array of terminal parent names parsed from the parents string.
+ */
+ private String[] parseStyleableParents(String parents,
+ Map<String, DeclareStyleableInfo> knownParents,
+ Map<String, String> unknownParents) {
+
+ ArrayList<String> result = new ArrayList<String>();
+
+ for (String parent : parents.split("[ \t\n\r\f,|]")) { //$NON-NLS-1$
+ parent = parent.trim();
+ if (parent.length() == 0) {
+ continue;
+ }
+ if (parent.indexOf('.') >= 0) {
+ // This is a grand-parent/parent chain. Make sure we know about the
+ // parents and only record the terminal one.
+ String last = null;
+ for (String name : parent.split("\\.")) { //$NON-NLS-1$
+ if (name.length() > 0) {
+ if (!knownParents.containsKey(name)) {
+ // Record this unknown parent and its grand parent.
+ unknownParents.put(name, last);
+ }
+ last = name;
+ }
+ }
+ parent = last;
+ }
+
+ result.add(parent);
+ }
+
+ return result.toArray(new String[result.size()]);
}
/**
- * Parses an <attr> node and convert it into an {@link AttributeInfo} if it is valid.
+ * Parses an &lt;attr&gt; node and convert it into an {@link AttributeInfo} if it is valid.
*/
private AttributeInfo parseAttr(Node attrNode, Node lastComment) {
AttributeInfo info = null;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/DeclareStyleableInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/DeclareStyleableInfo.java
index a412163..5013cf0 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/DeclareStyleableInfo.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/DeclareStyleableInfo.java
@@ -44,6 +44,34 @@ public class DeclareStyleableInfo {
mAttributes = attributes == null ? new AttributeInfo[0] : attributes;
}
+ /**
+ * Creates a new {@link DeclareStyleableInfo} that has the same attributes
+ * as an existing one and only differs by name.
+ *
+ * @param styleName The name of the style. Should not be empty nor null.
+ * @param existing The existing {@link DeclareStyleableInfo} to mirror.
+ */
+ public DeclareStyleableInfo(String styleName, DeclareStyleableInfo existing) {
+ mStyleName = styleName;
+
+ mJavaDoc = existing.getJavaDoc();
+
+ String[] parents = existing.getParents();
+ if (parents != null) {
+ mParents = new String[parents.length];
+ System.arraycopy(parents, 0, mParents, 0, parents.length);
+ }
+
+ AttributeInfo[] attrs = existing.getAttributes();
+ if (attrs == null || attrs.length == 0) {
+ mAttributes = new AttributeInfo[0];
+ } else {
+ mAttributes = new AttributeInfo[attrs.length];
+ System.arraycopy(attrs, 0, mAttributes, 0, attrs.length);
+ }
+ }
+
+
/** Returns style name */
public String getStyleName() {
return mStyleName;
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/AttrsXmlParserManifestTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/AttrsXmlParserManifestTest.java
index f1947a3..6ee9223 100755
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/AttrsXmlParserManifestTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/AttrsXmlParserManifestTest.java
@@ -17,7 +17,6 @@
package com.android.ide.eclipse.adt.internal.resources;
-import com.android.ide.common.api.IAttributeInfo.Format;
import com.android.ide.eclipse.tests.AdtTestData;
import java.util.Arrays;
@@ -48,7 +47,7 @@ public class AttrsXmlParserManifestTest extends TestCase {
assertEquals(mFilePath, mParser.getOsAttrsXmlPath());
}
- public final void testPreload() throws Exception {
+ private Map<String, DeclareStyleableInfo> preloadAndGetStyleables() {
assertSame(mParser, mParser.preload());
Map<String, DeclareStyleableInfo> styleableList = mParser.getDeclareStyleableList();
@@ -56,19 +55,40 @@ public class AttrsXmlParserManifestTest extends TestCase {
if (!(styleableList instanceof TreeMap<?, ?>)) {
styleableList = new TreeMap<String, DeclareStyleableInfo>(styleableList);
}
+ return styleableList;
+ }
+
+ public final void testPreload() throws Exception {
+ Map<String, DeclareStyleableInfo> styleableList = preloadAndGetStyleables();
assertEquals(
"[AndroidManifest, " +
"AndroidManifestActivityAlias, " +
"AndroidManifestApplication, " +
"AndroidManifestNewElement, " +
+ "AndroidManifestNewParent, " +
"AndroidManifestPermission" +
- // TODO This is for the next CL:
- //"AndroidManifestImplicitParentElement" +
- //"AndroidManifestNewElement" +
"]",
Arrays.toString(styleableList.keySet().toArray()));
+ }
+
+ /**
+ * Tests that AndroidManifestNewParentNewElement got renamed to AndroidManifestNewElement
+ * and a parent named AndroidManifestNewParent was automatically created.
+ */
+ public final void testNewParent() throws Exception {
+ Map<String, DeclareStyleableInfo> styleableList = preloadAndGetStyleables();
+
+ DeclareStyleableInfo newElement = styleableList.get("AndroidManifestNewElement");
+ assertNotNull(newElement);
+ assertEquals("AndroidManifestNewElement", newElement.getStyleName());
+ assertEquals("[AndroidManifestNewParent]",
+ Arrays.toString(newElement.getParents()));
+
+ DeclareStyleableInfo newParent = styleableList.get("AndroidManifestNewParent");
+ assertNotNull(newParent);
+ assertEquals("[AndroidManifest]",
+ Arrays.toString(newParent.getParents()));
- // TODO test a bit more the styleable info vlaues.
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/AttrsXmlParserTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/AttrsXmlParserTest.java
index 8662259..ae4837e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/AttrsXmlParserTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/AttrsXmlParserTest.java
@@ -20,8 +20,6 @@ package com.android.ide.eclipse.adt.internal.resources;
import com.android.ide.common.api.IAttributeInfo.Format;
import com.android.ide.eclipse.tests.AdtTestData;
-import org.w3c.dom.Document;
-
import java.util.Map;
import junit.framework.TestCase;
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/mock_manifest_attrs.xml b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/mock_manifest_attrs.xml
index 9dcb70b..2335d25 100755
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/mock_manifest_attrs.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/mock_manifest_attrs.xml
@@ -170,8 +170,8 @@
<attr name="icon" />
</declare-styleable>
- <declare-styleable name="AndroidManifestNewElement"
- parent="AndroidManifest.AndroidManifestImplicitParentElement">
+ <declare-styleable name="AndroidManifestNewParentNewElement"
+ parent="AndroidManifest.AndroidManifestNewParent">
<attr name="name" />
<attr name="label" />
<attr name="icon" />