| /* |
| * Copyright (C) 2010 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.resources; |
| |
| import static com.android.SdkConstants.DOT_XML; |
| |
| import com.android.annotations.NonNull; |
| import com.android.annotations.Nullable; |
| import com.android.ide.common.resources.ResourceItem; |
| import com.android.ide.eclipse.adt.AdtPlugin; |
| import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils; |
| import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources; |
| import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; |
| import com.android.resources.ResourceFolderType; |
| import com.android.resources.ResourceType; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.jdt.core.JavaConventions; |
| import org.eclipse.jface.dialogs.IInputValidator; |
| |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| /** |
| * Validator which ensures that new Android resource names are valid. |
| */ |
| public class ResourceNameValidator implements IInputValidator { |
| /** Set of existing names to check for conflicts with */ |
| private Set<String> mExisting; |
| |
| /** If true, the validated name must be unique */ |
| private boolean mUnique = true; |
| |
| /** If true, the validated name must exist */ |
| private boolean mExist; |
| |
| /** |
| * True if the resource name being considered is a "file" based resource (where the |
| * resource name is the actual file name, rather than just a value attribute inside an |
| * XML file name of arbitrary name |
| */ |
| private boolean mIsFileType; |
| |
| /** |
| * True if the resource type can point to image resources |
| */ |
| private boolean mIsImageType; |
| |
| /** If true, allow .xml as a name suffix */ |
| private boolean mAllowXmlExtension; |
| |
| private ResourceNameValidator(boolean allowXmlExtension, Set<String> existing, |
| boolean isFileType, boolean isImageType) { |
| mAllowXmlExtension = allowXmlExtension; |
| mExisting = existing; |
| mIsFileType = isFileType; |
| mIsImageType = isImageType; |
| } |
| |
| /** |
| * Makes the resource name validator require that names are unique. |
| * |
| * @return this, for construction chaining |
| */ |
| public ResourceNameValidator unique() { |
| mUnique = true; |
| mExist = false; |
| |
| return this; |
| } |
| |
| /** |
| * Makes the resource name validator require that names already exist |
| * |
| * @return this, for construction chaining |
| */ |
| public ResourceNameValidator exist() { |
| mExist = true; |
| mUnique = false; |
| |
| return this; |
| } |
| |
| @Override |
| public String isValid(String newText) { |
| // IValidator has the same interface as SWT's IInputValidator |
| try { |
| if (newText == null || newText.trim().length() == 0) { |
| return "Enter a new name"; |
| } |
| |
| if (mAllowXmlExtension && newText.endsWith(DOT_XML)) { |
| newText = newText.substring(0, newText.length() - DOT_XML.length()); |
| } |
| |
| if (mAllowXmlExtension && mIsImageType |
| && ImageUtils.hasImageExtension(newText)) { |
| newText = newText.substring(0, newText.lastIndexOf('.')); |
| } |
| |
| if (!mIsFileType) { |
| newText = newText.replace('.', '_'); |
| } |
| |
| if (newText.indexOf('.') != -1 && !newText.endsWith(DOT_XML)) { |
| if (mIsImageType) { |
| return "The filename must end with .xml or .png"; |
| } else { |
| return "The filename must end with .xml"; |
| } |
| } |
| |
| // Resource names must be valid Java identifiers, since they will |
| // be represented as Java identifiers in the R file: |
| if (!Character.isJavaIdentifierStart(newText.charAt(0))) { |
| return "The resource name must begin with a character"; |
| } |
| for (int i = 1, n = newText.length(); i < n; i++) { |
| char c = newText.charAt(i); |
| if (!Character.isJavaIdentifierPart(c)) { |
| return String.format("'%1$c' is not a valid resource name character", c); |
| } |
| } |
| |
| if (mIsFileType) { |
| char first = newText.charAt(0); |
| if (!(first >= 'a' && first <= 'z')) { |
| return String.format( |
| "File-based resource names must start with a lowercase letter."); |
| } |
| |
| // AAPT only allows lowercase+digits+_: |
| // "%s: Invalid file name: must contain only [a-z0-9_.]"," |
| for (int i = 0, n = newText.length(); i < n; i++) { |
| char c = newText.charAt(i); |
| if (!((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_')) { |
| return String.format( |
| "File-based resource names must contain only lowercase a-z, 0-9, or _."); |
| } |
| } |
| } |
| |
| String level = "1.5"; //$NON-NLS-1$ |
| IStatus validIdentifier = JavaConventions.validateIdentifier(newText, level, level); |
| if (!validIdentifier.isOK()) { |
| return String.format("%1$s is not a valid name (reserved Java keyword)", newText); |
| } |
| |
| |
| if (mExisting != null && (mUnique || mExist)) { |
| boolean exists = mExisting.contains(newText); |
| if (mUnique && exists) { |
| return String.format("%1$s already exists", newText); |
| } else if (mExist && !exists) { |
| return String.format("%1$s does not exist", newText); |
| } |
| } |
| |
| return null; |
| } catch (Exception e) { |
| AdtPlugin.log(e, "Validation failed: %s", e.toString()); |
| return ""; //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * Creates a new {@link ResourceNameValidator} |
| * |
| * @param allowXmlExtension if true, allow .xml to be entered as a suffix for the |
| * resource name |
| * @param type the resource type of the resource name being validated |
| * @return a new {@link ResourceNameValidator} |
| */ |
| public static ResourceNameValidator create(boolean allowXmlExtension, |
| ResourceFolderType type) { |
| boolean isFileType = type != ResourceFolderType.VALUES; |
| return new ResourceNameValidator(allowXmlExtension, null, isFileType, |
| type == ResourceFolderType.DRAWABLE); |
| } |
| |
| /** |
| * Creates a new {@link ResourceNameValidator} |
| * |
| * @param allowXmlExtension if true, allow .xml to be entered as a suffix for the |
| * resource name |
| * @param existing An optional set of names that already exist (and therefore will not |
| * be considered valid if entered as the new name) |
| * @param type the resource type of the resource name being validated |
| * @return a new {@link ResourceNameValidator} |
| */ |
| public static ResourceNameValidator create(boolean allowXmlExtension, Set<String> existing, |
| ResourceType type) { |
| boolean isFileType = ResourceHelper.isFileBasedResourceType(type); |
| return new ResourceNameValidator(allowXmlExtension, existing, isFileType, |
| type == ResourceType.DRAWABLE).unique(); |
| } |
| |
| /** |
| * Creates a new {@link ResourceNameValidator}. By default, the name will need to be |
| * unique in the project. |
| * |
| * @param allowXmlExtension if true, allow .xml to be entered as a suffix for the |
| * resource name |
| * @param project the project to validate new resource names for |
| * @param type the resource type of the resource name being validated |
| * @return a new {@link ResourceNameValidator} |
| */ |
| public static ResourceNameValidator create(boolean allowXmlExtension, |
| @Nullable IProject project, |
| @NonNull ResourceType type) { |
| Set<String> existing = null; |
| if (project != null) { |
| existing = new HashSet<String>(); |
| ResourceManager manager = ResourceManager.getInstance(); |
| ProjectResources projectResources = manager.getProjectResources(project); |
| Collection<ResourceItem> items = projectResources.getResourceItemsOfType(type); |
| for (ResourceItem item : items) { |
| existing.add(item.getName()); |
| } |
| } |
| |
| boolean isFileType = ResourceHelper.isFileBasedResourceType(type); |
| return new ResourceNameValidator(allowXmlExtension, existing, isFileType, |
| type == ResourceType.DRAWABLE); |
| } |
| } |