diff --git a/common-api/src/main/kotlin/org/apache/http/util/EntityUtils.kt b/common-api/src/main/kotlin/org/apache/http/util/EntityUtils.kt index 2145c31e..9d94aca6 100644 --- a/common-api/src/main/kotlin/org/apache/http/util/EntityUtils.kt +++ b/common-api/src/main/kotlin/org/apache/http/util/EntityUtils.kt @@ -87,7 +87,7 @@ object EntityUtils { @JvmOverloads fun toString(entity: HttpEntity, defaultCharset: Charset? = null): String? { Args.notNull(entity, "Entity") - return entity.content?.use { instream -> + return entity.content?.use { inStream -> Args.check(entity.contentLength <= 2147483647L, "HTTP entity too large to be buffered in memory") var i = entity.contentLength.toInt() if (i < 0) { @@ -115,7 +115,7 @@ object EntityUtils { charset = HTTP.DEF_CONTENT_CHARSET } - val reader = InputStreamReader(instream, charset!!) + val reader = InputStreamReader(inStream, charset!!) val buffer = CharArrayBuffer(i) val tmp = CharArray(1024) diff --git a/idea-plugin/build.gradle b/idea-plugin/build.gradle index 7cb8a56c..2641e73b 100644 --- a/idea-plugin/build.gradle +++ b/idea-plugin/build.gradle @@ -96,12 +96,12 @@ dependencies { // compile fileTree(dir: 'libs', include: ['*.jar']) - compile('com.itangcent:intellij-idea:0.1.8-SNAPSHOT') { + compile('com.itangcent:intellij-idea:0.1.9-SNAPSHOT') { exclude group: 'com.google.inject' exclude group: 'com.google.code.gson' } - compile('com.itangcent:intellij-kotlin-support:0.1.8-SNAPSHOT') { + compile('com.itangcent:intellij-kotlin-support:0.1.9-SNAPSHOT') { exclude group: 'com.google.inject' exclude group: 'com.google.code.gson' } diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/export/markdown/MarkdownApiExporter.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/export/markdown/MarkdownApiExporter.kt index 784f83ae..61e7cab6 100644 --- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/export/markdown/MarkdownApiExporter.kt +++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/export/markdown/MarkdownApiExporter.kt @@ -7,13 +7,14 @@ import com.intellij.openapi.ui.Messages import com.itangcent.common.model.Doc import com.itangcent.idea.plugin.Worker import com.itangcent.idea.plugin.api.export.ClassExporter -import com.itangcent.idea.plugin.api.export.RequestHelper +import com.itangcent.idea.plugin.settings.SettingBinder +import com.itangcent.idea.utils.Charsets import com.itangcent.idea.utils.FileSaveHelper import com.itangcent.intellij.context.ActionContext import com.itangcent.intellij.logger.Logger +import com.itangcent.intellij.logger.traceError import com.itangcent.intellij.psi.SelectedHelper import com.itangcent.intellij.util.ActionUtils -import com.itangcent.intellij.logger.traceError import java.util.* @Singleton @@ -34,6 +35,9 @@ class MarkdownApiExporter { @Inject private val markdownFormatter: MarkdownFormatter? = null + @Inject + private val settingBinder: SettingBinder? = null + fun export() { logger!!.info("Start find apis...") @@ -78,7 +82,8 @@ class MarkdownApiExporter { docs.clear() actionContext!!.runAsync { try { - fileSaveHelper!!.saveOrCopy(apiInfo, { + fileSaveHelper!!.saveOrCopy(apiInfo, Charsets.forName(settingBinder!!.read().outputCharset)?.charset() + ?: kotlin.text.Charsets.UTF_8, { logger.info("Exported data are copied to clipboard,you can paste to a md file now") }, { logger.info("Apis save success") @@ -86,12 +91,12 @@ class MarkdownApiExporter { logger.info("Apis save failed") } } catch (e: Exception) { - logger.traceError("Apis save failed",e) + logger.traceError("Apis save failed", e) } } } catch (e: Exception) { - logger.traceError("Apis save failed",e) + logger.traceError("Apis save failed", e) } } diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/export/suv/SuvApiExporter.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/export/suv/SuvApiExporter.kt index 931a8908..e0777ed4 100644 --- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/export/suv/SuvApiExporter.kt +++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/api/export/suv/SuvApiExporter.kt @@ -26,6 +26,7 @@ import com.itangcent.idea.plugin.script.GroovyActionExtLoader import com.itangcent.idea.plugin.script.LoggerBuffer import com.itangcent.idea.plugin.settings.SettingBinder import com.itangcent.idea.psi.PsiResource +import com.itangcent.idea.utils.Charsets import com.itangcent.idea.utils.CustomizedPsiClassHelper import com.itangcent.idea.utils.FileSaveHelper import com.itangcent.intellij.config.ConfigReader @@ -63,7 +64,6 @@ class SuvApiExporter { @Inject private val classExporter: ClassExporter? = null - @Suppress("UNCHECKED_CAST") fun showExportWindow() { @@ -447,6 +447,9 @@ class SuvApiExporter { @Inject private val markdownFormatter: MarkdownFormatter? = null + @Inject + private val settingBinder: SettingBinder? = null + override fun actionName(): String { return "MarkdownExportAction" } @@ -481,7 +484,8 @@ class SuvApiExporter { docs.clear() actionContext!!.runAsync { try { - fileSaveHelper!!.saveOrCopy(apiInfo, { + fileSaveHelper!!.saveOrCopy(apiInfo, Charsets.forName(settingBinder!!.read().outputCharset)?.charset() + ?: kotlin.text.Charsets.UTF_8, { logger!!.info("Exported data are copied to clipboard,you can paste to a md file now") }, { logger!!.info("Apis save success") diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/config/RecommendConfigReader.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/config/RecommendConfigReader.kt index 9f2518ca..0cfe6e8e 100644 --- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/config/RecommendConfigReader.kt +++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/config/RecommendConfigReader.kt @@ -126,7 +126,7 @@ class RecommendConfigReader : ConfigReader { private const val config_name = ".recommend.easy.api.config" // private const val config_version = ".recommend.easy.api.config.version" - private const val curr_version = "0.0.7" + private const val curr_version = "0.0.8" //$version$content private fun loadRecommendConfig(): String { diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/EasyApiSettingGUI.form b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/EasyApiSettingGUI.form index af3f975e..fdb22ff0 100644 --- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/EasyApiSettingGUI.form +++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/EasyApiSettingGUI.form @@ -239,19 +239,52 @@ - + + - + - - - - - + + + + + + + + + + + + + + + - + - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/EasyApiSettingGUI.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/EasyApiSettingGUI.kt index 7bb462c5..7f616b6e 100644 --- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/EasyApiSettingGUI.kt +++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/dialog/EasyApiSettingGUI.kt @@ -20,6 +20,7 @@ import com.itangcent.idea.icons.EasyIcons import com.itangcent.idea.icons.iconOnly import com.itangcent.idea.plugin.config.RecommendConfigReader import com.itangcent.idea.plugin.settings.Settings +import com.itangcent.idea.utils.Charsets import com.itangcent.idea.utils.ConfigurableLogger import com.itangcent.idea.utils.SwingUtils import com.itangcent.intellij.extend.rx.AutoComputer @@ -29,7 +30,6 @@ import com.itangcent.intellij.logger.Logger import com.itangcent.suv.http.ConfigurableHttpClientProvider import org.apache.commons.io.FileUtils import java.io.File -import java.nio.charset.Charset import java.util.* import javax.swing.* @@ -73,6 +73,8 @@ class EasyApiSettingGUI { private var outputDemoCheckBox: JCheckBox? = null + private var outputCharsetComboBox: JComboBox? = null + private var inferEnableCheckBox: JCheckBox? = null private var maxDeepTextField: JTextField? = null @@ -186,6 +188,13 @@ class EasyApiSettingGUI { .filter { throttleHelper.acquire("settings.logLevel", 300) } .eval { (it ?: ConfigurableLogger.CoarseLogLevel.LOW).getLevel() } + outputCharsetComboBox!!.model = DefaultComboBoxModel(Charsets.SUPPORTED_CHARSETS) + + autoComputer.bind(this, "settings.outputCharset") + .with(this.outputCharsetComboBox!!) + .filter { throttleHelper.acquire("settings.outputCharset", 300) } + .eval { (it ?: Charsets.UTF_8).displayName() } + autoComputer.bind(this.previewTextArea!!) .with(this, "settings.recommendConfigs") .eval { configs -> RecommendConfigReader.buildRecommendConfig(configs.split(",")) } @@ -226,6 +235,7 @@ class EasyApiSettingGUI { autoComputer.value(this::settings, settings.copy()) this.logLevelComboBox!!.selectedItem = ConfigurableLogger.CoarseLogLevel.toLevel(settings.logLevel) + this.outputCharsetComboBox!!.selectedItem = Charsets.forName(settings.outputCharset) val configs = settings.recommendConfigs.split(",") RecommendConfigReader.RECOMMEND_CONFIG_CODES.forEach { @@ -378,7 +388,7 @@ class EasyApiSettingGUI { } val fileWrapper = chooser.save(toSelect, "setting.json") if (fileWrapper != null) { - com.itangcent.intellij.util.FileUtils.forceSave(fileWrapper.file.path, GsonUtils.toJson(settings).toByteArray(Charset.defaultCharset())) + com.itangcent.intellij.util.FileUtils.forceSave(fileWrapper.file.path, GsonUtils.toJson(settings).toByteArray(kotlin.text.Charsets.UTF_8)) } } @@ -397,8 +407,10 @@ class EasyApiSettingGUI { val files = chooser.choose(null, toSelect) if (!files.isNullOrEmpty()) { val virtualFile = files[0] - val read = com.itangcent.common.utils.FileUtils.read(File(virtualFile.path)) - setSettings(GsonUtils.fromJson(read, Settings::class)) + val read = com.itangcent.common.utils.FileUtils.read(File(virtualFile.path), kotlin.text.Charsets.UTF_8) + if (!read.isNullOrEmpty()) { + setSettings(GsonUtils.fromJson(read, Settings::class)) + } } } diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/settings/Settings.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/settings/Settings.kt index 1c05ea30..6bbae4f9 100644 --- a/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/settings/Settings.kt +++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/plugin/settings/Settings.kt @@ -1,6 +1,7 @@ package com.itangcent.idea.plugin.settings import com.itangcent.idea.plugin.config.RecommendConfigReader +import com.itangcent.idea.utils.Charsets import com.itangcent.idea.utils.ConfigurableLogger class Settings { @@ -29,6 +30,8 @@ class Settings { var outputDemo: Boolean = true + var outputCharset: String = Charsets.UTF_8.displayName() + fun copy(): Settings { val newSetting = Settings() newSetting.postmanToken = this.postmanToken @@ -42,6 +45,7 @@ class Settings { newSetting.recommendConfigs = this.recommendConfigs newSetting.logLevel = this.logLevel newSetting.outputDemo = this.outputDemo + newSetting.outputCharset = this.outputCharset return newSetting } @@ -62,6 +66,7 @@ class Settings { if (recommendConfigs != other.recommendConfigs) return false if (logLevel != other.logLevel) return false if (outputDemo != other.outputDemo) return false + if (outputCharset != other.outputCharset) return false return true } @@ -78,10 +83,10 @@ class Settings { result = 31 * result + recommendConfigs.hashCode() result = 31 * result + logLevel result = 31 * result + outputDemo.hashCode() + result = 31 * result + outputCharset.hashCode() return result } - companion object { const val DEFAULT_INFER_MAX_DEEP = 4 } diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/utils/Charsets.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/utils/Charsets.kt new file mode 100644 index 00000000..2c9af858 --- /dev/null +++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/utils/Charsets.kt @@ -0,0 +1,87 @@ +package com.itangcent.idea.utils + +import java.nio.charset.Charset + +abstract class Charsets { + + companion object { + + val UTF_8 = ImmutableCharset(kotlin.text.Charsets.UTF_8) + val US_ASCII = ImmutableCharset(kotlin.text.Charsets.US_ASCII) + val UTF_16 = ImmutableCharset(kotlin.text.Charsets.UTF_16) + val ISO_8859_1 = ImmutableCharset(kotlin.text.Charsets.ISO_8859_1) + val UTF_32 = ImmutableCharset(kotlin.text.Charsets.UTF_32) + val GBK = tryLoadCharset("GBK") + val FOLLOW_SYSTEM = object : MutableCharset("follow system") { + override fun charset(): Charset { + return Charset.defaultCharset() + } + } + + fun forName(charset: String): Charsets? { + return SUPPORTED_CHARSETS.firstOrNull { it.displayName() == charset } + ?: tryLoadCharset(charset) + } + + private fun tryLoadCharset(charset: String): Charsets? { + return try { + ImmutableCharset(Charset.forName(charset)) + } catch (e: Exception) { + null + } + } + + val SUPPORTED_CHARSETS: Array + + init { + val charsets = ArrayList() + charsets.add(FOLLOW_SYSTEM) + charsets.add(UTF_8) + GBK?.let { charsets.add(it) } + charsets.add(US_ASCII) + charsets.add(UTF_16) + charsets.add(ISO_8859_1) + charsets.add(UTF_32) + SUPPORTED_CHARSETS = charsets.toTypedArray() + } + } + + abstract fun charset(): Charset + + abstract fun displayName(): String + + override fun toString(): String { + return displayName() + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + return charset() == (other as Charsets).charset() + } + + override fun hashCode(): Int { + return charset().hashCode() + } + + + class ImmutableCharset(private val charset: Charset) : Charsets() { + + override fun charset(): Charset { + return this.charset + } + + override fun displayName(): String { + return charset().displayName() + } + + } + + abstract class MutableCharset(private val displayName: String?) : Charsets() { + + override fun displayName(): String { + return displayName ?: charset().displayName() + } + } +} \ No newline at end of file diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/utils/FileSaveHelper.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/utils/FileSaveHelper.kt index 35213367..4e329923 100644 --- a/idea-plugin/src/main/kotlin/com/itangcent/idea/utils/FileSaveHelper.kt +++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/utils/FileSaveHelper.kt @@ -15,6 +15,7 @@ import com.itangcent.intellij.util.FileUtils import com.itangcent.intellij.util.ToolUtils import java.io.File import java.nio.charset.Charset +import kotlin.text.Charsets class FileSaveHelper { @@ -36,6 +37,14 @@ class FileSaveHelper { onCopy: () -> Unit, onSaveSuccess: () -> Unit, onSaveFailed: () -> Unit) { + saveOrCopy(info, Charsets.UTF_8, onCopy, onSaveSuccess, onSaveFailed) + } + + fun saveOrCopy(info: String?, + charset: Charset, + onCopy: () -> Unit, + onSaveSuccess: () -> Unit, + onSaveFailed: () -> Unit) { if (info == null) return @@ -59,7 +68,7 @@ class FileSaveHelper { if (file.isDirectory) { try { val defaultFile = getDefaultExportedFile() - FileUtils.forceSave("${file.path}${File.separator}$defaultFile", info.toByteArray(Charset.defaultCharset())) + FileUtils.forceSave("${file.path}${File.separator}$defaultFile", info.toByteArray(charset)) onSaveSuccess() } catch (e: Exception) { onSaveFailed() @@ -69,7 +78,7 @@ class FileSaveHelper { } } else { try { - FileUtils.forceSave(file, info.toByteArray(Charset.defaultCharset())) + FileUtils.forceSave(file, info.toByteArray(charset)) onSaveSuccess() } catch (e: Exception) { onSaveFailed() diff --git a/idea-plugin/src/main/kotlin/com/itangcent/idea/utils/IOUtils.kt b/idea-plugin/src/main/kotlin/com/itangcent/idea/utils/IOUtils.kt new file mode 100644 index 00000000..1ed7a82b --- /dev/null +++ b/idea-plugin/src/main/kotlin/com/itangcent/idea/utils/IOUtils.kt @@ -0,0 +1,94 @@ +package com.itangcent.idea.utils + +import java.io.InputStream + +/** + * - there's some function copy from [com.sun.org.apache.xerces.internal.xinclude.XIncludeTextReader] + */ +object IOUtils { + + fun getEncodingName(stream: InputStream): String? { + val b4 = ByteArray(4) + var encoding: String? = null + + // this has the potential to throw an exception + // it will be fixed when we ensure the stream is rewindable (see note above) + stream.mark(4) + val count = stream.read(b4, 0, 4) + stream.reset() + if (count == 4) { + encoding = getEncodingName(b4) + } + + return encoding + } + + /** + * REVISIT: This code is taken from com.sun.org.apache.xerces.internal.impl.XMLEntityManager. + * Is there any way we can share the code, without having it implemented twice? + * I think we should make it public and static in XMLEntityManager. --PJM + * + * Returns the IANA encoding name that is auto-detected from + * the bytes specified, with the endian-ness of that encoding where appropriate. + * + * @param b4 The first four bytes of the input. + * @return the encoding name, or null if no encoding could be detected + */ + fun getEncodingName(b4: ByteArray): String? { + + // UTF-16, with BOM + val b0: Int = (b4[0].toInt() and 0xFF) + val b1: Int = (b4[1].toInt() and 0xFF) + if (b0 == 0xFE && b1 == 0xFF) { + // UTF-16, big-endian + return "UTF-16BE" + } + if (b0 == 0xFF && b1 == 0xFE) { + // UTF-16, little-endian + return "UTF-16LE" + } + + // UTF-8 with a BOM + val b2: Int = (b4[2].toInt() and 0xFF) + if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF) { + return "UTF-8" + } + + // other encodings + val b3: Int = (b4[3].toInt() and 0xFF) + if (b0 == 0x00 && b1 == 0x00 && b2 == 0x00 && b3 == 0x3C) { + // UCS-4, big endian (1234) + return "ISO-10646-UCS-4" + } + if (b0 == 0x3C && b1 == 0x00 && b2 == 0x00 && b3 == 0x00) { + // UCS-4, little endian (4321) + return "ISO-10646-UCS-4" + } + if (b0 == 0x00 && b1 == 0x00 && b2 == 0x3C && b3 == 0x00) { + // UCS-4, unusual octet order (2143) + return "ISO-10646-UCS-4" + } + if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x00) { + // UCS-4, unusual octect order (3412) + return "ISO-10646-UCS-4" + } + if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) { + // UTF-16, big-endian, no BOM + // (or could turn out to be UCS-2... + return "UTF-16BE" + } + if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) { + // UTF-16, little-endian, no BOM + // (or could turn out to be UCS-2... + return "UTF-16LE" + } + if (b0 == 0x4C && b1 == 0x6F && b2 == 0xA7 && b3 == 0x94) { + // EBCDIC + // a la xerces1, return CP037 instead of EBCDIC here + return "CP037" + } + + // this signals us to use the value from the encoding attribute + return null + } +} \ No newline at end of file diff --git a/idea-plugin/src/main/resources/.recommend.easy.api.config b/idea-plugin/src/main/resources/.recommend.easy.api.config index 6b265626..03e2d5d6 100644 --- a/idea-plugin/src/main/resources/.recommend.easy.api.config +++ b/idea-plugin/src/main/resources/.recommend.easy.api.config @@ -82,9 +82,11 @@ type.is_file=groovy:it.isExtend("org.springframework.web.multipart.MultipartFile #[import_spring_properties] #Import spring properties +###set ignoreNotFoundFile = true properties.additional=${module_path}/src/main/resources/application.properties properties.additional=${module_path}/src/main/resources/application.yml properties.additional=${module_path}/src/main/resources/application.yaml +###set ignoreNotFoundFile = false #[resolve_spring_properties] #Resolve spring properties