| /* |
| * 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.ATTR_TEXT_SIZE; |
| import static com.android.SdkConstants.PREFIX_RESOURCE_REF; |
| import static com.android.SdkConstants.PREFIX_THEME_REF; |
| import static com.android.SdkConstants.UNIT_DP; |
| import static com.android.SdkConstants.UNIT_SP; |
| import static com.android.SdkConstants.VALUE_FALSE; |
| import static com.android.SdkConstants.VALUE_TRUE; |
| import static com.android.ide.common.api.IAttributeInfo.Format.BOOLEAN; |
| import static com.android.ide.common.api.IAttributeInfo.Format.DIMENSION; |
| import static com.android.ide.common.api.IAttributeInfo.Format.ENUM; |
| import static com.android.ide.common.api.IAttributeInfo.Format.FLAG; |
| import static com.android.ide.common.api.IAttributeInfo.Format.FLOAT; |
| import static com.android.ide.common.api.IAttributeInfo.Format.INTEGER; |
| import static com.android.ide.common.api.IAttributeInfo.Format.REFERENCE; |
| import static com.android.ide.common.api.IAttributeInfo.Format.STRING; |
| |
| import com.android.annotations.NonNull; |
| import com.android.annotations.Nullable; |
| import com.android.ide.common.api.IAttributeInfo; |
| import com.android.ide.common.api.IAttributeInfo.Format; |
| import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor; |
| import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; |
| import com.android.utils.SdkUtils; |
| |
| 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.EnumSet; |
| import java.util.List; |
| |
| /** |
| * An {@link IContentProposalProvider} which completes possible property values |
| * for Android properties, completing resource strings, flag values, enum |
| * values, as well as dimension units. |
| */ |
| abstract class ValueCompleter implements IContentProposalProvider { |
| @Nullable |
| protected abstract CommonXmlEditor getEditor(); |
| |
| @NonNull |
| protected abstract AttributeDescriptor getDescriptor(); |
| |
| @Override |
| public IContentProposal[] getProposals(String contents, int position) { |
| AttributeDescriptor descriptor = getDescriptor(); |
| IAttributeInfo info = descriptor.getAttributeInfo(); |
| EnumSet<Format> formats = info.getFormats(); |
| |
| List<IContentProposal> proposals = new ArrayList<IContentProposal>(); |
| |
| String prefix = contents; // TODO: Go back to position inside the array? |
| |
| // TODO: If the user is typing in a number, or a number plus a prefix of a dimension unit, |
| // then propose that number plus the completed dimension unit (using sp for text, dp |
| // for other properties and maybe both if I'm not sure) |
| if (formats.contains(STRING) |
| && !contents.isEmpty() |
| && (formats.size() > 1 && formats.contains(REFERENCE) || |
| formats.size() > 2) |
| && !contents.startsWith(PREFIX_RESOURCE_REF) |
| && !contents.startsWith(PREFIX_THEME_REF)) { |
| proposals.add(new ContentProposal(contents)); |
| } |
| |
| if (!contents.isEmpty() && Character.isDigit(contents.charAt(0)) |
| && (formats.contains(DIMENSION) |
| || formats.contains(INTEGER) |
| || formats.contains(FLOAT))) { |
| StringBuilder sb = new StringBuilder(); |
| for (int i = 0, n = contents.length(); i < n; i++) { |
| char c = contents.charAt(i); |
| if (Character.isDigit(c)) { |
| sb.append(c); |
| } else { |
| break; |
| } |
| } |
| |
| String number = sb.toString(); |
| if (formats.contains(Format.DIMENSION)) { |
| if (descriptor.getXmlLocalName().equals(ATTR_TEXT_SIZE)) { |
| proposals.add(new ContentProposal(number + UNIT_SP)); |
| } |
| proposals.add(new ContentProposal(number + UNIT_DP)); |
| } else if (formats.contains(Format.INTEGER)) { |
| proposals.add(new ContentProposal(number)); |
| } |
| // Perhaps offer other units too -- see AndroidContentAssist.sDimensionUnits |
| } |
| |
| if (formats.contains(REFERENCE) || contents.startsWith(PREFIX_RESOURCE_REF) |
| || contents.startsWith(PREFIX_THEME_REF)) { |
| CommonXmlEditor editor = getEditor(); |
| if (editor != null) { |
| String[] matches = ResourceValueCompleter.computeResourceStringMatches( |
| editor, |
| descriptor, contents.substring(0, position)); |
| for (String match : matches) { |
| proposals.add(new ContentProposal(match)); |
| } |
| } |
| } |
| |
| if (formats.contains(FLAG)) { |
| String[] values = info.getFlagValues(); |
| if (values != null) { |
| // Flag completion |
| int flagStart = prefix.lastIndexOf('|'); |
| String prepend = null; |
| if (flagStart != -1) { |
| prepend = prefix.substring(0, flagStart + 1); |
| prefix = prefix.substring(flagStart + 1).trim(); |
| } |
| |
| boolean exactMatch = false; |
| for (String value : values) { |
| if (prefix.equals(value)) { |
| exactMatch = true; |
| proposals.add(new ContentProposal(contents)); |
| |
| break; |
| } |
| } |
| |
| if (exactMatch) { |
| prepend = contents + '|'; |
| prefix = ""; |
| } |
| |
| for (String value : values) { |
| if (SdkUtils.startsWithIgnoreCase(value, prefix)) { |
| if (prepend != null && prepend.contains(value)) { |
| continue; |
| } |
| String match; |
| if (prepend != null) { |
| match = prepend + value; |
| } else { |
| match = value; |
| } |
| proposals.add(new ContentProposal(match)); |
| } |
| } |
| } |
| } else if (formats.contains(ENUM)) { |
| String[] values = info.getEnumValues(); |
| if (values != null) { |
| for (String value : values) { |
| if (SdkUtils.startsWithIgnoreCase(value, prefix)) { |
| proposals.add(new ContentProposal(value)); |
| } |
| } |
| |
| for (String value : values) { |
| if (!SdkUtils.startsWithIgnoreCase(value, prefix)) { |
| proposals.add(new ContentProposal(value)); |
| } |
| } |
| } |
| } else if (formats.contains(BOOLEAN)) { |
| proposals.add(new ContentProposal(VALUE_TRUE)); |
| proposals.add(new ContentProposal(VALUE_FALSE)); |
| } |
| |
| return proposals.toArray(new IContentProposal[proposals.size()]); |
| } |
| } |