de.fme.alfresco.repo.web.scripts.datalist.DataListDownloadWebScript.java Source code

Java tutorial

Introduction

Here is the source code for de.fme.alfresco.repo.web.scripts.datalist.DataListDownloadWebScript.java

Source

/*
 * Copyright (C) 2005-2010 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */
package de.fme.alfresco.repo.web.scripts.datalist;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import org.alfresco.model.ApplicationModel;
import org.alfresco.model.ContentModel;
import org.alfresco.model.ForumModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.site.SiteInfo;
import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.namespace.InvalidQNameException;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.DataFormat;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.tika.parser.ParsingReader;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest;

import de.fme.alfresco.repo.web.scripts.DeclarativeSpreadsheetWebScript;

/**
 * Data List Download
 * 
 * Exports the contents of a Data List as an Excel file
 * 
 * @author Nick Burch, Jan Pfitzner
 */
public class DataListDownloadWebScript extends DeclarativeSpreadsheetWebScript implements InitializingBean {
    // Logger
    private static final Log logger = LogFactory.getLog(DataListDownloadWebScript.class);
    private static final String DL_NAMESPACE = "http://www.alfresco.org/model/datalist/1.0";

    private static final QName DATA_LIST_ITEM_TYPE = QName.createQName(DL_NAMESPACE, "dataListItemType");

    private NodeService nodeService;
    private SiteService siteService;
    private ContentService contentService;
    private PersonService personService;
    private NamespaceService namespaceService;
    private Map<QName, List<QName>> modelOrder;
    private Map<String, String> rawModelOrder;

    public DataListDownloadWebScript() {
        this.filenameBase = "DataListExport";
    }

    /**
     * @param nodeService
     */
    public void setNodeService(NodeService nodeService) {
        this.nodeService = nodeService;
    }

    /**
     * @param nodeService
     */
    public void setSiteService(SiteService siteService) {
        this.siteService = siteService;
    }

    /**
     * @param namespaceService
     */
    public void setNamespaceService(NamespaceService namespaceService) {
        this.namespaceService = namespaceService;
    }

    /**
     * @param contentService the contentService to set
     */
    public void setContentService(ContentService contentService) {
        this.contentService = contentService;
    }

    /**
     * @param personService the personService to set
     */
    public void setPersonService(PersonService personService) {
        this.personService = personService;
    }

    public void setModelOrder(Map<String, String> rawModelOrder) {
        this.rawModelOrder = rawModelOrder;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        modelOrder = new HashMap<QName, List<QName>>();
        for (String key : rawModelOrder.keySet()) {
            QName model;
            List<QName> order = new ArrayList<QName>();

            try {
                model = QName.createQName(key, namespaceService);
            } catch (InvalidQNameException e) {
                logger.warn("Skipping invalid model type " + key);
                continue;
            }

            StringTokenizer st = new StringTokenizer(rawModelOrder.get(key), ",");
            while (st.hasMoreTokens()) {
                order.add(QName.createQName(st.nextToken(), namespaceService));
            }
            modelOrder.put(model, order);
        }
    }

    /**
     * Identify the datalist
     */
    @Override
    protected Object identifyResource(String format, WebScriptRequest req) {
        // Try to find the datalist they requested
        NodeRef list;
        Map<String, String> args = req.getServiceMatch().getTemplateVars();
        if (args.get("store_type") != null) {
            list = new NodeRef(args.get("store_type"), args.get("store_id"), args.get("id"));
        } else {
            // Get the site
            SiteInfo site = siteService.getSite(args.get("site"));
            if (site == null) {
                throw new WebScriptException(Status.STATUS_NOT_FOUND, "Site not found with supplied name");
            }

            // Now find the data list container with in
            NodeRef container = nodeService.getChildByName(site.getNodeRef(), ContentModel.ASSOC_CONTAINS,
                    args.get("container"));
            if (container == null) {
                throw new WebScriptException(Status.STATUS_NOT_FOUND, "Container not found within site");
            }

            // Now get the data list itself
            list = nodeService.getChildByName(container, ContentModel.ASSOC_CONTAINS, args.get("list"));
        }
        if (list == null || !nodeService.exists(list)) {
            throw new WebScriptException(Status.STATUS_NOT_FOUND, "The Data List could not be found");
        }

        return list;
    }

