/* * 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.wizards.templates; import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectWizard.ATTR_MIN_API; import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectWizard.ATTR_MIN_BUILD_API; import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectWizard.ATTR_REVISION; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_BACKGROUND; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_CLIPART_NAME; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_DESCRIPTION; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_FOREGROUND; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_FORMAT; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_NAME; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_PADDING; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_SHAPE; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_SOURCE_TYPE; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_TEXT; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_TRIM; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.ATTR_TYPE; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.CURRENT_FORMAT; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.TAG_DEPENDENCY; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.TAG_ICONS; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.TAG_PARAMETER; import static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateHandler.TAG_THUMB; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.assetstudiolib.GraphicGenerator; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.internal.assetstudio.AssetType; import com.android.ide.eclipse.adt.internal.assetstudio.CreateAssetSetWizardState; import com.android.ide.eclipse.adt.internal.assetstudio.CreateAssetSetWizardState.SourceType; import com.android.ide.eclipse.adt.internal.editors.IconFactory; import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils; import com.android.utils.Pair; import org.eclipse.core.resources.IProject; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.RGB; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import lombok.ast.libs.org.parboiled.google.collect.Lists; /** An ADT template along with metadata */ class TemplateMetadata { private final Document mDocument; private final List<Parameter> mParameters; private final Map<String, Parameter> mParameterMap; private List<Pair<String, Integer>> mDependencies; private Integer mMinApi; private Integer mMinBuildApi; private Integer mRevision; private boolean mNoIcons; private CreateAssetSetWizardState mIconState; TemplateMetadata(@NonNull Document document) { mDocument = document; NodeList parameters = mDocument.getElementsByTagName(TAG_PARAMETER); mParameters = new ArrayList<Parameter>(parameters.getLength()); mParameterMap = new HashMap<String, Parameter>(parameters.getLength()); for (int index = 0, max = parameters.getLength(); index < max; index++) { Element element = (Element) parameters.item(index); Parameter parameter = new Parameter(element); mParameters.add(parameter); if (parameter.id != null) { mParameterMap.put(parameter.id, parameter); } } } boolean isSupported() { String versionString = mDocument.getDocumentElement().getAttribute(ATTR_FORMAT); if (versionString != null && !versionString.isEmpty()) { try { int version = Integer.parseInt(versionString); return version <= CURRENT_FORMAT; } catch (NumberFormatException nufe) { return false; } } // Older templates without version specified: supported return true; } @Nullable String getTitle() { String name = mDocument.getDocumentElement().getAttribute(ATTR_NAME); if (name != null && !name.isEmpty()) { return name; } return null; } @Nullable String getDescription() { String description = mDocument.getDocumentElement().getAttribute(ATTR_DESCRIPTION); if (description != null && !description.isEmpty()) { return description; } return null; } int getMinSdk() { if (mMinApi == null) { mMinApi = 1; String api = mDocument.getDocumentElement().getAttribute(ATTR_MIN_API); if (api != null && !api.isEmpty()) { try { mMinApi = Integer.parseInt(api); } catch (NumberFormatException nufe) { // Templates aren't allowed to contain codenames, should always be an integer AdtPlugin.log(nufe, null); mMinApi = 1; } } } return mMinApi.intValue(); } int getMinBuildApi() { if (mMinBuildApi == null) { mMinBuildApi = 1; String api = mDocument.getDocumentElement().getAttribute(ATTR_MIN_BUILD_API); if (api != null && !api.isEmpty()) { try { mMinBuildApi = Integer.parseInt(api); } catch (NumberFormatException nufe) { // Templates aren't allowed to contain codenames, should always be an integer AdtPlugin.log(nufe, null); mMinBuildApi = 1; } } } return mMinBuildApi.intValue(); } public int getRevision() { if (mRevision == null) { mRevision = 1; String revision = mDocument.getDocumentElement().getAttribute(ATTR_REVISION); if (revision != null && !revision.isEmpty()) { try { mRevision = Integer.parseInt(revision); } catch (NumberFormatException nufe) { AdtPlugin.log(nufe, null); mRevision = 1; } } } return mRevision.intValue(); } /** * Returns a suitable icon wizard state instance if this wizard requests * icons to be created, and null otherwise * * @return icon wizard state or null */ @Nullable public CreateAssetSetWizardState getIconState(IProject project) { if (mIconState == null && !mNoIcons) { NodeList icons = mDocument.getElementsByTagName(TAG_ICONS); if (icons.getLength() < 1) { mNoIcons = true; return null; } Element icon = (Element) icons.item(0); mIconState = new CreateAssetSetWizardState(); mIconState.project = project; String typeString = getAttributeOrNull(icon, ATTR_TYPE); if (typeString != null) { typeString = typeString.toUpperCase(Locale.US); boolean found = false; for (AssetType type : AssetType.values()) { if (typeString.equals(type.name())) { mIconState.type = type; found = true; break; } } if (!found) { AdtPlugin.log(null, "Unknown asset type %1$s", typeString); } } mIconState.outputName = getAttributeOrNull(icon, ATTR_NAME); if (mIconState.outputName != null) { // Register parameter such that if it is referencing other values, it gets // updated when other values are edited Parameter outputParameter = new Parameter( Parameter.Type.STRING, "_iconname", mIconState.outputName); //$NON-NLS-1$ getParameters().add(outputParameter); } RGB background = getRgb(icon, ATTR_BACKGROUND); if (background != null) { mIconState.background = background; } RGB foreground = getRgb(icon, ATTR_FOREGROUND); if (foreground != null) { mIconState.foreground = foreground; } String shapeString = getAttributeOrNull(icon, ATTR_SHAPE); if (shapeString != null) { shapeString = shapeString.toUpperCase(Locale.US); boolean found = false; for (GraphicGenerator.Shape shape : GraphicGenerator.Shape.values()) { if (shapeString.equals(shape.name())) { mIconState.shape = shape; found = true; break; } } if (!found) { AdtPlugin.log(null, "Unknown shape %1$s", shapeString); } } String trimString = getAttributeOrNull(icon, ATTR_TRIM); if (trimString != null) { mIconState.trim = Boolean.valueOf(trimString); } String paddingString = getAttributeOrNull(icon, ATTR_PADDING); if (paddingString != null) { mIconState.padding = Integer.parseInt(paddingString); } String sourceTypeString = getAttributeOrNull(icon, ATTR_SOURCE_TYPE); if (sourceTypeString != null) { sourceTypeString = sourceTypeString.toUpperCase(Locale.US); boolean found = false; for (SourceType type : SourceType.values()) { if (sourceTypeString.equals(type.name())) { mIconState.sourceType = type; found = true; break; } } if (!found) { AdtPlugin.log(null, "Unknown source type %1$s", sourceTypeString); } } mIconState.clipartName = getAttributeOrNull(icon, ATTR_CLIPART_NAME); String textString = getAttributeOrNull(icon, ATTR_TEXT); if (textString != null) { mIconState.text = textString; } } return mIconState; } void updateIconName(List<Parameter> parameters, StringEvaluator evaluator) { if (mIconState != null) { NodeList icons = mDocument.getElementsByTagName(TAG_ICONS); if (icons.getLength() < 1) { return; } Element icon = (Element) icons.item(0); String name = getAttributeOrNull(icon, ATTR_NAME); if (name != null) { mIconState.outputName = evaluator.evaluate(name, parameters); } } } private static RGB getRgb(@NonNull Element element, @NonNull String name) { String colorString = getAttributeOrNull(element, name); if (colorString != null) { int rgb = ImageUtils.getColor(colorString.trim()); return ImageUtils.intToRgb(rgb); } return null; } @Nullable private static String getAttributeOrNull(@NonNull Element element, @NonNull String name) { String value = element.getAttribute(name); if (value != null && value.isEmpty()) { return null; } return value; } @Nullable String getThumbnailPath() { // Apply selector logic. Pick the thumb first thumb that satisfies the largest number // of conditions. NodeList thumbs = mDocument.getElementsByTagName(TAG_THUMB); if (thumbs.getLength() == 0) { return null; } int bestMatchCount = 0; Element bestMatch = null; for (int i = 0, n = thumbs.getLength(); i < n; i++) { Element thumb = (Element) thumbs.item(i); NamedNodeMap attributes = thumb.getAttributes(); if (bestMatch == null && attributes.getLength() == 0) { bestMatch = thumb; } else if (attributes.getLength() <= bestMatchCount) { // Already have a match with this number of attributes, no point checking continue; } else { boolean match = true; for (int j = 0, max = attributes.getLength(); j < max; j++) { Attr attribute = (Attr) attributes.item(j); Parameter parameter = mParameterMap.get(attribute.getName()); if (parameter == null) { AdtPlugin.log(null, "Unexpected parameter in template thumbnail: %1$s", attribute.getName()); continue; } String thumbNailValue = attribute.getValue(); String editedValue = parameter.value != null ? parameter.value.toString() : ""; if (!thumbNailValue.equals(editedValue)) { match = false; break; } } if (match) { bestMatch = thumb; bestMatchCount = attributes.getLength(); } } } if (bestMatch != null) { NodeList children = bestMatch.getChildNodes(); for (int i = 0, n = children.getLength(); i < n; i++) { Node child = children.item(i); if (child.getNodeType() == Node.TEXT_NODE) { return child.getNodeValue().trim(); } } } return null; } /** * Returns the dependencies (as a list of pairs of names and revisions) * required by this template */ List<Pair<String, Integer>> getDependencies() { if (mDependencies == null) { NodeList elements = mDocument.getElementsByTagName(TAG_DEPENDENCY); if (elements.getLength() == 0) { return Collections.emptyList(); } List<Pair<String, Integer>> dependencies = Lists.newArrayList(); for (int i = 0, n = elements.getLength(); i < n; i++) { Element element = (Element) elements.item(i); String name = element.getAttribute(ATTR_NAME); int revision = -1; String revisionString = element.getAttribute(ATTR_REVISION); if (!revisionString.isEmpty()) { revision = Integer.parseInt(revisionString); } dependencies.add(Pair.of(name, revision)); } mDependencies = dependencies; } return mDependencies; } /** Returns the list of available parameters */ @NonNull List<Parameter> getParameters() { return mParameters; } /** * Returns the parameter of the given id, or null if not found * * @param id the id of the target parameter * @return the corresponding parameter, or null if not found */ @Nullable public Parameter getParameter(@NonNull String id) { for (Parameter parameter : mParameters) { if (id.equals(parameter.id)) { return parameter; } } return null; } /** Returns a default icon for templates */ static Image getDefaultTemplateIcon() { return IconFactory.getInstance().getIcon("default_template"); //$NON-NLS-1$ } }