From 010c8c429acf7a91012d1752248873da34236b0f Mon Sep 17 00:00:00 2001 From: Chris Wiebe Date: Sat, 21 Aug 2004 01:24:29 +0000 Subject: [PATCH] 2004-08-20 Chris Wiebe class wizard now adds include paths dynamically * src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCreationWizardPage.java * src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCreationWizardMessages.properties * src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCodeGenerator.java --- core/org.eclipse.cdt.ui/ChangeLog | 7 + .../classwizard/NewClassCodeGenerator.java | 334 +++++++++++++----- .../NewClassCreationWizardPage.java | 273 ++++++++------ .../NewClassWizardMessages.properties | 8 +- .../classwizard/NewSourceFileGenerator.java | 1 + 5 files changed, 417 insertions(+), 206 deletions(-) diff --git a/core/org.eclipse.cdt.ui/ChangeLog b/core/org.eclipse.cdt.ui/ChangeLog index 0c5207985ba..e02e0115068 100644 --- a/core/org.eclipse.cdt.ui/ChangeLog +++ b/core/org.eclipse.cdt.ui/ChangeLog @@ -1,3 +1,10 @@ +2004-08-20 Chris Wiebe + + class wizard now adds include paths dynamically + * src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCreationWizardPage.java + * src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCreationWizardMessages.properties + * src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCodeGenerator.java + 2004-08-19 Alain Magloire Match the changing API of the Resolver Model. diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCodeGenerator.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCodeGenerator.java index 35ae153c3af..cf7ff0c6bb1 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCodeGenerator.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCodeGenerator.java @@ -16,13 +16,20 @@ import java.util.List; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.browser.ITypeReference; +import org.eclipse.cdt.core.browser.PathUtil; +import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.model.ICContainer; import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.IIncludeEntry; +import org.eclipse.cdt.core.model.IPathEntry; import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.cdt.core.model.IWorkingCopy; import org.eclipse.cdt.core.parser.IScannerInfo; import org.eclipse.cdt.core.parser.IScannerInfoProvider; import org.eclipse.cdt.core.parser.ast.ASTAccessVisibility; +import org.eclipse.cdt.internal.corext.util.CModelUtil; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; @@ -30,6 +37,7 @@ import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.SubProgressMonitor; public class NewClassCodeGenerator { @@ -43,6 +51,16 @@ public class NewClassCodeGenerator { private ITranslationUnit fCreatedHeaderTU = null; private ITranslationUnit fCreatedSourceTU = null; private ICElement fCreatedClass = null; + + //TODO this should be a prefs option + private boolean fCreateIncludePaths = true; + + public static class CodeGeneratorException extends Exception { + public CodeGeneratorException(String message) { + } + public CodeGeneratorException(Throwable e) { + } + } public NewClassCodeGenerator(IPath headerPath, IPath sourcePath, String className, String namespace, IBaseClassInfo[] baseClasses, IMethodStub[] methodStubs) { fHeaderPath = headerPath; @@ -76,33 +94,30 @@ public class NewClassCodeGenerator { * @throws InterruptedException * Thrown when the operation was cancelled. */ - public ICElement createClass(IProgressMonitor monitor) throws CoreException, InterruptedException { + public ICElement createClass(IProgressMonitor monitor) throws CodeGeneratorException, CoreException, InterruptedException { if (monitor == null) monitor = new NullProgressMonitor(); - monitor.beginTask(NewClassWizardMessages.getString("NewClassCodeGeneration.createType.task"), 10); //$NON-NLS-1$ + monitor.beginTask(NewClassWizardMessages.getString("NewClassCodeGeneration.createType.mainTask"), 400); //$NON-NLS-1$ - ITranslationUnit headerTU = null; + ITranslationUnit headerTU = null; ITranslationUnit sourceTU = null; ICElement createdClass = null; IWorkingCopy headerWorkingCopy = null; IWorkingCopy sourceWorkingCopy = null; try { - monitor.worked(1); - if (fHeaderPath != null) { - IFile headerFile = NewSourceFileGenerator.createHeaderFile(fHeaderPath, true, monitor); + IFile headerFile = NewSourceFileGenerator.createHeaderFile(fHeaderPath, true, new SubProgressMonitor(monitor, 50)); if (headerFile != null) { headerTU = (ITranslationUnit) CoreModel.getDefault().create(headerFile); } - monitor.worked(1); // create a working copy with a new owner headerWorkingCopy = headerTU.getWorkingCopy(); // headerWorkingCopy = headerTU.getSharedWorkingCopy(null, CUIPlugin.getDefault().getBufferFactory()); - String headerContent = constructHeaderFileContent(headerTU, headerWorkingCopy.getBuffer().getContents()); + String headerContent = constructHeaderFileContent(headerTU, headerWorkingCopy.getBuffer().getContents(), new SubProgressMonitor(monitor, 100)); headerWorkingCopy.getBuffer().setContents(headerContent); if (monitor.isCanceled()) { @@ -111,6 +126,7 @@ public class NewClassCodeGenerator { headerWorkingCopy.reconcile(); headerWorkingCopy.commit(true, monitor); + monitor.worked(50); createdClass = headerWorkingCopy.getElement(fClassName); fCreatedClass = createdClass; @@ -118,16 +134,16 @@ public class NewClassCodeGenerator { } if (fSourcePath != null) { - IFile sourceFile = NewSourceFileGenerator.createHeaderFile(fSourcePath, true, monitor); + IFile sourceFile = NewSourceFileGenerator.createHeaderFile(fSourcePath, true, new SubProgressMonitor(monitor, 50)); if (sourceFile != null) { sourceTU = (ITranslationUnit) CoreModel.getDefault().create(sourceFile); } - monitor.worked(1); + monitor.worked(50); // create a working copy with a new owner sourceWorkingCopy = sourceTU.getWorkingCopy(); - String sourceContent = constructSourceFileContent(sourceTU, headerTU); + String sourceContent = constructSourceFileContent(sourceTU, headerTU, new SubProgressMonitor(monitor, 100)); sourceWorkingCopy.getBuffer().setContents(sourceContent); if (monitor.isCanceled()) { @@ -136,6 +152,7 @@ public class NewClassCodeGenerator { sourceWorkingCopy.reconcile(); sourceWorkingCopy.commit(true, monitor); + monitor.worked(50); fCreatedSourceTU = sourceTU; } @@ -152,7 +169,10 @@ public class NewClassCodeGenerator { return fCreatedClass; } - public String constructHeaderFileContent(ITranslationUnit headerTU, String oldContents) { + public String constructHeaderFileContent(ITranslationUnit headerTU, String oldContents, IProgressMonitor monitor) throws CodeGeneratorException { + + monitor.beginTask(NewClassWizardMessages.getString("NewClassCodeGeneration.createType.task.header"), 100); //$NON-NLS-1$ + //TODO should use code templates StringBuffer text = new StringBuffer(); @@ -179,7 +199,7 @@ public class NewClassCodeGenerator { } if (fBaseClasses != null && fBaseClasses.length > 0) { - addBaseClassIncludes(headerTU, text); + addBaseClassIncludes(headerTU, text, new SubProgressMonitor(monitor, 50)); text.append(fLineDelimiter); } @@ -227,8 +247,10 @@ public class NewClassCodeGenerator { text.append(oldContents.substring(appendFirstCharPos)); } } - - return text.toString(); + + String newContents = text.toString(); + monitor.done(); + return newContents; } private int getInsertionPos(String contents) { @@ -334,49 +356,210 @@ public class NewClassCodeGenerator { } } - private void addBaseClassIncludes(ITranslationUnit headerTU, StringBuffer text) { - IProject project = headerTU.getCProject().getProject(); + private void addBaseClassIncludes(ITranslationUnit headerTU, StringBuffer text, IProgressMonitor monitor) throws CodeGeneratorException { + + monitor.beginTask(NewClassWizardMessages.getString("NewClassCodeGeneration.createType.task.header.includePaths"), 100); //$NON-NLS-1$ + + ICProject cProject = headerTU.getCProject(); + IProject project = cProject.getProject(); IPath projectLocation = project.getLocation(); IPath headerLocation = headerTU.getResource().getLocation(); - for (int i = 0; i < fBaseClasses.length; ++i) { - String baseClassFileName = null; - boolean isSystemIncludePath = false; - IBaseClassInfo baseClass = fBaseClasses[i]; - ITypeReference ref = baseClass.getType().getResolvedReference(); - if (ref != null) { - IPath baseClassLocation = ref.getLocation(); - IPath includePath = makeRelativePathToProjectIncludes(baseClassLocation, project); - if (includePath != null && !projectLocation.isPrefixOf(baseClassLocation)) { - isSystemIncludePath = true; - } else if (projectLocation.isPrefixOf(baseClassLocation) - && projectLocation.isPrefixOf(headerLocation)) { - includePath = makeRelativePath(baseClassLocation, headerLocation); - } - if (includePath == null) - includePath = baseClassLocation; - baseClassFileName = includePath.toString(); - } - if (baseClassFileName == null) { - baseClassFileName = NewSourceFileGenerator.generateHeaderFileNameFromClass(baseClass.getType().getName()); - } - - // add the include statement if we are extending a base class - // and we are not already in the base class header file - // (enclosing type) - if (!(headerTU.getElementName().equals(baseClassFileName))) { - String include = getIncludeString(baseClassFileName, isSystemIncludePath); - text.append(include); - text.append(fLineDelimiter); - } + + List includePaths = getIncludePaths(headerTU); + List baseClassPaths = getBaseClassPaths(); + + // add the missing include paths to the project + if (fCreateIncludePaths) { + List newIncludePaths = getMissingIncludePaths(projectLocation, includePaths, baseClassPaths); + if (!newIncludePaths.isEmpty()) { + addIncludePaths(cProject, newIncludePaths, monitor); + } } + + List systemIncludes = new ArrayList(); + List localIncludes = new ArrayList(); + + // sort the include paths into system and local + for (Iterator bcIter = baseClassPaths.iterator(); bcIter.hasNext(); ) { + IPath baseClassLocation = (IPath) bcIter.next(); + boolean isSystemIncludePath = false; + + IPath includePath = PathUtil.makeRelativePathToProjectIncludes(baseClassLocation, project); + if (includePath != null && !projectLocation.isPrefixOf(baseClassLocation)) { + isSystemIncludePath = true; + } else if (projectLocation.isPrefixOf(baseClassLocation) + && projectLocation.isPrefixOf(headerLocation)) { + includePath = PathUtil.makeRelativePath(baseClassLocation, headerLocation); + } + if (includePath == null) + includePath = baseClassLocation; + + if (isSystemIncludePath) + systemIncludes.add(includePath); + else + localIncludes.add(includePath); + } + + // write the system include paths, e.g. #include + for (Iterator i = systemIncludes.iterator(); i.hasNext(); ) { + IPath includePath = (IPath) i.next(); + if (!(headerTU.getElementName().equals(includePath.toString()))) { + String include = getIncludeString(includePath.toString(), true); + text.append(include); + text.append(fLineDelimiter); + } + } + + // write the local include paths, e.g. #include "header.h" + for (Iterator i = localIncludes.iterator(); i.hasNext(); ) { + IPath includePath = (IPath) i.next(); + if (!(headerTU.getElementName().equals(includePath.toString()))) { + String include = getIncludeString(includePath.toString(), false); + text.append(include); + text.append(fLineDelimiter); + } + } + + monitor.done(); } - public String constructSourceFileContent(ITranslationUnit sourceTU, ITranslationUnit headerTU) { + private void addIncludePaths(ICProject cProject, List newIncludePaths, IProgressMonitor monitor) throws CodeGeneratorException { + monitor.beginTask(NewClassWizardMessages.getString("NewClassCodeGeneration.createType.task.header.addIncludePaths"), 100); //$NON-NLS-1$ + + //TODO prefs option whether to add to project or parent source folder? + IPath addToResourcePath = cProject.getPath(); + try { + List pathEntryList = new ArrayList(); + IPathEntry[] pathEntries = cProject.getRawPathEntries(); + if (pathEntries != null) { + for (int i = 0; i < pathEntries.length; ++i) { + pathEntryList.add(pathEntries[i]); + } + } + for (Iterator ipIter = newIncludePaths.iterator(); ipIter.hasNext(); ) { + IPath folderToAdd = (IPath) ipIter.next(); + IPath basePath = null; + IPath includePath = folderToAdd; + IProject includeProject = PathUtil.getEnclosingProject(folderToAdd); + boolean isSystemInclude = (includeProject == null); + if (includeProject != null) { + includePath = PathUtil.makeRelativePath(folderToAdd, includeProject.getLocation()); + basePath = includeProject.getFullPath().makeRelative(); + } + IIncludeEntry entry = CoreModel.newIncludeEntry(addToResourcePath, basePath, includePath, isSystemInclude); + pathEntryList.add(entry); + } + pathEntries = (IPathEntry[]) pathEntryList.toArray(new IPathEntry[pathEntryList.size()]); + cProject.setRawPathEntries(pathEntries, new SubProgressMonitor(monitor, 80)); + } catch (CModelException e) { + throw new CodeGeneratorException(e); + } + monitor.done(); + } + + private List getMissingIncludePaths(IPath projectLocation, List includePaths, List baseClassPaths) { + // check for missing include paths + List newIncludePaths = new ArrayList(); + for (Iterator bcIter = baseClassPaths.iterator(); bcIter.hasNext(); ) { + IPath baseClassLocation = (IPath) bcIter.next(); + + // skip any paths inside the same project + //TODO possibly a preferences option? + if (projectLocation.isPrefixOf(baseClassLocation)) { + continue; + } + + IPath folderToAdd = baseClassLocation.removeLastSegments(1); + IPath canonPath = PathUtil.getCanonicalPath(folderToAdd); + if (canonPath != null) + folderToAdd = canonPath; + + // see if folder or its parent hasn't already been added + for (Iterator newIter = newIncludePaths.iterator(); newIter.hasNext(); ) { + IPath newFolder = (IPath) newIter.next(); + if (newFolder.isPrefixOf(folderToAdd)) { + folderToAdd = null; + break; + } + } + + if (folderToAdd != null) { + // search include paths + boolean foundPath = false; + for (Iterator ipIter = includePaths.iterator(); ipIter.hasNext(); ) { + IPath includePath = (IPath) ipIter.next(); + if (includePath.isPrefixOf(folderToAdd)) { + foundPath = true; + break; + } + } + if (!foundPath) { + // remove any children of this folder + for (Iterator newIter = newIncludePaths.iterator(); newIter.hasNext(); ) { + IPath newFolder = (IPath) newIter.next(); + if (folderToAdd.isPrefixOf(newFolder)) { + newIter.remove(); + } + } + newIncludePaths.add(folderToAdd); + } + } + } + return newIncludePaths; + } + + private List getIncludePaths(ITranslationUnit headerTU) throws CodeGeneratorException { + IProject project = headerTU.getCProject().getProject(); + // get the parent source folder + ICContainer sourceFolder = CModelUtil.getSourceFolder(headerTU); + if (sourceFolder == null) { + throw new CodeGeneratorException("Could not find source folder"); //$NON-NLS-1$ + } + + // get the include paths + IScannerInfoProvider provider = CCorePlugin.getDefault().getScannerInfoProvider(project); + if (provider != null) { + IScannerInfo info = provider.getScannerInformation(sourceFolder.getResource()); + if (info != null) { + String[] includePaths = info.getIncludePaths(); + if (includePaths != null) { + List list = new ArrayList(); + for (int i = 0; i < includePaths.length; ++i) { + //TODO do we need to canonicalize these paths first? + list.add(new Path(includePaths[i])); + } + return list; + } + } + } + return null; + } + + private List getBaseClassPaths() throws CodeGeneratorException { + List list = new ArrayList(); + for (int i = 0; i < fBaseClasses.length; ++i) { + IBaseClassInfo baseClass = fBaseClasses[i]; + ITypeReference ref = baseClass.getType().getResolvedReference(); + IPath baseClassLocation = null; + if (ref != null) + baseClassLocation = ref.getLocation(); + if (baseClassLocation == null) { + throw new CodeGeneratorException("Could not find base class " + baseClass.toString()); //$NON-NLS-1$ + } + list.add(baseClassLocation); + } + return list; + } + + public String constructSourceFileContent(ITranslationUnit sourceTU, ITranslationUnit headerTU, IProgressMonitor monitor) { + + monitor.beginTask(NewClassWizardMessages.getString("NewClassCodeGeneration.createType.task.source"), 150); //$NON-NLS-1$ + //TODO should use code templates StringBuffer text = new StringBuffer(); if (headerTU != null) { - addHeaderInclude(sourceTU, headerTU, text); + addHeaderInclude(sourceTU, headerTU, text, new SubProgressMonitor(monitor, 50)); text.append(fLineDelimiter); } @@ -394,29 +577,31 @@ public class NewClassCodeGenerator { beginNamespace(text); } - addMethodBodies(publicMethods, protectedMethods, privateMethods, text); + addMethodBodies(publicMethods, protectedMethods, privateMethods, text, new SubProgressMonitor(monitor, 50)); if (fNamespace != null && fNamespace.length() > 0) { endNamespace(text); } } - return text.toString(); + String newContents = text.toString(); + monitor.done(); + return newContents; } - private void addHeaderInclude(ITranslationUnit sourceTU, ITranslationUnit headerTU, StringBuffer text) { + private void addHeaderInclude(ITranslationUnit sourceTU, ITranslationUnit headerTU, StringBuffer text, IProgressMonitor monitor) { IProject project = headerTU.getCProject().getProject(); IPath projectLocation = project.getLocation(); IPath headerLocation = headerTU.getResource().getLocation(); IPath sourceLocation = sourceTU.getResource().getLocation(); - IPath includePath = makeRelativePathToProjectIncludes(headerLocation, project); + IPath includePath = PathUtil.makeRelativePathToProjectIncludes(headerLocation, project); boolean isSystemIncludePath = false; if (includePath != null && !projectLocation.isPrefixOf(headerLocation)) { isSystemIncludePath = true; } else if (projectLocation.isPrefixOf(headerLocation) && projectLocation.isPrefixOf(sourceLocation)) { - includePath = makeRelativePath(headerLocation, sourceLocation); + includePath = PathUtil.makeRelativePath(headerLocation, sourceLocation); } if (includePath == null) includePath = headerLocation; @@ -426,7 +611,7 @@ public class NewClassCodeGenerator { text.append(fLineDelimiter); } - private void addMethodBodies(List publicMethods, List protectedMethods, List privateMethods, StringBuffer text) { + private void addMethodBodies(List publicMethods, List protectedMethods, List privateMethods, StringBuffer text, IProgressMonitor monitor) { if (!publicMethods.isEmpty()) { for (Iterator i = publicMethods.iterator(); i.hasNext();) { IMethodStub stub = (IMethodStub) i.next(); @@ -461,45 +646,6 @@ public class NewClassCodeGenerator { } } - public static IPath makeRelativePathToProjectIncludes(IPath fullPath, IProject project) { - IScannerInfoProvider provider = CCorePlugin.getDefault().getScannerInfoProvider(project); - if (provider != null) { - IScannerInfo info = provider.getScannerInformation(project); - if (info != null) { - String[] includePaths = info.getIncludePaths(); - IPath relativePath = null; - int mostSegments = 0; - for (int i = 0; i < includePaths.length; ++i) { - IPath includePath = new Path(includePaths[i]); - if (includePath.isPrefixOf(fullPath)) { - int segments = includePath.matchingFirstSegments(fullPath); - if (segments > mostSegments) { - relativePath = fullPath.removeFirstSegments(segments).setDevice(null); - mostSegments = segments; - } - } - } - if (relativePath != null) - return relativePath; - } - } - return null; - } - - public static IPath makeRelativePath(IPath path, IPath relativeTo) { - int segments = relativeTo.matchingFirstSegments(path); - if (segments > 0) { - IPath prefix = relativeTo.removeFirstSegments(segments).removeLastSegments(1); - IPath suffix = path.removeFirstSegments(segments); - IPath relativePath = new Path(""); //$NON-NLS-1$ - for (int i = 0; i < prefix.segmentCount(); ++i) { - relativePath = relativePath.append(".." + IPath.SEPARATOR); //$NON-NLS-1$ - } - return relativePath.append(suffix); - } - return null; - } - private static String getIncludeString(String fileName, boolean isSystemInclude) { StringBuffer buf = new StringBuffer(); buf.append("#include "); //$NON-NLS-1$ diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCreationWizardPage.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCreationWizardPage.java index 6a2632ce838..66660a65cdc 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCreationWizardPage.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCreationWizardPage.java @@ -18,7 +18,9 @@ import org.eclipse.cdt.core.CConventions; import org.eclipse.cdt.core.browser.AllTypesCache; import org.eclipse.cdt.core.browser.IQualifiedTypeName; import org.eclipse.cdt.core.browser.ITypeInfo; +import org.eclipse.cdt.core.browser.ITypeReference; import org.eclipse.cdt.core.browser.ITypeSearchScope; +import org.eclipse.cdt.core.browser.PathUtil; import org.eclipse.cdt.core.browser.QualifiedTypeName; import org.eclipse.cdt.core.browser.TypeSearchScope; import org.eclipse.cdt.core.model.CModelException; @@ -39,6 +41,7 @@ import org.eclipse.cdt.internal.ui.wizards.NewElementWizardPage; import org.eclipse.cdt.internal.ui.wizards.TypedElementSelectionValidator; import org.eclipse.cdt.internal.ui.wizards.TypedViewerFilter; import org.eclipse.cdt.internal.ui.wizards.classwizard.NewBaseClassSelectionDialog.ITypeSelectionListener; +import org.eclipse.cdt.internal.ui.wizards.classwizard.NewClassCodeGenerator.CodeGeneratorException; import org.eclipse.cdt.internal.ui.wizards.dialogfields.DialogField; import org.eclipse.cdt.internal.ui.wizards.dialogfields.IDialogFieldListener; import org.eclipse.cdt.internal.ui.wizards.dialogfields.IListAdapter; @@ -142,13 +145,17 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { protected final static String SOURCEFILE = PAGE_NAME + ".sourcefile"; //$NON-NLS-1$ StringButtonDialogField fSourceFileDialogField; protected IStatus fSourceFileStatus; - IPath fCurrentSourceFile; protected final IStatus STATUS_OK = new StatusInfo(); protected String fLastFocusedField = null; private NewClassCodeGenerator fCodeGenerator = null; + //TODO this should be a prefs option + private boolean fCreateIncludePathsAsNeeded = false; + private boolean fWarnIfBaseClassNotInPath = true; + + public NewClassCreationWizardPage() { super(PAGE_NAME); @@ -216,7 +223,6 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { fCanModifyNamespace = true; fCurrentEnclosingClass = null; fCurrentHeaderFile = null; - fCurrentSourceFile = null; fCanModifyEnclosingClass = true; updateEnableState(); } @@ -622,7 +628,6 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { ICElement folder = chooseSourceFolder(fCurrentSourceFolder); if (folder != null) { setSourceFolder(folder, true); - prepareTypeCache(); } } @@ -701,23 +706,61 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { private class ClassNameFieldAdapter implements IDialogFieldListener { public void dialogFieldChanged(DialogField field) { - if (isUseDefaultSelected()) { - String className = fClassNameDialogField.getText(); - if (className.length() > 0) { - fHeaderFileDialogField.setText(NewSourceFileGenerator.generateHeaderFileNameFromClass(className)); - fSourceFileDialogField.setText(NewSourceFileGenerator.generateSourceFileNameFromClass(className)); - } else { - fHeaderFileDialogField.setText(""); //$NON-NLS-1$ - fSourceFileDialogField.setText(""); //$NON-NLS-1$ - } - } fClassNameStatus = classNameChanged(); + if (isUseDefaultSelected()) { + updateFilesFromClassName(fClassNameDialogField.getText()); + fHeaderFileStatus = headerFileChanged(); + fSourceFileStatus = sourceFileChanged(); + } // tell all others updateEnableState(); handleFieldChanged(CLASSNAME); } } + private static final int MAX_UNIQUE_CLASSNAME = 99; + + private String findUniqueName(String className) { + IPath folderPath = getSourceFolderFullPath(); + if (folderPath == null) + return className; + + String currName = className; + int count = 0; + String separator = ""; //$NON-NLS-1$ + //TODO could have a prefs option for how unique file names are generated + if (Character.isDigit(className.charAt(className.length()-1))) + separator = "_"; //$NON-NLS-1$ + while (count < MAX_UNIQUE_CLASSNAME) { + String headerfileName = NewSourceFileGenerator.generateHeaderFileNameFromClass(currName); + String sourcefileName = NewSourceFileGenerator.generateSourceFileNameFromClass(currName); + IPath path = folderPath.append(headerfileName); + if (!path.toFile().exists()) { + path = folderPath.append(sourcefileName); + if (!path.toFile().exists()) { + return currName; + } + } + ++count; + currName = className + separator + count; //$NON-NLS-1$ + } + return null; + } + + private void updateFilesFromClassName(String className) { + String headerName = ""; //$NON-NLS-1$ + String sourceName = ""; //$NON-NLS-1$ + if (className.length() > 0) { + className = findUniqueName(className); + if (className != null) { + headerName = NewSourceFileGenerator.generateHeaderFileNameFromClass(className); + sourceName = NewSourceFileGenerator.generateSourceFileNameFromClass(className); + } + } + fHeaderFileDialogField.setTextWithoutUpdate(headerName); + fSourceFileDialogField.setTextWithoutUpdate(sourceName); + } + private final class BaseClassesFieldAdapter implements IListAdapter { public void customButtonPressed(ListDialogField field, int index) { if (index == 0) { @@ -750,12 +793,12 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { private class FileGroupFieldAdapter implements IStringButtonAdapter, IDialogFieldListener { public void changeControlPressed(DialogField field) { if (field == fHeaderFileDialogField) { - IPath filePath = chooseSourceFile(getHeaderFilePath(), NewClassWizardMessages.getString("NewClassCreationWizardPage.ChooseHeaderFileDialog.title")); //$NON-NLS-1$ + IPath filePath = chooseSourceFile(getHeaderFileFullPath(), NewClassWizardMessages.getString("NewClassCreationWizardPage.ChooseHeaderFileDialog.title")); //$NON-NLS-1$ if (filePath != null) { setHeaderFile(filePath, true); } } else if (field == fSourceFileDialogField) { - IPath filePath = chooseSourceFile(getSourceFilePath(), NewClassWizardMessages.getString("NewClassCreationWizardPage.ChooseSourceFileDialog.title")); //$NON-NLS-1$ + IPath filePath = chooseSourceFile(getSourceFileFullPath(), NewClassWizardMessages.getString("NewClassCreationWizardPage.ChooseSourceFileDialog.title")); //$NON-NLS-1$ if (filePath != null) { setSourceFile(filePath, true); } @@ -767,6 +810,9 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { boolean enabled = !isUseDefaultSelected(); fHeaderFileDialogField.setEnabled(enabled); fSourceFileDialogField.setEnabled(enabled); + if (!enabled) { + updateFilesFromClassName(fClassNameDialogField.getText()); + } } fHeaderFileStatus = headerFileChanged(); fSourceFileStatus = sourceFileChanged(); @@ -861,12 +907,6 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { IProject project = getCurrentProject(); if (project != null) { - ITypeSearchScope scope = prepareTypeCache(); - if (scope == null) { - status.setError(NewClassWizardMessages.getString("NewClassCreationWizardPage.error.noTypeCache")); //$NON-NLS-1$ - return status; - } - IQualifiedTypeName qualName = new QualifiedTypeName(namespace); ITypeInfo[] types = AllTypesCache.getTypes(project, qualName, false); @@ -921,12 +961,6 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { IProject project = getCurrentProject(); if (project != null) { - ITypeSearchScope scope = prepareTypeCache(); - if (scope == null) { - status.setError(NewClassWizardMessages.getString("NewClassCreationWizardPage.error.noTypeCache")); //$NON-NLS-1$ - return status; - } - IQualifiedTypeName qualName = new QualifiedTypeName(enclosing); ITypeInfo[] types = AllTypesCache.getTypes(project, qualName, false); @@ -992,12 +1026,6 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { IProject project = getCurrentProject(); if (project != null) { - ITypeSearchScope scope = prepareTypeCache(); - if (scope == null) { - status.setError(NewClassWizardMessages.getString("NewClassCreationWizardPage.error.noTypeCache")); //$NON-NLS-1$ - return status; - } - IQualifiedTypeName qualName = new QualifiedTypeName(className); // must not exist @@ -1058,19 +1086,23 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { */ protected IStatus baseClassesChanged() { StatusInfo status = new StatusInfo(); - IProject project = getCurrentProject(); if (project != null) { - // make sure all classes belong to the project - IBaseClassInfo[] baseClasses = getBaseClasses(); - for (int i = 0; i < baseClasses.length; ++i) { - ITypeInfo baseType = baseClasses[i].getType(); - IProject baseProject = baseType.getEnclosingProject(); - if (baseProject != null && !baseProject.equals(project)) { - status.setError(NewClassWizardMessages.getFormattedString("NewClassCreationWizardPage.error.BaseClassNotExistsInProject", baseType.getQualifiedTypeName().toString())); //$NON-NLS-1$ - return status; - } - } + if (!fCreateIncludePathsAsNeeded && fWarnIfBaseClassNotInPath) { + // make sure all classes belong to the project + IBaseClassInfo[] baseClasses = getBaseClasses(); + for (int i = 0; i < baseClasses.length; ++i) { + ITypeInfo baseType = baseClasses[i].getType(); + IProject baseProject = baseType.getEnclosingProject(); + if (baseProject == null || !baseProject.equals(project)) { + ITypeReference ref = baseType.getResolvedReference(); + if (ref == null || PathUtil.makeRelativePathToProjectIncludes(ref.getLocation(), project) == null) { + status.setWarning(NewClassWizardMessages.getFormattedString("NewClassCreationWizardPage.warning.BaseClassNotExistsInProject", baseType.getQualifiedTypeName().toString())); //$NON-NLS-1$ + return status; + } + } + } + } } return status; } @@ -1112,7 +1144,7 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { return status; } - IPath path = getHeaderFilePath(); + IPath path = getHeaderFileFullPath(); IResource res = fWorkspaceRoot.findMember(path); if (res != null && res.exists()) { int resType = res.getType(); @@ -1154,7 +1186,6 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { protected IStatus sourceFileChanged() { StatusInfo status = new StatusInfo(); - fCurrentSourceFile = null; String str = getSourceFileName(); if (str.length() == 0) { status.setError(NewClassWizardMessages.getString("NewClassCreationWizardPage.error.EnterSourceFileName")); //$NON-NLS-1$ @@ -1167,24 +1198,29 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { return status; } - IPath path = getSourceFilePath(); - IResource res = fWorkspaceRoot.findMember(path); - if (res != null && res.exists()) { - int resType = res.getType(); - if (resType == IResource.FILE) { - IProject proj = res.getProject(); - if (!proj.isOpen()) { + IPath path = getSourceFileFullPath(); + if (path == null) + return status; + + if (path.toFile().exists()) { + IResource res = fWorkspaceRoot.findMember(path); + if (res != null && res.exists()) { + int resType = res.getType(); + if (resType == IResource.FILE) { + IProject proj = res.getProject(); + if (!proj.isOpen()) { + status.setError(NewClassWizardMessages.getFormattedString("NewClassCreationWizardPage.error.NotAFile", str)); //$NON-NLS-1$ + return status; + } + if (!CoreModel.hasCCNature(proj) && !CoreModel.hasCNature(proj)) { + status.setWarning(NewClassWizardMessages.getString("NewClassCreationWizardPage.warning.NotInACProject")); //$NON-NLS-1$ + } else { + status.setWarning(NewClassWizardMessages.getString("NewClassCreationWizardPage.warning.SourceFileExists")); //$NON-NLS-1$ + } + } else { status.setError(NewClassWizardMessages.getFormattedString("NewClassCreationWizardPage.error.NotAFile", str)); //$NON-NLS-1$ return status; } - if (!CoreModel.hasCCNature(proj) && !CoreModel.hasCNature(proj)) { - status.setWarning(NewClassWizardMessages.getString("NewClassCreationWizardPage.warning.NotInACProject")); //$NON-NLS-1$ - } else { - status.setWarning(NewClassWizardMessages.getString("NewClassCreationWizardPage.warning.SourceFileExists")); //$NON-NLS-1$ - } - } else { - status.setError(NewClassWizardMessages.getFormattedString("NewClassCreationWizardPage.error.NotAFile", str)); //$NON-NLS-1$ - return status; } } else { IStatus val = validateSourceFileName(project, path.lastSegment()); @@ -1328,6 +1364,13 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { return fSourceFolderDialogField.getText(); } + public IPath getSourceFolderFullPath() { + if (fCurrentSourceFolder != null) { + return fCurrentSourceFolder.getPath(); + } + return null; + } + /** * Returns the namespace entered into the namespace input field. * @@ -1409,14 +1452,16 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { return fHeaderFileDialogField.getText(); } - public IPath getHeaderFilePath() { + public IPath getHeaderFileFullPath() { String name = getHeaderFileName(); if (name.length() > 0) { - IPath headerPath = new Path(name); - if (headerPath.isAbsolute()) { - return headerPath; - } else if (fCurrentSourceFolder != null) { - return fCurrentSourceFolder.getPath().append(headerPath); + IPath path = new Path(name); + if (path.isAbsolute()) { + return path; + } else { + IPath folderPath = getSourceFolderFullPath(); + if (folderPath != null) + return folderPath.append(path); } } return null; @@ -1431,14 +1476,16 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { return fSourceFileDialogField.getText(); } - public IPath getSourceFilePath() { + public IPath getSourceFileFullPath() { String name = getSourceFileName(); if (name.length() > 0) { - IPath headerPath = new Path(name); - if (headerPath.isAbsolute()) { - return headerPath; - } else if (fCurrentSourceFolder != null) { - return fCurrentSourceFolder.getPath().append(headerPath); + IPath path = new Path(name); + if (path.isAbsolute()) { + return path; + } else { + IPath folderPath = getSourceFolderFullPath(); + if (folderPath != null) + return folderPath.append(path); } } return null; @@ -1466,6 +1513,7 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { public void setVisible(boolean visible) { super.setVisible(visible); if (visible) { + prepareTypeCache(); setFocus(); } } @@ -1503,7 +1551,6 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { } } if (!classExists) { - prepareTypeCache(); // resolve location of base class if (newBaseClass.getResolvedReference() == null) { final ITypeInfo[] typesToResolve = new ITypeInfo[] { newBaseClass }; @@ -1536,14 +1583,8 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { fMethodStubsDialogField.addMethodStub(methodStub, checked); } - ITypeSearchScope prepareTypeCache() { - IProject project = getCurrentProject(); - if (project == null) - return null; - - final ITypeSearchScope scope = new TypeSearchScope(); - scope.add(project); - + void prepareTypeCache() { + final ITypeSearchScope scope = new TypeSearchScope(true); if (!AllTypesCache.isCacheUpToDate(scope)) { IRunnableWithProgress runnable = new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { @@ -1560,13 +1601,10 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { String title = NewClassWizardMessages.getString("NewClassCreationWizardPage.getClasses.exception.title"); //$NON-NLS-1$ String message = NewClassWizardMessages.getString("NewClassCreationWizardPage.getClasses.exception.message"); //$NON-NLS-1$ ExceptionHandler.handle(e, title, message); - return null; } catch (InterruptedException e) { // cancelled by user - return null; } } - return scope; } /** @@ -1664,7 +1702,7 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { fCurrentHeaderFile = headerPath; if (fCurrentSourceFolder != null) { IPath sourcePath = fCurrentSourceFolder.getPath(); - IPath relPath = NewClassCodeGenerator.makeRelativePath(headerPath, sourcePath); + IPath relPath = PathUtil.makeRelativePath(headerPath, sourcePath); if (relPath != null) headerPath = relPath; } @@ -1681,12 +1719,18 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { * not be changed by the user. If true the field is editable */ public void setSourceFile(IPath sourcePath, boolean canBeModified) { - fCurrentSourceFile = sourcePath; + String pathString = ""; //$NON-NLS-1$ + IPath folderPath = getSourceFolderFullPath(); + if (folderPath != null) { + IPath relativePath = PathUtil.makeRelativePath(sourcePath, folderPath); + if (relativePath != null) { + } + } if (fCurrentSourceFolder != null && fCurrentSourceFolder.getPath().isPrefixOf(sourcePath)) { sourcePath = sourcePath.removeFirstSegments(fCurrentSourceFolder.getPath().segmentCount()); } - String str = (sourcePath == null) ? "" : sourcePath.makeRelative().toString(); //$NON-NLS-1$ - fSourceFileDialogField.setText(str); + pathString = (sourcePath == null) ? "" : sourcePath.makeRelative().toString(); //$NON-NLS-1$ + fSourceFileDialogField.setText(pathString); fSourceFileDialogField.setEnabled(!isUseDefaultSelected() && canBeModified); } @@ -1775,11 +1819,12 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { } ITypeInfo chooseNamespace() { - ITypeSearchScope scope = prepareTypeCache(); - if (scope == null) + IProject project = getCurrentProject(); + if (project == null) { return null; + } - ITypeInfo[] elements = AllTypesCache.getNamespaces(scope, false); + ITypeInfo[] elements = AllTypesCache.getNamespaces(new TypeSearchScope(project), false); if (elements == null || elements.length == 0) { String title = NewClassWizardMessages.getString("NewClassCreationWizardPage.getClasses.noclasses.title"); //$NON-NLS-1$ String message = NewClassWizardMessages.getString("NewClassCreationWizardPage.getClasses.noclasses.message"); //$NON-NLS-1$ @@ -1827,11 +1872,12 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { private final int[] ENCLOSING_CLASS_TYPES = { ICElement.C_CLASS }; ITypeInfo chooseEnclosingClass() { - ITypeSearchScope scope = prepareTypeCache(); - if (scope == null) + IProject project = getCurrentProject(); + if (project == null) { return null; - - ITypeInfo[] elements = AllTypesCache.getTypes(scope, ENCLOSING_CLASS_TYPES); + } + + ITypeInfo[] elements = AllTypesCache.getTypes(new TypeSearchScope(project), ENCLOSING_CLASS_TYPES); if (elements == null || elements.length == 0) { String title = NewClassWizardMessages.getString("NewClassCreationWizardPage.getClasses.noclasses.title"); //$NON-NLS-1$ String message = NewClassWizardMessages.getString("NewClassCreationWizardPage.getClasses.noclasses.message"); //$NON-NLS-1$ @@ -1879,11 +1925,7 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { private final int[] CLASS_TYPES = { ICElement.C_CLASS, ICElement.C_STRUCT }; void chooseBaseClasses() { - ITypeSearchScope scope = prepareTypeCache(); - if (scope == null) - return; - - ITypeInfo[] elements = AllTypesCache.getTypes(scope, CLASS_TYPES); + ITypeInfo[] elements = AllTypesCache.getTypes(new TypeSearchScope(true), CLASS_TYPES); if (elements == null || elements.length == 0) { String title = NewClassWizardMessages.getString("NewClassCreationWizardPage.getClasses.noclasses.title"); //$NON-NLS-1$ String message = NewClassWizardMessages.getString("NewClassCreationWizardPage.getClasses.noclasses.message"); //$NON-NLS-1$ @@ -1937,15 +1979,26 @@ public class NewClassCreationWizardPage extends NewElementWizardPage { * @throws CoreException Thrown when the creation failed. * @throws InterruptedException Thrown when the operation was cancelled. */ - public void createClass(IProgressMonitor monitor) throws CoreException, InterruptedException { - fCodeGenerator = new NewClassCodeGenerator( - getHeaderFilePath(), - getSourceFilePath(), - getClassName(), - getNamespace(), - getBaseClasses(), - getCheckedMethodStubs()); - fCodeGenerator.createClass(monitor); + public void createClass(IProgressMonitor monitor) { + try { + fCodeGenerator = new NewClassCodeGenerator( + getHeaderFileFullPath(), + getSourceFileFullPath(), + getClassName(), + getNamespace(), + getBaseClasses(), + getCheckedMethodStubs()); + fCodeGenerator.createClass(monitor); + } catch (CodeGeneratorException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (CoreException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } /** diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassWizardMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassWizardMessages.properties index f100f044aaf..cd4322147a3 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassWizardMessages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassWizardMessages.properties @@ -64,7 +64,7 @@ NewClassCreationWizardPage.warning.ClassNameDiscouraged=Class name is discourage NewClassCreationWizardPage.baseClasses.label=&Base Classes: NewClassCreationWizardPage.error.InvalidBaseClassName=Base class name is not valid. {0} -NewClassCreationWizardPage.error.BaseClassNotExistsInProject=Base class ''{0}'' does not exist in the current project. +NewClassCreationWizardPage.warning.BaseClassNotExistsInProject=Base class ''{0}'' not in include paths for current project. NewClassCreationWizardPage.methodStubs.label=Method &Stubs: @@ -161,7 +161,11 @@ SourceFileSelectionDialog.error.NotASourceFile=''{0}'' is not a valid source fil SourceFileSelectionDialog.warning.SourceFileExists=New class contents will be appended to existing file ''{0}''. # -----------NewClassCodeGeneration ------------- -NewClassCodeGeneration.createType.task=Creating type.... +NewClassCodeGeneration.createType.mainTask=Creating type.... +NewClassCodeGeneration.createType.task.header=Creating header file.... +NewClassCodeGeneration.createType.task.header.includePaths=Adding include paths.... +NewClassCodeGeneration.createType.task.header.addIncludePaths=Adding new include paths to project.... +NewClassCodeGeneration.createType.task.source=Creating source file.... NewClassCodeGeneration.createFile.task=Creating NewClassCodeGeneration.stub.constructor.name=Constructor NewClassCodeGeneration.stub.destructor.name=Destructor diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewSourceFileGenerator.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewSourceFileGenerator.java index ff5c8403c0b..9740bb4fead 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewSourceFileGenerator.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewSourceFileGenerator.java @@ -27,6 +27,7 @@ import org.eclipse.ui.dialogs.ContainerGenerator; public class NewSourceFileGenerator { + //TODO these should all be configurable in prefs private static final String HEADER_EXT = ".h"; //$NON-NLS-1$ private static final String SOURCE_EXT = ".cpp"; //$NON-NLS-1$ private static boolean fUseIncludeGuard = true;