net.duckling.ddl.service.export.impl.ExportServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for net.duckling.ddl.service.export.impl.ExportServiceImpl.java

Source

/*
 * Copyright (c) 2008-2016 Computer Network Information Center (CNIC), Chinese Academy of Sciences.
 * 
 * This file is part of Duckling project.
 *
 * 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 net.duckling.ddl.service.export.impl;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletResponse;

import net.duckling.ddl.common.VWBContext;
import net.duckling.ddl.constant.LynxConstants;
import net.duckling.ddl.service.bundle.BundleItem;
import net.duckling.ddl.service.bundle.IBundleService;
import net.duckling.ddl.service.export.ExportService;
import net.duckling.ddl.service.file.FileVersion;
import net.duckling.ddl.service.file.FileVersionService;
import net.duckling.ddl.service.render.RenderingService;
import net.duckling.ddl.service.resource.IResourceService;
import net.duckling.ddl.service.resource.ITagService;
import net.duckling.ddl.service.resource.Resource;
import net.duckling.ddl.service.resource.ResourceOperateService;
import net.duckling.ddl.service.resource.Tag;
import net.duckling.ddl.service.resource.TagItem;
import net.duckling.ddl.service.team.Team;
import net.duckling.ddl.service.team.TeamService;

import org.apache.commons.compress.archivers.ArchiveException;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.ArchiveStreamFactory;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import cn.vlabs.clb.api.AccessForbidden;
import cn.vlabs.clb.api.CLBException;
import cn.vlabs.clb.api.ResourceNotFound;

/**
 * ???? id2Title, Map? key: ?value? allPages,
 * List?Epub downResPath, Map?ID?key:
 * rid_tid_itemTypevalue
 * 
 * @author Yangxp
 * 
 */

@Service
public class ExportServiceImpl implements ExportService {

    @Autowired
    private IResourceService resourceService;
    @Autowired
    private ITagService tagService;
    @Autowired
    private IBundleService bundleService;
    @Autowired
    private RenderingService renderingService;
    @Autowired
    private TeamService teamService;
    @Autowired
    private FileVersionService fileVersionService;
    @Autowired
    private ResourceOperateService resourceOperateService;

    protected static final Logger LOG = Logger.getLogger(ExportServiceImpl.class);
    private static final String EPUB_XML_HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
    private static final String COVER = "/cover.jpg";
    private static final String CATALOG_TEM = "/catalog.template";
    private static final String HTML_TEM = "/html.template";
    private static final String AONE_CSS = "/aone.css";
    private static final String MIMETYPE = "/mimetype";
    private static final String META_INF = "/container.xml";
    private static final String TOC_NCX = "/toc.ncx";
    private static final String AONE_OPF = "/aone.opf";
    private static final String DEFAULT_TAG = "tag";
    private static final String FAKE_PAGE_TITLE = "404?";
    private Map<String, String> downResPath = new HashMap<String, String>();
    @Value("${ddl.root}WEB-INF/templates/epub")
    private String epubPath;

    public String getEpubPath() {
        return epubPath;
    }

    public void setEpubPath(String epubPath) {
        this.epubPath = epubPath;
    }

    /*
     * ---------------------------- ??
     * ------------------------------------------------
     */
    @Override
    public void download(VWBContext context, String tname, int[] rids, HttpServletResponse resp, String format) {
        downResPath.clear();
        if (format.equalsIgnoreCase("zip")) {
            writeZip2Client(context, tname, rids, resp);
        } else {
            writeEpub2Client(context, tname, rids, resp);
        }
    }

    @Override
    public void download(VWBContext context, String tname, Map<String, List<Tag>> tagMap, HttpServletResponse resp,
            String format) {
        downResPath.clear();
        if (format.equalsIgnoreCase("zip")) {
            writeZip2Client(context, tname, tagMap, resp);
        } else {
            writeEpub2Client(context, tname, tagMap, resp);
        }
    }

    /*
     * -------------------------------- Zip
     * -----------------------------------------
     */

    private void writeZip2Client(VWBContext context, String tname, int[] rids, HttpServletResponse resp) {
        Team team = teamService.getTeamByName(tname);
        setResponseHeader(tname + ".zip", resp);
        ArchiveOutputStream out = getArchiveOutputStream(resp);
        writeCss(tname, out);
        Map<String, String> id2Title = new HashMap<String, String>();
        // ?
        writeForResource(tname, team.getId(), rids, context, out, id2Title, null, false);
        // 
        writeCatalog(tname, id2Title, out);
        writeOverview(DEFAULT_TAG, tname, rids, out, context, false);
        try {
            out.close();
        } catch (IOException e) {
            LOG.error("", e);
        }
    }

