diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/ResourceValueCompleter.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/ResourceValueCompleter.java | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/ResourceValueCompleter.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/ResourceValueCompleter.java new file mode 100644 index 000000000..081ec8069 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/properties/ResourceValueCompleter.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2012 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.properties; + +import static com.android.SdkConstants.ANDROID_PKG; +import static com.android.SdkConstants.ANDROID_PREFIX; +import static com.android.SdkConstants.ANDROID_THEME_PREFIX; +import static com.android.SdkConstants.NEW_ID_PREFIX; +import static com.android.SdkConstants.PREFIX_RESOURCE_REF; +import static com.android.SdkConstants.PREFIX_THEME_REF; + +import com.android.ide.common.resources.ResourceItem; +import com.android.ide.common.resources.ResourceRepository; +import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; +import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiResourceAttributeNode; +import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; +import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; +import com.android.resources.ResourceType; +import com.android.utils.SdkUtils; + +import org.eclipse.core.resources.IProject; +import org.eclipse.jface.fieldassist.ContentProposal; +import org.eclipse.jface.fieldassist.IContentProposal; +import org.eclipse.jface.fieldassist.IContentProposalProvider; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Resource value completion for the given property + * <p> + * TODO: + * <ul> + * <li>also offer other values seen in the app + * <li>also offer previously set values for this property + * <li>also complete on properties + * </ul> + */ +class ResourceValueCompleter implements IContentProposalProvider { + protected final XmlProperty xmlProperty; + + ResourceValueCompleter(XmlProperty xmlProperty) { + this.xmlProperty = xmlProperty; + } + + @Override + public IContentProposal[] getProposals(String contents, int position) { + if (contents.startsWith(PREFIX_RESOURCE_REF)) { + CommonXmlEditor editor = this.xmlProperty.getXmlEditor(); + if (editor != null) { + String[] matches = computeResourceStringMatches( + editor, + this.xmlProperty.mDescriptor, contents.substring(0, position)); + List<IContentProposal> proposals = null; + if (matches != null && matches.length > 0) { + proposals = new ArrayList<IContentProposal>(matches.length); + for (String match : matches) { + proposals.add(new ContentProposal(match)); + } + return proposals.toArray(new IContentProposal[proposals.size()]); + } + } + } + + return new IContentProposal[0]; + } + + /** + * Similar to {@link UiResourceAttributeNode#computeResourceStringMatches} + * but computes complete results up front rather than dividing it up into + * smaller chunks like @{code @android:}, {@code string/}, and {@code ok}. + */ + static String[] computeResourceStringMatches(AndroidXmlEditor editor, + AttributeDescriptor attributeDescriptor, String prefix) { + List<String> results = new ArrayList<String>(200); + + // System matches: only do this if the value already matches at least @a, + // and doesn't start with something that can't possibly be @android + if (prefix.startsWith("@a") && //$NON-NLS-1$ + prefix.regionMatches(true /* ignoreCase */, 0, ANDROID_PREFIX, 0, + Math.min(prefix.length() - 1, ANDROID_PREFIX.length()))) { + AndroidTargetData data = editor.getTargetData(); + if (data != null) { + ResourceRepository repository = data.getFrameworkResources(); + addMatches(repository, prefix, true /* isSystem */, results); + } + } else if (prefix.startsWith("?") && //$NON-NLS-1$ + prefix.regionMatches(true /* ignoreCase */, 0, ANDROID_THEME_PREFIX, 0, + Math.min(prefix.length() - 1, ANDROID_THEME_PREFIX.length()))) { + AndroidTargetData data = editor.getTargetData(); + if (data != null) { + ResourceRepository repository = data.getFrameworkResources(); + addMatches(repository, prefix, true /* isSystem */, results); + } + } + + + // When completing project resources skip framework resources unless + // the prefix possibly completes both, such as "@an" which can match + // both the project resource @animator as well as @android:string + if (!prefix.startsWith("@and") && !prefix.startsWith("?and")) { //$NON-NLS-1$ //$NON-NLS-2$ + IProject project = editor.getProject(); + if (project != null) { + // get the resource repository for this project and the system resources. + ResourceManager manager = ResourceManager.getInstance(); + ResourceRepository repository = manager.getProjectResources(project); + if (repository != null) { + // We have a style name and a repository. Find all resources that match this + // type and recreate suggestions out of them. + addMatches(repository, prefix, false /* isSystem */, results); + } + + } + } + + if (attributeDescriptor != null) { + UiResourceAttributeNode.sortAttributeChoices(attributeDescriptor, results); + } else { + Collections.sort(results); + } + + return results.toArray(new String[results.size()]); + } + + private static void addMatches(ResourceRepository repository, String prefix, boolean isSystem, + List<String> results) { + int typeStart = isSystem + ? ANDROID_PREFIX.length() : PREFIX_RESOURCE_REF.length(); + + for (ResourceType type : repository.getAvailableResourceTypes()) { + if (prefix.regionMatches(typeStart, type.getName(), 0, + Math.min(type.getName().length(), prefix.length() - typeStart))) { + StringBuilder sb = new StringBuilder(); + if (prefix.length() == 0 || prefix.startsWith(PREFIX_RESOURCE_REF)) { + sb.append(PREFIX_RESOURCE_REF); + } else { + if (type != ResourceType.ATTR) { + continue; + } + sb.append(PREFIX_THEME_REF); + } + + if (type == ResourceType.ID && prefix.startsWith(NEW_ID_PREFIX)) { + sb.append('+'); + } + + if (isSystem) { + sb.append(ANDROID_PKG).append(':'); + } + + sb.append(type.getName()).append('/'); + String base = sb.toString(); + + int nameStart = typeStart + type.getName().length() + 1; // +1: add "/" divider + String namePrefix = + prefix.length() <= nameStart ? "" : prefix.substring(nameStart); + for (ResourceItem item : repository.getResourceItemsOfType(type)) { + String name = item.getName(); + if (SdkUtils.startsWithIgnoreCase(name, namePrefix)) { + results.add(base + name); + } + } + } + } + } +} |