    /**
     * We don't have a HTML version
     */
    @Override
    protected boolean allowHtmlFallback() {
        return false;
    }

    /**
     * Fetch the properties, in the requested order, from
     *  the data list definition
     */
    @Override
    protected List<Pair<QName, Boolean>> buildPropertiesForHeader(Object resource, String format,
            WebScriptRequest req) {
        NodeRef list = (NodeRef) resource;
        QName type = buildType(list);

        // Has the user given us rules for what to do
        //  with this type?
        List<QName> props;
        if (modelOrder.containsKey(type)) {
            props = modelOrder.get(type);
        } else {
            // We'll have to try to guess it for them
            // For now, just use DataList properties for the type
            TypeDefinition typeDef = dictionaryService.getType(type);
            Map<QName, PropertyDefinition> allProps = typeDef.getProperties();
            props = new ArrayList<QName>();

            for (QName prop : allProps.keySet()) {
                if (DL_NAMESPACE.equals(prop.getNamespaceURI())) {
                    props.add(prop);
                }
            }
        }

        // Everything is required
        List<Pair<QName, Boolean>> properties = new ArrayList<Pair<QName, Boolean>>();
        for (QName qname : props) {
            properties.add(new Pair<QName, Boolean>(qname, true));
        }
        return properties;
    }

    private QName buildType(NodeRef list) {
        String typeS = (String) nodeService.getProperty(list, DATA_LIST_ITEM_TYPE);
        return QName.createQName(typeS, namespaceService);
    }

    private List<NodeRef> getItems(NodeRef list) {
        Set<QName> typeSet = new HashSet<QName>(Arrays.asList(new QName[] { buildType(list) }));

        List<NodeRef> items = new ArrayList<NodeRef>();
        for (ChildAssociationRef ca : nodeService.getChildAssocs(list, typeSet)) {
            items.add(ca.getChildRef());
        }
        return items;
    }

    @Override
    protected void populateBody(Object resource, CSVPrinter csv, List<QName> properties) throws IOException {
        throw new WebScriptException(Status.STATUS_BAD_REQUEST, "CSV not currently supported");
    }

