blob: cead80ab71e8dc4fe60231694367a8cde952ca45 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 2018 IBM Corporation.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.core;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IModuleDescription;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.ExportsStatement;
import org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ModuleReference;
import org.eclipse.jdt.internal.compiler.ast.OpensStatement;
import org.eclipse.jdt.internal.compiler.ast.ProvidesStatement;
import org.eclipse.jdt.internal.compiler.ast.RequiresStatement;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.ast.UsesStatement;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.jdt.internal.compiler.env.IModule;
import org.eclipse.jdt.internal.compiler.env.ISourceModule;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
public class ModuleDescriptionInfo extends AnnotatableInfo implements ISourceModule {
protected static final char[][] NO_USES = new char[0][0];
protected static final ModuleReferenceInfo[] NO_REQUIRES = new ModuleReferenceInfo[0];
protected static final PackageExportInfo[] NO_EXPORTS = new PackageExportInfo[0];
protected static final ServiceInfo[] NO_PROVIDES = new ServiceInfo[0];
protected static final PackageExportInfo[] NO_OPENS = new PackageExportInfo[0];
protected IJavaElement[] children = JavaElement.NO_ELEMENTS;
ModuleReferenceInfo[] requires;
PackageExportInfo[] exports;
ServiceInfo[] services;
PackageExportInfo[] opens;
char[][] usedServices;
IModuleDescription handle;
char[] name;
private Map<IJavaElement,String[]> categories;
static class ModuleReferenceInfo extends MemberElementInfo implements IModule.IModuleReference {
char[] name;
int modifiers;
@Override
public char[] name() {
return this.name;
}
@Override
public int getModifiers() {
return this.modifiers;
}
}
static class PackageExportInfo extends MemberElementInfo implements IModule.IPackageExport {
char[] pack;
char[][] target;
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append(this.pack);
if (this.target != null) {
buffer.append(" to "); //$NON-NLS-1$
for (char[] mod : this.target) {
buffer.append(mod);
}
}
buffer.append(';');
return buffer.toString();
}
@Override
public char[] name() {
return this.pack;
}
@Override
public char[][] targets() {
return this.target;
}
}
static class ServiceInfo extends MemberElementInfo implements IModule.IService {
char[] serviceName;
char[][] implNames;
@Override
public char[] name() {
return this.serviceName;
}
@Override
public char[][] with() {
return this.implNames;
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append(this.serviceName);
buffer.append(" with "); //$NON-NLS-1$
for (int i = 0; i < this.implNames.length; i++) {
buffer.append(this.implNames[i]);
if (i < this.implNames.length - 1)
buffer.append(", "); //$NON-NLS-1$
}
buffer.append(';');
return buffer.toString();
}
}
public static ModuleDescriptionInfo createModule(ModuleDeclaration module) {
ModuleDescriptionInfo mod = new ModuleDescriptionInfo();
mod.name = module.moduleName;
mod.setFlags(module.modifiers);
if (module.requiresCount > 0) {
RequiresStatement[] refs = module.requires;
mod.requires = new ModuleReferenceInfo[refs.length+1];
mod.requires[0] = getJavaBaseReference();
for (int i = 0; i < refs.length; i++) {
mod.requires[i+1] = new ModuleReferenceInfo();
mod.requires[i+1].name = CharOperation.concatWith(refs[i].module.tokens, '.'); // Check why ModuleReference#tokens must be a char[][] and not a char[] or String;
mod.requires[i+1].modifiers = refs[i].modifiers;
}
} else {
mod.requires = CharOperation.equals(module.moduleName, TypeConstants.JAVA_BASE)
? NO_REQUIRES
: new ModuleReferenceInfo[] { getJavaBaseReference() };
}
if (module.exportsCount > 0) {
ExportsStatement[] refs = module.exports;
mod.exports = new PackageExportInfo[refs.length];
for (int i = 0; i < refs.length; i++) {
PackageExportInfo exp = createPackageExport(refs[i]);
mod.exports[i] = exp;
}
} else {
mod.exports = NO_EXPORTS;
}
if (module.usesCount > 0) {
UsesStatement[] uses = module.uses;
mod.usedServices = new char[uses.length][];
for (int i = 0; i < uses.length; i++) {
mod.usedServices[i] = CharOperation.concatWith(uses[i].serviceInterface.getTypeName(), '.');
}
} else {
mod.usedServices = NO_USES;
}
if (module.servicesCount > 0) {
ProvidesStatement[] provides = module.services;
mod.services = new ServiceInfo[provides.length];
for (int i = 0; i < provides.length; i++) {
mod.services[i] = createService(provides[i]);
}
} else {
mod.services = NO_PROVIDES;
}
if (module.opensCount > 0) {
OpensStatement[] opens = module.opens;
mod.opens = new PackageExportInfo[opens.length];
for (int i = 0; i < opens.length; i++) {
PackageExportInfo op = createOpensInfo(opens[i]);
mod.opens[i] = op;
}
} else {
mod.opens = NO_OPENS;
}
return mod;
}
private static ModuleReferenceInfo getJavaBaseReference() {
ModuleReferenceInfo ref = new ModuleReferenceInfo();
ref.name = TypeConstants.JAVA_BASE;
return ref;
}
private static PackageExportInfo createPackageExport(ExportsStatement ref) {
PackageExportInfo exp = new PackageExportInfo();
exp.pack = ref.pkgName;
ModuleReference[] imp = ref.targets;
if (imp != null) {
exp.target = new char[imp.length][];
for(int j = 0; j < imp.length; j++) {
exp.target[j] = imp[j].moduleName;
}
}
return exp;
}
private static PackageExportInfo createOpensInfo(OpensStatement opens) {
PackageExportInfo open = new PackageExportInfo();
open.pack = opens.pkgName;
ModuleReference[] imp = opens.targets;
if (imp != null) {
open.target = new char[imp.length][];
for(int j = 0; j < imp.length; j++) {
open.target[j] = imp[j].moduleName;
}
}
return open;
}
private static ServiceInfo createService(ProvidesStatement provides) {
ServiceInfo info = new ServiceInfo();
info.serviceName = CharOperation.concatWith(provides.serviceInterface.getTypeName(), '.');
TypeReference[] implementations = provides.implementations;
info.implNames = new char[implementations.length][];
for(int i = 0; i < implementations.length; i++) {
info.implNames[i] = CharOperation.concatWith(implementations[i].getTypeName(), '.');
}
return info;
}
protected void setHandle(IModuleDescription handle) {
this.handle = handle;
}
public IModuleDescription getHandle() {
return this.handle;
}
@Override
public IJavaElement[] getChildren() {
return this.children;
}
@Override
public ICompilationUnit getCompilationUnit() {
IJavaElement parent = this.handle.getParent();
if (parent instanceof CompilationUnit)
return (CompilationUnit) parent;
return null;
}
@Override
public boolean isOpen() {
return (this.flags & ClassFileConstants.ACC_OPEN) != 0;
}
@Override
public char[] name() {
return this.name;
}
@Override
public IModule.IModuleReference[] requires() {
return this.requires;
}
@Override
public IPackageExport[] exports() {
return this.exports;
}
@Override
public char[][] uses() {
return this.usedServices;
}
@Override
public IService[] provides() {
return this.services;
}
@Override
public IPackageExport[] opens() {
return this.opens;
}
public void addCategories(IJavaElement element, char[][] elementCategories) {
if (elementCategories == null) return;
if (this.categories == null)
this.categories = new HashMap<>();
this.categories.put(element, CharOperation.toStrings(elementCategories));
}
public Map<IJavaElement, String[]> getCategories() {
return this.categories;
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer(getClass().getName());
toStringContent(buffer);
return buffer.toString();
}
protected void toStringContent(StringBuffer buffer) {
buffer.append("\n"); //$NON-NLS-1$
if (this.isOpen())
buffer.append("open "); //$NON-NLS-1$
buffer.append("module "); //$NON-NLS-1$
buffer.append(this.name).append(' ');
buffer.append('{').append('\n');
if (this.requires != null && this.requires.length > 0) {
buffer.append('\n');
for(int i = 0; i < this.requires.length; i++) {
buffer.append("\trequires "); //$NON-NLS-1$
if (this.requires[i].isTransitive()) {
buffer.append("transitive "); //$NON-NLS-1$
}
if (this.requires[i].isStatic()) {
buffer.append("static "); //$NON-NLS-1$
}
buffer.append(this.requires[i].name);
buffer.append(';').append('\n');
}
}
if (this.exports != null && this.exports.length > 0) {
buffer.append('\n');
for(int i = 0; i < this.exports.length; i++) {
buffer.append("\texports "); //$NON-NLS-1$
buffer.append(this.exports[i].toString()).append('\n');
}
}
if (this.usedServices != null && this.usedServices.length > 0) {
buffer.append('\n');
for(int i = 0; i < this.usedServices.length; i++) {
buffer.append("\tuses "); //$NON-NLS-1$
buffer.append(this.usedServices[i]).append('\n');
}
}
if (this.services != null && this.services.length > 0) {
buffer.append('\n');
for(int i = 0; i < this.services.length; i++) {
buffer.append("\tprovides "); //$NON-NLS-1$
buffer.append(this.services[i].toString()).append('\n');
}
}
if (this.opens != null && this.opens.length > 0) {
buffer.append('\n');
for(int i = 0; i < this.opens.length; i++) {
buffer.append("\topens "); //$NON-NLS-1$
buffer.append(this.opens[i].toString()).append('\n');
}
}
buffer.append('\n').append('}').toString();
}
}