虽然论文已经写得挺明白的了(老师学长如是说道),但是本垃圾还是看得五步一百度,十步一谷歌…
菜死了,理解速度还跟不上,有时候一段文字需要查好几个小时才看明白…
XML格式
XML格式是一种用语传输以及存储数据的文件格式,特点如下:
- 纯文本,使用UTF-8编码
- 可嵌套,可以充分表示结构化的数据(可以理解为树状结构)
固定结构
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE note SYSTEM "book.dtd">
<book id="1">
<name>Java核心技术</name>
<author>Cay S. Horstmann</author>
<isbn lang="CN">1234567</isbn>
<tags>
<tag>Java</tag>
<tag>Network</tag>
</tags>
<pubDate/>
</book>
- 前两行
version
encoding
dtd
(文档定义类型Document Type Definition)都是可以选择的参数 book
标签为文档的根节点,一般来说根节点都只有一个- 格式要求完全正确才能被解析,任何没有正确嵌套的标签都会导致错误
DTD文档
上文中的第二行,是把这个xml文件交给了book.dtd
文档来验证规则,在DTD文档的内部,可以定义一些xml需要满足的规则,如果不满足就会判定为xml数据结构不符要求。如,可以要求根元素必须是book,还可以要求isbn元素必须有lang元素,甚至可以要求book下的子元素必须含有某些指定元素。
转义字符
由于文档格式中大量使用了一些字符,所以…
字符 | 表示 |
---|---|
< | &It; |
> | > |
& | & |
" | " |
' | ' |
相关拓展
- DTD和XSD:验证XML结构和数据是否有效;
- Namespace:XML节点和属性的名字空间;
- XSLT:把XML转化为另一种文本;
- XPath:一种XML节点查询语言;
- …
DOM API
DOM将xml文档一次读入,并把内容转换为树状结构放在内存中,在后期调用中十分方便,但是极为消耗内存。对应的也有以流形式读取xml的SAX API,通过一些些奇奇怪怪的方式进行使用(事件回调?)
InputStream input = Main.class.getResourceAsStream("/book.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(input);
通过对doc的调用就可以进行读取xml中的数据,调用printNode(doc, 0)
void printNode(Node n, int indent) { // 输出xml的内容
for (int i = 0; i < indent; i++) { // 打出缩进
System.out.print(' ');
}
switch (n.getNodeType()) {
case Node.DOCUMENT_NODE: // Document节点
System.out.println("Document: " + n.getNodeName());
break;
case Node.ELEMENT_NODE: // 元素节点
System.out.println("Element: " + n.getNodeName());
break;
case Node.TEXT_NODE: // 文本
System.out.println("Text: " + n.getNodeName() + " = " + n.getNodeValue());
break;
case Node.ATTRIBUTE_NODE: // 属性
System.out.println("Attr: " + n.getNodeName() + " = " + n.getNodeValue());
break;
default: // 其他
System.out.println("NodeType: " + n.getNodeType() + ", NodeName: " + n.getNodeName());
}
for (Node child = n.getFirstChild(); child != null; child = child.getNextSibling()) {
printNode(child, indent + 1);
}
}
单元测试 Assume 与 Assert
JUnit 单元测试
JUnit会吧带有@Test
的方法识别为测试方法,测试类一般明明为**Test
,测试方法一般命名为test**
。常用断言方法如下:
assertTrue()
: 期待结果为true
assertFalse()
: 期待结果为false
assertNotNull()
: 期待结果为非null
assertArrayEquals()
: 期待结果为数组并与期望数组每个元素的值均相等
导入JUnit之后,可以在Eclipse中快速完成单元测试,具体内容
Assume
对方法的参数进行合法性校验,如果校验不合格则直接抛异常,而不执行测试,默认的BlockJUnit4ClassRunner
及其子类会捕获这个异常并跳过当前测试,如果使用自定义的Runner则无法保证行为,视Runner的实现而定。
如果有的时候必须规定具备某个条件才允许测试,但又不判断为fail,则可以使用:
@Before
public void setUp() {
String versionNumber = "7"; //Get it from configuration on runtime
Assume.assumeTrue(Integer.valueOf(versionNumber) == 7);
}
@Test
public void testIfVersioonGreaterThan4() {
System.out.println("Test executed");
}
当检查versionNumber不是7的时候,跳过此次测试,并throw一个可被捕获的异常。
Tutorial 1
预设问题
闰年判定
在400年闰年判定上预设问题。
时间比较
没有考虑时区,直接比较了时间。
生成器
JQF利用junit-quickcheck
的框架生成结构化输入,为了生成T
类型的输入,就必须要有一个Generator<T>
的拓展类,生成这样的拓展类需要一些生成方法,也就是下文代码中的T_Generator
的方法,然后就可以通过拓展类批量生成内容参与测试。
import java.util.GregorianCalendar;
import java.util.TimeZone;
import com.pholser.junit.quickcheck.generator.GenerationStatus;
import com.pholser.junit.quickcheck.generator.Generator;
import com.pholser.junit.quickcheck.random.SourceOfRandomness;
// 以上三段代码需要后面仔细读一下
import static java.util.GregorianCalendar.*; // 导入GregorianCalendar的静态变量
public class CalendarGenerator extends Generator<GregorianCalendar> {
// 继承 GregorianCalendar 的构造方法
public CalendarGenerator() {
super(GregorianCalendar.class);
}
// 生成一个随机的时间用于测试
@Override
public GregorianCalendar generate(SourceOfRandomness random, GenerationStatus __ignore__) {
// cal 实例存储随机生成的时间
GregorianCalendar cal = new GregorianCalendar();
cal.setLenient(true); // 允许了时间的自动跳转,即可以从四月三十一号自动转换为五月一号
// 随机生成日期
cal.set(DAY_OF_MONTH, random.nextInt(31) + 1);
cal.set(MONTH, random.nextInt(12) + 1);
cal.set(YEAR, random.nextInt(cal.getMinimum(YEAR), cal.getMaximum(YEAR)));
// 随机生成时间
if (random.nextBoolean()) {
cal.set(HOUR, random.nextInt(24));
cal.set(MINUTE, random.nextInt(60));
cal.set(SECOND, random.nextInt(60));
}
// 随机生成时区
String[] allTzIds = TimeZone.getAvailableIDs(); // 获取时区列表(e.g. "America/Los_Angeles")
String tzId = random.choose(allTzIds); // 随机抽取一个时区
TimeZone tz = TimeZone.getTimeZone(tzId); // 把String时区转换成时区对象
cal.setTimeZone(tz);
return cal;
}
}
Test代码
import java.util.*;
import static java.util.GregorianCalendar.*;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
// 感觉以下的内容好像都要以后读一读,感觉用起来不明不白的
import org.junit.runner.RunWith;
import com.pholser.junit.quickcheck.*;
import com.pholser.junit.quickcheck.generator.*;
import edu.berkeley.cs.jqf.fuzz.*;
@RunWith(JQF.class) // 告知JUnit使用JQF来进行测试
public class CalendarTest {
@Fuzz // 标记测试用的代码,方便JQF找到,下文FROM标记了测试数据的来源
public void testLeapYear(@From(CalendarGenerator.class) GregorianCalendar cal) {
// 筛选掉那些不是二月29的时间,这样留下的日期都是闰年了
assumeTrue(cal.get(MONTH) == FEBRUARY);
assumeTrue(cal.get(DAY_OF_MONTH) == 29);
assertTrue(cal.get(YEAR) + " should be a leap year", CalendarLogic.isLeapYear(cal));
}
@Fuzz
public void testCompare(@Size(max=100) List<@From(CalendarGenerator.class) GregorianCalendar> cals) {
Collections.sort(cals, CalendarLogic::compare); // 调用有预设问题的逻辑
// 根据预设好的逻辑,应该是升序排序
for (int i = 1; i < cals.size(); i++) {
Calendar c1 = cals.get(i-1);
Calendar c2 = cals.get(i);
assumeFalse(c1.equals(c2)); // 排除掉相等的情况
assertTrue(c1 + " should be before " + c2, c1.before(c2)); // 通过正确的方法检验
}
}
}
比较迷惑的是文档中在最后一段的话,为什么这个From的标签没有给List而是给了List的元素,我理解的是,元素才是由CalendarGenerator.class
生成的,而把这个元素放到List中进行调用的话,在它Generator的父类中就能自动生成一个包含这些生成元素的List,而Size标签就是为了限制这个随机生成的List大小的。
编译内容与模糊测试及结果
Javac 编译指令参数 -cp
如果你这个java文件中,引入其他的jar包,需要用到 -cp
参数,全称是classpath
javac -cp .:$(/path/to/jqf/scripts/classpath.sh) CalendarLogic.java CalendarGenerator.java CalendarTest.java
对于.:$(/path/to/jqf/scripts/classpath.sh)
解释:
- $(): 执行括号中的内容,然后将执行所得结果填充到该位置
JQF/scripts/classpath.sh
是获取当前JQF所依赖的所有JAR包:
并列当前目录下的和classpath.sh
获取的内容
通过JUnit和QuickCheck进行测试
java -cp .:$(/path/to/jqf/scripts/classpath.sh) org.junit.runner.JUnitCore CalendarTest
-cp
参数的意义同上,但是这次是运行过程中依赖的jar,意义上也许略有不同?
可能会获得错误消息Assumption is too strong; too many inputs discarded
(反正我试了几次没试出来),原因是随机生成的时间落到闰年的二月二十九的概率太小了,可能结束之后都找不到一个能通过Assume
的数据…(好惨)
命令行开启模糊测试
/path/to/jqf/bin/jqf-zest -c .:$(/path/to/jqf/scripts/classpath.sh) CalendarTest testLeapYear
这是调用了jqf-zest
,-c参数是提供了配置类的路径,配置类还是从classpath.sh
中获取。通过testLeapYear
方法,测试了Calendartest
的内容,命令行看起来比较混乱,以后还是用mvn插件吧…运行起来之后如下图,加一些注释
Zest: Validity Fuzzing with Parametric Generators
-------------------------------------------------
# 通过通过 testLeapYear 方法,测试了 Calendartest 的内容
Test name: CalendarTest#testLeapYear
# 模糊测试结果的保存路径,路径下既有成功的数据,也有失败的的数据
Results directory: /path/to/tutorial/fuzz-results
# 模糊测试时长,可以在mvn插件中指定测试时长
Elapsed time: 5s (no time limit)
# 已经进行德测试数量,即总投入的数据量
Number of executions: 5,856
# 总量中有效的数据以及占比,因为那个闰年的条件太苛刻,所以有效比看起来不太高
Valid inputs: 271 (4.63%)
Cycles completed: 4
# 导致错误的输入的数量(可能导致的错误都是同一种类型)
Unique failures: 1
# 模糊测试中S集的大小
Queue size: 3 (3 favored last cycle)
Current parent input: 0 (favored) {240/240 mutations}
Execution speed: 1,300/sec now | 1,098/sec overall
# 覆盖率
Total coverage: 8 (0.01% of map)
Valid coverage: 6 (0.01% of map)
命令行查看模糊测试结果
/path/to/jqf/bin/jqf-repro -c .:$(/path/to/jqf/scripts/classpath.sh) CalendarTest testLeapYear
具体内容的解释和上文一样,不做注解了
Tutorial 2
上一个Tutorial使用的命令行操作JQF+Zest的工具,这个用了更加简洁的mvn插件。
依赖以及插件配置(通过pom.xml)
调用插件的话,需要把测试用的文件以及生成器文件放在一个maven的框架中,并在maven框架的pom.xml
中增加插件,具体信息如下。
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>examples</groupId>
<artifactId>zest-tutorial</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- Google Closure: 被检测的文件 -->
<dependency>
<groupId>com.google.javascript</groupId>
<artifactId>closure-compiler</artifactId>
<version>v20180204</version>
<!-- 使用阶段:test:仅参与测试相关的内容,包括测试的编译和执行 -->
<scope>test</scope>
</dependency>
<!-- JQF: @Fuzz标签要用到的依赖 -->
<dependency>
<groupId>edu.berkeley.cs.jqf</groupId>
<artifactId>jqf-fuzz</artifactId>
<!-- 确保安装最新版 https://mvnrepository.com/artifact/edu.berkeley.cs.jqf -->
<version>1.1</version>
<scope>test</scope>
</dependency>
<!-- JUnit-QuickCheck: 编写生成器要用到的API -->
<dependency>
<groupId>com.pholser</groupId>
<artifactId>junit-quickcheck-generators</artifactId>
<version>0.8</version>
<scope>test</scope>
</dependency>
</dependencies>
<!-- 安装mvn调用jqf-zest的插件 -->
<build>
<plugins>
<plugin>
<groupId>edu.berkeley.cs.jqf</groupId>
<artifactId>jqf-maven-plugin</artifactId>
<!-- 确保安装最新版 https://mvnrepository.com/artifact/edu.berkeley.cs.jqf -->
<version>1.1</version>
</plugin>
</plugins>
</build>
</project>
Test代码
内容保存到src/test/java/examples/CompilerTest.java
,即maven的资源文件下,用于test的、java语言的、examples包。
package examples;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
// 以下这些应该时被测试的内容,需要调用这些内容然后检查是否正确
import com.google.javascript.jscomp.CompilationLevel;
import com.google.javascript.jscomp.Compiler;
import com.google.javascript.jscomp.CompilerOptions;
import com.google.javascript.jscomp.Result;
import com.google.javascript.jscomp.SourceFile;
// 以下这些应该是和@出的标签配套的内容(猜测)
import com.pholser.junit.quickcheck.From;
import edu.berkeley.cs.jqf.fuzz.Fuzz;
import edu.berkeley.cs.jqf.fuzz.JQF;
import org.junit.Before;
import org.junit.runner.RunWith;
import static org.junit.Assume.*;
@RunWith(JQF.class)
public class CompilerTest {
static {
// 禁用所有因为Closure运行导致的日志读写操作,这样可以加快测试速度
java.util.logging.LogManager.getLogManager().reset();
}
// 用到的的编译器
private Compiler compiler = new Compiler(new PrintStream(new ByteArrayOutputStream(), false));
// 可供修改的默认编译选项
private CompilerOptions options = new CompilerOptions();
// 这个是啥???
private SourceFile externs = SourceFile.fromCode("externs", "");
@Before // 以下方法为在测试开始前运行一次的内容
public void initCompiler() {
// 禁用多线程以及过程信息打印
compiler.disableThreads();
options.setPrintConfig(false);
// 开启所有的安全优化(为啥???)
CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
}
// 进行编译并返回编译结果(期待在这个过程中发现未知的错误)
private Result compile(SourceFile input) {
Result result = compiler.compile(externs, input, options);
assumeTrue(result.success); // 这里看不懂了,为啥要有这个assume???
return result;
}
// 调用已有的默认的Generator,生成随机的字符串来进行测试(未结构化)
@Fuzz
public void testWithString(String code) {
SourceFile input = SourceFile.fromCode("input", code);
// 下句并未使用断言,想获取在编译过程中出乎意料的报错,而非我们可以预想到的问题
compile(input);
}
}
生成器代码
package examples;
import java.util.*;
import java.util.function.*;
import com.pholser.junit.quickcheck.generator.GenerationStatus;
import com.pholser.junit.quickcheck.generator.Generator;
import com.pholser.junit.quickcheck.random.SourceOfRandomness;
/* Generates random strings that are syntactically valid JavaScript */
public class JavaScriptCodeGenerator extends Generator<String> {
public JavaScriptCodeGenerator() {
super(String.class); // Register type of generated object
}
private GenerationStatus status; // saved state object when generating
private static final int MAX_IDENTIFIERS = 100;
private static final int MAX_EXPRESSION_DEPTH = 10;
private static final int MAX_STATEMENT_DEPTH = 6;
private static Set<String> identifiers; // Stores generated IDs, to promote re-use
private int statementDepth; // Keeps track of how deep the AST is at any point
private int expressionDepth; // Keeps track of how nested an expression is at any point
private static final String[] UNARY_TOKENS = {
"!", "++", "--", "~",
"delete", "new", "typeof"
};
private static final String[] BINARY_TOKENS = {
"!=", "!==", "%", "%=", "&", "&&", "&=", "*", "*=", "+", "+=", ",",
"-", "-=", "/", "/=", "<", "<<", ">>=", "<=", "=", "==", "===",
">", ">=", ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||",
"in", "instanceof"
};
/** Main entry point. Called once per test case. Returns a random JS program. */
@Override
public String generate(SourceOfRandomness random, GenerationStatus status) {
this.status = status; // we save this so that we can pass it on to other generators
this.identifiers = new HashSet<>();
this.statementDepth = 0;
this.expressionDepth = 0;
return generateStatement(random).toString();
}
/** Utility method for generating a random list of items (e.g. statements, arguments, attributes) */
private static List<String> generateItems(Function<SourceOfRandomness, String> genMethod, SourceOfRandomness random,
int mean) {
int len = random.nextInt(mean*2); // Generate random number in [0, mean*2)
List<String> items = new ArrayList<>(len);
for (int i = 0; i < len; i++) {
items.add(genMethod.apply(random));
}
return items;
}
/** Generates a random JavaScript statement */
private String generateStatement(SourceOfRandomness random) {
statementDepth++;
String result;
// If depth is too high, then generate only simple statements to prevent infinite recursion
// If not, generate simple statements after the flip of a coin
if (statementDepth >= MAX_STATEMENT_DEPTH || random.nextBoolean()) {
// Choose a random private method from this class, and then call it with `random`
result = random.choose(Arrays.<Function<SourceOfRandomness, String>>asList(
this::generateExpressionStatement,
this::generateBreakNode,
this::generateContinueNode,
this::generateReturnNode,
this::generateThrowNode,
this::generateVarNode,
this::generateEmptyNode
)).apply(random);
} else {
// If depth is low and we won the flip, then generate compound statements
// (that is, statements that contain other statements)
result = random.choose(Arrays.<Function<SourceOfRandomness, String>>asList(
this::generateIfNode,
this::generateForNode,
this::generateWhileNode,
this::generateNamedFunctionNode,
this::generateSwitchNode,
this::generateTryNode,
this::generateBlock
)).apply(random);
}
statementDepth--; // Reset statement depth when going up the recursive tree
return result;
}
/** Generates a random JavaScript expression using recursive calls */
private String generateExpression(SourceOfRandomness random) {
expressionDepth++;
String result;
// Choose terminal if nesting depth is too high or based on a random flip of a coin
if (expressionDepth >= MAX_EXPRESSION_DEPTH || random.nextBoolean()) {
result = random.choose(Arrays.<Function<SourceOfRandomness, String>>asList(
this::generateLiteralNode,
this::generateIdentNode
)).apply(random);
} else {
// Otherwise, choose a non-terminal generating function
result = random.choose(Arrays.<Function<SourceOfRandomness, String>>asList(
this::generateBinaryNode,
this::generateUnaryNode,
this::generateTernaryNode,
this::generateCallNode,
this::generateFunctionNode,
this::generatePropertyNode,
this::generateIndexNode,
this::generateArrowFunctionNode
)).apply(random);
}
expressionDepth--;
return "(" + result + ")";
}
/** Generates a random binary expression (e.g. A op B) */
private String generateBinaryNode(SourceOfRandomness random) {
String token = random.choose(BINARY_TOKENS); // Choose a binary operator at random
String lhs = generateExpression(random);
String rhs = generateExpression(random);
return lhs + " " + token + " " + rhs;
}
/** Generates a block of statements delimited by ';' and enclosed by '{' '}' */
private String generateBlock(SourceOfRandomness random) {
return "{ " + String.join(";", generateItems(this::generateStatement, random, 4)) + " }";
}
private String generateBreakNode(SourceOfRandomness random) {
return "break";
}
private String generateCallNode(SourceOfRandomness random) {
String func = generateExpression(random);
String args = String.join(",", generateItems(this::generateExpression, random, 3));
String call = func + "(" + args + ")";
if (random.nextBoolean()) {
return call;
} else {
return "new " + call;
}
}
private String generateCaseNode(SourceOfRandomness random) {
return "case " + generateExpression(random) + ": " + generateBlock(random);
}
private String generateCatchNode(SourceOfRandomness random) {
return "catch (" + generateIdentNode(random) + ") " +
generateBlock(random);
}
private String generateContinueNode(SourceOfRandomness random) {
return "continue";
}
private String generateEmptyNode(SourceOfRandomness random) {
return "";
}
private String generateExpressionStatement(SourceOfRandomness random) {
return generateExpression(random);
}
private String generateForNode(SourceOfRandomness random) {
String s = "for(";
if (random.nextBoolean()) {
s += generateExpression(random);
}
s += ";";
if (random.nextBoolean()) {
s += generateExpression(random);
}
s += ";";
if (random.nextBoolean()) {
s += generateExpression(random);
}
s += ")";
s += generateBlock(random);
return s;
}
private String generateFunctionNode(SourceOfRandomness random) {
return "function(" + String.join(", ", generateItems(this::generateIdentNode, random, 5)) + ")" + generateBlock(random);
}
private String generateNamedFunctionNode(SourceOfRandomness random) {
return "function " + generateIdentNode(random) + "(" + String.join(", ", generateItems(this::generateIdentNode, random, 5)) + ")" + generateBlock(random);
}
private String generateArrowFunctionNode(SourceOfRandomness random) {
String params = "(" + String.join(", ", generateItems(this::generateIdentNode, random, 3)) + ")";
if (random.nextBoolean()) {
return params + " => " + generateBlock(random);
} else {
return params + " => " + generateExpression(random);
}
}
private String generateIdentNode(SourceOfRandomness random) {
// Either generate a new identifier or use an existing one
String identifier;
if (identifiers.isEmpty() || (identifiers.size() < MAX_IDENTIFIERS && random.nextBoolean())) {
identifier = random.nextChar('a', 'z') + "_" + identifiers.size();
identifiers.add(identifier);
} else {
identifier = random.choose(identifiers);
}
return identifier;
}
private String generateIfNode(SourceOfRandomness random) {
return "if (" +
generateExpression(random) + ") " +
generateBlock(random) +
(random.nextBoolean() ? generateBlock(random) : "");
}
private String generateIndexNode(SourceOfRandomness random) {
return generateExpression(random) + "[" + generateExpression(random) + "]";
}
private String generateObjectProperty(SourceOfRandomness random) {
return generateIdentNode(random) + ": " + generateExpression(random);
}
private String generateLiteralNode(SourceOfRandomness random) {
// If we are not too deeply nested, then it is okay to generate array/object literals
if (expressionDepth < MAX_EXPRESSION_DEPTH && random.nextBoolean()) {
if (random.nextBoolean()) {
// Array literal
return "[" + String.join(", ", generateItems(this::generateExpression, random, 3)) + "]";
} else {
// Object literal
return "{" + String.join(", ", generateItems(this::generateObjectProperty, random, 3)) + "}";
}
} else {
// Otherwise, generate primitive literals
return random.choose(Arrays.<Supplier<String>>asList(
() -> String.valueOf(random.nextInt(-10, 1000)), // int literal
() -> String.valueOf(random.nextBoolean()), // bool literal
() -> generateStringLiteral(random),
() -> "undefined",
() -> "null",
() -> "this"
)).get();
}
}
private String generateStringLiteral(SourceOfRandomness random) {
// Generate an arbitrary string using the default string generator, and quote it
return '"' + gen().type(String.class).generate(random, status) + '"';
}
private String generatePropertyNode(SourceOfRandomness random) {
return generateExpression(random) + "." + generateIdentNode(random);
}
private String generateReturnNode(SourceOfRandomness random) {
return random.nextBoolean() ? "return" : "return " + generateExpression(random);
}
private String generateSwitchNode(SourceOfRandomness random) {
return "switch(" + generateExpression(random) + ") {"
+ String.join(" ", generateItems(this::generateCaseNode, random, 2)) + "}";
}
private String generateTernaryNode(SourceOfRandomness random) {
return generateExpression(random) + " ? " + generateExpression(random) +
" : " + generateExpression(random);
}
private String generateThrowNode(SourceOfRandomness random) {
return "throw " + generateExpression(random);
}
private String generateTryNode(SourceOfRandomness random) {
return "try " + generateBlock(random) + generateCatchNode(random);
}
private String generateUnaryNode(SourceOfRandomness random) {
String token = random.choose(UNARY_TOKENS);
return token + " " + generateExpression(random);
}
private String generateVarNode(SourceOfRandomness random) {
return "var " + generateIdentNode(random);
}
private String generateWhileNode(SourceOfRandomness random) {
return "while (" + generateExpression(random) + ")" + generateBlock(random);
}
}
在使用IDEA阅读代码过程中遇到的问题
can’t find the declaration to go to
问题背景
在使用IDEA过程中,需要通过Ctrl + click
的操作去方便阅读代码的上下文,更快确定父类、库之类的东西。但是在直接导入JQF的过程中发现无法找到,总是返回如题的报错。
解决过程
上网搜索发现是IDEA的配置中没有配置好本地已有的maven插件,IDEA中的maven插件与本地的maven插件发生了冲突,在File - New Project Settings 中修改Build - Build Tools - Maven中修改就行,然后清除掉之前已有的问题文件,重新建立依赖关系,就能准确定位了。
在此过程中,需要注意IDEA的Open和Import是不同的功能,在从外部导入的文件(git下载的,svn下载的,直接拷贝的)建议直接使用Import,而已经在本地Import过的文件如果需要再次打开,则使用Open。