| /** |
| * Copyright (c) 2002-2007 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.importer.java.builder; |
| |
| import java.io.BufferedInputStream; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| import java.util.TreeSet; |
| import java.util.jar.Manifest; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.Constants; |
| |
| import org.eclipse.core.resources.IContainer; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.jdt.core.Flags; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IPackageFragmentRoot; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.osgi.util.ManifestElement; |
| |
| import org.eclipse.emf.codegen.ecore.CodeGenEcorePlugin; |
| import org.eclipse.emf.codegen.ecore.genmodel.GenModel; |
| import org.eclipse.emf.codegen.ecore.genmodel.GenPackage; |
| import org.eclipse.emf.codegen.merge.java.JMerger; |
| import org.eclipse.emf.codegen.merge.java.facade.FacadeHelper; |
| import org.eclipse.emf.codegen.merge.java.facade.JAbstractType; |
| import org.eclipse.emf.codegen.merge.java.facade.JAnnotationType; |
| import org.eclipse.emf.codegen.merge.java.facade.JCompilationUnit; |
| import org.eclipse.emf.codegen.merge.java.facade.JEnum; |
| import org.eclipse.emf.codegen.merge.java.facade.JEnumConstant; |
| import org.eclipse.emf.codegen.merge.java.facade.JField; |
| import org.eclipse.emf.codegen.merge.java.facade.JImport; |
| import org.eclipse.emf.codegen.merge.java.facade.JMember; |
| import org.eclipse.emf.codegen.merge.java.facade.JMethod; |
| import org.eclipse.emf.codegen.merge.java.facade.JNode; |
| import org.eclipse.emf.codegen.merge.java.facade.JPackage; |
| import org.eclipse.emf.codegen.merge.java.facade.JType; |
| import org.eclipse.emf.codegen.util.CodeGenUtil; |
| import org.eclipse.emf.common.util.BasicDiagnostic; |
| import org.eclipse.emf.common.util.BasicMonitor; |
| import org.eclipse.emf.common.util.Diagnostic; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.common.util.EMap; |
| import org.eclipse.emf.common.util.Monitor; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.common.util.UniqueEList; |
| import org.eclipse.emf.ecore.EAnnotation; |
| import org.eclipse.emf.ecore.EAttribute; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EClassifier; |
| import org.eclipse.emf.ecore.EDataType; |
| import org.eclipse.emf.ecore.EEnum; |
| import org.eclipse.emf.ecore.EEnumLiteral; |
| import org.eclipse.emf.ecore.EGenericType; |
| import org.eclipse.emf.ecore.EModelElement; |
| import org.eclipse.emf.ecore.ENamedElement; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EOperation; |
| import org.eclipse.emf.ecore.EPackage; |
| import org.eclipse.emf.ecore.EParameter; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.ETypeParameter; |
| import org.eclipse.emf.ecore.ETypedElement; |
| import org.eclipse.emf.ecore.EcoreFactory; |
| import org.eclipse.emf.ecore.EcorePackage; |
| import org.eclipse.emf.ecore.plugin.EcorePlugin; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.resource.ResourceSet; |
| import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.emf.ecore.util.EcoreValidator; |
| import org.eclipse.emf.ecore.util.ExtendedMetaData; |
| import org.eclipse.emf.ecore.xml.namespace.XMLNamespacePackage; |
| import org.eclipse.emf.ecore.xml.type.XMLTypePackage; |
| import org.eclipse.emf.importer.ModelImporter; |
| import org.eclipse.emf.importer.java.JavaImporterPlugin; |
| |
| |
| public class JavaEcoreBuilder |
| { |
| /** |
| * The factory used to create JNodes. |
| */ |
| protected static FacadeHelper facadeHelper = CodeGenUtil.instantiateFacadeHelper(JMerger.DEFAULT_FACADE_HELPER_CLASS); |
| |
| /** |
| * The file being generated. |
| */ |
| protected IFile genModelFile; |
| |
| /** |
| * The GenModel being generated. |
| */ |
| protected GenModel genModel; |
| |
| /** |
| * The map from a package name to the corresponding package. |
| * These are populated from the GenModels of required projects. |
| */ |
| protected Map<String, EPackage> externalPackageNameToEPackageMap = new LinkedHashMap<String, EPackage>(); |
| |
| /** |
| * The map from a package name to the corresponding package. |
| * These are populated on demand during traversal as modeled class and enums are discovered. |
| */ |
| protected Map<String, EPackage> packageNameToEPackageMap = new LinkedHashMap<String, EPackage>(); |
| |
| /** |
| * The map from package to the map of ordering constants. |
| * These are populated as the package interface is traversed. |
| */ |
| protected Map<EPackage, Map<Object, Integer>> ePackageToOrderingMap = new LinkedHashMap<EPackage, Map<Object, Integer>>(); |
| |
| /** |
| * The map from package to the it's prefix. |
| * These are populated as the package interfaces are traversed. |
| */ |
| protected Map<EPackage, String> ePackageToPrefixMap = new LinkedHashMap<EPackage, String>(); |
| |
| /** |
| * The map from a model element to the corresponding JNode. |
| * These are populated during traversal as each model element is created. |
| */ |
| protected Map<EModelElement, JNode> eModelElementToJNodeMap = new LinkedHashMap<EModelElement, JNode>(); |
| |
| /** |
| * The features handled during the traversal. |
| * These are populated during traversal as each feature is created. |
| * They must be patched after traversal is completed to ensure they have resolved to the right type of classifier. |
| */ |
| protected Set<EStructuralFeature> eStructuralFeatures = new LinkedHashSet<EStructuralFeature>(); |
| |
| /** |
| * The attributes that must be treated as attributes because the are explicitly declared to be of that kind. |
| */ |
| protected Set<EAttribute> eAttributes = new LinkedHashSet<EAttribute>(); |
| |
| /** |
| * A map from a generic type to the possibly null generic type that represents its proper Java type. |
| */ |
| protected Map<EGenericType, EGenericType> ecoreEGenericTypeToJavaEGenericTypeMap = new LinkedHashMap<EGenericType, EGenericType>(); |
| |
| /** |
| * The set of classifiers that were demand created. |
| */ |
| protected Set<EClassifier> demandCreatedEClassifiers = new LinkedHashSet<EClassifier>(); |
| |
| /** |
| * The map from a reference to the name of its opposite. |
| * These are populated during traversal as each reference with an opposite is created. |
| * They must be patched after traversal and patching of typed elements is completed. |
| * The opposite found as a feature of the type. |
| */ |
| protected Map<EReference, String> eReferenceToOppositeNameMap = new LinkedHashMap<EReference, String>(); |
| |
| /** |
| * The set of references that have opposites and have been explicitly marked as being transient. |
| */ |
| protected Set<EReference> transientEReferenceWithOpposite = new HashSet<EReference>(); |
| |
| /** |
| * The map from a reference to the names of its keys. |
| * These are populated during traversal as each reference with keys is created. |
| * They must be patched after traversal and patching of typed elements is completed. |
| * The key is found as an attribute of the type. |
| */ |
| protected Map<EReference, List<String>> eReferenceToKeyNamesMap = new LinkedHashMap<EReference, List<String>>(); |
| |
| /** |
| * All the external GenModels from all required projects. |
| */ |
| protected Collection<GenModel> externalGenModels = new UniqueEList<GenModel>(); |
| |
| /** |
| * All the used GenPackages. |
| */ |
| protected Collection<GenPackage> usedGenPackages = new ArrayList<GenPackage>(); |
| |
| /** |
| * A collection of the problems encountered during the processing. |
| */ |
| protected BasicDiagnostic basicDiagnostic; |
| |
| /** |
| * Set to true when a compilation unit is processed and hence indicates if anything to process has been found. |
| */ |
| protected boolean foundJava; |
| |
| /** |
| * The old version to against which to reconcile. |
| */ |
| protected GenModel oldGenModelVersion; |
| |
| /** |
| * The utility used to convert a generate type to a fully qualified Java instance type. |
| */ |
| protected EcoreUtil.EGenericTypeConverter eGenericTypeConverter = |
| new EcoreUtil.EGenericTypeConverter() |
| { |
| @Override |
| protected String getInstanceTypeName(EClassifier eClassifier) |
| { |
| String result = super.getInstanceTypeName(eClassifier); |
| if (result == null) |
| { |
| EPackage ePackage = eClassifier.getEPackage(); |
| for (Map.Entry<String, EPackage> entry : packageNameToEPackageMap.entrySet()) |
| { |
| if (entry.getValue() == ePackage) |
| { |
| return entry.getKey() + '.' + eClassifier.getName(); |
| } |
| } |
| for (Map.Entry<String, EPackage> entry : externalPackageNameToEPackageMap.entrySet()) |
| { |
| if (entry.getValue() == ePackage) |
| { |
| return entry.getKey() + '.' + eClassifier.getName(); |
| } |
| } |
| } |
| return result; |
| } |
| }; |
| |
| protected static final String MAP_ENTRY_CLASS_CONTAINER_ANNOTATION_SOURCE = EcorePackage.eNS_URI + "#MapEntryClassContainer"; |
| |
| /** |
| * Creates a builder for the given file. |
| */ |
| public JavaEcoreBuilder(IFile genModelFile) |
| { |
| this.genModelFile = genModelFile; |
| |
| basicDiagnostic = new BasicDiagnostic( |
| JavaImporterPlugin.getPlugin().getBundle().getSymbolicName(), |
| 0, |
| CodeGenEcorePlugin.INSTANCE.getString("_UI_ErrorsWereDetectedJava_message"), |
| null); |
| } |
| |
| public JavaEcoreBuilder(IFile genModelFile, GenModel oldGenModelVersion) |
| { |
| this(genModelFile); |
| this.oldGenModelVersion = oldGenModelVersion; |
| } |
| |
| public JavaEcoreBuilder(IFile genModelFile, GenModel oldGenModelVersion, GenModel genModel) |
| { |
| this(genModelFile, oldGenModelVersion); |
| this.genModel = genModel; |
| } |
| |
| protected ResourceSet createResourceSet() |
| { |
| ResourceSet result = new ResourceSetImpl(); |
| result.getURIConverter().getURIMap().putAll(EcorePlugin.computePlatformURIMap(true)); |
| return result; |
| } |
| |
| protected IPath analyzeProject(IProject project) throws Exception |
| { |
| // Walk the project looking for .java files to analyze. |
| // |
| IJavaProject javaProject = JavaCore.create(project); |
| IPackageFragmentRoot[] packageFragmentRoots = javaProject.getPackageFragmentRoots(); |
| Set<IResource> visited = new HashSet<IResource>(); |
| for (int i = 0; i < packageFragmentRoots.length; ++i) |
| { |
| if (packageFragmentRoots[i].getKind() == IPackageFragmentRoot.K_SOURCE) |
| { |
| traverse((IContainer)packageFragmentRoots[i].getUnderlyingResource(), visited); |
| } |
| } |
| |
| for (Map.Entry<EGenericType, EGenericType> entry : ecoreEGenericTypeToJavaEGenericTypeMap.entrySet()) |
| { |
| EGenericType ecoreEGenericType = entry.getKey(); |
| EGenericType javaEGenericType = entry.getValue(); |
| EModelElement eModelElement = null; |
| for (EObject eObject = ecoreEGenericType.eContainer(); eObject != null; eObject = eObject.eContainer()) |
| { |
| if (eObject instanceof EModelElement) |
| { |
| eModelElement = (EModelElement)eObject; |
| break; |
| } |
| } |
| RequiredClassifierType requiredClassifierType = |
| ecoreEGenericType.eContainer() instanceof EClass || ecoreEGenericType.eContainer() instanceof EReference ? |
| RequiredClassifierType.CLASS : |
| eAttributes.contains(ecoreEGenericType.eContainer()) ? |
| RequiredClassifierType.DATA_TYPE : |
| RequiredClassifierType.NONE; |
| resolve(eModelElement, ecoreEGenericType, requiredClassifierType); |
| if (javaEGenericType != null) |
| { |
| resolve(eModelElement, javaEGenericType, requiredClassifierType); |
| } |
| |
| if (javaEGenericType != null) |
| { |
| resolve(eModelElement, ecoreEGenericType, javaEGenericType); |
| } |
| } |
| |
| for (Map.Entry<EGenericType, EGenericType> entry : ecoreEGenericTypeToJavaEGenericTypeMap.entrySet()) |
| { |
| EGenericType ecoreEGenericType = entry.getKey(); |
| EGenericType javaEGenericType = entry.getValue(); |
| EModelElement eModelElement = null; |
| for (EObject eObject = ecoreEGenericType.eContainer(); eObject != null; eObject = eObject.eContainer()) |
| { |
| if (eObject instanceof EModelElement) |
| { |
| eModelElement = (EModelElement)eObject; |
| break; |
| } |
| } |
| |
| if (javaEGenericType == null) |
| { |
| resolve(eModelElement, ecoreEGenericType, javaEGenericType); |
| } |
| used(ecoreEGenericType); |
| } |
| |
| for (EStructuralFeature eStructuralFeature : eStructuralFeatures) |
| { |
| EGenericType eGenericType = eStructuralFeature.getEGenericType(); |
| EClassifier eClassifier = eGenericType.getERawType(); |
| |
| // If we have resolved to an EClass but we have an EAttribute, we can change it to be an EReference. |
| // |
| if (eClassifier instanceof EClass && eStructuralFeature instanceof EAttribute) |
| { |
| EAttribute eAttribute = (EAttribute)eStructuralFeature; |
| EClass container = eAttribute.getEContainingClass(); |
| EReference eReference = EcoreFactory.eINSTANCE.createEReference(); |
| eReference.setChangeable(eAttribute.isChangeable()); |
| eReference.setVolatile(eAttribute.isVolatile()); |
| eReference.setTransient(eAttribute.isTransient()); |
| eReference.setDerived(eAttribute.isDerived()); |
| eReference.setUnsettable(eAttribute.isUnsettable()); |
| eReference.setLowerBound(eAttribute.getLowerBound()); |
| eReference.setUpperBound(eAttribute.getUpperBound()); |
| eReference.setName(eStructuralFeature.getName()); |
| eReference.setEGenericType(eGenericType); |
| eReference.getEAnnotations().addAll(eStructuralFeature.getEAnnotations()); |
| container.getEStructuralFeatures().add(container.getEStructuralFeatures().indexOf(eStructuralFeature), eReference); |
| container.getEStructuralFeatures().remove(eStructuralFeature); |
| eStructuralFeature = eReference; |
| } |
| else if (eClassifier instanceof EDataType && eStructuralFeature instanceof EReference) |
| { |
| EReference eReference = (EReference)eStructuralFeature; |
| EClass container = eReference.getEContainingClass(); |
| EAttribute eAttribute = EcoreFactory.eINSTANCE.createEAttribute(); |
| eAttribute.setChangeable(eReference.isChangeable()); |
| eAttribute.setVolatile(eReference.isVolatile()); |
| eAttribute.setTransient(eReference.isTransient()); |
| eAttribute.setDerived(eReference.isDerived()); |
| eAttribute.setUnsettable(eReference.isUnsettable()); |
| eAttribute.setLowerBound(eReference.getLowerBound()); |
| eAttribute.setUpperBound(eReference.getUpperBound()); |
| eAttribute.setName(eStructuralFeature.getName()); |
| eAttribute.setEGenericType(eGenericType); |
| eAttribute.getEAnnotations().addAll(eStructuralFeature.getEAnnotations()); |
| container.getEStructuralFeatures().remove(eStructuralFeature); |
| eStructuralFeature = eAttribute; |
| eReferenceToOppositeNameMap.remove(eReference); |
| } |
| |
| if (eClassifier.getEPackage() == null) |
| { |
| error(CodeGenEcorePlugin.INSTANCE.getString("_UI_TheTypeDoesNotResolveCorrectly_message", new Object []{ eClassifier.getInstanceTypeName() })); |
| |
| eGenericType = EcoreFactory.eINSTANCE.createEGenericType(); |
| eGenericType.setEClassifier(eClassifier instanceof EClass ? EcorePackage.Literals.EOBJECT : EcorePackage.Literals.EJAVA_OBJECT); |
| } |
| } |
| |
| // Now we need to hook up opposites by finding the named feature in the type. |
| // |
| for (Map.Entry<EReference, String> entry : eReferenceToOppositeNameMap.entrySet()) |
| { |
| EReference eReference = entry.getKey(); |
| String oppositeName = entry.getValue(); |
| EClass eClass = (EClass)eReference.getEType(); |
| // TODO handle class cast exception better. |
| EReference eOpposite = (EReference)eClass.getEStructuralFeature(oppositeName); |
| if (eOpposite == null) |
| { |
| error(CodeGenEcorePlugin.INSTANCE.getString("_UI_TheAttributeIsNotAMemberOf_message", new Object []{ oppositeName, eClass.getName() })); |
| } |
| else if (eOpposite.getEOpposite() != eReference && eOpposite.getEOpposite() != null) |
| { |
| error |
| (CodeGenEcorePlugin.INSTANCE.getString |
| ("_UI_TheOppositeAlreadyHasOpposite_message", |
| new Object []{ oppositeName, eOpposite.getEOpposite().getName(), eOpposite.getEOpposite().getEContainingClass().getName() })); |
| } |
| else |
| { |
| eReference.setEOpposite(eOpposite); |
| eOpposite.setEOpposite(eReference); |
| |
| used(eOpposite); |
| |
| // Containers are transient by default unless explicitly annotated otherwise. |
| // |
| if (eOpposite.isContainment() && !transientEReferenceWithOpposite.contains(eReference)) |
| { |
| eReference.setTransient(true); |
| } |
| } |
| } |
| |
| // Now we need to hook up keys by finding the named feature in the type. |
| // |
| for (Map.Entry<EReference, List<String>> entry : eReferenceToKeyNamesMap.entrySet()) |
| { |
| EReference eReference = entry.getKey(); |
| EClass eClass = (EClass)eReference.getEType(); |
| for (String keyName : entry.getValue()) |
| { |
| EStructuralFeature eKey = eClass.getEStructuralFeature(keyName); |
| if (eKey == null) |
| { |
| error(CodeGenEcorePlugin.INSTANCE.getString("_UI_TheAttributeIsNotAMemberOf_message", new Object []{ keyName, eClass.getName() })); |
| } |
| else if (!(eKey instanceof EAttribute)) |
| { |
| // TODO Ignore for now. |
| } |
| else |
| { |
| eReference.getEKeys().add((EAttribute)eKey); |
| used(eKey); |
| } |
| } |
| } |
| |
| // Clean up the temporary container annotations for holding map entry classes until they are for sure needed. |
| // |
| for (EPackage ePackage : packageNameToEPackageMap.values()) |
| { |
| EAnnotation eAnnotation = ePackage.getEAnnotation(MAP_ENTRY_CLASS_CONTAINER_ANNOTATION_SOURCE); |
| if (eAnnotation != null) |
| { |
| EcoreUtil.remove(eAnnotation); |
| } |
| } |
| |
| // Now we should sort. |
| // |
| for (Map.Entry<EPackage, Map<Object, Integer>> entry : ePackageToOrderingMap.entrySet()) |
| { |
| EPackage ePackage = entry.getKey(); |
| Map<Object, Integer> nameToIDMap = entry.getValue(); |
| |
| sort(ePackage.getEClassifiers(), nameToIDMap); |
| for (EClassifier eClassifier : ePackage.getEClassifiers()) |
| { |
| if (eClassifier instanceof EClass) |
| { |
| EClass eClass = (EClass)eClassifier; |
| sort(eClass.getEStructuralFeatures(), nameToIDMap); |
| } |
| } |
| } |
| |
| // Find the fragment root so that we can generate to the right location (by default). |
| // |
| IPath targetFragmentRoot = project.getFullPath(); |
| for (int i = 0; i < packageFragmentRoots.length; ++i) |
| { |
| if (packageFragmentRoots[i].getKind() == IPackageFragmentRoot.K_SOURCE) |
| { |
| IPath path = packageFragmentRoots[i].getUnderlyingResource().getFullPath(); |
| if (targetFragmentRoot.isPrefixOf(path)) |
| { |
| targetFragmentRoot = path; |
| break; |
| } |
| } |
| } |
| |
| facadeHelper.reset(); |
| return targetFragmentRoot; |
| } |
| |
| public void computeEPackages(Monitor monitor, ModelImporter modelImporter) throws Exception |
| { |
| IProject project = genModelFile.getProject(); |
| project.open(BasicMonitor.toIProgressMonitor(monitor)); |
| if (!project.hasNature(JavaCore.NATURE_ID) && oldGenModelVersion.getModelDirectory() != null) |
| { |
| project = project.getWorkspace().getRoot().getFolder(new Path(oldGenModelVersion.getModelDirectory())).getProject(); |
| } |
| |
| Collection<IFile> allGenModelFiles = new ArrayList<IFile>(); |
| Collection<IProject> allReferencedProjects = new ArrayList<IProject>(); |
| getAllReferencedProjects(allReferencedProjects, project.getDescription().getReferencedProjects()); |
| getAllReferencedProjects(allReferencedProjects, project.getDescription().getDynamicReferences()); |
| for (IProject referencedProject : allReferencedProjects) |
| { |
| getAllGenModelFiles(allGenModelFiles, referencedProject); |
| } |
| |
| ResourceSet resourceSet = modelImporter.createResourceSet(); |
| for (IFile file : allGenModelFiles) |
| { |
| Resource resource = resourceSet.getResource(modelImporter.createFileURI(file.getFullPath().toString()), true); |
| GenModel genModel = (GenModel)resource.getContents().get(0); |
| externalGenModels.add(genModel); |
| for (GenPackage genPackage : genModel.getGenPackages()) |
| { |
| determineExternalPackages(genPackage, modelImporter); |
| } |
| } |
| |
| // Iterate over all projects to look at the manifests. |
| // |
| List<String> allReferencedPluginIDs = new UniqueEList<String>(); |
| allReferencedProjects.add(project); |
| for (IProject referencedProject : allReferencedProjects) |
| { |
| try |
| { |
| // Determine the required plugins. |
| // |
| URI manifestURI = URI.createURI(referencedProject.getFullPath() + "/META-INF/MANIFEST.MF"); |
| Manifest manifest = new Manifest(resourceSet.getURIConverter().createInputStream(manifestURI)); |
| String requires = manifest.getMainAttributes().getValue("Require-Bundle"); |
| if (requires != null) |
| { |
| ManifestElement[] elements = ManifestElement.parseHeader(Constants.REQUIRE_BUNDLE, requires); |
| for (int j = 0; j < elements.length; ++j) |
| { |
| // Include each required plugin of the starting project and the exported ones of all the required projects. |
| // |
| ManifestElement element = elements[j]; |
| if (project == referencedProject || "reexport".equals(element.getDirective("visibility"))) |
| { |
| String pluginID = element.getValue(); |
| allReferencedPluginIDs.add(pluginID); |
| } |
| } |
| } |
| } |
| catch (IOException exception) |
| { |
| // Ignore |
| } |
| } |
| |
| // Iterate over all the plugin IDs to determine the dependency closure. |
| // The list grows as the plugins are visited. |
| // |
| for (int i = 0; i < allReferencedPluginIDs.size(); ++i) |
| { |
| // Determine the required plugins. |
| // |
| String pluginID = allReferencedPluginIDs.get(i); |
| Bundle bundle = Platform.getBundle(pluginID); |
| if (bundle != null) |
| { |
| String requires = (String)bundle.getHeaders().get(Constants.REQUIRE_BUNDLE); |
| if (requires != null) |
| { |
| ManifestElement[] elements = ManifestElement.parseHeader(Constants.REQUIRE_BUNDLE, requires); |
| for (int j = 0; j < elements.length; ++j) |
| { |
| // Also add each required plugin for consideration in the loop. |
| // |
| ManifestElement element = elements[j]; |
| String value = element.getValue(); |
| if ("reexport".equals(element.getDirective("visibility"))) |
| { |
| allReferencedPluginIDs.add(value); |
| } |
| } |
| } |
| } |
| } |
| |
| // Determine the inverse map from plugin IDs to their registered GenModel locations. |
| // |
| Map<String, List<URI>> allPluginsWithGenModels = new HashMap<String, List<URI>>(); |
| for (Map.Entry<String, URI> entry : EcorePlugin.getEPackageNsURIToGenModelLocationMap(true).entrySet()) |
| { |
| // If it's a platform plugin URI, include it in the map. |
| // |
| URI genModelLocation = entry.getValue(); |
| if (genModelLocation.isPlatformResource()) |
| { |
| List<URI> uris = allPluginsWithGenModels.get(genModelLocation.segment(1)); |
| if (uris == null) |
| { |
| uris = new UniqueEList<URI>(); |
| } |
| uris.add(genModelLocation); |
| allPluginsWithGenModels.put(genModelLocation.segment(1), uris); |
| } |
| } |
| |
| // Keep only the plugins that have GenModels for consideration. |
| // |
| allReferencedPluginIDs.retainAll(allPluginsWithGenModels.keySet()); |
| for (String pluginID : allReferencedPluginIDs) |
| { |
| // Consider each GenModel location URI for each required plugin. |
| // |
| for (URI uri : allPluginsWithGenModels.get(pluginID)) |
| { |
| // Load the model and if it's not one already considered, e.g., a local version in the workspace, process its GenPackages. |
| // |
| Resource resource = resourceSet.getResource(uri, true); |
| GenModel genModel = (GenModel)resource.getContents().get(0); |
| if (externalGenModels.add(genModel)) |
| { |
| for (GenPackage genPackage : genModel.getGenPackages()) |
| { |
| determineExternalPackages(genPackage, modelImporter); |
| } |
| } |
| } |
| } |
| |
| IPath targetFragmentRoot = analyzeProject(project); |
| modelImporter.setModelPluginDirectory(targetFragmentRoot.toString()); |
| |
| if (packageNameToEPackageMap.isEmpty()) |
| { |
| error(JavaImporterPlugin.INSTANCE.getString(foundJava ? "_UI_NoModelElementsInJava_message" : "_UI_NoModelElements_message")); |
| } |
| |
| for (EPackage ePackage : packageNameToEPackageMap.values()) |
| { |
| modelImporter.getEPackages().add(ePackage); |
| |
| ModelImporter.EPackageImportInfo ePackageInfo = modelImporter.getEPackageImportInfo(ePackage); |
| ePackageInfo.setPrefix(ePackageToPrefixMap.get(ePackage)); |
| for (Map.Entry<String, EPackage> entry : packageNameToEPackageMap.entrySet()) |
| { |
| if (entry.getValue() == ePackage) |
| { |
| String qualifiedPackageName = entry.getKey(); |
| if (qualifiedPackageName != null) |
| { |
| int index = qualifiedPackageName.lastIndexOf("."); |
| if (index != -1) |
| { |
| ePackageInfo.setBasePackage(qualifiedPackageName.substring(0, index)); |
| } |
| } |
| break; |
| } |
| } |
| } |
| } |
| |
| public void used(EGenericType eGenericType) |
| { |
| if (eGenericType != null) |
| { |
| EClassifier eClassifier = eGenericType.getEClassifier(); |
| if (eClassifier != null) |
| { |
| used(eClassifier); |
| } |
| used(eGenericType.getEUpperBound()); |
| used(eGenericType.getELowerBound()); |
| for (EGenericType eTypeArgument : eGenericType.getETypeArguments()) |
| { |
| used(eTypeArgument); |
| } |
| } |
| } |
| |
| public void used(EModelElement modelElement) |
| { |
| EPackage ePackage = (EPackage)EcoreUtil.getRootContainer(modelElement); |
| if (ePackage != EcorePackage.eINSTANCE) |
| { |
| for (GenModel genModel : externalGenModels) |
| { |
| GenPackage genPackage = genModel.findGenPackage(ePackage); |
| if (genPackage != null) |
| { |
| if (!getUsedGenPackages().contains(genPackage) && genPackage.eResource() != null) |
| { |
| getUsedGenPackages().add(genPackage); |
| |
| // Compute the closure. |
| // |
| for (Iterator<EObject> j = ePackage.eAllContents(); j.hasNext();) |
| { |
| for (Iterator<EObject> k = j.next().eCrossReferences().iterator(); k.hasNext();) |
| { |
| EObject o = k.next(); |
| if (o instanceof EModelElement) |
| { |
| used((EModelElement)o); |
| } |
| } |
| } |
| } |
| break; |
| } |
| } |
| } |
| } |
| |
| public void determineExternalPackages(GenPackage genPackage) |
| { |
| determineExternalPackages(genPackage, null); |
| } |
| |
| protected void determineExternalPackages(GenPackage genPackage, ModelImporter modelImporter) |
| { |
| if (modelImporter != null) |
| { |
| modelImporter.getReferencedGenPackages().add(genPackage); |
| } |
| EPackage ePackage = genPackage.getEcorePackage(); |
| externalPackageNameToEPackageMap.put(genPackage.getInterfacePackageName(), ePackage); |
| for (GenPackage nestedGenPackage : genPackage.getNestedGenPackages()) |
| { |
| determineExternalPackages(nestedGenPackage); |
| } |
| } |
| |
| /** |
| * Walks the projects recursively. |
| */ |
| public void getAllReferencedProjects(Collection<IProject> result, IProject[] projects) throws CoreException |
| { |
| for (int i = 0; i < projects.length; ++i) |
| { |
| IProject project = projects[i]; |
| if (!result.contains(project) && project.exists() && project.isOpen()) |
| { |
| result.add(project); |
| getAllReferencedProjects(result, project.getDescription().getReferencedProjects()); |
| getAllReferencedProjects(result, project.getDescription().getDynamicReferences()); |
| } |
| } |
| } |
| |
| /** |
| * Walks the container recursively. |
| */ |
| public void getAllGenModelFiles(Collection<IFile> result, IContainer container) throws CoreException |
| { |
| IResource[] contents = container.members(); |
| for (int i = 0; i < contents.length; ++i) |
| { |
| IResource resource = contents[i]; |
| if (resource.getType() == IResource.FILE) |
| { |
| getAllGenModelFiles(result, (IFile)resource); |
| } |
| else |
| { |
| getAllGenModelFiles(result, (IContainer)resource); |
| } |
| } |
| } |
| |
| /** |
| * Walks the container recursively. |
| */ |
| public void getAllGenModelFiles(Collection<IFile> result, IFile file) throws CoreException |
| { |
| if (file.getName().endsWith(".genmodel")) |
| { |
| IProject project = file.getProject(); |
| IJavaProject javaProject = JavaCore.create(project); |
| try |
| { |
| IPath outputLocation = javaProject.getOutputLocation(); |
| if (project == project.getWorkspace().getRoot().findMember(javaProject.getOutputLocation()) |
| || !outputLocation.isPrefixOf(file.getFullPath())) |
| { |
| result.add(file); |
| } |
| } |
| catch (JavaModelException exception) |
| { |
| JavaImporterPlugin.INSTANCE.log(exception); |
| } |
| } |
| } |
| |
| /** |
| * Walks the container recursively. |
| */ |
| public void traverse(IContainer container, Set<IResource> visited) throws CoreException |
| { |
| IResource[] contents = container.members(); |
| for (int i = 0; i < contents.length; ++i) |
| { |
| IResource resource = contents[i]; |
| if (visited.add(resource)) |
| { |
| if (resource.getType() == IResource.FILE) |
| { |
| traverse((IFile)resource); |
| } |
| else |
| { |
| traverse((IContainer)resource, visited); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Analyzes .java files as compilation units. |
| */ |
| public void traverse(IFile file) throws CoreException |
| { |
| if ("java".equalsIgnoreCase(file.getProjectRelativePath().getFileExtension())) |
| { |
| try |
| { |
| BufferedInputStream bufferedInputStream = new BufferedInputStream(file.getContents(true)); |
| byte[] input = new byte [bufferedInputStream.available()]; |
| bufferedInputStream.read(input); |
| bufferedInputStream.close(); |
| |
| String encoding = null; |
| try |
| { |
| encoding = file.getCharset(); |
| } |
| catch (CoreException ce) |
| { |
| // use no encoding |
| } |
| String contents = encoding == null ? new String(input) : new String(input, encoding); |
| JCompilationUnit compilationUnit = facadeHelper.createCompilationUnit("NAME", contents); |
| analyzeCompilationUnit(compilationUnit); |
| } |
| catch (IOException exception) |
| { |
| JavaImporterPlugin.INSTANCE.log(exception); |
| } |
| } |
| } |
| |
| /** |
| * Walks the compilation unit to analyze the type. |
| */ |
| protected void analyzeCompilationUnit(JCompilationUnit compilationUnit) |
| { |
| foundJava = true; |
| for (JAbstractType abstractType : facadeHelper.getChildren(compilationUnit, JAbstractType.class)) |
| { |
| if (abstractType instanceof JEnum) |
| { |
| analyzeEnum((JEnum)abstractType); |
| } |
| else if (abstractType instanceof JType) |
| { |
| analyzeType((JType)abstractType); |
| } |
| break; |
| } |
| } |
| |
| protected void analyzeEnum(JEnum enumeration) |
| { |
| String modelAnnotation = getModelAnnotation(enumeration.getComment()); |
| if (modelAnnotation != null) |
| { |
| EEnum eEnum = EcoreFactory.eINSTANCE.createEEnum(); |
| eModelElementToJNodeMap.put(eEnum, enumeration); |
| eEnum.setName(enumeration.getName()); |
| getEPackage(enumeration).getEClassifiers().add(eEnum); |
| eEnum.getEAnnotations().addAll(extractEAnnotations(modelAnnotation)); |
| EcoreUtil.setDocumentation(eEnum, getModelDocumentation(enumeration.getComment())); |
| |
| // Walk the fields. |
| // |
| boolean hasEnumLiteral = false; |
| for (JEnumConstant enumConstant : facadeHelper.getChildren(enumeration, JEnumConstant.class)) |
| { |
| hasEnumLiteral |= analyzeEnumLiteral(eEnum, enumConstant); |
| } |
| |
| if (!hasEnumLiteral) |
| { |
| for (JField field : facadeHelper.getChildren(enumeration, JField.class)) |
| { |
| hasEnumLiteral |= analyzeEnumLiteral(eEnum, field); |
| } |
| } |
| } |
| } |
| |
| protected EPackage getEPackage(JNode node) |
| { |
| JPackage jPackage = facadeHelper.getPackage(node); |
| String qualifiedPackageName = jPackage != null ? |
| jPackage.getQualifiedName() |
| : null; |
| |
| EPackage ePackage = packageNameToEPackageMap.get(qualifiedPackageName); |
| if (ePackage == null) |
| { |
| // Create the EPackage on demand. |
| // |
| ePackage = EcoreFactory.eINSTANCE.createEPackage(); |
| int index = qualifiedPackageName == null ? -1 : qualifiedPackageName.lastIndexOf("."); |
| String packageName = index == -1 ? qualifiedPackageName : qualifiedPackageName.substring(index + 1); |
| ePackage.setName(packageName); |
| ePackage.setNsURI("http:///" + (qualifiedPackageName == null ? "null" : qualifiedPackageName.replace('.', '/')) + ".ecore"); |
| ePackage.setNsPrefix(qualifiedPackageName == null ? "null" : qualifiedPackageName); |
| packageNameToEPackageMap.put(qualifiedPackageName, ePackage); |
| |
| if (packageName != null) |
| { |
| String prefix = Character.toUpperCase(packageName.charAt(0)) + packageName.substring(1); |
| ePackageToPrefixMap.put(ePackage, prefix); |
| } |
| } |
| |
| return ePackage; |
| } |
| |
| private static final String[] EMPTY_STRING_ARRAY = new String[0]; |
| |
| /** |
| * Walks the type either as an EClass or an ENum to analyze either the methods or the fields. |
| */ |
| protected void analyzeType(JType type) |
| { |
| // Check whether this has @model annotation contents. |
| // If not, it might be a package interface, for backwards compatibility. |
| // |
| String modelAnnotation = getModelAnnotation(type.getComment()); |
| boolean isEClassifier = false; |
| String kind = null; |
| |
| if (modelAnnotation != null) |
| { |
| kind = getModelAnnotationAttribute(modelAnnotation, "kind"); |
| isEClassifier = !"package".equals(kind); |
| } |
| |
| if (isEClassifier) |
| { |
| // Get the package name and see if there's an EPackage for it. |
| // |
| EPackage ePackage = getEPackage(type); |
| |
| // If it's an interface, then it will be treated as an EClass |
| // |
| if ((type.getFlags() & Flags.AccInterface) != 0 || "class".equals(kind)) |
| { |
| EClass eClass = EcoreFactory.eINSTANCE.createEClass(); |
| eModelElementToJNodeMap.put(eClass, type); |
| eClass.setName(type.getName()); |
| ePackage.getEClassifiers().add(eClass); |
| eClass.getEAnnotations().addAll(extractEAnnotations(modelAnnotation)); |
| EcoreUtil.setDocumentation(eClass, getModelDocumentation(type.getComment())); |
| |
| analyzeTypeParameters(eClass.getETypeParameters(), type.getTypeParameters(), modelAnnotation); |
| |
| String[] superInterfaces = type.getSuperInterfaces(); |
| String extend = getExtendsAnnotation(type.getComment()); |
| if (extend != null) |
| { |
| List<String> superInterfaceList = new ArrayList<String>(Arrays.asList(superInterfaces)); |
| Diagnostic diagnostic = EcoreValidator.EGenericTypeBuilder.INSTANCE.parseTypeArgumentList(extend); |
| if (diagnostic.getSeverity() == Diagnostic.OK) |
| { |
| @SuppressWarnings("unchecked") |
| List<EGenericType> eTypeArguments = (List<EGenericType>)diagnostic.getData().get(0); |
| for (EGenericType eGenericType : eTypeArguments) |
| { |
| superInterfaceList.remove(EcoreUtil.toJavaInstanceTypeName(eGenericType)); |
| } |
| } |
| superInterfaces = new String [superInterfaceList.size()]; |
| superInterfaceList.toArray(superInterfaces); |
| } |
| |
| // Create a generic super type with an EClass as the classifier for each super interface in the Java representation. |
| // |
| List<EGenericType> javaEGenericSuperTypes = new ArrayList<EGenericType>(); |
| for (String superType : superInterfaces) |
| { |
| Diagnostic diagnostic = EcoreValidator.EGenericTypeBuilder.INSTANCE.parseInstanceTypeName(superType); |
| if (diagnostic.getSeverity() == Diagnostic.OK) |
| { |
| EGenericType eGenericSuperType = (EGenericType)diagnostic.getData().get(0); |
| EClass eSuperClass = EcoreFactory.eINSTANCE.createEClass(); |
| eSuperClass.setAbstract(true); |
| eSuperClass.setInterface(true); |
| eSuperClass.setInstanceTypeName(eGenericSuperType.getEClassifier().getInstanceTypeName()); |
| eGenericSuperType.setEClassifier(eSuperClass); |
| javaEGenericSuperTypes.add(eGenericSuperType); |
| } |
| } |
| |
| // Create a generic super type with an EClass as the classifier for each super interface in the @model representation |
| // |
| List<EGenericType> ecoreEGenericSuperTypes = null; |
| String superTypes = getModelAnnotationAttribute(modelAnnotation, "superTypes"); |
| if (superTypes != null) |
| { |
| ecoreEGenericSuperTypes = new ArrayList<EGenericType>(); |
| for (EGenericType ecoreEGenericSuperType : analyzeEGenericTypes(superTypes)) |
| { |
| EClass eSuperClass = EcoreFactory.eINSTANCE.createEClass(); |
| eSuperClass.setAbstract(true); |
| eSuperClass.setInterface(true); |
| eSuperClass.setInstanceTypeName(ecoreEGenericSuperType.getEClassifier().getInstanceTypeName()); |
| ecoreEGenericSuperType.setEClassifier(eSuperClass); |
| ecoreEGenericSuperTypes.add(ecoreEGenericSuperType); |
| } |
| } |
| |
| // Filter out explicit EObject from super types, except in the Ecore package itself, or if it appears in the @model superTypes. |
| // |
| if (!javaEGenericSuperTypes.isEmpty() && |
| javaEGenericSuperTypes.get(0).getERawType().getInstanceTypeName().endsWith("EObject") && |
| (ecoreEGenericSuperTypes == null || |
| !ecoreEGenericSuperTypes.isEmpty() && !ecoreEGenericSuperTypes.get(0).getERawType().getInstanceTypeName().endsWith("EObject")) && |
| !EcorePackage.eNS_URI.equals(ePackage.getNsURI())) |
| { |
| EGenericType superType = resolve(eClass, javaEGenericSuperTypes.get(0).getERawType().getInstanceTypeName(), RequiredClassifierType.CLASS, false); |
| EClassifier superClass = superType.getEClassifier(); |
| if (superClass.getEPackage() != null && |
| EcorePackage.eNS_URI.equals(superClass.getEPackage().getNsURI()) && |
| "EObject".equals(superClass.getName())) |
| { |
| javaEGenericSuperTypes.remove(0); |
| } |
| } |
| |
| // Match them and accumulate the appropriate result. |
| // |
| match(eClass.getEGenericSuperTypes(), javaEGenericSuperTypes, ecoreEGenericSuperTypes); |
| |
| String isInterface = getModelAnnotationAttribute(modelAnnotation, "interface"); |
| eClass.setInterface("true".equals(isInterface)); |
| |
| String isAbstract = getModelAnnotationAttribute(modelAnnotation, "abstract"); |
| eClass.setAbstract("true".equals(isAbstract) || isAbstract == null && eClass.isInterface()); |
| |
| // Walk the methods. |
| // |
| for (JMethod method : facadeHelper.getChildren(type, JMethod.class)) |
| { |
| analyzeMethod(eClass, method); |
| } |
| |
| // Additional attributes and references may be defined directly on the interface in order to allow the |
| // get accessor method to have suppressed visibility. |
| // |
| String features = getModelAnnotationAttribute(modelAnnotation, "features"); |
| if (features != null) |
| { |
| for (StringTokenizer stringTokenizer = new StringTokenizer(features, " "); stringTokenizer.hasMoreTokens();) |
| { |
| String feature = stringTokenizer.nextToken(); |
| if (eClass.getEStructuralFeature(feature) == null) |
| { |
| analyzeMethod |
| (eClass, |
| getFilteredModelAnnotations(modelAnnotation, feature), |
| "get" + Character.toUpperCase(feature.charAt(0)) + feature.substring(1), |
| "java.lang.Object", |
| EMPTY_STRING_ARRAY, |
| EMPTY_STRING_ARRAY, |
| EMPTY_STRING_ARRAY, |
| EMPTY_STRING_ARRAY); |
| } |
| else |
| { |
| warning(CodeGenEcorePlugin.INSTANCE.getString("_UI_DuplicateFeature_message", new Object []{ feature, eClass.getName() })); |
| } |
| } |
| } |
| } |
| // Otherwise it's treated as an EEnum |
| // |
| else |
| { |
| EEnum eEnum = EcoreFactory.eINSTANCE.createEEnum(); |
| eModelElementToJNodeMap.put(eEnum, type); |
| eEnum.setName(type.getName()); |
| ePackage.getEClassifiers().add(eEnum); |
| eEnum.getEAnnotations().addAll(extractEAnnotations(modelAnnotation)); |
| EcoreUtil.setDocumentation(eEnum, getModelDocumentation(type.getComment())); |
| |
| // Walk the fields. |
| // |
| for (JField field : facadeHelper.getChildren(type, JField.class)) |
| { |
| analyzeEnumLiteral(eEnum, field); |
| } |
| } |
| } |
| // Find Packages and Factories |
| else |
| { |
| JPackage jPackage = facadeHelper.getPackage(type); |
| String qualifiedPackageName = jPackage != null ? jPackage.getQualifiedName() : null; |
| |
| String typeName = type.getName(); |
| boolean isEPackage = false; |
| if (typeName.endsWith("Package") && typeName.length() > 7) |
| { |
| String packagePrefix = typeName.substring(0, typeName.length() - 7); |
| |
| // It's definitely a package if it was declared as such. |
| // |
| if ("package".equals(kind)) |
| { |
| isEPackage = true; |
| } |
| |
| int index = qualifiedPackageName == null ? -1 : qualifiedPackageName.lastIndexOf("."); |
| String name = index == -1 ? qualifiedPackageName : qualifiedPackageName.substring(index + 1); |
| String nsURI = "http:///" + (qualifiedPackageName == null ? "null" : qualifiedPackageName.replace('.', '/')) + ".ecore"; |
| String nsPrefix = qualifiedPackageName == null ? "null" : qualifiedPackageName; |
| |
| List<EClass> eClasses = new ArrayList<EClass>(); |
| List<EDataType> eDataTypes = new ArrayList<EDataType>(); |
| Map<Object, Integer> ordering = new HashMap<Object, Integer>(); |
| |
| // It's definitely a package if expected constants eNAME, eNS_PREFIX, or eNS_URI appear. |
| // |
| for (JNode node : type.getChildren()) |
| { |
| if (node instanceof JField) |
| { |
| JField field = (JField)node; |
| String fieldName = field.getName(); |
| String fieldType = field.getType(); |
| if ("eNAME".equals(fieldName)) |
| { |
| isEPackage = true; |
| name = field.getInitializer(); |
| name = name.substring(1, name.length() - 1); |
| } |
| else if ("eNS_URI".equals(fieldName)) |
| { |
| isEPackage = true; |
| nsURI = field.getInitializer(); |
| nsURI = nsURI.substring(1, nsURI.length() - 1); |
| } |
| else if ("eNS_PREFIX".equals(fieldName)) |
| { |
| isEPackage = true; |
| nsPrefix = field.getInitializer(); |
| nsPrefix = nsPrefix.substring(1, nsPrefix.length() - 1); |
| } |
| else if ("int".equals(fieldType) && !fieldName.endsWith("FEATURE_COUNT")) |
| { |
| try |
| { |
| String initializer = field.getInitializer(); |
| int plusIndex = initializer.lastIndexOf("+"); |
| if (plusIndex != -1) |
| { |
| initializer = initializer.substring(plusIndex + 1); |
| } |
| initializer = initializer.trim(); |
| int value = Integer.parseInt(initializer); |
| ordering.put(fieldName, value); |
| } |
| catch (NumberFormatException exception) |
| { |
| // This will catch inherited features, or additional things we don't want to worry about. |
| } |
| } |
| } |
| else if (node instanceof JMethod) |
| { |
| JMethod method = (JMethod)node; |
| String methodAnnotation = getModelAnnotation(method.getComment()); |
| if (methodAnnotation != null) |
| { |
| String returnType = method.getReturnType(); |
| if (returnType != null) |
| { |
| if (returnType.endsWith("EDataType")) |
| { |
| EDataType eDataType = EcoreFactory.eINSTANCE.createEDataType(); |
| eDataType.setInstanceTypeName(getModelAnnotationAttribute(methodAnnotation, "instanceClass")); |
| eDataType.setName(method.getName().substring(3)); |
| String isSerializable = getModelAnnotationAttribute(methodAnnotation, "serializable"); |
| if ("false".equals(isSerializable)) |
| { |
| eDataType.setSerializable(false); |
| } |
| |
| String typeParameterNames = getModelAnnotationAttribute(methodAnnotation, "typeParameters"); |
| if (typeParameterNames != null) |
| { |
| for (StringTokenizer stringTokenizer = new StringTokenizer(typeParameterNames, " "); stringTokenizer.hasMoreTokens();) |
| { |
| String typeParameterName = stringTokenizer.nextToken(); |
| ETypeParameter eTypeParameter = EcoreFactory.eINSTANCE.createETypeParameter(); |
| eTypeParameter.setName(typeParameterName); |
| String typeParameterModelAnnotation = getFilteredModelAnnotations(methodAnnotation, typeParameterName); |
| String bounds = getModelAnnotationAttribute(typeParameterModelAnnotation, "bounds"); |
| if (bounds != null) |
| { |
| eTypeParameter.getEBounds().addAll(analyzeEGenericTypes(bounds)); |
| for (EGenericType eBound : eTypeParameter.getEBounds()) |
| { |
| ecoreEGenericTypeToJavaEGenericTypeMap.put(eBound, null); |
| } |
| } |
| |
| eDataType.getETypeParameters().add(eTypeParameter); |
| } |
| } |
| |
| eDataTypes.add(eDataType); |
| eDataType.getEAnnotations().addAll(extractEAnnotations(methodAnnotation)); |
| EcoreUtil.setDocumentation(eDataType, getModelDocumentation(method.getComment())); |
| } |
| else if (returnType.endsWith("EClass")) |
| { |
| EClass eClass = EcoreFactory.eINSTANCE.createEClass(); |
| String instanceClass = getModelAnnotationAttribute(methodAnnotation, "instanceClass"); |
| if (instanceClass != null) |
| { |
| eClass.setInterface(true); |
| eClass.setAbstract(true); |
| eClass.setInstanceTypeName(instanceClass); |
| eClass.setName(method.getName().substring(3)); |
| |
| String typeParameterNames = getModelAnnotationAttribute(methodAnnotation, "typeParameters"); |
| if (typeParameterNames != null) |
| { |
| for (StringTokenizer stringTokenizer = new StringTokenizer(typeParameterNames, " "); stringTokenizer.hasMoreTokens();) |
| { |
| String typeParameterName = stringTokenizer.nextToken(); |
| ETypeParameter eTypeParameter = EcoreFactory.eINSTANCE.createETypeParameter(); |
| eTypeParameter.setName(typeParameterName); |
| String typeParameterModelAnnotation = getFilteredModelAnnotations(methodAnnotation, typeParameterName); |
| String bounds = getModelAnnotationAttribute(typeParameterModelAnnotation, "bounds"); |
| if (bounds != null) |
| { |
| eTypeParameter.getEBounds().addAll(analyzeEGenericTypes(bounds)); |
| for (EGenericType eBound : eTypeParameter.getEBounds()) |
| { |
| ecoreEGenericTypeToJavaEGenericTypeMap.put(eBound, null); |
| } |
| } |
| |
| eClass.getETypeParameters().add(eTypeParameter); |
| } |
| } |
| |
| eClasses.add(eClass); |
| } |
| else |
| { |
| eClass.setInstanceTypeName("java.util.Map$Entry"); |
| eClass.setName(method.getName().substring(3)); |
| eClasses.add(eClass); |
| |
| String features = getModelAnnotationAttribute(methodAnnotation, "features"); |
| if (features != null) |
| { |
| for (StringTokenizer stringTokenizer = new StringTokenizer(features, " "); stringTokenizer.hasMoreTokens();) |
| { |
| String feature = stringTokenizer.nextToken(); |
| analyzeMethod |
| (eClass, |
| getFilteredModelAnnotations(methodAnnotation, feature), |
| "get" + Character.toUpperCase(feature.charAt(0)) + feature.substring(1), |
| "java.lang.Object", |
| EMPTY_STRING_ARRAY, |
| EMPTY_STRING_ARRAY, |
| EMPTY_STRING_ARRAY, |
| EMPTY_STRING_ARRAY); |
| } |
| } |
| else |
| { |
| analyzeMethod |
| (eClass, |
| getFilteredModelAnnotations(methodAnnotation, "key"), |
| "getKey", |
| "java.lang.Object", |
| EMPTY_STRING_ARRAY, |
| EMPTY_STRING_ARRAY, |
| EMPTY_STRING_ARRAY, |
| EMPTY_STRING_ARRAY); |
| |
| analyzeMethod |
| (eClass, |
| getFilteredModelAnnotations(methodAnnotation, "value"), |
| "getValue", |
| "java.lang.Object", |
| EMPTY_STRING_ARRAY, |
| EMPTY_STRING_ARRAY, |
| EMPTY_STRING_ARRAY, |
| EMPTY_STRING_ARRAY); |
| } |
| } |
| eClass.getEAnnotations().addAll(extractEAnnotations(methodAnnotation)); |
| EcoreUtil.setDocumentation(eClass, getModelDocumentation(method.getComment())); |
| } |
| } |
| } |
| } |
| } |
| |
| if (isEPackage || !eClasses.isEmpty() || !eDataTypes.isEmpty()) |
| { |
| EPackage ePackage = EcoreFactory.eINSTANCE.createEPackage(); |
| ePackageToOrderingMap.put(ePackage, ordering); |
| eModelElementToJNodeMap.put(ePackage, type); |
| ePackage.setNsURI(nsURI); |
| ePackage.setNsPrefix(nsPrefix); |
| ePackage.setName(name); |
| ePackage.getEClassifiers().addAll(eClasses); |
| ePackage.getEClassifiers().addAll(eDataTypes); |
| if (modelAnnotation != null) |
| { |
| ePackage.getEAnnotations().addAll(extractEAnnotations(modelAnnotation)); |
| } |
| EcoreUtil.setDocumentation(ePackage, getModelDocumentation(type.getComment())); |
| |
| ePackageToPrefixMap.put(ePackage, packagePrefix); |
| |
| EPackage existingEPackage = packageNameToEPackageMap.get(qualifiedPackageName); |
| if (existingEPackage != null) |
| { |
| ePackage.getEClassifiers().addAll(existingEPackage.getEClassifiers()); |
| ePackage.getEAnnotations().addAll(existingEPackage.getEAnnotations()); |
| } |
| |
| packageNameToEPackageMap.put(qualifiedPackageName, ePackage); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Creates an EOperation, EAttribute, or EReference as appropriate. |
| */ |
| protected void analyzeMethod(EClass eClass, JMethod method) |
| { |
| // Check whether this has @model annotation contents. |
| // |
| String modelAnnotation = getModelAnnotation(method.getComment()); |
| if (modelAnnotation != null && method.getName() != null) |
| { |
| String methodName = method.getName(); |
| String returnType = method.getReturnType(); |
| String[] parameterNames = method.getParameterNames(); |
| String[] parameterTypes = method.getFullParameterTypes(); |
| String[] exceptionTypes = method.getExceptions(); |
| String[] typeParameters = method.getTypeParameters(); |
| |
| ETypedElement eTypedElement = analyzeMethod(eClass, modelAnnotation, methodName, returnType, parameterNames, parameterTypes, exceptionTypes, typeParameters); |
| if (eTypedElement != null) |
| { |
| EcoreUtil.setDocumentation(eTypedElement, getModelDocumentation(method.getComment())); |
| } |
| |
| eModelElementToJNodeMap.put(eTypedElement, method); |
| if (eTypedElement instanceof EOperation) |
| { |
| EOperation eOperation = (EOperation)eTypedElement; |
| for (EParameter eParameter : eOperation.getEParameters()) |
| { |
| eModelElementToJNodeMap.put(eParameter, method); |
| } |
| } |
| } |
| } |
| |
| protected ETypedElement analyzeMethod( |
| EClass eClass, |
| String modelAnnotation, |
| String methodName, |
| String returnType, |
| String[] parameterNames, |
| String[] parameterTypes, |
| String[] exceptionTypes, |
| String[] typeParameters) |
| { |
| // We will create an EAttribute, EReference, or an EOperation. |
| // |
| ETypedElement eTypedElement = null; |
| String featureName = methodName; |
| String parameters = getModelAnnotationAttribute(modelAnnotation, "parameters"); |
| String kind = getModelAnnotationAttribute(modelAnnotation, "kind"); |
| |
| // An operation is declared via the kind property or, for backwards compatibility, by specifying parameters |
| // (though attribute or reference kind takes precedence). |
| // |
| boolean declaredEOperation = |
| "operation".equals(kind) || |
| (parameters != null && !"attribute".equals(kind) && !"reference".equals(kind)); |
| |
| // Check whether there are parameters; the special attribute and reference rules only apply for the case of no arguments. |
| // |
| if (parameterNames.length == 0 && |
| !declaredEOperation && |
| methodName.startsWith("get") && |
| methodName.length() > 3 && |
| Character.isUpperCase(methodName.charAt(3)) && |
| !"boolean".equals(returnType) && |
| !"void".equals(returnType)) |
| { |
| // The feature name is extracted lower cased. |
| // |
| featureName = CodeGenUtil.uncapName(methodName.substring(3)); |
| } |
| else if (parameterNames.length == 0 && |
| !declaredEOperation && |
| methodName.startsWith("is") && |
| methodName.length() > 2 && |
| Character.isUpperCase(methodName.charAt(2)) && |
| "boolean".equals(returnType)) |
| { |
| // The name is extracted and lower cased. |
| // |
| featureName = CodeGenUtil.uncapName(methodName.substring(2)); |
| } |
| else |
| { |
| // The method is not a structural feature, so it's modeled as an operation. |
| // |
| EOperation eOperation = EcoreFactory.eINSTANCE.createEOperation(); |
| eTypedElement = eOperation; |
| eClass.getEOperations().add(eOperation); |
| |
| analyzeTypeParameters(eOperation.getETypeParameters(), typeParameters, modelAnnotation); |
| |
| handleETypedElement(eOperation, methodName, returnType, modelAnnotation, eClass.getName() + "." + methodName); |
| |
| if (parameterTypes.length > 0) |
| { |
| // Each token in parameters will specify a dataType for the corresponding parameter, but can be overridden by a |
| // parameter-name-prefixed dataType property. |
| // |
| StringTokenizer stringTokenizer = new StringTokenizer(parameters == null ? "" : parameters); |
| for (int i = 0; i < parameterNames.length; ++i) |
| { |
| EParameter eParameter = EcoreFactory.eINSTANCE.createEParameter(); |
| eOperation.getEParameters().add(eParameter); |
| String parameterName = parameterNames[i]; |
| String parameterType = parameterTypes[i]; |
| String parameterModelAnnotation = getFilteredModelAnnotations(modelAnnotation, parameterName); |
| |
| if (stringTokenizer.hasMoreTokens()) |
| { |
| String dataType = stringTokenizer.nextToken(); |
| if (!"-".equals(dataType)) |
| { |
| StringBuilder sb = new StringBuilder(parameterModelAnnotation); |
| sb.append("dataType=\""); |
| sb.append(dataType); |
| sb.append("\" "); |
| parameterModelAnnotation = sb.toString(); |
| } |
| } |
| |
| StringBuilder identifierName = new StringBuilder(eClass.getName()); |
| identifierName.append('.'); |
| identifierName.append(methodName); |
| identifierName.append('('); |
| identifierName.append(parameterName); |
| identifierName.append(')'); |
| handleETypedElement(eParameter, parameterName, parameterType, parameterModelAnnotation, identifierName.toString()); |
| eParameter.getEAnnotations().addAll(extractEAnnotations(parameterModelAnnotation)); |
| } |
| } |
| |
| if (exceptionTypes.length > 0) |
| { |
| // Each token in exceptions will specify a data type for the corresponding exception. |
| // |
| List<EGenericType> ecoreEGenericExceptions = new ArrayList<EGenericType>(); |
| String exceptions = getModelAnnotationAttribute(modelAnnotation, "exceptions"); |
| if (exceptions != null) |
| { |
| ecoreEGenericExceptions.addAll(analyzeEGenericTypes(exceptions)); |
| } |
| List<EGenericType> javaEGenericExceptions = new ArrayList<EGenericType>(); |
| for (String exception : exceptionTypes) |
| { |
| Diagnostic diagnostic = EcoreValidator.EGenericTypeBuilder.INSTANCE.parseInstanceTypeName(exception); |
| if (diagnostic.getSeverity() == Diagnostic.OK) |
| { |
| javaEGenericExceptions.add((EGenericType)diagnostic.getData().get(0)); |
| } |
| } |
| match(eOperation.getEGenericExceptions(), javaEGenericExceptions, ecoreEGenericExceptions); |
| } |
| } |
| |
| // If we aren't doing an operation... |
| // |
| if (eTypedElement == null) |
| { |
| // We'll create one of these. |
| // |
| EStructuralFeature eStructuralFeature = null; |
| |
| // If any of these attributes appear, this must be a reference. |
| // |
| String opposite = getModelAnnotationAttribute(modelAnnotation, "opposite"); |
| String keys = getModelAnnotationAttribute(modelAnnotation, "keys"); |
| String containment = getModelAnnotationAttribute(modelAnnotation, "containment"); |
| String resolveProxies = getModelAnnotationAttribute(modelAnnotation, "resolveProxies"); |
| String mapType = getModelAnnotationAttribute(modelAnnotation, "mapType"); |
| String keyType = getModelAnnotationAttribute(modelAnnotation, "keyType"); |
| String valueType = getModelAnnotationAttribute(modelAnnotation, "valueType"); |
| |
| if ("reference".equals(kind) || |
| opposite != null || |
| keys != null || |
| containment != null || |
| resolveProxies != null || |
| mapType != null || |
| keyType != null && valueType != null) |
| { |
| EReference eReference = EcoreFactory.eINSTANCE.createEReference(); |
| eTypedElement = eStructuralFeature = eReference; |
| eClass.getEStructuralFeatures().add(eReference); |
| |
| // Set the EReference attributes. |
| // |
| eReference.setContainment |
| ("true".equals(containment) || |
| mapType != null && !stripTypeArguments(returnType).endsWith("Entry") || |
| keyType != null && valueType != null); |
| eReference.setResolveProxies(eReference.isContainment() ? "true".equals(resolveProxies) : !"false".equals(resolveProxies)); |
| eReference.setUnsettable("true".equals(getModelAnnotationAttribute(modelAnnotation, "unsettable"))); |
| |
| // Defer the handling of the opposite. |
| // |
| if (opposite != null) |
| { |
| eReferenceToOppositeNameMap.put(eReference, opposite); |
| if ("false".equals(getModelAnnotationAttribute(modelAnnotation, "transient"))) |
| { |
| transientEReferenceWithOpposite.add(eReference); |
| } |
| } |
| |
| if (keys != null) |
| { |
| List<String> keyNames = new ArrayList<String>(); |
| for (StringTokenizer stringTokenizer = new StringTokenizer(keys, " ,\t\n\r\f"); stringTokenizer.hasMoreTokens();) |
| { |
| keyNames.add(stringTokenizer.nextToken()); |
| } |
| eReferenceToKeyNamesMap.put(eReference, keyNames); |
| } |
| } |
| else |
| { |
| // Assume that it's an attribute for now. |
| // It will/could become a reference if the type resolves to an EClass. |
| // |
| EAttribute eAttribute = EcoreFactory.eINSTANCE.createEAttribute(); |
| if ("attribute".equals(kind)) |
| { |
| eAttributes.add(eAttribute); |
| } |
| eTypedElement = eStructuralFeature = eAttribute; |
| eClass.getEStructuralFeatures().add(eAttribute); |
| |
| // Set the EAttribute attributes. |
| // |
| eAttribute.setUnsettable("true".equals(getModelAnnotationAttribute(modelAnnotation, "unsettable"))); |
| eAttribute.setID("true".equals(getModelAnnotationAttribute(modelAnnotation, "id"))); |
| String defaultValueLiteral = getModelAnnotationAttribute(modelAnnotation, "defaultValue"); |
| if (defaultValueLiteral == null) |
| { |
| defaultValueLiteral = getModelAnnotationAttribute(modelAnnotation, "default"); |
| } |
| eStructuralFeature.setDefaultValueLiteral(defaultValueLiteral); |
| } |
| |
| // Handle the type, multiplicity and other ETypedElement attributes. |
| // |
| handleETypedElement(eStructuralFeature, featureName, returnType, modelAnnotation, eClass.getName() + "." + methodName); |
| |
| // Set the EStructuralFeature attributes. |
| // |
| eStructuralFeature.setChangeable(!"false".equals(getModelAnnotationAttribute(modelAnnotation, "changeable"))); |
| eStructuralFeature.setDerived("true".equals(getModelAnnotationAttribute(modelAnnotation, "derived"))); |
| eStructuralFeature.setVolatile("true".equals(getModelAnnotationAttribute(modelAnnotation, "volatile"))); |
| eStructuralFeature.setTransient("true".equals(getModelAnnotationAttribute(modelAnnotation, "transient"))); |
| |
| // Set the visibility annotations for the EstructuralFeature. |
| // |
| EcoreUtil.setSuppressedVisibility |
| (eStructuralFeature, EcoreUtil.GET, "true".equals(getModelAnnotationAttribute(modelAnnotation, "suppressedGetVisibility"))); |
| EcoreUtil.setSuppressedVisibility |
| (eStructuralFeature, EcoreUtil.SET, "true".equals(getModelAnnotationAttribute(modelAnnotation, "suppressedSetVisibility"))); |
| EcoreUtil.setSuppressedVisibility |
| (eStructuralFeature, EcoreUtil.IS_SET, "true".equals(getModelAnnotationAttribute(modelAnnotation, "suppressedIsSetVisibility"))); |
| EcoreUtil.setSuppressedVisibility |
| (eStructuralFeature, EcoreUtil.UNSET, "true".equals(getModelAnnotationAttribute(modelAnnotation, "suppressedUnsetVisibility"))); |
| } |
| |
| if (eTypedElement != null) |
| { |
| // Process the annotations. |
| // |
| eTypedElement.getEAnnotations().addAll(extractEAnnotations(modelAnnotation)); |
| if (eTypedElement instanceof EStructuralFeature) |
| { |
| eStructuralFeatures.add((EStructuralFeature)eTypedElement); |
| } |
| } |
| |
| return eTypedElement; |
| } |
| |
| protected List<EGenericType> analyzeEGenericTypes(String genericTypes) |
| { |
| List<EGenericType> result = new ArrayList<EGenericType>(); |
| int depth = 0; |
| int start = -1; |
| int length = genericTypes.length(); |
| for (int i = 0; i < length; i = Character.offsetByCodePoints(genericTypes, i, 1)) |
| { |
| int codePoint = genericTypes.codePointAt(i); |
| if (codePoint == '<') |
| { |
| ++depth; |
| } |
| else if (codePoint == '>') |
| { |
| --depth; |
| } |
| else if (Character.isWhitespace(codePoint)) |
| { |
| if (depth == 0 && start != -1) |
| { |
| EGenericType eGenericType = EcoreValidator.EGenericTypeBuilder.INSTANCE.buildEGenericType(genericTypes.substring(start, i)); |
| result.add(eGenericType); |
| start = -1; |
| } |
| } |
| else if (start == -1) |
| { |
| start = i; |
| } |
| } |
| if (start != -1) |
| { |
| EGenericType eGenericType = EcoreValidator.EGenericTypeBuilder.INSTANCE.buildEGenericType(genericTypes.substring(start)); |
| result.add(eGenericType); |
| } |
| return result; |
| } |
| |
| void analyzeTypeParameters(List<ETypeParameter> eTypeParameters, String[] typeParameters, String modelAnnotation) |
| { |
| if (typeParameters.length > 0) |
| { |
| for (String typeParameter : typeParameters) |
| { |
| Diagnostic diagnostic = EcoreValidator.EGenericTypeBuilder.INSTANCE.parseTypeParameter(typeParameter); |
| if (diagnostic.getSeverity() == Diagnostic.OK) |
| { |
| ETypeParameter eTypeParameter = (ETypeParameter)diagnostic.getData().get(0); |
| List<EGenericType> javaEBounds = new ArrayList<EGenericType>(eTypeParameter.getEBounds()); |
| eTypeParameter.getEBounds().clear(); |
| List<EGenericType> ecoreEBounds = null; |
| String bounds = getModelAnnotationAttribute(getFilteredModelAnnotations(modelAnnotation, eTypeParameter.getName()), "bounds"); |
| if (bounds != null) |
| { |
| ecoreEBounds = analyzeEGenericTypes(bounds); |
| } |
| match(eTypeParameter.getEBounds(), javaEBounds, ecoreEBounds); |
| eTypeParameters.add(eTypeParameter); |
| } |
| } |
| } |
| } |
| |
| protected boolean isListType(String type) |
| { |
| type = stripTypeArguments(type); |
| return "EList".equals(type) || "org.eclipse.emf.common.util.EList".equals(type) || "List".equals(type) || "java.util.List".equals(type); |
| } |
| |
| protected boolean isMapType(String type) |
| { |
| type = stripTypeArguments(type); |
| return "EMap".equals(type) || "org.eclipse.emf.common.util.EMap".equals(type) || "Map".equals(type) || "java.util.Map".equals(type); |
| } |
| |
| protected void handleETypedElement(ETypedElement eTypedElement, String name, String type, String modelAnnotation, String identifierName) |
| { |
| eTypedElement.setName(name); |
| if ("void".equals(type)) return; |
| |
| String mapType = getModelAnnotationAttribute(modelAnnotation, "mapType"); |
| String dataType = getModelAnnotationAttribute(modelAnnotation, "dataType"); |
| String modelType = getModelAnnotationAttribute(modelAnnotation, "type"); |
| String keyType = getModelAnnotationAttribute(modelAnnotation, "keyType"); |
| String valueType = getModelAnnotationAttribute(modelAnnotation, "valueType"); |
| String many = getModelAnnotationAttribute(modelAnnotation, "many"); |
| |
| // For lists, maps, and feature maps, the default is many-valued, which can be overridden by an upper-bound declaration. |
| // |
| if (isListType(type) && (dataType == null || modelType != null && !isListType(modelType))) |
| { |
| eTypedElement.setUpperBound(-1); |
| Diagnostic diagnostic = EcoreValidator.EGenericTypeBuilder.INSTANCE.parseInstanceTypeName(type); |
| if (diagnostic.getSeverity() == Diagnostic.OK) |
| { |
| EGenericType eGenericType = (EGenericType)diagnostic.getData().get(0); |
| if (eGenericType.getETypeArguments().size() == 1) |
| { |
| EGenericType typeArgument = eGenericType.getETypeArguments().get(0); |
| if (typeArgument.getEClassifier() != null || typeArgument.getETypeParameter() != null) |
| { |
| type = eGenericTypeConverter.toJavaInstanceTypeName(typeArgument); |
| if (modelType == null) |
| { |
| modelType = type; |
| } |
| } |
| else |
| { |
| eTypedElement.setUpperBound(1); |
| } |
| } |
| } |
| if (modelType == null && !"false".equals(many) && eTypedElement.getUpperBound() != 1) |
| { |
| error(CodeGenEcorePlugin.INSTANCE.getString("_UI_TheTypeMustBeSpecifiedFor_message", new Object [] { identifierName })); |
| modelType = "java.lang.Object"; |
| } |
| } |
| else if (mapType != null || |
| keyType != null && valueType != null || |
| "FeatureMap".equals(type) || |
| "org.eclipse.emf.common.util.FeatureMap".equals(type)) |
| { |
| eTypedElement.setUpperBound(-1); |
| } |
| |
| if (many != null) |
| { |
| eTypedElement.setUpperBound("true".equals(many) ? -1 : 1); |
| } |
| |
| eTypedElement.setLowerBound("true".equals(getModelAnnotationAttribute(modelAnnotation, "required")) ? 1 : 0); |
| |
| String lowerBound = getModelAnnotationAttribute(modelAnnotation, "lowerBound"); |
| if (lowerBound == null) |
| { |
| lowerBound = getModelAnnotationAttribute(modelAnnotation, "lower"); |
| } |
| if (lowerBound != null) |
| { |
| try |
| { |
| eTypedElement.setLowerBound(Integer.parseInt(lowerBound)); |
| } |
| catch (NumberFormatException exception) |
| { |
| warning(JavaImporterPlugin.INSTANCE.getString("_UI_ValueOfPropertyIsBad_message", new Object [] { lowerBound, "lower", identifierName })); |
| } |
| } |
| String upperBound = getModelAnnotationAttribute(modelAnnotation, "upperBound"); |
| if (upperBound == null) |
| { |
| upperBound = getModelAnnotationAttribute(modelAnnotation, "upper"); |
| } |
| if (upperBound != null) |
| { |
| try |
| { |
| eTypedElement.setUpperBound(Integer.parseInt(upperBound)); |
| } |
| catch (NumberFormatException exception) |
| { |
| warning(JavaImporterPlugin.INSTANCE.getString("_UI_ValueOfPropertyIsBad_message", new Object [] { upperBound, "upper", identifierName })); |
| } |
| } |
| |
| EGenericType ecoreEGenericType = null; |
| EGenericType javaEGenericType = null; |
| |
| // The type can be augmented by specifying the it explicitly in the annotation. |
| // This mostly makes sense only for many-valued typed elements, where the Java |
| // type is a list and the item type needs to be specified. |
| // |
| if (modelType != null) |
| { |
| Diagnostic diagnostic = EcoreValidator.EGenericTypeBuilder.INSTANCE.parseInstanceTypeName(modelType); |
| if (diagnostic.getSeverity() == Diagnostic.OK) |
| { |
| javaEGenericType = (EGenericType)diagnostic.getData().get(0); |
| } |
| type = modelType; |
| } |
| |
| if (mapType != null || keyType != null && valueType != null) |
| { |
| EPackage ePackage = (EPackage)EcoreUtil.getRootContainer(eTypedElement); |
| EGenericType ecoreKeyEGenericType = null; |
| EGenericType javaKeyEGenericType = null; |
| EGenericType ecoreValueEGenericType = null; |
| EGenericType javaValueEGenericType = null; |
| if (mapType != null) |
| { |
| Diagnostic diagnostic = EcoreValidator.EGenericTypeBuilder.INSTANCE.parseInstanceTypeName(mapType); |
| if (diagnostic.getSeverity() == Diagnostic.OK) |
| { |
| ecoreEGenericType = (EGenericType)diagnostic.getData().get(0); |
| resolve(eTypedElement, ecoreEGenericType, RequiredClassifierType.CLASS); |
| List<EGenericType> mapETypeArguments = ecoreEGenericType.getETypeArguments(); |
| if (mapETypeArguments.size() == 2) |
| { |
| ecoreKeyEGenericType = mapETypeArguments.get(0); |
| ecoreValueEGenericType = mapETypeArguments.get(1); |
| } |
| mapETypeArguments.clear(); |
| } |
| else |
| { |
| ecoreEGenericType = resolve(eTypedElement, mapType, RequiredClassifierType.CLASS, false); |
| } |
| if (ecoreEGenericType.getERawType().getEPackage() == null) |
| { |
| if (keyType == null || valueType == null && isMapType(type)) |
| { |
| diagnostic = EcoreValidator.EGenericTypeBuilder.INSTANCE.parseInstanceTypeName(type); |
| if (diagnostic.getSeverity() == Diagnostic.OK) |
| { |
| EGenericType eGenericType = (EGenericType)diagnostic.getData().get(0); |
| if (eGenericType.getETypeArguments().size() == 2) |
| { |
| javaKeyEGenericType = eGenericType.getETypeArguments().get(0); |
| javaValueEGenericType = eGenericType.getETypeArguments().get(1); |
| } |
| } |
| } |
| if (javaKeyEGenericType == null) |
| { |
| javaKeyEGenericType = resolve(eTypedElement, keyType == null ? "java.lang.Object" : keyType, RequiredClassifierType.NONE, false); |
| } |
| if (javaValueEGenericType == null) |
| { |
| javaValueEGenericType = resolve(eTypedElement, valueType == null ? "java.lang.Object" : valueType, RequiredClassifierType.NONE, false); |
| } |
| } |
| } |
| else |
| { |
| javaKeyEGenericType = resolve(eTypedElement, keyType, RequiredClassifierType.NONE, false); |
| javaValueEGenericType = resolve(eTypedElement, valueType, RequiredClassifierType.NONE, false); |
| ecoreEGenericType = resolveMapEntry(ePackage, javaKeyEGenericType, javaValueEGenericType); |
| if (ecoreEGenericType == null) |
| { |
| ecoreEGenericType = |
| resolve |
| (eTypedElement, |
| javaKeyEGenericType.getERawType().getName() + "To" + javaValueEGenericType.getERawType().getName() + "MapEntry", |
| RequiredClassifierType.CLASS, |
| false); |
| } |
| } |
| |
| EClass mapEClass = (EClass)ecoreEGenericType.getEClassifier(); |
| if (mapEClass.getEPackage() == null && mapEClass.getEStructuralFeatures().isEmpty()) |
| { |
| eModelElementToJNodeMap.put(mapEClass, getJCompilationUnit(eTypedElement)); |
| mapEClass.setInstanceTypeName("java.util.Map$Entry"); |
| mapEClass.setAbstract(false); |
| mapEClass.setInterface(false); |
| if (ecoreKeyEGenericType == null) |
| { |
| createFeature(mapEClass, "key", javaKeyEGenericType); |
| ecoreEGenericTypeToJavaEGenericTypeMap.put(javaKeyEGenericType, null); |
| } |
| else |
| { |
| createFeature(mapEClass, "key", ecoreKeyEGenericType); |
| ecoreEGenericTypeToJavaEGenericTypeMap.put(ecoreKeyEGenericType, javaKeyEGenericType); |
| } |
| if (ecoreValueEGenericType == null) |
| { |
| createFeature(mapEClass, "value", javaValueEGenericType); |
| ecoreEGenericTypeToJavaEGenericTypeMap.put(javaValueEGenericType, null); |
| } |
| else |
| { |
| createFeature(mapEClass, "value", ecoreValueEGenericType); |
| ecoreEGenericTypeToJavaEGenericTypeMap.put(ecoreValueEGenericType, javaValueEGenericType); |
| } |
| EAnnotation container = ePackage.getEAnnotation(MAP_ENTRY_CLASS_CONTAINER_ANNOTATION_SOURCE); |
| if (container == null) |
| { |
| container = EcoreFactory.eINSTANCE.createEAnnotation(); |
| container.setSource(MAP_ENTRY_CLASS_CONTAINER_ANNOTATION_SOURCE); |
| ePackage.getEAnnotations().add(container); |
| } |
| container.getContents().add(mapEClass); |
| } |
| } |
| else if (dataType != null) |
| { |
| Diagnostic diagnostic = EcoreValidator.EGenericTypeBuilder.INSTANCE.parseInstanceTypeName(dataType); |
| if (diagnostic.getSeverity() == Diagnostic.OK) |
| { |
| ecoreEGenericType = (EGenericType)diagnostic.getData().get(0); |
| diagnostic = EcoreValidator.EGenericTypeBuilder.INSTANCE.parseInstanceTypeName(type); |
| if (diagnostic.getSeverity() == Diagnostic.OK) |
| { |
| javaEGenericType = (EGenericType)diagnostic.getData().get(0); |
| } |
| } |
| else |
| { |
| ecoreEGenericType = EcoreFactory.eINSTANCE.createEGenericType(); |
| ecoreEGenericType.setEClassifier(EcorePackage.Literals.EJAVA_OBJECT); |
| basicDiagnostic.add(diagnostic); |
| } |
| } |
| else |
| { |
| Diagnostic diagnostic = EcoreValidator.EGenericTypeBuilder.INSTANCE.parseInstanceTypeName(type); |
| if (diagnostic.getSeverity() == Diagnostic.OK) |
| { |
| javaEGenericType = (EGenericType)diagnostic.getData().get(0); |
| } |
| else |
| { |
| ecoreEGenericType = EcoreFactory.eINSTANCE.createEGenericType(); |
| ecoreEGenericType.setEClassifier(EcorePackage.Literals.EJAVA_OBJECT); |
| basicDiagnostic.add(diagnostic); |
| } |
| } |
| |
| if (ecoreEGenericType == null) |
| { |
| eTypedElement.setEGenericType(javaEGenericType); |
| ecoreEGenericTypeToJavaEGenericTypeMap.put(javaEGenericType, null); |
| } |
| else |
| { |
| eTypedElement.setEGenericType(ecoreEGenericType); |
| ecoreEGenericTypeToJavaEGenericTypeMap.put(ecoreEGenericType, javaEGenericType); |
| } |
| |
| eTypedElement.setUnique(!"false".equals(getModelAnnotationAttribute(modelAnnotation, "unique"))); |
| eTypedElement.setOrdered(!"false".equals(getModelAnnotationAttribute(modelAnnotation, "ordered"))); |
| } |
| |
| protected JCompilationUnit getJCompilationUnit(EModelElement eModelElement) |
| { |
| for (; eModelElement != null; eModelElement = (EModelElement)eModelElement.eContainer()) |
| { |
| JCompilationUnit compilationUnit = facadeHelper.getCompilationUnit(eModelElementToJNodeMap.get(eModelElement)); |
| if (compilationUnit != null) |
| { |
| return compilationUnit; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Strips the type arguments from the type, if it has any. |
| */ |
| protected String stripTypeArguments(String type) |
| { |
| int start = type.indexOf('<'); |
| if (start > 0) |
| { |
| int end = type.lastIndexOf('>'); |
| if (end > start+1) |
| { |
| return type.substring(0, start).trim(); |
| } |
| } |
| return type; |
| } |
| |
| protected EStructuralFeature createFeature(EClass eClass, String name, EGenericType eGenericType) |
| { |
| EClassifier eClassifier = eGenericType.getERawType(); |
| if (eClassifier instanceof EClass) |
| { |
| EReference eReference = EcoreFactory.eINSTANCE.createEReference(); |
| eReference.setName(name); |
| eReference.setEGenericType(eGenericType); |
| eClass.getEStructuralFeatures().add(eReference); |
| eStructuralFeatures.add(eReference); |
| return eReference; |
| } |
| else |
| { |
| EAttribute eAttribute = EcoreFactory.eINSTANCE.createEAttribute(); |
| eAttribute.setName(name); |
| eAttribute.setEGenericType(eGenericType); |
| eClass.getEStructuralFeatures().add(eAttribute); |
| eStructuralFeatures.add(eAttribute); |
| return eAttribute; |
| } |
| } |
| |
| /** |
| * Creates EEnumLiteral as appropriate. |
| * @return <code>true</code> when the method has created an EnumLiteral after |
| * analyzing the JMember |
| */ |
| protected boolean analyzeEnumLiteral(EEnum eEnum, JMember member) |
| { |
| String modelAnnotation = getModelAnnotation(member.getComment()); |
| if (modelAnnotation != null) |
| { |
| String fieldName = member.getName(); |
| |
| EEnumLiteral eEnumLiteral = EcoreFactory.eINSTANCE.createEEnumLiteral(); |
| eModelElementToJNodeMap.put(eEnumLiteral, member); |
| eEnumLiteral.getEAnnotations().addAll(extractEAnnotations(modelAnnotation)); |
| EcoreUtil.setDocumentation(eEnumLiteral, getModelDocumentation(member.getComment())); |
| |
| // Allow the value to be defined by an annotation or by the field's initializer |
| // |
| String value = getModelAnnotationAttribute(modelAnnotation, "value"); |
| if (value == null) |
| { |
| if (member instanceof JField) |
| { |
| String initializer = ((JField)member).getInitializer(); |
| if (initializer != null) |
| { |
| value = initializer.trim(); |
| } |
| } |
| } |
| |
| if (value != null) |
| { |
| try |
| { |
| int intValue = Integer.parseInt(value); |
| eEnumLiteral.setValue(intValue); |
| } |
| catch (NumberFormatException exception) |
| { |
| warning |
| (JavaImporterPlugin.INSTANCE.getString |
| ("_UI_InvalidLiteralValueForField", |
| new Object [] { value, eEnum.getName() + "." + fieldName })); |
| eEnumLiteral.setValue(eEnum.getELiterals().size()); |
| } |
| } |
| else |
| { |
| eEnumLiteral.setValue(eEnum.getELiterals().size()); |
| } |
| |
| // Allow a mixed case version of the name to be provided. |
| // |
| String name = getModelAnnotationAttribute(modelAnnotation, "name"); |
| |
| if (fieldName.endsWith("_VALUE") && (name == null || !name.toLowerCase().endsWith("value"))) |
| { |
| fieldName = fieldName.substring(0, fieldName.length()-"_VALUE".length()); |
| } |
| |
| // But, if name doesn't expand into field name, ignore it. |
| // |
| if (name == null || !CodeGenUtil.format(name, '_', null, false, true).toUpperCase().equals(fieldName)) |
| { |
| if (name != null) |
| { |
| warning |
| (JavaImporterPlugin.INSTANCE.getString |
| ("_UI_InvalidLiteralNameForField", new Object[] { name, eEnum.getName() + "." + fieldName })); |
| } |
| name = fieldName; |
| } |
| eEnumLiteral.setName(name); |
| |
| // Allow a distinct literal value to be provided, too. |
| // |
| String literal = getModelAnnotationAttribute(modelAnnotation, "literal"); |
| if (literal != null) |
| { |
| eEnumLiteral.setLiteral(literal); |
| } |
| |
| return eEnum.getELiterals().add(eEnumLiteral); |
| } |
| return false; |
| } |
| |
| /** |
| * The pattern for extracting the model documentation. |
| */ |
| protected static Pattern modelDocExpression = |
| Pattern.compile |
| ("<!--\\s*begin-model-doc\\s*-->[ \\f\\n\\r\\t]*\\*\\s?(.*?)<!--\\s*end-model-doc\\s*-->", |
| Pattern.MULTILINE | Pattern.DOTALL); |
| |
| /** |
| * Returns the model documentation, or null. |
| */ |
| protected String getModelDocumentation(String comment) |
| { |
| if (comment != null) |
| { |
| Matcher matcher = modelDocExpression.matcher(comment); |
| if (matcher.find()) |
| { |
| return comment.substring(matcher.start(1), matcher.end(1)).replaceAll("[\\n\\r]*\\s*\\*[\\s]?", "\n").replaceAll("\\s*$", ""); |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * The pattern for extracting the @model annotations. |
| */ |
| protected static Pattern modelAnnotationExpression = |
| Pattern.compile |
| ("@[ \\f\\n\\r\\t*]*model[ \\f\\n\\r\\t*]*((\\w*\\s*=\\s*(['\"])(?>\\\\.|.)*?\\3[ \\f\\n\\r\\t*]*)*)", |
| Pattern.MULTILINE); |
| |
| /** |
| * Returns the @model annotation contents, or null. |
| */ |
| protected String getModelAnnotation(String comment) |
| { |
| if (comment != null) |
| { |
| Matcher matcher = modelAnnotationExpression.matcher(comment); |
| if (matcher.find()) |
| { |
| return CodeGenUtil.xmlEscapeDecode(comment.substring(matcher.start(1), matcher.end(1))); |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * The pattern for extracting the @extends annotations. |
| */ |
| protected static Pattern extendsAnnotationExpression = Pattern.compile("@\\s*extends\\s*([.\\w<>,?\\s]+)", Pattern.MULTILINE); |
| |
| /** |
| * The pattern for extracting the @implements annotations. |
| */ |
| protected static Pattern implementsAnnotationExpression = Pattern.compile("@\\s*implements\\s*([.\\w<>,?\\s]+)", Pattern.MULTILINE); |
| |
| /** |
| * Returns the @extends/@implements annotation contents, or null. |
| */ |
| protected String getExtendsAnnotation(String comment) |
| { |
| if (comment != null) |
| { |
| StringBuilder result = new StringBuilder(); |
| Matcher extendsMatcher = extendsAnnotationExpression.matcher(comment); |
| while (extendsMatcher.find()) |
| { |
| if (result.length() != 0) |
| { |
| result.append(','); |
| } |
| result.append(comment.substring(extendsMatcher.start(1), extendsMatcher.end(1))); |
| } |
| |
| Matcher implementsMatcher = implementsAnnotationExpression.matcher(comment); |
| while (implementsMatcher.find()) |
| { |
| if (result.length() != 0) |
| { |
| result.append(','); |
| } |
| result.append(comment.substring(implementsMatcher.start(1), implementsMatcher.end(1))); |
| } |
| |
| return result.length() == 0 ? null : result.toString(); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns the unquoted value of attribute-name="value" or of attribute-name='value', or null. |
| */ |
| protected String getModelAnnotationAttribute(String modelAnnotation, String attributeName) |
| { |
| Pattern modelAnnotationAttributeExpressionDoubleQuote = |
| Pattern.compile |
| ("\\b" + attributeName + "\\s*=\\s*([\"'])((?>\\\\.|.)*?)\\1", |
| Pattern.MULTILINE); |
| Matcher matcher = modelAnnotationAttributeExpressionDoubleQuote.matcher(modelAnnotation); |
| if (matcher.find()) |
| { |
| return modelAnnotation.substring(matcher.start(2), matcher.end(2)); |
| } |
| else |
| { |
| return null; |
| } |
| } |
| |
| /** |
| * Returns the space separated concatenation of the unquoted value of each attribute-name="value" or attribute-name='value' repeat, or null. |
| */ |
| protected String getModelAnnotationAttributes(String modelAnnotation, String attributeName) |
| { |
| StringBuilder result = null; |
| Pattern modelAnnotationAttributeExpressionDoubleQuote = |
| Pattern.compile |
| ("\\b" + attributeName + "\\s*=\\s*([\"'])((?>\\\\.|.)*?)\\1", |
| Pattern.MULTILINE); |
| for (Matcher matcher = modelAnnotationAttributeExpressionDoubleQuote.matcher(modelAnnotation); matcher.find();) |
| { |
| if (result == null) |
| { |
| result = new StringBuilder(); |
| } |
| else |
| { |
| result.append(' '); |
| } |
| result.append(modelAnnotation.subSequence(matcher.start(2), matcher.end(2))); |
| } |
| |
| return result == null ? null : result.toString(); |
| } |
| |
| protected static Pattern eAnnotationExpression = Pattern.compile("\\G\\s*((?>\\\\.|\\S)+)((?:\\s+(?>\\\\.|\\S)+\\s*+=\\s*(['\"])((?>\\\\.|.)*?)\\3)*)"); |
| |
| protected static Pattern eAnnotationDetailExpression = Pattern.compile("\\s+((?>\\\\.|\\S)+)\\s*+=\\s*((['\"])((?>\\\\.|.)*?)\\3)"); |
| |
| protected List<EAnnotation> extractEAnnotations(String modelAnnotation) |
| { |
| List<EAnnotation> result = Collections.emptyList(); |
| String annotations = getModelAnnotationAttributes(modelAnnotation, "annotation"); |
| if (annotations != null) |
| { |
| for (Matcher matcher = eAnnotationExpression.matcher(annotations); matcher.find();) |
| { |
| if (result == Collections.EMPTY_LIST) |
| { |
| result = new ArrayList<EAnnotation>(); |
| } |
| EAnnotation eAnnotation = EcoreFactory.eINSTANCE.createEAnnotation(); |
| result.add(eAnnotation); |
| eAnnotation.setSource(parseString(matcher.group(1))); |
| EMap<String, String> details = eAnnotation.getDetails(); |
| for (Matcher detailMatcher = eAnnotationDetailExpression.matcher(matcher.group(2)); detailMatcher.find();) |
| { |
| details.put(parseString(detailMatcher.group(1)), parseString(detailMatcher.group(4))); |
| } |
| } |
| } |
| |
| String extendedMetaDataAnnotations = getModelAnnotationAttributes(modelAnnotation, "extendedMetaData"); |
| if (extendedMetaDataAnnotations != null) |
| { |
| if (result == Collections.EMPTY_LIST) |
| { |
| result = new ArrayList<EAnnotation>(); |
| } |
| EAnnotation eAnnotation = EcoreFactory.eINSTANCE.createEAnnotation(); |
| result.add(eAnnotation); |
| eAnnotation.setSource(ExtendedMetaData.ANNOTATION_URI); |
| EMap<String, String> details = eAnnotation.getDetails(); |
| for (Matcher detailMatcher = eAnnotationDetailExpression.matcher(" " + extendedMetaDataAnnotations); detailMatcher.find();) |
| { |
| details.put(parseString(detailMatcher.group(1)), parseString(detailMatcher.group(4))); |
| } |
| } |
| |
| return result; |
| } |
| |
| private static String parseString(String stringLiteralBody) |
| { |
| return CodeGenUtil.parseString(stringLiteralBody); |
| } |
| |
| /** |
| * Returns the filtered matches xyzAttribute-name="value" or of xyxAttribute-name='value', or null. |
| */ |
| protected String getFilteredModelAnnotations(String modelAnnotation, String filter) |
| { |
| StringBuilder result = new StringBuilder(); |
| Pattern modelAnnotationAttributeExpressionDoubleQuote = |
| Pattern.compile |
| ("\\b" + filter + "([A-Z]\\w*\\s*=\\s*([\"'])((?>\\\\.|.)*?)\\2)", |
| Pattern.MULTILINE); |
| int start = 0; |
| int end = modelAnnotation.length(); |
| Matcher matcher; |
| while ((matcher = modelAnnotationAttributeExpressionDoubleQuote.matcher(modelAnnotation.subSequence(start, end))).find()) |
| { |
| result.append(modelAnnotation.substring(start + matcher.start(1), start + matcher.start(1) + 1).toLowerCase()); |
| result.append(modelAnnotation.substring(start + matcher.start(1) + 1, start + matcher.end(1))); |
| result.append(' '); |
| start += matcher.end(0); |
| } |
| return result.toString(); |
| } |
| |
| protected void match(List<EGenericType> target, List<EGenericType> javaEGenericTypes, List<EGenericType> ecoreEGenericTypes) |
| { |
| for (Iterator<EGenericType> i = javaEGenericTypes.iterator(), j = ecoreEGenericTypes == null ? null : ecoreEGenericTypes.iterator(); i.hasNext(); ) |
| { |
| EGenericType javaEGenericType = i.next(); |
| EGenericType ecoreEGenericType = j != null && j.hasNext() ? j.next() : null; |
| |
| // If there is no Ecore representation or it's invalid, e.g., when - is used to omit a specification, then just use the Java representation. |
| // |
| if (ecoreEGenericType == null || ecoreEGenericType.getEClassifier() == null) |
| { |
| ecoreEGenericTypeToJavaEGenericTypeMap.put(javaEGenericType, null); |
| target.add(javaEGenericType); |
| } |
| else |
| { |
| ecoreEGenericTypeToJavaEGenericTypeMap.put(ecoreEGenericType, javaEGenericType); |
| target.add(ecoreEGenericType); |
| } |
| } |
| } |
| |
| protected void resolve(EModelElement eModelElement, EGenericType ecoreEGenericType, EGenericType javaEGenericType) |
| { |
| EClassifier eClassifier = ecoreEGenericType.getERawType(); |
| if (eClassifier.getEPackage() == null) |
| { |
| if (javaEGenericType != null) |
| { |
| ETypeParameter eTypeParameter = javaEGenericType.getETypeParameter(); |
| if (eTypeParameter != null) |
| { |
| eClassifier = null; |
| ecoreEGenericType.setEClassifier(null); |
| ecoreEGenericType.setETypeParameter(eTypeParameter); |
| } |
| else |
| { |
| eClassifier.setInstanceTypeName |
| (ecoreEGenericType.getETypeArguments().isEmpty() ? |
| eGenericTypeConverter.toJavaInstanceTypeName(javaEGenericType) : |
| javaEGenericType.getEClassifier().getInstanceTypeName()); |
| } |
| } |
| |
| if (eClassifier != null) |
| { |
| EPackage ePackage = (EPackage)EcoreUtil.getRootContainer(eModelElement); |
| addToPackage(ePackage, eClassifier); |
| if (eClassifier instanceof EClass) |
| { |
| for (EStructuralFeature eStructuralFeature : ((EClass)eClassifier).getEStructuralFeatures()) |
| { |
| resolve(eStructuralFeature, eStructuralFeature.getEGenericType(), (EGenericType)null); |
| } |
| } |
| } |
| } |
| EGenericType ecoreEUpperBound = ecoreEGenericType.getEUpperBound(); |
| if (ecoreEUpperBound != null) |
| { |
| resolve(eModelElement, ecoreEUpperBound, javaEGenericType == null ? null : javaEGenericType.getEUpperBound()); |
| } |
| else |
| { |
| EGenericType ecoreELowerBound = ecoreEGenericType.getELowerBound(); |
| if (ecoreELowerBound != null) |
| { |
| resolve(eModelElement, ecoreELowerBound, javaEGenericType == null ? null : javaEGenericType.getELowerBound()); |
| } |
| } |
| |
| if (eClassifier != null) |
| { |
| int index = 0; |
| for (Iterator<EGenericType> |
| i = ecoreEGenericType.getETypeArguments().iterator(), |
| j = javaEGenericType == null ? null : javaEGenericType.getETypeArguments().iterator(); |
| i.hasNext(); |
| ++index) |
| { |
| EGenericType eTypeArgument = i.next(); |
| EGenericType eTypeArgumentInstance = j != null && j.hasNext() ? j.next() : null; |
| if (demandCreatedEClassifiers.contains(eClassifier) && eClassifier.getETypeParameters().size() <= index) |
| { |
| ETypeParameter eTypeParameter = EcoreFactory.eINSTANCE.createETypeParameter(); |
| eTypeParameter.setName(index == 0? "T" : "T" + index); |
| eClassifier.getETypeParameters().add(eTypeParameter); |
| } |
| resolve(eModelElement, eTypeArgument, eTypeArgumentInstance); |
| } |
| } |
| } |
| |
| protected void resolve(EModelElement eModelElement, EGenericType eGenericType, RequiredClassifierType requiredClassifierType) |
| { |
| EClassifier eClassifier = eGenericType.getEClassifier(); |
| if (eClassifier != null) |
| { |
| if (eClassifier.eContainer() == null) |
| { |
| EGenericType resolvedEGenericType = resolve(eModelElement, eClassifier.getInstanceTypeName(), requiredClassifierType, false); |
| eGenericType.setETypeParameter(resolvedEGenericType.getETypeParameter()); |
| eGenericType.setEClassifier(resolvedEGenericType.getEClassifier()); |
| } |
| else if (eClassifier.getEPackage() == null) |
| { |
| EGenericType resolvedEGenericType = resolve(eModelElement, eClassifier.getName(), requiredClassifierType, false); |
| if (resolvedEGenericType.getERawType().getEPackage() == null) |
| { |
| EPackage ePackage = (EPackage)EcoreUtil.getRootContainer(eClassifier); |
| addToPackage(ePackage, eClassifier); |
| } |
| else |
| { |
| eGenericType.setETypeParameter(null); |
| eGenericType.setEClassifier(resolvedEGenericType.getEClassifier()); |
| } |
| } |
| for (EGenericType eTypeArgument : eGenericType.getETypeArguments()) |
| { |
| resolve(eModelElement, eTypeArgument, RequiredClassifierType.NONE); |
| } |
| } |
| else |
| { |
| EGenericType eUpperBound = eGenericType.getEUpperBound(); |
| if (eUpperBound != null) |
| { |
| resolve(eModelElement, eUpperBound, RequiredClassifierType.NONE); |
| } |
| else |
| { |
| EGenericType eLowerBound = eGenericType.getELowerBound(); |
| if (eLowerBound != null) |
| { |
| resolve(eModelElement, eLowerBound, RequiredClassifierType.NONE); |
| } |
| } |
| } |
| } |
| |
| protected enum RequiredClassifierType |
| { |
| NONE |
| { |
| @Override |
| boolean isValid(EClassifier eClassifier) |
| { |
| return eClassifier != null; |
| } |
| }, |
| CLASS |
| { |
| @Override |
| boolean isValid(EClassifier eClassifier) |
| { |
| return eClassifier instanceof EClass; |
| } |
| }, |
| DATA_TYPE |
| { |
| @Override |
| boolean isValid(EClassifier eClassifier) |
| { |
| return eClassifier instanceof EDataType; |
| } |
| }; |
| |
| abstract boolean isValid(EClassifier eClassifier); |
| } |
| |
| protected EGenericType resolve(EModelElement eModelElement, String typeName, RequiredClassifierType requiredClassifierType, boolean recordDemandCreatedEClassifier) |
| { |
| EPackage ePackage = (EPackage)EcoreUtil.getRootContainer(eModelElement); |
| |
| // We want to resolve to this. |
| // |
| EGenericType eGenericType = null; |
| |
| // Check if it is exactly the name of a type parameter that's in scope. |
| // |
| for (EObject eObject = eModelElement; eObject != null; eObject = eObject.eContainer()) |
| { |
| EList<ETypeParameter> eTypeParameters = null; |
| if (eObject instanceof EOperation) |
| { |
| eTypeParameters = ((EOperation)eObject).getETypeParameters(); |
| } |
| else if (eObject instanceof EClass) |
| { |
| eTypeParameters = ((EClass)eObject).getETypeParameters(); |
| } |
| // If we've found thing with type parameters, search them for a match. |
| // |
| if (eTypeParameters != null) |
| { |
| for (ETypeParameter localETypeParameter : eTypeParameters) |
| { |
| if (typeName.equals(localETypeParameter.getName())) |
| { |
| // If we find a match, make this generic type be for this type parameter, |
| // and then stop the whole process. |
| eGenericType = EcoreFactory.eINSTANCE.createEGenericType(); |
| eGenericType.setETypeParameter(localETypeParameter); |
| return eGenericType; |
| } |
| } |
| } |
| } |
| |
| // Check if the name is qualified |
| // |
| String baseName = typeName; |
| String packageName = ""; |
| int index = baseName.lastIndexOf("."); |
| |
| // For arrays, consider the element type. |
| // |
| int i = baseName.indexOf('['); |
| String elementTypeName = i == -1 ? baseName : baseName.substring(0, i); |
| |
| if (index == -1) |
| { |
| // Look through the imports of the containing compilation unit. |
| // |
| JCompilationUnit compilationUnit = getJCompilationUnit(eModelElement); |
| if (compilationUnit != null) |
| { |
| boolean firstType = true; |
| LOOP: |
| for (JType jType : facadeHelper.getChildren(compilationUnit, JType.class)) |
| { |
| String name = jType.getName(); |
| if (elementTypeName.equals(name)) |
| { |
| packageName = facadeHelper.getPackage(compilationUnit).getQualifiedName(); |
| typeName = packageName + "." + baseName; |
| break; |
| } |
| else if (firstType) |
| { |
| for (JAbstractType jAbstractType : facadeHelper.getChildren(jType, JAbstractType.class)) |
| { |
| if (!(jAbstractType instanceof JAnnotationType)) |
| { |
| String nestedName = jAbstractType.getName(); |
| if (elementTypeName.equals(nestedName)) |
| { |
| packageName = facadeHelper.getPackage(compilationUnit).getQualifiedName(); |
| typeName = packageName + "." + name + '$' + baseName; |
| break LOOP; |
| } |
| } |
| } |
| firstType = false; |
| } |
| } |
| |
| // Find an explicit import or the first wildcard import. |
| // |
| if (packageName.length() == 0) |
| { |
| boolean firstWildcard = true; |
| for (JImport jImport : facadeHelper.getChildren(compilationUnit, JImport.class)) |
| { |
| String importName = jImport.getName(); |
| if (importName.endsWith("." + elementTypeName)) |
| { |
| int importIndex = importName.lastIndexOf("."); |
| packageName = importName.substring(0, importIndex); |
| typeName = packageName + "." + baseName; |
| break; |
| } |
| else if (firstWildcard && importName.endsWith(".*")) |
| { |
| int importIndex = importName.lastIndexOf("."); |
| packageName = importName.substring(0, importIndex); |
| typeName = packageName + "." + baseName; |
| firstWildcard = false; |
| } |
| } |
| } |
| |
| // Find the modeled package for the import and look up the name there. |
| // |
| EPackage otherEPackage = packageNameToEPackageMap.get(packageName); |
| if (otherEPackage == null) |
| { |
| otherEPackage = externalPackageNameToEPackageMap.get(packageName); |
| } |
| if (otherEPackage != null) |
| { |
| EClassifier eClassifier = otherEPackage.getEClassifier(baseName); |
| if (requiredClassifierType.isValid(eClassifier)) |
| { |
| eGenericType = EcoreFactory.eINSTANCE.createEGenericType(); |
| eGenericType.setEClassifier(eClassifier); |
| } |
| } |
| } |
| |
| // If we can't find it, try the simple name in the package... |
| // |
| if (eGenericType == null && "".equals(packageName)) |
| { |
| EClassifier eClassifier = ePackage.getEClassifier(baseName); |
| if (requiredClassifierType.isValid(eClassifier)) |
| { |
| eGenericType = EcoreFactory.eINSTANCE.createEGenericType(); |
| eGenericType.setEClassifier(eClassifier); |
| } |
| else if (i != -1 && CodeGenUtil.isJavaLangType(elementTypeName)) |
| { |
| packageName = "java.lang"; |
| typeName = packageName + "." + baseName; |
| } |
| } |
| } |
| else |
| { |
| // Find the modeled package for the name and look up the name there. |
| // |
| packageName = baseName.substring(0, index); |
| if (eModelElement.eContainer() instanceof EClassifier && |
| packageName.equals(((EClassifier)eModelElement.eContainer()).getName())) |
| { |
| JCompilationUnit compilationUnit = getJCompilationUnit(eModelElement); |
| if (compilationUnit != null) |
| { |
| typeName = packageName; |
| packageName = facadeHelper.getPackage(compilationUnit).getQualifiedName(); |
| typeName = packageName + "." + typeName + "$" + baseName.substring(index + 1); |
| } |
| } |
| else |
| { |
| baseName = baseName.substring(index + 1); |
| EPackage otherEPackage = packageNameToEPackageMap.get(packageName); |
| if (otherEPackage == null) |
| { |
| otherEPackage = externalPackageNameToEPackageMap.get(packageName); |
| if (otherEPackage == null) |
| { |
| if ("org.eclipse.emf.ecore".equals(packageName)) |
| { |
| otherEPackage = EcorePackage.eINSTANCE; |
| } |
| else if ("org.eclipse.emf.ecore.xml.type".equals(packageName)) |
| { |
| otherEPackage = XMLTypePackage.eINSTANCE; |
| } |
| else if ("org.eclipse.emf.ecore.xml.namespace".equals(packageName)) |
| { |
| otherEPackage = XMLNamespacePackage.eINSTANCE; |
| } |
| } |
| } |
| if (otherEPackage != null) |
| { |
| EClassifier eClassifier = otherEPackage.getEClassifier(baseName); |
| if (eClassifier != null) |
| { |
| eGenericType = EcoreFactory.eINSTANCE.createEGenericType(); |
| eGenericType.setEClassifier(eClassifier); |
| } |
| } |
| } |
| } |
| |
| // If we still don't have one, we'll have to settle for an EDataType or EClass with an instance class name. |
| // |
| if (eGenericType == null) |
| { |
| // See if we already have the EDataType. |
| // |
| String modifiedName = typeName.replace('$', '.'); |
| for (EClassifier ePackageClassifier : ePackage.getEClassifiers()) |
| { |
| String name = ePackageClassifier.getInstanceTypeName(); |
| if (name != null && |
| name.replace('$', '.').equals(modifiedName) && |
| requiredClassifierType.isValid(ePackageClassifier)) |
| { |
| eGenericType = EcoreFactory.eINSTANCE.createEGenericType(); |
| eGenericType.setEClassifier(ePackageClassifier); |
| break; |
| } |
| } |
| } |
| |
| if (eGenericType == null && |
| EcorePackage.Literals.EOBJECT.getInstanceTypeName().equals(typeName) && |
| requiredClassifierType != RequiredClassifierType.DATA_TYPE) |
| { |
| eGenericType = EcoreFactory.eINSTANCE.createEGenericType(); |
| eGenericType.setEClassifier(EcorePackage.Literals.EOBJECT); |
| } |
| |
| // Just to be helpful, we'll recognize a type of org.eclipse.emf.ecore.util.FeatureMap and convert it to EFeatureMapEntry. |
| // This way a dataType need not be specified. But, we won't do this if recordDemandCreateEDataType is false, so we don't |
| // change the instanceClass of a new EDataType that's implicitly being defined for FeatureMap. |
| // |
| if (eGenericType == null && |
| EcorePackage.Literals.EFEATURE_MAP.getInstanceTypeName().equals(typeName) && |
| requiredClassifierType != RequiredClassifierType.CLASS) |
| { |
| eGenericType = EcoreFactory.eINSTANCE.createEGenericType(); |
| eGenericType.setEClassifier(EcorePackage.Literals.EFEATURE_MAP_ENTRY); |
| } |
| |
| // If we don't have one yet, maybe it's one of the special types... |
| // |
| if (eGenericType == null && |
| requiredClassifierType != RequiredClassifierType.CLASS && |
| (packageName.length() == 0 || |
| packageName.equals("java.lang") || |
| packageName.equals("java.math") || |
| packageName.equals("java.util"))) |
| { |
| for (EClassifier ecoreEClassifier : EcorePackage.eINSTANCE.getEClassifiers()) |
| { |
| if (ecoreEClassifier instanceof EDataType) |
| { |
| String instanceClassName = ecoreEClassifier.getInstanceClassName(); |
| if ((instanceClassName.equals(typeName) || instanceClassName.equals("java.lang." + typeName)) && |
| (ecoreEClassifier.getETypeParameters().isEmpty() || |
| ecoreEClassifier == EcorePackage.Literals.EJAVA_CLASS)) |
| { |
| eGenericType = EcoreFactory.eINSTANCE.createEGenericType(); |
| eGenericType.setEClassifier(ecoreEClassifier); |
| break; |
| } |
| } |
| } |
| } |
| |
| // If we still don't have one, we'll have to settle for a demand created EDataType or EClass, |
| // so create a new EClassifier with a nice unique name. |
| // |
| if (eGenericType == null) |
| { |
| // If the name isn't qualified, it might be a primitive or from java.lang. Otherwise, assume it's in the current |
| // package and use the nsPrefix for the qualified package name. |
| // |
| boolean primitive = false; |
| String instanceTypeName = typeName; |
| if (packageName.length() == 0) |
| { |
| if (CodeGenUtil.isJavaPrimitiveType(elementTypeName)) |
| { |
| primitive = true; |
| } |
| else if (CodeGenUtil.isJavaLangType(elementTypeName)) |
| { |
| packageName = "java.lang"; |
| instanceTypeName = packageName + "." + instanceTypeName; |
| } |
| else |
| { |
| packageName = ePackage.getNsPrefix(); |
| instanceTypeName = packageName + '.' + instanceTypeName; |
| } |
| } |
| |
| EClassifier eClassifier = null; |
| if (!"java.util.Map$Entry".equals(instanceTypeName)) |
| { |
| for (EClassifier demandCreatedEClassifier : demandCreatedEClassifiers) |
| { |
| if (requiredClassifierType.isValid(demandCreatedEClassifier) && |
| instanceTypeName.equals(demandCreatedEClassifier.getInstanceTypeName())) |
| { |
| eClassifier = demandCreatedEClassifier; |
| } |
| } |
| } |
| |
| if (eClassifier == null) |
| { |
| if (requiredClassifierType == RequiredClassifierType.CLASS) |
| { |
| EClass eClass = EcoreFactory.eINSTANCE.createEClass(); |
| eClass.setAbstract(true); |
| eClass.setInterface(true); |
| eClassifier = eClass; |
| } |
| else |
| { |
| eClassifier = EcoreFactory.eINSTANCE.createEDataType(); |
| } |
| demandCreatedEClassifiers.add(eClassifier); |
| |
| eClassifier.setInstanceTypeName(instanceTypeName); |
| |
| // Even primitives should be represented by a data type with a conventional (i.e. capitalized) name. |
| // |
| String name = baseName; |
| if (primitive && name.length() > 0) |
| { |
| name = CodeGenUtil.capName(name); |
| } |
| |
| // Make array names legal. |
| // |
| while (name.endsWith("[]")) |
| { |
| name = name.substring(0, name.length() - 2) + "Array"; |
| } |
| |
| eClassifier.setName(name); |
| |
| if (recordDemandCreatedEClassifier) |
| { |
| addToPackage(ePackage, eClassifier); |
| } |
| } |
| |
| eGenericType = EcoreFactory.eINSTANCE.createEGenericType(); |
| eGenericType.setEClassifier(eClassifier); |
| } |
| |
| return eGenericType; |
| } |
| |
| protected void addToPackage(EPackage ePackage, EClassifier eClassifier) |
| { |
| String baseName = eClassifier.getName(); |
| String name = baseName; |
| |
| // Avoid classifier name collisions. |
| // |
| for (int j = 1; ePackage.getEClassifier(name) != null; ++j) |
| { |
| name = baseName + "_" + j; |
| } |
| |
| eClassifier.setName(name); |
| ePackage.getEClassifiers().add(eClassifier); |
| } |
| |
| protected EGenericType resolveMapEntry(EPackage ePackage, EGenericType keyEGenericType, EGenericType valueEGenericType) |
| { |
| for (EClassifier ePackageClassifier : ePackage.getEClassifiers()) |
| { |
| if (ePackageClassifier instanceof EClass && |
| ("java.util.Map.Entry".equals(ePackageClassifier.getInstanceClassName()) || "java.util.Map$Entry".equals(ePackageClassifier.getInstanceClassName()))) |
| { |
| EClass mapEntryInterface = (EClass)ePackageClassifier; |
| EStructuralFeature keyFeature = mapEntryInterface.getEStructuralFeature("key"); |
| if (keyFeature != null && EcoreUtil.equals(keyFeature.getEGenericType(), keyEGenericType) && !keyFeature.isMany()) |
| { |
| EStructuralFeature valueFeature = mapEntryInterface.getEStructuralFeature("value"); |
| if (valueFeature != null && EcoreUtil.equals(valueFeature.getEGenericType(), valueEGenericType) && !valueFeature.isMany()) |
| { |
| EGenericType result = EcoreFactory.eINSTANCE.createEGenericType(); |
| result.setEClassifier(mapEntryInterface); |
| return result; |
| } |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| protected <T extends ENamedElement> void sort(EList<T> namedElements, final Map<Object, Integer> nameToIDMap) |
| { |
| Collection<T> ordered = new TreeSet<T>(new Comparator<T>() |
| { |
| @Override |
| public boolean equals(Object object) |
| { |
| return object == this; |
| } |
| |
| public int compare(T firstObject, T secondObject) |
| { |
| int firstValue = getOrderingValue(firstObject, nameToIDMap); |
| int secondValue = getOrderingValue(secondObject, nameToIDMap); |
| return firstValue - secondValue; |
| } |
| }); |
| ordered.addAll(namedElements); |
| int index = 0; |
| for (T eNamedElement : ordered) |
| { |
| namedElements.move(index++, eNamedElement); |
| } |
| } |
| |
| protected int getOrderingValue(ENamedElement eNamedElement, Map<Object, Integer> nameToIDMap) |
| { |
| Integer result = nameToIDMap.get(eNamedElement); |
| if (result == null) |
| { |
| if (eNamedElement instanceof EClassifier) |
| { |
| String prefix = ePackageToPrefixMap.get(eNamedElement.eContainer()); |
| String name = eNamedElement.getName(); |
| String id = CodeGenUtil.format(name, '_', prefix, true, true).toUpperCase(); |
| result = nameToIDMap.get(id); |
| } |
| else |
| { |
| String prefix = ePackageToPrefixMap.get(eNamedElement.eContainer().eContainer()); |
| String eClassName = ((ENamedElement)eNamedElement.eContainer()).getName(); |
| String eFeatureName = eNamedElement.getName(); |
| String id = CodeGenUtil.format(eClassName, '_', prefix, true, true).toUpperCase() + "__" |
| + CodeGenUtil.format(eFeatureName, '_', prefix, true, false).toUpperCase(); |
| result = nameToIDMap.get(id); |
| } |
| if (result != null) |
| { |
| nameToIDMap.put(eNamedElement, result); |
| } |
| } |
| |
| if (result != null) |
| { |
| return result; |
| } |
| |
| return Integer.MAX_VALUE; |
| } |
| |
| protected <T extends ENamedElement> T getNamedElement(List<T> namedElements, String name) |
| { |
| if (name != null) |
| { |
| for (T namedElement : namedElements) |
| { |
| if (name.equals(namedElement.getName())) |
| { |
| return namedElement; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the diagnostic. |
| * @return the status. |
| */ |
| public Diagnostic getDiagnostic() |
| { |
| return basicDiagnostic; |
| } |
| |
| /** |
| * Returns the generator model. |
| * @return the generator model. |
| */ |
| public GenModel getGenModel() |
| { |
| return genModel; |
| } |
| |
| /** |
| * Returns the list of used GenPackages. |
| * @return the list of used GenPackages. |
| */ |
| public Collection<GenPackage> getUsedGenPackages() |
| { |
| return usedGenPackages; |
| } |
| |
| /** |
| * Produces another IStatus in the MultiStatus. |
| * @param message a description of the error. |
| */ |
| protected void error(String message) |
| { |
| System.err.println("-->Error: " + message); |
| basicDiagnostic.add(new BasicDiagnostic(Diagnostic.ERROR, JavaImporterPlugin.getPlugin().getBundle().getSymbolicName(), 0, message, null)); |
| } |
| |
| /** |
| * Produces another IStatus in the MultiStatus, with warning severity. |
| * @param message a description of the error. |
| */ |
| protected void warning(String message) |
| { |
| System.err.println("-->Warning: " + message); |
| basicDiagnostic.add(new BasicDiagnostic(Diagnostic.WARNING, JavaImporterPlugin.getPlugin().getBundle().getSymbolicName(), 0, message, null)); |
| } |
| } |