All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.pde.ui.templates.AbstractTemplateSection Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2000, 2016 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Les Jones  - Bug 185477
 *     Lars Vogel  - Bug 486261, 463272
 *******************************************************************************/
package org.eclipse.pde.ui.templates;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.pde.core.plugin.IPlugin;
import org.eclipse.pde.core.plugin.IPluginBase;
import org.eclipse.pde.core.plugin.IPluginExtension;
import org.eclipse.pde.core.plugin.IPluginModel;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.core.plugin.IPluginReference;
import org.eclipse.pde.internal.core.TargetPlatformHelper;
import org.eclipse.pde.internal.core.ibundle.IBundle;
import org.eclipse.pde.internal.core.ibundle.IBundleModel;
import org.eclipse.pde.internal.core.ibundle.IBundlePluginBase;
import org.eclipse.pde.internal.core.ibundle.IBundlePluginModelBase;
import org.eclipse.pde.internal.ui.PDEPlugin;
import org.eclipse.pde.internal.ui.PDEUIMessages;
import org.eclipse.pde.internal.ui.wizards.templates.ControlStack;

/**
 * Common function for template sections. It is recommended to subclass this
 * class rather than implementing ITemplateSection directly when providing
 * extension templates.
 *
 * @since 2.0
 */

public abstract class AbstractTemplateSection implements ITemplateSection, IVariableProvider {

	/**
	 * The project handle.
	 */
	protected IProject project;
	/**
	 * The plug-in model.
	 */
	protected IPluginModelBase model;
	/**
	 * The key for the main plug-in class of the plug-in that the template is
	 * used for (value="pluginClass").  The return value is a fully-qualified class name.
	 */
	public static final String KEY_PLUGIN_CLASS = "pluginClass"; //$NON-NLS-1$

	/**
	 * The key for the simple class name of a bundle activator (value="activator")
	 *
	 * @since 3.3
	 */
	public static final String KEY_ACTIVATOR_SIMPLE = "activator"; //$NON-NLS-1$
	/**
	 * The key for the plug-in id of the plug-in that the template is used for
	 * (value="pluginId").
	 */
	public static final String KEY_PLUGIN_ID = "pluginId"; //$NON-NLS-1$
	/**
	 * The key for the plug-in name of the plug-in that the template is used for
	 * (value="pluginName").
	 */
	public static final String KEY_PLUGIN_NAME = "pluginName"; //$NON-NLS-1$
	/**
	 * The key for the package name that will be created by this template
	 * (value="packageName").
	 */
	public static final String KEY_PACKAGE_NAME = "packageName"; //$NON-NLS-1$

	private boolean pagesAdded = false;

	/**
	 * The default implementation of this method provides values of the
	 * following keys: pluginClass , pluginId  and
	 * pluginName .
	 */
	@Override
	public String getReplacementString(String fileName, String key) {
		String result = getKeyValue(key);
		return result != null ? result : key;
	}

	@Override
	public Object getValue(String key) {
		return getKeyValue(key);
	}

	private String getKeyValue(String key) {
		if (model == null)
			return null;

		if (key.equals(KEY_PLUGIN_CLASS) && model instanceof IPluginModel) {
			IPlugin plugin = (IPlugin) model.getPluginBase();
			return plugin.getClassName();
		}

		if (key.equals(KEY_ACTIVATOR_SIMPLE) && model instanceof IPluginModel) {
			IPlugin plugin = (IPlugin) model.getPluginBase();
			String qualified = plugin.getClassName();
			if (qualified != null) {
				int lastDot = qualified.lastIndexOf('.');
				return (lastDot != -1 && lastDot < qualified.length() - 1) ? qualified.substring(lastDot + 1) : qualified;
			}
		}
		if (key.equals(KEY_PLUGIN_ID)) {
			IPluginBase plugin = model.getPluginBase();
			return plugin.getId();
		}
		if (key.equals(KEY_PLUGIN_NAME)) {
			IPluginBase plugin = model.getPluginBase();
			return plugin.getTranslatedName();
		}

		if (key.equals(KEY_PACKAGE_NAME) && model instanceof IPluginModel) {
			IPlugin plugin = (IPlugin) model.getPluginBase();
			String qualified = plugin.getClassName();
			if (qualified != null) {
				int lastDot = qualified.lastIndexOf('.');
				return (lastDot != -1) ? qualified.substring(0, lastDot) : qualified;
			}
		}
		return null;
	}

