Home | History | Annotate | Download | only in resources
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ide.eclipse.adt.internal.resources;
     18 
     19 import static com.android.SdkConstants.DOT_XML;
     20 
     21 import com.android.annotations.NonNull;
     22 import com.android.annotations.Nullable;
     23 import com.android.ide.common.resources.ResourceItem;
     24 import com.android.ide.eclipse.adt.AdtPlugin;
     25 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils;
     26 import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
     27 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
     28 import com.android.resources.ResourceFolderType;
     29 import com.android.resources.ResourceType;
     30 
     31 import org.eclipse.core.resources.IProject;
     32 import org.eclipse.core.runtime.IStatus;
     33 import org.eclipse.jdt.core.JavaConventions;
     34 import org.eclipse.jface.dialogs.IInputValidator;
     35 
     36 import java.util.Collection;
     37 import java.util.HashSet;
     38 import java.util.Set;
     39 
     40 /**
     41  * Validator which ensures that new Android resource names are valid.
     42  */
     43 public class ResourceNameValidator implements IInputValidator {
     44     /** Set of existing names to check for conflicts with */
     45     private Set<String> mExisting;
     46 
     47     /** If true, the validated name must be unique */
     48     private boolean mUnique = true;
     49 
     50     /** If true, the validated name must exist */
     51     private boolean mExist;
     52 
     53     /**
     54      * True if the resource name being considered is a "file" based resource (where the
     55      * resource name is the actual file name, rather than just a value attribute inside an
     56      * XML file name of arbitrary name
     57      */
     58     private boolean mIsFileType;
     59 
     60     /**
     61      * True if the resource type can point to image resources
     62      */
     63     private boolean mIsImageType;
     64 
     65     /** If true, allow .xml as a name suffix */
     66     private boolean mAllowXmlExtension;
     67 
     68     private ResourceNameValidator(boolean allowXmlExtension, Set<String> existing,
     69             boolean isFileType, boolean isImageType) {
     70         mAllowXmlExtension = allowXmlExtension;
     71         mExisting = existing;
     72         mIsFileType = isFileType;
     73         mIsImageType = isImageType;
     74     }
     75 
     76     /**
     77      * Makes the resource name validator require that names are unique.
     78      *
     79      * @return this, for construction chaining
     80      */
     81     public ResourceNameValidator unique() {
     82         mUnique = true;
     83         mExist = false;
     84 
     85         return this;
     86     }
     87 
     88     /**
     89      * Makes the resource name validator require that names already exist
     90      *
     91      * @return this, for construction chaining
     92      */
     93     public ResourceNameValidator exist() {
     94         mExist = true;
     95         mUnique = false;
     96 
     97         return this;
     98     }
     99 
    100     @Override
    101     public String isValid(String newText) {
    102         // IValidator has the same interface as SWT's IInputValidator
    103         try {
    104             if (newText == null || newText.trim().length() == 0) {
    105                 return "Enter a new name";
    106             }
    107 
    108             if (mAllowXmlExtension && newText.endsWith(DOT_XML)) {
    109                 newText = newText.substring(0, newText.length() - DOT_XML.length());
    110             }
    111 
    112             if (mAllowXmlExtension && mIsImageType
    113                     && ImageUtils.hasImageExtension(newText)) {
    114                 newText = newText.substring(0, newText.lastIndexOf('.'));
    115             }
    116 
    117             if (!mIsFileType) {
    118                 newText = newText.replace('.', '_');
    119             }
    120 
    121             if (newText.indexOf('.') != -1 && !newText.endsWith(DOT_XML)) {
    122                 if (mIsImageType) {
    123                     return "The filename must end with .xml or .png";
    124                 } else {
    125                     return "The filename must end with .xml";
    126                 }
    127             }
    128 
    129             // Resource names must be valid Java identifiers, since they will
    130             // be represented as Java identifiers in the R file:
    131             if (!Character.isJavaIdentifierStart(newText.charAt(0))) {
    132                 return "The resource name must begin with a character";
    133             }
    134             for (int i = 1, n = newText.length(); i < n; i++) {
    135                 char c = newText.charAt(i);
    136                 if (!Character.isJavaIdentifierPart(c)) {
    137                     return String.format("'%1$c' is not a valid resource name character", c);
    138                 }
    139             }
    140 
    141             if (mIsFileType) {
    142                 char first = newText.charAt(0);
    143                 if (!(first >= 'a' && first <= 'z')) {
    144                     return String.format(
    145                             "File-based resource names must start with a lowercase letter.");
    146                 }
    147 
    148                 // AAPT only allows lowercase+digits+_:
    149                 // "%s: Invalid file name: must contain only [a-z0-9_.]","
    150                 for (int i = 0, n = newText.length(); i < n; i++) {
    151                     char c = newText.charAt(i);
    152                     if (!((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_')) {
    153                         return String.format(
    154                           "File-based resource names must contain only lowercase a-z, 0-9, or _.");
    155                     }
    156                 }
    157             }
    158 
    159             String level = "1.5"; //$NON-NLS-1$
    160             IStatus validIdentifier = JavaConventions.validateIdentifier(newText, level, level);
    161             if (!validIdentifier.isOK()) {
    162                 return String.format("%1$s is not a valid name (reserved Java keyword)", newText);
    163             }
    164 
    165 
    166             if (mExisting != null && (mUnique || mExist)) {
    167                 boolean exists = mExisting.contains(newText);
    168                 if (mUnique && exists) {
    169                     return String.format("%1$s already exists", newText);
    170                 } else if (mExist && !exists) {
    171                     return String.format("%1$s does not exist", newText);
    172                 }
    173             }
    174 
    175             return null;
    176         } catch (Exception e) {
    177             AdtPlugin.log(e, "Validation failed: %s", e.toString());
    178             return ""; //$NON-NLS-1$
    179         }
    180     }
    181 
    182     /**
    183      * Creates a new {@link ResourceNameValidator}
    184      *
    185      * @param allowXmlExtension if true, allow .xml to be entered as a suffix for the
    186      *            resource name
    187      * @param type the resource type of the resource name being validated
    188      * @return a new {@link ResourceNameValidator}
    189      */
    190     public static ResourceNameValidator create(boolean allowXmlExtension,
    191             ResourceFolderType type) {
    192         boolean isFileType = type != ResourceFolderType.VALUES;
    193         return new ResourceNameValidator(allowXmlExtension, null, isFileType,
    194                 type == ResourceFolderType.DRAWABLE);
    195     }
    196 
    197     /**
    198      * Creates a new {@link ResourceNameValidator}
    199      *
    200      * @param allowXmlExtension if true, allow .xml to be entered as a suffix for the
    201      *            resource name
    202      * @param existing An optional set of names that already exist (and therefore will not
    203      *            be considered valid if entered as the new name)
    204      * @param type the resource type of the resource name being validated
    205      * @return a new {@link ResourceNameValidator}
    206      */
    207     public static ResourceNameValidator create(boolean allowXmlExtension, Set<String> existing,
    208             ResourceType type) {
    209         boolean isFileType = ResourceHelper.isFileBasedResourceType(type);
    210         return new ResourceNameValidator(allowXmlExtension, existing, isFileType,
    211                 type == ResourceType.DRAWABLE).unique();
    212     }
    213 
    214     /**
    215      * Creates a new {@link ResourceNameValidator}. By default, the name will need to be
    216      * unique in the project.
    217      *
    218      * @param allowXmlExtension if true, allow .xml to be entered as a suffix for the
    219      *            resource name
    220      * @param project the project to validate new resource names for
    221      * @param type the resource type of the resource name being validated
    222      * @return a new {@link ResourceNameValidator}
    223      */
    224     public static ResourceNameValidator create(boolean allowXmlExtension,
    225             @Nullable IProject project,
    226             @NonNull ResourceType type) {
    227         Set<String> existing = null;
    228         if (project != null) {
    229             existing = new HashSet<String>();
    230             ResourceManager manager = ResourceManager.getInstance();
    231             ProjectResources projectResources = manager.getProjectResources(project);
    232             Collection<ResourceItem> items = projectResources.getResourceItemsOfType(type);
    233             for (ResourceItem item : items) {
    234                 existing.add(item.getName());
    235             }
    236         }
    237 
    238         boolean isFileType = ResourceHelper.isFileBasedResourceType(type);
    239         return new ResourceNameValidator(allowXmlExtension, existing, isFileType,
    240                 type == ResourceType.DRAWABLE);
    241     }
    242 }
    243