    private void writeZip2Client(VWBContext context, String tname, Map<String, List<Tag>> tagMap,
            HttpServletResponse resp) {
        Team team = teamService.getTeamByName(tname);
        setResponseHeader(tname + ".zip", resp);
        ArchiveOutputStream out = getArchiveOutputStream(resp);
        writeCss(tname, out);
        Map<String, String> id2Title = new HashMap<String, String>();
        writeForTag(tname, tagMap, team.getId(), context, out, id2Title, null, false);
        writeCatalog(tname, id2Title, out);
        writeOverview(tname, tagMap, out, context, false);
        try {
            out.close();
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }
    }

    private void writeCatalog(String tname, Map<String, String> id2Title, ArchiveOutputStream out) {
        writeAllPagesFile(tname, id2Title, out);
        writeOverviewSummary(tname, out);
        writeIndexFile(tname, out);
    }

    private void writeAllPagesFile(String tname, Map<String, String> id2Title, ArchiveOutputStream out) {
        String htmlHeader = getTemplate(epubPath + HTML_TEM);
        htmlHeader = htmlHeader.replace("TITLE", "?");
        htmlHeader = htmlHeader.replace("../aone.css", "./aone.css");
        StringBuilder sb = new StringBuilder();
        sb.append(htmlHeader);
        sb.append("<body><h1>?</h1><ul>");
        for (Entry<String, String> entry : id2Title.entrySet()) {
            sb.append("<li><a target=\"pageFrame\" href=\"").append(entry.getKey()).append("\">")
                    .append(entry.getValue()).append("</a></li>");
        }
        sb.append("</ul></body>");
        InputStream in = new ByteArrayInputStream(sb.toString().getBytes());
        try {
            out.putArchiveEntry(new ZipArchiveEntry(tname + "/allPages.html"));
            IOUtils.copy(in, out);
            in.close();
            out.closeArchiveEntry();
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }
    }

    private void writeOverviewSummary(String tname, ArchiveOutputStream out) {
        String htmlHeader = getTemplate(epubPath + HTML_TEM);
        htmlHeader = htmlHeader.replace("TITLE", tname + " overview");
        StringBuilder sb = new StringBuilder();
        sb.append(htmlHeader);
        sb.append("<body><h1>" + tname + "</h1></body>");
        InputStream in = new ByteArrayInputStream(sb.toString().getBytes());
        try {
            out.putArchiveEntry(new ZipArchiveEntry(tname + "/overviewSummary.html"));
            IOUtils.copy(in, out);
            in.close();
            out.closeArchiveEntry();
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }
    }

    private void writeIndexFile(String tname, ArchiveOutputStream out) {
        String html = getTemplate(epubPath + CATALOG_TEM);
        html = html.replaceAll("TEAMNAME", tname);
        InputStream in = new ByteArrayInputStream(html.getBytes());
        try {
            out.putArchiveEntry(new ZipArchiveEntry(tname + "/index.html"));
            IOUtils.copy(in, out);
            in.close();
            out.closeArchiveEntry();
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }
    }

    /*
     * -------------------------------- Epub
     * -----------------------------------------
     */

    private void writeEpub2Client(VWBContext context, String tname, int[] rids, HttpServletResponse resp) {
        Team team = teamService.getTeamByName(tname);
        setResponseHeader(tname + ".epub", resp);
        ArchiveOutputStream out = getArchiveOutputStream(resp);
        writeCss(tname, out);
        Map<String, String> id2Title = new HashMap<String, String>();
        List<String> allPages = new ArrayList<String>();
        writeForResource(tname, team.getId(), rids, context, out, id2Title, allPages, true);
        writeOverview(DEFAULT_TAG, tname, rids, out, context, true);
        writeEpubFiles(tname, allPages, out, id2Title);
        try {
            out.close();
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }
    }

    private void writeEpub2Client(VWBContext context, String tname, Map<String, List<Tag>> tagMap,
            HttpServletResponse resp) {
        Team team = teamService.getTeamByName(tname);
        setResponseHeader(tname + ".epub", resp);
        ArchiveOutputStream out = getArchiveOutputStream(resp);
        writeCss(tname, out);
        Map<String, String> id2Title = new HashMap<String, String>();
        List<String> allPages = new ArrayList<String>();
        writeForTag(tname, tagMap, team.getId(), context, out, id2Title, allPages, true);
        writeOverview(tname, tagMap, out, context, true);
        writeEpubFiles(tname, allPages, out, id2Title);
        try {
            out.close();
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }
    }

    private void writeEpubFiles(String tname, List<String> allPages, ArchiveOutputStream out,
            Map<String, String> id2Title) {
        writeMimetype(out);
        writeMetaInfo(tname, out);
        writeCover(tname, out);
        writeOpf(tname, allPages, out);
        writeNcx(tname, allPages, id2Title, out);
    }

    private void writeMimetype(ArchiveOutputStream out) {
        String mimeType = getTemplate(epubPath + MIMETYPE);
        InputStream in = new ByteArrayInputStream(mimeType.getBytes());
        try {
            out.putArchiveEntry(new ZipArchiveEntry("mimetype"));
            IOUtils.copy(in, out);
            in.close();
            out.closeArchiveEntry();
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }
    }

