package com.trs.components.metadata.publish;
/*
 * History          Who         What
 * 2009-11-11       wenyh       添加wml支持
 */
/**
 * TRS_ViewData解析器
 */


import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.trs.DreamFactory;
import com.trs.cms.content.CMSObj;
import com.trs.components.common.publish.PublishConstants;
import com.trs.components.common.publish.domain.publisher.PublishPathCompass;
import com.trs.components.common.publish.domain.publisher.PublishSyncGlobalTuner;
import com.trs.components.common.publish.domain.publisher.PublishTagContext;
import com.trs.components.common.publish.domain.tagparser.TagBeanInfo;
import com.trs.components.common.publish.domain.tagparser.TagItem;
import com.trs.components.common.publish.domain.tagparser.TagParseHelper;
import com.trs.components.common.publish.domain.tagparser.TagParserContentBase;
import com.trs.components.common.publish.parser.HTMLContentParseHelper;
import com.trs.components.common.publish.persistent.element.IPublishContent;
import com.trs.components.common.publish.persistent.element.IPublishElement;
import com.trs.components.common.publish.persistent.element.IPublishFolder;
import com.trs.components.common.publish.persistent.element.PublishElementFactory;
import com.trs.components.common.publish.util.RelativeURLHelper;
import com.trs.components.metadata.MetaDataConstants;
import com.trs.components.metadata.center.MetaViewData;
import com.trs.components.metadata.definition.ClassInfo;
import com.trs.components.metadata.definition.ClassInfos;
import com.trs.components.metadata.definition.IMetaDataDefCacheMgr;
import com.trs.components.metadata.definition.MetaView;
import com.trs.components.metadata.definition.MetaViewField;
import com.trs.components.wcm.content.persistent.Channel;
import com.trs.components.wcm.content.persistent.Document;
import com.trs.components.wcm.content.persistent.WebSite;
import com.trs.components.wcm.publish.tagparser.WCMAppendixGenerator;
import com.trs.infra.I18NMessage;
import com.trs.infra.common.WCMException;
import com.trs.infra.persistent.db.DBManager;
import com.trs.infra.support.config.ConfigServer;
import com.trs.infra.support.file.FileHelper;
import com.trs.infra.support.file.FilesMan;
import com.trs.infra.util.CMyDateTime;
import com.trs.infra.util.CMyException;
import com.trs.infra.util.CMyFile;
import com.trs.infra.util.CMyString;
import com.trs.infra.util.CMyUnzip;
import com.trs.infra.util.ExceptionNumber;

/**
 * @author 曹辉
 * 
 */
public class WCMTagParserViewData extends TagParserContentBase {
    private static org.apache.log4j.Logger logger = org.apache.log4j.Logger
            .getLogger(WCMTagParserViewData.class);

    private IMetaDataDefCacheMgr m_oDataDefCacheMgr = null;

    private final static int CHNL_DESC = 100;

    private final static int CHNL_NAME = 200;

    private final static int HOME_SITE = CHNL_NAME + 1;

    private final static int HOME_SITENAME = HOME_SITE + 1;

    private static ArrayList FIELDS_DEFAULT_LIST_OF_METADATA = new ArrayList(4);
    static {
        FIELDS_DEFAULT_LIST_OF_METADATA
                .add(MetaDataConstants.FIELDNAME_METADATA_ID);
        FIELDS_DEFAULT_LIST_OF_METADATA
                .add(MetaDataConstants.FIELDNAME_CHANNEL_ID);
        FIELDS_DEFAULT_LIST_OF_METADATA.add("CRTIME");
    }

    /**
     * 
     */
    public WCMTagParserViewData() {
        m_oDataDefCacheMgr = (IMetaDataDefCacheMgr) DreamFactory
                .createObjectById("IMetaDataDefCacheMgr");
    }

