添加mail read库;为Parser添加对Set的支持;完善Yaml.toYaml方法

This commit is contained in:
tursom 2020-03-08 01:48:36 +08:00
parent a19d4f12d2
commit 654d721428
4 changed files with 268 additions and 98 deletions

View File

@ -10,75 +10,106 @@ object Parser {
fun <T> parse(yaml: Any, clazz: Class<T>): T? {
@Suppress("UNCHECKED_CAST")
return if (yaml is List<*> && clazz.isArray) {
parseArray(yaml, clazz) as T
} else when (clazz) {
Any::class.java -> yaml
Int::class.java -> yaml.toInt()
Long::class.java -> yaml.toLong()
Float::class.java -> yaml.toFloat()
Double::class.java -> yaml.toDouble()
Boolean::class.java -> yaml.toBoolean()
getClazz<Int>() -> yaml.toInt()
getClazz<Long>() -> yaml.toLong()
getClazz<Float>() -> yaml.toFloat()
getClazz<Double>() -> yaml.toDouble()
getClazz<Boolean>() -> yaml.toBoolean()
String::class.java -> yaml.toString()
else -> {
if (yaml !is Map<*, *>) return null
val instance = try {
clazz.newInstance()
} catch (e: Exception) {
unsafe.allocateInstance(clazz)
}
val fields = clazz.declaredFields
fields.forEach {
if ((it.modifiers and (Modifier.STATIC or Modifier.TRANSIENT)) != 0) return@forEach
try {
val parse = parseField(yaml[it.name] ?: return@forEach, it) ?: return@forEach
it.isAccessible = true
it.set(instance, parse)
} catch (e: Exception) {
}
}
instance
return when {
clazz.isInstance(yaml) -> yaml.cast()
clazz.isInheritanceFrom(Enum::class.java) -> try {
val valueOf = clazz.getDeclaredMethod("valueOf", String::class.java)
valueOf.invoke(null, yaml.toString().toUpperCase()) as T
} catch (e: Exception) {
null
}
} as T
yaml is List<*> && clazz.isArray -> parseArray(yaml, clazz) as T
else -> when (clazz) {
Any::class.java -> yaml
Int::class.java -> yaml.toInt()
Long::class.java -> yaml.toLong()
Float::class.java -> yaml.toFloat()
Double::class.java -> yaml.toDouble()
Boolean::class.java -> yaml.toBoolean()
getClazz<Int>() -> yaml.toInt()
getClazz<Long>() -> yaml.toLong()
getClazz<Float>() -> yaml.toFloat()
getClazz<Double>() -> yaml.toDouble()
getClazz<Boolean>() -> yaml.toBoolean()
String::class.java -> yaml.toString()
else -> {
if (yaml !is Map<*, *>) return null
val instance = try {
clazz.newInstance()
} catch (e: Exception) {
unsafe.allocateInstance(clazz)
}
val fields = clazz.declaredFields
fields.forEach {
if ((it.modifiers and (Modifier.STATIC or Modifier.TRANSIENT)) != 0) return@forEach
try {
val parse = parseField(yaml[it.name] ?: return@forEach, it) ?: return@forEach
it.isAccessible = true
it.set(instance, parse)
} catch (e: Exception) {
}
}
instance
}
} as T
}
}
private fun parseField(yaml: Any, field: Field): Any? {
val clazz = field.type
@Suppress("UNCHECKED_CAST")
return if (yaml is List<*>) {
when {
clazz.isAssignableFrom(List::class.java) -> {
val type = field.actualTypeArguments
if (type == Any::class.java) {
yaml
} else {
val list = try {
clazz.newInstance() as MutableList<Any>
} catch (e: Exception) {
try {
unsafe.allocateInstance(clazz) as MutableList<Any>
return when (yaml) {
is List<*> -> {
when {
clazz.isAssignableFrom(List::class.java) -> {
val type = field.actualTypeArguments
if (type == Any::class.java) {
yaml
} else {
val list = try {
clazz.newInstance() as MutableList<Any>
} catch (e: Exception) {
ArrayList<Any>()
try {
unsafe.allocateInstance(clazz) as MutableList<Any>
} catch (e: Exception) {
ArrayList<Any>()
}
}
yaml.forEach {
list.add(parse(it ?: return@forEach, type) ?: return@forEach)
}
list
}
yaml.forEach {
list.add(parse(it ?: return@forEach, type) ?: return@forEach)
}
list
}
clazz.isAssignableFrom(Set::class.java) -> {
val type = field.actualTypeArguments
if (type == Any::class.java) {
yaml
} else {
val set: MutableSet<Any> = try {
clazz.newInstance() as MutableSet<Any>
} catch (e: Exception) {
try {
unsafe.allocateInstance(clazz) as MutableSet<Any>
} catch (e: Exception) {
HashSet()
}
}
yaml.forEach {
set.add(parse(it ?: return@forEach, type) ?: return@forEach)
}
set
}
}
clazz.isArray -> parseArray(yaml, clazz)
else -> null
}
clazz.isArray -> parseArray(yaml, clazz)
else -> null
}
} else {
parse(yaml, clazz)
else -> {
parse(yaml, clazz)
}
}
}

View File

@ -8,7 +8,6 @@ import javax.activation.DataHandler
import javax.activation.FileDataSource
import javax.mail.Address
import javax.mail.Session
import javax.mail.event.TransportEvent
import javax.mail.event.TransportListener
import javax.mail.internet.InternetAddress
import javax.mail.internet.MimeBodyPart

View File

@ -0,0 +1,120 @@
package cn.tursom.mail
import java.nio.charset.Charset
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.ScheduledThreadPoolExecutor
import java.util.concurrent.ThreadFactory
import java.util.concurrent.TimeUnit
import javax.mail.*
import javax.mail.event.MessageCountListener
import javax.mail.internet.InternetAddress
private val threadPool by lazy {
ScheduledThreadPoolExecutor(
1,
ThreadFactory { Thread(it, "MailLoop") })
}
fun addMailListener(
folder: Folder,
freq: Long, timeUnit: TimeUnit,
listener: MessageCountListener,
newFolder: () -> Folder? = { null }
): ScheduledFuture<*> {
folder.addMessageCountListener(listener)
var mailFOlder = folder
return threadPool.scheduleAtFixedRate({
try {
mailFOlder.messageCount
} catch (e: FolderClosedException) {
mailFOlder = newFolder() ?: throw e
mailFOlder.addMessageCountListener(listener)
} catch (e: Exception) {
e.printStackTrace()
throw e
}
}, 0, freq, timeUnit)
}
fun addMailListener(
newFolder: () -> Folder,
freq: Long, timeUnit: TimeUnit,
listener: MessageCountListener
): ScheduledFuture<*> = addMailListener(newFolder(), freq, timeUnit, listener, newFolder)
fun getStore(host: String, port: Int, account: String, password: String): Store {
val props = System.getProperties()
props["mail.imap.host"] = host
props["mail.imap.port"] = port
props["mail.imap.auth"] = "true"
props["mail.imap.ssl.enable"] = "true"
props["mail.imap.socketFactory.port"] = port
props["mail.imap.socketFactory.fallback"] = "false"
props["mail.imap.socketFactory.class"] = "javax.net.ssl.SSLSocketFactory"
val session = Session.getDefaultInstance(props)
val store = session.getStore("imap")
store.connect(account, password)
return store
}
fun getFolder(host: String, port: Int, account: String, password: String): Pair<Store, Folder> {
val store: Store = getStore(host, port, account, password)
val folder: Folder = store.getFolder("INBOX")
folder.open(Folder.READ_ONLY)
return store to folder
}
fun forEachMail(host: String, port: Int, account: String, password: String, onMsg: (msg: Message) -> Unit) {
val (store, folder) = getFolder(host, port, account, password)
// 全部邮件数
val messages: Array<Message> = folder.messages
messages.forEach(onMsg)
folder.close(true)
store.close()
}
fun getText(message: Message): String? = when (val content: Any = message.content) {
is String -> content
is Multipart -> {
val sb = StringBuilder()
repeat(content.count) {
val part: Part = content.getBodyPart(it)
if (part.contentType.contains("text/plain")) {
var charsetStart = part.contentType.indexOf("charset=", 0, true)
if (charsetStart != -1) charsetStart += "charset=".length
var charsetEnd = part.contentType.indexOf(';', charsetStart)
if (charsetEnd == -1) {
charsetEnd = part.contentType.length
}
val msg = part.inputStream.bufferedReader(
if (charsetStart == -1) {
Charsets.UTF_8
} else {
Charset.forName(part.contentType.substring(charsetStart, charsetEnd))
}
).readText()
sb.append(msg)
}
}
if (sb.isEmpty()) {
null
} else {
sb.toString()
}
}
else -> null
}
fun forEachUnreadMail(host: String, port: Int, account: String, password: String, onMsg: (from: InternetAddress, msg: String) -> Boolean) {
forEachMail(host, port, account, password) { message ->
// 跳过已读邮件
if (message.flags.contains(Flags.Flag.SEEN)) return@forEachMail
val msg = getText(message)
val read = when (val text = getText(message)) {
null -> false
else -> onMsg(message.from[0] as InternetAddress, text)
}
//未读邮件标记为已读
message.setFlag(Flags.Flag.SEEN, read)
}
}

View File

@ -16,57 +16,78 @@ object Yaml {
return stringBuilder.toString()
}
private fun toYaml(obj: Any, stringBuilder: StringBuilder, indentation: String) {
private fun toYaml(obj: Any, stringBuilder: StringBuilder, indentation: String, inCollection: Boolean = false) {
when (obj) {
is Byte -> stringBuilder.append("$obj")
is Char -> stringBuilder.append("$obj")
is Short -> stringBuilder.append("$obj")
is Int -> stringBuilder.append("$obj")
is Long -> stringBuilder.append("$obj")
is Float -> stringBuilder.append("$obj")
is Double -> stringBuilder.append("$obj")
is String -> stringBuilder.append("$obj")
is Collection<*> -> obj.forEach {
it ?: return@forEach
stringBuilder.append("$indentation- ")
toYaml(it, stringBuilder, "$indentation ")
if (!stringBuilder.endsWith('\n')) {
stringBuilder.append("\n")
is Byte -> stringBuilder.append(obj)
is Char -> stringBuilder.append(obj)
is Short -> stringBuilder.append(obj)
is Int -> stringBuilder.append(obj)
is Long -> stringBuilder.append(obj)
is Float -> stringBuilder.append(obj)
is Double -> stringBuilder.append(obj)
is String -> stringBuilder.append(obj)
is Map<*, *> -> {
var first = true
obj.forEach { (any, u) ->
if (inCollection && first) {
stringBuilder.append("${any ?: return@forEach}: ")
first = false
} else {
stringBuilder.append("$indentation${any ?: return@forEach}: ")
}
toYaml(u ?: return@forEach, stringBuilder, "$indentation ")
if (!stringBuilder.endsWith('\n')) {
stringBuilder.append("\n")
}
}
}
is Map<*, *> -> obj.forEach { (any, u) ->
stringBuilder.append("$indentation${any ?: return@forEach}: ")
toYaml(u ?: return@forEach, stringBuilder, "$indentation ")
if (!stringBuilder.endsWith('\n')) {
stringBuilder.append("\n")
is Collection<*> -> if (obj.isEmpty()) {
stringBuilder.append("[]")
} else {
var appended = 0
obj.forEach {
it ?: return@forEach
stringBuilder.append("${if (appended == 0) '\n' else ""}$indentation- ")
appended++
toYaml(it, stringBuilder, "$indentation ", true)
if (!stringBuilder.endsWith('\n')) {
stringBuilder.append("\n")
}
}
}
else -> {
var first = true
fun getIndentation() = if (inCollection && first) {
first = false
""
} else {
indentation
}
obj.javaClass.declaredFields.forEach {
if ((it.modifiers and (Modifier.STATIC or Modifier.TRANSIENT)) != 0) return@forEach
it.isAccessible = true
val value = it.get(obj)
when (it.type) {
Byte::class.java -> stringBuilder.append("$indentation${it.name}: $value\n")
Char::class.java -> stringBuilder.append("$indentation${it.name}: $value\n")
Short::class.java -> stringBuilder.append("$indentation${it.name}: $value\n")
Int::class.java -> stringBuilder.append("$indentation${it.name}: $value\n")
Long::class.java -> stringBuilder.append("$indentation${it.name}: $value\n")
Float::class.java -> stringBuilder.append("$indentation${it.name}: $value\n")
Double::class.java -> stringBuilder.append("$indentation${it.name}: $value\n")
Byte::class.java -> stringBuilder.append("${getIndentation()}${it.name}: $value\n")
Char::class.java -> stringBuilder.append("${getIndentation()}${it.name}: $value\n")
Short::class.java -> stringBuilder.append("${getIndentation()}${it.name}: $value\n")
Int::class.java -> stringBuilder.append("${getIndentation()}${it.name}: $value\n")
Long::class.java -> stringBuilder.append("${getIndentation()}${it.name}: $value\n")
Float::class.java -> stringBuilder.append("${getIndentation()}${it.name}: $value\n")
Double::class.java -> stringBuilder.append("${getIndentation()}${it.name}: $value\n")
getClazz<Byte>() -> stringBuilder.append("$indentation${it.name}: $value\n")
getClazz<Char>() -> stringBuilder.append("$indentation${it.name}: $value\n")
getClazz<Short>() -> stringBuilder.append("$indentation${it.name}: $value\n")
getClazz<Int>() -> stringBuilder.append("$indentation${it.name}: $value\n")
getClazz<Long>() -> stringBuilder.append("$indentation${it.name}: $value\n")
getClazz<Float>() -> stringBuilder.append("$indentation${it.name}: $value\n")
getClazz<Double>() -> stringBuilder.append("$indentation${it.name}: $value\n")
String::class.java -> stringBuilder.append("$indentation${it.name}: $value\n")
getClazz<Byte>() -> stringBuilder.append("${getIndentation()}${it.name}: $value\n")
getClazz<Char>() -> stringBuilder.append("${getIndentation()}${it.name}: $value\n")
getClazz<Short>() -> stringBuilder.append("${getIndentation()}${it.name}: $value\n")
getClazz<Int>() -> stringBuilder.append("${getIndentation()}${it.name}: $value\n")
getClazz<Long>() -> stringBuilder.append("${getIndentation()}${it.name}: $value\n")
getClazz<Float>() -> stringBuilder.append("${getIndentation()}${it.name}: $value\n")
getClazz<Double>() -> stringBuilder.append("${getIndentation()}${it.name}: $value\n")
String::class.java -> stringBuilder.append("${getIndentation()}${it.name}: $value\n")
else -> {
stringBuilder.append("$indentation${it.name}:\n")
toYaml(value, stringBuilder, "$indentation ")
stringBuilder.append("${getIndentation()}${it.name}: ")
toYaml(value, stringBuilder, "${getIndentation()} ")
}
}
}
@ -81,7 +102,6 @@ object Yaml {
inline fun <reified T> parseResource(classLoader: ClassLoader, path: String) = parseResource(classLoader, path, T::class.java)
fun <T> parseResource(path: String, clazz: Class<T>) = parseResource(this.javaClass.classLoader, path, clazz)
fun <T> parseResource(classLoader: ClassLoader, path: String, clazz: Class<T>) = Parser.parse(yaml.load<Any>(classLoader.getResourceAsStream(path)), clazz)
fun <T> parse(yaml: String, clazz: Class<T>) = Parser.parse(this.yaml.load<Any>(yaml), clazz)
fun <T> parseResource(classLoader: ClassLoader, path: String, clazz: Class<T>) = Parser.parse(yaml.load(classLoader.getResourceAsStream(path)), clazz)
fun <T> parse(yaml: String, clazz: Class<T>) = Parser.parse(this.yaml.load(yaml), clazz)
}