    private void writeMetaInfo(String tname, ArchiveOutputStream out) {
        String metaTemplate = getTemplate(epubPath + META_INF);
        if (null != tname) {
            metaTemplate = metaTemplate.replace("TEAMNAME", "" + tname);
        } else {
            metaTemplate = metaTemplate.replace("TEAMNAME/", "");
        }
        InputStream in = new ByteArrayInputStream(metaTemplate.getBytes());
        try {
            out.putArchiveEntry(new ZipArchiveEntry("META-INF/container.xml"));
            IOUtils.copy(in, out);
            in.close();
            out.closeArchiveEntry();
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }
    }

    private void writeCover(String path, ArchiveOutputStream out) {
        try {
            FileInputStream fis = new FileInputStream(epubPath + COVER);
            String entry = null != path ? path + "/cover.jpg" : "cover.jpg";
            out.putArchiveEntry(new ZipArchiveEntry(entry));
            IOUtils.copy(fis, out);
            fis.close();
            out.closeArchiveEntry();
        } catch (FileNotFoundException e) {
            LOG.error(COVER + "");
            LOG.error(e.getMessage(), e);
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }
    }

    private void writeOpf(String tname, List<String> allPages, ArchiveOutputStream out) {
        String pofTemplate = getTemplate(epubPath + AONE_OPF);
        StringBuilder sbItems = new StringBuilder();
        StringBuilder sbItemrefs = new StringBuilder();
        sbItems.append("<item id=\"css\" href=\"aone.css\" media-type=\"text/css\" />\n");
        sbItems.append("<item id=\"cover\" href=\"cover.jpg\" media-type=\"image/jpeg\" />\n");
        if (null != tname) {
            sbItems.append("<item id=\"index\" href=\"overview.html\" media-type=\"application/xhtml+xml\" />\n");
            sbItemrefs.append("<itemref idref=\"index\" />\n");
        }
        Iterator<String> it = allPages.iterator();
        while (it.hasNext()) {
            String str = it.next();
            if (str.contains("/index.html")) {
                String tagname = getIDFromResPath(str);
                String path = str.substring(str.indexOf("/") + 1, str.length());
                sbItems.append("<item id=\"" + tagname + "\" href=\"" + path
                        + "\" media-type=\"application/xhtml+xml\" />\n");
                sbItemrefs.append("<itemref idref=\"" + tagname + "\" />\n");
            } else {
                String pid = getIDFromResPath(str);
                sbItems.append(
                        "<item id=\"" + pid + "\" href=\"" + str + "\" media-type=\"application/xhtml+xml\" />\n");
                sbItemrefs.append("<itemref idref=\"" + pid + "\" />\n");
            }
        }
        sbItems.append("<item id=\"ncx\" href=\"toc.ncx\" media-type=\"application/x-dtbncx+xml\" />");
        pofTemplate = pofTemplate.replace("ITEMS", sbItems.toString());
        pofTemplate = pofTemplate.replace("ITEMREFS", sbItemrefs.toString());
        InputStream in = new ByteArrayInputStream(pofTemplate.getBytes());
        try {
            String entry = null != tname ? tname + "/aone.opf" : "aone.opf";
            out.putArchiveEntry(new ZipArchiveEntry(entry));
            IOUtils.copy(in, out);
            in.close();
            out.closeArchiveEntry();
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }
    }

    private void writeNcx(String tname, List<String> allPages, Map<String, String> id2Title,
            ArchiveOutputStream out) {
        String ncxTemplate = getTemplate(epubPath + TOC_NCX);
        if (null != tname) {
            ncxTemplate = ncxTemplate.replace("DOCTITLE", "" + tname);
            ncxTemplate = ncxTemplate.replace("DOCAUTHOR", "" + tname);
        }
        StringBuilder sb = new StringBuilder();
        int playOrder = 0;
        if (null != tname) {
            sb.append("<navPoint id=\"nv-catalog\" playOrder=\"" + (++playOrder) + "\">\n"
                    + "<navLabel><text></text></navLabel>\n<content src=\"overview.html\"/>\n</navPoint>\n");
        }
        int len = allPages.size();
        for (int par = 0; par < len; par++) {
            String str = allPages.get(par);
            if (str.contains("/index.html")) {
                String tagName = getTagNameFromResPath(str);
                String path = str.substring(str.indexOf("/") + 1, str.length());
                playOrder++;
                sb.append("<navPoint id=\"nv-" + playOrder + "\" playOrder=\"" + playOrder + "\">\n<navLabel><text>"
                        + tagName + "</text></navLabel>\n<content src=\"" + path + "\"/>\n");
                for (int child = par + 1; child < len; child++) {
                    String item = allPages.get(child);
                    if (item.contains("index")) {
                        break;
                    }
                    String pageTitle = id2Title.get(item);
                    playOrder++;
                    sb.append("<navPoint id=\"nv-" + playOrder + "\" playOrder=\"" + playOrder
                            + "\">\n<navLabel><text>" + pageTitle + "</text></navLabel>\n<content src=\"" + item
                            + "\"/>\n</navPoint>\n");
                }
                sb.append("</navPoint>\n");
            }
        }
        ncxTemplate = ncxTemplate.replace("NAV_POINTS", sb.toString());
        InputStream in = new ByteArrayInputStream(ncxTemplate.getBytes());
        try {
            String entry = null != tname ? tname + "/toc.ncx" : "toc.ncx";
            out.putArchiveEntry(new ZipArchiveEntry(entry));
            IOUtils.copy(in, out);
            in.close();
            out.closeArchiveEntry();
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }
    }

