| /** |
| * Copyright (c) 2006 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v20.html |
| * |
| * Contributors: |
| * IBM - Initial API and implementation |
| */ |
| package org.eclipse.emf.codegen.ecore.generator; |
| |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.emf.codegen.ecore.CodeGenEcorePlugin; |
| import org.eclipse.emf.codegen.ecore.genmodel.GenModel; |
| import org.eclipse.emf.codegen.ecore.genmodel.util.GenModelUtil; |
| import org.eclipse.emf.codegen.merge.java.JControlModel; |
| import org.eclipse.emf.codegen.merge.java.facade.FacadeHelper; |
| import org.eclipse.emf.codegen.util.CodeGenUtil; |
| import org.eclipse.emf.common.CommonPlugin; |
| import org.eclipse.emf.common.notify.AdapterFactory; |
| import org.eclipse.emf.common.util.BasicDiagnostic; |
| import org.eclipse.emf.common.util.Diagnostic; |
| import org.eclipse.emf.common.util.Monitor; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.plugin.EcorePlugin; |
| import org.eclipse.emf.ecore.resource.ResourceSet; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants; |
| |
| |
| /** |
| * An extensible code generator that delegates its work to model-specific {@link GeneratorAdapter adapters} created |
| * provided by an {@link GeneratorAdapterFactory adapter factory}. |
| * |
| * <p>Adapter factories are obtained for model objects from a particular EMF or Java package by consulting a |
| * {@link GeneratorAdapterFactory.Descriptor.Registry registry}, which can be local to the generator or globally |
| * shared. Local registries typically {@link GeneratorAdapterFactory.Descriptor.DelegatingRegistry delegate} to the |
| * global registry, overriding the adapter factories returned for certain packages. Global registration is usually |
| * done via the <code>org.eclipse.emf.codegen.ecore.generatorAdapters</code> extension point. |
| * |
| * <p>Here is a typical usage example, where we generate code for a standard GenModel-decorated Ecore model: |
| * |
| * <pre> |
| * // Globally register the default generator adapter factory for GenModel |
| * // elements (only needed in stand-alone). |
| * // |
| * GeneratorAdapterFactory.Descriptor.Registry.INSTANCE.addDescriptor |
| * (GenModelPackage.eNS_URI, GenModelGeneratorAdapterFactory.DESCRIPTOR); |
| * |
| * // Create the generator and set the model-level input object. |
| * // |
| * Generator generator = new Generator(); |
| * generator.setInput(genModel); |
| * |
| * // Generator model code. |
| * // |
| * generator.generate |
| * (genModel, GenBaseGeneratorAdapter.MODEL_PROJECT_TYPE, |
| * new BasicMonitor.Printing(System.out)); |
| * </pre> |
| * |
| * <p> |
| * The adapter factories for the input object handle initializing the generator's {@link #getOptions() options}, and the |
| * generator walks the tree of objects defined by the relevant adapters, invoking code generation for each one. |
| * |
| * @since 2.2.0 |
| */ |
| public class Generator |
| { |
| private static final boolean SYSOUT_BEGIN_END = false; |
| |
| /** |
| * A set of code generation options that should be shared among the generator, adapter factories and adapters. |
| * Additional options may be added to this class in the future. |
| */ |
| public static class Options |
| { |
| /** |
| * A filename redirection pattern for generated files. |
| */ |
| public String redirectionPattern; |
| |
| /** |
| * Whether to overwrite read-only files. |
| */ |
| public boolean forceOverwrite; |
| |
| /** |
| * Whether to try to use dynamically compiled templates, in place of supplied static templates. |
| */ |
| public boolean dynamicTemplates; |
| |
| /** |
| * A path for dynamic templates: ordered list of URIs corresponding to base locations under which to find templates. |
| * A single path for all code generation is actually insufficient. This path needs to specified and extended on a |
| * per-adapter basis, so this field should not be used. |
| * @deprecated org.eclipse.emf.codegen.ecore 2.2.2 Override {@link AbstractGeneratorAdapter#addBaseTemplatePathEntries(List)} instead. |
| * @see AbstractGeneratorAdapter#addBaseTemplatePathEntries(List) |
| */ |
| @Deprecated |
| public String[] templatePath; |
| |
| /** |
| * The name of the {@link org.eclipse.emf.codegen.merge.java.facade.FacadeHelper facade helper} class to be used in |
| * Java merging. |
| * @see org.eclipse.emf.codegen.merge.java.facade.FacadeHelper |
| */ |
| public String mergerFacadeHelperClass; |
| |
| /** |
| * The URI of the JMerge rules file. |
| */ |
| public String mergeRulesURI; |
| |
| /** |
| * Whether Eclipse JDT code formatting should be applied to generated Java code. |
| */ |
| public boolean codeFormatting; |
| |
| /** |
| * Whether Eclipse JDT code formatting should be applied to comments. |
| * @since 2.8 |
| */ |
| public boolean commentFormatting; |
| |
| /** |
| * Whether Eclipse JDT unused import checking should be used to remove unused imports from generated Java code. |
| * @since 2.9 |
| */ |
| public boolean importOrganizing; |
| |
| /** |
| * Whether registered cleanup actions should be applied to the generated source code. |
| * @since 2.10 |
| */ |
| public boolean cleanup; |
| |
| /** |
| * Code formatter options to be used instead of the defaults for Java code formatting. |
| */ |
| public Map<?, ?> codeFormatterOptions; |
| |
| /** |
| * The resource set containing the input, from which a URI converter, package registry, resource factory registry, |
| * etc can be obtained. |
| */ |
| public ResourceSet resourceSet; |
| |
| /** |
| * A list of strings of the form [<variable-name>=]<plugin-id> |
| * that will be used by {@link AbstractGeneratorAdapter#addClasspathEntries(org.eclipse.emf.codegen.jet.JETEmitter)} |
| * to add classpath entries to each JETEmitter. |
| * @see AbstractGeneratorAdapter#addClasspathEntries(org.eclipse.emf.codegen.jet.JETEmitter) |
| */ |
| public List<String> templateClasspath; |
| |
| /** |
| * Arbitrary data for extensibility. |
| */ |
| public Object[] data; |
| |
| public Options() |
| { |
| super(); |
| } |
| } |
| |
| /** |
| * The local registry from which generator adapter factories are created. |
| */ |
| protected GeneratorAdapterFactory.Descriptor.Registry adapterFactoryDescriptorRegistry; |
| |
| /** |
| * The cached set of generator adapter factories for this generator, keyed by package ID. |
| */ |
| protected Map<String, Collection<GeneratorAdapterFactory>> packageIDToAdapterFactories; |
| |
| protected Object input; |
| protected Options options; |
| |
| /** |
| * Whether the adapter factories for the input need to be called to initialize before generating code. |
| * |
| * @see #requestInitialize() |
| */ |
| protected boolean initializeNeeded = true; |
| |
| protected JControlModel jControlModel; |
| |
| /** |
| * @since 2.10 |
| */ |
| protected Set<URI> generatedOutputs = new LinkedHashSet<URI>(); |
| |
| /** |
| * Creates a generator that delegates directly to the {@link GeneratorAdapterFactory.Descriptor.Registry#INSTANCE global} |
| * adapter factory descriptor registry. |
| * |
| * @see GeneratorAdapterFactory.Descriptor.Registry#INSTANCE |
| */ |
| public Generator() |
| { |
| super(); |
| } |
| |
| /** |
| * Creates a generator that delegates to the given adapter factory descriptor registry. |
| */ |
| public Generator(GeneratorAdapterFactory.Descriptor.Registry adapterFactoryDescriptorRegistry) |
| { |
| this.adapterFactoryDescriptorRegistry = adapterFactoryDescriptorRegistry; |
| } |
| |
| /** |
| * Returns the current model-level input object. |
| * |
| * @see #setInput(Object) |
| */ |
| public Object getInput() |
| { |
| return input; |
| } |
| |
| /** |
| * Sets the model-level input object, and invokes {@link GeneratorAdapterFactory#initialize(Object) initialize(Object)} |
| * on any of its adapter factories. This initialization will also be repeated in the next |
| * {@link #generate(Object, Object, Monitor)} or {@link #generate(Object, Object, String, Monitor)}, when the full |
| * tree of objects and adapters has been discovered. |
| * |
| * @see #getInput() |
| * @see #generate(Object, Object, Monitor) |
| * @see #generate(Object, Object, String, Monitor) |
| * @see GeneratorAdapterFactory#initialize(Object) |
| */ |
| public void setInput(Object input) |
| { |
| this.input = input; |
| initialize(); |
| initializeNeeded = true; |
| } |
| |
| /** |
| * Signals that {@link GeneratorAdapterFactory#initialize(Object) initialize(object)} should be called on any adapter |
| * factories for the {@link #getInput() input} object during the next {@link #generate(Object, Object, Monitor)} or |
| * {@link #generate(Object, Object, String, Monitor)}, when the full tree of objects and adapters has been discovered. |
| * |
| * @see #getInput() |
| * @see #generate(Object, Object, Monitor) |
| * @see #generate(Object, Object, String, Monitor) |
| * @see GeneratorAdapterFactory#initialize(Object) |
| */ |
| public void requestInitialize() |
| { |
| initializeNeeded = true; |
| } |
| |
| /** |
| * Invokes {@link GeneratorAdapterFactory#initialize(Object) initialize(object)} on each adapter factory for the |
| * {@link #getInput() input} object. Note that adapter factory order is non-deterministic, so if more than |
| * one tries to set the same options, the result is undefined. |
| * |
| * @see #getInput() |
| * @see GeneratorAdapterFactory#initialize(Object) |
| */ |
| protected void initialize() |
| { |
| for (GeneratorAdapterFactory adapterFactory : getAdapterFactories(input)) |
| { |
| adapterFactory.initialize(input); |
| } |
| } |
| |
| /** |
| * Returns the {@link Options} for this generator. Note that these are accessible to (and can be set by) all of the |
| * adapter factories and adapters supporting this generator and by clients of the generator itself. |
| */ |
| public Options getOptions() |
| { |
| if (options == null) |
| { |
| options = new Options(); |
| } |
| return options; |
| } |
| |
| protected Set<String> badFacadeHelperClasses = new HashSet<String>(); |
| |
| /** |
| * Returns a {@link org.eclipse.emf.codegen.merge.java.JControlModel JControlModel} that the generator's adapters can |
| * use for merging. It is initialized with the {@link Options#mergerFacadeHelperClass facade helper class} and |
| * {@link Options#mergeRulesURI merge rules URI} specified in the {@link #getOptions() options}. |
| * |
| * @see #getOptions() |
| * @see Options#mergerFacadeHelperClass |
| * @see Options#mergeRulesURI |
| */ |
| public JControlModel getJControlModel() |
| { |
| if (jControlModel == null) |
| { |
| jControlModel = new JControlModel(); |
| } |
| |
| String facadeHelperClass = options.mergerFacadeHelperClass; |
| if (!badFacadeHelperClasses.contains(facadeHelperClass) && |
| (jControlModel.getFacadeHelper() == null || !jControlModel.getFacadeHelper().getClass().getName().equals(facadeHelperClass))) |
| { |
| FacadeHelper facadeHelper = CodeGenUtil.instantiateFacadeHelper(facadeHelperClass); |
| if (facadeHelper == null) |
| { |
| badFacadeHelperClasses.add(facadeHelperClass); |
| } |
| else |
| { |
| if (input instanceof GenModel) |
| { |
| GenModel genModel = (GenModel)input; |
| switch (genModel.getComplianceLevel()) |
| { |
| case JDK14_LITERAL: |
| { |
| facadeHelper.setCompilerCompliance(JavaCore.VERSION_1_4); |
| break; |
| } |
| case JDK50_LITERAL: |
| { |
| facadeHelper.setCompilerCompliance(JavaCore.VERSION_1_5); |
| break; |
| } |
| case JDK60_LITERAL: |
| { |
| facadeHelper.setCompilerCompliance(JavaCore.VERSION_1_6); |
| break; |
| } |
| case JDK70_LITERAL: |
| { |
| facadeHelper.setCompilerCompliance(JavaCore.VERSION_1_7); |
| break; |
| } |
| case JDK80_LITERAL: |
| { |
| facadeHelper.setCompilerCompliance("1.8"); |
| break; |
| } |
| case JDK90_LITERAL: |
| { |
| facadeHelper.setCompilerCompliance("9"); |
| break; |
| } |
| case JDK100_LITERAL: |
| { |
| facadeHelper.setCompilerCompliance("10"); |
| break; |
| } |
| case JDK110_LITERAL: |
| { |
| facadeHelper.setCompilerCompliance("11"); |
| break; |
| } |
| case JDK120_LITERAL: |
| { |
| facadeHelper.setCompilerCompliance("12"); |
| break; |
| } |
| case JDK130_LITERAL: |
| { |
| facadeHelper.setCompilerCompliance("13"); |
| break; |
| } |
| case JDK140_LITERAL: |
| { |
| facadeHelper.setCompilerCompliance("14"); |
| break; |
| } |
| case JDK150_LITERAL: |
| { |
| facadeHelper.setCompilerCompliance("15"); |
| break; |
| } |
| case JDK160_LITERAL: |
| { |
| facadeHelper.setCompilerCompliance("16"); |
| break; |
| } |
| } |
| |
| if (genModel.isCodeFormatting()) |
| { |
| jControlModel.setLeadingTabReplacement(null); |
| jControlModel.setConvertToStandardBraceStyle(false); |
| } |
| else |
| { |
| Map<String, String> options = GenModelUtil.getJavaOptions(genModel); |
| String tabSize = options.get(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE); |
| String braceStyle = options.get(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_TYPE_DECLARATION); |
| String tabCharacter = options.get(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR); |
| if (JavaCore.TAB.equals(tabCharacter)) |
| { |
| jControlModel.setLeadingTabReplacement("\t"); |
| } |
| else |
| { |
| String spaces = ""; |
| for (int i = Integer.parseInt(tabSize); i > 0; --i) |
| { |
| spaces += " "; |
| } |
| jControlModel.setLeadingTabReplacement(spaces); |
| } |
| jControlModel.setConvertToStandardBraceStyle(DefaultCodeFormatterConstants.END_OF_LINE.equals(braceStyle)); |
| } |
| } |
| } |
| jControlModel.initialize(facadeHelper, options.mergeRulesURI); |
| } |
| return jControlModel; |
| } |
| |
| /** |
| * Returns the generator's adapter factory descriptor registry. |
| */ |
| public GeneratorAdapterFactory.Descriptor.Registry getAdapterFactoryDescriptorRegistry() |
| { |
| if (adapterFactoryDescriptorRegistry == null) |
| { |
| adapterFactoryDescriptorRegistry = |
| new GeneratorAdapterFactory.Descriptor.DelegatingRegistry(GeneratorAdapterFactory.Descriptor.Registry.INSTANCE); |
| } |
| return adapterFactoryDescriptorRegistry; |
| } |
| |
| /** |
| * Returns the generator adapter factories for a given object. The {@link #getAdapterFactoryDescriptorRegistry() registry} |
| * for the generator is used to obtain the {@link GeneratorAdapterFactory.Descriptor descriptors} for the object's |
| * {@link #getPackageID(Object) package ID}, and those descriptors are used to create the adapter factories, which are |
| * cached. |
| * |
| * @see #getAdapterFactoryDescriptorRegistry() |
| * @see #getPackageID(Object) |
| * @see GeneratorAdapterFactory.Descriptor |
| */ |
| protected Collection<GeneratorAdapterFactory> getAdapterFactories(Object object) |
| { |
| if (packageIDToAdapterFactories == null) |
| { |
| packageIDToAdapterFactories = new HashMap<String, Collection<GeneratorAdapterFactory>>(); |
| } |
| |
| String packageID = getPackageID(object); |
| Collection<GeneratorAdapterFactory> result = packageIDToAdapterFactories.get(packageID); |
| if (result == null) |
| { |
| Collection<GeneratorAdapterFactory.Descriptor> descriptors = getAdapterFactoryDescriptorRegistry().getDescriptors(packageID); |
| result = new ArrayList<GeneratorAdapterFactory>(descriptors.size()); |
| for (GeneratorAdapterFactory.Descriptor descriptor : descriptors) |
| { |
| GeneratorAdapterFactory adapterFactory = descriptor.createAdapterFactory(); |
| adapterFactory.setGenerator(this); |
| result.add(adapterFactory); |
| } |
| packageIDToAdapterFactories.put(packageID, result); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns the package ID for the given object. |
| * |
| * <p>This implementation returns the {@link org.eclipse.emf.ecore.EPackage#getNsURI() EPackage nsURI} for an |
| * {@link org.eclipse.emf.ecore.EObject}, and the Java {@link java.lang.Package#getName() package name} for any other |
| * object. |
| * |
| * @see org.eclipse.emf.ecore.EPackage#getNsURI() |
| * @see java.lang.Package#getName() |
| */ |
| protected String getPackageID(Object object) |
| { |
| return object instanceof EObject ? ((EObject)object).eClass().getEPackage().getNsURI() : object.getClass().getPackage().getName(); |
| } |
| |
| /** |
| * Returns the generator adapters for the given object. The adapter factories are obtained from |
| * {@link #getAdapterFactories(Object)}, and each may provide one adapter of type |
| * {@link GeneratorAdapter GeneratorAdapter.class}. |
| * |
| * @see #getAdapterFactories(Object) |
| * @see GeneratorAdapter |
| */ |
| protected Collection<GeneratorAdapter> getAdapters(Object object) |
| { |
| Collection<GeneratorAdapterFactory> adapterFactories = getAdapterFactories(object); |
| List<GeneratorAdapter> result = new ArrayList<GeneratorAdapter>(adapterFactories.size()); |
| |
| for (AdapterFactory adapterFactory : adapterFactories) |
| { |
| if (adapterFactory.isFactoryForType(GeneratorAdapter.class)) |
| { |
| Object adapter = adapterFactory.adapt(object, GeneratorAdapter.class); |
| if (adapter != null) |
| { |
| result.add((GeneratorAdapter)adapter); |
| } |
| } |
| } |
| return result; |
| } |
| |
| private static class GeneratorData |
| { |
| public Object object; |
| public GeneratorAdapter adapter; |
| |
| public GeneratorData(Object object, GeneratorAdapter adapter) |
| { |
| this.object = object; |
| this.adapter = adapter; |
| } |
| } |
| |
| private GeneratorData[] getGeneratorData(Object object, Object projectType, boolean forGenerate) |
| { |
| // Since we're invoking plugged-in code, we must be defensive against cycles. |
| // |
| Set<Object> objects = new HashSet<Object>(); |
| |
| // Compute the GeneratorData for the given object and its children, then for the parents of the given object. |
| // |
| |
| List<GeneratorData> childrenData = getGeneratorData(object, projectType, forGenerate, true, false, objects); |
| List<GeneratorData> parentsData = getGeneratorData(object, projectType, forGenerate, false, true, objects); |
| |
| // Combine the two lists. |
| // |
| List<GeneratorData> result = new ArrayList<GeneratorData>(parentsData.size() + childrenData.size()); |
| Collections.reverse(parentsData); |
| result.addAll(parentsData); |
| result.addAll(childrenData); |
| return result.toArray(new GeneratorData[result.size()]); |
| } |
| |
| private List<GeneratorData> getGeneratorData(Object object, Object projectType, boolean forGenerate, boolean forChildren, boolean skipFirst, Set<Object> objects) |
| { |
| List<Object> result = new ArrayList<Object>(); |
| result.add(object); |
| |
| for (int i = 0; i < result.size(); skipFirst = false) |
| { |
| Object o = result.get(i); |
| |
| Collection<GeneratorAdapter> adapters = getAdapters(o); |
| result.remove(i); |
| if (!adapters.isEmpty()) |
| { |
| for (GeneratorAdapter adapter : adapters) |
| { |
| if (forChildren) |
| { |
| Collection<?> children = forGenerate ? adapter.getGenerateChildren(o, projectType) : adapter.getCanGenerateChildren(o, projectType); |
| for (Object child : children) |
| { |
| if (objects.add(child)) |
| { |
| result.add(child); |
| } |
| } |
| } |
| else |
| { |
| Object parent = forGenerate ? adapter.getGenerateParent(o, projectType) : adapter.getCanGenerateParent(o, projectType); |
| if (parent != null && objects.add(parent)) |
| { |
| result.add(parent); |
| } |
| } |
| |
| if (!skipFirst) |
| { |
| result.add(i++, new GeneratorData(o, adapter)); |
| } |
| } |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| List<GeneratorData> list = (List<GeneratorData>)(List<?>)result; |
| return list; |
| } |
| |
| /** |
| * Returns whether code can be generated for the given object and project type. |
| * A project type is represented by an arbitrary object that is meaningful to the generator adapters for the relevant |
| * objects. |
| * |
| * <p>This result is obtained as follows: |
| * |
| * <ol> |
| * <li>The {@link GeneratorAdapter adapters} for the object are obtained. |
| * <li>A complete collection of objects to be considered is formed by iteratively invoking the |
| * {@link GeneratorAdapter#getCanGenerateParent(Object, Object) getCanGenerateParent(Object, Object)} and |
| * {@link GeneratorAdapter#getCanGenerateChildren(Object, Object) getCanGenerateChildren(Object, Object)} methods |
| * on the adapters for the object, the adapters for the object's parent and children, and so on. It is the |
| * adapters' responsibility to determine the relevant objects through their implementations of these methods. |
| * <li>The {@link GeneratorAdapter#canGenerate(Object, Object) canGenerate(Object, Object)} method is invoked on all |
| * the adapters for every object in the set formed in step 2. If any adapter returns <code>true</code> for any |
| * object, this method returns <code>true</code>. |
| * <li>Otherwise, <code>false</code> is returned. |
| * </ol> |
| * |
| * @see GeneratorAdapter#getCanGenerateParent(Object, Object) |
| * @see GeneratorAdapter#getCanGenerateChildren(Object, Object) |
| * @see GeneratorAdapter#canGenerate(Object, Object) |
| */ |
| public boolean canGenerate(Object object, Object projectType) |
| { |
| GeneratorData[] data = getGeneratorData(object, projectType, false); |
| for (int i = 0; i < data.length; i++) |
| { |
| if (data[i].adapter.canGenerate(data[i].object, projectType)) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Performs code generation for the given object and project type. A project type is represented by an arbitrary object |
| * that is meaningful to the generator adapters for the relevant objects. Since this is a long-running operation, it |
| * reports progress using the given <code>Monitor</code>. Its final status is returned as a <code>Diagnostic</code>. |
| * |
| * <p>This method operates exactly as {@link #generate(Object, Object, String, Monitor)} does when null is specified |
| * as the <code>projectTypeName</code>. |
| * |
| * <p>It is not necessary to call {@link #canGenerate(Object, Object)} before this method. If that method would |
| * return <code>false</code>, this method should generate nothing when invoked with the same arguments. |
| * |
| * @see #generate(Object, Object, String, Monitor) |
| * @see #canGenerate(Object, Object) |
| */ |
| public Diagnostic generate(Object object, Object projectType, Monitor monitor) |
| { |
| return generate(object, projectType, null, monitor); |
| } |
| |
| /** |
| * Performs code generation for the given object and project type. A project type is represented by an arbitrary object |
| * that is meaningful to the generator adapters for the relevant objects. Since this is a long-running operation, it |
| * reports progress using the given <code>Monitor</code>. Its final status is returned as a <code>Diagnostic</code>. |
| * The <code>projectTypeName</code>, if non-null, is used only to provide to the <code>Monitor</code> a more specific |
| * message for the task. |
| * |
| * <p>It is not necessary to call {@link #canGenerate(Object, Object)} before this method. If that method would |
| * return <code>false</code>, this method should generate nothing when invoked with the same arguments. |
| * |
| * <p>Code generation is performed as follows: |
| * |
| * <ol> |
| * <li>The {@link GeneratorAdapter adapters} for the object are obtained. |
| * <li>A complete collection of objects to be considered is formed by iteratively invoking the |
| * {@link GeneratorAdapter#getGenerateParent(Object, Object) getGenerateParent(Object, Object)} and |
| * {@link GeneratorAdapter#getGenerateChildren(Object, Object) getGenerateChildren(Object, Object)} methods on the |
| * adapters for the object, the adapters for the object's parent and children, and so on. It is the adapters' |
| * responsibility to determine the relevant objects through their implementations of these methods. |
| * <li>If this is the first invocation of {@link #generate(Object, Object, String, Monitor)} or initialization has |
| * been {@link #requestInitialize() requested}, {@link GeneratorAdapterFactory#initialize(Object) initialize(Object)} |
| * is invoked on each adapter factory for the {@link #getInput() input} object. |
| * <li>The {@link GeneratorAdapter#preGenerate(Object, Object) preGenerate(Object, Object)} method is invoked on all |
| * the adapters for every object in the set formed in step 2, giving the adapters a chance to perform setup |
| * before any code is generated. |
| * <li>The {@link GeneratorAdapter#generate(Object, Object, Monitor) generate(Object, Object, Monitor)} method is |
| * invoked on all the adapters for every object in the set formed in step 2. This is where code generation |
| * actually occurs. |
| * <li>The {@link GeneratorAdapter#postGenerate(Object, Object) postGenerate(Object, Object)} method is invoked on all |
| * the adapters for every object in the set formed in step 2, giving adapters a chance to clean up from code |
| * generation. |
| * </ol> |
| * |
| * <p>The operation may be canceled during step 4 or 5, either based on the <code>Monitor</code> or the |
| * <code>Diagnostic</code> returned by any generator adapter invocation. By default, only a <code>CANCEL</code> |
| * {@link org.eclipse.emf.common.util.Diagnostic#getSeverity severity} will cause code generation to stop; however, |
| * this can be customized by overriding {@link #canContinue(Diagnostic) canContinue(Diagnostic)}. Even if code |
| * generation is canceled, {@link GeneratorAdapter#postGenerate(Object, Object) postGenerate(Object, Object)} will |
| * still be called on all the adapters on which {@link GeneratorAdapter#preGenerate(Object, Object) preGenerate(Object, Object)} |
| * has been called. |
| * |
| * @see #canGenerate(Object, Object) |
| * @see #requestInitialize() |
| * @see GeneratorAdapter#getGenerateParent(Object, Object) |
| * @see GeneratorAdapter#getGenerateChildren(Object, Object) |
| * @see GeneratorAdapter#preGenerate(Object, Object) |
| * @see GeneratorAdapter#generate(Object, Object, Monitor) |
| * @see GeneratorAdapter#postGenerate(Object, Object) |
| * @see GeneratorAdapterFactory#initialize(Object) |
| */ |
| public Diagnostic generate(Object object, Object projectType, String projectTypeName, Monitor monitor) |
| { |
| if (SYSOUT_BEGIN_END) System.out.println("******* Begin: " + new java.util.Date()); |
| try |
| { |
| String message = projectTypeName != null ? |
| CodeGenEcorePlugin.INSTANCE.getString("_UI_Generating_message", new Object[] { projectTypeName }) : |
| CodeGenEcorePlugin.INSTANCE.getString("_UI_GeneratingCode_message"); |
| BasicDiagnostic result = new BasicDiagnostic(CodeGenEcorePlugin.ID, 0, message, null); |
| |
| GeneratorData[] data = getGeneratorData(object, projectType, true); |
| monitor.beginTask("", data.length + 2); |
| monitor.subTask(message); |
| |
| // Initialization is deferred until adapters are attached to all the objects of interest and we're |
| // about to ask them to generate. |
| // |
| if (initializeNeeded) |
| { |
| initializeNeeded = false; |
| initialize(); |
| } |
| |
| // Give all generator adapters the chance to do setup work. |
| // |
| int preIndex = 0; |
| for (; preIndex < data.length && canContinue(result); preIndex++) |
| { |
| result.add(data[preIndex].adapter.preGenerate(data[preIndex].object, projectType)); |
| } |
| monitor.worked(1); |
| |
| // Invoke generator adapters for each object. |
| // |
| for (int i = 0; i < data.length && canContinue(result); i++) |
| { |
| result.add(data[i].adapter.generate(data[i].object, projectType, CodeGenUtil.createMonitor(monitor, 1))); |
| if (monitor.isCanceled()) |
| { |
| result.add(Diagnostic.CANCEL_INSTANCE); |
| } |
| } |
| |
| // Give all generator adapters the chance to do tear down. |
| // |
| for (int i = 0; i < preIndex; i++) |
| { |
| result.add(data[i].adapter.postGenerate(data[i].object, projectType)); |
| } |
| |
| // Optionally invoke any source cleanup actions. |
| // This is only possible if JDT and JDT UI are available. |
| // |
| if (getOptions().cleanup && CommonPlugin.IS_RESOURCES_BUNDLE_AVAILABLE && !generatedOutputs.isEmpty() && jControlModel != null && jControlModel.getFacadeHelper() != null) |
| { |
| EclipseHelper.sourceCleanup(generatedOutputs); |
| } |
| |
| return result; |
| } |
| finally |
| { |
| monitor.done(); |
| if (SYSOUT_BEGIN_END) System.out.println("******* End: " + new java.util.Date()); |
| } |
| } |
| |
| /** |
| * Clients are not expect to implement this interface. |
| * It can only be implemented if the JDT UI is available, because source cleanup actions are implemented there. |
| * @since 2.10 |
| */ |
| public interface CleanupScheduler |
| { |
| void schedule(Set<ICompilationUnit> compilationUnits); |
| } |
| |
| private static class EclipseHelper |
| { |
| private static final CleanupScheduler SCHEDULER; |
| static |
| { |
| CleanupScheduler cleanupScheduler = null; |
| try |
| { |
| Class<?> generatorUIUtilClass = CommonPlugin.loadClass("org.eclipse.emf.codegen.ecore.ui", "org.eclipse.emf.codegen.ecore.genmodel.presentation.GeneratorUIUtil"); |
| cleanupScheduler = (CleanupScheduler)generatorUIUtilClass.getField("CLEANUP_SCHEDULER").get(null); |
| } |
| catch (Exception exception) |
| { |
| // Ignore |
| } |
| |
| SCHEDULER = cleanupScheduler; |
| } |
| |
| public static void sourceCleanup(final Set<URI> generatedOutputs) |
| { |
| if (SCHEDULER != null) |
| { |
| IWorkspaceRoot workspaceRoot = EcorePlugin.getWorkspaceRoot(); |
| if (workspaceRoot != null) |
| { |
| Set<ICompilationUnit> compilationUnits = new LinkedHashSet<ICompilationUnit>(); |
| for (URI generatedOutput : generatedOutputs) |
| { |
| if ("java".equals(generatedOutput.fileExtension())) |
| { |
| IFile file = workspaceRoot.getFile(new Path(generatedOutput.toString())); |
| ICompilationUnit compilationUnit = JavaCore.createCompilationUnitFrom(file); |
| if (compilationUnit.getJavaProject().isOnClasspath(compilationUnit)) |
| { |
| compilationUnits.add(compilationUnit); |
| } |
| } |
| } |
| |
| SCHEDULER.schedule(compilationUnits); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Determines whether code generation can continue, based on the given <code>Diagnostic</code>. This implementation |
| * returns true unless the {@link org.eclipse.emf.common.util.Diagnostic#getSeverity severity} is |
| * <code>CANCEL</code>. |
| * |
| * @see org.eclipse.emf.common.util.Diagnostic#getSeverity() |
| */ |
| protected boolean canContinue(Diagnostic diagnostic) |
| { |
| return diagnostic.getSeverity() != Diagnostic.CANCEL; |
| } |
| |
| /** |
| * A {@link AbstractGeneratorAdapter#createOutputStream(URI) callback} indicating that output was generated for the given workspace path. |
| * @see #getGeneratedOutputs() |
| * @see AbstractGeneratorAdapter#createOutputStream(URI) |
| * @since 2.10 |
| */ |
| public void generatedOutput(URI workspacePath) |
| { |
| generatedOutputs.add(workspacePath); |
| } |
| |
| /** |
| * Returns the workspace paths for which outputs have been {@link AbstractGeneratorAdapter#createOutputStream(URI) generated}. |
| * @since 2.10 |
| * @see #generatedOutput(URI) |
| * @see AbstractGeneratorAdapter#createOutputStream(URI) |
| */ |
| public Set<URI> getGeneratedOutputs() |
| { |
| return generatedOutputs; |
| } |
| |
| /** |
| * Disposes all of the generator's adapter factories, by calling {@link GeneratorAdapterFactory#dispose() dispose()} |
| * on each. |
| * |
| * @see GeneratorAdapterFactory#dispose() |
| */ |
| public void dispose() |
| { |
| if (packageIDToAdapterFactories != null) |
| { |
| for (Collection<GeneratorAdapterFactory> adapterFactories : packageIDToAdapterFactories.values()) |
| { |
| for (GeneratorAdapterFactory adapterFactory : adapterFactories) |
| { |
| adapterFactory.dispose(); |
| } |
| } |
| } |
| } |
| } |