小弟不才,去年学习 flutter 的过程中,搞过几个小玩具,其中就有一个将 json 数据转换成 dart class 的 idea 插件。
gayhub 传送门
不想点的朋友可以看一下 gif
半年多来,随着 flutter 的火热,github 上也有了那么几十个 star。后来我也发布到了全家桶插件官网。陆陆续续也有几千个的下载量了。对于我这个小透明来说,还是挺开心的,不管写得有多烂,总归是有人在用,总归是为 flutter 的生态贡献了自己的一点绵薄之力。
然而就在今天,当我想去看看那些下载量更高的同类项目的时候,很不凑巧的就让我发现了这么一个作品:
先上一张他的 gif 吧
敏感的我,一下子就感觉到了有点不对劲,于是我点开了他的源码开始查看,有如下收获:
1.ClassGenerator 类名一样的
2.NameValuePair/NamePair 极其相似,而且讲道理,NamePair 这个命名很不自然
3.Param 类名一样
4.Util/Utils 基本没差别
没耐心的朋友可以不用看代码,我以我的人格担保,最后的结论是没错的
class ClassGenerator(private val generateComments: Boolean, private val ignoreEmptyOrNull: Boolean) {
val classes = mutableMapOf<String, List<Param>>()
fun generate(name: String, string: String): String {
return try {
val parseResult = JsonParser().parse(string)
val json: JsonObject? = if (parseResult.isJsonObject) {
parseResult.asJsonObject
} else if (parseResult.isJsonArray) {
parseResult.asJsonArray[0].asJsonObject
} else null
val fields = Param.json2Params(json)
"class $name {/n${printClassWithParams(fields, 2, name)}/n}/n${buildClasses()}"
} catch (jsonParseException: JsonParseException) {
jsonParseException.printStackTrace()
"error: not supported json"
} catch (illegalStateException: IllegalStateException) {
illegalStateException.printStackTrace()
if (illegalStateException.message?.startsWith("Not a JSON Object") == true) {
"error: not supported json"
} else {
"error: unknown"
}
}
}
private fun printClassWithParams(params: List<Param>, space: Int, className: String): String {
val commentSb = StringBuilder()
val sb = StringBuilder()
val tempClasses = HashMap<String, List<Param>>() // 统计子类
var spaceStr = ""
repeat(space) { spaceStr += " " }
val commentPrefix = "$spaceStr *"
fun List<Param>.insertComment(): List<Param> {
return if (generateComments) this.map {
commentSb.append(commentPrefix).append(" ${it.comment}/n")
it
} else this
}
/* 基本类型参数声明与统计 **/
val orderedList = params
.filter { it.key == "String" || it.key == "int" || it.key == "double" || it.key == "bool" || (!ignoreEmptyOrNull && it.key == "dynamic") }
.sortedBy { it.key }
.insertComment()
.map {
sb.append("$spaceStr${it.key} ${it.value};/n")
it.value
}
/* 对象类型参数声明与统计 **/
val objectList = params
.filter { it.key == "object" }
.sortedBy { it.value }
.insertComment()
.map {
val clazzName = Util.toUpperCaseFirstOne(it.value + "Bean")
classes[clazzName] = it.clazz
tempClasses[clazzName] = it.clazz
sb.append(spaceStr).append(clazzName).append(" ").append(it.value).append(";").append("/n")
NameValuePair(clazzName, it.value)
}
/* 基本类型 list 参数声明与统计 **/
val listBaseList = params
.filter { it.key.startsWith("List<") }
.filterNot { it.key.contains("null") }
.sortedBy { it.value }
.insertComment()
.map {
sb.append(spaceStr).append(it.key).append(" ").append(it.value).append(";").append("/n")
NameValuePair(it.key, it.value)
}
/* 对象类型 list 参数声明与统计 **/
val listList = params
.filter { "list" == it.key }
.filter { it.clazz != null }
.sortedBy { it.value }
.insertComment()
.map {
val clazzName = Util.toUpperCaseFirstOne(it.value + "ListBean")
classes[clazzName] = it.clazz
tempClasses[clazzName] = it.clazz
sb.append(spaceStr).append("List<").append(clazzName).append(">").append(" ").append(it.value).append(";").append("/n")
NameValuePair(clazzName, it.value)
}
/* dynamic list **/
var dynamiclist: List<NameValuePair>? = null
if (!ignoreEmptyOrNull) {
dynamiclist = params
.filter { it.key == "dynamicList" }
.sortedBy { it.value }
.insertComment()
.map {
sb.append(spaceStr).append("List<dynamic>").append(" ").append(it.value).append(";").append("/n")
NameValuePair(it.key, it.value)
}
}
val tempSpaceStr = "$spaceStr "
/* map.value 转换为对象的静态函数 start **/
val fieldName = Util.toLowerCaseFirstOne(className)
sb.append("/n").append(spaceStr)
.append("static ").append(className).append(" fromMap").append("(Map<String, dynamic> map) {")
.append("/n").append(tempSpaceStr)
.append(className).append(" ").append(fieldName).append(" = new ").append(className).append("();")
orderedList.forEach {
sb.append("/n").append(tempSpaceStr).append(fieldName).append(".").append(it).append(" = ").append("map['").append(it).append("'];")
}
dynamiclist?.forEach {
sb.append("/n").append(tempSpaceStr).append(fieldName).append(".").append(it.value).append(" = ").append("map['").append(it.value).append("'];");
}
objectList.forEach {
sb.append("/n").append(tempSpaceStr).append(fieldName).append(".").append(it.value).append(" = ").append(it.name).append(".fromMap(map['").append(it.value).append("']);")
}
listList.forEach {
sb.append("/n").append(tempSpaceStr).append(fieldName).append(".").append(it.value).append(" = ").append(it.name).append(".fromMapList(map['").append(it.value).append("']);")
}
/* map.value 转换为基础类型 list start **/
if (listBaseList.isNotEmpty())
sb.append("/n")
for ((count, pair) in listBaseList.withIndex()) {
sb.append("/n").append(tempSpaceStr).append("List<dynamic> dynamicList").append(count).append(" = map['").append(pair.value).append("'] ?? [];")
sb.append("/n").append(tempSpaceStr).append(fieldName).append(".").append(pair.value).append(" = new List();")
var function = "o.toString()"
when (pair.name) {
"List<int>" -> function = "int.parse(o.toString())"
"List<double>" -> function = "double.parse(o.toString())"
"List<bool>" -> function = "o.toString() == 'true'"
}
sb.append("/n").append(tempSpaceStr).append(fieldName).append(".").append(pair.value).append(".addAll(dynamicList").append(count).append(".map((o) => ").append(function).append("));")
sb.append("/n")
}
/* map.value 转换为基础类型 list end **/
sb.append("/n").append(tempSpaceStr).append("return ").append(fieldName).append(";/n")
sb.append(spaceStr).append("}/n")
/* map.value 转换为对象的静态函数 end **/
/* map.value 转换为 list 的静态函数 start **/
sb.append("/n").append(spaceStr)
.append("static ").append("List<").append(className).append(">").append(" fromMapList").append("(dynamic mapList) {")
.append("/n").append(tempSpaceStr).append("if (mapList == null) return [];")
.append("/n").append(tempSpaceStr).append("List<").append(className).append("> list = new List(mapList.length);")
.append("/n").append(tempSpaceStr).append("for (int i = 0; i < mapList.length; i++) {")
.append("/n").append(tempSpaceStr).append(" ").append("list[i] = fromMap(mapList[i]);")
.append("/n").append(tempSpaceStr).append("}")
.append("/n").append(tempSpaceStr).append("return list;")
.append("/n").append(spaceStr).append("}")
.append("/n")
/* map.value 转换为 list 的静态函数 end **/
// 遍历类中类(主要目的是添加进 classes 统计,而不 append)
// 还是由于 dart 不支持内部类导致的
tempClasses.forEach { key, value ->
printClassWithParams(value, space + 2, key)
}
val commentString = if (commentSb.toString().isBlank() || commentSb.isEmpty()) {
""
} else {
"/n$spaceStr/**/n$commentSb$spaceStr *//n/n"
}
return "$commentString$sb"
}
private fun buildClasses(): String {
val sb = StringBuilder()
// 开始定义类
classes.forEach { key, value ->
sb.append("/n")
.append("class").append(" ").append(key).append(" ").append("{").append("/n")
.append(printClassWithParams(value, 2, key))
.append("}").append("/n")
}
return sb.toString()
}
}
复制代码
class ClassGenerator {
val classes = mutableMapOf<String, List<Param>>()
fun generate(name: String, jsonText: String): String {
return try {
val fields = Param.json2Params(JsonParser().parse(jsonText).asJsonObject)
"class $name {/n${printClassWithParams(fields, 2, name)}/n}/n${buildClasses()}"
} catch (jsonParseException: JsonParseException) {
jsonParseException.printStackTrace()
"error: not supported json"
} catch (illegalStateException: IllegalStateException) {
illegalStateException.printStackTrace()
if (illegalStateException.message?.startsWith("Not a JSON Object") == true) {
"error: not supported json"
} else {
"error: unknown"
}
}
}
fun generate(name: String, fields: JsonObject): String {
return try {
"class $name {/n${printClassWithParams(Param.json2Params(fields), 2, name)}/n}/n${buildClasses()}"
} catch (jsonParseException: JsonParseException) {
jsonParseException.printStackTrace()
"error: not supported json"
} catch (illegalStateException: IllegalStateException) {
illegalStateException.printStackTrace()
if (illegalStateException.message?.startsWith("Not a JSON Object") == true) {
"error: not supported json"
} else {
"error: unknown"
}
}
}
private fun printClassWithParams(params: List<Param>, space: Int, className: String): String {
val sb = StringBuilder()
val tempClasses = HashMap<String, List<Param>>()
var spaceStr = ""
repeat(space) { spaceStr += " " }
val orderedList = params
.filter { it.key == "String" || it.key == "int" || it.key == "double" || it.key == "bool" || it.key == "num" }
.sortedBy { it.key }
.map {
sb.append("$spaceStr${it.key} ${it.camelWord};/n")
NameValuePair(it.value, it.camelWord)
}
val objectList = params
.filter { it.key == "object" }
.sortedBy { it.value }
.map {
val clazzName = Utils.toUpperCaseFirstOne(it.value + "Bean")
classes[clazzName] = it.clazz
tempClasses[clazzName] = it.clazz
sb.append(spaceStr).append(clazzName).append(" ").append(it.camelWord).append(";").append("/n")
NamePair(it.camelWord, clazzName, it.value)
}
val listBaseList = params
.filter { it.key.startsWith("List<") }
.sortedBy { it.value }
.map {
sb.append(spaceStr).append(it.key).append(" ").append(it.camelWord).append(";").append("/n")
NamePair(it.camelWord, it.key, it.value)
}
val listList = params
.filter { "list" == it.key }
.sortedBy { it.value }
.map {
val clazzName = Utils.toUpperCaseFirstOne(it.value + "ListBean")
classes[clazzName] = it.clazz
tempClasses[clazzName] = it.clazz
sb.append(spaceStr).append("List<").append(clazzName).append(">").append(" ").append(it.camelWord).append(";").append("/n")
NamePair(it.camelWord, clazzName, it.value)
}
val tempSpaceStr = "$spaceStr "
/**
* 构造
*/
sb.append("/n").append(spaceStr)
.append(className).append("({")
orderedList.forEach {
sb.append("this").append(".").append(it.value)
sb.append(", ")
}
objectList.forEach {
sb.append("this").append(".").append(it.camelKey)
sb.append(", ")
}
listList.forEach {
sb.append("this").append(".").append(it.camelKey)
sb.append(", ")
}
listBaseList.forEach {
sb.append("this").append(".").append(it.camelKey)
sb.append(", ")
}
if (sb.endsWith(", ")) {
sb.delete(sb.lastIndexOf(", "), sb.length)
}
sb.append("});/n")
sb.append("/n").append(spaceStr)
.append(className).append(".fromJson").append("(Map<String, dynamic> json) {")
.append(tempSpaceStr)
orderedList.forEach {
sb.append("/n").append(tempSpaceStr).append("this").append(".").append(it.value).append(" = ").append("json['").append(it.name).append("'];")
}
objectList.forEach {
sb.append("/n").append(tempSpaceStr).append("this").append(".").append(it.camelKey).append(" = ").append("json['").append(it.value).append("'] != null? ").append(it.key).append(".fromJson(json['").append(it.value).append("']) : null;")
}
listList.forEach {
sb.append("/n").append(tempSpaceStr).append("this").append(".").append(it.camelKey).append(" = ").append("(json['").append(it.value).append("'] as List)!=null?(json['").append(it.value).append("'] as List).map((i) => ").append(it.key).append(".fromJson(i)).toList():null;")
}
listBaseList.forEach {
sb.append("/n")
sb.append("/n").append(tempSpaceStr).append("List<dynamic> ").append(it.camelKey).append("List").append(" = json['").append(it.value).append("'];")
sb.append("/n").append(tempSpaceStr).append("this").append(".").append(it.camelKey).append(" = new List();")
var function = "o.toString()"
when (it.key) {
"List<int>" -> function = "int.parse(o.toString())"
"List<double>" -> function = "double.parse(o.toString())"
"List<bool>" -> function = "o.toString() == 'true'"
}
sb.append("/n").append(tempSpaceStr).append("this").append(".").append(it.camelKey).append(".addAll(").append(it.camelKey).append("List").append(".map((o) => ").append(function).append("));")
}
sb.append("/n").append(spaceStr).append("}/n/n")
sb.append(spaceStr)
.append("Map<String, dynamic> toJson() {/n").append(tempSpaceStr).append("final Map<String, dynamic> data = new Map<String, dynamic>();")
orderedList.forEach {
sb.append("/n").append(tempSpaceStr).append("data['").append(it.name).append("'] = ").append("this").append(".").append(it.value).append(";")
}
objectList.forEach {
sb.append("/n").append(tempSpaceStr).append("data['").append(it.value).append("'] = ").append("this").append(".").append(it.camelKey).append(".toJson();")
}
listList.forEach {
sb.append("/n").append(tempSpaceStr).append("data['").append(it.value).append("'] = ").append("this").append(".").append(it.camelKey).append(" != null?this.").append(it.camelKey).append(".map((i) => i.toJson()).toList():null;")
}
listBaseList.forEach {
sb.append("/n").append(tempSpaceStr).append("data['").append(it.value).append("'] = ").append("this").append(".").append(it.camelKey).append(";")
}
sb.append("/n").append(tempSpaceStr).append("return data;/n")
sb.append(spaceStr).append("}/n")
tempClasses.forEach { key, value ->
printClassWithParams(value, space + 2, key)
}
return sb.toString()
}
private fun buildClasses(): String {
val sb = StringBuilder()
classes.forEach { key, value ->
sb.append("/n")
.append("class").append(" ").append(key).append(" ").append("{").append("/n")
.append(printClassWithParams(value, 2, key))
.append("}").append("/n")
}
return sb.toString()
}
}
复制代码
是不是一个模子刻出来的?大量的函数名、属性名都是一致的,换行空行也都是一致的
由于我已经将 NameValuePair 用 kotlin 改写了,下面的代码是从 git 提交记录里找到的
/**
* Created by zhangll on 2018/8/3.
*/
public class NameValuePair {
String name;
String value;
public NameValuePair(String name, String value) {
this.name = name;
this.value = value;
}
}
复制代码
public class NamePair {
private String camelKey;
private String key;
private String value;
public NamePair(String camelKey, String key, String value) {
this.camelKey = camelKey;
this.key = key;
this.value = value;
}
public String getCamelKey() {
return camelKey;
}
public void setCamelKey(String camelKey) {
this.camelKey = camelKey;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
复制代码
乍一看似乎不一样,其实他只是多写了 getter setter,又加了一个驼峰命名功能的字段。我怎么看出来一致的呢?因为 NameValuePair 严格来说,就应该给属性命名 name 和 value。而另一个人的类名叫 NamePair,中间偏偏少了个 Value ,属性命名却又是 key(不是理论上的 name)、value,这是为什么呢?明眼人应该一看就知道了。
/**
* Created by zhangll on 2018/8/3.
*/
public class Param {
String key;
String value;
List<Param> clazz;
String comment;
/**
*
* @param key 变量名
* @param object 变量内容
* @return
*/
public static Param makeParam(String key, Object object) {
if (object == null || "null".equals(object.toString())) {
return new Param("dynamic", key, null, object);
}
if (object instanceof JsonObject) {
JsonObject jsonObject = (JsonObject) object;
return new Param("object", key, json2Params(jsonObject), jsonObject);
} else if (object instanceof JsonArray) {
JsonArray jsonArray = (JsonArray) object;
if (jsonArray.size() != 0) {
Object obj = jsonArray.get(0);
if (obj instanceof JsonObject) {
return new Param("list", key, json2Params(jsonArray.get(0).getAsJsonObject()), jsonArray);
} else if (obj instanceof JsonArray) {
return new Param("dynamicList", key, null, object);
} else {
Param temp = makeParam("placeholder", obj);
if (temp.key.equals("dynamic")) {
return new Param("dynamicList", key, null, object);
}
return new Param("List<" + temp.key + ">", key, null, object);
}
} else {
return new Param("dynamicList", key, null, object);
}
} else if (tryParseBoolean(object)) {
return new Param("bool", key, null, "true".equals(object.toString()));
} else if (tryParseInt(object)) {
return new Param("int", key, null, Integer.parseInt(object.toString()));
} else if (tryParseLong(object)) {
return new Param("int", key, null, Long.parseLong(object.toString()));
} else if (tryParseDouble(object)) {
return new Param("double", key, null, Double.parseDouble(object.toString()));
} else if (tryParseFloat(object)) {
return new Param("double", key, null, Float.parseFloat(object.toString()));
} else {
return new Param("String", key, null, object.toString());
}
}
public static List<Param> json2Params(JsonObject jsonObject) {
List<Param> list = new ArrayList<>();
for (Object o : jsonObject.entrySet()) {
Map.Entry entry = (Map.Entry) o;
list.add(Param.makeParam(entry.getKey().toString(), entry.getValue()));
}
return list;
}
private static boolean tryParseInt(Object object) {
try {
int i = Integer.parseInt(object.toString());
return true;
} catch (Exception e) {
return false;
}
}
private static boolean tryParseLong(Object object) {
try {
long i = Long.parseLong(object.toString());
return true;
} catch (Exception e) {
return false;
}
}
private static boolean tryParseDouble(Object object) {
try {
double d = Double.parseDouble(object.toString());
return true;
} catch (Exception e) {
return false;
}
}
private static boolean tryParseFloat(Object object) {
try {
float f = Float.parseFloat(object.toString());
return true;
} catch (Exception e) {
return false;
}
}
private static boolean tryParseBoolean(Object object) {
String b = object.toString();
return Objects.equals(b, "true") || Objects.equals(b, "false");
}
public Param(String key, String value, List<Param> clazz, Object content) {
this.key = key;
this.value = value;
this.clazz = clazz;
if (content == null) return;
// 注释处理
this.comment = value + " : " + content.toString().replaceAll("/n", "");
}
@Override
public String toString() {
return "Param{" +
"key='" + key + '/'' +
", value='" + value + '/'' +
", classes=" + clazz +
'}';
}
}
复制代码
public class Param {
String key;
String value;
String camelWord;
List<Param> clazz;
/**
*
* @param key 变量名
* @param object 变量内容
* @return
*/
public static Param makeParam(String key, Object object) {
if (object instanceof JsonObject) {
JsonObject jsonObject = (JsonObject) object;
return new Param("object", key, json2Params(jsonObject));
} else if (object instanceof JsonArray) {
JsonArray jsonArray = (JsonArray) object;
if (jsonArray.size() != 0) {
Object obj = jsonArray.get(0);
if (obj instanceof JsonObject) {
return new Param("list", key, json2Params(jsonArray.get(0).getAsJsonObject()));
} else {
Param temp = makeParam("placeholder", obj);
return new Param("List<" + temp.key + ">", key, null);
}
} else {
return new Param("list", key, null);
}
} else if (tryParseBoolean(object)) {
return new Param("bool", key, null);
} else if (tryParseInt(object)) {
return new Param("int", key, null);
} else if (tryParseLong(object)) {
return new Param("num", key, null);
} else if (tryParseDouble(object)) {
return new Param("double", key, null);
} else if (tryParseFloat(object)) {
return new Param("double", key, null);
} else {
return new Param("String", key, null);
}
}
public static List<Param> json2Params(JsonObject jsonObject) {
List<Param> list = new ArrayList<>();
for (Object o : jsonObject.entrySet()) {
Map.Entry entry = (Map.Entry) o;
list.add(Param.makeParam(entry.getKey().toString(), entry.getValue()));
}
return list;
}
private static boolean tryParseInt(Object object) {
try {
int i = Integer.parseInt(object.toString());
return true;
} catch (Exception e) {
return false;
}
}
private static boolean tryParseLong(Object object) {
try {
long i = Long.parseLong(object.toString());
return true;
} catch (Exception e) {
return false;
}
}
private static boolean tryParseDouble(Object object) {
try {
double d = Double.parseDouble(object.toString());
return true;
} catch (Exception e) {
return false;
}
}
private static boolean tryParseFloat(Object object) {
try {
float f = Float.parseFloat(object.toString());
return true;
} catch (Exception e) {
return false;
}
}
private static boolean tryParseBoolean(Object object) {
String b = object.toString();
return Objects.equals(b, "true") || Objects.equals(b, "false");
}
public Param(String key, String value, List<Param> clazz) {
this.key = key;
this.value = value;
this.camelWord = Utils.toUpperCaseParams(value);
this.clazz = clazz;
}
@Override
public String toString() {
return "Param{" +
"key='" + key + '/'' +
", value='" + value + '/'' +
", classes=" + clazz +
'}';
}
}
复制代码
又是一个模子刻出来的。除了我新增了注释功能和部分 bug 修复导致的代码变动,他新增了驼峰命名功能。
/**
* Created by zhangll on 2018/8/3.
*/
public class Util {
/**
* 将字符串复制到剪切板。
*/
public static void setSysClipboardText(String writeMe) {
Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable tText = new StringSelection(writeMe);
clip.setContents(tText, null);
}
// 首字母转大写
public static String toUpperCaseFirstOne(String s){
if(Character.isUpperCase(s.charAt(0)))
return s;
else
return (new StringBuilder()).append(Character.toUpperCase(s.charAt(0))).append(s.substring(1)).toString();
}
// 首字母转小写
public static String toLowerCaseFirstOne(String s){
if(Character.isLowerCase(s.charAt(0)))
return s;
else
return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
}
// 将 string 写入文件
public static void writeToFile(Project project, VirtualFile file, String content) {
Runnable runnable = () -> {
try {
file.setBinaryContent(content.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
};
WriteCommandAction.runWriteCommandAction(project, runnable);
}
}
复制代码
public class Utils {
/**
* 将字符串复制到剪切板。
*/
public static void setSysClipboardText(String writeMe) {
Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable tText = new StringSelection(writeMe);
clip.setContents(tText, null);
}
// 首字母转大写
public static String toUpperCaseFirstOne(String s) {
if (s.contains("_")) {
String[] a = s.split("_");
StringBuilder builder = new StringBuilder();
for (String anA : a) {
if (Character.isUpperCase(anA.charAt(0)))
builder.append(anA);
else
builder.append(Character.toUpperCase(anA.charAt(0))).append(anA.substring(1));
}
return builder.toString();
}
if (Character.isUpperCase(s.charAt(0)))
return s;
else
return (new StringBuilder()).append(Character.toUpperCase(s.charAt(0))).append(s.substring(1)).toString();
}
// 下划线参数转驼峰
public static String toUpperCaseParams(String s) {
if (s.contains("_")) {
String[] a = s.split("_");
StringBuilder builder = new StringBuilder();
for (int i = 0; i < a.length; i++) {
if (i==0){
builder.append(a[i]);
}
if (Character.isUpperCase(a[i].charAt(0)))
builder.append(a[i]);
else if (i != 0)
builder.append(Character.toUpperCase(a[i].charAt(0))).append(a[i].substring(1));
}
return builder.toString();
}
return s;
}
// 首字母转小写
public static String toLowerCaseFirstOne(String s) {
if (Character.isLowerCase(s.charAt(0)))
return s;
else
return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
}
// 将 string 写入文件
public static void writeToFile(VirtualFile file, String content) {
try {
file.setBinaryContent(content.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
复制代码
写文件的代码好像有不同?没关系,我 git 里拉老代码出来
// 将 string 写入文件
public static void writeToFile(VirtualFile file, String content) {
try {
file.setBinaryContent(content.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
复制代码
所以,唯一的不同就是我类头有注释,而他多了一个驼峰命名的方法。
github.com/xlyasdasd/F…
如果就 copy 一下我的代码过去用,其实我肯定也没什么,按道理来说作为一个程序猿我还应该觉得开心才是。
但是 copy 了我整个项目,又极其不专业的只改了几个不痛不痒的地方,改得此地无银三百两,留着项目结构、类命名、方法命名、空行、换行这些个人风格浓重的东西不动,我是 Kotlin 写的地方他就是 Kotlin,我用 Java 写的地方他就是 Java, 难道是因为核心逻辑看不懂不敢改动吗?既然能自己加上驼峰命名的功能,想来也不至于。
如果只是 copy 项目也就罢了,他甚至还发布到了全家桶插件的官网,如果不是因为下载量比我自己的插件还高,恐怕这事我到死都不会知道。
当然,他没什么错,是我自己没给开源项目加上 LICENSE。但想来就算我有 LICENSE,按他能去发布插件这个勇气来看,只怕也是不在乎的。
有朋友跟我说,我应该开心,应该有成就感,因为至少这证明我做的东西是有价值的,但我觉得事情不应该这么去看待。我花了心思去写出来的东西,我心肝情愿的开源,我心肝情愿的给别人用,但是拿了我的心血去换个包装贴上他自己的牌子,这谁能接受?
作为一个喜欢 flutter 的开发者,我也不至于因为这事就丧了气,尽管也付出了不少时间,做了不少迭代,但毕竟只是一个小玩具项目而已。只是这次被好好的上了一课,以后不论多小的项目,LICENSE 是一定不能少的。