    /*
     * -------------------------------- 
     * -----------------------------------------
     */

    private void setResponseHeader(String filename, HttpServletResponse resp) {
        try {
            filename = new String(filename.getBytes("utf8"), "ISO-8859-1");
        } catch (UnsupportedEncodingException e) {
            LOG.error(e.getMessage(), e);
        }
        resp.setContentType("application/zip");
        resp.setCharacterEncoding("UTF-8");
        resp.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
    }

    private ArchiveOutputStream getArchiveOutputStream(HttpServletResponse resp) {
        ArchiveOutputStream aos = null;
        try {
            aos = new ArchiveStreamFactory().createArchiveOutputStream(ArchiveStreamFactory.ZIP,
                    resp.getOutputStream());
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        } catch (ArchiveException e) {
            LOG.error(e.getMessage(), e);
        }
        return aos;
    }

    private void writeOverview(String tag, String tname, int[] rids, ArchiveOutputStream out, VWBContext context,
            boolean isEpub) {
        String htmlHeader = getTemplate(epubPath + HTML_TEM);
        htmlHeader = htmlHeader.replace("TITLE", "" + tname);
        htmlHeader = htmlHeader.replaceAll("[\\.]{2}", "\\.");

        StringBuilder sb = new StringBuilder();
        if (isEpub) {
            sb.append(EPUB_XML_HEADER);
            sb.append(htmlHeader);
            sb.append("<body><h1></h1><ul>");
            for (int rid : rids) {
                writeOverviewItemByResource(context, rid, sb, tag, tname, true);
            }
        } else {
            sb.append(htmlHeader);
            sb.append("<body><h1></h1><ul>");
            for (int rid : rids) {
                writeOverviewItemByResource(context, rid, sb, tag, tname, false);
            }
        }
        sb.append("</ul></body></html>");
        InputStream in = new ByteArrayInputStream(sb.toString().getBytes());
        try {
            out.putArchiveEntry(new ZipArchiveEntry(tname + "/overview.html"));
            IOUtils.copy(in, out);
            in.close();
            out.closeArchiveEntry();
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }
    }

    private void writeOverview(String tname, Map<String, List<Tag>> tagMap, ArchiveOutputStream out,
            VWBContext context, boolean isEpub) {
        String htmlHeader = getTemplate(epubPath + HTML_TEM);
        htmlHeader = htmlHeader.replace("TITLE", "" + tname);
        htmlHeader = htmlHeader.replaceAll("[\\.]{2}", "\\.");

        StringBuilder sb = new StringBuilder();
        if (isEpub) {
            sb.append(EPUB_XML_HEADER);
            sb.append(htmlHeader);
            sb.append("<body><h1></h1><ul>");
            writeOverviewItemByTag(context, tagMap, sb, true);
        } else {
            sb.append(htmlHeader);
            sb.append("<body><h1></h1><ul>");
            writeOverviewItemByTag(context, tagMap, sb, false);
        }
        sb.append("</ul></body></html>");
        InputStream in = new ByteArrayInputStream(sb.toString().getBytes());
        try {
            out.putArchiveEntry(new ZipArchiveEntry(tname + "/overview.html"));
            IOUtils.copy(in, out);
            in.close();
            out.closeArchiveEntry();
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }
    }

    private void writeOverviewItemByResource(VWBContext context, int rid, StringBuilder sb, String tag,
            String tname, boolean isEpub) {
        IResourceService rs = resourceService;
        Resource res = rs.getResource(rid);
        if (null == res) {
            return;
        }
        if (LynxConstants.TYPE_PAGE.equals(res.getItemType())) {
            String element = "<li><a";
            element = isEpub ? element : (element + " target=\"pageFrame\"");
            element = element + " href=\"" + tag + "/" + rid + ".html\"" + ">" + res.getTitle() + "</a></li>";
            sb.append(element);
        } else if (LynxConstants.TYPE_FILE.equals(res.getItemType())) {
            String resKey = res.getRid() + "_" + res.getTid() + "_" + res.getItemType();
            String element = "<li><a";
            element = isEpub ? element : (element + " target=\"pageFrame\"");
            element = element + " href=\"" + getResTagPath(resKey) + "\">" + res.getTitle() + "</a></li>";
            sb.append(element);
        } else {
            sb.append("<li><ul><p class=\"listTitle\">" + res.getTitle() + "</p>");
            List<BundleItem> items = bundleService.getBundleItems(rid);
            List<Long> itemsIds = new ArrayList<Long>();
            if (null != items && items.size() > 0) {
                for (BundleItem item : items) {
                    itemsIds.add((long) item.getRid());
                }
            }
            List<Resource> resList = rs.getResourcesBySphinxID(itemsIds);
            if (null != resList && resList.size() > 0) {
                for (Resource resource : resList) {
                    writeOverviewItemByResource(context, resource.getRid(), sb, tag, tname, isEpub);
                }
            }
            sb.append("</ul></li>");
        }
    }

