Java tutorial
/******************************************************************************* * Copyright (c) 2000, 2009 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation * Les Jones <lesojones@gmail.com> - Bug 185477 *******************************************************************************/ package com.siteview.mde.ui.templates; import com.siteview.mde.core.monitor.*; import java.io.*; import java.net.MalformedURLException; import java.net.URL; import java.util.*; import java.util.zip.*; import org.eclipse.core.resources.*; import org.eclipse.core.runtime.*; import org.eclipse.jdt.core.*; import org.eclipse.jface.wizard.Wizard; import com.siteview.mde.internal.core.TargetPlatformHelper; import com.siteview.mde.internal.core.ibundle.*; import com.siteview.mde.internal.ui.MDEPlugin; import com.siteview.mde.internal.ui.MDEUIMessages; import com.siteview.mde.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 IMonitorModelBase 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: <samp>pluginClass </samp>, <samp>pluginId </samp> and * <samp>pluginName </samp>. * * @see ITemplateSection#getReplacementString(String,String) */ public String getReplacementString(String fileName, String key) { String result = getKeyValue(key); return result != null ? result : key; } /** * @see IVariableProvider#getValue(String) */ 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 IMonitorModel) { IMonitor plugin = (IMonitor) model.getMonitorBase(); return plugin.getClassName(); } if (key.equals(KEY_ACTIVATOR_SIMPLE) && model instanceof IMonitorModel) { IMonitor plugin = (IMonitor) model.getMonitorBase(); 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)) { IMonitorBase plugin = model.getMonitorBase(); return plugin.getId(); } if (key.equals(KEY_PLUGIN_NAME)) { IMonitorBase plugin = model.getMonitorBase(); return plugin.getTranslatedName(); } if (key.equals(KEY_PACKAGE_NAME) && model instanceof IMonitorModel) { IMonitor plugin = (IMonitor) model.getMonitorBase(); String qualified = plugin.getClassName(); if (qualified != null) { int lastDot = qualified.lastIndexOf('.'); return (lastDot != -1) ? qualified.substring(0, lastDot) : qualified; } } return null; } /** * @see ITemplateSection#getTemplateLocation() */ public URL getTemplateLocation() { return null; } /** * @see ITemplateSection#getDescription() */ 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 <samp>plugin.properties </samp> file for the current * locale (in case of fragments, the file is <samp>fragment.properties * </samp>). * * @return resource bundle for plug-in properties file or <samp>null </samp> * if not found. */ protected abstract ResourceBundle getPluginResourceBundle(); /* (non-Javadoc) * @see com.siteview.mde.ui.templates.ITemplateSection#addPages(org.eclipse.jface.wizard.Wizard) */ public void addPages(Wizard wizard) { } /** * Tests if wizard pages for this template section have been added. * * @return <code>true</code> if wizard pages for this section have been * added, <code>false</code> otherwise. */ 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. * * @see ITemplateSection#getNumberOfWorkUnits() */ public int getNumberOfWorkUnits() { return 1; } /* (non-Javadoc) * @see com.siteview.mde.ui.templates.ITemplateSection#getDependencies(java.lang.String) */ public IMonitorReference[] getDependencies(String schemaVersion) { return new IMonitorReference[] { new PluginReference("org.eclipse.ui", //$NON-NLS-1$ null, 0) }; } /** * 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 * <samp>null </samp> if none found. */ protected IFolder getSourceFolder(IProgressMonitor monitor) { IFolder sourceFolder = null; try { IJavaProject javaProject = JavaCore.create(project); IClasspathEntry[] classpath = javaProject.getRawClasspath(); for (int i = 0; i < classpath.length; i++) { IClasspathEntry entry = classpath[i]; if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { IPath path = entry.getPath().removeFirstSegments(1); if (path.segmentCount() > 0) sourceFolder = project.getFolder(path); break; } } } catch (JavaModelException e) { MDEPlugin.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: * <ul> * <li>Files and folders found in the directory <samp>bin </samp> are * copied into the target project without modification.</li> * <li>Files found in the directory <samp>java </samp> are copied into the * Java source folder by creating the folder structure that corresponds to * the package name (variable <samp>packageName </samp>). Java files are * subject to conditional generation and variable replacement.</li> * <li>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.</li> * </ul> * * @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(MDEUIMessages.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 = new Path(templateDirectory); ZipFile zipFile = null; try { zipFile = new ZipFile(pluginJar); generateFiles(zipFile, path, project, true, false, monitor); } catch (ZipException ze) { } catch (IOException ioe) { } finally { if (zipFile != null) { try { zipFile.close(); } catch (IOException e) { } } } } 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 <code>true</code> if the provided folder should be created in * the workspace, <code>false</code> 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 <samp>true </samp> if the specified file should be created in the * project or <samp>false </samp> to skip it. The default * implementation is <samp>true </samp>. */ 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 * <samp>updateModel </samp> to add the required manifest entires. * * @see ITemplateSection#execute(IProject, IMonitorModelBase, * IProgressMonitor) */ public void execute(IProject project, IMonitorModelBase 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 <samp>reuse </samp> is * <samp>true </samp>), or a new extension object otherwise. */ protected IMonitorExtension createExtension(String pointId, boolean reuse) throws CoreException { if (reuse) { IMonitorExtension[] extensions = model.getMonitorBase().getExtensions(); for (int i = 0; i < extensions.length; i++) { IMonitorExtension extension = extensions[i]; if (extension.getPoint().equalsIgnoreCase(pointId)) { return extension; } } } IMonitorExtension 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 (int i = 0; i < members.length; i++) { File member = members[i]; 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(new Path(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; InputStream in = null; try { in = new FileInputStream(member); copyFile(member.getName(), in, dst, binary, monitor); } catch (IOException ioe) { } finally { if (in != null) try { in.close(); } catch (IOException ioe2) { } } } } } } 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 = (ZipEntry) zipEntries.nextElement(); IPath entryPath = new Path(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 (Iterator it = childZipEntries.values().iterator(); it.hasNext();) { ZipEntry zipEnry = (ZipEntry) it.next(); String name = new Path(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(new Path(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; InputStream in = null; try { in = zipFile.getInputStream(zipEnry); copyFile(name, in, dst, binary, monitor); } catch (IOException ioe) { } finally { if (in != null) try { in.close(); } catch (IOException ioe2) { } } } } } } 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.getMonitorBase().getId(); IPath path = new Path(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(new Path(targetFileName)); try { InputStream stream = getProcessedStream(fileName, input, binary); if (dstFile.exists()) { dstFile.setContents(stream, true, true, monitor); } else { dstFile.create(stream, true, monitor); } stream.close(); } catch (IOException e) { } } private String getProcessedString(String fileName, String source) { if (source.indexOf('$') == -1) return source; int loc = -1; StringBuffer buffer = new StringBuffer(); 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; StringBuffer keyBuffer = new StringBuffer(); StringBuffer outBuffer = new StringBuffer(); StringBuffer preBuffer = new StringBuffer(); 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) { StringBuffer 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 { IMonitorBase plugin = model.getMonitorBase(); 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 * <samp>updateModel(...)</samp> method). <p/> For example: * <dl> * <dd><samp>setManifestHeader(Constants.BUNDLE_LOCALIZATION, * "plugin")</samp></dd> * </dl> * * @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 * <samp>null</samp>. 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 * <samp>updateModel(...)</samp> method). * * @param name * The name of the header to fetch * @return The value of the manifest header, if available, otherwise * <samp>null</samp> * @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 <samp>false</samp> 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 * <samp>updateModel(...)</samp> method). * * @return <sampl>true</samp> if the plug-in has a manifest, <samp>false</samp> * 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)) { IBundlePluginModelBase bundlePModel = (IBundlePluginModelBase) model; IBundleModel bundleModel = bundlePModel.getBundleModel(); if (bundleModel != null) { return bundleModel.getBundle(); } } return null; } }