Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
keyescgm
jadx
提交
94fb91ce
J
jadx
项目概览
keyescgm
/
jadx
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
J
jadx
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
94fb91ce
编写于
3月 02, 2022
作者:
S
Skylot
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat: add options for java-convert plugin
上级
c54dd77f
变更
10
隐藏空白更改
内联
并排
Showing
10 changed file
with
238 addition
and
61 deletion
+238
-61
jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java
jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java
+27
-7
jadx-core/src/test/java/jadx/tests/api/extensions/profiles/TestProfile.java
.../java/jadx/tests/api/extensions/profiles/TestProfile.java
+14
-4
jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaResugar.java
.../java/jadx/tests/integration/java8/TestLambdaResugar.java
+35
-0
jadx-core/src/test/java/jadx/tests/integration/others/TestStringConcatJava11.java
...jadx/tests/integration/others/TestStringConcatJava11.java
+1
-2
jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexInputOptions.java
...src/main/java/jadx/plugins/input/dex/DexInputOptions.java
+9
-26
jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/D8Converter.java
...main/java/jadx/plugins/input/javaconvert/D8Converter.java
+2
-2
jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertLoader.java
...ava/jadx/plugins/input/javaconvert/JavaConvertLoader.java
+48
-17
jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertOptions.java
...va/jadx/plugins/input/javaconvert/JavaConvertOptions.java
+50
-0
jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertPlugin.java
...ava/jadx/plugins/input/javaconvert/JavaConvertPlugin.java
+20
-3
jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/options/impl/BaseOptionsParser.java
...java/jadx/api/plugins/options/impl/BaseOptionsParser.java
+32
-0
未找到文件。
jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java
浏览文件 @
94fb91ce
...
...
@@ -104,7 +104,8 @@ public abstract class IntegrationTest extends TestUtils {
private
boolean
printLineNumbers
;
private
boolean
printOffsets
;
private
boolean
printDisassemble
;
private
Boolean
useJavaInput
=
null
;
private
@Nullable
Boolean
useJavaInput
;
private
boolean
removeParentClassOnInput
;
private
@Nullable
TestCompiler
sourceCompiler
;
private
@Nullable
TestCompiler
decompiledCompiler
;
...
...
@@ -121,6 +122,8 @@ public abstract class IntegrationTest extends TestUtils {
this
.
compile
=
true
;
this
.
compilerOptions
=
new
CompilerOptions
();
this
.
resMap
=
Collections
.
emptyMap
();
this
.
removeParentClassOnInput
=
true
;
this
.
useJavaInput
=
null
;
args
=
new
JadxArgs
();
args
.
setOutDir
(
new
File
(
OUT_DIR
));
...
...
@@ -173,8 +176,14 @@ public abstract class IntegrationTest extends TestUtils {
ClassNode
cls
=
root
.
resolveClass
(
clsName
);
assertThat
(
"Class not found: "
+
clsName
,
cls
,
notNullValue
());
assertThat
(
clsName
,
is
(
cls
.
getClassInfo
().
getFullName
()));
if
(
removeParentClassOnInput
)
{
assertThat
(
clsName
,
is
(
cls
.
getClassInfo
().
getFullName
()));
}
else
{
LOG
.
info
(
"Convert back to top level: {}"
,
cls
);
cls
.
getTopParentClass
().
decompile
();
// keep correct process order
cls
.
getClassInfo
().
notInner
(
root
);
cls
.
updateParentClass
();
}
decompileAndCheck
(
cls
);
return
cls
;
}
...
...
@@ -332,7 +341,7 @@ public abstract class IntegrationTest extends TestUtils {
}
private
void
runAutoCheck
(
ClassNode
cls
)
{
String
clsName
=
cls
.
getClassInfo
().
get
FullName
(
);
String
clsName
=
cls
.
getClassInfo
().
get
RawName
().
replace
(
'/'
,
'.'
);
try
{
// run 'check' method from original class
if
(
runSourceAutoCheck
(
clsName
))
{
...
...
@@ -473,9 +482,11 @@ public abstract class IntegrationTest extends TestUtils {
if
(
saveTestJar
)
{
saveToJar
(
files
,
outTmp
);
}
// remove classes which are parents for test class
String
clsName
=
clsFullName
.
substring
(
clsFullName
.
lastIndexOf
(
'.'
)
+
1
);
files
.
removeIf
(
next
->
!
next
.
getName
().
contains
(
clsName
));
if
(
removeParentClassOnInput
)
{
// remove classes which are parents for test class
String
clsName
=
clsFullName
.
substring
(
clsFullName
.
lastIndexOf
(
'.'
)
+
1
);
files
.
removeIf
(
next
->
!
next
.
getName
().
contains
(
clsName
));
}
return
files
;
}
...
...
@@ -561,10 +572,19 @@ public abstract class IntegrationTest extends TestUtils {
this
.
useJavaInput
=
false
;
}
public
void
useDexInput
(
String
mode
)
{
useDexInput
();
this
.
getArgs
().
getPluginOptions
().
put
(
"java-convert.mode"
,
mode
);
}
protected
boolean
isJavaInput
()
{
return
Utils
.
getOrElse
(
useJavaInput
,
USE_JAVA_INPUT
);
}
public
void
keepParentClassOnInput
()
{
this
.
removeParentClassOnInput
=
false
;
}
// Use only for debug purpose
protected
void
printDisassemble
()
{
this
.
printDisassemble
=
true
;
...
...
jadx-core/src/test/java/jadx/tests/api/extensions/profiles/TestProfile.java
浏览文件 @
94fb91ce
...
...
@@ -5,13 +5,23 @@ import java.util.function.Consumer;
import
jadx.tests.api.IntegrationTest
;
public
enum
TestProfile
implements
Consumer
<
IntegrationTest
>
{
DX_J8
(
"dx-j
ava-
8"
,
test
->
{
DX_J8
(
"dx-j8"
,
test
->
{
test
.
useTargetJavaVersion
(
8
);
test
.
useDexInput
();
test
.
useDexInput
(
"dx"
);
}),
D8_J8
(
"d8-j8"
,
test
->
{
test
.
useTargetJavaVersion
(
8
);
test
.
useDexInput
(
"d8"
);
}),
D8_J11
(
"d8-j
ava-
11"
,
test
->
{
D8_J11
(
"d8-j11"
,
test
->
{
test
.
useTargetJavaVersion
(
11
);
test
.
useDexInput
();
test
.
useDexInput
(
"d8"
);
}),
D8_J11_DESUGAR
(
"d8-j11-desugar"
,
test
->
{
test
.
useTargetJavaVersion
(
11
);
test
.
useDexInput
(
"d8"
);
test
.
keepParentClassOnInput
();
test
.
getArgs
().
getPluginOptions
().
put
(
"java-convert.d8-desugar"
,
"yes"
);
}),
JAVA8
(
"java-8"
,
test
->
{
test
.
useTargetJavaVersion
(
8
);
...
...
jadx-core/src/test/java/jadx/tests/integration/java8/TestLambdaResugar.java
0 → 100644
浏览文件 @
94fb91ce
package
jadx.tests.integration.java8
;
import
java.util.function.Function
;
import
jadx.NotYetImplemented
;
import
jadx.tests.api.IntegrationTest
;
import
jadx.tests.api.extensions.profiles.TestProfile
;
import
jadx.tests.api.extensions.profiles.TestWithProfiles
;
import
static
jadx
.
tests
.
api
.
utils
.
assertj
.
JadxAssertions
.
assertThat
;
public
class
TestLambdaResugar
extends
IntegrationTest
{
public
static
class
TestCls
{
private
String
field
;
public
void
test
()
{
call
(
s
->
{
this
.
field
=
s
;
return
s
.
length
();
});
}
public
void
call
(
Function
<
String
,
Integer
>
func
)
{
}
}
@NotYetImplemented
(
"Inline lambda methods"
)
@TestWithProfiles
(
TestProfile
.
D8_J11_DESUGAR
)
public
void
test
()
{
assertThat
(
getClassNode
(
TestCls
.
class
))
.
code
()
.
doesNotContain
(
"lambda$"
);
}
}
jadx-core/src/test/java/jadx/tests/integration/others/TestStringConcatJava11.java
浏览文件 @
94fb91ce
...
...
@@ -52,9 +52,8 @@ public class TestStringConcatJava11 extends RaungTest {
"return str + \"test\" + str + \"7\";"
);
// dynamic concat add const to string recipe
}
@TestWithProfiles
({
TestProfile
.
D
X_J8
,
TestProfile
.
JAVA8
})
@TestWithProfiles
({
TestProfile
.
D
8_J11
,
TestProfile
.
JAVA11
})
public
void
testJava11
()
{
useTargetJavaVersion
(
11
);
noDebugInfo
();
assertThat
(
getClassNode
(
TestCls
.
class
))
.
code
()
...
...
jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexInputOptions.java
浏览文件 @
94fb91ce
package
jadx.plugins.input.dex
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.Locale
;
import
java.util.Map
;
import
jadx.api.plugins.options.OptionDescription
;
import
jadx.api.plugins.options.impl.BaseOptionsParser
;
import
jadx.api.plugins.options.impl.JadxOptionDescription
;
public
class
DexInputOptions
{
public
class
DexInputOptions
extends
BaseOptionsParser
{
private
static
final
String
VERIFY_CHECKSUM_OPT
=
DexInputPlugin
.
PLUGIN_ID
+
".verify-checksum"
;
...
...
@@ -20,29 +20,12 @@ public class DexInputOptions {
}
public
List
<
OptionDescription
>
buildOptionsDescriptions
()
{
List
<
OptionDescription
>
list
=
new
ArrayList
<>(
1
);
list
.
add
(
new
JadxOptionDescription
(
VERIFY_CHECKSUM_OPT
,
"Verify dex file checksum before load"
,
"yes"
,
Arrays
.
asList
(
"yes"
,
"no"
)));
return
list
;
}
private
boolean
getBooleanOption
(
Map
<
String
,
String
>
options
,
String
key
,
boolean
defValue
)
{
String
val
=
options
.
get
(
key
);
if
(
val
==
null
)
{
return
defValue
;
}
String
valLower
=
val
.
toLowerCase
(
Locale
.
ROOT
);
if
(
valLower
.
equals
(
"yes"
)
||
valLower
.
equals
(
"true"
))
{
return
true
;
}
if
(
valLower
.
equals
(
"no"
)
||
valLower
.
equals
(
"false"
))
{
return
false
;
}
throw
new
IllegalArgumentException
(
"Unknown value '"
+
val
+
"' for option '"
+
key
+
"'"
+
", expect: 'yes' or 'no'"
);
return
Collections
.
singletonList
(
new
JadxOptionDescription
(
VERIFY_CHECKSUM_OPT
,
"Verify dex file checksum before load"
,
"yes"
,
Arrays
.
asList
(
"yes"
,
"no"
)));
}
public
boolean
isVerifyChecksum
()
{
...
...
jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/D8Converter.java
浏览文件 @
94fb91ce
...
...
@@ -16,14 +16,14 @@ import com.android.tools.r8.OutputMode;
public
class
D8Converter
{
private
static
final
Logger
LOG
=
LoggerFactory
.
getLogger
(
D8Converter
.
class
);
public
static
void
run
(
Path
path
,
Path
tempDirectory
)
throws
CompilationFailedException
{
public
static
void
run
(
Path
path
,
Path
tempDirectory
,
JavaConvertOptions
options
)
throws
CompilationFailedException
{
D8Command
d8Command
=
D8Command
.
builder
(
new
LogHandler
())
.
addProgramFiles
(
path
)
.
setOutput
(
tempDirectory
,
OutputMode
.
DexIndexed
)
.
setMode
(
CompilationMode
.
DEBUG
)
.
setMinApiLevel
(
30
)
.
setIntermediate
(
true
)
.
setDisableDesugaring
(
true
)
.
setDisableDesugaring
(
!
options
.
isD8Desugar
()
)
.
build
();
D8
.
run
(
d8Command
);
}
...
...
jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertLoader.java
浏览文件 @
94fb91ce
...
...
@@ -23,7 +23,13 @@ import jadx.api.plugins.utils.ZipSecurity;
public
class
JavaConvertLoader
{
private
static
final
Logger
LOG
=
LoggerFactory
.
getLogger
(
JavaConvertLoader
.
class
);
public
static
ConvertResult
process
(
List
<
Path
>
input
)
{
private
final
JavaConvertOptions
options
;
public
JavaConvertLoader
(
JavaConvertOptions
options
)
{
this
.
options
=
options
;
}
public
ConvertResult
process
(
List
<
Path
>
input
)
{
ConvertResult
result
=
new
ConvertResult
();
processJars
(
input
,
result
);
processAars
(
input
,
result
);
...
...
@@ -31,7 +37,7 @@ public class JavaConvertLoader {
return
result
;
}
private
static
void
processJars
(
List
<
Path
>
input
,
ConvertResult
result
)
{
private
void
processJars
(
List
<
Path
>
input
,
ConvertResult
result
)
{
PathMatcher
jarMatcher
=
FileSystems
.
getDefault
().
getPathMatcher
(
"glob:**.jar"
);
input
.
stream
()
.
filter
(
jarMatcher:
:
matches
)
...
...
@@ -44,7 +50,7 @@ public class JavaConvertLoader {
});
}
private
static
void
processClassFiles
(
List
<
Path
>
input
,
ConvertResult
result
)
{
private
void
processClassFiles
(
List
<
Path
>
input
,
ConvertResult
result
)
{
PathMatcher
jarMatcher
=
FileSystems
.
getDefault
().
getPathMatcher
(
"glob:**.class"
);
List
<
Path
>
clsFiles
=
input
.
stream
()
.
filter
(
jarMatcher:
:
matches
)
...
...
@@ -72,7 +78,7 @@ public class JavaConvertLoader {
}
}
private
static
void
processAars
(
List
<
Path
>
input
,
ConvertResult
result
)
{
private
void
processAars
(
List
<
Path
>
input
,
ConvertResult
result
)
{
PathMatcher
aarMatcher
=
FileSystems
.
getDefault
().
getPathMatcher
(
"glob:**.aar"
);
input
.
stream
()
.
filter
(
aarMatcher:
:
matches
)
...
...
@@ -91,14 +97,14 @@ public class JavaConvertLoader {
}));
}
private
static
void
convertJar
(
ConvertResult
result
,
Path
path
)
throws
Exception
{
private
void
convertJar
(
ConvertResult
result
,
Path
path
)
throws
Exception
{
if
(
repackAndConvertJar
(
result
,
path
))
{
return
;
}
convertSimpleJar
(
result
,
path
);
}
private
static
boolean
repackAndConvertJar
(
ConvertResult
result
,
Path
path
)
throws
Exception
{
private
boolean
repackAndConvertJar
(
ConvertResult
result
,
Path
path
)
throws
Exception
{
// check if jar need a full repackage
Boolean
repackNeeded
=
ZipSecurity
.
visitZipEntries
(
path
.
toFile
(),
(
zipFile
,
zipEntry
)
->
{
String
entryName
=
zipEntry
.
getName
();
...
...
@@ -154,25 +160,50 @@ public class JavaConvertLoader {
return
true
;
}
private
static
void
convertSimpleJar
(
ConvertResult
result
,
Path
path
)
throws
Exception
{
private
void
convertSimpleJar
(
ConvertResult
result
,
Path
path
)
throws
Exception
{
Path
tempDirectory
=
Files
.
createTempDirectory
(
"jadx-"
);
result
.
addTempPath
(
tempDirectory
);
LOG
.
debug
(
"Converting to dex ..."
);
try
{
DxConverter
.
run
(
path
,
tempDirectory
);
}
catch
(
Throwable
e
)
{
LOG
.
warn
(
"DX convert failed, trying D8, path: {}"
,
path
);
try
{
D8Converter
.
run
(
path
,
tempDirectory
);
}
catch
(
Throwable
ex
)
{
LOG
.
error
(
"D8 convert failed: {}"
,
ex
.
getMessage
());
}
}
convert
(
path
,
tempDirectory
);
List
<
Path
>
dexFiles
=
collectFilesInDir
(
tempDirectory
);
LOG
.
debug
(
"Converted {} to {} dex"
,
path
.
toAbsolutePath
(),
dexFiles
.
size
());
result
.
addConvertedFiles
(
dexFiles
);
}
private
void
convert
(
Path
path
,
Path
tempDirectory
)
{
JavaConvertOptions
.
Mode
mode
=
options
.
getMode
();
switch
(
mode
)
{
case
DX:
try
{
DxConverter
.
run
(
path
,
tempDirectory
);
}
catch
(
Throwable
e
)
{
LOG
.
error
(
"DX convert failed, path: {}"
,
path
,
e
);
}
break
;
case
D8:
try
{
D8Converter
.
run
(
path
,
tempDirectory
,
options
);
}
catch
(
Throwable
e
)
{
LOG
.
error
(
"D8 convert failed, path: {}"
,
path
,
e
);
}
break
;
case
BOTH:
try
{
DxConverter
.
run
(
path
,
tempDirectory
);
}
catch
(
Throwable
e
)
{
LOG
.
warn
(
"DX convert failed, trying D8, path: {}"
,
path
);
try
{
D8Converter
.
run
(
path
,
tempDirectory
,
options
);
}
catch
(
Throwable
ex
)
{
LOG
.
error
(
"D8 convert failed: {}"
,
ex
.
getMessage
());
}
}
break
;
}
}
private
static
List
<
Path
>
collectFilesInDir
(
Path
tempDirectory
)
throws
IOException
{
PathMatcher
dexMatcher
=
FileSystems
.
getDefault
().
getPathMatcher
(
"glob:**.dex"
);
try
(
Stream
<
Path
>
pathStream
=
Files
.
walk
(
tempDirectory
,
1
))
{
...
...
jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertOptions.java
0 → 100644
浏览文件 @
94fb91ce
package
jadx.plugins.input.javaconvert
;
import
java.util.Arrays
;
import
java.util.List
;
import
java.util.Locale
;
import
java.util.Map
;
import
jadx.api.plugins.options.OptionDescription
;
import
jadx.api.plugins.options.impl.BaseOptionsParser
;
import
jadx.api.plugins.options.impl.JadxOptionDescription
;
public
class
JavaConvertOptions
extends
BaseOptionsParser
{
private
static
final
String
MODE_OPT
=
JavaConvertPlugin
.
PLUGIN_ID
+
".mode"
;
private
static
final
String
D8_DESUGAR_OPT
=
JavaConvertPlugin
.
PLUGIN_ID
+
".d8-desugar"
;
public
enum
Mode
{
DX
,
D8
,
BOTH
}
private
Mode
mode
=
Mode
.
BOTH
;
private
boolean
d8Desugar
=
false
;
public
void
apply
(
Map
<
String
,
String
>
options
)
{
mode
=
getOption
(
options
,
MODE_OPT
,
name
->
Mode
.
valueOf
(
name
.
toUpperCase
(
Locale
.
ROOT
)),
Mode
.
BOTH
);
d8Desugar
=
getBooleanOption
(
options
,
D8_DESUGAR_OPT
,
false
);
}
public
List
<
OptionDescription
>
buildOptionsDescriptions
()
{
return
Arrays
.
asList
(
new
JadxOptionDescription
(
MODE_OPT
,
"Convert mode"
,
"both"
,
Arrays
.
asList
(
"dx"
,
"d8"
,
"both"
)),
new
JadxOptionDescription
(
D8_DESUGAR_OPT
,
"Use desugar in d8"
,
"no"
,
Arrays
.
asList
(
"yes"
,
"no"
)));
}
public
Mode
getMode
()
{
return
mode
;
}
public
boolean
isD8Desugar
()
{
return
d8Desugar
;
}
}
jadx-plugins/jadx-java-convert/src/main/java/jadx/plugins/input/javaconvert/JavaConvertPlugin.java
浏览文件 @
94fb91ce
...
...
@@ -2,21 +2,28 @@ package jadx.plugins.input.javaconvert;
import
java.nio.file.Path
;
import
java.util.List
;
import
java.util.Map
;
import
jadx.api.plugins.JadxPluginInfo
;
import
jadx.api.plugins.input.JadxInputPlugin
;
import
jadx.api.plugins.input.data.ILoadResult
;
import
jadx.api.plugins.input.data.impl.EmptyLoadResult
;
import
jadx.api.plugins.options.JadxPluginOptions
;
import
jadx.api.plugins.options.OptionDescription
;
import
jadx.plugins.input.dex.DexInputPlugin
;
public
class
JavaConvertPlugin
implements
JadxInputPlugin
{
public
class
JavaConvertPlugin
implements
JadxInputPlugin
,
JadxPluginOptions
{
public
static
final
String
PLUGIN_ID
=
"java-convert"
;
private
final
DexInputPlugin
dexInput
=
new
DexInputPlugin
();
private
final
JavaConvertOptions
options
=
new
JavaConvertOptions
();
private
final
JavaConvertLoader
loader
=
new
JavaConvertLoader
(
options
);
@Override
public
JadxPluginInfo
getPluginInfo
()
{
return
new
JadxPluginInfo
(
"java-convert"
,
PLUGIN_ID
,
"JavaConvert"
,
"Convert .jar and .class files to dex"
,
"java-input"
);
...
...
@@ -24,11 +31,21 @@ public class JavaConvertPlugin implements JadxInputPlugin {
@Override
public
ILoadResult
loadFiles
(
List
<
Path
>
input
)
{
ConvertResult
result
=
JavaConvertL
oader
.
process
(
input
);
ConvertResult
result
=
l
oader
.
process
(
input
);
if
(
result
.
isEmpty
())
{
result
.
close
();
return
EmptyLoadResult
.
INSTANCE
;
}
return
dexInput
.
loadFiles
(
result
.
getConverted
(),
result
);
}
@Override
public
void
setOptions
(
Map
<
String
,
String
>
options
)
{
this
.
options
.
apply
(
options
);
}
@Override
public
List
<
OptionDescription
>
getOptionsDescriptions
()
{
return
this
.
options
.
buildOptionsDescriptions
();
}
}
jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/options/impl/BaseOptionsParser.java
0 → 100644
浏览文件 @
94fb91ce
package
jadx.api.plugins.options.impl
;
import
java.util.Locale
;
import
java.util.Map
;
import
java.util.function.Function
;
public
class
BaseOptionsParser
{
public
boolean
getBooleanOption
(
Map
<
String
,
String
>
options
,
String
key
,
boolean
defValue
)
{
String
val
=
options
.
get
(
key
);
if
(
val
==
null
)
{
return
defValue
;
}
String
valLower
=
val
.
toLowerCase
(
Locale
.
ROOT
);
if
(
valLower
.
equals
(
"yes"
)
||
valLower
.
equals
(
"true"
))
{
return
true
;
}
if
(
valLower
.
equals
(
"no"
)
||
valLower
.
equals
(
"false"
))
{
return
false
;
}
throw
new
IllegalArgumentException
(
"Unknown value '"
+
val
+
"' for option '"
+
key
+
"'"
+
", expect: 'yes' or 'no'"
);
}
public
<
T
>
T
getOption
(
Map
<
String
,
String
>
options
,
String
key
,
Function
<
String
,
T
>
parse
,
T
defValue
)
{
String
val
=
options
.
get
(
key
);
if
(
val
==
null
)
{
return
defValue
;
}
return
parse
.
apply
(
val
);
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录