Merge classes from AndroidXml

* src: https://github.com/REAndroid/AndroidXml
This commit is contained in:
REAndroid 2022-12-28 06:24:53 -05:00
parent 754e1c10e6
commit fb9a14113b
19 changed files with 6072 additions and 0 deletions

View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.xml;
import java.io.IOException;
import java.io.Writer;
class ElementWriter extends Writer {
private final Writer mWriter;
private final long mMaxLen;
private final boolean mUnlimitedLength;
private long mCurrentLength;
private boolean mLengthFinished;
ElementWriter(Writer writer, long maxLen){
mWriter=writer;
this.mMaxLen=maxLen;
this.mUnlimitedLength=maxLen<0;
}
ElementWriter(Writer writer){
this(writer, -1);
}
boolean isFinished(){
return mLengthFinished;
}
private boolean mInterruptedWritten;
void writeInterrupted(){
if(!mLengthFinished){
return;
}
if(mInterruptedWritten){
return;
}
mInterruptedWritten=true;
String txt="\n .\n .\n .\n more items ...\n";
try {
mWriter.write(txt);
} catch (IOException e) {
}
}
@Override
public void write(char[] chars, int i, int i1) throws IOException {
updateCurrentLength(i1);
mWriter.write(chars, i, i1);
}
@Override
public void flush() throws IOException {
mWriter.flush();
}
@Override
public void close() throws IOException {
mWriter.close();
}
private boolean updateCurrentLength(int len){
if(mUnlimitedLength){
return false;
}
if(mLengthFinished){
mLengthFinished=true;
//return true;
}
mCurrentLength+=len;
mLengthFinished=mCurrentLength>=mMaxLen;
return mLengthFinished;
}
}

View File

@ -0,0 +1,156 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.xml;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class NameSpaceItem {
private String prefix;
private String namespaceUri;
public NameSpaceItem(String prefix, String nsUri){
this.prefix=prefix;
this.namespaceUri=nsUri;
validate();
}
public String toAttributeName(){
return ATTR_PREFIX+":"+prefix;
}
public SchemaAttr toSchemaAttribute(){
return new SchemaAttr(getPrefix(), getNamespaceUri());
}
public boolean isPrefixEqual(String p){
if(XMLUtil.isEmpty(prefix)){
return false;
}
return prefix.equals(p);
}
public boolean isUriEqual(String nsUri){
if(XMLUtil.isEmpty(namespaceUri)){
return false;
}
return namespaceUri.equals(nsUri);
}
public boolean isValid(){
return isPrefixValid() && isUriValid();
}
private boolean validate(){
boolean preOk=isPrefixValid();
boolean uriOk=isUriValid();
if(preOk && uriOk){
if(!NAME_ANDROID.equals(prefix) && URI_ANDROID.equals(namespaceUri)){
namespaceUri= URI_APP;
}
return true;
}
if(!preOk && !uriOk){
return false;
}
if(!preOk){
if(URI_ANDROID.equals(namespaceUri)){
prefix= NAME_ANDROID;
}else {
prefix= NAME_APP;
}
}
if(!uriOk){
if(NAME_ANDROID.equals(prefix)){
namespaceUri= URI_ANDROID;
}else {
namespaceUri= URI_APP;
}
}
return true;
}
private boolean isPrefixValid(){
return !XMLUtil.isEmpty(prefix);
}
private boolean isUriValid(){
if(XMLUtil.isEmpty(namespaceUri)){
return false;
}
Matcher matcher=PATTERN_URI.matcher(namespaceUri);
return matcher.find();
}
public String getNamespaceUri() {
return namespaceUri;
}
public String getPrefix() {
return prefix;
}
public void setNamespaceUri(String namespaceUri) {
this.namespaceUri = namespaceUri;
validate();
}
public void setPrefix(String prefix) {
this.prefix = prefix;
validate();
}
@Override
public boolean equals(Object o){
if(o instanceof NameSpaceItem){
return isUriEqual(((NameSpaceItem)o).namespaceUri);
}
return false;
}
@Override
public int hashCode(){
String u=namespaceUri;
if(u==null){
u="";
}
return u.hashCode();
}
@Override
public String toString(){
StringBuilder builder=new StringBuilder();
boolean appendOnce=false;
if(namespaceUri!=null){
builder.append(namespaceUri);
appendOnce=true;
}
if(prefix!=null){
if(appendOnce){
builder.append(':');
}
builder.append(prefix);
}
return builder.toString();
}
private static NameSpaceItem ns_android;
private static NameSpaceItem ns_app;
public static NameSpaceItem getAndroid(){
if(ns_android==null){
ns_android=new NameSpaceItem(NAME_ANDROID, URI_ANDROID);
}
return ns_android;
}
public static NameSpaceItem getApp(){
if(ns_app==null){
ns_app=new NameSpaceItem(NAME_APP, URI_APP);
}
return ns_app;
}
private static final Pattern PATTERN_URI=Pattern.compile("^https?://[^\\s/]+/[^\\s]+$");
private static final String ATTR_PREFIX = "xmlns";
private static final String URI_ANDROID = "http://schemas.android.com/apk/res/android";
private static final String URI_APP = "http://schemas.android.com/apk/res-auto";
private static final String NAME_ANDROID = "android";
private static final String NAME_APP = "app";
}

View File

@ -0,0 +1,117 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.xml;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SchemaAttr extends XMLAttribute {
private static final String DEFAULT_XMLNS="xmlns";
private String mXmlns;
private String mPrefix;
public SchemaAttr(String prefix, String uri) {
this(DEFAULT_XMLNS, prefix, uri);
}
public SchemaAttr(String xmlns, String prefix, String uri) {
super(prefix, uri);
this.set(xmlns, prefix, uri);
}
private void set(String xmlns, String prefix, String uri){
setXmlns(xmlns);
if(XMLUtil.isEmpty(prefix)){
prefix=null;
}
setName(prefix);
setUri(uri);
}
@Override
public void setName(String fullName){
if(fullName==null){
setPrefix(null);
return;
}
int i=fullName.indexOf(':');
if(i>0 && i<fullName.length()){
mXmlns=fullName.substring(0, i);
mPrefix=fullName.substring(i+1);
}else {
setPrefix(fullName);
}
}
public String getXmlns(){
return mXmlns;
}
public String getPrefix(){
return mPrefix;
}
public void setPrefix(String prefix){
mPrefix=prefix;
}
public void setXmlns(String xmlns){
if(XMLUtil.isEmpty(xmlns)){
xmlns=DEFAULT_XMLNS;
}
mXmlns=xmlns;
}
public String getUri(){
return super.getValue();
}
public void setUri(String uri){
if(uri==null){
super.setValue(null);
return;
}
Matcher matcher=PATTERN_URI.matcher(uri);
if(!matcher.find()){
super.setValue(uri);
return;
}
String prf=matcher.group("B");
if(!XMLUtil.isEmpty(prf)){
setPrefix(prf);
}
uri=matcher.group("A");
super.setValue(uri);
}
@Override
public XMLAttribute cloneAttr(){
SchemaAttr attr=new SchemaAttr(getXmlns(), getPrefix(), getUri());
attr.setNameId(getNameId());
attr.setValueId(getValueId());
return attr;
}
@Override
public String getName(){
StringBuilder builder=new StringBuilder();
builder.append(getXmlns());
builder.append(':');
String prf=getPrefix();
if(prf==null){
prf="NULL";
}
builder.append(prf);
return builder.toString();
}
static boolean looksSchema(String name, String value){
if(value==null || !name.startsWith("xmlns:")){
return false;
}
Matcher matcher=PATTERN_URI.matcher(value);
return matcher.find();
}
private static final Pattern PATTERN_URI=Pattern.compile("^\\s*(?<A>https?://[^:\\s]+)(:(?<B>([^:/\\s]+)))?\\s*$");
}

