| /* |
| * Copyright (c) 2006, 2007 Borland Software Corporation |
| * |
| * 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: |
| * Michael Golubev (Borland) - initial API and implementation |
| */ |
| package org.eclipse.gmf.internal.graphdef.codegen.ui; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.URL; |
| import java.text.MessageFormat; |
| import java.util.Collections; |
| import java.util.Locale; |
| import java.util.ResourceBundle; |
| import java.util.jar.Manifest; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| 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.emf.common.util.TreeIterator; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.resource.ContentHandler; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; |
| import org.eclipse.gmf.gmfgraph.DiagramElement; |
| import org.eclipse.gmf.gmfgraph.FigureGallery; |
| import org.eclipse.gmf.graphdef.codegen.StandaloneGenerator; |
| import org.eclipse.jface.dialogs.IMessageProvider; |
| import org.eclipse.jface.wizard.Wizard; |
| import org.eclipse.jface.wizard.WizardPage; |
| import org.eclipse.osgi.util.ManifestElement; |
| import org.eclipse.pde.core.plugin.IPluginImport; |
| import org.eclipse.pde.core.plugin.IPluginModel; |
| import org.eclipse.pde.core.plugin.IPluginReference; |
| import org.eclipse.pde.ui.IFieldData; |
| import org.eclipse.pde.ui.templates.BooleanOption; |
| import org.eclipse.pde.ui.templates.OptionTemplateSection; |
| import org.eclipse.pde.ui.templates.TemplateOption; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleException; |
| import org.osgi.framework.Constants; |
| |
| public class ConverterSection extends OptionTemplateSection { |
| private static final String MY_PLUGIN_ID = "org.eclipse.gmf.graphdef.codegen.ui"; |
| private static final String SECTION_ID = "org.eclipse.gmf.graphdef.codegen.ui.ConverterSection"; |
| private static final int THE_ONLY_PAGE_INDEX = 0; |
| |
| public static final String OPTION_MAIN_PACKAGE_NAME = SECTION_ID + ".mainPackageName"; |
| public static final String OPTION_NEEDS_MAP_MODE = SECTION_ID + ".needsMapMode"; |
| public static final String OPTION_USE_RUNTIME_FIGURES = SECTION_ID + ".useRuntimeFigures"; |
| public static final String OPTION_INPUT_RESOURCE_FULL_PATH = SECTION_ID + ".inputResource"; |
| public static final String OPTION_OUTPUT_GALLERY_FULL_PATH = SECTION_ID + ".outputGallery"; |
| public static final String OPTION_OUTPUT_DIAGRAM_ELEMENTS_FULL_PATH = SECTION_ID + ".outputDiagramElements"; |
| |
| private TemplateOption myPackageNameOption; |
| private FileNameOption myInputPathOption; |
| private FileNameOption myOutputGalleryPathOption; |
| private FileNameOption myOutputDiagramElementsPathOption; |
| private final InputValidationState myCachedInputValidationState; |
| private BooleanOption myNeedsMapModeOption; |
| private BooleanOption myUseRuntimeFiguresOption; |
| private final boolean shouldWarnLiteVerstionDoesNotSupportMapMode; |
| private ManifestElement[] myRequiredBundles; |
| |
| public ConverterSection(){ |
| setPageCount(THE_ONLY_PAGE_INDEX + 1); |
| myPackageNameOption = addOption(OPTION_MAIN_PACKAGE_NAME, "Generate figures package", null, THE_ONLY_PAGE_INDEX); |
| myInputPathOption = addFileNameOption(false, OPTION_INPUT_RESOURCE_FULL_PATH, "Input GMFGraph instance", "", THE_ONLY_PAGE_INDEX); |
| myOutputGalleryPathOption = addFileNameOption(true, OPTION_OUTPUT_GALLERY_FULL_PATH, "Create Figure Gallery", "", THE_ONLY_PAGE_INDEX); |
| myOutputGalleryPathOption.setRequired(false); |
| myOutputDiagramElementsPathOption = addFileNameOption(true, OPTION_OUTPUT_DIAGRAM_ELEMENTS_FULL_PATH, "Mirror diagram elements", "", THE_ONLY_PAGE_INDEX); |
| myOutputDiagramElementsPathOption.setRequired(false); |
| myNeedsMapModeOption = (BooleanOption) addOption(OPTION_NEEDS_MAP_MODE, "Use IMapMode", false, THE_ONLY_PAGE_INDEX); |
| myUseRuntimeFiguresOption = (BooleanOption) addOption(OPTION_USE_RUNTIME_FIGURES, "Use Enhanced Figures", true, THE_ONLY_PAGE_INDEX); |
| myCachedInputValidationState = new InputValidationState(myOutputGalleryPathOption, myOutputDiagramElementsPathOption); |
| shouldWarnLiteVerstionDoesNotSupportMapMode = Platform.getBundle("org.eclipse.gmf.codegen.lite") != null; |
| } |
| |
| public void addPages(Wizard wizard) { |
| super.addPages(wizard); |
| WizardPage page = createPage(THE_ONLY_PAGE_INDEX); |
| page.setDescription("Converts an existing instance of the gmfgraph model into plugin code"); |
| page.setTitle("Figure definitions converter"); |
| wizard.addPage(page); |
| markPagesAdded(); |
| validateOptions(myPackageNameOption); |
| } |
| |
| public IPluginReference[] getDependencies(String schemaVersion) { |
| // no explicit dependencies |
| return new IPluginReference[0]; |
| } |
| |
| protected void generateFiles(IProgressMonitor monitor) throws CoreException { |
| Resource input = loadResource(myInputPathOption.getText()); |
| StandaloneGenerator.Config config = new StandaloneGeneratorConfigAdapter(this); |
| final ConverterOptions options = newConverterOptions(); |
| final ConverterOutcome converterOutcome = new ConverterOutcome(options, new Resource[] {input}); |
| assert converterOutcome.checkInputAgainstOptions().isOK(); |
| StandaloneGenerator generator = new StandaloneGenerator(converterOutcome.getProcessor(), config); |
| generator.setSkipPluginStructure(false); |
| try { |
| generator.run(new SubProgressMonitor(monitor, 1)); |
| readRequiredBundles(); |
| // XXX readBuildProperties() and use getNewFiles to propagate |
| // XXX readPluginProperties(), use ??? |
| if (!generator.getRunStatus().isOK()){ |
| throw new CoreException(generator.getRunStatus()); |
| } |
| IStatus s = converterOutcome.createResources(new ResourceSetImpl(), URI.createFileURI(myOutputGalleryPathOption.getText()), URI.createFileURI(myOutputDiagramElementsPathOption.getText())); |
| if (s.getSeverity() == IStatus.ERROR) { |
| throw new CoreException(s); |
| } |
| } catch (InterruptedException e) { |
| String message = e.getMessage(); |
| if (message == null){ |
| message = "Interrupted"; |
| } |
| throw new CoreException(new Status(IStatus.ERROR, MY_PLUGIN_ID, 0, message, e)); |
| } catch (IOException ex) { |
| // perhaps, don't need to treat this as error? |
| throw new CoreException(new Status(IStatus.ERROR, MY_PLUGIN_ID, 0, "Failed to read generated manifest.mf", ex)); |
| } finally { |
| input.unload(); |
| } |
| } |
| |
| private ConverterOptions newConverterOptions() { |
| final ConverterOptions options = new ConverterOptions(); |
| options.needMirroredGalleries = shouldGenerate(myOutputGalleryPathOption); |
| options.needMirroredCanvas = shouldGenerate(myOutputDiagramElementsPathOption); |
| options.separateMirrorFiles = options.needMirroredCanvas && myOutputGalleryPathOption.getText().equals(myOutputDiagramElementsPathOption.getText()); |
| return options; |
| } |
| |
| private static boolean shouldGenerate(FileNameOption option){ |
| return option.isEnabled() && !option.isEmpty(); |
| } |
| |
| private void readRequiredBundles() throws CoreException, IOException { |
| try { |
| IFile f = findGeneratedManifest(); |
| if (f == null || !f.exists()) { |
| // fail - we do expect manifest to be there? |
| return; |
| } |
| InputStream is = f.getContents(); |
| String requiredBundles = new Manifest(is).getMainAttributes().getValue(Constants.REQUIRE_BUNDLE); |
| is.close(); |
| myRequiredBundles = ManifestElement.parseHeader(Constants.REQUIRE_BUNDLE, requiredBundles); |
| } catch (BundleException ex) { |
| throw new IOException(ex.getMessage()); |
| } |
| } |
| |
| private IFile findGeneratedManifest() { |
| return (IFile) project.findMember(new Path("META-INF/MANIFEST.MF")); |
| } |
| |
| public String getPluginActivatorClassFQN(){ |
| return model instanceof IPluginModel ? ((IPluginModel)model).getPlugin().getClassName() : null; |
| } |
| |
| public String getPluginFriendlyName(){ |
| return model.getPluginBase().getName(); |
| } |
| |
| public String getPluginID(){ |
| return model.getPluginBase().getId(); |
| } |
| |
| public String getPluginProviderName() { |
| return model.getPluginBase().getProviderName(); |
| } |
| |
| protected URL getInstallURL() { |
| return getContributingBundle().getEntry("/"); |
| } |
| |
| public String getSectionId() { |
| return SECTION_ID; |
| } |
| |
| public void validateOptions(TemplateOption changed) { |
| if ((myUseRuntimeFiguresOption.equals(changed) || myNeedsMapModeOption.equals(changed)) && shouldWarnLiteVerstionDoesNotSupportMapMode) { |
| boolean useRuntimeFigures = myUseRuntimeFiguresOption.isSelected(); |
| boolean needsMapMode = myNeedsMapModeOption.isSelected(); |
| if (!useRuntimeFigures && needsMapMode) { |
| getTheOnlyPage().setMessage("It is not recommended to use IMapMode for pure-GEF diagram editors", IMessageProvider.INFORMATION); |
| } else { |
| getTheOnlyPage().setMessage(null); |
| } |
| } |
| if (OPTION_NEEDS_MAP_MODE.equals(changed)){ |
| //does not affect state |
| return; |
| } |
| if (validateInputPath() && validatePackageName() && |
| validateOutputOption(myOutputDiagramElementsPathOption) && |
| validateOutputOption(myOutputGalleryPathOption)){ |
| resetPageState(); |
| } |
| } |
| |
| public boolean isDependentOnParentWizard() { |
| return true; |
| } |
| |
| protected void initializeFields(IFieldData data) { |
| super.initializeFields(data); |
| String packageName = getFormattedPackageName(data.getId()); |
| initializeOption(OPTION_MAIN_PACKAGE_NAME, packageName); |
| } |
| |
| protected ResourceBundle getPluginResourceBundle() { |
| return Platform.getResourceBundle(getContributingBundle()); |
| } |
| |
| protected void updateModel(IProgressMonitor monitor) throws CoreException { |
| if (myRequiredBundles == null) { |
| return; |
| } |
| for (int i = 0; i < myRequiredBundles.length; i++) { |
| // take first component, ignore any attributes or directives |
| addImport(myRequiredBundles[i].getValueComponents()[0]); |
| } |
| } |
| |
| private void addImport(String importedPluginId) throws CoreException { |
| IPluginImport pluginImport = model.getPluginFactory().createImport(); |
| pluginImport.setId(importedPluginId); |
| model.getPluginBase().add(pluginImport); |
| } |
| |
| public String[] getNewFiles() { |
| return new String[0]; |
| } |
| |
| public String getUsedExtensionPoint() { |
| return null; |
| } |
| |
| private Bundle getContributingBundle(){ |
| return Platform.getBundle(MY_PLUGIN_ID); |
| } |
| |
| /** |
| * Stolen from PDETemplateSection, which can not be reused due to export limitations. |
| */ |
| private String getFormattedPackageName(String id){ |
| StringBuffer buffer = new StringBuffer(); |
| for (int i = 0; i < id.length(); i++) { |
| char ch = id.charAt(i); |
| if (buffer.length() == 0) { |
| if (Character.isJavaIdentifierStart(ch)) |
| buffer.append(Character.toLowerCase(ch)); |
| } else { |
| if (Character.isJavaIdentifierPart(ch) || ch == '.') |
| buffer.append(ch); |
| } |
| } |
| return buffer.toString().toLowerCase(Locale.ENGLISH); |
| } |
| |
| private FileNameOption addFileNameOption(boolean saveNotLoad, String name, String label, String value, int pageIndex) { |
| FileNameOption result = new FileNameOption(this, name, label, new String[] {"*.gmfgraph"}); |
| result.setSaveNotLoad(saveNotLoad); |
| registerOption(result, value, pageIndex); |
| return result; |
| } |
| |
| private boolean validatePackageName(){ |
| boolean isValid = !myPackageNameOption.isEmpty(); |
| if (!isValid){ |
| flagMissingRequiredOption(myPackageNameOption); |
| } |
| return isValid; |
| } |
| |
| private boolean validateInputPath() { |
| if (myInputPathOption.isEmpty()){ |
| flagMissingRequiredOption(myInputPathOption); |
| myOutputDiagramElementsPathOption.setEnabled(false); |
| myOutputGalleryPathOption.setEnabled(false); |
| return false; |
| } |
| String path = myInputPathOption.getText(); |
| myCachedInputValidationState.updateInput(path); |
| if (!myCachedInputValidationState.isValid()){ |
| flagError(myCachedInputValidationState.getErrorMessage()); |
| return false; |
| } |
| return true; |
| } |
| |
| private boolean validateOutputOption(FileNameOption option) { |
| if (!option.isEnabled()){ |
| return false; |
| } |
| if (!validateMirrorDiagramWithoutFigureGallery()){ |
| return false; |
| } |
| if (option.isEmpty()){ |
| //optional -- ok |
| return true; |
| } |
| String path = option.getText(); |
| return validatePath(path); |
| } |
| |
| private boolean validateMirrorDiagramWithoutFigureGallery(){ |
| if (!myOutputDiagramElementsPathOption.isEmpty()){ |
| if (myOutputGalleryPathOption.isEmpty() || myOutputDiagramElementsPathOption.getText().equals(myOutputGalleryPathOption.getText())){ |
| flagError("In order to mirror diagram elements you have to generate separate figure gallery"); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private boolean validatePath(String path){ |
| try { |
| return URI.createFileURI(path) != null; |
| } catch (IllegalArgumentException e){ |
| flagError(MessageFormat.format("Path {0} is invalid", new Object[] {path})); |
| return false; |
| } |
| } |
| |
| private WizardPage getTheOnlyPage() { |
| return getPage(THE_ONLY_PAGE_INDEX); |
| } |
| |
| private void flagError(String message){ |
| getTheOnlyPage().setPageComplete(false); |
| getTheOnlyPage().setErrorMessage(message); |
| } |
| |
| private static Resource loadResource(String path){ |
| Resource resource = new ResourceSetImpl().createResource(URI.createFileURI(path), ContentHandler.UNSPECIFIED_CONTENT_TYPE); |
| try { |
| resource.load(Collections.EMPTY_MAP); |
| return resource; |
| } catch (IOException e) { |
| return null; |
| } |
| } |
| |
| private static class InputValidationState { |
| private String myCachedPath; |
| private String myCachedErrorMessage; |
| private boolean myHasDiagramElement; |
| private boolean myHasFigure; |
| private final FileNameOption myDiagramElementsOption; |
| private final FileNameOption myGalleryOption; |
| |
| public InputValidationState(FileNameOption galleryOption, FileNameOption diagramElementsOption){ |
| myGalleryOption = galleryOption; |
| myDiagramElementsOption = diagramElementsOption; |
| } |
| |
| public void updateInput(String path){ |
| if (myCachedPath == null || !myCachedPath.equals(path)){ |
| myCachedPath = path; |
| validateInputPath(path); |
| myGalleryOption.setEnabled(myHasFigure); |
| myDiagramElementsOption.setEnabled(myHasDiagramElement); |
| } |
| } |
| |
| public boolean isValid(){ |
| return myHasFigure; |
| } |
| |
| public String getErrorMessage(){ |
| return myCachedErrorMessage; |
| } |
| |
| private void validateInputPath(String path) { |
| myHasDiagramElement = false; |
| myHasFigure = false; |
| myCachedErrorMessage = null; |
| |
| if (path == null || !new File(path).exists()){ |
| myCachedErrorMessage = MessageFormat.format("Can not find file {0}", new Object[] {path}); |
| return; |
| } |
| |
| Resource resource = loadResource(path); |
| if (resource != null){ |
| classifyContents(resource); |
| } |
| |
| if (!myHasFigure){ |
| myCachedErrorMessage = MessageFormat.format("File {0} does not contain any figure definitions", new Object[] {path}); |
| } |
| } |
| |
| private void classifyContents(Resource resource){ |
| myHasDiagramElement = false; |
| myHasFigure = false; |
| for (TreeIterator<EObject> contents = resource.getAllContents(); contents.hasNext();){ |
| EObject next = contents.next(); |
| if (next instanceof FigureGallery){ |
| if (!myHasFigure){ |
| FigureGallery nextGallery = (FigureGallery) next; |
| myHasFigure = !nextGallery.getFigures().isEmpty(); |
| } |
| contents.prune(); |
| } |
| if (next instanceof DiagramElement){ |
| myHasDiagramElement = true; |
| contents.prune(); |
| } |
| if (myHasDiagramElement && myHasFigure){ |
| break; |
| } |
| } |
| } |
| |
| } |
| } |