blob: d0a6e606cab47e12b16f956eedb0c44201390f95 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.weex.ui.component.richtext.node;
import static org.apache.weex.dom.WXStyle.UNSET;
import android.content.Context;
import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.util.ArrayMap;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.weex.WXSDKInstance;
import org.apache.weex.WXSDKManager;
import org.apache.weex.common.Constants;
import org.apache.weex.dom.WXCustomStyleSpan;
import org.apache.weex.dom.WXStyle;
import org.apache.weex.utils.WXLogUtils;
import org.apache.weex.utils.WXResourceUtils;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public abstract class RichTextNode {
public static final String TYPE = "type";
public static final String STYLE = "style";
public static final String ATTR = "attr";
public static final String CHILDREN = "children";
public static final String VALUE = Constants.Name.VALUE;
public static final String ITEM_CLICK="itemclick";
public static final String PSEUDO_REF="pseudoRef";
private static final int MAX_LEVEL = Spanned.SPAN_PRIORITY >> Spanned.SPAN_PRIORITY_SHIFT;
protected final Context mContext;
protected final String mInstanceId;
protected final String mComponentRef;
protected final String mRef;
protected Map<String, Object> style;
protected Map<String, Object> attr;
protected List<RichTextNode> children;
protected RichTextNode(Context context, String instanceId, String componentRef) {
mContext = context;
mInstanceId = instanceId;
mComponentRef = componentRef;
mRef = null;
}
protected RichTextNode(Context context, String instanceId, String componentRef, String ref, Map<String,Object> styles, Map<String,Object> attrs) {
mContext = context;
mInstanceId = instanceId;
mComponentRef = componentRef;
mRef = ref;
if(styles != null){
style = styles;
}
else {
style = new ArrayMap<>(0);
}
if (attrs != null) {
attr = attrs;
} else {
attr = new ArrayMap<>(0);
}
children = new LinkedList<>();
}
public static
@NonNull
Spannable parse(@NonNull Context context, @NonNull String instanceId, @NonNull String componentRef, String json) {
JSONArray jsonArray = JSON.parseArray(json);
JSONObject jsonObject;
List<RichTextNode> nodes;
RichTextNode node;
if (jsonArray != null && !jsonArray.isEmpty()) {
nodes = new ArrayList<>(jsonArray.size());
for (int i = 0; i < jsonArray.size(); i++) {
jsonObject = jsonArray.getJSONObject(i);
if (jsonObject != null) {
node = RichTextNodeManager.createRichTextNode(context, instanceId, componentRef, jsonObject);
if (node != null) {
nodes.add(node);
}
}
}
return parse(nodes);
}
return new SpannableString("");
}
public static int createSpanFlag(int level) {
return createPriorityFlag(level) | Spanned.SPAN_INCLUSIVE_EXCLUSIVE;
}
@Override
public abstract String toString();
protected abstract boolean isInternalNode();
public String getRef(){
return mRef;
}
final void parse(@NonNull Context context, @NonNull String instanceId, @NonNull String componentRef, JSONObject jsonObject) {
JSONObject jsonStyle, jsonAttr, child;
JSONArray jsonChildren;
RichTextNode node;
if ((jsonStyle = jsonObject.getJSONObject(STYLE)) != null) {
style = new ArrayMap<>();
style.putAll(jsonStyle);
} else {
style = new ArrayMap<>(0);
}
if ((jsonAttr = jsonObject.getJSONObject(ATTR)) != null) {
attr = new ArrayMap<>(jsonAttr.size());
attr.putAll(jsonAttr);
} else {
attr = new ArrayMap<>(0);
}
if ((jsonChildren=jsonObject.getJSONArray(CHILDREN))!=null) {
children = new ArrayList<>(jsonChildren.size());
for (int i = 0; i < jsonChildren.size(); i++) {
child = jsonChildren.getJSONObject(i);
node = RichTextNodeManager.createRichTextNode(context, instanceId, componentRef, child);
if (node != null) {
children.add(node);
}
}
} else {
children = new ArrayList<>(0);
}
}
public void addChildNode(RichTextNode child){
if(children == null){
children = new LinkedList<>();
}
if(child != null && isInternalNode()){
children.add(child);
}
}
public void removeChildNode(String ref){
if(children != null && !children.isEmpty() && !TextUtils.isEmpty(ref)){
try {
for (RichTextNode child : children) {
if (TextUtils.equals(child.mRef, ref)) {
children.remove(child);
}
}
}catch(Exception e){
WXLogUtils.getStackTrace(e);
}
}
}
public void updateStyles(Map<String,Object> styles){
if(styles != null && !styles.isEmpty()){
style.putAll(styles);
}
}
public void updateAttrs(Map<String,Object> attrs){
if(attr != null && !attrs.isEmpty()){
attr.putAll(attrs);
}
}
protected void updateSpans(SpannableStringBuilder spannableStringBuilder, int level) {
WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(mInstanceId);
if (style != null && instance != null) {
List<Object> spans = new LinkedList<>();
WXCustomStyleSpan customStyleSpan = createCustomStyleSpan();
if (customStyleSpan != null) {
spans.add(customStyleSpan);
}
if (style.containsKey(Constants.Name.FONT_SIZE)) {
spans.add(new AbsoluteSizeSpan(WXStyle.getFontSize(style, instance.getInstanceViewPortWidth())));
}
if (style.containsKey(Constants.Name.BACKGROUND_COLOR)) {
int color = WXResourceUtils.getColor(style.get(Constants.Name.BACKGROUND_COLOR).toString(),
Color.TRANSPARENT);
if (color != Color.TRANSPARENT) {
spans.add(new BackgroundColorSpan(color));
}
}
if (style.containsKey(Constants.Name.COLOR)) {
spans.add(new ForegroundColorSpan(WXResourceUtils.getColor(WXStyle.getTextColor(style))));
}
int spanFlag = createSpanFlag(level);
for (Object span : spans) {
spannableStringBuilder.setSpan(span, 0, spannableStringBuilder.length(), spanFlag);
}
}
}
private static int createPriorityFlag(int level) {
return level <= MAX_LEVEL ?
(MAX_LEVEL - level) << Spanned.SPAN_PRIORITY_SHIFT :
MAX_LEVEL << Spanned.SPAN_PRIORITY_SHIFT;
}
private static
@NonNull
Spannable parse(@NonNull List<RichTextNode> list) {
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();
for (RichTextNode richTextNode : list) {
spannableStringBuilder.append(richTextNode.toSpan(1));
}
return spannableStringBuilder;
}
public Spannable toSpan(int level) {
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();
spannableStringBuilder.append(toString());
if (isInternalNode() && children != null) {
for (RichTextNode child : children) {
spannableStringBuilder.append(child.toSpan(level + 1));
}
}
updateSpans(spannableStringBuilder, level);
return spannableStringBuilder;
}
private
@Nullable
WXCustomStyleSpan createCustomStyleSpan() {
int fontWeight = UNSET, fontStyle = UNSET;
String fontFamily = null;
if (style.containsKey(Constants.Name.FONT_WEIGHT)) {
fontWeight = WXStyle.getFontWeight(style);
}
if (style.containsKey(Constants.Name.FONT_STYLE)) {
fontStyle = WXStyle.getFontStyle(style);
}
if (style.containsKey(Constants.Name.FONT_FAMILY)) {
fontFamily = WXStyle.getFontFamily(style);
}
if (fontWeight != UNSET
|| fontStyle != UNSET
|| fontFamily != null) {
return new WXCustomStyleSpan(fontStyle, fontWeight, fontFamily);
} else {
return null;
}
}
public RichTextNode findRichNode(String ref){
RichTextNode theNode;
if(mRef != null && TextUtils.equals(mRef,ref)){
return this;
}
if(children != null && !children.isEmpty()){
for (RichTextNode child:children) {
if((theNode = child.findRichNode(ref)) != null)
return theNode;
}
}
return null;
}
}