View File

@ -0,0 +1,158 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.xml;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
public class XMLAttribute {
private int mNameId;
private int mValueId;
private Object mValueTag;
private Object mNameTag;
private String mName;
private String mValue;
public XMLAttribute(String name, String val){
mName=name;
mValue= XMLUtil.escapeXmlChars(val);
}
public void setNameId(String id){
setNameId(XMLUtil.hexToInt(id,0));
}
public void setNameId(int id){
mNameId=id;
}
public void setValueId(String id){
setValueId(XMLUtil.hexToInt(id,0));
}
public void setValueId(int id){
mValueId=id;
}
public int getNameId(){
return mNameId;
}
public int getValueId(){
return mValueId;
}
public XMLAttribute cloneAttr(){
XMLAttribute baseAttr=new XMLAttribute(getName(),getValue());
baseAttr.setNameId(getNameId());
baseAttr.setValueId(getValueId());
return baseAttr;
}
public Object getValueTag(){
return mValueTag;
}
public void setValueTag(Object obj){
mValueTag =obj;
}
public Object geNameTag(){
return mNameTag;
}
public void setNameTag(Object obj){
mNameTag =obj;
}
public String getName(){
return mName;
}
public String getNamePrefix(){
int i=mName.indexOf(":");
if(i>0){
return mName.substring(0,i);
}
return null;
}
public String getNameWoPrefix(){
int i=mName.indexOf(":");
if(i>0){
return mName.substring(i+1);
}
return mName;
}
public String getValue(){
if(mValue==null){
mValue="";
}
return mValue;
}
public int getValueInt(){
long l=Long.decode(getValue());
return (int)l;
}
public boolean getValueBool(){
String str=getValue().toLowerCase();
if("true".equals(str)){
return true;
}
return false;
}
public boolean isValueBool(){
String str=getValue().toLowerCase();
if("true".equals(str)){
return true;
}
return "false".equals(str);
}
public void setName(String name){
mName=name;
}
public void setValue(String val){
mValue= XMLUtil.escapeXmlChars(val);
}
public boolean isEmpty(){
return XMLUtil.isEmpty(getName());
}
@Override
public boolean equals(Object obj){
if(obj instanceof XMLAttribute){
XMLAttribute attr=(XMLAttribute)obj;
if(isEmpty()){
return attr.isEmpty();
}
String s=toString();
return s.equals(attr.toString());
}
return false;
}
public boolean write(Writer writer) throws IOException {
if(isEmpty()){
return false;
}
writer.write(getName());
writer.write("=\"");
String val= XMLUtil.trimQuote(getValue());
val= XMLUtil.escapeXmlChars(val);
val= XMLUtil.escapeQuote(val);
writer.write(val);
writer.write('"');
return true;
}
@Override
public String toString(){
if(isEmpty()){
return null;
}
StringWriter writer=new StringWriter();
try {
write(writer);
} catch (IOException e) {
}
writer.flush();
return writer.toString();
}
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.xml;
import java.io.IOException;
import java.io.Writer;
public class XMLComment extends XMLElement {
private String mStart;
private String mEnd;
private boolean mIsHidden;
public XMLComment(String commentText){
this();
setCommentText(commentText);
}
public XMLComment(){
super();
initializeStartEnd();
}
@Override
XMLElement onCloneElement(){
XMLComment ce=new XMLComment(getCommentText());
ce.setHidden(isHidden());
return ce;
}
@Override
void cloneAllAttributes(XMLElement element){
}
public void setHidden(boolean hide){
mIsHidden=hide;
}
public boolean isHidden(){
return mIsHidden;
}
public void setCommentText(String text){
setTextContent(text);
}
public String getCommentText(){
return getTextContent();
}
private void initializeStartEnd(){
setTagName("");
mStart="<!--";
mEnd="-->";
setStart(mStart);
setEnd(mEnd);
setStartPrefix("");
setEndPrefix("");
}
@Override
int getChildIndent(){
return getIndent();
}
@Override
boolean isEmpty(){
return XMLUtil.isEmpty(getTextContent());
}
@Override
public boolean write(Writer writer, boolean newLineAttributes) throws IOException {
if(isHidden()){
return false;
}
if(isEmpty()){
return false;
}
boolean appendOnce=appendComments(writer);
if(appendOnce){
writer.write(XMLUtil.NEW_LINE);
}
appendIndentText(writer);
writer.write(mStart);
writer.write(getCommentText());
writer.write(mEnd);
return true;
}
}

View File

@ -0,0 +1,261 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.xml;
import com.reandroid.xml.parser.XMLDocumentParser;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Comparator;
public class XMLDocument {
private XMLElement mDocumentElement;
private Object mTag;
private String mName;
private String mConfigName;
private float mIndentScale;
private XmlHeaderElement mHeaderElement;
private Object mLastElementSorter;
public XMLDocument(String elementName){
this();
XMLElement docElem=new XMLElement(elementName);
setDocumentElement(docElem);
}
public XMLDocument(){
mIndentScale=0.5f;
mHeaderElement = new XmlHeaderElement();
}
public void setHeaderElement(XmlHeaderElement headerElement){
this.mHeaderElement=headerElement;
}
public void hideComments(boolean hide){
hideComments(true, hide);
}
public void hideComments(boolean recursive, boolean hide){
if(mDocumentElement==null){
return;
}
mDocumentElement.hideComments(recursive, hide);
}
public XmlHeaderElement getHeaderElement(){
return mHeaderElement;
}
public void sortDocumentElement(Comparator<XMLElement> comparator){
if(mDocumentElement==null||comparator==null){
return;
}
if(mLastElementSorter !=null){
if(mLastElementSorter.getClass().equals(comparator.getClass())){
return;
}
}
mLastElementSorter=comparator;
mDocumentElement.sortChildes(comparator);
}
public void setIndentScalePercent(int val){
int percent;
if(val>100){
percent=100;
}else if(val<0){
percent=0;
}else {
percent=val;
}
mIndentScale=percent/100.0f;
XMLElement docElem=getDocumentElement();
if(docElem!=null){
docElem.setIndentScale(mIndentScale);
}
}
public String getName(){
return mName;
}
public String getConfigName(){
return mConfigName;
}
public void setName(String name){
mName=name;
}
public void setConfigName(String configName){
mConfigName=configName;
}
public Object getTag(){
return mTag;
}
public void setTag(Object obj){
mTag=obj;
}
public XMLElement createElement(String tag) {
XMLElement docEl=getDocumentElement();
if(docEl==null){
docEl=new XMLElement(tag);
setDocumentElement(docEl);
return docEl;
}
XMLElement baseElement=docEl.createElement(tag);
return baseElement;
}
public XMLElement getDocumentElement(){
return mDocumentElement;
}
public void setDocumentElement(XMLElement baseElement){
mDocumentElement=baseElement;
if(baseElement!=null){
baseElement.setIndentScale(mIndentScale);
}
}
private String getElementString(boolean newLineAttributes){
XMLElement baseElement=getDocumentElement();
if(baseElement==null){
return null;
}
return baseElement.toString();
}
private boolean appendDocumentElement(Writer writer, boolean newLineAttributes) throws IOException {
if(mDocumentElement==null){
return false;
}
return mDocumentElement.write(writer, newLineAttributes);
}
private boolean appendDocumentAttribute(Writer writer) throws IOException {
XmlHeaderElement headerElement=getHeaderElement();
if(headerElement==null){
return false;
}
return headerElement.write(writer, false);
}
public boolean saveAndroidResource(File file) throws IOException{
if(file==null){
throw new IOException("File is null");
}
File dir=file.getParentFile();
if(!dir.exists()){
dir.mkdirs();
}
FileOutputStream out=new FileOutputStream(file,false);
return saveAndroidResource(out);
}
public boolean saveAndroidValuesResource(File file) throws IOException{
if(file==null){
throw new IOException("File is null");
}
File dir=file.getParentFile();
if(!dir.exists()){
dir.mkdirs();
}
FileOutputStream out=new FileOutputStream(file,false);
return saveAndroidValuesResource(out);
}
public boolean saveAndroidResource(OutputStream out) throws IOException{
setIndent(1);
hideComments(true);
return save(out, true);
}
public boolean saveAndroidValuesResource(OutputStream out) throws IOException{
setIndent(1);
//hideComments(true);
return save(out, false);
}
public boolean save(OutputStream out, boolean newLineAttributes) throws IOException{
OutputStreamWriter writer=new OutputStreamWriter(out, StandardCharsets.UTF_8);
boolean result= write(writer, newLineAttributes);
writer.flush();
writer.close();
return result;
}
public boolean save(File file, boolean newLineAttributes) throws IOException{
File dir=file.getParentFile();
if(dir!=null&&!dir.exists()){
dir.mkdirs();
}
setIndent(1);
FileWriter writer=new FileWriter(file,false);
boolean result= write(writer, newLineAttributes);
writer.flush();
writer.close();
return result;
}
public boolean write(Writer writer, boolean newLineAttributes) throws IOException{
boolean has_header=appendDocumentAttribute(writer);
if(has_header){
writer.write(XMLUtil.NEW_LINE);
}
return appendDocumentElement(writer, newLineAttributes);
}
public String toText(){
return toText(1, false);
}
public String toText(boolean newLineAttributes){
return toText(1, newLineAttributes);
}
public String toText(int indent, boolean newLineAttributes){
StringWriter writer=new StringWriter();
setIndent(indent);
try {
write(writer, newLineAttributes);
writer.flush();
writer.close();
} catch (IOException ignored) {
}
return writer.toString();
}
@Override
public String toString(){
StringWriter strWriter=new StringWriter();
ElementWriter writer=new ElementWriter(strWriter, XMLElement.DEBUG_TO_STRING);
try {
write(writer, false);
} catch (IOException e) {
}
strWriter.flush();
return strWriter.toString();
}
public static XMLDocument load(String text) throws XMLException {
XMLDocumentParser parser=new XMLDocumentParser(text);
return parser.parse();
}
public static XMLDocument load(InputStream in) throws XMLException {
if(in==null){
throw new XMLException("InputStream=null");
}
XMLDocumentParser parser=new XMLDocumentParser(in);
return parser.parse();
}
public static XMLDocument load(File file) throws XMLException {
XMLDocumentParser parser=new XMLDocumentParser(file);
XMLDocument resDocument=parser.parse();
if(resDocument!=null){
if(resDocument.getTag()==null){
resDocument.setTag(file);
}
}
return resDocument;
}
public void setIndent(int indent){
XMLElement docEle=getDocumentElement();
if(docEle==null){
return;
}
docEle.setIndent(indent);
}
public static String htmlToXml(String htmlString){
return XMLUtil.htmlToXml(htmlString);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.xml;
public class XMLException extends Exception {
public XMLException(String msg){
super(msg);
}
}

View File

@ -0,0 +1,92 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.xml;
import java.io.IOException;
import java.io.Writer;
public class XMLTextAttribute extends XMLAttribute {
private String mText;
public XMLTextAttribute(){
super(null,null);
}
public void setText(String txt){
mText=txt;
}
public String getText(){
return mText;
}
public String getText(boolean unEscape){
if(unEscape){
return XMLUtil.unEscapeXmlChars(mText);
}
if(mText!=null){
String junk= XMLUtil.unEscapeXmlChars(mText);
if(!mText.equals(junk)){
junk.trim();
}
}
return mText;
}
@Override
public XMLTextAttribute cloneAttr(){
XMLTextAttribute textAttribute=new XMLTextAttribute();
textAttribute.setText(getText());
textAttribute.setValueId(getValueId());
return textAttribute;
}
@Override
public String getValue(){
return getText();
}
@Override
public void setValue(String val){
setText(val);
}
@Override
public boolean equals(Object obj){
if(obj instanceof XMLTextAttribute){
XMLTextAttribute attr=(XMLTextAttribute)obj;
String s=getText();
if(s==null){
return attr.getText()==null;
}
return s.equals(attr.getText());
}
if(obj instanceof String){
String s2=(String)obj;
return s2.equals(getText());
}
return false;
}
@Override
public boolean isEmpty(){
//return XMLUtil.isEmpty(getText());
return getText()==null;
}
@Override
public boolean write(Writer writer) throws IOException {
if(isEmpty()){
return false;
}
writer.append(getText());
return true;
}
@Override
public String toString(){
return getText();
}
}

View File

@ -0,0 +1,162 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.xml;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class XMLUtil {
public static String NEW_LINE="\n";
public static boolean isEmpty(String s){
if(s==null){
return true;
}
//String txt=s.trim();
String txt=s;
return txt.length()==0;
}
public static String escapeXmlChars(String str){
if(str==null){
return null;
}
str=str.replaceAll("&amp;", "&");
str=str.replaceAll("&lt;", "<");
str=str.replaceAll("&gt;", ">");
str=str.replaceAll("&", "&amp;");
str=str.replaceAll("<", "&lt;");
str=str.replaceAll(">", "&gt;");
return str;
}
public static String escapeQuote(String str){
if(str==null){
return null;
}
str=str.replaceAll("\"", "&quot;");
return str;
}
public static String unEscapeXmlChars(String str){
if(str==null){
return null;
}
str=str.replaceAll("&amp;", "&");
str=str.replaceAll("&lt;", "<");
str=str.replaceAll("&gt;", ">");
str=str.replaceAll("&quot;", "\"");
if(str.startsWith("\"")&&str.endsWith("\"")){
// str=str.substring(1, str.length()-1);
}
return str;
}
public static String intToHex(int val){
return String.format("0x%08x", val);
}
public static int hexToInt(String hexStr, int def){
if(hexStr==null){
return def;
}
Matcher matcher=PATTERN_HEX.matcher(hexStr);
if(!matcher.find()){
return def;
}
hexStr=matcher.group("A");
return Integer.parseInt(hexStr, 16);
}
public static String trimQuote(String txt){
if(txt==null){
return null;
}
String tmp=txt.trim();
if(tmp.length()==0){
return txt;
}
char c1=tmp.charAt(0);
if(c1!='"'){
return txt;
}
int end=tmp.length()-1;
c1=tmp.charAt(end);
if(c1!='"'){
return txt;
}
if(end<=1){
return "";
}
return tmp.substring(1,end);
}
public static boolean isStringEqual(String s1, String s2) {
if(s1==null&&s2==null){
return true;
}
if(s1==null||s2==null){
return false;
}
return s1.equals(s2);
}
static String htmlToXml(String htmlString){
if(htmlString==null){
return null;
}
int i=0;
htmlString=htmlString.trim();
String result=htmlString;
Matcher matcher=PATTERN_HTML_HEADER_CHILDES.matcher(htmlString);
while (matcher.find()){
String openedTag=matcher.group("Element");
if(openedTag.contains("https://github.githubassets.com")){
openedTag.trim();
}
int len=openedTag.length();
String tagName=matcher.group("Tag");
if(isOpenHtmlTag(tagName) && !openedTag.endsWith("/>")&& !openedTag.endsWith("/ >")){
String rep=openedTag.substring(0, len-1);
rep=rep+"/>";
result=result.replace(openedTag, rep);
result=result.replace(" crossorigin/>", "/>");
result=result.replace(" data-pjax-transient/>", "/>");
}
i=htmlString.indexOf(openedTag);
i=i+len;
htmlString=htmlString.substring(i);
htmlString=htmlString.trim();
matcher=PATTERN_HTML_HEADER_CHILDES.matcher(htmlString);
}
result="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"+result;
return result;
}
private static boolean isOpenHtmlTag(String tagName){
if("link".equals(tagName)){
return true;
}
if("meta".equals(tagName)){
return true;
}
if("img".equals(tagName)){
return true;
}
if("style".equals(tagName)){
return true;
}
return false;
}
private static Pattern PATTERN_HEX=Pattern.compile("^\\s*(0x)?(?<A>[a-f0-9]+)\\s*$");
//<link rel="preconnect" href="https://avatars.githubusercontent.com">
private static Pattern PATTERN_HTML_HEADER_CHILDES=Pattern.compile("(?<Element><\\s*(?<Tag>[a-zA-Z]+)\\s*[^<>]+>)");
}

View File

@ -0,0 +1,110 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.xml;
public class XmlHeaderElement extends XMLElement {
private static final String ATTR_VERSION="version";
private static final String ATTR_ENCODING="encoding";
private static final String ATTR_STANDALONE="standalone";
public XmlHeaderElement(XmlHeaderElement element){
this();
copyAll(element);
}
public XmlHeaderElement(){
super();
initializeStartEnd();
setDefaultAttr();
}
@Override
XMLElement onCloneElement(){
return new XmlHeaderElement(this);
}
@Override
void cloneAllAttributes(XMLElement element){
}
private void copyAll(XmlHeaderElement element){
if(element==null){
return;
}
int max=element.getAttributeCount();
for(int i=0;i<max;i++){
XMLAttribute exist=element.getAttributeAt(i);
setAttribute(exist.getName(), exist.getValue());
}
}
private void initializeStartEnd(){
setTagName("xml");
setStart("<?");
setEnd("?>");
setStartPrefix("");
setEndPrefix("");
}
private void setDefaultAttr(){
setVersion("1.0");
setEncoding("utf-8");
setStandalone(null);
}
public Object getProperty(String name){
XMLAttribute attr=getAttribute(name);
if(attr==null){
return null;
}
String val=attr.getValue();
if(ATTR_STANDALONE.equalsIgnoreCase(name)){
boolean res=false;
if("true".equals(val)){
res=true;
}
return res;
}
return val;
}
public void setProperty(String name, Object o){
if(ATTR_STANDALONE.equalsIgnoreCase(name)){
if(o instanceof Boolean){
setStandalone((Boolean)o);
return;
}
}
String val=null;
if(o!=null){
val=o.toString();
}
setAttribute(name, val);
}
public void setVersion(String version){
setAttribute(ATTR_VERSION, version);
}
public void setEncoding(String encoding){
setAttribute(ATTR_ENCODING, encoding);
}
public void setStandalone(Boolean flag){
if(flag==null){
removeAttribute(ATTR_STANDALONE);
return;
}
String str=flag?"yes":"no";
setAttribute(ATTR_STANDALONE, str);
}
@Override
int getChildIndent(){
return 0;
}
@Override
int getIndent(){
return 0;
}
}

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.xml.parser;
public interface AttrIDProvider {
int getAttributeNameResourceId(int position);
int getAttributeValueResourceId(int position);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,171 @@
/*
* This class is taken from org.xmlpull.*
*
* Check license: http://xmlpull.org
*
*/
/*This package is renamed from org.xmlpull.* to avoid conflicts*/
package com.reandroid.xml.parser;
public class MXParserCachingStrings extends MXParser implements Cloneable
{
protected final static boolean CACHE_STATISTICS = false;
protected final static boolean TRACE_SIZING = false;
protected final static int INITIAL_CAPACITY = 13;
protected int cacheStatCalls;
protected int cacheStatWalks;
protected int cacheStatResets;
protected int cacheStatRehash;
protected static final int CACHE_LOAD = 77;
protected int cacheEntriesCount;
protected int cacheEntriesThreshold;
protected char[][] keys;
protected String[] values;
public MXParserCachingStrings() {
super();
allStringsInterned = true;
initStringCache();
}
@Override
public void setFeature(String name, boolean state) throws XmlPullParserException
{
if(FEATURE_NAMES_INTERNED.equals(name)) {
if(eventType != START_DOCUMENT) throw new XmlPullParserException(
"interning names feature can only be changed before parsing", this, null);
allStringsInterned = state;
if(!state && keys != null) {
resetStringCache();
}
} else {
super.setFeature(name, state);
}
}
public boolean getFeature(String name)
{
if(FEATURE_NAMES_INTERNED.equals(name)) {
return allStringsInterned;
} else {
return super.getFeature(name);
}
}
protected String newString(char[] cbuf, int off, int len) {
if(allStringsInterned) {
return newStringIntern(cbuf, off, len);
} else {
return super.newString(cbuf, off, len);
}
}
protected String newStringIntern(char[] cbuf, int off, int len) {
if(CACHE_STATISTICS) {
++cacheStatCalls;
}
if (cacheEntriesCount >= cacheEntriesThreshold) {
rehash();
}
int offset = fastHash(cbuf, off, len) % keys.length;
char[] k = null;
while( (k = keys[offset]) != null
&& !keysAreEqual(k, 0, k.length,
cbuf, off, len))
{
offset = (offset + 1) % keys.length;
if(CACHE_STATISTICS) ++cacheStatWalks;
}
if (k != null) {
return values[offset];
} else {
k = new char[len];
System.arraycopy(cbuf, off, k, 0, len);
final String v = new String(k).intern();
keys[offset] = k;
values[offset] = v;
++cacheEntriesCount;
return v;
}
}
protected void initStringCache() {
if(keys == null) {
if(INITIAL_CAPACITY < 0) {
throw new IllegalArgumentException("Illegal initial capacity: " + INITIAL_CAPACITY);
}
if(CACHE_LOAD < 0 || CACHE_LOAD > 99) {
throw new IllegalArgumentException("Illegal load factor: " + CACHE_LOAD);
}
cacheEntriesThreshold = (int)((INITIAL_CAPACITY * CACHE_LOAD)/100);
if(cacheEntriesThreshold >= INITIAL_CAPACITY) {
throw new RuntimeException(
"internal error: threshold must be less than capacity: "+INITIAL_CAPACITY);
}
keys = new char[INITIAL_CAPACITY][];
values = new String[INITIAL_CAPACITY];
cacheEntriesCount = 0;
}
}
protected void resetStringCache() {
if(CACHE_STATISTICS) {
++cacheStatResets;
}
initStringCache();
}
private void rehash() {
if(CACHE_STATISTICS) ++cacheStatRehash;
final int newSize = 2 * keys.length + 1;
cacheEntriesThreshold = (int)((newSize * CACHE_LOAD)/100);
if(cacheEntriesThreshold >= newSize) throw new RuntimeException(
"internal error: threshold must be less than capacity: "+newSize);
final char[][] newKeys = new char[newSize][];
final String[] newValues = new String[newSize];
for(int i = 0; i < keys.length; i++) {
final char[] k = keys[i];
keys[i] = null;
final String v = values[i];
values[i] = null;
if(k != null) {
int newOffset = fastHash(k, 0, k.length) % newSize;
char[] newk = null;
while((newk = newKeys[newOffset]) != null) {
if(keysAreEqual(newk, 0, newk.length,
k, 0, k.length)) {
throw new RuntimeException("internal cache error: duplicated keys: "+
new String(newk)+" and "+new String(k));
}
newOffset = (newOffset + 1) % newSize;
}
newKeys[newOffset] = k;
newValues[newOffset] = v;
}
}
keys = newKeys;
values = newValues;
}
private static boolean keysAreEqual (char[] a, int astart, int alength,
char[] b, int bstart, int blength) {
if(alength != blength) {
return false;
} else {
for(int i = 0; i < alength; i++) {
if(a[astart + i] != b[bstart + i]) {
return false;
}
}
return true;
}
}
}

View File

@ -0,0 +1,309 @@
/*
* This class is taken from org.xmlpull.*
*
* Check license: http://xmlpull.org
*
*/
/*This package is renamed from org.xmlpull.* to avoid conflicts*/
package com.reandroid.xml.parser;
import java.io.IOException;
public class MXParserNonValidating extends MXParserCachingStrings
{
private boolean processDocDecl;
public MXParserNonValidating() {
super();
}
@Override
public void setFeature(String name,
boolean state) throws XmlPullParserException
{
if(FEATURE_PROCESS_DOCDECL.equals(name)) {
if(eventType != START_DOCUMENT) throw new XmlPullParserException(
"process DOCDECL feature can only be changed before parsing", this, null);
processDocDecl = state;
} else {
super.setFeature(name, state);
}
}
@Override
public boolean getFeature(String name)
{
if(FEATURE_PROCESS_DOCDECL.equals(name)) {
return processDocDecl;
} else {
return super.getFeature(name);
}
}
@Override
protected char more() throws IOException, XmlPullParserException {
return super.more();
}
@Override
protected char[] lookuEntityReplacement(int entitNameLen) throws XmlPullParserException, IOException
{
if(!allStringsInterned) {
final int hash = fastHash(buf, posStart, posEnd - posStart);
LOOP:
for (int i = entityEnd - 1; i >= 0; --i)
{
if(hash == entityNameHash[ i ] && entitNameLen == entityNameBuf[ i ].length) {
final char[] entityBuf = entityNameBuf[ i ];
for (int j = 0; j < entitNameLen; j++)
{
if(buf[posStart + j] != entityBuf[j]) continue LOOP;
}
if(tokenize) text = entityReplacement[ i ];
return entityReplacementBuf[ i ];
}
}
} else {
entityRefName = newString(buf, posStart, posEnd - posStart);
for (int i = entityEnd - 1; i >= 0; --i)
{
// take advantage that interning for newStirng is enforced
if(entityRefName == entityName[ i ]) {
if(tokenize) {
text = entityReplacement[ i ];
}
return entityReplacementBuf[ i ];
}
}
}
return null;
}
@Override
protected void parseDocdecl()
throws XmlPullParserException, IOException
{
//make sure that tokenize flag is disabled temporarily!!!!
final boolean oldTokenize = tokenize;
try {
//ASSUMPTION: seen <!D
char ch = more();
if(ch != 'O') throw new XmlPullParserException(
"expected <!DOCTYPE", this, null);
ch = more();
if(ch != 'C') throw new XmlPullParserException(
"expected <!DOCTYPE", this, null);
ch = more();
if(ch != 'T') throw new XmlPullParserException(
"expected <!DOCTYPE", this, null);
ch = more();
if(ch != 'Y') throw new XmlPullParserException(
"expected <!DOCTYPE", this, null);
ch = more();
if(ch != 'P') throw new XmlPullParserException(
"expected <!DOCTYPE", this, null);
ch = more();
if(ch != 'E') throw new XmlPullParserException(
"expected <!DOCTYPE", this, null);
posStart = pos;
// do simple and crude scanning for end of doctype
// [28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S? ('['
// (markupdecl | DeclSep)* ']' S?)? '>'
ch = requireNextS();
int nameStart = pos;
ch = readName(ch);
int nameEnd = pos;
ch = skipS(ch);
// [75] ExternalID ::= 'SYSTEM' S SystemLiteral | 'PUBLIC' S PubidLiteral S SystemLiteral
if(ch == 'S' || ch == 'P') {
ch = processExternalId(ch);
ch = skipS(ch);
}
if(ch == '[') {
processInternalSubset();
}
ch = skipS(ch);
if(ch != '>') {
throw new XmlPullParserException(
"expected > to finish <[DOCTYPE but got "+printable(ch), this, null);
}
posEnd = pos - 1;
} finally {
tokenize = oldTokenize;
}
}
protected char processExternalId(char ch)
throws XmlPullParserException, IOException
{
// [75] ExternalID ::= 'SYSTEM' S SystemLiteral | 'PUBLIC' S PubidLiteral S SystemLiteral
// [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'")
// [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'"
// [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%]
//TODO
return ch;
}
protected void processInternalSubset()
throws XmlPullParserException, IOException
{
// [28] ... (markupdecl | DeclSep)* ']' // [WFC: External Subset]
// [28a] DeclSep ::= PEReference | S // [WFC: PE Between Declarations]
// [69] PEReference ::= '%' Name ';' //[WFC: No Recursion] [WFC: In DTD]
while(true) {
char ch = more(); // firs ttime called it will skip initial "["
if(ch == ']') break;
if(ch == '%') {
processPEReference();
} else if(isS(ch)) {
ch = skipS(ch);
} else {
processMarkupDecl(ch);
}
}
}
protected void processPEReference()
throws XmlPullParserException, IOException
{
//TODO
}
protected void processMarkupDecl(char ch)
throws XmlPullParserException, IOException
{
// [29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl | NotationDecl | PI | Comment
// [WFC: PEs in Internal Subset]
//BIG SWITCH statement
if(ch != '<') {
throw new XmlPullParserException("expected < for markupdecl in DTD not "+printable(ch),
this, null);
}
ch = more();
if(ch == '?') {
parsePI();
} else if(ch == '!') {
ch = more();
if(ch == '-') {
// note: if(tokenize == false) posStart/End is NOT changed!!!!
parseComment();
} else {
ch = more();
if(ch == 'A') {
processAttlistDecl(ch); //A-TTLIST
} else if(ch == 'E') {
ch = more();
if(ch == 'L') {
processElementDecl(ch); //EL-EMENT
} else if(ch == 'N') {
processEntityDecl(ch); // EN-TITY
} else {
throw new XmlPullParserException(
"expected ELEMENT or ENTITY after <! in DTD not "+printable(ch),
this, null);
}
} else if(ch == 'N') {
processNotationDecl(ch); //N-OTATION
} else {
throw new XmlPullParserException(
"expected markupdecl after <! in DTD not "+printable(ch),this, null);
}
}
} else {
throw new XmlPullParserException("expected markupdecl in DTD not "+printable(ch),
this, null);
}
}
protected void processElementDecl(char ch)
throws XmlPullParserException, IOException
{
//[45] elementdecl ::= '<!ELEMENT' S Name S contentspec S? '>'
//???? [VC: Unique Element Type Declaration]
// [46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children
// [47] children ::= (choice | seq) ('?' | '*' | '+')?
// [48] cp ::= (Name | choice | seq) ('?' | '*' | '+')?
// [49] choice ::= '(' S? cp ( S? '|' S? cp )+ S? ')'
// [50] seq ::= '(' S? cp ( S? ',' S? cp )* S? ')'
// [51] Mixed ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*'
// | '(' S? '#PCDATA' S? ')'
//assert ch == 'L'
ch = requireNextS();
readName(ch);
ch = requireNextS();
// readContentSpec(ch);
}
protected void processAttlistDecl(char ch)
throws XmlPullParserException, IOException
{
// [52] AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>'
// [53] AttDef ::= S Name S AttType S DefaultDecl
// [54] AttType ::= StringType | TokenizedType | EnumeratedType
// [55] StringType ::= 'CDATA'
// [56] TokenizedType ::= 'ID' | 'IDREF' | 'IDREFS' | 'ENTITY' | 'ENTITIES' | 'NMTOKEN'
// | 'NMTOKENS'
// [57] EnumeratedType ::= NotationType | Enumeration
// [58] NotationType ::= 'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')'
// [59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')'
// [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue)
// [WFC: No < in Attribute Values]
//assert ch == 'A'
}
protected void processEntityDecl(char ch)
throws XmlPullParserException, IOException
{
// [70] EntityDecl ::= GEDecl | PEDecl
// [71] GEDecl ::= '<!ENTITY' S Name S EntityDef S? '>'
// [72] PEDecl ::= '<!ENTITY' S '%' S Name S PEDef S? '>'
// [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?)
// [74] PEDef ::= EntityValue | ExternalID
// [75] ExternalID ::= 'SYSTEM' S SystemLiteral | 'PUBLIC' S PubidLiteral S SystemLiteral
//[9] EntityValue ::= '"' ([^%&"] | PEReference | Reference)* '"'
// | "'" ([^%&'] | PEReference | Reference)* "'"
//assert ch == 'N'
}
protected void processNotationDecl(char ch)
throws XmlPullParserException, IOException
{
// [82] NotationDecl ::= '<!NOTATION' S Name S (ExternalID | PublicID) S? '>'
// [83] PublicID ::= 'PUBLIC' S PubidLiteral
//assert ch == 'N'
}
protected char readName(char ch)
throws XmlPullParserException, IOException
{
if(isNameStartChar(ch)) {
throw new XmlPullParserException(
"XML name must start with name start character not "+printable(ch), this, null);
}
while(isNameChar(ch)) {
ch = more();
}
return ch;
}
}

View File

@ -0,0 +1,396 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.xml.parser;
import com.reandroid.xml.*;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class XMLDocumentParser {
private final XmlPullParser mParser;
private XMLDocument mResDocument;
private XMLElement mCurrentElement;
private boolean mNameSpaceCreated;
private StringBuilder mCurrentText;
private List<XMLComment> mComments;
private int mIndent;
public XMLDocumentParser(XmlPullParser parser){
this.mParser=parser;
}
public XMLDocumentParser(InputStream in) throws XMLParseException {
this(createParser(in));
}
public XMLDocumentParser(File file) throws XMLParseException {
this(createParser(file));
}
public XMLDocumentParser(String text) throws XMLParseException {
this(createParser(text));
}
public XMLDocument parse() throws XMLParseException {
try {
XMLDocument document= parseDocument();
closeFileInputStream();
return document;
} catch (XmlPullParserException | IOException e) {
XMLParseException ex=new XMLParseException(e.getMessage());
ex.setStackTrace(e.getStackTrace());
throw ex;
}
}
private void closeFileInputStream(){
if(!(mParser instanceof MXParser)){
return;
}
MXParser mxParser=(MXParser) mParser;
InputStream inputStream = mxParser.getInputStream();
if(!(inputStream instanceof FileInputStream)){
return;
}
try {
inputStream.close();
} catch (IOException ignored) {
}
}
private XMLDocument parseDocument() throws XmlPullParserException, IOException {
mResDocument=null;
int type;
while ((type=mParser.nextToken()) !=XmlPullParser.END_DOCUMENT){
event(type);
}
event(XmlPullParser.END_DOCUMENT);
if(mResDocument==null){
throw new XmlPullParserException("Failed to parse/empty document");
}
return mResDocument;
}
private void event(int type) {
if (type == XmlPullParser.START_DOCUMENT){
onStartDocument();
}else if (type == XmlPullParser.END_DOCUMENT){
onEndDocument();
}else if (type == XmlPullParser.START_TAG){
onStartTag();
}else if (type == XmlPullParser.END_TAG){
onEndTag();
}else if (type == XmlPullParser.TEXT){
onText();
}else if (type == XmlPullParser.ENTITY_REF){
onEntityRef();
}else if (type == XmlPullParser.COMMENT){
onComment();
}else if (type == XmlPullParser.IGNORABLE_WHITESPACE){
onIgnorableWhiteSpace();
}else {
onUnknownType(type);
}
}
private void onStartDocument(){
mResDocument=new XMLDocument();
mIndent=-1;
}
private void onEndDocument(){
flushComments(null);
applyIndent(mResDocument);
}
private void onStartTag(){
String name=mParser.getName();
flushTextContent();
if(mCurrentElement==null){
if(mResDocument==null){
onStartDocument();
}
mCurrentElement=new XMLElement(name);
mResDocument.setDocumentElement(mCurrentElement);
}else {
mCurrentElement=mCurrentElement.createElement(name);
}
checkIndent();
flushComments(mCurrentElement);
String ns=mParser.getNamespace();
if(!XMLUtil.isEmpty(ns)){
String prefix=mParser.getPrefix();
if(!XMLUtil.isEmpty(prefix)){
String tagName=appendPrefix(prefix,name);
mCurrentElement.setTagName(tagName);
checkNamespace(prefix, ns);
}
}
loadAttributes();
}
private void loadAttributes(){
int max=mParser.getAttributeCount();
for(int i=0; i<max; i++){
onAttribute(i);
}
}
private void onAttribute(int i){
String attrName=mParser.getAttributeName(i);
String attrValue=mParser.getAttributeValue(i);
String prefix=mParser.getAttributePrefix(i);
if(!XMLUtil.isEmpty(prefix)){
prefix=validateNameSpace(prefix, i);
attrName=appendPrefix(prefix, attrName);
checkNamespace(prefix, i);
}
XMLAttribute attr=mCurrentElement.setAttribute(attrName, attrValue);
addIds(attr, i);
}
private void addIds(XMLAttribute attr, int i){
if(attr==null){
return;
}
XmlPullParser parser=mParser;
if(!(parser instanceof AttrIDProvider)){
return;
}
AttrIDProvider idProvider=(AttrIDProvider)parser;
int nameId=idProvider.getAttributeNameResourceId(i);
int valueId=idProvider.getAttributeValueResourceId(i);
attr.setNameId(nameId);
attr.setValueId(valueId);
}
private String appendPrefix(String prefix, String attrName){
if(!prefix.endsWith(":")){
prefix=prefix+":";
}
if(!attrName.startsWith(prefix)){
attrName=prefix+attrName;
}
return attrName;
}
private void checkNamespace(String prefix, int i){
NameSpaceItem nsItem=mCurrentElement.getNameSpaceItemForPrefix(prefix);
if(nsItem!=null){
return;
}
String nsUri=null;
try {
nsUri=mParser.getNamespaceUri(i);
} catch (XmlPullParserException e) {
}
checkNamespace(prefix, nsUri);
}
private String validateNameSpace(String prefix, int i){
XmlPullParser parser=mParser;
if(!(parser instanceof AttrIDProvider)){
return prefix;
}
AttrIDProvider idProvider=(AttrIDProvider)parser;
int resId=idProvider.getAttributeNameResourceId(i);
if(!isResourceId(resId)){
return prefix;
}
boolean isAndroid;
if(!isAndroid(resId)){
if(!"android".equals(prefix)){
return prefix;
}
isAndroid=false;
}else {
if("android".equals(prefix)){
return prefix;
}
isAndroid=true;
}
NameSpaceItem ns;
if(isAndroid){
ns=NameSpaceItem.getAndroid();
}else {
ns=NameSpaceItem.getApp();
}
String prefixNew=ns.getPrefix();
NameSpaceItem nsItem=mCurrentElement.getNameSpaceItemForPrefix(prefixNew);
if(nsItem==null){
mCurrentElement.addNameSpace(ns);
mNameSpaceCreated=true;
}
return prefixNew;
}
private void checkNamespace(String prefix, String nsUri){
NameSpaceItem nsItem=mCurrentElement.getNameSpaceItemForPrefix(prefix);
if(nsItem!=null){
return;
}
nsItem=new NameSpaceItem(prefix, nsUri);
mCurrentElement.addNameSpace(nsItem);
mNameSpaceCreated=true;
}
private void onEndTag(){
flushTextContent();
if(mNameSpaceCreated){
mCurrentElement.applyNameSpaceItems();
mNameSpaceCreated=false;
}
mCurrentElement=mCurrentElement.getParent();
}
private void onText(){
String textContent=mParser.getText();
appendText(textContent);
}
private void appendText(String text){
if(text==null){
return;
}
if(mCurrentText==null){
mCurrentText=new StringBuilder();
}
mCurrentText.append(text);
}
private void flushTextContent(){
if(mCurrentText==null){
return;
}
String text=mCurrentText.toString();
mCurrentText=null;
if(XMLUtil.isEmpty(text)){
return;
}
mCurrentElement.setTextContent(text, true);
}
private void onEntityRef(){
String name=mParser.getName();
if(XMLUtil.isEmpty(name)){
return;
}
appendText("&");
appendText(name);
appendText(";");
}
private void onComment(){
String commentText=mParser.getText();
addComment(commentText);
}
private void addComment(String commentText){
if(XMLUtil.isEmpty(commentText)){
return;
}
XMLComment commentElement=new XMLComment();
commentElement.setCommentText(commentText);
addComment(commentElement);
}
private void addComment(XMLComment ce){
if(ce==null){
return;
}
if(mComments==null){
mComments=new ArrayList<>();
}
mComments.add(ce);
}
private void flushComments(XMLElement element){
if(mComments==null){
return;
}
if(element!=null){
element.addComments(mComments);
}
mComments.clear();
mComments=null;
}
private void onIgnorableWhiteSpace(){
}
private void onIgnore(int type){
}
private void onUnknownType(int type){
String typeName=toTypeName(type);
//System.err.println("Unknown TYPE = "+typeName+" "+type);
}
private String toTypeName(int type){
String[] allTypes=XmlPullParser.TYPES;
if(type<0 || type>=allTypes.length){
return "type:"+type;
}
return allTypes[type];
}
private void checkIndent(){
if(mIndent>=0){
return;
}
String txt=mParser.getText();
if(txt==null){
return;
}
int len=txt.length();
int col=mParser.getColumnNumber();
mIndent=col-len;
if(mIndent<0){
mIndent=0;
}
}
private void applyIndent(XMLDocument resDocument){
if(mIndent<=0 || mIndent>5 || resDocument==null){
mIndent=-1;
return;
}
resDocument.setIndent(mIndent);
mIndent=-1;
}
private static XmlPullParser createParser(String text) throws XMLParseException {
if(text == null){
throw new XMLParseException("Text is null, failed to create XmlPullParser");
}
InputStream in = new ByteArrayInputStream(text.getBytes());
return createParser(in);
}
private static XmlPullParser createParser(File file) throws XMLParseException {
if(file == null){
throw new XMLParseException("File is null, failed to create XmlPullParser");
}
if(!file.isFile()){
throw new XMLParseException("No such file : "+file.getAbsolutePath());
}
InputStream in;
try {
in=new FileInputStream(file);
return createParser(in);
} catch (FileNotFoundException e) {
throw new XMLParseException(e.getMessage());
}
}
private static XmlPullParser createParser(InputStream in) throws XMLParseException {
try {
XmlPullParser parser = new MXParserNonValidating();
parser.setInput(in, null);
return parser;
} catch (XmlPullParserException e) {
throw new XMLParseException(e.getMessage());
}
}
private static boolean isAndroid(int id){
int pkgId=toPackageId(id);
return pkgId>0 && pkgId<=ANDROID_PACKAGE_MAX;
}
private static boolean isResourceId(int id){
int pkgId=toPackageId(id);
return pkgId>0 && pkgId<128;
}
private static int toPackageId(int id){
if(id<=0xff){
return id;
}
return ((id >> 24) & 0xff);
}
private static final int ANDROID_PACKAGE_MAX=3;
}

View File

@ -0,0 +1,24 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reandroid.xml.parser;
import com.reandroid.xml.XMLException;
public class XMLParseException extends XMLException {
public XMLParseException(String msg) {
super(msg);
}
}

View File

@ -0,0 +1,89 @@
/*
* This class is taken from org.xmlpull.*
*
* Check license: http://xmlpull.org
*
*/
/*This package is renamed from org.xmlpull.* to avoid conflicts*/
package com.reandroid.xml.parser;
import java.io.InputStream;
import java.io.IOException;
import java.io.Reader;
public interface XmlPullParser {
String NO_NAMESPACE = "";
int START_DOCUMENT = 0;
int END_DOCUMENT = 1;
int START_TAG = 2;
int END_TAG = 3;
int TEXT = 4;
int CDSECT = 5;
int ENTITY_REF = 6;
int IGNORABLE_WHITESPACE = 7;
int PROCESSING_INSTRUCTION = 8;
int COMMENT = 9;
int DOCDECL = 10;
String [] TYPES = {
"START_DOCUMENT",
"END_DOCUMENT",
"START_TAG",
"END_TAG",
"TEXT",
"CDSECT",
"ENTITY_REF",
"IGNORABLE_WHITESPACE",
"PROCESSING_INSTRUCTION",
"COMMENT",
"DOCDECL"
};
String FEATURE_PROCESS_NAMESPACES = "http://xmlpull.org/v1/doc/features.html#process-namespaces";
String FEATURE_REPORT_NAMESPACE_ATTRIBUTES = "http://xmlpull.org/v1/doc/features.html#report-namespace-prefixes";
String FEATURE_PROCESS_DOCDECL = "http://xmlpull.org/v1/doc/features.html#process-docdecl";
String FEATURE_VALIDATION = "http://xmlpull.org/v1/doc/features.html#validation";
void setFeature(String name, boolean state) throws XmlPullParserException;
boolean getFeature(String name);
void setProperty(String name, Object value) throws XmlPullParserException;
Object getProperty(String name);
void setInput(Reader in) throws XmlPullParserException;
void setInput(InputStream inputStream, String inputEncoding) throws XmlPullParserException;
String getInputEncoding();
void defineEntityReplacementText( String entityName, String replacementText ) throws XmlPullParserException;
int getNamespaceCount(int depth) throws XmlPullParserException;
String getNamespacePrefix(int pos) throws XmlPullParserException;
String getNamespaceUri(int pos) throws XmlPullParserException;
String getNamespace (String prefix);
int getDepth();
String getPositionDescription();
int getLineNumber();
int getColumnNumber();
boolean isWhitespace() throws XmlPullParserException;
String getText ();
char[] getTextCharacters(int [] holderForStartAndLength);
String getNamespace ();
String getName();
String getPrefix();
boolean isEmptyElementTag() throws XmlPullParserException;
int getAttributeCount();
String getAttributeNamespace (int index);
String getAttributeName (int index);
String getAttributePrefix(int index);
String getAttributeType(int index);
boolean isAttributeDefault(int index);
String getAttributeValue(int index);
String getAttributeValue(String namespace, String name);
int getEventType() throws XmlPullParserException;
int next() throws XmlPullParserException, IOException;
int nextToken() throws XmlPullParserException, IOException;
void require(int type, String namespace, String name) throws XmlPullParserException, IOException;
String nextText() throws XmlPullParserException, IOException;
int nextTag() throws XmlPullParserException, IOException;
// public void skipSubTree() throws XmlPullParserException, IOException;
}

View File

@ -0,0 +1,34 @@
/*
* This class is taken from org.xmlpull.*
*
* Check license: http://xmlpull.org
*
*/
/*This package is renamed from org.xmlpull.* to avoid conflicts*/
package com.reandroid.xml.parser;
public class XmlPullParserException extends Exception {
protected Throwable detail;
protected int row = -1;
protected int column = -1;
public XmlPullParserException(String s) {
super(s);
}
public XmlPullParserException(String msg, XmlPullParser parser, Throwable chain) {
super ((msg == null ? "" : msg+" ")
+ (parser == null ? "" : "(position:"+parser.getPositionDescription()+") ")
+ (chain == null ? "" : "caused by: "+chain));
if (parser != null) {
this.row = parser.getLineNumber();
this.column = parser.getColumnNumber();
}
this.detail = chain;
}
public Throwable getDetail() { return detail; }
public int getLineNumber() { return row; }
public int getColumnNumber() { return column; }
}