/** * This file is licensed under the University of Illinois/NCSA Open Source License. See LICENSE.TXT for details. */ package edu.illinois.codingtracker.operations.resources; import java.io.IOException; import java.util.HashSet; import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.IParent; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.ui.PartInitException; import org.eclipse.ui.texteditor.ITextEditor; import edu.illinois.codingtracker.compare.helpers.EditorHelper; import edu.illinois.codingtracker.helpers.ResourceHelper; import edu.illinois.codingtracker.jdt.project.manipulation.JavaProjectHelper; import edu.illinois.codingtracker.operations.JavaProjectsUpkeeper; import edu.illinois.codingtracker.operations.OperationLexer; import edu.illinois.codingtracker.operations.OperationTextChunk; import edu.illinois.codingtracker.operations.UserOperation; /** * * @author Stas Negara * */ public abstract class ResourceOperation extends UserOperation { private static final String FILE_PATH_SEPARATOR= String.valueOf(IPath.SEPARATOR); private static final String PACKAGE_NAME_SEPARATOR= "."; //The following field is not serialized/deserialized. It is used for replay only. protected static final Set<String> externallyModifiedResources= new HashSet<String>(); protected String resourcePath; public ResourceOperation() { super(); } public ResourceOperation(IResource resource) { super(); resourcePath= ResourceHelper.getPortableResourcePath(resource); } public String getResourcePath() { return resourcePath; } @Override protected void populateTextChunk(OperationTextChunk textChunk) { textChunk.append(resourcePath); } @Override protected void initializeFrom(OperationLexer operationLexer) { resourcePath= operationLexer.readString(); } protected void createContainer() throws CoreException { //Simulate one more element to create the whole path as the parent of this added fake element findOrCreateParent(resourcePath + FILE_PATH_SEPARATOR + "fake"); } protected void createCompilationUnit(String content) throws CoreException { IPackageFragment packageFragment= findOrCreateCompilationUnitParent(resourcePath); String[] filePathFragments= resourcePath.split(FILE_PATH_SEPARATOR); String fileName= filePathFragments[filePathFragments.length - 1]; try { packageFragment.createCompilationUnit(fileName, content, true, null); } catch (JavaModelException exception) { handleCompilationUnitCreationException(exception, fileName, content); } //Save the created compilation unit in case it is opened in an editor (e.g. an editor showing an externally changed file). saveResourceInEditor(); } private void handleCompilationUnitCreationException(JavaModelException exception, String fileName, String content) throws CoreException { if (exception.getMessage().startsWith("Invalid name specified:")) { //If the name is invalid as a CompilationUnit name, create it as a regular file. IWorkspaceRoot workspaceRoot= ResourceHelper.getWorkspaceRoot(); IPath fullPath= workspaceRoot.getLocation().append(resourcePath); try { ResourceHelper.writeFileContent(fullPath.toFile(), content, false); } catch (IOException e) { throw new RuntimeException("Could not create a file for a CompilationUnit with an invalid name: " + fileName, e); } workspaceRoot.refreshLocal(IResource.DEPTH_INFINITE, null); } else { throw exception; //If can not handle, just re-throw the exception. } } protected ITextEditor saveResourceInEditor() throws PartInitException { ITextEditor editor= EditorHelper.getExistingEditor(resourcePath); if (editor != null) { editor.doSave(null); //FIXME: Instead of sleeping, should listen to IProgressMonitor.done() try { Thread.sleep(100); } catch (InterruptedException e) { //do nothing } } return editor; } protected IPackageFragment findOrCreateCompilationUnitParent(String path) throws CoreException { IParent parent= findOrCreateParent(path); //Can be either null, IJavaProject, or IPackageFragment. if (parent instanceof IJavaProject) { return createPackageFragment((IJavaProject)parent, "", ""); } else { return (IPackageFragment)parent; } } protected IParent findOrCreateParent(String path) throws CoreException { String[] filePathFragments= path.split(FILE_PATH_SEPARATOR); //ignore filePathFragments[0] which is an empty string, because the file path starts with '/' if (filePathFragments.length <= 2) { return null; //no parent } String projectName= filePathFragments[1]; IJavaProject javaProject= JavaProjectsUpkeeper.findOrCreateJavaProject(projectName); if (filePathFragments.length <= 3) { return javaProject; } String sourceFolderName= filePathFragments[2]; String packageName= ""; if (hasValidPackageName(filePathFragments)) { packageName= filePathFragments[3]; for (int i= 4; i < filePathFragments.length - 1; i++) { packageName= packageName + PACKAGE_NAME_SEPARATOR + filePathFragments[i]; } } else { for (int i= 3; i < filePathFragments.length - 1; i++) { sourceFolderName= sourceFolderName + FILE_PATH_SEPARATOR + filePathFragments[i]; } } return createPackageFragment(javaProject, sourceFolderName, packageName); } private IPackageFragment createPackageFragment(IJavaProject javaProject, String sourceFolderName, String packageName) throws CoreException { IPackageFragment packageFragment= null; try { IPackageFragmentRoot fragmentRoot= JavaProjectHelper.addSourceContainer(javaProject, sourceFolderName); packageFragment= fragmentRoot.createPackageFragment(packageName, true, null); } catch (Exception e) { //If the package fragment could not be created normally, clear the project's class path and try again. This helps, //when compilation units are created directly in a project (i.e. no source folder, no package name). javaProject.setRawClasspath(new IClasspathEntry[0], null); IPackageFragmentRoot fragmentRoot= JavaProjectHelper.addSourceContainer(javaProject, sourceFolderName); packageFragment= fragmentRoot.createPackageFragment(packageName, true, null); } return packageFragment; } private boolean hasValidPackageName(String[] filePathFragments) { if (filePathFragments.length <= 4) { return false; } for (int i= 3; i < filePathFragments.length - 1; i++) { if (!Character.isJavaIdentifierStart(filePathFragments[i].charAt(0)) || filePathFragments[i].contains("-") || filePathFragments[i].contains(" ")) { return false; } } return true; } protected IResource findResource() { IResource resource= ResourceHelper.findWorkspaceMember(resourcePath); if (resource != null && !isIgnored(resource)) { return resource; } return null; } private boolean isIgnored(IResource resource) { return resource instanceof IFile && !ResourceHelper.isJavaFile((IFile)resource); } @Override public String toString() { StringBuffer sb= new StringBuffer(); sb.append("Resource path: " + resourcePath + "\n"); sb.append(super.toString()); return sb.toString(); } }