    private void writeOverviewItemByTag(VWBContext context, Map<String, List<Tag>> tagMap, StringBuilder sb,
            boolean isEpub) {
        if (null != tagMap && !tagMap.isEmpty()) {
            for (Map.Entry<String, List<Tag>> entry : tagMap.entrySet()) {
                sb.append("<li><ul><p  class=\"listTitle\">" + entry.getKey() + "</p>");
                List<Tag> tags = entry.getValue();
                if (null != tags && !tags.isEmpty()) {
                    for (Tag tag : tags) {
                        String target = isEpub ? " " : " target=\"pageListFrame\" ";
                        sb.append("<li><a" + target + "href=\"" + tag.getTitle() + "_" + tag.getId()
                                + "/index.html\">" + tag.getTitle() + "</a></li>");
                    }
                }
                sb.append("</ul></li>");
            }
        }
    }

    private String getTemplate(String filename) {
        try {
            FileInputStream fis = new FileInputStream(filename);
            BufferedReader br = new BufferedReader(new InputStreamReader(fis));
            StringBuilder sb = new StringBuilder();
            String str = br.readLine();
            while (str != null) {
                sb.append(str + "\n");
                str = br.readLine();
            }
            return sb.toString();
        } catch (FileNotFoundException e) {
            LOG.error(filename + "");
            LOG.error(e.getMessage(), e);
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }
        return "";
    }

    private void writeCss(String tname, ArchiveOutputStream out) {
        String css = getTemplate(epubPath + AONE_CSS);
        InputStream in = new ByteArrayInputStream(css.getBytes());
        String entry = tname != null ? tname + "/aone.css" : "aone.css";
        try {
            out.putArchiveEntry(new ZipArchiveEntry(entry));
            IOUtils.copy(in, out);
            in.close();
            out.closeArchiveEntry();
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }
    }

    private String getHtmlHeader(Resource page, boolean isEpub) {
        StringBuilder sb = new StringBuilder();
        if (isEpub) {
            sb.append(EPUB_XML_HEADER);
        }
        String htmlHeader = getTemplate(epubPath + HTML_TEM);
        htmlHeader = htmlHeader.replace("TITLE", page.getTitle());
        sb.append(htmlHeader);
        sb.append("<body>");
        return sb.toString();
    }

    private String getHtmlFooter() {
        return "</body></html>";
    }

    private String getProcessedHtml(String html, VWBContext context, String path, ArchiveOutputStream out,
            Map<String, String> id2Title, List<String> allPages, boolean isEpub) {
        String s = processFileOrPageLink(html, LynxConstants.TYPE_FILE, context, path, out, id2Title, allPages,
                isEpub);
        String ss = processImageLink(s, context, path, out, id2Title, allPages, isEpub);
        String sss = processFileOrPageLink(ss, LynxConstants.TYPE_PAGE, context, path, out, id2Title, allPages,
                isEpub);
        return sss;
    }

    private String processImageLink(String html, VWBContext context, String path, ArchiveOutputStream out,
            Map<String, String> id2Title, List<String> allPages, boolean isEpub) {
        String regex = "[0-9a-zA-Z\\-\\/]+/download/([0-9]+)(\\?func=cache){0,1}";
        Pattern p = Pattern.compile(regex);
        String[] cells = p.split(html);
        Matcher m = p.matcher(html);
        StringBuilder sb = new StringBuilder();
        sb.append(cells[0]);
        int index = 1;
        while (m.find()) {
            int imageId = Integer.parseInt(m.group(1));
            String resKey = imageId + "_" + VWBContext.getCurrentTid() + "_" + LynxConstants.TYPE_FILE;
            String tagname = path.substring(0, path.lastIndexOf("/"));
            String resPath = this.getRelativeResPath(resKey, tagname);
            if (null == resPath) {// ?
                writeAttFile(path, VWBContext.getCurrentTid(), imageId, context, out);
                resPath = getResNoTagPath(resKey);
                resPath = (null == resPath) ? "#" : resPath;
            }
            sb.append(resPath);
            sb.append(cells[index++]);
        }
        if (index < cells.length) {
            sb.append(cells[index]);
        }
        return sb.toString();
    }

