Java tutorial
/******************************************************************************* * Copyright (c) 2013, 2015 Red Hat, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Red Hat Inc. - initial API and implementation and/or initial documentation *******************************************************************************/ package org.eclipse.thym.core.plugin; import static org.eclipse.thym.core.plugin.CordovaPluginXMLHelper.getAssetNodes; import static org.eclipse.thym.core.plugin.CordovaPluginXMLHelper.getAttributeValue; import static org.eclipse.thym.core.plugin.CordovaPluginXMLHelper.getConfigFileNodes; import static org.eclipse.thym.core.plugin.CordovaPluginXMLHelper.getDependencyNodes; import static org.eclipse.thym.core.plugin.CordovaPluginXMLHelper.getFrameworkNodes; import static org.eclipse.thym.core.plugin.CordovaPluginXMLHelper.getLibFileNodes; import static org.eclipse.thym.core.plugin.CordovaPluginXMLHelper.getPlatformNode; import static org.eclipse.thym.core.plugin.CordovaPluginXMLHelper.getPreferencesNodes; import static org.eclipse.thym.core.plugin.CordovaPluginXMLHelper.getResourceFileNodes; import static org.eclipse.thym.core.plugin.CordovaPluginXMLHelper.getSourceFileNodes; import static org.eclipse.thym.core.plugin.CordovaPluginXMLHelper.stringifyNode; import java.io.ByteArrayInputStream; import java.io.File; import java.io.InputStream; import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.io.FileUtils; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceVisitor; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.CanceledException; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.osgi.util.NLS; import org.eclipse.thym.core.HybridCore; import org.eclipse.thym.core.HybridMobileStatus; import org.eclipse.thym.core.HybridProject; import org.eclipse.thym.core.config.Feature; import org.eclipse.thym.core.config.Widget; import org.eclipse.thym.core.config.WidgetModel; import org.eclipse.thym.core.extensions.PlatformSupport; import org.eclipse.thym.core.internal.util.XMLUtil; import org.eclipse.thym.core.platform.AbstractPluginInstallationActionsFactory; import org.eclipse.thym.core.platform.IPluginInstallationAction; import org.eclipse.thym.core.platform.PlatformConstants; import org.eclipse.thym.core.plugin.RestorableCordovaPlugin.Type; import org.eclipse.thym.core.plugin.actions.ActionVariableHelper; import org.eclipse.thym.core.plugin.actions.ConfigXMLUpdateAction; import org.eclipse.thym.core.plugin.actions.CopyFileAction; import org.eclipse.thym.core.plugin.actions.DependencyInstallAction; import org.eclipse.thym.core.plugin.actions.PluginInstallRecordAction; import org.eclipse.thym.core.plugin.registry.CordovaPluginRegistryManager; import org.eclipse.thym.core.plugin.registry.CordovaPluginRegistryMapper; import org.eclipse.thym.core.plugin.registry.CordovaRegistryPlugin.RegistryPluginVersion; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.SAXParseException; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; /** * Manages the Cordova plug-ins for a project. * * @author Gorkem Ercan * */ public class CordovaPluginManager { private final HybridProject project; private List<CordovaPlugin> installedPlugins = new ArrayList<CordovaPlugin>(); public CordovaPluginManager(HybridProject project) { this.project = project; } /** * Installs a Cordova plug-in to {@link HybridProject} from directory. * A plug-ins installation is a two step process. This method triggers the * first step where Cordova Plug-ins is installed to HybridProject. * * @see #completePluginInstallationsForPlatform(File, String) * @param directory * @param overwrite * @param monitor * @throws CoreException <ul> *<li>if plugin.xml is missing</li> *<li>if plug-ins directory is missing on the project</li> *<li>if an error occurs during installation</li> *</ul> */ public void installPlugin(File directory, FileOverwriteCallback overwrite, IProgressMonitor monitor) throws CoreException { if (monitor == null) monitor = new NullProgressMonitor(); if (monitor.isCanceled()) return; Document doc = readPluginXML(directory); doInstallPlugin(directory, doc, overwrite, monitor); String id = CordovaPluginXMLHelper.getAttributeValue(doc.getDocumentElement(), "id"); JsonObject source = new JsonObject(); source.addProperty("type", "local"); source.addProperty("path", directory.toString()); this.saveFetchMetadata(source, id, monitor); List<IPluginInstallationAction> actions = new ArrayList<IPluginInstallationAction>(1); Map<String, String> params = new HashMap<String, String>(); params.put("installPath", directory.toString()); actions.add(getPluginInstallRecordAction(doc, params)); runActions(actions, false, overwrite, monitor); } /** * Installs a Cordova plug-in from registry. This method * delegates to {@link #doInstallPlugin()} after downloading the plugin from registry. * * @param plugin * @param overwrite * @param isDependency * @param monitor * @throws CoreException *<ul> *<li>if plugin.xml is missing</li> *<li>if plug-ins directory is missing on the project</li> *<li>if an error occurs during installation</li> *</ul> */ public void installPlugin(RegistryPluginVersion plugin, FileOverwriteCallback overwrite, boolean isDependency, IProgressMonitor monitor) throws CoreException { if (monitor == null) monitor = new NullProgressMonitor(); if (monitor.isCanceled()) return; CordovaPluginRegistryManager regMgr = new CordovaPluginRegistryManager(); File directory = regMgr.getInstallationDirectory(plugin, monitor); Document doc = readPluginXML(directory); doInstallPlugin(directory, doc, overwrite, monitor); String id = CordovaPluginXMLHelper.getAttributeValue(doc.getDocumentElement(), "id"); JsonObject source = new JsonObject(); source.addProperty("type", "registry"); source.addProperty("id", id); this.saveFetchMetadata(source, id, monitor); if (!isDependency) {//update config.xml List<IPluginInstallationAction> actions = new ArrayList<IPluginInstallationAction>(1); actions.add(getPluginInstallRecordAction(doc, null)); runActions(actions, false, overwrite, monitor); } } /** * Installs a Cordova plug-in from a git repository. * This method delegates to {@link #doInstallPlugin(File)} after cloning the * repository to a temporary location to complete the installation of the * plug-in. * <br/> * If commit is not null the cloned repository will be checked out to * commit. * <br/> * If subdir is not null it is assumed that the subdir path exists and installation * will be done from that location. * * @param uri * @param overwrite * @param isDependency * @param monitor * @param commit * @param subdir * @throws CoreException */ public void installPlugin(URI uri, FileOverwriteCallback overwrite, boolean isDependency, IProgressMonitor monitor) throws CoreException { File tempRepoDirectory = new File(FileUtils.getTempDirectory(), "cordova_plugin_tmp_" + Long.toString(System.currentTimeMillis())); tempRepoDirectory.deleteOnExit(); try { if (monitor.isCanceled()) return; monitor.subTask("Clone plugin repository"); String gitUrl = uri.getScheme() + ":" + uri.getSchemeSpecificPart(); Git git = Git.cloneRepository().setDirectory(tempRepoDirectory).setURI(gitUrl).call(); File pluginDirectory = tempRepoDirectory; String fragment = uri.getFragment(); String commit = null; String subdir = null; if (fragment != null) { int idx = fragment.indexOf(':'); if (idx < 0) { idx = fragment.length(); } commit = fragment.substring(0, idx); subdir = fragment.substring(Math.min(idx + 1, fragment.length())); if (monitor.isCanceled()) { throw new CanceledException("Plug-in installation cancelled"); } if (commit != null && !commit.isEmpty()) { git.checkout().setName(commit).call(); } monitor.worked(1); if (subdir != null && !subdir.isEmpty()) { pluginDirectory = new File(tempRepoDirectory, subdir); if (!pluginDirectory.isDirectory()) { throw new CoreException(new Status(IStatus.ERROR, HybridCore.PLUGIN_ID, NLS.bind("{0} directory does not exist in this git repository", subdir))); } } } SubProgressMonitor sm = new SubProgressMonitor(monitor, 1); Document doc = readPluginXML(pluginDirectory); this.doInstallPlugin(pluginDirectory, doc, overwrite, sm); String id = CordovaPluginXMLHelper.getAttributeValue(doc.getDocumentElement(), "id"); JsonObject source = new JsonObject(); source.addProperty("type", "git"); source.addProperty("url", gitUrl); if (subdir != null && !subdir.isEmpty()) { source.addProperty("subdir", subdir); } if (commit != null && !commit.isEmpty()) { source.addProperty("ref", commit); } this.saveFetchMetadata(source, id, monitor); if (!isDependency) {//update config.xml List<IPluginInstallationAction> actions = new ArrayList<IPluginInstallationAction>(1); Map<String, String> params = new HashMap<String, String>(); params.put("url", uri.toString()); actions.add(getPluginInstallRecordAction(doc, params)); runActions(actions, false, overwrite, monitor); } } catch (GitAPIException e) { throw new CoreException( new Status(IStatus.ERROR, HybridCore.PLUGIN_ID, "Error cloning the plugin repository", e)); } finally { monitor.done(); } } /** * Fixes the installation of a plugin by running the first stage actions. * * @see {@link #collectInstallActions(Document, String, FileOverwriteCallback)} * @param plugin * @param overwrite * @param monitor * @throws CoreException */ public void fixInstalledPlugin(final CordovaPlugin plugin, final FileOverwriteCallback overwrite, IProgressMonitor monitor) throws CoreException { IFolder pluginHome = getPluginHomeFolder(plugin); if (pluginHome == null) return; File directory = pluginHome.getLocation().toFile(); Document doc = readPluginXML(directory); List<IPluginInstallationAction> actions = collectInstallActions(doc, plugin.getId(), overwrite); actions.add(getPluginInstallRecordAction(doc, null)); runActions(actions, false, overwrite, monitor); resetInstalledPlugins(); } /** * Does the actual copying of the plugin to plugins directory and runs the first stage * install actions. * * @param directory * @param doc * @param overwrite * @param monitor * @throws CoreException */ private void doInstallPlugin(File directory, Document doc, FileOverwriteCallback overwrite, IProgressMonitor monitor) throws CoreException { String id = CordovaPluginXMLHelper.getAttributeValue(doc.getDocumentElement(), "id"); if (isPluginInstalled(id)) { HybridCore.log(IStatus.WARNING, "Cordova Plugin (" + id + ") is already installed, skipping.", null); } IFolder plugins = getPluginsFolder(); if (!plugins.exists()) { plugins.create(true, true, monitor); } //collect first stage install actions List<IPluginInstallationAction> actions = new ArrayList<IPluginInstallationAction>(); File destination = new File(plugins.getLocation().toFile(), id); CopyFileAction copy = new CopyFileAction(directory, destination); actions.add(copy); actions.addAll(collectInstallActions(doc, id, overwrite)); runActions(actions, false, overwrite, monitor); resetInstalledPlugins(); } private Document readPluginXML(File directory) throws CoreException { File pluginFile = new File(directory, PlatformConstants.FILE_XML_PLUGIN); if (!pluginFile.exists()) { throw new CoreException(new Status(IStatus.ERROR, HybridCore.PLUGIN_ID, NLS.bind("plugin.xml can not be located at {0}", pluginFile.toString()))); } Document doc = null; try { doc = XMLUtil.loadXML(pluginFile, false); } catch (CoreException e) { //Convert the SAXParseException exceptions to HybridMobileStatus because //it may indicate a broken plugin.xml or an platform not supported // see https://issues.jboss.org/browse/JBIDE-15768 if (e.getCause() != null && e.getCause() instanceof SAXParseException) { HybridMobileStatus hms = new HybridMobileStatus(IStatus.ERROR, HybridCore.PLUGIN_ID, HybridMobileStatus.STATUS_CODE_CONFIG_PARSE_ERROR, e.getStatus().getMessage(), e.getCause()); e = new CoreException(hms); } throw e; } return doc; } private void saveFetchMetadata(JsonObject source, String id, IProgressMonitor monitor) throws CoreException { IFolder pluginHome = getPluginHomeFolder(id); if (pluginHome == null) return; IFile file = pluginHome.getFile(".fetch.json"); JsonObject object = new JsonObject(); object.add("source", source); Gson gson = new Gson(); String jsonString = gson.toJson(object); InputStream stream = new ByteArrayInputStream(jsonString.getBytes()); if (file.exists()) { file.setContents(stream, IFile.FORCE, monitor); } else { file.create(stream, true, monitor); } } /** * Removes the plug-in with given id * @param id * @param overwrite * @param monitor * * @throws CoreException */ public void unInstallPlugin(String id, IProgressMonitor monitor) throws CoreException { if (id == null || !isPluginInstalled(id)) return; IFolder dir = getPluginHomeFolder(id); File pluginFile = new File(dir.getLocation().toFile(), PlatformConstants.FILE_XML_PLUGIN); if (!pluginFile.exists()) { throw new CoreException(new Status(IStatus.ERROR, HybridCore.PLUGIN_ID, "Not a valid plugin id , no plugin.xml exists")); } Document doc = XMLUtil.loadXML(pluginFile, false); FileOverwriteCallback cb = new FileOverwriteCallback() { @Override public boolean isOverwiteAllowed(String[] files) { return true; } }; List<IPluginInstallationAction> actions = new ArrayList<IPluginInstallationAction>(); File destination = new File(getPluginsFolder().getLocation().toFile(), id); CopyFileAction copy = new CopyFileAction(dir.getLocation().toFile(), destination); actions.add(copy); actions.addAll(collectInstallActions(doc, id, cb)); actions.add(getPluginInstallRecordAction(doc, null)); runActions(actions, true, cb, monitor); resetInstalledPlugins(); } private void resetInstalledPlugins() { installedPlugins.clear(); } /** * Completes the installation of all the installed plug-ins in this HybridProject * to the given platform project location. * This installation involves modifying of necessary files and * copying/generation of the others. * * @param platformProjectLocation * @param platform * @param overwrite * @param monitor * * @throws CoreException */ public void completePluginInstallationsForPlatform(File platformProjectLocation, String platform, FileOverwriteCallback overwrite, IProgressMonitor monitor) throws CoreException { List<CordovaPlugin> plugins = getInstalledPlugins(); PlatformSupport platformSupport = HybridCore.getPlatformSupport(platform); for (CordovaPlugin cordovaPlugin : plugins) { completePluginInstallationToPlatform(cordovaPlugin, platformSupport, platformProjectLocation, overwrite, monitor); } } /** * <p> * Return unmodifiable list of currently installed plug-ins. * </p> * <p> * This is a cached call so subsequent calls will perform better. * However, it is cached per {@link CordovaPluginManager} instance * which is also a single instance per {@link HybridProject} however * HybridProject instances are created on demand and the client should * handle the optimal caching. * </p> * @return list of installedPlugins * @throws CoreException */ public List<CordovaPlugin> getInstalledPlugins() throws CoreException { updatePluginList(); return Collections.unmodifiableList(installedPlugins); } /** * Checks if the given plug-in with pluginId is installed for the project. * Also uses {@link CordovaPluginRegistryMapper} to check alternate IDs * for plugins. * * @param pluginId * @return true if the plug-in is installed */ public boolean isPluginInstalled(String pluginId) { if (pluginId == null) return false; try { IFolder pluginHome = getPluginHomeFolder(pluginId); if (pluginHome != null) { IFile pluginxml = pluginHome.getFile(PlatformConstants.FILE_XML_PLUGIN); return pluginxml.getLocation() != null && pluginHome.getLocation().toFile().exists(); } } catch (CoreException e) { //ignore to return false } return false; } /** * Constructs the contents for the cordova_plugin.js from the list of * installed plugins. * * @return * @throws CoreException */ public String getCordovaPluginJSContent(String platformId) throws CoreException { JsonArray moduleObjects = new JsonArray(); List<CordovaPlugin> plugins = getInstalledPlugins(); for (CordovaPlugin cordovaPlugin : plugins) { List<PluginJavaScriptModule> modules = cordovaPlugin.getModules(); for (PluginJavaScriptModule pluginJavaScriptModule : modules) { if (platformId == null || pluginJavaScriptModule.getPlatform() == null || pluginJavaScriptModule.getPlatform().equals(platformId)) { JsonObject obj = new JsonObject(); obj.addProperty("file", (new Path("plugins")).append(cordovaPlugin.getId()) .append(pluginJavaScriptModule.getSource()).toString()); obj.addProperty("id", pluginJavaScriptModule.getName()); if (pluginJavaScriptModule.isRuns()) { obj.addProperty("runs", true); } if (pluginJavaScriptModule.getClobbers() != null) { List<String> clobbers = pluginJavaScriptModule.getClobbers(); JsonArray clobbersArray = new JsonArray(); for (String string : clobbers) { clobbersArray.add(new JsonPrimitive(string)); } obj.add("clobbers", clobbersArray); } if (pluginJavaScriptModule.getMerges() != null) { List<String> merges = pluginJavaScriptModule.getMerges(); JsonArray mergesArray = new JsonArray(); for (String string : merges) { mergesArray.add(new JsonPrimitive(string)); } obj.add("merges", mergesArray); } moduleObjects.add(obj); } } } StringBuilder finalContents = new StringBuilder(); finalContents.append("cordova.define('cordova/plugin_list', function(require, exports, module) {\n"); Gson gson = new Gson(); finalContents.append("module.exports = ").append(gson.toJson(moduleObjects)).append("\n});"); return finalContents.toString(); } /** * Returns the list of plugin ids that are listed on config.xml and are not already installed. * * @param monitor * @return plugin ids * @throws CoreException */ public List<RestorableCordovaPlugin> getRestorablePlugins(IProgressMonitor monitor) throws CoreException { Widget widget = WidgetModel.getModel(this.project).getWidgetForRead(); if (widget == null) { HybridCore.log(IStatus.ERROR, "Unable to read config.xml for restorable plugins", null); return Collections.emptyList(); } List<Feature> features = widget.getFeatures(); List<RestorableCordovaPlugin> restorable = new ArrayList<RestorableCordovaPlugin>(); if (features != null) { for (Feature feature : features) { Map<String, String> params = feature.getParams(); String id = params.get("id"); //Check if we can map the given id to a new ID. If the we can map a new id we use that to install. String newId = CordovaPluginRegistryMapper.toNew(id); if (newId != null) { id = newId; } if (id != null && !isPluginInstalled(id)) { RestorableCordovaPlugin rp = new RestorableCordovaPlugin(); rp.setId(id); rp.setType(Type.REGISTRY); String version = params.get("version"); if (version != null) { rp.setVersion(version); } String url = params.get("url"); if (url != null) { rp.setUrl(url); rp.setType(Type.GIT); } String path = params.get("installPath"); if (path != null) { rp.setPath(path); rp.setType(Type.LOCAL); } restorable.add(rp); } } } return restorable; } /** * Collects all the actions for first stage install/uninstall * First stage actions are. * <ul> * <li>dependency installations</li> * <li>config.xml changes</li> * <li>preferences with variables</li> * </ul> */ private List<IPluginInstallationAction> collectInstallActions(Document doc, String id, FileOverwriteCallback overwrite) { List<IPluginInstallationAction> actions = new ArrayList<IPluginInstallationAction>(); //collect platform independent actions actions.addAll(collectDependencyActions(doc.getDocumentElement(), overwrite)); actions.addAll(collectConfigXMLActions(doc.getDocumentElement())); actions.addAll(collectVariablePreferences(doc.getDocumentElement())); //platform actions List<PlatformSupport> platforms = HybridCore.getPlatformSupports(); for (PlatformSupport platformSupport : platforms) { Element platformNode = getPlatformNode(doc, platformSupport.getPlatformId()); if (platformNode != null) { actions.addAll(collectDependencyActions(platformNode, overwrite)); actions.addAll(collectConfigXMLActions(platformNode)); actions.addAll(collectVariablePreferences(platformNode)); } } return actions; } private void updatePluginList() throws CoreException { long start = System.currentTimeMillis(); if (installedPlugins == null || installedPlugins.isEmpty()) { HybridCore.trace("Really updating the installed plugin list"); IResourceVisitor visitor = new IResourceVisitor() { @Override public boolean visit(IResource resource) throws CoreException { if (resource.getType() == IResource.FOLDER) { IFolder folder = (IFolder) resource.getAdapter(IFolder.class); IFile file = folder.getFile(PlatformConstants.FILE_XML_PLUGIN); if (file.exists()) { addInstalledPlugin(file); } } return resource.getName().equals(PlatformConstants.DIR_PLUGINS); } }; IFolder plugins = this.project.getProject().getFolder(PlatformConstants.DIR_PLUGINS); if (plugins != null && plugins.exists()) { synchronized (installedPlugins) { plugins.accept(visitor, IResource.DEPTH_ONE, false); } } } HybridCore.trace(NLS.bind("Updated plugin list in {0} ms", (System.currentTimeMillis() - start))); } private void addInstalledPlugin(IFile pluginxml) throws CoreException { CordovaPlugin plugin = CordovaPluginXMLHelper.createCordovaPlugin(pluginxml.getContents()); plugin.setFolder((IFolder) pluginxml.getParent().getAdapter(IFolder.class)); int index = installedPlugins.indexOf(plugin); if (index > -1) { installedPlugins.set(index, plugin); } else { installedPlugins.add(plugin); } } /** * Returns the folder that the plugin with id is installed under the plugins folder. * It also uses {@link CordovaPluginRegistryMapper} to check for alternate ids. * * <p>convenience method for passing {@link CordovaPlugin} instances</p> * * @param plugin * @return null or folder * @throws CoreException - if <i>plugins</i> folder does not exist * */ private IFolder getPluginHomeFolder(CordovaPlugin plugin) throws CoreException { if (plugin == null) return null; return getPluginHomeFolder(plugin.getId()); } /** * Returns the folder that the plugin with id is installed under the plugins folder. * It also uses {@link CordovaPluginRegistryMapper} to check for alternate ids. * * @param id * @return null or a folder * throws CoreException - if <i>plugins</i> folder does not exist */ private IFolder getPluginHomeFolder(String id) throws CoreException { if (id == null) return null; IFolder plugins = getPluginsFolder(); if (!plugins.exists()) { throw new CoreException( new Status(IStatus.ERROR, HybridCore.PLUGIN_ID, "Plugin folder does not exist")); } IFolder pluginHome = plugins.getFolder(id); IPath location = pluginHome.getLocation(); if (pluginHome.exists() && location != null && location.toFile().isDirectory()) { return pluginHome; } // try the alternate ID String alternateId = CordovaPluginRegistryMapper.alternateID(id); if (alternateId != null) { pluginHome = plugins.getFolder(alternateId); location = pluginHome.getLocation(); if (pluginHome.exists() && location != null && location.toFile().isDirectory()) { return pluginHome; } } return null; } private IFolder getPluginsFolder() { IFolder plugins = this.project.getProject().getFolder(PlatformConstants.DIR_PLUGINS); return plugins; } private void runActions(final List<IPluginInstallationAction> actions, boolean runUnInstall, FileOverwriteCallback overwrite, IProgressMonitor monitor) throws CoreException { PluginInstallActionsRunOperation op = new PluginInstallActionsRunOperation(actions, runUnInstall, overwrite, project.getProject()); ResourcesPlugin.getWorkspace().run(op, monitor); } /* * . collect common actions * . collect all js-module actions (for copying source files) * . create cordova_plugin.js * . collect all platform specific tags * */ private void completePluginInstallationToPlatform(CordovaPlugin plugin, PlatformSupport platform, File platformProject, FileOverwriteCallback overwrite, IProgressMonitor monitor) throws CoreException { if (platform == null) return; IFolder pluginHomeFolder = getPluginHomeFolder(plugin); if (pluginHomeFolder == null) return; File pluginHome = pluginHomeFolder.getLocation().toFile(); File pluginFile = new File(pluginHome, PlatformConstants.FILE_XML_PLUGIN); Document doc = XMLUtil.loadXML(pluginFile, false); //TODO: check supported engines ArrayList<IPluginInstallationAction> allActions = new ArrayList<IPluginInstallationAction>(); AbstractPluginInstallationActionsFactory actionFactory = platform .getPluginInstallationActionsFactory(this.project.getProject(), pluginHome, platformProject); // Process jsmodules allActions.addAll(collectCommonAndPlatformJSModuleActions(plugin, platform.getPlatformId(), actionFactory)); // add all js-module actions //collect common actions allActions.addAll(collectAssetActions(doc.getDocumentElement(), actionFactory)); allActions.addAll(collectConfigFileActions(doc.getDocumentElement(), actionFactory)); allActions.addAll(collectSourceFilesActions(doc.getDocumentElement(), actionFactory)); allActions.addAll(collectResourceFileActions(doc.getDocumentElement(), actionFactory)); allActions.addAll(collectHeaderFileActions(doc.getDocumentElement(), actionFactory)); allActions.addAll(collectLibFileActions(doc.getDocumentElement(), actionFactory)); allActions.addAll(collectFrameworkActions(doc.getDocumentElement(), actionFactory, plugin)); //collect platform actions Element node = getPlatformNode(doc, platform.getPlatformId()); if (node != null) { allActions.addAll(collectAssetActions(node, actionFactory)); allActions.addAll(collectConfigFileActions(node, actionFactory)); allActions.addAll(collectSourceFilesActions(node, actionFactory)); allActions.addAll(collectResourceFileActions(node, actionFactory)); allActions.addAll(collectHeaderFileActions(node, actionFactory)); allActions.addAll(collectLibFileActions(node, actionFactory)); allActions.addAll(collectFrameworkActions(node, actionFactory, plugin)); } //with every plugin. TODO: find a better place //We do not need to create this file allActions.add( actionFactory.getCreatePluginJSAction(this.getCordovaPluginJSContent(platform.getPlatformId()))); runActions(allActions, false, overwrite, monitor); } private List<IPluginInstallationAction> collectCommonAndPlatformJSModuleActions(CordovaPlugin plugin, String platformId, AbstractPluginInstallationActionsFactory factory) { List<PluginJavaScriptModule> modules = plugin.getModules(); List<IPluginInstallationAction> actions = new ArrayList<IPluginInstallationAction>(); for (PluginJavaScriptModule scriptModule : modules) { if (scriptModule.getPlatform() == null || scriptModule.getPlatform().equals(platformId)) { IPluginInstallationAction action = factory.getJSModuleAction(scriptModule.getSource(), plugin.getId(), scriptModule.getName()); actions.add(action); } } return actions; } /** * Collect the actions that are targeted for the config.xml file on the node. * Collects actions from immediate children only. * @param node * @return */ private List<IPluginInstallationAction> collectConfigXMLActions(Element node) { ArrayList<IPluginInstallationAction> list = new ArrayList<IPluginInstallationAction>(); List<Element> configFiles = getConfigFileNodes(node); for (Element current : configFiles) { String target = getAttributeValue(current, "target"); if (!target.endsWith(PlatformConstants.FILE_XML_CONFIG)) { continue; } String parent = getAttributeValue(current, "parent"); String resolvedValue = stringifyNode(current); try { resolvedValue = ActionVariableHelper.replaceVariables(this.project, resolvedValue); } catch (CoreException ex) { HybridCore.log(IStatus.ERROR, "Error while resolving variables", ex); } IPluginInstallationAction action = new ConfigXMLUpdateAction(this.project, parent, resolvedValue); list.add(action); } return list; } /** * Collects actions that require preference definitions on the config.xml * This method actually creates {@link ConfigXMLUpdateAction}s that will inject a * preference node to config.xml * * Collects actions from immediate children only. * * @param node * @return */ private List<IPluginInstallationAction> collectVariablePreferences(Element node) { List<Element> preferences = getPreferencesNodes(node); ArrayList<IPluginInstallationAction> list = new ArrayList<IPluginInstallationAction>(); for (Element current : preferences) { String name = getAttributeValue(current, "name"); IPluginInstallationAction action = new ConfigXMLUpdateAction(this.project, "/widget", " <config-file target=\"res/xml/config.xml\" parent=\"/widget\">" + "<preference name=\"" + name + "\" value=\"PLEASE_DEFINE\"/>" + "</config-file>"); list.add(action); } return list; } /** * Collects dependency actions on the given node. * Collects actions from immediate children only. * * @param node * @return */ private List<IPluginInstallationAction> collectDependencyActions(Element node, FileOverwriteCallback overwrite) { List<Element> dependencyNodes = getDependencyNodes(node); ArrayList<IPluginInstallationAction> list = new ArrayList<IPluginInstallationAction>(); for (Element dependencyNode : dependencyNodes) { String dependencyId = getAttributeValue(dependencyNode, "id"); String url = getAttributeValue(dependencyNode, "url"); String commit = getAttributeValue(dependencyNode, "commit"); String subdir = getAttributeValue(dependencyNode, "subdir"); URI uri = null; if (url != null && !url.isEmpty()) { if (!url.endsWith(".git")) { url = url + ".git"; } if (commit != null || subdir != null) { url = url + "#"; if (commit != null) { url = url + commit; } if (subdir != null) { url = url + ":" + subdir; } } uri = URI.create(url); } DependencyInstallAction action = new DependencyInstallAction(dependencyId, uri, this.project, overwrite); list.add(action); } return list; } private List<IPluginInstallationAction> collectFrameworkActions(Element node, AbstractPluginInstallationActionsFactory factory, CordovaPlugin plugin) { ArrayList<IPluginInstallationAction> list = new ArrayList<IPluginInstallationAction>(); List<Element> frameworks = getFrameworkNodes(node); for (Element current : frameworks) { String src = getAttributeValue(current, "src"); String weak = getAttributeValue(current, "weak"); String custom = getAttributeValue(current, "custom"); String type = getAttributeValue(current, "type"); String parent = getAttributeValue(current, "parent"); IPluginInstallationAction action = factory.getFrameworkAction(src, weak, plugin.getId(), custom, type, parent); list.add(action); } return list; } private List<IPluginInstallationAction> collectLibFileActions(Element node, AbstractPluginInstallationActionsFactory factory) { ArrayList<IPluginInstallationAction> list = new ArrayList<IPluginInstallationAction>(); List<Element> libFiles = getLibFileNodes(node); for (Element current : libFiles) { String src = getAttributeValue(current, "src"); String arch = getAttributeValue(current, "arch"); IPluginInstallationAction action = factory.getLibFileAction(src, arch); list.add(action); } return list; } private List<IPluginInstallationAction> collectConfigFileActions(Element node, AbstractPluginInstallationActionsFactory factory) { ArrayList<IPluginInstallationAction> list = new ArrayList<IPluginInstallationAction>(); List<Element> configFiles = getConfigFileNodes(node); for (Element current : configFiles) { String target = getAttributeValue(current, "target"); if (target.endsWith(PlatformConstants.FILE_XML_CONFIG)) {//config.xmls are handled on #collectAllConfigXMLActions continue; } String parent = getAttributeValue(current, "parent"); String resolvedValue = stringifyNode(current); try { resolvedValue = ActionVariableHelper.replaceVariables(this.project, resolvedValue); } catch (CoreException e) { HybridCore.log(IStatus.ERROR, "Error while resolving the variables", e); } IPluginInstallationAction action = factory.getConfigFileAction(target, parent, resolvedValue); list.add(action); } return list; } private List<IPluginInstallationAction> collectHeaderFileActions(Element node, AbstractPluginInstallationActionsFactory factory) { ArrayList<IPluginInstallationAction> list = new ArrayList<IPluginInstallationAction>(); List<Element> headerFiles = CordovaPluginXMLHelper.getHeaderFileNodes(node); for (Element current : headerFiles) { String src = getAttributeValue(current, "src"); String targetDir = getAttributeValue(current, "target-dir"); String id = CordovaPluginXMLHelper.getAttributeValue(node.getOwnerDocument().getDocumentElement(), "id"); IPluginInstallationAction action = factory.getHeaderFileAction(src, targetDir, id); list.add(action); } return list; } private List<IPluginInstallationAction> collectResourceFileActions(Element node, AbstractPluginInstallationActionsFactory factory) { ArrayList<IPluginInstallationAction> list = new ArrayList<IPluginInstallationAction>(); List<Element> resourceFiles = getResourceFileNodes(node); for (Element current : resourceFiles) { String src = getAttributeValue(current, "src"); String target = getAttributeValue(current, "target"); IPluginInstallationAction action = factory.getResourceFileAction(src, target); list.add(action); } return list; } private List<IPluginInstallationAction> collectSourceFilesActions(Element node, AbstractPluginInstallationActionsFactory factory) { ArrayList<IPluginInstallationAction> list = new ArrayList<IPluginInstallationAction>(); List<Element> sourceFiles = getSourceFileNodes(node); for (Element current : sourceFiles) { String src = getAttributeValue(current, "src"); String targetDir = getAttributeValue(current, "target-dir"); String framework = getAttributeValue(current, "framework"); String compilerFlags = getAttributeValue(current, "compiler-flags"); String id = CordovaPluginXMLHelper.getAttributeValue(node.getOwnerDocument().getDocumentElement(), "id"); IPluginInstallationAction action = factory.getSourceFileAction(src, targetDir, framework, id, compilerFlags); list.add(action); } return list; } private List<IPluginInstallationAction> collectAssetActions(Element node, AbstractPluginInstallationActionsFactory factory) { ArrayList<IPluginInstallationAction> list = new ArrayList<IPluginInstallationAction>(); List<Element> assets = getAssetNodes(node); for (Element current : assets) { String src = getAttributeValue(current, "src"); String target = getAttributeValue(current, "target"); IPluginInstallationAction action = factory.getAssetAction(src, target); list.add(action); } return list; } private PluginInstallRecordAction getPluginInstallRecordAction(Document pluginXml, Map<String, String> additionalParams) throws CoreException { Map<String, String> params = new HashMap<String, String>(); if (additionalParams != null) { params.putAll(additionalParams); } String id = CordovaPluginXMLHelper.getAttributeValue(pluginXml.getDocumentElement(), "id"); params.put("id", id); boolean saveVersion = Platform.getPreferencesService().getBoolean(PlatformConstants.HYBRID_UI_PLUGIN_ID, PlatformConstants.PREF_SHRINKWRAP_PLUGIN_VERSIONS, false, null); String version = null; if (saveVersion) { version = CordovaPluginXMLHelper.getAttributeValue(pluginXml.getDocumentElement(), "version"); params.put("version", version); } Node n = CordovaPluginXMLHelper.getNameNode(pluginXml.getDocumentElement()); if (n == null) { throw new CoreException(new Status(IStatus.ERROR, HybridCore.PLUGIN_ID, "plugin.xml is missing name")); } return new PluginInstallRecordAction(project, n.getTextContent().trim(), params); } }