    @SuppressWarnings("deprecation")
    @Override
    protected void populateBody(Object resource, Workbook workbook, Sheet sheet, List<QName> properties)
            throws IOException {
        NodeRef list = (NodeRef) resource;
        List<NodeRef> items = getItems(list);

        // Our various formats
        DataFormat formatter = workbook.createDataFormat();
        CreationHelper createHelper = workbook.getCreationHelper();

        CellStyle styleInt = workbook.createCellStyle();
        styleInt.setDataFormat(formatter.getFormat("0"));
        CellStyle styleDate = workbook.createCellStyle();
        styleDate.setDataFormat(formatter.getFormat("yyyy-mm-dd"));
        CellStyle styleDouble = workbook.createCellStyle();
        styleDouble.setDataFormat(formatter.getFormat("General"));
        CellStyle styleNewLines = workbook.createCellStyle();
        styleNewLines.setWrapText(true);

        CellStyle hlink_style = workbook.createCellStyle();
        Font hlink_font = workbook.createFont();
        hlink_font.setUnderline(Font.U_SINGLE);
        hlink_font.setColor(IndexedColors.BLUE.getIndex());
        hlink_style.setFont(hlink_font);

        // Export the items
        int rowNum = 1, colNum = 0;
        for (NodeRef item : items) {
            Row r = sheet.createRow(rowNum);

            colNum = 0;
            for (QName prop : properties) {
                Cell c = r.createCell(colNum);

                Serializable val = nodeService.getProperty(item, prop);
                if (val == null) {
                    // Is it an association, or just missing?
                    List<AssociationRef> assocs = nodeService.getTargetAssocs(item, prop);
                    Set<QName> qnames = new HashSet<QName>(1, 1.0f);
                    qnames.add(prop);
                    List<ChildAssociationRef> childAssocs = nodeService.getChildAssocs(item, qnames);
                    if (assocs.size() > 0) {
                        StringBuffer text = new StringBuffer();
                        int lines = 1;

                        for (AssociationRef ref : assocs) {
                            NodeRef child = ref.getTargetRef();
                            QName type = nodeService.getType(child);
                            if (ContentModel.TYPE_PERSON.equals(type)) {
                                if (text.length() > 0) {
                                    text.append('\n');
                                    lines++;
                                }
                                text.append(nodeService.getProperty(child, ContentModel.PROP_FIRSTNAME));
                                text.append(" ");
                                text.append(nodeService.getProperty(child, ContentModel.PROP_LASTNAME));
                            } else if (ContentModel.TYPE_CONTENT.equals(type)) {
                                // TODO Link to the content
                                if (text.length() > 0) {
                                    text.append('\n');
                                    lines++;
                                }
                                text.append(nodeService.getProperty(child, ContentModel.PROP_NAME));
                                text.append(" (");
                                text.append(nodeService.getProperty(child, ContentModel.PROP_TITLE));
                                text.append(") ");
                                /*MessageFormat.format(CONTENT_DOWNLOAD_PROP_URL, new Object[] {
                                        child.getStoreRef().getProtocol(),
                                        child.getStoreRef().getIdentifier(),
                                        child.getId(),
                                        URLEncoder.encode((String)nodeService.getProperty(child, ContentModel.PROP_TITLE)),
                                        URLEncoder.encode(ContentModel.PROP_CONTENT.toString()) });
                                */
                                /*currently only one link per cell possible
                                 * Hyperlink link = createHelper.createHyperlink(Hyperlink.LINK_URL);
                                 *link.setAddress("http://poi.apache.org/");
                                 *c.setHyperlink(link);
                                 *c.setCellStyle(hlink_style);*/
                            } else if (ApplicationModel.TYPE_FILELINK.equals(type)) {
                                NodeRef linkRef = (NodeRef) nodeService.getProperty(child,
                                        ContentModel.PROP_LINK_DESTINATION);
                                if (linkRef != null) {
                                    if (text.length() > 0) {
                                        text.append('\n');
                                        lines++;
                                    }
                                    text.append("link to: ");
                                    try {
                                        text.append(nodeService.getProperty(linkRef, ContentModel.PROP_NAME));
                                        text.append(" (");
                                        text.append(nodeService.getProperty(linkRef, ContentModel.PROP_TITLE));
                                        text.append(") ");
                                    } catch (Exception e) {
                                        text.append(nodeService.getProperty(child, ContentModel.PROP_NAME));
                                        text.append(" (");
                                        text.append(nodeService.getProperty(child, ContentModel.PROP_TITLE));
                                        text.append(") ");

                                    }
                                }
                            } else {
                                System.err.println("TODO: handle " + type + " for " + child);
                            }
                        }

                        String v = text.toString();
                        c.setCellValue(v);
                        if (lines > 1) {
                            c.setCellStyle(styleNewLines);
                            r.setHeightInPoints(lines * sheet.getDefaultRowHeightInPoints());
                        }
                    } else if (childAssocs.size() > 0) {
                        StringBuffer text = new StringBuffer();
                        for (ChildAssociationRef childAssociationRef : childAssocs) {
                            NodeRef child = childAssociationRef.getChildRef();
                            QName type = nodeService.getType(child);
                            if (type.equals(ForumModel.TYPE_FORUM)) {
                                List<ChildAssociationRef> topics = nodeService.getChildAssocs(child);
                                if (topics.size() > 0) {
                                    ChildAssociationRef topicRef = topics.get(0);
                                    List<ChildAssociationRef> comments = nodeService
                                            .getChildAssocs(topicRef.getChildRef());
                                    for (ChildAssociationRef commentChildRef : comments) {
                                        NodeRef commentRef = commentChildRef.getChildRef();

                                        ContentData data = (ContentData) nodeService.getProperty(commentRef,
                                                ContentModel.PROP_CONTENT);
                                        TemplateContentData contentData = new TemplateContentData(data,
                                                ContentModel.PROP_CONTENT);

                                        String commentString = "";
                                        try {
                                            commentString = contentData.getContentAsText(commentRef, -1);
                                        } catch (Exception e) {
                                            logger.warn("failed to extract content for nodeRef " + commentRef, e);
                                        }

                                        String creator = (String) nodeService.getProperty(commentRef,
                                                ContentModel.PROP_CREATOR);
                                        NodeRef person = personService.getPerson(creator, false);
                                        if (person != null) {
                                            creator = nodeService.getProperty(person, ContentModel.PROP_FIRSTNAME)
                                                    + " "
                                                    + nodeService.getProperty(person, ContentModel.PROP_LASTNAME);
                                        }
                                        Date created = (Date) nodeService.getProperty(commentRef,
                                                ContentModel.PROP_CREATED);

                                        text.append(creator).append(" (")
                                                .append(DateFormatUtils.format(created, "yyyy-MM-dd"))
                                                .append("):\n ");
                                        text.append(commentString).append("\n");
                                    }
                                }
                            }
                        }
                        String v = text.toString();
                        c.setCellValue(v);
                        c.setCellStyle(styleNewLines);

                    } else {
                        // This property isn't set
                        c.setCellType(Cell.CELL_TYPE_BLANK);
                    }
                } else {
                    // Regular property, set
                    if (val instanceof String) {
                        c.setCellValue((String) val);
                        c.setCellStyle(styleNewLines);
                    } else if (val instanceof Date) {
                        c.setCellValue((Date) val);
                        c.setCellStyle(styleDate);
                    } else if (val instanceof Integer || val instanceof Long) {
                        double v = 0.0;
                        if (val instanceof Long)
                            v = (double) (Long) val;
                        if (val instanceof Integer)
                            v = (double) (Integer) val;
                        c.setCellValue(v);
                        c.setCellStyle(styleInt);
                    } else if (val instanceof Float || val instanceof Double) {
                        double v = 0.0;
                        if (val instanceof Float)
                            v = (double) (Float) val;
                        if (val instanceof Double)
                            v = (double) (Double) val;
                        c.setCellValue(v);
                        c.setCellStyle(styleDouble);
                    } else {
                        // TODO
                        System.err.println("TODO: handle " + val.getClass().getName() + " - " + val);
                    }
                }

                colNum++;
            }

            rowNum++;
        }

        // Sensible column widths please!
        colNum = 0;
        for (QName prop : properties) {
            try {
                sheet.autoSizeColumn(colNum);
            } catch (IllegalArgumentException e) {
                sheet.setColumnWidth(colNum, 40 * 256);
            }

            colNum++;
        }
    }