	@Override
	public URL getTemplateLocation() {
		return null;
	}

	@Override
	public String getDescription() {
		return ""; //$NON-NLS-1$
	}

	/**
	 * Returns the translated version of the resource string represented by the
	 * provided key.
	 *
	 * @param key
	 *            the key of the required resource string
	 * @return the translated version of the required resource string
	 * @see #getPluginResourceBundle()
	 */
	public String getPluginResourceString(String key) {
		ResourceBundle bundle = getPluginResourceBundle();
		if (bundle == null)
			return key;
		try {
			return bundle.getString(key);
		} catch (MissingResourceException e) {
			return key;
		}
	}

	/**
	 * An abstract method that returns the resource bundle that corresponds to
	 * the best match of plugin.properties  file for the current
	 * locale (in case of fragments, the file is fragment.properties
	 * ).
	 *
	 * @return resource bundle for plug-in properties file or null 
	 *         if not found.
	 */
	protected abstract ResourceBundle getPluginResourceBundle();

	@Override
	public void addPages(Wizard wizard) {
	}


	@Override
	public boolean getPagesAdded() {
		return pagesAdded;
	}

	/**
	 * Marks that pages have been added to the wizard by this template. Call
	 * this method in 'addPages'.
	 *
	 * @see #addPages(Wizard)
	 */
	protected void markPagesAdded() {
		pagesAdded = true;
	}

	/**
	 * The default implementation of the interface method. The returned value is
	 * 1.
	 */
	@Override
	public int getNumberOfWorkUnits() {
		return 1;
	}

	@Override
	public IPluginReference[] getDependencies(String schemaVersion) {
		return null;
	}

	/**
	 * Returns the folder with Java files in the target project. The default
	 * implementation looks for source folders in the classpath of the target
	 * folders and picks the first one encountered. Subclasses may override this
	 * behaviour.
	 *
	 * @param monitor
	 *            progress monitor to use
	 * @return source folder that will be used to generate Java files or
	 *         null  if none found.
	 */

