001: /*******************************************************************************
002: * Copyright (c) 2005, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.pde.internal.ui.wizards.product;
011:
012: import java.lang.reflect.InvocationTargetException;
013: import java.util.Locale;
014:
015: import org.eclipse.core.resources.IFile;
016: import org.eclipse.core.resources.IProject;
017: import org.eclipse.core.runtime.CoreException;
018: import org.eclipse.core.runtime.IPath;
019: import org.eclipse.core.runtime.IProgressMonitor;
020: import org.eclipse.core.runtime.IStatus;
021: import org.eclipse.core.runtime.Path;
022: import org.eclipse.core.runtime.Status;
023: import org.eclipse.osgi.util.NLS;
024: import org.eclipse.pde.core.IBaseModel;
025: import org.eclipse.pde.core.plugin.IPluginAttribute;
026: import org.eclipse.pde.core.plugin.IPluginBase;
027: import org.eclipse.pde.core.plugin.IPluginElement;
028: import org.eclipse.pde.core.plugin.IPluginExtension;
029: import org.eclipse.pde.core.plugin.IPluginModelBase;
030: import org.eclipse.pde.core.plugin.IPluginObject;
031: import org.eclipse.pde.core.plugin.PluginRegistry;
032: import org.eclipse.pde.internal.core.TargetPlatformHelper;
033: import org.eclipse.pde.internal.core.iproduct.IAboutInfo;
034: import org.eclipse.pde.internal.core.iproduct.IProduct;
035: import org.eclipse.pde.internal.core.iproduct.ISplashInfo;
036: import org.eclipse.pde.internal.core.iproduct.IWindowImages;
037: import org.eclipse.pde.internal.core.plugin.WorkspacePluginModelBase;
038: import org.eclipse.pde.internal.core.product.SplashInfo;
039: import org.eclipse.pde.internal.core.text.plugin.PluginElementNode;
040: import org.eclipse.pde.internal.ui.PDEPlugin;
041: import org.eclipse.pde.internal.ui.PDEUIMessages;
042: import org.eclipse.pde.internal.ui.util.ModelModification;
043: import org.eclipse.pde.internal.ui.util.PDEModelUtility;
044: import org.eclipse.pde.internal.ui.util.TemplateFileGenerator;
045: import org.eclipse.swt.widgets.Shell;
046: import org.eclipse.ui.branding.IProductConstants;
047:
048: public class ProductDefinitionOperation extends BaseManifestOperation {
049:
050: private String fProductId;
051: private String fApplication;
052: private IProduct fProduct;
053:
054: protected IProject fProject;
055:
056: private UpdateSplashHandlerAction fUpdateSplashAction;
057:
058: private RemoveSplashHandlerBindingAction fRemoveSplashAction;
059:
060: private UpdateSplashProgressOperation fUpdateSplashProgressOperation;
061:
062: public ProductDefinitionOperation(IProduct product,
063: String pluginId, String productId, String application,
064: Shell shell) {
065: super (shell, pluginId);
066: fProductId = productId;
067: fApplication = application;
068: fProduct = product;
069: fProject = null;
070: }
071:
072: /**
073: * @param product
074: * @param pluginId
075: * @param productId
076: * @param application
077: * @param shell
078: * @param project
079: */
080: public ProductDefinitionOperation(IProduct product,
081: String pluginId, String productId, String application,
082: Shell shell, IProject project) {
083: super (shell, pluginId);
084: fProductId = productId;
085: fApplication = application;
086: fProduct = product;
087: // Needed for splash handler updates (file copying)
088: fProject = project;
089: }
090:
091: /**
092: * @param id
093: * @return
094: */
095: protected String getFormattedPackageName(String id) {
096: StringBuffer buffer = new StringBuffer();
097: for (int i = 0; i < id.length(); i++) {
098: char ch = id.charAt(i);
099: if (buffer.length() == 0) {
100: if (Character.isJavaIdentifierStart(ch))
101: buffer.append(Character.toLowerCase(ch));
102: } else {
103: if (Character.isJavaIdentifierPart(ch) || ch == '.')
104: buffer.append(ch);
105: }
106: }
107: return buffer.toString().toLowerCase(Locale.ENGLISH);
108: }
109:
110: /**
111: * @return
112: */
113: protected String createTargetPackage() {
114: // Package name addition to create a location for containing
115: // any classes required by the splash handlers.
116: String packageName = getFormattedPackageName(fPluginId);
117: // Unqualifed
118: if (packageName.length() == 0) {
119: return ISplashHandlerConstants.F_UNQUALIFIED_EXTENSION_ID;
120: }
121: // Qualified
122: return packageName + '.'
123: + ISplashHandlerConstants.F_UNQUALIFIED_EXTENSION_ID;
124: }
125:
126: /**
127: * @return fully-qualified class (with package)
128: */
129: private String createAttributeValueClass() {
130: String targetPackage = createTargetPackage();
131: String targetClass = createTargetClass();
132: // Ensure target class is defined
133: if (targetClass == null) {
134: return null;
135: }
136:
137: return targetPackage + "." + //$NON-NLS-1$
138: targetClass;
139: }
140:
141: /**
142: * @return unqualified class
143: */
144: private String createTargetClass() {
145: // Get the splash handler type
146: String splashHandlerType = getSplashHandlerType();
147: // Ensure splash handler type was specfied
148: if (splashHandlerType == null) {
149: return null;
150: }
151: // Update the class name depending on the splash screen type
152: for (int i = 0; i < ISplashHandlerConstants.F_SPLASH_SCREEN_TYPE_CHOICES.length; i++) {
153: String choice = ISplashHandlerConstants.F_SPLASH_SCREEN_TYPE_CHOICES[i][0];
154: if (splashHandlerType.equals(choice)) {
155: return ISplashHandlerConstants.F_SPLASH_SCREEN_CLASSES[i];
156: }
157: }
158: return null;
159: }
160:
161: /**
162: * @return splash screen type qualified with package name
163: */
164: private String createAttributeValueID() {
165: // Create the ID based on the splash screen type
166: return createTargetPackage() + "." + //$NON-NLS-1$
167: getSplashHandlerType();
168: }
169:
170: /**
171: * @return
172: */
173: private UpdateSplashProgressOperation getUpdateSplashProgressOperation() {
174: if (fUpdateSplashProgressOperation == null) {
175: fUpdateSplashProgressOperation = new UpdateSplashProgressOperation();
176: } else {
177: fUpdateSplashProgressOperation.reset();
178: }
179: return fUpdateSplashProgressOperation;
180: }
181:
182: /**
183: * @param model
184: * @param monitor
185: * @throws CoreException
186: */
187: private void updateSplashProgress(IPluginModelBase model,
188: IProgressMonitor monitor) throws CoreException {
189: // Sanity checks
190: if (fProject == null) {
191: return;
192: } else if (model == null) {
193: return;
194: } else if (monitor == null) {
195: return;
196: }
197: // Get the action
198: UpdateSplashProgressOperation operation = getUpdateSplashProgressOperation();
199: operation.setModel(model);
200: operation.setShowProgress(isProgressDefined());
201: operation.setProject(fProject);
202: operation.setProductID(fProduct.getId());
203: operation.setPluginID(fPluginId);
204: // Execute the action
205: operation.run(monitor);
206: }
207:
208: /**
209: * @return
210: */
211: private boolean isProgressDefined() {
212: // Get the splash info from the model
213: ISplashInfo info = fProduct.getProduct().getSplashInfo();
214: // Ensure splash info was defined
215: if (info == null) {
216: return false;
217: }
218: // Ensure splash progress was defined
219: return info.isDefinedGeometry();
220: }
221:
222: /**
223: * @return
224: */
225: private String getSplashHandlerType() {
226: // Get the splash info from the model
227: ISplashInfo info = fProduct.getProduct().getSplashInfo();
228: // Ensure splash info was defined
229: if (info == null) {
230: return null;
231: }
232: // Ensure splash type was defined
233: if (info.isDefinedSplashHandlerType() == false) {
234: return null;
235: }
236: return info.getFieldSplashHandlerType();
237: }
238:
239: /**
240: * @param model
241: * @param monitor
242: * @throws CoreException
243: */
244: private void updateSplashHandler(IPluginModelBase model,
245: IProgressMonitor monitor) throws CoreException {
246: // Copy the applicable splash handler artifacts and perform parameter
247: // substitution (like in templates plug-in)
248: // Artifacts may include code, images and extension point schemas
249: updateSplashHandlerFiles(model, monitor);
250: // Update the plug-in model with the applicable splash handler extension
251: // and extension point related mark-up
252: updateSplashHandlerModel(model, monitor);
253: }
254:
255: /**
256: * @param model
257: * @param monitor
258: * @throws CoreException
259: */
260: private void updateSplashHandlerFiles(IPluginModelBase model,
261: IProgressMonitor monitor) throws CoreException {
262: // If the project is not defined, abort this operation
263: if (fProject == null) {
264: return;
265: }
266: // Get the splash handler type
267: String splashHandlerType = getSplashHandlerType();
268: // If the splash handler type was not defined, abort this operation
269: if (splashHandlerType == null) {
270: return;
271: }
272: // Create and configure the template file generator
273: // Note: Plug-in ID must be passed in separately from model, because
274: // the underlying model does not contain the ID (even when it is
275: // a workspace model)
276: TemplateFileGenerator generator = new TemplateFileGenerator(
277: fProject, model, fPluginId, createTargetPackage(),
278: createTargetClass(), splashHandlerType);
279: // Generate the necessary files
280: generator.generateFiles(monitor);
281: }
282:
283: /**
284: * @param model
285: * @param monitor
286: * @throws CoreException
287: */
288: private void updateSplashHandlerModel(IPluginModelBase model,
289: IProgressMonitor monitor) throws CoreException {
290: // Get the splash handler type
291: String splashHandlerType = getSplashHandlerType();
292: // If the splash handler type is not defined, abort this operation
293: if (splashHandlerType == null) {
294: runRemoveSplashAction(model, monitor);
295: } else {
296: runUpdateSplashAction(model, monitor, splashHandlerType);
297: }
298: }
299:
300: /**
301: * @param model
302: * @param monitor
303: */
304: private void runRemoveSplashAction(IPluginModelBase model,
305: IProgressMonitor monitor) throws CoreException {
306: // Create the remove splash handler action
307: fRemoveSplashAction = new RemoveSplashHandlerBindingAction();
308: // Configure the action
309: fRemoveSplashAction.setFieldProductID(fProduct.getId());
310: fRemoveSplashAction
311: .setFieldTargetPackage(createTargetPackage());
312:
313: fRemoveSplashAction.setModel(model);
314: fRemoveSplashAction.setMonitor(monitor);
315: // Execute the action
316: fRemoveSplashAction.run();
317: // If an core exception was thrown and caught, release it
318: fRemoveSplashAction.hasException();
319: }
320:
321: /**
322: * @param model
323: * @param monitor
324: * @param splashHandlerType
325: * @throws CoreException
326: */
327: private void runUpdateSplashAction(IPluginModelBase model,
328: IProgressMonitor monitor, String splashHandlerType)
329: throws CoreException {
330: // Create the update splash handler action
331: fUpdateSplashAction = new UpdateSplashHandlerAction();
332: // Configure the action
333: String id = createAttributeValueID();
334: fUpdateSplashAction.setFieldID(id);
335: fUpdateSplashAction.setFieldClass(createAttributeValueClass());
336: fUpdateSplashAction.setFieldSplashID(id);
337: fUpdateSplashAction.setFieldProductID(fProduct.getId());
338: fUpdateSplashAction.setFieldTemplate(splashHandlerType);
339: fUpdateSplashAction.setFieldPluginID(fPluginId);
340:
341: fUpdateSplashAction.setModel(model);
342: fUpdateSplashAction.setMonitor(monitor);
343: // Execute the action
344: fUpdateSplashAction.run();
345: // If an core exception was thrown and caught, release it
346: fUpdateSplashAction.hasException();
347: }
348:
349: public void run(IProgressMonitor monitor)
350: throws InvocationTargetException, InterruptedException {
351: try {
352: IFile file = getFile();
353: if (!file.exists()) {
354: createNewFile(file, monitor);
355: } else {
356: modifyExistingFile(file, monitor);
357: }
358: updateSingleton(monitor);
359: } catch (CoreException e) {
360: throw new InvocationTargetException(e);
361: }
362: }
363:
364: private void createNewFile(IFile file, IProgressMonitor monitor)
365: throws CoreException {
366: WorkspacePluginModelBase model = (WorkspacePluginModelBase) getModel(file);
367: IPluginBase base = model.getPluginBase();
368: base
369: .setSchemaVersion(TargetPlatformHelper
370: .getTargetVersion() < 3.2 ? "3.0" : "3.2"); //$NON-NLS-1$ //$NON-NLS-2$
371: base.add(createExtension(model));
372: // Update the splash handler. Update plug-in model and copy files
373: updateSplashHandler(model, monitor);
374: // Update splash progress. Update plug-in model and copy files
375: updateSplashProgress(model, monitor);
376:
377: model.save();
378: }
379:
380: private IPluginExtension createExtension(IPluginModelBase model)
381: throws CoreException {
382: IPluginExtension extension = model.getFactory()
383: .createExtension();
384: extension.setPoint("org.eclipse.core.runtime.products"); //$NON-NLS-1$
385: extension.setId(fProductId);
386: extension.add(createExtensionContent(extension));
387: return extension;
388: }
389:
390: private IPluginElement createExtensionContent(
391: IPluginExtension extension) throws CoreException {
392: IPluginElement element = extension.getModel().getFactory()
393: .createElement(extension);
394: element.setName("product"); //$NON-NLS-1$
395: element.setAttribute("name", fProduct.getName()); //$NON-NLS-1$
396: element.setAttribute("application", fApplication); //$NON-NLS-1$
397:
398: IPluginElement child = createElement(element,
399: IProductConstants.WINDOW_IMAGES,
400: getWindowImagesString());
401: if (child != null)
402: element.add(child);
403:
404: child = createElement(element, IProductConstants.ABOUT_TEXT,
405: getAboutText());
406: if (child != null)
407: element.add(child);
408:
409: child = createElement(element, IProductConstants.ABOUT_IMAGE,
410: getAboutImage());
411: if (child != null)
412: element.add(child);
413:
414: child = createElement(element,
415: IProductConstants.STARTUP_FOREGROUND_COLOR,
416: getForegroundColor());
417: if (child != null)
418: element.add(child);
419:
420: child = createElement(element,
421: IProductConstants.STARTUP_PROGRESS_RECT,
422: getProgressRect());
423: if (child != null)
424: element.add(child);
425:
426: child = createElement(element,
427: IProductConstants.STARTUP_MESSAGE_RECT,
428: getMessageRect());
429: if (child != null)
430: element.add(child);
431:
432: return element;
433: }
434:
435: private IPluginElement createElement(IPluginElement parent,
436: String name, String value) throws CoreException {
437: IPluginElement element = null;
438: if (value != null && value.length() > 0) {
439: element = parent.getModel().getFactory().createElement(
440: parent);
441: element.setName("property"); //$NON-NLS-1$
442: element.setAttribute("name", name); //$NON-NLS-1$
443: element.setAttribute("value", value); //$NON-NLS-1$
444: }
445: return element;
446: }
447:
448: private String getAboutText() {
449: IAboutInfo info = fProduct.getAboutInfo();
450: if (info != null) {
451: String text = info.getText();
452: return text == null || text.length() == 0 ? null : text;
453: }
454: return null;
455: }
456:
457: private String getAboutImage() {
458: IAboutInfo info = fProduct.getAboutInfo();
459: return info != null ? getURL(info.getImagePath()) : null;
460: }
461:
462: private String getURL(String location) {
463: if (location == null || location.trim().length() == 0)
464: return null;
465: IPath path = new Path(location);
466: if (!path.isAbsolute())
467: return location;
468: String projectName = path.segment(0);
469: IProject project = PDEPlugin.getWorkspace().getRoot()
470: .getProject(projectName);
471: if (project.exists()) {
472: IPluginModelBase model = PluginRegistry.findModel(project);
473: if (model != null) {
474: String id = model.getPluginBase().getId();
475: if (fPluginId.equals(id))
476: return path.removeFirstSegments(1).toString();
477: return "platform:/plugin/" + id + "/" + path.removeFirstSegments(1); //$NON-NLS-1$ //$NON-NLS-2$
478: }
479: }
480: return location;
481: }
482:
483: private String getWindowImagesString() {
484: IWindowImages images = fProduct.getWindowImages();
485: StringBuffer buffer = new StringBuffer();
486: if (images != null) {
487: for (int i = 0; i < IWindowImages.TOTAL_IMAGES; i++) {
488: String image = getURL(images.getImagePath(i));
489: if (image != null) {
490: if (buffer.length() > 0)
491: buffer.append(","); //$NON-NLS-1$
492: buffer.append(image);
493: }
494:
495: }
496: }
497: return buffer.length() == 0 ? null : buffer.toString(); //$NON-NLS-1$
498: }
499:
500: private String getForegroundColor() {
501: ISplashInfo info = fProduct.getSplashInfo();
502: return info != null ? info.getForegroundColor() : null;
503: }
504:
505: private String getProgressRect() {
506: ISplashInfo info = fProduct.getSplashInfo();
507: return info != null ? SplashInfo.getGeometryString(info
508: .getProgressGeometry()) : null;
509: }
510:
511: private String getMessageRect() {
512: ISplashInfo info = fProduct.getSplashInfo();
513: return info != null ? SplashInfo.getGeometryString(info
514: .getMessageGeometry()) : null;
515: }
516:
517: private void modifyExistingFile(IFile file, IProgressMonitor monitor)
518: throws CoreException {
519: IStatus status = PDEPlugin.getWorkspace().validateEdit(
520: new IFile[] { file }, getShell());
521: if (status.getSeverity() != IStatus.OK)
522: throw new CoreException(
523: new Status(
524: IStatus.ERROR,
525: "org.eclipse.pde.ui", IStatus.ERROR, NLS.bind(PDEUIMessages.ProductDefinitionOperation_readOnly, fPluginId), null)); //$NON-NLS-1$
526:
527: ModelModification mod = new ModelModification(file) {
528: protected void modifyModel(IBaseModel model,
529: IProgressMonitor monitor) throws CoreException {
530: if (!(model instanceof IPluginModelBase))
531: return;
532: IPluginExtension extension = findProductExtension((IPluginModelBase) model);
533: if (extension == null)
534: insertNewExtension((IPluginModelBase) model);
535: else
536: modifyExistingExtension(extension);
537: // Update the splash handler. Update plug-in model and copy files
538: updateSplashHandler((IPluginModelBase) model, monitor);
539: // Update splash progress. Update plug-in model and copy files
540: updateSplashProgress((IPluginModelBase) model, monitor);
541: }
542: };
543: PDEModelUtility.modifyModel(mod, monitor);
544: }
545:
546: private IPluginExtension findProductExtension(IPluginModelBase model) {
547: IPluginExtension[] extensions = model.getPluginBase()
548: .getExtensions();
549: for (int i = 0; i < extensions.length; i++) {
550: String point = extensions[i].getPoint();
551: String id = extensions[i].getId();
552: if (fProductId.equals(id)
553: && "org.eclipse.core.runtime.products".equals(point)) { //$NON-NLS-1$
554: return extensions[i];
555: }
556: }
557: return null;
558: }
559:
560: private void insertNewExtension(IPluginModelBase model)
561: throws CoreException {
562: IPluginExtension extension = createExtension(model);
563: model.getPluginBase().add(extension);
564: }
565:
566: private void modifyExistingExtension(IPluginExtension extension)
567: throws CoreException {
568: if (extension.getChildCount() == 0) {
569: insertNewProductElement(extension);
570: return;
571: }
572:
573: PluginElementNode element = (PluginElementNode) extension
574: .getChildren()[0];
575:
576: if (!"product".equals(element.getName())) { //$NON-NLS-1$
577: insertNewProductElement(extension);
578: return;
579: }
580:
581: element.setAttribute("application", fApplication); //$NON-NLS-1$
582: element.setAttribute("name", fProduct.getName()); //$NON-NLS-1$
583: synchronizeChild(element, IProductConstants.APP_NAME, fProduct
584: .getName());
585:
586: synchronizeChild(element, IProductConstants.ABOUT_IMAGE,
587: getAboutImage());
588: synchronizeChild(element, IProductConstants.ABOUT_TEXT,
589: getAboutText());
590: synchronizeChild(element, IProductConstants.WINDOW_IMAGES,
591: getWindowImagesString());
592: synchronizeChild(element,
593: IProductConstants.STARTUP_FOREGROUND_COLOR,
594: getForegroundColor());
595: synchronizeChild(element,
596: IProductConstants.STARTUP_MESSAGE_RECT,
597: getMessageRect());
598: synchronizeChild(element,
599: IProductConstants.STARTUP_PROGRESS_RECT,
600: getProgressRect());
601:
602: }
603:
604: private void synchronizeChild(IPluginElement element,
605: String propertyName, String value) throws CoreException {
606: IPluginElement child = null;
607: IPluginObject[] children = element.getChildren();
608: for (int i = 0; i < children.length; i++) {
609: IPluginElement candidate = (IPluginElement) children[i];
610: if (candidate.getName().equals("property")) { //$NON-NLS-1$
611: IPluginAttribute attr = candidate.getAttribute("name"); //$NON-NLS-1$
612: if (attr != null
613: && attr.getValue().equals(propertyName)) {
614: child = candidate;
615: break;
616: }
617: }
618: }
619: if (child != null && value == null)
620: element.remove(child);
621:
622: if (value == null)
623: return;
624:
625: if (child == null) {
626: child = element.getModel().getFactory().createElement(
627: element);
628: child.setName("property"); //$NON-NLS-1$
629: element.add(child);
630: }
631: child.setAttribute("value", value); //$NON-NLS-1$
632: child.setAttribute("name", propertyName); //$NON-NLS-1$
633: }
634:
635: private void insertNewProductElement(IPluginExtension extension)
636: throws CoreException {
637: IPluginElement element = createExtensionContent(extension);
638: extension.add(element);
639: }
640:
641: }
|