mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-06 03:21:12 +07:00
Java: Quick fix for merging duplicate statements in module-info (IDEA-169211)
This commit is contained in:
@@ -197,6 +197,7 @@ public class ModuleHighlightUtil {
|
||||
String message = JavaErrorMessages.message(key, refText);
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(message).create();
|
||||
QuickFixAction.registerQuickFixAction(info, factory().createDeleteFix(statement));
|
||||
QuickFixAction.registerQuickFixAction(info, MergeModuleStatementsFix.createFix(statement));
|
||||
results.add(info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInsight.daemon.impl.quickfix;
|
||||
|
||||
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/**
|
||||
* @author Pavel.Dolgov
|
||||
*/
|
||||
public abstract class MergeModuleStatementsFix<T extends PsiElement> extends LocalQuickFixAndIntentionActionOnPsiElement {
|
||||
protected final SmartPsiElementPointer<T> myOtherStatement;
|
||||
|
||||
protected MergeModuleStatementsFix(@NotNull T thisStatement, @NotNull T otherStatement) {
|
||||
super(thisStatement);
|
||||
final PsiFile file = otherStatement.getContainingFile();
|
||||
myOtherStatement = SmartPointerManager.getInstance(otherStatement.getProject()).createSmartPsiElementPointer(otherStatement, file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project,
|
||||
@NotNull PsiFile file,
|
||||
@NotNull PsiElement startElement,
|
||||
@NotNull PsiElement endElement) {
|
||||
final T otherStatement = myOtherStatement.getElement();
|
||||
return otherStatement != null && otherStatement.isValid() && PsiUtil.isLanguageLevel9OrHigher(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull Project project,
|
||||
@NotNull PsiFile file,
|
||||
@Nullable Editor editor,
|
||||
@NotNull PsiElement thisStatement,
|
||||
@NotNull PsiElement endElement) {
|
||||
final T otherStatement = myOtherStatement.getElement();
|
||||
|
||||
if (otherStatement != null) {
|
||||
final PsiElement parent = otherStatement.getParent();
|
||||
if (parent instanceof PsiJavaModule) {
|
||||
final String moduleName = ((PsiJavaModule)parent).getName();
|
||||
final String moduleText = PsiKeyword.MODULE + " " + moduleName + " {" + getReplacementText(otherStatement) + "}";
|
||||
final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
|
||||
final PsiJavaModule tempModule = factory.createModuleFromText(moduleText);
|
||||
|
||||
final Iterator<T> statementIterator = getStatements(tempModule).iterator();
|
||||
LOG.assertTrue(statementIterator.hasNext());
|
||||
final T replacement = statementIterator.next();
|
||||
|
||||
final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
|
||||
codeStyleManager.reformat(otherStatement.replace(replacement));
|
||||
thisStatement.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected abstract String getReplacementText(@NotNull T otherStatement);
|
||||
|
||||
@NotNull
|
||||
protected abstract Iterable<T> getStatements(@NotNull PsiJavaModule javaModule);
|
||||
|
||||
@NotNull
|
||||
protected static String joinNames(@NotNull List<String> oldNames, @NotNull List<String> newNames) {
|
||||
final StringJoiner joiner = new StringJoiner(",");
|
||||
oldNames.forEach(joiner::add);
|
||||
newNames.stream().filter(name -> !oldNames.contains(name)).forEach(joiner::add);
|
||||
return joiner.toString();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static MergeModuleStatementsFix createFix(@Nullable PsiElement statement) {
|
||||
if (statement instanceof PsiPackageAccessibilityStatement) {
|
||||
return MergePackageAccessibilityStatementsFix.createFix((PsiPackageAccessibilityStatement)statement);
|
||||
}
|
||||
else if (statement instanceof PsiProvidesStatement) {
|
||||
return MergeProvidesStatementsFix.createFix((PsiProvidesStatement)statement);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInsight.daemon.impl.quickfix;
|
||||
|
||||
import com.intellij.codeInsight.daemon.QuickFixBundle;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiJavaModule;
|
||||
import com.intellij.psi.PsiKeyword;
|
||||
import com.intellij.psi.PsiPackageAccessibilityStatement;
|
||||
import com.intellij.psi.PsiPackageAccessibilityStatement.Role;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Pavel.Dolgov
|
||||
*/
|
||||
public class MergePackageAccessibilityStatementsFix
|
||||
extends MergeModuleStatementsFix<PsiPackageAccessibilityStatement> {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(MergePackageAccessibilityStatementsFix.class);
|
||||
private final String myPackageName;
|
||||
private final List<String> myModuleNames;
|
||||
private final Role myRole;
|
||||
|
||||
protected MergePackageAccessibilityStatementsFix(@NotNull PsiPackageAccessibilityStatement thisStatement,
|
||||
@NotNull String packageName,
|
||||
@NotNull List<String> moduleNames,
|
||||
@NotNull PsiPackageAccessibilityStatement otherStatement) {
|
||||
super(thisStatement, otherStatement);
|
||||
myPackageName = packageName;
|
||||
myModuleNames = moduleNames;
|
||||
myRole = thisStatement.getRole();
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getText() {
|
||||
return QuickFixBundle.message("java.9.merge.module.statements.fix.name", getKeyword(), myPackageName);
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return QuickFixBundle.message("java.9.merge.module.statements.fix.family.name", getKeyword());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getReplacementText(@NotNull PsiPackageAccessibilityStatement otherStatement) {
|
||||
return getKeyword() + " " + myPackageName + " " + PsiKeyword.TO + " " +
|
||||
joinNames(otherStatement.getModuleNames(), myModuleNames) + ";";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Iterable<PsiPackageAccessibilityStatement> getStatements(@NotNull PsiJavaModule javaModule) {
|
||||
return getStatements(javaModule, myRole);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static MergeModuleStatementsFix createFix(@Nullable PsiPackageAccessibilityStatement statement) {
|
||||
if (statement != null) {
|
||||
final PsiElement parent = statement.getParent();
|
||||
if (parent instanceof PsiJavaModule) {
|
||||
final PsiJavaModule javaModule = (PsiJavaModule)parent;
|
||||
|
||||
final String packageName = statement.getPackageName();
|
||||
if (packageName != null) {
|
||||
final List<String> moduleNames = statement.getModuleNames();
|
||||
if (!moduleNames.isEmpty()) {
|
||||
for (PsiPackageAccessibilityStatement candidate : getStatements(javaModule, statement.getRole())) {
|
||||
if (candidate != statement &&
|
||||
packageName.equals(candidate.getPackageName()) &&
|
||||
candidate.getModuleNames().iterator().hasNext()) {
|
||||
return new MergePackageAccessibilityStatementsFix(statement, packageName, moduleNames, candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Iterable<PsiPackageAccessibilityStatement> getStatements(@NotNull PsiJavaModule javaModule, @NotNull Role role) {
|
||||
switch (role) {
|
||||
case OPENS:
|
||||
return javaModule.getOpens();
|
||||
case EXPORTS:
|
||||
return javaModule.getExports();
|
||||
}
|
||||
LOG.error("Unexpected role " + role);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getKeyword() {
|
||||
switch (myRole) {
|
||||
case OPENS:
|
||||
return PsiKeyword.OPENS;
|
||||
case EXPORTS:
|
||||
return PsiKeyword.EXPORTS;
|
||||
}
|
||||
LOG.error("Unexpected role " + myRole);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInsight.daemon.impl.quickfix;
|
||||
|
||||
import com.intellij.codeInsight.daemon.QuickFixBundle;
|
||||
import com.intellij.psi.*;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Pavel.Dolgov
|
||||
*/
|
||||
public class MergeProvidesStatementsFix extends MergeModuleStatementsFix<PsiProvidesStatement> {
|
||||
private final String myInterfaceName;
|
||||
private final List<String> myImplementationNames;
|
||||
|
||||
MergeProvidesStatementsFix(@NotNull PsiProvidesStatement thisStatement,
|
||||
@NotNull String interfaceName,
|
||||
@NotNull List<String> implementationNames,
|
||||
@NotNull PsiProvidesStatement otherStatement) {
|
||||
super(thisStatement, otherStatement);
|
||||
myInterfaceName = interfaceName;
|
||||
myImplementationNames = implementationNames;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getText() {
|
||||
return QuickFixBundle.message("java.9.merge.module.statements.fix.name", PsiKeyword.PROVIDES, myInterfaceName);
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return QuickFixBundle.message("java.9.merge.module.statements.fix.family.name", PsiKeyword.PROVIDES);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getReplacementText(@NotNull PsiProvidesStatement otherStatement) {
|
||||
return PsiKeyword.PROVIDES + " " + myInterfaceName + " " + PsiKeyword.WITH + " " +
|
||||
joinNames(getImplementationNames(otherStatement), myImplementationNames) + ";";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Iterable<PsiProvidesStatement> getStatements(@NotNull PsiJavaModule javaModule) {
|
||||
return javaModule.getProvides();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<String> getImplementationNames(@Nullable PsiProvidesStatement statement) {
|
||||
if (statement != null) {
|
||||
final PsiReferenceList implementationList = statement.getImplementationList();
|
||||
if (implementationList != null) {
|
||||
return Arrays.stream(implementationList.getReferenceElements())
|
||||
.map(PsiJavaCodeReferenceElement::getQualifiedName)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static MergeModuleStatementsFix createFix(@Nullable PsiProvidesStatement statement) {
|
||||
if (statement != null) {
|
||||
final PsiElement parent = statement.getParent();
|
||||
if (parent instanceof PsiJavaModule) {
|
||||
final PsiJavaModule javaModule = (PsiJavaModule)parent;
|
||||
|
||||
final PsiJavaCodeReferenceElement interfaceReference = statement.getInterfaceReference();
|
||||
if (interfaceReference != null) {
|
||||
final String interfaceName = interfaceReference.getQualifiedName();
|
||||
if (interfaceName != null) {
|
||||
final List<String> implementationNames = getImplementationNames(statement);
|
||||
if (!implementationNames.isEmpty()) {
|
||||
for (PsiProvidesStatement candidate : javaModule.getProvides()) {
|
||||
final PsiJavaCodeReferenceElement candidateInterfaceReference = candidate.getInterfaceReference();
|
||||
if (candidateInterfaceReference != null && interfaceName.equals(candidateInterfaceReference.getQualifiedName())) {
|
||||
return new MergeProvidesStatementsFix(statement, interfaceName, implementationNames, candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
module M {
|
||||
exports my.api to M4;
|
||||
exports <caret>my.api to M6;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module M {
|
||||
exports my.api to M4, M6;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
module M {
|
||||
exports my.api;
|
||||
exports <caret>my.api to M2, M4;
|
||||
exports my.api to M6;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
module M {
|
||||
exports my.api;
|
||||
exports my.api to M6, M2, M4;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
module M {
|
||||
opens my.api to M4;
|
||||
opens <caret>my.api to M6;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module M {
|
||||
opens my.api to M4, M6;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
module M {
|
||||
opens my.api;
|
||||
opens <caret>my.api to M2, M4;
|
||||
opens my.api to M6;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
module M {
|
||||
opens my.api;
|
||||
opens my.api to M6, M2, M4;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
module M {
|
||||
provides my.api.MyService with my.impl.MyServiceImpl;
|
||||
provides my.api.MyService with my.impl.<caret>MyServiceImpl1;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module M {
|
||||
provides my.api.MyService with my.impl.MyServiceImpl,my.impl.MyServiceImpl1;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import my.impl.MyServiceImpl;
|
||||
import my.impl.MyServiceImpl1;
|
||||
import my.impl.MyServiceImpl2;
|
||||
|
||||
module M {
|
||||
provides my.api.MyService with MyServiceImpl, MyServiceImpl2;
|
||||
provides my.api.MyService with <caret>MyServiceImpl1;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import my.impl.MyServiceImpl;
|
||||
import my.impl.MyServiceImpl1;
|
||||
import my.impl.MyServiceImpl2;
|
||||
|
||||
module M {
|
||||
provides my.api.MyService with MyServiceImpl,MyServiceImpl2,MyServiceImpl1;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import my.impl.MyServiceImpl;
|
||||
import my.impl.MyServiceImpl1;
|
||||
import my.impl.MyServiceImpl2;
|
||||
|
||||
module M {
|
||||
provides my.api.MyService with MyServiceImpl;
|
||||
provides my.api.MyService with <caret>MyServiceImpl1, MyServiceImpl2;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import my.impl.MyServiceImpl;
|
||||
import my.impl.MyServiceImpl1;
|
||||
import my.impl.MyServiceImpl2;
|
||||
|
||||
module M {
|
||||
provides my.api.MyService with MyServiceImpl,MyServiceImpl1,MyServiceImpl2;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInsight.daemon.quickFix
|
||||
|
||||
import com.intellij.JavaTestUtil.getRelativeJavaTestDataPath
|
||||
import com.intellij.codeInsight.daemon.QuickFixBundle
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.testFramework.fixtures.LightJava9ModulesCodeInsightFixtureTestCase
|
||||
import com.intellij.testFramework.fixtures.MultiModuleJava9ProjectDescriptor.ModuleDescriptor.*
|
||||
|
||||
/**
|
||||
* @author Pavel.Dolgov
|
||||
*/
|
||||
class MergeModuleStatementsFixTest : LightJava9ModulesCodeInsightFixtureTestCase() {
|
||||
|
||||
override fun getBasePath() = getRelativeJavaTestDataPath() + "/codeInsight/daemonCodeAnalyzer/quickFix/mergeModuleStatementsFix"
|
||||
|
||||
fun testExports1() = doTest("exports", "my.api")
|
||||
fun testExports2() = doTest("exports", "my.api")
|
||||
|
||||
fun testProvides1() = doTest("provides", "my.api.MyService")
|
||||
fun testProvides2() = doTest("provides", "my.api.MyService")
|
||||
fun testProvides3() = doTest("provides", "my.api.MyService")
|
||||
|
||||
fun testOpens1() = doTest("opens", "my.api")
|
||||
fun testOpens2() = doTest("opens", "my.api")
|
||||
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
addFile("module-info.java", "module M2 { }", M2)
|
||||
addFile("module-info.java", "module M4 { }", M4)
|
||||
addFile("module-info.java", "module M6 { }", M6)
|
||||
|
||||
addFile("my/api/MyService.java", "package my.api; public class MyService {}")
|
||||
addFile("my/impl/MyServiceImpl.java", "package my.impl; public class MyServiceImpl extends my.api.MyService {}")
|
||||
addFile("my/impl/MyServiceImpl1.java", "package my.impl; public class MyServiceImpl1 extends my.api.MyService {}")
|
||||
addFile("my/impl/MyServiceImpl2.java", "package my.impl; public class MyServiceImpl2 extends my.api.MyService {}")
|
||||
}
|
||||
|
||||
private fun doTest(type: String, name: String) {
|
||||
val testName = getTestName(false)
|
||||
val virtualFile = myFixture.copyFileToProject("${testName}.java", "module-info.java")
|
||||
myFixture.configureFromExistingVirtualFile(virtualFile)
|
||||
|
||||
val action = findActionWithText(QuickFixBundle.message("java.9.merge.module.statements.fix.name", type, name))
|
||||
myFixture.launchAction(action)
|
||||
myFixture.checkResultByFile("${testName}_after.java")
|
||||
}
|
||||
|
||||
private fun findActionWithText(actionText: String): IntentionAction {
|
||||
myFixture.doHighlighting()
|
||||
|
||||
val actions = LightQuickFixTestCase.getAvailableActions(editor, file)
|
||||
val action = LightQuickFixTestCase.findActionWithText(actions, actionText)
|
||||
assertNotNull("No action [$actionText] in ${actions.map { it.text }}", action)
|
||||
return action
|
||||
}
|
||||
}
|
||||
@@ -310,3 +310,6 @@ insert.sam.method.call.fix.family.name=Insert single abstract method call
|
||||
wrap.with.java.io.file.text=Wrap using 'new File()'
|
||||
wrap.with.java.io.file.parameter.single.text=Wrap parameter using 'new File()'
|
||||
wrap.with.java.io.file.parameter.multiple.text=Wrap {0, choice, 1#1st|2#2nd|3#3rd|4#{0,number}th} parameter using ''new File()''
|
||||
|
||||
java.9.merge.module.statements.fix.family.name=Merge with other ''{0}'' statement
|
||||
java.9.merge.module.statements.fix.name=Merge with other ''{0} {1}'' statement
|
||||
Reference in New Issue
Block a user