    public String[] parseSubItems(PublishTagContext _context)
            throws WCMException {
        TagItem oTagItem = _context.getTagItem();
        List tagChildren = oTagItem.getChildren();
        PublishTagContext tagContext = new PublishTagContext(_context, oTagItem);
        tagContext.setUpperHost(PublishElementFactory.makeElementFrom(m_host));
        return TagParseHelper.parseItems(tagChildren, tagContext);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.trs.components.common.publish.domain.tagparser.TagParserCMSObjBase
     * #parse
     * (com.trs.components.common.publish.domain.publisher.PublishTagContext)
     */
    public String[] parse(PublishTagContext _context) throws WCMException {
        m_host = findHost(_context);
        if (m_host == null)
            return null;

        if (_context.getAttribute("ParseIFNotNull") == null) {
            // 处理一些包含子tag的情况
            TagItem oTagItem = _context.getTagItem();
            // comment by caohui@2009-2-24 上午08:41:44
            // 改为只要包含子置标就解析
            // boolean bContainConditionItem =
            // oTagItem.containsChild("TRS_CONDITION");
            boolean bContainConditionItem = containsTagItemChild(oTagItem);
            if (bContainConditionItem) {
                return parseSubItems(_context);
            }

            return parsePropety(_context);
        }
        // 如果启用了IFNotNull解析逻辑，需要预解析出值，然后判断
        else {
            String[] pResult = parsePropety(_context);
            if (!_context.getAttribute("ParseIFNotNull", false)) {
                return pResult;
            }
            // 如果启用了不为空继续解析的逻辑，继续对于解析结果进行判断
            if (pResult == null
                    || (pResult.length == 1 && CMyString.isEmpty(pResult[0]))) {
                return null;
            }

            // 不为空继续解析，将上下文恢复，权当没有这个置标
            TagItem oTagItem = _context.getTagItem();
            List tagChildren = oTagItem.getChildren();
            return TagParseHelper.parseItems(tagChildren, _context);
        }
    }

    private String[] parsePropety(PublishTagContext _context)
            throws WCMException {
        MetaViewData viewData = (MetaViewData) m_host;
        String sFieldName = _context.getAttribute("Field");
        MetaViewField viewField = m_oDataDefCacheMgr.getMetaViewField(viewData
                .getMetaView().getId(), sFieldName);
        int nViewFieldType = -1;
        if (viewField != null) {
            nViewFieldType = viewField.getType();
        }
        switch (nViewFieldType) {
        case MetaDataConstants.FIELD_TYPE_APPENDIX:
            return generateAppendix(_context);
        case MetaDataConstants.FIELD_TYPE_HTML:
        case MetaDataConstants.FIELD_TYPE_HTML_CHAR:
            return parseHtmlContent(viewData.getPropertyAsString(sFieldName),
                    _context);
        case MetaDataConstants.FIELD_TYPE_CLASS:
            return makeClassInfo(_context, viewData, sFieldName);
        case MetaDataConstants.FIELD_TYPE_MULTITEXT:
        case MetaDataConstants.FIELD_TYPE_NORMALTEXT:
            return parseText(_context, viewData.getPropertyAsString(sFieldName));
        case MetaDataConstants.FIELD_TYPE_LINK:
            String sValue = viewData.getPropertyAsString(sFieldName);
            if (sValue == null || (sValue = sValue.trim()).length() <= 0)
                return null;

            sValue = RelativeURLHelper.makeLinkValue(_context, sValue);
            return parseProperty(sValue, _context);
        default:
            return super.parse(_context);
        }
    }

    /**
     * @param _context
     * @param propertyAsString
     * @return
     * @throws WCMException
     */
    private String[] parseText(PublishTagContext _context, String sText)
            throws WCMException {
        if (sText == null || !_context.getAttribute("WML", false)) {
            return super.parse(_context);
        }

        return makeWmlContent(_context, sText);
    }

    private String[] makeWmlContent(PublishTagContext _context, String _sResult)
            throws WCMException {
        IPublishElement element = _context.getPageElement();
        if (!(element instanceof IPublishContent)) {
            return super.parse(_context);
        }
        int nMaxNum = _context.getAttribute("PAGENUM", 900);
        if (nMaxNum <= 0) {
            return super.parse(_context);
        }

        String sFileName = PublishPathCompass
                .makeDetailPageFileName((IPublishContent) element);
        String sFileExt = _context.getPageContext().getTemplate()
                .getOutputFileExt();

        List list = new ArrayList(5);
        StringBuffer buff = new StringBuffer(1024);
        int nEnLen = 1;
        int nChLen = 3;
        int nCurrLen = 0;
        int nPageIndex = 0;
        int nGet = 0; // 已经取得的字符串长度（长度：英文字符记1，中文字符记3）
        String temp = null;
        char[] chars = _sResult.toCharArray();
        char ch = 0;
        for (int i = 0, len = chars.length; i < len; i++) {
            ch = chars[i];
            nCurrLen = (ch <= 0x7f) ? nEnLen : nChLen;
            nGet += nCurrLen;

            // 如果达到最大长度，需要输出到结果中
            if (nGet > nMaxNum) {
                temp = TagParseHelper.formatTextIfNeeded(_context,
                        buff.toString());
                buff.setLength(0);
                buff.append(temp);

                // 分页导航
                buff.append("<br />");
                buff.append(makeCurrNavContent(sFileName, sFileExt, nPageIndex,
                        true));

                // 保存结果
                list.add(buff.toString());

                buff.setLength(0);
                nPageIndex++;
                nGet = nCurrLen;
            }

            // 保存到临时对象中
            buff.append(ch);
        }
        int nImgCount = 0;
        try {
            temp = _context.getPageContext().getExtraAttribute(
                    PublishConstants.PGVARNAME_WMLIMAGECOUNT);
            nImgCount = Integer.parseInt(temp) - 1;// 与分页相关,从0开始
        } catch (Exception ex) {
        }

        if (buff.length() > 0) {
            // 格式化当前结果
            temp = TagParseHelper.formatTextIfNeeded(_context, buff.toString());
            buff.setLength(0);
            buff.append(temp);

            // 追加最后一页的数据
            if (nPageIndex > 0 || nImgCount > nPageIndex) {
                // 追加分页导航
                buff.append("<br />");
                buff.append(makeCurrNavContent(sFileName, sFileExt, nPageIndex,
                        nImgCount > nPageIndex));
            }

            // 保存结果
            list.add(buff.toString());
            buff.setLength(0);
        }

        buff.setLength(0);
        // 显示图片,空分页
        nImgCount -= nPageIndex;
        while (--nImgCount >= 0) {
            buff.append("<br />");
            buff.append(makeCurrNavContent(sFileName, sFileExt, ++nPageIndex,
                    nImgCount > 0));
            list.add(buff.toString());
            buff.setLength(0);
        }

        String[] result = new String[list.size()];
        result = (String[]) list.toArray(result);
        return result;
    }

    private static String makeCurrNavContent(String _sFileName,
            String _sFileExt, int _nIndex, boolean _bDisplayPre) {
        StringBuffer sbNavContent = new StringBuffer();
        if (_nIndex > 0) {
            String sURL = null;
            if (_nIndex == 1) {
                sURL = _sFileName + "." + _sFileExt;
            } else {
                sURL = _sFileName + "_" + (_nIndex - 1) + "." + _sFileExt;
            }
            sbNavContent.append("<a href=\""
                    + sURL
                    + "\">&lt;&lt;"
                    + I18NMessage.get(WCMTagParserViewData.class,
                            "WCMTagParserViewData.label1", "上一页") + "</a>");
            sbNavContent.append("&nbsp;&nbsp;");
        }

        if (_bDisplayPre) {
            String sURL = _sFileName + "_" + (_nIndex + 1) + "." + _sFileExt;
            sbNavContent.append("<a href=\""
                    + sURL
                    + "\">"
                    + I18NMessage.get(WCMTagParserViewData.class,
                            "WCMTagParserViewData.label2", "下一页")
                    + "&gt;&gt;</a>");
        }

        return sbNavContent.toString();
    }

    /**
     * @param _context
     * @param _viewData
     * @param _sFieldName
     * @return
     * @throws WCMException
     */
    private String[] makeClassInfo(PublishTagContext _context,
            MetaViewData _viewData, String _sFieldName) throws WCMException {
        ClassInfos classInfos = ClassInfos.findByIds(null,
                _viewData.getPropertyAsString(_sFieldName));
        if (classInfos.isEmpty())
            return null;

        String sClassFieldName = _context.getAttribute("ClassField");
        if (CMyString.isEmpty(sClassFieldName)) {
            sClassFieldName = "CNAME";
        }

        boolean bFullPath = _context.getAttribute("FullPath", false);
        String sSeperator = _context.getAttribute("SEPERATOR");
        if (CMyString.isEmpty(sSeperator))
            sSeperator = "\\";
        boolean bDisplayParentName = _context.getAttribute("DisplayParentName",
                false);

        StringBuffer sbClassPaths = new StringBuffer();
        for (int i = 0, nSize = classInfos.size(); i < nSize; i++) {
            ClassInfo classInfo = (ClassInfo) classInfos.getAt(i);
            if (classInfo == null)
                continue;

            // 追加多个分类路径
            if (bFullPath) {
                String sClassInfoPath = classInfo
                        .getPropertyAsString(sClassFieldName);
                ClassInfo parentClassInfo = ClassInfo.findById(classInfo
                        .getParentId());

                while (parentClassInfo != null) {
                    sClassInfoPath = parentClassInfo
                            .getPropertyAsString(sClassFieldName)
                            + sSeperator
                            + sClassInfoPath;
                    parentClassInfo = ClassInfo.findById(parentClassInfo
                            .getParentId());
                    if (parentClassInfo == null || parentClassInfo.isRoot())
                        break;
                }

                sbClassPaths.append(sClassInfoPath);
            }
            // 构造单个分类的路径
            else if (bDisplayParentName) {
                ClassInfo parentClassInfo = ClassInfo.findById(classInfo
                        .getParentId());
                if (parentClassInfo != null && parentClassInfo.isRoot()) {
                    parentClassInfo = classInfo;
                }

                sbClassPaths.append(parentClassInfo == null ? classInfo
                        .getPropertyAsString(sClassFieldName) : parentClassInfo
                        .getPropertyAsString(sClassFieldName));
            } else {
                // 追加多个分类路径
                sbClassPaths.append(classInfo
                        .getPropertyAsString(sClassFieldName));

            }

            sbClassPaths.append(";");
        }
        if (sbClassPaths.length() <= 0)
            return null;

        sbClassPaths.setLength(sbClassPaths.length() - 1);

        return new String[] { sbClassPaths.toString() };
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.trs.components.common.publish.domain.tagparser.TagParserCMSObjBase
     * #parseHostNormalProperty(java.lang.String,
     * com.trs.components.common.publish.domain.publisher.PublishTagContext)
     */
    protected String[] parseHostNormalProperty(String _sName,
            PublishTagContext _context) throws WCMException {
        Object oValue;

        MetaViewData viewData = (MetaViewData) m_host;
        boolean bIsLabel = _context.getAttribute("isLabel", true);
        String sDelim = _context.getAttribute("Delim");

        if (_sName.charAt(0) == PublishConstants.TAGID_ATTRIBUTE_FLAG) {
            oValue = m_host.getAttributeValue(_sName.substring(1));
        } else if (!bIsLabel
                || (m_host.getProperty(_sName) instanceof CMyDateTime)) {// 為了支持DateFormat，不能轉成String
            oValue = m_host.getProperty(_sName);
        } else {// 需要适应是非框，或者单选为数字的情况
            oValue = viewData.getRealProperty(_sName, sDelim);
        }

        if (oValue == null)
            return null;

        return parseProperty(oValue, _context);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.trs.components.common.publish.domain.tagparser.TagParserCMSObjBase
     * #parseHostSpecialProperty(int,
     * com.trs.components.common.publish.domain.publisher.PublishTagContext)
     */
    protected String[] parseHostSpecialProperty(int _nPropertyId,
            PublishTagContext _context) {

        if (m_host == null) {
            logger.error(_context.getTagItem()
                    + I18NMessage.get(WCMTagParserViewData.class,
                            "WCMTagParserViewData.label3",
                            "解析失败！相关MetaViewData没有找到！"));
            return null;
        }

        try {
            Channel channel = ((MetaViewData) m_host).getChannel();
            switch (_nPropertyId) {
            case CHNL_DESC:
                String sDesc = channel.getDesc();
                if (sDesc == null || (sDesc = sDesc.trim()).length() <= 0) {
                    sDesc = channel.getName();
                }
                return this.parseProperty(sDesc, _context);
            case CHNL_NAME:
                String sName = channel.getName();
                return this.parseProperty(sName, _context);
            case HOME_SITE:
            case HOME_SITENAME: {
                return parseHomeSite(channel, _context, _nPropertyId);
            }
            default:
                break;
            }
        } catch (Exception e) {
            // logger me
            e.printStackTrace();
        }
        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.trs.components.common.publish.domain.tagparser.TagParserCMSObjBase
     * #registerHostSpecialProperties()
     */
    protected void registerHostSpecialProperties() {
        this.registerHostSpecialProperty("CHNLDESC", CHNL_DESC);
        this.registerHostSpecialProperty("CHNLNAME", CHNL_NAME);
        this.registerHostSpecialProperty("HOMESITE", HOME_SITE);
        this.registerHostSpecialProperty("HOMESITENAME", HOME_SITENAME);

    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.trs.components.common.publish.domain.tagparser.ITagParser#getBeanInfo
     * ()
     */
    public TagBeanInfo getBeanInfo() {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.trs.components.common.publish.domain.tagparser.TagParserCMSObjBase
     * #findHost
     * (com.trs.components.common.publish.domain.publisher.PublishTagContext)
     */
    protected CMSObj findHost(PublishTagContext _context) throws WCMException {
        if (m_host != null) {
            return m_host;
        }
        //如果发布任务中提供了可返回对象，则直接返回
        Object oFormTaskObject = _context.getPageContext().getTaskContext().getCachObject(MetaViewData.METAVIEWDATA_OBJ_IN_PREVIEW);
        if (oFormTaskObject != null /*&& oFormTaskObject instanceof MetaViewData*/) {
            return (MetaViewData) oFormTaskObject;
        }
        // 判断当前upperHost是否是Document
        IPublishElement upperHost = _context.getUpperHost();
        CMSObj obj = upperHost.getSubstance();
        if (obj instanceof MetaViewData) {
            return obj;
        } else if (obj instanceof Document) {
            // 将当前获取到的视图对象缓存起来
            MetaView view = getMetaViewCachedByTagContext(_context,
                    (Document) obj);
            return getViewDataCachedByTagContext(_context, obj, view);
        } else if (obj instanceof MetaViewField) {
            // 需要判断一下当前置标是否在TRS_RelNews下，如果在，需要
            int nCount = 1, nMaxCount = 100;
            PublishTagContext tempContext = _context.getParent();
            while (tempContext != null) {
                if (nCount > nMaxCount) {
                    logger.error(nCount
                            + I18NMessage.get(WCMTagParserViewData.class,
                                    "WCMTagParserViewData.label4",
                                    "次都没有找到UppserHost是ViewDAta"));
                    break;
                }
                nCount++;

                CMSObj tempObj = tempContext.getUpperHost().getSubstance();
                if (tempObj instanceof MetaViewData) {
                    return tempObj;
                } else if (tempObj instanceof Document) {
                    // 将当前获取到的视图对象缓存起来
                    MetaView view = getMetaViewCachedByTagContext(tempContext,
                            (Document) tempObj);
                    return getViewDataCachedByTagContext(tempContext, tempObj,
                            view);
                }

                tempContext = tempContext.getParent();
            }

            CMSObj pageObj = _context.getPageElement().getSubstance();
            MetaView view = getMetaViewCachedByTagContext(_context,
                    (Document) pageObj);
            return getViewDataCachedByTagContext(_context, pageObj, view);
        } else {
            throw new WCMException(I18NMessage.get(WCMTagParserViewData.class,
                    "WCMTagParserViewData.label5",
                    "TRS_ViewData置标不能应用到当前场景下！[UpperHost=")
                    + obj.getClassName(true) + "]");
        }
    }

    private MetaView getMetaViewCachedByTagContext(
            PublishTagContext _oTagContext, Document _document)
            throws WCMException {
        // add by caohui@2010-4-22 上午09:07:59
        // 取消在置标上的Cache，直接从已有Cache获取
        // 从性能分析来看，涉及到多次View读取
        // 已经将View放入Cache中
        MetaView view = MetaView.findById(_document.getKindId());
        if (view == null) {
            throw new WCMException(I18NMessage.get(WCMTagParserViewData.class,
                    "WCMTagParserViewData.label6", "当前文档关联的视图不存在！[DocId=")
                    + _document.getId()
                    + ",ViewId="
                    + _document.getKindId()
                    + "]");
        }
        return view;

        // Map hExtraAttributes = _oTagContext.getExtraAttributes();
        // if (hExtraAttributes == null) {
        // hExtraAttributes = new HashMap(1);
        // _oTagContext.setExtraAttributes(hExtraAttributes);
        // }
        // String sKey = "Curr.View." + _document.getId();
        // MetaView view = (MetaView) hExtraAttributes.get(sKey);
        // if (view == null) {
        // view = MetaView.findById(_document.getKindId());
        // if (view == null) {
        // throw new WCMException("当前文档关联的视图不存在！[DocId="
        // + _document.getId() + ",ViewId="
        // + _document.getKindId() + "]");
        // }
        // hExtraAttributes.put(sKey, view);
        // }
        // return view;
    }

    private MetaViewData getViewDataCachedByTagContext(
            PublishTagContext _oTagContext, CMSObj _host, MetaView _view)
            throws WCMException {
        Map hExtraAttributes = _oTagContext.getExtraAttributes();
        if (hExtraAttributes == null) {
            hExtraAttributes = new HashMap(1);
            _oTagContext.setExtraAttributes(hExtraAttributes);
        }
        String sKey = "Curr.ViewData." + _host.getId();
        MetaViewData viewData = (MetaViewData) hExtraAttributes.get(sKey);
        if (viewData != null)
            return viewData;

        // add by caohui@2010-4-22 下午06:08:02
        // todo的重要性还是很重要，以前已经预估到会出现性能问题，可以没有修改
        // TODO 根据父节点缓存的SelectFields，装载数据
        viewData = new MetaViewData(_view);
        // viewData.loadById(_host.getId(), viewData.getSelectFields());
        String sSelectFields = getSelectFields(_view.getTrueTableName(),
                "TRS_VIEWDATA", _oTagContext);
        viewData.loadById(_host.getId(), sSelectFields);
        hExtraAttributes.put(sKey, viewData);
        return viewData;
    }

    /**
     * @param _context
     * @return
     */
    private String[] generateAppendix(PublishTagContext _context)
            throws WCMException {
        // 1. 判断当前参数的有效性，文件名是否有效，是否存在
        String sFieldName = _context.getAttribute("Field");
        String sFileName = m_host.getPropertyAsString(sFieldName);
        if (CMyString.isEmpty(sFileName)) {
            return new String[] { "" };
        }

        // 1.2 获取文件名
        sFileName = CMyFile.extractFileName(sFileName);

        // 1.3 判断是不是WCM的Protect文件
        if (!FilesMan.isValidFile(sFileName, FilesMan.FLAG_PROTECTED)
                && !FilesMan.isValidFile(sFileName, FilesMan.FLAG_WEBFILE)) {
            _context.getPageContext().addErrorLog(
                    PublishConstants.TASK_STATUS_FAILED,
                    I18NMessage.get(WCMTagParserViewData.class,
                            "WCMTagParserViewData.label7", "相关附件[")
                            + sFileName
                            + I18NMessage.get(WCMTagParserViewData.class,
                                    "WCMTagParserViewData.label8",
                                    "]不是有效的WCM文件！"), m_tagItem.toString());
            return new String[] { "NotValidFileName" };
        }

        // 1.4 判断文件是否存在
        // 1.4.1 如果是图片文件并且限制了大小，需要转换为新的文件地址
        String sAbsoluteFieldName = FileHelper.makeAbsoluteFilePathOfImage(
                sFileName, _context.getAttribute("InWidth", 0));
        sFileName = CMyFile.extractFileName(sAbsoluteFieldName);
        File file = new File(sAbsoluteFieldName);
        if (!file.exists()) {
            if (!_context.isPreview())
                _context.getPageContext().addErrorLog(
                        PublishConstants.TASK_STATUS_FAILED,
                        I18NMessage.get(WCMTagParserViewData.class,
                                "WCMTagParserViewData.label7", "相关附件[")
                                + sFileName
                                + I18NMessage.get(WCMTagParserViewData.class,
                                        "WCMTagParserViewData.label9",
                                        "]不存在！[AbsoluteFile=")
                                + sAbsoluteFieldName + "]",
                        m_tagItem.toString());
            return new String[] { "NotExistFile" };
        }

        // 1.5 如果要求返回文件大小，直接返回文件大小
        if (_context.getAttribute("ReturnFileSize", false)) {
            return new String[] { FileHelper.convertFileSize(file.length()) };
        }

        // 2. 判断目标文件是否已经上传（判断标准：pub中是否存在文件），如果已经传了，那么不再分发
        IPublishContent oApdOwner = (IPublishContent) _context.getUpperHost();
        PublishPathCompass compass = _context.getPathCompass();
        boolean bPreview = _context.isPreview();
        String sLocalPath = compass.getLocalPath(oApdOwner, bPreview);
        if (!CMyFile.fileExists(sLocalPath + sFileName)) {
            PublishSyncGlobalTuner.insureLocalPathExists(sLocalPath);

            // 如果是需要解压的文件，需要做特殊处理
            String sUnZipPathField = _context
                    .getAttributeTrim("UnZipPathField");
            String sFileExt = CMyFile.extractFileExt(sFileName);
            if (!CMyString.isEmpty(sUnZipPathField)
                    && "ZIP".equalsIgnoreCase(sFileExt)) {
                String sUnZipPath = m_host.getPropertyAsString(sUnZipPathField);
                if (!CMyString.isEmpty(sUnZipPath)) {
                    try {
                        doUnZipAndDistribute(_context, sFileName, sUnZipPath);
                        return new String[] { sUnZipPath };
                    } catch (Exception e) {
                        throw new WCMException("Fail to unzip ["
                                + sAbsoluteFieldName + "]!", e);
                    }
                }
            }

            try {
                CMyFile.copyFile(sAbsoluteFieldName, sLocalPath + sFileName);
                // 若不是预览模式，则需要分发到发布服务器
                if (!bPreview) {
                    _context.getFileDistributeShip().distributeFile(
                            sAbsoluteFieldName, oApdOwner, null, true);
                }
            } catch (Exception e) {
                throw new WCMException(I18NMessage.get(
                        WCMTagParserViewData.class,
                        "WCMTagParserViewData.label10", "分发文件失败！[UpperHost=")
                        + _context.getUpperHost().getInfo()
                        + "][FileName="
                        + sFileName + "]", e);
            }

        }

        // 3. 解析附件的路径，返回结果
        // boolean sUrlIsAbs = _context.getAttribute("UrlIsAbs", false);
        // String sResult = null;
        //
        // if (!sUrlIsAbs) {
        // sResult = HtmlUtil.calRelativePath(
        // compass.getAbsoluteHttpPath(oApdOwner),
        // _context.getPageHttpPath())
        // + sFileName;
        // } else {
        // if (bPreview) {
        // sResult = compass.getPreviewHttpPath(oApdOwner) + sFileName;
        // } else {
        // sResult = compass.getAbsoluteHttpPath(oApdOwner) + sFileName;
        // }
        // }
        // 统一由附件解析器构造路径，有多域名处理
        // add by caohui@2011-7-22 下午03:29:49
        String sResult = WCMAppendixGenerator.makeAppendixURL(_context,
                oApdOwner, sFileName);
        return new String[] { sResult };
    }

    private void doUnZipAndDistribute(PublishTagContext _context,
            String _sZipFileName, String _sUnZipPathName) throws Exception {
        FilesMan filesMan = FilesMan.getFilesMan();
        String sAbsoluteZipFilePath = filesMan.mapFilePath(_sZipFileName,
                FilesMan.PATH_LOCAL);
        sAbsoluteZipFilePath = CMyString.setStrEndWith(sAbsoluteZipFilePath,
                File.separatorChar);

        String sNewPath = sAbsoluteZipFilePath + _sUnZipPathName
                + File.separatorChar;
        boolean bPreview = _context.isPreview();
        String sFolderLocalPath = _context.getPathCompass().getLocalPath(
                _context.getPageFolder(), bPreview);
        String sContentLocalPath = _context.getPathCompass().getLocalPath(
                _context.getUpperHost(), bPreview);
        // 如果已经解压，就不再解压，也不再分发
        if (new File(sContentLocalPath + _sZipFileName).exists()) {
            return;
        }

        // 因为压缩文件的可能带有一层目录，所以需要复杂的rename操作，先解压到临时目录，然后再rename
        String sUnzipPath = sAbsoluteZipFilePath
                + CMyFile.excludeFileExt(_sZipFileName) + File.separatorChar;
        CMyUnzip myUnzip = new CMyUnzip();
        myUnzip.addStrictFile(ConfigServer.getServer().getInitProperty(
                "FILE_UPLOAD_ALLOW_SUFFIX")
                + ",shtml,shtm");
        myUnzip.setZipFile(sAbsoluteZipFilePath + _sZipFileName);
        myUnzip.unzip(sUnzipPath, true);

        File newFilePath = new File(sNewPath);
        if (newFilePath.exists()) {
            String sBackupPath = sNewPath.substring(0, sNewPath.length() - 1)
                    + "_" + System.currentTimeMillis() + File.separatorChar;
            newFilePath.renameTo(new File(sBackupPath));

            newFilePath = new File(sNewPath);
        }

        File file = new File(sUnzipPath);
        File oRenameFile = file;
        File[] pSubFiles = file.listFiles();
        if (pSubFiles.length == 1 && pSubFiles[0].isDirectory()) {
            oRenameFile = pSubFiles[0];
        }
        if (!oRenameFile.renameTo(newFilePath)) {
            throw new Exception("Fail rename " + oRenameFile.getAbsolutePath()
                    + " to " + sNewPath);
        }

        // 将目录拷贝到Preview或者Pub目录中
        try {
            CMyFile.copyFileDir(sNewPath, sFolderLocalPath, true);

            // 若不是预览模式，则需要分发到发布服务器
            if (!bPreview) {
                // 底层分发目录实现的有问题，自己写递归函数实现
                distributePath(_context, _context.getPageFolder(), sNewPath,
                        _sUnZipPathName);
            }

            // 拷贝ZIP文件为了表示已经分发过
            CMyFile.copyFile(sAbsoluteZipFilePath + _sZipFileName,
                    sContentLocalPath + _sZipFileName, true);
        } catch (Exception e) {
            throw new WCMException(I18NMessage.get(WCMTagParserViewData.class,
                    "WCMTagParserViewData.label10", "分发文件失败！[UpperHost=")
                    + _context.getUpperHost().getInfo()
                    + "][FileName="
                    + _sZipFileName + "]", e);
        }
    }

    private void distributePath(PublishTagContext _context,
            IPublishElement _dstElement, String _sSrcFilePath,
            String _sSubPathName) throws WCMException {
        File[] pFiles = new File(_sSrcFilePath).listFiles();
        for (int i = 0; i < pFiles.length; i++) {
            File oFile = pFiles[i];
            // 直接分发
            if (oFile.isFile()) {
                _context.getFileDistributeShip().distributeFile(
                        oFile.getAbsolutePath(), _dstElement, _sSubPathName,
                        false);
                continue;
            }

            // 需要加一级子目录，才可以让分发时将子目录也创建
            String sSubPathName = _sSubPathName;
            if (CMyString.isEmpty(sSubPathName)) {
                sSubPathName = "";
            } else {
                sSubPathName += '/';
            }
            sSubPathName += oFile.getName();

            // 然后递归分发
            distributePath(_context, _dstElement, oFile.getAbsolutePath(),
                    sSubPathName);

        }

    }

    /**
     * 提取文档正文内容
     * 
     * @return 文档的正文内容
     * @throws WCMException
     *             提取失败，会抛出异常。
     */
    private String[] parseHtmlContent(String _sHTMLContent,
            PublishTagContext _context) throws WCMException {
        if (CMyString.isEmpty(_sHTMLContent)) {
            return null;
        }

        // wenyh@2009-10-29 comment:wml.
        if (_context.getAttribute("WML", false)
                && _context.getAttribute("PAGENUM", 0) > 0) {
            String sText = _sHTMLContent.replaceAll("<{1}[^>]{1,}>{1}", "");
            return parseText(_context, sText);
        }

        String sResult = null;
        try {
            sResult = (new HTMLContentParseHelper()).makeHTMLContentParsed(
                    _context, _sHTMLContent);
            
            String sCleanTagsAttrs = _context.getAttribute("cleanattrs4tags");
            if (!CMyString.isEmpty(sCleanTagsAttrs)) {
                sResult = CMyString.cleanAttributes4Tags(sResult,
                        sCleanTagsAttrs);
            }

            String sExcludeTags = _context.getAttribute("excludetags");
            if (!CMyString.isEmpty(sExcludeTags)) {
                sResult = CMyString.excludeTags(sResult, sExcludeTags);
            }

            String sIncludeTags = _context.getAttribute("includetags");
            if (!CMyString.isEmpty(sIncludeTags)) {
                sResult = CMyString.includeTags(sResult, sIncludeTags);
            }
            
            // add by liuhm @2013-03-06
            // 置标手册中写的格式化属性看起来以前是没支持的，在此移动门户的正文内容需要格式化为innerHTML，因此添加了此处理
            sResult = TagParseHelper.formatTextIfNeeded(_context, sResult);
            
            // CC@2012-06-15 comment: filter for num value
            // 因为try代码块中并没有调用到父类进行NUM过滤，因此这里需要添加上对NUM的逻辑处理
            if (_context.getAttribute("NUM", 0) > 0) {
                sResult = TagParseHelper
                        .truncateTextIfNeeded(_context, sResult);
            }
            // 只有细览页面才需要分页
            if (_context.getPageElement() instanceof IPublishContent) {
                return TagParseHelper.separatePages(sResult);
            }

            return new String[] { sResult };
        } catch (Exception ex) {
            throw new WCMException(ExceptionNumber.ERR_WCMEXCEPTION,
                    I18NMessage.get(WCMTagParserViewData.class,
                            "WCMTagParserViewData.label11", "解析 ")
                            + m_host
                            + I18NMessage.get(WCMTagParserViewData.class,
                                    "WCMTagParserViewData.label12", " 的正文失败!"),
                    ex);
        }
    }// END:getContent()

    private static boolean containsTagItemChild(TagItem _currTagItem) {
        List m_children = _currTagItem.getChildren();
        if (m_children == null) {
            return false;
        }

        // else
        Object child;
        for (int i = 0, nSize = m_children.size(); i < nSize; i++) {
            child = m_children.get(i);
            if (child != null && (child instanceof TagItem)) {
                return true;
            }
        }

        // else, not found
        return false;
    }

    private String[] parseHomeSite(Channel channel, PublishTagContext _context,
            int _nPropId) throws WCMException {
        if (channel == null) {
            return null;
        }

        WebSite site = channel.getSite();
        if (site == null) {
            return null;
        }
        String sHomeName = _nPropId == HOME_SITE ? site.getDesc() : site
                .getName();
        try {
            // to truncate text
            String sResult = TagParseHelper.truncateTextIfNeeded(_context,
                    sHomeName);
            sResult = TagParseHelper.formatTextIfNeeded(_context, sResult);

            // to render link
            if (_context.getAttribute("AUTOLINK", false)) {
                IPublishFolder folder = (IPublishFolder) PublishElementFactory
                        .makeElementFrom(site);
                sResult = TagParseHelper.addAutoLink(folder.getRootDomain(),
                        sResult, sHomeName, _context);
            }

            return new String[] { sResult };
        } catch (Exception ex) {
            throw new WCMException(I18NMessage.get(WCMTagParserViewData.class,
                    "WCMTagParserViewData.label11", "解析 ")
                    + channel
                    + I18NMessage.get(WCMTagParserViewData.class,
                            "WCMTagParserViewData.label15", " 的所属站点失败!"), ex);
        }
    }

    private TagItem findObjectsTagItem(TagItem _currParentTagItem) {
        if (_currParentTagItem == null)
            return null;

        String sTagName = _currParentTagItem.getName().toUpperCase();
        // 先判断父置标是否是需要继续向上递归寻找的置标
        if (sTagName.equals(PublishConstants.TAGNAME_RECORD)
                || sTagName.equals(PublishConstants.TAGNAME_EXISTSDATA)
                || sTagName.equals(PublishConstants.TAGNAME_NOTEXISTSDATA)
                || sTagName.equals("TRS_CONDITION")) {
            return findObjectsTagItem(_currParentTagItem.getParent());
        }

        return _currParentTagItem;
    }

    protected String getSelectFields(String _sTableName,
            String _sElementTagName, PublishTagContext _tagContext) {
        // 如果是细览页面，暂时确定为SelectFields为*
        if (_tagContext.getPageElement() instanceof IPublishContent)
            return "*";

        // 找到集合父置标，根据所有的TRS_ViewData置标构造出SelectFields
        TagItem oObjectsTagItem = findObjectsTagItem(_tagContext.getTagItem()
                .getParent());
        if (oObjectsTagItem == null)
            return "*";
        // 概览中获取所有ViewData字段的值
        String sSelectAll = oObjectsTagItem.getAttribute("SelectAll");
        if ("true".equalsIgnoreCase(sSelectAll)) {
            return "*";
        }

        // 检查Tag上是否缓存了计算结果，Lazy Load
        String sKey = (_sTableName + ".SELECT").toUpperCase();
        String sSelectFields = oObjectsTagItem.getExtraAttributeValue(sKey);
        if (sSelectFields == null) {
            synchronized (oObjectsTagItem) {
                sSelectFields = oObjectsTagItem.getExtraAttributeValue(sKey);
                if (sSelectFields == null) {
                    sSelectFields = makeSelectFields(_sTableName,
                            _sElementTagName, oObjectsTagItem, _tagContext);

                    // 将结果缓存在TagItem上，避免多次计算
                    oObjectsTagItem.setExtraAttribute(sKey, sSelectFields);
                }
            }
        }
        return sSelectFields;
    }

    /**
     * @param _tagContext
     * @return
     */
    private String makeSelectFields(String _sTableName, String _sTagName,
            TagItem _oObjectsTagItem, PublishTagContext _tagContext) {

        ArrayList arDefaultFields = FIELDS_DEFAULT_LIST_OF_METADATA;

        String sSelectFields = (String) arDefaultFields.get(0);
        for (int i = 1, nSize = arDefaultFields.size(); i < nSize; i++) {
            sSelectFields += "," + arDefaultFields.get(i);
        }

        ArrayList listChildFields = TagParseHelper.makeSelectFields(
                _oObjectsTagItem, _sTagName);

        DBManager dbMgr = DBManager.getDBManager();
        for (int i = 0, nSize = listChildFields.size(); i < nSize; i++) {
            // Already Add
            String sFieldName = (String) listChildFields.get(i);
            if (arDefaultFields.indexOf(sFieldName) >= 0)
                continue;

            // Check Field Exists
            try {
                if (dbMgr.getFieldInfo(_sTableName, sFieldName) == null) {
                    _tagContext.addWarning(_sTagName
                            + I18NMessage.get(WCMTagParserViewData.class,
                                    "WCMTagParserViewData.label16",
                                    "置标指定的Field[")
                            + sFieldName
                            + I18NMessage.get(WCMTagParserViewData.class,
                                    "WCMTagParserViewData.label17", "]在")
                            + _sTableName
                            + I18NMessage.get(WCMTagParserViewData.class,
                                    "WCMTagParserViewData.label18", "表中不存在！"));
                    continue;
                }
            } catch (WCMException ex) {
                ex.printStackTrace();
                try {
                    _tagContext.addWarning(I18NMessage.get(
                            WCMTagParserViewData.class,
                            "WCMTagParserViewData.label19", "获取指定的Field[")
                            + sFieldName
                            + I18NMessage.get(WCMTagParserViewData.class,
                                    "WCMTagParserViewData.label20", "]从")
                            + _sTableName
                            + I18NMessage.get(WCMTagParserViewData.class,
                                    "WCMTagParserViewData.label21", "表中发生异常！")
                            + CMyException.getStackTraceText(ex));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            // Add Field
            sSelectFields += "," + sFieldName;
        }

        return sSelectFields;
    }

}