/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.metamodels.builder.execution.util; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.StringTokenizer; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Status; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EClassifier; import org.eclipse.emf.ecore.EFactory; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.xsd.XSDSchema; import org.eclipse.xsd.XSDTypeDefinition; import org.eclipse.xsd.impl.XSDSchemaImpl; import org.eclipse.xsd.util.XSDConstants; import org.eclipse.xsd.util.XSDResourceImpl; import org.teiid.core.designer.ModelerCoreException; import org.teiid.core.designer.util.CoreArgCheck; import org.teiid.core.designer.util.I18nUtil; import org.teiid.designer.core.ObjectExtension; import org.teiid.designer.core.XsdObjectExtension; import org.teiid.designer.metamodels.builder.execution.MetamodelBuilderConstants; import org.teiid.designer.metamodels.builder.util.MetaClassUriHelper; import org.teiid.designer.metamodels.core.AnnotationContainer; import org.teiid.designer.metamodels.core.ModelAnnotation; import org.teiid.designer.metamodels.core.ModelType; import org.teiid.designer.metamodels.core.extension.ExtensionFactory; import org.teiid.designer.metamodels.core.extension.XClass; import org.teiid.designer.metamodels.core.extension.XPackage; import org.teiid.designer.metamodels.core.extension.impl.ExtensionFactoryImpl; import org.teiid.designer.metamodels.relational.RelationalPackage; /** * This is a helper class to encapsulate reusable methods developed for the Metamodel Entity builder framework. * * @since 8.0 */ public class MetamodelBuilderUtil implements MetamodelBuilderConstants { // Cache of all datatypes already looked up. private static final HashMap DT_CACHE = new HashMap(); // The Global XSD Schema. Cache so we don't have to look this up everytime we need to // search for datatypes. private static XSDSchema GLOBAL_XSD; private static final String I18N_PREFIX = I18nUtil.getPropertyPrefix(MetamodelBuilderUtil.class); private static String getString( final String id ) { return UTIL.getString(I18N_PREFIX + id); } private static String getString( final String id, final Object param1 ) { return UTIL.getString(I18N_PREFIX + id, param1); } private static String getString( final String id, final Object param1, final Object param2 ) { return UTIL.getString(I18N_PREFIX + id, param1, param2); } // ================================================================================== // S T A T I C M E T H O D S // ================================================================================== /** * Helper method to find a child with the given name of the given metaClass (may be null). If the metaClass is null you will * get the first child with a matching name. * * @param name - Name of the entity to find - May not be null * @param parent - The parent of the object to find (may be an EObject or a Resource, but may not be null) * @param metaClassUri - The metaClassUri of the given parent to resolve name collisions (may be null) * @return * @since 4.3 */ public static Object findChild( final String name, final Object parent, final String metaClassUri ) { // Name and parent may not be null. if (name == null || parent == null) { return null; } // Get collection of children to search final String eClassName = metaClassUri == null ? null : MetaClassUriHelper.getEClassName(metaClassUri); Collection children = Collections.EMPTY_LIST; if (parent instanceof Resource) { children = ((Resource)parent).getContents(); } else if (parent instanceof EObject) { children = ((EObject)parent).eContents(); } // Important to note that metaClassUri is only usable at the leaf level. At all other // levels the first entity with the correct name will be returned. final Iterator it = children.iterator(); while (it.hasNext()) { final EObject next = (EObject)it.next(); final String nextName = getName(next); if (name.equals(nextName)) { // As mentioned above... metaClassUri will only be passed in when we are at // the leaf node of a given path. It would result in incorrect results if // passed in at every level of the path. if (metaClassUri == null) { return next; } if (next.eClass().getName().equals(eClassName)) { return next; } } } return null; } /** * Helper method to return the name of a given EObject * * @param entity - EObject to search for name feature - May not be null * @return the name of the EObject or it's eClass name. * @since 4.3 */ public static String getName( final EObject entity ) { if (entity == null) { return null; } // Look for a name feature and return that value final EStructuralFeature feature = entity.eClass().getEStructuralFeature(NAME); if (feature != null) { return (String)entity.eGet(feature); } return entity.eClass().getName(); } /** * Search for an existing extension for a given EObject - This method was ported from ModelEditor with the removal of logic * that made it not UnitTestable * * @param eObject - EObject to find existing extension for - May not be null * @param status - MultiStatus to use to accumulate warnings and errors * @return an ObjectExtension for the given EObject * @throws ModelerCoreException * @since 4.3 */ public static EObject getExtension( EObject eObject, final MultiStatus status ) throws ModelerCoreException { CoreArgCheck.isNotNull(eObject); final EClass eClass = eObject.eClass(); final EPackage ePackage = eClass.getEPackage(); // There is never an extension object for ECore ... if (EcorePackage.eINSTANCE.equals(ePackage)) { return null; } // Get the Resource for the given EObject... may not be null final Resource resource = eObject.eResource(); if (resource == null) { return null; } if (resource instanceof XSDResourceImpl) { // Special logic for XSDResources that do not have ModelAnnotations XSDResourceImpl xsdResource = (XSDResourceImpl)resource; XSDSchema xsdSchema = xsdResource.getSchema(); if (xsdSchema != null) { // Get the extension XPackage for the schema - null is returned if extension is defined. final XPackage extPackage = XsdObjectExtension.getExtensionPackage(xsdSchema); if (extPackage != null) { final XClass xclass = extPackage.findXClass(eClass); if (xclass != null) { EObject result = null; try { EPackage pkg = extPackage; ExtensionFactory factory = null; final EFactory existingFactory = pkg.getEFactoryInstance(); if (existingFactory == null || !(existingFactory instanceof ExtensionFactory)) { factory = new ExtensionFactoryImpl(); factory.setEPackage(extPackage); } result = new XsdObjectExtension(eObject, xclass, null); } catch (Throwable e) { final String msg = getString("extensionErr"); //$NON-NLS-1$ MetamodelBuilderUtil.addStatus(status, IStatus.ERROR, msg, e); } return result; } } } } else { // Get the ModelAnnotation for the model that contains the eObject - May not be null final ModelAnnotation model = getModelAnnotation(resource); if (model != null) { final XPackage extPackage = model.getExtensionPackage(); if (extPackage != null) { final XClass xclass = extPackage.findXClass(eClass); if (xclass != null) { EObject result = null; try { EPackage pkg = extPackage; ExtensionFactory factory = null; final EFactory existingFactory = pkg.getEFactoryInstance(); if (existingFactory == null || !(existingFactory instanceof ExtensionFactory)) { factory = new ExtensionFactoryImpl(); factory.setEPackage(extPackage); } result = new ObjectExtension(eObject, xclass); } catch (Throwable e) { final String msg = getString("extensionErr"); //$NON-NLS-1$ MetamodelBuilderUtil.addStatus(status, IStatus.ERROR, msg, e); } return result; } } } } return null; } /** * Helper method to find an entity by path within the given resourceSet * * @param resources - ResourceSet to search * @param path - Full path (including ModelName) to EObject * @param metaClassUri - EClass for entity - May be null. * @param status - MultiStatus to accumulate errors and warnings * @return matching entity - May be a Resource or EObject * @since 4.3 */ public static Object findEObjectByPath( final ResourceSet resources, final String path, final String metaClassUri, final MultiStatus status ) { Object currentParent = null; // Tokenize the path using the path seperator final StringTokenizer pathTokens = new StringTokenizer(path, PATH_SEPARATOR); boolean failed = false; while (pathTokens.hasMoreTokens() && !failed) { if (currentParent == null) { currentParent = findResource(resources, pathTokens.nextToken()); if (currentParent == null) { // We did not find a resource for the first token... break out failed = true; } } else { final String name = pathTokens.nextToken(); // Look for child with a name that matches the current token. // If we are at the last token, use the metaClassUri passed in (optional) if (pathTokens.hasMoreTokens()) { currentParent = findChild(name, currentParent, null); } else { currentParent = findChild(name, currentParent, metaClassUri); } if (currentParent == null) { // No enity found for current token in path... break out failed = true; } } } // If we did not find a match, look for internal or existing objects // Current examples are Types which can be XSD types or MM Internal types or MetaModel Entities if (currentParent == null) { if (path.startsWith(MM_DT_URI) || path.startsWith(XSD_DT_URI)) { // Datatypes... find inernal EOBject currentParent = getInternalObjectByUri(resources, path, status); } else if (path.startsWith(MM_URI) || path.startsWith(ECORE_URI)) { // MetaModel Entity... find EClass currentParent = getEClassForUri(path, status); } } return currentParent; } /** * Find a resource in the given resourceSet using the given path * * @param resources - ResourceSet to search... guaranteed to be nonNull * @param path - Path to use... guaranteed to be nonNull * @return matching resource * @since 4.3 */ public static Resource findResource( final ResourceSet resources, final String path ) { // TODO add logic for XSDs final StringTokenizer pathTokens = new StringTokenizer(path, PATH_SEPARATOR); final String modelName = pathTokens.nextToken() + MODEL_EXT; final Iterator rsrcs = resources.getResources().iterator(); while (rsrcs.hasNext()) { // Use the last segment of the URI adding the ModelExtension to search for a resource // This logic will break when we start processing XSDs. final Resource rsrc = (Resource)rsrcs.next(); if (rsrc.getURI().lastSegment().equals(modelName)) { return rsrc; } } return null; } /** * Helper to find a ModelAnnotation in a given Resource. Ported from ModelEditorImpl and removed logic making this method Unit * Testable * * @param eResource - resrource to search * @return given ModelAnnotation * @since 4.3 */ public static ModelAnnotation getModelAnnotation( final Resource eResource ) { if (eResource == null) { return null; } ModelAnnotation annot = null; for (final Iterator iter = eResource.getContents().iterator(); iter.hasNext();) { final EObject root = (EObject)iter.next(); if (root instanceof ModelAnnotation) { annot = (ModelAnnotation)root; break; } } return annot; } /** * Helper to return constant for model type * * @param rsr - Resource to interrogate * @return model type constant * @since 4.3 */ public static int getModelType( final Resource rsr ) { final ModelAnnotation ma = getModelAnnotation(rsr); if (ma != null) { final String pmu = ma.getPrimaryMetamodelUri(); if (RelationalPackage.eNS_URI.equals(pmu)) { return RELATIONAL_MODEL; } else if (ma.getModelType() == ModelType.EXTENSION_LITERAL) { return EXTENSION_MODEL; } } return UNKNOWN_MODEL; } /** * Helper to find the AnnotationContainer for the given resource * * @param eResource - Resource to search * @return * @since 4.3 */ public static AnnotationContainer getAnnotationContainer( final Resource eResource ) { if (eResource == null) { return null; } for (final Iterator iter = eResource.getContents().iterator(); iter.hasNext();) { final EObject root = (EObject)iter.next(); if (root instanceof AnnotationContainer) { return (AnnotationContainer)root; } } return null; } /** * Helper to get the ePkg for a give ePkg URI * * @param ePkgUri * @param status * @return * @since 4.3 */ public static EPackage getEPackageForUri( final String ePkgUri, final MultiStatus status ) { final EPackage ePkg = EPackage.Registry.INSTANCE.getEPackage(ePkgUri); if (ePkg == null) { final String msg = getString("noPkg", ePkgUri); //$NON-NLS-1$ MetamodelBuilderUtil.addStatus(status, IStatus.ERROR, msg); return null; } return ePkg; } /** * Helper to get the EClass for a given metaclass uri * * @param metaClassUri * @param status * @return * @since 4.3 */ public static EClassifier getEClassForUri( final String metaClassUri, final MultiStatus status ) { String ePkgUri = MetaClassUriHelper.getPackageUri(metaClassUri); String eClassName = MetaClassUriHelper.getEClassName(metaClassUri); EPackage ePkg = getEPackageForUri(ePkgUri, status); if (ePkg == null) { return null; } EClassifier eClass = ePkg.getEClassifier(eClassName); if (eClass == null) { final String msg = getString("noEntity", eClassName, ePkgUri); //$NON-NLS-1$ MetamodelBuilderUtil.addStatus(status, IStatus.ERROR, msg); return null; } return eClass; } /** * Helper to find an internal EObject in the given resource set using the given URI * * @param resources - ResourceSet to search * @param uri - URI to use for searching * @param status - MultiStatus to record warnings and errors * @return - matching EObject * @since 4.3 */ public static Object getInternalObjectByUri( final ResourceSet resources, final String uri, final MultiStatus status ) { // URI may not be null and must contain the POUND seperator between resource and entity values if (uri == null || uri.indexOf(POUND) == -1) { final String msg = getString("badInternalUri", uri); //$NON-NLS-1$ addStatus(status, IStatus.ERROR, msg); return null; } // Create a search name for both XSD and MM Types final int index = uri.indexOf(POUND); final String resourceUri = uri.substring(0, index); final String entityName = uri.substring(index + 1); final String mmName = MM_PREFIX + entityName; final String xsdName = XSD_PREFIX + entityName; if (resourceUri == null || entityName == null) { // Log error and return null final String msg = getString("badInternalUri", uri); //$NON-NLS-1$ addStatus(status, IStatus.ERROR, msg); return null; } // Search the cache first Object result = DT_CACHE.get(entityName); if (result != null) { return result; } // Find the XSD Schema for either the Global types or MM BuiltIn types depending on // the passed in URI. XSDSchema schema = null; boolean isMM = false; if (resourceUri.equals(XSD_RESOURCE_URI)) { schema = getGlobalXsd(); } else if (resourceUri.equals(MM_DT_RESOURCE_URI)) { // The builtInDatatypes.xsd must either be contained in the give resourceSet // or that resourceSet must know how to resolve it (Our Container knows how to do that) isMM = true; final URI dtURI = URI.createURI(MM_DT_RESOURCE_URI); final XSDResourceImpl dts = (XSDResourceImpl)resources.getResource(dtURI, false); if (dts != null && dts.isLoaded()) { schema = dts.getSchema(); } } // The schema must not be null if (schema == null) { final String msg = getString("noSchema", uri); //$NON-NLS-1$ addStatus(status, IStatus.ERROR, msg); return null; } // Search the given schema's global types for one with the correct name final Iterator types = schema.getTypeDefinitions().iterator(); while (types.hasNext()) { XSDTypeDefinition type = (XSDTypeDefinition)types.next(); if (entityName.equals(type.getName())) { if (isMM) { DT_CACHE.put(mmName, type); } else { DT_CACHE.put(xsdName, type); } return type; } } return null; } /** * Helper to add a new status with no exception */ public static void addStatus( final MultiStatus parent, final int severity, final String msg ) { addStatus(parent, severity, msg, null); } /** * Helper to add a new status to the given MultiStatus */ public static void addStatus( final MultiStatus parent, final int severity, final String msg, final Throwable err ) { final Status sts = new Status(severity, pluginID, 0, msg, err); parent.add(sts); } // Lazy accessor for the cached Global XSD Schema private static XSDSchema getGlobalXsd() { if (GLOBAL_XSD == null) { GLOBAL_XSD = XSDSchemaImpl.getSchemaForSchema(XSDConstants.SCHEMA_FOR_SCHEMA_URI_2001); } return GLOBAL_XSD; } }