Kotlin Facet: Get rid of copy constructors for compiler arguments/settings and use reflection-based copying instead

This commit is contained in:
Alexey Sedunov
2016-10-27 15:52:41 +03:00
parent a2948a624f
commit d0de9dd43c
7 changed files with 89 additions and 115 deletions
@@ -20,7 +20,6 @@ import com.intellij.util.SmartList;
import com.sampullara.cli.Argument;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.List;
public abstract class CommonCompilerArguments {
@@ -73,44 +72,11 @@ public abstract class CommonCompilerArguments {
public List<String> unknownExtraFlags = new SmartList<String>();
public CommonCompilerArguments() {
}
protected CommonCompilerArguments(CommonCompilerArguments arguments) {
this.languageVersion = arguments.languageVersion;
this.apiVersion = arguments.apiVersion;
this.suppressWarnings = arguments.suppressWarnings;
this.verbose = arguments.verbose;
this.version = arguments.version;
this.help = arguments.help;
this.extraHelp = arguments.extraHelp;
this.noInline = arguments.noInline;
this.repeat = arguments.repeat;
this.pluginClasspaths = arguments.pluginClasspaths != null ? Arrays.copyOf(arguments.pluginClasspaths, arguments.pluginClasspaths.length) : null;
this.pluginOptions = arguments.pluginOptions != null ? Arrays.copyOf(arguments.pluginOptions, arguments.pluginOptions.length) : null;
this.freeArgs.addAll(arguments.freeArgs);
this.unknownExtraFlags.addAll(arguments.unknownExtraFlags);
}
public abstract CommonCompilerArguments copy();
@NotNull
public String executableScriptFileName() {
return "kotlinc";
}
// Used only for serialize and deserialize settings. Don't use in other places!
public static final class DummyImpl extends CommonCompilerArguments {
public DummyImpl() {
}
public DummyImpl(DummyImpl arguments) {
super(arguments);
}
@Override
public CommonCompilerArguments copy() {
return new DummyImpl(this);
}
}
public static final class DummyImpl extends CommonCompilerArguments {}
}
@@ -20,8 +20,6 @@ import com.sampullara.cli.Argument;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import static org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants.CALL;
import static org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants.NO_CALL;
@@ -75,29 +73,6 @@ public class K2JSCompilerArguments extends CommonCompilerArguments {
@ValueDescription("<path>")
public String outputPostfix;
public K2JSCompilerArguments() {
}
private K2JSCompilerArguments(K2JSCompilerArguments arguments) {
super(arguments);
this.outputFile = arguments.outputFile;
this.noStdlib = arguments.noStdlib;
this.libraryFiles = arguments.libraryFiles != null ? Arrays.copyOf(arguments.libraryFiles, arguments.libraryFiles.length) : null;
this.sourceMap = arguments.sourceMap;
this.metaInfo = arguments.metaInfo;
this.kjsm = arguments.kjsm;
this.target = arguments.target;
this.moduleKind = arguments.moduleKind;
this.main = arguments.main;
this.outputPrefix = arguments.outputPrefix;
this.outputPostfix = arguments.outputPostfix;
}
@Override
public K2JSCompilerArguments copy() {
return new K2JSCompilerArguments(this);
}
@Override
@NotNull
public String executableScriptFileName() {
@@ -110,12 +110,6 @@ public class K2JVMCompilerArguments extends CommonCompilerArguments {
// Paths to output directories for friend modules.
public String[] friendPaths;
@Override
public CommonCompilerArguments copy() {
// No need to copy these arguments yet
throw new UnsupportedOperationException();
}
@Override
@NotNull
public String executableScriptFileName() {
@@ -0,0 +1,82 @@
/*
* 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.cli.common.arguments
import com.intellij.util.xmlb.XmlSerializerUtil
import java.lang.reflect.Field
import java.lang.reflect.Modifier
import java.util.*
fun <T : Any> copyBean(bean: T) = copyFields(bean, bean.javaClass.newInstance(), true)
fun <From : Any, To : From> mergeBeans(from: From, to: To): To {
// TODO: rewrite when updated version of com.intellij.util.xmlb is available on TeamCity
return copyFields(from, XmlSerializerUtil.createCopy(to), false)
}
private fun <From : Any, To : From> copyFields(from: From, to: To, deepCopyWhenNeeded: Boolean = false): To {
val fromFields = collectFieldsToCopy(from.javaClass)
for (fromField in fromFields) {
val toField = to.javaClass.getField(fromField.name)
val fromValue = fromField.get(from)
toField.set(to, if (deepCopyWhenNeeded) fromValue?.copyValueIfNeeded() else fromValue)
}
return to
}
private fun Any.copyValueIfNeeded(): Any {
@Suppress("UNCHECKED_CAST")
return when (this) {
is ByteArray -> Arrays.copyOf(this, size)
is CharArray -> Arrays.copyOf(this, size)
is ShortArray -> Arrays.copyOf(this, size)
is IntArray -> Arrays.copyOf(this, size)
is LongArray -> Arrays.copyOf(this, size)
is FloatArray -> Arrays.copyOf(this, size)
is DoubleArray -> Arrays.copyOf(this, size)
is BooleanArray -> Arrays.copyOf(this, size)
is Array<*> -> Array(size) { this[it]?.copyValueIfNeeded() }
is MutableCollection<*> -> (this as Collection<Any?>).mapTo(javaClass.newInstance() as MutableCollection<Any?>) { it?.copyValueIfNeeded() }
is MutableMap<*, *> -> (javaClass.newInstance() as MutableMap<Any?, Any?>).apply {
for ((k, v) in this@copyValueIfNeeded.entries) {
put(k?.copyValueIfNeeded(), v?.copyValueIfNeeded())
}
}
else -> this
}
}
private fun collectFieldsToCopy(clazz: Class<*>): List<Field> {
val fromFields = ArrayList<Field>()
var currentClass: Class<*>? = clazz
while (currentClass != null) {
for (field in currentClass.declaredFields) {
val modifiers = field.modifiers
if (!Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) {
fromFields.add(field)
}
}
currentClass = currentClass.superclass
}
return fromFields
}