1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-29 19:45:01 +02:00

Bug 456099. More flexible file name matching algorithm.

This commit is contained in:
Sergey Prigogin 2014-12-30 16:05:35 -08:00
parent 6de0d0163b
commit fb6cf9f410
5 changed files with 159 additions and 68 deletions

View file

@ -58,7 +58,7 @@ import org.eclipse.cdt.internal.ui.refactoring.CRefactoringContext;
* Common base for refactoring tests.
*/
public abstract class RefactoringTestBase extends BaseTestCase {
private static final Pattern FILENAME_PATTERN = Pattern.compile("^(\\w+/)*\\w+\\.\\w+$");
private static final Pattern FILENAME_PATTERN = Pattern.compile("((\\w|_|-)+/)*(\\w|_|-)+\\.\\w+");
/** Allows empty files to be created during test setup. */
protected boolean createEmptyFiles = true;
/** See {@link PreferenceConstants.CLASS_MEMBER_ASCENDING_VISIBILITY_ORDER} */

View file

@ -277,44 +277,44 @@ public class RenameMoveHeaderRefactoringTest extends RefactoringTestBase {
compareFiles();
}
// OriginalClass.h
//#ifndef ORIGINALCLASS_H_
//#define ORIGINALCLASS_H_
// original-class.h
//#ifndef ORIGINAL_CLASS_H_
//#define ORIGINAL_CLASS_H_
//
//class OriginalClass {};
//
//#endif // ORIGINALCLASS_H_
//#endif // ORIGINAL_CLASS_H_
//====================
// RenamedClass.h
//#ifndef RENAMEDCLASS_H_
//#define RENAMEDCLASS_H_
// renamed-class.h
//#ifndef RENAMED_CLASS_H_
//#define RENAMED_CLASS_H_
//
//class RenamedClass {};
//
//#endif // RENAMEDCLASS_H_
//#endif // RENAMED_CLASS_H_
// OriginalClass.cpp
//#include "OriginalClass.h"
// original-class.cpp
//#include "original-class.h"
//
//#include <cstdio>
//====================
// RenamedClass.cpp
//#include "RenamedClass.h"
// renamed-class.cpp
//#include "renamed-class.h"
//
//#include <cstdio>
// OriginalClass_test.cpp
//#include "OriginalClass.h"
// original-class_test.cpp
//#include "original-class.h"
//====================
// RenamedClass_test.cpp
//#include "RenamedClass.h"
// renamed-class_test.cpp
//#include "renamed-class.h"
// SomeOtherFile.cpp
//#include "OriginalClass.h"
// some-other-file.cpp
//#include "original-class.h"
///*$*/OriginalClass/*$$*/ a;
//====================
// SomeOtherFile.cpp
//#include "RenamedClass.h"
// some-other-file.cpp
//#include "renamed-class.h"
//RenamedClass a;
public void testClassRename() throws Exception {
executeRenameRefactoring("RenamedClass", true);

View file

@ -46,10 +46,9 @@ import org.eclipse.cdt.core.parser.util.ArrayUtil;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.PreferenceConstants;
import org.eclipse.cdt.internal.ui.editor.SourceHeaderPartnerFinder;
import org.eclipse.cdt.internal.ui.refactoring.changes.CCompositeChange;
import org.eclipse.cdt.internal.ui.refactoring.changes.RenameTranslationUnitChange;
import org.eclipse.cdt.internal.ui.wizards.filewizard.NewSourceFileGenerator;
import org.eclipse.cdt.internal.ui.util.NameComposer;
/**
* Processor adding constructor and destructor to the bindings to be renamed.
@ -130,51 +129,57 @@ public class CRenameClassProcessor extends CRenameTypeProcessor {
if (fullPath == null)
return;
IPath headerPath = new Path(fullPath);
String className = binding.getName();
String headerName = NewSourceFileGenerator.generateHeaderFileNameFromClass(className);
if (!headerPath.lastSegment().equals(headerName))
return;
IResource file = ResourcesPlugin.getWorkspace().getRoot().findMember(headerPath);
if (file == null || file.getType() != IResource.FILE)
return;
String newClassName = getReplacementText();
String newHeaderName = NewSourceFileGenerator.generateHeaderFileNameFromClass(newClassName);
IProject project = getProject();
int headerCapitalization = PreferenceConstants.getPreference(
PreferenceConstants.NAME_STYLE_CPP_HEADER_CAPITALIZATION, project,
PreferenceConstants.NAME_STYLE_CAPITALIZATION_ORIGINAL);
String headerWordDelimiter = PreferenceConstants.getPreference(
PreferenceConstants.NAME_STYLE_CPP_HEADER_WORD_DELIMITER, project, ""); //$NON-NLS-1$
int sourceCapitalization = PreferenceConstants.getPreference(
PreferenceConstants.NAME_STYLE_CPP_SOURCE_CAPITALIZATION, project,
PreferenceConstants.NAME_STYLE_CAPITALIZATION_ORIGINAL);
String sourceWordDelimiter = PreferenceConstants.getPreference(
PreferenceConstants.NAME_STYLE_CPP_SOURCE_WORD_DELIMITER, project, ""); //$NON-NLS-1$
String headerName = headerPath.lastSegment();
String className = binding.getName();
NameComposer nameComposer = NameComposer.createByExample(className, headerName,
headerCapitalization, headerWordDelimiter);
if (nameComposer == null)
return;
String newClassName = getReplacementText();
String newHeaderName = nameComposer.compose(newClassName);
if (!newHeaderName.equals(headerName)) {
renameTranslationUnit((IFile) file, newHeaderName);
}
String sourceName = NewSourceFileGenerator.generateSourceFileNameFromClass(className);
String testName = NewSourceFileGenerator.generateTestFileNameFromClass(className);
String[] partnerFileSuffixes = getPartnerFileSuffixes();
IIndexInclude[] includedBy = index.findIncludedBy(names[0].getFile());
for (IIndexInclude include : includedBy) {
location = include.getIncludedByLocation();
fullPath = location.getFullPath();
if (fullPath == null)
continue;
IPath sourcePath = new Path(fullPath);
file = ResourcesPlugin.getWorkspace().getRoot().findMember(sourcePath);
IPath filePath = new Path(fullPath);
file = ResourcesPlugin.getWorkspace().getRoot().findMember(filePath);
if (file != null && file.getType() == IResource.FILE) {
if (sourcePath.lastSegment().equals(sourceName)) {
String newName = NewSourceFileGenerator.generateSourceFileNameFromClass(newClassName);
if (!newName.equals(sourceName)) {
String fileName = filePath.lastSegment();
if (CoreModel.isValidHeaderUnitName(project, fileName)) {
nameComposer = NameComposer.createByExample(className, fileName,
headerCapitalization, headerWordDelimiter);
} else {
nameComposer = NameComposer.createByExample(className, fileName,
sourceCapitalization, sourceWordDelimiter);
}
if (nameComposer != null) {
String newName = nameComposer.compose(newClassName);
if (!newName.equals(fileName)) {
renameTranslationUnit((IFile) file, newName);
}
} else if (sourcePath.lastSegment().equals(testName)) {
String newName = NewSourceFileGenerator.generateTestFileNameFromClass(newClassName);
file = ResourcesPlugin.getWorkspace().getRoot().findMember(sourcePath);
if (!newName.equals(testName)) {
renameTranslationUnit((IFile) file, newName);
}
} else if (SourceHeaderPartnerFinder.isPartnerFile(sourcePath, headerPath, partnerFileSuffixes)) {
String name = sourcePath.lastSegment();
String baseName = headerPath.removeFileExtension().lastSegment();
if (name.startsWith(baseName)) {
String newBaseName = new Path(headerName).removeFileExtension().lastSegment();
String newName = newBaseName + name.substring(baseName.length());
if (!newName.equals(name)) {
renameTranslationUnit((IFile) file, newName);
}
}
}
}
}
@ -196,11 +201,10 @@ public class CRenameClassProcessor extends CRenameTypeProcessor {
}
}
private String[] getPartnerFileSuffixes() {
IFile file= getArgument().getSourceFile();
IProject project = file == null ? null : file.getProject();
String value = PreferenceConstants.getPreference(
PreferenceConstants.INCLUDES_PARTNER_FILE_SUFFIXES, project, ""); //$NON-NLS-1$
return value.split(","); //$NON-NLS-1$
protected IProject getProject() {
IFile file= getArgument().getSourceFile();
if (file == null)
return null;
return file.getProject();
}
}

View file

@ -486,11 +486,12 @@ public class HeaderFileReferenceAdjuster {
}
private static boolean areIncludeGuardsAffected(IFile oldfile, IFile newFile) {
IProject project = oldfile.getProject();
String filename = oldfile.getLocation().lastSegment();
if (!CoreModel.isValidHeaderUnitName(null, filename))
if (!CoreModel.isValidHeaderUnitName(project, filename))
return false;
IPreferencesService preferences = Platform.getPreferencesService();
IScopeContext[] scopes = PreferenceConstants.getPreferenceScopes(oldfile.getProject());
IScopeContext[] scopes = PreferenceConstants.getPreferenceScopes(project);
int scheme = preferences.getInt(CUIPlugin.PLUGIN_ID,
PreferenceConstants.CODE_TEMPLATES_INCLUDE_GUARD_SCHEME,
PreferenceConstants.CODE_TEMPLATES_INCLUDE_GUARD_SCHEME_FILE_NAME, scopes);

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2011 Google, Inc and others.
* Copyright (c) 2011, 2014 Google, Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@ -31,16 +31,23 @@ public class NameComposer {
private static final int CAPITALIZATION_LOWER_CASE = PreferenceConstants.NAME_STYLE_CAPITALIZATION_LOWER_CASE;
private static final int CAPITALIZATION_CAMEL_CASE = PreferenceConstants.NAME_STYLE_CAPITALIZATION_CAMEL_CASE;
private static final int CAPITALIZATION_LOWER_CAMEL_CASE = PreferenceConstants.NAME_STYLE_CAPITALIZATION_LOWER_CAMEL_CASE;
private static final int[] ALL_CAPITALIZATIONS = {
CAPITALIZATION_ORIGINAL,
CAPITALIZATION_UPPER_CASE,
CAPITALIZATION_LOWER_CASE,
CAPITALIZATION_CAMEL_CASE,
CAPITALIZATION_LOWER_CAMEL_CASE,
};
private final int capitalization;
private int capitalization;
private final String wordDelimiter;
private final String prefix;
private final String suffix;
private String suffix;
/**
* Creates a name composer for a given style.
*
* @param capitalization capitalization transformation applied to name. Possible values: <ul>
* @param capitalization capitalization transformation applied to a name. Possible values: <ul>
* <li>PreferenceConstants.NAME_STYLE_CAPITALIZATION_ORIGINAL,</li>
* <li>PreferenceConstants.NAME_STYLE_CAPITALIZATION_UPPER_CASE,</li>
* <li>PreferenceConstants.NAME_STYLE_CAPITALIZATION_LOWER_CASE,</li>
@ -64,7 +71,7 @@ public class NameComposer {
* @param seedName the name used as an inspiration
* @return the composed name
*/
public String compose(String seedName) {
public String compose(CharSequence seedName) {
List<CharSequence> words = splitIntoWords(seedName);
return compose(words);
}
@ -112,8 +119,8 @@ public class NameComposer {
/**
* Splits a name into words at non-alphanumeric characters and camel case boundaries.
*/
public List<CharSequence> splitIntoWords(CharSequence name) {
List<CharSequence> words = new ArrayList<CharSequence>();
public static List<CharSequence> splitIntoWords(CharSequence name) {
List<CharSequence> words = new ArrayList<>();
CBreakIterator iterator = new CBreakIterator();
iterator.setText(name);
int end;
@ -146,4 +153,83 @@ public class NameComposer {
Character.toUpperCase(word.charAt(i)) : Character.toLowerCase(word.charAt(i)));
}
}
/**
* Creates a NameComposer such that it would compose {@code composedName} given {@code seedName}
* as a seed.
*
* @param seedName the seed name
* @param composedName the composed name
* @param defaultCapitalization used to disambiguate capitalization if it cannot be uniquely
* determined from the composed name
* @param defaultWordDelimiter used to disambiguate word delimiter if it cannot be uniquely
* determined from the composed name
* @return a name composer based on the composed name, or {@code null} if such name composer
* does not exist
*/
public static NameComposer createByExample(String seedName, String composedName,
int defaultCapitalization, String defaultWordDelimiter) {
List<CharSequence> seedWords = splitIntoWords(seedName);
if (seedWords.isEmpty())
return null;
List<CharSequence> composedWords = splitIntoWords(composedName);
int numPrefixWords = indexOfSublistIgnoreCase(composedWords, seedWords);
if (numPrefixWords < 0)
return null;
String prefix = deducePrefix(composedName, numPrefixWords);
String delimiter = defaultWordDelimiter;
if (seedWords.size() > 1) {
delimiter = ""; //$NON-NLS-1$
int start = prefix.length() + composedWords.get(0).length();
for (int i = start; i < composedName.length(); i++) {
if (Character.isLetterOrDigit(composedName.charAt(i))) {
delimiter = composedName.substring(start, i);
break;
}
}
}
NameComposer composer = new NameComposer(defaultCapitalization, delimiter, prefix, ""); //$NON-NLS-1$
for (int i = -1; i < ALL_CAPITALIZATIONS.length; i++) {
if (i >= 0)
composer.capitalization = ALL_CAPITALIZATIONS[i];
String name = composer.compose(seedWords);
if (composedName.startsWith(name)) {
composer.suffix = composedName.substring(name.length());
return composer;
}
}
return null;
}
private static int indexOfSublistIgnoreCase(List<CharSequence> list, List<CharSequence> subList) {
int subListSize = subList.size();
int limit = list.size() - subListSize;
outer:
for (int k = 0; k <= limit; k++) {
for (int i = 0, j = k; i < subListSize; i++, j++) {
if (!subList.get(i).toString().equalsIgnoreCase(list.get(j).toString()))
continue outer;
}
return k;
}
return -1;
}
private static String deducePrefix(CharSequence name, int numPrefixWords) {
CBreakIterator iterator = new CBreakIterator();
iterator.setText(name);
int end;
int wordCount = 0;
for (int start = iterator.first(); (end = iterator.next()) != BreakIterator.DONE; start = end) {
if (Character.isLetterOrDigit(name.charAt(start))) {
if (wordCount == numPrefixWords)
return name.subSequence(0, start).toString();
wordCount++;
}
}
if (wordCount == numPrefixWords)
return name.toString();
throw new IllegalArgumentException(numPrefixWords + " is larger than the number of words in \"" + name + "\""); //$NON-NLS-1$//$NON-NLS-2$
}
}