standalone JavaToKotlinTranslator added

This commit is contained in:
ignatov
2011-12-28 21:34:52 +04:00
parent 891f844fda
commit 9a808c10c9
6 changed files with 248 additions and 52 deletions
+18 -31
View File
@@ -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) {