Kotlin implement members initial (not much works)

This commit is contained in:
Dmitry Jemerov
2011-11-04 17:59:59 +01:00
parent 0e4a7ebe90
commit 8f15eea9c5
19 changed files with 390 additions and 73 deletions
@@ -75,7 +75,7 @@ public class JetClass extends JetTypeParameterListOwner implements JetClassOrObj
@NotNull
public List<JetClassInitializer> getAnonymousInitializers() {
JetClassBody body = (JetClassBody) findChildByType(JetNodeTypes.CLASS_BODY);
JetClassBody body = getBody();
if (body == null) return Collections.emptyList();
return body.getAnonymousInitializers();
@@ -90,15 +90,20 @@ public class JetClass extends JetTypeParameterListOwner implements JetClassOrObj
return (JetObjectDeclarationName) findChildByType(JetNodeTypes.OBJECT_DECLARATION_NAME);
}
@Override
public JetClassBody getBody() {
return (JetClassBody) findChildByType(JetNodeTypes.CLASS_BODY);
}
@Nullable
public JetClassObject getClassObject() {
JetClassBody body = (JetClassBody) findChildByType(JetNodeTypes.CLASS_BODY);
JetClassBody body = getBody();
if (body == null) return null;
return body.getClassObject();
}
public List<JetProperty> getProperties() {
JetClassBody body = (JetClassBody) findChildByType(JetNodeTypes.CLASS_BODY);
JetClassBody body = getBody();
if (body == null) return Collections.emptyList();
return body.getProperties();
@@ -1,10 +1,13 @@
package org.jetbrains.jet.lang.psi;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.JetNodeTypes;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.List;
@@ -48,4 +51,9 @@ public class JetClassBody extends JetElement {
public JetClassObject getClassObject() {
return (JetClassObject) findChildByType(JetNodeTypes.CLASS_OBJECT);
}
public PsiElement getRBrace() {
final ASTNode[] children = getNode().getChildren(TokenSet.create(JetTokens.RBRACE));
return children.length == 1 ? children[0].getPsi() : null;
}
}
@@ -32,4 +32,7 @@ public interface JetClassOrObject extends PsiElement {
@Nullable
JetObjectDeclarationName getNameAsDeclaration();
@Nullable
JetClassBody getBody();
}
@@ -69,10 +69,15 @@ public class JetObjectDeclaration extends JetNamedDeclaration implements JetClas
return true;
}
@Override
public JetClassBody getBody() {
return (JetClassBody) findChildByType(JetNodeTypes.CLASS_BODY);
}
@Override
@NotNull
public List<JetDeclaration> getDeclarations() {
JetClassBody body = (JetClassBody) findChildByType(JetNodeTypes.CLASS_BODY);
JetClassBody body = getBody();
if (body == null) return Collections.emptyList();
return body.getDeclarations();
@@ -103,66 +103,10 @@ public class OverrideResolver {
}
// Check if everything that must be overridden, actually is
// Everything from supertypes
Map<CallableMemberDescriptor, CallableMemberDescriptor> implementedWithDelegationBy = Maps.newHashMap();
Set<CallableMemberDescriptor> inheritedFunctions = Sets.newLinkedHashSet();
for (JetType supertype : classDescriptor.getSupertypes()) {
for (DeclarationDescriptor descriptor : supertype.getMemberScope().getAllDescriptors()) {
if (descriptor instanceof CallableMemberDescriptor) {
CallableMemberDescriptor memberDescriptor = (CallableMemberDescriptor) descriptor;
inheritedFunctions.add(memberDescriptor);
}
}
}
// Only those actually inherited
Set<CallableMemberDescriptor> filteredMembers = OverridingUtil.filterOverrides(inheritedFunctions);
// Group members with "the same" signature
Multimap<CallableMemberDescriptor, CallableMemberDescriptor> factoredMembers = CommonSuppliers.newLinkedHashSetHashSetMultimap();
for (CallableMemberDescriptor one : filteredMembers) {
if (factoredMembers.values().contains(one)) continue;
for (CallableMemberDescriptor another : filteredMembers) {
// if (one == another) continue;
factoredMembers.put(one, one);
if (OverridingUtil.isOverridableBy(one, another).isSuccess()
|| OverridingUtil.isOverridableBy(another, one).isSuccess()) {
factoredMembers.put(one, another);
}
}
}
// More than one implementation or no implementations at all
Set<CallableMemberDescriptor> abstractNoImpl = Sets.newLinkedHashSet();
Set<CallableMemberDescriptor> manyImpl = Sets.newLinkedHashSet();
for (CallableMemberDescriptor key : factoredMembers.keySet()) {
Collection<CallableMemberDescriptor> mutuallyOverridable = factoredMembers.get(key);
int implementationCount = 0;
for (CallableMemberDescriptor member : mutuallyOverridable) {
if (member.getModality() != Modality.ABSTRACT) {
implementationCount++;
}
}
if (implementationCount == 0) {
abstractNoImpl.addAll(mutuallyOverridable);
}
else if (implementationCount > 1) {
manyImpl.addAll(mutuallyOverridable);
}
}
// Members actually present (declared) in the class
Set<CallableMemberDescriptor> actuallyOverridden = Sets.newHashSet(implementedWithDelegationBy.keySet());
for (CallableMemberDescriptor member : classDescriptor.getCallableMembers()) {
actuallyOverridden.addAll(member.getOverriddenDescriptors());
}
// Those to be overridden that are actually not
abstractNoImpl.removeAll(actuallyOverridden);
manyImpl.removeAll(actuallyOverridden);
collectMissingImplementations(classDescriptor, abstractNoImpl, manyImpl);
PsiElement nameIdentifier = klass;
if (klass instanceof JetClass) {
@@ -216,6 +160,65 @@ public class OverrideResolver {
// }
}
public static void collectMissingImplementations(MutableClassDescriptor classDescriptor, Set<CallableMemberDescriptor> abstractNoImpl, Set<CallableMemberDescriptor> manyImpl) {
// Everything from supertypes
Map<CallableMemberDescriptor, CallableMemberDescriptor> implementedWithDelegationBy = Maps.newHashMap();
Set<CallableMemberDescriptor> inheritedFunctions = Sets.newLinkedHashSet();
for (JetType supertype : classDescriptor.getSupertypes()) {
for (DeclarationDescriptor descriptor : supertype.getMemberScope().getAllDescriptors()) {
if (descriptor instanceof CallableMemberDescriptor) {
CallableMemberDescriptor memberDescriptor = (CallableMemberDescriptor) descriptor;
inheritedFunctions.add(memberDescriptor);
}
}
}
// Only those actually inherited
Set<CallableMemberDescriptor> filteredMembers = OverridingUtil.filterOverrides(inheritedFunctions);
// Group members with "the same" signature
Multimap<CallableMemberDescriptor, CallableMemberDescriptor> factoredMembers = CommonSuppliers.newLinkedHashSetHashSetMultimap();
for (CallableMemberDescriptor one : filteredMembers) {
if (factoredMembers.values().contains(one)) continue;
for (CallableMemberDescriptor another : filteredMembers) {
// if (one == another) continue;
factoredMembers.put(one, one);
if (OverridingUtil.isOverridableBy(one, another).isSuccess()
|| OverridingUtil.isOverridableBy(another, one).isSuccess()) {
factoredMembers.put(one, another);
}
}
}
for (CallableMemberDescriptor key : factoredMembers.keySet()) {
Collection<CallableMemberDescriptor> mutuallyOverridable = factoredMembers.get(key);
int implementationCount = 0;
for (CallableMemberDescriptor member : mutuallyOverridable) {
if (member.getModality() != Modality.ABSTRACT) {
implementationCount++;
}
}
if (implementationCount == 0) {
abstractNoImpl.addAll(mutuallyOverridable);
}
else if (implementationCount > 1) {
manyImpl.addAll(mutuallyOverridable);
}
}
// Members actually present (declared) in the class
Set<CallableMemberDescriptor> actuallyOverridden = Sets.newHashSet(implementedWithDelegationBy.keySet());
for (CallableMemberDescriptor member : classDescriptor.getCallableMembers()) {
actuallyOverridden.addAll(member.getOverriddenDescriptors());
}
// Those to be overridden that are actually not
abstractNoImpl.removeAll(actuallyOverridden);
manyImpl.removeAll(actuallyOverridden);
}
private void checkOverride(CallableMemberDescriptor declared) {
JetNamedDeclaration member = (JetNamedDeclaration) context.getTrace().get(BindingContext.DESCRIPTOR_TO_DECLARATION, declared);
if (member == null) {
+1
View File
@@ -54,5 +54,6 @@
<fileTypeIndentOptionsProvider implementation="org.jetbrains.jet.plugin.formatter.JetIndentOptionsProvider"/>
<elementDescriptionProvider implementation="org.jetbrains.jet.plugin.findUsages.JetElementDescriptionProvider"/>
<debugger.positionManagerFactory implementation="org.jetbrains.jet.plugin.debugger.JetPositionManagerFactory"/>
<codeInsight.implementMethod language="jet" implementationClass="org.jetbrains.jet.plugin.codeInsight.ImplementMethodsHandler"/>
</extensions>
</idea-plugin>
@@ -0,0 +1,40 @@
package org.jetbrains.jet.plugin.codeInsight;
import com.intellij.codeInsight.generation.ClassMember;
import com.intellij.codeInsight.generation.MemberChooserObject;
import com.intellij.ui.SimpleColoredComponent;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.resolve.DescriptorRenderer;
import javax.swing.*;
/**
* @author yole
*/
public class DescriptorClassMember implements ClassMember {
private final DeclarationDescriptor myDescriptor;
public DescriptorClassMember(DeclarationDescriptor descriptor) {
myDescriptor = descriptor;
}
@Override
public MemberChooserObject getParentNodeDelegate() {
final DeclarationDescriptor parent = myDescriptor.getContainingDeclaration();
return new DescriptorClassMember(parent);
}
@Override
public void renderTreeNode(SimpleColoredComponent component, JTree tree) {
component.append(getText());
}
@Override
public String getText() {
return DescriptorRenderer.TEXT.render(myDescriptor);
}
public DeclarationDescriptor getDescriptor() {
return myDescriptor;
}
}
@@ -0,0 +1,132 @@
package org.jetbrains.jet.plugin.codeInsight;
import com.google.common.collect.Sets;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.ide.util.MemberChooser;
import com.intellij.lang.LanguageCodeInsightActionHandler;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.MutableClassDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.OverrideResolver;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.plugin.compiler.WholeProjectAnalyzerFacade;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* @author yole
*/
public class ImplementMethodsHandler implements LanguageCodeInsightActionHandler {
@Override
public boolean isValidFor(Editor editor, PsiFile file) {
if (!(file instanceof JetFile)) {
return false;
}
final PsiElement elementAtCaret = file.findElementAt(editor.getCaretModel().getOffset());
final JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(elementAtCaret, JetClassOrObject.class);
return classOrObject != null;
}
@Override
public void invoke(@NotNull final Project project, @NotNull final Editor editor, @NotNull PsiFile file) {
final PsiElement elementAtCaret = file.findElementAt(editor.getCaretModel().getOffset());
final JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(elementAtCaret, JetClassOrObject.class);
Set<CallableMemberDescriptor> missingImplementations = collectMethodsToImplement(classOrObject);
if (missingImplementations.isEmpty()) {
HintManager.getInstance().showErrorHint(editor, "No methods to implement have been found");
return;
}
List<DescriptorClassMember> members = membersFromDescriptors(missingImplementations);
final MemberChooser<DescriptorClassMember> chooser = showOverrideImplementChooser(project,
members.toArray(new DescriptorClassMember[members.size()]));
if (chooser == null) {
return;
}
final List<DescriptorClassMember> selectedElements = chooser.getSelectedElements();
if (selectedElements == null || selectedElements.isEmpty()) return;
new WriteCommandAction(project, file) {
protected void run(final Result result) throws Throwable {
overrideOrImplementMethodsInRightPlace(project, editor, classOrObject, selectedElements);
}
}.execute();
}
public static List<DescriptorClassMember> membersFromDescriptors(Set<CallableMemberDescriptor> missingImplementations) {
List<DescriptorClassMember> members = new ArrayList<DescriptorClassMember>();
for (CallableMemberDescriptor memberDescriptor : missingImplementations) {
members.add(new DescriptorClassMember(memberDescriptor));
}
return members;
}
public static Set<CallableMemberDescriptor> collectMethodsToImplement(JetClassOrObject classOrObject) {
BindingContext bindingContext = WholeProjectAnalyzerFacade.analyzeProjectWithCacheOnAFile((JetFile) classOrObject.getContainingFile());
final DeclarationDescriptor descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, classOrObject);
Set<CallableMemberDescriptor> missingImplementations = Sets.newLinkedHashSet();
if (descriptor instanceof MutableClassDescriptor) {
OverrideResolver.collectMissingImplementations((MutableClassDescriptor) descriptor,
missingImplementations, missingImplementations);
}
return missingImplementations;
}
public static void overrideOrImplementMethodsInRightPlace(Project project, Editor editor, JetClassOrObject classOrObject, List<DescriptorClassMember> selectedElements) {
final JetClassBody body = classOrObject.getBody();
if (body == null) {
return;
}
for (DescriptorClassMember selectedElement : selectedElements) {
final DeclarationDescriptor descriptor = selectedElement.getDescriptor();
if (descriptor instanceof FunctionDescriptor) {
JetElement target = overrideFunction(project, (FunctionDescriptor) descriptor);
body.addBefore(target, body.getRBrace());
}
}
}
private static JetElement overrideFunction(Project project, FunctionDescriptor descriptor) {
StringBuilder bodyBuilder = new StringBuilder("override fun ");
bodyBuilder.append(descriptor.getName());
bodyBuilder.append("(");
bodyBuilder.append(")");
final JetType returnType = descriptor.getReturnType();
bodyBuilder.append(": ").append(returnType.toString());
bodyBuilder.append("{");
if (returnType.isNullable()) {
bodyBuilder.append("return null");
}
bodyBuilder.append("}");
return JetPsiFactory.createFunction(project, bodyBuilder.toString());
}
private static MemberChooser<DescriptorClassMember> showOverrideImplementChooser(Project project,
DescriptorClassMember[] members) {
final MemberChooser<DescriptorClassMember> chooser = new MemberChooser<DescriptorClassMember>(members, true, true, project);
chooser.setTitle("Implement Members");
chooser.show();
if (chooser.getExitCode() != DialogWrapper.OK_EXIT_CODE) return null;
return chooser;
}
@Override
public boolean startInWriteAction() {
return false;
}
}
@@ -24,6 +24,7 @@ public class JetBlock implements ASTBlock {
private Indent myIndent;
private Wrap myWrap;
private CodeStyleSettings mySettings;
private final SpacingBuilder mySpacingBuilder;
private List<Block> mySubBlocks;
private static final TokenSet CODE_BLOCKS = TokenSet.create(
@@ -35,12 +36,14 @@ public class JetBlock implements ASTBlock {
JetNodeTypes.THEN,
JetNodeTypes.ELSE);
public JetBlock(ASTNode node, Alignment alignment, Indent indent, Wrap wrap, CodeStyleSettings settings) {
public JetBlock(ASTNode node, Alignment alignment, Indent indent, Wrap wrap, CodeStyleSettings settings,
SpacingBuilder spacingBuilder) {
myNode = node;
myAlignment = alignment;
myIndent = indent;
myWrap = wrap;
mySettings = settings;
mySpacingBuilder = spacingBuilder;
}
@Override
@@ -110,7 +113,7 @@ public class JetBlock implements ASTBlock {
childIndent = Indent.getNormalIndent();
}
return new JetBlock(child, childAlignment, childIndent, wrap, mySettings);
return new JetBlock(child, childAlignment, childIndent, wrap, mySettings, mySpacingBuilder);
}
private static Indent indentIfNotBrace(ASTNode child) {
@@ -120,8 +123,8 @@ public class JetBlock implements ASTBlock {
}
@Override
public Spacing getSpacing(Block block, Block block1) {
return null;
public Spacing getSpacing(Block child1, Block child2) {
return mySpacingBuilder.getSpacing(this, child1, child2);
}
@NotNull
@@ -1,15 +1,17 @@
package org.jetbrains.jet.plugin.formatter;
import com.intellij.formatting.FormattingModel;
import com.intellij.formatting.FormattingModelBuilder;
import com.intellij.formatting.FormattingModelProvider;
import com.intellij.formatting.Indent;
import com.intellij.formatting.*;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.JetNodeTypes;
import org.jetbrains.jet.lexer.JetTokens;
import static org.jetbrains.jet.JetNodeTypes.*;
import static org.jetbrains.jet.lexer.JetTokens.*;
/**
* @author yole
@@ -18,10 +20,19 @@ public class JetFormattingModelBuilder implements FormattingModelBuilder {
@NotNull
@Override
public FormattingModel createModel(PsiElement element, CodeStyleSettings settings) {
final JetBlock block = new JetBlock(element.getNode(), null, Indent.getNoneIndent(), null, settings);
final JetBlock block = new JetBlock(element.getNode(), null, Indent.getNoneIndent(), null, settings,
createSpacingBuilder(settings));
return FormattingModelProvider.createFormattingModelForPsiFile(element.getContainingFile(), block, settings);
}
private static SpacingBuilder createSpacingBuilder(CodeStyleSettings settings) {
return new SpacingBuilder(settings)
.beforeInside(BLOCK, FUN).spaceIf(settings.SPACE_BEFORE_METHOD_LBRACE)
.afterInside(LBRACE, BLOCK).lineBreakInCode()
.beforeInside(RBRACE, CLASS_BODY).lineBreakInCode()
.beforeInside(RBRACE, BLOCK).lineBreakInCode();
}
@Override
public TextRange getRangeAffectingIndent(PsiFile psiFile, int i, ASTNode astNode) {
return null;
@@ -0,0 +1,5 @@
import foo.Intf
class Impl(): Intf {
<caret>
}
@@ -0,0 +1,8 @@
import foo.Intf
class Impl(): Intf {
override fun getFooBar(): String? {
return null
}
}
@@ -0,0 +1,5 @@
package foo;
interface Intf {
String getFooBar();
}
@@ -6,7 +6,7 @@ import junit.framework.Test;
import junit.framework.TestSuite;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.JetTestCaseBuilder;
import org.jetbrains.jet.plugin.quickfix.PluginTestCaseBase;
import org.jetbrains.jet.plugin.PluginTestCaseBase;
import java.io.File;
@@ -0,0 +1,33 @@
package org.jetbrains.jet.plugin;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.module.StdModuleTypes;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.testFramework.LightProjectDescriptor;
/**
* @author yole
*/
public class JetLightProjectDescriptor implements LightProjectDescriptor {
private JetLightProjectDescriptor() {
}
public static final JetLightProjectDescriptor INSTANCE = new JetLightProjectDescriptor();
@Override
public ModuleType getModuleType() {
return StdModuleTypes.JAVA;
}
@Override
public Sdk getSdk() {
return PluginTestCaseBase.jdkFromIdeaHome();
}
@Override
public void configureModule(Module module, ModifiableRootModel model, ContentEntry contentEntry) {
}
}
@@ -1,4 +1,4 @@
package org.jetbrains.jet.plugin.quickfix;
package org.jetbrains.jet.plugin;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.impl.JavaSdkImpl;
@@ -8,6 +8,9 @@ import org.jetbrains.jet.JetTestCaseBuilder;
* @author yole
*/
public class PluginTestCaseBase {
private PluginTestCaseBase() {
}
public static String getTestDataPathBase() {
return JetTestCaseBuilder.getHomeDirectory() + "/idea/testData";
}
@@ -0,0 +1,50 @@
package org.jetbrains.jet.plugin.codeInsight;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor;
import org.jetbrains.jet.lang.psi.JetClassOrObject;
import org.jetbrains.jet.plugin.JetLightProjectDescriptor;
import org.jetbrains.jet.plugin.PluginTestCaseBase;
import java.util.Set;
/**
* @author yole
*/
public class OverrideImplementTest extends LightCodeInsightFixtureTestCase {
@NotNull
@Override
protected LightProjectDescriptor getProjectDescriptor() {
return JetLightProjectDescriptor.INSTANCE;
}
@Override
public void setUp() throws Exception {
super.setUp();
myFixture.setTestDataPath(PluginTestCaseBase.getTestDataPathBase() + "/codeInsight/overrideImplement");
}
public void testJavaInterfaceMethod() {
myFixture.copyDirectoryToProject("javaInterfaceMethod", "");
myFixture.configureFromTempProjectFile("foo/Impl.kt");
final PsiElement elementAtCaret = myFixture.getFile().findElementAt(myFixture.getEditor().getCaretModel().getOffset());
final JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(elementAtCaret, JetClassOrObject.class);
final Set<CallableMemberDescriptor> descriptors = ImplementMethodsHandler.collectMethodsToImplement(classOrObject);
assertEquals(1, descriptors.size());
new WriteCommandAction(myFixture.getProject(), myFixture.getFile()) {
@Override
protected void run(Result result) throws Throwable {
ImplementMethodsHandler.overrideOrImplementMethodsInRightPlace(myFixture.getProject(), myFixture.getEditor(), classOrObject,
ImplementMethodsHandler.membersFromDescriptors(descriptors));
}
}.execute();
myFixture.checkResultByFile("javaInterfaceMethod/foo/Impl.kt.after");
}
}
@@ -7,6 +7,7 @@ import junit.framework.Test;
import junit.framework.TestSuite;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.JetTestCaseBuilder;
import org.jetbrains.jet.plugin.PluginTestCaseBase;
import java.io.File;
import java.io.FilenameFilter;
+2 -1
View File
@@ -66,4 +66,5 @@ class ModuleBuilder(val name: String): IModuleBuilder {
override fun getModuleName(): String? = name
override fun getJarName(): String? = _jarName
}
}
}