	protected IFolder getSourceFolder(IProgressMonitor monitor) {
		IFolder sourceFolder = null;

		try {
			IJavaProject javaProject = JavaCore.create(project);
			IClasspathEntry[] classpath = javaProject.getRawClasspath();
			for (IClasspathEntry entry : classpath) {
				if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
					IPath path = entry.getPath().removeFirstSegments(1);
					if (path.segmentCount() > 0)
						sourceFolder = project.getFolder(path);
					break;
				}
			}
		} catch (JavaModelException e) {
			PDEPlugin.logException(e);
		}
		return sourceFolder;
	}

	/**
	 * Generates files as part of the template execution. The default
	 * implementation uses template location as a root of the file templates.
	 * {@link #generateFiles(IProgressMonitor monitor, URL locationUrl)} on how
	 * the location gets processed.
	 *
	 * @param monitor
	 *            progress monitor to use to indicate generation progress
	 */
	protected void generateFiles(IProgressMonitor monitor) throws CoreException {
		generateFiles(monitor, getTemplateLocation());
	}

	/**
	 * Generates files as part of the template execution.
	 * The files found in the location are processed in the following way:
	 * 
    *
  • Files and folders found in the directory bin are * copied into the target project without modification.
  • *
  • Files found in the directory java are copied into the * Java source folder by creating the folder structure that corresponds to * the package name (variable packageName ). Java files are * subject to conditional generation and variable replacement.
  • *
  • All other files and folders are copied directly into the target * folder with the conditional generation and variable replacement for * files. Variable replacement also includes file names.
  • *
* * @since 3.3 * @param monitor * progress monitor to use to indicate generation progress * @param locationUrl a url pointing to a file/directory that will be copied into the template */ protected void generateFiles(IProgressMonitor monitor, URL locationUrl) throws CoreException { monitor.setTaskName(PDEUIMessages.AbstractTemplateSection_generating); if (locationUrl == null) { return; } try { locationUrl = FileLocator.resolve(locationUrl); locationUrl = FileLocator.toFileURL(locationUrl); } catch (IOException e) { return; } if ("file".equals(locationUrl.getProtocol())) { //$NON-NLS-1$ File templateDirectory = new File(locationUrl.getFile()); if (!templateDirectory.exists()) return; generateFiles(templateDirectory, project, true, false, monitor); } else if ("jar".equals(locationUrl.getProtocol())) { //$NON-NLS-1$ String file = locationUrl.getFile(); int exclamation = file.indexOf('!'); if (exclamation < 0) return; URL fileUrl = null; try { fileUrl = new URL(file.substring(0, exclamation)); } catch (MalformedURLException mue) { return; } File pluginJar = new File(fileUrl.getFile()); if (!pluginJar.exists()) return; String templateDirectory = file.substring(exclamation + 1); // "/some/path/" IPath path = IPath.fromOSString(templateDirectory); try (ZipFile zipFile = new ZipFile(pluginJar)) { generateFiles(zipFile, path, project, true, false, monitor); } catch (IOException ioe) { } } monitor.subTask(""); //$NON-NLS-1$ monitor.worked(1); } /** * Tests if the folder found in the template location should be created in * the target project. Subclasses may use this method to conditionally block * the creation of entire directories (subject to user choices). * * @param sourceFolder * the folder that is tested * @return true if the provided folder should be created in * the workspace, false if the values of the * substitution variables indicate otherwise. */ protected boolean isOkToCreateFolder(File sourceFolder) { return true; } /** * Tests if the file found in the template location should be created in the * target project. Subclasses may use this method to conditionally block * creation of the file (subject to user choices). * * @param sourceFile * the file found in the template location that needs to be * created. * @return true if the specified file should be created in the * project or false to skip it. The default * implementation is true . */ protected boolean isOkToCreateFile(File sourceFile) { return true; } /** * Subclass must implement this method to add the required entries in the * plug-in model. * * @param monitor * the progress monitor to be used */ protected abstract void updateModel(IProgressMonitor monitor) throws CoreException; /** * The default implementation of the interface method. It will generate * required files found in the template location and then call * updateModel to add the required manifest entires. */ @Override public void execute(IProject project, IPluginModelBase model, IProgressMonitor monitor) throws CoreException { this.project = project; this.model = model; generateFiles(monitor); updateModel(monitor); } /** * A utility method to create an extension object for the plug-in model from * the provided extension point id. * * @param pointId * the identifier of the target extension point * @param reuse * if true, new extension object will be created only if an * extension with the same Id does not exist. * @return an existing extension (if exists and reuse is * true ), or a new extension object otherwise. */ protected IPluginExtension createExtension(String pointId, boolean reuse) throws CoreException { if (reuse) { IPluginExtension[] extensions = model.getPluginBase().getExtensions(); for (IPluginExtension extension : extensions) { if (extension.getPoint().equalsIgnoreCase(pointId)) { return extension; } } } IPluginExtension extension = model.getFactory().createExtension(); extension.setPoint(pointId); return extension; } private void generateFiles(File src, IContainer dst, boolean firstLevel, boolean binary, IProgressMonitor monitor) throws CoreException { File[] members = src.listFiles(); for (File member : members) { if (member.isDirectory()) { IContainer dstContainer = null; if (firstLevel) { binary = false; if (!isOkToCreateFolder(member)) continue; if (member.getName().equals("java")) { //$NON-NLS-1$ IFolder sourceFolder = getSourceFolder(monitor); dstContainer = generateJavaSourceFolder(sourceFolder, monitor); } else if (member.getName().equals("bin")) { //$NON-NLS-1$ binary = true; dstContainer = dst; } } if (dstContainer == null) { if (isOkToCreateFolder(member) == false) continue; String folderName = getProcessedString(member.getName(), member.getName()); dstContainer = dst.getFolder(IPath.fromOSString(folderName)); } if (dstContainer instanceof IFolder && !dstContainer.exists()) ((IFolder) dstContainer).create(true, true, monitor); generateFiles(member, dstContainer, false, binary, monitor); } else { if (isOkToCreateFile(member)) { if (firstLevel) binary = false; try (InputStream in = new FileInputStream(member)) { copyFile(member.getName(), in, dst, binary, monitor); } catch (IOException ioe) { } } } } } private void generateFiles(ZipFile zipFile, IPath path, IContainer dst, boolean firstLevel, boolean binary, IProgressMonitor monitor) throws CoreException { int pathLength = path.segmentCount(); // Immidiate children Map childZipEntries = new HashMap<>(); // "dir/" or "dir/file.java" for (Enumeration zipEntries = zipFile.entries(); zipEntries.hasMoreElements();) { ZipEntry zipEntry = zipEntries.nextElement(); IPath entryPath = IPath.fromOSString(zipEntry.getName()); if (entryPath.segmentCount() <= pathLength) { // ancestor or current directory continue; } if (!path.isPrefixOf(entryPath)) { // not a descendant continue; } if (entryPath.segmentCount() == pathLength + 1) { childZipEntries.put(zipEntry.getName(), zipEntry); } else { String name = entryPath.uptoSegment(pathLength + 1).addTrailingSeparator().toString(); if (!childZipEntries.containsKey(name)) { ZipEntry dirEntry = new ZipEntry(name); childZipEntries.put(name, dirEntry); } } } for (ZipEntry zipEnry : childZipEntries.values()) { String name = IPath.fromOSString(zipEnry.getName()).lastSegment().toString(); if (zipEnry.isDirectory()) { IContainer dstContainer = null; if (firstLevel) { binary = false; if (name.equals("java")) { //$NON-NLS-1$ IFolder sourceFolder = getSourceFolder(monitor); dstContainer = generateJavaSourceFolder(sourceFolder, monitor); } else if (name.equals("bin")) { //$NON-NLS-1$ binary = true; dstContainer = dst; } } if (dstContainer == null) { if (isOkToCreateFolder(new File(path.toFile(), name)) == false) continue; String folderName = getProcessedString(name, name); dstContainer = dst.getFolder(IPath.fromOSString(folderName)); } if (dstContainer instanceof IFolder && !dstContainer.exists()) ((IFolder) dstContainer).create(true, true, monitor); generateFiles(zipFile, path.append(name), dstContainer, false, binary, monitor); } else { if (isOkToCreateFile(new File(path.toFile(), name))) { if (firstLevel) binary = false; try (InputStream in = zipFile.getInputStream(zipEnry)) { copyFile(name, in, dst, binary, monitor); } catch (IOException ioe) { } } } } } private IFolder generateJavaSourceFolder(IFolder sourceFolder, IProgressMonitor monitor) throws CoreException { Object packageValue = getValue(KEY_PACKAGE_NAME); String packageName = packageValue != null ? packageValue.toString() : null; if (packageName == null) packageName = model.getPluginBase().getId(); IPath path = IPath.fromOSString(packageName.replace('.', File.separatorChar)); if (sourceFolder != null) path = sourceFolder.getProjectRelativePath().append(path); for (int i = 1; i <= path.segmentCount(); i++) { IPath subpath = path.uptoSegment(i); IFolder subfolder = project.getFolder(subpath); if (subfolder.exists() == false) subfolder.create(true, true, monitor); } return project.getFolder(path); } private void copyFile(String fileName, InputStream input, IContainer dst, boolean binary, IProgressMonitor monitor) throws CoreException { String targetFileName = getProcessedString(fileName, fileName); monitor.subTask(targetFileName); IFile dstFile = dst.getFile(IPath.fromOSString(targetFileName)); try (InputStream stream = getProcessedStream(fileName, input, binary)) { if (dstFile.exists()) { dstFile.setContents(stream, true, true, monitor); } else { dstFile.create(stream, true, monitor); } } catch (IOException e) { } } private String getProcessedString(String fileName, String source) { if (source.indexOf('$') == -1) return source; int loc = -1; StringBuilder buffer = new StringBuilder(); boolean replacementMode = false; for (int i = 0; i < source.length(); i++) { char c = source.charAt(i); if (c == '$') { if (replacementMode) { String key = source.substring(loc, i); String value = key.length() == 0 ? "$" //$NON-NLS-1$ : getReplacementString(fileName, key); buffer.append(value); replacementMode = false; } else { replacementMode = true; loc = i + 1; continue; } } else if (!replacementMode) buffer.append(c); } return buffer.toString(); } private InputStream getProcessedStream(String fileName, InputStream stream, boolean binary) throws IOException, CoreException { if (binary) return stream; InputStreamReader reader = new InputStreamReader(stream); int bufsize = 1024; char[] cbuffer = new char[bufsize]; int read = 0; StringBuilder keyBuffer = new StringBuilder(); StringBuilder outBuffer = new StringBuilder(); StringBuilder preBuffer = new StringBuilder(); boolean newLine = true; ControlStack preStack = new ControlStack(); preStack.setValueProvider(this); boolean replacementMode = false; boolean preprocessorMode = false; boolean escape = false; while (read != -1) { read = reader.read(cbuffer); for (int i = 0; i < read; i++) { char c = cbuffer[i]; if (escape) { StringBuilder buf = preprocessorMode ? preBuffer : outBuffer; buf.append(c); escape = false; continue; } if (newLine && c == '%') { // preprocessor line preprocessorMode = true; preBuffer.delete(0, preBuffer.length()); continue; } if (preprocessorMode) { if (c == '\\') { escape = true; continue; } if (c == '\n') { // handle line preprocessorMode = false; newLine = true; String line = preBuffer.toString().trim(); preStack.processLine(line); continue; } preBuffer.append(c); continue; } if (preStack.getCurrentState() == false) { continue; } if (c == '$') { if (replacementMode) { replacementMode = false; String key = keyBuffer.toString(); String value = key.length() == 0 ? "$" //$NON-NLS-1$ : getReplacementString(fileName, key); outBuffer.append(value); keyBuffer.delete(0, keyBuffer.length()); } else { replacementMode = true; } } else { if (replacementMode) keyBuffer.append(c); else { outBuffer.append(c); if (c == '\n') { newLine = true; } else newLine = false; } } } } return new ByteArrayInputStream(outBuffer.toString().getBytes(project.getDefaultCharset())); } protected double getTargetVersion() { try { IPluginBase plugin = model.getPluginBase(); if (plugin instanceof IBundlePluginBase) return Double.parseDouble(((IBundlePluginBase) plugin).getTargetVersion()); } catch (NumberFormatException e) { } return TargetPlatformHelper.getTargetVersion(); } /** * Sets a header within the plug-in's underlying manifest header, if it has * one. It the plug-in doesn't have a manifest, this method does nothing. * It's expected that this method will only be called by sub-classes during * execution of the template (i.e. during the sub-class's * updateModel(...) method).
* For example: *
*
setManifestHeader(Constants.BUNDLE_LOCALIZATION, * "plugin")
*
* * @see org.osgi.framework.Constants * * @param name * The name of the header to set * @param value * The value of the header * @since 3.4 */ protected void setManifestHeader(String name, String value) { IBundle bundle = getBundleFromModel(); if (bundle != null) { bundle.setHeader(name, value); } } /** * Gets a header from within the plug-in's underlying manifest header, if it * has one. If the plug-in doesn't have a manifest, this method returns * null. It's expected that this method will only be called by * sub-classes during execution of the template (i.e. during the sub-class's * updateModel(...) method). * * @param name * The name of the header to fetch * @return The value of the manifest header, if available, otherwise * null * @since 3.4 */ protected String getManifestHeader(String name) { IBundle bundle = getBundleFromModel(); if (bundle != null) { return bundle.getHeader(name); } return null; } /** * Determines whether this plug-in has a manifest on which to set/get * headers. This method will return false if the plug-in * doesn't have a manifest (e.g. it's a v3.0 plug-in) or if the method is * called before the model has been set on the template. * * It's expected that this method will only be called by sub-classes during * execution of the template (i.e. during the sub-class's * updateModel(...) method). * * @return true if the plug-in has a manifest, * false otherwise * @since 3.4 */ protected boolean hasBundleManifest() { IBundle bundle = getBundleFromModel(); // essentially, do we have a bundle? return (bundle != null); } /** * Try to get hold of the underlying bundle for the model, if applicable. * * @return The bundle instance, or null if not a bundle or if the model * isn't available. */ private IBundle getBundleFromModel() { // Do early exit checks if (model != null && (model instanceof IBundlePluginModelBase bundlePModel)) { IBundleModel bundleModel = bundlePModel.getBundleModel(); if (bundleModel != null) { return bundleModel.getBundle(); } } return null; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy