| /* |
| * 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 com.taobao.weex.dom; |
| |
| import android.support.annotation.NonNull; |
| import android.support.annotation.Nullable; |
| import android.text.TextUtils; |
| |
| import com.alibaba.fastjson.JSONArray; |
| import com.alibaba.fastjson.JSONObject; |
| import com.taobao.weex.WXEnvironment; |
| import com.taobao.weex.WXSDKInstance; |
| import com.taobao.weex.WXSDKManager; |
| import com.taobao.weex.bridge.WXValidateProcessor; |
| import com.taobao.weex.common.Constants; |
| import com.taobao.weex.common.Constants.Name; |
| import com.taobao.weex.dom.flex.CSSLayoutContext; |
| import com.taobao.weex.dom.flex.CSSNode; |
| import com.taobao.weex.dom.flex.Spacing; |
| import com.taobao.weex.ui.component.WXBasicComponentType; |
| import com.taobao.weex.utils.WXLogUtils; |
| import com.taobao.weex.utils.WXViewUtils; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| |
| /** |
| * WXDomObject contains all the info about the given node, including style, attribute and event. |
| * Unlike {@link com.taobao.weex.ui.component.WXComponent}, WXDomObject only contains info about |
| * the dom, has nothing to do with rendering. |
| * Actually, {@link com.taobao.weex.ui.component.WXComponent} hold references to |
| * {@link android.view.View} and {@link WXDomObject}. |
| */ |
| public class WXDomObject extends CSSNode implements Cloneable,ImmutableDomObject { |
| public static final String CHILDREN = "children"; |
| public static final String TYPE = "type"; |
| public static final String TAG = WXDomObject.class.getSimpleName(); |
| public static final String ROOT = "_root"; |
| |
| /** |
| * Use {@link Name#TRANSFORM} instead. |
| */ |
| @Deprecated |
| public static final String TRANSFORM = Name.TRANSFORM; |
| |
| /** |
| * Use {@link Name#TRANSFORM_ORIGIN} instead. |
| */ |
| @Deprecated |
| public static final String TRANSFORM_ORIGIN = Name.TRANSFORM_ORIGIN; |
| static final WXDomObject DESTROYED = new WXDomObject(); |
| static{ |
| DESTROYED.mRef = "_destroyed"; |
| } |
| private AtomicBoolean sDestroy = new AtomicBoolean(); |
| |
| private int mViewPortWidth =750; |
| |
| private DomContext mDomContext; |
| |
| /** package **/ String mRef = ROOT; |
| |
| /** package **/ String mType = WXBasicComponentType.DIV; |
| |
| /** package **/ WXStyle mStyles; |
| |
| /** package **/ WXAttr mAttributes; |
| |
| /** package **/ WXEvent mEvents; |
| |
| |
| |
| |
| |
| private List<WXDomObject> mDomChildren; |
| |
| /** Do not access this field directly. This field will be removed soon. **/ |
| @Deprecated |
| public WXDomObject parent; |
| |
| private ArrayList<String> fixedStyleRefs; |
| |
| private boolean mYoung = false; |
| |
| public long mDomThreadNanos; |
| public long mDomThreadTimestamp; |
| |
| private boolean cloneThis = false; |
| |
| public void traverseTree(Consumer...consumers){ |
| long startNanos = System.nanoTime(); |
| if (consumers == null) { |
| return; |
| } |
| |
| for (Consumer consumer:consumers){ |
| consumer.accept(this); |
| } |
| |
| int count = childCount(); |
| WXDomObject child; |
| for (int i = 0; i < count; ++i) { |
| child = getChild(i); |
| child.traverseTree(consumers); |
| } |
| mDomThreadNanos += (System.nanoTime() - startNanos); |
| } |
| |
| |
| public int getViewPortWidth() { |
| return mViewPortWidth; |
| } |
| |
| public void setViewPortWidth(int mViewPortWidth) { |
| this.mViewPortWidth = mViewPortWidth; |
| } |
| |
| public String getRef(){ |
| return mRef; |
| } |
| |
| |
| public String getType(){ |
| return mType; |
| } |
| |
| public @NonNull WXStyle getStyles(){ |
| if(mStyles == null ){ |
| mStyles = new WXStyle(); |
| } |
| return mStyles; |
| } |
| |
| public @NonNull WXAttr getAttrs(){ |
| if(mAttributes == null){ |
| mAttributes = new WXAttr(); |
| } |
| return mAttributes; |
| } |
| |
| public @NonNull WXEvent getEvents(){ |
| if(mEvents == null){ |
| mEvents = new WXEvent(); |
| } |
| |
| return mEvents; |
| } |
| |
| |
| public @NonNull DomContext getDomContext() { |
| return mDomContext; |
| } |
| |
| public void clearEvents(){ |
| if(mEvents != null){ |
| mEvents.clear(); |
| } |
| } |
| |
| public static void prepareRoot(WXDomObject domObj,float defaultHeight,float defaultWidth) { |
| domObj.mRef = WXDomObject.ROOT; |
| |
| WXStyle domStyles = domObj.getStyles(); |
| Map<String, Object> style = new HashMap<>(5); |
| if (!domStyles.containsKey(Constants.Name.FLEX_DIRECTION)) { |
| style.put(Constants.Name.FLEX_DIRECTION, "column"); |
| } |
| if (!domStyles.containsKey(Constants.Name.BACKGROUND_COLOR)) { |
| style.put(Constants.Name.BACKGROUND_COLOR, "#ffffff"); |
| } |
| |
| style.put(Constants.Name.DEFAULT_WIDTH, defaultWidth); |
| style.put(Constants.Name.DEFAULT_HEIGHT, defaultHeight); |
| |
| domObj.updateStyle(style); |
| } |
| |
| protected final void copyFields(WXDomObject dest) { |
| dest.cssstyle.copy(this.cssstyle); |
| dest.mRef = mRef; |
| dest.mType = mType; |
| dest.mStyles = mStyles == null ? null : mStyles.clone();//mStyles == null ? null : mStyles.clone(); |
| dest.mAttributes = mAttributes == null ? null : mAttributes.clone();//mAttrs == null ? null : mAttrs.clone(); |
| dest.mEvents = mEvents == null ? null : mEvents.clone(); |
| dest.csslayout.copy(this.csslayout); |
| } |
| |
| /** |
| * Parse the jsonObject to {@link WXDomObject} recursively |
| * @param map the original JSONObject |
| */ |
| public void parseFromJson(JSONObject map){ |
| if (map == null || map.size() <= 0) { |
| return; |
| } |
| |
| String type = (String) map.get("type"); |
| this.mType = type; |
| this.mRef = (String) map.get("ref"); |
| Object style = map.get("style"); |
| if (style != null && style instanceof JSONObject) { |
| WXStyle styles = new WXStyle(); |
| styles.putAll((JSONObject) style,false); |
| this.mStyles = styles; |
| } |
| Object attr = map.get("attr"); |
| if (attr != null && attr instanceof JSONObject) { |
| WXAttr attrs = new WXAttr((JSONObject) attr); |
| //WXJsonUtils.putAll(attrs, (JSONObject) attr); |
| this.mAttributes = attrs; |
| } |
| Object event = map.get("event"); |
| if (event != null && event instanceof JSONArray) { |
| WXEvent events = new WXEvent(); |
| JSONArray eventArray = (JSONArray) event; |
| int count = eventArray.size(); |
| for (int i = 0; i < count; i++) { |
| Object value = eventArray.get(i); |
| events.addEvent(value); |
| } |
| this.mEvents = events; |
| } |
| |
| } |
| |
| |
| |
| |
| |
| /** |
| * Do pre-staff before layout. Subclass may provide different implementation. |
| */ |
| public void layoutBefore() { |
| |
| } |
| |
| /** |
| * Do post-staff before layout. Subclass may provide different implementation. |
| */ |
| public void layoutAfter(){ |
| |
| } |
| |
| /** |
| * Tell whether this object need to be updated. This is usually called when |
| * {@link CSSNode#calculateLayout(CSSLayoutContext)} finishes and new layout has been |
| * calculated. This method is a simple wrapper method for {@link #hasNewLayout()} and |
| * {@link #isDirty()}. |
| * @return true for need update since last update. |
| */ |
| public final boolean hasUpdate() { |
| return hasNewLayout() || isDirty(); |
| } |
| |
| /** |
| * Mark the current node is young and unconsumed. |
| */ |
| void young() { |
| mYoung = true; |
| } |
| |
| /** |
| * Mark the current node is old and consumed. |
| */ |
| void old() { |
| mYoung = false; |
| } |
| |
| /** |
| * Tell whether this node is consumed since last layout. |
| * @return true for consumed, false for not. |
| */ |
| boolean isYoung() { |
| return mYoung; |
| } |
| |
| /** |
| * Mark the update has been seen. After this method call, following call for {@link |
| * #hasUpdate()} will return false. This method is also a wrapper for {@link #markUpdateSeen()} |
| */ |
| public final void markUpdateSeen() { |
| if (hasNewLayout()) { |
| markLayoutSeen(); |
| } |
| } |
| |
| |
| public boolean isFixed() { |
| return mStyles == null ? false : mStyles.isFixed(); |
| } |
| |
| public boolean canRecycled() { |
| return mAttributes == null ? false : mAttributes.canRecycled(); |
| } |
| |
| public Object getExtra() { |
| return null; |
| } |
| |
| public void remove(WXDomObject child) { |
| if (child == null || mDomChildren == null || sDestroy.get()) { |
| return; |
| } |
| |
| int index = mDomChildren.indexOf(child); |
| removeFromDom(child); |
| if (index != -1) { |
| super.removeChildAt(index); |
| } |
| } |
| |
| public void removeFromDom(WXDomObject child) { |
| if (child == null || mDomChildren == null || sDestroy.get()) { |
| return; |
| } |
| |
| int index = mDomChildren.indexOf(child); |
| if (index == -1) { |
| WXLogUtils.e("[WXDomObject] remove function error"); |
| return; |
| } |
| mDomChildren.remove(index).parent = null; |
| } |
| |
| public int index(WXDomObject child) { |
| if (child == null || mDomChildren == null || sDestroy.get()) { |
| return -1; |
| } |
| return mDomChildren.indexOf(child); |
| } |
| |
| /** |
| * Add the given WXDomObject as this object's child at specified index. |
| * @param child the child to be added |
| * @param index the index of child to be added. If the index is -1, the child will be added |
| * as the last child of current dom object. |
| */ |
| public void add(WXDomObject child, int index) { |
| if (child == null || index < -1 || sDestroy.get()) { |
| return; |
| } |
| if (mDomChildren == null) { |
| mDomChildren = new ArrayList<>(); |
| } |
| |
| int count = mDomChildren.size(); |
| index = index >= count ? -1 : index; |
| if (index == -1) { |
| mDomChildren.add(child); |
| super.addChildAt(child, super.getChildCount()); |
| } else { |
| mDomChildren.add(index, child); |
| super.addChildAt(child, index); |
| } |
| child.parent = this; |
| } |
| |
| @Deprecated |
| public void add2Dom(WXDomObject child, int index) { |
| if (child == null || index < -1 || sDestroy.get()) { |
| return; |
| } |
| |
| int count = super.getChildCount(); |
| index = index >= count ? -1 : index; |
| if (index == -1) { |
| super.addChildAt(child, super.getChildCount()); |
| } else { |
| super.addChildAt(child, index); |
| } |
| child.parent = this; |
| } |
| |
| public WXDomObject getChild(int index) { |
| if (mDomChildren == null || sDestroy.get()) { |
| return null; |
| } |
| return mDomChildren.get(index); |
| } |
| |
| /** |
| * Add the given event for current object. |
| * @param e |
| */ |
| public void addEvent(String e) { |
| if (TextUtils.isEmpty(e)) { |
| return; |
| } |
| if (mEvents == null) { |
| mEvents = new WXEvent(); |
| } |
| if (containsEvent(e)) { |
| return; |
| } |
| mEvents.add(e); |
| } |
| |
| public boolean containsEvent(String e) { |
| if (mEvents == null) { |
| return false; |
| } |
| return mEvents.contains(e); |
| } |
| |
| public void removeEvent(String e) { |
| if (TextUtils.isEmpty(e)) { |
| return; |
| } |
| if (mEvents == null) { |
| return; |
| } |
| mEvents.remove(e); |
| } |
| |
| public void updateAttr(Map<String, Object> attrs) { |
| if (attrs == null || attrs.isEmpty()) { |
| return; |
| } |
| if (mAttributes == null) { |
| mAttributes = new WXAttr(); |
| } |
| mAttributes.putAll(attrs); |
| if(hasNewLayout()){ |
| markUpdateSeen(); |
| } |
| super.dirty(); |
| } |
| |
| public void updateStyle(Map<String, Object> styles){ |
| updateStyle(styles,false); |
| } |
| |
| public void updateStyle(Map<String, Object> styles, boolean byPesudo) { |
| if (styles == null || styles.isEmpty()) { |
| return; |
| } |
| if (mStyles == null) { |
| mStyles = new WXStyle(); |
| } |
| mStyles.putAll(styles,byPesudo); |
| super.dirty(); |
| } |
| |
| /** package **/ void applyStyleToNode() { |
| WXStyle stylesMap = getStyles(); |
| int vp = getViewPortWidth(); |
| if (!stylesMap.isEmpty()) { |
| for(Map.Entry<String,Object> item:stylesMap.entrySet()) { |
| switch (item.getKey()) { |
| case Constants.Name.ALIGN_ITEMS: |
| setAlignItems(stylesMap.getAlignItems()); |
| break; |
| case Constants.Name.ALIGN_SELF: |
| setAlignSelf(stylesMap.getAlignSelf()); |
| break; |
| case Constants.Name.FLEX: |
| setFlex(stylesMap.getFlex()); |
| break; |
| case Constants.Name.FLEX_DIRECTION: |
| setFlexDirection(stylesMap.getFlexDirection()); |
| break; |
| case Constants.Name.JUSTIFY_CONTENT: |
| setJustifyContent(stylesMap.getJustifyContent()); |
| break; |
| case Constants.Name.FLEX_WRAP: |
| setWrap(stylesMap.getCSSWrap()); |
| break; |
| case Constants.Name.MIN_WIDTH: |
| setMinWidth(WXViewUtils.getRealPxByWidth(stylesMap.getMinWidth(vp),vp)); |
| break; |
| case Constants.Name.MIN_HEIGHT: |
| setMinHeight(WXViewUtils.getRealPxByWidth(stylesMap.getMinHeight(vp),vp)); |
| break; |
| case Constants.Name.MAX_WIDTH: |
| setMaxWidth(WXViewUtils.getRealPxByWidth(stylesMap.getMaxWidth(vp),vp)); |
| break; |
| case Constants.Name.MAX_HEIGHT: |
| setMaxHeight(WXViewUtils.getRealPxByWidth(stylesMap.getMaxHeight(vp),vp)); |
| break; |
| case Constants.Name.DEFAULT_HEIGHT: |
| case Constants.Name.HEIGHT: |
| setStyleHeight(WXViewUtils.getRealPxByWidth(stylesMap.containsKey(Constants.Name.HEIGHT)?stylesMap.getHeight(vp):stylesMap.getDefaultHeight(),vp)); |
| break; |
| case Constants.Name.WIDTH: |
| case Constants.Name.DEFAULT_WIDTH: |
| setStyleWidth(WXViewUtils.getRealPxByWidth(stylesMap.containsKey(Constants.Name.WIDTH)?stylesMap.getWidth(vp):stylesMap.getDefaultWidth(),vp)); |
| break; |
| case Constants.Name.POSITION: |
| setPositionType(stylesMap.getPosition()); |
| break; |
| case Constants.Name.LEFT: |
| setPositionLeft(WXViewUtils.getRealPxByWidth(stylesMap.getLeft(vp),vp)); |
| break; |
| case Constants.Name.TOP: |
| setPositionTop(WXViewUtils.getRealPxByWidth(stylesMap.getTop(vp),vp)); |
| break; |
| case Constants.Name.RIGHT: |
| setPositionRight(WXViewUtils.getRealPxByWidth(stylesMap.getRight(vp),vp)); |
| break; |
| case Constants.Name.BOTTOM: |
| setPositionBottom(WXViewUtils.getRealPxByWidth(stylesMap.getBottom(vp),vp)); |
| break; |
| case Constants.Name.MARGIN: |
| setMargin(Spacing.ALL, WXViewUtils.getRealPxByWidth(stylesMap.getMargin(vp), vp)); |
| break; |
| case Constants.Name.MARGIN_LEFT: |
| setMargin(Spacing.LEFT, WXViewUtils.getRealPxByWidth(stylesMap.getMarginLeft(vp), vp)); |
| break; |
| case Constants.Name.MARGIN_TOP: |
| setMargin(Spacing.TOP, WXViewUtils.getRealPxByWidth(stylesMap.getMarginTop(vp), vp)); |
| break; |
| case Constants.Name.MARGIN_RIGHT: |
| setMargin(Spacing.RIGHT, WXViewUtils.getRealPxByWidth(stylesMap.getMarginRight(vp), vp)); |
| break; |
| case Constants.Name.MARGIN_BOTTOM: |
| setMargin(Spacing.BOTTOM, WXViewUtils.getRealPxByWidth(stylesMap.getMarginBottom(vp), vp)); |
| break; |
| case Constants.Name.BORDER_WIDTH: |
| setBorder(Spacing.ALL, WXViewUtils.getRealPxByWidth(stylesMap.getBorderWidth(vp), vp)); |
| break; |
| case Constants.Name.BORDER_TOP_WIDTH: |
| setBorder(Spacing.TOP, WXViewUtils.getRealPxByWidth(stylesMap.getBorderTopWidth(vp), vp)); |
| break; |
| case Constants.Name.BORDER_RIGHT_WIDTH: |
| setBorder(Spacing.RIGHT, WXViewUtils.getRealPxByWidth(stylesMap.getBorderRightWidth(vp), vp)); |
| break; |
| case Constants.Name.BORDER_BOTTOM_WIDTH: |
| setBorder(Spacing.BOTTOM, WXViewUtils.getRealPxByWidth(stylesMap.getBorderBottomWidth(vp), vp)); |
| break; |
| case Constants.Name.BORDER_LEFT_WIDTH: |
| setBorder(Spacing.LEFT, WXViewUtils.getRealPxByWidth(stylesMap.getBorderLeftWidth(vp), vp)); |
| break; |
| case Constants.Name.PADDING: |
| setPadding(Spacing.ALL, WXViewUtils.getRealPxByWidth(stylesMap.getPadding(vp), vp)); |
| break; |
| case Constants.Name.PADDING_LEFT: |
| setPadding(Spacing.LEFT, WXViewUtils.getRealPxByWidth(stylesMap.getPaddingLeft(vp), vp)); |
| break; |
| case Constants.Name.PADDING_TOP: |
| setPadding(Spacing.TOP, WXViewUtils.getRealPxByWidth(stylesMap.getPaddingTop(vp), vp)); |
| break; |
| case Constants.Name.PADDING_RIGHT: |
| setPadding(Spacing.RIGHT, WXViewUtils.getRealPxByWidth(stylesMap.getPaddingRight(vp), vp)); |
| break; |
| case Constants.Name.PADDING_BOTTOM: |
| setPadding(Spacing.BOTTOM, WXViewUtils.getRealPxByWidth(stylesMap.getPaddingBottom(vp), vp)); |
| break; |
| } |
| } |
| } |
| } |
| |
| public int childCount() { |
| return mDomChildren == null ? 0 : mDomChildren.size(); |
| } |
| |
| public void hide() { |
| setVisible(false); |
| } |
| |
| public void show() { |
| setVisible(true); |
| } |
| |
| public boolean isVisible() { |
| return super.isShow(); |
| } |
| |
| /** |
| * Clone the current object. This is not a deep copy, only shadow copy of some reference. |
| * @return The result object of clone. |
| */ |
| @Override |
| public WXDomObject clone() { |
| if (sDestroy.get()) { |
| return null; |
| } |
| if(cloneThis){ |
| return this; |
| } |
| WXDomObject dom = null; |
| try { |
| dom = WXDomObjectFactory.newInstance(mType); |
| copyFields(dom); |
| } catch (Exception e) { |
| if (WXEnvironment.isApkDebugable()) { |
| WXLogUtils.e("WXDomObject clone error: ", e); |
| } |
| } |
| |
| return dom; |
| } |
| |
| public void destroy() { |
| sDestroy.set(true); |
| if (mStyles != null) { |
| mStyles.clear(); |
| } |
| if (mAttributes != null) { |
| mAttributes.clear(); |
| } |
| if (mEvents != null) { |
| mEvents.clear(); |
| } |
| if (mDomChildren != null) { |
| int count = mDomChildren.size(); |
| for (int i = 0; i < count; ++i) { |
| mDomChildren.get(i).destroy(); |
| } |
| mDomChildren.clear(); |
| } |
| mDomContext = null; |
| } |
| |
| /** package **/ |
| /** |
| * Get default style map for component. |
| * @return |
| */ |
| protected Map<String,String> getDefaultStyle(){ |
| return null; |
| } |
| |
| public ArrayList<String> getFixedStyleRefs() { |
| return fixedStyleRefs; |
| } |
| |
| public void add2FixedDomList(String ref) { |
| if (fixedStyleRefs == null) { |
| fixedStyleRefs = new ArrayList<>(); |
| } |
| fixedStyleRefs.add(ref); |
| } |
| |
| public String dumpDomTree() { |
| return mRef + ": " + toString(); |
| } |
| |
| /** |
| * Parse the jsonObject to {@link WXDomObject} recursively |
| * @param json the original JSONObject |
| * @return Dom Object corresponding to the JSONObject. |
| */ |
| public static @Nullable WXDomObject parse(JSONObject json, WXSDKInstance wxsdkInstance) { |
| return parse(json, wxsdkInstance, null); |
| } |
| |
| public static @Nullable WXDomObject parse(JSONObject json, WXSDKInstance wxsdkInstance, WXDomObject parentDomObject){ |
| long startNanos = System.nanoTime(); |
| long timestamp = System.currentTimeMillis(); |
| if (json == null || json.size() <= 0) { |
| return null; |
| } |
| |
| String type = (String) json.get(TYPE); |
| |
| if (wxsdkInstance.isNeedValidate()) { |
| WXValidateProcessor processor = WXSDKManager.getInstance() |
| .getValidateProcessor(); |
| if (processor != null) { |
| WXValidateProcessor.WXComponentValidateResult result = processor |
| .onComponentValidate(wxsdkInstance, type, parentDomObject); |
| if (result != null && !result.isSuccess) { |
| type = TextUtils.isEmpty(result.replacedComponent) ? WXBasicComponentType.DIV |
| : result.replacedComponent; |
| json.put(TYPE, type); |
| if (result.validateInfo != null) { |
| String tag = "[WXDomObject]onComponentValidate failure. >>> " + result.validateInfo.toJSONString(); |
| WXLogUtils.e(tag); |
| } |
| } else if (result == null){ |
| return null; |
| } |
| } |
| } |
| |
| WXDomObject domObject = WXDomObjectFactory.newInstance(type); |
| |
| domObject.setViewPortWidth(wxsdkInstance.getInstanceViewPortWidth()); |
| |
| if(domObject == null){ |
| return null; |
| } |
| domObject.parseFromJson(json); |
| domObject.mDomContext = wxsdkInstance; |
| domObject.parent = parentDomObject; |
| |
| Object children = json.get(CHILDREN); |
| if (children != null && children instanceof JSONArray) { |
| JSONArray childrenArray = (JSONArray) children; |
| int count = childrenArray.size(); |
| for (int i = 0; i < count; ++i) { |
| domObject.add(parse(childrenArray.getJSONObject(i),wxsdkInstance, domObject),-1); |
| } |
| } |
| |
| domObject.mDomThreadNanos = System.nanoTime() - startNanos; |
| domObject.mDomThreadTimestamp = timestamp; |
| return domObject; |
| } |
| |
| public interface Consumer{ |
| void accept(WXDomObject dom); |
| } |
| |
| public boolean isCloneThis() { |
| return cloneThis; |
| } |
| |
| public void setCloneThis(boolean cloneThis) { |
| this.cloneThis = cloneThis; |
| } |
| } |