package com.mozz.htmlnative.dom;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import com.mozz.htmlnative.HNLog;
import com.mozz.htmlnative.css.AttrsSet;
import com.mozz.htmlnative.parser.ParseCallback;
import com.mozz.htmlnative.utils.ParametersUtils;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public final class HNDomTree implements ParseCallback, AttrsSet.AttrsOwner, DomElement {
private static final String TREE_ORDER_PARAMETER = "order";
private int mDepth;
private HNDomTree mParent;
/**
* Represent the appearance position in HNativeEngine file.
* Notice this is not the actual position in tree's children. See {@link HNDomTree#mOrder}
*/
private int mIndex;
private LinkedList<HNDomTree> mChildren;
@Nullable
private String mType;
private AttrsSet mInlineStyle;
@Nullable
private String mInnerText = null;
/**
* Id in html
*/
private String mId = null;
/**
* class property in html
*/
private String[] mClass = null;
private int mAttrIndex;
private int mOrder = -1;
/**
* Mark whether a tree's children are in order. Set default true, being set to false only when
* a tree with mOrder!=-1 has been added as child. {@link HNDomTree#addChild(HNDomTree)}
*/
private boolean mIsInOrder = true;
public HNDomTree(@NonNull AttrsSet inlineStyle, HNDomTree parent, int depth, int index) {
this(inlineStyle, null, parent, depth, index);
}
private HNDomTree(@NonNull AttrsSet inlineStyle, String tag, HNDomTree parent, int depth, int
index) {
mInlineStyle = inlineStyle;
mType = tag;
mDepth = depth;
mParent = parent;
mIndex = index;
mChildren = new LinkedList<>();
inlineStyle.register(this);
}
public HNDomTree(@NonNull HNDomTree parent, String nodeName, int index) {
this(parent.mInlineStyle, nodeName, parent, parent.mDepth + 1, index);
}
public void addInlineStyle(String styleName, @NonNull Object style) {
if (TREE_ORDER_PARAMETER.equalsIgnoreCase(styleName)) {
try {
mOrder = ParametersUtils.toInt(style);
if (mParent != null && mOrder != -1) {
mParent.onChangeChildOrder();
}
} catch (IllegalArgumentException e) {
HNLog.e(HNLog.DOM, "Wrong when read order, expecting integer while actual is " +
style +
", " + style.getClass().toString());
}
}
mInlineStyle.put(this, styleName, style);
}
private void onChangeChildOrder() {
if (mIsInOrder) {
mIsInOrder = false;
}
}
public void appendText(String text) {
if (mInnerText == null) {
mInnerText = text;
} else {
mInnerText += text;
}
}
public void addChild(HNDomTree child) {
if (child.mOrder != -1) {
if (mIsInOrder) {
mIsInOrder = false;
}
}
mChildren.add(child);
}
public boolean isLeaf() {
return mChildren.isEmpty();
}
public boolean isContainer() {
return !isLeaf();
}
public int childrenCount() {
return mChildren.size();
}
private void walkThrough(WalkAction action) {
this.walkThroughInternal(action, mDepth);
}
private void walkThroughInternal(@Nullable WalkAction action, int depth) {
if (action != null) {
action.act(this, depth);
}
Iterator<HNDomTree> itr = mChildren.iterator();
while (itr.hasNext()) {
HNDomTree child = itr.next();
child.walkThroughInternal(action, this.mDepth + 1);
}
}
@Override
public List<HNDomTree> children() {
sortChildrenIfNecessary();
return mChildren;
}
private void sortChildrenIfNecessary() {
if (!mIsInOrder) {
Collections.sort(mChildren, DEFAULT_TREE_COMPARATOR);
mIsInOrder = true;
}
}
@Nullable
public String getType() {
return mType;
}
@Nullable
@Override
public String getInner() {
return mInnerText;
}
@Override
public void setType(String type) {
this.mType = type;
}
public int getDepth() {
return mDepth;
}
public HNDomTree last() {
return mChildren.getLast();
}
public String wholeTreeToString() {
final StringBuilder sb = new StringBuilder();
this.walkThrough(new WalkAction() {
@Override
public void act(HNDomTree node, int depth) {
for (int i = 0; i < depth; i++) {
sb.append("--");
}
sb.append(node);
sb.append('\n');
}
});
return sb.toString();
}
@Override
public void onStartParse() {
}
@Override
public void onLeaveParse() {
if (mInnerText != null) {
mInlineStyle.put(this, "text", mInnerText);
}
}
@NonNull
@Override
public String toString() {
String index = "@" + mIndex + ":" + mOrder + ", ";
String text = (mInnerText == null ? "" : ", text=" + mInnerText);
return "[" + index + mType + ", attrs=" + mInlineStyle.toString(this) + text + "]";
}
public HNDomTree getParent() {
return mParent;
}
@Override
public boolean hasClazz() {
return mClass != null && mClass.length > 0;
}
@Override
public boolean hasId() {
return !TextUtils.isEmpty(mId);
}
@Override
public int attrIndex() {
return mAttrIndex;
}
@Override
public void setAttrIndex(int newIndex) {
mAttrIndex = newIndex;
}
public String getId() {
return mId;
}
public void setId(String id) {
this.mId = id;
}
@Override
public void setParent(DomElement parent) {
mParent = (HNDomTree) parent;
}
public String[] getClazz() {
return mClass;
}
public void setClazz(String[] clazz) {
this.mClass = clazz;
}
interface WalkAction {
void act(HNDomTree node, int depth);
}
private static class RVDomTreeComparator implements Comparator<HNDomTree> {
@Override
public int compare(HNDomTree o1, HNDomTree o2) {
return o1.mOrder - o2.mOrder;
}
}
private final static RVDomTreeComparator DEFAULT_TREE_COMPARATOR = new RVDomTreeComparator();
}