    private String processFileOrPageLink(String html, String type, VWBContext context, String path,
            ArchiveOutputStream out, Map<String, String> id2Title, List<String> allPages, boolean isEpub) {
        String regex;
        if (LynxConstants.TYPE_FILE.equals(type)) {
            regex = "/file/([0-9]+)";
        } else {
            regex = "/page/([0-9]+)";
        }
        Pattern p = Pattern.compile(regex);
        String[] cells = p.split(html);
        if (cells.length == 1) {
            return html;
        }
        for (int i = 0; i < cells.length; i++) {
            int indexHref = cells[i].lastIndexOf("href=\"");
            if (i < cells.length - 1 && indexHref > 0) {
                cells[i] = cells[i].substring(0, indexHref);
            }
            int indexQuote = cells[i].indexOf('"');
            if (i > 0) {
                cells[i] = cells[i].substring(indexQuote + 1);
            }
        }
        Matcher m = p.matcher(html);
        StringBuilder sb = new StringBuilder();
        sb.append(cells[0]);
        int index = 1;
        while (m.find()) {
            int attId = Integer.parseInt(m.group(1));
            String resKey = attId + "_" + VWBContext.getCurrentTid() + "_" + type;
            String tagname = path.substring(0, path.lastIndexOf("/"));
            String resPath = getRelativeResPath(resKey, tagname);
            if (null == resPath) {// ???
                if (regex.contains("file")) {
                    writeAttFile(path, VWBContext.getCurrentTid(), attId, context, out);
                } else {
                    Resource res = resourceService.getResource(attId, context.getTid());
                    if (null != res) {
                        List<FileVersion> attFiles = fileVersionService.getFilesOfPage(res.getRid(),
                                VWBContext.getCurrentTid());
                        for (FileVersion file : attFiles) {
                            writeAttFile(path, VWBContext.getCurrentTid(), file.getRid(), context, out);
                        }
                        writePage(path, res.getRid(), context, out, id2Title, allPages, isEpub);
                    }
                }
                resPath = getResNoTagPath(resKey);
                resPath = (null == resPath) ? "#" : resPath;
            }
            sb.append("href=\"" + resPath + "\"");
            sb.append(cells[index++]);
        }
        return sb.toString();
    }

    // ?downResPathresKey???tag???
    private String getResNoTagPath(String resKey) {
        if (downResPath.containsKey(resKey)) {
            String fullPath = downResPath.get(resKey);
            String tagPath = fullPath.substring(fullPath.indexOf("/") + 1, fullPath.length());
            String noTagPath = tagPath.substring(tagPath.indexOf("/") + 1, tagPath.length());
            return noTagPath;
        } else
            return null;
    }

    // ?downResPathresKey???tag
    private String getResTagPath(String resKey) {
        if (downResPath.containsKey(resKey)) {
            String fullPath = downResPath.get(resKey);
            String tagPath = fullPath.substring(fullPath.indexOf("/") + 1, fullPath.length());
            return tagPath;
        } else {
            return null;
        }
    }

    // ?downResPathresKey?????tagName?
    private String getRelativeResPath(String resKey, String tagName) {
        if (downResPath.containsKey(resKey)) {
            String fullPath = downResPath.get(resKey);
            String tagPath = fullPath.substring(fullPath.indexOf("/") + 1, fullPath.length());
            String tag = tagPath.substring(0, tagPath.indexOf("/"));
            if (tagName.equals(tag)) {
                return tagPath.substring(tagPath.indexOf("/") + 1, tagPath.length());
            } else {
                return "../" + tagPath;
            }
        } else {
            return null;
        }
    }

    // resPath??IDID?Epubopf
    private String getIDFromResPath(String resPath) {
        String temp = resPath.substring(0, resPath.lastIndexOf("."));
        temp = temp.replace("/", "_");
        return temp;
    }

    // resPath?tag??
    private String getTagNameFromResPath(String resPath) {
        String subStr = resPath.substring(0, resPath.lastIndexOf("/"));
        return subStr.substring(subStr.lastIndexOf("/") + 1, subStr.length());
    }

    // ???titleresKey???
    private String getRestoreFileName(String title, String resKey) {
        int dotIndex = title.lastIndexOf(".");
        dotIndex = (dotIndex <= 0) ? title.length() : dotIndex;
        String newFilename = title.substring(0, dotIndex) + "_" + resKey
                + title.substring(dotIndex, title.length());
        return newFilename;
    }

    /* --------------------------- ? --------------------------------- */

    /*--------------------------------   ?   ----------------------------------------*/
    // 
    private void writeForResource(String parent, int tid, int[] rids, VWBContext context, ArchiveOutputStream out,
            Map<String, String> id2Title, List<String> allPages, boolean isEpub) {
        String path = parent + "/" + DEFAULT_TAG;
        int size = rids.length;
        for (int i = 0; i < size; i++) {
            writeResource(path, tid, rids[i], context, out, id2Title, allPages, isEpub);
        }
    }