    /**
     * Inner class wrapping and providing access to a ContentData property.
     * Slightly modified copy of
     * {@link org.alfresco.repo.template.BaseContentNode.TemplateContentData}
     */
    private class TemplateContentData {

        private ContentData contentData;
        private QName property;

        /**
         * Constructor
         * 
         * @param contentData
         *            The ContentData object this object wraps
         * @param property
         *            The property the ContentData is attached too
         */
        public TemplateContentData(ContentData contentData, QName property) {
            this.contentData = contentData;
            this.property = property;
        }

        /**
         * @param nodeRef 
         * @param length 
         * @return the content stream to the specified maximum length in
         *         characters
         */
        public String getContentMaxLength(final NodeRef nodeRef, int length) {
            ContentReader reader = contentService.getReader(nodeRef, property);

            return (reader != null && reader.exists()) ? reader.getContentString(length) : "";
        }

        /**
         * @param nodeRef 
         * @param length
         *            Length of the character stream to return, or -1 for all
         * @return the binary content stream converted to text using any
         *         available transformer if fails to convert then null will be
         *         returned
         */
        public String getContentAsText(final NodeRef nodeRef, int length) {
            String result = null;

            String mimetype = contentData.getMimetype();
            if (MimetypeMap.MIMETYPE_TEXT_PLAIN.equals(mimetype)) {
                result = getContentMaxLength(nodeRef, length);
            } else {
                //try to use Apache Tika Framework
                if (!MimetypeMap.MIMETYPE_HTML.equals(mimetype)) {
                    result = getContentTextViaTika(nodeRef, length);
                }
                if (result == null || "".equalsIgnoreCase(result.trim())) {
                    result = getContentTextViaAlfresco(nodeRef, length);
                }
            }

            return result;
        }

