All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gitee.qdbp.staticize.parse.AttrCollector Maven / Gradle / Ivy

There is a newer version: 3.5.18
Show newest version
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