aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutContentAssist.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutContentAssist.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutContentAssist.java234
1 files changed, 234 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutContentAssist.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutContentAssist.java
new file mode 100644
index 000000000..99549ab89
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutContentAssist.java
@@ -0,0 +1,234 @@
+/*
+ * 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.layout;
+
+import static com.android.SdkConstants.ANDROID_PKG_PREFIX;
+import static com.android.SdkConstants.ATTR_CLASS;
+import static com.android.SdkConstants.ATTR_CONTEXT;
+import static com.android.SdkConstants.ATTR_NAME;
+import static com.android.SdkConstants.CLASS_ACTIVITY;
+import static com.android.SdkConstants.CLASS_FRAGMENT;
+import static com.android.SdkConstants.CLASS_V4_FRAGMENT;
+import static com.android.SdkConstants.CLASS_VIEW;
+import static com.android.SdkConstants.VIEW_FRAGMENT;
+import static com.android.SdkConstants.VIEW_TAG;
+
+import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.AndroidContentAssist;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.CustomViewDescriptorService;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CustomViewFinder;
+import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+import com.google.common.collect.Lists;
+import com.google.common.collect.ObjectArrays;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeHierarchy;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.w3c.dom.Node;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Content Assist Processor for /res/layout XML files
+ */
+@VisibleForTesting
+public final class LayoutContentAssist extends AndroidContentAssist {
+
+ /**
+ * Constructor for LayoutContentAssist
+ */
+ public LayoutContentAssist() {
+ super(AndroidTargetData.DESCRIPTOR_LAYOUT);
+ }
+
+ @Override
+ protected Object[] getChoicesForElement(String parent, Node currentNode) {
+ Object[] choices = super.getChoicesForElement(parent, currentNode);
+ if (choices == null) {
+ if (currentNode.getParentNode().getNodeType() == Node.ELEMENT_NODE) {
+ String parentName = currentNode.getParentNode().getNodeName();
+ if (parentName.indexOf('.') != -1) {
+ // Custom view with unknown children; just use the root descriptor
+ // to get all eligible views instead
+ ElementDescriptor[] children = getRootDescriptor().getChildren();
+ for (ElementDescriptor e : children) {
+ if (e.getXmlName().startsWith(parent)) {
+ return sort(children);
+ }
+ }
+ }
+ }
+ }
+
+ if (choices == null && parent.length() >= 1 && Character.isLowerCase(parent.charAt(0))) {
+ // Custom view prefix?
+ List<ElementDescriptor> descriptors = getCustomViews();
+ if (descriptors != null && !descriptors.isEmpty()) {
+ List<ElementDescriptor> matches = Lists.newArrayList();
+ for (ElementDescriptor descriptor : descriptors) {
+ if (descriptor.getXmlLocalName().startsWith(parent)) {
+ matches.add(descriptor);
+ }
+ }
+ if (!matches.isEmpty()) {
+ return matches.toArray(new ElementDescriptor[matches.size()]);
+ }
+ }
+ }
+
+ return choices;
+ }
+
+ @Override
+ protected ElementDescriptor[] getElementChoicesForTextNode(Node parentNode) {
+ ElementDescriptor[] choices = super.getElementChoicesForTextNode(parentNode);
+
+ // Add in custom views, if any
+ List<ElementDescriptor> descriptors = getCustomViews();
+ if (descriptors != null && !descriptors.isEmpty()) {
+ ElementDescriptor[] array = descriptors.toArray(
+ new ElementDescriptor[descriptors.size()]);
+ choices = ObjectArrays.concat(choices, array, ElementDescriptor.class);
+ choices = sort(choices);
+ }
+
+ return choices;
+ }
+
+ @Nullable
+ private List<ElementDescriptor> getCustomViews() {
+ // Add in custom views, if any
+ IProject project = mEditor.getProject();
+ CustomViewFinder finder = CustomViewFinder.get(project);
+ Collection<String> views = finder.getAllViews();
+ if (views == null) {
+ finder.refresh();
+ views = finder.getAllViews();
+ }
+ if (views != null && !views.isEmpty()) {
+ List<ElementDescriptor> descriptors = Lists.newArrayListWithExpectedSize(views.size());
+ CustomViewDescriptorService customViews = CustomViewDescriptorService.getInstance();
+ for (String fqcn : views) {
+ ViewElementDescriptor descriptor = customViews.getDescriptor(project, fqcn);
+ if (descriptor != null) {
+ descriptors.add(descriptor);
+ }
+ }
+
+ return descriptors;
+ }
+
+ return null;
+ }
+
+ @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);
+
+ boolean projectOnly = false;
+ List<String> superClasses = null;
+ if (VIEW_FRAGMENT.equals(parentTagName) && (attributeName.endsWith(ATTR_NAME)
+ || attributeName.equals(ATTR_CLASS))) {
+ // Insert fragment class matches
+ superClasses = Arrays.asList(CLASS_V4_FRAGMENT, CLASS_FRAGMENT);
+ } else if (VIEW_TAG.equals(parentTagName) && attributeName.endsWith(ATTR_CLASS)) {
+ // Insert custom view matches
+ superClasses = Collections.singletonList(CLASS_VIEW);
+ projectOnly = true;
+ } else if (attributeName.endsWith(ATTR_CONTEXT)) {
+ // Insert activity matches
+ superClasses = Collections.singletonList(CLASS_ACTIVITY);
+ }
+
+ if (superClasses != null) {
+ IProject project = mEditor.getProject();
+ if (project == null) {
+ return false;
+ }
+ try {
+ IJavaProject javaProject = BaseProjectHelper.getJavaProject(project);
+ IType type = javaProject.findType(superClasses.get(0));
+ Set<IType> elements = new HashSet<IType>();
+ if (type != null) {
+ ITypeHierarchy hierarchy = type.newTypeHierarchy(new NullProgressMonitor());
+ IType[] allSubtypes = hierarchy.getAllSubtypes(type);
+ for (IType subType : allSubtypes) {
+ if (!projectOnly || subType.getResource() != null) {
+ elements.add(subType);
+ }
+ }
+ }
+ assert superClasses.size() <= 2; // If more, need to do additional work below
+ if (superClasses.size() == 2) {
+ type = javaProject.findType(superClasses.get(1));
+ if (type != null) {
+ ITypeHierarchy hierarchy = type.newTypeHierarchy(
+ new NullProgressMonitor());
+ IType[] allSubtypes = hierarchy.getAllSubtypes(type);
+ for (IType subType : allSubtypes) {
+ if (!projectOnly || subType.getResource() != null) {
+ elements.add(subType);
+ }
+ }
+ }
+ }
+
+ List<IType> sorted = new ArrayList<IType>(elements);
+ Collections.sort(sorted, new Comparator<IType>() {
+ @Override
+ public int compare(IType type1, IType type2) {
+ String fqcn1 = type1.getFullyQualifiedName();
+ String fqcn2 = type2.getFullyQualifiedName();
+ int category1 = fqcn1.startsWith(ANDROID_PKG_PREFIX) ? 1 : -1;
+ int category2 = fqcn2.startsWith(ANDROID_PKG_PREFIX) ? 1 : -1;
+ if (category1 != category2) {
+ return category1 - category2;
+ }
+ return fqcn1.compareTo(fqcn2);
+ }
+ });
+ addMatchingProposals(proposals, sorted.toArray(), offset, node, wordPrefix,
+ (char) 0, false /* isAttribute */, false /* isNew */,
+ false /* skipEndTag */, replaceLength);
+ return true;
+ } catch (CoreException e) {
+ AdtPlugin.log(e, null);
+ }
+ }
+
+ return false;
+ }
+}