    // ?
    private void writeForTag(String parent, Map<String, List<Tag>> tagMap, int tid, VWBContext context,
            ArchiveOutputStream out, Map<String, String> id2Title, List<String> allPages, boolean isEpub) {
        ITagService ts = tagService;
        Team team = teamService.getTeamByID(tid);
        if (null == tagMap || tagMap.isEmpty()) {
            return;
        }
        Map<String, List<Integer>> resMap = new HashMap<String, List<Integer>>();
        for (Map.Entry<String, List<Tag>> entry : tagMap.entrySet()) {
            List<Tag> tagList = entry.getValue();
            if (null != tagList && tagList.size() > 0) {
                for (Tag tag : tagList) {
                    String path = parent + "/" + tag.getTitle() + "_" + tag.getId();
                    List<TagItem> items = ts.getTagItems(tag.getId());
                    if (null != items && items.size() > 0) {
                        List<Integer> rids = new ArrayList<Integer>();
                        for (TagItem item : items) {
                            rids.add(item.getRid());
                        }
                        resMap.put(path, rids);
                    }
                }
            }
        }
        for (Map.Entry<String, List<Integer>> entry : resMap.entrySet()) {
            String path = entry.getKey();
            if (allPages != null) {
                allPages.add(path + "/index.html");
            }
            List<Integer> rids = entry.getValue();
            for (Integer rid : rids) {
                writeResource(path, tid, rid, context, out, id2Title, allPages, isEpub);
            }
            String title = path.substring(path.lastIndexOf("/") + 1, path.length());
            writeTagIndex(path, title, rids, team.getName(), isEpub, out, context);
        }
    }

    // ??
    private void writeResource(String parent, int tid, int rid, VWBContext context, ArchiveOutputStream out,
            Map<String, String> id2Title, List<String> allPages, boolean isEpub) {
        Resource res = resourceService.getResource(rid);
        if (null == res) {
            LOG.error("Resource doesn't exist! rid=" + rid + " and tid=" + tid);
            return;
        }
        String attPath = parent + "/attachfiles";

        if (LynxConstants.TYPE_PAGE.equals(res.getItemType())) {
            List<FileVersion> attFiles = fileVersionService.getFilesOfPage(res.getRid(), tid);
            for (FileVersion file : attFiles) {
                writeAttFile(attPath, tid, file.getRid(), context, out);
            }
            writePage(parent, rid, context, out, id2Title, allPages, isEpub);
        } else if (LynxConstants.TYPE_FILE.equals(res.getItemType())) {
            writeAttFile(attPath, tid, res.getRid(), context, out);
        } else {
            writeBundle(parent, tid, res, context, out, id2Title, allPages, isEpub);
        }
    }

    private void writePage(String path, int resourceId, VWBContext context, ArchiveOutputStream out,
            Map<String, String> id2Title, List<String> allPages, boolean isEpub) {
        Resource page = resourceService.getResource(resourceId);
        StringBuilder sbuf = new StringBuilder();
        String pagePath = path + "/" + resourceId + ".html";
        if (null != page) {
            String resKey = "" + page.getRid() + "_" + page.getTid() + "_" + LynxConstants.TYPE_PAGE;
            if (null != getResTagPath(resKey)) { // ??
                return;
            }
            downResPath.put(resKey, pagePath);
            String html = renderingService.getHTML(context, page);
            // site.getHTML(context, page);
            sbuf.append(getHtmlHeader(page, isEpub));
            sbuf.append("<h1 class=\"title\">" + page.getTitle() + "</h1>");
            sbuf.append(getProcessedHtml(html, context, path, out, id2Title, allPages, isEpub));// ?????
            sbuf.append(getHtmlFooter());
        } else {
            sbuf.append(getPageNotFoundHtml(resourceId, isEpub));
        }
        InputStream in = new ByteArrayInputStream(sbuf.toString().getBytes());
        try {
            out.putArchiveEntry(new ZipArchiveEntry(pagePath));
            IOUtils.copy(in, out);
            in.close();
            out.closeArchiveEntry();
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }
        String tagname = path.substring(path.indexOf('/') + 1);
        String title = (null == page) ? FAKE_PAGE_TITLE : page.getTitle();
        id2Title.put(tagname + "/" + resourceId + ".html", title);
        if (allPages != null) {
            allPages.add(tagname + "/" + resourceId + ".html");
        }
    }

    private String getPageNotFoundHtml(int resourceId, boolean isEpub) {
        StringBuilder result = new StringBuilder();
        Resource fakePage = new Resource();// 
        fakePage.setTitle(FAKE_PAGE_TITLE);
        result.append(getHtmlHeader(fakePage, isEpub));
        result.append(
                "<h1 class=\"title\">ID" + resourceId + "????</h1>");
        result.append(getHtmlFooter());
        return result.toString();
    }