        private String getContentTextViaAlfresco(final NodeRef nodeRef, int length) {
            String result = null;
            // get the content reader
            ContentReader reader = contentService.getReader(nodeRef, property);

            // get the writer and set it up for text convert
            ContentWriter writer = contentService.getWriter(null, ContentModel.PROP_CONTENT, true);
            writer.setMimetype("text/plain");
            writer.setEncoding(reader.getEncoding());

            // try and transform the content
            if (contentService.isTransformable(reader, writer)) {
                contentService.transform(reader, writer);

                ContentReader resultReader = writer.getReader();
                if (resultReader != null && reader.exists()) {
                    if (length != -1) {
                        result = getContentString(resultReader, length);
                    } else {
                        result = resultReader.getContentString();
                    }

                }
            }
            return result;
        }

        private String getContentTextViaTika(final NodeRef nodeRef, int length) {
            // get the content reader
            String result = null;
            ContentReader reader = contentService.getReader(nodeRef, property);

            ParsingReader parsingReader = null;
            try {
                parsingReader = new ParsingReader(reader.getContentInputStream());
                result = getContentString(parsingReader, length);

            } catch (ContentIOException e) {
                logger.error(e);
            } catch (IOException e) {
                logger.error(e);
            } finally {
                if (parsingReader != null) {
                    try {
                        parsingReader.close();
                    } catch (IOException e) {
                        logger.error(e);
                    }
                }
            }
            return result;
        }

        private String getContentString(ParsingReader parsingReader, int length) throws ContentIOException {
            if (length < 0 || length > Integer.MAX_VALUE) {
                throw new IllegalArgumentException("Character count must be positive and within range");
            }
            BufferedReader reader = null;
            try {
                reader = new BufferedReader(parsingReader);
                StringBuilder stringBuilder = new StringBuilder();
                String line = null;

                while ((line = reader.readLine()) != null && stringBuilder.length() < length) {
                    stringBuilder.append(line + "\n");
                }
                return stringBuilder.toString();
            } catch (IOException e) {
                logger.error(e);
                throw new ContentIOException("Failed to copy content to string: \n" + "   accessor: " + this + "\n"
                        + "   length: " + length, e);
            } finally {
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (Throwable e) {
                        logger.error(e);
                    }
                }
            }

        }

        private String getContentString(ContentReader contentReader, int length) throws ContentIOException {
            if (length < 0 || length > Integer.MAX_VALUE) {
                throw new IllegalArgumentException("Character count must be positive and within range");
            }
            BufferedReader reader = null;
            try {
                String encoding = contentReader.getEncoding();
                // create a reader from the input stream
                if (encoding == null) {
                    reader = new BufferedReader(new InputStreamReader(contentReader.getContentInputStream()));
                } else {
                    reader = new BufferedReader(
                            new InputStreamReader(contentReader.getContentInputStream(), encoding));
                }
                StringBuilder stringBuilder = new StringBuilder();
                String line = null;

                while ((line = reader.readLine()) != null && stringBuilder.length() < length) {
                    stringBuilder.append(line + "\n");
                }
                return stringBuilder.toString();
            } catch (IOException e) {
                throw new ContentIOException("Failed to copy content to string: \n" + "   accessor: " + this + "\n"
                        + "   length: " + length, e);
            } finally {
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (Throwable e) {
                        logger.error(e);
                    }
                }
            }
        }
    }

}