| /* |
| * 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); |
| } |
| } |
| } |
| } |
| } |
| } |