diff --git a/j2k.iml b/j2k.iml index f7e0d92adb2..2b7c256c235 100644 --- a/j2k.iml +++ b/j2k.iml @@ -9,6 +9,8 @@ + + diff --git a/j2k.ipr b/j2k.ipr index 85640093dc3..f91fa7f6738 100644 --- a/j2k.ipr +++ b/j2k.ipr @@ -13,6 +13,17 @@ + + $PROJECT_DIR$/out/artifacts/java2kotlin_jar + + + + + + + + + + + + + + + + diff --git a/lib/commons-cli-1.2.jar b/lib/commons-cli-1.2.jar new file mode 100644 index 00000000000..ce4b9fffe40 Binary files /dev/null and b/lib/commons-cli-1.2.jar differ diff --git a/src/META-INF/MANIFEST.MF b/src/META-INF/MANIFEST.MF index 558fbaba78d..e71f8509eeb 100644 --- a/src/META-INF/MANIFEST.MF +++ b/src/META-INF/MANIFEST.MF @@ -1,3 +1,3 @@ -Manifest-Version: 1.0 -Main-Class: org.jetbrains.jet.j2k.JavaToKotlinTranslator - +Manifest-Version: 1.0 +Main-Class: org.jetbrains.jet.j2k.JavaToKotlinCli + diff --git a/src/org/jetbrains/jet/j2k/Converter.java b/src/org/jetbrains/jet/j2k/Converter.java index 4ed80d216c5..8d976288e52 100644 --- a/src/org/jetbrains/jet/j2k/Converter.java +++ b/src/org/jetbrains/jet/j2k/Converter.java @@ -6,7 +6,6 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.j2k.ast.*; import org.jetbrains.jet.j2k.ast.Class; import org.jetbrains.jet.j2k.ast.Enum; -import org.jetbrains.jet.j2k.ast.Modifier; import org.jetbrains.jet.j2k.util.AstUtil; import org.jetbrains.jet.j2k.visitors.*; @@ -35,9 +34,20 @@ public class Converter { @Nullable private static PsiType ourMethodReturnType = null; + @NotNull + private static Set settings = new HashSet(); + private Converter() { } + public static boolean addSetting(@NotNull String value) { + return settings.add(value); + } + + public static boolean hasSetting(@NotNull String value) { + return settings.contains(value); + } + public static void setClassIdentifiers(@NotNull Set identifiers) { ourClassIdentifiers = identifiers; } @@ -327,7 +337,7 @@ public class Converter { final IdentifierImpl identifier = new IdentifierImpl(method.getName()); final Type returnType = typeToType(method.getReturnType(), ConverterUtil.isAnnotatedAsNotNull(method.getModifierList())); - final Block body = blockToBlock(method.getBody(), notEmpty); + final Block body = hasSetting("declarations-only") ? Block.EMPTY_BLOCK : blockToBlock(method.getBody(), notEmpty); // #TODO final Element params = createFunctionParameters(method); final List typeParameters = elementsToElementList(method.getTypeParameters()); @@ -608,10 +618,10 @@ public class Converter { for (PsiExpression e : arguments) actualTypes.add(e.getType()); - assert actualTypes.size() == expectedTypes.size() : "The type list must have the same length"; - - for (int i = 0; i < actualTypes.size(); i++) - conversions.set(i, createConversionForExpression(arguments[i], expectedTypes.get(i))); + if (conversions.size() == actualTypes.size() && actualTypes.size() == expectedTypes.size()) { + for (int i = 0; i < actualTypes.size(); i++) + conversions.set(i, createConversionForExpression(arguments[i], expectedTypes.get(i))); + } } return conversions; } diff --git a/src/org/jetbrains/jet/j2k/JavaToKotlinCli.java b/src/org/jetbrains/jet/j2k/JavaToKotlinCli.java new file mode 100644 index 00000000000..1f322e030ee --- /dev/null +++ b/src/org/jetbrains/jet/j2k/JavaToKotlinCli.java @@ -0,0 +1,147 @@ +package org.jetbrains.jet.j2k; + +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiJavaFile; +import org.apache.commons.cli.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Logger; +import java.util.regex.Pattern; + +import static org.apache.commons.io.FileUtils.readFileToString; +import static org.apache.commons.io.FileUtils.writeStringToFile; + +/** + * @author ignatov + */ +@SuppressWarnings({"CallToPrintStackTrace", "UseOfSystemOutOrSystemErr"}) +public class JavaToKotlinCli { + private static final Logger myLogger = Logger.getAnonymousLogger(); + + private JavaToKotlinCli() { + } + + public static void main(String[] args) { + CommandLineParser parser = new BasicParser(); + Options options = new Options() + .addOption("h", "help", false, "Print usage information") + .addOption("f", "from", true, "Directory with Java sources") + .addOption("t", "to", true, "Directory with Kotlin sources") + .addOption("p", "public-only", false, "Only public and protected members") + .addOption("fqn", "fqn", false, "Full qualified names") + .addOption("d", "declarations-only", false, "Declarations only") + ; + + try { + CommandLine commandLine = parser.parse(options, args); + + if (commandLine.hasOption("help")) + showHelpAndExit(); + + if (commandLine.hasOption("from") && commandLine.hasOption("to")) { + String from = commandLine.getOptionValue("from"); + String to = commandLine.getOptionValue("to"); + + for (Option o : commandLine.getOptions()) { + Converter.addSetting(o.getLongOpt()); + } + + if (!from.isEmpty() && !to.isEmpty()) + convertSourceTree(from, to); + else + showHelpAndExit(); + } else + showHelpAndExit(); + } catch (ParseException e) { + e.printStackTrace(); + } + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + private static void convertSourceTree(String javaPath, String kotlinPath) { + try { + File javaDir = new File(javaPath); + File kotlinDir = new File(kotlinPath); + + if (kotlinDir.exists()) + kotlinDir.delete(); + + if (!kotlinDir.exists() && !kotlinDir.mkdir()) + myLogger.warning("Creation failed: " + kotlinDir.getAbsolutePath()); + + for (File f : getJavaFiles(javaDir.getAbsolutePath())) { + String relative = javaDir.toURI().relativize(f.toURI()).getPath().replace(".java", ".kt"); + File file = new File(kotlinPath, relative); + + if (file.exists()) + file.delete(); + + if (f.isDirectory()) + if (!file.exists() && !file.mkdir()) + myLogger.warning("Creation failed: " + file.getAbsolutePath()); + + if (f.isFile()) { + writeStringToFile(file, fileToKotlin(f)); + } + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @NotNull + private static String fileToKotlin(File f) throws IOException { + final String javaCode = readJavaFileToString(f); + return generateKotlinCode(JavaToKotlinTranslator.createFile(JavaToKotlinTranslator.setUpJavaCoreEnvironment(), javaCode)); + } + + @NotNull + private static String generateKotlinCode(@Nullable PsiFile file) { + if (file != null && file instanceof PsiJavaFile) { + JavaToKotlinTranslator.setClassIdentifiers(file); + return JavaToKotlinTranslator.prettify(Converter.fileToFile((PsiJavaFile) file).toKotlin()); + } + return ""; + } + + @NotNull + private static String readJavaFileToString(@NotNull File javaFile) throws IOException { + return Pattern.compile("\\s*/\\*.*\\*/", Pattern.DOTALL).matcher(readFileToString(javaFile)).replaceAll(""); + } + + private static void showHelpAndExit() { + System.err.println("Usage: java -jar java2kotlin.jar -f -t "); + System.exit(1); + } + + static public List getJavaFiles(String startDirName) throws FileNotFoundException { + return getJavaFiles(new File(startDirName)); + } + + private static List getJavaFiles(File start) throws FileNotFoundException { + List result = new ArrayList(); + + if (start.isFile()) + return Arrays.asList(start); + + for (File file : Arrays.asList(start.listFiles())) { + if ((file.isFile() && file.getName().endsWith(".java")) || file.isDirectory()) + result.add(file); + + if (file.isDirectory()) { + List deeperList = getJavaFiles(file); + result.addAll(deeperList); + } + } + return result; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/jet/j2k/JavaToKotlinTranslator.java b/src/org/jetbrains/jet/j2k/JavaToKotlinTranslator.java index 7698eda8c5e..f87a8952a6e 100644 --- a/src/org/jetbrains/jet/j2k/JavaToKotlinTranslator.java +++ b/src/org/jetbrains/jet/j2k/JavaToKotlinTranslator.java @@ -73,7 +73,7 @@ public class JavaToKotlinTranslator { } @NotNull - private static String prettify(@Nullable String code) { + static String prettify(@Nullable String code) { if (code == null) return ""; return code @@ -175,8 +175,8 @@ public class JavaToKotlinTranslator { } @NotNull - static String generateKotlinCode(@NotNull String arg) { - PsiFile file = createFile(arg); + static String generateKotlinCode(@NotNull String javaCode) { + PsiFile file = createFile(javaCode); if (file != null && file instanceof PsiJavaFile) { setClassIdentifiers(file); return prettify(Converter.fileToFile((PsiJavaFile) file).toKotlin()); @@ -185,8 +185,8 @@ public class JavaToKotlinTranslator { } @NotNull - static String generateKotlinCodeWithCompatibilityImport(@NotNull String arg) { - PsiFile file = createFile(arg); + static String generateKotlinCodeWithCompatibilityImport(@NotNull String javaCode) { + PsiFile file = createFile(javaCode); if (file != null && file instanceof PsiJavaFile) { setClassIdentifiers(file); return prettify(Converter.fileToFileWithCompatibilityImport((PsiJavaFile) file).toKotlin()); diff --git a/src/org/jetbrains/jet/j2k/ast/AnonymousClass.java b/src/org/jetbrains/jet/j2k/ast/AnonymousClass.java index e8a87d2b3f7..c5524a0bb91 100644 --- a/src/org/jetbrains/jet/j2k/ast/AnonymousClass.java +++ b/src/org/jetbrains/jet/j2k/ast/AnonymousClass.java @@ -16,7 +16,7 @@ public class AnonymousClass extends Class { Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), - members + getMembers(members) ); } diff --git a/src/org/jetbrains/jet/j2k/ast/Class.java b/src/org/jetbrains/jet/j2k/ast/Class.java index 141a12699f0..87667feb136 100644 --- a/src/org/jetbrains/jet/j2k/ast/Class.java +++ b/src/org/jetbrains/jet/j2k/ast/Class.java @@ -2,6 +2,7 @@ package org.jetbrains.jet.j2k.ast; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.j2k.Converter; import org.jetbrains.jet.j2k.util.AstUtil; import java.util.HashSet; @@ -19,20 +20,34 @@ public class Class extends Member { String TYPE = "class"; final Identifier myName; private final List myBaseClassParams; - private final List myMembers; + private final List myMembers; private final List myTypeParameters; private final List myExtendsTypes; private final List myImplementsTypes; public Class(Identifier name, Set modifiers, List typeParameters, List extendsTypes, - List baseClassParams, List implementsTypes, List members) { + List baseClassParams, List implementsTypes, List members) { myName = name; myBaseClassParams = baseClassParams; myModifiers = modifiers; myTypeParameters = typeParameters; myExtendsTypes = extendsTypes; myImplementsTypes = implementsTypes; - myMembers = members; + myMembers= getMembers(members); + } + + static List getMembers(List members) { + List withoutPrivate = new LinkedList(); + if (Converter.hasSetting("public-only")) { + for (Member m : members) { + if (m.accessModifier().equals("public") || m.accessModifier().equals("protected") || m.accessModifier().isEmpty()) { + withoutPrivate.add(m); + } + } + } else { + withoutPrivate = members; + } + return withoutPrivate; } diff --git a/src/org/jetbrains/jet/j2k/ast/Enum.java b/src/org/jetbrains/jet/j2k/ast/Enum.java index 2fd28501176..cbf2cc7872c 100644 --- a/src/org/jetbrains/jet/j2k/ast/Enum.java +++ b/src/org/jetbrains/jet/j2k/ast/Enum.java @@ -12,7 +12,7 @@ import java.util.Set; public class Enum extends Class { public Enum(Identifier name, Set modifiers, List typeParameters, List extendsTypes, List baseClassParams, List implementsTypes, List members) { - super(name, modifiers, typeParameters, extendsTypes, baseClassParams, implementsTypes, members); + super(name, modifiers, typeParameters, extendsTypes, baseClassParams, implementsTypes, getMembers(members)); } String primaryConstructorSignatureToKotlin() { diff --git a/src/org/jetbrains/jet/j2k/ast/Trait.java b/src/org/jetbrains/jet/j2k/ast/Trait.java index 60bcd122add..aed3f075e3d 100644 --- a/src/org/jetbrains/jet/j2k/ast/Trait.java +++ b/src/org/jetbrains/jet/j2k/ast/Trait.java @@ -9,7 +9,7 @@ import java.util.Set; public class Trait extends Class { public Trait(Identifier name, Set modifiers, List typeParameters, List extendsTypes, List baseClassParams, List implementsTypes, List members) { - super(name, modifiers, typeParameters, extendsTypes, baseClassParams, implementsTypes, members); + super(name, modifiers, typeParameters, extendsTypes, baseClassParams, implementsTypes, getMembers(members)); TYPE = "trait"; } diff --git a/src/org/jetbrains/jet/j2k/visitors/TypeVisitor.java b/src/org/jetbrains/jet/j2k/visitors/TypeVisitor.java index dc7d1814e07..4d71752d174 100644 --- a/src/org/jetbrains/jet/j2k/visitors/TypeVisitor.java +++ b/src/org/jetbrains/jet/j2k/visitors/TypeVisitor.java @@ -3,6 +3,7 @@ package org.jetbrains.jet.j2k.visitors; import com.intellij.psi.*; import com.intellij.psi.impl.source.PsiClassReferenceType; import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.j2k.Converter; import org.jetbrains.jet.j2k.ast.*; import org.jetbrains.jet.j2k.util.AstUtil; @@ -74,6 +75,8 @@ public class TypeVisitor extends PsiTypeVisitor { if (psiClass != null) { String qualifiedName = psiClass.getQualifiedName(); if (qualifiedName != null) { + if (!qualifiedName.equals("java.lang.Object") && Converter.hasSetting("fqn")) + return new IdentifierImpl(qualifiedName); if (qualifiedName.equals(JAVA_LANG_ITERABLE)) return new IdentifierImpl(JAVA_LANG_ITERABLE); if (qualifiedName.equals(JAVA_UTIL_ITERATOR))