    private void writeAttFile(String path, int tid, int rid, VWBContext context, ArchiveOutputStream out) {
        Resource res = resourceService.getResource(rid, tid);
        if (null == res) {
            LOG.error("File doesn't exist! rid=" + rid + " and tid=" + tid);
            return;
        }
        String resKey = res.getRid() + "_" + res.getTid() + "_" + LynxConstants.TYPE_FILE;
        if (null != getResTagPath(resKey)) {
            return;
        }
        String newFilename = getRestoreFileName(res.getTitle(), resKey);
        downResPath.put(resKey, path + "/" + newFilename);
        FileVersion file = fileVersionService.getLatestFileVersion(rid, tid);
        try {
            //            file = fileService.getFile(rid, tid);
        } catch (net.duckling.ddl.service.export.FileNotFoundException e) {
            LOG.error("ID" + rid + "?");
            return;
        }
        ExportAttachSaver eas = new ExportAttachSaver(path, tid, rid, out);
        try {
            resourceOperateService.getContent(file.getClbId(), "" + file.getClbVersion(), eas);
        } catch (AccessForbidden e) {
            LOG.warn("ID" + file.getClbId() + "??");
        } catch (ResourceNotFound e) {
            LOG.warn("ID" + file.getClbId() + "?");
        } catch (CLBException e) {
            LOG.warn("?ID" + file.getClbId() + "CLBException?");
        }
    }

    private void writeBundle(String parent, int tid, Resource res, VWBContext context, ArchiveOutputStream out,
            Map<String, String> id2Title, List<String> allPages, boolean isEpub) {
        List<BundleItem> items = bundleService.getBundleItems(res.getRid());
        if (null != items && items.size() > 0) {
            for (BundleItem item : items) {
                writeResource(parent, tid, item.getRid(), context, out, id2Title, allPages, isEpub);
            }
        }
    }

    // ??
    private void writeTagIndex(String path, String tagname, List<Integer> rids, String tname, boolean isEpub,
            ArchiveOutputStream out, VWBContext context) {
        String html = getIndexHtml(tagname, rids, tname, isEpub, context);
        InputStream in = new ByteArrayInputStream(html.getBytes());
        try {
            out.putArchiveEntry(new ZipArchiveEntry(path + "/index.html"));
            IOUtils.copy(in, out);
            in.close();
            out.closeArchiveEntry();
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }
    }

    private String getIndexHtml(String tagname, List<Integer> rids, String tname, boolean isEpub,
            VWBContext context) {
        String htmlHeader = getTemplate(epubPath + HTML_TEM);
        htmlHeader = htmlHeader.replace("TITLE", tagname);
        StringBuilder sb = new StringBuilder();
        if (isEpub) {
            sb.append(EPUB_XML_HEADER);
            sb.append(htmlHeader);
            sb.append("<body><h1>" + tagname.substring(0, tagname.lastIndexOf("_")) + "</h1><ul>");
            if (rids != null) {
                for (Integer rid : rids) {
                    writeTagIndexItemByResource(context, rid, sb, tagname, tname, true);
                }

            }
        } else {
            sb.append(htmlHeader);
            sb.append("<body><h1>" + tagname + "</h1><ul>");
            if (rids != null) {
                for (Integer rid : rids) {
                    writeTagIndexItemByResource(context, rid, sb, tagname, tname, false);
                }
            }
        }
        sb.append("</ul></body></html>");
        return sb.toString();
    }

    private void writeTagIndexItemByResource(VWBContext context, int rid, StringBuilder sb, String tag,
            String tname, boolean isEpub) {
        IResourceService rs = resourceService;
        Resource res = rs.getResource(rid);
        if (null == res) {
            LOG.error("Resource doesn't exist! rid=" + rid);
            return;
        }
        if (LynxConstants.TYPE_PAGE.equals(res.getItemType())) {
            String resKey = res.getRid() + "_" + res.getTid() + "_" + res.getItemType();
            String element = "<li><a";
            element = isEpub ? element : (element + " target=\"pageFrame\"");
            element = element + " href=\"" + getRelativeResPath(resKey, tag) + "\">" + res.getTitle() + "</a></li>";
            sb.append(element);
        } else if (LynxConstants.TYPE_FILE.equals(res.getItemType())) {
            String resKey = res.getRid() + "_" + res.getTid() + "_" + res.getItemType();
            String element = "<li><a";
            element = isEpub ? element : (element + " target=\"pageFrame\"");
            element = element + " href=\"" + getRelativeResPath(resKey, tag) + "\">" + res.getTitle() + "</a></li>";
            sb.append(element);
        } else {
            sb.append("<li><ul><p class=\"listTitle\">" + res.getTitle() + "</p>");
            List<BundleItem> items = bundleService.getBundleItems(rid);
            List<Long> itemsIds = new ArrayList<Long>();
            if (null != items && items.size() > 0) {
                for (BundleItem item : items) {
                    itemsIds.add((long) item.getRid());
                }
            }
            List<Resource> resList = rs.getResourcesBySphinxID(itemsIds);
            if (null != resList && resList.size() > 0) {
                for (Resource resource : resList) {
                    writeTagIndexItemByResource(context, resource.getRid(), sb, tag, tname, isEpub);
                }
            }
            sb.append("</ul></li>");
        }
    }
}