aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/ValuesContentAssist.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/ValuesContentAssist.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/ValuesContentAssist.java242
1 files changed, 242 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/ValuesContentAssist.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/ValuesContentAssist.java
new file mode 100644
index 000000000..d0ee92ca1
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/values/ValuesContentAssist.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2007 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.eclipse.adt.internal.editors.values;
+
+import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX;
+import static com.android.SdkConstants.ANDROID_PREFIX;
+import static com.android.SdkConstants.ATTR_NAME;
+import static com.android.SdkConstants.ATTR_TYPE;
+import static com.android.SdkConstants.PREFIX_RESOURCE_REF;
+import static com.android.SdkConstants.TAG_ITEM;
+import static com.android.SdkConstants.TAG_STYLE;
+import static com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor.ATTRIBUTE_ICON_FILENAME;
+import static com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData.DESCRIPTOR_LAYOUT;
+
+import com.android.annotations.VisibleForTesting;
+import com.android.ide.eclipse.adt.internal.editors.AndroidContentAssist;
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.SeparatorAttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+import com.android.ide.eclipse.adt.internal.editors.uimodel.UiResourceAttributeNode;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+
+import org.eclipse.jface.text.contentassist.CompletionProposal;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Content Assist Processor for /res/values and /res/drawable XML files
+ * <p>
+ * Further enhancements:
+ * <ul>
+ * <li> Complete prefixes in the style element itself for the name attribute
+ * <li> Complete parent names
+ * </ul>
+ */
+@VisibleForTesting
+public class ValuesContentAssist extends AndroidContentAssist {
+
+ /**
+ * Constructor for ResourcesContentAssist
+ */
+ public ValuesContentAssist() {
+ super(AndroidTargetData.DESCRIPTOR_RESOURCES);
+ }
+
+ @Override
+ protected boolean computeAttributeValues(List<ICompletionProposal> proposals, int offset,
+ String parentTagName, String attributeName, Node node, String wordPrefix,
+ boolean skipEndTag, int replaceLength) {
+ super.computeAttributeValues(proposals, offset, parentTagName, attributeName, node,
+ wordPrefix, skipEndTag, replaceLength);
+
+ if (parentTagName.equals(TAG_ITEM) && ATTR_NAME.equals(attributeName)) {
+
+ // Special case: the user is code completing inside
+ // <style><item name="^"/></style>
+ // In this case, ALL attributes are valid so we need to synthesize
+ // a choice list from all the layout descriptors
+
+ // Add in android: as a completion item?
+ if (startsWith(ANDROID_NS_NAME_PREFIX, wordPrefix)) {
+ proposals.add(new CompletionProposal(ANDROID_NS_NAME_PREFIX,
+ offset - wordPrefix.length(), // replacementOffset
+ wordPrefix.length() + replaceLength, // replacementLength
+ ANDROID_NS_NAME_PREFIX.length(), // cursorPosition
+ IconFactory.getInstance().getIcon(ATTRIBUTE_ICON_FILENAME),
+ null, null, null));
+ }
+
+
+ String attributePrefix = wordPrefix;
+ if (startsWith(attributePrefix, ANDROID_NS_NAME_PREFIX)) {
+ attributePrefix = attributePrefix.substring(ANDROID_NS_NAME_PREFIX.length());
+ }
+
+ AndroidTargetData data = mEditor.getTargetData();
+ if (data != null) {
+ IDescriptorProvider descriptorProvider =
+ data.getDescriptorProvider(
+ AndroidTargetData.DESCRIPTOR_LAYOUT);
+ if (descriptorProvider != null) {
+ ElementDescriptor[] rootElementDescriptors =
+ descriptorProvider.getRootElementDescriptors();
+ Map<String, AttributeDescriptor> matches =
+ new HashMap<String, AttributeDescriptor>(180);
+ for (ElementDescriptor elementDesc : rootElementDescriptors) {
+ for (AttributeDescriptor desc : elementDesc.getAttributes()) {
+ if (desc instanceof SeparatorAttributeDescriptor) {
+ continue;
+ }
+ String name = desc.getXmlLocalName();
+ if (startsWith(name, attributePrefix)) {
+ matches.put(name, desc);
+ }
+ }
+ }
+
+ List<AttributeDescriptor> sorted =
+ new ArrayList<AttributeDescriptor>(matches.size());
+ sorted.addAll(matches.values());
+ Collections.sort(sorted);
+ char needTag = 0;
+ addMatchingProposals(proposals, sorted.toArray(), offset, node, wordPrefix,
+ needTag, true /* isAttribute */, false /* isNew */,
+ skipEndTag /* skipEndTag */, replaceLength);
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ protected void computeTextValues(List<ICompletionProposal> proposals, int offset,
+ Node parentNode, Node currentNode, UiElementNode uiParent,
+ String prefix) {
+ super.computeTextValues(proposals, offset, parentNode, currentNode, uiParent,
+ prefix);
+
+ if (parentNode.getNodeName().equals(TAG_ITEM) &&
+ parentNode.getParentNode() != null &&
+ TAG_STYLE.equals(parentNode.getParentNode().getNodeName())) {
+
+ // Special case: the user is code completing inside
+ // <style><item name="android:foo"/>|</style>
+ // In this case, we need to find the right AttributeDescriptor
+ // for the given attribute and offer its values
+
+ AndroidTargetData data = mEditor.getTargetData();
+ if (data != null) {
+ IDescriptorProvider descriptorProvider =
+ data.getDescriptorProvider(DESCRIPTOR_LAYOUT);
+ if (descriptorProvider != null) {
+
+ Element element = (Element) parentNode;
+ String attrName = element.getAttribute(ATTR_NAME);
+ int pos = attrName.indexOf(':');
+ if (pos >= 0) {
+ attrName = attrName.substring(pos + 1);
+ }
+
+ // Search for an attribute match
+ ElementDescriptor[] rootElementDescriptors =
+ descriptorProvider.getRootElementDescriptors();
+ for (ElementDescriptor elementDesc : rootElementDescriptors) {
+ for (AttributeDescriptor desc : elementDesc.getAttributes()) {
+ if (desc.getXmlLocalName().equals(attrName)) {
+ // Make a ui parent node such that we can attach our
+ // newfound attribute node to something (the code we delegate
+ // to for looking up attribute completions will look at the
+ // parent node and ask for its editor etc.)
+ if (uiParent == null) {
+ DocumentDescriptor documentDescriptor =
+ data.getLayoutDescriptors().getDescriptor();
+ uiParent = documentDescriptor.createUiNode();
+ uiParent.setEditor(mEditor);
+ }
+
+ UiAttributeNode currAttrNode = desc.createUiNode(uiParent);
+ AttribInfo attrInfo = new AttribInfo();
+ Object[] values = getAttributeValueChoices(currAttrNode, attrInfo,
+ prefix);
+ char needTag = attrInfo.needTag;
+ if (attrInfo.correctedPrefix != null) {
+ prefix = attrInfo.correctedPrefix;
+ }
+ boolean isAttribute = true;
+ boolean isNew = false;
+ int replaceLength = computeTextReplaceLength(currentNode, offset);
+ addMatchingProposals(proposals, values, offset, currentNode,
+ prefix, needTag, isAttribute, isNew,
+ false /* skipEndTag */, replaceLength);
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (parentNode.getNodeName().equals(TAG_ITEM)) {
+ // Completing text content inside an <item> tag: offer @resource completion.
+ if (prefix.startsWith(PREFIX_RESOURCE_REF) || prefix.trim().length() == 0) {
+ String[] choices = UiResourceAttributeNode.computeResourceStringMatches(
+ mEditor, null /*attributeDescriptor*/, prefix);
+ if (choices == null || choices.length == 0) {
+ return;
+ }
+
+ // If the parent item tag specifies a type, filter the results
+ Node typeNode = parentNode.getAttributes().getNamedItem(ATTR_TYPE);
+ if (typeNode != null) {
+ String value = typeNode.getNodeValue();
+ List<String> filtered = new ArrayList<String>();
+ for (String s : choices) {
+ if (s.startsWith(ANDROID_PREFIX) ||
+ s.startsWith(PREFIX_RESOURCE_REF+ value)) {
+ filtered.add(s);
+ }
+ }
+ if (filtered.size() > 0) {
+ choices = filtered.toArray(new String[filtered.size()]);
+ }
+ }
+
+ int replaceLength = computeTextReplaceLength(currentNode, offset);
+ addMatchingProposals(proposals, choices, offset, currentNode,
+ prefix, (char) 0 /*needTag*/, true /* isAttribute */, false /*isNew*/,
+ false /* skipEndTag*/,
+ replaceLength);
+
+ }
+ }
+ }
+}