Create from Usage: Java -> Kotlin interoperability
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index cd9fdec..8dff411 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -628,6 +628,10 @@
         order="first"
         implementationClass="org.jetbrains.kotlin.idea.refactoring.pullUp.JavaToKotlinPullUpHelperFactory"/>
 
+    <psi.jvmElementMutatorFactory
+        language="kotlin"
+        implementationClass="org.jetbrains.kotlin.idea.KotlinJVMElementMutatorFactory"/>
+
     <problemFileHighlightFilter implementation="org.jetbrains.kotlin.idea.projectView.KotlinProblemFileHighlightFilter"/>
 
     <codeInsight.typeInfo language="kotlin" implementationClass="org.jetbrains.kotlin.idea.codeInsight.KotlinExpressionTypeProvider"/>
diff --git a/idea/src/com/intellij/codeInsight/daemon/impl/quickfix/JVMElementMutatorWithEditor.java b/idea/src/com/intellij/codeInsight/daemon/impl/quickfix/JVMElementMutatorWithEditor.java
new file mode 100644
index 0000000..a8048aa
--- /dev/null
+++ b/idea/src/com/intellij/codeInsight/daemon/impl/quickfix/JVMElementMutatorWithEditor.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2010-2016 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.openapi.editor.Editor;
+import com.intellij.psi.JVMElementMutator;
+import com.intellij.psi.PsiElement;
+
+public interface JVMElementMutatorWithEditor extends JVMElementMutator {
+  Editor openEditor(PsiElement element);
+}
\ No newline at end of file
diff --git a/idea/src/com/intellij/psi/JVMElementMutator.java b/idea/src/com/intellij/psi/JVMElementMutator.java
new file mode 100644
index 0000000..42d6654
--- /dev/null
+++ b/idea/src/com/intellij/psi/JVMElementMutator.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010-2016 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.psi;
+
+import com.intellij.openapi.Disposable;
+import org.jetbrains.annotations.NotNull;
+
+public interface JVMElementMutator extends Disposable {
+  @NotNull
+  JVMElementFactory getFactory();
+
+  @NotNull
+  PsiElement getRootView();
+
+  void synchronize();
+}
\ No newline at end of file
diff --git a/idea/src/com/intellij/psi/JVMElementMutatorFactory.java b/idea/src/com/intellij/psi/JVMElementMutatorFactory.java
new file mode 100644
index 0000000..5a5a128
--- /dev/null
+++ b/idea/src/com/intellij/psi/JVMElementMutatorFactory.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2010-2016 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.psi;
+
+import com.intellij.lang.LanguageExtension;
+import com.intellij.openapi.editor.Editor;
+import org.jetbrains.annotations.NotNull;
+
+public interface JVMElementMutatorFactory {
+  LanguageExtension<JVMElementMutatorFactory> EXTENSION_POINT =
+    new LanguageExtension<JVMElementMutatorFactory>("com.intellij.psi.jvmElementMutatorFactory");
+
+  JVMElementMutator createMutator(@NotNull PsiElement rootElement, @NotNull Editor editor);
+}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/KotlinJVMElementMutator.kt b/idea/src/org/jetbrains/kotlin/idea/KotlinJVMElementMutator.kt
new file mode 100644
index 0000000..7ce60e41
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/KotlinJVMElementMutator.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2010-2016 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.kotlin.idea
+
+import com.intellij.codeInsight.daemon.impl.quickfix.JVMElementMutatorWithEditor
+import com.intellij.ide.highlighter.JavaFileType
+import com.intellij.openapi.editor.Editor
+import com.intellij.pom.java.LanguageLevel
+import com.intellij.psi.*
+import com.intellij.psi.search.GlobalSearchScope
+import com.intellij.testFramework.LightVirtualFile
+import org.jetbrains.kotlin.asJava.KtLightClassForExplicitDeclaration
+import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.DialogWithTemplateEditor
+import org.jetbrains.kotlin.idea.refactoring.createJavaClass
+import org.jetbrains.kotlin.idea.util.DialogWithEditor
+
+abstract class KotlinJVMElementFactoryBase : JVMElementFactory {
+    override fun createDocCommentFromText(name: String) = throw UnsupportedOperationException()
+
+    override fun createInterface(name: String) = throw UnsupportedOperationException()
+
+    override fun createPrimitiveType(name: String) = throw UnsupportedOperationException()
+
+    override fun createExpressionFromText(text: String, context: PsiElement?) = throw UnsupportedOperationException()
+
+    override fun isValidMethodName(name: String) = throw UnsupportedOperationException()
+
+    override fun createMethodFromText(text: String?, context: PsiElement?) = throw UnsupportedOperationException()
+
+    override fun createType(klass: PsiClass) = throw UnsupportedOperationException()
+
+    override fun createType(klass: PsiClass, substitutor: PsiSubstitutor) = throw UnsupportedOperationException()
+
+    override fun createType(klass: PsiClass, substitutor: PsiSubstitutor, level: LanguageLevel) = throw UnsupportedOperationException()
+
+    override fun createType(klass: PsiClass, substitutor: PsiSubstitutor, level: LanguageLevel, annotations: Array<out PsiAnnotation>) =
+            throw UnsupportedOperationException()
+
+    override fun createType(klass: PsiClass, parameter: PsiType?) = throw UnsupportedOperationException()
+
+    override fun createType(klass: PsiClass, vararg parameters: PsiType?) = throw UnsupportedOperationException()
+
+    override fun createField(name: String, type: PsiType) = throw UnsupportedOperationException()
+
+    override fun createParameterList(names: Array<out String>, types: Array<out PsiType>) = throw UnsupportedOperationException()
+
+    override fun createClass(name: String) = throw UnsupportedOperationException()
+
+    override fun createEnum(name: String) = throw UnsupportedOperationException()
+
+    override fun createClassInitializer() = throw UnsupportedOperationException()
+
+    override fun createConstructor() = throw UnsupportedOperationException()
+
+    override fun createConstructor(name: String) = throw UnsupportedOperationException()
+
+    override fun createConstructor(name: String, context: PsiElement?) = throw UnsupportedOperationException()
+
+    override fun createRawSubstitutor(owner: PsiTypeParameterListOwner) = throw UnsupportedOperationException()
+
+    override fun isValidLocalVariableName(name: String) = throw UnsupportedOperationException()
+
+    override fun createAnnotationFromText(text: String, context: PsiElement?) = throw UnsupportedOperationException()
+
+    override fun createReferenceElementByType(type: PsiClassType?) = throw UnsupportedOperationException()
+
+    override fun isValidFieldName(name: String) = throw UnsupportedOperationException()
+
+    override fun createTypeParameterList() = throw UnsupportedOperationException()
+
+    override fun createTypeByFQClassName(fqName: String) = throw UnsupportedOperationException()
+
+    override fun createTypeByFQClassName(fqName: String, searchScope: GlobalSearchScope) = throw UnsupportedOperationException()
+
+    override fun createParameter(name: String, type: PsiType?) = throw UnsupportedOperationException()
+
+    override fun createParameter(name: String, type: PsiType?, context: PsiElement?) = throw UnsupportedOperationException()
+
+    override fun createTypeParameter(name: String?, superTypes: Array<out PsiClassType>?) = throw UnsupportedOperationException()
+
+    override fun createSubstitutor(substitutionMap: MutableMap<PsiTypeParameter, PsiType>) = throw UnsupportedOperationException()
+
+    override fun createMethod(name: String, returnType: PsiType?) = throw UnsupportedOperationException()
+
+    override fun createMethod(name: String, returnType: PsiType?, context: PsiElement?) = throw UnsupportedOperationException()
+
+    override fun isValidClassName(name: String) = throw UnsupportedOperationException()
+
+    override fun isValidParameterName(name: String) = throw UnsupportedOperationException()
+
+    override fun createAnnotationType(name: String) = throw UnsupportedOperationException()
+}
+
+class KotlinJVMElementMutator private constructor (
+        private val rootElement: PsiElement,
+        private val rootElementView: PsiElement,
+        private val mainEditor: Editor
+) : JVMElementMutatorWithEditor {
+    private var isDisposed = false
+
+    private val elementFactory: JVMElementFactory by lazy {
+        checkNotDisposed()
+        object : KotlinJVMElementFactoryBase() {
+
+        }
+    }
+
+    private val dialogWithEditor: DialogWithEditor
+
+    init {
+        dialogWithEditor = DialogWithTemplateEditor(rootElement.project, mainEditor, "Create from usage") {
+            LightVirtualFile("dummy.java", JavaFileType.INSTANCE, "")
+        }.apply {
+            psiFile!!.add(rootElementView)
+        }
+    }
+
+    private fun checkNotDisposed() {
+        if (isDisposed) throw IllegalStateException("Mutator is already disposed")
+    }
+
+    override fun getFactory() = elementFactory
+
+    override fun getRootView(): PsiElement = rootElementView
+
+    override fun openEditor(element: PsiElement?): Editor? {
+        checkNotDisposed()
+        if (!dialogWithEditor.isVisible) {
+            dialogWithEditor.show()
+        }
+        return dialogWithEditor.editor
+    }
+
+    override fun synchronize() {
+        checkNotDisposed()
+    }
+
+    override fun dispose() {
+        isDisposed = true
+    }
+
+    companion object {
+        fun create(rootElement: PsiElement, editor: Editor): KotlinJVMElementMutator? {
+            val elementView = when (rootElement) {
+                is KtLightClassForExplicitDeclaration -> createJavaClass(rootElement.getOrigin(), null)
+                else -> return null
+            }
+            return KotlinJVMElementMutator(rootElement, elementView, editor)
+        }
+    }
+}
+
+class KotlinJVMElementMutatorFactory : JVMElementMutatorFactory {
+    override fun createMutator(rootElement: PsiElement, editor: Editor) = KotlinJVMElementMutator.create(rootElement, editor)
+}
diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt
index c9cb3d7..26e4cdd 100644
--- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt
@@ -20,7 +20,6 @@
 import com.intellij.codeInsight.navigation.NavigationUtil
 import com.intellij.codeInsight.template.*
 import com.intellij.codeInsight.template.impl.TemplateImpl
