/* * 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.ui.refactor; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.xsd.XSDAnnotation; import org.eclipse.xsd.XSDSchema; import org.eclipse.xsd.XSDSimpleTypeDefinition; import org.eclipse.xsd.util.XSDResourceImpl; import org.teiid.core.designer.id.IDGenerator; import org.teiid.core.designer.plugin.PluginUtilities; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.core.builder.ModelBuildUtil; import org.teiid.designer.core.refactor.RefactorCommand; import org.teiid.designer.core.refactor.ReferenceUpdator; import org.teiid.designer.core.resource.EmfResource; import org.teiid.designer.core.types.DatatypeConstants; import org.teiid.designer.core.util.ConcurrentModelVisitorProcessor; import org.teiid.designer.core.util.ModelVisitor; import org.teiid.designer.core.util.ModelVisitorProcessor; import org.teiid.designer.core.validation.ValidationProblem; import org.teiid.designer.core.validation.ValidationResultImpl; import org.teiid.designer.core.validation.rules.CoreValidationRulesUtil; import org.teiid.designer.core.workspace.ModelResource; import org.teiid.designer.core.workspace.ModelUtil; import org.teiid.designer.metamodels.core.ModelAnnotation; import org.teiid.designer.transformation.aspects.sql.SqlTransformationMappingRootSqlAspect; import org.w3c.dom.Element; /** * ModelCopyCommand is a RefactorCommand implementation that can generate a new copy of a model in memory. The command assumes * that there may be existing changes to the * * @since 8.0 */ public class ModelCopyCommand implements RefactorCommand { /** IStatus code indicating that no target Resource has been set for this command before calling canExecute */ public static final int ERROR_MISSING_RESOURCE = 1800; /** IStatus code indicating that no destination has been set */ public static final int ERROR_MISSING_DESTINATION = 1801; /** IStatus code indicating that no new model name has been set */ public static final int ERROR_MISSING_NAME = 1802; /** IStatus code indicating that the destination is not in a model project */ public static final int ERROR_PROJECT_CLOSED = 1805; /** IStatus code indicating that the destination is not in a model project */ public static final int ERROR_PROJECT_NATURE = 1806; /** IStatus code indicating that an exception occurred obtaining the destination project nature */ public static final int EXCEPTION_PROJECT_NATURE = 1807; /** IStatus code indicating that a file already exists with the specified new path */ public static final int ERROR_FILE_ALREADY_EXISTS = 1808; /** IStatus code indicating that a file already exists with the specified new path */ public static final int ERROR_CREATING_FILE = 1809; /** IStatus code indicating that the command is ready to execute */ public static final int READY_TO_EXECUTE = 1810; /** IStatus code indicating that the an error occurred in copying the resource contents */ public static final int ERROR_COPYING_RESOURCE = 1811; /** IStatus codes for the result of the execute operation */ public static final int EXECUTE_WITH_NO_PROBLEMS = 1812; public static final int EXECUTE_WITH_WARNINGS = 1813; public static final int EXECUTE_WITH_ERRORS = 1814; public static final int EXECUTE_WITH_WARNINGS_AND_ERRORS = 1815; public static final int EXECUTE_WITH_NO_WARNINGS_AND_ERRORS = 1816; static final String PID = ModelerCore.PLUGIN_ID; // The name of the attribute in the XSDSimpleTypeDefinition application information private static final String UUID_ATTRIBUTE_NAME = "UUID"; //$NON-NLS-1$ private static Collection REFERENCE_UPDATORS; private ModelResource resourceToCopy; private IContainer destination; private String newModelName; private String extension; private IFile newIFile; private ArrayList problemList = new ArrayList(); private Collection referencingResources; /** * Construct an instance of ModelCopyCommand. */ public ModelCopyCommand() { } // ================================================================================== // P U B L I C M E T H O D S // ================================================================================== public void setModelToCopy( final ModelResource modelResource ) { this.resourceToCopy = modelResource; this.extension = modelResource.getResource().getFileExtension(); } public void setNewModelDestination( final IContainer destination, final String name ) { this.newModelName = name; this.destination = destination; } /** * Sets a list of models that import the model being copied, such that all object references to the original model should be * redirected to the corresponding object in the copy. * * @param modelResourceList a collection of ModelResource objects. * @since 4.2 */ public void setModelsToRedirect( final Collection modelResourceList ) { this.referencingResources = modelResourceList; } /* (non-Javadoc) * @See org.teiid.designer.core.refactor.RefactorCommand#canExecute() */ @Override public IStatus canExecute() { if (this.newModelName.length() == 0) { final String msg = ModelerCore.Util.getString("ModelCopyCommand.No_new_model_name"); //$NON-NLS-1$ return new Status(IStatus.ERROR, PID, ERROR_MISSING_NAME, msg, null); } if (this.resourceToCopy == null) { final String msg = ModelerCore.Util.getString("ModelCopyCommand.No_resource_has_been_selected"); //$NON-NLS-1$ return new Status(IStatus.ERROR, PID, ERROR_MISSING_RESOURCE, msg, null); } if (this.destination == null) { final String msg = ModelerCore.Util.getString("ModelCopyCommand.No_destination_has_been_selected"); //$NON-NLS-1$ return new Status(IStatus.ERROR, PID, ERROR_READONLY_RESOURCE, msg, null); } if (!this.destination.getProject().isOpen()) { final String msg = ModelerCore.Util.getString("ModelCopyCommand.Error_project_closed"); //$NON-NLS-1$ return new Status(IStatus.ERROR, PID, ERROR_PROJECT_CLOSED, msg, null); } // can't save into non-model projects try { if (this.destination.getProject().getNature(ModelerCore.NATURE_ID) == null) { final String msg = ModelerCore.Util.getString("ModelCopyCommand.Cannot_move_to_non_model_project"); //$NON-NLS-1$ return new Status(IStatus.ERROR, PID, ERROR_PROJECT_NATURE, msg, null); } } catch (CoreException e) { final String msg = ModelerCore.Util.getString("ModelCopyCommand.Cannot_determine_project_nature"); //$NON-NLS-1$ return new Status(IStatus.ERROR, PID, EXCEPTION_PROJECT_NATURE, msg, e); } // check the validity of the name final ValidationResultImpl result = new ValidationResultImpl(this.newModelName); CoreValidationRulesUtil.validateStringNameChars(result, this.newModelName, null); if (result.hasProblems()) { ValidationProblem problem = result.getProblems()[0]; return new Status(problem.getSeverity(), PID, problem.getCode(), problem.getMessage(), null); } // check for siblings if (fileAlreadyExists(this.newModelName)) { final String msg = ModelerCore.Util.getString("ModelCopyCommand.Name_already_exists_in_container"); //$NON-NLS-1$ return new Status(IStatus.ERROR, PID, ERROR_FILE_ALREADY_EXISTS, msg, null); } final String msg = ModelerCore.Util.getString("ModelCopyCommand.Ready_to_execute"); //$NON-NLS-1$ return new Status(IStatus.OK, PID, CAN_EXECUTE, msg, null); } /* (non-Javadoc) * @See org.teiid.designer.core.refactor.RefactorCommand#execute(org.eclipse.core.runtime.IProgressMonitor) */ @Override public IStatus execute( final IProgressMonitor monitor ) { this.problemList.clear(); // create the new file this.newIFile = this.destination.getFile(new Path(this.newModelName + '.' + this.extension)); // Create copy of an XML schema model file ... if (this.isXsdResource(this.newIFile)) { executeXsdCopy(monitor); } // Create copy of an Teiid Designer xmi model file ... else { executeXmiCopy(monitor); } // Put all of the problems into a single IStatus ... IStatus resultStatus = null; if (problemList.isEmpty()) { final int code = EXECUTE_WITH_NO_PROBLEMS; final String msg = ModelerCore.Util.getString("ModelCopyCommand.Model_copy_complete_1"); //$NON-NLS-1$ final IStatus status = new Status(IStatus.OK, PID, code, msg, null); resultStatus = status; } else if (problemList.size() == 1) { resultStatus = (IStatus)problemList.get(0); } else { // There were problems, so determine whether there were warnings and errors ... int numErrors = 0; int numWarnings = 0; for (final Iterator problemIter = problemList.iterator(); 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[])problemList.toArray(new IStatus[problemList.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("ModelCopyCommand.Model_copy_with_warnings_8", params); //$NON-NLS-1$ resultStatus = new MultiStatus(PID, 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("ModelCopyCommand.Model_copy_with_errors_9", params); //$NON-NLS-1$ resultStatus = new MultiStatus(PID, 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("ModelCopyCommand.Model_copy_with_warnings_and_errors_10", params); //$NON-NLS-1$ resultStatus = new MultiStatus(PID, code, statusArray, msg, null); } else { final int code = EXECUTE_WITH_NO_WARNINGS_AND_ERRORS; final String msg = ModelerCore.Util.getString("ModelCopyCommand.Model_copy_with_no_warnings_or_errors_11"); //$NON-NLS-1$ resultStatus = new MultiStatus(PID, code, statusArray, msg, null); } } return resultStatus; } /* (non-Javadoc) * @See org.teiid.designer.core.refactor.RefactorCommand#canUndo() */ @Override public boolean canUndo() { return false; } /* (non-Javadoc) * @See org.teiid.designer.core.refactor.RefactorCommand#canRedo() */ @Override public boolean canRedo() { return false; } /* (non-Javadoc) * @See org.teiid.designer.core.refactor.RefactorCommand#undo(org.eclipse.core.runtime.IProgressMonitor) */ @Override public IStatus undo( final IProgressMonitor monitor ) { return null; } /* (non-Javadoc) * @See org.teiid.designer.core.refactor.RefactorCommand#redo(org.eclipse.core.runtime.IProgressMonitor) */ @Override public IStatus redo( final IProgressMonitor monitor ) { return null; } /* (non-Javadoc) * @See org.teiid.designer.core.refactor.RefactorCommand#getResult() */ @Override public Collection getResult() { return null; } /* (non-Javadoc) * @See org.teiid.designer.core.refactor.RefactorCommand#getAffectedObjects() */ @Override public Collection getAffectedObjects() { return null; } /* (non-Javadoc) * @See org.teiid.designer.core.refactor.RefactorCommand#getLabel() */ @Override public String getLabel() { return ModelerCore.Util.getString("ModelCopyCommand.label"); //$NON-NLS-1$; } /* (non-Javadoc) * @See org.teiid.designer.core.refactor.RefactorCommand#getDescription() */ @Override public String getDescription() { return ModelerCore.Util.getString("ModelCopyCommand.description"); //$NON-NLS-1$; } /* (non-Javadoc) * @See org.teiid.designer.core.refactor.RefactorCommand#getPostExecuteMessages() */ @Override public Collection getPostExecuteMessages() { return problemList; } /** * @return */ public IFile getNewIFile() { return newIFile; } // ================================================================================== // P R O T E C T E D M E T H O D S // ================================================================================== /** * Create a copy of the Xsd resource. Different logic is used to copy an XSD resource because XSD models don't have all the * right EStructuralFeatures set to be derived to avoid generically copying things that should not be copied because they are * computed. An alternative would be to use cloneConcreteComponent to do the copy but since we want a copy of the DOM itself * we instead chose to use Resource.save with an output stream for to the new resource location. That new resource is then * loaded and the UUIDs to the simple datatypes are recreated. * * @param monitor */ protected void executeXsdCopy( final IProgressMonitor monitor ) { // Copy the contents from the original resource into the new resource Resource source = null; try { // get the Emf resource for the original source = this.resourceToCopy.getEmfResource(); if (source == null) { final Object[] params = new Object[] {this.resourceToCopy}; final String msg = ModelerCore.Util.getString("ModelCopyCommand.Error_getting_Emf_resource_for_2", params); //$NON-NLS-1$ this.problemList.add(new Status(IStatus.ERROR, PID, ERROR_COPYING_RESOURCE, msg, null)); return; } } catch (Throwable e) { final String msg = ModelerCore.Util.getString("ModelCopyCommand.Error_getting_Emf_resource_references_4"); //$NON-NLS-1$ this.problemList.add(new Status(IStatus.ERROR, PID, ERROR_COPYING_RESOURCE, msg, e)); } // since we are processing an XSD resource then set the incremental build state // to false to improve performance final boolean incrementalBuildState = this.getXsdIncrementalBuild(source); this.setXsdIncrementalBuild(source, false); // get the modified state of the resource before copying it final boolean modifiedState = source.isModified(); // create an output stream and write the resource to a new file ... final IPath newFilePath = this.newIFile.getLocation(); final File newFile = newFilePath.toFile(); OutputStream fos = null; OutputStream bos = null; try { fos = new FileOutputStream(newFile); bos = new BufferedOutputStream(fos); source.save(bos, Collections.EMPTY_MAP); } catch (Throwable t) { final Object[] params = new Object[] {newFile}; final String msg = ModelerCore.Util.getString("ModelCopyCommand.Exception_saving_XSD_resource_to_0_1", params); //$NON-NLS-1$ this.problemList.add(new Status(IStatus.ERROR, PID, ERROR_COPYING_RESOURCE, msg, t)); } finally { if (bos != null) { try { bos.close(); } catch (IOException e1) { // do nothing } } if (fos != null) { try { fos.close(); } catch (IOException e1) { // do nothing } } } // create the ModelResource for the new file final ModelResource modelResource = ModelerCore.create(this.newIFile); if (modelResource == null) { final Object[] params = new Object[] {this.newModelName}; final String msg = ModelerCore.Util.getString("ModelCopyCommand.Error_creating_ModelResource_for_1", params); //$NON-NLS-1$ this.problemList.add(new Status(IStatus.ERROR, PID, ERROR_COPYING_RESOURCE, msg, null)); return; } Resource target = null; try { // get the Emf resource for the destination target = modelResource.getEmfResource(); if (target == null) { final Object[] params = new Object[] {modelResource}; final String msg = ModelerCore.Util.getString("ModelCopyCommand.Error_getting_Emf_resource_for_3", params); //$NON-NLS-1$ this.problemList.add(new Status(IStatus.ERROR, PID, ERROR_COPYING_RESOURCE, msg, null)); return; } // Create new UUIDs for all XSDSimpleTypeDefinition entities in the model copy resetXsdSimpleTypeUuids(target); // Resave the EMF resource target.save(Collections.EMPTY_MAP); target.setModified(false); } catch (Throwable e) { final String msg = ModelerCore.Util.getString("ModelCopyCommand.Error_getting_Emf_resource_references_4"); //$NON-NLS-1$ this.problemList.add(new Status(IStatus.ERROR, PID, ERROR_COPYING_RESOURCE, msg, e)); } // Save the resulting target resource try { modelResource.save(monitor, true); } catch (Throwable e) { final Object[] params = new Object[] {modelResource}; final String msg = ModelerCore.Util.getString("ModelCopyCommand.Error_saving_new_model_resource_7", params); //$NON-NLS-1$ this.problemList.add(new Status(IStatus.ERROR, PID, ERROR_COPYING_RESOURCE, msg, e)); } // reset the incremental build state to what it was prior to the copy operation this.setXsdIncrementalBuild(source, incrementalBuildState); // reset the modified state of the resource to what is was prior to the copy operation source.setModified(modifiedState); // collect the originals to copied map Map originalsToCopies = getOriginalToCopiesMap(source, target); // Process the referencing resources and save ... updateReferencingResources(monitor, originalsToCopies, modelResource); } protected Map getOriginalToCopiesMap( final Resource source, final Resource target ) { Map originalToCopied = new HashMap(); Map uriFragmentToObject = new HashMap(); for (final Iterator iter = source.getAllContents(); iter.hasNext();) { EObject originalObj = (EObject)iter.next(); String uri = EcoreUtil.getURI(originalObj).toString(); // get the uri fragment from the uri int beginIndex = uri.indexOf(DatatypeConstants.URI_REFERENCE_DELIMITER) + 1; String uriFragment = (beginIndex > 0 ? uri.substring(beginIndex) : uri); if (uriFragment != null) { uriFragmentToObject.put(uriFragment.toUpperCase(), originalObj); } } for (final Iterator iter = target.getAllContents(); iter.hasNext();) { EObject copiedObj = (EObject)iter.next(); String uri = EcoreUtil.getURI(copiedObj).toString(); // get the uri fragment from the uri int beginIndex = uri.indexOf(DatatypeConstants.URI_REFERENCE_DELIMITER) + 1; String uriFragment = (beginIndex > 0 ? uri.substring(beginIndex) : uri); if (uriFragment != null) { EObject originalObject = (EObject)uriFragmentToObject.get(uriFragment.toUpperCase()); if (originalObject != null) { originalToCopied.put(originalObject, copiedObj); } } } return originalToCopied; } /** * Create a copy of the Xmi resource * * @param monitor */ protected void executeXmiCopy( final IProgressMonitor monitor ) { // create the ModelResource for the new file final ModelResource modelResource = ModelerCore.create(this.newIFile); if (modelResource == null) { final Object[] params = new Object[] {this.newModelName}; final String msg = ModelerCore.Util.getString("ModelCopyCommand.Error_creating_ModelResource_for_1", params); //$NON-NLS-1$ problemList.add(new Status(IStatus.ERROR, PID, ERROR_COPYING_RESOURCE, msg, null)); return; } // Copy the contents from the original resource into the new resource Resource source = null; Resource target = null; try { // get the Emf resource for the original source = resourceToCopy.getEmfResource(); if (source == null) { final Object[] params = new Object[] {resourceToCopy}; final String msg = ModelerCore.Util.getString("ModelCopyCommand.Error_getting_Emf_resource_for_2", params); //$NON-NLS-1$ problemList.add(new Status(IStatus.ERROR, PID, ERROR_COPYING_RESOURCE, msg, null)); return; } // get the Emf resource for the destination target = modelResource.getEmfResource(); if (target == null) { final Object[] params = new Object[] {modelResource}; final String msg = ModelerCore.Util.getString("ModelCopyCommand.Error_getting_Emf_resource_for_3", params); //$NON-NLS-1$ problemList.add(new Status(IStatus.ERROR, PID, ERROR_COPYING_RESOURCE, msg, null)); return; } } catch (Throwable e) { final String msg = ModelerCore.Util.getString("ModelCopyCommand.Error_getting_Emf_resource_references_4"); //$NON-NLS-1$ problemList.add(new Status(IStatus.ERROR, PID, ERROR_COPYING_RESOURCE, msg, e)); } // copy the ModelAnnotation information from the source to the target final Map originalsToCopies = new HashMap(); try { if (source instanceof EmfResource && target instanceof EmfResource) { ModelAnnotation sourceAnnot = ((EmfResource)source).getModelAnnotation(); ModelAnnotation targetAnnot = ((EmfResource)target).getModelAnnotation(); // If the ModelAnnotation does not yet exist in the target then simple copy it if (targetAnnot == null) { targetAnnot = (ModelAnnotation)this.copyEObject(sourceAnnot); target.getContents().add(targetAnnot); } else { // Set the ModelAnnotation properties on the target targetAnnot.setDescription(sourceAnnot.getDescription()); targetAnnot.setExtensionPackage(sourceAnnot.getExtensionPackage()); targetAnnot.setMaxSetSize(sourceAnnot.getMaxSetSize()); targetAnnot.setModelType(sourceAnnot.getModelType()); targetAnnot.setNameInSource(sourceAnnot.getNameInSource()); targetAnnot.setPrimaryMetamodelUri(sourceAnnot.getPrimaryMetamodelUri()); targetAnnot.setSupportsDistinct(sourceAnnot.isSupportsDistinct()); targetAnnot.setSupportsJoin(sourceAnnot.isSupportsJoin()); targetAnnot.setSupportsOrderBy(sourceAnnot.isSupportsOrderBy()); targetAnnot.setSupportsOuterJoin(sourceAnnot.isSupportsOuterJoin()); targetAnnot.setSupportsWhereAll(sourceAnnot.isSupportsWhereAll()); targetAnnot.setVisible(sourceAnnot.isVisible()); // Copy the model imports ... targetAnnot.eContents().clear(); for (final Iterator iter = sourceAnnot.eContents().iterator(); iter.hasNext();) { EObject sourceImport = (EObject)iter.next(); if (sourceImport != null) { EObject targetImport = this.copyEObject(sourceImport); targetAnnot.getModelImports().add(targetImport); originalsToCopies.put(sourceImport, targetImport); } } originalsToCopies.put(sourceAnnot, targetAnnot); } } } catch (Throwable e) { final String msg = ModelerCore.Util.getString("ModelCopyCommand.Error_copying_ModelAnnotation_information_5"); //$NON-NLS-1$ problemList.add(new Status(IStatus.ERROR, PID, ERROR_COPYING_RESOURCE, msg, e)); } // Copy the remaining roots from the source to the target try { final List sourceRootsToCopy = new ArrayList(source.getContents()); for (final Iterator iter = sourceRootsToCopy.iterator(); iter.hasNext();) { EObject sourceRoot = (EObject)iter.next(); if (sourceRoot instanceof ModelAnnotation) { iter.remove(); } } final Collection targetRoots = this.copyEObject(sourceRootsToCopy, originalsToCopies); /* * Replicating the model objects is not quite good enough since the transformations may * still contain the name of the source model. * Need to replace those references with the new name. */ SqlTransformationMappingRootSqlAspect.replaceTransformationLiteral( targetRoots, resourceToCopy.getItemName(), modelResource.getItemName()); // Process the copied root EObjects ... for (final Iterator iter = targetRoots.iterator(); iter.hasNext();) { EObject targetRoot = (EObject)iter.next(); // Check the copied EObjects for references to entities in the original // model. If found, reset the reference from the original to the copy. ResetReferencesVisitor visitor = new ResetReferencesVisitor(originalsToCopies, false); ConcurrentModelVisitorProcessor processor = new ConcurrentModelVisitorProcessor(visitor); processor.walk(targetRoot, ModelVisitorProcessor.DEPTH_INFINITE); // Add the root to the target resource target.getContents().add(targetRoot); } } catch (Throwable e) { final String msg = ModelerCore.Util.getString("ModelCopyCommand.Error_copying_model_roots_6"); //$NON-NLS-1$ problemList.add(new Status(IStatus.ERROR, PID, ERROR_COPYING_RESOURCE, msg, e)); } // Save the resulting target resource try { ModelBuildUtil.rebuildImports(target, true); modelResource.save(monitor, true); } catch (Throwable e) { final Object[] params = new Object[] {modelResource}; final String msg = ModelerCore.Util.getString("ModelCopyCommand.Error_saving_new_model_resource_7", params); //$NON-NLS-1$ problemList.add(new Status(IStatus.ERROR, PID, ERROR_COPYING_RESOURCE, msg, e)); } // Process the referencing resources and save ... updateReferencingResources(monitor, originalsToCopies, modelResource); } // ================================================================================== // P R I V A T E M E T H O D S // ================================================================================== /** * Visit the resource or eObject to check if it or any of its children has references to any of eObjects in the original * model. If found, reset the reference value from the original to the copy. */ private class ResetReferencesVisitor implements ModelVisitor { private final Map originalsToCopies; private final Collection updatedExternalObjects = new HashSet(); private final boolean isExternalResource; /** * ResetReferencesVisitor * * @param originalsToCopies the map of original EObject to copy * @since 4.2 */ public ResetReferencesVisitor( final Map originalsToCopies, final boolean isExternalResource ) { this.originalsToCopies = originalsToCopies; this.isExternalResource = isExternalResource; } /** * @param eObj the EObject whose contents will be checked for references to the original model and updated to new model * @see org.teiid.designer.core.util.ModelVisitor#visit(org.eclipse.emf.ecore.EObject) * @since 4.2 */ @Override public boolean visit( final EObject eObj ) { // Get all EReference features for this EClass final List refs = eObj.eClass().getEAllReferences(); for (final Iterator iter1 = refs.iterator(); iter1.hasNext();) { final EReference eReference = (EReference)iter1.next(); // since we are going to visit all children // need not visit container references if (eReference.isContainment() && eReference.isContainer() && eReference.isVolatile()) { continue; } final Object value = eObj.eGet(eReference); if (eReference.isMany()) { // multi-valued feature ... final List values = (List)value; if (!values.isEmpty()) { final Collection newValues = new ArrayList(values.size()); boolean resetList = false; // Check each value in the List for references to // EObjects in the original model. If the value // is found in the map then the EObject was copied // so the reference needs to be reset for (final Iterator iter2 = values.iterator(); iter2.hasNext();) { final Object valueInList = iter2.next(); if (valueInList != null && valueInList instanceof EObject) { final EObject orig = (EObject)valueInList; final EObject copy = (EObject)originalsToCopies.get(orig); if (copy != null) { newValues.add(copy); resetList = true; } else { // copy does not exist, // values on the object may need to be updated updateReference(orig); newValues.add(orig); } } } if (resetList) { // always fails (see SetCommand.java:340): // eObj.eSet(eReference,newValues); ModelerCore.getModelEditor().setPropertyValue(eObj, newValues, eReference); } } } else { // There may be 0..1 value ... if (value != null && value instanceof EObject) { final EObject orig = (EObject)value; final EObject copy = (EObject)originalsToCopies.get(orig); if (copy != null) { eObj.eSet(eReference, copy); } else { // copy does not exist, // values on the object may need to be updated updateReference(orig); } } } } return true; } private void updateReference( final EObject eObject ) { if (isExternalResource && !this.updatedExternalObjects.contains(eObject)) { for (final Iterator iter = getReferenceUpdators().iterator(); iter.hasNext();) { ReferenceUpdator updator = (ReferenceUpdator)iter.next(); updator.updateEObject(eObject, this.originalsToCopies); } this.updatedExternalObjects.add(eObject); } } /** * @see org.teiid.designer.core.util.ModelVisitor#visit(org.eclipse.emf.ecore.resource.Resource) * @since 4.2 */ @Override public boolean visit( final Resource resource ) { return (resource != null); } } /** * Update references on the resources that reference the original model that is copied. * * @since 4.2 */ public void updateReferencingResources( final IProgressMonitor monitor, final Map originalsToCopies, final ModelResource referencedResource ) { if (this.referencingResources == null || this.referencingResources.isEmpty()) { return; } // reset references on external resources ResetReferencesVisitor visitor = new ResetReferencesVisitor(originalsToCopies, true); // Process the referencing resources and save ... try { for (final Iterator iter = ModelCopyCommand.this.referencingResources.iterator(); iter.hasNext();) { ModelResource referenceResource = (ModelResource)iter.next(); // process the model resource to reset references ConcurrentModelVisitorProcessor processor = new ConcurrentModelVisitorProcessor(visitor); processor.walk(referenceResource, ModelVisitorProcessor.DEPTH_INFINITE); // Save the resulting resource referenceResource.save(monitor, true); } } catch (Throwable e) { final Object[] params = new Object[] {referencedResource}; final String msg = ModelerCore.Util.getString("ModelCopyCommand.0", params); //$NON-NLS-1$ problemList.add(new Status(IStatus.ERROR, PID, ERROR_COPYING_RESOURCE, msg, e)); } } static Collection getReferenceUpdators() { if (REFERENCE_UPDATORS == null) { // Find all extensions of the notifiers extension point final String id = ModelerCore.EXTENSION_POINT.REFERENCE_UPDATOR.UNIQUE_ID; final IExtension[] extensions = PluginUtilities.getExtensions(id); // initialize the validators array REFERENCE_UPDATORS = new ArrayList(extensions.length); for (int i = 0; i < extensions.length; ++i) { final IExtension extension = extensions[i]; final String element = ModelerCore.EXTENSION_POINT.REFERENCE_UPDATOR.ELEMENTS.CLASS; final String attribute = ModelerCore.EXTENSION_POINT.REFERENCE_UPDATOR.ATTRIBUTES.NAME; try { final Object instance = PluginUtilities.createExecutableExtension(extension, element, attribute); if (instance instanceof ReferenceUpdator) { REFERENCE_UPDATORS.add(instance); } else { final String message = ModelerCore.Util.getString("ModelCopyCommand.0"); //$NON-NLS-1$ ModelerCore.Util.log(message); } } catch (CoreException e) { ModelerCore.Util.log(e); } } } return REFERENCE_UPDATORS; } /** * Determine the path for the target resource if the name were to change to the proposed name. Takes into account the * extension, if a file and the extension exists. * * @param proposedName * @return */ private String getModifiedPathString( final String proposedName ) { final String parentPath = this.destination.getFullPath().toString(); String newPath = parentPath + '/' + proposedName; if (this.extension != null && this.extension.length() > 0) { newPath += ('.' + this.extension); } return newPath; } /** * Determine if the target resource were changed to the proposed name, is there another resource in the same container already * named the proposed name. * * @param proposedName * @return true if the proposed name clashes with a sibling; otherwise, false. */ private boolean fileAlreadyExists( final String proposedName ) { final String newPath = getModifiedPathString(proposedName); final IWorkspaceRoot workspaceRoot = this.resourceToCopy.getResource().getWorkspace().getRoot(); return (workspaceRoot.findMember(newPath) != null); } private EObject copyEObject( final EObject eObject ) { // return ModelerCore.getModelEditor().copy(sourceRoot); // For some reason the ModelEditor.copy operation is not producing // a deep copy of some root entities so use EcoreUtil.copy. We can // use EcoreUtil.copy since the command result is not undoable. return EcoreUtil.copy(eObject); } // private Collection copyEObject(final Collection eObjects) { // //return ModelerCore.getModelEditor().copy(sourceRoot); // // For some reason the ModelEditor.copy operation is not producing // // a deep copy of some root entities so use EcoreUtil.copy. We can // // use EcoreUtil.copy since the command result is not undoable. // return EcoreUtil.copyAll(eObjects); // } private Collection copyEObject( final List eObjects, final Map originalsToCopies ) throws Exception { return ModelerCore.getModelEditor().copyAll(eObjects, originalsToCopies); } private boolean isXsdResource( final IFile resource ) { return ModelUtil.isXsdFile(resource); } private boolean getXsdIncrementalBuild( final Resource resource ) { if (resource instanceof XSDResourceImpl) { final XSDResourceImpl xsdResource = (XSDResourceImpl)resource; final XSDSchema schema = xsdResource.getSchema(); if (schema != null) { return schema.isIncrementalUpdate(); } } return false; } private void resetXsdSimpleTypeUuids( final Resource resource ) throws Exception { if (resource instanceof XSDResourceImpl) { final XSDResourceImpl xsdResource = (XSDResourceImpl)resource; final XSDSchema schema = xsdResource.getSchema(); if (schema != null) { for (final Iterator iter = schema.getContents().iterator(); iter.hasNext();) { EObject eObj = (EObject)iter.next(); // Only process global simple type definitions ... if (eObj instanceof XSDSimpleTypeDefinition) { final XSDSimpleTypeDefinition type = (XSDSimpleTypeDefinition)eObj; // Get the application information ... final XSDAnnotation annotation = type.getAnnotation(); // If no annotation exists then no UUID attribute exists to reset ... if (annotation == null) { continue; } for (final Iterator appInfos = annotation.getApplicationInformation().iterator(); appInfos.hasNext();) { final Element appInfo = (Element)appInfos.next(); String uuid = appInfo.getAttribute(UUID_ATTRIBUTE_NAME); if (uuid != null) { uuid = IDGenerator.getInstance().create().toString(); appInfo.setAttribute(UUID_ATTRIBUTE_NAME, uuid); uuid = appInfo.getAttribute(UUID_ATTRIBUTE_NAME); } } } } } } } private void setXsdIncrementalBuild( final Resource resource, final boolean isIncrementalUpdate ) { if (resource instanceof XSDResourceImpl) { final XSDResourceImpl xsdResource = (XSDResourceImpl)resource; final XSDSchema schema = xsdResource.getSchema(); if (schema != null) { schema.setIncrementalUpdate(isIncrementalUpdate); } } } }