create styled strings parser

This commit is contained in:
REAndroid 2023-01-06 13:14:49 -05:00
parent 472eb1c46c
commit c49b04cc1b
14 changed files with 624 additions and 466 deletions

View File

@ -22,6 +22,15 @@ package com.reandroid.lib.apk.xmlencoder;
import java.util.regex.Pattern;
public class EncodeUtil {
public static void sortStrings(List<String> stringList){
Comparator<String> cmp=new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
};
stringList.sort(cmp);
}
public static void sortPublicXml(List<File> fileList){
Comparator<File> cmp=new Comparator<File>() {
@Override

View File

@ -15,14 +15,18 @@
*/
package com.reandroid.lib.apk.xmlencoder;
import com.reandroid.lib.arsc.array.StyleArray;
import com.reandroid.lib.arsc.decoder.ValueDecoder;
import com.reandroid.lib.arsc.item.StyleItem;
import com.reandroid.lib.arsc.item.TableString;
import com.reandroid.lib.arsc.pool.TableStringPool;
import com.reandroid.xml.XMLDocument;
import com.reandroid.xml.XMLElement;
import com.reandroid.xml.XMLSpanInfo;
import com.reandroid.xml.XMLSpannable;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
import java.util.*;
public class ValuesStringPoolBuilder {
private final Set<String> stringList;
@ -30,8 +34,56 @@ public class ValuesStringPoolBuilder {
this.stringList=new HashSet<>();
}
public void addTo(TableStringPool stringPool){
if(stringPool.getStringsArray().childesCount()==0){
buildWithStyles(stringPool);
}
stringPool.addStrings(stringList);
stringList.clear();
stringPool.refresh();
}
private void buildWithStyles(TableStringPool stringPool){
List<XMLSpannable> spannableList = buildSpannable();
if(spannableList.size()==0){
return;
}
Map<String, TableString> stringsMap = stringPool
.insertStrings(XMLSpannable.toTextList(spannableList));
List<String> tagList =
new ArrayList<>(XMLSpannable.tagList(spannableList));
EncodeUtil.sortStrings(tagList);
Map<String, TableString> tagsMap =
stringPool.insertStrings(tagList);
StyleArray styleArray = stringPool.getStyleArray();
styleArray.setChildesCount(stringsMap.size());
for(XMLSpannable spannable:spannableList){
TableString tableString=stringsMap.get(spannable.getText());
StyleItem styleItem = styleArray.get(tableString.getIndex());
for(XMLSpanInfo spanInfo:spannable.getSpanInfoList()){
int tagRef=tagsMap.get(spanInfo.tag).getIndex();
styleItem.addStylePiece(tagRef, spanInfo.start, spanInfo.end);
}
}
stringPool.refreshUniqueIdMap();
}
private List<XMLSpannable> buildSpannable(){
List<XMLSpannable> results=new ArrayList<>();
Set<String> removeList=new HashSet<>();
for(String text:stringList){
XMLSpannable spannable=XMLSpannable.parse(text);
if(spannable!=null){
results.add(spannable);
removeList.add(text);
}
}
stringList.removeAll(removeList);
XMLSpannable.sort(results);
return results;
}
public void scanValuesDirectory(File dir){
addStringsFile(new File(dir, "strings.xml"));

View File

@ -102,9 +102,9 @@ public abstract class BaseStringPool<T extends StringItem> extends BaseChunk imp
}
List<String> sortedList=new ArrayList<>(stringList);
sortedList.sort(this);
insertStrings(sortedList);
insertStringList(sortedList);
}
private void insertStrings(List<String> stringList){
private void insertStringList(List<String> stringList){
StringArray<T> stringsArray = getStringsArray();
int initialSize=stringsArray.childesCount();
stringsArray.ensureSize(initialSize + stringList.size());
@ -117,6 +117,23 @@ public abstract class BaseStringPool<T extends StringItem> extends BaseChunk imp
}
refreshUniqueIdMap();
}
public Map<String, T> insertStrings(List<String> stringList){
Map<String, T> results=new HashMap<>();
StringArray<T> stringsArray = getStringsArray();
int initialSize=stringsArray.childesCount();
stringsArray.ensureSize(initialSize + stringList.size());
int size=stringsArray.childesCount();
int j=0;
for (int i=initialSize;i<size;i++){
T item=stringsArray.get(i);
String str=stringList.get(j);
item.set(str);
results.put(str, item);
j++;
}
refreshUniqueIdMap();
return results;
}
// call this after modifying string values
public void refreshUniqueIdMap(){
mUniqueMap.clear();

View File

@ -20,20 +20,15 @@ import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
public class XMLAttribute {
public class XMLAttribute extends XMLNode{
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;
}
@ -55,18 +50,6 @@ public class XMLAttribute {
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;
}
@ -117,19 +100,9 @@ public class XMLAttribute {
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 {
public boolean write(Writer writer, boolean newLineAttributes) throws IOException {
if(isEmpty()){
return false;
}
@ -143,16 +116,40 @@ public class XMLAttribute {
return true;
}
@Override
public String toString(){
public String toText(int indent, boolean newLineAttributes) {
if(isEmpty()){
return null;
}
StringWriter writer=new StringWriter();
try {
write(writer);
} catch (IOException e) {
} catch (IOException ignored) {
}
writer.flush();
return writer.toString();
}
@Override
public int hashCode(){
String name=getName();
if(name==null){
name="";
}
name=getClass().getName()+name;
return name.hashCode();
}
@Override
public boolean equals(Object obj){
if(obj instanceof XMLAttribute){
XMLAttribute attr=(XMLAttribute)obj;
if(isEmpty()){
return attr.isEmpty();
}
return getName().equals(attr.getName());
}
return false;
}
@Override
public String toString(){
return toText();
}
}

View File

@ -31,15 +31,7 @@ public class XMLComment extends XMLElement {
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;
}

View File

@ -20,7 +20,7 @@ import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Comparator;
public class XMLDocument {
public class XMLDocument extends XMLNode{
private XMLElement mDocumentElement;
private Object mTag;
private String mName;
@ -189,6 +189,7 @@ public class XMLDocument {
writer.close();
return result;
}
@Override
public boolean write(Writer writer, boolean newLineAttributes) throws IOException{
boolean has_header=appendDocumentAttribute(writer);
if(has_header){
@ -196,12 +197,7 @@ public class XMLDocument {
}
return appendDocumentElement(writer, newLineAttributes);
}
public String toText(){
return toText(1, false);
}
public String toText(boolean newLineAttributes){
return toText(1, newLineAttributes);
}
@Override
public String toText(int indent, boolean newLineAttributes){
StringWriter writer=new StringWriter();
setIndent(indent);

View File

@ -21,20 +21,19 @@ import java.io.StringWriter;
import java.io.Writer;
import java.util.*;
public class XMLElement {
public class XMLElement extends XMLNode{
static final long DEBUG_TO_STRING=500;
private String mTagName;
private XMLTextAttribute mTextAttribute;
private List<XMLAttribute> mAttributes;
private List<XMLElement> mChildes;
private final List<XMLAttribute> mAttributes = new ArrayList<>();
private final List<XMLElement> mChildes = new ArrayList<>();
private List<XMLComment> mComments;
private final List<XMLText> mTexts = new ArrayList<>();
private XMLElement mParent;
private int mIndent;
private Object mTag;
private int mResId;
private float mIndentScale;
private String mUniqueAttrName;
private List<String> mUniqueNameValues;
private String mStart;
private String mStartPrefix;
private String mEnd;
@ -48,6 +47,19 @@ public class XMLElement {
setDefaultStartEnd();
}
public void addText(XMLText text){
if(text==null){
return;
}
mTexts.add(text);
super.addChildNode(text);
}
private void appendText(String text){
if(text==null || text.length()==0){
return;
}
addText(new XMLText(text));
}
public String getTagNamePrefix(){
int i=mTagName.indexOf(":");
if(i>0){
@ -68,45 +80,6 @@ public class XMLElement {
this.mStartPrefix="/";
this.mEndPrefix="/";
}
public XMLElement removeChild(XMLElement element){
if(mChildes==null || element==null){
return null;
}
int i=mChildes.indexOf(element);
if(i<0){
return null;
}
XMLElement result=mChildes.get(i);
mChildes.remove(i);
result.setParent(null);
return result;
}
public XMLElement getFirstChildWithAttrName(String name){
if(mChildes==null || name==null){
return null;
}
for(XMLElement element:mChildes){
XMLAttribute attr=element.getAttribute(name);
if(attr!=null){
return element;
}
}
return null;
}
public XMLElement getFirstChildWithAttr(String attrName, String attrValue){
if(mChildes==null || attrName==null || attrValue==null){
return null;
}
for(XMLElement element:mChildes){
XMLAttribute attr=element.getAttribute(attrName);
if(attr!=null){
if(attrValue.equals(attr.getValue())){
return element;
}
}
}
return null;
}
public void applyNameSpaceItems(){
if(nameSpaceItems!=null){
for(NameSpaceItem nsItem:nameSpaceItems){
@ -162,82 +135,24 @@ public class XMLElement {
}
return null;
}
public void setStart(String start) {
void setStart(String start) {
this.mStart = start;
}
public void setEnd(String end) {
void setEnd(String end) {
this.mEnd = end;
}
public void setStartPrefix(String pfx) {
void setStartPrefix(String pfx) {
if(pfx==null){
pfx="";
}
this.mStartPrefix = pfx;
}
public void setEndPrefix(String pfx) {
void setEndPrefix(String pfx) {
if(pfx==null){
pfx="";
}
this.mEndPrefix = pfx;
}
public void setUniqueAttrName(String attrName){
clearUniqueNameValues();
if(XMLUtil.isEmpty(attrName)){
mUniqueAttrName=null;
return;
}
mUniqueAttrName=attrName;
}
public String getUniqueAttrName(){
return mUniqueAttrName;
}
private void clearUniqueNameValues(){
if(mUniqueNameValues!=null){
mUniqueNameValues.clear();
mUniqueNameValues=null;
}
}
private void addUniqueNameValues(XMLElement element){
if(element==null){
return;
}
XMLAttribute baseAttr=element.getAttribute(getUniqueAttrName());
if(baseAttr==null){
return;
}
addUniqueNameValues(baseAttr.getValue());
}
private void addUniqueNameValues(String nameVal){
if(XMLUtil.isEmpty(nameVal)){
return;
}
if(mUniqueNameValues==null){
mUniqueNameValues=new ArrayList<>();
}
mUniqueNameValues.add(nameVal);
}
private boolean shouldCheckUniqueElement(){
return mUniqueAttrName!=null;
}
private boolean containsUniqueNameValue(XMLElement element){
if(element==null){
return false;
}
return containsUniqueNameValue(element.getAttribute(getUniqueAttrName()));
}
private boolean containsUniqueNameValue(XMLAttribute baseAttr){
if(baseAttr==null){
return false;
}
return containsUniqueNameValue(baseAttr.getValue());
}
private boolean containsUniqueNameValue(String nameVal){
if(mUniqueNameValues==null|| XMLUtil.isEmpty(nameVal)){
return false;
}
return mUniqueNameValues.contains(nameVal);
}
void setIndentScale(float scale){
mIndentScale=scale;
}
@ -254,82 +169,11 @@ public class XMLElement {
public void setResourceId(int id){
mResId=id;
}
public boolean containsSameChild(XMLElement baseElement){
if(baseElement==null||mChildes==null){
return false;
}
for(XMLElement ch:mChildes){
if(baseElement.isSame(ch)){
return true;
}
}
return false;
}
private String getUniqueId(){
StringBuilder builder=new StringBuilder(getTagName());
builder.append(attributesToString(false));
return builder.toString();
}
private boolean isSame(XMLElement baseElement){
if(baseElement==null){
return false;
}
String s1=getUniqueId();
String s2=baseElement.getUniqueId();
return XMLUtil.isStringEqual(s1,s2);
}
public XMLElement cloneElement(){
XMLElement baseElement=onCloneElement();
baseElement.setTag(getTag());
cloneAllAttributes(baseElement);
if(mChildes!=null){
for(XMLElement element:mChildes){
baseElement.addChildNoCheck(element.cloneElement());
}
}
if(mComments!=null){
for(XMLComment ce:mComments){
baseElement.addComment((XMLComment) ce.cloneElement());
}
}
return baseElement;
}
void cloneAllAttributes(XMLElement element){
if(mAttributes!=null){
for(XMLAttribute attr:mAttributes){
element.addAttributeNoCheck(attr.cloneAttr());
}
}
XMLTextAttribute textAttribute=mTextAttribute;
if(textAttribute!=null){
element.mTextAttribute=mTextAttribute.cloneAttr();
}
}
XMLElement onCloneElement(){
return new XMLElement(getTagName());
}
public XMLElement createElement(String tag) {
XMLElement baseElement=new XMLElement(tag);
addChildNoCheck(baseElement);
return baseElement;
}
public boolean containsChild(XMLElement baseElement){
if(baseElement==null||mChildes==null){
return false;
}
return mChildes.contains(baseElement);
}
public void addChild(XMLElement[] elements) {
if(elements==null){
return;
}
int max=elements.length;
for(int i=0;i<max;i++){
XMLElement baseElement=elements[i];
addChild(baseElement);
}
}
public void addChild(Collection<XMLElement> elements) {
if(elements==null){
return;
@ -338,8 +182,14 @@ public class XMLElement {
addChild(element);
}
}
public void addChild(XMLElement baseElement) {
addChildNoCheck(baseElement);
public void addChild(XMLElement child) {
addChildNoCheck(child);
}
private void clearChildElements(){
mChildes.clear();
}
private void clearTexts(){
mTexts.clear();
}
public XMLComment getCommentAt(int index){
if(mComments==null || index<0){
@ -352,7 +202,7 @@ public class XMLElement {
}
public void hideComments(boolean recursive, boolean hide){
hideComments(hide);
if(recursive && mChildes!=null){
if(recursive){
for(XMLElement child:mChildes){
child.hideComments(recursive, hide);
}
@ -397,75 +247,30 @@ public class XMLElement {
mComments.add(commentElement);
commentElement.setIndent(getIndent());
commentElement.setParent(this);
super.addChildNode(commentElement);
}
public void removeAllChildes(){
if(mChildes==null){
return;
}
public void removeChildElements(){
mChildes.clear();
}
public int getChildesCount(){
if(mChildes==null){
return 0;
public List<XMLAttribute> listAttributes(){
return mAttributes;
}
public int getChildesCount(){
return mChildes.size();
}
public XMLElement getFirstElementByTagName(String tagName){
if(tagName==null||mChildes==null){
return null;
}
for(XMLElement element:mChildes){
if(tagName.equals(element.getTagName())){
return element;
}
}
return null;
}
public XMLElement[] getElementsByTagName(String tagName){
if(tagName==null||mChildes==null){
return null;
}
List<XMLElement> results=new ArrayList<>();
for(XMLElement element:mChildes){
if(tagName.equals(element.getTagName())){
results.add(element);
}
}
int max=results.size();
if(max==0){
return null;
}
return results.toArray(new XMLElement[max]);
}
public XMLElement[] getAllChildes(){
if(mChildes==null){
return null;
}
int max=mChildes.size();
if(max==0){
return null;
}
return mChildes.toArray(new XMLElement[max]);
public List<XMLElement> listChildElements(){
return mChildes;
}
public XMLElement getChildAt(int index){
if(mChildes==null||index<0){
return null;
}
if(index>=mChildes.size()){
if(index<0 || index>=mChildes.size()){
return null;
}
return mChildes.get(index);
}
public int getAttributeCount(){
if(mAttributes==null){
return 0;
}
return mAttributes.size();
}
public XMLAttribute getAttributeAt(int index){
if(mAttributes==null||index<0){
return null;
}
if(index>=mAttributes.size()){
return null;
}
@ -514,9 +319,6 @@ public class XMLElement {
return attr.getValueBool();
}
public XMLAttribute getAttribute(String name){
if(mAttributes==null){
return null;
}
if(XMLUtil.isEmpty(name)){
return null;
}
@ -585,42 +387,22 @@ public class XMLElement {
if(exist!=null){
return;
}
if(mAttributes==null){
mAttributes=new ArrayList<>();
}
mAttributes.add(attr);
}
private void addAttributeNoCheck(XMLAttribute attr){
if(attr==null){
if(attr==null || attr.isEmpty()){
return;
}
if(XMLUtil.isEmpty(attr.getName())){
return;
}
if(mAttributes==null){
mAttributes=new ArrayList<>();
}
mAttributes.add(attr);
}
public void sortChildes(Comparator<XMLElement> comparator){
if(mChildes==null||comparator==null){
if(comparator==null){
return;
}
mChildes.sort(comparator);
}
public void sortAttributesWithChildes(Comparator<XMLAttribute> comparator){
if(comparator==null){
return;
}
sortAttributes(comparator);
if(mChildes!=null){
for(XMLElement element:mChildes){
element.sortAttributesWithChildes(comparator);
}
}
}
public void sortAttributes(Comparator<XMLAttribute> comparator){
if(mAttributes==null || comparator==null){
if(comparator==null){
return;
}
mAttributes.sort(comparator);
@ -631,16 +413,14 @@ public class XMLElement {
void setParent(XMLElement baseElement){
mParent=baseElement;
}
private void addChildNoCheck(XMLElement baseElement){
if(baseElement==null){
private void addChildNoCheck(XMLElement child){
if(child==null || child == this){
return;
}
if(mChildes==null){
mChildes=new ArrayList<>();
}
baseElement.setParent(this);
baseElement.setIndent(getChildIndent());
mChildes.add(baseElement);
child.setParent(this);
child.setIndent(getChildIndent());
mChildes.add(child);
super.addChildNode(child);
}
public int getLevel(){
int rs=0;
@ -652,10 +432,13 @@ public class XMLElement {
return rs;
}
int getIndent(){
if(hasTextContent()){
return 0;
}
return mIndent;
}
int getChildIndent(){
if(mIndent<=0){
if(mIndent<=0 || hasTextContent()){
return 0;
}
int rs=mIndent+1;
@ -671,11 +454,9 @@ public class XMLElement {
}
public void setIndent(int indent){
mIndent=indent;
if(mChildes!=null){
int chIndent=getChildIndent();
for(XMLElement be:mChildes){
be.setIndent(chIndent);
}
for(XMLElement child:mChildes){
child.setIndent(chIndent);
}
if(mComments!=null){
for(XMLComment ce:mComments){
@ -683,22 +464,6 @@ public class XMLElement {
}
}
}
private String getAttributesIndentText(){
int i=getLevel()+1;
String tagName=getTagName();
if(tagName!=null){
i+=tagName.length();
}
if(i>12){
i=12;
}
tagName="";
while (tagName.length()<i){
tagName+=" ";
}
String rs=getIndentText();
return rs+tagName;
}
private boolean appendAttributesIndentText(Writer writer) throws IOException {
int i=0;
String tagName=getTagName();
@ -770,18 +535,32 @@ public class XMLElement {
mTag =tag;
}
public String getTextContent(){
return getTextContent(false);
return getTextContent(true);
}
public String getTextContent(boolean unEscape){
XMLTextAttribute textAttribute= getTextAttr();
return textAttribute.getText(unEscape);
String text=buildTextContent();
if(unEscape){
text=XMLUtil.unEscapeXmlChars(text);
}
public XMLTextAttribute getTextAttribute(){
String txt=getTextContent();
if(txt==null){
return text;
}
private String buildTextContent(){
if(!hasTextContent()){
return null;
}
return mTextAttribute;
StringWriter writer=new StringWriter();
for(XMLNode child:getChildNodes()){
try {
child.write(writer, false);
} catch (IOException ignored) {
}
}
writer.flush();
try {
writer.close();
} catch (IOException ignored) {
}
return writer.toString();
}
XMLTextAttribute getTextAttr(){
if(mTextAttribute==null){
@ -789,43 +568,27 @@ public class XMLElement {
}
return mTextAttribute;
}
private boolean appendTextContent(Writer writer) throws IOException {
if(mTextAttribute==null){
return false;
private void appendTextContent(Writer writer) throws IOException {
for(XMLNode child:getChildNodes()){
if(child instanceof XMLElement){
((XMLElement)child).setIndent(0);
}
return mTextAttribute.write(writer);
}
private boolean hasTextContent() {
if(mTextAttribute==null){
return false;
}
return !mTextAttribute.isEmpty();
}
private void appendText(String text){
String old= getTextContent();
if (old!=null){
if(text!=null){
setTextContent(old+text);
return;
child.write(writer, false);
}
}
setTextContent(text);
public boolean hasTextContent() {
return mTexts.size()>0;
}
public XMLAttribute setTextContent(String text){
return setTextContent(text, true);
public void setTextContent(String text){
setTextContent(text, true);
}
public XMLAttribute setTextContent(String text, boolean escape){
XMLTextAttribute textAttribute= getTextAttr();
if(XMLUtil.isEmpty(text)){
textAttribute.setText(null);
//textAttribute.setText(text);
}else {
public void setTextContent(String text, boolean escape){
clearChildElements();
clearTexts();
if(escape){
text=XMLUtil.escapeXmlChars(text);
}
textAttribute.setText(text);
}
return textAttribute;
appendText(text);
}
private boolean appendAttributes(Writer writer, boolean newLineAttributes) throws IOException {
if(mAttributes==null){
@ -851,50 +614,24 @@ public class XMLElement {
}
return addedOnce;
}
private String attributesToString(boolean newLineAttributes){
if(mAttributes==null){
return null;
}
StringBuilder builder=new StringBuilder();
boolean addedOnce=false;
for(XMLAttribute attr:mAttributes){
if(attr.isEmpty()){
continue;
}
if(addedOnce){
if(newLineAttributes){
builder.append("\n");
builder.append(getAttributesIndentText());
}else{
builder.append(" ");
}
}
builder.append(attr.toString());
addedOnce=true;
}
if(addedOnce){
return builder.toString();
}
return null;
}
boolean isEmpty(){
if(mTagName!=null){
return false;
}
if(mAttributes!=null && mAttributes.size()>0){
if(mAttributes.size()>0){
return false;
}
if(mComments!=null && mComments.size()>0){
return false;
}
return getTextContent()==null;
}
private boolean canAppendChildes(){
if(mChildes==null){
if(mTexts.size()>0){
return false;
}
for(XMLElement be:mChildes){
if (!be.isEmpty()){
return true;
}
private boolean canAppendChildes(){
for(XMLElement child:mChildes){
if (!child.isEmpty()){
return true;
}
}
@ -921,22 +658,19 @@ public class XMLElement {
return addedOnce;
}
private boolean appendChildes(Writer writer, boolean newLineAttributes) throws IOException {
if(mChildes==null){
return false;
}
boolean appendPrevious=true;
boolean addedOnce=false;
for(XMLElement be:mChildes){
for(XMLElement child:mChildes){
if(stopWriting(writer)){
break;
}
if(be.isEmpty()){
if(child.isEmpty()){
continue;
}
if(appendPrevious){
writer.write(XMLUtil.NEW_LINE);
}
appendPrevious=be.write(writer, newLineAttributes);
appendPrevious=child.write(writer, newLineAttributes);
if(!addedOnce && appendPrevious){
addedOnce=true;
}
@ -954,6 +688,7 @@ public class XMLElement {
}
return false;
}
@Override
public boolean write(Writer writer, boolean newLineAttributes) throws IOException {
if(isEmpty()){
return false;
@ -973,20 +708,15 @@ public class XMLElement {
}
appendAttributes(writer, newLineAttributes);
boolean useEndTag=false;
if(canAppendChildes()){
writer.write(mEnd);
appendChildes(writer, newLineAttributes);
useEndTag=true;
}
boolean hasTextCon=hasTextContent();
if(hasTextCon){
if(!useEndTag){
writer.write(mEnd);
}else {
writer.write(XMLUtil.NEW_LINE);
}
appendTextContent(writer);
useEndTag=true;
}else if(canAppendChildes()){
writer.write(mEnd);
appendChildes(writer, newLineAttributes);
useEndTag=true;
}
if(useEndTag){
if(!hasTextCon){
@ -996,19 +726,13 @@ public class XMLElement {
writer.write(mStart);
writer.write(mStartPrefix);
writer.write(getTagName());
writer.write(mEnd);
}else {
writer.write(mEndPrefix);
writer.write(mEnd);
}
writer.write(mEnd);
return true;
}
public String toText(){
return toText(1, false);
}
public String toText(boolean newLineAttributes){
return toText(1, newLineAttributes);
}
@Override
public String toText(int indent, boolean newLineAttributes){
StringWriter writer=new StringWriter();
setIndent(indent);
@ -1020,6 +744,24 @@ public class XMLElement {
}
return writer.toString();
}
protected List<XMLNode> listSpannable(){
List<XMLNode> results = new ArrayList<>();
for(XMLNode child:getChildNodes()){
if((child instanceof XMLElement) || (child instanceof XMLText)){
results.add(child);
}
}
return results;
}
protected String getSpannableText() {
StringBuilder builder = new StringBuilder();
builder.append(getTagName());
for(XMLAttribute attribute:listAttributes()){
builder.append(' ');
builder.append(attribute.toText(0, false));
}
return builder.toString();
}
@Override
public String toString(){
StringWriter strWriter=new StringWriter();
@ -1032,23 +774,4 @@ public class XMLElement {
return strWriter.toString();
}
public static List<XMLElement> getAllElementsRecursive(XMLElement parent){
List<XMLElement> results=new ArrayList<>();
if(parent==null){
return results;
}
XMLElement[] allChildes=parent.getAllChildes();
if(allChildes==null){
return results;
}
int max=allChildes.length;
for(int i=0;i<max;i++){
XMLElement child=allChildes[i];
results.add(child);
results.addAll(getAllElementsRecursive(child));
}
return results;
}
}

View File

@ -0,0 +1,58 @@
/*
* 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;
import java.util.ArrayList;
import java.util.List;
public abstract class XMLNode {
private final List<XMLNode> mChildNodes = new ArrayList<>();
void addChildNode(XMLNode xmlNode){
if(xmlNode!=null && canAdd(xmlNode)){
mChildNodes.add(xmlNode);
}
}
boolean canAdd(XMLNode xmlNode){
return !mChildNodes.contains(xmlNode);
}
boolean contains(XMLNode xmlNode){
return mChildNodes.contains(xmlNode);
}
void removeChildNode(XMLNode xmlNode){
int i = mChildNodes.indexOf(xmlNode);
while (i>=0){
i = mChildNodes.indexOf(xmlNode);
}
mChildNodes.remove(xmlNode);
}
public List<XMLNode> getChildNodes() {
return mChildNodes;
}
public boolean write(Writer writer) throws IOException {
return write(writer, false);
}
public String toText(){
return toText(1, false);
}
public String toText(boolean newLineAttributes){
return toText(1, newLineAttributes);
}
public abstract boolean write(Writer writer, boolean newLineAttributes) throws IOException;
public abstract String toText(int indent, boolean newLineAttributes);
}

View File

@ -0,0 +1,27 @@
/*
* 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 XMLSpanInfo {
public final String tag;
public final int start;
public int end;
public XMLSpanInfo(String tag, int start, int end){
this.tag=tag;
this.start=start;
this.end=end;
}
}

View File

@ -0,0 +1,139 @@
/*
* 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.XMLSpanParser;
import java.util.*;
public class XMLSpannable implements Comparable<XMLSpannable>{
private XMLElement mElement;
private String mText;
private List<XMLSpanInfo> mSpanInfoList;
private XMLSpannable(XMLElement element){
this.mElement=element;
}
public boolean isValid(){
List<XMLSpanInfo> spanInfoList = getSpanInfoList();
if(spanInfoList.size()==0){
return false;
}
for(XMLSpanInfo spanInfo:spanInfoList){
if(spanInfo.end<spanInfo.start){
return false;
}
}
return true;
}
public String getText(){
if(mText==null){
buildSpanInfo();
}
return mText;
}
public List<XMLSpanInfo> getSpanInfoList(){
if(mSpanInfoList==null){
buildSpanInfo();
}
return mSpanInfoList;
}
private void buildSpanInfo(){
mSpanInfoList=new ArrayList<>();
StringBuilder builder=new StringBuilder();
buildSpanInfo(mElement, builder);
mText=builder.toString();
mElement=null;
}
private void buildSpanInfo(XMLElement element, StringBuilder builder){
XMLSpanInfo info=null;
for(XMLNode node:element.listSpannable()){
if(info!=null){
info.end=builder.length();
info=null;
}
if(node instanceof XMLText){
builder.append(((XMLText)node).getText());
continue;
}
XMLElement child = (XMLElement) node;
info=new XMLSpanInfo(
child.getSpannableText(),
builder.length(), 0);
mSpanInfoList.add(info);
buildSpanInfo(child, builder);
}
if(info!=null){
info.end=builder.length();
}
}
@Override
public int compareTo(XMLSpannable xmlSpannable) {
return getText().compareTo(xmlSpannable.getText());
}
public static XMLSpannable parse(String text){
if(!hasStyle(text)){
return null;
}
try {
XMLSpannable spannable=new XMLSpannable(PARSER.parse(text));
if(spannable.isValid()){
return spannable;
}
} catch (Exception ignored) {
}
return null;
}
public static Set<String> tagList(Collection<XMLSpannable> spannableList){
Set<String> results=new HashSet<>();
for(XMLSpannable xmlSpannable:spannableList){
for(XMLSpanInfo spanInfo: xmlSpannable.getSpanInfoList()){
results.add(spanInfo.tag);
}
}
return results;
}
public static List<String> toTextList(Collection<XMLSpannable> spannableList){
List<String> results=new ArrayList<>(spannableList.size());
for(XMLSpannable xmlSpannable:spannableList){
results.add(xmlSpannable.getText());
}
return results;
}
public static void sort(List<XMLSpannable> spannableList){
Comparator<XMLSpannable> cmp=new Comparator<XMLSpannable>() {
@Override
public int compare(XMLSpannable s1, XMLSpannable s2) {
return s1.compareTo(s2);
}
};
spannableList.sort(cmp);
}
private static boolean hasStyle(String text){
if(text==null){
return false;
}
int i=text.indexOf('<');
if(i<0){
return false;
}
i=text.indexOf('>');
return i>1;
}
private static final XMLSpanParser PARSER=new XMLSpanParser();
}

View File

@ -0,0 +1,57 @@
/*
* 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 XMLText extends XMLNode{
private String text;
public XMLText(String text){
this.text=text;
}
public XMLText(){
this(null);
}
public String getText(){
return getText(true);
}
public String getText(boolean unEscape){
if(unEscape){
return XMLUtil.unEscapeXmlChars(text);
}
return text;
}
public void setText(String text){
this.text=XMLUtil.escapeXmlChars(text);
}
@Override
public boolean write(Writer writer, boolean newLineAttributes) throws IOException {
if(!XMLUtil.isEmpty(this.text)){
writer.write(this.text);
return true;
}
return false;
}
@Override
public String toText(int indent, boolean newLineAttributes) {
return getText(false);
}
@Override
public String toString(){
return getText();
}
}

View File

@ -28,13 +28,6 @@ public class XmlHeaderElement extends XMLElement {
initializeStartEnd();
setDefaultAttr();
}
@Override
XMLElement onCloneElement(){
return new XmlHeaderElement(this);
}
@Override
void cloneAllAttributes(XMLElement element){
}
private void copyAll(XmlHeaderElement element){
if(element==null){
return;

View File

@ -260,10 +260,10 @@ public class XMLDocumentParser {
}
String text=mCurrentText.toString();
mCurrentText=null;
if(XMLUtil.isEmpty(text)){
if(text.trim().length()==0 && !mCurrentElement.hasTextContent()){
return;
}
mCurrentElement.setTextContent(text, true);
mCurrentElement.addText(new XMLText(text));
}
private void onEntityRef(){
String name=mParser.getName();

View File

@ -0,0 +1,98 @@
/*
* 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.IOException;
import java.io.StringReader;
public class XMLSpanParser {
private final XmlPullParser mParser;
private XMLElement mCurrentElement;
public XMLSpanParser(){
this.mParser = new MXParserNonValidating();
}
public XMLElement parse(String text) throws XMLException {
try {
text="<spannable-parser>"+text+"</spannable-parser>";
parseString(text);
} catch (XmlPullParserException|IOException ex) {
throw new XMLException(ex.getMessage());
}
XMLElement element=mCurrentElement;
mCurrentElement=null;
return element;
}
private void parseString(String text) throws XmlPullParserException, IOException {
mCurrentElement=null;
StringReader reader=new StringReader(text);
this.mParser.setInput(reader);
int type;
while ((type=mParser.nextToken()) !=XmlPullParser.END_DOCUMENT){
event(type);
}
}
private void event(int type) {
if (type == XmlPullParser.START_DOCUMENT){
onStartDocument();
}else if (type == XmlPullParser.START_TAG){
onStartTag();
}else if (type == XmlPullParser.END_TAG){
onEndTag();
}else if (type == XmlPullParser.TEXT){
onText();
}
}
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);
mCurrentElement.setAttribute(attrName, attrValue);
}
private void onStartTag() {
String name=mParser.getName();
if(mCurrentElement==null){
mCurrentElement=new XMLElement(name);
}else {
mCurrentElement=mCurrentElement.createElement(name);
}
loadAttributes();
}
private void onEndTag() {
XMLElement parent=mCurrentElement.getParent();
if(parent!=null){
mCurrentElement=parent;
}
}
private void onText() {
String text=mParser.getText();
if(text!=null && text.length()>0){
mCurrentElement.addText(new XMLText(text));
}
}
private void onStartDocument() {
this.mCurrentElement=null;
}
}