-import com.intellij.codeInsight.template.impl.TemplateManagerImpl
 import com.intellij.ide.fileTemplates.FileTemplate
 import com.intellij.ide.fileTemplates.FileTemplateManager
 import com.intellij.openapi.application.ApplicationManager
@@ -33,6 +32,7 @@
 import com.intellij.psi.*
 import com.intellij.psi.codeStyle.JavaCodeStyleManager
 import com.intellij.psi.util.PsiTreeUtil
+import com.intellij.testFramework.LightVirtualFile
 import com.intellij.util.IncorrectOperationException
 import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
 import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
@@ -42,6 +42,7 @@
 import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor
 import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
 import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl
+import org.jetbrains.kotlin.idea.KotlinFileType
 import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult
 import org.jetbrains.kotlin.idea.caches.resolve.getJavaClassDescriptor
 import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
@@ -53,7 +54,6 @@
 import org.jetbrains.kotlin.idea.util.DialogWithEditor
 import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
 import org.jetbrains.kotlin.idea.util.ShortenReferences
-import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
 import org.jetbrains.kotlin.idea.util.application.runWriteAction
 import org.jetbrains.kotlin.incremental.components.NoLookupLocation
 import org.jetbrains.kotlin.lexer.KtTokens
@@ -273,20 +273,11 @@
                 }
                 dialogWithEditor = null
             } else {
-                val dialog = object: DialogWithEditor(project, "Create from usage", "") {
-                    override fun doOKAction() {
-                        project.executeWriteCommand("Premature end of template") {
-                            TemplateManagerImpl.getTemplateState(editor)?.gotoEnd(false)
-                        }
-                        super.doOKAction()
-                    }
+                val dialog = DialogWithTemplateEditor(project, config.currentEditor!!, "Create from usage") {
+                    LightVirtualFile("dummy.kt", KotlinFileType.INSTANCE, "")
                 }
                 containingFileEditor = dialog.editor
-                with(containingFileEditor.settings) {
-                    additionalColumnsCount = config.currentEditor!!.settings.getRightMargin(project)
-                    additionalLinesCount = 5
-                }
-                jetFileToEdit = PsiDocumentManager.getInstance(project).getPsiFile(containingFileEditor.document) as KtFile
+                jetFileToEdit = dialog.psiFile as KtFile
                 jetFileToEdit.analysisContext = config.currentFile
                 dialogWithEditor = dialog
             }
diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/DialogWithTemplateEditor.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/DialogWithTemplateEditor.kt
new file mode 100644
index 0000000..1004dbc
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/DialogWithTemplateEditor.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2010-2016 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.kotlin.idea.quickfix.createFromUsage.callableBuilder
+
+import com.intellij.codeInsight.template.impl.TemplateManagerImpl
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.vfs.VirtualFile
+import org.jetbrains.kotlin.idea.util.DialogWithEditor
+import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
+
+class DialogWithTemplateEditor(
+        project: Project,
+        mainEditor: Editor,
+        title: String,
+        createFile: () -> VirtualFile
+): DialogWithEditor(project, title, createFile) {
+    init {
+        with(editor.settings) {
+            additionalColumnsCount = mainEditor.settings.getRightMargin(project)
+            additionalLinesCount = 5
+        }
+    }
+
+    override fun doOKAction() {
+        project.executeWriteCommand("Premature end of template") {
+            TemplateManagerImpl.getTemplateState(editor)?.gotoEnd(false)
+        }
+        super.doOKAction()
+    }
+}
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt
index e67b997..e510e9a 100644
--- a/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt
@@ -537,13 +537,13 @@
     return field
 }
 
