Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2021 IBM Corporation and others.
* Copyright (c) 2000, 2025 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -280,6 +280,7 @@ public IAnnotationBinding[] removeRedundantTypeAnnotations(IAnnotationBinding[]

private static final char STATIC_PREFIX= 's';
private static final char NORMAL_PREFIX= 'n';
private static final char MODULE_PREFIX= 'm';

/** @deprecated using deprecated code */
private static final int JLS8_INTERNAL = AST.JLS8;
Expand All @@ -291,6 +292,7 @@ public IAnnotationBinding[] removeRedundantTypeAnnotations(IAnnotationBinding[]

private final boolean restoreExistingImports;
private final List existingImports;
private final Map<String, List<String>> moduleEntries;
private final Map importsKindMap;

private String[] importOrder;
Expand All @@ -314,6 +316,7 @@ public IAnnotationBinding[] removeRedundantTypeAnnotations(IAnnotationBinding[]

private String[] createdImports;
private String[] createdStaticImports;
private String[] createdModuleImports;

private boolean filterImplicitImports;
private boolean useContextToFilterImplicitImports;
Expand All @@ -337,15 +340,64 @@ public static ImportRewrite create(ICompilationUnit cu, boolean restoreExistingI
throw new IllegalArgumentException("Compilation unit must not be null"); //$NON-NLS-1$
}
List existingImport= null;
Map<String, List<String>> moduleEntries= null;
if (restoreExistingImports) {
existingImport= new ArrayList();
moduleEntries= new HashMap<>();
IImportDeclaration[] imports= cu.getImports();
CompilationUnit compilationUnit= null;
for (IImportDeclaration curr : imports) {
char prefix= Flags.isStatic(curr.getFlags()) ? STATIC_PREFIX : NORMAL_PREFIX;
existingImport.add(prefix + curr.getElementName());
char prefix= Flags.isStatic(curr.getFlags()) ? STATIC_PREFIX :
Flags.isModule(curr.getFlags()) ? MODULE_PREFIX : NORMAL_PREFIX;
String currName= curr.getElementName();
if (currName.endsWith(".*")) { //$NON-NLS-1$
currName= currName.substring(0, currName.length() - 2);
}
existingImport.add(prefix + currName);
if (Flags.isModule(curr.getFlags())) {
List<String> packageNames= new ArrayList<>();
if (compilationUnit == null) {
compilationUnit= convertICUtoCU(cu);
}
if (compilationUnit != null) {
List<ImportDeclaration> astImports= compilationUnit.imports();
ImportDeclaration foundModuleImport= null;
for (ImportDeclaration astImport : astImports) {
if (astImport.getName().getFullyQualifiedName().equals(currName)) {
foundModuleImport= astImport;
break;
}
}
if (foundModuleImport != null) {
IBinding moduleImportBinding= foundModuleImport.resolveBinding();
if (moduleImportBinding instanceof IModuleBinding moduleBinding) {
packageNames= getPackageNames(moduleBinding);
}
}
}
moduleEntries.put(curr.getElementName(), packageNames);
}
}
}
return new ImportRewrite(cu, null, existingImport);
return new ImportRewrite(cu, null, existingImport, moduleEntries);
}

private static List<String> getPackageNames(IModuleBinding binding) {
List<String> result= new ArrayList<>();
IPackageBinding[] packageBindings= binding.getExportedPackages();
for (IPackageBinding packageBinding : packageBindings) {
result.add(packageBinding.getName());
}
return result;
}

private static CompilationUnit convertICUtoCU(ICompilationUnit compilationUnit) {
ASTParser parser= ASTParser.newParser(AST.getJLSLatest());
parser.setKind(ASTParser.K_COMPILATION_UNIT);
parser.setSource(compilationUnit);
parser.setResolveBindings(true);

return (CompilationUnit) parser.createAST(null);
}

/**
Expand All @@ -371,27 +423,42 @@ public static ImportRewrite create(CompilationUnit astRoot, boolean restoreExist
throw new IllegalArgumentException("AST must have been constructed from a Java element"); //$NON-NLS-1$
}
List existingImport= null;
Map<String, List<String>> moduleEntries= null;
if (restoreExistingImports) {
existingImport= new ArrayList();
moduleEntries= new HashMap<>();
List imports= astRoot.imports();
for (int i= 0; i < imports.size(); i++) {
ImportDeclaration curr= (ImportDeclaration) imports.get(i);
StringBuilder buf= new StringBuilder();
buf.append(curr.isStatic() ? STATIC_PREFIX : NORMAL_PREFIX).append(curr.getName().getFullyQualifiedName());
buf.append(curr.isStatic() ? STATIC_PREFIX : Flags.isModule(curr.getFlags()) ? MODULE_PREFIX : NORMAL_PREFIX).append(curr.getName().getFullyQualifiedName());
if (curr.isOnDemand()) {
if (buf.length() > 1)
buf.append('.');
buf.append('*');
}
if (Flags.isModule(curr.getFlags())) {
List<String> packageList= new ArrayList<>();
IBinding binding= curr.resolveBinding();
if (binding instanceof IModuleBinding moduleBinding) {
packageList= getPackageNames(moduleBinding);
}
moduleEntries.put(curr.getName().getFullyQualifiedName(), packageList);
}
existingImport.add(buf.toString());
}
}
return new ImportRewrite((ICompilationUnit) typeRoot, astRoot, existingImport);
return new ImportRewrite((ICompilationUnit) typeRoot, astRoot, existingImport, moduleEntries);
}

private ImportRewrite(ICompilationUnit cu, CompilationUnit astRoot, List existingImports) {
private ImportRewrite(ICompilationUnit cu, CompilationUnit astRoot, List existingImports, Map<String, List<String>> moduleEntries) {
this.compilationUnit= cu;
this.astRoot= astRoot; // might be null
if (moduleEntries != null) {
this.moduleEntries= moduleEntries;
} else {
this.moduleEntries= new HashMap<>();
}
if (existingImports != null) {
this.existingImports= existingImports;
this.restoreExistingImports= !existingImports.isEmpty();
Expand All @@ -415,6 +482,7 @@ public int findInContext(String qualifier, String name, int kind) {
this.staticExplicitSimpleNames = new HashSet<>();
this.createdImports= null;
this.createdStaticImports= null;
this.createdModuleImports= null;

this.importOrder= CharOperation.NO_STRINGS;
this.importOnDemandThreshold= 99;
Expand Down Expand Up @@ -527,7 +595,18 @@ public void setUseContextToFilterImplicitImports(boolean useContextToFilterImpli
this.useContextToFilterImplicitImports = useContextToFilterImplicitImports;
}

private static int compareImport(char prefix, String qualifier, String name, String curr) {
private static int compareImport(char prefix, String qualifier, String name, String curr, Map<String, List<String>> moduleExportsMap) {
if (curr.charAt(0) == MODULE_PREFIX) {
List<String> exportedPackageList= moduleExportsMap.get(curr.substring(1));
if (exportedPackageList != null) {
for (String exportedPackage : exportedPackageList) {
if (exportedPackage.equals(qualifier)) {
return ImportRewriteContext.RES_NAME_FOUND;
}
}
}
return ImportRewriteContext.RES_NAME_UNKNOWN;
}
if (curr.charAt(0) != prefix || !curr.endsWith(name)) {
return ImportRewriteContext.RES_NAME_UNKNOWN;
}
Expand Down Expand Up @@ -562,7 +641,7 @@ private static int compareImport(char prefix, String qualifier, String name, Str

for (int i= imports.size() - 1; i >= 0 ; i--) {
String curr= (String) imports.get(i);
int res= compareImport(prefix, qualifier, name, curr);
int res= compareImport(prefix, qualifier, name, curr, this.moduleEntries);
if (res != ImportRewriteContext.RES_NAME_UNKNOWN) {
if (!allowAmbiguity || res == ImportRewriteContext.RES_NAME_FOUND) {
if (prefix != STATIC_PREFIX) {
Expand Down Expand Up @@ -917,6 +996,18 @@ private static ITypeBinding normalizeTypeBinding(ITypeBinding binding) {
return null;
}


/**
* Adds a new module import to the rewriter's record.
* @since 3.43
*
*/

public void addModuleImport(String name, List<String> packageList) {
addEntry(MODULE_PREFIX + name);
this.moduleEntries.put(name, packageList);
}

/**
* Adds a new import to the rewriter's record and returns a {@link Type} that can be used
* in the code. The type binding can be an array binding, type variable or wildcard.
Expand Down Expand Up @@ -1274,6 +1365,7 @@ public final TextEdit rewriteImports(IProgressMonitor monitor) throws CoreExcept
if (!hasRecordedChanges()) {
this.createdImports= CharOperation.NO_STRINGS;
this.createdStaticImports= CharOperation.NO_STRINGS;
this.createdModuleImports= CharOperation.NO_STRINGS;
return new MultiTextEdit();
}

Expand All @@ -1293,14 +1385,16 @@ public final TextEdit rewriteImports(IProgressMonitor monitor) throws CoreExcept

for (String addedImport : this.addedImports) {
boolean isStatic = STATIC_PREFIX == addedImport.charAt(0);
boolean isModule = MODULE_PREFIX == addedImport.charAt(0);
String qualifiedName = addedImport.substring(1);
computer.addImport(isStatic, qualifiedName);
computer.addImport(isStatic, isModule, qualifiedName);
}

for (String removedImport : this.removedImports) {
boolean isStatic = STATIC_PREFIX == removedImport.charAt(0);
boolean isModule = MODULE_PREFIX == removedImport.charAt(0);
String qualifiedName = removedImport.substring(1);
computer.removeImport(isStatic, qualifiedName);
computer.removeImport(isStatic, isModule, qualifiedName);
}

for (String typeExplicitSimpleName : this.typeExplicitSimpleNames) {
Expand All @@ -1315,6 +1409,7 @@ public final TextEdit rewriteImports(IProgressMonitor monitor) throws CoreExcept

this.createdImports= result.getCreatedImports();
this.createdStaticImports= result.getCreatedStaticImports();
this.createdModuleImports= result.getCreatedModuleImports();

return result.getTextEdit();
}
Expand Down Expand Up @@ -1369,6 +1464,20 @@ public String[] getCreatedStaticImports() {
return this.createdStaticImports;
}

/**
* Returns all new module imports created by the last invocation of {@link #rewriteImports(IProgressMonitor)}
* or <code>null</code> if these methods have not been called yet.
* <p>
* Note that this list doesn't need to be the same as the added static imports ({@link #getAddedStaticImports()}) as
* implicit imports are not created and some imports are represented by on-demand imports instead.
* </p>
* @return the created imports
* @since 3.43
*/
public String[] getCreatedModuleImports() {
return this.createdModuleImports;
}

/**
* Returns all non-static imports that are recorded to be added.
*
Expand All @@ -1387,6 +1496,26 @@ public String[] getAddedStaticImports() {
return filterFromList(this.addedImports, STATIC_PREFIX);
}

/**
* Returns all module imports that are recorded to be added.
*
* @return the module imports recorded to be added.
* @since 3.43
*/
public String[] getAddedModuleImports() {
return filterFromList(this.addedImports, MODULE_PREFIX);
}

/**
* Returns all the exported packages registeted for an import module.
* @param moduleName name of module to get exports
* @return list of exported package names
* @since 3.43
*/
public List<String> getAddedModuleExportedPackages(String moduleName) {
return this.moduleEntries.get(moduleName);
}

/**
* Returns all non-static imports that are recorded to be removed.
*
Expand All @@ -1405,6 +1534,16 @@ public String[] getRemovedStaticImports() {
return filterFromList(this.removedImports, STATIC_PREFIX);
}

/**
* Returns all static imports that are recorded to be removed.
*
* @return the static imports recorded to be removed.
* @since 3.43
*/
public String[] getRemovedModuleImports() {
return filterFromList(this.removedImports, MODULE_PREFIX);
}

/**
* Returns <code>true</code> if imports have been recorded to be added or removed.
* @return boolean returns if any changes to imports have been recorded.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ String writeImportDeclaration(ImportName importName) {
sb.append("static "); //$NON-NLS-1$
}

if (importName.isModule) {
sb.append("module "); //$NON-NLS-1$
}

sb.append(importName.qualifiedName);

if (this.insertSpaceBeforeSemicolon) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2015 Google Inc and others.
* Copyright (c) 2015, 2025 Google Inc and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand All @@ -15,6 +15,7 @@

import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.Modifier;

/**
* Encapsulates an import's fully qualified name, whether it is on-demand, and whether it is static.
Expand All @@ -28,29 +29,34 @@
public final class ImportName {
static ImportName createFor(ImportDeclaration importDeclaration) {
String declName = importDeclaration.getName().getFullyQualifiedName();
if (importDeclaration.isOnDemand()) {
if (Modifier.isModule(importDeclaration.getModifiers())) {
return createFor(importDeclaration.isStatic(), Modifier.isModule(importDeclaration.getModifiers()), declName);
} else if (importDeclaration.isOnDemand()) {
return createOnDemand(importDeclaration.isStatic(), declName);
}
return createFor(importDeclaration.isStatic(), declName);
return createFor(importDeclaration.isStatic(), Modifier.isModule(importDeclaration.getModifiers()), declName);
}

static ImportName createOnDemand(boolean isStatic, String containerName) {
return new ImportName(isStatic, containerName, "*"); //$NON-NLS-1$
return new ImportName(isStatic, false, containerName, "*"); //$NON-NLS-1$
}

public static ImportName createFor(boolean isStatic, String qualifiedName) {
public static ImportName createFor(boolean isStatic, boolean isModule, String qualifiedName) {
String containerName = Signature.getQualifier(qualifiedName);
String simpleName = Signature.getSimpleName(qualifiedName);
return new ImportName(isStatic, containerName, simpleName);
return new ImportName(isStatic, isModule, containerName, simpleName);
}

public final boolean isStatic;
public final boolean isModule;
public final String containerName;
public final String simpleName;
public final String qualifiedName;

private ImportName(boolean isStatic, String containerName, String simpleName) {
private ImportName(boolean isStatic, boolean isModule, String containerName, String simpleName) {
this.isStatic = isStatic;
this.isModule = isModule;
assert(!(this.isStatic && this.isModule));
this.containerName = containerName;
this.simpleName = simpleName;

Expand All @@ -59,14 +65,14 @@ private ImportName(boolean isStatic, String containerName, String simpleName) {

@Override
public String toString() {
String template = this.isStatic ? "staticImport(%s)" : "typeImport(%s)"; //$NON-NLS-1$ //$NON-NLS-2$
String template = this.isStatic ? "staticImport(%s)" : this.isModule ? "moduleImport(%s)" : "typeImport(%s)"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
return String.format(template, this.qualifiedName);
}

@Override
public int hashCode() {
int result = this.qualifiedName.hashCode();
result = 31 * result + (this.isStatic ? 1 : 0);
result = 31 * result + (this.isStatic ? 1 : 0) + (this.isModule ? 3 : 0);
return result;
}

Expand All @@ -78,7 +84,7 @@ public boolean equals(Object obj) {

ImportName other = (ImportName) obj;

return this.qualifiedName.equals(other.qualifiedName) && this.isStatic == other.isStatic;
return this.qualifiedName.equals(other.qualifiedName) && this.isStatic == other.isStatic && this.isModule == other.isModule;
}

public boolean isOnDemand() {
Expand Down
Loading
Loading