Home | History | Annotate | Download | only in templates
      1 /*
      2  * Copyright (C) 2012 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 package com.android.ide.eclipse.adt.internal.wizards.templates;
     17 
     18 import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectWizard.ATTR_MIN_API;
     19 import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectWizard.ATTR_MIN_BUILD_API;
     20 import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectWizard.ATTR_REVISION;
     21 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_BACKGROUND;
     22 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_CLIPART_NAME;
     23 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_DESCRIPTION;
     24 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_FOREGROUND;
     25 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_FORMAT;
     26 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_NAME;
     27 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_PADDING;
     28 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_SHAPE;
     29 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_SOURCE_TYPE;
     30 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_TEXT;
     31 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_TRIM;
     32 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_TYPE;
     33 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.CURRENT_FORMAT;
     34 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.TAG_DEPENDENCY;
     35 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.TAG_ICONS;
     36 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.TAG_PARAMETER;
     37 import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.TAG_THUMB;
     38 
     39 import com.android.annotations.NonNull;
     40 import com.android.annotations.Nullable;
     41 import com.android.assetstudiolib.GraphicGenerator;
     42 import com.android.ide.eclipse.adt.AdtPlugin;
     43 import com.android.ide.eclipse.adt.internal.assetstudio.AssetType;
     44 import com.android.ide.eclipse.adt.internal.assetstudio.CreateAssetSetWizardState;
     45 import com.android.ide.eclipse.adt.internal.assetstudio.CreateAssetSetWizardState.SourceType;
     46 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
     47 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils;
     48 import com.android.utils.Pair;
     49 
     50 import org.eclipse.core.resources.IProject;
     51 import org.eclipse.swt.graphics.Image;
     52 import org.eclipse.swt.graphics.RGB;
     53 import org.w3c.dom.Attr;
     54 import org.w3c.dom.Document;
     55 import org.w3c.dom.Element;
     56 import org.w3c.dom.NamedNodeMap;
     57 import org.w3c.dom.Node;
     58 import org.w3c.dom.NodeList;
     59 
     60 import java.util.ArrayList;
     61 import java.util.Collections;
     62 import java.util.HashMap;
     63 import java.util.List;
     64 import java.util.Locale;
     65 import java.util.Map;
     66 
     67 import lombok.ast.libs.org.parboiled.google.collect.Lists;
     68 
     69 /** An ADT template along with metadata */
     70 class TemplateMetadata {
     71     private final Document mDocument;
     72     private final List<Parameter> mParameters;
     73     private final Map<String, Parameter> mParameterMap;
     74     private List<Pair<String, Integer>> mDependencies;
     75     private Integer mMinApi;
     76     private Integer mMinBuildApi;
     77     private Integer mRevision;
     78     private boolean mNoIcons;
     79     private CreateAssetSetWizardState mIconState;
     80 
     81     TemplateMetadata(@NonNull Document document) {
     82         mDocument = document;
     83 
     84         NodeList parameters = mDocument.getElementsByTagName(TAG_PARAMETER);
     85         mParameters = new ArrayList<Parameter>(parameters.getLength());
     86         mParameterMap = new HashMap<String, Parameter>(parameters.getLength());
     87         for (int index = 0, max = parameters.getLength(); index < max; index++) {
     88             Element element = (Element) parameters.item(index);
     89             Parameter parameter = new Parameter(element);
     90             mParameters.add(parameter);
     91             if (parameter.id != null) {
     92                 mParameterMap.put(parameter.id, parameter);
     93             }
     94         }
     95     }
     96 
     97     boolean isSupported() {
     98         String versionString = mDocument.getDocumentElement().getAttribute(ATTR_FORMAT);
     99         if (versionString != null && !versionString.isEmpty()) {
    100             try {
    101                 int version = Integer.parseInt(versionString);
    102                 return version <= CURRENT_FORMAT;
    103             } catch (NumberFormatException nufe) {
    104                 return false;
    105             }
    106         }
    107 
    108         // Older templates without version specified: supported
    109         return true;
    110     }
    111 
    112     @Nullable
    113     String getTitle() {
    114         String name = mDocument.getDocumentElement().getAttribute(ATTR_NAME);
    115         if (name != null && !name.isEmpty()) {
    116             return name;
    117         }
    118 
    119         return null;
    120     }
    121 
    122     @Nullable
    123     String getDescription() {
    124         String description = mDocument.getDocumentElement().getAttribute(ATTR_DESCRIPTION);
    125         if (description != null && !description.isEmpty()) {
    126             return description;
    127         }
    128 
    129         return null;
    130     }
    131 
    132     int getMinSdk() {
    133         if (mMinApi == null) {
    134             mMinApi = 1;
    135             String api = mDocument.getDocumentElement().getAttribute(ATTR_MIN_API);
    136             if (api != null && !api.isEmpty()) {
    137                 try {
    138                     mMinApi = Integer.parseInt(api);
    139                 } catch (NumberFormatException nufe) {
    140                     // Templates aren't allowed to contain codenames, should always be an integer
    141                     AdtPlugin.log(nufe, null);
    142                     mMinApi = 1;
    143                 }
    144             }
    145         }
    146 
    147         return mMinApi.intValue();
    148     }
    149 
    150     int getMinBuildApi() {
    151         if (mMinBuildApi == null) {
    152             mMinBuildApi = 1;
    153             String api = mDocument.getDocumentElement().getAttribute(ATTR_MIN_BUILD_API);
    154             if (api != null && !api.isEmpty()) {
    155                 try {
    156                     mMinBuildApi = Integer.parseInt(api);
    157                 } catch (NumberFormatException nufe) {
    158                     // Templates aren't allowed to contain codenames, should always be an integer
    159                     AdtPlugin.log(nufe, null);
    160                     mMinBuildApi = 1;
    161                 }
    162             }
    163         }
    164 
    165         return mMinBuildApi.intValue();
    166     }
    167 
    168     public int getRevision() {
    169         if (mRevision == null) {
    170             mRevision = 1;
    171             String revision = mDocument.getDocumentElement().getAttribute(ATTR_REVISION);
    172             if (revision != null && !revision.isEmpty()) {
    173                 try {
    174                     mRevision = Integer.parseInt(revision);
    175                 } catch (NumberFormatException nufe) {
    176                     AdtPlugin.log(nufe, null);
    177                     mRevision = 1;
    178                 }
    179             }
    180         }
    181 
    182         return mRevision.intValue();
    183     }
    184 
    185     /**
    186      * Returns a suitable icon wizard state instance if this wizard requests
    187      * icons to be created, and null otherwise
    188      *
    189      * @return icon wizard state or null
    190      */
    191     @Nullable
    192     public CreateAssetSetWizardState getIconState(IProject project) {
    193         if (mIconState == null && !mNoIcons) {
    194             NodeList icons = mDocument.getElementsByTagName(TAG_ICONS);
    195             if (icons.getLength() < 1) {
    196                 mNoIcons = true;
    197                 return null;
    198             }
    199             Element icon = (Element) icons.item(0);
    200 
    201             mIconState = new CreateAssetSetWizardState();
    202             mIconState.project = project;
    203 
    204             String typeString = getAttributeOrNull(icon, ATTR_TYPE);
    205             if (typeString != null) {
    206                 typeString = typeString.toUpperCase(Locale.US);
    207                 boolean found = false;
    208                 for (AssetType type : AssetType.values()) {
    209                     if (typeString.equals(type.name())) {
    210                         mIconState.type = type;
    211                         found = true;
    212                         break;
    213                     }
    214                 }
    215                 if (!found) {
    216                     AdtPlugin.log(null, "Unknown asset type %1$s", typeString);
    217                 }
    218             }
    219 
    220             mIconState.outputName = getAttributeOrNull(icon, ATTR_NAME);
    221             if (mIconState.outputName != null) {
    222                 // Register parameter such that if it is referencing other values, it gets
    223                 // updated when other values are edited
    224                 Parameter outputParameter = new Parameter(
    225                         Parameter.Type.STRING, "_iconname", mIconState.outputName); //$NON-NLS-1$
    226                 getParameters().add(outputParameter);
    227             }
    228 
    229             RGB background = getRgb(icon, ATTR_BACKGROUND);
    230             if (background != null) {
    231                 mIconState.background = background;
    232             }
    233             RGB foreground = getRgb(icon, ATTR_FOREGROUND);
    234             if (foreground != null) {
    235                 mIconState.foreground = foreground;
    236             }
    237             String shapeString = getAttributeOrNull(icon, ATTR_SHAPE);
    238             if (shapeString != null) {
    239                 shapeString = shapeString.toUpperCase(Locale.US);
    240                 boolean found = false;
    241                 for (GraphicGenerator.Shape shape : GraphicGenerator.Shape.values()) {
    242                     if (shapeString.equals(shape.name())) {
    243                         mIconState.shape = shape;
    244                         found = true;
    245                         break;
    246                     }
    247                 }
    248                 if (!found) {
    249                     AdtPlugin.log(null, "Unknown shape %1$s", shapeString);
    250                 }
    251             }
    252             String trimString = getAttributeOrNull(icon, ATTR_TRIM);
    253             if (trimString != null) {
    254                 mIconState.trim = Boolean.valueOf(trimString);
    255             }
    256             String paddingString = getAttributeOrNull(icon, ATTR_PADDING);
    257             if (paddingString != null) {
    258                 mIconState.padding = Integer.parseInt(paddingString);
    259             }
    260             String sourceTypeString = getAttributeOrNull(icon, ATTR_SOURCE_TYPE);
    261             if (sourceTypeString != null) {
    262                 sourceTypeString = sourceTypeString.toUpperCase(Locale.US);
    263                 boolean found = false;
    264                 for (SourceType type : SourceType.values()) {
    265                     if (sourceTypeString.equals(type.name())) {
    266                         mIconState.sourceType = type;
    267                         found = true;
    268                         break;
    269                     }
    270                 }
    271                 if (!found) {
    272                     AdtPlugin.log(null, "Unknown source type %1$s", sourceTypeString);
    273                 }
    274             }
    275             mIconState.clipartName = getAttributeOrNull(icon, ATTR_CLIPART_NAME);
    276 
    277             String textString = getAttributeOrNull(icon, ATTR_TEXT);
    278             if (textString != null) {
    279                 mIconState.text = textString;
    280             }
    281         }
    282 
    283         return mIconState;
    284     }
    285 
    286     void updateIconName(List<Parameter> parameters, StringEvaluator evaluator) {
    287         if (mIconState != null) {
    288             NodeList icons = mDocument.getElementsByTagName(TAG_ICONS);
    289             if (icons.getLength() < 1) {
    290                 return;
    291             }
    292             Element icon = (Element) icons.item(0);
    293             String name = getAttributeOrNull(icon, ATTR_NAME);
    294             if (name != null) {
    295                 mIconState.outputName = evaluator.evaluate(name, parameters);
    296             }
    297         }
    298     }
    299 
    300     private static RGB getRgb(@NonNull Element element, @NonNull String name) {
    301         String colorString = getAttributeOrNull(element, name);
    302         if (colorString != null) {
    303             int rgb = ImageUtils.getColor(colorString.trim());
    304             return ImageUtils.intToRgb(rgb);
    305         }
    306 
    307         return null;
    308     }
    309 
    310     @Nullable
    311     private static String getAttributeOrNull(@NonNull Element element, @NonNull String name) {
    312         String value = element.getAttribute(name);
    313         if (value != null && value.isEmpty()) {
    314             return null;
    315         }
    316         return value;
    317     }
    318 
    319     @Nullable
    320     String getThumbnailPath() {
    321         // Apply selector logic. Pick the thumb first thumb that satisfies the largest number
    322         // of conditions.
    323         NodeList thumbs = mDocument.getElementsByTagName(TAG_THUMB);
    324         if (thumbs.getLength() == 0) {
    325             return null;
    326         }
    327 
    328 
    329         int bestMatchCount = 0;
    330         Element bestMatch = null;
    331 
    332         for (int i = 0, n = thumbs.getLength(); i < n; i++) {
    333             Element thumb = (Element) thumbs.item(i);
    334 
    335             NamedNodeMap attributes = thumb.getAttributes();
    336             if (bestMatch == null && attributes.getLength() == 0) {
    337                 bestMatch = thumb;
    338             } else if (attributes.getLength() <= bestMatchCount) {
    339                 // Already have a match with this number of attributes, no point checking
    340                 continue;
    341             } else {
    342                 boolean match = true;
    343                 for (int j = 0, max = attributes.getLength(); j < max; j++) {
    344                     Attr attribute = (Attr) attributes.item(j);
    345                     Parameter parameter = mParameterMap.get(attribute.getName());
    346                     if (parameter == null) {
    347                         AdtPlugin.log(null, "Unexpected parameter in template thumbnail: %1$s",
    348                                 attribute.getName());
    349                         continue;
    350                     }
    351                     String thumbNailValue = attribute.getValue();
    352                     String editedValue = parameter.value != null ? parameter.value.toString() : "";
    353                     if (!thumbNailValue.equals(editedValue)) {
    354                         match = false;
    355                         break;
    356                     }
    357                 }
    358                 if (match) {
    359                     bestMatch = thumb;
    360                     bestMatchCount = attributes.getLength();
    361                 }
    362             }
    363         }
    364 
    365         if (bestMatch != null) {
    366             NodeList children = bestMatch.getChildNodes();
    367             for (int i = 0, n = children.getLength(); i < n; i++) {
    368                 Node child = children.item(i);
    369                 if (child.getNodeType() == Node.TEXT_NODE) {
    370                     return child.getNodeValue().trim();
    371                 }
    372             }
    373         }
    374 
    375         return null;
    376     }
    377 
    378     /**
    379      * Returns the dependencies (as a list of pairs of names and revisions)
    380      * required by this template
    381      */
    382     List<Pair<String, Integer>> getDependencies() {
    383         if (mDependencies == null) {
    384             NodeList elements = mDocument.getElementsByTagName(TAG_DEPENDENCY);
    385             if (elements.getLength() == 0) {
    386                 return Collections.emptyList();
    387             }
    388 
    389             List<Pair<String, Integer>> dependencies = Lists.newArrayList();
    390             for (int i = 0, n = elements.getLength(); i < n; i++) {
    391                 Element element = (Element) elements.item(i);
    392                 String name = element.getAttribute(ATTR_NAME);
    393                 int revision = -1;
    394                 String revisionString = element.getAttribute(ATTR_REVISION);
    395                 if (!revisionString.isEmpty()) {
    396                     revision = Integer.parseInt(revisionString);
    397                 }
    398                 dependencies.add(Pair.of(name, revision));
    399             }
    400             mDependencies = dependencies;
    401         }
    402 
    403         return mDependencies;
    404     }
    405 
    406     /** Returns the list of available parameters */
    407     @NonNull
    408     List<Parameter> getParameters() {
    409         return mParameters;
    410     }
    411 
    412     /**
    413      * Returns the parameter of the given id, or null if not found
    414      *
    415      * @param id the id of the target parameter
    416      * @return the corresponding parameter, or null if not found
    417      */
    418     @Nullable
    419     public Parameter getParameter(@NonNull String id) {
    420         for (Parameter parameter : mParameters) {
    421             if (id.equals(parameter.id)) {
    422                 return parameter;
    423             }
    424         }
    425 
    426         return null;
    427     }
    428 
    429     /** Returns a default icon for templates */
    430     static Image getDefaultTemplateIcon() {
    431         return IconFactory.getInstance().getIcon("default_template"); //$NON-NLS-1$
    432     }
    433 }
    434