/* * 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.core.refactor; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IProgressMonitor; 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.InternalEObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.teiid.core.designer.ModelerCoreException; import org.teiid.core.designer.util.CoreArgCheck; import org.teiid.core.designer.util.CoreStringUtil; import org.teiid.core.designer.util.StringConstants; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.core.container.Container; import org.teiid.designer.core.container.ResourceFinder; import org.teiid.designer.core.refactor.ExternalReferenceVisitor.ExternalReferences; import org.teiid.designer.core.resource.EmfResource; import org.teiid.designer.core.util.ModelVisitorProcessor; import org.teiid.designer.core.workspace.WorkspaceResourceFinderUtil; import org.teiid.designer.metamodels.core.ModelAnnotation; import org.teiid.designer.metamodels.core.ModelImport; import org.teiid.designer.metamodels.core.util.ModelImportComparator; /** * OrganizeImportCommand * * @since 8.0 */ public class OrganizeImportCommandHelperNonXsd extends OrganizeImportCommandHelper { protected OrganizeImportCommandHelperNonXsd() { super(); } /** * @see org.teiid.designer.core.refactor.ModelRefactorCommand#execute(org.eclipse.core.runtime.IProgressMonitor) */ @Override protected IStatus execute( final IProgressMonitor monitor ) { return process(monitor); } /** * @param monitor * @return IStatus * @since 4.3 */ private IStatus process( IProgressMonitor monitor ) { // Find all the external references ... final List problems = new LinkedList(); // Resource whose imports and hrefs are being rebuilt final Resource eResource = getResource(); // Get annotation ... final ModelAnnotation modelAnnot = getModelAnnotation(eResource); // Clear current state ... this.modelImports.clear(); // // reset the model imports // this.modelImports = new ArrayList(modelAnnot.getModelImports()); // collect new imports(should be no duplicates) Set newImports = new HashSet(this.modelImports.size()); // Build all of the imports for the external references ... buildAllImportsForExternalRef(eResource, problems, newImports, monitor); this.modelImports.addAll(newImports); // Defect 23518 - improving checks so we don't change imports if nothing changed. // Update the imports on the model annotation ... if imports have changed. if (importsChanged(modelAnnot)) { updateImport(modelAnnot); } // Put all of the problems into a single IStatus ... IStatus resultStatus = createFinalStatus(problems); // MyDefect : 16368 refactored the method end return resultStatus; } /** * checks if the expected imports (see this.importList) are different than the current imports This method allows the * process() method to NOT update the imports if they are ok. This way we don't modify the model when Update/Build imports is * called on a resource and nothing has changed to affect the import list. * * @param modelAnnot * @return true if missing import or stale import exists in the import list of the ModelAnnotation * @since 5.0.2 */ private boolean importsChanged( final ModelAnnotation modelAnnot ) { boolean result = false; result = modelAnnot.getModelImports().size() != this.modelImports.size(); if (!result && (hasStaleImports(modelAnnot) || isMissingImports(modelAnnot))) { result = true; } return result; } /** * @param modelAnnot * @since 4.3 */ private boolean clearExistingImports( final ModelAnnotation modelAnnot ) { if (modelAnnot.getModelImports().isEmpty()) { return false; } List existingImportList = new ArrayList(modelAnnot.getModelImports()); try { ModelerCore.getModelEditor().removeValue(modelAnnot, existingImportList, modelAnnot.getModelImports()); } catch (ModelerCoreException err) { ModelerCore.Util.log(err); return false; } return true; } /** * Checks if any imports in the given ModelAnnotation contains any imports not in this class's import list * * @param modelAnnot * @return true if an import exists and doesn't need to be there. * @since 5.0.2 */ private boolean hasStaleImports( final ModelAnnotation modelAnnot ) { // If any existing import is not in the modelImports list, assume imports have changed List existingImportList = new ArrayList(modelAnnot.getModelImports()); for (Iterator iter = existingImportList.iterator(); iter.hasNext();) { ModelImport nextImport = (ModelImport)iter.next(); if (!importInList(nextImport, this.modelImports)) { return true; } } return false; } /** * Checks if the import list for the give ModelAnnotation is missing any import in this class's importList * * @param modelAnnot * @return true if an expected import is not in the import list of the ModelAnnotation * @since 5.0.2 */ private boolean isMissingImports( final ModelAnnotation modelAnnot ) { // If any imports are missing (i.e. existing Imports does not include any import from the modelImports list // then assume imports have changed List existingImportList = new ArrayList(modelAnnot.getModelImports()); for (Iterator iter = modelImports.iterator(); iter.hasNext();) { ModelImport nextImport = (ModelImport)iter.next(); if (!importInList(nextImport, existingImportList)) { return true; } } return false; } /** * Checks the modelLocation value for a given import versus the modelLocations in a list of imports * * @param someModelImport * @param importList * @return true if import is already in the list * @since 5.0.2 */ private boolean importInList( final ModelImport someModelImport, final List importList ) { if (someModelImport.getModelLocation() == null || someModelImport.getModelLocation().trim().equals("")) { //$NON-NLS-1$ return false; } String importLocation = someModelImport.getModelLocation(); for (Iterator iter = importList.iterator(); iter.hasNext();) { ModelImport nextImport = (ModelImport)iter.next(); if (nextImport.getModelLocation() != null && nextImport.getModelLocation().equalsIgnoreCase(importLocation)) { return true; } } return false; } /** * @param importChanged * @param modelAnnot * @since 4.3 */ private void updateImport( final ModelAnnotation modelAnnot ) { if (modelAnnot != null) { // Update the imports on the model annotation ... boolean importsChanged = clearExistingImports(modelAnnot); try { if (!modelImports.isEmpty()) { importsChanged = true; Collections.sort(this.modelImports, new ModelImportComparator()); // Add all at once ModelerCore.getModelEditor().addValue(modelAnnot, this.modelImports, modelAnnot.getModelImports()); } } catch (ModelerCoreException e1) { ModelerCore.Util.log(e1); } finally { if (importsChanged) { this.getResource().setModified(true); } } } } /** * @param visitor * @param problems * @param newImports * @param modelAnnot * @param monitor * @since 4.3 */ private void buildAllImportsForExternalRef( final Resource eResource, final List problems, final Set newImports, final IProgressMonitor monitor ) { final ExternalReferenceVisitor visitor = processExternalResourcesReferences(eResource, problems); Collection externRefList = visitor.getExternalReferences(); // Create a set of the existing ModelImport location strings to be used when determining // if href need to be adjusted ModelAnnotation modelAnnot = getModelAnnotation(eResource); Collection existingImportLocations = new HashSet(modelAnnot.getModelImports().size()); for (Iterator iterator = modelAnnot.getModelImports().iterator(); iterator.hasNext();) { ModelImport existingImport = (ModelImport)iterator.next(); existingImportLocations.add(existingImport.getModelLocation()); } for (Iterator iter = externRefList.iterator(); iter.hasNext();) { final ExternalReferenceVisitor.ExternalReferences externalRefs = (ExternalReferenceVisitor.ExternalReferences)iter.next(); final URI externalUri = externalRefs.getResourceUri(); CoreArgCheck.isNotNull(externalUri); // If the resourceURI for the external reference is to the same resource as the // one we are updating the imports for then this is really an internal reference. // A reference of this type may show up if an href appears in the model file // and cannot be resolved. Unresolved references will be caught by the // ResourceInScopeValidationRule. if (externalUri.equals(eResource.getURI())) { continue; } try { final ModelImport modelImport = createModelImport(eResource, externalRefs, problems, monitor); // If a non-null ModelImport is returned then we know that the URI for this // ExternalReferences was found in the selected resource's import list if (modelImport != null) { // Add the import (even if it already existed in the original list) to the list of new imports if (!containsImport(newImports, modelImport)) { newImports.add(modelImport); } // adjust external reference for the new import if (!existingImportLocations.contains(modelImport.getModelLocation())) { // System.out.println("\nOrganizeImportCommand on "+eResource.getURI().lastSegment()); // System.out.println(" adjusting references for import: "+modelImport.getModelLocation()); adjustReferences(eResource, externalRefs, modelImport); eResource.setModified(true); } } } catch (Throwable t) { final Object[] params = new Object[] {URI.decode(externalUri.toString())}; final String msg = ModelerCore.Util.getString("OrganizeImportCommand.Error_while_building_import", params); //$NON-NLS-1$ problems.add(new Status(IStatus.ERROR, PID, UNKNOWN_ERROR_BUILDING_IMPORT, msg, t)); } } } private boolean containsImport( final Collection importCollection, final ModelImport modelImport ) { String modelImportUUID = modelImport.getUuid(); String modelImportLoc = modelImport.getModelLocation(); for (Iterator i = importCollection.iterator(); i.hasNext();) { ModelImport mi = (ModelImport)i.next(); if (modelImportUUID != null && modelImportUUID.equals(mi.getUuid())) { return true; } if (modelImportLoc != null && modelImportLoc.equals(mi.getModelLocation())) { return true; } } return false; } /** * @param problems * @return ExternalReferenceVisitor * @since 4.3 */ private ExternalReferenceVisitor processExternalResourcesReferences( final Resource eResource, final List problems ) { final ExternalReferenceVisitor visitor = new ExternalReferenceVisitor(eResource); visitor.setIncludeDiagramReferences(this.includeDiagramReferences); final ModelVisitorProcessor processor = new ModelVisitorProcessor(visitor); try { processor.walk(this.getResource(), ModelVisitorProcessor.DEPTH_INFINITE); } catch (ModelerCoreException e) { final Object[] params = new Object[] {this.getResource().getURI(), e.getLocalizedMessage()}; final String msg = ModelerCore.Util.getString("OrganizeImportCommand.Error_while_organizing_imports", params); //$NON-NLS-1$ problems.add(new Status(IStatus.ERROR, PID, ERROR_ORGANIZING_IMPORTS, msg, e)); } catch (Throwable t) { final Object[] params = new Object[] {this.getResource().getURI(), t.getLocalizedMessage()}; final String msg = ModelerCore.Util.getString("OrganizeImportCommand.Unknown_error_while_organizing_imports", params); //$NON-NLS-1$ problems.add(new Status(IStatus.ERROR, PID, UNKNOWN_ERROR_ORGANIZING_IMPORTS, msg, t)); } return visitor; } /** * @param problems * @return IStatus * @since 4.3 */ private IStatus createFinalStatus( List problems ) { IStatus resultStatus = null; if (problems.isEmpty()) { final int code = EXECUTE_WITH_NO_PROBLEMS; final String msg = ModelerCore.Util.getString("OrganizeImportCommand.complete"); //$NON-NLS-1$ final IStatus status = new Status(IStatus.OK, PLUGINID, code, msg, null); resultStatus = status; } else if (problems.size() == 1) { resultStatus = (IStatus)problems.get(0); } else { // There were problems, so determine whether there were warnings and errors ... resultStatus = createFinalResultStatus(problems); } return resultStatus; } /** * @param problems * @return IStatus * @since 4.3 */ private IStatus createFinalResultStatus( List problems ) { IStatus resultStatus = null; int numErrors = 0; int numWarnings = 0; final Iterator problemIter = problems.iterator(); while (problemIter.hasNext()) { final IStatus aStatus = (IStatus)problemIter.next(); if (aStatus.getSeverity() == IStatus.WARNING) { ++numWarnings; } else if (aStatus.getSeverity() == IStatus.ERROR) { ++numErrors; } } // Create the final status ... final IStatus[] statusArray = (IStatus[])problems.toArray(new IStatus[problems.size()]); if (numWarnings != 0 && numErrors == 0) { final int code = EXECUTE_WITH_WARNINGS; final Object[] params = new Object[] {new Integer(numWarnings)}; final String msg = ModelerCore.Util.getString("OrganizeImportCommand.warnings", params); //$NON-NLS-1$ resultStatus = new MultiStatus(PLUGINID, code, statusArray, msg, null); } else if (numWarnings == 0 && numErrors != 0) { final int code = EXECUTE_WITH_ERRORS; final Object[] params = new Object[] {new Integer(numErrors)}; final String msg = ModelerCore.Util.getString("OrganizeImportCommand.errors", params); //$NON-NLS-1$ resultStatus = new MultiStatus(PLUGINID, code, statusArray, msg, null); } else if (numWarnings != 0 && numErrors != 0) { final int code = EXECUTE_WITH_WARNINGS_AND_ERRORS; final Object[] params = new Object[] {new Integer(numWarnings), new Integer(numErrors)}; final String msg = ModelerCore.Util.getString("OrganizeImportCommand.warnings_and_errors", params); //$NON-NLS-1$ resultStatus = new MultiStatus(PLUGINID, code, statusArray, msg, null); } else { final int code = EXECUTE_WITH_NO_WARNINGS_AND_ERRORS; final String msg = ModelerCore.Util.getString("OrganizeImportCommand.no_warnings_or_errors"); //$NON-NLS-1$ resultStatus = new MultiStatus(PLUGINID, code, statusArray, msg, null); } return resultStatus; } /** * Create an appropriate {@link ModelImport} for the external resource. * * @param uri the URI of the resource; never null * @param resource the reference to the Resource; null if there was at least one reference in {@link #getResource() this * resource} that was resolved * @param resourceSet the ResourceSet to search for a referenced model * @param factory the factory that should be used; never null, and simply the value returned from {@link #getFactory()}. * @return the ModelImport object, or null if a ModelImport could not be created */ private ModelImport createModelImport( final Resource eResource, final ExternalReferenceVisitor.ExternalReferences externalRefs, final List problems, final IProgressMonitor monitor ) { ModelImport theImport = null; // Get the external resource URI and possibly the external resource itself from the externalRefs final URI uri = externalRefs.getResourceUri(); // System.out.println(" #### OrganizeImportCommand.createModelImport() externalRefs.URI = " + uri); // Defect 23340 - if the uri is relative, we DONT CARE because the resource is TEMPORARY. Just return if (uri.isRelative()) { return null; } Resource theResource = externalRefs.getResource(); // If the resource is null, then look it up in the resource set ... ResourceSet eResourceSet = eResource.getResourceSet(); if (theResource == null) { final boolean loadOnDemand = false; theResource = eResourceSet.getResource(uri, loadOnDemand); } // The resource is still null, so start searching for referenced objects ... if (theResource == null) { final Collection refs = externalRefs.getReferencedObjects(); theResource = getHelper().findResourceWithObject(refs, problems, this.handler); } // If the resource is still null or the actual underlying resource does not exit if (theResource == null || resourceMightBeRefactored(theResource, uri)) { // Attempt to find the EMF resource by matching the name of the resource to others in the resource set theResource = getHelper().findRefactoredResource(eResource, uri, monitor, problems); } // See if there is already an import with this URI ... // Do not use the ModelAnnotation.findModelImportByPath() method // because the map is never cleared or references to non-existent // ModelImport instances are never removed. final ModelImport existingImport = ModelerCore.getModelEditor().findModelImport((EmfResource)eResource, theResource); if (existingImport != null) { // Make sure the location information in the existing import is up-to-date ModelerCore.getModelEditor().updateModelImport(existingImport, theResource); theImport = existingImport; } if (theImport == null) { // If the resource is non-null, then we know the resource exists, so just create a new import ... if (theResource != null) { try { theImport = ModelerCore.getModelEditor().createModelImport((EmfResource)eResource, theResource); } catch (ModelerCoreException e) { ModelerCore.Util.log(e); } } } return theImport; } /** * @param theResource * @param uri * @return boolean * @since 4.3 */ private boolean resourceMightBeRefactored( Resource theResource, URI uri ) { if (uri.isFile()) { if (!new File(theResource.getURI().toFileString()).exists()) { return false; } IResource resource = WorkspaceResourceFinderUtil.findIResource(theResource); if (resource == null || !resource.exists()) { return true; } } return false; } /** * @see org.teiid.designer.core.refactor.OrganizeImportCommandHelper#setRefactoredPaths(java.util.Map) * @since 4.3 */ @Override protected void setRefactoredPaths( Collection<PathPair> pathPairs ) { getHelper().setRefactoredPaths(pathPairs); } /** * @param resource * @return ModelAnnotation * @since 4.3 */ private ModelAnnotation getModelAnnotation( final Resource resource ) { if (resource instanceof EmfResource) { final EmfResource emfResource = (EmfResource)resource; return emfResource.getModelAnnotation(); } return null; } /** * Adjust the existing external references to reference the supplied model import. * * @param externalRefs * @param modelImport */ private void adjustReferences( final Resource eResource, final ExternalReferences externalRefs, final ModelImport modelImport ) { Resource importedResource = null; final Container cntr = ModelerCore.getContainer(eResource); if (cntr != null) { final ResourceFinder finder = cntr.getResourceFinder(); // Find the imported resource by its relative path ... final String modelLocation = modelImport.getModelLocation(); if (modelImport.eResource() == null && !CoreStringUtil.isEmpty(modelLocation)) { URI baseLocationURI = eResource.getURI(); final boolean isXSD = baseLocationURI.lastSegment().endsWith(StringConstants.XSD); // If the base resource URI was created as a file URI then it's path is encoded so before we // resolve the referenced resource we need to encode it's relative path URI modelLocationURI = (baseLocationURI.isFile() ? URI.createURI(modelLocation, false) : URI.createURI(modelLocation)); // Defect 23340 - if the resource is an XSD, treat as before and modify the modelLocationURI if (isXSD) { if (baseLocationURI.isHierarchical() && !baseLocationURI.isRelative() && modelLocationURI.isRelative()) { modelLocationURI = modelLocationURI.resolve(baseLocationURI); } } // Defect 23340 - if location is relative, then we just find the resource from the workspace (lighter weight // check) // rather than the findByURI() call. if (modelLocationURI.isRelative()) { importedResource = finder.findByWorkspaceUri(modelLocationURI, eResource); } else { importedResource = finder.findByURI(modelLocationURI, true); } } // If the imported resource was not found try the ResourceFinder which // will search by UUID and name. if (importedResource == null) { importedResource = finder.findByImport(modelImport, false); } if (importedResource != null) { final Collection refs = externalRefs.getReferencedObjects(); final URI uri = importedResource.getURI(); for (Iterator iter = refs.iterator(); iter.hasNext();) { final Object obj = iter.next(); if (obj instanceof InternalEObject) { final InternalEObject iobj = (InternalEObject)obj; if (iobj.eIsProxy()) { final URI existingUri = iobj.eProxyURI(); final String frag = existingUri.fragment(); final URI newUri = uri.appendFragment(frag); iobj.eSetProxyURI(newUri); } } } } } } // /** // * // * @param newImports // * @return boolean // * @since 4.3 // */ // private boolean isImportChanged(Set newImports) { // boolean importChanged = false; // if(this.modelImports.size() != newImports.size()) { // this.modelImports.clear(); // this.modelImports = new ArrayList(newImports); // importChanged = true; // } else { // for(Iterator importIter= newImports.iterator(); importIter.hasNext();) { // final ModelImport modelImport = (ModelImport) importIter.next(); // if (ModelerCore.getModelEditor().findModelImport(this.modelImports, modelImport.getPath()) == null) { // importChanged = true; // this.modelImports.clear(); // this.modelImports = new ArrayList(newImports); // break; // } // } // } // // return importChanged; // } // /** // * // * @param resourceUri // * @return String // * @since 4.3 // */ // private String getModelName( final URI resourceUri ) { // final String modelNameWithExt = resourceUri.lastSegment(); // final String extension = resourceUri.fileExtension(); // if ( extension != null ) { // final int index = modelNameWithExt.indexOf(extension); // if ( index > 1 ) { // return modelNameWithExt.substring(0,index-1); // also remove the "." // } // } // return modelNameWithExt; // } // /** // * Adjust the existing external references to reference the supplied IResource. // * @param externalRefs // * @param iResource // */ // private void adjustReferences(final ExternalReferences externalRefs, final IResource iResource) { // // final String modelPath = (iResource != null ? iResource.getLocation().toString() : null); // if ( modelPath == null ) { // return; // } // // final Collection refs = externalRefs.getReferencedObjects(); // final URI uri = URI.createFileURI(modelPath); // final Iterator iter = refs.iterator(); // // while (iter.hasNext()) { // final Object obj = iter.next(); // if ( obj instanceof InternalEObject ) { // final InternalEObject iobj = (InternalEObject)obj; // if ( iobj.eIsProxy() ) { // final URI existingUri = iobj.eProxyURI(); // final String frag = existingUri.fragment(); // // //MyDefect : 17647 // final URI newUri = uri.appendFragment(frag); // iobj.eSetProxyURI(newUri); // } // } // } // } }