-fun createJavaClass(klass: KtClass, targetClass: PsiClass?, forcePlainClass: Boolean = false): PsiClass {
+fun createJavaClass(klass: KtClassOrObject, targetClass: PsiClass?, forcePlainClass: Boolean = false): PsiClass {
     val kind = if (forcePlainClass) ClassKind.CLASS else (klass.resolveToDescriptor() as ClassDescriptor).kind
 
     val factory = PsiElementFactory.SERVICE.getInstance(klass.project)
     val className = klass.name!!
     val javaClassToAdd = when (kind) {
-        ClassKind.CLASS -> factory.createClass(className)
+        ClassKind.CLASS, ClassKind.OBJECT -> factory.createClass(className)
         ClassKind.INTERFACE -> factory.createInterface(className)
         ClassKind.ANNOTATION_CLASS -> factory.createAnnotationType(className)
         ClassKind.ENUM_CLASS -> factory.createEnum(className)
diff --git a/idea/src/org/jetbrains/kotlin/idea/util/DialogWithEditor.kt b/idea/src/org/jetbrains/kotlin/idea/util/DialogWithEditor.kt
index 3a09fc9..891c28e 100644
--- a/idea/src/org/jetbrains/kotlin/idea/util/DialogWithEditor.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/util/DialogWithEditor.kt
@@ -27,13 +27,14 @@
 import com.intellij.openapi.fileEditor.*
 import com.intellij.openapi.editor.ex.*
 import com.intellij.openapi.editor.colors.*
+import com.intellij.openapi.vfs.VirtualFile
 import java.awt.event.*
 import com.intellij.psi.*
 
 open class DialogWithEditor(
         val project: Project,
         title: String,
-        val initialText: String
+        private val createFile: () -> VirtualFile
 ) : DialogWrapper(project, true) {
     val editor: Editor = createEditor()
 
@@ -44,7 +45,7 @@
 
     private fun createEditor(): Editor {
         val editorFactory = EditorFactory.getInstance()!!
-        val virtualFile = LightVirtualFile("dummy.kt", KotlinFileType.INSTANCE, initialText)
+        val virtualFile = createFile()
         val document = FileDocumentManager.getInstance().getDocument(virtualFile)!!
 
         val editor = editorFactory.createEditor(document, project, KotlinFileType.INSTANCE, false)
@@ -79,4 +80,7 @@
         super.dispose()
         EditorFactory.getInstance()!!.releaseEditor(editor)
     }
+
+    val psiFile: PsiFile?
+        get() = PsiDocumentManager.getInstance(project).getPsiFile(editor.document)
 }