mirror of
https://github.com/revanced/ARSCLib.git
synced 2025-05-01 06:34:26 +02:00
Merge classes from AndroidXml
* src: https://github.com/REAndroid/AndroidXml
This commit is contained in:
parent
754e1c10e6
commit
fb9a14113b
79
src/main/java/com/reandroid/xml/ElementWriter.java
Executable file
79
src/main/java/com/reandroid/xml/ElementWriter.java
Executable 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;
|
||||
}
|
||||
}
|
156
src/main/java/com/reandroid/xml/NameSpaceItem.java
Executable file
156
src/main/java/com/reandroid/xml/NameSpaceItem.java
Executable 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";
|
||||
|
||||
}
|
117
src/main/java/com/reandroid/xml/SchemaAttr.java
Executable file
117
src/main/java/com/reandroid/xml/SchemaAttr.java
Executable 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*$");
|
||||
}
|
158
src/main/java/com/reandroid/xml/XMLAttribute.java
Executable file
158
src/main/java/com/reandroid/xml/XMLAttribute.java
Executable 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();
|
||||
}
|
||||
}
|
91
src/main/java/com/reandroid/xml/XMLComment.java
Executable file
91
src/main/java/com/reandroid/xml/XMLComment.java
Executable 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;
|
||||
}
|
||||
}
|
261
src/main/java/com/reandroid/xml/XMLDocument.java
Executable file
261
src/main/java/com/reandroid/xml/XMLDocument.java
Executable 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);
|
||||
}
|
||||
|
||||
}
|
1054
src/main/java/com/reandroid/xml/XMLElement.java
Executable file
1054
src/main/java/com/reandroid/xml/XMLElement.java
Executable file
File diff suppressed because it is too large
Load Diff
22
src/main/java/com/reandroid/xml/XMLException.java
Executable file
22
src/main/java/com/reandroid/xml/XMLException.java
Executable 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);
|
||||
}
|
||||
}
|
92
src/main/java/com/reandroid/xml/XMLTextAttribute.java
Executable file
92
src/main/java/com/reandroid/xml/XMLTextAttribute.java
Executable 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();
|
||||
}
|
||||
}
|
162
src/main/java/com/reandroid/xml/XMLUtil.java
Executable file
162
src/main/java/com/reandroid/xml/XMLUtil.java
Executable 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("&", "&");
|
||||
str=str.replaceAll("<", "<");
|
||||
str=str.replaceAll(">", ">");
|
||||
str=str.replaceAll("&", "&");
|
||||
str=str.replaceAll("<", "<");
|
||||
str=str.replaceAll(">", ">");
|
||||
return str;
|
||||
}
|
||||
public static String escapeQuote(String str){
|
||||
if(str==null){
|
||||
return null;
|
||||
}
|
||||
str=str.replaceAll("\"", """);
|
||||
return str;
|
||||
}
|
||||
public static String unEscapeXmlChars(String str){
|
||||
if(str==null){
|
||||
return null;
|
||||
}
|
||||
str=str.replaceAll("&", "&");
|
||||
str=str.replaceAll("<", "<");
|
||||
str=str.replaceAll(">", ">");
|
||||
str=str.replaceAll(""", "\"");
|
||||
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*[^<>]+>)");
|
||||
|
||||
}
|
110
src/main/java/com/reandroid/xml/XmlHeaderElement.java
Executable file
110
src/main/java/com/reandroid/xml/XmlHeaderElement.java
Executable 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;
|
||||
}
|
||||
}
|
21
src/main/java/com/reandroid/xml/parser/AttrIDProvider.java
Executable file
21
src/main/java/com/reandroid/xml/parser/AttrIDProvider.java
Executable 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);
|
||||
}
|
2726
src/main/java/com/reandroid/xml/parser/MXParser.java
Normal file
2726
src/main/java/com/reandroid/xml/parser/MXParser.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
396
src/main/java/com/reandroid/xml/parser/XMLDocumentParser.java
Executable file
396
src/main/java/com/reandroid/xml/parser/XMLDocumentParser.java
Executable 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;
|
||||
}
|
24
src/main/java/com/reandroid/xml/parser/XMLParseException.java
Executable file
24
src/main/java/com/reandroid/xml/parser/XMLParseException.java
Executable 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);
|
||||
}
|
||||
}
|
89
src/main/java/com/reandroid/xml/parser/XmlPullParser.java
Normal file
89
src/main/java/com/reandroid/xml/parser/XmlPullParser.java
Normal 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;
|
||||
}
|
||||
|
@ -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; }
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user