standalone JavaToKotlinTranslator added
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
package org.jetbrains.jet.j2k;
|
||||
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.j2k.ast.*;
|
||||
@@ -22,7 +20,7 @@ import static org.jetbrains.jet.j2k.visitors.TypeVisitor.*;
|
||||
*/
|
||||
public class Converter {
|
||||
@NotNull
|
||||
private final static Set<String> NOT_NULL_ANNOTATIONS = new HashSet<String>() {
|
||||
final static Set<String> NOT_NULL_ANNOTATIONS = new HashSet<String>() {
|
||||
{
|
||||
add("org.jetbrains.annotations.NotNull");
|
||||
add("com.sun.istack.internal.NotNull");
|
||||
@@ -326,9 +324,9 @@ public class Converter {
|
||||
ourMethodReturnType = method.getReturnType();
|
||||
|
||||
final IdentifierImpl identifier = new IdentifierImpl(method.getName());
|
||||
final Type returnType = typeToType(method.getReturnType(), isNotNull(method.getModifierList()));
|
||||
final Type returnType = typeToType(method.getReturnType(), ConverterUtil.isAnnotatedAsNotNull(method.getModifierList()));
|
||||
final Block body = blockToBlock(method.getBody(), notEmpty);
|
||||
final Element params = elementToElement(method.getParameterList());
|
||||
final Element params = createFunctionParameters(method);
|
||||
final List<Element> typeParameters = elementsToElementList(method.getTypeParameters());
|
||||
|
||||
final Set<String> modifiers = modifiersListToModifiersSet(method.getModifierList());
|
||||
@@ -361,6 +359,19 @@ public class Converter {
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static ParameterList createFunctionParameters(@NotNull PsiMethod method) {
|
||||
List<Parameter> result = new LinkedList<Parameter>();
|
||||
for (PsiParameter parameter : method.getParameterList().getParameters()) {
|
||||
result.add(new Parameter(
|
||||
new IdentifierImpl(parameter.getName()),
|
||||
typeToType(parameter.getType(), ConverterUtil.isAnnotatedAsNotNull(parameter.getModifierList())),
|
||||
ConverterUtil.isReadOnly(parameter, method.getBody())
|
||||
));
|
||||
}
|
||||
return new ParameterList(result);
|
||||
}
|
||||
|
||||
private static boolean isNotOpenMethod(@NotNull final PsiMethod method) {
|
||||
if (method.getParent() instanceof PsiClass) {
|
||||
final PsiModifierList parentModifierList = ((PsiClass) method.getParent()).getModifierList();
|
||||
@@ -549,35 +560,10 @@ public class Converter {
|
||||
public static Parameter parameterToParameter(@NotNull PsiParameter parameter) {
|
||||
return new Parameter(
|
||||
new IdentifierImpl(parameter.getName()),
|
||||
typeToType(parameter.getType(), isNotNull(parameter.getModifierList())),
|
||||
isReadOnly(parameter)
|
||||
typeToType(parameter.getType(), ConverterUtil.isAnnotatedAsNotNull(parameter.getModifierList()))
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean isNotNull(@Nullable PsiModifierList modifierList) {
|
||||
if (modifierList != null) {
|
||||
PsiAnnotation[] annotations = modifierList.getAnnotations();
|
||||
for (PsiAnnotation a : annotations) {
|
||||
String qualifiedName = a.getQualifiedName();
|
||||
if (qualifiedName != null && NOT_NULL_ANNOTATIONS.contains(qualifiedName))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isReadOnly(@NotNull PsiParameter parameter) {
|
||||
try {
|
||||
for (PsiReference r : (ReferencesSearch.search(parameter))) {
|
||||
if (r instanceof PsiExpression && PsiUtil.isAccessedForWriting((PsiExpression) r)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Identifier identifierToIdentifier(@Nullable PsiIdentifier identifier) {
|
||||
if (identifier == null) return Identifier.EMPTY_IDENTIFIER;
|
||||
@@ -722,4 +708,5 @@ public class Converter {
|
||||
createConversionForExpression(expression, type) : "";
|
||||
return new SureCallChainExpression(expressionToExpression(expression), conversion);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package org.jetbrains.jet.j2k;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -9,6 +10,8 @@ import java.text.MessageFormat;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.jet.j2k.Converter.NOT_NULL_ANNOTATIONS;
|
||||
|
||||
/**
|
||||
* @author ignatov
|
||||
*/
|
||||
@@ -68,4 +71,46 @@ public class ConverterUtil {
|
||||
final PsiType componentType = ((PsiArrayType) type).getComponentType();
|
||||
return componentType.equalsToText("java.lang.String");
|
||||
}
|
||||
|
||||
public static int countWritingAccesses(@Nullable PsiElement element, @Nullable PsiElement container) {
|
||||
int counter = 0;
|
||||
if (container != null) {
|
||||
ReferenceCollector visitor = new ReferenceCollector();
|
||||
container.accept(visitor);
|
||||
for (PsiReferenceExpression e : visitor.getCollectedReferences())
|
||||
if (e.isReferenceTo(element) && PsiUtil.isAccessedForWriting(e))
|
||||
counter++;
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
static boolean isReadOnly(PsiElement element, PsiElement container) {
|
||||
return countWritingAccesses(element, container) == 0;
|
||||
}
|
||||
|
||||
public static boolean isAnnotatedAsNotNull(@Nullable PsiModifierList modifierList) {
|
||||
if (modifierList != null) {
|
||||
PsiAnnotation[] annotations = modifierList.getAnnotations();
|
||||
for (PsiAnnotation a : annotations) {
|
||||
String qualifiedName = a.getQualifiedName();
|
||||
if (qualifiedName != null && NOT_NULL_ANNOTATIONS.contains(qualifiedName))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static class ReferenceCollector extends JavaRecursiveElementVisitor {
|
||||
public List<PsiReferenceExpression> getCollectedReferences() {
|
||||
return myCollectedReferences;
|
||||
}
|
||||
|
||||
private List<PsiReferenceExpression> myCollectedReferences = new LinkedList<PsiReferenceExpression>();
|
||||
|
||||
@Override
|
||||
public void visitReferenceExpression(PsiReferenceExpression expression) {
|
||||
super.visitReferenceExpression(expression);
|
||||
myCollectedReferences.add(expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2000-2011 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 org.jetbrains.jet.j2k;
|
||||
|
||||
import com.intellij.core.JavaCoreEnvironment;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiFileFactory;
|
||||
import com.intellij.psi.PsiJavaFile;
|
||||
import com.intellij.util.Function;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.j2k.visitors.ClassVisitor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
|
||||
/**
|
||||
* @author ignatov
|
||||
*/
|
||||
public class JavaToKotlinTranslator {
|
||||
private JavaToKotlinTranslator() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected static PsiFile createFile(@NonNls String name, String text) {
|
||||
JavaCoreEnvironment javaCoreEnvironment = new JavaCoreEnvironment(new Disposable() {
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
});
|
||||
|
||||
javaCoreEnvironment.addToClasspath(findRtJar(true));
|
||||
javaCoreEnvironment.addToClasspath(new File("jre/lib/annotations.jar"));
|
||||
|
||||
return PsiFileFactory.getInstance(javaCoreEnvironment.getProject()).createFileFromText(
|
||||
name, JavaLanguage.INSTANCE, text
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String prettify(@Nullable String code) {
|
||||
if (code == null)
|
||||
return "";
|
||||
return code
|
||||
.trim()
|
||||
.replaceAll("\r\n", "\n")
|
||||
.replaceAll(" \n", "\n")
|
||||
.replaceAll("\n ", "\n")
|
||||
.replaceAll("\n+", "\n")
|
||||
.replaceAll(" +", " ")
|
||||
.trim()
|
||||
;
|
||||
}
|
||||
|
||||
public static File findRtJar(boolean failOnError) {
|
||||
String javaHome = System.getenv("JAVA_HOME");
|
||||
File rtJar;
|
||||
if (javaHome == null) {
|
||||
rtJar = findActiveRtJar(failOnError);
|
||||
|
||||
if (rtJar == null && failOnError) {
|
||||
throw new SetupJavaCoreEnvironmentException("JAVA_HOME environment variable needs to be defined");
|
||||
}
|
||||
} else {
|
||||
rtJar = findRtJar(javaHome);
|
||||
}
|
||||
|
||||
if ((rtJar == null || !rtJar.exists()) && failOnError) {
|
||||
rtJar = findActiveRtJar(failOnError);
|
||||
|
||||
if ((rtJar == null || !rtJar.exists())) {
|
||||
throw new SetupJavaCoreEnvironmentException("No rt.jar found under JAVA_HOME=" + javaHome);
|
||||
}
|
||||
}
|
||||
return rtJar;
|
||||
}
|
||||
|
||||
private static File findRtJar(String javaHome) {
|
||||
File rtJar = new File(javaHome, "jre/lib/rt.jar");
|
||||
if (rtJar.exists()) {
|
||||
return rtJar;
|
||||
}
|
||||
|
||||
File classesJar = new File(new File(javaHome).getParentFile().getAbsolutePath(), "Classes/classes.jar");
|
||||
if (classesJar.exists()) {
|
||||
return classesJar;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static File findActiveRtJar(boolean failOnError) {
|
||||
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
|
||||
if (systemClassLoader instanceof URLClassLoader) {
|
||||
URLClassLoader loader = (URLClassLoader) systemClassLoader;
|
||||
for (URL url : loader.getURLs()) {
|
||||
if ("file".equals(url.getProtocol())) {
|
||||
if (url.getFile().endsWith("/lib/rt.jar")) {
|
||||
return new File(url.getFile());
|
||||
}
|
||||
if (url.getFile().endsWith("/Classes/classes.jar")) {
|
||||
return new File(url.getFile()).getAbsoluteFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (failOnError) {
|
||||
throw new SetupJavaCoreEnvironmentException("Could not find rt.jar in system class loader: " + StringUtil.join(loader.getURLs(), new Function<URL, String>() {
|
||||
@Override
|
||||
public String fun(URL url) {
|
||||
return url.toString() + "\n";
|
||||
}
|
||||
}, ", "));
|
||||
}
|
||||
} else if (failOnError) {
|
||||
throw new SetupJavaCoreEnvironmentException("System class loader is not an URLClassLoader: " + systemClassLoader);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void setClassIdentifiers(PsiElement psiFile) {
|
||||
ClassVisitor c = new ClassVisitor();
|
||||
psiFile.accept(c);
|
||||
Converter.clearClassIdentifiers();
|
||||
Converter.setClassIdentifiers(c.getClassIdentifiers());
|
||||
}
|
||||
|
||||
static String generateKotlinCode(String arg) {
|
||||
PsiFile file = createFile("test.java", arg);
|
||||
if (file != null && file instanceof PsiJavaFile) {
|
||||
setClassIdentifiers(file);
|
||||
return prettify(Converter.fileToFile((PsiJavaFile) file).toKotlin());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
if (args.length > 0) {
|
||||
//noinspection UseOfSystemOutOrSystemErr
|
||||
System.out.println(generateKotlinCode(args[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.jetbrains.jet.j2k;
|
||||
|
||||
/**
|
||||
* @author ignatov
|
||||
*/
|
||||
public class SetupJavaCoreEnvironmentException extends RuntimeException {
|
||||
public SetupJavaCoreEnvironmentException(String s) {
|
||||
}
|
||||
}
|
||||
@@ -2,22 +2,21 @@ package org.jetbrains.jet.j2k.visitors;
|
||||
|
||||
import com.intellij.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.j2k.Converter;
|
||||
import org.jetbrains.jet.j2k.ast.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.jet.j2k.Converter.*;
|
||||
import static org.jetbrains.jet.j2k.ConverterUtil.*;
|
||||
|
||||
/**
|
||||
* @author ignatov
|
||||
*/
|
||||
public class ElementVisitor extends JavaElementVisitor {
|
||||
@Nullable
|
||||
@NotNull
|
||||
private Element myResult = Element.EMPTY_ELEMENT;
|
||||
|
||||
@Nullable
|
||||
@NotNull
|
||||
public Element getResult() {
|
||||
return myResult;
|
||||
}
|
||||
@@ -29,7 +28,7 @@ public class ElementVisitor extends JavaElementVisitor {
|
||||
myResult = new LocalVariable(
|
||||
new IdentifierImpl(variable.getName()), // TODO
|
||||
modifiersListToModifiersSet(variable.getModifierList()),
|
||||
typeToType(variable.getType(), Converter.isNotNull(variable.getModifierList())),
|
||||
typeToType(variable.getType(), isAnnotatedAsNotNull(variable.getModifierList())),
|
||||
createSureCallOnlyForChain(variable.getInitializer(), variable.getType())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package org.jetbrains.jet.j2k.visitors;
|
||||
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.j2k.Converter;
|
||||
@@ -15,6 +13,7 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.jet.j2k.Converter.*;
|
||||
import static org.jetbrains.jet.j2k.ConverterUtil.countWritingAccesses;
|
||||
|
||||
/**
|
||||
* @author ignatov
|
||||
@@ -110,9 +109,16 @@ public class StatementVisitor extends ElementVisitor {
|
||||
final PsiStatement initialization = statement.getInitialization();
|
||||
final PsiStatement update = statement.getUpdate();
|
||||
final PsiExpression condition = statement.getCondition();
|
||||
final PsiStatement body = statement.getBody();
|
||||
|
||||
final PsiLocalVariable firstChild = initialization != null && initialization.getFirstChild() instanceof PsiLocalVariable ?
|
||||
(PsiLocalVariable) initialization.getFirstChild() : null;
|
||||
|
||||
int bodyWriteCount = countWritingAccesses(firstChild, body);
|
||||
int conditionWriteCount = countWritingAccesses(firstChild, condition);
|
||||
int updateWriteCount = countWritingAccesses(firstChild, update);
|
||||
boolean onceWritableIterator = updateWriteCount == 1 && bodyWriteCount + conditionWriteCount == 0;
|
||||
|
||||
final IElementType operationTokenType = condition != null && condition instanceof PsiBinaryExpression ?
|
||||
((PsiBinaryExpression) condition).getOperationTokenType() : null;
|
||||
if (
|
||||
@@ -130,7 +136,7 @@ public class StatementVisitor extends ElementVisitor {
|
||||
&& initialization.getFirstChild() instanceof PsiLocalVariable
|
||||
&& firstChild != null
|
||||
&& firstChild.getNameIdentifier() != null
|
||||
&& isOnceWritableIterator(firstChild)
|
||||
&& onceWritableIterator
|
||||
) {
|
||||
final Expression end = expressionToExpression(((PsiBinaryExpression) condition).getROperand());
|
||||
final Expression endExpression = operationTokenType == JavaTokenType.LT ?
|
||||
@@ -140,7 +146,7 @@ public class StatementVisitor extends ElementVisitor {
|
||||
new IdentifierImpl(firstChild.getName()),
|
||||
expressionToExpression(firstChild.getInitializer()),
|
||||
endExpression,
|
||||
statementToStatement(statement.getBody())
|
||||
statementToStatement(body)
|
||||
);
|
||||
} else { // common case: while loop instead of for loop
|
||||
List<Statement> forStatements = new LinkedList<Statement>();
|
||||
@@ -148,23 +154,12 @@ public class StatementVisitor extends ElementVisitor {
|
||||
forStatements.add(new WhileStatement(
|
||||
expressionToExpression(condition),
|
||||
new Block(
|
||||
Arrays.asList(statementToStatement(statement.getBody()),
|
||||
Arrays.asList(statementToStatement(body),
|
||||
new Block(Arrays.asList(statementToStatement(update)))))));
|
||||
myResult = new Block(forStatements);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isOnceWritableIterator(@Nullable PsiLocalVariable firstChild) {
|
||||
int counter = 0;
|
||||
if (firstChild != null)
|
||||
for (PsiReference r : (ReferencesSearch.search(firstChild))) {
|
||||
if (r instanceof PsiExpression) {
|
||||
if (PsiUtil.isAccessedForWriting((PsiExpression) r))
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
return counter == 1; // only increment usage
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitForeachStatement(@NotNull PsiForeachStatement statement) {
|
||||
|
||||
Reference in New Issue
Block a user