Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openanolis
dragonwell8_jdk
提交
a1c492e8
D
dragonwell8_jdk
项目概览
openanolis
/
dragonwell8_jdk
通知
4
Star
2
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
dragonwell8_jdk
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
a1c492e8
编写于
8月 20, 2010
作者:
K
ksrini
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
6966737: (pack200) the pack200 regression tests need to be more robust.
Reviewed-by: jrose, ohair
上级
b1f0edac
变更
22
隐藏空白更改
内联
并排
Showing
22 changed file
with
10128 addition
and
399 deletion
+10128
-399
test/tools/pack200/CommandLineTests.java
test/tools/pack200/CommandLineTests.java
+179
-0
test/tools/pack200/Pack200Simple.sh
test/tools/pack200/Pack200Simple.sh
+0
-197
test/tools/pack200/Pack200Test.java
test/tools/pack200/Pack200Test.java
+72
-85
test/tools/pack200/PackageVersionTest.java
test/tools/pack200/PackageVersionTest.java
+13
-34
test/tools/pack200/SegmentLimit.java
test/tools/pack200/SegmentLimit.java
+26
-83
test/tools/pack200/Utils.java
test/tools/pack200/Utils.java
+499
-0
test/tools/pack200/pack200-verifier/data/README
test/tools/pack200/pack200-verifier/data/README
+45
-0
test/tools/pack200/pack200-verifier/data/golden.jar
test/tools/pack200/pack200-verifier/data/golden.jar
+0
-0
test/tools/pack200/pack200-verifier/make/build.xml
test/tools/pack200/pack200-verifier/make/build.xml
+59
-0
test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/ClassCompare.java
...k200-verifier/src/sun/tools/pack/verify/ClassCompare.java
+160
-0
test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/Globals.java
...0/pack200-verifier/src/sun/tools/pack/verify/Globals.java
+310
-0
test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/JarFileCompare.java
...00-verifier/src/sun/tools/pack/verify/JarFileCompare.java
+199
-0
test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/Main.java
...k200/pack200-verifier/src/sun/tools/pack/verify/Main.java
+171
-0
test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/VerifyTreeSet.java
...200-verifier/src/sun/tools/pack/verify/VerifyTreeSet.java
+46
-0
test/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java
...ools/pack200/pack200-verifier/src/xmlkit/ClassReader.java
+1003
-0
test/tools/pack200/pack200-verifier/src/xmlkit/ClassSyntax.java
...ools/pack200/pack200-verifier/src/xmlkit/ClassSyntax.java
+518
-0
test/tools/pack200/pack200-verifier/src/xmlkit/ClassWriter.java
...ools/pack200/pack200-verifier/src/xmlkit/ClassWriter.java
+818
-0
test/tools/pack200/pack200-verifier/src/xmlkit/CommandLineParser.java
...ack200/pack200-verifier/src/xmlkit/CommandLineParser.java
+284
-0
test/tools/pack200/pack200-verifier/src/xmlkit/InstructionAssembler.java
...200/pack200-verifier/src/xmlkit/InstructionAssembler.java
+464
-0
test/tools/pack200/pack200-verifier/src/xmlkit/InstructionSyntax.java
...ack200/pack200-verifier/src/xmlkit/InstructionSyntax.java
+483
-0
test/tools/pack200/pack200-verifier/src/xmlkit/TokenList.java
.../tools/pack200/pack200-verifier/src/xmlkit/TokenList.java
+449
-0
test/tools/pack200/pack200-verifier/src/xmlkit/XMLKit.java
test/tools/pack200/pack200-verifier/src/xmlkit/XMLKit.java
+4330
-0
未找到文件。
test/tools/pack200/CommandLineTests.java
0 → 100644
浏览文件 @
a1c492e8
/*
* Copyright (c) 2007, 2010 Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test CommandLineTests.sh
* @bug 6521334 6965836 6965836
* @compile -XDignore.symbol.file CommandLineTests.java Pack200Test.java
* @run main/timeout=1200 CommandLineTests
* @summary An ad hoc test to verify the behavior of pack200/unpack200 CLIs,
* and a simulation of pack/unpacking in the install repo.
* @author ksrini
*/
import
java.io.File
;
import
java.io.FileOutputStream
;
import
java.io.IOException
;
import
java.io.PrintStream
;
import
java.util.ArrayList
;
import
java.util.List
;
/*
* We try a potpouri of things ie. we have pack.conf to setup some
* options as well as a couple of command line options. We also test
* the packing and unpacking mechanism using the Java APIs. This also
* simulates pack200 the install workspace, noting that this is a simulation
* and can only test jars that are guaranteed to be available, also the
* configuration may not be in sync with the installer workspace.
*/
public
class
CommandLineTests
{
private
static
final
File
CWD
=
new
File
(
"."
);
private
static
final
File
EXP_SDK
=
new
File
(
CWD
,
"exp-sdk-image"
);
private
static
final
File
EXP_SDK_LIB_DIR
=
new
File
(
EXP_SDK
,
"lib"
);
private
static
final
File
EXP_SDK_BIN_DIR
=
new
File
(
EXP_SDK
,
"bin"
);
private
static
final
File
EXP_JRE_DIR
=
new
File
(
EXP_SDK
,
"jre"
);
private
static
final
File
EXP_JRE_LIB_DIR
=
new
File
(
EXP_JRE_DIR
,
"lib"
);
private
static
final
File
RtJar
=
new
File
(
EXP_JRE_LIB_DIR
,
"rt.jar"
);
private
static
final
File
CharsetsJar
=
new
File
(
EXP_JRE_LIB_DIR
,
"charsets.jar"
);
private
static
final
File
JsseJar
=
new
File
(
EXP_JRE_LIB_DIR
,
"jsse.jar"
);
private
static
final
File
ToolsJar
=
new
File
(
EXP_SDK_LIB_DIR
,
"tools.jar"
);
private
static
final
File
javaCmd
;
private
static
final
File
javacCmd
;
private
static
final
File
ConfigFile
=
new
File
(
"pack.conf"
);
private
static
final
List
<
File
>
jarList
;
static
{
javaCmd
=
Utils
.
IsWindows
?
new
File
(
EXP_SDK_BIN_DIR
,
"java.exe"
)
:
new
File
(
EXP_SDK_BIN_DIR
,
"java"
);
javacCmd
=
Utils
.
IsWindows
?
new
File
(
EXP_SDK_BIN_DIR
,
"javac.exe"
)
:
new
File
(
EXP_SDK_BIN_DIR
,
"javac"
);
jarList
=
new
ArrayList
<
File
>();
jarList
.
add
(
RtJar
);
jarList
.
add
(
CharsetsJar
);
jarList
.
add
(
JsseJar
);
jarList
.
add
(
ToolsJar
);
}
// init test area with a copy of the sdk
static
void
init
()
throws
IOException
{
Utils
.
recursiveCopy
(
Utils
.
JavaSDK
,
EXP_SDK
);
creatConfigFile
();
}
// Hopefully, this should be kept in sync with what the installer does.
static
void
creatConfigFile
()
throws
IOException
{
FileOutputStream
fos
=
null
;
PrintStream
ps
=
null
;
try
{
fos
=
new
FileOutputStream
(
ConfigFile
);
ps
=
new
PrintStream
(
fos
);
ps
.
println
(
"com.sun.java.util.jar.pack.debug.verbose=0"
);
ps
.
println
(
"pack.modification.time=keep"
);
ps
.
println
(
"pack.keep.class.order=true"
);
ps
.
println
(
"pack.deflate.hint=false"
);
// Fail the build, if new or unknown attributes are introduced.
ps
.
println
(
"pack.unknown.attribute=error"
);
ps
.
println
(
"pack.segment.limit=-1"
);
// BugId: 6328502, These files will be passed-through as-is.
ps
.
println
(
"pack.pass.file.0=java/lang/Error.class"
);
ps
.
println
(
"pack.pass.file.1=java/lang/LinkageError.class"
);
ps
.
println
(
"pack.pass.file.2=java/lang/Object.class"
);
ps
.
println
(
"pack.pass.file.3=java/lang/Throwable.class"
);
ps
.
println
(
"pack.pass.file.4=java/lang/VerifyError.class"
);
ps
.
println
(
"pack.pass.file.5=com/sun/demo/jvmti/hprof/Tracker.class"
);
}
finally
{
Utils
.
close
(
ps
);
Utils
.
close
(
fos
);
}
}
static
void
runPack200
(
boolean
jre
)
throws
IOException
{
List
<
String
>
cmdsList
=
new
ArrayList
<
String
>();
for
(
File
f
:
jarList
)
{
if
(
jre
&&
f
.
getName
().
equals
(
"tools.jar"
))
{
continue
;
// need not worry about tools.jar for JRE
}
// make a backup copy for re-use
File
bakFile
=
new
File
(
f
.
getName
()
+
".bak"
);
if
(!
bakFile
.
exists
())
{
// backup
Utils
.
copyFile
(
f
.
getAbsoluteFile
(),
bakFile
.
getAbsoluteFile
());
}
else
{
// restore
Utils
.
copyFile
(
bakFile
.
getAbsoluteFile
(),
f
.
getAbsoluteFile
());
}
cmdsList
.
clear
();
cmdsList
.
add
(
Utils
.
getPack200Cmd
());
cmdsList
.
add
(
"-J-esa"
);
cmdsList
.
add
(
"-J-ea"
);
cmdsList
.
add
(
Utils
.
Is64Bit
?
"-J-Xmx1g"
:
"-J-Xmx512m"
);
cmdsList
.
add
(
"--repack"
);
cmdsList
.
add
(
"--config-file="
+
ConfigFile
.
getAbsolutePath
());
if
(
jre
)
{
cmdsList
.
add
(
"--strip-debug"
);
}
// NOTE: commented until 6965836 is fixed
// cmdsList.add("--code-attribute=StackMapTable=strip");
cmdsList
.
add
(
f
.
getAbsolutePath
());
Utils
.
runExec
(
cmdsList
);
}
}
static
void
testJRE
()
throws
IOException
{
runPack200
(
true
);
// the speciment JRE
List
<
String
>
cmdsList
=
new
ArrayList
<
String
>();
cmdsList
.
add
(
javaCmd
.
getAbsolutePath
());
cmdsList
.
add
(
"-verify"
);
cmdsList
.
add
(
"-version"
);
Utils
.
runExec
(
cmdsList
);
}
static
void
testJDK
()
throws
IOException
{
runPack200
(
false
);
// test the specimen JDK
List
<
String
>
cmdsList
=
new
ArrayList
<
String
>();
cmdsList
.
add
(
javaCmd
.
getAbsolutePath
());
cmdsList
.
add
(
"-verify"
);
cmdsList
.
add
(
"-version"
);
Utils
.
runExec
(
cmdsList
);
// invoke javac to test the tools.jar
cmdsList
.
clear
();
cmdsList
.
add
(
javacCmd
.
getAbsolutePath
());
cmdsList
.
add
(
"-J-verify"
);
cmdsList
.
add
(
"-help"
);
Utils
.
runExec
(
cmdsList
);
}
public
static
void
main
(
String
...
args
)
{
try
{
init
();
testJRE
();
testJDK
();
}
catch
(
IOException
ioe
)
{
throw
new
RuntimeException
(
ioe
);
}
}
}
test/tools/pack200/Pack200Simple.sh
已删除
100644 → 0
浏览文件 @
b1f0edac
#
# Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
# @test Pack200Simple.sh
# @bug 6521334
# @build Pack200Test
# @run shell/timeout=1200 Pack200Simple.sh
# @summary An ad hoc test to verify class-file format.
# @author Kumar Srinivasan
# The goal of this test is to assist javac or other developers
# who modify class file formats, to quickly test those modifications
# without having to build the install workspace. However it must
# be noted that building the install workspace is the only know
# way to prevent build breakages.
# Pack200 developers could use this as a basic smoke-test, however
# please note, there are other more elaborate and thorough tests for
# this very purpose.
# We try a potpouri of things ie. we have pack.conf to setup some
# options as well as a couple of command line options. We also test
# the packing and unpacking mechanism using the Java APIs.
# print error and exit with a message
errorOut
()
{
if
[
"x
$1
"
=
"x"
]
;
then
printf
"Error: Unknown error
\n
"
else
printf
"Error: %s
\n
"
"
$1
"
fi
exit
1
}
# Verify directory context variables are set
if
[
"
${
TESTJAVA
}
"
=
""
]
;
then
errorOut
"TESTJAVA not set. Test cannot execute. Failed."
fi
if
[
"
${
TESTSRC
}
"
=
""
]
;
then
errorOut
"TESTSRC not set. Test cannot execute. Failed."
fi
if
[
"
${
TESTCLASSES
}
"
=
""
]
;
then
errorOut
"TESTCLASSES not set. Test cannot execute. Failed."
fi
# The common java utils we need
PACK200
=
${
TESTJAVA
}
/bin/pack200
UNPACK200
=
${
TESTJAVA
}
/bin/unpack200
JAR
=
${
TESTJAVA
}
/bin/jar
# For Windows and Linux needs the heap to be set, for others ergonomics
# will do the rest. It is important to use ea, which can expose class
# format errors much earlier than later.
OS
=
`
uname
-s
`
case
"
$OS
"
in
Windows
*
|
CYGWIN
*
)
PackOptions
=
"-J-Xmx512m -J-ea"
break
;;
Linux
)
PackOptions
=
"-J-Xmx512m -J-ea"
break
;;
*
)
PackOptions
=
"-J-ea"
;;
esac
# Creates a packfile of choice expects 1 argument the filename
createConfigFile
()
{
# optimize for speed
printf
"pack.effort=1
\n
"
>
$1
# we DO want to know about new attributes
printf
"pack.unknown.attribute=error
\n
"
>>
$1
# optimize for speed
printf
"pack.deflate.hint=false
\n
"
>>
$1
# keep the ordering for easy compare
printf
"pack.keep.class.order=true
\n
"
>>
$1
}
# Tests a given jar, expects 1 argument the fully qualified
# name to a test jar, it writes all output to the current
# directory which is a scratch area.
testAJar
()
{
PackConf
=
"pack.conf"
createConfigFile
$PackConf
# Try some command line options
CLIPackOptions
=
"
$PackOptions
-v --no-gzip --segment-limit=10000 --config-file=
$PackConf
"
jfName
=
`
basename
$1
`
${
PACK200
}
$CLIPackOptions
${
jfName
}
.pack
$1
>
${
jfName
}
.pack.log 2>&1
if
[
$?
!=
0
]
;
then
errorOut
"
$jfName
packing failed"
fi
# We want to test unpack200, therefore we dont use -r with pack
${
UNPACK200
}
-v
${
jfName
}
.pack
$jfName
>
${
jfName
}
.unpack.log 2>&1
if
[
$?
!=
0
]
;
then
errorOut
"
$jfName
unpacking failed"
fi
# A quick crc compare test to ensure a well formed zip
# archive, this is a critical unpack200 behaviour.
unzip
-t
$jfName
>
${
jfName
}
.unzip.log 2>&1
if
[
$?
!=
0
]
;
then
errorOut
"
$jfName
unzip -t test failed"
fi
# The PACK200 signature should be at the top of the log
# this tag is critical for deployment related tools.
head
-5
${
jfName
}
.unzip.log |
grep
PACK200
>
/dev/null 2>&1
if
[
$?
!=
0
]
;
then
errorOut
"
$jfName
PACK200 signature missing"
fi
# we know the size fields don't match, strip 'em out, its
# extremely important to ensure that the date stamps match up.
# Don't EVER sort the output we are checking for correct ordering.
${
JAR
}
-tvf
$1
|
sed
-e
's/^ *[0-9]* //g'
>
${
jfName
}
.ref.txt
${
JAR
}
-tvf
$jfName
|
sed
-e
's/^ *[0-9]* //g'
>
${
jfName
}
.cmp.txt
diff
${
jfName
}
.ref.txt
${
jfName
}
.cmp.txt
>
${
jfName
}
.diff.log 2>&1
if
[
$?
!=
0
]
;
then
errorOut
"
$jfName
files missing"
fi
}
# These JARs are the largest and also the most likely specimens to
# expose class format issues and stress the packer as well.
JLIST
=
"
${
TESTJAVA
}
/lib/tools.jar
${
TESTJAVA
}
/jre/lib/rt.jar"
# Test the Command Line Interfaces (CLI).
mkdir
cliTestDir
_pwd
=
`
pwd
`
cd
cliTestDir
for
jarfile
in
$JLIST
;
do
if
[
-f
$jarfile
]
;
then
testAJar
$jarfile
else
errorOut
"Error: '
$jarFile
' does not exist
\n
Test requires a j2sdk-image
\n
"
fi
done
cd
$_pwd
# Test the Java APIs.
mkdir
apiTestDir
_pwd
=
`
pwd
`
cd
apiTestDir
# Strip out the -J prefixes.
JavaPackOptions
=
`
printf
%s
"
$PackOptions
"
|
sed
-e
's/-J//g'
`
# Test the Java APIs now.
$TESTJAVA
/bin/java
$JavaPackOptions
-cp
$TESTCLASSES
Pack200Test
$JLIST
||
exit
1
cd
$_pwd
exit
0
test/tools/pack200/Pack200Test.java
浏览文件 @
a1c492e8
...
...
@@ -24,111 +24,97 @@
import
java.util.*
;
import
java.io.*
;
import
java.lang.management.ManagementFactory
;
import
java.lang.management.MemoryMXBean
;
import
java.util.jar.*
;
import
java.util.zip.*
;
/*
* Pack200Test.java
*
* @author ksrini
*/
/*
* @test
* @bug 6521334 6712743
* @summary check for memory leaks, test general packer/unpacker functionality\
* using native and java unpackers
* @compile -XDignore.symbol.file Utils.java Pack200Test.java
* @run main/othervm/timeout=1200 -Xmx512m Pack200Test
* @author ksrini
*/
/**
* These tests are very rudimentary smoke tests to ensure that the packing
* unpacking process works on a select set of JARs.
* Tests the packing/unpacking via the APIs.
*/
public
class
Pack200Test
{
private
static
ArrayList
<
File
>
jarList
=
new
ArrayList
<
File
>();
static
final
String
PACKEXT
=
".pack"
;
static
final
MemoryMXBean
mmxbean
=
ManagementFactory
.
getMemoryMXBean
();
static
final
long
m0
=
getUsedMemory
();
static
final
int
LEAK_TOLERANCE
=
20000
;
// OS and GC related variations.
/** Creates a new instance of Pack200Test */
private
Pack200Test
()
{}
static
long
getUsedMemory
()
{
mmxbean
.
gc
();
mmxbean
.
gc
();
mmxbean
.
gc
();
return
mmxbean
.
getHeapMemoryUsage
().
getUsed
()/
1024
;
}
private
static
void
leakCheck
()
throws
Exception
{
long
diff
=
getUsedMemory
()
-
m0
;
System
.
out
.
println
(
" Info: memory diff = "
+
diff
+
"K"
);
if
(
diff
>
LEAK_TOLERANCE
)
{
throw
new
Exception
(
"memory leak detected "
+
diff
);
}
}
private
static
void
doPackUnpack
()
{
for
(
File
in
:
jarList
)
{
Pack200
.
Packer
packer
=
Pack200
.
newPacker
();
Map
<
String
,
String
>
p
=
packer
.
properties
();
// Take the time optimization vs. space
p
.
put
(
packer
.
EFFORT
,
"1"
);
// CAUTION: do not use 0.
// Make the memory consumption as effective as possible
p
.
put
(
packer
.
SEGMENT_LIMIT
,
"10000"
);
// throw an error if an attribute is unrecognized
p
.
put
(
packer
.
UNKNOWN_ATTRIBUTE
,
packer
.
ERROR
);
// ignore all JAR deflation requests to save time
p
.
put
(
packer
.
DEFLATE_HINT
,
packer
.
FALSE
);
// save the file ordering of the original JAR
p
.
put
(
packer
.
KEEP_FILE_ORDER
,
packer
.
TRUE
);
JarOutputStream
javaUnpackerStream
=
null
;
JarOutputStream
nativeUnpackerStream
=
null
;
JarFile
jarFile
=
null
;
try
{
JarFile
jarFile
=
new
JarFile
(
in
);
jarFile
=
new
JarFile
(
in
);
// Write out to a jtreg scratch area
File
OutputStream
fos
=
new
FileOutputStream
(
in
.
getName
()
+
PACK
EXT
);
File
packFile
=
new
File
(
in
.
getName
()
+
Utils
.
PACK_FILE_
EXT
);
System
.
out
.
print
(
"Packing ["
+
in
.
toString
()
+
"]...
"
);
System
.
out
.
print
ln
(
"Packing ["
+
in
.
toString
()
+
"]
"
);
// Call the packer
packer
.
pack
(
jarFile
,
fos
);
Utils
.
pack
(
jarFile
,
packFile
);
jarFile
.
close
();
fos
.
close
();
System
.
out
.
print
(
"Unpacking..."
);
File
f
=
new
File
(
in
.
getName
()
+
PACKEXT
);
leakCheck
();
System
.
out
.
println
(
" Unpacking using java unpacker"
);
File
javaUnpackedJar
=
new
File
(
"java-"
+
in
.
getName
());
// Write out to current directory, jtreg will setup a scratch area
JarOutputStream
jostream
=
new
JarOutputStream
(
new
FileOutputStream
(
in
.
getName
()));
// Unpack the files
Pack200
.
Unpacker
unpacker
=
Pack200
.
newUnpacker
();
// Call the unpacker
unpacker
.
unpack
(
f
,
jostream
);
// Must explicitly close the output.
jostream
.
close
();
System
.
out
.
print
(
"Testing..."
);
javaUnpackerStream
=
new
JarOutputStream
(
new
FileOutputStream
(
javaUnpackedJar
));
Utils
.
unpackj
(
packFile
,
javaUnpackerStream
);
javaUnpackerStream
.
close
();
System
.
out
.
println
(
" Testing...java unpacker"
);
leakCheck
();
// Ok we have unpacked the file, lets test it.
doTest
(
in
);
Utils
.
doCompareVerify
(
in
.
getAbsoluteFile
(),
javaUnpackedJar
);
System
.
out
.
println
(
" Unpacking using native unpacker"
);
// Write out to current directory
File
nativeUnpackedJar
=
new
File
(
"native-"
+
in
.
getName
());
nativeUnpackerStream
=
new
JarOutputStream
(
new
FileOutputStream
(
nativeUnpackedJar
));
Utils
.
unpackn
(
packFile
,
nativeUnpackerStream
);
nativeUnpackerStream
.
close
();
System
.
out
.
println
(
" Testing...native unpacker"
);
leakCheck
();
// the unpackers (native and java) should produce identical bits
// so we use use bit wise compare, the verification compare is
// very expensive wrt. time.
Utils
.
doCompareBitWise
(
javaUnpackedJar
,
nativeUnpackedJar
);
System
.
out
.
println
(
"Done."
);
}
catch
(
Exception
e
)
{
System
.
out
.
println
(
"ERROR: "
+
e
.
getMessage
());
System
.
exit
(
1
);
}
}
}
private
static
ArrayList
<
String
>
getZipFileEntryNames
(
ZipFile
z
)
{
ArrayList
<
String
>
out
=
new
ArrayList
<
String
>();
for
(
ZipEntry
ze
:
Collections
.
list
(
z
.
entries
()))
{
out
.
add
(
ze
.
getName
());
}
return
out
;
}
private
static
void
doTest
(
File
in
)
throws
Exception
{
// make sure all the files in the original jar exists in the other
ArrayList
<
String
>
refList
=
getZipFileEntryNames
(
new
ZipFile
(
in
));
ArrayList
<
String
>
cmpList
=
getZipFileEntryNames
(
new
ZipFile
(
in
.
getName
()));
System
.
out
.
print
(
refList
.
size
()
+
"/"
+
cmpList
.
size
()
+
" entries..."
);
if
(
refList
.
size
()
!=
cmpList
.
size
())
{
throw
new
Exception
(
"Missing: files ?, entries don't match"
);
}
for
(
String
ename:
refList
)
{
if
(!
cmpList
.
contains
(
ename
))
{
throw
new
Exception
(
"Does not contain : "
+
ename
);
}
}
}
private
static
void
doSanity
(
String
[]
args
)
{
for
(
String
s:
args
)
{
File
f
=
new
File
(
s
);
if
(
f
.
exists
())
{
jarList
.
add
(
f
);
}
else
{
System
.
out
.
println
(
"Warning: The JAR file "
+
f
.
toString
()
+
" does not exist,"
);
System
.
out
.
println
(
" this test requires a JDK image, this file will be skipped."
);
throw
new
RuntimeException
(
e
);
}
finally
{
Utils
.
close
(
nativeUnpackerStream
);
Utils
.
close
(
javaUnpackerStream
);
Utils
.
close
((
Closeable
)
jarFile
);
}
}
}
...
...
@@ -137,11 +123,12 @@ public class Pack200Test {
* @param args the command line arguments
*/
public
static
void
main
(
String
[]
args
)
{
if
(
args
.
length
<
1
)
{
System
.
out
.
println
(
"Usage: jar1 jar2 jar3 ....."
);
System
.
exit
(
1
);
}
doSanity
(
args
);
// select the jars carefully, adding more jars will increase the
// testing time, especially for jprt.
jarList
.
add
(
Utils
.
locateJar
(
"tools.jar"
));
jarList
.
add
(
Utils
.
locateJar
(
"rt.jar"
));
jarList
.
add
(
Utils
.
locateJar
(
"golden.jar"
));
System
.
out
.
println
(
jarList
);
doPackUnpack
();
}
}
test/tools/pack200/PackageVersionTest.java
浏览文件 @
a1c492e8
...
...
@@ -22,13 +22,14 @@
* questions.
*/
/**
* @test
* @bug 6712743
* @summary verify package versioning
* @compile -XDignore.symbol.file PackageVersionTest.java
* @run main PackageVersionTest
*/
/*
* @test
* @bug 6712743
* @summary verify package versions
* @compile -XDignore.symbol.file Utils.java PackageVersionTest.java
* @run main PackageVersionTest
* @author ksrini
*/
import
java.io.ByteArrayOutputStream
;
import
java.io.Closeable
;
...
...
@@ -74,14 +75,6 @@ public class PackageVersionTest {
JAVA5_PACKAGE_MINOR_VERSION
);
}
static
void
close
(
Closeable
c
)
{
if
(
c
==
null
)
{
return
;
}
try
{
c
.
close
();
}
catch
(
IOException
ignore
)
{}
}
static
void
createClassFile
(
String
name
)
{
createJavaFile
(
name
);
...
...
@@ -93,7 +86,7 @@ public class PackageVersionTest {
name
.
substring
(
name
.
length
()
-
1
),
name
+
".java"
};
compileJava
(
javacCmds
);
Utils
.
compiler
(
javacCmds
);
}
static
void
createJavaFile
(
String
name
)
{
...
...
@@ -108,22 +101,8 @@ public class PackageVersionTest {
}
catch
(
IOException
ioe
)
{
throw
new
RuntimeException
(
"creation of test file failed"
);
}
finally
{
close
(
ps
);
close
(
fos
);
}
}
static
void
compileJava
(
String
...
javacCmds
)
{
if
(
com
.
sun
.
tools
.
javac
.
Main
.
compile
(
javacCmds
)
!=
0
)
{
throw
new
RuntimeException
(
"compilation failed"
);
}
}
static
void
makeJar
(
String
...
jargs
)
{
sun
.
tools
.
jar
.
Main
jarTool
=
new
sun
.
tools
.
jar
.
Main
(
System
.
out
,
System
.
err
,
"jartool"
);
if
(!
jarTool
.
run
(
jargs
))
{
throw
new
RuntimeException
(
"jar command failed"
);
Utils
.
close
(
ps
);
Utils
.
close
(
fos
);
}
}
...
...
@@ -136,7 +115,7 @@ public class PackageVersionTest {
jarFileName
.
getName
(),
filename
};
makeJ
ar
(
jargs
);
Utils
.
j
ar
(
jargs
);
JarFile
jfin
=
null
;
try
{
...
...
@@ -163,7 +142,7 @@ public class PackageVersionTest {
}
catch
(
IOException
ioe
)
{
throw
new
RuntimeException
(
ioe
.
getMessage
());
}
finally
{
close
(
jfin
);
Utils
.
close
((
Closeable
)
jfin
);
}
}
}
test/tools/pack200/SegmentLimit.java
浏览文件 @
a1c492e8
...
...
@@ -21,22 +21,18 @@
* questions.
*/
/*
*
/*
* @test
* @bug 6575373
* @summary verify default segment limit
* @compile SegmentLimit.java
* @compile
-XDignore.symbol.file Utils.java
SegmentLimit.java
* @run main SegmentLimit
* @author ksrini
*/
import
java.io.BufferedReader
;
import
java.io.Closeable
;
import
java.io.File
;
import
java.io.FileOutputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.InputStreamReader
;
import
java.io.PrintStream
;
import
java.util.ArrayList
;
import
java.util.List
;
/*
* Run this against a large jar file, by default the packer should generate only
...
...
@@ -45,89 +41,36 @@ import java.io.PrintStream;
public
class
SegmentLimit
{
private
static
final
File
javaHome
=
new
File
(
System
.
getProperty
(
"java.home"
));
public
static
void
main
(
String
...
args
)
{
if
(!
javaHome
.
getName
().
endsWith
(
"jre"
))
{
throw
new
RuntimeException
(
"Error: requires an SDK to run"
);
}
File
out
=
new
File
(
"test"
+
Pack200Test
.
PACKEXT
);
File
out
=
new
File
(
"test"
+
Utils
.
PACK_FILE_EXT
);
out
.
delete
();
runPack200
(
out
);
}
static
void
close
(
Closeable
c
)
{
if
(
c
==
null
)
{
return
;
}
try
{
c
.
close
();
}
catch
(
IOException
ignore
)
{}
}
static
void
runPack200
(
File
outFile
)
{
File
binDir
=
new
File
(
javaHome
,
"bin"
);
File
pack200Exe
=
System
.
getProperty
(
"os.name"
).
startsWith
(
"Windows"
)
?
new
File
(
binDir
,
"pack200.exe"
)
:
new
File
(
binDir
,
"pack200"
);
File
sdkHome
=
javaHome
.
getParentFile
();
File
sdkHome
=
Utils
.
JavaSDK
;
File
testJar
=
new
File
(
new
File
(
sdkHome
,
"lib"
),
"tools.jar"
);
System
.
out
.
println
(
"using pack200: "
+
pack200Exe
.
getAbsolutePath
());
String
[]
cmds
=
{
pack200Exe
.
getAbsolutePath
(),
"--effort=1"
,
"--verbose"
,
"--no-gzip"
,
outFile
.
getName
(),
testJar
.
getAbsolutePath
()
};
InputStream
is
=
null
;
BufferedReader
br
=
null
;
InputStreamReader
ir
=
null
;
FileOutputStream
fos
=
null
;
PrintStream
ps
=
null
;
try
{
ProcessBuilder
pb
=
new
ProcessBuilder
(
cmds
);
pb
.
redirectErrorStream
(
true
);
Process
p
=
pb
.
start
();
is
=
p
.
getInputStream
();
ir
=
new
InputStreamReader
(
is
);
br
=
new
BufferedReader
(
ir
);
File
logFile
=
new
File
(
"pack200.log"
);
fos
=
new
FileOutputStream
(
logFile
);
ps
=
new
PrintStream
(
fos
);
String
line
=
br
.
readLine
();
int
count
=
0
;
while
(
line
!=
null
)
{
line
=
line
.
trim
();
if
(
line
.
matches
(
".*Transmitted.*files of.*input bytes in a segment of.*bytes"
))
{
count
++;
}
ps
.
println
(
line
);
line
=
br
.
readLine
();
System
.
out
.
println
(
"using pack200: "
+
Utils
.
getPack200Cmd
());
List
<
String
>
cmdsList
=
new
ArrayList
<
String
>();
cmdsList
.
add
(
Utils
.
getPack200Cmd
());
cmdsList
.
add
(
"--effort=1"
);
cmdsList
.
add
(
"--verbose"
);
cmdsList
.
add
(
"--no-gzip"
);
cmdsList
.
add
(
outFile
.
getName
());
cmdsList
.
add
(
testJar
.
getAbsolutePath
());
List
<
String
>
outList
=
Utils
.
runExec
(
cmdsList
);
int
count
=
0
;
for
(
String
line
:
outList
)
{
System
.
out
.
println
(
line
);
if
(
line
.
matches
(
".*Transmitted.*files of.*input bytes in a segment of.*bytes"
))
{
count
++;
}
p
.
waitFor
();
if
(
p
.
exitValue
()
!=
0
)
{
throw
new
RuntimeException
(
"pack200 failed"
);
}
p
.
destroy
();
if
(
count
>
1
)
{
throw
new
Error
(
"test fails: check for multiple segments("
+
count
+
") in: "
+
logFile
.
getAbsolutePath
());
}
}
catch
(
IOException
ex
)
{
throw
new
RuntimeException
(
ex
.
getMessage
());
}
catch
(
InterruptedException
ignore
){
}
finally
{
close
(
is
);
close
(
ps
);
close
(
fos
);
}
if
(
count
!=
1
)
{
throw
new
Error
(
"test fails: check for 0 or multiple segments"
);
}
}
}
...
...
test/tools/pack200/Utils.java
0 → 100644
浏览文件 @
a1c492e8
/*
* Copyright (c) 2007, 2010 Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import
java.io.BufferedReader
;
import
java.io.ByteArrayOutputStream
;
import
java.io.Closeable
;
import
java.io.File
;
import
java.io.FileFilter
;
import
java.io.FileInputStream
;
import
java.io.FileOutputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.InputStreamReader
;
import
java.io.PrintStream
;
import
java.nio.channels.FileChannel
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.jar.JarFile
;
import
java.util.jar.JarOutputStream
;
import
java.util.jar.Pack200
;
import
java.util.zip.ZipEntry
;
import
java.util.zip.ZipFile
;
/**
*
* @author ksrini
*/
/*
* This class contains all the commonly used utilities used by various tests
* in this directory.
*/
class
Utils
{
static
final
String
JavaHome
=
System
.
getProperty
(
"test.java"
,
System
.
getProperty
(
"java.home"
));
static
final
boolean
IsWindows
=
System
.
getProperty
(
"os.name"
).
startsWith
(
"Windows"
);
static
final
boolean
Is64Bit
=
System
.
getProperty
(
"sun.arch.data.model"
,
"32"
).
equals
(
"64"
);
static
final
File
JavaSDK
=
new
File
(
JavaHome
).
getParentFile
();
static
final
String
PACK_FILE_EXT
=
".pack"
;
static
final
String
JAVA_FILE_EXT
=
".java"
;
static
final
String
CLASS_FILE_EXT
=
".class"
;
static
final
String
JAR_FILE_EXT
=
".jar"
;
static
final
File
TEST_SRC_DIR
=
new
File
(
System
.
getProperty
(
"test.src"
));
static
final
String
VERIFIER_DIR_NAME
=
"pack200-verifier"
;
static
final
File
VerifierJar
=
new
File
(
VERIFIER_DIR_NAME
+
JAR_FILE_EXT
);
private
Utils
()
{}
// all static
static
{
if
(!
JavaHome
.
endsWith
(
"jre"
))
{
throw
new
RuntimeException
(
"Error: requires an SDK to run"
);
}
}
private
static
void
init
()
throws
IOException
{
if
(
VerifierJar
.
exists
())
{
return
;
}
File
srcDir
=
new
File
(
TEST_SRC_DIR
,
VERIFIER_DIR_NAME
);
List
<
File
>
javaFileList
=
findFiles
(
srcDir
,
createFilter
(
JAVA_FILE_EXT
));
File
tmpFile
=
File
.
createTempFile
(
"javac"
,
".tmp"
);
File
classesDir
=
new
File
(
"xclasses"
);
classesDir
.
mkdirs
();
FileOutputStream
fos
=
null
;
PrintStream
ps
=
null
;
try
{
fos
=
new
FileOutputStream
(
tmpFile
);
ps
=
new
PrintStream
(
fos
);
for
(
File
f
:
javaFileList
)
{
ps
.
println
(
f
.
getAbsolutePath
());
}
}
finally
{
close
(
ps
);
close
(
fos
);
}
compiler
(
"-d"
,
"xclasses"
,
"@"
+
tmpFile
.
getAbsolutePath
());
jar
(
"cvfe"
,
VerifierJar
.
getName
(),
"sun.tools.pack.verify.Main"
,
"-C"
,
"xclasses"
,
"."
);
}
static
void
dirlist
(
File
dir
)
{
File
[]
files
=
dir
.
listFiles
();
System
.
out
.
println
(
"--listing "
+
dir
.
getAbsolutePath
()
+
"---"
);
for
(
File
f
:
files
)
{
StringBuffer
sb
=
new
StringBuffer
();
sb
.
append
(
f
.
isDirectory
()
?
"d "
:
"- "
);
sb
.
append
(
f
.
getName
());
System
.
out
.
println
(
sb
);
}
}
static
void
doCompareVerify
(
File
reference
,
File
specimen
)
throws
IOException
{
init
();
List
<
String
>
cmds
=
new
ArrayList
<
String
>();
cmds
.
add
(
getJavaCmd
());
cmds
.
add
(
"-jar"
);
cmds
.
add
(
VerifierJar
.
getName
());
cmds
.
add
(
reference
.
getAbsolutePath
());
cmds
.
add
(
specimen
.
getAbsolutePath
());
cmds
.
add
(
"-O"
);
runExec
(
cmds
);
}
static
void
doCompareBitWise
(
File
reference
,
File
specimen
)
throws
IOException
{
init
();
List
<
String
>
cmds
=
new
ArrayList
<
String
>();
cmds
.
add
(
getJavaCmd
());
cmds
.
add
(
"-jar"
);
cmds
.
add
(
VerifierJar
.
getName
());
cmds
.
add
(
reference
.
getName
());
cmds
.
add
(
specimen
.
getName
());
cmds
.
add
(
"-O"
);
cmds
.
add
(
"-b"
);
runExec
(
cmds
);
}
static
FileFilter
createFilter
(
final
String
extension
)
{
return
new
FileFilter
()
{
@Override
public
boolean
accept
(
File
pathname
)
{
String
name
=
pathname
.
getName
();
if
(
name
.
endsWith
(
extension
))
{
return
true
;
}
return
false
;
}
};
}
static
final
FileFilter
DIR_FILTER
=
new
FileFilter
()
{
public
boolean
accept
(
File
pathname
)
{
if
(
pathname
.
isDirectory
())
{
return
true
;
}
return
false
;
}
};
static
final
FileFilter
FILE_FILTER
=
new
FileFilter
()
{
public
boolean
accept
(
File
pathname
)
{
if
(
pathname
.
isFile
())
{
return
true
;
}
return
false
;
}
};
private
static
void
setFileAttributes
(
File
src
,
File
dst
)
throws
IOException
{
dst
.
setExecutable
(
src
.
canExecute
());
dst
.
setReadable
(
src
.
canRead
());
dst
.
setWritable
(
src
.
canWrite
());
dst
.
setLastModified
(
src
.
lastModified
());
}
static
void
copyFile
(
File
src
,
File
dst
)
throws
IOException
{
if
(
src
.
isDirectory
())
{
dst
.
mkdirs
();
setFileAttributes
(
src
,
dst
);
return
;
}
else
{
File
baseDirFile
=
dst
.
getParentFile
();
if
(!
baseDirFile
.
exists
())
{
baseDirFile
.
mkdirs
();
}
}
FileInputStream
in
=
null
;
FileOutputStream
out
=
null
;
FileChannel
srcChannel
=
null
;
FileChannel
dstChannel
=
null
;
try
{
in
=
new
FileInputStream
(
src
);
out
=
new
FileOutputStream
(
dst
);
srcChannel
=
in
.
getChannel
();
dstChannel
=
out
.
getChannel
();
long
retval
=
srcChannel
.
transferTo
(
0
,
src
.
length
(),
dstChannel
);
if
(
src
.
length
()
!=
dst
.
length
())
{
throw
new
IOException
(
"file copy failed for "
+
src
);
}
}
finally
{
close
(
srcChannel
);
close
(
dstChannel
);
close
(
in
);
close
(
out
);
}
setFileAttributes
(
src
,
dst
);
}
/*
* Suppose a path is provided which consists of a full path
* this method returns the sub path for a full path ex: /foo/bar/baz/foobar.z
* and the base path is /foo/bar it will will return baz/foobar.z.
*/
private
static
String
getEntryPath
(
String
basePath
,
String
fullPath
)
{
if
(!
fullPath
.
startsWith
(
basePath
))
{
return
null
;
}
return
fullPath
.
substring
(
basePath
.
length
());
}
static
String
getEntryPath
(
File
basePathFile
,
File
fullPathFile
)
{
return
getEntryPath
(
basePathFile
.
toString
(),
fullPathFile
.
toString
());
}
public
static
void
recursiveCopy
(
File
src
,
File
dest
)
throws
IOException
{
if
(!
src
.
exists
()
||
!
src
.
canRead
())
{
throw
new
IOException
(
"file not found or readable: "
+
src
);
}
if
(
dest
.
exists
()
&&
!
dest
.
isDirectory
()
&&
!
dest
.
canWrite
())
{
throw
new
IOException
(
"file not found or writeable: "
+
dest
);
}
if
(!
dest
.
exists
())
{
dest
.
mkdirs
();
}
List
<
File
>
a
=
directoryList
(
src
);
for
(
File
f
:
a
)
{
copyFile
(
f
,
new
File
(
dest
,
getEntryPath
(
src
,
f
)));
}
}
static
List
<
File
>
directoryList
(
File
dirname
)
{
List
<
File
>
dirList
=
new
ArrayList
<
File
>();
return
directoryList
(
dirname
,
dirList
,
null
);
}
private
static
List
<
File
>
directoryList
(
File
dirname
,
List
<
File
>
dirList
,
File
[]
dirs
)
{
dirList
.
addAll
(
Arrays
.
asList
(
dirname
.
listFiles
(
FILE_FILTER
)));
dirs
=
dirname
.
listFiles
(
DIR_FILTER
);
for
(
File
f
:
dirs
)
{
if
(
f
.
isDirectory
()
&&
!
f
.
equals
(
dirname
))
{
dirList
.
add
(
f
);
directoryList
(
f
,
dirList
,
dirs
);
}
}
return
dirList
;
}
static
void
recursiveDelete
(
File
dir
)
throws
IOException
{
if
(
dir
.
isFile
())
{
dir
.
delete
();
}
else
if
(
dir
.
isDirectory
())
{
File
[]
entries
=
dir
.
listFiles
();
for
(
int
i
=
0
;
i
<
entries
.
length
;
i
++)
{
if
(
entries
[
i
].
isDirectory
())
{
recursiveDelete
(
entries
[
i
]);
}
entries
[
i
].
delete
();
}
dir
.
delete
();
}
}
static
List
<
File
>
findFiles
(
File
startDir
,
FileFilter
filter
)
throws
IOException
{
List
<
File
>
list
=
new
ArrayList
<
File
>();
findFiles0
(
startDir
,
list
,
filter
);
return
list
;
}
/*
* finds files in the start directory using the the filter, appends
* the files to the dirList.
*/
private
static
void
findFiles0
(
File
startDir
,
List
<
File
>
list
,
FileFilter
filter
)
throws
IOException
{
File
[]
foundFiles
=
startDir
.
listFiles
(
filter
);
list
.
addAll
(
Arrays
.
asList
(
foundFiles
));
File
[]
dirs
=
startDir
.
listFiles
(
DIR_FILTER
);
for
(
File
dir
:
dirs
)
{
findFiles0
(
dir
,
list
,
filter
);
}
}
static
void
close
(
Closeable
c
)
{
if
(
c
==
null
)
{
return
;
}
try
{
c
.
close
();
}
catch
(
IOException
ignore
)
{
}
}
static
void
compiler
(
String
...
javacCmds
)
{
if
(
com
.
sun
.
tools
.
javac
.
Main
.
compile
(
javacCmds
)
!=
0
)
{
throw
new
RuntimeException
(
"compilation failed"
);
}
}
static
void
jar
(
String
...
jargs
)
{
sun
.
tools
.
jar
.
Main
jarTool
=
new
sun
.
tools
.
jar
.
Main
(
System
.
out
,
System
.
err
,
"jartool"
);
if
(!
jarTool
.
run
(
jargs
))
{
throw
new
RuntimeException
(
"jar command failed"
);
}
}
// given a jar file foo.jar will write to foo.pack
static
void
pack
(
JarFile
jarFile
,
File
packFile
)
throws
IOException
{
Pack200
.
Packer
packer
=
Pack200
.
newPacker
();
Map
<
String
,
String
>
p
=
packer
.
properties
();
// Take the time optimization vs. space
p
.
put
(
packer
.
EFFORT
,
"1"
);
// CAUTION: do not use 0.
// Make the memory consumption as effective as possible
p
.
put
(
packer
.
SEGMENT_LIMIT
,
"10000"
);
// ignore all JAR deflation requests to save time
p
.
put
(
packer
.
DEFLATE_HINT
,
packer
.
FALSE
);
// save the file ordering of the original JAR
p
.
put
(
packer
.
KEEP_FILE_ORDER
,
packer
.
TRUE
);
FileOutputStream
fos
=
null
;
try
{
// Write out to a jtreg scratch area
fos
=
new
FileOutputStream
(
packFile
);
// Call the packer
packer
.
pack
(
jarFile
,
fos
);
}
finally
{
close
(
fos
);
}
}
// uses java unpacker, slow but useful to discover issues with the packer
static
void
unpackj
(
File
inFile
,
JarOutputStream
jarStream
)
throws
IOException
{
unpack0
(
inFile
,
jarStream
,
true
);
}
// uses native unpacker using the java APIs
static
void
unpackn
(
File
inFile
,
JarOutputStream
jarStream
)
throws
IOException
{
unpack0
(
inFile
,
jarStream
,
false
);
}
// given a packed file, create the jar file in the current directory.
private
static
void
unpack0
(
File
inFile
,
JarOutputStream
jarStream
,
boolean
useJavaUnpack
)
throws
IOException
{
// Unpack the files
Pack200
.
Unpacker
unpacker
=
Pack200
.
newUnpacker
();
Map
<
String
,
String
>
props
=
unpacker
.
properties
();
if
(
useJavaUnpack
)
{
props
.
put
(
"com.sun.java.util.jar.pack.disable.native"
,
"true"
);
}
// Call the unpacker
unpacker
.
unpack
(
inFile
,
jarStream
);
}
static
byte
[]
getBuffer
(
ZipFile
zf
,
ZipEntry
ze
)
throws
IOException
{
ByteArrayOutputStream
baos
=
new
ByteArrayOutputStream
();
byte
buf
[]
=
new
byte
[
8192
];
InputStream
is
=
null
;
try
{
is
=
zf
.
getInputStream
(
ze
);
int
n
=
is
.
read
(
buf
);
while
(
n
>
0
)
{
baos
.
write
(
buf
,
0
,
n
);
n
=
is
.
read
(
buf
);
}
return
baos
.
toByteArray
();
}
finally
{
close
(
is
);
}
}
static
ArrayList
<
String
>
getZipFileEntryNames
(
ZipFile
z
)
{
ArrayList
<
String
>
out
=
new
ArrayList
<
String
>();
for
(
ZipEntry
ze
:
Collections
.
list
(
z
.
entries
()))
{
out
.
add
(
ze
.
getName
());
}
return
out
;
}
static
List
<
String
>
runExec
(
List
<
String
>
cmdsList
)
{
ArrayList
<
String
>
alist
=
new
ArrayList
<
String
>();
ProcessBuilder
pb
=
new
ProcessBuilder
(
cmdsList
);
Map
<
String
,
String
>
env
=
pb
.
environment
();
pb
.
directory
(
new
File
(
"."
));
dirlist
(
new
File
(
"."
));
for
(
String
x
:
cmdsList
)
{
System
.
out
.
print
(
x
+
" "
);
}
System
.
out
.
println
(
""
);
int
retval
=
0
;
Process
p
=
null
;
InputStreamReader
ir
=
null
;
BufferedReader
rd
=
null
;
InputStream
is
=
null
;
try
{
pb
.
redirectErrorStream
(
true
);
p
=
pb
.
start
();
is
=
p
.
getInputStream
();
ir
=
new
InputStreamReader
(
is
);
rd
=
new
BufferedReader
(
ir
,
8192
);
String
in
=
rd
.
readLine
();
while
(
in
!=
null
)
{
alist
.
add
(
in
);
System
.
out
.
println
(
in
);
in
=
rd
.
readLine
();
}
retval
=
p
.
waitFor
();
if
(
retval
!=
0
)
{
throw
new
RuntimeException
(
"process failed with non-zero exit"
);
}
}
catch
(
Exception
ex
)
{
throw
new
RuntimeException
(
ex
.
getMessage
());
}
finally
{
close
(
rd
);
close
(
ir
);
close
(
is
);
if
(
p
!=
null
)
{
p
.
destroy
();
}
}
return
alist
;
}
static
String
getUnpack200Cmd
()
{
return
getAjavaCmd
(
"unpack200"
);
}
static
String
getPack200Cmd
()
{
return
getAjavaCmd
(
"pack200"
);
}
static
String
getJavaCmd
()
{
return
getAjavaCmd
(
"java"
);
}
static
String
getAjavaCmd
(
String
cmdStr
)
{
File
binDir
=
new
File
(
JavaHome
,
"bin"
);
File
unpack200File
=
IsWindows
?
new
File
(
binDir
,
cmdStr
+
".exe"
)
:
new
File
(
binDir
,
cmdStr
);
String
cmd
=
unpack200File
.
getAbsolutePath
();
if
(!
unpack200File
.
canExecute
())
{
throw
new
RuntimeException
(
"please check"
+
cmd
+
" exists and is executable"
);
}
return
cmd
;
}
private
static
List
<
File
>
locaterCache
=
null
;
// search the source dir and jdk dir for requested file and returns
// the first location it finds.
static
File
locateJar
(
String
name
)
{
try
{
if
(
locaterCache
==
null
)
{
locaterCache
=
new
ArrayList
<
File
>();
locaterCache
.
addAll
(
findFiles
(
TEST_SRC_DIR
,
createFilter
(
JAR_FILE_EXT
)));
locaterCache
.
addAll
(
findFiles
(
JavaSDK
,
createFilter
(
JAR_FILE_EXT
)));
}
for
(
File
f
:
locaterCache
)
{
if
(
f
.
getName
().
equals
(
name
))
{
return
f
;
}
}
throw
new
IOException
(
"file not found: "
+
name
);
}
catch
(
IOException
e
)
{
throw
new
RuntimeException
(
e
);
}
}
}
test/tools/pack200/pack200-verifier/data/README
0 → 100644
浏览文件 @
a1c492e8
The files contained in the golden.jar have been harvested from many
different sources, some are hand-crafted invalid class files (odds directory),
or from random JDK builds.
Generally these files serve to ensure the integrity of the packer and unpacker
by,
1. maximizing the test coverage.
2. exercising all the Bands in the pack200 specification.
2. testing the behavior of the packer with invalid classes.
3. testing the archive integrity, ordering and description (date, sizes,
CRC etc.)
Build:
To rebuild this JAR follow these steps:
1. unzip the golden.jar to some directory lets call it "example"
2. now we can add any directories with files into example.
2. run the script BUILDME.sh as
% sh BUILDME.sh example
Note: the BUILDME.sh is known to work on all Unix platforms as well as Windows
using Cygwin.
The above will create two JAR files in the current directory,
example.jar and example-cls.jar, now the example.jar can be used as the
golden.jar.
To ensure the JAR has been built correctly use jar -tvf and compare the
results of the old jar and the newly built one, note that the compressed sizes
may differ, however the timestamps etc. should be consistent.
Test:
Basic:
% pack200 --repack test.jar golden.jar
Advanced:
Create a pack.conf as follows:
% cat pack.conf
com.sun.java.util.jar.pack.dump.bands=true
% pack200 --no-gzip --config-file=pack.conf \
--verbose golden.jar.pack golden.jar
This command will dump the Bands in a unique directory BD_XXXXXX,
one can inspect the directory to ensure all of the bands are being
generated. Familiarity of the Pack200 specification is suggested.
\ No newline at end of file
test/tools/pack200/pack200-verifier/data/golden.jar
0 → 100644
浏览文件 @
a1c492e8
文件已添加
test/tools/pack200/pack200-verifier/make/build.xml
0 → 100644
浏览文件 @
a1c492e8
<project
name=
"PackageVerify"
default=
"dist"
basedir=
".."
>
<!-- Requires ant 1.6.1+ and JDK 1.6+-->
<!-- set global properties for this build -->
<property
name=
"src"
value=
"${basedir}/src"
/>
<property
name=
"build"
value=
"${basedir}/build"
/>
<property
name=
"dist"
value=
"${basedir}/dist"
/>
<property
name=
"make"
value=
"${basedir}/make"
/>
<property
name=
"classes"
value=
"${build}/classes"
/>
<property
name=
"api"
value=
"${build}/api"
/>
<target
name=
"init"
>
<!-- Create the time stamp -->
<tstamp/>
<!-- Create the build directory structure used by compile -->
<mkdir
dir=
"${build}"
/>
<mkdir
dir=
"${dist}"
/>
<mkdir
dir=
"${classes}"
/>
<mkdir
dir=
"${api}"
/>
</target>
<target
name=
"compile"
depends=
"init"
>
<!-- Compile the java code from ${src} into ${build} -->
<javac
source=
"1.6"
srcdir=
"${src}"
destdir=
"${build}/classes"
verbose=
"no"
debug=
"on"
/>
</target>
<target
name=
"doc"
depends=
"init, compile"
>
<javadoc
source=
"1.6"
sourcepath=
"${src}"
destdir=
"${api}"
/>
</target>
<target
name=
"dist"
depends=
"compile, doc"
>
<!-- Put everything in jar file -->
<jar
destfile=
"${dist}/pack200-verifier.jar"
>
<manifest>
<attribute
name=
"Main-Class"
value=
"sun.tools.pack.verify.Main"
/>
</manifest>
<fileset
dir=
"${classes}"
/>
</jar>
<zip
destfile=
"dist/pack200-verifier-doc.zip"
>
<fileset
dir=
"${api}"
/>
</zip>
</target>
<target
name=
"clean"
>
<!-- Delete the ${build} and ${dist} directory trees -->
<delete
dir=
"${build}"
/>
<delete
dir=
"${dist}"
/>
</target>
</project>
test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/ClassCompare.java
0 → 100644
浏览文件 @
a1c492e8
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package
sun.tools.pack.verify
;
import
java.io.*
;
import
java.util.*
;
import
java.util.jar.*
;
import
xmlkit.*
;
public
class
ClassCompare
{
/*
* @author ksrini
*/
private
static
XMLKit
.
Element
getXMLelement
(
InputStream
is
,
boolean
ignoreUnkAttrs
,
List
<
String
>
ignoreElements
)
throws
IOException
{
ClassReader
cr
=
new
ClassReader
();
cr
.
keepOrder
=
false
;
XMLKit
.
Element
e
=
cr
.
readFrom
(
is
);
if
(
ignoreElements
!=
null
)
{
XMLKit
.
Filter
filter
=
XMLKit
.
elementFilter
(
ignoreElements
);
e
.
removeAllInTree
(
filter
);
}
if
(
ignoreUnkAttrs
==
true
)
{
// This removes any unknown attributes
e
.
removeAllInTree
(
XMLKit
.
elementFilter
(
"Attribute"
));
}
return
e
;
}
private
static
String
getXMLPrettyString
(
XMLKit
.
Element
e
)
throws
IOException
{
StringWriter
out
=
new
StringWriter
();
e
.
writePrettyTo
(
out
);
return
out
.
toString
();
}
private
static
boolean
compareClass0
(
JarFile
jf1
,
JarFile
jf2
,
JarEntry
je
,
boolean
ignoreUnkAttrs
,
List
<
String
>
ignoreElements
)
throws
IOException
{
InputStream
is1
=
jf1
.
getInputStream
(
je
);
InputStream
is2
=
jf2
.
getInputStream
(
je
);
// First we try to compare the bits if they are the same
boolean
bCompare
=
JarFileCompare
.
compareStreams
(
is1
,
is2
);
// If they are the same there is nothing more to do.
if
(
bCompare
)
{
Globals
.
println
(
"+++"
+
je
.
getName
()
+
"+++\t"
+
"b/b:PASS"
);
return
bCompare
;
}
is1
.
close
();
is2
.
close
();
is1
=
jf1
.
getInputStream
(
je
);
is2
=
jf2
.
getInputStream
(
je
);
XMLKit
.
Element
e1
=
getXMLelement
(
is1
,
ignoreUnkAttrs
,
ignoreElements
);
XMLKit
.
Element
e2
=
getXMLelement
(
is2
,
ignoreUnkAttrs
,
ignoreElements
);
Globals
.
print
(
"+++"
+
je
.
getName
()
+
"+++\t"
+
e1
.
size
()
+
"/"
+
e1
.
size
()
+
":"
);
boolean
result
=
true
;
if
(
e1
.
equals
(
e2
))
{
Globals
.
println
(
"PASS"
);
}
else
{
Globals
.
println
(
"FAIL"
);
Globals
.
log
(
"Strings differs"
);
Globals
.
log
(
getXMLPrettyString
(
e1
));
Globals
.
log
(
"----------"
);
Globals
.
log
(
getXMLPrettyString
(
e2
));
result
=
false
;
}
return
result
;
}
/*
* Given two Class Paths could be jars the first being a reference
* will execute a series of comparisons on the classname specified
* The className could be null in which case it will iterate through
* all the classes, otherwise it will compare one class and exit.
*/
public
static
boolean
compareClass
(
String
jar1
,
String
jar2
,
String
className
,
boolean
ignoreUnkAttrs
,
List
<
String
>
ignoreElements
)
throws
IOException
{
Globals
.
println
(
"Unknown attributes ignored:"
+
ignoreUnkAttrs
);
if
(
ignoreElements
!=
null
)
{
Globals
.
println
(
ignoreElements
.
toString
());
}
JarFile
jf1
=
new
JarFile
(
jar1
);
JarFile
jf2
=
new
JarFile
(
jar2
);
boolean
result
=
true
;
if
(
className
==
null
)
{
for
(
JarEntry
je1
:
Collections
.
list
((
Enumeration
<
JarEntry
>)
jf1
.
entries
()))
{
if
(
je1
.
getName
().
endsWith
(
".class"
))
{
JarEntry
je2
=
jf2
.
getJarEntry
(
je1
.
getName
());
boolean
pf
=
compareClass0
(
jf1
,
jf2
,
je1
,
ignoreUnkAttrs
,
ignoreElements
);
if
(
result
==
true
)
{
result
=
pf
;
}
}
}
}
else
{
JarEntry
je1
=
jf1
.
getJarEntry
(
className
);
result
=
compareClass0
(
jf1
,
jf2
,
je1
,
ignoreUnkAttrs
,
ignoreElements
);
}
if
(
result
==
false
)
{
throw
new
RuntimeException
(
"Class structural comparison failure"
);
}
return
result
;
}
public
static
boolean
compareClass
(
String
jar1
,
String
jar2
,
String
className
)
throws
IOException
{
Stack
<
String
>
s
=
new
Stack
();
if
(
Globals
.
ignoreDebugAttributes
())
{
s
=
new
Stack
();
s
.
push
(
"LocalVariable"
);
s
.
push
(
"LocalVariableType"
);
s
.
push
(
"LineNumber"
);
s
.
push
(
"SourceFile"
);
}
return
compareClass
(
jar1
,
jar2
,
className
,
Globals
.
ignoreUnknownAttributes
(),
s
);
}
}
test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/Globals.java
0 → 100644
浏览文件 @
a1c492e8
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* A collection of useful global utilities commonly used.
*/
package
sun.tools.pack.verify
;
import
java.io.*
;
import
java.util.*
;
/*
* @author ksrini
*/
class
Globals
{
private
static
int
errors
=
0
;
private
static
PrintWriter
_pw
=
null
;
private
static
String
_logFileName
=
null
;
private
static
final
String
DEFAULT_LOG_FILE
=
"verifier.log"
;
private
static
boolean
_verbose
=
true
;
private
static
boolean
_ignoreJarDirectories
=
false
;
private
static
boolean
_checkJarClassOrdering
=
true
;
private
static
boolean
_bitWiseClassCompare
=
false
;
// Ignore Deprecated, SourceFile and Synthetic
private
static
boolean
_ignoreCompileAttributes
=
false
;
// Ignore Debug Attributes LocalVariableTable, LocalVariableType,LineNumberTable
private
static
boolean
_ignoreDebugAttributes
=
false
;
private
static
boolean
_ignoreUnknownAttributes
=
false
;
private
static
boolean
_validateClass
=
true
;
private
static
Globals
_instance
=
null
;
static
Globals
getInstance
()
{
if
(
_instance
==
null
)
{
_instance
=
new
Globals
();
_verbose
=
(
System
.
getProperty
(
"sun.tools.pack.verify.verbose"
)
==
null
)
?
false
:
true
;
_ignoreJarDirectories
=
(
System
.
getProperty
(
"ignoreJarDirectories"
)
==
null
)
?
false
:
true
;
}
return
_instance
;
}
static
boolean
ignoreCompileAttributes
()
{
return
_ignoreCompileAttributes
;
}
static
boolean
ignoreDebugAttributes
()
{
return
_ignoreDebugAttributes
;
}
static
boolean
ignoreUnknownAttributes
()
{
return
_ignoreUnknownAttributes
;
}
static
boolean
ignoreJarDirectories
()
{
return
_ignoreJarDirectories
;
}
static
boolean
validateClass
()
{
return
_validateClass
;
}
static
void
setCheckJarClassOrdering
(
boolean
flag
)
{
_checkJarClassOrdering
=
flag
;
}
static
boolean
checkJarClassOrdering
()
{
return
_checkJarClassOrdering
;
}
static
boolean
bitWiseClassCompare
()
{
return
_bitWiseClassCompare
;
}
static
boolean
setBitWiseClassCompare
(
boolean
flag
)
{
return
_bitWiseClassCompare
=
flag
;
}
public
static
boolean
setIgnoreCompileAttributes
(
boolean
flag
)
{
return
_ignoreCompileAttributes
=
flag
;
}
static
boolean
setIgnoreDebugAttributes
(
boolean
flag
)
{
return
_ignoreDebugAttributes
=
flag
;
}
static
boolean
setIgnoreUnknownAttributes
(
boolean
flag
)
{
return
_ignoreUnknownAttributes
=
flag
;
}
static
boolean
setValidateClass
(
boolean
flag
)
{
return
_validateClass
=
flag
;
}
static
int
getErrors
()
{
return
errors
;
}
static
void
trace
(
String
s
)
{
if
(
_verbose
)
{
println
(
s
);
}
}
static
void
print
(
String
s
)
{
_pw
.
print
(
s
);
}
static
void
println
(
String
s
)
{
_pw
.
println
(
s
);
}
static
void
log
(
String
s
)
{
errors
++;
_pw
.
println
(
"ERROR:"
+
s
);
}
static
void
lognoln
(
String
s
)
{
errors
++;
_pw
.
print
(
s
);
}
private
static
PrintWriter
openFile
(
String
fileName
)
{
//Lets create the directory if it does not exist.
File
f
=
new
File
(
fileName
);
File
baseDir
=
f
.
getParentFile
();
if
(
baseDir
!=
null
&&
baseDir
.
exists
()
==
false
)
{
baseDir
.
mkdirs
();
}
try
{
return
new
PrintWriter
(
new
FileWriter
(
f
),
true
);
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
e
);
}
}
private
static
void
closeFile
()
{
_pw
.
flush
();
_pw
.
close
();
}
static
void
printPropsToLog
()
{
println
(
"Log started "
+
new
Date
(
System
.
currentTimeMillis
()));
print
(
System
.
getProperty
(
"java.vm.version"
));
println
(
"\t"
+
System
.
getProperty
(
"java.vm.name"
));
println
(
"System properties"
);
println
(
"\tjava.home="
+
System
.
getProperty
(
"java.home"
));
println
(
"\tjava.class.version="
+
System
.
getProperty
(
"java.class.version"
));
println
(
"\tjava.class.path="
+
System
.
getProperty
(
"java.class.path"
));
println
(
"\tjava.ext.dirs="
+
System
.
getProperty
(
"java.ext.dirs"
));
println
(
"\tos.name="
+
System
.
getProperty
(
"os.name"
));
println
(
"\tos.arch="
+
System
.
getProperty
(
"os.arch"
));
println
(
"\tos.version="
+
System
.
getProperty
(
"os.version"
));
println
(
"\tuser.name="
+
System
.
getProperty
(
"user.name"
));
println
(
"\tuser.home="
+
System
.
getProperty
(
"user.home"
));
println
(
"\tuser.dir="
+
System
.
getProperty
(
"user.dir"
));
println
(
"\tLocale.getDefault="
+
Locale
.
getDefault
());
println
(
"System properties end"
);
}
static
void
openLog
(
String
s
)
{
_logFileName
=
(
s
!=
null
)
?
s
:
"."
+
File
.
separator
+
DEFAULT_LOG_FILE
;
_logFileName
=
(
new
File
(
_logFileName
).
isDirectory
())
?
_logFileName
+
File
.
separator
+
DEFAULT_LOG_FILE
:
_logFileName
;
_pw
=
openFile
(
_logFileName
);
printPropsToLog
();
}
static
void
closeLog
()
{
closeFile
();
}
static
String
getLogFileName
()
{
return
_logFileName
;
}
static
void
diffCharData
(
String
s1
,
String
s2
)
{
boolean
diff
=
false
;
char
[]
c1
=
s1
.
toCharArray
();
char
[]
c2
=
s2
.
toCharArray
();
if
(
c1
.
length
!=
c2
.
length
)
{
diff
=
true
;
Globals
.
log
(
"Length differs: "
+
(
c1
.
length
-
c2
.
length
));
}
// Take the smaller of the two arrays to prevent Array...Exception
int
minlen
=
(
c1
.
length
<
c2
.
length
)
?
c1
.
length
:
c2
.
length
;
for
(
int
i
=
0
;
i
<
c1
.
length
;
i
++)
{
if
(
c1
[
i
]
!=
c2
[
i
])
{
diff
=
true
;
Globals
.
lognoln
(
"\t idx["
+
i
+
"] 0x"
+
Integer
.
toHexString
(
c1
[
i
])
+
"<>"
+
"0x"
+
Integer
.
toHexString
(
c2
[
i
]));
Globals
.
log
(
" -> "
+
c1
[
i
]
+
"<>"
+
c2
[
i
]);
}
}
}
static
void
diffByteData
(
String
s1
,
String
s2
)
{
boolean
diff
=
false
;
byte
[]
b1
=
s1
.
getBytes
();
byte
[]
b2
=
s2
.
getBytes
();
if
(
b1
.
length
!=
b2
.
length
)
{
diff
=
true
;
//(+) b1 is greater, (-) b2 is greater
Globals
.
log
(
"Length differs diff: "
+
(
b1
.
length
-
b2
.
length
));
}
// Take the smaller of the two array to prevent Array...Exception
int
minlen
=
(
b1
.
length
<
b2
.
length
)
?
b1
.
length
:
b2
.
length
;
for
(
int
i
=
0
;
i
<
b1
.
length
;
i
++)
{
if
(
b1
[
i
]
!=
b2
[
i
])
{
diff
=
true
;
Globals
.
log
(
"\t"
+
"idx["
+
i
+
"] 0x"
+
Integer
.
toHexString
(
b1
[
i
])
+
"<>"
+
"0x"
+
Integer
.
toHexString
(
b2
[
i
]));
}
}
}
static
void
dumpToHex
(
String
s
)
{
try
{
dumpToHex
(
s
.
getBytes
(
"UTF-8"
));
}
catch
(
UnsupportedEncodingException
uce
)
{
throw
new
RuntimeException
(
uce
);
}
}
static
void
dumpToHex
(
byte
[]
buffer
)
{
int
linecount
=
0
;
byte
[]
b
=
new
byte
[
16
];
for
(
int
i
=
0
;
i
<
buffer
.
length
;
i
+=
16
)
{
if
(
buffer
.
length
-
i
>
16
)
{
System
.
arraycopy
(
buffer
,
i
,
b
,
0
,
16
);
print16Bytes
(
b
,
linecount
);
linecount
+=
16
;
}
else
{
System
.
arraycopy
(
buffer
,
i
,
b
,
0
,
buffer
.
length
-
i
);
for
(
int
n
=
buffer
.
length
-
(
i
+
1
);
n
<
16
;
n
++)
{
b
[
n
]
=
0
;
}
print16Bytes
(
b
,
linecount
);
linecount
+=
16
;
}
}
Globals
.
log
(
"-----------------------------------------------------------------"
);
}
static
void
print16Bytes
(
byte
[]
buffer
,
int
linecount
)
{
final
int
MAX
=
4
;
Globals
.
lognoln
(
paddedHexString
(
linecount
,
4
)
+
" "
);
for
(
int
i
=
0
;
i
<
buffer
.
length
;
i
+=
2
)
{
int
iOut
=
pack2Bytes2Int
(
buffer
[
i
],
buffer
[
i
+
1
]);
Globals
.
lognoln
(
paddedHexString
(
iOut
,
4
)
+
" "
);
}
Globals
.
lognoln
(
"| "
);
StringBuilder
sb
=
new
StringBuilder
(
new
String
(
buffer
));
for
(
int
i
=
0
;
i
<
buffer
.
length
;
i
++)
{
if
(
Character
.
isISOControl
(
sb
.
charAt
(
i
)))
{
sb
.
setCharAt
(
i
,
'.'
);
}
}
Globals
.
log
(
sb
.
toString
());
}
static
int
pack2Bytes2Int
(
byte
b1
,
byte
b2
)
{
int
out
=
0x0
;
out
+=
b1
;
out
<<=
8
;
out
&=
0x0000ffff
;
out
|=
0x000000ff
&
b2
;
return
out
;
}
static
String
paddedHexString
(
int
n
,
int
max
)
{
char
[]
c
=
Integer
.
toHexString
(
n
).
toCharArray
();
char
[]
out
=
new
char
[
max
];
for
(
int
i
=
0
;
i
<
max
;
i
++)
{
out
[
i
]
=
'0'
;
}
int
offset
=
(
max
-
c
.
length
<
0
)
?
0
:
max
-
c
.
length
;
for
(
int
i
=
0
;
i
<
c
.
length
;
i
++)
{
out
[
offset
+
i
]
=
c
[
i
];
}
return
new
String
(
out
);
}
}
test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/JarFileCompare.java
0 → 100644
浏览文件 @
a1c492e8
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package
sun.tools.pack.verify
;
import
java.io.*
;
import
java.util.*
;
import
java.util.jar.*
;
class
JarFileCompare
{
/*
* @author ksrini
*/
private
static
VerifyTreeSet
getVerifyTreeSet
(
String
jarPath
)
{
VerifyTreeSet
vts
=
new
VerifyTreeSet
();
try
{
JarFile
j
=
new
JarFile
(
jarPath
);
for
(
JarEntry
je
:
Collections
.
list
((
Enumeration
<
JarEntry
>)
j
.
entries
()))
{
if
(!
je
.
isDirectory
())
{
// totally ignore directories
vts
.
add
(
je
.
getName
());
}
}
}
catch
(
IOException
ioe
)
{
throw
new
RuntimeException
(
ioe
);
}
return
vts
;
}
private
static
LinkedList
getListOfClasses
(
String
jarPath
)
{
LinkedList
l
=
new
LinkedList
();
try
{
JarFile
j
=
new
JarFile
(
jarPath
);
for
(
JarEntry
je
:
Collections
.
list
((
Enumeration
<
JarEntry
>)
j
.
entries
()))
{
if
(!
je
.
isDirectory
()
&&
je
.
getName
().
endsWith
(
".class"
))
{
l
.
add
(
je
.
getName
());
}
}
}
catch
(
IOException
ioe
)
{
throw
new
RuntimeException
(
ioe
);
}
return
l
;
}
private
static
void
jarDirectoryCompare
(
String
jarPath1
,
String
jarPath2
)
{
VerifyTreeSet
vts1
=
getVerifyTreeSet
(
jarPath1
);
VerifyTreeSet
vts2
=
getVerifyTreeSet
(
jarPath2
);
TreeSet
diff1
=
vts1
.
diff
(
vts2
);
if
(
diff1
.
size
()
>
0
)
{
Globals
.
log
(
"Left has the following entries that right does not have"
);
Globals
.
log
(
diff1
.
toString
());
}
TreeSet
diff2
=
vts2
.
diff
(
vts1
);
if
(
diff2
.
size
()
>
0
)
{
Globals
.
log
(
"Right has the following entries that left does not have"
);
Globals
.
log
(
diff2
.
toString
());
}
if
(
Globals
.
checkJarClassOrdering
())
{
boolean
error
=
false
;
Globals
.
println
(
"Checking Class Ordering"
);
LinkedList
l1
=
getListOfClasses
(
jarPath1
);
LinkedList
l2
=
getListOfClasses
(
jarPath2
);
if
(
l1
.
size
()
!=
l2
.
size
())
{
error
=
true
;
Globals
.
log
(
"The number of classes differs"
);
Globals
.
log
(
"\t"
+
l1
.
size
()
+
"<>"
+
l2
.
size
());
}
for
(
int
i
=
0
;
i
<
l1
.
size
();
i
++)
{
String
s1
=
(
String
)
l1
.
get
(
i
);
String
s2
=
(
String
)
l2
.
get
(
i
);
if
(
s1
.
compareTo
(
s2
)
!=
0
)
{
error
=
true
;
Globals
.
log
(
"Ordering differs at["
+
i
+
"] = "
+
s1
);
Globals
.
log
(
"\t"
+
s2
);
}
}
}
}
/*
* Returns true if the two Streams are bit identical, and false if they
* are not, no further diagnostics
*/
static
boolean
compareStreams
(
InputStream
is1
,
InputStream
is2
)
{
BufferedInputStream
bis1
=
new
BufferedInputStream
(
is1
,
8192
);
BufferedInputStream
bis2
=
new
BufferedInputStream
(
is2
,
8192
);
try
{
int
i1
,
i2
;
int
count
=
0
;
while
((
i1
=
bis1
.
read
())
==
(
i2
=
bis2
.
read
()))
{
count
++;
if
(
i1
<
0
)
{
// System.out.println("bytes read " + count);
return
true
;
// got all the way to EOF
}
}
return
false
;
// reads returned dif
}
catch
(
IOException
ioe
)
{
throw
new
RuntimeException
(
ioe
);
}
}
private
static
void
checkEntry
(
JarFile
jf1
,
JarFile
jf2
,
JarEntry
je
)
throws
IOException
{
InputStream
is1
=
jf1
.
getInputStream
(
je
);
InputStream
is2
=
jf2
.
getInputStream
(
je
);
if
(
is1
!=
null
&&
is2
!=
null
)
{
if
(!
compareStreams
(
jf1
.
getInputStream
(
je
),
jf2
.
getInputStream
(
je
)))
{
Globals
.
println
(
"+++"
+
je
.
getName
()
+
"+++"
);
Globals
.
log
(
"Error: File:"
+
je
.
getName
()
+
" differs, use a diff util for further diagnostics"
);
}
}
else
{
Globals
.
println
(
"+++"
+
je
.
getName
()
+
"+++"
);
Globals
.
log
(
"Error: File:"
+
je
.
getName
()
+
" not found in "
+
jf2
.
getName
());
}
}
/*
* Given two jar files we compare and see if the jarfiles have all the
* entries. The property ignoreJarDirectories is set to true by default
* which means that Directory entries in a jar may be ignore.
*/
static
void
jarCompare
(
String
jarPath1
,
String
jarPath2
)
{
jarDirectoryCompare
(
jarPath1
,
jarPath2
);
try
{
JarFile
jf1
=
new
JarFile
(
jarPath1
);
JarFile
jf2
=
new
JarFile
(
jarPath2
);
int
nclasses
=
0
;
int
nentries
=
0
;
int
entries_checked
=
0
;
int
classes_checked
=
0
;
for
(
JarEntry
je
:
Collections
.
list
((
Enumeration
<
JarEntry
>)
jf1
.
entries
()))
{
if
(!
je
.
isDirectory
()
&&
!
je
.
getName
().
endsWith
(
".class"
))
{
nentries
++;
}
else
if
(
je
.
getName
().
endsWith
(
".class"
))
{
nclasses
++;
}
}
for
(
JarEntry
je
:
Collections
.
list
((
Enumeration
<
JarEntry
>)
jf1
.
entries
()))
{
if
(
je
.
isDirectory
())
{
continue
;
// Ignore directories
}
if
(!
je
.
getName
().
endsWith
(
".class"
))
{
entries_checked
++;
if
(
je
.
getName
().
compareTo
(
"META-INF/MANIFEST.MF"
)
==
0
)
{
Manifest
mf1
=
new
Manifest
(
jf1
.
getInputStream
(
je
));
Manifest
mf2
=
new
Manifest
(
jf2
.
getInputStream
(
je
));
if
(!
mf1
.
equals
(
mf2
))
{
Globals
.
log
(
"Error: Manifests differ"
);
Globals
.
log
(
"Manifest1"
);
Globals
.
log
(
mf1
.
getMainAttributes
().
entrySet
().
toString
());
Globals
.
log
(
"Manifest2"
);
Globals
.
log
(
mf2
.
getMainAttributes
().
entrySet
().
toString
());
}
}
else
{
checkEntry
(
jf1
,
jf2
,
je
);
}
}
else
if
(
Globals
.
bitWiseClassCompare
()
==
true
)
{
checkEntry
(
jf1
,
jf2
,
je
);
classes_checked
++;
}
}
if
(
Globals
.
bitWiseClassCompare
())
{
Globals
.
println
(
"Class entries checked (byte wise)/Total Class entries = "
+
classes_checked
+
"/"
+
nclasses
);
}
Globals
.
println
(
"Non-class entries checked/Total non-class entries = "
+
entries_checked
+
"/"
+
nentries
);
}
catch
(
IOException
ioe
)
{
throw
new
RuntimeException
(
ioe
);
}
}
}
test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/Main.java
0 → 100644
浏览文件 @
a1c492e8
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// The Main Entry point
package
sun.tools.pack.verify
;
import
java.io.*
;
/**
* This class provides a convenient entry point to the pack200 verifier. This
* compares two classes, either in path or in an archive.
* @see xmlkit.XMLKit
* @author ksrini
*/
public
class
Main
{
private
static
void
syntax
()
{
System
.
out
.
println
(
"Usage: "
);
System
.
out
.
println
(
"\tREFERENCE_CLASSPATH COMPARED_CLASSPATH [Options]"
);
System
.
out
.
println
(
"\tOptions:"
);
System
.
out
.
println
(
"\t\t-O check jar ordering"
);
System
.
out
.
println
(
"\t\t-C ignore compile attributes (Deprecated, SourceFile, Synthetic, )"
);
System
.
out
.
println
(
"\t\t-D ignore debug attributes (LocalVariable, LineNumber)"
);
System
.
out
.
println
(
"\t\t-u ignore unknown attributes"
);
System
.
out
.
println
(
"\t\t-V turn off class validation"
);
System
.
out
.
println
(
"\t\t-c CLASS, compare CLASS only"
);
System
.
out
.
println
(
"\t\t-b Compares all entries bitwise only"
);
System
.
out
.
println
(
"\t\t-l Directory or Log File Name"
);
}
/**
* main entry point to the class file comparator, which compares semantically
* class files in a classpath or an archive.
* @param args String array as described below
* @throws RuntimeException
* <pre>
* Usage:
* ReferenceClasspath SpecimenClaspath [Options]
* Options:
* -O check jar ordering
* -C do not compare compile attributes (Deprecated, SourceFile, Synthetic)
* -D do not compare debug attribute (LocalVariableTable, LineNumberTable)
* -u ignore unknown attributes
* -V turn off class validation
* -c class, compare a single class
* -b compares all entries bitwise (fastest)
* -l directory or log file name
* </pre>
*/
public
static
void
main
(
String
args
[])
{
Globals
.
getInstance
();
if
(
args
==
null
||
args
.
length
<
2
)
{
syntax
();
System
.
exit
(
1
);
}
String
refJarFileName
=
null
;
String
cmpJarFileName
=
null
;
String
specificClass
=
null
;
String
logDirFileName
=
null
;
for
(
int
i
=
0
;
i
<
args
.
length
;
i
++)
{
if
(
i
==
0
)
{
refJarFileName
=
args
[
0
];
continue
;
}
if
(
i
==
1
)
{
cmpJarFileName
=
args
[
1
];
continue
;
}
if
(
args
[
i
].
startsWith
(
"-O"
))
{
Globals
.
setCheckJarClassOrdering
(
true
);
}
if
(
args
[
i
].
startsWith
(
"-b"
))
{
Globals
.
setBitWiseClassCompare
(
true
);
}
if
(
args
[
i
].
startsWith
(
"-C"
))
{
Globals
.
setIgnoreCompileAttributes
(
true
);
}
if
(
args
[
i
].
startsWith
(
"-D"
))
{
Globals
.
setIgnoreDebugAttributes
(
true
);
}
if
(
args
[
i
].
startsWith
(
"-V"
))
{
Globals
.
setValidateClass
(
false
);
}
if
(
args
[
i
].
startsWith
(
"-c"
))
{
i
++;
specificClass
=
args
[
i
].
trim
();
}
if
(
args
[
i
].
startsWith
(
"-u"
))
{
i
++;
Globals
.
setIgnoreUnknownAttributes
(
true
);
}
if
(
args
[
i
].
startsWith
(
"-l"
))
{
i
++;
logDirFileName
=
args
[
i
].
trim
();
}
}
Globals
.
openLog
(
logDirFileName
);
File
refJarFile
=
new
File
(
refJarFileName
);
File
cmpJarFile
=
new
File
(
cmpJarFileName
);
String
f1
=
refJarFile
.
getAbsoluteFile
().
toString
();
String
f2
=
cmpJarFile
.
getAbsoluteFile
().
toString
();
System
.
out
.
println
(
"LogFile:"
+
Globals
.
getLogFileName
());
System
.
out
.
println
(
"Reference JAR:"
+
f1
);
System
.
out
.
println
(
"Compared JAR:"
+
f2
);
Globals
.
println
(
"LogFile:"
+
Globals
.
getLogFileName
());
Globals
.
println
(
"Reference JAR:"
+
f1
);
Globals
.
println
(
"Compared JAR:"
+
f2
);
Globals
.
println
(
"Ignore Compile Attributes:"
+
Globals
.
ignoreCompileAttributes
());
Globals
.
println
(
"Ignore Debug Attributes:"
+
Globals
.
ignoreDebugAttributes
());
Globals
.
println
(
"Ignore Unknown Attributes:"
+
Globals
.
ignoreUnknownAttributes
());
Globals
.
println
(
"Class ordering check:"
+
Globals
.
checkJarClassOrdering
());
Globals
.
println
(
"Class validation check:"
+
Globals
.
validateClass
());
Globals
.
println
(
"Bit-wise compare:"
+
Globals
.
bitWiseClassCompare
());
Globals
.
println
(
"ClassName:"
+
((
specificClass
==
null
)
?
"ALL"
:
specificClass
));
if
(
specificClass
==
null
&&
Globals
.
bitWiseClassCompare
()
==
true
)
{
JarFileCompare
.
jarCompare
(
refJarFileName
,
cmpJarFileName
);
}
else
{
try
{
ClassCompare
.
compareClass
(
refJarFileName
,
cmpJarFileName
,
specificClass
);
}
catch
(
Exception
e
)
{
Globals
.
log
(
"Exception "
+
e
);
throw
new
RuntimeException
(
e
);
}
}
if
(
Globals
.
getErrors
()
>
0
)
{
System
.
out
.
println
(
"FAIL"
);
Globals
.
println
(
"FAIL"
);
System
.
exit
(
Globals
.
getErrors
());
}
System
.
out
.
println
(
"PASS"
);
Globals
.
println
(
"PASS"
);
System
.
exit
(
Globals
.
getErrors
());
}
}
test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/VerifyTreeSet.java
0 → 100644
浏览文件 @
a1c492e8
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package
sun.tools.pack.verify
;
import
java.util.*
;
/*
* @author ksrini
*/
class
VerifyTreeSet
<
K
>
extends
java
.
util
.
TreeSet
{
VerifyTreeSet
()
{
super
();
}
public
VerifyTreeSet
(
Comparator
c
)
{
super
(
c
);
}
public
TreeSet
<
K
>
diff
(
TreeSet
in
)
{
TreeSet
<
K
>
delta
=
(
TreeSet
<
K
>)
this
.
clone
();
delta
.
removeAll
(
in
);
return
delta
;
}
}
test/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java
0 → 100644
浏览文件 @
a1c492e8
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package
xmlkit
;
// -*- mode: java; indent-tabs-mode: nil -*-
import
java.util.*
;
import
java.util.jar.*
;
import
java.lang.reflect.*
;
import
java.io.*
;
import
xmlkit.XMLKit.Element
;
/*
* @author jrose
*/
public
class
ClassReader
extends
ClassSyntax
{
private
static
final
CommandLineParser
CLP
=
new
CommandLineParser
(
""
+
"-source: +> = \n"
+
"-dest: +> = \n"
+
"-encoding: +> = \n"
+
"-jcov $ \n -nojcov !-jcov \n"
+
"-verbose $ \n -noverbose !-verbose \n"
+
"-pretty $ \n -nopretty !-pretty \n"
+
"-keepPath $ \n -nokeepPath !-keepPath \n"
+
"-keepCP $ \n -nokeepCP !-keepCP \n"
+
"-keepBytes $ \n -nokeepBytes !-keepBytes \n"
+
"-parseBytes $ \n -noparseBytes !-parseBytes \n"
+
"-resolveRefs $ \n -noresolveRefs !-resolveRefs \n"
+
"-keepOrder $ \n -nokeepOrder !-keepOrder \n"
+
"-keepSizes $ \n -nokeepSizes !-keepSizes \n"
+
"-continue $ \n -nocontinue !-continue \n"
+
"-attrDef & \n"
+
"-@ >-@ . \n"
+
"- +? \n"
+
"\n"
);
public
static
void
main
(
String
[]
ava
)
throws
IOException
{
ArrayList
<
String
>
av
=
new
ArrayList
<
String
>(
Arrays
.
asList
(
ava
));
HashMap
<
String
,
String
>
props
=
new
HashMap
<
String
,
String
>();
props
.
put
(
"-encoding:"
,
"UTF8"
);
// default
props
.
put
(
"-keepOrder"
,
null
);
// CLI default
props
.
put
(
"-pretty"
,
"1"
);
// CLI default
props
.
put
(
"-continue"
,
"1"
);
// CLI default
CLP
.
parse
(
av
,
props
);
//System.out.println(props+" ++ "+av);
File
source
=
asFile
(
props
.
get
(
"-source:"
));
File
dest
=
asFile
(
props
.
get
(
"-dest:"
));
String
encoding
=
props
.
get
(
"-encoding:"
);
boolean
contError
=
props
.
containsKey
(
"-continue"
);
ClassReader
options
=
new
ClassReader
();
options
.
copyOptionsFrom
(
props
);
/*
if (dest == null && av.size() > 1) {
dest = File.createTempFile("TestOut", ".dir", new File("."));
dest.delete();
if (!dest.mkdir())
throw new RuntimeException("Cannot create "+dest);
System.out.println("Writing results to "+dest);
}
*/
if
(
av
.
isEmpty
())
{
av
.
add
(
"doit"
);
//to enter this loop
}
boolean
readList
=
false
;
for
(
String
a
:
av
)
{
if
(
readList
)
{
readList
=
false
;
InputStream
fin
;
if
(
a
.
equals
(
"-"
))
{
fin
=
System
.
in
;
}
else
{
fin
=
new
FileInputStream
(
a
);
}
BufferedReader
files
=
makeReader
(
fin
,
encoding
);
for
(
String
file
;
(
file
=
files
.
readLine
())
!=
null
;)
{
doFile
(
file
,
source
,
dest
,
options
,
encoding
,
contError
);
}
if
(
fin
!=
System
.
in
)
{
fin
.
close
();
}
}
else
if
(
a
.
equals
(
"-@"
))
{
readList
=
true
;
}
else
if
(
a
.
startsWith
(
"-"
))
{
throw
new
RuntimeException
(
"Bad flag argument: "
+
a
);
}
else
if
(
source
.
getName
().
endsWith
(
".jar"
))
{
doJar
(
a
,
source
,
dest
,
options
,
encoding
,
contError
);
}
else
{
doFile
(
a
,
source
,
dest
,
options
,
encoding
,
contError
);
}
}
}
private
static
File
asFile
(
String
str
)
{
return
(
str
==
null
)
?
null
:
new
File
(
str
);
}
private
static
void
doFile
(
String
a
,
File
source
,
File
dest
,
ClassReader
options
,
String
encoding
,
boolean
contError
)
throws
IOException
{
if
(!
contError
)
{
doFile
(
a
,
source
,
dest
,
options
,
encoding
);
}
else
{
try
{
doFile
(
a
,
source
,
dest
,
options
,
encoding
);
}
catch
(
Exception
ee
)
{
System
.
out
.
println
(
"Error processing "
+
source
+
": "
+
ee
);
}
}
}
private
static
void
doJar
(
String
a
,
File
source
,
File
dest
,
ClassReader
options
,
String
encoding
,
Boolean
contError
)
throws
IOException
{
try
{
JarFile
jf
=
new
JarFile
(
source
);
for
(
JarEntry
je
:
Collections
.
list
((
Enumeration
<
JarEntry
>)
jf
.
entries
()))
{
String
name
=
je
.
getName
();
if
(!
name
.
endsWith
(
".class"
))
{
continue
;
}
doStream
(
name
,
jf
.
getInputStream
(
je
),
dest
,
options
,
encoding
);
}
}
catch
(
IOException
ioe
)
{
if
(
contError
)
{
System
.
out
.
println
(
"Error processing "
+
source
+
": "
+
ioe
);
}
else
{
throw
ioe
;
}
}
}
private
static
void
doStream
(
String
a
,
InputStream
in
,
File
dest
,
ClassReader
options
,
String
encoding
)
throws
IOException
{
File
f
=
new
File
(
a
);
ClassReader
cr
=
new
ClassReader
(
options
);
Element
e
=
cr
.
readFrom
(
in
);
OutputStream
out
;
if
(
dest
==
null
)
{
//System.out.println(e.prettyString());
out
=
System
.
out
;
}
else
{
File
outf
=
new
File
(
dest
,
f
.
isAbsolute
()
?
f
.
getName
()
:
f
.
getPath
());
String
outName
=
outf
.
getName
();
File
outSubdir
=
outf
.
getParentFile
();
outSubdir
.
mkdirs
();
int
extPos
=
outName
.
lastIndexOf
(
'.'
);
if
(
extPos
>
0
)
{
outf
=
new
File
(
outSubdir
,
outName
.
substring
(
0
,
extPos
)
+
".xml"
);
}
out
=
new
FileOutputStream
(
outf
);
}
Writer
outw
=
makeWriter
(
out
,
encoding
);
if
(
options
.
pretty
||
!
options
.
keepOrder
)
{
e
.
writePrettyTo
(
outw
);
}
else
{
e
.
writeTo
(
outw
);
}
if
(
out
==
System
.
out
)
{
outw
.
write
(
"\n"
);
outw
.
flush
();
}
else
{
outw
.
close
();
}
}
private
static
void
doFile
(
String
a
,
File
source
,
File
dest
,
ClassReader
options
,
String
encoding
)
throws
IOException
{
File
inf
=
new
File
(
source
,
a
);
if
(
dest
!=
null
&&
options
.
verbose
)
{
System
.
out
.
println
(
"Reading "
+
inf
);
}
BufferedInputStream
in
=
new
BufferedInputStream
(
new
FileInputStream
(
inf
));
doStream
(
a
,
in
,
dest
,
options
,
encoding
);
}
public
static
BufferedReader
makeReader
(
InputStream
in
,
String
encoding
)
throws
IOException
{
// encoding in DEFAULT, '', UTF8, 8BIT, , or any valid encoding name
if
(
encoding
.
equals
(
"8BIT"
))
{
encoding
=
EIGHT_BIT_CHAR_ENCODING
;
}
if
(
encoding
.
equals
(
"UTF8"
))
{
encoding
=
UTF8_ENCODING
;
}
if
(
encoding
.
equals
(
"DEFAULT"
))
{
encoding
=
null
;
}
if
(
encoding
.
equals
(
"-"
))
{
encoding
=
null
;
}
Reader
inw
;
in
=
new
BufferedInputStream
(
in
);
// add buffering
if
(
encoding
==
null
)
{
inw
=
new
InputStreamReader
(
in
);
}
else
{
inw
=
new
InputStreamReader
(
in
,
encoding
);
}
return
new
BufferedReader
(
inw
);
// add buffering
}
public
static
Writer
makeWriter
(
OutputStream
out
,
String
encoding
)
throws
IOException
{
// encoding in DEFAULT, '', UTF8, 8BIT, , or any valid encoding name
if
(
encoding
.
equals
(
"8BIT"
))
{
encoding
=
EIGHT_BIT_CHAR_ENCODING
;
}
if
(
encoding
.
equals
(
"UTF8"
))
{
encoding
=
UTF8_ENCODING
;
}
if
(
encoding
.
equals
(
"DEFAULT"
))
{
encoding
=
null
;
}
if
(
encoding
.
equals
(
"-"
))
{
encoding
=
null
;
}
Writer
outw
;
if
(
encoding
==
null
)
{
outw
=
new
OutputStreamWriter
(
out
);
}
else
{
outw
=
new
OutputStreamWriter
(
out
,
encoding
);
}
return
new
BufferedWriter
(
outw
);
// add buffering
}
public
Element
result
()
{
return
cfile
;
}
protected
InputStream
in
;
protected
ByteArrayOutputStream
buf
=
new
ByteArrayOutputStream
(
1024
);
protected
byte
cpTag
[];
protected
String
cpName
[];
protected
String
[]
callables
;
// varies
public
static
final
String
REF_PREFIX
=
"#"
;
// input options
public
boolean
pretty
=
false
;
public
boolean
verbose
=
false
;
public
boolean
keepPath
=
false
;
public
boolean
keepCP
=
false
;
public
boolean
keepBytes
=
false
;
public
boolean
parseBytes
=
true
;
public
boolean
resolveRefs
=
true
;
public
boolean
keepOrder
=
true
;
public
boolean
keepSizes
=
false
;
public
ClassReader
()
{
super
.
cfile
=
new
Element
(
"ClassFile"
);
}
public
ClassReader
(
ClassReader
options
)
{
this
();
copyOptionsFrom
(
options
);
}
public
void
copyOptionsFrom
(
ClassReader
options
)
{
pretty
=
options
.
pretty
;
verbose
=
options
.
verbose
;
keepPath
=
options
.
keepPath
;
keepCP
=
options
.
keepCP
;
keepBytes
=
options
.
keepBytes
;
parseBytes
=
options
.
parseBytes
;
resolveRefs
=
options
.
resolveRefs
;
keepSizes
=
options
.
keepSizes
;
keepOrder
=
options
.
keepOrder
;
attrTypes
=
options
.
attrTypes
;
}
public
void
copyOptionsFrom
(
Map
<
String
,
String
>
options
)
{
if
(
options
.
containsKey
(
"-pretty"
))
{
pretty
=
(
options
.
get
(
"-pretty"
)
!=
null
);
}
if
(
options
.
containsKey
(
"-verbose"
))
{
verbose
=
(
options
.
get
(
"-verbose"
)
!=
null
);
}
if
(
options
.
containsKey
(
"-keepPath"
))
{
keepPath
=
(
options
.
get
(
"-keepPath"
)
!=
null
);
}
if
(
options
.
containsKey
(
"-keepCP"
))
{
keepCP
=
(
options
.
get
(
"-keepCP"
)
!=
null
);
}
if
(
options
.
containsKey
(
"-keepBytes"
))
{
keepBytes
=
(
options
.
get
(
"-keepBytes"
)
!=
null
);
}
if
(
options
.
containsKey
(
"-parseBytes"
))
{
parseBytes
=
(
options
.
get
(
"-parseBytes"
)
!=
null
);
}
if
(
options
.
containsKey
(
"-resolveRefs"
))
{
resolveRefs
=
(
options
.
get
(
"-resolveRefs"
)
!=
null
);
}
if
(
options
.
containsKey
(
"-keepSizes"
))
{
keepSizes
=
(
options
.
get
(
"-keepSizes"
)
!=
null
);
}
if
(
options
.
containsKey
(
"-keepOrder"
))
{
keepOrder
=
(
options
.
get
(
"-keepOrder"
)
!=
null
);
}
if
(
options
.
containsKey
(
"-attrDef"
))
{
addAttrTypes
(
options
.
get
(
"-attrDef"
).
split
(
" "
));
}
if
(
options
.
get
(
"-jcov"
)
!=
null
)
{
addJcovAttrTypes
();
}
}
public
Element
readFrom
(
InputStream
in
)
throws
IOException
{
this
.
in
=
in
;
// read the file header
int
magic
=
u4
();
if
(
magic
!=
0xCAFEBABE
)
{
throw
new
RuntimeException
(
"bad magic number "
+
Integer
.
toHexString
(
magic
));
}
cfile
.
setAttr
(
"magic"
,
""
+
magic
);
int
minver
=
u2
();
int
majver
=
u2
();
cfile
.
setAttr
(
"minver"
,
""
+
minver
);
cfile
.
setAttr
(
"majver"
,
""
+
majver
);
readCP
();
readClass
();
return
result
();
}
public
Element
readFrom
(
File
file
)
throws
IOException
{
InputStream
in
=
null
;
try
{
in
=
new
FileInputStream
(
file
);
Element
e
=
readFrom
(
new
BufferedInputStream
(
in
));
if
(
keepPath
)
{
e
.
setAttr
(
"path"
,
file
.
toString
());
}
return
e
;
}
finally
{
if
(
in
!=
null
)
{
in
.
close
();
}
}
}
private
void
readClass
()
throws
IOException
{
klass
=
new
Element
(
"Class"
);
cfile
.
add
(
klass
);
int
flags
=
u2
();
String
thisk
=
cpRef
();
String
superk
=
cpRef
();
klass
.
setAttr
(
"name"
,
thisk
);
boolean
flagsSync
=
((
flags
&
Modifier
.
SYNCHRONIZED
)
!=
0
);
flags
&=
~
Modifier
.
SYNCHRONIZED
;
String
flagString
=
flagString
(
flags
,
klass
);
if
(!
flagsSync
)
{
if
(
flagString
.
length
()
>
0
)
{
flagString
+=
" "
;
}
flagString
+=
"!synchronized"
;
}
klass
.
setAttr
(
"flags"
,
flagString
);
klass
.
setAttr
(
"super"
,
superk
);
for
(
int
len
=
u2
(),
i
=
0
;
i
<
len
;
i
++)
{
String
interk
=
cpRef
();
klass
.
add
(
new
Element
(
"Interface"
,
"name"
,
interk
));
}
Element
fields
=
readMembers
(
"Field"
);
klass
.
addAll
(
fields
);
Element
methods
=
readMembers
(
"Method"
);
if
(!
keepOrder
)
{
methods
.
sort
();
}
klass
.
addAll
(
methods
);
readAttributesFor
(
klass
);
klass
.
trimToSize
();
if
(
keepSizes
)
{
attachTo
(
cfile
,
formatAttrSizes
());
}
if
(
paddingSize
!=
0
)
{
cfile
.
setAttr
(
"padding"
,
""
+
paddingSize
);
}
}
private
Element
readMembers
(
String
kind
)
throws
IOException
{
int
len
=
u2
();
Element
members
=
new
Element
(
len
);
for
(
int
i
=
0
;
i
<
len
;
i
++)
{
Element
member
=
new
Element
(
kind
);
int
flags
=
u2
();
String
name
=
cpRef
();
String
type
=
cpRef
();
member
.
setAttr
(
"name"
,
name
);
member
.
setAttr
(
"type"
,
type
);
member
.
setAttr
(
"flags"
,
flagString
(
flags
,
member
));
readAttributesFor
(
member
);
member
.
trimToSize
();
members
.
add
(
member
);
}
return
members
;
}
protected
String
flagString
(
int
flags
,
Element
holder
)
{
// Superset of Modifier.toString.
int
kind
=
0
;
if
(
holder
.
getName
()
==
"Field"
)
{
kind
=
1
;
}
if
(
holder
.
getName
()
==
"Method"
)
{
kind
=
2
;
}
StringBuffer
sb
=
new
StringBuffer
();
for
(
int
i
=
0
;
flags
!=
0
;
i
++,
flags
>>>=
1
)
{
if
((
flags
&
1
)
!=
0
)
{
if
(
sb
.
length
()
>
0
)
{
sb
.
append
(
' '
);
}
if
(
i
<
modifierNames
.
length
)
{
String
[]
names
=
modifierNames
[
i
];
String
name
=
(
kind
<
names
.
length
)
?
names
[
kind
]
:
null
;
for
(
String
name2
:
names
)
{
if
(
name
!=
null
)
{
break
;
}
name
=
name2
;
}
sb
.
append
(
name
);
}
else
{
sb
.
append
(
"#"
).
append
(
1
<<
i
);
}
}
}
return
sb
.
toString
();
}
private
void
readAttributesFor
(
Element
x
)
throws
IOException
{
Element
prevCurrent
;
Element
y
=
new
Element
();
if
(
x
.
getName
()
==
"Code"
)
{
prevCurrent
=
currentCode
;
currentCode
=
x
;
}
else
{
prevCurrent
=
currentMember
;
currentMember
=
x
;
}
for
(
int
len
=
u2
(),
i
=
0
;
i
<
len
;
i
++)
{
int
ref
=
u2
();
String
uname
=
cpName
(
ref
).
intern
();
String
refName
=
uname
;
if
(!
resolveRefs
)
{
refName
=
(
REF_PREFIX
+
ref
).
intern
();
}
String
qname
=
(
x
.
getName
()
+
"."
+
uname
).
intern
();
String
wname
=
(
"*."
+
uname
).
intern
();
String
type
=
attrTypes
.
get
(
qname
);
if
(
type
==
null
||
""
.
equals
(
type
))
{
type
=
attrTypes
.
get
(
wname
);
}
if
(
""
.
equals
(
type
))
{
type
=
null
;
}
int
size
=
u4
();
int
[]
countVar
=
attrSizes
.
get
(
qname
);
if
(
countVar
==
null
)
{
attrSizes
.
put
(
qname
,
countVar
=
new
int
[
2
]);
}
countVar
[
0
]
+=
1
;
countVar
[
1
]
+=
size
;
buf
.
reset
();
for
(
int
j
=
0
;
j
<
size
;
j
++)
{
buf
.
write
(
u1
());
}
if
(
type
==
null
&&
size
==
0
)
{
y
.
add
(
new
Element
(
uname
));
// <Bridge>, etc.
}
else
if
(
type
==
null
)
{
//System.out.println("Warning: No attribute type description: "+qname);
// write cdata attribute
Element
a
=
new
Element
(
"Attribute"
,
new
String
[]{
"Name"
,
refName
},
buf
.
toString
(
EIGHT_BIT_CHAR_ENCODING
));
a
.
addContent
(
getCPDigest
());
y
.
add
(
a
);
}
else
if
(
type
.
equals
(
""
))
{
// ignore this attribute...
}
else
{
InputStream
in0
=
in
;
int
fileSize0
=
fileSize
;
ByteArrayInputStream
in1
=
new
ByteArrayInputStream
(
buf
.
toByteArray
());
boolean
ok
=
false
;
try
{
in
=
in1
;
// parse according to type desc.
Element
aval
;
if
(
type
.
equals
(
"<Code>..."
))
{
// delve into Code attribute
aval
=
readCode
();
}
else
if
(
type
.
equals
(
"<Frame>..."
))
{
// delve into StackMap attribute
aval
=
readStackMap
(
false
);
}
else
if
(
type
.
equals
(
"<FrameX>..."
))
{
// delve into StackMap attribute
aval
=
readStackMap
(
true
);
}
else
if
(
type
.
startsWith
(
"["
))
{
aval
=
readAttributeCallables
(
type
);
}
else
{
aval
=
readAttribute
(
type
);
}
//System.out.println("attachTo 1 "+y+" <- "+aval);
attachTo
(
y
,
aval
);
if
(
false
&&
in1
.
available
()
!=
0
)
{
throw
new
RuntimeException
(
"extra bytes in "
+
qname
+
" :"
+
in1
.
available
());
}
ok
=
true
;
}
finally
{
in
=
in0
;
fileSize
=
fileSize0
;
if
(!
ok
)
{
System
.
out
.
println
(
"*** Failed to read "
+
type
);
}
}
}
}
if
(
x
.
getName
()
==
"Code"
)
{
currentCode
=
prevCurrent
;
}
else
{
currentMember
=
prevCurrent
;
}
if
(!
keepOrder
)
{
y
.
sort
();
y
.
sortAttrs
();
}
//System.out.println("attachTo 2 "+x+" <- "+y);
attachTo
(
x
,
y
);
}
private
int
fileSize
=
0
;
private
int
paddingSize
=
0
;
private
HashMap
<
String
,
int
[]>
attrSizes
=
new
HashMap
<
String
,
int
[]>();
private
Element
formatAttrSizes
()
{
Element
e
=
new
Element
(
"Sizes"
);
e
.
setAttr
(
"fileSize"
,
""
+
fileSize
);
for
(
Map
.
Entry
<
String
,
int
[]>
ie
:
attrSizes
.
entrySet
())
{
int
[]
countVar
=
ie
.
getValue
();
e
.
add
(
new
Element
(
"AttrSize"
,
"name"
,
ie
.
getKey
().
toString
(),
"count"
,
""
+
countVar
[
0
],
"size"
,
""
+
countVar
[
1
]));
}
return
e
;
}
private
void
attachTo
(
Element
x
,
Object
aval0
)
{
if
(
aval0
==
null
)
{
return
;
}
//System.out.println("attachTo "+x+" : "+aval0);
if
(!(
aval0
instanceof
Element
))
{
x
.
add
(
aval0
);
return
;
}
Element
aval
=
(
Element
)
aval0
;
if
(!
aval
.
isAnonymous
())
{
x
.
add
(
aval
);
return
;
}
for
(
int
imax
=
aval
.
attrSize
(),
i
=
0
;
i
<
imax
;
i
++)
{
//%%
attachAttrTo
(
x
,
aval
.
getAttrName
(
i
),
aval
.
getAttr
(
i
));
}
x
.
addAll
(
aval
);
}
private
void
attachAttrTo
(
Element
x
,
String
aname
,
String
aval
)
{
//System.out.println("attachAttrTo "+x+" : "+aname+"="+aval);
String
aval0
=
x
.
getAttr
(
aname
);
if
(
aval0
!=
null
)
{
aval
=
aval0
+
" "
+
aval
;
}
x
.
setAttr
(
aname
,
aval
);
}
private
Element
readAttributeCallables
(
String
type
)
throws
IOException
{
assert
(
callables
==
null
);
callables
=
getBodies
(
type
);
Element
res
=
readAttribute
(
callables
[
0
]);
callables
=
null
;
return
res
;
}
private
Element
readAttribute
(
String
type
)
throws
IOException
{
//System.out.println("readAttribute "+type);
Element
aval
=
new
Element
();
String
nextAttrName
=
null
;
for
(
int
len
=
type
.
length
(),
next
,
i
=
0
;
i
<
len
;
i
=
next
)
{
String
value
;
switch
(
type
.
charAt
(
i
))
{
case
'<'
:
assert
(
nextAttrName
==
null
);
next
=
type
.
indexOf
(
'>'
,
++
i
);
String
form
=
type
.
substring
(
i
,
next
++);
if
(
form
.
indexOf
(
'='
)
<
0
)
{
// elem_placement = '<' elemname '>'
assert
(
aval
.
attrSize
()
==
0
);
assert
(
aval
.
isAnonymous
());
aval
.
setName
(
form
.
intern
());
}
else
{
// attr_placement = '<' attrname '=' (value)? '>'
int
eqPos
=
form
.
indexOf
(
'='
);
nextAttrName
=
form
.
substring
(
0
,
eqPos
).
intern
();
if
(
eqPos
!=
form
.
length
()
-
1
)
{
value
=
form
.
substring
(
eqPos
+
1
);
attachAttrTo
(
aval
,
nextAttrName
,
value
);
nextAttrName
=
null
;
}
// ...else subsequent type parsing will find the attr value
// and add it as "nextAttrName".
}
continue
;
case
'('
:
next
=
type
.
indexOf
(
')'
,
++
i
);
int
callee
=
Integer
.
parseInt
(
type
.
substring
(
i
,
next
++));
attachTo
(
aval
,
readAttribute
(
callables
[
callee
]));
continue
;
case
'N'
:
// replication = 'N' int '[' type ... ']'
{
int
count
=
getInt
(
type
.
charAt
(
i
+
1
),
false
);
assert
(
count
>=
0
);
next
=
i
+
2
;
String
type1
=
getBody
(
type
,
next
);
next
+=
type1
.
length
()
+
2
;
// skip body and brackets
for
(
int
j
=
0
;
j
<
count
;
j
++)
{
attachTo
(
aval
,
readAttribute
(
type1
));
}
}
continue
;
case
'T'
:
// union = 'T' any_int union_case* '(' ')' '[' body ']'
int
tagValue
;
if
(
type
.
charAt
(++
i
)
==
'S'
)
{
tagValue
=
getInt
(
type
.
charAt
(++
i
),
true
);
}
else
{
tagValue
=
getInt
(
type
.
charAt
(
i
),
false
);
}
attachAttrTo
(
aval
,
"tag"
,
""
+
tagValue
);
// always named "tag"
++
i
;
// skip the int type char
// union_case = '(' uc_tag (',' uc_tag)* ')' '[' body ']'
// uc_tag = ('-')? digit+
for
(
boolean
foundCase
=
false
;;
i
=
next
)
{
assert
(
type
.
charAt
(
i
)
==
'('
);
next
=
type
.
indexOf
(
')'
,
++
i
);
assert
(
next
>=
i
);
if
(
type
.
charAt
(
next
-
1
)
==
'\\'
&&
type
.
charAt
(
next
-
2
)
!=
'\\'
)
// Skip an escaped paren.
{
next
=
type
.
indexOf
(
')'
,
next
+
1
);
}
String
caseStr
=
type
.
substring
(
i
,
next
++);
String
type1
=
getBody
(
type
,
next
);
next
+=
type1
.
length
()
+
2
;
// skip body and brackets
boolean
lastCase
=
(
caseStr
.
length
()
==
0
);
if
(!
foundCase
&&
(
lastCase
||
matchTag
(
tagValue
,
caseStr
)))
{
foundCase
=
true
;
// Execute this body.
attachTo
(
aval
,
readAttribute
(
type1
));
}
if
(
lastCase
)
{
break
;
}
}
continue
;
case
'B'
:
case
'H'
:
case
'I'
:
// int = oneof "BHI"
next
=
i
+
1
;
value
=
""
+
getInt
(
type
.
charAt
(
i
),
false
);
break
;
case
'K'
:
assert
(
"IJFDLQ"
.
indexOf
(
type
.
charAt
(
i
+
1
))
>=
0
);
assert
(
type
.
charAt
(
i
+
2
)
==
'H'
);
// only H works for now
next
=
i
+
3
;
value
=
cpRef
();
break
;
case
'R'
:
assert
(
"CSDFMIU?"
.
indexOf
(
type
.
charAt
(
i
+
1
))
>=
0
);
assert
(
type
.
charAt
(
i
+
2
)
==
'H'
);
// only H works for now
next
=
i
+
3
;
value
=
cpRef
();
break
;
case
'P'
:
// bci = 'P' int
next
=
i
+
2
;
value
=
""
+
getInt
(
type
.
charAt
(
i
+
1
),
false
);
break
;
case
'S'
:
// signed_int = 'S' int
next
=
i
+
2
;
value
=
""
+
getInt
(
type
.
charAt
(
i
+
1
),
true
);
break
;
case
'F'
:
next
=
i
+
2
;
value
=
flagString
(
getInt
(
type
.
charAt
(
i
+
1
),
false
),
currentMember
);
break
;
default
:
throw
new
RuntimeException
(
"bad attr format '"
+
type
.
charAt
(
i
)
+
"': "
+
type
);
}
// store the value
if
(
nextAttrName
!=
null
)
{
attachAttrTo
(
aval
,
nextAttrName
,
value
);
nextAttrName
=
null
;
}
else
{
attachTo
(
aval
,
value
);
}
}
//System.out.println("readAttribute => "+aval);
assert
(
nextAttrName
==
null
);
return
aval
;
}
private
int
getInt
(
char
ch
,
boolean
signed
)
throws
IOException
{
if
(
signed
)
{
switch
(
ch
)
{
case
'B'
:
return
(
byte
)
u1
();
case
'H'
:
return
(
short
)
u2
();
case
'I'
:
return
(
int
)
u4
();
}
}
else
{
switch
(
ch
)
{
case
'B'
:
return
u1
();
case
'H'
:
return
u2
();
case
'I'
:
return
u4
();
}
}
assert
(
"BHIJ"
.
indexOf
(
ch
)
>=
0
);
return
0
;
}
private
Element
readCode
()
throws
IOException
{
int
stack
=
u2
();
int
local
=
u2
();
int
length
=
u4
();
StringBuilder
sb
=
new
StringBuilder
(
length
);
for
(
int
i
=
0
;
i
<
length
;
i
++)
{
sb
.
append
((
char
)
u1
());
}
String
bytecodes
=
sb
.
toString
();
Element
e
=
new
Element
(
"Code"
,
"stack"
,
""
+
stack
,
"local"
,
""
+
local
);
Element
bytes
=
new
Element
(
"Bytes"
,
(
String
[])
null
,
bytecodes
);
if
(
keepBytes
)
{
e
.
add
(
bytes
);
}
if
(
parseBytes
)
{
e
.
add
(
parseByteCodes
(
bytecodes
));
}
for
(
int
len
=
u2
(),
i
=
0
;
i
<
len
;
i
++)
{
int
start
=
u2
();
int
end
=
u2
();
int
catsh
=
u2
();
String
clasz
=
cpRef
();
e
.
add
(
new
Element
(
"Handler"
,
"start"
,
""
+
start
,
"end"
,
""
+
end
,
"catch"
,
""
+
catsh
,
"class"
,
clasz
));
}
readAttributesFor
(
e
);
e
.
trimToSize
();
return
e
;
}
private
Element
parseByteCodes
(
String
bytecodes
)
{
Element
e
=
InstructionSyntax
.
parse
(
bytecodes
);
for
(
Element
ins
:
e
.
elements
())
{
Number
ref
=
ins
.
getAttrNumber
(
"ref"
);
if
(
ref
!=
null
&&
resolveRefs
)
{
int
id
=
ref
.
intValue
();
String
val
=
cpName
(
id
);
if
(
ins
.
getName
().
startsWith
(
"ldc"
))
{
// Yuck: Arb. string cannot be an XML attribute.
ins
.
add
(
val
);
val
=
""
;
byte
tag
=
(
id
>=
0
&&
id
<
cpTag
.
length
)
?
cpTag
[
id
]
:
0
;
if
(
tag
!=
0
)
{
ins
.
setAttrLong
(
"tag"
,
tag
);
}
}
if
(
ins
.
getName
()
==
"invokeinterface"
&&
computeInterfaceNum
(
val
)
==
ins
.
getAttrLong
(
"num"
))
{
ins
.
setAttr
(
"num"
,
null
);
// garbage bytes
}
ins
.
setAttr
(
"ref"
,
null
);
ins
.
setAttr
(
"val"
,
val
);
}
}
return
e
;
}
private
Element
readStackMap
(
boolean
hasXOption
)
throws
IOException
{
Element
result
=
new
Element
();
Element
bytes
=
currentCode
.
findElement
(
"Bytes"
);
assert
(
bytes
!=
null
&&
bytes
.
size
()
==
1
);
int
byteLength
=
((
String
)
bytes
.
get
(
0
)).
length
();
boolean
uoffsetIsU4
=
(
byteLength
>=
(
1
<<
16
));
boolean
ulocalvarIsU4
=
currentCode
.
getAttrLong
(
"local"
)
>=
(
1
<<
16
);
boolean
ustackIsU4
=
currentCode
.
getAttrLong
(
"stack"
)
>=
(
1
<<
16
);
if
(
hasXOption
||
uoffsetIsU4
||
ulocalvarIsU4
||
ustackIsU4
)
{
Element
flags
=
new
Element
(
"StackMapFlags"
);
if
(
hasXOption
)
{
flags
.
setAttr
(
"hasXOption"
,
"true"
);
}
if
(
uoffsetIsU4
)
{
flags
.
setAttr
(
"uoffsetIsU4"
,
"true"
);
}
if
(
ulocalvarIsU4
)
{
flags
.
setAttr
(
"ulocalvarIsU4"
,
"true"
);
}
if
(
ustackIsU4
)
{
flags
.
setAttr
(
"ustackIsU4"
,
"true"
);
}
currentCode
.
add
(
flags
);
}
int
frame_count
=
(
uoffsetIsU4
?
u4
()
:
u2
());
for
(
int
i
=
0
;
i
<
frame_count
;
i
++)
{
int
bci
=
(
uoffsetIsU4
?
u4
()
:
u2
());
int
flags
=
(
hasXOption
?
u1
()
:
0
);
Element
frame
=
new
Element
(
"Frame"
);
result
.
add
(
frame
);
if
(
flags
!=
0
)
{
frame
.
setAttr
(
"flags"
,
""
+
flags
);
}
frame
.
setAttr
(
"bci"
,
""
+
bci
);
// Scan local and stack types in this frame:
final
int
LOCALS
=
0
,
STACK
=
1
;
for
(
int
j
=
LOCALS
;
j
<=
STACK
;
j
++)
{
int
typeSize
;
if
(
j
==
LOCALS
)
{
typeSize
=
(
ulocalvarIsU4
?
u4
()
:
u2
());
}
else
{
// STACK
typeSize
=
(
ustackIsU4
?
u4
()
:
u2
());
}
Element
types
=
new
Element
(
j
==
LOCALS
?
"Local"
:
"Stack"
);
for
(
int
k
=
0
;
k
<
typeSize
;
k
++)
{
int
tag
=
u1
();
Element
type
=
new
Element
(
itemTagName
(
tag
));
types
.
add
(
type
);
switch
(
tag
)
{
case
ITEM_Object:
type
.
setAttr
(
"class"
,
cpRef
());
break
;
case
ITEM_Uninitialized:
case
ITEM_ReturnAddress:
type
.
setAttr
(
"bci"
,
""
+
(
uoffsetIsU4
?
u4
()
:
u2
()));
break
;
}
}
if
(
types
.
size
()
>
0
)
{
frame
.
add
(
types
);
}
}
}
return
result
;
}
private
void
readCP
()
throws
IOException
{
int
cpLen
=
u2
();
cpTag
=
new
byte
[
cpLen
];
cpName
=
new
String
[
cpLen
];
int
cpTem
[][]
=
new
int
[
cpLen
][];
for
(
int
i
=
1
;
i
<
cpLen
;
i
++)
{
cpTag
[
i
]
=
(
byte
)
u1
();
switch
(
cpTag
[
i
])
{
case
CONSTANT_Utf8:
buf
.
reset
();
for
(
int
len
=
u2
(),
j
=
0
;
j
<
len
;
j
++)
{
buf
.
write
(
u1
());
}
cpName
[
i
]
=
buf
.
toString
(
UTF8_ENCODING
);
break
;
case
CONSTANT_Integer:
cpName
[
i
]
=
String
.
valueOf
((
int
)
u4
());
break
;
case
CONSTANT_Float:
cpName
[
i
]
=
String
.
valueOf
(
Float
.
intBitsToFloat
(
u4
()));
break
;
case
CONSTANT_Long:
cpName
[
i
]
=
String
.
valueOf
(
u8
());
i
+=
1
;
break
;
case
CONSTANT_Double:
cpName
[
i
]
=
String
.
valueOf
(
Double
.
longBitsToDouble
(
u8
()));
i
+=
1
;
break
;
case
CONSTANT_Class:
case
CONSTANT_String:
cpTem
[
i
]
=
new
int
[]{
u2
()};
break
;
case
CONSTANT_Fieldref:
case
CONSTANT_Methodref:
case
CONSTANT_InterfaceMethodref:
case
CONSTANT_NameAndType:
cpTem
[
i
]
=
new
int
[]{
u2
(),
u2
()};
break
;
}
}
for
(
int
i
=
1
;
i
<
cpLen
;
i
++)
{
switch
(
cpTag
[
i
])
{
case
CONSTANT_Class:
case
CONSTANT_String:
cpName
[
i
]
=
cpName
[
cpTem
[
i
][
0
]];
break
;
case
CONSTANT_NameAndType:
cpName
[
i
]
=
cpName
[
cpTem
[
i
][
0
]]
+
" "
+
cpName
[
cpTem
[
i
][
1
]];
break
;
}
}
// do fieldref et al after nameandtype are all resolved
for
(
int
i
=
1
;
i
<
cpLen
;
i
++)
{
switch
(
cpTag
[
i
])
{
case
CONSTANT_Fieldref:
case
CONSTANT_Methodref:
case
CONSTANT_InterfaceMethodref:
cpName
[
i
]
=
cpName
[
cpTem
[
i
][
0
]]
+
" "
+
cpName
[
cpTem
[
i
][
1
]];
break
;
}
}
cpool
=
new
Element
(
"ConstantPool"
,
cpName
.
length
);
for
(
int
i
=
0
;
i
<
cpName
.
length
;
i
++)
{
if
(
cpName
[
i
]
==
null
)
{
continue
;
}
cpool
.
add
(
new
Element
(
cpTagName
(
cpTag
[
i
]),
new
String
[]{
"id"
,
""
+
i
},
cpName
[
i
]));
}
if
(
keepCP
)
{
cfile
.
add
(
cpool
);
}
}
private
String
cpRef
()
throws
IOException
{
int
ref
=
u2
();
if
(
resolveRefs
)
{
return
cpName
(
ref
);
}
else
{
return
REF_PREFIX
+
ref
;
}
}
private
String
cpName
(
int
id
)
{
if
(
id
>=
0
&&
id
<
cpName
.
length
)
{
return
cpName
[
id
];
}
else
{
return
"[CP#"
+
Integer
.
toHexString
(
id
)
+
"]"
;
}
}
private
long
u8
()
throws
IOException
{
return
((
long
)
u4
()
<<
32
)
+
(((
long
)
u4
()
<<
32
)
>>>
32
);
}
private
int
u4
()
throws
IOException
{
return
(
u2
()
<<
16
)
+
u2
();
}
private
int
u2
()
throws
IOException
{
return
(
u1
()
<<
8
)
+
u1
();
}
private
int
u1
()
throws
IOException
{
int
x
=
in
.
read
();
if
(
x
<
0
)
{
paddingSize
++;
return
0
;
// error recovery
}
fileSize
++;
assert
(
x
==
(
x
&
0xFF
));
return
x
;
}
}
test/tools/pack200/pack200-verifier/src/xmlkit/ClassSyntax.java
0 → 100644
浏览文件 @
a1c492e8
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package
xmlkit
;
// -*- mode: java; indent-tabs-mode: nil -*-
import
xmlkit.XMLKit.*
;
import
java.util.*
;
import
java.security.MessageDigest
;
import
java.nio.ByteBuffer
;
import
xmlkit.XMLKit.Element
;
/*
* @author jrose
*/
public
abstract
class
ClassSyntax
{
public
interface
GetCPIndex
{
int
getCPIndex
(
int
tag
,
String
name
);
// cp finder
}
public
static
final
int
CONSTANT_Utf8
=
1
,
CONSTANT_Integer
=
3
,
CONSTANT_Float
=
4
,
CONSTANT_Long
=
5
,
CONSTANT_Double
=
6
,
CONSTANT_Class
=
7
,
CONSTANT_String
=
8
,
CONSTANT_Fieldref
=
9
,
CONSTANT_Methodref
=
10
,
CONSTANT_InterfaceMethodref
=
11
,
CONSTANT_NameAndType
=
12
;
private
static
final
String
[]
cpTagName
=
{
/* 0: */
null
,
/* 1: */
"Utf8"
,
/* 2: */
null
,
/* 3: */
"Integer"
,
/* 4: */
"Float"
,
/* 5: */
"Long"
,
/* 6: */
"Double"
,
/* 7: */
"Class"
,
/* 8: */
"String"
,
/* 9: */
"Fieldref"
,
/* 10: */
"Methodref"
,
/* 11: */
"InterfaceMethodref"
,
/* 12: */
"NameAndType"
,
null
};
private
static
final
Set
<
String
>
cpTagNames
;
static
{
Set
<
String
>
set
=
new
HashSet
<
String
>(
Arrays
.
asList
(
cpTagName
));
set
.
remove
(
null
);
cpTagNames
=
Collections
.
unmodifiableSet
(
set
);
}
public
static
final
int
ITEM_Top
=
0
,
// replicates by [1..4,1..4]
ITEM_Integer
=
1
,
// (ditto)
ITEM_Float
=
2
,
ITEM_Double
=
3
,
ITEM_Long
=
4
,
ITEM_Null
=
5
,
ITEM_UninitializedThis
=
6
,
ITEM_Object
=
7
,
ITEM_Uninitialized
=
8
,
ITEM_ReturnAddress
=
9
,
ITEM_LIMIT
=
10
;
private
static
final
String
[]
itemTagName
=
{
"Top"
,
"Integer"
,
"Float"
,
"Double"
,
"Long"
,
"Null"
,
"UninitializedThis"
,
"Object"
,
"Uninitialized"
,
"ReturnAddress"
,};
private
static
final
Set
<
String
>
itemTagNames
;
static
{
Set
<
String
>
set
=
new
HashSet
<
String
>(
Arrays
.
asList
(
itemTagName
));
set
.
remove
(
null
);
itemTagNames
=
Collections
.
unmodifiableSet
(
set
);
}
protected
static
final
HashMap
<
String
,
String
>
attrTypesBacking
;
protected
static
final
Map
<
String
,
String
>
attrTypesInit
;
static
{
HashMap
<
String
,
String
>
at
=
new
HashMap
<
String
,
String
>();
//at.put("*.Deprecated", "<deprecated=true>");
//at.put("*.Synthetic", "<synthetic=true>");
////at.put("Field.ConstantValue", "<constantValue=>KQH");
//at.put("Class.SourceFile", "<sourceFile=>RUH");
at
.
put
(
"Method.Bridge"
,
"<Bridge>"
);
at
.
put
(
"Method.Varargs"
,
"<Varargs>"
);
at
.
put
(
"Class.Enum"
,
"<Enum>"
);
at
.
put
(
"*.Signature"
,
"<Signature>RSH"
);
//at.put("*.Deprecated", "<Deprecated>");
//at.put("*.Synthetic", "<Synthetic>");
at
.
put
(
"Field.ConstantValue"
,
"<ConstantValue>KQH"
);
at
.
put
(
"Class.SourceFile"
,
"<SourceFile>RUH"
);
at
.
put
(
"Class.InnerClasses"
,
"NH[<InnerClass><class=>RCH<outer=>RCH<name=>RUH<flags=>FH]"
);
at
.
put
(
"Code.LineNumberTable"
,
"NH[<LineNumber><bci=>PH<line=>H]"
);
at
.
put
(
"Code.LocalVariableTable"
,
"NH[<LocalVariable><bci=>PH<span=>H<name=>RUH<type=>RSH<slot=>H]"
);
at
.
put
(
"Code.LocalVariableTypeTable"
,
"NH[<LocalVariableType><bci=>PH<span=>H<name=>RUH<type=>RSH<slot=>H]"
);
at
.
put
(
"Method.Exceptions"
,
"NH[<Exception><name=>RCH]"
);
at
.
put
(
"Method.Code"
,
"<Code>..."
);
at
.
put
(
"Code.StackMapTable"
,
"<Frame>..."
);
//at.put("Code.StkMapX", "<FrameX>...");
if
(
true
)
{
at
.
put
(
"Code.StackMapTable"
,
"[NH[<Frame>(1)]]"
+
"[TB"
+
"(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79"
+
",80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95"
+
",96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111"
+
",112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127"
+
")[<SameLocals1StackItemFrame>(4)]"
+
"(247)[<SameLocals1StackItemExtended>H(4)]"
+
"(248)[<Chop3>H]"
+
"(249)[<Chop2>H]"
+
"(250)[<Chop1>H]"
+
"(251)[<SameFrameExtended>H]"
+
"(252)[<Append1>H(4)]"
+
"(253)[<Append2>H(4)(4)]"
+
"(254)[<Append3>H(4)(4)(4)]"
+
"(255)[<FullFrame>H(2)(3)]"
+
"()[<SameFrame>]]"
+
"[NH[<Local>(4)]]"
+
"[NH[<Stack>(4)]]"
+
"[TB"
+
(
"(0)[<Top>]"
+
"(1)[<ItemInteger>](2)[<ItemFloat>](3)[<ItemDouble>](4)[<ItemLong>]"
+
"(5)[<ItemNull>](6)[<ItemUninitializedThis>]"
+
"(7)[<ItemObject><class=>RCH]"
+
"(8)[<ItemUninitialized><bci=>PH]"
+
"()[<ItemUnknown>]]"
));
}
at
.
put
(
"Class.EnclosingMethod"
,
"<EnclosingMethod><class=>RCH<desc=>RDH"
);
//RDNH
// Layouts of metadata attrs:
String
vpf
=
"[<RuntimeVisibleAnnotation>"
;
String
ipf
=
"[<RuntimeInvisibleAnnotation>"
;
String
apf
=
"[<Annotation>"
;
String
mdanno2
=
""
+
"<type=>RSHNH[<Member><name=>RUH(3)]]"
+
(
"[TB"
+
"(\\B,\\C,\\I,\\S,\\Z)[<value=>KIH]"
+
"(\\D)[<value=>KDH]"
+
"(\\F)[<value=>KFH]"
+
"(\\J)[<value=>KJH]"
+
"(\\c)[<class=>RSH]"
+
"(\\e)[<type=>RSH<name=>RUH]"
+
"(\\s)[<String>RUH]"
+
"(\\@)[(2)]"
+
"(\\[)[NH[<Element>(3)]]"
+
"()[]"
+
"]"
);
String
visanno
=
"[NH[(2)]][(1)]"
+
vpf
+
mdanno2
;
String
invanno
=
"[NH[(2)]][(1)]"
+
ipf
+
mdanno2
;
String
vparamanno
=
""
+
"[NB[<RuntimeVisibleParameterAnnotation>(1)]][NH[(2)]]"
+
apf
+
mdanno2
;
String
iparamanno
=
""
+
"[NB[<RuntimeInvisibleParameterAnnotation>(1)]][NH[(2)]]"
+
apf
+
mdanno2
;
String
mdannodef
=
"[<AnnotationDefault>(3)][(1)]"
+
apf
+
mdanno2
;
String
[]
mdplaces
=
{
"Class"
,
"Field"
,
"Method"
};
for
(
String
place
:
mdplaces
)
{
at
.
put
(
place
+
".RuntimeVisibleAnnotations"
,
visanno
);
at
.
put
(
place
+
".RuntimeInvisibleAnnotations"
,
invanno
);
}
at
.
put
(
"Method.RuntimeVisibleParameterAnnotations"
,
vparamanno
);
at
.
put
(
"Method.RuntimeInvisibleParameterAnnotations"
,
iparamanno
);
at
.
put
(
"Method.AnnotationDefault"
,
mdannodef
);
attrTypesBacking
=
at
;
attrTypesInit
=
Collections
.
unmodifiableMap
(
at
);
}
;
private
static
final
String
[]
jcovAttrTypes
=
{
"Code.CoverageTable=NH[<Coverage><bci=>PH<type=>H<line=>I<pos=>I]"
,
"Code.CharacterRangeTable=NH[<CharacterRange><bci=>PH<endbci=>POH<from=>I<to=>I<flag=>H]"
,
"Class.SourceID=<SourceID><id=>RUH"
,
"Class.CompilationID=<CompilationID><id=>RUH"
};
protected
static
final
String
[][]
modifierNames
=
{
{
"public"
},
{
"private"
},
{
"protected"
},
{
"static"
},
{
"final"
},
{
"synchronized"
},
{
null
,
"volatile"
,
"bridge"
},
{
null
,
"transient"
,
"varargs"
},
{
null
,
null
,
"native"
},
{
"interface"
},
{
"abstract"
},
{
"strictfp"
},
{
"synthetic"
},
{
"annotation"
},
{
"enum"
},};
protected
static
final
String
EIGHT_BIT_CHAR_ENCODING
=
"ISO8859_1"
;
protected
static
final
String
UTF8_ENCODING
=
"UTF8"
;
// What XML tags are used by this syntax, apart from attributes?
protected
static
final
Set
<
String
>
nonAttrTags
;
static
{
HashSet
<
String
>
tagSet
=
new
HashSet
<
String
>();
Collections
.
addAll
(
tagSet
,
new
String
[]{
"ConstantPool"
,
// the CP
"Class"
,
// the class
"Interface"
,
// implemented interfaces
"Method"
,
// methods
"Field"
,
// fields
"Handler"
,
// exception handler pseudo-attribute
"Attribute"
,
// unparsed attribute
"Bytes"
,
// bytecodes
"Instructions"
// bytecodes, parsed
});
nonAttrTags
=
Collections
.
unmodifiableSet
(
tagSet
);
}
// Accessors.
public
static
Set
<
String
>
nonAttrTags
()
{
return
nonAttrTags
;
}
public
static
String
cpTagName
(
int
t
)
{
t
&=
0xFF
;
String
ts
=
null
;
if
(
t
<
cpTagName
.
length
)
{
ts
=
cpTagName
[
t
];
}
if
(
ts
!=
null
)
{
return
ts
;
}
return
(
"UnknownTag"
+
(
int
)
t
).
intern
();
}
public
static
int
cpTagValue
(
String
name
)
{
for
(
int
t
=
0
;
t
<
cpTagName
.
length
;
t
++)
{
if
(
name
.
equals
(
cpTagName
[
t
]))
{
return
t
;
}
}
return
0
;
}
public
static
String
itemTagName
(
int
t
)
{
t
&=
0xFF
;
String
ts
=
null
;
if
(
t
<
itemTagName
.
length
)
{
ts
=
itemTagName
[
t
];
}
if
(
ts
!=
null
)
{
return
ts
;
}
return
(
"UnknownItem"
+
(
int
)
t
).
intern
();
}
public
static
int
itemTagValue
(
String
name
)
{
for
(
int
t
=
0
;
t
<
itemTagName
.
length
;
t
++)
{
if
(
name
.
equals
(
itemTagName
[
t
]))
{
return
t
;
}
}
return
-
1
;
}
public
void
addJcovAttrTypes
()
{
addAttrTypes
(
jcovAttrTypes
);
}
// Public methods for declaring attribute types.
protected
Map
<
String
,
String
>
attrTypes
=
attrTypesInit
;
public
void
addAttrType
(
String
opt
)
{
int
eqpos
=
opt
.
indexOf
(
'='
);
addAttrType
(
opt
.
substring
(
0
,
eqpos
),
opt
.
substring
(
eqpos
+
1
));
}
public
void
addAttrTypes
(
String
[]
opts
)
{
for
(
String
opt
:
opts
)
{
addAttrType
(
opt
);
}
}
private
void
checkAttr
(
String
attr
)
{
if
(!
attr
.
startsWith
(
"Class."
)
&&
!
attr
.
startsWith
(
"Field."
)
&&
!
attr
.
startsWith
(
"Method."
)
&&
!
attr
.
startsWith
(
"Code."
)
&&
!
attr
.
startsWith
(
"*."
))
{
throw
new
IllegalArgumentException
(
"attr name must start with 'Class.', etc."
);
}
String
uattr
=
attr
.
substring
(
attr
.
indexOf
(
'.'
)
+
1
);
if
(
nonAttrTags
.
contains
(
uattr
))
{
throw
new
IllegalArgumentException
(
"attr name must not be one of "
+
nonAttrTags
);
}
}
private
void
checkAttrs
(
Map
<
String
,
String
>
at
)
{
for
(
String
attr
:
at
.
keySet
())
{
checkAttr
(
attr
);
}
}
private
void
modAttrs
()
{
if
(
attrTypes
==
attrTypesInit
)
{
// Make modifiable.
attrTypes
=
new
HashMap
<
String
,
String
>(
attrTypesBacking
);
}
}
public
void
addAttrType
(
String
attr
,
String
fmt
)
{
checkAttr
(
attr
);
modAttrs
();
attrTypes
.
put
(
attr
,
fmt
);
}
public
void
addAttrTypes
(
Map
<
String
,
String
>
at
)
{
checkAttrs
(
at
);
modAttrs
();
attrTypes
.
putAll
(
at
);
}
public
Map
<
String
,
String
>
getAttrTypes
()
{
if
(
attrTypes
==
attrTypesInit
)
{
return
attrTypes
;
}
return
Collections
.
unmodifiableMap
(
attrTypes
);
}
public
void
setAttrTypes
(
Map
<
String
,
String
>
at
)
{
checkAttrs
(
at
);
modAttrs
();
attrTypes
.
keySet
().
retainAll
(
at
.
keySet
());
attrTypes
.
putAll
(
at
);
}
// attr format helpers
protected
static
boolean
matchTag
(
int
tagValue
,
String
caseStr
)
{
//System.out.println("matchTag "+tagValue+" in "+caseStr);
for
(
int
pos
=
0
,
max
=
caseStr
.
length
(),
comma
;
pos
<
max
;
pos
=
comma
+
1
)
{
int
caseValue
;
if
(
caseStr
.
charAt
(
pos
)
==
'\\'
)
{
caseValue
=
caseStr
.
charAt
(
pos
+
1
);
comma
=
pos
+
2
;
assert
(
comma
==
max
||
caseStr
.
charAt
(
comma
)
==
','
);
}
else
{
comma
=
caseStr
.
indexOf
(
','
,
pos
);
if
(
comma
<
0
)
{
comma
=
max
;
}
caseValue
=
Integer
.
parseInt
(
caseStr
.
substring
(
pos
,
comma
));
}
if
(
tagValue
==
caseValue
)
{
return
true
;
}
}
return
false
;
}
protected
static
String
[]
getBodies
(
String
type
)
{
ArrayList
<
String
>
bodies
=
new
ArrayList
<
String
>();
for
(
int
i
=
0
;
i
<
type
.
length
();)
{
String
body
=
getBody
(
type
,
i
);
bodies
.
add
(
body
);
i
+=
body
.
length
()
+
2
;
// skip body and brackets
}
return
bodies
.
toArray
(
new
String
[
bodies
.
size
()]);
}
protected
static
String
getBody
(
String
type
,
int
i
)
{
assert
(
type
.
charAt
(
i
)
==
'['
);
int
next
=
++
i
;
// skip bracket
for
(
int
depth
=
1
;
depth
>
0
;
next
++)
{
switch
(
type
.
charAt
(
next
))
{
case
'['
:
depth
++;
break
;
case
']'
:
depth
--;
break
;
case
'('
:
next
=
type
.
indexOf
(
')'
,
next
);
break
;
case
'<'
:
next
=
type
.
indexOf
(
'>'
,
next
);
break
;
}
assert
(
next
>
0
);
}
--
next
;
// get before bracket
assert
(
type
.
charAt
(
next
)
==
']'
);
return
type
.
substring
(
i
,
next
);
}
public
Element
makeCPDigest
(
int
length
)
{
MessageDigest
md
;
try
{
md
=
MessageDigest
.
getInstance
(
"MD5"
);
}
catch
(
java
.
security
.
NoSuchAlgorithmException
ee
)
{
throw
new
Error
(
ee
);
}
int
items
=
0
;
for
(
Element
e
:
cpool
.
elements
())
{
if
(
items
==
length
)
{
break
;
}
if
(
cpTagNames
.
contains
(
e
.
getName
()))
{
items
+=
1
;
md
.
update
((
byte
)
cpTagValue
(
e
.
getName
()));
try
{
md
.
update
(
e
.
getText
().
toString
().
getBytes
(
UTF8_ENCODING
));
}
catch
(
java
.
io
.
UnsupportedEncodingException
ee
)
{
throw
new
Error
(
ee
);
}
}
}
ByteBuffer
bb
=
ByteBuffer
.
wrap
(
md
.
digest
());
String
l0
=
Long
.
toHexString
(
bb
.
getLong
(
0
));
String
l1
=
Long
.
toHexString
(
bb
.
getLong
(
8
));
while
(
l0
.
length
()
<
16
)
{
l0
=
"0"
+
l0
;
}
while
(
l1
.
length
()
<
16
)
{
l1
=
"0"
+
l1
;
}
return
new
Element
(
"Digest"
,
"length"
,
""
+
items
,
"bytes"
,
l0
+
l1
);
}
public
Element
getCPDigest
(
int
length
)
{
if
(
length
==
-
1
)
{
length
=
cpool
.
countAll
(
XMLKit
.
elementFilter
(
cpTagNames
));
}
for
(
Element
md
:
cpool
.
findAllElements
(
"Digest"
).
elements
())
{
if
(
md
.
getAttrLong
(
"length"
)
==
length
)
{
return
md
;
}
}
Element
md
=
makeCPDigest
(
length
);
cpool
.
add
(
md
);
return
md
;
}
public
Element
getCPDigest
()
{
return
getCPDigest
(-
1
);
}
public
boolean
checkCPDigest
(
Element
md
)
{
return
md
.
equals
(
getCPDigest
((
int
)
md
.
getAttrLong
(
"length"
)));
}
public
static
int
computeInterfaceNum
(
String
intMethRef
)
{
intMethRef
=
intMethRef
.
substring
(
1
+
intMethRef
.
lastIndexOf
(
' '
));
if
(!
intMethRef
.
startsWith
(
"("
))
{
return
-
1
;
}
int
signum
=
1
;
// start with one for "this"
scanSig:
for
(
int
i
=
1
;
i
<
intMethRef
.
length
();
i
++)
{
char
ch
=
intMethRef
.
charAt
(
i
);
signum
++;
switch
(
ch
)
{
case
')'
:
--
signum
;
break
scanSig
;
case
'L'
:
i
=
intMethRef
.
indexOf
(
';'
,
i
);
break
;
case
'['
:
while
(
ch
==
'['
)
{
ch
=
intMethRef
.
charAt
(++
i
);
}
if
(
ch
==
'L'
)
{
i
=
intMethRef
.
indexOf
(
';'
,
i
);
}
break
;
}
}
int
num
=
(
signum
<<
8
)
|
0
;
//System.out.println("computeInterfaceNum "+intMethRef+" => "+num);
return
num
;
}
// Protected state for representing the class file.
protected
Element
cfile
;
// <ClassFile ...>
protected
Element
cpool
;
// <ConstantPool ...>
protected
Element
klass
;
// <Class ...>
protected
Element
currentMember
;
// varies during scans
protected
Element
currentCode
;
// varies during scans
}
test/tools/pack200/pack200-verifier/src/xmlkit/ClassWriter.java
0 → 100644
浏览文件 @
a1c492e8
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package
xmlkit
;
// -*- mode: java; indent-tabs-mode: nil -*-
import
java.util.*
;
import
java.lang.reflect.*
;
import
java.io.*
;
import
xmlkit.XMLKit.Element
;
/*
* @author jrose
*/
public
class
ClassWriter
extends
ClassSyntax
implements
ClassSyntax
.
GetCPIndex
{
private
static
final
CommandLineParser
CLP
=
new
CommandLineParser
(
""
+
"-source: +> = \n"
+
"-dest: +> = \n"
+
"-encoding: +> = \n"
+
"-parseBytes $ \n"
+
"- *? \n"
+
"\n"
);
public
static
void
main
(
String
[]
ava
)
throws
IOException
{
ArrayList
<
String
>
av
=
new
ArrayList
<
String
>(
Arrays
.
asList
(
ava
));
HashMap
<
String
,
String
>
props
=
new
HashMap
<
String
,
String
>();
props
.
put
(
"-encoding:"
,
"UTF8"
);
// default
CLP
.
parse
(
av
,
props
);
File
source
=
asFile
(
props
.
get
(
"-source:"
));
File
dest
=
asFile
(
props
.
get
(
"-dest:"
));
String
encoding
=
props
.
get
(
"-encoding:"
);
boolean
parseBytes
=
props
.
containsKey
(
"-parseBytes"
);
boolean
destMade
=
false
;
for
(
String
a
:
av
)
{
File
f
;
File
inf
=
new
File
(
source
,
a
);
System
.
out
.
println
(
"Reading "
+
inf
);
Element
e
;
if
(
inf
.
getName
().
endsWith
(
".class"
))
{
ClassReader
cr
=
new
ClassReader
();
cr
.
parseBytes
=
parseBytes
;
e
=
cr
.
readFrom
(
inf
);
f
=
new
File
(
a
);
}
else
if
(
inf
.
getName
().
endsWith
(
".xml"
))
{
InputStream
in
=
new
FileInputStream
(
inf
);
Reader
inw
=
ClassReader
.
makeReader
(
in
,
encoding
);
e
=
XMLKit
.
readFrom
(
inw
);
e
.
findAllInTree
(
XMLKit
.
and
(
XMLKit
.
elementFilter
(
nonAttrTags
()),
XMLKit
.
methodFilter
(
Element
.
method
(
"trimText"
))));
//System.out.println(e);
inw
.
close
();
f
=
new
File
(
a
.
substring
(
0
,
a
.
length
()
-
".xml"
.
length
())
+
".class"
);
}
else
{
System
.
out
.
println
(
"Warning: unknown input "
+
a
);
continue
;
}
// Now write it:
if
(!
destMade
)
{
destMade
=
true
;
if
(
dest
==
null
)
{
dest
=
File
.
createTempFile
(
"TestOut"
,
".dir"
,
new
File
(
"."
));
dest
.
delete
();
System
.
out
.
println
(
"Writing results to "
+
dest
);
}
if
(!(
dest
.
isDirectory
()
||
dest
.
mkdir
()))
{
throw
new
RuntimeException
(
"Cannot create "
+
dest
);
}
}
File
outf
=
new
File
(
dest
,
f
.
isAbsolute
()
?
f
.
getName
()
:
f
.
getPath
());
outf
.
getParentFile
().
mkdirs
();
new
ClassWriter
(
e
).
writeTo
(
outf
);
}
}
private
static
File
asFile
(
String
str
)
{
return
(
str
==
null
)
?
null
:
new
File
(
str
);
}
public
void
writeTo
(
File
file
)
throws
IOException
{
OutputStream
out
=
null
;
try
{
out
=
new
BufferedOutputStream
(
new
FileOutputStream
(
file
));
writeTo
(
out
);
}
finally
{
if
(
out
!=
null
)
{
out
.
close
();
}
}
}
protected
String
[]
callables
;
// varies
protected
int
cpoolSize
=
0
;
protected
HashMap
<
String
,
String
>
attrTypesByTag
;
protected
OutputStream
out
;
protected
HashMap
<
String
,
int
[]>
cpMap
=
new
HashMap
<
String
,
int
[]>();
protected
ArrayList
<
ByteArrayOutputStream
>
attrBufs
=
new
ArrayList
<
ByteArrayOutputStream
>();
private
void
setupAttrTypes
()
{
attrTypesByTag
=
new
HashMap
<
String
,
String
>();
for
(
String
key
:
attrTypes
.
keySet
())
{
String
pfx
=
key
.
substring
(
0
,
key
.
indexOf
(
'.'
)
+
1
);
String
val
=
attrTypes
.
get
(
key
);
int
pos
=
val
.
indexOf
(
'<'
);
if
(
pos
>=
0
)
{
String
tag
=
val
.
substring
(
pos
+
1
,
val
.
indexOf
(
'>'
,
pos
));
attrTypesByTag
.
put
(
pfx
+
tag
,
key
);
}
}
//System.out.println("attrTypesByTag: "+attrTypesByTag);
}
protected
ByteArrayOutputStream
getAttrBuf
()
{
int
nab
=
attrBufs
.
size
();
if
(
nab
==
0
)
{
return
new
ByteArrayOutputStream
(
1024
);
}
ByteArrayOutputStream
ab
=
attrBufs
.
get
(
nab
-
1
);
attrBufs
.
remove
(
nab
-
1
);
return
ab
;
}
protected
void
putAttrBuf
(
ByteArrayOutputStream
ab
)
{
ab
.
reset
();
attrBufs
.
add
(
ab
);
}
public
ClassWriter
(
Element
root
)
{
this
(
root
,
null
);
}
public
ClassWriter
(
Element
root
,
ClassSyntax
cr
)
{
if
(
cr
!=
null
)
{
attrTypes
=
cr
.
attrTypes
;
}
setupAttrTypes
();
if
(
root
.
getName
()
==
"ClassFile"
)
{
cfile
=
root
;
cpool
=
root
.
findElement
(
"ConstantPool"
);
klass
=
root
.
findElement
(
"Class"
);
}
else
if
(
root
.
getName
()
==
"Class"
)
{
cfile
=
new
Element
(
"ClassFile"
,
new
String
[]{
"magic"
,
String
.
valueOf
(
0xCAFEBABE
),
"minver"
,
"0"
,
"majver"
,
"46"
,});
cpool
=
new
Element
(
"ConstantPool"
);
klass
=
root
;
}
else
{
throw
new
IllegalArgumentException
(
"bad element type "
+
root
.
getName
());
}
if
(
cpool
==
null
)
{
cpool
=
new
Element
(
"ConstantPool"
);
}
int
cpLen
=
1
+
cpool
.
size
();
for
(
Element
c
:
cpool
.
elements
())
{
int
id
=
(
int
)
c
.
getAttrLong
(
"id"
);
int
tag
=
cpTagValue
(
c
.
getName
());
setCPIndex
(
tag
,
c
.
getText
().
toString
(),
id
);
switch
(
tag
)
{
case
CONSTANT_Long:
case
CONSTANT_Double:
cpLen
+=
1
;
}
}
cpoolSize
=
cpLen
;
}
public
int
findCPIndex
(
int
tag
,
String
name
)
{
if
(
name
==
null
)
{
return
0
;
}
int
[]
ids
=
cpMap
.
get
(
name
.
toString
());
return
(
ids
==
null
)
?
0
:
ids
[
tag
];
}
public
int
getCPIndex
(
int
tag
,
String
name
)
{
//System.out.println("getCPIndex "+cpTagName(tag)+" "+name);
if
(
name
==
null
)
{
return
0
;
}
int
id
=
findCPIndex
(
tag
,
name
);
if
(
id
==
0
)
{
id
=
cpoolSize
;
cpoolSize
+=
1
;
setCPIndex
(
tag
,
name
,
id
);
cpool
.
add
(
new
Element
(
cpTagName
(
tag
),
new
String
[]{
"id"
,
""
+
id
},
new
Object
[]{
name
}));
int
pos
;
switch
(
tag
)
{
case
CONSTANT_Long:
case
CONSTANT_Double:
cpoolSize
+=
1
;
break
;
case
CONSTANT_Class:
case
CONSTANT_String:
getCPIndex
(
CONSTANT_Utf8
,
name
);
break
;
case
CONSTANT_Fieldref:
case
CONSTANT_Methodref:
case
CONSTANT_InterfaceMethodref:
pos
=
name
.
indexOf
(
' '
);
getCPIndex
(
CONSTANT_Class
,
name
.
substring
(
0
,
pos
));
getCPIndex
(
CONSTANT_NameAndType
,
name
.
substring
(
pos
+
1
));
break
;
case
CONSTANT_NameAndType:
pos
=
name
.
indexOf
(
' '
);
getCPIndex
(
CONSTANT_Utf8
,
name
.
substring
(
0
,
pos
));
getCPIndex
(
CONSTANT_Utf8
,
name
.
substring
(
pos
+
1
));
break
;
}
}
return
id
;
}
public
void
setCPIndex
(
int
tag
,
String
name
,
int
id
)
{
//System.out.println("setCPIndex id="+id+" tag="+tag+" name="+name);
int
[]
ids
=
cpMap
.
get
(
name
);
if
(
ids
==
null
)
{
cpMap
.
put
(
name
,
ids
=
new
int
[
13
]);
}
if
(
ids
[
tag
]
!=
0
&&
ids
[
tag
]
!=
id
)
{
System
.
out
.
println
(
"Warning: Duplicate CP entries for "
+
ids
[
tag
]
+
" and "
+
id
);
}
//assert(ids[tag] == 0 || ids[tag] == id);
ids
[
tag
]
=
id
;
}
public
int
parseFlags
(
String
flagString
)
{
int
flags
=
0
;
int
i
=
-
1
;
for
(
String
[]
names
:
modifierNames
)
{
++
i
;
for
(
String
name
:
names
)
{
if
(
name
==
null
)
{
continue
;
}
int
pos
=
flagString
.
indexOf
(
name
);
if
(
pos
>=
0
)
{
flags
|=
(
1
<<
i
);
}
}
}
return
flags
;
}
public
void
writeTo
(
OutputStream
realOut
)
throws
IOException
{
OutputStream
headOut
=
realOut
;
ByteArrayOutputStream
tailOut
=
new
ByteArrayOutputStream
();
// write the body of the class file first
this
.
out
=
tailOut
;
writeClass
();
// write the file header last
this
.
out
=
headOut
;
u4
((
int
)
cfile
.
getAttrLong
(
"magic"
));
u2
((
int
)
cfile
.
getAttrLong
(
"minver"
));
u2
((
int
)
cfile
.
getAttrLong
(
"majver"
));
writeCP
();
// recopy the file tail
this
.
out
=
null
;
tailOut
.
writeTo
(
realOut
);
}
void
writeClass
()
throws
IOException
{
int
flags
=
parseFlags
(
klass
.
getAttr
(
"flags"
));
flags
^=
Modifier
.
SYNCHRONIZED
;
u2
(
flags
);
cpRef
(
CONSTANT_Class
,
klass
.
getAttr
(
"name"
));
cpRef
(
CONSTANT_Class
,
klass
.
getAttr
(
"super"
));
Element
interfaces
=
klass
.
findAllElements
(
"Interface"
);
u2
(
interfaces
.
size
());
for
(
Element
e
:
interfaces
.
elements
())
{
cpRef
(
CONSTANT_Class
,
e
.
getAttr
(
"name"
));
}
for
(
int
isMethod
=
0
;
isMethod
<=
1
;
isMethod
++)
{
Element
members
=
klass
.
findAllElements
(
isMethod
!=
0
?
"Method"
:
"Field"
);
u2
(
members
.
size
());
for
(
Element
m
:
members
.
elements
())
{
writeMember
(
m
,
isMethod
!=
0
);
}
}
writeAttributesFor
(
klass
);
}
private
void
writeMember
(
Element
member
,
boolean
isMethod
)
throws
IOException
{
//System.out.println("writeMember "+member);
u2
(
parseFlags
(
member
.
getAttr
(
"flags"
)));
cpRef
(
CONSTANT_Utf8
,
member
.
getAttr
(
"name"
));
cpRef
(
CONSTANT_Utf8
,
member
.
getAttr
(
"type"
));
writeAttributesFor
(
member
);
}
protected
void
writeAttributesFor
(
Element
x
)
throws
IOException
{
LinkedHashSet
<
String
>
attrNames
=
new
LinkedHashSet
<
String
>();
for
(
Element
e
:
x
.
elements
())
{
attrNames
.
add
(
e
.
getName
());
// uniquifying
}
attrNames
.
removeAll
(
nonAttrTags
());
u2
(
attrNames
.
size
());
if
(
attrNames
.
isEmpty
())
{
return
;
}
Element
prevCurrent
;
if
(
x
.
getName
()
==
"Code"
)
{
prevCurrent
=
currentCode
;
currentCode
=
x
;
}
else
{
prevCurrent
=
currentMember
;
currentMember
=
x
;
}
OutputStream
realOut
=
this
.
out
;
for
(
String
utag
:
attrNames
)
{
String
qtag
=
x
.
getName
()
+
"."
+
utag
;
String
wtag
=
"*."
+
utag
;
String
key
=
attrTypesByTag
.
get
(
qtag
);
if
(
key
==
null
)
{
key
=
attrTypesByTag
.
get
(
wtag
);
}
String
type
=
attrTypes
.
get
(
key
);
//System.out.println("tag "+qtag+" => key "+key+"; type "+type);
Element
attrs
=
x
.
findAllElements
(
utag
);
ByteArrayOutputStream
attrBuf
=
getAttrBuf
();
if
(
type
==
null
)
{
if
(
attrs
.
size
()
!=
1
||
!
attrs
.
get
(
0
).
equals
(
new
Element
(
utag
)))
{
System
.
out
.
println
(
"Warning: No attribute type description: "
+
qtag
);
}
key
=
wtag
;
}
else
{
try
{
this
.
out
=
attrBuf
;
// unparse according to type desc.
if
(
type
.
equals
(
"<Code>..."
))
{
writeCode
((
Element
)
attrs
.
get
(
0
));
// assume only 1
}
else
if
(
type
.
equals
(
"<Frame>..."
))
{
writeStackMap
(
attrs
,
false
);
}
else
if
(
type
.
equals
(
"<FrameX>..."
))
{
writeStackMap
(
attrs
,
true
);
}
else
if
(
type
.
startsWith
(
"["
))
{
writeAttributeRecursive
(
attrs
,
type
);
}
else
{
writeAttribute
(
attrs
,
type
);
}
}
finally
{
//System.out.println("Attr Bytes = \""+attrBuf.toString(EIGHT_BIT_CHAR_ENCODING).replace('"', (char)('"'|0x80))+"\"");
this
.
out
=
realOut
;
}
}
cpRef
(
CONSTANT_Utf8
,
key
.
substring
(
key
.
indexOf
(
'.'
)
+
1
));
u4
(
attrBuf
.
size
());
attrBuf
.
writeTo
(
out
);
putAttrBuf
(
attrBuf
);
}
if
(
x
.
getName
()
==
"Code"
)
{
currentCode
=
prevCurrent
;
}
else
{
currentMember
=
prevCurrent
;
}
}
private
void
writeAttributeRecursive
(
Element
aval
,
String
type
)
throws
IOException
{
assert
(
callables
==
null
);
callables
=
getBodies
(
type
);
writeAttribute
(
aval
,
callables
[
0
]);
callables
=
null
;
}
private
void
writeAttribute
(
Element
aval
,
String
type
)
throws
IOException
{
//System.out.println("writeAttribute "+aval+" using "+type);
String
nextAttrName
=
null
;
boolean
afterElemHead
=
false
;
for
(
int
len
=
type
.
length
(),
next
,
i
=
0
;
i
<
len
;
i
=
next
)
{
int
value
;
char
intKind
;
int
tag
;
int
sigChar
;
String
attrValue
;
switch
(
type
.
charAt
(
i
))
{
case
'<'
:
assert
(
nextAttrName
==
null
);
next
=
type
.
indexOf
(
'>'
,
i
);
String
form
=
type
.
substring
(
i
+
1
,
next
++);
if
(
form
.
indexOf
(
'='
)
<
0
)
{
// elem_placement = '<' elemname '>'
if
(
aval
.
isAnonymous
())
{
assert
(
aval
.
size
()
==
1
);
aval
=
(
Element
)
aval
.
get
(
0
);
}
assert
(
aval
.
getName
().
equals
(
form
))
:
aval
+
" // "
+
form
;
afterElemHead
=
true
;
}
else
{
// attr_placement = '(' attrname '=' (value)? ')'
int
eqPos
=
form
.
indexOf
(
'='
);
assert
(
eqPos
>=
0
);
nextAttrName
=
form
.
substring
(
0
,
eqPos
).
intern
();
if
(
eqPos
!=
form
.
length
()
-
1
)
{
// value is implicit, not placed in file
nextAttrName
=
null
;
}
afterElemHead
=
false
;
}
continue
;
case
'('
:
next
=
type
.
indexOf
(
')'
,
++
i
);
int
callee
=
Integer
.
parseInt
(
type
.
substring
(
i
,
next
++));
writeAttribute
(
aval
,
callables
[
callee
]);
continue
;
case
'N'
:
// replication = 'N' int '[' type ... ']'
{
assert
(
nextAttrName
==
null
);
afterElemHead
=
false
;
char
countType
=
type
.
charAt
(
i
+
1
);
next
=
i
+
2
;
String
type1
=
getBody
(
type
,
next
);
Element
elems
=
aval
;
if
(
type1
.
startsWith
(
"<"
))
{
// Select only matching members of aval.
String
elemName
=
type1
.
substring
(
1
,
type1
.
indexOf
(
'>'
));
elems
=
aval
.
findAllElements
(
elemName
);
}
putInt
(
elems
.
size
(),
countType
);
next
+=
type1
.
length
()
+
2
;
// skip body and brackets
for
(
Element
elem
:
elems
.
elements
())
{
writeAttribute
(
elem
,
type1
);
}
}
continue
;
case
'T'
:
// union = 'T' any_int union_case* '(' ')' '[' body ']'
// write the value
value
=
(
int
)
aval
.
getAttrLong
(
"tag"
);
assert
(
aval
.
getAttr
(
"tag"
)
!=
null
)
:
aval
;
intKind
=
type
.
charAt
(++
i
);
if
(
intKind
==
'S'
)
{
intKind
=
type
.
charAt
(++
i
);
}
putInt
(
value
,
intKind
);
nextAttrName
=
null
;
afterElemHead
=
false
;
++
i
;
// skip the int type char
// union_case = '(' ('-')? digit+ ')' '[' body ']'
for
(
boolean
foundCase
=
false
;;)
{
assert
(
type
.
charAt
(
i
)
==
'('
);
next
=
type
.
indexOf
(
')'
,
++
i
);
assert
(
next
>=
i
);
String
caseStr
=
type
.
substring
(
i
,
next
++);
String
type1
=
getBody
(
type
,
next
);
next
+=
type1
.
length
()
+
2
;
// skip body and brackets
boolean
lastCase
=
(
caseStr
.
length
()
==
0
);
if
(!
foundCase
&&
(
lastCase
||
matchTag
(
value
,
caseStr
)))
{
foundCase
=
true
;
// Execute this body.
writeAttribute
(
aval
,
type1
);
}
if
(
lastCase
)
{
break
;
}
}
continue
;
case
'B'
:
case
'H'
:
case
'I'
:
// int = oneof "BHI"
value
=
(
int
)
aval
.
getAttrLong
(
nextAttrName
);
intKind
=
type
.
charAt
(
i
);
next
=
i
+
1
;
break
;
case
'K'
:
sigChar
=
type
.
charAt
(
i
+
1
);
if
(
sigChar
==
'Q'
)
{
assert
(
currentMember
.
getName
()
==
"Field"
);
assert
(
aval
.
getName
()
==
"ConstantValue"
);
String
sig
=
currentMember
.
getAttr
(
"type"
);
sigChar
=
sig
.
charAt
(
0
);
switch
(
sigChar
)
{
case
'Z'
:
case
'B'
:
case
'C'
:
case
'S'
:
sigChar
=
'I'
;
break
;
}
}
switch
(
sigChar
)
{
case
'I'
:
tag
=
CONSTANT_Integer
;
break
;
case
'J'
:
tag
=
CONSTANT_Long
;
break
;
case
'F'
:
tag
=
CONSTANT_Float
;
break
;
case
'D'
:
tag
=
CONSTANT_Double
;
break
;
case
'L'
:
tag
=
CONSTANT_String
;
break
;
default
:
assert
(
false
);
tag
=
0
;
}
assert
(
type
.
charAt
(
i
+
2
)
==
'H'
);
// only H works for now
next
=
i
+
3
;
assert
(
afterElemHead
||
nextAttrName
!=
null
);
//System.out.println("get attr "+nextAttrName+" in "+aval);
if
(
nextAttrName
!=
null
)
{
attrValue
=
aval
.
getAttr
(
nextAttrName
);
assert
(
attrValue
!=
null
);
}
else
{
assert
(
aval
.
isText
())
:
aval
;
attrValue
=
aval
.
getText
().
toString
();
}
value
=
getCPIndex
(
tag
,
attrValue
);
intKind
=
'H'
;
//type.charAt(i+2);
break
;
case
'R'
:
sigChar
=
type
.
charAt
(
i
+
1
);
switch
(
sigChar
)
{
case
'C'
:
tag
=
CONSTANT_Class
;
break
;
case
'S'
:
tag
=
CONSTANT_Utf8
;
break
;
case
'D'
:
tag
=
CONSTANT_Class
;
break
;
case
'F'
:
tag
=
CONSTANT_Fieldref
;
break
;
case
'M'
:
tag
=
CONSTANT_Methodref
;
break
;
case
'I'
:
tag
=
CONSTANT_InterfaceMethodref
;
break
;
case
'U'
:
tag
=
CONSTANT_Utf8
;
break
;
//case 'Q': tag = CONSTANT_Class; break;
default
:
assert
(
false
);
tag
=
0
;
}
assert
(
type
.
charAt
(
i
+
2
)
==
'H'
);
// only H works for now
next
=
i
+
3
;
assert
(
afterElemHead
||
nextAttrName
!=
null
);
//System.out.println("get attr "+nextAttrName+" in "+aval);
if
(
nextAttrName
!=
null
)
{
attrValue
=
aval
.
getAttr
(
nextAttrName
);
}
else
if
(
aval
.
hasText
())
{
attrValue
=
aval
.
getText
().
toString
();
}
else
{
attrValue
=
null
;
}
value
=
getCPIndex
(
tag
,
attrValue
);
intKind
=
'H'
;
//type.charAt(i+2);
break
;
case
'P'
:
// bci = 'P' int
case
'S'
:
// signed_int = 'S' int
next
=
i
+
2
;
value
=
(
int
)
aval
.
getAttrLong
(
nextAttrName
);
intKind
=
type
.
charAt
(
i
+
1
);
break
;
case
'F'
:
next
=
i
+
2
;
value
=
parseFlags
(
aval
.
getAttr
(
nextAttrName
));
intKind
=
type
.
charAt
(
i
+
1
);
break
;
default
:
throw
new
RuntimeException
(
"bad attr format '"
+
type
.
charAt
(
i
)
+
"': "
+
type
);
}
// write the value
putInt
(
value
,
intKind
);
nextAttrName
=
null
;
afterElemHead
=
false
;
}
assert
(
nextAttrName
==
null
);
}
private
void
putInt
(
int
x
,
char
ch
)
throws
IOException
{
switch
(
ch
)
{
case
'B'
:
u1
(
x
);
break
;
case
'H'
:
u2
(
x
);
break
;
case
'I'
:
u4
(
x
);
break
;
}
assert
(
"BHI"
.
indexOf
(
ch
)
>=
0
);
}
private
void
writeCode
(
Element
code
)
throws
IOException
{
//System.out.println("writeCode "+code);
//Element m = new Element(currentMember); m.remove(code);
//System.out.println(" in "+m);
int
stack
=
(
int
)
code
.
getAttrLong
(
"stack"
);
int
local
=
(
int
)
code
.
getAttrLong
(
"local"
);
Element
bytes
=
code
.
findElement
(
"Bytes"
);
Element
insns
=
code
.
findElement
(
"Instructions"
);
String
bytecodes
;
if
(
insns
==
null
)
{
bytecodes
=
bytes
.
getText
().
toString
();
}
else
{
bytecodes
=
InstructionSyntax
.
assemble
(
insns
,
this
);
// Cache the assembled bytecodes:
bytes
=
new
Element
(
"Bytes"
,
(
String
[])
null
,
bytecodes
);
code
.
add
(
0
,
bytes
);
}
u2
(
stack
);
u2
(
local
);
int
length
=
bytecodes
.
length
();
u4
(
length
);
for
(
int
i
=
0
;
i
<
length
;
i
++)
{
u1
((
byte
)
bytecodes
.
charAt
(
i
));
}
Element
handlers
=
code
.
findAllElements
(
"Handler"
);
u2
(
handlers
.
size
());
for
(
Element
handler
:
handlers
.
elements
())
{
int
start
=
(
int
)
handler
.
getAttrLong
(
"start"
);
int
end
=
(
int
)
handler
.
getAttrLong
(
"end"
);
int
catsh
=
(
int
)
handler
.
getAttrLong
(
"catch"
);
u2
(
start
);
u2
(
end
);
u2
(
catsh
);
cpRef
(
CONSTANT_Class
,
handler
.
getAttr
(
"class"
));
}
writeAttributesFor
(
code
);
}
protected
void
writeStackMap
(
Element
attrs
,
boolean
hasXOption
)
throws
IOException
{
Element
bytes
=
currentCode
.
findElement
(
"Bytes"
);
assert
(
bytes
!=
null
&&
bytes
.
size
()
==
1
);
int
byteLength
=
((
String
)
bytes
.
get
(
0
)).
length
();
boolean
uoffsetIsU4
=
(
byteLength
>=
(
1
<<
16
));
boolean
ulocalvarIsU4
=
currentCode
.
getAttrLong
(
"local"
)
>=
(
1
<<
16
);
boolean
ustackIsU4
=
currentCode
.
getAttrLong
(
"stack"
)
>=
(
1
<<
16
);
if
(
uoffsetIsU4
)
{
u4
(
attrs
.
size
());
}
else
{
u2
(
attrs
.
size
());
}
for
(
Element
frame
:
attrs
.
elements
())
{
int
bci
=
(
int
)
frame
.
getAttrLong
(
"bci"
);
if
(
uoffsetIsU4
)
{
u4
(
bci
);
}
else
{
u2
(
bci
);
}
if
(
hasXOption
)
{
u1
((
int
)
frame
.
getAttrLong
(
"flags"
));
}
// Scan local and stack types in this frame:
final
int
LOCALS
=
0
,
STACK
=
1
;
for
(
int
j
=
LOCALS
;
j
<=
STACK
;
j
++)
{
Element
types
=
frame
.
findElement
(
j
==
LOCALS
?
"Local"
:
"Stack"
);
int
typeSize
=
(
types
==
null
)
?
0
:
types
.
size
();
if
(
j
==
LOCALS
)
{
if
(
ulocalvarIsU4
)
{
u4
(
typeSize
);
}
else
{
u2
(
typeSize
);
}
}
else
{
// STACK
if
(
ustackIsU4
)
{
u4
(
typeSize
);
}
else
{
u2
(
typeSize
);
}
}
if
(
types
==
null
)
{
continue
;
}
for
(
Element
type
:
types
.
elements
())
{
int
tag
=
itemTagValue
(
type
.
getName
());
u1
(
tag
);
switch
(
tag
)
{
case
ITEM_Object:
cpRef
(
CONSTANT_Class
,
type
.
getAttr
(
"class"
));
break
;
case
ITEM_Uninitialized:
case
ITEM_ReturnAddress:
{
int
offset
=
(
int
)
type
.
getAttrLong
(
"bci"
);
if
(
uoffsetIsU4
)
{
u4
(
offset
);
}
else
{
u2
(
offset
);
}
}
break
;
}
}
}
}
}
public
void
writeCP
()
throws
IOException
{
int
cpLen
=
cpoolSize
;
u2
(
cpLen
);
ByteArrayOutputStream
buf
=
getAttrBuf
();
for
(
Element
c
:
cpool
.
elements
())
{
if
(!
c
.
isText
())
{
System
.
out
.
println
(
"## !isText "
+
c
);
}
int
id
=
(
int
)
c
.
getAttrLong
(
"id"
);
int
tag
=
cpTagValue
(
c
.
getName
());
String
name
=
c
.
getText
().
toString
();
int
pos
;
u1
(
tag
);
switch
(
tag
)
{
case
CONSTANT_Utf8:
{
int
done
=
0
;
buf
.
reset
();
int
nameLen
=
name
.
length
();
while
(
done
<
nameLen
)
{
int
next
=
name
.
indexOf
((
char
)
0
,
done
);
if
(
next
<
0
)
{
next
=
nameLen
;
}
if
(
done
<
next
)
{
buf
.
write
(
name
.
substring
(
done
,
next
).
getBytes
(
UTF8_ENCODING
));
}
if
(
next
<
nameLen
)
{
buf
.
write
(
0300
);
buf
.
write
(
0200
);
next
++;
}
done
=
next
;
}
u2
(
buf
.
size
());
buf
.
writeTo
(
out
);
}
break
;
case
CONSTANT_Integer:
u4
(
Integer
.
parseInt
(
name
));
break
;
case
CONSTANT_Float:
u4
(
Float
.
floatToIntBits
(
Float
.
parseFloat
(
name
)));
break
;
case
CONSTANT_Long:
u8
(
Long
.
parseLong
(
name
));
//i += 1; // no need: extra cp slot is implicit
break
;
case
CONSTANT_Double:
u8
(
Double
.
doubleToLongBits
(
Double
.
parseDouble
(
name
)));
//i += 1; // no need: extra cp slot is implicit
break
;
case
CONSTANT_Class:
case
CONSTANT_String:
u2
(
getCPIndex
(
CONSTANT_Utf8
,
name
));
break
;
case
CONSTANT_Fieldref:
case
CONSTANT_Methodref:
case
CONSTANT_InterfaceMethodref:
pos
=
name
.
indexOf
(
' '
);
u2
(
getCPIndex
(
CONSTANT_Class
,
name
.
substring
(
0
,
pos
)));
u2
(
getCPIndex
(
CONSTANT_NameAndType
,
name
.
substring
(
pos
+
1
)));
break
;
case
CONSTANT_NameAndType:
pos
=
name
.
indexOf
(
' '
);
u2
(
getCPIndex
(
CONSTANT_Utf8
,
name
.
substring
(
0
,
pos
)));
u2
(
getCPIndex
(
CONSTANT_Utf8
,
name
.
substring
(
pos
+
1
)));
break
;
}
}
putAttrBuf
(
buf
);
}
public
void
cpRef
(
int
tag
,
String
name
)
throws
IOException
{
u2
(
getCPIndex
(
tag
,
name
));
}
public
void
u8
(
long
x
)
throws
IOException
{
u4
((
int
)
(
x
>>>
32
));
u4
((
int
)
(
x
>>>
0
));
}
public
void
u4
(
int
x
)
throws
IOException
{
u2
(
x
>>>
16
);
u2
(
x
>>>
0
);
}
public
void
u2
(
int
x
)
throws
IOException
{
u1
(
x
>>>
8
);
u1
(
x
>>>
0
);
}
public
void
u1
(
int
x
)
throws
IOException
{
out
.
write
(
x
&
0xFF
);
}
}
test/tools/pack200/pack200-verifier/src/xmlkit/CommandLineParser.java
0 → 100644
浏览文件 @
a1c492e8
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package
xmlkit
;
// -*- mode: java; indent-tabs-mode: nil -*-
import
java.util.*
;
/*
* @author jrose
*/
public
class
CommandLineParser
{
public
CommandLineParser
(
String
optionString
)
{
setOptionMap
(
optionString
);
}
TreeMap
<
String
,
String
[]>
optionMap
;
public
void
setOptionMap
(
String
options
)
{
// Convert options string into optLines dictionary.
TreeMap
<
String
,
String
[]>
optmap
=
new
TreeMap
<
String
,
String
[]>();
loadOptmap:
for
(
String
optline
:
options
.
split
(
"\n"
))
{
String
[]
words
=
optline
.
split
(
"\\p{Space}+"
);
if
(
words
.
length
==
0
)
{
continue
loadOptmap
;
}
String
opt
=
words
[
0
];
words
[
0
]
=
""
;
// initial word is not a spec
if
(
opt
.
length
()
==
0
&&
words
.
length
>=
1
)
{
opt
=
words
[
1
];
// initial "word" is empty due to leading ' '
words
[
1
]
=
""
;
}
if
(
opt
.
length
()
==
0
)
{
continue
loadOptmap
;
}
String
[]
prevWords
=
optmap
.
put
(
opt
,
words
);
if
(
prevWords
!=
null
)
{
throw
new
RuntimeException
(
"duplicate option: "
+
optline
.
trim
());
}
}
optionMap
=
optmap
;
}
public
String
getOptionMap
()
{
TreeMap
<
String
,
String
[]>
optmap
=
optionMap
;
StringBuffer
sb
=
new
StringBuffer
();
for
(
String
opt
:
optmap
.
keySet
())
{
sb
.
append
(
opt
);
for
(
String
spec
:
optmap
.
get
(
opt
))
{
sb
.
append
(
' '
).
append
(
spec
);
}
sb
.
append
(
'\n'
);
}
return
sb
.
toString
();
}
/**
* Remove a set of command-line options from args,
* storing them in the properties map in a canonicalized form.
*/
public
String
parse
(
List
<
String
>
args
,
Map
<
String
,
String
>
properties
)
{
//System.out.println(args+" // "+properties);
String
resultString
=
null
;
TreeMap
<
String
,
String
[]>
optmap
=
optionMap
;
// State machine for parsing a command line.
ListIterator
<
String
>
argp
=
args
.
listIterator
();
ListIterator
<
String
>
pbp
=
new
ArrayList
<
String
>().
listIterator
();
doArgs:
for
(;;)
{
// One trip through this loop per argument.
// Multiple trips per option only if several options per argument.
String
arg
;
if
(
pbp
.
hasPrevious
())
{
arg
=
pbp
.
previous
();
pbp
.
remove
();
}
else
if
(
argp
.
hasNext
())
{
arg
=
argp
.
next
();
}
else
{
// No more arguments at all.
break
doArgs
;
}
tryOpt:
for
(
int
optlen
=
arg
.
length
();;
optlen
--)
{
// One time through this loop for each matching arg prefix.
String
opt
;
// Match some prefix of the argument to a key in optmap.
findOpt:
for
(;;)
{
opt
=
arg
.
substring
(
0
,
optlen
);
if
(
optmap
.
containsKey
(
opt
))
{
break
findOpt
;
}
if
(
optlen
==
0
)
{
break
tryOpt
;
}
// Decide on a smaller prefix to search for.
SortedMap
<
String
,
String
[]>
pfxmap
=
optmap
.
headMap
(
opt
);
// pfxmap.lastKey is no shorter than any prefix in optmap.
int
len
=
pfxmap
.
isEmpty
()
?
0
:
pfxmap
.
lastKey
().
length
();
optlen
=
Math
.
min
(
len
,
optlen
-
1
);
opt
=
arg
.
substring
(
0
,
optlen
);
// (Note: We could cut opt down to its common prefix with
// pfxmap.lastKey, but that wouldn't save many cycles.)
}
opt
=
opt
.
intern
();
assert
(
arg
.
startsWith
(
opt
));
assert
(
opt
.
length
()
==
optlen
);
String
val
=
arg
.
substring
(
optlen
);
// arg == opt+val
// Execute the option processing specs for this opt.
// If no actions are taken, then look for a shorter prefix.
boolean
didAction
=
false
;
boolean
isError
=
false
;
int
pbpMark
=
pbp
.
nextIndex
();
// in case of backtracking
String
[]
specs
=
optmap
.
get
(
opt
);
eachSpec:
for
(
String
spec
:
specs
)
{
if
(
spec
.
length
()
==
0
)
{
continue
eachSpec
;
}
if
(
spec
.
startsWith
(
"#"
))
{
break
eachSpec
;
}
int
sidx
=
0
;
char
specop
=
spec
.
charAt
(
sidx
++);
// Deal with '+'/'*' prefixes (spec conditions).
boolean
ok
;
switch
(
specop
)
{
case
'+'
:
// + means we want an non-empty val suffix.
ok
=
(
val
.
length
()
!=
0
);
specop
=
spec
.
charAt
(
sidx
++);
break
;
case
'*'
:
// * means we accept empty or non-empty
ok
=
true
;
specop
=
spec
.
charAt
(
sidx
++);
break
;
default
:
// No condition prefix means we require an exact
// match, as indicated by an empty val suffix.
ok
=
(
val
.
length
()
==
0
);
break
;
}
if
(!
ok
)
{
continue
eachSpec
;
}
String
specarg
=
spec
.
substring
(
sidx
);
switch
(
specop
)
{
case
'.'
:
// terminate the option sequence
resultString
=
(
specarg
.
length
()
!=
0
)
?
specarg
.
intern
()
:
opt
;
break
doArgs
;
case
'?'
:
// abort the option sequence
resultString
=
(
specarg
.
length
()
!=
0
)
?
specarg
.
intern
()
:
arg
;
isError
=
true
;
break
eachSpec
;
case
'@'
:
// change the effective opt name
opt
=
specarg
.
intern
();
break
;
case
'>'
:
// shift remaining arg val to next arg
pbp
.
add
(
specarg
+
val
);
// push a new argument
val
=
""
;
break
;
case
'!'
:
// negation option
String
negopt
=
(
specarg
.
length
()
!=
0
)
?
specarg
.
intern
()
:
opt
;
properties
.
remove
(
negopt
);
properties
.
put
(
negopt
,
null
);
// leave placeholder
didAction
=
true
;
break
;
case
'$'
:
// normal "boolean" option
String
boolval
;
if
(
specarg
.
length
()
!=
0
)
{
// If there is a given spec token, store it.
boolval
=
specarg
;
}
else
{
String
old
=
properties
.
get
(
opt
);
if
(
old
==
null
||
old
.
length
()
==
0
)
{
boolval
=
"1"
;
}
else
{
// Increment any previous value as a numeral.
boolval
=
""
+
(
1
+
Integer
.
parseInt
(
old
));
}
}
properties
.
put
(
opt
,
boolval
);
didAction
=
true
;
break
;
case
'='
:
// "string" option
case
'&'
:
// "collection" option
// Read an option.
boolean
append
=
(
specop
==
'&'
);
String
strval
;
if
(
pbp
.
hasPrevious
())
{
strval
=
pbp
.
previous
();
pbp
.
remove
();
}
else
if
(
argp
.
hasNext
())
{
strval
=
argp
.
next
();
}
else
{
resultString
=
arg
+
" ?"
;
isError
=
true
;
break
eachSpec
;
}
if
(
append
)
{
String
old
=
properties
.
get
(
opt
);
if
(
old
!=
null
)
{
// Append new val to old with embedded delim.
String
delim
=
specarg
;
if
(
delim
.
length
()
==
0
)
{
delim
=
" "
;
}
strval
=
old
+
specarg
+
strval
;
}
}
properties
.
put
(
opt
,
strval
);
didAction
=
true
;
break
;
default
:
throw
new
RuntimeException
(
"bad spec for "
+
opt
+
": "
+
spec
);
}
}
// Done processing specs.
if
(
didAction
&&
!
isError
)
{
continue
doArgs
;
}
// The specs should have done something, but did not.
while
(
pbp
.
nextIndex
()
>
pbpMark
)
{
// Remove anything pushed during these specs.
pbp
.
previous
();
pbp
.
remove
();
}
if
(
isError
)
{
throw
new
IllegalArgumentException
(
resultString
);
}
if
(
optlen
==
0
)
{
// We cannot try a shorter matching option.
break
tryOpt
;
}
}
// If we come here, there was no matching option.
// So, push back the argument, and return to caller.
pbp
.
add
(
arg
);
break
doArgs
;
}
// Report number of arguments consumed.
args
.
subList
(
0
,
argp
.
nextIndex
()).
clear
();
// Report any unconsumed partial argument.
while
(
pbp
.
hasPrevious
())
{
args
.
add
(
0
,
pbp
.
previous
());
}
//System.out.println(args+" // "+properties+" -> "+resultString);
return
resultString
;
}
}
test/tools/pack200/pack200-verifier/src/xmlkit/InstructionAssembler.java
0 → 100644
浏览文件 @
a1c492e8
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package
xmlkit
;
// -*- mode: java; indent-tabs-mode: nil -*-
import
xmlkit.XMLKit.Element
;
import
java.util.HashMap
;
/*
* @author jrose
*/
abstract
class
InstructionAssembler
extends
InstructionSyntax
{
InstructionAssembler
()
{
}
public
static
String
assemble
(
Element
instructions
,
String
pcAttrName
,
ClassSyntax
.
GetCPIndex
getCPI
)
{
int
insCount
=
instructions
.
size
();
Element
[]
insElems
=
new
Element
[
insCount
];
int
[]
elemToIndexMap
;
int
[]
insLocs
;
byte
[]
ops
=
new
byte
[
insCount
];
int
[]
operands
=
new
int
[
insCount
];
boolean
[]
isWide
=
new
boolean
[
insCount
];
int
[]
branches
;
int
[]
branchInsLocs
;
HashMap
<
String
,
String
>
labels
=
new
HashMap
<
String
,
String
>();
final
int
WIDE
=
0xc4
;
final
int
GOTO
=
0xa7
;
final
int
GOTO_W
=
0xc8
;
final
int
GOTO_LEN
=
3
;
final
int
GOTO_W_LEN
=
5
;
assert
(
"wide"
.
equals
(
bcNames
[
WIDE
]));
assert
(
"goto"
.
equals
(
bcNames
[
GOTO
]));
assert
(
"goto_w"
.
equals
(
bcNames
[
GOTO_W
]));
assert
(
bcFormats
[
GOTO
].
length
()
==
GOTO_LEN
);
assert
(
bcFormats
[
GOTO_W
].
length
()
==
GOTO_W_LEN
);
// Unpack instructions into temp. arrays, and find branches and labels.
{
elemToIndexMap
=
(
pcAttrName
!=
null
)
?
new
int
[
insCount
]
:
null
;
int
[]
buffer
=
operands
;
int
id
=
0
;
int
branchCount
=
0
;
for
(
int
i
=
0
;
i
<
insCount
;
i
++)
{
Element
ins
=
(
Element
)
instructions
.
get
(
i
);
if
(
elemToIndexMap
!=
null
)
{
elemToIndexMap
[
i
]
=
(
ins
.
getAttr
(
pcAttrName
)
!=
null
?
id
:
-
1
);
}
String
lab
=
ins
.
getAttr
(
"pc"
);
if
(
lab
!=
null
)
{
labels
.
put
(
lab
,
String
.
valueOf
(
id
));
}
int
op
=
opCode
(
ins
.
getName
());
if
(
op
<
0
)
{
assert
(
ins
.
getAttr
(
pcAttrName
)
!=
null
||
ins
.
getName
().
equals
(
"label"
));
continue
;
// delete PC holder element
}
if
(
op
==
WIDE
)
{
//0xc4
isWide
[
id
]
=
true
;
// force wide format
continue
;
}
if
(
bcFormats
[
op
].
indexOf
(
'o'
)
>=
0
)
{
buffer
[
branchCount
++]
=
id
;
}
if
(
bcFormats
[
op
]
==
bcWideFormats
[
op
])
{
isWide
[
id
]
=
false
;
}
insElems
[
id
]
=
ins
;
ops
[
id
]
=
(
byte
)
op
;
id
++;
}
insCount
=
id
;
// maybe we deleted some wide prefixes, etc.
branches
=
new
int
[
branchCount
+
1
];
System
.
arraycopy
(
buffer
,
0
,
branches
,
0
,
branchCount
);
branches
[
branchCount
]
=
-
1
;
// sentinel
}
// Compute instruction sizes. These sizes are final,
// except for branch instructions, which may need lengthening.
// Some instructions (ldc, bipush, iload, iinc) are automagically widened.
insLocs
=
new
int
[
insCount
+
1
];
int
loc
=
0
;
for
(
int
bn
=
0
,
id
=
0
;
id
<
insCount
;
id
++)
{
insLocs
[
id
]
=
loc
;
Element
ins
=
insElems
[
id
];
int
op
=
ops
[
id
]
&
0xFF
;
String
format
=
opFormat
(
op
,
isWide
[
id
]);
// Make sure operands fit within the given format.
for
(
int
j
=
1
,
jlimit
=
format
.
length
();
j
<
jlimit
;
j
++)
{
char
fc
=
format
.
charAt
(
j
);
int
x
=
0
;
switch
(
fc
)
{
case
'l'
:
x
=
(
int
)
ins
.
getAttrLong
(
"loc"
);
assert
(
x
>=
0
);
if
(
x
>
0xFF
&&
!
isWide
[
id
])
{
isWide
[
id
]
=
true
;
format
=
opFormat
(
op
,
isWide
[
id
]);
}
assert
(
x
<=
0xFFFF
);
break
;
case
'k'
:
char
fc2
=
format
.
charAt
(
Math
.
min
(
j
+
1
,
format
.
length
()
-
1
));
x
=
getCPIndex
(
ins
,
fc2
,
getCPI
);
if
(
x
>
0xFF
&&
j
==
jlimit
-
1
)
{
assert
(
op
==
0x12
);
//ldc
ops
[
id
]
=
(
byte
)
(
op
=
0x13
);
//ldc_w
format
=
opFormat
(
op
);
}
assert
(
x
<=
0xFFFF
);
j
++;
// skip type-of-constant marker
break
;
case
'x'
:
x
=
(
int
)
ins
.
getAttrLong
(
"num"
);
assert
(
x
>=
0
&&
x
<=
((
j
==
jlimit
-
1
)
?
0xFF
:
0xFFFF
));
break
;
case
's'
:
x
=
(
int
)
ins
.
getAttrLong
(
"num"
);
if
(
x
!=
(
byte
)
x
&&
j
==
jlimit
-
1
)
{
switch
(
op
)
{
case
0x10
:
//bipush
ops
[
id
]
=
(
byte
)
(
op
=
0x11
);
//sipush
break
;
case
0x84
:
//iinc
isWide
[
id
]
=
true
;
format
=
opFormat
(
op
,
isWide
[
id
]);
break
;
default
:
assert
(
false
);
// cannot lengthen
}
}
// unsign the value now, to make later steps clearer
if
(
j
==
jlimit
-
1
)
{
assert
(
x
==
(
byte
)
x
);
x
=
x
&
0xFF
;
}
else
{
assert
(
x
==
(
short
)
x
);
x
=
x
&
0xFFFF
;
}
break
;
case
'o'
:
assert
(
branches
[
bn
]
==
id
);
bn
++;
// make local copies of the branches, and fix up labels
insElems
[
id
]
=
ins
=
new
Element
(
ins
);
String
newLab
=
labels
.
get
(
ins
.
getAttr
(
"lab"
));
assert
(
newLab
!=
null
);
ins
.
setAttr
(
"lab"
,
newLab
);
int
prevCas
=
0
;
int
k
=
0
;
for
(
Element
cas
:
ins
.
elements
())
{
assert
(
cas
.
getName
().
equals
(
"Case"
));
ins
.
set
(
k
++,
cas
=
new
Element
(
cas
));
newLab
=
labels
.
get
(
cas
.
getAttr
(
"lab"
));
assert
(
newLab
!=
null
);
cas
.
setAttr
(
"lab"
,
newLab
);
int
thisCas
=
(
int
)
cas
.
getAttrLong
(
"num"
);
assert
(
op
==
0xab
||
op
==
0xaa
&&
(
k
==
0
||
thisCas
==
prevCas
+
1
));
prevCas
=
thisCas
;
}
break
;
case
't'
:
// switch table is represented as Switch.Case sub-elements
break
;
default
:
assert
(
false
);
}
operands
[
id
]
=
x
;
// record operand (last if there are 2)
// skip redundant chars
while
(
j
+
1
<
jlimit
&&
format
.
charAt
(
j
+
1
)
==
fc
)
{
++
j
;
}
}
switch
(
op
)
{
case
0xaa
:
//tableswitch
loc
=
switchBase
(
loc
);
loc
+=
4
*
(
3
+
ins
.
size
());
break
;
case
0xab
:
//lookupswitch
loc
=
switchBase
(
loc
);
loc
+=
4
*
(
2
+
2
*
ins
.
size
());
break
;
default
:
if
(
isWide
[
id
])
{
loc
++;
// 'wide' opcode prefix
}
loc
+=
format
.
length
();
break
;
}
}
insLocs
[
insCount
]
=
loc
;
// compute branch offsets, and see if any branches need expansion
for
(
int
maxTries
=
9
,
tries
=
0
;;
++
tries
)
{
boolean
overflowing
=
false
;
boolean
[]
branchExpansions
=
null
;
for
(
int
bn
=
0
;
bn
<
branches
.
length
-
1
;
bn
++)
{
int
id
=
branches
[
bn
];
Element
ins
=
insElems
[
id
];
int
insSize
=
insLocs
[
id
+
1
]
-
insLocs
[
id
];
int
origin
=
insLocs
[
id
];
int
target
=
insLocs
[(
int
)
ins
.
getAttrLong
(
"lab"
)];
int
offset
=
target
-
origin
;
operands
[
id
]
=
offset
;
//System.out.println("branch id="+id+" len="+insSize+" to="+target+" offset="+offset);
assert
(
insSize
==
GOTO_LEN
||
insSize
==
GOTO_W_LEN
||
ins
.
getName
().
indexOf
(
"switch"
)
>
0
);
boolean
thisOverflow
=
(
insSize
==
GOTO_LEN
&&
(
offset
!=
(
short
)
offset
));
if
(
thisOverflow
&&
!
overflowing
)
{
overflowing
=
true
;
branchExpansions
=
new
boolean
[
branches
.
length
];
}
if
(
thisOverflow
||
tries
==
maxTries
-
1
)
{
// lengthen the branch
assert
(!(
thisOverflow
&&
isWide
[
id
]));
isWide
[
id
]
=
true
;
branchExpansions
[
bn
]
=
true
;
}
}
if
(!
overflowing
)
{
break
;
// done, usually on first try
}
assert
(
tries
<=
maxTries
);
// Walk over all instructions, expanding branches and updating locations.
int
fixup
=
0
;
for
(
int
bn
=
0
,
id
=
0
;
id
<
insCount
;
id
++)
{
insLocs
[
id
]
+=
fixup
;
if
(
branches
[
bn
]
==
id
)
{
int
op
=
ops
[
id
]
&
0xFF
;
int
wop
;
boolean
invert
;
if
(
branchExpansions
[
bn
])
{
switch
(
op
)
{
case
GOTO:
//0xa7
wop
=
GOTO_W
;
//0xc8
invert
=
false
;
break
;
case
0xa8
:
//jsr
wop
=
0xc9
;
//jsr_w
invert
=
false
;
break
;
default
:
wop
=
invertBranchOp
(
op
);
invert
=
true
;
break
;
}
assert
(
op
!=
wop
);
ops
[
id
]
=
(
byte
)
wop
;
isWide
[
id
]
=
invert
;
if
(
invert
)
{
fixup
+=
GOTO_W_LEN
;
//branch around a wide goto
}
else
{
fixup
+=
(
GOTO_W_LEN
-
GOTO_LEN
);
}
// done expanding: ops and isWide reflect the decision
}
bn
++;
}
}
insLocs
[
insCount
]
+=
fixup
;
}
// we know the layout now
// notify the caller of offsets, if requested
if
(
elemToIndexMap
!=
null
)
{
for
(
int
i
=
0
;
i
<
elemToIndexMap
.
length
;
i
++)
{
int
id
=
elemToIndexMap
[
i
];
if
(
id
>=
0
)
{
Element
ins
=
(
Element
)
instructions
.
get
(
i
);
ins
.
setAttr
(
pcAttrName
,
""
+
insLocs
[
id
]);
}
}
elemToIndexMap
=
null
;
// release the pointer
}
// output the bytes
StringBuffer
sbuf
=
new
StringBuffer
(
insLocs
[
insCount
]);
for
(
int
bn
=
0
,
id
=
0
;
id
<
insCount
;
id
++)
{
//System.out.println("output id="+id+" loc="+insLocs[id]+" len="+(insLocs[id+1]-insLocs[id])+" #sbuf="+sbuf.length());
assert
(
sbuf
.
length
()
==
insLocs
[
id
]);
Element
ins
;
int
pc
=
insLocs
[
id
];
int
nextpc
=
insLocs
[
id
+
1
];
int
op
=
ops
[
id
]
&
0xFF
;
int
opnd
=
operands
[
id
];
String
format
;
if
(
branches
[
bn
]
==
id
)
{
bn
++;
sbuf
.
append
((
char
)
op
);
if
(
isWide
[
id
])
{
// emit <ifop lab=1f> <goto_w target> <label pc=1f>
int
target
=
pc
+
opnd
;
putInt
(
sbuf
,
nextpc
-
pc
,
-
2
);
assert
(
sbuf
.
length
()
==
pc
+
GOTO_LEN
);
sbuf
.
append
((
char
)
GOTO_W
);
putInt
(
sbuf
,
target
-
(
pc
+
GOTO_LEN
),
4
);
}
else
if
(
op
==
0xaa
||
//tableswitch
op
==
0xab
)
{
//lookupswitch
ins
=
insElems
[
id
];
for
(
int
pad
=
switchBase
(
pc
)
-
(
pc
+
1
);
pad
>
0
;
pad
--)
{
sbuf
.
append
((
char
)
0
);
}
assert
(
pc
+
opnd
==
insLocs
[(
int
)
ins
.
getAttrLong
(
"lab"
)]);
putInt
(
sbuf
,
opnd
,
4
);
// default label
if
(
op
==
0xaa
)
{
//tableswitch
Element
cas0
=
(
Element
)
ins
.
get
(
0
);
int
lowCase
=
(
int
)
cas0
.
getAttrLong
(
"num"
);
Element
casN
=
(
Element
)
ins
.
get
(
ins
.
size
()
-
1
);
int
highCase
=
(
int
)
casN
.
getAttrLong
(
"num"
);
assert
(
highCase
-
lowCase
+
1
==
ins
.
size
());
putInt
(
sbuf
,
lowCase
,
4
);
putInt
(
sbuf
,
highCase
,
4
);
int
caseForAssert
=
lowCase
;
for
(
Element
cas
:
ins
.
elements
())
{
int
target
=
insLocs
[(
int
)
cas
.
getAttrLong
(
"lab"
)];
assert
(
cas
.
getAttrLong
(
"num"
)
==
caseForAssert
++);
putInt
(
sbuf
,
target
-
pc
,
4
);
}
}
else
{
//lookupswitch
int
caseCount
=
ins
.
size
();
putInt
(
sbuf
,
caseCount
,
4
);
for
(
Element
cas
:
ins
.
elements
())
{
int
target
=
insLocs
[(
int
)
cas
.
getAttrLong
(
"lab"
)];
putInt
(
sbuf
,
(
int
)
cas
.
getAttrLong
(
"num"
),
4
);
putInt
(
sbuf
,
target
-
pc
,
4
);
}
}
assert
(
nextpc
==
sbuf
.
length
());
}
else
{
putInt
(
sbuf
,
opnd
,
-(
nextpc
-
(
pc
+
1
)));
}
}
else
if
(
nextpc
==
pc
+
1
)
{
// a single-byte instruction
sbuf
.
append
((
char
)
op
);
}
else
{
// picky stuff
boolean
wide
=
isWide
[
id
];
if
(
wide
)
{
sbuf
.
append
((
char
)
WIDE
);
pc
++;
}
sbuf
.
append
((
char
)
op
);
int
opnd1
;
int
opnd2
=
opnd
;
switch
(
op
)
{
case
0x84
:
//iinc
ins
=
insElems
[
id
];
opnd1
=
(
int
)
ins
.
getAttrLong
(
"loc"
);
if
(
isWide
[
id
])
{
putInt
(
sbuf
,
opnd1
,
2
);
putInt
(
sbuf
,
opnd2
,
2
);
}
else
{
putInt
(
sbuf
,
opnd1
,
1
);
putInt
(
sbuf
,
opnd2
,
1
);
}
break
;
case
0xc5
:
//multianewarray
ins
=
insElems
[
id
];
opnd1
=
getCPIndex
(
ins
,
'c'
,
getCPI
);
putInt
(
sbuf
,
opnd1
,
2
);
putInt
(
sbuf
,
opnd2
,
1
);
break
;
case
0xb9
:
//invokeinterface
ins
=
insElems
[
id
];
opnd1
=
getCPIndex
(
ins
,
'n'
,
getCPI
);
putInt
(
sbuf
,
opnd1
,
2
);
opnd2
=
(
int
)
ins
.
getAttrLong
(
"num"
);
if
(
opnd2
==
0
)
{
opnd2
=
ClassSyntax
.
computeInterfaceNum
(
ins
.
getAttr
(
"val"
));
}
putInt
(
sbuf
,
opnd2
,
2
);
break
;
default
:
// put the single operand and be done
putInt
(
sbuf
,
opnd
,
nextpc
-
(
pc
+
1
));
break
;
}
}
}
assert
(
sbuf
.
length
()
==
insLocs
[
insCount
]);
return
sbuf
.
toString
();
}
static
int
getCPIndex
(
Element
ins
,
char
ctype
,
ClassSyntax
.
GetCPIndex
getCPI
)
{
int
x
=
(
int
)
ins
.
getAttrLong
(
"ref"
);
if
(
x
==
0
&&
getCPI
!=
null
)
{
String
val
=
ins
.
getAttr
(
"val"
);
if
(
val
==
null
||
val
.
equals
(
""
))
{
val
=
ins
.
getText
().
toString
();
}
byte
tag
;
switch
(
ctype
)
{
case
'k'
:
tag
=
(
byte
)
ins
.
getAttrLong
(
"tag"
);
break
;
case
'c'
:
tag
=
ClassSyntax
.
CONSTANT_Class
;
break
;
case
'f'
:
tag
=
ClassSyntax
.
CONSTANT_Fieldref
;
break
;
case
'm'
:
tag
=
ClassSyntax
.
CONSTANT_Methodref
;
break
;
case
'n'
:
tag
=
ClassSyntax
.
CONSTANT_InterfaceMethodref
;
break
;
default
:
throw
new
Error
(
"bad ctype "
+
ctype
+
" in "
+
ins
);
}
x
=
getCPI
.
getCPIndex
(
tag
,
val
);
//System.out.println("getCPIndex "+ins+" => "+tag+"/"+val+" => "+x);
}
else
{
assert
(
x
>
0
);
}
return
x
;
}
static
void
putInt
(
StringBuffer
sbuf
,
int
x
,
int
len
)
{
//System.out.println("putInt x="+x+" len="+len);
boolean
isSigned
=
false
;
if
(
len
<
0
)
{
len
=
-
len
;
isSigned
=
true
;
}
assert
(
len
==
1
||
len
==
2
||
len
==
4
);
int
insig
=
((
4
-
len
)
*
8
);
// how many insignificant bits?
int
sx
=
x
<<
insig
;
;
assert
(
x
==
(
isSigned
?
(
sx
>>
insig
)
:
(
sx
>>>
insig
)));
for
(
int
i
=
0
;
i
<
len
;
i
++)
{
sbuf
.
append
((
char
)
(
sx
>>>
24
));
sx
<<=
8
;
}
}
}
test/tools/pack200/pack200-verifier/src/xmlkit/InstructionSyntax.java
0 → 100644
浏览文件 @
a1c492e8
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package
xmlkit
;
// -*- mode: java; indent-tabs-mode: nil -*-
import
xmlkit.XMLKit.Element
;
import
java.util.HashMap
;
import
java.util.Map
;
/*
* @author jrose
*/
public
abstract
class
InstructionSyntax
{
InstructionSyntax
()
{
}
static
final
String
[]
bcNames
;
static
final
String
[]
bcFormats
;
static
final
String
[]
bcWideFormats
;
static
final
HashMap
<
String
,
Integer
>
bcCodes
;
static
final
HashMap
<
String
,
Element
>
abbrevs
;
static
final
HashMap
<
Element
,
String
>
rabbrevs
;
static
{
TokenList
tl
=
new
TokenList
(
" nop aconst_null iconst_m1 iconst_0 iconst_1 iconst_2 iconst_3"
+
" iconst_4 iconst_5 lconst_0 lconst_1 fconst_0 fconst_1 fconst_2"
+
" dconst_0 dconst_1 bipush/s sipush/ss ldc/k ldc_w/kk ldc2_w/kk"
+
" iload/wl lload/wl fload/wl dload/wl aload/wl iload_0 iload_1"
+
" iload_2 iload_3 lload_0 lload_1 lload_2 lload_3 fload_0 fload_1"
+
" fload_2 fload_3 dload_0 dload_1 dload_2 dload_3 aload_0 aload_1"
+
" aload_2 aload_3 iaload laload faload daload aaload baload caload"
+
" saload istore/wl lstore/wl fstore/wl dstore/wl astore/wl"
+
" istore_0 istore_1 istore_2 istore_3 lstore_0 lstore_1 lstore_2"
+
" lstore_3 fstore_0 fstore_1 fstore_2 fstore_3 dstore_0 dstore_1"
+
" dstore_2 dstore_3 astore_0 astore_1 astore_2 astore_3 iastore"
+
" lastore fastore dastore aastore bastore castore sastore pop pop2"
+
" dup dup_x1 dup_x2 dup2 dup2_x1 dup2_x2 swap iadd ladd fadd dadd"
+
" isub lsub fsub dsub imul lmul fmul dmul idiv ldiv fdiv ddiv irem"
+
" lrem frem drem ineg lneg fneg dneg ishl lshl ishr lshr iushr"
+
" lushr iand land ior lor ixor lxor iinc/wls i2l i2f i2d l2i l2f"
+
" l2d f2i f2l f2d d2i d2l d2f i2b i2c i2s lcmp fcmpl fcmpg dcmpl"
+
" dcmpg ifeq/oo ifne/oo iflt/oo ifge/oo ifgt/oo ifle/oo"
+
" if_icmpeq/oo if_icmpne/oo if_icmplt/oo if_icmpge/oo if_icmpgt/oo"
+
" if_icmple/oo if_acmpeq/oo if_acmpne/oo goto/oo jsr/oo ret/wl"
+
" tableswitch/oooot lookupswitch/oooot ireturn lreturn freturn dreturn areturn"
+
" return getstatic/kf putstatic/kf getfield/kf putfield/kf"
+
" invokevirtual/km invokespecial/km invokestatic/km"
+
" invokeinterface/knxx xxxunusedxxx new/kc newarray/x anewarray/kc"
+
" arraylength athrow checkcast/kc instanceof/kc monitorenter"
+
" monitorexit wide multianewarray/kcx ifnull/oo ifnonnull/oo"
+
" goto_w/oooo jsr_w/oooo"
);
assert
(
tl
.
size
()
==
202
);
// this many instructions!
HashMap
<
String
,
Integer
>
map
=
new
HashMap
<
String
,
Integer
>(
tl
.
size
());
String
[]
names
=
tl
.
toArray
(
new
String
[
tl
.
size
()]);
String
[]
formats
=
new
String
[
names
.
length
];
String
[]
wideFormats
=
new
String
[
names
.
length
];
StringBuilder
sbuf
=
new
StringBuilder
();
sbuf
.
append
(
'i'
);
// all op formats begin with "i"
int
i
=
0
;
for
(
String
ins
:
names
)
{
assert
(
ins
==
ins
.
trim
());
// no whitespace
int
sfx
=
ins
.
indexOf
(
'/'
);
String
format
=
"i"
;
String
wideFormat
=
null
;
if
(
sfx
>=
0
)
{
format
=
ins
.
substring
(
sfx
+
1
);
ins
=
ins
.
substring
(
0
,
sfx
);
if
(
format
.
charAt
(
0
)
==
'w'
)
{
format
=
format
.
substring
(
1
);
sbuf
.
setLength
(
1
);
for
(
int
j
=
0
;
j
<
format
.
length
();
j
++)
{
// double everything except the initial 'i'
sbuf
.
append
(
format
.
charAt
(
j
));
sbuf
.
append
(
format
.
charAt
(
j
));
}
wideFormat
=
sbuf
.
toString
().
intern
();
}
sbuf
.
setLength
(
1
);
sbuf
.
append
(
format
);
format
=
sbuf
.
toString
().
intern
();
}
ins
=
ins
.
intern
();
names
[
i
]
=
ins
;
formats
[
i
]
=
format
;
wideFormats
[
i
]
=
(
wideFormat
!=
null
)
?
wideFormat
:
format
;
//System.out.println(ins+" "+format+" "+wideFormat);
map
.
put
(
ins
,
i
++);
}
//map = Collections.unmodifiableMap(map);
HashMap
<
String
,
Element
>
abb
=
new
HashMap
<
String
,
Element
>(
tl
.
size
()
/
2
);
abb
.
put
(
"iconst_m1"
,
new
Element
(
"bipush"
,
"num"
,
"-1"
));
for
(
String
ins
:
names
)
{
int
sfx
=
ins
.
indexOf
(
'_'
);
if
(
sfx
>=
0
&&
Character
.
isDigit
(
ins
.
charAt
(
sfx
+
1
)))
{
String
pfx
=
ins
.
substring
(
0
,
sfx
).
intern
();
String
num
=
ins
.
substring
(
sfx
+
1
);
String
att
=
pfx
.
endsWith
(
"const"
)
?
"num"
:
"loc"
;
Element
exp
=
new
Element
(
pfx
,
att
,
num
).
deepFreeze
();
abb
.
put
(
ins
,
exp
);
}
}
//abb = Collections.unmodifiableMap(abb);
HashMap
<
Element
,
String
>
rabb
=
new
HashMap
<
Element
,
String
>(
tl
.
size
()
/
2
);
for
(
Map
.
Entry
<
String
,
Element
>
e
:
abb
.
entrySet
())
{
rabb
.
put
(
e
.
getValue
(),
e
.
getKey
());
}
//rabb = Collections.unmodifiableMap(rabb);
bcNames
=
names
;
bcFormats
=
formats
;
bcWideFormats
=
wideFormats
;
bcCodes
=
map
;
abbrevs
=
abb
;
rabbrevs
=
rabb
;
}
public
static
String
opName
(
int
op
)
{
if
(
op
>=
0
&&
op
<
bcNames
.
length
)
{
return
bcNames
[
op
];
}
return
"unknown#"
+
op
;
}
public
static
String
opFormat
(
int
op
)
{
return
opFormat
(
op
,
false
);
}
public
static
String
opFormat
(
int
op
,
boolean
isWide
)
{
if
(
op
>=
0
&&
op
<
bcFormats
.
length
)
{
return
(
isWide
?
bcWideFormats
[
op
]
:
bcFormats
[
op
]);
}
return
"?"
;
}
public
static
int
opCode
(
String
opName
)
{
Integer
op
=
(
Integer
)
bcCodes
.
get
(
opName
);
if
(
op
!=
null
)
{
return
op
.
intValue
();
}
return
-
1
;
}
public
static
Element
expandAbbrev
(
String
opName
)
{
return
abbrevs
.
get
(
opName
);
}
public
static
String
findAbbrev
(
Element
op
)
{
return
rabbrevs
.
get
(
op
);
}
public
static
int
invertBranchOp
(
int
op
)
{
assert
(
opFormat
(
op
).
indexOf
(
'o'
)
>=
0
);
final
int
IFMIN
=
0x99
;
final
int
IFMAX
=
0xa6
;
final
int
IFMIN2
=
0xc6
;
final
int
IFMAX2
=
0xc7
;
assert
(
bcNames
[
IFMIN
]
==
"ifeq"
);
assert
(
bcNames
[
IFMAX
]
==
"if_acmpne"
);
assert
(
bcNames
[
IFMIN2
]
==
"ifnonnull"
);
assert
(
bcNames
[
IFMAX2
]
==
"ifnull"
);
int
rop
;
if
(
op
>=
IFMIN
&&
op
<=
IFMAX
)
{
rop
=
IFMIN
+
((
op
-
IFMIN
)
^
1
);
}
else
if
(
op
>=
IFMIN2
&&
op
<=
IFMAX2
)
{
rop
=
IFMIN2
+
((
op
-
IFMIN2
)
^
1
);
}
else
{
assert
(
false
);
rop
=
op
;
}
assert
(
opFormat
(
rop
).
indexOf
(
'o'
)
>=
0
);
return
rop
;
}
public
static
Element
parse
(
String
bytes
)
{
Element
e
=
new
Element
(
"Instructions"
,
bytes
.
length
());
boolean
willBeWide
;
boolean
isWide
=
false
;
Element
[]
tempMap
=
new
Element
[
bytes
.
length
()];
for
(
int
pc
=
0
,
nextpc
;
pc
<
bytes
.
length
();
pc
=
nextpc
)
{
int
op
=
bytes
.
charAt
(
pc
);
Element
i
=
new
Element
(
opName
(
op
));
nextpc
=
pc
+
1
;
int
locarg
=
0
;
int
cparg
=
0
;
int
intarg
=
0
;
int
labelarg
=
0
;
willBeWide
=
false
;
switch
(
op
)
{
case
0xc4
:
//wide
willBeWide
=
true
;
break
;
case
0x10
:
//bipush
intarg
=
nextpc
++;
intarg
*=
-
1
;
//mark signed
break
;
case
0x11
:
//sipush
intarg
=
nextpc
;
nextpc
+=
2
;
intarg
*=
-
1
;
//mark signed
break
;
case
0x12
:
//ldc
cparg
=
nextpc
++;
break
;
case
0x13
:
//ldc_w
case
0x14
:
//ldc2_w
case
0xb2
:
//getstatic
case
0xb3
:
//putstatic
case
0xb4
:
//getfield
case
0xb5
:
//putfield
case
0xb6
:
//invokevirtual
case
0xb7
:
//invokespecial
case
0xb8
:
//invokestatic
case
0xbb
:
//new
case
0xbd
:
//anewarray
case
0xc0
:
//checkcast
case
0xc1
:
//instanceof
cparg
=
nextpc
;
nextpc
+=
2
;
break
;
case
0xb9
:
//invokeinterface
cparg
=
nextpc
;
nextpc
+=
2
;
intarg
=
nextpc
;
nextpc
+=
2
;
break
;
case
0xc5
:
//multianewarray
cparg
=
nextpc
;
nextpc
+=
2
;
intarg
=
nextpc
++;
break
;
case
0x15
:
//iload
case
0x16
:
//lload
case
0x17
:
//fload
case
0x18
:
//dload
case
0x19
:
//aload
case
0x36
:
//istore
case
0x37
:
//lstore
case
0x38
:
//fstore
case
0x39
:
//dstore
case
0x3a
:
//astore
case
0xa9
:
//ret
locarg
=
nextpc
++;
if
(
isWide
)
{
nextpc
++;
}
break
;
case
0x84
:
//iinc
locarg
=
nextpc
++;
if
(
isWide
)
{
nextpc
++;
}
intarg
=
nextpc
++;
if
(
isWide
)
{
nextpc
++;
}
intarg
*=
-
1
;
//mark signed
break
;
case
0x99
:
//ifeq
case
0x9a
:
//ifne
case
0x9b
:
//iflt
case
0x9c
:
//ifge
case
0x9d
:
//ifgt
case
0x9e
:
//ifle
case
0x9f
:
//if_icmpeq
case
0xa0
:
//if_icmpne
case
0xa1
:
//if_icmplt
case
0xa2
:
//if_icmpge
case
0xa3
:
//if_icmpgt
case
0xa4
:
//if_icmple
case
0xa5
:
//if_acmpeq
case
0xa6
:
//if_acmpne
case
0xa7
:
//goto
case
0xa8
:
//jsr
labelarg
=
nextpc
;
nextpc
+=
2
;
break
;
case
0xbc
:
//newarray
intarg
=
nextpc
++;
break
;
case
0xc6
:
//ifnull
case
0xc7
:
//ifnonnull
labelarg
=
nextpc
;
nextpc
+=
2
;
break
;
case
0xc8
:
//goto_w
case
0xc9
:
//jsr_w
labelarg
=
nextpc
;
nextpc
+=
4
;
break
;
// save the best for last:
case
0xaa
:
//tableswitch
nextpc
=
parseSwitch
(
bytes
,
pc
,
true
,
i
);
break
;
case
0xab
:
//lookupswitch
nextpc
=
parseSwitch
(
bytes
,
pc
,
false
,
i
);
break
;
}
String
format
=
null
;
assert
((
format
=
opFormat
(
op
,
isWide
))
!=
null
);
//System.out.println("pc="+pc+" len="+(nextpc - pc)+" w="+isWide+" op="+op+" name="+opName(op)+" format="+format);
assert
((
nextpc
-
pc
)
==
format
.
length
()
||
format
.
indexOf
(
't'
)
>=
0
);
// Parse out instruction fields.
if
(
locarg
!=
0
)
{
int
len
=
nextpc
-
locarg
;
if
(
intarg
!=
0
)
{
len
/=
2
;
// split
}
i
.
setAttr
(
"loc"
,
""
+
getInt
(
bytes
,
locarg
,
len
));
assert
(
'l'
==
format
.
charAt
(
locarg
-
pc
+
0
));
assert
(
'l'
==
format
.
charAt
(
locarg
-
pc
+
len
-
1
));
}
if
(
cparg
!=
0
)
{
int
len
=
nextpc
-
cparg
;
if
(
len
>
2
)
{
len
=
2
;
}
i
.
setAttr
(
"ref"
,
""
+
getInt
(
bytes
,
cparg
,
len
));
assert
(
'k'
==
format
.
charAt
(
cparg
-
pc
+
0
));
}
if
(
intarg
!=
0
)
{
boolean
isSigned
=
(
intarg
<
0
);
if
(
isSigned
)
{
intarg
*=
-
1
;
}
int
len
=
nextpc
-
intarg
;
i
.
setAttr
(
"num"
,
""
+
getInt
(
bytes
,
intarg
,
isSigned
?
-
len
:
len
));
assert
((
isSigned
?
's'
:
'x'
)
==
format
.
charAt
(
intarg
-
pc
+
0
));
assert
((
isSigned
?
's'
:
'x'
)
==
format
.
charAt
(
intarg
-
pc
+
len
-
1
));
}
if
(
labelarg
!=
0
)
{
int
len
=
nextpc
-
labelarg
;
int
offset
=
getInt
(
bytes
,
labelarg
,
-
len
);
int
target
=
pc
+
offset
;
i
.
setAttr
(
"lab"
,
""
+
target
);
assert
(
'o'
==
format
.
charAt
(
labelarg
-
pc
+
0
));
assert
(
'o'
==
format
.
charAt
(
labelarg
-
pc
+
len
-
1
));
}
e
.
add
(
i
);
tempMap
[
pc
]
=
i
;
isWide
=
willBeWide
;
}
// Mark targets of branches.
for
(
Element
i
:
e
.
elements
())
{
for
(
int
j
=
-
1
;
j
<
i
.
size
();
j
++)
{
Element
c
=
(
j
<
0
)
?
i
:
(
Element
)
i
.
get
(
j
);
Number
targetNum
=
c
.
getAttrNumber
(
"lab"
);
if
(
targetNum
!=
null
)
{
int
target
=
targetNum
.
intValue
();
Element
ti
=
null
;
if
(
target
>=
0
&&
target
<
tempMap
.
length
)
{
ti
=
tempMap
[
target
];
}
if
(
ti
!=
null
)
{
ti
.
setAttr
(
"pc"
,
""
+
target
);
}
else
{
c
.
setAttr
(
"lab.error"
,
""
);
}
}
}
}
// Shrink to fit:
for
(
Element
i
:
e
.
elements
())
{
i
.
trimToSize
();
}
e
.
trimToSize
();
/*
String assem = assemble(e);
if (!assem.equals(bytes)) {
System.out.println("Bytes: "+bytes);
System.out.println("Insns: "+e);
System.out.println("Assem: "+parse(assem));
}
*/
return
e
;
}
static
int
switchBase
(
int
pc
)
{
int
apc
=
pc
+
1
;
apc
+=
(-
apc
)
&
3
;
return
apc
;
}
static
int
parseSwitch
(
String
s
,
int
pc
,
boolean
isTable
,
Element
i
)
{
int
apc
=
switchBase
(
pc
);
int
defLabel
=
pc
+
getInt
(
s
,
apc
+
4
*
0
,
4
);
i
.
setAttr
(
"lab"
,
""
+
defLabel
);
if
(
isTable
)
{
int
lowCase
=
getInt
(
s
,
apc
+
4
*
1
,
4
);
int
highCase
=
getInt
(
s
,
apc
+
4
*
2
,
4
);
int
caseCount
=
highCase
-
lowCase
+
1
;
for
(
int
n
=
0
;
n
<
caseCount
;
n
++)
{
Element
c
=
new
Element
(
"Case"
,
4
);
int
caseVal
=
lowCase
+
n
;
int
caseLab
=
getInt
(
s
,
apc
+
4
*
(
3
+
n
),
4
)
+
pc
;
c
.
setAttr
(
"num"
,
""
+
caseVal
);
c
.
setAttr
(
"lab"
,
""
+
caseLab
);
assert
(
c
.
getExtraCapacity
()
==
0
);
i
.
add
(
c
);
}
return
apc
+
4
*
(
3
+
caseCount
);
}
else
{
int
caseCount
=
getInt
(
s
,
apc
+
4
*
1
,
4
);
for
(
int
n
=
0
;
n
<
caseCount
;
n
++)
{
Element
c
=
new
Element
(
"Case"
,
4
);
int
caseVal
=
getInt
(
s
,
apc
+
4
*
(
2
+
(
2
*
n
)
+
0
),
4
);
int
caseLab
=
getInt
(
s
,
apc
+
4
*
(
2
+
(
2
*
n
)
+
1
),
4
)
+
pc
;
c
.
setAttr
(
"num"
,
""
+
caseVal
);
c
.
setAttr
(
"lab"
,
""
+
caseLab
);
assert
(
c
.
getExtraCapacity
()
==
0
);
i
.
add
(
c
);
}
return
apc
+
4
*
(
2
+
2
*
caseCount
);
}
}
static
int
getInt
(
String
s
,
int
pc
,
int
len
)
{
//System.out.println("getInt s["+s.length()+"] pc="+pc+" len="+len);
int
result
=
s
.
charAt
(
pc
);
if
(
len
<
0
)
{
len
=
-
len
;
result
=
(
byte
)
result
;
}
if
(!(
len
==
1
||
len
==
2
||
len
==
4
))
{
System
.
out
.
println
(
"len="
+
len
);
}
assert
(
len
==
1
||
len
==
2
||
len
==
4
);
for
(
int
i
=
1
;
i
<
len
;
i
++)
{
result
<<=
8
;
result
+=
s
.
charAt
(
pc
+
i
)
&
0xFF
;
}
return
result
;
}
public
static
String
assemble
(
Element
instructions
)
{
return
InstructionAssembler
.
assemble
(
instructions
,
null
,
null
);
}
public
static
String
assemble
(
Element
instructions
,
String
pcAttrName
)
{
return
InstructionAssembler
.
assemble
(
instructions
,
pcAttrName
,
null
);
}
public
static
String
assemble
(
Element
instructions
,
ClassSyntax
.
GetCPIndex
getCPI
)
{
return
InstructionAssembler
.
assemble
(
instructions
,
null
,
getCPI
);
}
public
static
String
assemble
(
Element
instructions
,
String
pcAttrName
,
ClassSyntax
.
GetCPIndex
getCPI
)
{
return
InstructionAssembler
.
assemble
(
instructions
,
pcAttrName
,
getCPI
);
}
}
test/tools/pack200/pack200-verifier/src/xmlkit/TokenList.java
0 → 100644
浏览文件 @
a1c492e8
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package
xmlkit
;
// -*- mode: java; indent-tabs-mode: nil -*-
import
java.util.*
;
/**
* A List of Strings each representing a word or token.
* This object itself is a CharSequence whose characters consist
* of all the tokens, separated by blanks.
*
* @author jrose
*/
public
class
TokenList
extends
ArrayList
<
String
>
implements
CharSequence
{
protected
String
separator
;
protected
boolean
frozen
;
public
TokenList
()
{
this
.
separator
=
" "
;
}
public
TokenList
(
Collection
<?
extends
Object
>
tokens
)
{
super
(
tokens
.
size
());
this
.
separator
=
" "
;
addTokens
(
tokens
);
}
public
TokenList
(
Collection
<?
extends
Object
>
tokens
,
String
separator
)
{
super
(
tokens
.
size
());
this
.
separator
=
separator
;
addTokens
(
tokens
);
}
public
TokenList
(
Object
[]
tokens
)
{
super
(
tokens
.
length
);
this
.
separator
=
" "
;
addTokens
(
tokens
,
0
,
tokens
.
length
);
}
public
TokenList
(
Object
[]
tokens
,
int
beg
,
int
end
)
{
super
(
end
-
beg
);
// capacity
this
.
separator
=
" "
;
addTokens
(
tokens
,
beg
,
end
);
}
public
TokenList
(
Object
[]
tokens
,
int
beg
,
int
end
,
String
separator
)
{
super
(
end
-
beg
);
// capacity
this
.
separator
=
separator
;
addTokens
(
tokens
,
beg
,
end
);
}
public
TokenList
(
String
tokenStr
)
{
this
(
tokenStr
,
" "
,
false
);
}
public
TokenList
(
String
tokenStr
,
String
separator
)
{
this
(
tokenStr
,
separator
,
true
);
}
public
TokenList
(
String
tokenStr
,
String
separator
,
boolean
allowNulls
)
{
super
(
tokenStr
.
length
()
/
5
);
this
.
separator
=
separator
;
addTokens
(
tokenStr
,
allowNulls
);
}
static
public
final
TokenList
EMPTY
;
static
{
TokenList
tl
=
new
TokenList
(
new
Object
[
0
]);
tl
.
freeze
();
EMPTY
=
tl
;
}
public
void
freeze
()
{
if
(!
frozen
)
{
for
(
ListIterator
<
String
>
i
=
listIterator
();
i
.
hasNext
();)
{
i
.
set
(
i
.
next
().
toString
());
}
trimToSize
();
frozen
=
true
;
}
}
public
boolean
isFrozen
()
{
return
frozen
;
}
void
checkNotFrozen
()
{
if
(
isFrozen
())
{
throw
new
UnsupportedOperationException
(
"cannot modify frozen TokenList"
);
}
}
public
String
getSeparator
()
{
return
separator
;
}
public
void
setSeparator
(
String
separator
)
{
checkNotFrozen
();
this
.
separator
=
separator
;
}
/// All normal List mutators must check the frozen bit:
public
String
set
(
int
index
,
String
o
)
{
checkNotFrozen
();
return
super
.
set
(
index
,
o
);
}
public
boolean
add
(
String
o
)
{
checkNotFrozen
();
return
super
.
add
(
o
);
}
public
void
add
(
int
index
,
String
o
)
{
checkNotFrozen
();
super
.
add
(
index
,
o
);
}
public
boolean
addAll
(
Collection
<?
extends
String
>
c
)
{
checkNotFrozen
();
return
super
.
addAll
(
c
);
}
public
boolean
addAll
(
int
index
,
Collection
<?
extends
String
>
c
)
{
checkNotFrozen
();
return
super
.
addAll
(
index
,
c
);
}
public
boolean
remove
(
Object
o
)
{
checkNotFrozen
();
return
super
.
remove
(
o
);
}
public
String
remove
(
int
index
)
{
checkNotFrozen
();
return
super
.
remove
(
index
);
}
public
void
clear
()
{
checkNotFrozen
();
super
.
clear
();
}
/** Add a collection of tokens to the list, applying toString to each. */
public
boolean
addTokens
(
Collection
<?
extends
Object
>
tokens
)
{
// Note that if this sequence is empty, no tokens are added.
// This is different from adding a null string, which is
// a single token.
boolean
added
=
false
;
for
(
Object
token
:
tokens
)
{
add
(
token
.
toString
());
added
=
true
;
}
return
added
;
}
public
boolean
addTokens
(
Object
[]
tokens
,
int
beg
,
int
end
)
{
boolean
added
=
false
;
for
(
int
i
=
beg
;
i
<
end
;
i
++)
{
add
(
tokens
[
i
].
toString
());
added
=
true
;
}
return
added
;
}
public
boolean
addTokens
(
String
tokenStr
)
{
return
addTokens
(
tokenStr
,
false
);
}
public
boolean
addTokens
(
String
tokenStr
,
boolean
allowNulls
)
{
boolean
added
=
false
;
int
pos
=
0
,
limit
=
tokenStr
.
length
(),
sep
=
limit
;
while
(
pos
<
limit
)
{
sep
=
tokenStr
.
indexOf
(
separator
,
pos
);
if
(
sep
<
0
)
{
sep
=
limit
;
}
if
(
sep
==
pos
)
{
if
(
allowNulls
)
{
add
(
""
);
added
=
true
;
}
pos
+=
separator
.
length
();
}
else
{
add
(
tokenStr
.
substring
(
pos
,
sep
));
added
=
true
;
pos
=
sep
+
separator
.
length
();
}
}
if
(
allowNulls
&&
sep
<
limit
)
{
// Input was something like "tok1 tok2 ".
add
(
""
);
added
=
true
;
}
return
added
;
}
public
boolean
addToken
(
Object
token
)
{
return
add
(
token
.
toString
());
}
/** Format the token string, using quotes and escapes.
* Quotes must contain an odd number of 3 or more elements,
* a sequence of begin/end quote pairs, plus a superquote.
* For each token, the first begin/end pair is used for
* which the end quote does not occur in the token.
* If the token contains all end quotes, the last pair
* is used, with all occurrences of the end quote replaced
* by the superquote. If an end quote is the empty string,
* the separator is used instead.
*/
public
String
format
(
String
separator
,
String
[]
quotes
)
{
return
""
;
//@@
}
protected
int
[]
lengths
;
protected
static
final
int
MODC
=
0
,
HINT
=
1
,
BEG0
=
2
,
END0
=
3
;
// Layout of lengths:
// { modCount, hint, -1==beg[0], end[0]==beg[1], ..., length }
// Note that each beg[i]..end[i] span includes a leading separator,
// which is not part of the corresponding token.
protected
final
CharSequence
getCS
(
int
i
)
{
return
(
CharSequence
)
get
(
i
);
}
// Produce (and cache) an table of indexes for each token.
protected
int
[]
getLengths
()
{
int
[]
lengths
=
this
.
lengths
;
;
int
sepLength
=
separator
.
length
();
if
(
lengths
==
null
||
lengths
[
MODC
]
!=
modCount
)
{
int
size
=
this
.
size
();
lengths
=
new
int
[
END0
+
size
+
(
size
==
0
?
1
:
0
)];
lengths
[
MODC
]
=
modCount
;
int
end
=
-
sepLength
;
// cancels leading separator
lengths
[
BEG0
]
=
end
;
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
end
+=
sepLength
;
// count leading separator
end
+=
getCS
(
i
).
length
();
lengths
[
END0
+
i
]
=
end
;
}
this
.
lengths
=
lengths
;
}
return
lengths
;
}
public
int
length
()
{
int
[]
lengths
=
getLengths
();
return
lengths
[
lengths
.
length
-
1
];
}
// Which token does the given index belong to?
protected
int
which
(
int
i
)
{
if
(
i
<
0
)
{
return
-
1
;
}
int
[]
lengths
=
getLengths
();
for
(
int
hint
=
lengths
[
HINT
];;
hint
=
0
)
{
for
(
int
wh
=
hint
;
wh
<
lengths
.
length
-
END0
;
wh
++)
{
int
beg
=
lengths
[
BEG0
+
wh
];
int
end
=
lengths
[
END0
+
wh
];
if
(
i
>=
beg
&&
i
<
end
)
{
lengths
[
HINT
]
=
wh
;
return
wh
;
}
}
if
(
hint
==
0
)
{
return
size
();
// end of the line
}
}
}
public
char
charAt
(
int
i
)
{
if
(
i
<
0
)
{
return
""
.
charAt
(
i
);
}
int
wh
=
which
(
i
);
int
beg
=
lengths
[
BEG0
+
wh
];
int
j
=
i
-
beg
;
int
sepLength
=
separator
.
length
();
if
(
j
<
sepLength
)
{
return
separator
.
charAt
(
j
);
}
return
getCS
(
wh
).
charAt
(
j
-
sepLength
);
}
public
CharSequence
subSequence
(
int
beg
,
int
end
)
{
//System.out.println("i: "+beg+".."+end);
if
(
beg
==
end
)
{
return
""
;
}
if
(
beg
<
0
)
{
charAt
(
beg
);
// raise exception
}
if
(
beg
>
end
)
{
charAt
(-
1
);
// raise exception
}
int
begWh
=
which
(
beg
);
int
endWh
=
which
(
end
);
if
(
endWh
==
size
()
||
end
==
lengths
[
BEG0
+
endWh
])
{
--
endWh
;
}
//System.out.println("wh: "+begWh+".."+endWh);
int
begBase
=
lengths
[
BEG0
+
begWh
];
int
endBase
=
lengths
[
BEG0
+
endWh
];
int
sepLength
=
separator
.
length
();
int
begFrag
=
0
;
if
((
beg
-
begBase
)
<
sepLength
)
{
begFrag
=
sepLength
-
(
beg
-
begBase
);
beg
+=
begFrag
;
}
int
endFrag
=
0
;
if
((
end
-
endBase
)
<
sepLength
)
{
endFrag
=
(
end
-
endBase
);
end
=
endBase
;
endBase
=
lengths
[
BEG0
+
--
endWh
];
}
if
(
false
)
{
System
.
out
.
print
(
"beg[wbf]end[wbf]"
);
int
pr
[]
=
{
begWh
,
begBase
,
begFrag
,
beg
,
endWh
,
endBase
,
endFrag
,
end
};
for
(
int
k
=
0
;
k
<
pr
.
length
;
k
++)
{
System
.
out
.
print
((
k
==
4
?
" "
:
" "
)
+
(
pr
[
k
]));
}
System
.
out
.
println
();
}
if
(
begFrag
>
0
&&
(
end
+
endFrag
)
-
begBase
<=
sepLength
)
{
// Special case: Slice the separator.
beg
-=
begFrag
;
end
+=
endFrag
;
return
separator
.
substring
(
beg
-
begBase
,
end
-
begBase
);
}
if
(
begWh
==
endWh
&&
(
begFrag
+
endFrag
)
==
0
)
{
// Special case: Slice a single token.
return
getCS
(
begWh
).
subSequence
(
beg
-
begBase
-
sepLength
,
end
-
endBase
-
sepLength
);
}
Object
[]
subTokens
=
new
Object
[
1
+
(
endWh
-
begWh
)
+
1
];
int
fillp
=
0
;
if
(
begFrag
==
sepLength
)
{
// Insert a leading null token to force an initial separator.
subTokens
[
fillp
++]
=
""
;
begFrag
=
0
;
}
for
(
int
wh
=
begWh
;
wh
<=
endWh
;
wh
++)
{
CharSequence
cs
=
getCS
(
wh
);
if
(
wh
==
begWh
||
wh
==
endWh
)
{
// Slice it.
int
csBeg
=
(
wh
==
begWh
)
?
(
beg
-
begBase
)
-
sepLength
:
0
;
int
csEnd
=
(
wh
==
endWh
)
?
(
end
-
endBase
)
-
sepLength
:
cs
.
length
();
cs
=
cs
.
subSequence
(
csBeg
,
csEnd
);
if
(
begFrag
>
0
&&
wh
==
begWh
)
{
cs
=
separator
.
substring
(
sepLength
-
begFrag
)
+
cs
;
}
if
(
endFrag
>
0
&&
wh
==
endWh
)
{
cs
=
cs
.
toString
()
+
separator
.
substring
(
0
,
endFrag
);
}
}
subTokens
[
fillp
++]
=
cs
;
}
return
new
TokenList
(
subTokens
,
0
,
fillp
,
separator
);
}
/** Returns the concatenation of all tokens,
* with intervening separator characters.
*/
public
String
toString
()
{
StringBuilder
buf
=
new
StringBuilder
(
length
());
int
size
=
this
.
size
();
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
if
(
i
>
0
)
{
buf
.
append
(
separator
);
}
buf
.
append
(
get
(
i
));
}
return
buf
.
toString
();
}
/*---- TESTING CODE ----
public static void main(String[] av) {
if (av.length == 0) av = new String[]{"one", "2", "", "four"};
TokenList ts = new TokenList();
final String SEP = ", ";
ts.setSeparator(SEP);
for (int i = -1; i < av.length; i++) {
if (i >= 0) ts.addToken(av[i]);
{
TokenList tsCopy = new TokenList(ts.toString(), SEP);
if (!tsCopy.equals(ts)) {
tsCopy.setSeparator(")(");
System.out.println("!= ("+tsCopy+")");
}
}
{
TokenList tsBar = new TokenList(ts, "|");
tsBar.add(0, "[");
tsBar.add("]");
System.out.println(tsBar);
}
if (false) {
int[] ls = ts.getLengths();
System.out.println("ts: "+ts);
System.out.print("ls: {");
for (int j = 0; j < ls.length; j++) System.out.print(" "+ls[j]);
System.out.println(" }");
}
assert0(ts.size() == i+1);
assert0(i < 0 || ts.get(i) == av[i]);
String tss = ts.toString();
int tslen = tss.length();
assert0(ts.length() == tss.length());
for (int n = 0; n < tslen; n++) {
assert0(ts.charAt(n) == tss.charAt(n));
}
for (int j = 0; j < tslen; j++) {
for (int k = tslen; k >= j; k--) {
CharSequence sub = ts.subSequence(j, k);
//System.out.println("|"+sub+"|");
assert0(sub.toString().equals(tss.substring(j, k)));
}
}
}
}
static void assert0(boolean z) {
if (!z) throw new RuntimeException("assert failed");
}
// ---- TESTING CODE ----*/
}
test/tools/pack200/pack200-verifier/src/xmlkit/XMLKit.java
0 → 100644
浏览文件 @
a1c492e8
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package
xmlkit
;
// -*- mode: java; indent-tabs-mode: nil -*-
// XML Implementation packages:
import
java.util.*
;
import
java.io.Reader
;
import
java.io.Writer
;
import
java.io.OutputStream
;
import
java.io.InputStreamReader
;
import
java.io.OutputStreamWriter
;
import
java.io.BufferedReader
;
import
java.io.PrintWriter
;
import
java.io.StringWriter
;
import
java.io.StringReader
;
import
java.io.IOException
;
import
org.xml.sax.XMLReader
;
import
org.xml.sax.InputSource
;
import
org.xml.sax.ContentHandler
;
import
org.xml.sax.SAXException
;
import
org.xml.sax.SAXParseException
;
import
org.xml.sax.Attributes
;
import
org.xml.sax.ext.LexicalHandler
;
import
org.xml.sax.helpers.AttributesImpl
;
/**
* A kit of methods and classes useful for manipulating XML trees in
* memory. They are very compact and easy to use. An XML element
* occupies six pointers of overhead (like two arrays) plus a pointer
* for its name, each attribute name and value, and each sub-element.
* Many useful XML operations (or Lisp-like calls) can be accomplished
* with a single method call on an element itself.
* <p>
* There is strong integration with the Java collection classes.
* There are viewing and conversion operators to and from various
* collection types. Elements directly support list iterators.
* Most <tt>List</tt> methods work analogously on elements.
* <p>
* Because of implementation compromises, these XML trees are less
* functional than many standard XML classes.
* <ul>
* <li>There are no parent or sibling pointers in the tree.</li>
* <li>Attribute names are simple strings, with no namespaces.</li>
* <li>There is no internal support for schemas or validation.</li>
* </ul>
* <p>
* Here is a summary of functionality in <tt>XMLKit</tt>.
* (Overloaded groups of methods are summarized by marking some
* arguments optional with their default values. Some overloaded
* arguments are marked with their alternative types separated by
* a bar "|". Arguments or return values for which a null is
* specially significant are marked by an alternative "|null".
* Accessors which have corresponding setters are marked
* by "/set". Removers which have corresponding retainers are marked
* by "/retain".)
* <pre>
* --- element construction
* new Element(int elemCapacity=4), String name=""
* new Element(String name, String[] attrs={}, Element[] elems={}, int elemCapacity=4)
* new Element(String name, String[] attrs, Object[] elems, int elemCapacity=4)
* new Element(Element original) // shallow copy
* new Element(String name="", Collection elems) // coercion
*
* Element shallowCopy()
* Element shallowFreeze() // side-effecting
* Element deepCopy()
* Element deepFreeze() // not side-effecting
*
* EMPTY // frozen empty anonymous element
* void ensureExtraCapacity(int)
* void trimToSize()
* void sortAttrs() // sort by key
*
* --- field accessors
* String getName()/set
* int size()
* boolean isEmpty()
* boolean isFrozen()
* boolean isAnonymous()
* int getExtraCapacity()/set
* int attrSize()
*
* --- attribute accessors
* String getAttr(int i)/set
* String getAttrName(int i)
*
* String getAttr(String key)/set
* List getAttrList(String key)/set
* Number getAttrNumber(String key)/set
* long getAttrLong(String key)/set
* double getAttrDouble(String key)/set
*
* String getAttr(String key, String dflt=null)
* long getAttrLong(String key, long dflt=0)
* double getAttrDouble(String key, double dflt=0)
*
* Element copyAttrsOnly()
* Element getAttrs()/set => <em><><key>value</key>...</></em>
* void addAttrs(Element attrs)
*
* void removeAttr(int i)
* void clearAttrs()
*
* --- element accessors
* Object get(int i)/set
* Object getLast() | null
* Object[] toArray()
* Element copyContentOnly()
*
* void add(int i=0, Object subElem)
* int addAll(int i=0, Collection | Element elems)
* int addContent(int i=0, TokenList|Element|Object|null)
* void XMLKit.addContent(TokenList|Element|Object|null, Collection sink|null)
*
* void clear(int beg=0, int end=size)
* void sort(Comparator=contentOrder())
* void reverse()
* void shuffle(Random rnd=(anonymous))
* void rotate(int distance)
* Object min/max(Comparator=contentOrder())
*
* --- text accessors
* CharSequence getText()/set
* CharSequence getUnmarkedText()
* int addText(int i=size, CharSequence)
* void trimText();
*
* --- views
* List asList() // element view
* ListIterator iterator()
* PrintWriter asWriter()
* Map asAttrMap()
* Iterable<CharSequence> texts()
* Iterable<Element> elements()
* Iterable<T> partsOnly(Class<T>)
* String[] toStrings()
*
* --- queries
* boolean equals(Element | Object)
* int compareTo(Element | Object)
* boolean equalAttrs(Element)
* int hashCode()
* boolean isText() // every sub-elem is CharSequence
* boolean hasText() // some sub-elem is CharSequence
*
* boolean contains(Object)
* boolean containsAttr(String)
*
* int indexOf(Object)
* int indexOf(Filter, int fromIndex=0)
* int lastIndexOf(Object)
* int lastIndexOf(Filter, int fromIndex=size-1)
*
* int indexOfAttr(String)
*
* // finders, removers, and replacers do addContent of each filtered value
* // (i.e., TokenLists and anonymous Elements are broken out into their parts)
* boolean matches(Filter)
*
* Object find(Filter, int fromIndex=0)
* Object findLast(Filter, int fromIndex=size-1)
* Element findAll(Filter, int fromIndex=0 & int toIndex=size)
* int findAll(Filter, Collection sink | null, int fromIndex=0 & int toIndex=size)
*
* Element removeAllInTree(Filter)/retain
* int findAllInTree(Filter, Collection sink | null)
* int countAllInTree(Filter)
* Element removeAllInTree(Filter)/retain
* int removeAllInTree(Filter, Collection sink | null)/retain
* void replaceAllInTree(Filter)
*
* Element findElement(String name=any)
* Element findAllElements(String name=any)
*
* Element findWithAttr(String key, String value=any)
* Element findAllWithAttr(String key, String value=any)
*
* Element removeElement(String name=any)
* Element removeAllElements(String name=any)/retain
*
* Element removeWithAttr(String key, String value=any)
* Element removeAllWithAttr(String key, String value=any)/retain
*
* //countAll is the same as findAll but with null sink
* int countAll(Filter)
* int countAllElements(String name=any)
* int countAllWithAttr(String key, String value=any)
*
* void replaceAll(Filter, int fromIndex=0 & int toIndex=size)
* void replaceAllInTree(Filter)
* void XMLKit.replaceAll(Filter, List target) //if(fx){remove x;addContent fx}
*
* --- element mutators
* boolean remove(Object)
* Object remove(int)
* Object removeLast() | null
*
* Object remove(Filter, int fromIndex=0)
* Object removeLast(Filter, int fromIndex=size-1)
* Element sink = removeAll(Filter, int fromIndex=0 & int toIndex=size)/retain
* int count = removeAll(Filter, int fromIndex=0 & int toIndex=size, Collection sink | null)/retain
*
* Element removeAllElements(String name=any)
*
* --- attribute mutators
* ??int addAllAttrsFrom(Element attrSource)
*
* --- parsing and printing
* void tokenize(String delims=whitespace, returnDelims=false)
* void writeTo(Writer)
* void writePrettyTo(Writer)
* String prettyString()
* String toString()
*
* ContentHandler XMLKit.makeBuilder(Collection sink, tokenizing=false, makeFrozen=false) // for standard XML parser
* Element XMLKit.readFrom(Reader, tokenizing=false, makeFrozen=false)
* void XMLKit.prettyPrintTo(Writer | OutputStream, Element)
* class XMLKit.Printer(Writer) { void print/Recursive(Element) }
* void XMLKit.output(Object elem, ContentHandler, LexicalHandler=null)
* void XMLKit.writeToken(String, char quote, Writer)
* void XMLKit.writeCData(String, Writer)
* Number XMLKit.convertToNumber(String, Number dflt=null)
* long XMLKit.convertToLong(String, long dflt=0)
* double XMLKit.convertToDouble(String, double dflt=0)
*
* --- filters
* XMLKit.ElementFilter { Element filter(Element) }
* XMLKit.elementFilter(String name=any | Collection nameSet)
* XMLKit.AttrFilter(String key) { boolean test(String value) }
* XMLKit.attrFilter(String key, String value=any)
* XMLKit.attrFilter(Element matchThis, String key)
* XMLKit.classFilter(Class)
* XMLKit.textFilter() // matches any CharSequence
* XMLKit.specialFilter() // matches any Special element
* XMLKit.methodFilter(Method m, Object[] args=null, falseResult=null)
* XMLKit.testMethodFilter(Method m, Object[] args=null)
* XMLKit.not(Filter) // inverts sense of Filter
* XMLKit.and(Filter&Filter | Filter[])
* XMLKit.or(Filter&Filter | Filter[])
* XMLKit.stack(Filter&Filter | Filter[]) // result is (fx && g(fx))
* XMLKit.content(Filter, Collection sink) // copies content to sink
* XMLKit.replaceInTree(Filter pre, Filter post=null) // pre-replace else recur
* XMLKit.findInTree(Filter pre, Collection sink=null) // pre-find else recur
* XMLKit.nullFilter() // ignores input, always returns null (i.e., false)
* XMLKit.selfFilter( ) // always returns input (i.e., true)
* XMLKit.emptyFilter() // ignores input, always returns EMPTY
* XMLKit.constantFilter(Object) // ignores input, always returns constant
*
* --- misc
* Comparator XMLKit.contentOrder() // for comparing/sorting mixed content
* Method XMLKit.Element.method(String name) // returns Element method
* </pre>
*
* @author jrose
*/
public
abstract
class
XMLKit
{
private
XMLKit
()
{
}
// We need at least this much slop if the element is to stay unfrozen.
static
final
int
NEED_SLOP
=
1
;
static
final
Object
[]
noPartsFrozen
=
{};
static
final
Object
[]
noPartsNotFrozen
=
new
Object
[
NEED_SLOP
];
static
final
String
WHITESPACE_CHARS
=
" \t\n\r\f"
;
static
final
String
ANON_NAME
=
new
String
(
"*"
);
// unique copy of "*"
public
static
final
class
Element
implements
Comparable
<
Element
>,
Iterable
<
Object
>
{
// Note: Does not implement List, because it has more
// significant parts besides its sub-elements. Therefore,
// hashCode and equals must be more distinctive than Lists.
// <name> of element
String
name
;
// number of child elements, in parts[0..size-1]
int
size
;
// The parts start with child elements:: {e0, e1, e2, ...}.
// Following that are optional filler elements, all null.
// Following that are attributes as key/value pairs.
// They are in reverse: {...key2, val2, key1, val1, key0, val0}.
// Child elements and attr keys and values are never null.
Object
[]
parts
;
// Build a partially-constructed node.
// Caller is responsible for initializing promised attributes.
Element
(
String
name
,
int
size
,
int
capacity
)
{
this
.
name
=
name
.
toString
();
this
.
size
=
size
;
assert
(
size
<=
capacity
);
this
.
parts
=
capacity
>
0
?
new
Object
[
capacity
]
:
noPartsFrozen
;
}
/** An anonymous, empty element.
* Optional elemCapacity argument is expected number of sub-elements.
*/
public
Element
()
{
this
(
ANON_NAME
,
0
,
NEED_SLOP
+
4
);
}
public
Element
(
int
extraCapacity
)
{
this
(
ANON_NAME
,
0
,
NEED_SLOP
+
Math
.
max
(
0
,
extraCapacity
));
}
/** An empty element with the given name.
* Optional extraCapacity argument is expected number of sub-elements.
*/
public
Element
(
String
name
)
{
this
(
name
,
0
,
NEED_SLOP
+
4
);
}
public
Element
(
String
name
,
int
extraCapacity
)
{
this
(
name
,
0
,
NEED_SLOP
+
Math
.
max
(
0
,
extraCapacity
));
}
/** An empty element with the given name and attributes.
* Optional extraCapacity argument is expected number of sub-elements.
*/
public
Element
(
String
name
,
String
...
attrs
)
{
this
(
name
,
attrs
,
(
Element
[])
null
,
0
);
}
public
Element
(
String
name
,
String
[]
attrs
,
int
extraCapacity
)
{
this
(
name
,
attrs
,
(
Element
[])
null
,
extraCapacity
);
}
/** An empty element with the given name and sub-elements.
* Optional extraCapacity argument is expected extra sub-elements.
*/
public
Element
(
String
name
,
Element
...
elems
)
{
this
(
name
,
(
String
[])
null
,
elems
,
0
);
}
public
Element
(
String
name
,
Element
[]
elems
,
int
extraCapacity
)
{
this
(
name
,
(
String
[])
null
,
elems
,
extraCapacity
);
}
/** An empty element with the given name, attributes, and sub-elements.
* Optional extraCapacity argument is expected extra sub-elements.
*/
public
Element
(
String
name
,
String
[]
attrs
,
Object
...
elems
)
{
this
(
name
,
attrs
,
elems
,
0
);
}
public
Element
(
String
name
,
String
[]
attrs
,
Object
[]
elems
,
int
extraCapacity
)
{
this
(
name
,
0
,
((
elems
==
null
)
?
0
:
elems
.
length
)
+
Math
.
max
(
0
,
extraCapacity
)
+
NEED_SLOP
+
((
attrs
==
null
)
?
0
:
attrs
.
length
));
int
ne
=
((
elems
==
null
)
?
0
:
elems
.
length
);
int
na
=
((
attrs
==
null
)
?
0
:
attrs
.
length
);
int
fillp
=
0
;
for
(
int
i
=
0
;
i
<
ne
;
i
++)
{
if
(
elems
[
i
]
!=
null
)
{
parts
[
fillp
++]
=
elems
[
i
];
}
}
size
=
fillp
;
for
(
int
i
=
0
;
i
<
na
;
i
+=
2
)
{
setAttr
(
attrs
[
i
+
0
],
attrs
[
i
+
1
]);
}
}
public
Element
(
Collection
c
)
{
this
(
c
.
size
());
addAll
(
c
);
}
public
Element
(
String
name
,
Collection
c
)
{
this
(
name
,
c
.
size
());
addAll
(
c
);
}
/** Shallow copy. Same as old.shallowCopy().
* Optional extraCapacity argument is expected extra sub-elements.
*/
public
Element
(
Element
old
)
{
this
(
old
,
0
);
}
public
Element
(
Element
old
,
int
extraCapacity
)
{
this
(
old
.
name
,
old
.
size
,
old
.
size
+
Math
.
max
(
0
,
extraCapacity
)
+
NEED_SLOP
+
old
.
attrLength
());
// copy sub-elements
System
.
arraycopy
(
old
.
parts
,
0
,
parts
,
0
,
size
);
int
alen
=
parts
.
length
-
(
size
+
Math
.
max
(
0
,
extraCapacity
)
+
NEED_SLOP
);
// copy attributes
System
.
arraycopy
(
old
.
parts
,
old
.
parts
.
length
-
alen
,
parts
,
parts
.
length
-
alen
,
alen
);
assert
(!
isFrozen
());
}
/** Shallow copy. Same as new Element(this). */
public
Element
shallowCopy
()
{
return
new
Element
(
this
);
}
static
public
final
Element
EMPTY
=
new
Element
(
ANON_NAME
,
0
,
0
);
Element
deepFreezeOrCopy
(
boolean
makeFrozen
)
{
if
(
makeFrozen
&&
isFrozen
())
{
return
this
;
// no need to copy it
}
int
alen
=
attrLength
();
int
plen
=
size
+
(
makeFrozen
?
0
:
NEED_SLOP
)
+
alen
;
Element
copy
=
new
Element
(
name
,
size
,
plen
);
// copy attributes
System
.
arraycopy
(
parts
,
parts
.
length
-
alen
,
copy
.
parts
,
plen
-
alen
,
alen
);
// copy sub-elements
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
Object
e
=
parts
[
i
];
String
str
;
if
(
e
instanceof
Element
)
{
// recursion is common case
e
=
((
Element
)
e
).
deepFreezeOrCopy
(
makeFrozen
);
}
else
if
(
makeFrozen
)
{
// Freeze StringBuffers, etc.
e
=
fixupString
(
e
);
}
copy
.
setRaw
(
i
,
e
);
}
return
copy
;
}
/** Returns new Element(this), and also recursively copies sub-elements. */
public
Element
deepCopy
()
{
return
deepFreezeOrCopy
(
false
);
}
/** Returns frozen version of deepCopy. */
public
Element
deepFreeze
()
{
return
deepFreezeOrCopy
(
true
);
}
/** Freeze this element.
* Throw an IllegalArgumentException if any sub-element is not already frozen.
* (Use deepFreeze() to make a frozen copy of an entire element tree.)
*/
public
void
shallowFreeze
()
{
if
(
isFrozen
())
{
return
;
}
int
alen
=
attrLength
();
Object
[]
nparts
=
new
Object
[
size
+
alen
];
// copy attributes
System
.
arraycopy
(
parts
,
parts
.
length
-
alen
,
nparts
,
size
,
alen
);
// copy sub-elements
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
Object
e
=
parts
[
i
];
String
str
;
if
(
e
instanceof
Element
)
{
// recursion is common case
if
(!((
Element
)
e
).
isFrozen
())
{
throw
new
IllegalArgumentException
(
"Sub-element must be frozen."
);
}
}
else
{
// Freeze StringBuffers, etc.
e
=
fixupString
(
e
);
}
nparts
[
i
]
=
e
;
}
parts
=
nparts
;
assert
(
isFrozen
());
}
/** Return the name of this element. */
public
String
getName
()
{
return
name
;
}
/** Change the name of this element. */
public
void
setName
(
String
name
)
{
checkNotFrozen
();
this
.
name
=
name
.
toString
();
}
/** Reports if the element's name is a particular string (spelled "*").
* Such elements are created by the nullary Element constructor,
* and by query functions which return multiple values,
* such as <tt>findAll</tt>.
*/
public
boolean
isAnonymous
()
{
return
name
==
ANON_NAME
;
}
/** Return number of elements. (Does not include attributes.) */
public
int
size
()
{
return
size
;
}
/** True if no elements. (Does not consider attributes.) */
public
boolean
isEmpty
()
{
return
size
==
0
;
}
/** True if this element does not allow modification. */
public
boolean
isFrozen
()
{
// It is frozen iff there is no slop space.
return
!
hasNulls
(
NEED_SLOP
);
}
void
checkNotFrozen
()
{
if
(
isFrozen
())
{
throw
new
UnsupportedOperationException
(
"cannot modify frozen element"
);
}
}
/** Remove specified elements. (Does not affect attributes.) */
public
void
clear
()
{
clear
(
0
,
size
);
}
public
void
clear
(
int
beg
)
{
clear
(
beg
,
size
);
}
public
void
clear
(
int
beg
,
int
end
)
{
if
(
end
>
size
)
{
badIndex
(
end
);
}
if
(
beg
<
0
||
beg
>
end
)
{
badIndex
(
beg
);
}
if
(
beg
==
end
)
{
return
;
}
checkNotFrozen
();
if
(
end
==
size
)
{
if
(
beg
==
0
&&
parts
.
length
>
0
&&
parts
[
parts
.
length
-
1
]
==
null
)
{
// If no attributes, free the parts array.
parts
=
noPartsNotFrozen
;
size
=
0
;
}
else
{
clearParts
(
beg
,
size
);
size
=
beg
;
}
}
else
{
close
(
beg
,
end
-
beg
);
}
}
void
clearParts
(
int
beg
,
int
end
)
{
for
(
int
i
=
beg
;
i
<
end
;
i
++)
{
parts
[
i
]
=
null
;
}
}
/** True if name, attributes, and elements are the same. */
public
boolean
equals
(
Element
that
)
{
if
(!
this
.
name
.
equals
(
that
.
name
))
{
return
false
;
}
if
(
this
.
size
!=
that
.
size
)
{
return
false
;
}
// elements must be equal and ordered
Object
[]
thisParts
=
this
.
parts
;
Object
[]
thatParts
=
that
.
parts
;
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
Object
thisPart
=
thisParts
[
i
];
Object
thatPart
=
thatParts
[
i
];
if
(
thisPart
instanceof
Element
)
{
// recursion is common case
if
(!
thisPart
.
equals
(
thatPart
))
{
return
false
;
}
}
else
{
// If either is a non-string char sequence, normalize it.
thisPart
=
fixupString
(
thisPart
);
thatPart
=
fixupString
(
thatPart
);
if
(!
thisPart
.
equals
(
thatPart
))
{
return
false
;
}
}
}
// finally, attributes must be equal (unordered)
return
this
.
equalAttrs
(
that
);
}
// bridge method
public
boolean
equals
(
Object
o
)
{
if
(!(
o
instanceof
Element
))
{
return
false
;
}
return
equals
((
Element
)
o
);
}
public
int
hashCode
()
{
int
hc
=
0
;
int
alen
=
attrLength
();
for
(
int
i
=
parts
.
length
-
alen
;
i
<
parts
.
length
;
i
+=
2
)
{
hc
+=
(
parts
[
i
+
0
].
hashCode
()
^
parts
[
i
+
1
].
hashCode
());
}
hc
^=
hc
<<
11
;
hc
+=
name
.
hashCode
();
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
hc
^=
hc
<<
7
;
Object
p
=
parts
[
i
];
if
(
p
instanceof
Element
)
{
hc
+=
p
.
hashCode
();
// recursion is common case
}
else
{
hc
+=
fixupString
(
p
).
hashCode
();
}
}
hc
^=
hc
>>>
19
;
return
hc
;
}
/** Compare lexicographically. Earlier-spelled attrs are more sigificant. */
public
int
compareTo
(
Element
that
)
{
int
r
;
// Primary key is element name.
r
=
this
.
name
.
compareTo
(
that
.
name
);
if
(
r
!=
0
)
{
return
r
;
}
// Secondary key is attributes, as if in normal key order.
// The key/value pairs are sorted as a token sequence.
int
thisAlen
=
this
.
attrLength
();
int
thatAlen
=
that
.
attrLength
();
if
(
thisAlen
!=
0
||
thatAlen
!=
0
)
{
r
=
compareAttrs
(
thisAlen
,
that
,
thatAlen
,
true
);
assert
(
assertAttrCompareOK
(
r
,
that
));
if
(
r
!=
0
)
{
return
r
;
}
}
// Finally, elements should be equal and ordered,
// and the first difference rules.
Object
[]
thisParts
=
this
.
parts
;
Object
[]
thatParts
=
that
.
parts
;
int
minSize
=
this
.
size
;
if
(
minSize
>
that
.
size
)
{
minSize
=
that
.
size
;
}
Comparator
<
Object
>
cc
=
contentOrder
();
for
(
int
i
=
0
;
i
<
minSize
;
i
++)
{
r
=
cc
.
compare
(
thisParts
[
i
],
thatParts
[
i
]);
if
(
r
!=
0
)
{
return
r
;
}
}
//if (this.size < that.size) return -1;
return
this
.
size
-
that
.
size
;
}
private
boolean
assertAttrCompareOK
(
int
r
,
Element
that
)
{
Element
e0
=
this
.
copyAttrsOnly
();
Element
e1
=
that
.
copyAttrsOnly
();
e0
.
sortAttrs
();
e1
.
sortAttrs
();
int
r2
;
for
(
int
k
=
0
;;
k
++)
{
boolean
con0
=
e0
.
containsAttr
(
k
);
boolean
con1
=
e1
.
containsAttr
(
k
);
if
(
con0
!=
con1
)
{
if
(!
con0
)
{
r2
=
0
-
1
;
break
;
}
if
(!
con1
)
{
r2
=
1
-
0
;
break
;
}
}
if
(!
con0
)
{
r2
=
0
;
break
;
}
String
k0
=
e0
.
getAttrName
(
k
);
String
k1
=
e1
.
getAttrName
(
k
);
r2
=
k0
.
compareTo
(
k1
);
if
(
r2
!=
0
)
{
break
;
}
String
v0
=
e0
.
getAttr
(
k
);
String
v1
=
e1
.
getAttr
(
k
);
r2
=
v0
.
compareTo
(
v1
);
if
(
r2
!=
0
)
{
break
;
}
}
if
(
r
!=
0
)
{
r
=
(
r
>
0
)
?
1
:
-
1
;
}
if
(
r2
!=
0
)
{
r2
=
(
r2
>
0
)
?
1
:
-
1
;
}
if
(
r
!=
r2
)
{
System
.
out
.
println
(
"*** wrong attr compare, "
+
r
+
" != "
+
r2
);
System
.
out
.
println
(
" this = "
+
this
);
System
.
out
.
println
(
" attr->"
+
e0
);
System
.
out
.
println
(
" that = "
+
that
);
System
.
out
.
println
(
" attr->"
+
e1
);
}
return
r
==
r2
;
}
private
void
badIndex
(
int
i
)
{
Object
badRef
=
(
new
Object
[
0
])[
i
];
}
public
Object
get
(
int
i
)
{
if
(
i
>=
size
)
{
badIndex
(
i
);
}
return
parts
[
i
];
}
public
Object
set
(
int
i
,
Object
e
)
{
if
(
i
>=
size
)
{
badIndex
(
i
);
}
e
.
getClass
();
// null check
checkNotFrozen
();
Object
old
=
parts
[
i
];
setRaw
(
i
,
e
);
return
old
;
}
void
setRaw
(
int
i
,
Object
e
)
{
parts
[
i
]
=
e
;
}
public
boolean
remove
(
Object
e
)
{
int
i
=
indexOf
(
e
);
if
(
i
<
0
)
{
return
false
;
}
close
(
i
,
1
);
return
true
;
}
public
Object
remove
(
int
i
)
{
if
(
i
>=
size
)
{
badIndex
(
i
);
}
Object
e
=
parts
[
i
];
close
(
i
,
1
);
return
e
;
}
public
Object
removeLast
()
{
if
(
size
==
0
)
{
return
null
;
}
return
remove
(
size
-
1
);
}
/** Remove the first element matching the given filter.
* Return the filtered value.
*/
public
Object
remove
(
Filter
f
)
{
return
findOrRemove
(
f
,
0
,
true
);
}
public
Object
remove
(
Filter
f
,
int
fromIndex
)
{
if
(
fromIndex
<
0
)
{
fromIndex
=
0
;
}
return
findOrRemove
(
f
,
fromIndex
,
true
);
}
/** Remove the last element matching the given filter.
* Return the filtered value.
*/
public
Object
removeLast
(
Filter
f
)
{
return
findOrRemoveLast
(
f
,
size
-
1
,
true
);
}
public
Object
removeLast
(
Filter
f
,
int
fromIndex
)
{
if
(
fromIndex
>=
size
)
{
fromIndex
=
size
-
1
;
}
return
findOrRemoveLast
(
f
,
fromIndex
,
true
);
}
/** Remove all elements matching the given filter.
* If there is a non-null collection given as a sink,
* transfer removed elements to the given collection.
* The int result is the number of removed elements.
* If there is a null sink given, the removed elements
* are discarded. If there is no sink given, the removed
* elements are returned in an anonymous container element.
*/
public
Element
removeAll
(
Filter
f
)
{
Element
result
=
new
Element
();
findOrRemoveAll
(
f
,
false
,
0
,
size
,
result
.
asList
(),
true
);
return
result
;
}
public
Element
removeAll
(
Filter
f
,
int
fromIndex
,
int
toIndex
)
{
Element
result
=
new
Element
();
findOrRemoveAll
(
f
,
true
,
fromIndex
,
toIndex
,
result
.
asList
(),
true
);
return
result
;
}
public
int
removeAll
(
Filter
f
,
Collection
<
Object
>
sink
)
{
return
findOrRemoveAll
(
f
,
false
,
0
,
size
,
sink
,
true
);
}
public
int
removeAll
(
Filter
f
,
int
fromIndex
,
int
toIndex
,
Collection
<
Object
>
sink
)
{
return
findOrRemoveAll
(
f
,
false
,
fromIndex
,
toIndex
,
sink
,
true
);
}
/** Remove all elements not matching the given filter.
* If there is a non-null collection given as a sink,
* transfer removed elements to the given collection.
* The int result is the number of removed elements.
* If there is a null sink given, the removed elements
* are discarded. If there is no sink given, the removed
* elements are returned in an anonymous container element.
*/
public
Element
retainAll
(
Filter
f
)
{
Element
result
=
new
Element
();
findOrRemoveAll
(
f
,
true
,
0
,
size
,
result
.
asList
(),
true
);
return
result
;
}
public
Element
retainAll
(
Filter
f
,
int
fromIndex
,
int
toIndex
)
{
Element
result
=
new
Element
();
findOrRemoveAll
(
f
,
true
,
fromIndex
,
toIndex
,
result
.
asList
(),
true
);
return
result
;
}
public
int
retainAll
(
Filter
f
,
Collection
<
Object
>
sink
)
{
return
findOrRemoveAll
(
f
,
true
,
0
,
size
,
sink
,
true
);
}
public
int
retainAll
(
Filter
f
,
int
fromIndex
,
int
toIndex
,
Collection
<
Object
>
sink
)
{
return
findOrRemoveAll
(
f
,
true
,
fromIndex
,
toIndex
,
sink
,
true
);
}
public
void
add
(
int
i
,
Object
e
)
{
// (The shape of this method is tweaked for common cases.)
e
.
getClass
();
// force a null check on e
if
(
hasNulls
(
1
+
NEED_SLOP
))
{
// Common case: Have some slop space.
if
(
i
==
size
)
{
// Most common case: Append.
setRaw
(
i
,
e
);
size
++;
return
;
}
if
(
i
>
size
)
{
badIndex
(
i
);
}
// Second most common case: Shift right by one.
open
(
i
,
1
);
setRaw
(
i
,
e
);
return
;
}
// Ran out of space. Do something complicated.
size
=
expand
(
i
,
1
);
setRaw
(
i
,
e
);
}
public
boolean
add
(
Object
e
)
{
add
(
size
,
e
);
return
true
;
}
public
Object
getLast
()
{
return
size
==
0
?
null
:
parts
[
size
-
1
];
}
/** Returns the text of this Element.
* All sub-elements of this Element must be of type CharSequence.
* A ClassCastException is raised if there are non-character sub-elements.
* If there is one sub-element, return it.
* Otherwise, returns a TokenList of all sub-elements.
* This results in a space being placed between each adjacent pair of sub-elements.
*/
public
CharSequence
getText
()
{
checkTextOnly
();
if
(
size
==
1
)
{
return
parts
[
0
].
toString
();
}
else
{
return
new
TokenList
(
parts
,
0
,
size
);
}
}
/** Provides an iterable view of this object as a series of texts.
* All sub-elements of this Element must be of type CharSequence.
* A ClassCastException is raised if there are non-character sub-elements.
*/
public
Iterable
<
CharSequence
>
texts
()
{
checkTextOnly
();
return
(
Iterable
<
CharSequence
>)
(
Iterable
)
this
;
}
/** Returns an array of strings derived from the sub-elements of this object.
* All sub-elements of this Element must be of type CharSequence.
* A ClassCastException is raised if there are non-character sub-elements.
*/
public
String
[]
toStrings
()
{
//checkTextOnly();
String
[]
result
=
new
String
[
size
];
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
result
[
i
]
=
((
CharSequence
)
parts
[
i
]).
toString
();
}
return
result
;
}
/** Like getText, except that it disregards non-text elements.
* Non-text elements are replaced by their textual contents, if any.
* Text elements which were separated only by non-text element
* boundaries are merged into single tokens.
* <p>
* There is no corresponding setter, since this accessor does
* not report the full state of the element.
*/
public
CharSequence
getFlatText
()
{
if
(
size
==
1
)
{
// Simple cases.
if
(
parts
[
0
]
instanceof
CharSequence
)
{
return
parts
[
0
].
toString
();
}
else
{
return
new
TokenList
();
}
}
if
(
isText
())
{
return
getText
();
}
// Filter and merge.
Element
result
=
new
Element
(
size
);
boolean
merge
=
false
;
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
Object
text
=
parts
[
i
];
if
(!(
text
instanceof
CharSequence
))
{
// Skip, but erase this boundary.
if
(
text
instanceof
Element
)
{
Element
te
=
(
Element
)
text
;
if
(!
te
.
isEmpty
())
{
result
.
addText
(
te
.
getFlatText
());
}
}
merge
=
true
;
continue
;
}
if
(
merge
)
{
// Merge w/ previous token.
result
.
addText
((
CharSequence
)
text
);
merge
=
false
;
}
else
{
result
.
add
(
text
);
}
}
if
(
result
.
size
()
==
1
)
{
return
(
CharSequence
)
result
.
parts
[
0
];
}
else
{
return
result
.
getText
();
}
}
/** Return true if all sub-elements are of type CharSequence. */
public
boolean
isText
()
{
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
if
(!(
parts
[
i
]
instanceof
CharSequence
))
{
return
false
;
}
}
return
true
;
}
/** Return true if at least one sub-element is of type CharSequence. */
public
boolean
hasText
()
{
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
if
(
parts
[
i
]
instanceof
CharSequence
)
{
return
true
;
}
}
return
false
;
}
/** Raise a ClassCastException if !isText. */
public
void
checkTextOnly
()
{
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
((
CharSequence
)
parts
[
i
]).
getClass
();
}
}
/** Clears out all sub-elements, and replaces them by the given text.
* A ClassCastException is raised if there are non-character sub-elements,
* either before or after the change.
*/
public
void
setText
(
CharSequence
text
)
{
checkTextOnly
();
clear
();
if
(
text
instanceof
TokenList
)
{
// TL's contain only strings
addAll
(
0
,
(
TokenList
)
text
);
}
else
{
add
(
text
);
}
}
/** Add text at the given position, merging with any previous
* text element, but preserving token boundaries where possible.
* <p>
* In all cases, the new value of getText() is the string
* concatenation of the old value of getText() plus the new text.
* <p>
* The total effect is to concatenate the given text to any
* pre-existing text, and to do so efficiently even if there
* are many such concatenations. Also, getText calls which
* return multiple tokens (in a TokenList) are respected.
* For example, if x is empty, x.addText(y.getText()) puts
* an exact structural copy of y's text into x.
* <p>
* Internal token boundaries in the original text, and in the new
* text (i.e., if it is a TokenList), are preserved. However,
* at the point where new text joins old text, a StringBuffer
* or new String may be created to join the last old and first
* new token.
* <p>
* If the given text is a TokenList, add the tokens as
* separate sub-elements, possibly merging the first token to
* a previous text item (to avoid making a new token boundary).
* <p>
* If the element preceding position i is a StringBuffer,
* append the first new token to it.
* <p>
* If the preceding element is a CharSequence, replace it by a
* StringBuffer containing both its and the first new token.
* <p>
* If tokens are added after a StringBuffer, freeze it into a String.
* <p>
* Every token not merged into a previous CharSequence is added
* as a new sub-element, starting at position i.
* <p>
* Returns the number of elements added, which is useful
* for further calls to addText. This number is zero
* if the input string was null, or was successfully
* merged into a StringBuffer at position i-1.
* <p>
* By contrast, calling add(text) always adds a new sub-element.
* In that case, if there is a previous string, a separating
* space is virtually present also, and will be observed if
* getText() is used to return all the text together.
*/
public
int
addText
(
int
i
,
CharSequence
text
)
{
if
(
text
instanceof
String
)
{
return
addText
(
i
,
(
String
)
text
);
}
else
if
(
text
instanceof
TokenList
)
{
// Text is a list of tokens.
TokenList
tl
=
(
TokenList
)
text
;
int
tlsize
=
tl
.
size
();
if
(
tlsize
==
0
)
{
return
0
;
}
String
token0
=
tl
.
get
(
0
).
toString
();
if
(
tlsize
==
1
)
{
return
addText
(
i
,
token0
);
}
if
(
mergeWithPrev
(
i
,
token0
,
false
))
{
// Add the n-1 remaining tokens.
addAll
(
i
,
tl
.
subList
(
1
,
tlsize
));
return
tlsize
-
1
;
}
else
{
addAll
(
i
,
(
Collection
)
tl
);
return
tlsize
;
}
}
else
{
return
addText
(
i
,
text
.
toString
());
}
}
public
int
addText
(
CharSequence
text
)
{
return
addText
(
size
,
text
);
}
private
// no reason to make this helper public
int
addText
(
int
i
,
String
text
)
{
if
(
text
.
length
()
==
0
)
{
return
0
;
// Trivial success.
}
if
(
mergeWithPrev
(
i
,
text
,
true
))
{
return
0
;
// Merged with previous token.
}
// No previous token.
add
(
i
,
text
);
return
1
;
}
// Tries to merge token with previous contents.
// Returns true if token is successfully disposed of.
// If keepSB is false, any previous StringBuffer is frozen.
// If keepSB is true, a StringBuffer may be created to hold
// the merged token.
private
boolean
mergeWithPrev
(
int
i
,
String
token
,
boolean
keepSB
)
{
if
(
i
==
0
)
// Trivial success if the token is length zero.
{
return
(
token
.
length
()
==
0
);
}
Object
prev
=
parts
[
i
-
1
];
if
(
prev
instanceof
StringBuffer
)
{
StringBuffer
psb
=
(
StringBuffer
)
prev
;
psb
.
append
(
token
);
if
(!
keepSB
)
{
parts
[
i
-
1
]
=
psb
.
toString
();
}
return
true
;
}
if
(
token
.
length
()
==
0
)
{
return
true
;
// Trivial success.
}
if
(
prev
instanceof
CharSequence
)
{
// Must concatenate.
StringBuffer
psb
=
new
StringBuffer
(
prev
.
toString
());
psb
.
append
(
token
);
if
(
keepSB
)
{
parts
[
i
-
1
]
=
psb
;
}
else
{
parts
[
i
-
1
]
=
psb
.
toString
();
}
return
true
;
}
return
false
;
}
/** Trim all strings, using String.trim().
* Remove empty strings.
* Normalize CharSequences to Strings.
*/
public
void
trimText
()
{
checkNotFrozen
();
int
fillp
=
0
;
int
size
=
this
.
size
;
Object
[]
parts
=
this
.
parts
;
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
Object
e
=
parts
[
i
];
if
(
e
instanceof
CharSequence
)
{
String
tt
=
e
.
toString
().
trim
();
if
(
tt
.
length
()
==
0
)
{
continue
;
}
e
=
tt
;
}
parts
[
fillp
++]
=
e
;
}
while
(
size
>
fillp
)
{
parts
[--
size
]
=
null
;
}
this
.
size
=
fillp
;
}
/** Add one or more subelements at the given position.
* If the object reference is null, nothing happens.
* If the object is an anonymous Element, addAll is called.
* If the object is a TokenList, addAll is called (to add the tokens).
* Otherwise, add is called, adding a single subelement or string.
* The net effect is to add zero or more tokens.
* The returned value is the number of added elements.
* <p>
* Note that getText() can return a TokenList which preserves
* token boundaries in the text source. Such a text will be
* added as multiple text sub-elements.
* <p>
* If a text string is added adjacent to an immediately
* preceding string, there will be a token boundary between
* the strings, which will print as an extra space.
*/
public
int
addContent
(
int
i
,
Object
e
)
{
if
(
e
==
null
)
{
return
0
;
}
else
if
(
e
instanceof
TokenList
)
{
return
addAll
(
i
,
(
Collection
)
e
);
}
else
if
(
e
instanceof
Element
&&
((
Element
)
e
).
isAnonymous
())
{
return
addAll
(
i
,
(
Element
)
e
);
}
else
{
add
(
i
,
e
);
return
1
;
}
}
public
int
addContent
(
Object
e
)
{
return
addContent
(
size
,
e
);
}
public
Object
[]
toArray
()
{
Object
[]
result
=
new
Object
[
size
];
System
.
arraycopy
(
parts
,
0
,
result
,
0
,
size
);
return
result
;
}
public
Element
copyContentOnly
()
{
Element
content
=
new
Element
(
size
);
System
.
arraycopy
(
parts
,
0
,
content
.
parts
,
0
,
size
);
content
.
size
=
size
;
return
content
;
}
public
void
sort
(
Comparator
<
Object
>
c
)
{
Arrays
.
sort
(
parts
,
0
,
size
,
c
);
}
public
void
sort
()
{
sort
(
CONTENT_ORDER
);
}
/** Equivalent to Collections.reverse(this.asList()). */
public
void
reverse
()
{
for
(
int
i
=
0
,
mid
=
size
>>
1
,
j
=
size
-
1
;
i
<
mid
;
i
++,
j
--)
{
Object
p
=
parts
[
i
];
parts
[
i
]
=
parts
[
j
];
parts
[
j
]
=
p
;
}
}
/** Equivalent to Collections.shuffle(this.asList() [, rnd]). */
public
void
shuffle
()
{
Collections
.
shuffle
(
this
.
asList
());
}
public
void
shuffle
(
Random
rnd
)
{
Collections
.
shuffle
(
this
.
asList
(),
rnd
);
}
/** Equivalent to Collections.rotate(this.asList(), dist). */
public
void
rotate
(
int
dist
)
{
Collections
.
rotate
(
this
.
asList
(),
dist
);
}
/** Equivalent to Collections.min(this.asList(), c). */
public
Object
min
(
Comparator
<
Object
>
c
)
{
return
Collections
.
min
(
this
.
asList
(),
c
);
}
public
Object
min
()
{
return
min
(
CONTENT_ORDER
);
}
/** Equivalent to Collections.max(this.asList(), c). */
public
Object
max
(
Comparator
<
Object
>
c
)
{
return
Collections
.
max
(
this
.
asList
(),
c
);
}
public
Object
max
()
{
return
max
(
CONTENT_ORDER
);
}
public
int
addAll
(
int
i
,
Collection
c
)
{
if
(
c
instanceof
LView
)
{
return
addAll
(
i
,
((
LView
)
c
).
asElement
());
}
else
{
int
csize
=
c
.
size
();
if
(
csize
==
0
)
{
return
0
;
}
openOrExpand
(
i
,
csize
);
int
fill
=
i
;
for
(
Object
part
:
c
)
{
parts
[
fill
++]
=
part
;
}
return
csize
;
}
}
public
int
addAll
(
int
i
,
Element
e
)
{
int
esize
=
e
.
size
;
if
(
esize
==
0
)
{
return
0
;
}
openOrExpand
(
i
,
esize
);
System
.
arraycopy
(
e
.
parts
,
0
,
parts
,
i
,
esize
);
return
esize
;
}
public
int
addAll
(
Collection
c
)
{
return
addAll
(
size
,
c
);
}
public
int
addAll
(
Element
e
)
{
return
addAll
(
size
,
e
);
}
public
int
addAllAttrsFrom
(
Element
e
)
{
int
added
=
0
;
for
(
int
k
=
0
;
e
.
containsAttr
(
k
);
k
++)
{
String
old
=
setAttr
(
e
.
getAttrName
(
k
),
e
.
getAttr
(
k
));
if
(
old
==
null
)
{
added
+=
1
;
}
}
// Return number of added (not merely changed) attrs.
return
added
;
}
// Search.
public
boolean
matches
(
Filter
f
)
{
return
f
.
filter
(
this
)
!=
null
;
}
public
Object
find
(
Filter
f
)
{
return
findOrRemove
(
f
,
0
,
false
);
}
public
Object
find
(
Filter
f
,
int
fromIndex
)
{
if
(
fromIndex
<
0
)
{
fromIndex
=
0
;
}
return
findOrRemove
(
f
,
fromIndex
,
false
);
}
/** Find the last element matching the given filter.
* Return the filtered value.
*/
public
Object
findLast
(
Filter
f
)
{
return
findOrRemoveLast
(
f
,
size
-
1
,
false
);
}
public
Object
findLast
(
Filter
f
,
int
fromIndex
)
{
if
(
fromIndex
>=
size
)
{
fromIndex
=
size
-
1
;
}
return
findOrRemoveLast
(
f
,
fromIndex
,
false
);
}
/** Find all elements matching the given filter.
* If there is a non-null collection given as a sink,
* transfer matching elements to the given collection.
* The int result is the number of matching elements.
* If there is a null sink given, the matching elements are
* not collected. If there is no sink given, the matching
* elements are returned in an anonymous container element.
* In no case is the receiver element changed.
* <p>
* Note that a simple count of matching elements can be
* obtained by passing a null collection argument.
*/
public
Element
findAll
(
Filter
f
)
{
Element
result
=
new
Element
();
findOrRemoveAll
(
f
,
false
,
0
,
size
,
result
.
asList
(),
false
);
return
result
;
}
public
Element
findAll
(
Filter
f
,
int
fromIndex
,
int
toIndex
)
{
Element
result
=
new
Element
(
name
);
findOrRemoveAll
(
f
,
false
,
fromIndex
,
toIndex
,
result
.
asList
(),
false
);
return
result
;
}
public
int
findAll
(
Filter
f
,
Collection
<
Object
>
sink
)
{
return
findOrRemoveAll
(
f
,
false
,
0
,
size
,
sink
,
false
);
}
public
int
findAll
(
Filter
f
,
int
fromIndex
,
int
toIndex
,
Collection
<
Object
>
sink
)
{
return
findOrRemoveAll
(
f
,
false
,
fromIndex
,
toIndex
,
sink
,
false
);
}
/// Driver routines.
private
Object
findOrRemove
(
Filter
f
,
int
fromIndex
,
boolean
remove
)
{
for
(
int
i
=
fromIndex
;
i
<
size
;
i
++)
{
Object
x
=
f
.
filter
(
parts
[
i
]);
if
(
x
!=
null
)
{
if
(
remove
)
{
close
(
i
,
1
);
}
return
x
;
}
}
return
null
;
}
private
Object
findOrRemoveLast
(
Filter
f
,
int
fromIndex
,
boolean
remove
)
{
for
(
int
i
=
fromIndex
;
i
>=
0
;
i
--)
{
Object
x
=
f
.
filter
(
parts
[
i
]);
if
(
x
!=
null
)
{
if
(
remove
)
{
close
(
i
,
1
);
}
return
x
;
}
}
return
null
;
}
private
int
findOrRemoveAll
(
Filter
f
,
boolean
fInvert
,
int
fromIndex
,
int
toIndex
,
Collection
<
Object
>
sink
,
boolean
remove
)
{
if
(
fromIndex
<
0
)
{
badIndex
(
fromIndex
);
}
if
(
toIndex
>
size
)
{
badIndex
(
toIndex
);
}
int
found
=
0
;
for
(
int
i
=
fromIndex
;
i
<
toIndex
;
i
++)
{
Object
p
=
parts
[
i
];
Object
x
=
f
.
filter
(
p
);
if
(
fInvert
?
(
x
==
null
)
:
(
x
!=
null
))
{
if
(
remove
)
{
close
(
i
--,
1
);
toIndex
--;
}
found
+=
XMLKit
.
addContent
(
fInvert
?
p
:
x
,
sink
);
}
}
return
found
;
}
public
void
replaceAll
(
Filter
f
)
{
XMLKit
.
replaceAll
(
f
,
this
.
asList
());
}
public
void
replaceAll
(
Filter
f
,
int
fromIndex
,
int
toIndex
)
{
XMLKit
.
replaceAll
(
f
,
this
.
asList
().
subList
(
fromIndex
,
toIndex
));
}
/// Recursive walks.
// findAllInTree(f) == findAll(findInTree(f,S)), S.toElement
// findAllInTree(f,S) == findAll(findInTree(content(f,S)))
// removeAllInTree(f) == replaceAll(replaceInTree(and(f,emptyF)))
// removeAllInTree(f,S) == replaceAll(replaceInTree(and(content(f,S),emptyF)))
// retainAllInTree(f) == removeAllInTree(not(f))
// replaceAllInTree(f) == replaceAll(replaceInTree(f))
public
Element
findAllInTree
(
Filter
f
)
{
Element
result
=
new
Element
();
findAllInTree
(
f
,
result
.
asList
());
return
result
;
}
public
int
findAllInTree
(
Filter
f
,
Collection
<
Object
>
sink
)
{
int
found
=
0
;
int
size
=
this
.
size
;
// cache copy
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
Object
p
=
parts
[
i
];
Object
x
=
f
.
filter
(
p
);
if
(
x
!=
null
)
{
found
+=
XMLKit
.
addContent
(
x
,
sink
);
}
else
if
(
p
instanceof
Element
)
{
found
+=
((
Element
)
p
).
findAllInTree
(
f
,
sink
);
}
}
return
found
;
}
public
int
countAllInTree
(
Filter
f
)
{
return
findAllInTree
(
f
,
null
);
}
public
int
removeAllInTree
(
Filter
f
,
Collection
<
Object
>
sink
)
{
if
(
sink
==
null
)
{
sink
=
newCounterColl
();
}
replaceAll
(
replaceInTree
(
and
(
content
(
f
,
sink
),
emptyFilter
())));
return
sink
.
size
();
}
public
Element
removeAllInTree
(
Filter
f
)
{
Element
result
=
new
Element
();
removeAllInTree
(
f
,
result
.
asList
());
return
result
;
}
public
int
retainAllInTree
(
Filter
f
,
Collection
<
Object
>
sink
)
{
return
removeAllInTree
(
not
(
f
),
sink
);
}
public
Element
retainAllInTree
(
Filter
f
)
{
Element
result
=
new
Element
();
retainAllInTree
(
f
,
result
.
asList
());
return
result
;
}
public
void
replaceAllInTree
(
Filter
f
)
{
replaceAll
(
replaceInTree
(
f
));
}
/** Raise a ClassCastException if any subelements are the wrong type. */
public
void
checkPartsOnly
(
Class
<?>
elementClass
)
{
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
elementClass
.
cast
(
parts
[
i
]).
getClass
();
}
}
/** Return true if all sub-elements are of the given type. */
public
boolean
isPartsOnly
(
Class
<?>
elementClass
)
{
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
if
(!
elementClass
.
isInstance
(
parts
[
i
]))
{
return
false
;
}
}
return
true
;
}
/** Provides an iterable view of this object as a series of elements.
* All sub-elements of this Element must be of type Element.
* A ClassCastException is raised if there are non-Element sub-elements.
*/
public
<
T
>
Iterable
<
T
>
partsOnly
(
Class
<
T
>
elementClass
)
{
checkPartsOnly
(
elementClass
);
return
(
Iterable
<
T
>)
(
Iterable
)
this
;
}
public
Iterable
<
Element
>
elements
()
{
return
partsOnly
(
Element
.
class
);
}
/// Useful shorthands.
// Finding or removing elements w/o regard to their type or content.
public
Element
findElement
()
{
return
(
Element
)
find
(
elementFilter
());
}
public
Element
findAllElements
()
{
return
findAll
(
elementFilter
());
}
public
Element
removeElement
()
{
return
(
Element
)
remove
(
elementFilter
());
}
public
Element
removeAllElements
()
{
return
(
Element
)
removeAll
(
elementFilter
());
}
// Finding or removing by element tag or selected attribute,
// as if by elementFilter(name) or attrFilter(name, value).
// Roughly akin to Common Lisp ASSOC.
public
Element
findElement
(
String
name
)
{
return
(
Element
)
find
(
elementFilter
(
name
));
}
public
Element
removeElement
(
String
name
)
{
return
(
Element
)
remove
(
elementFilter
(
name
));
}
public
Element
findWithAttr
(
String
key
)
{
return
(
Element
)
find
(
attrFilter
(
name
));
}
public
Element
findWithAttr
(
String
key
,
String
value
)
{
return
(
Element
)
find
(
attrFilter
(
name
,
value
));
}
public
Element
removeWithAttr
(
String
key
)
{
return
(
Element
)
remove
(
attrFilter
(
name
));
}
public
Element
removeWithAttr
(
String
key
,
String
value
)
{
return
(
Element
)
remove
(
attrFilter
(
name
,
value
));
}
public
Element
findAllElements
(
String
name
)
{
return
findAll
(
elementFilter
(
name
));
}
public
Element
removeAllElements
(
String
name
)
{
return
removeAll
(
elementFilter
(
name
));
}
public
Element
retainAllElements
(
String
name
)
{
return
retainAll
(
elementFilter
(
name
));
}
public
Element
findAllWithAttr
(
String
key
)
{
return
findAll
(
attrFilter
(
key
));
}
public
Element
removeAllWithAttr
(
String
key
)
{
return
removeAll
(
attrFilter
(
key
));
}
public
Element
retainAllWithAttr
(
String
key
)
{
return
retainAll
(
attrFilter
(
key
));
}
public
Element
findAllWithAttr
(
String
key
,
String
value
)
{
return
findAll
(
attrFilter
(
key
,
value
));
}
public
Element
removeAllWithAttr
(
String
key
,
String
value
)
{
return
removeAll
(
attrFilter
(
key
,
value
));
}
public
Element
retainAllWithAttr
(
String
key
,
String
value
)
{
return
retainAll
(
attrFilter
(
key
,
value
));
}
public
int
countAll
(
Filter
f
)
{
return
findAll
(
f
,
null
);
}
public
int
countAllElements
()
{
return
countAll
(
elementFilter
());
}
public
int
countAllElements
(
String
name
)
{
return
countAll
(
elementFilter
(
name
));
}
public
int
countAllWithAttr
(
String
key
)
{
return
countAll
(
attrFilter
(
name
));
}
public
int
countAllWithAttr
(
String
key
,
String
value
)
{
return
countAll
(
attrFilter
(
key
,
value
));
}
public
int
indexOf
(
Object
e
)
{
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
if
(
e
.
equals
(
parts
[
i
]))
{
return
i
;
}
}
return
-
1
;
}
public
int
lastIndexOf
(
Object
e
)
{
for
(
int
i
=
size
-
1
;
i
>=
0
;
i
--)
{
if
(
e
.
equals
(
parts
[
i
]))
{
return
i
;
}
}
return
-
1
;
}
/** Remove the first element matching the given filter.
* Return the filtered value.
*/
public
int
indexOf
(
Filter
f
)
{
return
indexOf
(
f
,
0
);
}
public
int
indexOf
(
Filter
f
,
int
fromIndex
)
{
if
(
fromIndex
<
0
)
{
fromIndex
=
0
;
}
for
(
int
i
=
fromIndex
;
i
<
size
;
i
++)
{
Object
x
=
f
.
filter
(
parts
[
i
]);
if
(
x
!=
null
)
{
return
i
;
}
}
return
-
1
;
}
/** Remove the last element matching the given filter.
* Return the filtered value.
*/
public
int
lastIndexOf
(
Filter
f
)
{
return
lastIndexOf
(
f
,
size
-
1
);
}
public
int
lastIndexOf
(
Filter
f
,
int
fromIndex
)
{
if
(
fromIndex
>=
size
)
{
fromIndex
=
size
-
1
;
}
for
(
int
i
=
fromIndex
;
i
>=
0
;
i
--)
{
Object
x
=
f
.
filter
(
parts
[
i
]);
if
(
x
!=
null
)
{
return
i
;
}
}
return
-
1
;
}
public
boolean
contains
(
Object
e
)
{
return
indexOf
(
e
)
>=
0
;
}
// attributes
private
int
findOrCreateAttr
(
String
key
,
boolean
create
)
{
key
.
toString
();
// null check
int
attrBase
=
parts
.
length
;
for
(
int
i
=
parts
.
length
-
2
;
i
>=
size
;
i
-=
2
)
{
String
akey
=
(
String
)
parts
[
i
+
0
];
if
(
akey
==
null
)
{
if
(!
create
)
{
return
-
1
;
}
if
(
i
==
size
)
{
break
;
// NEED_SLOP
}
parts
[
i
+
0
]
=
key
;
//parts[i+1] = ""; //caller responsibility
return
i
;
}
attrBase
=
i
;
if
(
akey
.
equals
(
key
))
{
return
i
;
}
}
// If we fell through, we ran into an element part.
// Therefore we have run out of empty slots.
if
(!
create
)
{
return
-
1
;
}
assert
(!
isFrozen
());
int
alen
=
parts
.
length
-
attrBase
;
expand
(
size
,
2
);
// generally expands by more than 2
// since there was a reallocation, the garbage slots are really null
assert
(
parts
[
size
+
0
]
==
null
&&
parts
[
size
+
1
]
==
null
);
alen
+=
2
;
int
i
=
parts
.
length
-
alen
;
parts
[
i
+
0
]
=
key
;
//parts[i+1] = ""; //caller responsibility
return
i
;
}
public
int
attrSize
()
{
return
attrLength
()
>>>
1
;
}
public
int
indexOfAttr
(
String
key
)
{
return
findOrCreateAttr
(
key
,
false
);
}
public
boolean
containsAttr
(
String
key
)
{
return
indexOfAttr
(
key
)
>=
0
;
}
public
String
getAttr
(
String
key
)
{
return
getAttr
(
key
,
null
);
}
public
String
getAttr
(
String
key
,
String
dflt
)
{
int
i
=
findOrCreateAttr
(
key
,
false
);
return
(
i
<
0
)
?
dflt
:
(
String
)
parts
[
i
+
1
];
}
public
TokenList
getAttrList
(
String
key
)
{
return
convertToList
(
getAttr
(
key
));
}
public
Number
getAttrNumber
(
String
key
)
{
return
convertToNumber
(
getAttr
(
key
));
}
public
long
getAttrLong
(
String
key
)
{
return
getAttrLong
(
key
,
0
);
}
public
double
getAttrDouble
(
String
key
)
{
return
getAttrDouble
(
key
,
0.0
);
}
public
long
getAttrLong
(
String
key
,
long
dflt
)
{
return
convertToLong
(
getAttr
(
key
),
dflt
);
}
public
double
getAttrDouble
(
String
key
,
double
dflt
)
{
return
convertToDouble
(
getAttr
(
key
),
dflt
);
}
int
indexAttr
(
int
k
)
{
int
i
=
parts
.
length
-
(
k
*
2
)
-
2
;
if
(
i
<
size
||
parts
[
i
]
==
null
)
{
return
-
2
;
// always oob
}
return
i
;
}
public
boolean
containsAttr
(
int
k
)
{
return
indexAttr
(
k
)
>=
0
;
}
public
String
getAttr
(
int
k
)
{
return
(
String
)
parts
[
indexAttr
(
k
)
+
1
];
}
public
String
getAttrName
(
int
k
)
{
return
(
String
)
parts
[
indexAttr
(
k
)
+
0
];
}
public
Iterable
<
String
>
attrNames
()
{
//return asAttrMap().keySet();
return
new
Iterable
<
String
>()
{
public
Iterator
<
String
>
iterator
()
{
return
new
ANItr
();
}
};
}
// Hand-inlined replacement for asAttrMap().keySet().iterator():
class
ANItr
implements
Iterator
<
String
>
{
boolean
lastRet
;
int
cursor
=
-
2
;
// pointer from end of parts
public
boolean
hasNext
()
{
int
i
=
cursor
+
parts
.
length
;
return
i
>=
size
&&
parts
[
i
]
==
null
;
}
public
String
next
()
{
int
i
=
cursor
+
parts
.
length
;
Object
x
;
if
(
i
<
size
||
(
x
=
parts
[
i
])
==
null
)
{
nsee
();
return
null
;
}
cursor
-=
2
;
lastRet
=
true
;
return
(
String
)
x
;
}
public
void
remove
()
{
if
(!
lastRet
)
{
throw
new
IllegalStateException
();
}
Element
.
this
.
removeAttr
((-
4
-
cursor
)
/
2
);
cursor
+=
2
;
lastRet
=
false
;
}
Exception
nsee
()
{
throw
new
NoSuchElementException
(
"attribute "
+
(-
2
-
cursor
)
/
2
);
}
}
/** Return an anonymous copy of self, but only with attributes.
*/
public
Element
copyAttrsOnly
()
{
int
alen
=
attrLength
();
Element
attrs
=
new
Element
(
alen
);
Object
[]
attrParts
=
attrs
.
parts
;
assert
(
attrParts
.
length
==
NEED_SLOP
+
alen
);
System
.
arraycopy
(
parts
,
parts
.
length
-
alen
,
attrParts
,
NEED_SLOP
,
alen
);
return
attrs
;
}
/** Get all attributes, represented as an element with sub-elements.
* The name of each sub-element is the attribute key, and the text
* This is a fresh copy, and can be updated with affecting the original.
* of each sub-element is the corresponding attribute value.
* See also asAttrMap() for a "live" view of all the attributes as a Map.
*/
public
Element
getAttrs
()
{
int
asize
=
attrSize
();
Element
attrs
=
new
Element
(
ANON_NAME
,
asize
,
NEED_SLOP
+
asize
);
for
(
int
i
=
0
;
i
<
asize
;
i
++)
{
Element
attr
=
new
Element
(
getAttrName
(
i
),
1
,
NEED_SLOP
+
1
);
// %%% normalize attrs to token lists?
attr
.
setRaw
(
0
,
getAttr
(
i
));
attrs
.
setRaw
(
i
,
attr
);
}
return
attrs
;
}
public
void
setAttrs
(
Element
attrs
)
{
int
alen
=
attrLength
();
clearParts
(
parts
.
length
-
alen
,
alen
);
if
(!
hasNulls
(
NEED_SLOP
+
attrs
.
size
*
2
))
{
expand
(
size
,
attrs
.
size
*
2
);
}
addAttrs
(
attrs
);
}
public
void
addAttrs
(
Element
attrs
)
{
for
(
int
i
=
0
;
i
<
attrs
.
size
;
i
++)
{
Element
attr
=
(
Element
)
attrs
.
get
(
i
);
setAttr
(
attr
.
name
,
attr
.
getText
().
toString
());
}
}
public
void
removeAttr
(
int
i
)
{
checkNotFrozen
();
while
((
i
-=
2
)
>=
size
)
{
Object
k
=
parts
[
i
+
0
];
Object
v
=
parts
[
i
+
1
];
if
(
k
==
null
)
{
break
;
}
parts
[
i
+
2
]
=
k
;
parts
[
i
+
3
]
=
v
;
}
parts
[
i
+
2
]
=
null
;
parts
[
i
+
3
]
=
null
;
}
public
void
clearAttrs
()
{
if
(
parts
.
length
==
0
||
parts
[
parts
.
length
-
1
]
==
null
)
{
return
;
// no attrs to clear
}
checkNotFrozen
();
if
(
size
==
0
)
{
// If no elements, free the parts array.
parts
=
noPartsNotFrozen
;
return
;
}
for
(
int
i
=
parts
.
length
-
1
;
parts
[
i
]
!=
null
;
i
--)
{
assert
(
i
>=
size
);
parts
[
i
]
=
null
;
}
}
public
String
setAttr
(
String
key
,
String
value
)
{
String
old
;
if
(
value
==
null
)
{
int
i
=
findOrCreateAttr
(
key
,
false
);
if
(
i
>=
0
)
{
old
=
(
String
)
parts
[
i
+
1
];
removeAttr
(
i
);
}
else
{
old
=
null
;
}
}
else
{
checkNotFrozen
();
int
i
=
findOrCreateAttr
(
key
,
true
);
old
=
(
String
)
parts
[
i
+
1
];
parts
[
i
+
1
]
=
value
;
}
return
old
;
}
public
String
setAttrList
(
String
key
,
List
<
String
>
l
)
{
if
(
l
==
null
)
{
return
setAttr
(
key
,
null
);
}
if
(!(
l
instanceof
TokenList
))
{
l
=
new
TokenList
(
l
);
}
return
setAttr
(
key
,
l
.
toString
());
}
public
String
setAttrNumber
(
String
key
,
Number
n
)
{
return
setAttr
(
key
,
(
n
==
null
)
?
null
:
n
.
toString
());
}
public
String
setAttrLong
(
String
key
,
long
n
)
{
return
setAttr
(
key
,
(
n
==
0
)
?
null
:
String
.
valueOf
(
n
));
}
public
String
setAttrDouble
(
String
key
,
double
n
)
{
return
setAttr
(
key
,
(
n
==
0
)
?
null
:
String
.
valueOf
(
n
));
}
public
String
setAttr
(
int
k
,
String
value
)
{
int
i
=
indexAttr
(
k
);
String
old
=
(
String
)
parts
[
i
+
1
];
if
(
value
==
null
)
{
removeAttr
(
i
);
}
else
{
checkNotFrozen
();
parts
[
i
+
1
]
=
value
;
}
return
old
;
}
int
attrLength
()
{
return
parts
.
length
-
attrBase
();
}
/** Are the attributes of the two two elements equal?
* Disregards name, sub-elements, and ordering of attributes.
*/
public
boolean
equalAttrs
(
Element
that
)
{
int
alen
=
this
.
attrLength
();
if
(
alen
!=
that
.
attrLength
())
{
return
false
;
}
if
(
alen
==
0
)
{
return
true
;
}
return
compareAttrs
(
alen
,
that
,
alen
,
false
)
==
0
;
}
private
int
compareAttrs
(
int
thisAlen
,
Element
that
,
int
thatAlen
,
boolean
fullCompare
)
{
Object
[]
thisParts
=
this
.
parts
;
Object
[]
thatParts
=
that
.
parts
;
int
thisBase
=
thisParts
.
length
-
thisAlen
;
int
thatBase
=
thatParts
.
length
-
thatAlen
;
// search indexes into unmatched parts of this.attrs:
int
firstI
=
0
;
// search indexes into unmatched parts of that.attrs:
int
firstJ
=
0
;
int
lastJ
=
thatAlen
-
2
;
// try to find the mismatch with the first key:
String
firstKey
=
null
;
int
firstKeyValCmp
=
0
;
int
foundKeys
=
0
;
for
(
int
i
=
0
;
i
<
thisAlen
;
i
+=
2
)
{
String
key
=
(
String
)
thisParts
[
thisBase
+
i
+
0
];
String
val
=
(
String
)
thisParts
[
thisBase
+
i
+
1
];
String
otherVal
=
null
;
for
(
int
j
=
firstJ
;
j
<=
lastJ
;
j
+=
2
)
{
if
(
key
.
equals
(
thatParts
[
thatBase
+
j
+
0
]))
{
foundKeys
+=
1
;
otherVal
=
(
String
)
thatParts
[
thatBase
+
j
+
1
];
// Optimization: Narrow subsequent searches when easy.
if
(
j
==
lastJ
)
{
lastJ
-=
2
;
}
else
if
(
j
==
firstJ
)
{
firstJ
+=
2
;
}
if
(
i
==
firstI
)
{
firstI
+=
2
;
}
break
;
}
}
int
valCmp
;
if
(
otherVal
!=
null
)
{
// The key was found.
if
(!
fullCompare
)
{
if
(!
val
.
equals
(
otherVal
))
{
return
1
-
0
;
//arb.
}
continue
;
}
valCmp
=
val
.
compareTo
(
otherVal
);
}
else
{
// Found the key in this but not that.
// Such a mismatch puts the guy missing the key last.
valCmp
=
0
-
1
;
}
if
(
valCmp
!=
0
)
{
// found a mismatch, key present in both elems
if
(
firstKey
==
null
||
firstKey
.
compareTo
(
key
)
>
0
)
{
// found a better key
firstKey
=
key
;
firstKeyValCmp
=
valCmp
;
}
}
}
// We have located the first mismatch of all keys in this.attrs.
// In general we must also look for keys in that.attrs but missing
// from this.attrs; such missing keys, if earlier than firstKey,
// rule the comparison.
// We can sometimes prove quickly there is no missing key.
if
(
foundKeys
==
thatAlen
/
2
)
{
// Exhausted all keys in that.attrs.
return
firstKeyValCmp
;
}
// Search for a missing key in that.attrs earlier than firstKey.
findMissingKey:
for
(
int
j
=
firstJ
;
j
<=
lastJ
;
j
+=
2
)
{
String
otherKey
=
(
String
)
thatParts
[
thatBase
+
j
+
0
];
if
(
firstKey
==
null
||
firstKey
.
compareTo
(
otherKey
)
>
0
)
{
// Found a better key; is it missing?
for
(
int
i
=
firstI
;
i
<
thisAlen
;
i
+=
2
)
{
if
(
otherKey
.
equals
(
thisParts
[
thisBase
+
i
+
0
]))
{
continue
findMissingKey
;
}
}
// If we get here, there was no match in this.attrs.
return
1
-
0
;
}
}
// No missing key. Previous comparison value rules.
return
firstKeyValCmp
;
}
// Binary search looking for first non-null after size.
int
attrBase
()
{
// Smallest & largest possible attribute indexes:
int
kmin
=
0
;
int
kmax
=
(
parts
.
length
-
size
)
>>>
1
;
// earlist possible attribute position:
int
abase
=
parts
.
length
-
(
kmax
*
2
);
// binary search using scaled indexes:
while
(
kmin
!=
kmax
)
{
int
kmid
=
kmin
+
((
kmax
-
kmin
)
>>>
1
);
if
(
parts
[
abase
+
(
kmid
*
2
)]
==
null
)
{
kmin
=
kmid
+
1
;
}
else
{
kmax
=
kmid
;
}
assert
(
kmin
<=
kmax
);
}
return
abase
+
(
kmax
*
2
);
}
/** Sort attributes by name. */
public
void
sortAttrs
()
{
checkNotFrozen
();
int
abase
=
attrBase
();
int
alen
=
parts
.
length
-
abase
;
String
[]
buf
=
new
String
[
alen
];
// collect keys
for
(
int
k
=
0
;
k
<
alen
/
2
;
k
++)
{
String
akey
=
(
String
)
parts
[
abase
+
(
k
*
2
)
+
0
];
buf
[
k
]
=
akey
;
}
Arrays
.
sort
(
buf
,
0
,
alen
/
2
);
// collect values
for
(
int
k
=
0
;
k
<
alen
/
2
;
k
++)
{
String
akey
=
buf
[
k
];
buf
[
k
+
alen
/
2
]
=
getAttr
(
akey
);
}
// reorder keys and values
int
fillp
=
parts
.
length
;
for
(
int
k
=
0
;
k
<
alen
/
2
;
k
++)
{
String
akey
=
buf
[
k
];
String
aval
=
buf
[
k
+
alen
/
2
];
fillp
-=
2
;
parts
[
fillp
+
0
]
=
akey
;
parts
[
fillp
+
1
]
=
aval
;
}
assert
(
fillp
==
abase
);
}
/*
Notes on whitespace and tokenization.
On input, never split CDATA blocks. They remain single tokens.
?Try to treat encoded characters as CDATA-quoted, also?
Internally, each String sub-element is logically a token.
However, if there was no token-splitting on input,
consecutive strings are merged by the parser.
Internally, we need addToken (intervening blank) and addText
(hard concatenation).
Optionally on input, tokenize unquoted text into words.
Between each adjacent word pair, elide either one space
or all space.
On output, we always add spaces between tokens.
The Element("a", {"b", "c", Element("d"), "e f"})
outputs as "<a>b c<d/>e f</a>"
*/
/** Split strings into tokens, using a StringTokenizer. */
public
void
tokenize
(
String
delims
,
boolean
returnDelims
)
{
checkNotFrozen
();
if
(
delims
==
null
)
{
delims
=
WHITESPACE_CHARS
;
// StringTokenizer default
}
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
if
(!(
parts
[
i
]
instanceof
CharSequence
))
{
continue
;
}
int
osize
=
size
;
String
str
=
parts
[
i
].
toString
();
StringTokenizer
st
=
new
StringTokenizer
(
str
,
delims
,
returnDelims
);
int
nstrs
=
st
.
countTokens
();
switch
(
nstrs
)
{
case
0
:
close
(
i
--,
1
);
break
;
case
1
:
parts
[
i
]
=
st
.
nextToken
();
break
;
default
:
openOrExpand
(
i
+
1
,
nstrs
-
1
);
for
(
int
j
=
0
;
j
<
nstrs
;
j
++)
{
parts
[
i
+
j
]
=
st
.
nextToken
();
}
i
+=
nstrs
-
1
;
break
;
}
}
}
public
void
tokenize
(
String
delims
)
{
tokenize
(
delims
,
false
);
}
public
void
tokenize
()
{
tokenize
(
null
,
false
);
}
// views
class
LView
extends
AbstractList
<
Object
>
{
Element
asElement
()
{
return
Element
.
this
;
}
public
int
size
()
{
return
Element
.
this
.
size
();
}
public
Object
get
(
int
i
)
{
return
Element
.
this
.
get
(
i
);
}
@Override
public
boolean
contains
(
Object
e
)
{
return
Element
.
this
.
contains
(
e
);
}
@Override
public
Object
[]
toArray
()
{
return
Element
.
this
.
toArray
();
}
@Override
public
int
indexOf
(
Object
e
)
{
return
Element
.
this
.
indexOf
(
e
);
}
@Override
public
int
lastIndexOf
(
Object
e
)
{
return
Element
.
this
.
lastIndexOf
(
e
);
}
@Override
public
void
add
(
int
i
,
Object
e
)
{
++
modCount
;
Element
.
this
.
add
(
i
,
e
);
}
@Override
public
boolean
addAll
(
int
i
,
Collection
<?
extends
Object
>
c
)
{
++
modCount
;
return
Element
.
this
.
addAll
(
i
,
c
)
>
0
;
}
@Override
public
boolean
addAll
(
Collection
<?
extends
Object
>
c
)
{
++
modCount
;
return
Element
.
this
.
addAll
(
c
)
>
0
;
}
@Override
public
Object
remove
(
int
i
)
{
++
modCount
;
return
Element
.
this
.
remove
(
i
);
}
@Override
public
Object
set
(
int
i
,
Object
e
)
{
++
modCount
;
return
Element
.
this
.
set
(
i
,
e
);
}
@Override
public
void
clear
()
{
++
modCount
;
Element
.
this
.
clear
();
}
// Others: toArray(Object[]), containsAll, removeAll, retainAll
}
/** Produce a list view of sub-elements.
* (The list view does not provide access to the element's
* name or attributes.)
* Changes to this view are immediately reflected in the
* element itself.
*/
public
List
<
Object
>
asList
()
{
return
new
LView
();
}
/** Produce a list iterator on all sub-elements. */
public
ListIterator
<
Object
>
iterator
()
{
//return asList().listIterator();
return
new
Itr
();
}
// Hand-inlined replacement for LView.listIterator():
class
Itr
implements
ListIterator
<
Object
>
{
int
lastRet
=
-
1
;
int
cursor
=
0
;
public
boolean
hasNext
()
{
return
cursor
<
size
;
}
public
boolean
hasPrevious
()
{
return
cursor
>
0
&&
cursor
<=
size
;
}
public
Object
next
()
{
if
(!
hasNext
())
{
nsee
();
}
return
parts
[
lastRet
=
cursor
++];
}
public
Object
previous
()
{
if
(!
hasPrevious
())
{
nsee
();
}
return
parts
[--
cursor
];
}
public
int
nextIndex
()
{
return
cursor
;
}
public
int
previousIndex
()
{
return
cursor
-
1
;
}
public
void
set
(
Object
x
)
{
parts
[
lastRet
]
=
x
;
}
public
void
add
(
Object
x
)
{
lastRet
=
-
1
;
Element
.
this
.
add
(
cursor
++,
x
);
}
public
void
remove
()
{
if
(
lastRet
<
0
)
{
throw
new
IllegalStateException
();
}
Element
.
this
.
remove
(
lastRet
);
if
(
lastRet
<
cursor
)
{
--
cursor
;
}
lastRet
=
-
1
;
}
void
nsee
()
{
throw
new
NoSuchElementException
(
"element "
+
cursor
);
}
}
/** A PrintWriter which always appends as if by addText.
* Use of this stream may insert a StringBuffer at the end
* of the Element. The user must not directly modify this
* StringBuffer, or use it in other data structures.
* From time to time, the StringBuffer may be replaced by a
* constant string as a result of using the PrintWriter.
*/
public
PrintWriter
asWriter
()
{
return
new
ElemW
();
}
class
ElemW
extends
PrintWriter
{
ElemW
()
{
super
(
new
StringWriter
());
}
final
StringBuffer
buf
=
((
StringWriter
)
out
).
getBuffer
();
{
lock
=
buf
;
}
// synchronize on this buffer
@Override
public
void
println
()
{
synchronized
(
buf
)
{
ensureCursor
();
super
.
println
();
}
}
@Override
public
void
write
(
int
ch
)
{
synchronized
(
buf
)
{
ensureCursor
();
//buf.append(ch);
super
.
write
(
ch
);
}
}
@Override
public
void
write
(
char
buf
[],
int
off
,
int
len
)
{
synchronized
(
buf
)
{
ensureCursor
();
super
.
write
(
buf
,
off
,
len
);
}
}
@Override
public
void
write
(
String
s
,
int
off
,
int
len
)
{
synchronized
(
buf
)
{
ensureCursor
();
//buf.append(s.substring(off, off+len));
super
.
write
(
s
,
off
,
len
);
}
}
@Override
public
void
write
(
String
s
)
{
synchronized
(
buf
)
{
ensureCursor
();
//buf.append(s);
super
.
write
(
s
);
}
}
private
void
ensureCursor
()
{
checkNotFrozen
();
if
(
getLast
()
!=
buf
)
{
int
pos
=
indexOf
(
buf
);
if
(
pos
>=
0
)
{
// Freeze the pre-existing use of buf.
setRaw
(
pos
,
buf
.
toString
());
}
add
(
buf
);
}
}
}
/** Produce a map view of attributes, in which the attribute
* name strings are the keys.
* (The map view does not provide access to the element's
* name or sub-elements.)
* Changes to this view are immediately reflected in the
* element itself.
*/
public
Map
<
String
,
String
>
asAttrMap
()
{
class
Entry
implements
Map
.
Entry
<
String
,
String
>
{
final
int
k
;
Entry
(
int
k
)
{
this
.
k
=
k
;
assert
(((
String
)
getKey
()).
toString
()
!=
null
);
// check, fail-fast
}
public
String
getKey
()
{
return
Element
.
this
.
getAttrName
(
k
);
}
public
String
getValue
()
{
return
Element
.
this
.
getAttr
(
k
);
}
public
String
setValue
(
String
v
)
{
return
Element
.
this
.
setAttr
(
k
,
v
.
toString
());
}
@Override
public
boolean
equals
(
Object
o
)
{
if
(!(
o
instanceof
Map
.
Entry
))
{
return
false
;
}
Map
.
Entry
that
=
(
Map
.
Entry
)
o
;
return
(
this
.
getKey
().
equals
(
that
.
getKey
())
&&
this
.
getValue
().
equals
(
that
.
getValue
()));
}
@Override
public
int
hashCode
()
{
return
getKey
().
hashCode
()
^
getValue
().
hashCode
();
}
}
class
EIter
implements
Iterator
<
Map
.
Entry
<
String
,
String
>>
{
int
k
=
0
;
// index of pending next() attribute
public
boolean
hasNext
()
{
return
Element
.
this
.
containsAttr
(
k
);
}
public
Map
.
Entry
<
String
,
String
>
next
()
{
return
new
Entry
(
k
++);
}
public
void
remove
()
{
Element
.
this
.
removeAttr
(--
k
);
}
}
class
ESet
extends
AbstractSet
<
Map
.
Entry
<
String
,
String
>>
{
public
int
size
()
{
return
Element
.
this
.
attrSize
();
}
public
Iterator
<
Map
.
Entry
<
String
,
String
>>
iterator
()
{
return
new
EIter
();
}
@Override
public
void
clear
()
{
Element
.
this
.
clearAttrs
();
}
}
class
AView
extends
AbstractMap
<
String
,
String
>
{
private
transient
Set
<
Map
.
Entry
<
String
,
String
>>
eSet
;
public
Set
<
Map
.
Entry
<
String
,
String
>>
entrySet
()
{
if
(
eSet
==
null
)
{
eSet
=
new
ESet
();
}
return
eSet
;
}
@Override
public
int
size
()
{
return
Element
.
this
.
attrSize
();
}
public
boolean
containsKey
(
String
k
)
{
return
Element
.
this
.
containsAttr
(
k
);
}
public
String
get
(
String
k
)
{
return
Element
.
this
.
getAttr
(
k
);
}
@Override
public
String
put
(
String
k
,
String
v
)
{
return
Element
.
this
.
setAttr
(
k
,
v
.
toString
());
}
public
String
remove
(
String
k
)
{
return
Element
.
this
.
setAttr
(
k
,
null
);
}
}
return
new
AView
();
}
/** Reports number of additional elements this object can accommodate
* without reallocation.
*/
public
int
getExtraCapacity
()
{
int
abase
=
attrBase
();
return
Math
.
max
(
0
,
abase
-
size
-
NEED_SLOP
);
}
/** Ensures that at least the given number of additional elements
* can be added to this object without reallocation.
*/
public
void
ensureExtraCapacity
(
int
cap
)
{
if
(
cap
==
0
||
hasNulls
(
cap
+
NEED_SLOP
))
{
return
;
}
setExtraCapacity
(
cap
);
}
/**
* Trim excess capacity to zero, or do nothing if frozen.
* This minimizes the space occupied by this Element,
* at the expense of a reallocation if sub-elements or attributes
* are added later.
*/
public
void
trimToSize
()
{
if
(
isFrozen
())
{
return
;
}
setExtraCapacity
(
0
);
}
/** Changes the number of additional elements this object can accommodate
* without reallocation.
*/
public
void
setExtraCapacity
(
int
cap
)
{
checkNotFrozen
();
int
abase
=
attrBase
();
int
alen
=
parts
.
length
-
abase
;
// slots allocated for attrs
int
nlen
=
size
+
cap
+
NEED_SLOP
+
alen
;
if
(
nlen
!=
parts
.
length
)
{
Object
[]
nparts
=
new
Object
[
nlen
];
// copy attributes
System
.
arraycopy
(
parts
,
abase
,
nparts
,
nlen
-
alen
,
alen
);
// copy sub-elements
System
.
arraycopy
(
parts
,
0
,
nparts
,
0
,
size
);
parts
=
nparts
;
}
assert
(
cap
==
getExtraCapacity
());
}
// Return true if there are at least len nulls of slop available.
boolean
hasNulls
(
int
len
)
{
if
(
len
==
0
)
{
return
true
;
}
int
lastNull
=
size
+
len
-
1
;
if
(
lastNull
>=
parts
.
length
)
{
return
false
;
}
return
(
parts
[
lastNull
]
==
null
);
}
// Opens up parts array at pos by len spaces.
void
open
(
int
pos
,
int
len
)
{
assert
(
pos
<
size
);
assert
(
hasNulls
(
len
+
NEED_SLOP
));
checkNotFrozen
();
int
nsize
=
size
+
len
;
int
tlen
=
size
-
pos
;
System
.
arraycopy
(
parts
,
pos
,
parts
,
pos
+
len
,
tlen
);
size
=
nsize
;
}
// Reallocate and open up at parts[pos] to at least len empty places.
// Shift anything after pos right by len. Reallocate if necessary.
// If pos < size, caller must fill it in with non-null values.
// Returns incremented size; caller is responsible for storing it
// down, if desired.
int
expand
(
int
pos
,
int
len
)
{
assert
(
pos
<=
size
);
// There must be at least len nulls between elems and attrs.
assert
(!
hasNulls
(
NEED_SLOP
+
len
));
// caller responsibility
checkNotFrozen
();
int
nsize
=
size
+
len
;
// length of all elements
int
tlen
=
size
-
pos
;
// length of elements in post-pos tail
int
abase
=
attrBase
();
int
alen
=
parts
.
length
-
abase
;
// slots allocated for attrs
int
nlen
=
nsize
+
alen
+
NEED_SLOP
;
nlen
+=
(
nlen
>>>
1
);
// add new slop!
Object
[]
nparts
=
new
Object
[
nlen
];
// copy head of sub-elements
System
.
arraycopy
(
parts
,
0
,
nparts
,
0
,
pos
);
// copy tail of sub-elements
System
.
arraycopy
(
parts
,
pos
,
nparts
,
pos
+
len
,
tlen
);
// copy attributes
System
.
arraycopy
(
parts
,
abase
,
nparts
,
nlen
-
alen
,
alen
);
// update self
parts
=
nparts
;
//assert(hasNulls(len)); <- not yet true, since size != nsize
return
nsize
;
}
// Open or expand at the given position, as appropriate.
boolean
openOrExpand
(
int
pos
,
int
len
)
{
if
(
pos
<
0
||
pos
>
size
)
{
badIndex
(
pos
);
}
if
(
hasNulls
(
len
+
NEED_SLOP
))
{
if
(
pos
==
size
)
{
size
+=
len
;
}
else
{
open
(
pos
,
len
);
}
return
false
;
}
else
{
size
=
expand
(
pos
,
len
);
return
true
;
}
}
// Close up at parts[pos] len old places.
// Shift anything after pos left by len.
// Fill unused end of parts with null.
void
close
(
int
pos
,
int
len
)
{
assert
(
len
>
0
);
assert
((
size
-
pos
)
>=
len
);
checkNotFrozen
();
int
tlen
=
(
size
-
pos
)
-
len
;
// length of elements in post-pos tail
int
nsize
=
size
-
len
;
System
.
arraycopy
(
parts
,
pos
+
len
,
parts
,
pos
,
tlen
);
// reinitialize the unoccupied slots to null
clearParts
(
nsize
,
nsize
+
len
);
// update self
size
=
nsize
;
assert
(
hasNulls
(
len
));
}
public
void
writeTo
(
Writer
w
)
throws
IOException
{
new
Printer
(
w
).
print
(
this
);
}
public
void
writePrettyTo
(
Writer
w
)
throws
IOException
{
prettyPrintTo
(
w
,
this
);
}
public
String
prettyString
()
{
StringWriter
sw
=
new
StringWriter
();
try
{
writePrettyTo
(
sw
);
}
catch
(
IOException
ee
)
{
throw
new
Error
(
ee
);
// should not happen
}
return
sw
.
toString
();
}
@Override
public
String
toString
()
{
StringWriter
sw
=
new
StringWriter
();
try
{
writeTo
(
sw
);
}
catch
(
IOException
ee
)
{
throw
new
Error
(
ee
);
// should not happen
}
return
sw
.
toString
();
}
public
String
dump
()
{
// For debugging only. Reveals internal layout.
StringBuilder
buf
=
new
StringBuilder
();
buf
.
append
(
"<"
).
append
(
name
).
append
(
"["
).
append
(
size
).
append
(
"]"
);
for
(
int
i
=
0
;
i
<
parts
.
length
;
i
++)
{
Object
p
=
parts
[
i
];
if
(
p
==
null
)
{
buf
.
append
(
" null"
);
}
else
{
buf
.
append
(
" {"
);
String
cname
=
p
.
getClass
().
getName
();
cname
=
cname
.
substring
(
1
+
cname
.
indexOf
(
'/'
));
cname
=
cname
.
substring
(
1
+
cname
.
indexOf
(
'$'
));
cname
=
cname
.
substring
(
1
+
cname
.
indexOf
(
'#'
));
if
(!
cname
.
equals
(
"String"
))
{
buf
.
append
(
cname
).
append
(
":"
);
}
buf
.
append
(
p
);
buf
.
append
(
"}"
);
}
}
return
buf
.
append
(
">"
).
toString
();
}
public
static
java
.
lang
.
reflect
.
Method
method
(
String
name
)
{
HashMap
allM
=
allMethods
;
if
(
allM
==
null
)
{
allM
=
makeAllMethods
();
}
java
.
lang
.
reflect
.
Method
res
=
(
java
.
lang
.
reflect
.
Method
)
allMethods
.
get
(
name
);
if
(
res
==
null
)
{
throw
new
IllegalArgumentException
(
name
);
}
return
res
;
}
private
static
HashMap
allMethods
;
private
static
synchronized
HashMap
makeAllMethods
()
{
if
(
allMethods
!=
null
)
{
return
allMethods
;
}
java
.
lang
.
reflect
.
Method
[]
methods
=
Element
.
class
.
getMethods
();
HashMap
<
String
,
java
.
lang
.
reflect
.
Method
>
allM
=
new
HashMap
<
String
,
java
.
lang
.
reflect
.
Method
>(),
ambig
=
new
HashMap
<
String
,
java
.
lang
.
reflect
.
Method
>();
for
(
int
i
=
0
;
i
<
methods
.
length
;
i
++)
{
java
.
lang
.
reflect
.
Method
m
=
methods
[
i
];
Class
[]
args
=
m
.
getParameterTypes
();
String
name
=
m
.
getName
();
assert
(
java
.
lang
.
reflect
.
Modifier
.
isPublic
(
m
.
getModifiers
()));
if
(
name
.
startsWith
(
"notify"
))
{
continue
;
}
if
(
name
.
endsWith
(
"Attr"
)
&&
args
.
length
>
0
&&
args
[
0
]
==
int
.
class
)
// ignore getAttr(int), etc.
{
continue
;
}
if
(
name
.
endsWith
(
"All"
)
&&
args
.
length
>
1
&&
args
[
0
]
==
Filter
.
class
)
// ignore findAll(Filter, int...), etc.
{
continue
;
}
java
.
lang
.
reflect
.
Method
pm
=
allM
.
put
(
name
,
m
);
if
(
pm
!=
null
)
{
Class
[]
pargs
=
pm
.
getParameterTypes
();
if
(
pargs
.
length
>
args
.
length
)
{
allM
.
put
(
name
,
pm
);
// put it back
}
else
if
(
pargs
.
length
==
args
.
length
)
{
ambig
.
put
(
name
,
pm
);
// make a note of it
}
}
}
// Delete ambiguous methods.
for
(
Map
.
Entry
<
String
,
java
.
lang
.
reflect
.
Method
>
e
:
ambig
.
entrySet
())
{
String
name
=
e
.
getKey
();
java
.
lang
.
reflect
.
Method
pm
=
e
.
getValue
();
java
.
lang
.
reflect
.
Method
m
=
allM
.
get
(
name
);
Class
[]
args
=
m
.
getParameterTypes
();
Class
[]
pargs
=
pm
.
getParameterTypes
();
if
(
pargs
.
length
==
args
.
length
)
{
//System.out.println("ambig: "+pm);
//System.out.println(" with: "+m);
//ambig: int addAll(int,Element)
// with: int addAll(int,Collection)
allM
.
put
(
name
,
null
);
// get rid of
}
}
//System.out.println("allM: "+allM);
return
allMethods
=
allM
;
}
}
static
Object
fixupString
(
Object
part
)
{
if
(
part
instanceof
CharSequence
&&
!(
part
instanceof
String
))
{
return
part
.
toString
();
}
else
{
return
part
;
}
}
public
static
final
class
Special
implements
Comparable
<
Special
>
{
String
kind
;
Object
value
;
public
Special
(
String
kind
,
Object
value
)
{
this
.
kind
=
kind
;
this
.
value
=
value
;
}
public
String
getKind
()
{
return
kind
;
}
public
Object
getValue
()
{
return
value
;
}
@Override
public
boolean
equals
(
Object
o
)
{
if
(!(
o
instanceof
Special
))
{
return
false
;
}
Special
that
=
(
Special
)
o
;
return
this
.
kind
.
equals
(
that
.
kind
)
&&
this
.
value
.
equals
(
that
.
value
);
}
@Override
public
int
hashCode
()
{
return
kind
.
hashCode
()
*
65
+
value
.
hashCode
();
}
public
int
compareTo
(
Special
that
)
{
int
r
=
this
.
kind
.
compareTo
(
that
.
kind
);
if
(
r
!=
0
)
{
return
r
;
}
return
((
Comparable
)
this
.
value
).
compareTo
(
that
.
value
);
}
@Override
public
String
toString
()
{
int
split
=
kind
.
indexOf
(
' '
);
String
pref
=
kind
.
substring
(
0
,
split
<
0
?
0
:
split
);
String
post
=
kind
.
substring
(
split
+
1
);
return
pref
+
value
+
post
;
}
}
/** Supports sorting of mixed content. Sorts strings first,
* then Elements, then everything else (as Comparable).
*/
public
static
Comparator
<
Object
>
contentOrder
()
{
return
CONTENT_ORDER
;
}
private
static
Comparator
<
Object
>
CONTENT_ORDER
=
new
ContentComparator
();
private
static
class
ContentComparator
implements
Comparator
<
Object
>
{
public
int
compare
(
Object
o1
,
Object
o2
)
{
boolean
cs1
=
(
o1
instanceof
CharSequence
);
boolean
cs2
=
(
o2
instanceof
CharSequence
);
if
(
cs1
&&
cs2
)
{
String
s1
=
(
String
)
fixupString
(
o1
);
String
s2
=
(
String
)
fixupString
(
o2
);
return
s1
.
compareTo
(
s2
);
}
if
(
cs1
)
{
return
0
-
1
;
}
if
(
cs2
)
{
return
1
-
0
;
}
boolean
el1
=
(
o1
instanceof
Element
);
boolean
el2
=
(
o2
instanceof
Element
);
if
(
el1
&&
el2
)
{
return
((
Element
)
o1
).
compareTo
((
Element
)
o2
);
}
if
(
el1
)
{
return
0
-
1
;
}
if
(
el2
)
{
return
1
-
0
;
}
return
((
Comparable
)
o1
).
compareTo
(
o2
);
}
}
/** Used to find, filter, or transform sub-elements.
* When used as a predicate, the filter returns a null
* value for false, and the original object value for true.
* When used as a transformer, the filter may return
* null, for no values, the original object, a new object,
* or an anonymous Element (meaning multiple results).
*/
public
interface
Filter
{
Object
filter
(
Object
value
);
}
/** Use this to find an element, perhaps with a given name. */
public
static
class
ElementFilter
implements
Filter
{
/** Subclasses may override this to implement better value tests.
* By default, it returns the element itself, thus recognizing
* all elements, regardless of name.
*/
public
Element
filter
(
Element
elem
)
{
return
elem
;
// override this
}
public
final
Object
filter
(
Object
value
)
{
if
(!(
value
instanceof
Element
))
{
return
null
;
}
return
filter
((
Element
)
value
);
}
@Override
public
String
toString
()
{
return
"<ElementFilter name='*'/>"
;
}
}
private
static
Filter
elementFilter
;
public
static
Filter
elementFilter
()
{
return
(
elementFilter
!=
null
)
?
elementFilter
:
(
elementFilter
=
new
ElementFilter
());
}
public
static
Filter
elementFilter
(
final
String
name
)
{
name
.
toString
();
// null check
return
new
ElementFilter
()
{
@Override
public
Element
filter
(
Element
elem
)
{
return
name
.
equals
(
elem
.
name
)
?
elem
:
null
;
}
@Override
public
String
toString
()
{
return
"<ElementFilter name='"
+
name
+
"'/>"
;
}
};
}
public
static
Filter
elementFilter
(
final
Collection
nameSet
)
{
nameSet
.
getClass
();
// null check
return
new
ElementFilter
()
{
@Override
public
Element
filter
(
Element
elem
)
{
return
nameSet
.
contains
(
elem
.
name
)
?
elem
:
null
;
}
@Override
public
String
toString
()
{
return
"<ElementFilter name='"
+
nameSet
+
"'/>"
;
}
};
}
public
static
Filter
elementFilter
(
String
...
nameSet
)
{
Collection
<
String
>
ncoll
=
Arrays
.
asList
(
nameSet
);
if
(
nameSet
.
length
>
10
)
{
ncoll
=
new
HashSet
<
String
>(
ncoll
);
}
return
elementFilter
(
ncoll
);
}
/** Use this to find an element with a named attribute,
* possibly with a particular value.
* (Note that an attribute is missing if and only if its value is null.)
*/
public
static
class
AttrFilter
extends
ElementFilter
{
protected
final
String
attrName
;
public
AttrFilter
(
String
attrName
)
{
this
.
attrName
=
attrName
.
toString
();
}
/** Subclasses may override this to implement better value tests.
* By default, it returns true for any non-null value, thus
* recognizing any attribute of the given name, regardless of value.
*/
public
boolean
test
(
String
attrVal
)
{
return
attrVal
!=
null
;
// override this
}
@Override
public
final
Element
filter
(
Element
elem
)
{
return
test
(
elem
.
getAttr
(
attrName
))
?
elem
:
null
;
}
@Override
public
String
toString
()
{
return
"<AttrFilter name='"
+
attrName
+
"' value='*'/>"
;
}
}
public
static
Filter
attrFilter
(
String
attrName
)
{
return
new
AttrFilter
(
attrName
);
}
public
static
Filter
attrFilter
(
String
attrName
,
final
String
attrVal
)
{
if
(
attrVal
==
null
)
{
return
not
(
attrFilter
(
attrName
));
}
return
new
AttrFilter
(
attrName
)
{
@Override
public
boolean
test
(
String
attrVal2
)
{
return
attrVal
.
equals
(
attrVal2
);
}
@Override
public
String
toString
()
{
return
"<AttrFilter name='"
+
attrName
+
"' value='"
+
attrVal
+
"'/>"
;
}
};
}
public
static
Filter
attrFilter
(
Element
matchThis
,
String
attrName
)
{
return
attrFilter
(
attrName
,
matchThis
.
getAttr
(
attrName
));
}
/** Use this to find a sub-element of a given class. */
public
static
Filter
classFilter
(
final
Class
clazz
)
{
return
new
Filter
()
{
public
Object
filter
(
Object
value
)
{
return
clazz
.
isInstance
(
value
)
?
value
:
null
;
}
@Override
public
String
toString
()
{
return
"<ClassFilter class='"
+
clazz
.
getName
()
+
"'/>"
;
}
};
}
private
static
Filter
textFilter
;
public
static
Filter
textFilter
()
{
return
(
textFilter
!=
null
)
?
textFilter
:
(
textFilter
=
classFilter
(
CharSequence
.
class
));
}
private
static
Filter
specialFilter
;
public
static
Filter
specialFilter
()
{
return
(
specialFilter
!=
null
)
?
specialFilter
:
(
specialFilter
=
classFilter
(
Special
.
class
));
}
private
static
Filter
selfFilter
;
/** This filter always returns its own argument. */
public
static
Filter
selfFilter
()
{
if
(
selfFilter
!=
null
)
{
return
selfFilter
;
}
return
selfFilter
=
new
Filter
()
{
public
Object
filter
(
Object
value
)
{
return
value
;
}
@Override
public
String
toString
()
{
return
"<Self/>"
;
}
};
}
/** This filter always returns a fixed value, regardless of argument. */
public
static
Filter
constantFilter
(
final
Object
value
)
{
return
new
Filter
()
{
public
Object
filter
(
Object
ignore
)
{
return
value
;
}
@Override
public
String
toString
()
{
return
"<Constant>"
+
value
+
"</Constant>"
;
}
};
}
private
static
Filter
nullFilter
;
public
static
Filter
nullFilter
()
{
return
(
nullFilter
!=
null
)
?
nullFilter
:
(
nullFilter
=
constantFilter
(
null
));
}
private
static
Filter
emptyFilter
;
public
static
Filter
emptyFilter
()
{
return
(
emptyFilter
!=
null
)
?
emptyFilter
:
(
emptyFilter
=
constantFilter
(
Element
.
EMPTY
));
}
/** Use this to invert the logical sense of the given filter. */
public
static
Filter
not
(
final
Filter
f
)
{
return
new
Filter
()
{
public
Object
filter
(
Object
value
)
{
return
f
.
filter
(
value
)
==
null
?
value
:
null
;
}
@Override
public
String
toString
()
{
return
"<Not>"
+
f
+
"</Not>"
;
}
};
}
/** Use this to combine several filters with logical AND.
* Returns either the first null or the last non-null value.
*/
public
static
Filter
and
(
final
Filter
f0
,
final
Filter
f1
)
{
return
and
(
new
Filter
[]{
f0
,
f1
});
}
public
static
Filter
and
(
final
Filter
...
fs
)
{
switch
(
fs
.
length
)
{
case
0
:
return
selfFilter
();
// always true (on non-null inputs)
case
1
:
return
fs
[
0
];
}
return
new
Filter
()
{
public
Object
filter
(
Object
value
)
{
Object
res
=
fs
[
0
].
filter
(
value
);
if
(
res
!=
null
)
{
res
=
fs
[
1
].
filter
(
value
);
for
(
int
i
=
2
;
res
!=
null
&&
i
<
fs
.
length
;
i
++)
{
res
=
fs
[
i
].
filter
(
value
);
}
}
return
res
;
}
@Override
public
String
toString
()
{
return
opToString
(
"<And>"
,
fs
,
"</And>"
);
}
};
}
/** Use this to combine several filters with logical OR.
* Returns either the first non-null or the last null value.
*/
public
static
Filter
or
(
final
Filter
f0
,
final
Filter
f1
)
{
return
or
(
new
Filter
[]{
f0
,
f1
});
}
public
static
Filter
or
(
final
Filter
...
fs
)
{
switch
(
fs
.
length
)
{
case
0
:
return
nullFilter
();
case
1
:
return
fs
[
0
];
}
return
new
Filter
()
{
public
Object
filter
(
Object
value
)
{
Object
res
=
fs
[
0
].
filter
(
value
);
if
(
res
==
null
)
{
res
=
fs
[
1
].
filter
(
value
);
for
(
int
i
=
2
;
res
==
null
&&
i
<
fs
.
length
;
i
++)
{
res
=
fs
[
i
].
filter
(
value
);
}
}
return
res
;
}
@Override
public
String
toString
()
{
return
opToString
(
"<Or>"
,
fs
,
"</Or>"
);
}
};
}
/** Use this to combine several filters with logical AND,
* and where each non-null result is passed as the argument
* to the next filter.
* Returns either the first null or the last non-null value.
*/
public
static
Filter
stack
(
final
Filter
f0
,
final
Filter
f1
)
{
return
stack
(
new
Filter
[]{
f0
,
f1
});
}
public
static
Filter
stack
(
final
Filter
...
fs
)
{
switch
(
fs
.
length
)
{
case
0
:
return
nullFilter
();
case
1
:
return
fs
[
0
];
}
return
new
Filter
()
{
public
Object
filter
(
Object
value
)
{
Object
res
=
fs
[
0
].
filter
(
value
);
if
(
res
!=
null
)
{
res
=
fs
[
1
].
filter
(
res
);
for
(
int
i
=
2
;
res
!=
null
&&
i
<
fs
.
length
;
i
++)
{
res
=
fs
[
i
].
filter
(
res
);
}
}
return
res
;
}
@Override
public
String
toString
()
{
return
opToString
(
"<Stack>"
,
fs
,
"</Stack>"
);
}
};
}
/** Copy everything produced by f to sink, using addContent. */
public
static
Filter
content
(
final
Filter
f
,
final
Collection
<
Object
>
sink
)
{
return
new
Filter
()
{
public
Object
filter
(
Object
value
)
{
Object
res
=
f
.
filter
(
value
);
addContent
(
res
,
sink
);
return
res
;
}
@Override
public
String
toString
()
{
return
opToString
(
"<addContent>"
,
new
Object
[]{
f
,
sink
},
"</addContent>"
);
}
};
}
/** Look down the tree using f, collecting fx, else recursing into x.
* Identities:
* <code>
* findInTree(f, s) == findInTree(content(f, s))
* findInTree(f) == replaceInTree(and(f, selfFilter())).
* </code>
*/
public
static
Filter
findInTree
(
Filter
f
,
Collection
<
Object
>
sink
)
{
if
(
sink
!=
null
)
{
f
=
content
(
f
,
sink
);
}
return
findInTree
(
f
);
}
/** Look down the tree using f, recursing into x unless fx. */
public
static
Filter
findInTree
(
final
Filter
f
)
{
return
new
Filter
()
{
public
Object
filter
(
Object
value
)
{
Object
res
=
f
.
filter
(
value
);
if
(
res
!=
null
)
{
return
res
;
}
if
(
value
instanceof
Element
)
{
// recurse
return
((
Element
)
value
).
find
(
this
);
}
return
null
;
}
@Override
public
String
toString
()
{
return
opToString
(
"<FindInTree>"
,
new
Object
[]{
f
},
"</FindInTree>"
);
}
};
}
/** Look down the tree using f. Replace each x with fx, else recurse.
* If post filter g is given, optionally replace with gx after recursion.
*/
public
static
Filter
replaceInTree
(
final
Filter
f
,
final
Filter
g
)
{
return
new
Filter
()
{
public
Object
filter
(
Object
value
)
{
Object
res
=
(
f
==
null
)
?
null
:
f
.
filter
(
value
);
if
(
res
!=
null
)
{
return
res
;
}
if
(
value
instanceof
Element
)
{
// recurse
((
Element
)
value
).
replaceAll
(
this
);
// Optional postorder traversal:
if
(
g
!=
null
)
{
res
=
g
.
filter
(
value
);
}
}
return
res
;
// usually null, meaning no replacement
}
@Override
public
String
toString
()
{
return
opToString
(
"<ReplaceInTree>"
,
new
Object
[]{
f
,
g
},
"</ReplaceInTree>"
);
}
};
}
public
static
Filter
replaceInTree
(
Filter
f
)
{
f
.
getClass
();
// null check
return
replaceInTree
(
f
,
null
);
}
/** Make a filter which calls this method on the given element.
* If the method is static, the first argument is passed the
* the subtree value being filtered.
* If the method is non-static, the receiver is the subtree value itself.
* <p>
* Optionally, additional arguments may be specified.
* <p>
* If the filtered value does not match the receiver class
* (or else the first argument type, if the method is static),
* the filter returns null without invoking the method.
* <p>
* The returned filter value is the result returned from the method.
* Optionally, a non-null special false result value may be specified.
* If the result returned from the method is equal to that false value,
* the filter will return null.
*/
public
static
Filter
methodFilter
(
java
.
lang
.
reflect
.
Method
m
,
Object
[]
extraArgs
,
Object
falseResult
)
{
return
methodFilter
(
m
,
false
,
extraArgs
,
falseResult
);
}
public
static
Filter
methodFilter
(
java
.
lang
.
reflect
.
Method
m
,
Object
[]
args
)
{
return
methodFilter
(
m
,
args
,
null
);
}
public
static
Filter
methodFilter
(
java
.
lang
.
reflect
.
Method
m
)
{
return
methodFilter
(
m
,
null
,
null
);
}
public
static
Filter
testMethodFilter
(
java
.
lang
.
reflect
.
Method
m
,
Object
[]
extraArgs
,
Object
falseResult
)
{
return
methodFilter
(
m
,
true
,
extraArgs
,
falseResult
);
}
public
static
Filter
testMethodFilter
(
java
.
lang
.
reflect
.
Method
m
,
Object
[]
extraArgs
)
{
return
methodFilter
(
m
,
true
,
extraArgs
,
zeroArgs
.
get
(
m
.
getReturnType
()));
}
public
static
Filter
testMethodFilter
(
java
.
lang
.
reflect
.
Method
m
)
{
return
methodFilter
(
m
,
true
,
null
,
zeroArgs
.
get
(
m
.
getReturnType
()));
}
private
static
Filter
methodFilter
(
final
java
.
lang
.
reflect
.
Method
m
,
final
boolean
isTest
,
Object
[]
extraArgs
,
final
Object
falseResult
)
{
Class
[]
params
=
m
.
getParameterTypes
();
final
boolean
isStatic
=
java
.
lang
.
reflect
.
Modifier
.
isStatic
(
m
.
getModifiers
());
int
insertLen
=
(
isStatic
?
1
:
0
);
if
(
insertLen
+
(
extraArgs
==
null
?
0
:
extraArgs
.
length
)
>
params
.
length
)
{
throw
new
IllegalArgumentException
(
"too many arguments"
);
}
final
Object
[]
args
=
(
params
.
length
==
insertLen
)
?
null
:
new
Object
[
params
.
length
];
final
Class
valueType
=
!
isStatic
?
m
.
getDeclaringClass
()
:
params
[
0
];
if
(
valueType
.
isPrimitive
())
{
throw
new
IllegalArgumentException
(
"filtered value must be reference type"
);
}
int
fillp
=
insertLen
;
if
(
extraArgs
!=
null
)
{
for
(
int
i
=
0
;
i
<
extraArgs
.
length
;
i
++)
{
args
[
fillp
++]
=
extraArgs
[
i
];
}
}
if
(
args
!=
null
)
{
while
(
fillp
<
args
.
length
)
{
Class
param
=
params
[
fillp
];
args
[
fillp
++]
=
param
.
isPrimitive
()
?
zeroArgs
.
get
(
param
)
:
null
;
}
}
final
Thread
curt
=
Thread
.
currentThread
();
class
MFilt
implements
Filter
{
public
Object
filter
(
Object
value
)
{
if
(!
valueType
.
isInstance
(
value
))
{
return
null
;
// filter fails quickly
}
Object
[]
args1
=
args
;
if
(
isStatic
)
{
if
(
args1
==
null
)
{
args1
=
new
Object
[
1
];
}
else
if
(
curt
!=
Thread
.
currentThread
())
// Dirty hack to curtail array copying in common case.
{
args1
=
(
Object
[])
args1
.
clone
();
}
args1
[
0
]
=
value
;
}
Object
res
;
try
{
res
=
m
.
invoke
(
value
,
args1
);
}
catch
(
java
.
lang
.
reflect
.
InvocationTargetException
te
)
{
Throwable
ee
=
te
.
getCause
();
if
(
ee
instanceof
RuntimeException
)
{
throw
(
RuntimeException
)
ee
;
}
if
(
ee
instanceof
Error
)
{
throw
(
Error
)
ee
;
}
throw
new
RuntimeException
(
"throw in filter"
,
ee
);
}
catch
(
IllegalAccessException
ee
)
{
throw
new
RuntimeException
(
"access error in filter"
,
ee
);
}
if
(
res
==
null
)
{
if
(!
isTest
&&
m
.
getReturnType
()
==
Void
.
TYPE
)
{
// Void methods return self by convention.
// (But void "tests" always return false.)
res
=
value
;
}
}
else
{
if
(
falseResult
!=
null
&&
falseResult
.
equals
(
res
))
{
res
=
null
;
}
else
if
(
isTest
)
{
// Tests return self by convention.
res
=
value
;
}
}
return
res
;
}
@Override
public
String
toString
()
{
return
"<Method>"
+
m
+
"</Method>"
;
}
}
return
new
MFilt
();
}
private
static
HashMap
<
Class
,
Object
>
zeroArgs
=
new
HashMap
<
Class
,
Object
>();
static
{
zeroArgs
.
put
(
Boolean
.
TYPE
,
Boolean
.
FALSE
);
zeroArgs
.
put
(
Character
.
TYPE
,
new
Character
((
char
)
0
));
zeroArgs
.
put
(
Byte
.
TYPE
,
new
Byte
((
byte
)
0
));
zeroArgs
.
put
(
Short
.
TYPE
,
new
Short
((
short
)
0
));
zeroArgs
.
put
(
Integer
.
TYPE
,
new
Integer
(
0
));
zeroArgs
.
put
(
Float
.
TYPE
,
new
Float
(
0
));
zeroArgs
.
put
(
Long
.
TYPE
,
new
Long
(
0
));
zeroArgs
.
put
(
Double
.
TYPE
,
new
Double
(
0
));
}
private
static
String
opToString
(
String
s1
,
Object
[]
s2
,
String
s3
)
{
StringBuilder
buf
=
new
StringBuilder
(
s1
);
for
(
int
i
=
0
;
i
<
s2
.
length
;
i
++)
{
if
(
s2
[
i
]
!=
null
)
{
buf
.
append
(
s2
[
i
]);
}
}
buf
.
append
(
s3
);
return
buf
.
toString
();
}
/** Call the filter on each list element x, and replace x with the
* resulting filter value e, or its parts.
* If e is null, keep x. (This eases use of partial-domain filters.)
* If e is a TokenList or an anonymous Element, add e's parts
* to the list instead of x.
* Otherwise, replace x by e.
* <p>
* The effect at each list position <code>n</code> may be expressed
* in terms of XMLKit.addContent as follows:
* <pre>
* Object e = f.filter(target.get(n));
* if (e != null) {
* target.remove(n);
* addContent(e, target.subList(n,n));
* }
* </pre>
* <p>
* Note: To force deletion of x, simply have the filter return
* Element.EMPTY or TokenList.EMPTY.
* To force null filter values to have this effect,
* use the expression: <code>or(f, emptyFilter())</code>.
*/
public
static
void
replaceAll
(
Filter
f
,
List
<
Object
>
target
)
{
for
(
ListIterator
<
Object
>
i
=
target
.
listIterator
();
i
.
hasNext
();)
{
Object
x
=
i
.
next
();
Object
fx
=
f
.
filter
(
x
);
if
(
fx
==
null
)
{
// Unliked addContent, a null is a no-op here.
// i.remove();
}
else
if
(
fx
instanceof
TokenList
)
{
TokenList
tl
=
(
TokenList
)
fx
;
if
(
tl
.
size
()
==
1
)
{
i
.
set
(
tl
);
}
else
{
i
.
remove
();
for
(
String
part
:
tl
)
{
i
.
add
(
part
);
}
}
}
else
if
(
fx
instanceof
Element
&&
((
Element
)
fx
).
isAnonymous
())
{
Element
anon
=
(
Element
)
fx
;
if
(
anon
.
size
()
==
1
)
{
i
.
set
(
anon
);
}
else
{
i
.
remove
();
for
(
Object
part
:
anon
)
{
i
.
add
(
part
);
}
}
}
else
if
(
x
!=
fx
)
{
i
.
set
(
fx
);
}
}
}
/** If e is null, return zero.
* If e is a TokenList or an anonymous Element, add e's parts
* to the collection, and return the number of parts.
* Otherwise, add e to the collection, and return one.
* If the collection reference is null, the result is as if
* a throwaway collection were used.
*/
public
static
int
addContent
(
Object
e
,
Collection
<
Object
>
sink
)
{
if
(
e
==
null
)
{
return
0
;
}
else
if
(
e
instanceof
TokenList
)
{
TokenList
tl
=
(
TokenList
)
e
;
if
(
sink
!=
null
)
{
sink
.
addAll
(
tl
);
}
return
tl
.
size
();
}
else
if
(
e
instanceof
Element
&&
((
Element
)
e
).
isAnonymous
())
{
Element
anon
=
(
Element
)
e
;
if
(
sink
!=
null
)
{
sink
.
addAll
(
anon
.
asList
());
}
return
anon
.
size
();
}
else
{
if
(
sink
!=
null
)
{
sink
.
add
(
e
);
}
return
1
;
}
}
static
Collection
<
Object
>
newCounterColl
()
{
return
new
AbstractCollection
<
Object
>()
{
int
size
;
public
int
size
()
{
return
size
;
}
@Override
public
boolean
add
(
Object
o
)
{
++
size
;
return
true
;
}
public
Iterator
<
Object
>
iterator
()
{
throw
new
UnsupportedOperationException
();
}
};
}
/** SAX2 document handler for building Element trees. */
private
static
class
Builder
implements
ContentHandler
,
LexicalHandler
{
/*, EntityResolver, DTDHandler, ErrorHandler*/
Collection
<
Object
>
sink
;
boolean
makeFrozen
;
boolean
tokenizing
;
Builder
(
Collection
<
Object
>
sink
,
boolean
tokenizing
,
boolean
makeFrozen
)
{
this
.
sink
=
sink
;
this
.
tokenizing
=
tokenizing
;
this
.
makeFrozen
=
makeFrozen
;
}
Object
[]
parts
=
new
Object
[
30
];
int
nparts
=
0
;
int
[]
attrBases
=
new
int
[
10
];
// index into parts
int
[]
elemBases
=
new
int
[
10
];
// index into parts
int
depth
=
-
1
;
// index into attrBases, elemBases
// Parts is organized this way:
// | name0 | akey aval ... | subelem ... | name1 | ... |
// The position of the first "akey" after name0 is attrBases[0].
// The position of the first "subelem" after name0 is elemBases[0].
// The position after the last part is always nparts.
int
mergeableToken
=
-
1
;
// index into parts of recent CharSequence
boolean
inCData
=
false
;
void
addPart
(
Object
x
)
{
//System.out.println("addPart "+x);
if
(
nparts
==
parts
.
length
)
{
Object
[]
newParts
=
new
Object
[
parts
.
length
*
2
];
System
.
arraycopy
(
parts
,
0
,
newParts
,
0
,
parts
.
length
);
parts
=
newParts
;
}
parts
[
nparts
++]
=
x
;
}
Object
getMergeableToken
()
{
if
(
mergeableToken
==
nparts
-
1
)
{
assert
(
parts
[
mergeableToken
]
instanceof
CharSequence
);
return
parts
[
nparts
-
1
];
}
else
{
return
null
;
}
}
void
clearMergeableToken
()
{
if
(
mergeableToken
>=
0
)
{
// Freeze temporary StringBuffers into strings.
assert
(
parts
[
mergeableToken
]
instanceof
CharSequence
);
parts
[
mergeableToken
]
=
parts
[
mergeableToken
].
toString
();
mergeableToken
=
-
1
;
}
}
void
setMergeableToken
()
{
if
(
mergeableToken
!=
nparts
-
1
)
{
clearMergeableToken
();
mergeableToken
=
nparts
-
1
;
assert
(
parts
[
mergeableToken
]
instanceof
CharSequence
);
}
}
// ContentHandler callbacks
public
void
startElement
(
String
ns
,
String
localName
,
String
name
,
Attributes
atts
)
{
clearMergeableToken
();
addPart
(
name
.
intern
());
++
depth
;
if
(
depth
==
attrBases
.
length
)
{
int
oldlen
=
depth
;
int
newlen
=
depth
*
2
;
int
[]
newAB
=
new
int
[
newlen
];
int
[]
newEB
=
new
int
[
newlen
];
System
.
arraycopy
(
attrBases
,
0
,
newAB
,
0
,
oldlen
);
System
.
arraycopy
(
elemBases
,
0
,
newEB
,
0
,
oldlen
);
attrBases
=
newAB
;
elemBases
=
newEB
;
}
attrBases
[
depth
]
=
nparts
;
// Collect attributes.
int
na
=
atts
.
getLength
();
for
(
int
k
=
0
;
k
<
na
;
k
++)
{
addPart
(
atts
.
getQName
(
k
).
intern
());
addPart
(
atts
.
getValue
(
k
));
}
// Get ready to collect elements.
elemBases
[
depth
]
=
nparts
;
}
public
void
endElement
(
String
ns
,
String
localName
,
String
name
)
{
assert
(
depth
>=
0
);
clearMergeableToken
();
int
ebase
=
elemBases
[
depth
];
int
elen
=
nparts
-
ebase
;
int
abase
=
attrBases
[
depth
];
int
alen
=
ebase
-
abase
;
int
nbase
=
abase
-
1
;
int
cap
=
alen
+
(
makeFrozen
?
0
:
NEED_SLOP
)
+
elen
;
Element
e
=
new
Element
((
String
)
parts
[
nbase
],
elen
,
cap
);
// Set up attributes.
for
(
int
k
=
0
;
k
<
alen
;
k
+=
2
)
{
e
.
parts
[
cap
-
k
-
2
]
=
parts
[
abase
+
k
+
0
];
e
.
parts
[
cap
-
k
-
1
]
=
parts
[
abase
+
k
+
1
];
}
// Set up sub-elements.
System
.
arraycopy
(
parts
,
ebase
,
e
.
parts
,
0
,
elen
);
// Back out of this level.
--
depth
;
nparts
=
nbase
;
assert
(
e
.
isFrozen
()
==
makeFrozen
);
assert
(
e
.
size
()
==
elen
);
assert
(
e
.
attrSize
()
*
2
==
alen
);
if
(
depth
>=
0
)
{
addPart
(
e
);
}
else
{
sink
.
add
(
e
);
}
}
public
void
startCDATA
()
{
inCData
=
true
;
}
public
void
endCDATA
()
{
inCData
=
false
;
}
public
void
characters
(
char
[]
buf
,
int
off
,
int
len
)
{
boolean
headSpace
=
false
;
boolean
tailSpace
=
false
;
int
firstLen
;
if
(
tokenizing
&&
!
inCData
)
{
// Strip unquoted blanks.
while
(
len
>
0
&&
isWhitespace
(
buf
[
off
]))
{
headSpace
=
true
;
++
off
;
--
len
;
}
if
(
len
==
0
)
{
tailSpace
=
true
;
// it is all space
}
while
(
len
>
0
&&
isWhitespace
(
buf
[
off
+
len
-
1
]))
{
tailSpace
=
true
;
--
len
;
}
firstLen
=
0
;
while
(
firstLen
<
len
&&
!
isWhitespace
(
buf
[
off
+
firstLen
]))
{
++
firstLen
;
}
}
else
{
firstLen
=
len
;
}
if
(
headSpace
)
{
clearMergeableToken
();
}
boolean
mergeAtEnd
=
!
tailSpace
;
// If buffer was empty, or had only ignorable blanks, do nothing.
if
(
len
==
0
)
{
return
;
}
// Decide whether to merge some of these chars into a previous token.
Object
prev
=
getMergeableToken
();
if
(
prev
instanceof
StringBuffer
)
{
((
StringBuffer
)
prev
).
append
(
buf
,
off
,
firstLen
);
}
else
if
(
prev
==
null
)
{
addPart
(
new
String
(
buf
,
off
,
firstLen
));
}
else
{
// Merge two strings.
String
prevStr
=
prev
.
toString
();
StringBuffer
prevBuf
=
new
StringBuffer
(
prevStr
.
length
()
+
firstLen
);
prevBuf
.
append
(
prevStr
);
prevBuf
.
append
(
buf
,
off
,
firstLen
);
if
(
mergeAtEnd
&&
len
==
firstLen
)
{
// Replace previous string with new StringBuffer.
parts
[
nparts
-
1
]
=
prevBuf
;
}
else
{
// Freeze it now.
parts
[
nparts
-
1
]
=
prevBuf
.
toString
();
}
}
off
+=
firstLen
;
len
-=
firstLen
;
if
(
len
>
0
)
{
// Appended only the first token.
clearMergeableToken
();
// Add the rest as separate parts.
while
(
len
>
0
)
{
while
(
len
>
0
&&
isWhitespace
(
buf
[
off
]))
{
++
off
;
--
len
;
}
int
nextLen
=
0
;
while
(
nextLen
<
len
&&
!
isWhitespace
(
buf
[
off
+
nextLen
]))
{
++
nextLen
;
}
assert
(
nextLen
>
0
);
addPart
(
new
String
(
buf
,
off
,
nextLen
));
off
+=
nextLen
;
len
-=
nextLen
;
}
}
if
(
mergeAtEnd
)
{
setMergeableToken
();
}
}
public
void
ignorableWhitespace
(
char
[]
buf
,
int
off
,
int
len
)
{
clearMergeableToken
();
if
(
false
)
{
characters
(
buf
,
off
,
len
);
clearMergeableToken
();
}
}
public
void
comment
(
char
[]
buf
,
int
off
,
int
len
)
{
addPart
(
new
Special
(
"<!-- -->"
,
new
String
(
buf
,
off
,
len
)));
}
public
void
processingInstruction
(
String
name
,
String
instruction
)
{
Element
pi
=
new
Element
(
name
);
pi
.
add
(
instruction
);
addPart
(
new
Special
(
"<? ?>"
,
pi
));
}
public
void
skippedEntity
(
String
name
)
{
}
public
void
startDTD
(
String
name
,
String
publicId
,
String
systemId
)
{
}
public
void
endDTD
()
{
}
public
void
startEntity
(
String
name
)
{
}
public
void
endEntity
(
String
name
)
{
}
public
void
setDocumentLocator
(
org
.
xml
.
sax
.
Locator
locator
)
{
}
public
void
startDocument
()
{
}
public
void
endDocument
()
{
}
public
void
startPrefixMapping
(
String
prefix
,
String
uri
)
{
}
public
void
endPrefixMapping
(
String
prefix
)
{
}
}
/** Produce a ContentHandler for use with an XML parser.
* The object is <em>also</em> a LexicalHandler.
* Every top-level Element produced will get added to sink.
* All elements will be frozen iff makeFrozen is true.
*/
public
static
ContentHandler
makeBuilder
(
Collection
<
Object
>
sink
,
boolean
tokenizing
,
boolean
makeFrozen
)
{
return
new
Builder
(
sink
,
tokenizing
,
makeFrozen
);
}
public
static
ContentHandler
makeBuilder
(
Collection
<
Object
>
sink
,
boolean
tokenizing
)
{
return
new
Builder
(
sink
,
tokenizing
,
false
);
}
public
static
ContentHandler
makeBuilder
(
Collection
<
Object
>
sink
)
{
return
makeBuilder
(
sink
,
false
,
false
);
}
public
static
Element
readFrom
(
Reader
in
,
boolean
tokenizing
,
boolean
makeFrozen
)
throws
IOException
{
Element
sink
=
new
Element
();
ContentHandler
b
=
makeBuilder
(
sink
.
asList
(),
tokenizing
,
makeFrozen
);
XMLReader
parser
;
try
{
parser
=
org
.
xml
.
sax
.
helpers
.
XMLReaderFactory
.
createXMLReader
();
}
catch
(
SAXException
ee
)
{
throw
new
Error
(
ee
);
}
//parser.setFastStandalone(true);
parser
.
setContentHandler
(
b
);
try
{
parser
.
setProperty
(
"http://xml.org/sax/properties/lexical-handler"
,
(
LexicalHandler
)
b
);
}
catch
(
SAXException
ee
)
{
// Ignore. We will miss the comments and whitespace.
}
try
{
parser
.
parse
(
new
InputSource
(
in
));
}
catch
(
SAXParseException
ee
)
{
throw
new
RuntimeException
(
"line "
+
ee
.
getLineNumber
()
+
" col "
+
ee
.
getColumnNumber
()
+
": "
,
ee
);
}
catch
(
SAXException
ee
)
{
throw
new
RuntimeException
(
ee
);
}
switch
(
sink
.
size
())
{
case
0
:
return
null
;
case
1
:
if
(
sink
.
get
(
0
)
instanceof
Element
)
{
return
(
Element
)
sink
.
get
(
0
);
}
// fall through
default
:
if
(
makeFrozen
)
{
sink
.
shallowFreeze
();
}
return
sink
;
}
}
public
static
Element
readFrom
(
Reader
in
,
boolean
tokenizing
)
throws
IOException
{
return
readFrom
(
in
,
tokenizing
,
false
);
}
public
static
Element
readFrom
(
Reader
in
)
throws
IOException
{
return
readFrom
(
in
,
false
,
false
);
}
public
static
void
prettyPrintTo
(
OutputStream
out
,
Element
e
)
throws
IOException
{
prettyPrintTo
(
new
OutputStreamWriter
(
out
),
e
);
}
public
static
void
prettyPrintTo
(
Writer
out
,
Element
e
)
throws
IOException
{
Printer
pr
=
new
Printer
(
out
);
pr
.
pretty
=
true
;
pr
.
print
(
e
);
}
static
class
Outputter
{
ContentHandler
ch
;
LexicalHandler
lh
;
Outputter
(
ContentHandler
ch
,
LexicalHandler
lh
)
{
this
.
ch
=
ch
;
this
.
lh
=
lh
;
}
AttributesImpl
atts
=
new
AttributesImpl
();
// handy
void
output
(
Object
x
)
throws
SAXException
{
// Cf. jdom.org/jdom-b8/src/java/org/jdom/output/SAXOutputter.java
if
(
x
instanceof
Element
)
{
Element
e
=
(
Element
)
x
;
atts
.
clear
();
for
(
int
asize
=
e
.
attrSize
(),
k
=
0
;
k
<
asize
;
k
++)
{
String
key
=
e
.
getAttrName
(
k
);
String
val
=
e
.
getAttr
(
k
);
atts
.
addAttribute
(
""
,
""
,
key
,
"CDATA"
,
val
);
}
ch
.
startElement
(
""
,
""
,
e
.
getName
(),
atts
);
for
(
int
i
=
0
;
i
<
e
.
size
();
i
++)
{
output
(
e
.
get
(
i
));
}
ch
.
endElement
(
""
,
""
,
e
.
getName
());
}
else
if
(
x
instanceof
Special
)
{
Special
sp
=
(
Special
)
x
;
if
(
sp
.
kind
.
startsWith
(
"<!--"
))
{
char
[]
chars
=
sp
.
value
.
toString
().
toCharArray
();
lh
.
comment
(
chars
,
0
,
chars
.
length
);
}
else
if
(
sp
.
kind
.
startsWith
(
"<?"
))
{
Element
nameInstr
=
(
Element
)
sp
.
value
;
ch
.
processingInstruction
(
nameInstr
.
name
,
nameInstr
.
get
(
0
).
toString
());
}
else
{
// drop silently
}
}
else
{
char
[]
chars
=
x
.
toString
().
toCharArray
();
ch
.
characters
(
chars
,
0
,
chars
.
length
);
}
}
}
public
static
class
Printer
{
public
Writer
w
;
public
boolean
tokenizing
;
public
boolean
pretty
;
public
boolean
abbreviated
;
// nonstandard format cuts down on noise
int
depth
=
0
;
boolean
prevStr
;
int
tabStop
=
2
;
public
Printer
(
Writer
w
)
{
this
.
w
=
w
;
}
public
Printer
()
{
StringWriter
sw
=
new
StringWriter
();
this
.
w
=
sw
;
}
public
String
nextString
()
{
StringBuffer
sb
=
((
StringWriter
)
w
).
getBuffer
();
String
next
=
sb
.
toString
();
sb
.
setLength
(
0
);
// reset
return
next
;
}
void
indent
(
int
depth
)
throws
IOException
{
if
(
depth
>
0
)
{
w
.
write
(
"\n"
);
}
int
nsp
=
tabStop
*
depth
;
while
(
nsp
>
0
)
{
String
s
=
" "
;
String
t
=
s
.
substring
(
0
,
nsp
<
s
.
length
()
?
nsp
:
s
.
length
());
w
.
write
(
t
);
nsp
-=
t
.
length
();
}
}
public
void
print
(
Element
e
)
throws
IOException
{
if
(
e
.
isAnonymous
())
{
printParts
(
e
);
return
;
}
printRecursive
(
e
);
}
public
void
println
(
Element
e
)
throws
IOException
{
print
(
e
);
w
.
write
(
"\n"
);
w
.
flush
();
}
public
void
printRecursive
(
Element
e
)
throws
IOException
{
boolean
indented
=
false
;
if
(
pretty
&&
!
prevStr
&&
e
.
size
()
+
e
.
attrSize
()
>
0
)
{
indent
(
depth
);
indented
=
true
;
}
w
.
write
(
"<"
);
w
.
write
(
e
.
name
);
for
(
int
asize
=
e
.
attrSize
(),
k
=
0
;
k
<
asize
;
k
++)
{
String
key
=
e
.
getAttrName
(
k
);
String
val
=
e
.
getAttr
(
k
);
w
.
write
(
" "
);
w
.
write
(
key
);
w
.
write
(
"="
);
if
(
val
==
null
)
{
w
.
write
(
"null"
);
// Should not happen....
}
else
if
(
val
.
indexOf
(
"\""
)
<
0
)
{
w
.
write
(
"\""
);
writeToken
(
val
,
'"'
,
w
);
w
.
write
(
"\""
);
}
else
{
w
.
write
(
"'"
);
writeToken
(
val
,
'\''
,
w
);
w
.
write
(
"'"
);
}
}
if
(
e
.
size
()
==
0
)
{
w
.
write
(
"/>"
);
}
else
{
++
depth
;
if
(
abbreviated
)
{
w
.
write
(
"/"
);
}
else
{
w
.
write
(
">"
);
}
prevStr
=
false
;
printParts
(
e
);
if
(
abbreviated
)
{
w
.
write
(
">"
);
}
else
{
if
(
indented
&&
!
prevStr
)
{
indent
(
depth
-
1
);
}
w
.
write
(
"</"
);
w
.
write
(
e
.
name
);
w
.
write
(
">"
);
}
prevStr
=
false
;
--
depth
;
}
}
private
void
printParts
(
Element
e
)
throws
IOException
{
for
(
int
i
=
0
;
i
<
e
.
size
();
i
++)
{
Object
x
=
e
.
get
(
i
);
if
(
x
instanceof
Element
)
{
printRecursive
((
Element
)
x
);
prevStr
=
false
;
}
else
if
(
x
instanceof
Special
)
{
w
.
write
(((
Special
)
x
).
toString
());
prevStr
=
false
;
}
else
{
String
s
=
String
.
valueOf
(
x
);
if
(
pretty
)
{
s
=
s
.
trim
();
if
(
s
.
length
()
==
0
)
{
continue
;
}
}
if
(
prevStr
)
{
w
.
write
(
' '
);
}
writeToken
(
s
,
tokenizing
?
' '
:
(
char
)
-
1
,
w
);
prevStr
=
true
;
}
if
(
pretty
&&
depth
==
0
)
{
w
.
write
(
"\n"
);
prevStr
=
false
;
}
}
}
}
public
static
void
output
(
Object
e
,
ContentHandler
ch
,
LexicalHandler
lh
)
throws
SAXException
{
new
Outputter
(
ch
,
lh
).
output
(
e
);
}
public
static
void
output
(
Object
e
,
ContentHandler
ch
)
throws
SAXException
{
if
(
ch
instanceof
LexicalHandler
)
{
output
(
e
,
ch
,
(
LexicalHandler
)
ch
);
}
else
{
output
(
e
,
ch
,
null
);
}
}
public
static
void
writeToken
(
String
val
,
char
quote
,
Writer
w
)
throws
IOException
{
int
len
=
val
.
length
();
boolean
canUseCData
=
(
quote
!=
'"'
&&
quote
!=
'\''
);
int
vpos
=
0
;
for
(
int
i
=
0
;
i
<
len
;
i
++)
{
char
ch
=
val
.
charAt
(
i
);
if
((
ch
==
'<'
||
ch
==
'&'
||
ch
==
'>'
||
ch
==
quote
)
||
(
quote
==
' '
&&
isWhitespace
(
ch
)))
{
if
(
canUseCData
)
{
assert
(
vpos
==
0
);
writeCData
(
val
,
w
);
return
;
}
else
{
if
(
vpos
<
i
)
{
w
.
write
(
val
,
vpos
,
i
-
vpos
);
}
String
esc
;
switch
(
ch
)
{
case
'&'
:
esc
=
"&"
;
break
;
case
'<'
:
esc
=
"<"
;
break
;
case
'\''
:
esc
=
"'"
;
break
;
case
'"'
:
esc
=
"""
;
break
;
case
'>'
:
esc
=
">"
;
break
;
default
:
esc
=
"&#"
+
(
int
)
ch
+
";"
;
break
;
}
w
.
write
(
esc
);
vpos
=
i
+
1
;
// skip escaped char
}
}
}
// write the unquoted tail
w
.
write
(
val
,
vpos
,
val
.
length
()
-
vpos
);
}
public
static
void
writeCData
(
String
val
,
Writer
w
)
throws
IOException
{
String
begCData
=
"<![CDATA["
;
String
endCData
=
"]]>"
;
w
.
write
(
begCData
);
for
(
int
vpos
=
0
,
split
;;
vpos
=
split
)
{
split
=
val
.
indexOf
(
endCData
,
vpos
);
if
(
split
<
0
)
{
w
.
write
(
val
,
vpos
,
val
.
length
()
-
vpos
);
w
.
write
(
endCData
);
return
;
}
split
+=
2
;
// bisect the "]]>" goo
w
.
write
(
val
,
vpos
,
split
-
vpos
);
w
.
write
(
endCData
);
w
.
write
(
begCData
);
}
}
public
static
TokenList
convertToList
(
String
str
)
{
if
(
str
==
null
)
{
return
null
;
}
return
new
TokenList
(
str
);
}
/** If str is null, empty, or blank, returns null.
* Otherwise, return a Double if str spells a double value and contains '.' or 'e'.
* Otherwise, return an Integer if str spells an int value.
* Otherwise, return a Long if str spells a long value.
* Otherwise, return a BigInteger for the string.
* Otherwise, throw NumberFormatException.
*/
public
static
Number
convertToNumber
(
String
str
)
{
if
(
str
==
null
)
{
return
null
;
}
str
=
str
.
trim
();
if
(
str
.
length
()
==
0
)
{
return
null
;
}
if
(
str
.
indexOf
(
'.'
)
>=
0
||
str
.
indexOf
(
'e'
)
>=
0
||
str
.
indexOf
(
'E'
)
>=
0
)
{
return
Double
.
valueOf
(
str
);
}
try
{
long
lval
=
Long
.
parseLong
(
str
);
if
(
lval
==
(
int
)
lval
)
{
// Narrow to Integer, if possible.
return
new
Integer
((
int
)
lval
);
}
return
new
Long
(
lval
);
}
catch
(
NumberFormatException
ee
)
{
// Could not represent it as a long.
return
new
java
.
math
.
BigInteger
(
str
,
10
);
}
}
public
static
Number
convertToNumber
(
String
str
,
Number
dflt
)
{
Number
n
=
convertToNumber
(
str
);
return
(
n
==
null
)
?
dflt
:
n
;
}
public
static
long
convertToLong
(
String
str
)
{
return
convertToLong
(
str
,
0
);
}
public
static
long
convertToLong
(
String
str
,
long
dflt
)
{
Number
n
=
convertToNumber
(
str
);
return
(
n
==
null
)
?
dflt
:
n
.
longValue
();
}
public
static
double
convertToDouble
(
String
str
)
{
return
convertToDouble
(
str
,
0
);
}
public
static
double
convertToDouble
(
String
str
,
double
dflt
)
{
Number
n
=
convertToNumber
(
str
);
return
(
n
==
null
)
?
dflt
:
n
.
doubleValue
();
}
// Testing:
public
static
void
main
(
String
...
av
)
throws
Exception
{
Element
.
method
(
"getAttr"
);
//new org.jdom.input.SAXBuilder().build(file).getRootElement();
//jdom.org/jdom-b8/src/java/org/jdom/input/SAXBuilder.java
//Document build(InputSource in) throws JDOMException
int
reps
=
0
;
boolean
tokenizing
=
false
;
boolean
makeFrozen
=
false
;
if
(
av
.
length
>
0
)
{
tokenizing
=
true
;
try
{
reps
=
Integer
.
parseInt
(
av
[
0
]);
}
catch
(
NumberFormatException
ee
)
{
}
}
Reader
inR
=
new
BufferedReader
(
new
InputStreamReader
(
System
.
in
));
String
inS
=
null
;
if
(
reps
>
1
)
{
StringWriter
inBufR
=
new
StringWriter
(
1
<<
14
);
char
[]
cbuf
=
new
char
[
1024
];
for
(
int
nr
;
(
nr
=
inR
.
read
(
cbuf
))
>=
0
;)
{
inBufR
.
write
(
cbuf
,
0
,
nr
);
}
inS
=
inBufR
.
toString
();
inR
=
new
StringReader
(
inS
);
}
Element
e
=
XMLKit
.
readFrom
(
inR
,
tokenizing
,
makeFrozen
);
System
.
out
.
println
(
"transform = "
+
e
.
findAll
(
methodFilter
(
Element
.
method
(
"prettyString"
))));
System
.
out
.
println
(
"transform = "
+
e
.
findAll
(
testMethodFilter
(
Element
.
method
(
"hasText"
))));
long
tm0
=
0
;
int
warmup
=
10
;
for
(
int
i
=
1
;
i
<
reps
;
i
++)
{
inR
=
new
StringReader
(
inS
);
readFrom
(
inR
,
tokenizing
,
makeFrozen
);
if
(
i
==
warmup
)
{
System
.
out
.
println
(
"Start timing..."
);
tm0
=
System
.
currentTimeMillis
();
}
}
if
(
tm0
!=
0
)
{
long
tm1
=
System
.
currentTimeMillis
();
System
.
out
.
println
((
reps
-
warmup
)
+
" in "
+
(
tm1
-
tm0
)
+
" ms"
);
}
System
.
out
.
println
(
"hashCode = "
+
e
.
hashCode
());
String
eStr
=
e
.
toString
();
System
.
out
.
println
(
eStr
);
Element
e2
=
readFrom
(
new
StringReader
(
eStr
),
tokenizing
,
!
makeFrozen
);
System
.
out
.
println
(
"hashCode = "
+
e2
.
hashCode
());
if
(!
e
.
equals
(
e2
))
{
System
.
out
.
println
(
"**** NOT EQUAL 1\n"
+
e2
);
}
e
=
e
.
deepCopy
();
System
.
out
.
println
(
"hashCode = "
+
e
.
hashCode
());
if
(!
e
.
equals
(
e2
))
{
System
.
out
.
println
(
"**** NOT EQUAL 2"
);
}
e2
.
shallowFreeze
();
System
.
out
.
println
(
"hashCode = "
+
e2
.
hashCode
());
if
(!
e
.
equals
(
e2
))
{
System
.
out
.
println
(
"**** NOT EQUAL 3"
);
}
if
(
false
)
{
System
.
out
.
println
(
e
);
}
else
{
prettyPrintTo
(
new
OutputStreamWriter
(
System
.
out
),
e
);
}
System
.
out
.
println
(
"Flat text:|"
+
e
.
getFlatText
()
+
"|"
);
{
System
.
out
.
println
(
"<!--- Sorted: --->"
);
Element
ce
=
e
.
copyContentOnly
();
ce
.
sort
();
prettyPrintTo
(
new
OutputStreamWriter
(
System
.
out
),
ce
);
}
{
System
.
out
.
println
(
"<!--- Trimmed: --->"
);
Element
tr
=
e
.
deepCopy
();
findInTree
(
testMethodFilter
(
Element
.
method
(
"trimText"
))).
filter
(
tr
);
System
.
out
.
println
(
tr
);
}
{
System
.
out
.
println
(
"<!--- Unstrung: --->"
);
Element
us
=
e
.
deepCopy
();
int
nr
=
us
.
retainAllInTree
(
elementFilter
(),
null
);
System
.
out
.
println
(
"nr="
+
nr
);
System
.
out
.
println
(
us
);
}
{
System
.
out
.
println
(
"<!--- Rollup: --->"
);
Element
ru
=
e
.
deepCopy
();
Filter
makeAnonF
=
methodFilter
(
Element
.
method
(
"setName"
),
new
Object
[]{
ANON_NAME
});
Filter
testSizeF
=
testMethodFilter
(
Element
.
method
(
"size"
));
Filter
walk
=
replaceInTree
(
and
(
not
(
elementFilter
()),
emptyFilter
()),
and
(
testSizeF
,
makeAnonF
));
ru
=
(
Element
)
walk
.
filter
(
ru
);
//System.out.println(ru);
prettyPrintTo
(
new
OutputStreamWriter
(
System
.
out
),
ru
);
}
}
static
boolean
isWhitespace
(
char
c
)
{
switch
(
c
)
{
case
0x20
:
case
0x09
:
case
0x0D
:
case
0x0A
:
return
true
;
}
return
false
;
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录