• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TemplateMetadata(@onNull Document document)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(this, element);
90             mParameters.add(parameter);
91             if (parameter.id != null) {
92                 mParameterMap.put(parameter.id, parameter);
93             }
94         }
95     }
96 
isSupported()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
getTitle()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
getDescription()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 
getMinSdk()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 
getMinBuildApi()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 
getRevision()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
getIconState(IProject project)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(this,
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 
updateIconName(List<Parameter> parameters, StringEvaluator evaluator)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 
getRgb(@onNull Element element, @NonNull String name)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
getAttributeOrNull(@onNull Element element, @NonNull String name)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
getThumbnailPath()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      */
getDependencies()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
getParameters()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
getParameter(@onNull String id)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 */
getDefaultTemplateIcon()430     static Image getDefaultTemplateIcon() {
431         return IconFactory.getInstance().getIcon("default_template"); //$NON-NLS-1$
432     }
433 }
434