a、在parseProlog()和next()过程中会被调用,用于解析元素的定义信息。
b、代码注解
public int parseStartTag() throws XmlPullParserException, IOException {
//ASSUMPTION ch is past '
// [44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>'
++depth; //FIXME
//当前方法进入前,实际是已经判定过<和startNameChar的,pos-2实际是将posStart记录在<字符上。
//因为START_TAG事件反馈后,用户通过getText取到的值是类这种样式
posStart = pos - 2;
//空元素标识位复位为false,应该清楚这个第三方类的解析过程是线程不安全的,所以这里定义的是一个全局变量
emptyElementTag = false;
//元素属性数量值复位为0
attributeCount = 0;
//元素名称的起始绝对位置,注意绝对两字,涉及到多个字符的计算过程都需要使用绝对位置来结算,否则会出错
//因为more()的过程中,原字符数据可能被扩展或压缩,这个时候pos来计算会得到错误值
final int nameStart = pos - 1 + bufAbsoluteStart;
//冒号偏移量初始值-1,一旦发现不等于-1,就说明元素名称存在冒号
int colonPos = -1;
char ch = buf[ pos - 1];
//在关注命名空间的前提下,冒号不能作为元素名称的起始字符,虽然作为起始名称字符是合法的
if(ch == ':' && processNamespaces) throw new XmlPullParserException(
"when namespaces processing enabled colon can not be at element name start",
this, null);
while(true) {
ch = more();
//遇到非名称合法字符,则说明元素名称该收尾了,退出循环
if(!isNameChar(ch)) break;
if(ch == ':' && processNamespaces) {
//关注命名空间的时候,名称中冒号最多只能出现一次
if(colonPos != -1) throw new XmlPullParserException(
"only one colon is allowed in name of element when namespaces are enabled",
this, null);
colonPos = pos - 1 + bufAbsoluteStart;
}
}
//确保元素名称的相关信息的存储变量有足够空间
ensureElementsCapacity();
//记录元素原始名称、名称长度以及元素名称所在行数
int elLen = (pos - 1) - (nameStart - bufAbsoluteStart);
if(elRawName[ depth ] == null || elRawName[ depth ].length < elLen) {
//?没看懂,为什么要乘以2
elRawName[ depth ] = new char[ 2 * elLen ];
}
//之所以起始偏移量是nameStart-bufAbsoluteStart,是因为nameStart也是绝对位置,需要减去最新的绝对位置来获取准确的当前起点位置
System.arraycopy(buf, nameStart - bufAbsoluteStart, elRawName[ depth ], 0, elLen);
elRawNameEnd[ depth ] = elLen;
elRawNameLine[ depth ] = lineNumber;
String name = null;
//记录处理后元素前缀和元素名称
String prefix = null;
if(processNamespaces) {
if(colonPos != -1) {
prefix = elPrefix[ depth ] = newString(buf, nameStart - bufAbsoluteStart,
colonPos - nameStart);
name = elName[ depth ] = newString(buf, colonPos + 1 - bufAbsoluteStart,
//(pos -1) - (colonPos + 1));
pos - 2 - (colonPos - bufAbsoluteStart));
} else {
prefix = elPrefix[ depth ] = null;
name = elName[ depth ] = newString(buf, nameStart - bufAbsoluteStart, elLen);
}
} else {
name = elName[ depth ] = newString(buf, nameStart - bufAbsoluteStart, elLen);
}
//开始循环处理元素的属性信息了
while(true) {
while(isS(ch)) { ch = more(); } // skip additional white spaces
if(ch == '>') {
//与<配对,退出
break;
} else if(ch == '/') {
//说明是空元素,但如果发现重复声明,抛异常
if(emptyElementTag) throw new XmlPullParserException(
"repeated / in tag declaration", this, null);
emptyElementTag = true;
ch = more();
if(ch != '>') throw new XmlPullParserException(
"expected > to end empty tag not "+printable(ch), this, null);
break;
} else if(isNameStartChar(ch)) {
ch = parseAttribute();
ch = more();
continue;
} else {
throw new XmlPullParserException(
"start tag unexpected character "+printable(ch), this, null);
}
//ch = more(); // skip space
}
// now when namespaces were declared we can resolve them
if(processNamespaces) {
String uri = getNamespace(prefix);
if(uri == null) {
if(prefix == null) { // no prefix and no uri => use default namespace
uri = NO_NAMESPACE;
} else {
throw new XmlPullParserException(
"could not determine namespace bound to element prefix "+prefix,
this, null);
}
}
elUri[ depth ] = uri;
//String uri = getNamespace(prefix);
//if(uri == null && prefix == null) { // no prefix and no uri => use default namespace
// uri = "";
//}
// resolve attribute namespaces
for (int i = 0; i < attributeCount; i++)
{
final String attrPrefix = attributePrefix[ i ];
if(attrPrefix != null) {
final String attrUri = getNamespace(attrPrefix);
if(attrUri == null) {
throw new XmlPullParserException(
"could not determine namespace bound to attribute prefix "+attrPrefix,
this, null);
}
attributeUri[ i ] = attrUri;
} else {
attributeUri[ i ] = NO_NAMESPACE;
}
}
//TODO
//[ WFC: Unique Att Spec ]
// check attribute uniqueness constraint for attributes that has namespace!!!
for (int i = 1; i < attributeCount; i++)
{
for (int j = 0; j < i; j++)
{
if( attributeUri[j] == attributeUri[i]
&& (allStringsInterned && attributeName[j].equals(attributeName[i])
|| (!allStringsInterned
&& attributeNameHash[ j ] == attributeNameHash[ i ]
&& attributeName[j].equals(attributeName[i])) )
) {
// prepare data for nice error message?
String attr1 = attributeName[j];
if(attributeUri[j] != null) attr1 = attributeUri[j]+":"+attr1;
String attr2 = attributeName[i];
if(attributeUri[i] != null) attr2 = attributeUri[i]+":"+attr2;
throw new XmlPullParserException(
"duplicated attributes "+attr1+" and "+attr2, this, null);
}
}
}
} else { // ! processNamespaces
//[ WFC: Unique Att Spec ]
// check raw attribute uniqueness constraint!!!
for (int i = 1; i < attributeCount; i++)
{
for (int j = 0; j < i; j++)
{
if((allStringsInterned && attributeName[j].equals(attributeName[i])
|| (!allStringsInterned
&& attributeNameHash[ j ] == attributeNameHash[ i ]
&& attributeName[j].equals(attributeName[i])) )
) {
// prepare data for nice error message?
final String attr1 = attributeName[j];
final String attr2 = attributeName[i];
throw new XmlPullParserException(
"duplicated attributes "+attr1+" and "+attr2, this, null);
}
}
}
}
elNamespaceCount[ depth ] = namespaceEnd;
posEnd = pos;
return eventType = START_TAG;
}
c、代码要点
c.1、如果当前要关注命名空间,则第一个字符不能是冒号。
c.2、如果当前要关注命名空间,则不能出现2个及其以上冒号。
c.3、元素名称的解析过程是直到isNameChar()为false才结束。
c.4、记录当前元素的原始名称、原始名称长度以及行号。
c.5、如果当前要关注命名空间,则记录前缀名和元素的名称。特别是无前缀名,就在elPrefix指定位置记录空;如果不关注命名空间,就仅在elName中记录元素名称。
c.6、开始进行元素属性信息的识别:>直接退出;/说明是空元素,置emptyElementTag为true(要判定是否出现了两次);isNameStartChar()为true,则进行属性信息解析parseAttribute()。
c.7、如果当前要关注命名空间,在elUri中记录命名空间的URI定义,如果有前缀,确未找到声明,则抛出异常,另外,要检查元素属性是否存在重复定义的情况;不关注命名空间,也要检查元素属性是否重复定义。
c.8、返回START_TAG事件。