17dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye/*
27dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye * Copyright (C) 2012 The Android Open Source Project
37dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye *
47dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye * Licensed under the Eclipse Public License, Version 1.0 (the "License");
57dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye * you may not use this file except in compliance with the License.
67dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye * You may obtain a copy of the License at
77dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye *
87dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye *      http://www.eclipse.org/org/documents/epl-v10.php
97dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye *
107dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye * Unless required by applicable law or agreed to in writing, software
117dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye * distributed under the License is distributed on an "AS IS" BASIS,
127dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye * See the License for the specific language governing permissions and
147dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye * limitations under the License.
157dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye */
167dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyepackage com.android.ide.eclipse.adt.internal.wizards.templates;
177dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
180386f5dcf9d0f472f243506be3c40f5cf46287a2Raphael Mollimport static com.android.SdkConstants.DOT_AIDL;
1912d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.DOT_FTL;
2012d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.DOT_JAVA;
2112d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.DOT_RS;
2212d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.DOT_SVG;
2312d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.DOT_TXT;
2412d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.DOT_XML;
2512d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.EXT_XML;
260386f5dcf9d0f472f243506be3c40f5cf46287a2Raphael Mollimport static com.android.SdkConstants.FD_NATIVE_LIBS;
27dd0a8b2ec052e0cc670cb8738b0f6ed7b292f122Tor Norbyeimport static com.android.ide.eclipse.adt.internal.wizards.templates.InstallDependencyPage.SUPPORT_LIBRARY_NAME;
286d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbyeimport static com.android.ide.eclipse.adt.internal.wizards.templates.TemplateManager.getTemplateRootFolder;
297dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
3081cefe2a26dd6db8a878e30874d12cdcbff0e83bXavier Ducrohetimport com.android.SdkConstants;
317dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport com.android.annotations.NonNull;
327dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport com.android.annotations.Nullable;
337dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport com.android.ide.eclipse.adt.AdtPlugin;
347dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport com.android.ide.eclipse.adt.AdtUtils;
35b8397698e2834ed8af03c1bff7d570aff0e2f5bbRaphael Mollimport com.android.ide.eclipse.adt.internal.actions.AddSupportJarAction;
367dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.formatting.XmlFormatPreferences;
377dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.formatting.XmlFormatStyle;
387dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.formatting.XmlPrettyPrinter;
397dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities;
4057d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbyeimport com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
410386f5dcf9d0f472f243506be3c40f5cf46287a2Raphael Mollimport com.android.ide.eclipse.adt.internal.sdk.AdtManifestMergeCallback;
427dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport com.android.manifmerger.ManifestMerger;
431311018325c75993ae84183b41d3fd915bce965dRaphael Mollimport com.android.manifmerger.MergerLog;
447dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport com.android.resources.ResourceFolderType;
451fb460987f7d832adf12290f41448d0c16a95972Siva Velusamyimport com.android.utils.SdkUtils;
467dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport com.google.common.base.Charsets;
47dd0a8b2ec052e0cc670cb8738b0f6ed7b292f122Tor Norbyeimport com.google.common.collect.Lists;
487dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport com.google.common.io.Files;
497dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
507dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport freemarker.cache.TemplateLoader;
517dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport freemarker.template.Configuration;
527dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport freemarker.template.DefaultObjectWrapper;
537dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport freemarker.template.Template;
547dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport freemarker.template.TemplateException;
557dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
56802de810020fba3f86282cd1d66597a2a41698e3Tor Norbyeimport org.eclipse.core.resources.IFile;
57802de810020fba3f86282cd1d66597a2a41698e3Tor Norbyeimport org.eclipse.core.resources.IProject;
58802de810020fba3f86282cd1d66597a2a41698e3Tor Norbyeimport org.eclipse.core.resources.IResource;
5957d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbyeimport org.eclipse.core.runtime.CoreException;
60802de810020fba3f86282cd1d66597a2a41698e3Tor Norbyeimport org.eclipse.core.runtime.IPath;
617f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbyeimport org.eclipse.core.runtime.IStatus;
627dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport org.eclipse.core.runtime.Path;
637f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbyeimport org.eclipse.core.runtime.Status;
6457d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbyeimport org.eclipse.jdt.core.IJavaProject;
6557d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbyeimport org.eclipse.jdt.core.JavaCore;
6657d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbyeimport org.eclipse.jdt.core.ToolFactory;
6757d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbyeimport org.eclipse.jdt.core.formatter.CodeFormatter;
687dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport org.eclipse.jface.dialogs.MessageDialog;
6957d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbyeimport org.eclipse.jface.text.IDocument;
70802de810020fba3f86282cd1d66597a2a41698e3Tor Norbyeimport org.eclipse.ltk.core.refactoring.Change;
71802de810020fba3f86282cd1d66597a2a41698e3Tor Norbyeimport org.eclipse.ltk.core.refactoring.NullChange;
72802de810020fba3f86282cd1d66597a2a41698e3Tor Norbyeimport org.eclipse.ltk.core.refactoring.TextFileChange;
737dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport org.eclipse.swt.SWT;
74802de810020fba3f86282cd1d66597a2a41698e3Tor Norbyeimport org.eclipse.text.edits.InsertEdit;
75802de810020fba3f86282cd1d66597a2a41698e3Tor Norbyeimport org.eclipse.text.edits.MultiTextEdit;
76802de810020fba3f86282cd1d66597a2a41698e3Tor Norbyeimport org.eclipse.text.edits.ReplaceEdit;
7757d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbyeimport org.eclipse.text.edits.TextEdit;
787f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbyeimport org.osgi.framework.Constants;
797f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbyeimport org.osgi.framework.Version;
807dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport org.w3c.dom.Document;
817dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport org.w3c.dom.Element;
827dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport org.w3c.dom.Node;
837dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport org.w3c.dom.NodeList;
847dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport org.xml.sax.Attributes;
857dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport org.xml.sax.SAXException;
867dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport org.xml.sax.helpers.DefaultHandler;
877dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
887dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport java.io.ByteArrayInputStream;
897dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport java.io.File;
907dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport java.io.IOException;
917dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport java.io.InputStreamReader;
927dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport java.io.Reader;
937dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport java.io.StringWriter;
947dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport java.io.Writer;
957dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport java.net.URL;
967dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport java.util.ArrayList;
977dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport java.util.Arrays;
987dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport java.util.HashMap;
997dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport java.util.List;
1007dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport java.util.Map;
1017dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
1027dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport javax.xml.parsers.SAXParser;
1037dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeimport javax.xml.parsers.SAXParserFactory;
1047dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
1057dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye/**
1067dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye * Handler which manages instantiating FreeMarker templates, copying resources
1077dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye * and merging into existing files
1087dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye */
1097dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbyeclass TemplateHandler {
110c689003930bebacf2aeffe48a361302c8b4697b5Tor Norbye    /** Highest supported format; templates with a higher number will be skipped
111c689003930bebacf2aeffe48a361302c8b4697b5Tor Norbye     * <p>
112c689003930bebacf2aeffe48a361302c8b4697b5Tor Norbye     * <ul>
113c689003930bebacf2aeffe48a361302c8b4697b5Tor Norbye     * <li> 1: Initial format, supported by ADT 20 and up.
114c689003930bebacf2aeffe48a361302c8b4697b5Tor Norbye     * <li> 2: ADT 21 and up. Boolean variables that have a default value and are not
115c689003930bebacf2aeffe48a361302c8b4697b5Tor Norbye     *    edited by the user would end up as strings in ADT 20; now they are always
116c689003930bebacf2aeffe48a361302c8b4697b5Tor Norbye     *    proper Booleans. Templates which rely on this should specify format >= 2.
11733c1e883ce229219268bb5584d4769106c736c39Tor Norbye     * <li> 3: The wizard infrastructure passes the {@code isNewProject} boolean variable
11833c1e883ce229219268bb5584d4769106c736c39Tor Norbye     *    to indicate whether a wizard is created as part of a new blank project
119c689003930bebacf2aeffe48a361302c8b4697b5Tor Norbye     * </ul>
120c689003930bebacf2aeffe48a361302c8b4697b5Tor Norbye     */
12133c1e883ce229219268bb5584d4769106c736c39Tor Norbye    static final int CURRENT_FORMAT = 3;
1227f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye
1237dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /**
1247dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * Special marker indicating that this path refers to the special shared
1257dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * resource directory rather than being somewhere inside the root/ directory
1267dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * where all template specific resources are found
1277dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     */
1287dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    private static final String VALUE_TEMPLATE_DIR = "$TEMPLATEDIR"; //$NON-NLS-1$
1297dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
1307dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /**
1317dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * Directory within the template which contains the resources referenced
1327dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * from the template.xml file
1337dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     */
1347dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    private static final String DATA_ROOT = "root";      //$NON-NLS-1$
1357dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
1367dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /**
1377dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * Shared resource directory containing common resources shared among
1387dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * multiple templates
1397dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     */
14056afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye    private static final String RESOURCE_ROOT = "resources";   //$NON-NLS-1$
1417dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
1427dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /** Reserved filename which describes each template */
1437dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String TEMPLATE_XML = "template.xml";   //$NON-NLS-1$
1447dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
1457dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    // Various tags and attributes used in the template metadata files - template.xml,
1467dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    // globals.xml.ftl, recipe.xml.ftl, etc.
1477dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
1487dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String TAG_MERGE = "merge";             //$NON-NLS-1$
1497dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String TAG_EXECUTE = "execute";         //$NON-NLS-1$
1507dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String TAG_GLOBALS = "globals";         //$NON-NLS-1$
1517dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String TAG_GLOBAL = "global";           //$NON-NLS-1$
1527dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String TAG_PARAMETER = "parameter";     //$NON-NLS-1$
1537dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String TAG_COPY = "copy";               //$NON-NLS-1$
1547dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String TAG_INSTANTIATE = "instantiate"; //$NON-NLS-1$
1557dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String TAG_OPEN = "open";               //$NON-NLS-1$
156fd54b68d77bc90d5c607517c27bd7a30a2a8a57aTor Norbye    static final String TAG_THUMB = "thumb";             //$NON-NLS-1$
157fd54b68d77bc90d5c607517c27bd7a30a2a8a57aTor Norbye    static final String TAG_THUMBS = "thumbs";           //$NON-NLS-1$
1587f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye    static final String TAG_DEPENDENCY = "dependency";   //$NON-NLS-1$
159279445ad4561895db41309681de8dd1544d0ae22Tor Norbye    static final String TAG_ICONS = "icons";             //$NON-NLS-1$
1607f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye    static final String ATTR_FORMAT = "format";          //$NON-NLS-1$
1617f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye    static final String ATTR_REVISION = "revision";      //$NON-NLS-1$
1627dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String ATTR_VALUE = "value";            //$NON-NLS-1$
1637dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String ATTR_DEFAULT = "default";        //$NON-NLS-1$
1647dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String ATTR_SUGGEST = "suggest";        //$NON-NLS-1$
1657dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String ATTR_ID = "id";                  //$NON-NLS-1$
1667dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String ATTR_NAME = "name";              //$NON-NLS-1$
1677dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String ATTR_DESCRIPTION = "description";//$NON-NLS-1$
1687dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String ATTR_TYPE = "type";              //$NON-NLS-1$
1697dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String ATTR_HELP = "help";              //$NON-NLS-1$
1707dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String ATTR_FILE = "file";              //$NON-NLS-1$
1717dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String ATTR_TO = "to";                  //$NON-NLS-1$
1727dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String ATTR_FROM = "from";              //$NON-NLS-1$
1737dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final String ATTR_CONSTRAINTS = "constraints";//$NON-NLS-1$
174279445ad4561895db41309681de8dd1544d0ae22Tor Norbye    static final String ATTR_BACKGROUND = "background";  //$NON-NLS-1$
175279445ad4561895db41309681de8dd1544d0ae22Tor Norbye    static final String ATTR_FOREGROUND = "foreground";  //$NON-NLS-1$
176279445ad4561895db41309681de8dd1544d0ae22Tor Norbye    static final String ATTR_SHAPE = "shape";            //$NON-NLS-1$
177279445ad4561895db41309681de8dd1544d0ae22Tor Norbye    static final String ATTR_TRIM = "trim";              //$NON-NLS-1$
178279445ad4561895db41309681de8dd1544d0ae22Tor Norbye    static final String ATTR_PADDING = "padding";        //$NON-NLS-1$
179279445ad4561895db41309681de8dd1544d0ae22Tor Norbye    static final String ATTR_SOURCE_TYPE = "source";     //$NON-NLS-1$
180279445ad4561895db41309681de8dd1544d0ae22Tor Norbye    static final String ATTR_CLIPART_NAME = "clipartName";//$NON-NLS-1$
181279445ad4561895db41309681de8dd1544d0ae22Tor Norbye    static final String ATTR_TEXT = "text";              //$NON-NLS-1$
1827dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
18356afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye    static final String CATEGORY_ACTIVITIES = "activities";//$NON-NLS-1$
18456afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye    static final String CATEGORY_PROJECTS = "projects";    //$NON-NLS-1$
18556afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye    static final String CATEGORY_OTHER = "other";          //$NON-NLS-1$
18656afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye
18756afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye
1887dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /** Default padding to apply in wizards around the thumbnail preview images */
1897dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final int PREVIEW_PADDING = 10;
1907dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
1917dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /** Default width to scale thumbnail preview images in wizards to */
1927dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static final int PREVIEW_WIDTH = 200;
1937dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
1947dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /**
1957dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * List of files to open after the wizard has been created (these are
1967dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * identified by {@link #TAG_OPEN} elements in the recipe file
1977dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     */
1987dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    private final List<String> mOpen = Lists.newArrayList();
1997dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
2007dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /** Path to the directory containing the templates */
20156afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye    @NonNull
2027dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    private final File mRootPath;
2037dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
204802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye    /** The changes being processed by the template handler */
205802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye    private List<Change> mMergeChanges;
206802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye    private List<Change> mTextChanges;
207802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye    private List<Change> mOtherChanges;
208802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye
209802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye    /** The project to write the template into */
210802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye    private IProject mProject;
211802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye
2127dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /** The template loader which is responsible for finding (and sharing) template files */
2137dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    private final MyTemplateLoader mLoader;
2147dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
2157dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /** Agree to all file-overwrites from now on? */
2167dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    private boolean mYesToAll = false;
2177dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
2187dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /** Is writing the template cancelled? */
2197dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    private boolean mNoToAll = false;
2207dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
2217dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /**
2227dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * Should files that we merge contents into be backed up? If yes, will
2237dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * create emacs-style tilde-file backups (filename.xml~)
2247dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     */
2257dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    private boolean mBackupMergedFiles = true;
2267dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
227fd54b68d77bc90d5c607517c27bd7a30a2a8a57aTor Norbye    /**
228fd54b68d77bc90d5c607517c27bd7a30a2a8a57aTor Norbye     * Template metadata
229fd54b68d77bc90d5c607517c27bd7a30a2a8a57aTor Norbye     */
230fd54b68d77bc90d5c607517c27bd7a30a2a8a57aTor Norbye    private TemplateMetadata mTemplate;
231fd54b68d77bc90d5c607517c27bd7a30a2a8a57aTor Norbye
2326d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye    private TemplateManager mManager;
2336d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye
2347dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /** Creates a new {@link TemplateHandler} for the given root path */
2357dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static TemplateHandler createFromPath(File rootPath) {
2366d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye        return new TemplateHandler(rootPath, new TemplateManager());
2377dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
2387dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
23956afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye    /** Creates a new {@link TemplateHandler} for the template name, which should
24056afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye     * be relative to the templates directory */
2416d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye    static TemplateHandler createFromName(String category, String name) {
2426d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye        TemplateManager manager = new TemplateManager();
2436d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye
2446d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye        // Use the TemplateManager iteration which should merge contents between the
2456d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye        // extras/templates/ and tools/templates folders and pick the most recent version
2466d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye        List<File> templates = manager.getTemplates(category);
2476d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye        for (File file : templates) {
2486d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye            if (file.getName().equals(name) && category.equals(file.getParentFile().getName())) {
2496d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye                return new TemplateHandler(file, manager);
2506d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye            }
2516d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye        }
2526d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye
2536d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye        return new TemplateHandler(new File(getTemplateRootFolder(),
2546d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye                category + File.separator + name), manager);
25556afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye    }
25656afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye
2576d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye    private TemplateHandler(File rootPath, TemplateManager manager) {
2587dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        mRootPath = rootPath;
2596d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye        mManager = manager;
2607dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        mLoader = new MyTemplateLoader();
2617dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        mLoader.setPrefix(mRootPath.getPath());
2627dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
2637dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
2646d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye    public TemplateManager getManager() {
2656d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye        return mManager;
2666d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye    }
2676d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye
2687dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    public void setBackupMergedFiles(boolean backupMergedFiles) {
2697dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        mBackupMergedFiles = backupMergedFiles;
2707dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
2717dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
272802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye    @NonNull
273802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye    public List<Change> render(IProject project, Map<String, Object> args) {
274802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        mOpen.clear();
275802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye
276802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        mProject = project;
277802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        mMergeChanges = new ArrayList<Change>();
278802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        mTextChanges = new ArrayList<Change>();
279802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        mOtherChanges = new ArrayList<Change>();
2807dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
2817dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        // Render the instruction list template.
2827dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        Map<String, Object> paramMap = createParameterMap(args);
2837dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        Configuration freemarker = new Configuration();
2847dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        freemarker.setObjectWrapper(new DefaultObjectWrapper());
2857dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        freemarker.setTemplateLoader(mLoader);
2867dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
287802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        processVariables(freemarker, TEMPLATE_XML, paramMap);
288802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye
289802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        // Add the changes in the order where merges are shown first, then text files,
290802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        // and finally other files (like jars and icons which don't have previews).
291802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        List<Change> changes = new ArrayList<Change>();
292802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        changes.addAll(mMergeChanges);
293802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        changes.addAll(mTextChanges);
294802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        changes.addAll(mOtherChanges);
295802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        return changes;
2967dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
2977dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
2987dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    Map<String, Object> createParameterMap(Map<String, Object> args) {
2997dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        final Map<String, Object> paramMap = createBuiltinMap();
3007dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
3017dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        // Wizard parameters supplied by user, specific to this template
3027dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        paramMap.putAll(args);
3037dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
3047dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        return paramMap;
3057dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
3067dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
3077dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /** Data model for the templates */
3087dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    static Map<String, Object> createBuiltinMap() {
3097dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        // Create the data model.
3107dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        final Map<String, Object> paramMap = new HashMap<String, Object>();
3117dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
3127dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        // Builtin conversion methods
3137dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        paramMap.put("slashedPackageName", new FmSlashedPackageNameMethod());       //$NON-NLS-1$
3147dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        paramMap.put("camelCaseToUnderscore", new FmCamelCaseToUnderscoreMethod()); //$NON-NLS-1$
3157dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        paramMap.put("underscoreToCamelCase", new FmUnderscoreToCamelCaseMethod()); //$NON-NLS-1$
3167dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        paramMap.put("activityToLayout", new FmActivityToLayoutMethod());           //$NON-NLS-1$
3177dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        paramMap.put("layoutToActivity", new FmLayoutToActivityMethod());           //$NON-NLS-1$
318c2548413ad5c85a5e2863fb178f478a9fad3d0f6Tor Norbye        paramMap.put("classToResource", new FmClassNameToResourceMethod());         //$NON-NLS-1$
319454f0e05d3e202320b0cd7bc176360458e88658eTor Norbye        paramMap.put("escapeXmlAttribute", new FmEscapeXmlStringMethod());          //$NON-NLS-1$
320454f0e05d3e202320b0cd7bc176360458e88658eTor Norbye        paramMap.put("escapeXmlText", new FmEscapeXmlStringMethod());               //$NON-NLS-1$
321454f0e05d3e202320b0cd7bc176360458e88658eTor Norbye        paramMap.put("escapeXmlString", new FmEscapeXmlStringMethod());             //$NON-NLS-1$
322454f0e05d3e202320b0cd7bc176360458e88658eTor Norbye        paramMap.put("extractLetters", new FmExtractLettersMethod());               //$NON-NLS-1$
3237dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
3247dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        // This should be handled better: perhaps declared "required packages" as part of the
3257dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        // inputs? (It would be better if we could conditionally disable template based
3267dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        // on availability)
3277dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        Map<String, String> builtin = new HashMap<String, String>();
3287dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        builtin.put("templatesRes", VALUE_TEMPLATE_DIR); //$NON-NLS-1$
3297dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        paramMap.put("android", builtin);                //$NON-NLS-1$
3307dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
3317dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        return paramMap;
3327dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
3337dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
3347dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    @Nullable
335fd54b68d77bc90d5c607517c27bd7a30a2a8a57aTor Norbye    public TemplateMetadata getTemplate() {
336fd54b68d77bc90d5c607517c27bd7a30a2a8a57aTor Norbye        if (mTemplate == null) {
3376d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye            mTemplate = mManager.getTemplate(mRootPath);
3387dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
339fd54b68d77bc90d5c607517c27bd7a30a2a8a57aTor Norbye
340fd54b68d77bc90d5c607517c27bd7a30a2a8a57aTor Norbye        return mTemplate;
3417dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
3427dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
343fd54b68d77bc90d5c607517c27bd7a30a2a8a57aTor Norbye    @NonNull
3447dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    public String getResourcePath(String templateName) {
3457dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        return new File(mRootPath.getPath(), templateName).getPath();
3467dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
3477dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
3486d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye     /**
3497dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * Load a text resource for the given relative path within the template
3507dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     *
3517dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * @param relativePath relative path within the template
3527dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * @return the string contents of the template text file
3537dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     */
3547dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    @Nullable
3557dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    public String readTemplateTextResource(@NonNull String relativePath) {
35656afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye        try {
35756afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye            return Files.toString(new File(mRootPath,
35856afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye                    relativePath.replace('/', File.separatorChar)), Charsets.UTF_8);
35956afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye        } catch (IOException e) {
36056afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye            AdtPlugin.log(e, null);
36156afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye            return null;
3627dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
3637dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
3647dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
3657dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    @Nullable
3667dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    public String readTemplateTextResource(@NonNull File file) {
36756afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye        assert file.isAbsolute();
36856afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye        try {
36956afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye            return Files.toString(file, Charsets.UTF_8);
37056afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye        } catch (IOException e) {
37156afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye            AdtPlugin.log(e, null);
37256afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye            return null;
3737dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
3747dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
3757dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
3767dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /**
3777dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * Reads the contents of a resource
3787dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     *
3797dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * @param relativePath the path relative to the template directory
3807dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * @return the binary data read from the file
3817dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     */
3827dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    @Nullable
3837dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    public byte[] readTemplateResource(@NonNull String relativePath) {
38456afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye        try {
38556afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye            return Files.toByteArray(new File(mRootPath, relativePath));
38656afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye        } catch (IOException e) {
38756afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye            AdtPlugin.log(e, null);
38856afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye            return null;
3897dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
3907dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
3917dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
3927dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /** Read the given FreeMarker file and process the variable definitions */
3937dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    private void processVariables(final Configuration freemarker,
394802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            String file, final Map<String, Object> paramMap) {
3957dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        try {
3967dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            String xml;
3977dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            if (file.endsWith(DOT_XML)) {
3987dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                // Just read the file
3997dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                xml = readTemplateTextResource(file);
40056afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye                if (xml == null) {
40156afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye                    return;
40256afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye                }
4037dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            } else {
4047dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                mLoader.setTemplateFile(new File(mRootPath, file));
4057dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                Template inputsTemplate = freemarker.getTemplate(file);
4067dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                StringWriter out = new StringWriter();
4077dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                inputsTemplate.process(paramMap, out);
4087dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                out.flush();
4097dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                xml = out.toString();
4107dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            }
4117dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
4127dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            SAXParserFactory factory = SAXParserFactory.newInstance();
4137dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            SAXParser saxParser = factory.newSAXParser();
4147dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            saxParser.parse(new ByteArrayInputStream(xml.getBytes()), new DefaultHandler() {
4157dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                @Override
4167dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                public void startElement(String uri, String localName, String name,
4177dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        Attributes attributes)
4187dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        throws SAXException {
4197dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    if (TAG_PARAMETER.equals(name)) {
4207dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        String id = attributes.getValue(ATTR_ID);
4217dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        if (!paramMap.containsKey(id)) {
4227dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            String value = attributes.getValue(ATTR_DEFAULT);
423c689003930bebacf2aeffe48a361302c8b4697b5Tor Norbye                            Object mapValue = value;
424c689003930bebacf2aeffe48a361302c8b4697b5Tor Norbye                            if (value != null && !value.isEmpty()) {
425c689003930bebacf2aeffe48a361302c8b4697b5Tor Norbye                                String type = attributes.getValue(ATTR_TYPE);
426c689003930bebacf2aeffe48a361302c8b4697b5Tor Norbye                                if ("boolean".equals(type)) { //$NON-NLS-1$
427c689003930bebacf2aeffe48a361302c8b4697b5Tor Norbye                                    mapValue = Boolean.valueOf(value);
428c689003930bebacf2aeffe48a361302c8b4697b5Tor Norbye                                }
429c689003930bebacf2aeffe48a361302c8b4697b5Tor Norbye                            }
430c689003930bebacf2aeffe48a361302c8b4697b5Tor Norbye                            paramMap.put(id, mapValue);
4317dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        }
4327dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    } else if (TAG_GLOBAL.equals(name)) {
4337dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        String id = attributes.getValue(ATTR_ID);
4347dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        if (!paramMap.containsKey(id)) {
4357dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            String value = attributes.getValue(ATTR_VALUE);
4367dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            paramMap.put(id, value);
4377dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        }
4387dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    } else if (TAG_GLOBALS.equals(name)) {
4397dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        // Handle evaluation of variables
4407dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        String path = attributes.getValue(ATTR_FILE);
4417dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        if (path != null) {
442802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                            processVariables(freemarker, path, paramMap);
4437dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        } // else: <globals> root element
4447dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    } else if (TAG_EXECUTE.equals(name)) {
4457dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        String path = attributes.getValue(ATTR_FILE);
4467dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        if (path != null) {
447802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                            execute(freemarker, path, paramMap);
4487dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        }
449dd0a8b2ec052e0cc670cb8738b0f6ed7b292f122Tor Norbye                    } else if (TAG_DEPENDENCY.equals(name)) {
450dd0a8b2ec052e0cc670cb8738b0f6ed7b292f122Tor Norbye                        String dependencyName = attributes.getValue(ATTR_NAME);
451dd0a8b2ec052e0cc670cb8738b0f6ed7b292f122Tor Norbye                        if (dependencyName.equals(SUPPORT_LIBRARY_NAME)) {
452dd0a8b2ec052e0cc670cb8738b0f6ed7b292f122Tor Norbye                            // We assume the revision requirement has been satisfied
453dd0a8b2ec052e0cc670cb8738b0f6ed7b292f122Tor Norbye                            // by the wizard
454b8397698e2834ed8af03c1bff7d570aff0e2f5bbRaphael Moll                            File path = AddSupportJarAction.getSupportJarFile();
455dd0a8b2ec052e0cc670cb8738b0f6ed7b292f122Tor Norbye                            if (path != null) {
456802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                                IPath to = getTargetPath(FD_NATIVE_LIBS +'/' + path.getName());
457dd0a8b2ec052e0cc670cb8738b0f6ed7b292f122Tor Norbye                                try {
458dd0a8b2ec052e0cc670cb8738b0f6ed7b292f122Tor Norbye                                    copy(path, to);
459dd0a8b2ec052e0cc670cb8738b0f6ed7b292f122Tor Norbye                                } catch (IOException ioe) {
460dd0a8b2ec052e0cc670cb8738b0f6ed7b292f122Tor Norbye                                    AdtPlugin.log(ioe, null);
461dd0a8b2ec052e0cc670cb8738b0f6ed7b292f122Tor Norbye                                }
462dd0a8b2ec052e0cc670cb8738b0f6ed7b292f122Tor Norbye                            }
463dd0a8b2ec052e0cc670cb8738b0f6ed7b292f122Tor Norbye                        }
4647dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    } else if (!name.equals("template") && !name.equals("category")
46556afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye                            && !name.equals("option") && !name.equals(TAG_THUMBS) &&
466279445ad4561895db41309681de8dd1544d0ae22Tor Norbye                            !name.equals(TAG_THUMB) && !name.equals(TAG_ICONS)) {
4677dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        System.err.println("WARNING: Unknown template directive " + name);
4687dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    }
4697dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                }
4707dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            });
4717dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        } catch (Exception e) {
4727dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            AdtPlugin.log(e, null);
4737dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
4747dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
4757dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
476b8397698e2834ed8af03c1bff7d570aff0e2f5bbRaphael Moll    @SuppressWarnings("unused")
4777dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    private boolean canOverwrite(File file) {
478802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        if (file.exists()) {
4797dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            // Warn that the file already exists and ask the user what to do
4807dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            if (!mYesToAll) {
4817dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                MessageDialog dialog = new MessageDialog(null, "File Already Exists", null,
4827dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        String.format(
4837dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                                "%1$s already exists.\nWould you like to replace it?",
4847dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                                file.getPath()),
4857dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        MessageDialog.QUESTION, new String[] {
4867dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                                // Yes will be moved to the end because it's the default
4877dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                                "Yes", "No", "Cancel", "Yes to All"
4887dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        }, 0);
4897dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                int result = dialog.open();
4907dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                switch (result) {
4917dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    case 0:
4927dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        // Yes
4937dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        break;
4947dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    case 3:
4957dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        // Yes to all
4967dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        mYesToAll = true;
4977dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        break;
4987dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    case 1:
4997dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        // No
5007dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        return false;
5017dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    case SWT.DEFAULT:
5027dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    case 2:
5037dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        // Cancel
5047dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        mNoToAll = true;
5057dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        return false;
5067dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                }
5077dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            }
5087dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
5097dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            if (mBackupMergedFiles) {
5107dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                return makeBackup(file);
5117dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            } else {
5127dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                return file.delete();
5137dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            }
5147dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
5157dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
5167dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        return true;
5177dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
5187dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
5197dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /** Executes the given recipe file: copying, merging, instantiating, opening files etc */
5207dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    private void execute(
5217dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            final Configuration freemarker,
5227dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            String file,
523802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            final Map<String, Object> paramMap) {
5247dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        try {
5257dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            mLoader.setTemplateFile(new File(mRootPath, file));
5267dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            Template freemarkerTemplate = freemarker.getTemplate(file);
5277dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
5287dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            StringWriter out = new StringWriter();
5297dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            freemarkerTemplate.process(paramMap, out);
5307dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            out.flush();
5317dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            String xml = out.toString();
5327dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
5337dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            // Parse and execute the resulting instruction list.
5347dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            SAXParserFactory factory = SAXParserFactory.newInstance();
5357dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            SAXParser saxParser = factory.newSAXParser();
5367dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
5377dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            saxParser.parse(new ByteArrayInputStream(xml.getBytes()),
5387dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    new DefaultHandler() {
5397dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                @Override
5407dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                public void startElement(String uri, String localName, String name,
5417dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        Attributes attributes)
5427dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        throws SAXException {
5437dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    if (mNoToAll) {
5447dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        return;
5457dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    }
5467dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
5477dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    try {
5487dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        boolean instantiate = TAG_INSTANTIATE.equals(name);
5497dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        if (TAG_COPY.equals(name) || instantiate) {
5507dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            String fromPath = attributes.getValue(ATTR_FROM);
5517dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            String toPath = attributes.getValue(ATTR_TO);
5527dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            if (toPath == null || toPath.isEmpty()) {
5537dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                                toPath = attributes.getValue(ATTR_FROM);
5547dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                                toPath = AdtUtils.stripSuffix(toPath, DOT_FTL);
5557dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            }
556802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                            IPath to = getTargetPath(toPath);
5577dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            if (instantiate) {
5587dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                                instantiate(freemarker, paramMap, fromPath, to);
5597dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            } else {
560802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                                copyTemplateResource(fromPath, to);
5617dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            }
5627dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        } else if (TAG_MERGE.equals(name)) {
5637dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            String fromPath = attributes.getValue(ATTR_FROM);
5647dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            String toPath = attributes.getValue(ATTR_TO);
5657dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            if (toPath == null || toPath.isEmpty()) {
5667dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                                toPath = attributes.getValue(ATTR_FROM);
5677dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                                toPath = AdtUtils.stripSuffix(toPath, DOT_FTL);
5687dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            }
5697dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            // Resources in template.xml are located within root/
570802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                            IPath to = getTargetPath(toPath);
5717dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            merge(freemarker, paramMap, fromPath, to);
5727dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        } else if (name.equals(TAG_OPEN)) {
5737dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            // The relative path here is within the output directory:
5747dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            String relativePath = attributes.getValue(ATTR_FILE);
5757dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            if (relativePath != null && !relativePath.isEmpty()) {
5767dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                                mOpen.add(relativePath);
5777dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            }
5787dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        } else if (!name.equals("recipe")) { //$NON-NLS-1$
5797dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                            System.err.println("WARNING: Unknown template directive " + name);
5807dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        }
5817dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    } catch (Exception e) {
5827dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        AdtPlugin.log(e, null);
5837dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    }
5847dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                }
5857dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            });
5867dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
5877dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        } catch (Exception e) {
5887dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            AdtPlugin.log(e, null);
5897dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
5907dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
5917dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
59256afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye    @NonNull
59356afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye    private File getFullPath(@NonNull String fromPath) {
5947dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        if (fromPath.startsWith(VALUE_TEMPLATE_DIR)) {
59556afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye            return new File(getTemplateRootFolder(), RESOURCE_ROOT + File.separator
59656afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye                    + fromPath.substring(VALUE_TEMPLATE_DIR.length() + 1).replace('/',
59756afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye                            File.separatorChar));
5987dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
5997dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        return new File(mRootPath, DATA_ROOT + File.separator + fromPath);
6007dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
6017dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
602802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye    @NonNull
603802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye    private IPath getTargetPath(@NonNull String relative) {
604802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        if (relative.indexOf('\\') != -1) {
605802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            relative = relative.replace('\\', '/');
606802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        }
607802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        return new Path(relative);
608802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye    }
609802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye
610802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye    @NonNull
611802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye    private IFile getTargetFile(@NonNull IPath path) {
612802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        return mProject.getFile(path);
613802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye    }
614802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye
6157dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    private void merge(
6167dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            @NonNull final Configuration freemarker,
6177dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            @NonNull final Map<String, Object> paramMap,
6187dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            @NonNull String relativeFrom,
619802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            @NonNull IPath toPath) throws IOException, TemplateException {
620802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye
621802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        String currentXml = null;
622802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye
623802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        IFile to = getTargetFile(toPath);
624802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        if (to.exists()) {
625802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            currentXml = AdtPlugin.readFile(to);
626802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        }
627802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye
628802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        if (currentXml == null) {
6297dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            // The target file doesn't exist: don't merge, just copy
6307dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            boolean instantiate = relativeFrom.endsWith(DOT_FTL);
6317dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            if (instantiate) {
632802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                instantiate(freemarker, paramMap, relativeFrom, toPath);
6337dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            } else {
634802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                copyTemplateResource(relativeFrom, toPath);
6357dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            }
6367dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            return;
6377dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
6387dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
639802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        if (!to.getFileExtension().equals(EXT_XML)) {
6407dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            throw new RuntimeException("Only XML files can be merged at this point: " + to);
6417dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
6427dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
6437dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        String xml = null;
6447dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        File from = getFullPath(relativeFrom);
6457dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        if (relativeFrom.endsWith(DOT_FTL)) {
6467dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            // Perform template substitution of the template prior to merging
6477dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            mLoader.setTemplateFile(from);
6487dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            Template template = freemarker.getTemplate(from.getName());
6497dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            Writer out = new StringWriter();
6507dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            template.process(paramMap, out);
6517dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            out.flush();
6527dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            xml = out.toString();
6537dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        } else {
6547dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            xml = readTemplateTextResource(from);
6557e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye            if (xml == null) {
6567e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye                return;
6577e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye            }
6587dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
6597dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
6607dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        Document currentManifest = DomUtilities.parseStructuredDocument(currentXml);
6617dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        Document fragment = DomUtilities.parseStructuredDocument(xml);
6627dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
6637dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        XmlFormatStyle formatStyle = XmlFormatStyle.MANIFEST;
6647dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        boolean modified;
6657dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        boolean ok;
666802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        String fileName = to.getName();
667802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        if (fileName.equals(SdkConstants.FN_ANDROID_MANIFEST_XML)) {
6687dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            modified = ok = mergeManifest(currentManifest, fragment);
6697dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        } else {
6707dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            // Merge plain XML files
671802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            String parentFolderName = to.getParent().getName();
672802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            ResourceFolderType folderType = ResourceFolderType.getFolderType(parentFolderName);
6737dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            if (folderType != null) {
674802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                formatStyle = XmlFormatStyle.getForFile(toPath);
6757dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            } else {
6767dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                formatStyle = XmlFormatStyle.FILE;
6777dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            }
6787dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
6797dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            modified = mergeResourceFile(currentManifest, fragment, folderType, paramMap);
6807dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            ok = true;
6817dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
6827dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
6837dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        // Finally write out the merged file (formatting etc)
684802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        String contents = null;
6857dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        if (ok) {
6867dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            if (modified) {
6877dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                XmlPrettyPrinter printer = new XmlPrettyPrinter(
6887dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        XmlFormatPreferences.create(), formatStyle, null);
6897dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                StringBuilder sb = new StringBuilder(2 );
6907dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                printer.prettyPrint(-1, currentManifest, null, null, sb, false /*openTagOnly*/);
691802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                contents = sb.toString();
6927dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            }
6937dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        } else {
6947dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            // Just insert into file along with comment, using the "standard" conflict
6957dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            // syntax that many tools and editors recognize.
6961fb460987f7d832adf12290f41448d0c16a95972Siva Velusamy            String sep = SdkUtils.getLineSeparator();
697802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            contents =
6987dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    "<<<<<<< Original" + sep
6997dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    + currentXml + sep
7007dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    + "=======" + sep
7017dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    + xml
7027dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    + ">>>>>>> Added" + sep;
7037dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
7047dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
705802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        if (contents != null) {
706802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            TextFileChange change = new TextFileChange("Merge " + fileName, to);
707802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            MultiTextEdit rootEdit = new MultiTextEdit();
708802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            rootEdit.addChild(new ReplaceEdit(0, currentXml.length(), contents));
709802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            change.setEdit(rootEdit);
71012d4581faa6438941e65a9dc83213be34c6ca970Tor Norbye            change.setTextType(SdkConstants.EXT_XML);
711802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            mMergeChanges.add(change);
7127dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
7137dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
7147dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
7157dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /** Merges the given resource file contents into the given resource file
7167dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * @param paramMap */
7177dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    private boolean mergeResourceFile(Document currentManifest, Document fragment,
7187dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            ResourceFolderType folderType, Map<String, Object> paramMap) {
7197dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        boolean modified = false;
7207dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
7217dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        // For layouts for example, I want to *append* inside the root all the
7227dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        // contents of the new file.
7237dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        // But for resources for example, I want to combine elements which specify
7247dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        // the same name or id attribute.
7257dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        // For elements like manifest files we need to insert stuff at the right
7267dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        // location in a nested way (activities in the application element etc)
7277dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        // but that doesn't happen for the other file types.
7287dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        Element root = fragment.getDocumentElement();
7297dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        NodeList children = root.getChildNodes();
7307dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        List<Node> nodes = new ArrayList<Node>(children.getLength());
7317dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        for (int i = children.getLength() - 1; i >= 0; i--) {
7327dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            Node child = children.item(i);
7337dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            nodes.add(child);
7347dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            root.removeChild(child);
7357dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
7367dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
7377dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        root = currentManifest.getDocumentElement();
7387dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
7397dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        if (folderType == ResourceFolderType.VALUES) {
7407dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            // Try to merge items of the same name
7417dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            Map<String, Node> old = new HashMap<String, Node>();
7427dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            NodeList newSiblings = root.getChildNodes();
7437dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            for (int i = newSiblings.getLength() - 1; i >= 0; i--) {
7447dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                Node child = newSiblings.item(i);
7457dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                if (child.getNodeType() == Node.ELEMENT_NODE) {
7467dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    Element element = (Element) child;
7477dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    String name = getResourceId(element);
7487dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    if (name != null) {
7497dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        old.put(name, element);
7507dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    }
7517dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                }
7527dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            }
7537dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
7547dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            for (Node node : nodes) {
7557dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                if (node.getNodeType() == Node.ELEMENT_NODE) {
7567dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    Element element = (Element) node;
7577dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    String name = getResourceId(element);
7587dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    Node replace = name != null ? old.get(name) : null;
7597dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    if (replace != null) {
7607dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        // There is an existing item with the same id: just replace it
7617dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        // ACTUALLY -- let's NOT change it.
7627dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        // Let's say you've used the activity wizard once, and it
7637dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        // emits some configuration parameter as a resource that
7647dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        // it depends on, say "padding". Then the user goes and
7657dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        // tweaks the padding to some other number.
7667dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        // Now running the wizard a *second* time for some new activity,
7677dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        // we should NOT go and set the value back to the template's
7687dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        // default!
7697dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        //root.replaceChild(node, replace);
7707dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
7717dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        // ... ON THE OTHER HAND... What if it's a parameter class
7727dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        // (where the template rewrites a common attribute). Here it's
7737dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        // really confusing if the new parameter is not set. This is
7747dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        // really an error in the template, since we shouldn't have conflicts
7757dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        // like that, but we need to do something to help track this down.
7767dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        AdtPlugin.log(null,
7777dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                                "Warning: Ignoring name conflict in resource file for name %1$s",
7787dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                                name);
7797dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    } else {
7807dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        root.appendChild(node);
7817dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                        modified = true;
7827dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                    }
7837dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                }
7847dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            }
7857dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        } else {
7867dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            // In other file types, such as layouts, just append all the new content
7877dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            // at the end.
7887dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            for (Node node : nodes) {
7897dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                root.appendChild(node);
7907dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                modified = true;
7917dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            }
7927dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
7937dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        return modified;
7947dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
7957dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
7967dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /** Merges the given manifest fragment into the given manifest file */
7977dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    private boolean mergeManifest(Document currentManifest, Document fragment) {
7981311018325c75993ae84183b41d3fd915bce965dRaphael Moll        // TODO change MergerLog.wrapSdkLog by a custom IMergerLog that will create
7991311018325c75993ae84183b41d3fd915bce965dRaphael Moll        // and maintain error markers.
8000386f5dcf9d0f472f243506be3c40f5cf46287a2Raphael Moll        ManifestMerger merger = new ManifestMerger(
8010386f5dcf9d0f472f243506be3c40f5cf46287a2Raphael Moll                MergerLog.wrapSdkLog(AdtPlugin.getDefault()),
8020386f5dcf9d0f472f243506be3c40f5cf46287a2Raphael Moll                new AdtManifestMergeCallback());
8030386f5dcf9d0f472f243506be3c40f5cf46287a2Raphael Moll        return currentManifest != null &&
8040386f5dcf9d0f472f243506be3c40f5cf46287a2Raphael Moll               fragment != null &&
8050386f5dcf9d0f472f243506be3c40f5cf46287a2Raphael Moll               merger.process(currentManifest, fragment);
8067dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
8077dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
8087dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /**
8097dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * Makes a backup of the given file, if it exists, by renaming it to name~
8107dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * (and removing an old name~ file if it exists)
8117dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     */
8127dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    private static boolean makeBackup(File file) {
8137dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        if (!file.exists()) {
8147dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            return true;
8157dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
8167dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        if (file.isDirectory()) {
8177dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            return false;
8187dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
8197dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
8207dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        File backupFile = new File(file.getParentFile(), file.getName() + '~');
8217dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        if (backupFile.exists()) {
8227dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            backupFile.delete();
8237dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
8247dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        return file.renameTo(backupFile);
8257dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
8267dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
8277dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    private static String getResourceId(Element element) {
8287dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        String name = element.getAttribute(ATTR_NAME);
8297dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        if (name == null) {
8307dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            name = element.getAttribute(ATTR_ID);
8317dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
8327dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
8337dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        return name;
8347dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
8357dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
8367dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /** Instantiates the given template file into the given output file */
8377dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    private void instantiate(
8387dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            @NonNull final Configuration freemarker,
8397dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            @NonNull final Map<String, Object> paramMap,
8407dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            @NonNull String relativeFrom,
841802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            @NonNull IPath to) throws IOException, TemplateException {
8427dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        // For now, treat extension-less files as directories... this isn't quite right
8437dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        // so I should refine this! Maybe with a unique attribute in the template file?
8447dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        boolean isDirectory = relativeFrom.indexOf('.') == -1;
8457dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        if (isDirectory) {
8467dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            // It's a directory
847802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            copyTemplateResource(relativeFrom, to);
8487dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        } else {
8497dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            File from = getFullPath(relativeFrom);
8507dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            mLoader.setTemplateFile(from);
8517dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            Template template = freemarker.getTemplate(from.getName());
8527dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            Writer out = new StringWriter(1024);
8537dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            template.process(paramMap, out);
8547dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            out.flush();
8557dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            String contents = out.toString();
8567dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
85757d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye            contents = format(mProject, contents, to);
858802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            IFile targetFile = getTargetFile(to);
859802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            TextFileChange change = createTextChange(targetFile);
860802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            MultiTextEdit rootEdit = new MultiTextEdit();
861802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            rootEdit.addChild(new InsertEdit(0, contents));
862802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            change.setEdit(rootEdit);
863802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            mTextChanges.add(change);
864802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        }
865802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye    }
866802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye
86757d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye    private static String format(IProject project, String contents, IPath to) {
86857d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye        String name = to.lastSegment();
86957d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye        if (name.endsWith(DOT_XML)) {
87057d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye            XmlFormatStyle formatStyle = XmlFormatStyle.getForFile(to);
87157d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye            XmlFormatPreferences prefs = XmlFormatPreferences.create();
87257d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye            return XmlPrettyPrinter.prettyPrint(contents, prefs, formatStyle, null);
87357d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye        } else if (name.endsWith(DOT_JAVA)) {
87457d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye            Map<?, ?> options = null;
87557d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye            if (project != null && project.isAccessible()) {
87657d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                try {
87757d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                    IJavaProject javaProject = BaseProjectHelper.getJavaProject(project);
87857d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                    if (javaProject != null) {
87957d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                        options = javaProject.getOptions(true);
88057d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                    }
88157d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                } catch (CoreException e) {
88257d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                    AdtPlugin.log(e, null);
88357d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                }
88457d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye            }
88557d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye            if (options == null) {
88657d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                options = JavaCore.getOptions();
88757d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye            }
88857d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye
88957d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye            CodeFormatter formatter = ToolFactory.createCodeFormatter(options);
89057d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye
89157d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye            try {
89257d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                IDocument doc = new org.eclipse.jface.text.Document();
89357d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                // format the file (the meat and potatoes)
89457d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                doc.set(contents);
89557d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                TextEdit edit = formatter.format(
89657d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                        CodeFormatter.K_COMPILATION_UNIT | CodeFormatter.F_INCLUDE_COMMENTS,
89757d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                        contents, 0, contents.length(), 0, null);
89857d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                if (edit != null) {
89957d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                    edit.apply(doc);
90057d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                }
90157d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye
90257d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                return doc.get();
90357d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye            } catch (Exception e) {
90457d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                AdtPlugin.log(e, null);
90557d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye            }
90657d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye        }
90757d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye
90857d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye        return contents;
90957d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye    }
91057d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye
911802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye    private static TextFileChange createTextChange(IFile targetFile) {
912802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        String fileName = targetFile.getName();
913802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        String message;
914802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        if (targetFile.exists()) {
915802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            message = String.format("Replace %1$s", fileName);
916802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        } else {
917802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            message = String.format("Create %1$s", fileName);
9187dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
919802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye
920802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        TextFileChange change = new TextFileChange(message, targetFile);
921802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        change.setTextType(fileName.substring(fileName.lastIndexOf('.') + 1));
922802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        return change;
9237dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
9247dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
9257dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /**
9267dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * Returns the list of files to open when the template has been created
9277dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     *
9287dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * @return the list of files to open
9297dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     */
9307dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    @NonNull
9317dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    public List<String> getFilesToOpen() {
9327dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        return mOpen;
9337dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
9347dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
935802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye    /** Copy a template resource */
936802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye    private final void copyTemplateResource(
93756afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye            @NonNull String relativeFrom,
938802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            @NonNull IPath output) throws IOException {
9397dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        File from = getFullPath(relativeFrom);
94056afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye        copy(from, output);
9417dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
9427dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
9437dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /** Returns true if the given file contains the given bytes */
944802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye    private static boolean isIdentical(@Nullable byte[] data, @NonNull IFile dest) {
945802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        assert dest.exists();
946802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        byte[] existing = AdtUtils.readData(dest);
9477dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        return Arrays.equals(existing, data);
9487dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
9497dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
9507dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /**
9517dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * Copies the given source file into the given destination file (where the
9527dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * source is allowed to be a directory, in which case the whole directory is
9537dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * copied recursively)
9547dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     */
955802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye    private void copy(File src, IPath path) throws IOException {
956802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye        if (src.isDirectory()) {
9577dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            File[] children = src.listFiles();
9587dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            if (children != null) {
9597dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                for (File child : children) {
960802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                    copy(child, path.append(child.getName()));
9617dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                }
9627dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            }
9637dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        } else {
964802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            IResource dest = mProject.getFile(path);
965802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            if (dest.exists() && !(dest instanceof IFile)) {// Don't attempt to overwrite a folder
966802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                assert false : dest.getClass().getName();
9677dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye                return;
9687dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            }
969802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            IFile file = (IFile) dest;
970802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            String targetName = path.lastSegment();
971802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            if (dest instanceof IFile) {
972802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                if (dest.exists() && isIdentical(Files.toByteArray(src), file)) {
973802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                    String label = String.format(
974802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                            "Not overwriting %1$s because the files are identical", targetName);
975802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                    NullChange change = new NullChange(label);
976802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                    change.setEnabled(false);
977802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                    mOtherChanges.add(change);
978802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                    return;
979802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                }
9807dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            }
9817dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
982802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            if (targetName.endsWith(DOT_XML)
983802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                    || targetName.endsWith(DOT_JAVA)
984802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                    || targetName.endsWith(DOT_TXT)
985802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                    || targetName.endsWith(DOT_RS)
986802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                    || targetName.endsWith(DOT_AIDL)
987802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                    || targetName.endsWith(DOT_SVG)) {
988802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye
989802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                String newFile = Files.toString(src, Charsets.UTF_8);
99057d3c8a0e1c17318f72af6f7e417d248c034f583Tor Norbye                newFile = format(mProject, newFile, path);
991802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye
992802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                TextFileChange addFile = createTextChange(file);
993802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                addFile.setEdit(new InsertEdit(0, newFile));
994802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                mTextChanges.add(addFile);
995802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye            } else {
996802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                // Write binary file: Need custom change for that
997802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                IPath workspacePath = mProject.getFullPath().append(path);
998802de810020fba3f86282cd1d66597a2a41698e3Tor Norbye                mOtherChanges.add(new CreateFileChange(targetName, workspacePath, src));
9997dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            }
10007dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
10017dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
10027dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
10037dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    /**
10047dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * A custom {@link TemplateLoader} which locates and provides templates
10057dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     * within the plugin .jar file
10067dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye     */
10077dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    private static final class MyTemplateLoader implements TemplateLoader {
10087dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        private String mPrefix;
10097dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
10107dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        public void setPrefix(String prefix) {
10117dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            mPrefix = prefix;
10127dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
10137dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
10147dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        public void setTemplateFile(File file) {
10157dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            setTemplateParent(file.getParentFile());
10167dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
10177dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
10187dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        public void setTemplateParent(File parent) {
10197dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            mPrefix = parent.getPath();
10207dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
10217dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
10227dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        @Override
10237dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        public Reader getReader(Object templateSource, String encoding) throws IOException {
10247dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            URL url = (URL) templateSource;
10257dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            return new InputStreamReader(url.openStream(), encoding);
10267dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
10277dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
10287dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        @Override
10297dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        public long getLastModified(Object templateSource) {
10307dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            return 0;
10317dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
10327dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
10337dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        @Override
10347dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        public Object findTemplateSource(String name) throws IOException {
10357dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            String path = mPrefix != null ? mPrefix + '/' + name : name;
103656afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye            File file = new File(path);
103756afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye            if (file.exists()) {
103856afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye                return file.toURI().toURL();
10397dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye            }
104056afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye            return null;
10417dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
10427dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye
10437dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        @Override
10447dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        public void closeTemplateSource(Object templateSource) throws IOException {
10457dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye        }
10467dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye    }
104756afaff9c34a60c0e850324938d101a0509f8a38Tor Norbye
10487f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye    /**
10497f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye     * Validates this template to make sure it's supported
10506d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye     * @param currentMinSdk the minimum SDK in the project, or -1 or 0 if unknown (e.g. codename)
1051c58247bd46d86afe244d2d9dd9160da10e0db648Tor Norbye     * @param buildApi the build API, or -1 or 0 if unknown (e.g. codename)
10527f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye     *
10537f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye     * @return a status object with the error, or null if there is no problem
10547f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye     */
10557f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye    @SuppressWarnings("cast") // In Eclipse 3.6.2 cast below is needed
10567f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye    @Nullable
1057c58247bd46d86afe244d2d9dd9160da10e0db648Tor Norbye    public IStatus validateTemplate(int currentMinSdk, int buildApi) {
10587f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye        TemplateMetadata template = getTemplate();
10596d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye        if (template == null) {
10606d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye            return null;
10616d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye        }
10626d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye        if (!template.isSupported()) {
10637f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye            String versionString = (String) AdtPlugin.getDefault().getBundle().getHeaders().get(
10647f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye                    Constants.BUNDLE_VERSION);
10657f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye            Version version = new Version(versionString);
10667f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye            return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
10677f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye                String.format("This template requires a more recent version of the " +
10687f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye                        "Android Eclipse plugin. Please update from version %1$d.%2$d.%3$d.",
10697f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye                        version.getMajor(), version.getMinor(), version.getMicro()));
10707f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye        }
10716d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye        int templateMinSdk = template.getMinSdk();
10726d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye        if (templateMinSdk > currentMinSdk && currentMinSdk >= 1) {
10736d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye            return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
10746d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye                    String.format("This template requires a minimum SDK version of at " +
10756d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye                            "least %1$d, and the current min version is %2$d",
10766d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye                            templateMinSdk, currentMinSdk));
10776d1e4fdd6b2132b895aae80de3dba83c3960d43fTor Norbye        }
1078c58247bd46d86afe244d2d9dd9160da10e0db648Tor Norbye        int templateMinBuildApi = template.getMinBuildApi();
1079c58247bd46d86afe244d2d9dd9160da10e0db648Tor Norbye        if (templateMinBuildApi >  buildApi && buildApi >= 1) {
1080c58247bd46d86afe244d2d9dd9160da10e0db648Tor Norbye            return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
1081c58247bd46d86afe244d2d9dd9160da10e0db648Tor Norbye                    String.format("This template requires a build target API version of at " +
1082c58247bd46d86afe244d2d9dd9160da10e0db648Tor Norbye                            "least %1$d, and the current version is %2$d",
1083c58247bd46d86afe244d2d9dd9160da10e0db648Tor Norbye                            templateMinBuildApi, buildApi));
1084c58247bd46d86afe244d2d9dd9160da10e0db648Tor Norbye        }
10857f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye
10867f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye        return null;
10877f10682cf646bcb3e761fbb6bcb2f645bd748c03Tor Norbye    }
10887dd444ea0125e50a5e88604afb6de43e80b7c270Tor Norbye}
1089