com.gitee.qdbp.staticize.parse.AttrCollector Maven / Gradle / Ivy
package com.gitee.qdbp.staticize.parse;
import java.util.ArrayList;
import java.util.List;
import com.gitee.qdbp.staticize.exception.TagException;
import com.gitee.qdbp.staticize.utils.TagUtils;
import com.gitee.qdbp.tools.utils.CharTools;
/**
* 属性收集器
*
* @author zhaohuihua
* @version 140521
*/
class AttrCollector {
/** 当前状态枚举值 **/
private enum Status {
/** 未开始 **/
BEFORE,
/** 正在取名称 **/
NAME,
/** 准备取等号 **/
EQUAL,
/** 准备取前置引号 **/
QUOTES_BEGIN,
/** 准备取属性值 **/
VALUE,
/** 取后置引号 **/
QUOTES_END
}
/** 当前状态 **/
private Status status = Status.BEFORE;
/** 属性名称 **/
private StringBuilder name = new StringBuilder();
/** 属性值 **/
private StringBuilder value = new StringBuilder();
/** 属性集合 **/
private final List list = new ArrayList<>();
/** 引号, 用来记录是单引号还是双引号 **/
private char quotes = 0;
/** 最后一个字符 **/
private char last = 0;
/** 有没有打开某个属性, 也就是说正在取属性取到一半 **/
private boolean open = false;
/**
* 有没有打开某个属性, 也就是说正在取属性取到一半
*
* @return 是否正在取属性
*/
public boolean isOpen() {
return this.open;
}
/**
* 获取已经取到的属性集合
*
* @return 属性集合
*/
public List getAttributes() {
return this.list;
}
/**
* 解析属性, 每次传过来一个字符
*
* @param c 属性字符
* @throws TagException 解析失败
*/
public void parse(char c) throws TagException {
if (!open && last == '/') { // 结束之后又有字符了
// 这个错误的column不准, 比实际值大1
// 因为到下一字符才能判断上一个/是错误的
//
throw new TagException("Unexpected slash character '/' encountered.");
}
if (!open && c == '/') { // 准备结束
last = c;
return;
}
// 不是取属性值, 遇到<
if (status != Status.VALUE && c == '<') {
//
throw new TagException("Attribute name should be preceded by a space.");
}
status = Status.NAME;
open = true;
// 不返回, 继续往下走, 取属性名称
} else if (CharTools.isBlank(c)) {
last = c;
return;
} else {
//
throw new TagException("Attribute name error.");
}
}
if (status == Status.NAME) { // 取名称
if (CharTools.isAttrName(c)) {
name.append(c);
last = c;
return;
} else {
status = Status.EQUAL;
// 不返回, 继续往下走, 取等号
}
}
if (status == Status.EQUAL) { // 取等号
if (c == '=') {
status = Status.QUOTES_BEGIN;
last = c;
return;
} else if (CharTools.isBlank(c)) {
last = c;
return;
} else {
//
String msg = String.format("Attribute '%s' should be followed by '=' sign.", name);
throw new TagException(msg);
}
}
if (status == Status.QUOTES_BEGIN) { // 准备取前置引号
if (CharTools.isQuotes(c)) {
status = Status.VALUE;
quotes = c;
last = c;
return;
} else if (CharTools.isBlank(c)) {
last = c;
return;
} else {
//
String fmt = "The '=' sign of attribute '%s' should be followed by quotation marks.";
throw new TagException(String.format(fmt, name));
}
}
if (status == Status.VALUE) { // 取属性值
if (CharTools.isNewLine(c)) {
//
String fmt = "The value of attribute '%s' cannot have newline character.";
throw new TagException(String.format(fmt, name));
} else if (c == '\\') {
if (last == '\\') {
value.append(c);
last = 0;
} else {
last = c;
}
return;
} else if (last == '\\') {
switch (c) {
case 't':
value.append('\t');
break;
case 'r':
value.append('\r');
break;
case 'n':
value.append('\n');
break;
case 'f':
value.append('\f');
break;
case '\'':
value.append('\'');
break;
case '"':
value.append('"');
break;
default:
value.append("\\").append(c);
break;
}
last = c;
return;
}
// 后置引号(前面是转义符的引号不会走到这里, 在前一个分支走掉了)
else if (c == quotes) {
status = Status.QUOTES_END;
// 不返回, 继续往下走
} else {
value.append(c);
last = c;
return;
}
}
if (status == Status.QUOTES_END) { // 后置引号, 结束
String name = this.name.toString();
String value = TagUtils.parseEscape(this.value.toString());
if (contains(name)) {
list.add(new AttrData(name, new ExpItem(value, false)));
//
String fmt = "Attribute '%s' appears more than once.";
throw new TagException(String.format(fmt, this.name));
}
Object parsedValue;
try {
parsedValue = ExpParser.parse(value);
} catch (TagException e) {
e.prependMessage("Attribute '" + name + "', ");
throw e;
}
list.add(new AttrData(name, parsedValue));
// 还原, 准备取下一个属性
this.name = new StringBuilder();
this.value = new StringBuilder();
this.quotes = 0;
this.last = 0;
this.open = false;
// 从第1步开始
this.status = Status.BEFORE;
}
}
private boolean contains(String name) {
for (AttrData item : list) {
if (name.equals(item.getKey())) {
return true;
}
}
return false;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy