1 /*******************************************************************************
2  * Copyright (c) 2005, 2015 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     EclipseSource Corporation - ongoing enhancements
14  *******************************************************************************/
15 package org.eclipse.pde.internal.ui.wizards.product;
16 
17 import java.io.*;
18 import java.lang.reflect.InvocationTargetException;
19 import java.net.MalformedURLException;
20 import java.net.URL;
21 import org.eclipse.core.resources.*;
22 import org.eclipse.core.runtime.*;
23 import org.eclipse.osgi.util.NLS;
24 import org.eclipse.pde.core.IBaseModel;
25 import org.eclipse.pde.core.plugin.*;
26 import org.eclipse.pde.internal.core.TargetPlatformHelper;
27 import org.eclipse.pde.internal.core.iproduct.IProduct;
28 import org.eclipse.pde.internal.core.plugin.WorkspacePluginModelBase;
29 import org.eclipse.pde.internal.ui.PDEPlugin;
30 import org.eclipse.pde.internal.ui.PDEUIMessages;
31 import org.eclipse.pde.internal.ui.util.ModelModification;
32 import org.eclipse.pde.internal.ui.util.PDEModelUtility;
33 import org.eclipse.pde.internal.ui.wizards.templates.ControlStack;
34 import org.eclipse.pde.ui.templates.IVariableProvider;
35 import org.eclipse.swt.widgets.Shell;
36 
37 public class ProductIntroOperation extends BaseManifestOperation implements IVariableProvider {
38 
39 	protected String fIntroId;
40 	private Shell fShell;
41 	private IProduct fProduct;
42 	private IProject fProject;
43 	private static final String INTRO_POINT = "org.eclipse.ui.intro"; //$NON-NLS-1$
44 	private static final String INTRO_CONFIG_POINT = "org.eclipse.ui.intro.config"; //$NON-NLS-1$
45 	private static final String INTRO_CLASS = "org.eclipse.ui.intro.config.CustomizableIntroPart"; //$NON-NLS-1$
46 	private static final String KEY_PRODUCT_NAME = "productName"; //$NON-NLS-1$
47 
ProductIntroOperation(IProduct product, String pluginId, String introId, Shell shell)48 	public ProductIntroOperation(IProduct product, String pluginId, String introId, Shell shell) {
49 		super(shell, pluginId);
50 		fIntroId = introId;
51 		fProduct = product;
52 		fProject = PluginRegistry.findModel(pluginId).getUnderlyingResource().getProject();
53 	}
54 
55 	@Override
run(IProgressMonitor monitor)56 	public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
57 		try {
58 			IFile file = getFile();
59 			if (!file.exists()) {
60 				createNewFile(file);
61 			} else {
62 				modifyExistingFile(file, monitor);
63 			}
64 			updateSingleton(monitor);
65 			generateFiles(monitor);
66 		} catch (CoreException e) {
67 			throw new InvocationTargetException(e);
68 		}
69 	}
70 
createNewFile(IFile file)71 	private void createNewFile(IFile file) throws CoreException {
72 		WorkspacePluginModelBase model = (WorkspacePluginModelBase) getModel(file);
73 		IPluginBase base = model.getPluginBase();
74 		base.setSchemaVersion(TargetPlatformHelper.getSchemaVersion());
75 		base.add(createIntroExtension(model));
76 		base.add(createIntroConfigExtension(model));
77 		model.save();
78 	}
79 
createIntroExtension(IPluginModelBase model)80 	private IPluginExtension createIntroExtension(IPluginModelBase model) throws CoreException {
81 		IPluginExtension extension = model.getFactory().createExtension();
82 		extension.setPoint(INTRO_POINT);
83 		extension.add(createIntroExtensionContent(extension));
84 		extension.add(createIntroBindingExtensionContent(extension));
85 		return extension;
86 	}
87 
createIntroConfigExtension(IPluginModelBase model)88 	private IPluginExtension createIntroConfigExtension(IPluginModelBase model) throws CoreException {
89 		IPluginExtension extension = model.getFactory().createExtension();
90 		extension.setPoint(INTRO_CONFIG_POINT);
91 		extension.add(createIntroConfigExtensionContent(extension));
92 		return extension;
93 	}
94 
createIntroExtensionContent(IPluginExtension extension)95 	private IPluginElement createIntroExtensionContent(IPluginExtension extension) throws CoreException {
96 		IPluginElement element = extension.getModel().getFactory().createElement(extension);
97 		element.setName("intro"); //$NON-NLS-1$
98 		element.setAttribute("id", fIntroId); //$NON-NLS-1$
99 		element.setAttribute("class", INTRO_CLASS); //$NON-NLS-1$
100 		return element;
101 	}
102 
createIntroBindingExtensionContent(IPluginExtension extension)103 	private IPluginElement createIntroBindingExtensionContent(IPluginExtension extension) throws CoreException {
104 		IPluginElement element = extension.getModel().getFactory().createElement(extension);
105 		element.setName("introProductBinding"); //$NON-NLS-1$
106 		element.setAttribute("productId", fProduct.getProductId()); //$NON-NLS-1$
107 		element.setAttribute("introId", fIntroId); //$NON-NLS-1$
108 		return element;
109 	}
110 
createIntroConfigExtensionContent(IPluginExtension extension)111 	private IPluginElement createIntroConfigExtensionContent(IPluginExtension extension) throws CoreException {
112 		IPluginElement element = extension.getModel().getFactory().createElement(extension);
113 		element.setName("config"); //$NON-NLS-1$
114 		element.setAttribute("id", fPluginId + ".introConfigId"); //$NON-NLS-1$ //$NON-NLS-2$
115 		element.setAttribute("introId", fIntroId); //$NON-NLS-1$
116 		element.setAttribute("content", "introContent.xml"); //$NON-NLS-1$ //$NON-NLS-2$
117 		element.add(createPresentationElement(element));
118 
119 		return element;
120 	}
121 
createPresentationElement(IPluginElement parent)122 	private IPluginElement createPresentationElement(IPluginElement parent) throws CoreException {
123 		IPluginElement presentation = null;
124 		IPluginElement implementation = null;
125 		IExtensionsModelFactory factory = parent.getModel().getFactory();
126 
127 		presentation = factory.createElement(parent);
128 		presentation.setName("presentation"); //$NON-NLS-1$
129 		presentation.setAttribute("home-page-id", "root"); //$NON-NLS-1$ //$NON-NLS-2$
130 
131 		implementation = factory.createElement(presentation);
132 		implementation.setName("implementation"); //$NON-NLS-1$
133 		implementation.setAttribute("kind", "html"); //$NON-NLS-1$ //$NON-NLS-2$
134 		implementation.setAttribute("style", "content/shared.css"); //$NON-NLS-1$ //$NON-NLS-2$
135 		implementation.setAttribute("os", "win32,linux,macosx"); //$NON-NLS-1$ //$NON-NLS-2$
136 
137 		presentation.add(implementation);
138 
139 		return presentation;
140 	}
141 
modifyExistingFile(IFile file, IProgressMonitor monitor)142 	private void modifyExistingFile(IFile file, IProgressMonitor monitor) throws CoreException {
143 		IStatus status = PDEPlugin.getWorkspace().validateEdit(new IFile[] {file}, fShell);
144 		if (status.getSeverity() != IStatus.OK)
145 			throw new CoreException(new Status(IStatus.ERROR, "org.eclipse.pde.ui", IStatus.ERROR, NLS.bind(PDEUIMessages.ProductDefinitionOperation_readOnly, fPluginId), null)); //$NON-NLS-1$
146 
147 		ModelModification mod = new ModelModification(file) {
148 			@Override
149 			protected void modifyModel(IBaseModel model, IProgressMonitor monitor) throws CoreException {
150 				if (!(model instanceof IPluginModelBase))
151 					return;
152 				IPluginModelBase pluginModel = (IPluginModelBase) model;
153 				IPluginExtension extension = getExtension(pluginModel, INTRO_POINT);
154 				if (extension == null) {
155 					extension = createIntroExtension(pluginModel);
156 					pluginModel.getPluginBase().add(extension);
157 				} else {
158 					extension.add(createIntroExtensionContent(extension));
159 					extension.add(createIntroBindingExtensionContent(extension));
160 				}
161 
162 				extension = getExtension(pluginModel, INTRO_CONFIG_POINT);
163 				if (extension == null) {
164 					extension = createIntroConfigExtension(pluginModel);
165 					pluginModel.getPluginBase().add(extension);
166 				} else {
167 					extension.add(createIntroConfigExtensionContent(extension));
168 				}
169 			}
170 		};
171 		PDEModelUtility.modifyModel(mod, monitor);
172 	}
173 
getExtension(IPluginModelBase model, String tPoint)174 	private IPluginExtension getExtension(IPluginModelBase model, String tPoint) {
175 		IPluginExtension[] extensions = model.getPluginBase().getExtensions();
176 		for (IPluginExtension extension : extensions) {
177 			String point = extension.getPoint();
178 			if (tPoint.equals(point)) {
179 				return extension;
180 			}
181 		}
182 		return null;
183 	}
184 
generateFiles(IProgressMonitor monitor)185 	protected void generateFiles(IProgressMonitor monitor) throws CoreException {
186 		monitor.setTaskName(PDEUIMessages.AbstractTemplateSection_generating);
187 
188 		URL locationUrl = null;
189 		try {
190 			locationUrl = new URL(PDEPlugin.getDefault().getInstallURL(), "templates_3.1/intro/"); //$NON-NLS-1$
191 		} catch (MalformedURLException e1) {
192 			return;
193 		}
194 		try {
195 			locationUrl = FileLocator.resolve(locationUrl);
196 			locationUrl = FileLocator.toFileURL(locationUrl);
197 		} catch (IOException e) {
198 			return;
199 		}
200 		if ("file".equals(locationUrl.getProtocol())) { //$NON-NLS-1$
201 			File templateDirectory = new File(locationUrl.getFile());
202 			if (!templateDirectory.exists())
203 				return;
204 			generateFiles(templateDirectory, fProject, true, false, monitor);
205 		}
206 		monitor.subTask(""); //$NON-NLS-1$
207 		monitor.worked(1);
208 	}
209 
generateFiles(File src, IContainer dst, boolean firstLevel, boolean binary, IProgressMonitor monitor)210 	private void generateFiles(File src, IContainer dst, boolean firstLevel, boolean binary, IProgressMonitor monitor) throws CoreException {
211 		File[] members = src.listFiles();
212 
213 		for (File member : members) {
214 			if (member.getName().equals("ext.xml") || //$NON-NLS-1$
215 					member.getName().equals("java") || //$NON-NLS-1$
216 					member.getName().equals("concept3.xhtml") || //$NON-NLS-1$
217 					member.getName().equals("extContent.xhtml")) //$NON-NLS-1$
218 				continue;
219 			else if (member.isDirectory()) {
220 				IContainer dstContainer = null;
221 				if (firstLevel) {
222 					binary = false;
223 					if (member.getName().equals("bin")) { //$NON-NLS-1$
224 						binary = true;
225 						dstContainer = dst;
226 					}
227 				}
228 				if (dstContainer == null) {
229 					dstContainer = dst.getFolder(new Path(member.getName()));
230 				}
231 				if (dstContainer instanceof IFolder && !dstContainer.exists())
232 					((IFolder) dstContainer).create(true, true, monitor);
233 				generateFiles(member, dstContainer, false, binary, monitor);
234 			} else {
235 				if (firstLevel)
236 					binary = false;
237 				try (InputStream in = new FileInputStream(member);) {
238 					copyFile(member.getName(), in, dst, binary, monitor);
239 				} catch (IOException ioe) {
240 				}
241 			}
242 		}
243 	}
244 
copyFile(String fileName, InputStream input, IContainer dst, boolean binary, IProgressMonitor monitor)245 	private void copyFile(String fileName, InputStream input, IContainer dst, boolean binary, IProgressMonitor monitor) throws CoreException {
246 
247 		monitor.subTask(fileName);
248 		IFile dstFile = dst.getFile(new Path(fileName));
249 
250 		try (InputStream stream = getProcessedStream(fileName, input, binary)) {
251 			if (dstFile.exists()) {
252 				dstFile.setContents(stream, true, true, monitor);
253 			} else {
254 				dstFile.create(stream, true, monitor);
255 			}
256 		} catch (IOException e) {
257 		}
258 	}
259 
getProcessedStream(String fileName, InputStream stream, boolean binary)260 	private InputStream getProcessedStream(String fileName, InputStream stream, boolean binary) throws IOException, CoreException {
261 		if (binary)
262 			return stream;
263 
264 		InputStreamReader reader = new InputStreamReader(stream);
265 		int bufsize = 1024;
266 		char[] cbuffer = new char[bufsize];
267 		int read = 0;
268 		StringBuilder keyBuffer = new StringBuilder();
269 		StringBuilder outBuffer = new StringBuilder();
270 		ControlStack preStack = new ControlStack();
271 		preStack.setValueProvider(this);
272 
273 		boolean replacementMode = false;
274 		while (read != -1) {
275 			read = reader.read(cbuffer);
276 			for (int i = 0; i < read; i++) {
277 				char c = cbuffer[i];
278 
279 				if (preStack.getCurrentState() == false) {
280 					continue;
281 				}
282 
283 				if (c == '$') {
284 					if (replacementMode) {
285 						replacementMode = false;
286 						String key = keyBuffer.toString();
287 						String value = key.length() == 0 ? "$" //$NON-NLS-1$
288 								: getReplacementString(fileName, key);
289 						outBuffer.append(value);
290 						keyBuffer.delete(0, keyBuffer.length());
291 					} else {
292 						replacementMode = true;
293 					}
294 				} else {
295 					if (replacementMode)
296 						keyBuffer.append(c);
297 					else {
298 						outBuffer.append(c);
299 					}
300 				}
301 			}
302 		}
303 		return new ByteArrayInputStream(outBuffer.toString().getBytes(fProject.getDefaultCharset()));
304 	}
305 
getReplacementString(String fileName, String key)306 	private String getReplacementString(String fileName, String key) {
307 		if (key.equals(KEY_PRODUCT_NAME)) {
308 			return fProduct.getName();
309 		}
310 		return key;
311 	}
312 
313 	@Override
getValue(String variable)314 	public Object getValue(String variable) {
315 		return null;
316 	}
317 }
318