/* * 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.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.eclipse.core.filebuffers.FileBuffers; import org.eclipse.core.filebuffers.ITextFileBuffer; import org.eclipse.core.filebuffers.LocationKind; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceVisitor; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.ltk.core.refactoring.TextFileChange; import org.eclipse.text.edits.MultiTextEdit; import org.eclipse.text.edits.ReplaceEdit; import org.eclipse.text.edits.TextEdit; import org.eclipse.ui.IEditorPart; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.core.index.IndexUtil; import org.teiid.designer.core.refactor.IRefactorModelHandler.RefactorType; import org.teiid.designer.core.refactor.ModelResourceCollectorVisitor; import org.teiid.designer.core.refactor.PathPair; import org.teiid.designer.core.refactor.RefactorModelExtensionManager; import org.teiid.designer.core.refactor.RelatedResourceFinder; import org.teiid.designer.core.refactor.RelatedResourceFinder.Relationship; import org.teiid.designer.core.workspace.ModelResource; import org.teiid.designer.core.workspace.ModelResourceImpl; import org.teiid.designer.core.workspace.ModelUtil; import org.teiid.designer.core.workspace.WorkspaceResourceFinderUtil; import org.teiid.designer.ui.UiConstants; import org.teiid.designer.ui.common.util.UiUtil; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * Utilities for moving a resource */ public class RefactorResourcesUtils { private static final String PARENT_DIRECTORY = ".."; //$NON-NLS-1$ private static final String MODEL_IMPORTS_ELEMENT_START = "<modelImports "; //$NON-NLS-1$ private static final String SQL_STATEMENT_START = "Sql="; //$NON-NLS-1$ private static final String WINDOWS_LINE_TERMINATOR = "\r\n"; //$NON-NLS-1$ /** * Options for types of pair to be included in calculations */ public enum Option { /** * Exclude folders from collection of path pairs */ EXCLUDE_FOLDERS; } /** * Callback for individual refactoring classes to process resources/files * found to be related to a given resource. */ public interface IResourceCallback { /** * Check that the given related file is valid according to the rules of * the refactoring taking place. * * Populates the given status with any problems. * * @param relatedFile * @param status */ void checkValidFile(IFile relatedFile, RefactoringStatus status); /** * Index the given related file against the resource. The type of indexing * is dependent on the refactoring class implementing the interface * * Populates the given status with any problems. * * @param resource * @param relatedFile * @param status * * @throws Exception */ void indexFile(IResource resource, IFile relatedFile, RefactoringStatus status) throws Exception; /** * Index the given related vdb against the resource. The type of indexing * is dependent on the refactoring class implementing the interface * * Populates the given status with any problems. * * @param resource * @param vdbFile * @param status */ void indexVdb(IResource resource, IFile vdbFile, RefactoringStatus status); } /** * Abstract implementation of {@link IResourceCallback} */ public abstract static class AbstractResourceCallback implements IResourceCallback { @Override public void checkValidFile(IFile relatedFile, RefactoringStatus status) { // do nothing } @Override public void indexFile(IResource resource, IFile relatedFile, RefactoringStatus status) throws Exception { // do nothing } @Override public void indexVdb(IResource resource, IFile vdbFile, RefactoringStatus status) { // do nothing } } /** * Get i18n string value * * @param key * @param parameters * * @return i18n string */ public static String getString(String key, Object...parameters) { return UiConstants.Util.getString(key, parameters); } /** * Take an array of path components and convert them into * a string buffer, complete with path separators. * * @param pathArray * @param startingIndex * @param pathBuffer buffer to be populated */ private static void createPathBuffer(String[] pathArray, int startingIndex, StringBuffer pathBuffer) { for (int i = startingIndex; i < pathArray.length; ++i) { if (pathArray[i].length() == 0) continue; pathBuffer.append(pathArray[i]); if ((i + 1) < pathArray.length) pathBuffer.append(File.separator); } } /** * Take an array of path components and convert them into * a string buffer, complete with path separators. * * @param pathArray * @param startingIndex * * @return {@link StringBuffer} of a path */ private static StringBuffer createPathBuffer(String[] pathArray, int startingIndex) { StringBuffer pathBuffer = new StringBuffer(); createPathBuffer(pathArray, startingIndex, pathBuffer); return pathBuffer; } /** * Append the given number of parent symbols ("..") to the given buffer. * * @param buffer to append to * @param numder of parent symbols to append */ private static void appendParentSymbol(StringBuffer buffer, int number) { for (int i = 0; i < number; ++i) { buffer.append(PARENT_DIRECTORY); buffer.append(File.separator); } } /** * Derive a relative path pair from the given target absolute path pair * using the given base directory * * This is just horrible but seems little option. A test is available in * TestRefactorResourceUtils with different use-cases. If a bug is found then * add to this test. * * @param baseDirectory directory to relativise against * @param absPair path pair containing absolute paths * * @return path of target relative to the base directory * * @throws IOException */ public static PathPair getRelativePath(String baseDirectory, PathPair absPair) throws IOException { String source = absPair.getSourcePath(); String target = absPair.getTargetPath(); String separator = java.util.regex.Pattern.quote(File.separator); String[] barr = baseDirectory.split(separator); String[] sarr = source.split(separator); String[] tarr = target.split(separator); if (barr[0].equals(sarr[0]) && sarr[0].equals(tarr[0])) { StringBuffer baseBuffer = createPathBuffer(barr, 1); StringBuffer srcBuffer = createPathBuffer(sarr, 1); StringBuffer tgtBuffer = createPathBuffer(tarr, 1); return getRelativePath(baseBuffer.toString(), new PathPair(srcBuffer.toString(), tgtBuffer.toString())); } /* * The common pieces of the paths have been removed so only the * different components remain. */ boolean RENAME = false; /* * Determine whether this is a rename by analysing the * remaining components of sarr and tarr. * * Only if the lengths of sarr and tarr match and the last component in * tarr differs from sarr then it could be a rename. */ if (sarr.length == tarr.length && ! sarr[sarr.length - 1].equals(tarr[tarr.length - 1])) { // Need to analyse whether components other than the last // are different. If other components differ as well then its a move // and not a rename RENAME = true; for (int i = 0; i < (sarr.length - 1); ++i) { if (! sarr[i].equals(tarr[i])) { RENAME = false; break; } } } StringBuffer src = new StringBuffer(); StringBuffer tgt = new StringBuffer(); if (baseDirectory.length() == 0) { /* both source and target are inside base directory */ // Append the remainder of source components to the src and target paths createPathBuffer(sarr, 0, src); createPathBuffer(tarr, 0, tgt); } else if (barr[0].equals(sarr[0])) { /* source is below base but target has now branched off */ appendParentSymbol(src, barr.length - 1); createPathBuffer(sarr, 1, src); if (RENAME) { appendParentSymbol(tgt, barr.length - 1); createPathBuffer(tarr, 1, tgt); } else { appendParentSymbol(tgt, barr.length); createPathBuffer(tarr, 0, tgt); } } else if (barr[0].equals(tarr[0])) { /* target is below base but source has now branched off */ appendParentSymbol(src, barr.length); createPathBuffer(sarr, 0, src); if (RENAME) { appendParentSymbol(tgt, barr.length); createPathBuffer(tarr, 1, tgt); } else { appendParentSymbol(tgt, barr.length - 1); createPathBuffer(tarr, 1, tgt); } } else { /* both source and target have branched off from base */ appendParentSymbol(src, barr.length); createPathBuffer(sarr, 0, src); appendParentSymbol(tgt, barr.length); createPathBuffer(tarr, 0, tgt); } return new PathPair(src.toString(), tgt.toString()); } private static void calculateResourceMoves(IResource resource, String destination, Collection<PathPair> pathPairs, Option...options) throws Exception { if (! (resource instanceof IFolder) && ! (resource instanceof IFile)) { // Ignore other types of resource return; } List<Option> optionList = Collections.emptyList(); if (options != null && options.length > 0) { optionList = Arrays.asList(options); } String resourcePath = ModelUtil.getLocation(resource).makeAbsolute().toOSString(); if (resource instanceof IFolder) { IFolder folder = (IFolder) resource; if (! optionList.contains(Option.EXCLUDE_FOLDERS)) { pathPairs.add(new PathPair(resourcePath, destination + IPath.SEPARATOR + resource.getName())); } for (IResource subResource : folder.members(false)) { calculateResourceMoves(subResource, destination + IPath.SEPARATOR + folder.getName(), pathPairs); } } else { /* * resource is a file */ pathPairs.add(new PathPair(resourcePath, destination + IPath.SEPARATOR + resource.getName())); } } private static void calculateResourceMoves(List<IResource> resources, String destination, Collection<PathPair> pathPairs, Option...options) throws Exception { for (IResource resource : resources) { calculateResourceMoves(resource, destination, pathPairs, options); } } /** * Calculates the absolute {@link PathPair}s produced when moving the given resources * to the given destination. * * <p> * Assumptions: * <ul> * <li>Resources are either files or directories and are absolute</li> * <li>Destination is an absolute path</li> * </ul> * </p> * * @param resources * @param destination * @param options * * @return list of absolute {@link PathPair}s * @throws Exception */ public static Set<PathPair> calculateResourceMoves(List<IResource> resources, String destination, Option... options) throws Exception { Set<PathPair> pathPairs = new HashSet<PathPair>(); calculateResourceMoves(resources, destination, pathPairs, options); return pathPairs; } private static TextEdit setRootEdit(TextFileChange textFileChange) { if (textFileChange.getEdit() != null) return textFileChange.getEdit(); MultiTextEdit edit = new MultiTextEdit(); textFileChange.setEdit(edit); return edit; } /** * Calculates a {@link TextFileChange} for all the substitutions that need to take place in the * given file based on the collection of path pairs. * * @param file * @param pathPairs * @return text file change * * @throws Exception */ public static TextFileChange calculateTextChanges(IFile file, Collection<PathPair> pathPairs) throws Exception { File nativeFile = ModelUtil.getLocation(file).makeAbsolute().toFile(); if (nativeFile == null || ! nativeFile.exists()) throw new Exception(getString("ResourcesRefactoring.fileNotFoundError", file.getFullPath())); //$NON-NLS-1$ boolean isWindows = hasWindowsLineTerminator(nativeFile); TextFileChange textFileChange = new TextFileChange(file.getName(), file); TextEdit fileChangeRootEdit = setRootEdit(textFileChange); if (pathPairs.isEmpty()) { return textFileChange; } BufferedReader reader = null; String line; int docOffset = 0; try { reader = new BufferedReader(new FileReader(nativeFile)); while ((line = reader.readLine()) != null) { if (!line.contains(MODEL_IMPORTS_ELEMENT_START)) { for (PathPair pathPair : pathPairs) { if (pathPair.getSourcePath().equals(pathPair.getTargetPath())) { /* * Absolutely nothing to do since the replacement is * the same as the change */ continue; } String sourcePath = pathPair.getSourcePath().replace('\\','/'); // TEIIDDES-2434 - Ensure srcPath separators agree with xmi line boolean sourcePathHasSlash = sourcePath.indexOf('/') > -1; int lineOffset = line.indexOf(sourcePath); if (lineOffset < 0) continue; if( sourcePathHasSlash || line.charAt(lineOffset-1) == '"') { int offset = docOffset + lineOffset; ReplaceEdit edit = new ReplaceEdit(offset, pathPair.getSourcePath().length(), pathPair.getTargetPath().replace('\\','/')); fileChangeRootEdit.addChild(edit); } } } // Add the line length and a +1 represent the newline character docOffset += line.length() + 1; if( isWindows ) { docOffset++; } } } finally { if (reader != null) reader.close(); } return textFileChange; } /** * Finds the import locations in the given file and calculates the modified paths against the * given destination. If an import location points to a resource in the given set then nothing * should be done since that resource is also being moved to the destination and no change * is necessary. * * @param file the file to be refactored * @param destination the location the file is to be refactored to * @param refactorResources the collection of all resources being refactored * * @return a set of path pairs representing the import locations * * @throws Exception */ public static Set<PathPair> calculateImportChanges(IFile file, String destination, Set<IResource> refactorResources) throws Exception { File nativeFile = ModelUtil.getLocation(file).makeAbsolute().toFile(); if (nativeFile == null || ! nativeFile.exists()) throw new Exception(getString("ResourcesRefactoring.fileNotFoundError", file.getFullPath())); //$NON-NLS-1$ Set<String> refactorResourcePaths = new HashSet<String>(); for (IResource resource : refactorResources) { File nativeRes = ModelUtil.getLocation(resource).makeAbsolute().toFile(); refactorResourcePaths.add(nativeRes.getCanonicalPath()); } Set<PathPair> importPairs = new HashSet<PathPair>(); File parentFolder = nativeFile.getParentFile(); // Find the imports in the file and return a collection of relative resource paths DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); Document xmlDocument = dBuilder.parse(nativeFile); NodeList modelList = xmlDocument.getElementsByTagName("modelImports"); //$NON-NLS-1$ for (int i = 0; i < modelList.getLength(); ++i) { Node modelNode = modelList.item(i); if (modelNode.getNodeType() != Node.ELEMENT_NODE) continue; Element element = (Element) modelNode; if (!element.hasAttribute("modelLocation")) //$NON-NLS-1$ continue; String relativeLocation = element.getAttribute("modelLocation"); //$NON-NLS-1$ if (relativeLocation.startsWith("http:") || relativeLocation.startsWith("https:")) { //$NON-NLS-1$ //$NON-NLS-2$ continue; } // Find the absolute path of the model location based on the location of the file File absLocationFile = new File(parentFolder, relativeLocation); String absLocation = absLocationFile.getCanonicalPath(); if (refactorResourcePaths.contains(absLocation)) continue; // Use the new proposed location of the file to extrapolate the relative path of the // import location. This takes advantage of the getRelativePath function by adding // the absLocation to the source and target of a path pair. This is for convenience. // Original model location -> ../sources/sourcemodel.xmi // Absolute model location -> /home/test1/programming/java/td-projects/parts/sources/sourcemodel.xmi // /home/test1/programming/java/td-projects/parts -> sources/sourcemodel.xmi // /home/test1/programming/java/td-projects/parts/test -> ../sources/sourcemodel.xmi // /home/test1/programming/java/td-projects/parts/sources -> sourcemodel.xmi PathPair newRelativePair = getRelativePath(destination, new PathPair(absLocation, absLocation)); String newRelativeLocation = newRelativePair.getSourcePath(); if (! relativeLocation.equals(newRelativeLocation)) { // Only if the import location must change do we need to include it importPairs.add(new PathPair(relativeLocation, newRelativeLocation)); } } return importPairs; } /** * Calculate changes to user-generated sql * * @param file * @param pathPair * @param textFileChange * * @throws Exception */ public static void calculateSQLChanges(IFile file, PathPair pathPair, TextFileChange textFileChange) throws Exception { if (!ModelUtil.isModelFile(file)) { // Only model files' names will be included in any SQL statements. Projects and folders will certainly not be. return; } IPath sourcePath = new Path(pathPair.getSourcePath()); IPath targetPath = new Path(pathPair.getTargetPath()); String sourceName = sourcePath.removeFileExtension().lastSegment(); String targetName = targetPath.removeFileExtension().lastSegment(); if (sourceName.equals(targetName)) return; File nativeFile = ModelUtil.getLocation(file).makeAbsolute().toFile(); if (nativeFile == null || ! nativeFile.exists()) throw new Exception(getString("ResourcesRefactoring.fileNotFoundError", file.getFullPath())); //$NON-NLS-1$ boolean isWindows = hasWindowsLineTerminator(nativeFile); TextEdit fileChangeRootEdit = setRootEdit(textFileChange); BufferedReader reader = null; String line; int docOffset = 0; char[] prefixChars = { ' ', ',', ';', '\t', '\n', '(' }; try { reader = new BufferedReader(new FileReader(nativeFile)); while ((line = reader.readLine()) != null) { if (line.contains(SQL_STATEMENT_START)) { //$NON-NLS-1$ for (char prefixChar : prefixChars) { String toReplace = prefixChar + sourceName + '.'; for (int lineOffset = line.indexOf(toReplace); lineOffset >= 0; lineOffset = line.indexOf(toReplace, lineOffset + 1)) { if (lineOffset < 0) continue; // +1 on the end taking care of the prefix character int offset = docOffset + lineOffset + 1; ReplaceEdit edit = new ReplaceEdit(offset, sourceName.length(), targetName); fileChangeRootEdit.addChild(edit); } } } // Add the line length and a +1 represent the newline character docOffset += line.length() + 1; if( isWindows ) { docOffset++; } } } finally { if (reader != null) reader.close(); } } /** * Calculate changes to user-generated sql * * @param file * @param pathPair * @param textFileChange * * @throws Exception */ public static void calculateModelImportsElementLChanges(IFile file, PathPair pathPair, TextFileChange textFileChange) throws Exception { File nativeFile = ModelUtil.getLocation(file).makeAbsolute().toFile(); if (nativeFile == null || ! nativeFile.exists()) throw new Exception(getString("ResourcesRefactoring.fileNotFoundError", file.getFullPath())); //$NON-NLS-1$ TextEdit fileChangeRootEdit = setRootEdit(textFileChange); boolean isWindows = hasWindowsLineTerminator(nativeFile); BufferedReader reader = null; String line; int docOffset = 0; try { reader = new BufferedReader(new FileReader(nativeFile)); while ((line = reader.readLine()) != null) { if (line.contains(MODEL_IMPORTS_ELEMENT_START)) { //$NON-NLS-1$ // Let's replace entire line StringBuilder sb = new StringBuilder(line); // Assuming it's amodel import, we need to 1) Check for path changes and create an edit // 2) if (1) is performed, the create a second edit to replace the model name if (pathPair.getSourcePath().equals(pathPair.getTargetPath())) { /* * Absolutely nothing to do since the replacement is * the same as the change */ // Add the line length and a +1 represent the newline character docOffset += line.length() + 1; continue; } // name="partssupplier" modelLocation="partssupplier.xmi" // name="partssupplier_view_2" modelLocation="views/partssupplier_view_2.xmi" // HAVE TO PREVENT THE FOLLOWING // name="partssupplier_RENAMED" modelLocation="partssupplier_RENAMED.xmi" // name="partssupplier_RENAMED_view_2" modelLocation="views/partssupplier_view_2.xmi" // Note that a file renaming a file named "partssupplier.xmi" will end up modifying "my_partssupplier.xmi" // Need to add a check to see that the index-1 is a " double-quote character // Note that a file renaming a file named "partssupplier.xmi" will end up modifying "partssupplier_otherfile.xmi" // Need to add a check to see that the index + path/name length is a " double-quote character // Model location may not contain any '/' chars, so the indexOf(sourcePath) will look the same as // EXAMPLE : name="views_products" modelLocation="views_products.xmi" // where products.xmi is being renamed to products_RENAMED.xmi String sourcePath = pathPair.getSourcePath().replace('\\','/'); // TEIIDDES-2434 - Ensure srcPath separators agree with xmi line String sourceNameOnly = pathPair.getSourceNameNoExtension(); boolean sourcePathHasSlash = sourcePath.indexOf('/') > -1; int lineOffset = line.indexOf(sourcePath); if (lineOffset > 0 ) { boolean startsWithDQuote = line.charAt(lineOffset-1) == '"'; // Check for use-case where there is no slash in path and source name is the suffix of a different model // EXAMPLE products.xmi is in the modelLocation="view_products.xmi" // If slash is in path, then it won't get here because of full path check if( sourcePathHasSlash || startsWithDQuote) { int offset = docOffset + lineOffset; ReplaceEdit edit = new ReplaceEdit(offset, pathPair.getSourcePath().length(), pathPair.getTargetPath().replace('\\','/')); fileChangeRootEdit.addChild(edit); } } lineOffset = line.indexOf(sourceNameOnly); boolean startsWithDQuote = false; boolean endsWithDQuote = false; if(lineOffset>0) { startsWithDQuote = line.charAt(lineOffset-1) == '"'; endsWithDQuote = line.charAt(lineOffset + sourceNameOnly.length()) =='"'; } if (lineOffset > 0 && startsWithDQuote && endsWithDQuote) { int offset = docOffset + lineOffset; ReplaceEdit edit = new ReplaceEdit(offset, pathPair.getSourceNameNoExtension().length(), pathPair.getTargetNameNoExtension()); fileChangeRootEdit.addChild(edit); } } // Add the line length and a +1 represent the newline character docOffset += line.length() + 1; if( isWindows ) { docOffset++; } } } finally { if (reader != null) reader.close(); } } /** * Is the given resource a closed project * * @param resource * @return true if the resource is a closed project */ public static boolean isClosedProject(IResource resource) { if (resource instanceof IProject && !((IProject) resource).isOpen()) return true; return false; } private static boolean hasWindowsLineTerminator(File file) { boolean hasWindowsTerminator = false; try { String lineTerminator = getLineTerminator(file); if(lineTerminator!=null && lineTerminator.equals(WINDOWS_LINE_TERMINATOR)) { hasWindowsTerminator = true; } } catch (IOException ex) { // Nothing to do. Defaults to linux terminator on error } return hasWindowsTerminator; } private static String getLineTerminator(File file) throws IOException { char current; String lineTerminator = ""; //$NON-NLS-1$ FileInputStream fis = new FileInputStream(file); try { while (fis.available() > 0) { current = (char) fis.read(); if ((current == '\n') || (current == '\r')) { lineTerminator += current; if (fis.available() > 0) { char next = (char) fis.read(); if ((next != current) && ((next == '\r') || (next == '\n'))) { lineTerminator += next; } } return lineTerminator; } } } finally { fis.close(); } return null; } /** * Unload the model resource related to the given resource * * @param resource * @throws CoreException */ public static void unloadModelResource(IResource resource) throws CoreException { if (isClosedProject(resource)) { /* * A closed project will not have any resources to unload */ return; } // Collect all IResources within all IProjects ModelResourceCollectorVisitor visitor = new ModelResourceCollectorVisitor(); resource.accept(visitor); for (Iterator iter = visitor.getModelResources().iterator(); iter.hasNext();) { ModelResource mResource = (ModelResource)iter.next(); mResource.unload(); mResource.close(); if (mResource instanceof ModelResourceImpl) { ((ModelResourceImpl)mResource).removeEmfResource(); } } // The resources move/rename will trigger the event that will actually remove and create // the corresponding resources, since these too are workspace management events // they are processed after the refactoring is done. But since we need the index files at // the old path to be deleted and the index files at the new path to be created, // we do it explicitly. // Delete the index files corresponding to the model resource at the old path for (Iterator iter = visitor.getResources().iterator(); iter.hasNext();) { IResource tmpResource = (IResource)iter.next(); if (ModelUtil.isModelFile(tmpResource) && tmpResource.getLocation() != null) { // Remove the runtime index file associated with the resource being removed String runtimeIndexFileName = IndexUtil.getRuntimeIndexFileName(tmpResource); File runtimeIndexFile = new File(IndexUtil.INDEX_PATH, runtimeIndexFileName); if (!runtimeIndexFile.delete()) { runtimeIndexFile.deleteOnExit(); } // Remove the search index file associated with the resource being removed String searchIndexFileName = IndexUtil.getSearchIndexFileName(tmpResource); File searchIndexFile = new File(IndexUtil.INDEX_PATH, searchIndexFileName); if (!searchIndexFile.delete()) { searchIndexFile.deleteOnExit(); } } } } /** * Return the given resources cumulative type * * @param resources * * @return int representing the cumulative type of resources */ public static int getResourceTypes(List<IResource> resources) { if (resources == null || resources.isEmpty()) return 0; int types = 0; for (IResource resource : resources) { types |= resource.getType(); } return types; } /** * @param resources * * @return true if resources are only non-projects */ public static boolean containsOnlyNonProjects(List<IResource> resources) { int types = getResourceTypes(resources); // check for empty selection if (types == 0) { return false; } return (types & IResource.PROJECT) == 0; } /** * @param resources * * @return true if resources are only projects */ public static boolean containsOnlyProjects(List<IResource> resources) { int types = getResourceTypes(resources); return types == IResource.PROJECT; } /** * @param resources * * @return true is resources contain linked resource */ public static boolean containsLinkedResource(List<IResource> resources) { if (resources == null || resources.isEmpty()) return false; for (IResource resource : resources) { if (resource != null && resource.isLinked()) { // paranoia code, can not be null return true; } } return false; } private static void checkSavedFile(IFile file, RefactoringStatus status) { if (!file.exists()) { status.merge(RefactoringStatus.createFatalErrorStatus(getString("ResourcesRefactoring.resourceNoExistError", file.getName()))); //$NON-NLS-1$ return; } IEditorPart fileEditor = UiUtil.getEditorForFile(file, false); if (fileEditor != null && fileEditor.isDirty()) { status.addFatalError(getString("ResourcesRefactoring.unsavedFile", file.getFullPath())); //$NON-NLS-1$ return; } ITextFileBuffer buffer= FileBuffers.getTextFileBufferManager().getTextFileBuffer(file.getFullPath(), LocationKind.IFILE); if (buffer != null && buffer.isDirty()) { if (buffer.isStateValidated() && buffer.isSynchronized()) { status.addWarning(getString("ResourcesRefactoring.unsavedFile", file.getFullPath())); //$NON-NLS-1$ } else { status.addFatalError(getString("ResourcesRefactoring.unsavedFile", file.getFullPath())); //$NON-NLS-1$ } } } /** * Check if the given resource has unsaved content. * * Populates the given status object accordingly. * * @param resource * @param status * */ public static void checkSavedResource(IResource resource, final RefactoringStatus status) { if (isClosedProject(resource)) { /* * All resources in a closed project must already be saved */ return; } try { resource.accept(new IResourceVisitor() { @Override public boolean visit(IResource visitedResource) { if (visitedResource instanceof IFile) { checkSavedFile((IFile)visitedResource, status); } return true; } }, IResource.DEPTH_INFINITE, false); } catch (CoreException ex) { status.merge(RefactoringStatus.createFatalErrorStatus(ex.getMessage())); } } /** * Check if the given file has an open editor. * * @param file * @param status */ private static void checkOpenEditors(IFile file, RefactoringStatus status) { if (!file.exists()) { status.merge(RefactoringStatus.createFatalErrorStatus(getString("ResourcesRefactoring.resourceNoExistError", file.getName()))); //$NON-NLS-1$ return; } IEditorPart fileEditor = UiUtil.getEditorForFile(file, false); if (fileEditor != null) { status.addFatalError(getString("ResourcesRefactoring.openEditorError", file.getFullPath())); //$NON-NLS-1$ return; } } /** * Check if the given resource has open editors. * * Populates the given status object accordingly. * * @param resource * @param status * */ public static void checkOpenEditors(IResource resource, final RefactoringStatus status) { if (isClosedProject(resource)) { /* * A closed project cannot have any open editors since its not open! */ return; } try { resource.accept(new IResourceVisitor() { @Override public boolean visit(IResource visitedResource) { if (visitedResource instanceof IFile) { checkOpenEditors((IFile) visitedResource, status); } return true; } }, IResource.DEPTH_INFINITE, false); } catch (CoreException ex) { status.merge(RefactoringStatus.createFatalErrorStatus(ex.getMessage())); } } /** * Check the given resource exists. * * Populates the given status object accordingly. * * @param resource * @param status * */ public static void checkResourceExists(IResource resource, RefactoringStatus status) { if (!resource.exists()) { status.merge(RefactoringStatus.createFatalErrorStatus(getString("ResourcesRefactoring.resourceNoExistError", resource.getName()))); //$NON-NLS-1$ } } /** * Check the given resource is synchronized. * * Populates the given status object accordingly. * * @param resource * @param status * */ public static void checkResourceSynched(IResource resource, RefactoringStatus status) { if (!resource.isSynchronized(IResource.DEPTH_INFINITE)) { status.merge(RefactoringStatus.createFatalErrorStatus(getString("ResourcesRefactoring.warningOutOfSync", resource.getFullPath()))); //$NON-NLS-1$ } } /** * Check the given resource is writable * * Populates the given status object accordingly. * * @param resource * @param status * @param statusLevel * @param statusMsg * */ public static void checkResourceWritable(IResource resource, RefactoringStatus status, int statusLevel, String statusMsg) { if (ModelUtil.isIResourceReadOnly(resource)) { switch (statusLevel) { case IStatus.INFO: status.merge(RefactoringStatus.createInfoStatus(statusMsg)); break; case IStatus.WARNING: status.merge(RefactoringStatus.createWarningStatus(statusMsg)); break; default: status.merge(RefactoringStatus.createFatalErrorStatus(statusMsg)); } } } /** * Checks whether the given resource's related {@link ModelResource} and its * related resources are writable. * * Populates the given status object accordingly. * * @param resource * @param status * @param statusLevel * @param statusMsg * */ public static void checkModelResourceWritable(IResource resource, RefactoringStatus status, int statusLevel, String statusMsg) { try { ModelResource modelResource = ModelUtil.getModel(resource); if (modelResource == null) { // Test does not apply since this resource is not a model return; } if (modelResource.isReadOnly()) { switch (statusLevel) { case IStatus.INFO: status.merge(RefactoringStatus.createInfoStatus(statusMsg)); break; case IStatus.WARNING: status.merge(RefactoringStatus.createWarningStatus(statusMsg)); break; default: status.merge(RefactoringStatus.createFatalErrorStatus(statusMsg)); } } } catch (Exception err) { ModelerCore.Util.log(IStatus.ERROR, err, err.getMessage()); status.merge(RefactoringStatus.createFatalErrorStatus(err.getMessage())); } } /** * Check the given resource is not a project * * Populates the given status object accordingly. * * @param resource * @param status * */ public static void checkResourceIsNotProject(IResource resource, RefactoringStatus status) { if (resource instanceof IProject) { status.merge(RefactoringStatus.createFatalErrorStatus(getString("ResourcesRefactoring.refactorProjectError", resource.getName()))); //$NON-NLS-1$ } } /** * Check the {@link RefactorModelExtensionManager#preProcess(RefactorType, IResource, IProgressMonitor)} * for problems. * * Populates the given status object accordingly. * * @param resource * @param refactorType * @param progressMonitor * @param status */ public static void checkExtensionManager(IResource resource, RefactorType refactorType, IProgressMonitor progressMonitor, RefactoringStatus status) { if (! RefactorModelExtensionManager.preProcess(refactorType, resource, progressMonitor)) { status.merge(RefactoringStatus.createFatalErrorStatus(getString("ResourcesRefactoring.extensionManagerError"))); //$NON-NLS-1$ } } /** * Calculate VDBs related to the given resource and use the given * callback to process them appropriately * * @param resource * @param status * @param callback */ public static void calculateRelatedVdbResources(IResource resource, RefactoringStatus status, IResourceCallback callback) { Collection<IFile> vdbResources = WorkspaceResourceFinderUtil.getVdbResourcesThatContain(resource); for (IFile vdb : vdbResources) { callback.checkValidFile(vdb, status); if (! status.isOK()) { return; } callback.indexVdb(resource, vdb, status); } } /** * Calculate resources related to the given resource and use the given * callback to process them appropriately * * @param resource * @param status * @param callback * @param relationship one of {@link Relationship} */ public static void calculateRelatedResources(IResource resource, RefactoringStatus status, IResourceCallback callback, Relationship relationship) { RelatedResourceFinder finder = new RelatedResourceFinder(resource); // Determine dependent resources Collection<IFile> searchResults = finder.findRelatedResources(relationship); if (searchResults == null) return; for (IFile file : searchResults) { try { callback.checkValidFile(file, status); if (status.getSeverity() > IStatus.WARNING) { return; } callback.indexFile(resource, file, status); } catch (Exception ex) { UiConstants.Util.log(ex); status.merge(RefactoringStatus.createFatalErrorStatus(ex.getMessage())); return; } } } }