org.alfresco.repo.jscript.app.JSONConversionComponent.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.repo.jscript.app.JSONConversionComponent.java

Source

/*
 * #%L
 * Alfresco Repository
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * 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/>.
 * #L%
 */
package org.alfresco.repo.jscript.app;

import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.alfresco.model.ContentModel;
import org.alfresco.repo.jscript.ScriptNode;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.lock.LockService;
import org.alfresco.service.cmr.lock.LockStatus;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AccessPermission;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PublicServiceAccessService;
import org.alfresco.service.namespace.NamespaceException;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.ISO8601DateFormat;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONException;
import org.json.simple.JSONArray;
import org.json.simple.JSONAware;
import org.json.simple.JSONObject;
import org.springframework.extensions.surf.util.URLEncoder;

/**
 * JSON Conversion Component
 * 
 * @author Roy Wetherall
 * @author Kevin Roast
 */
public class JSONConversionComponent {
    /** Content download API URL template */
    private final static String CONTENT_DOWNLOAD_API_URL = "/slingshot/node/content/{0}/{1}/{2}/{3}";

    /** Logger */
    private static Log logger = LogFactory.getLog(JSONConversionComponent.class);

    /** Registered decorators */
    protected Map<QName, PropertyDecorator> propertyDecorators = new HashMap<QName, PropertyDecorator>(8);

    /** User permissions */
    protected String[] userPermissions;

    /** Thread local cache of namespace prefixes for long QName to short prefix name conversions */
    protected static ThreadLocal<Map<String, String>> namespacePrefixCache = new ThreadLocal<Map<String, String>>() {
        @Override
        protected Map<String, String> initialValue() {
            return new HashMap<String, String>(8);
        }
    };

    /** Services */
    protected NodeService nodeService;
    protected PublicServiceAccessService publicServiceAccessService;
    protected NamespaceService namespaceService;
    protected FileFolderService fileFolderService;
    protected LockService lockService;
    protected ContentService contentService;
    protected PermissionService permissionService;
    protected MimetypeService mimetypeService;

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

    /**
     * @param publicServiceAccessService    public service access service
     */
    public void setPublicServiceAccessService(PublicServiceAccessService publicServiceAccessService) {
        this.publicServiceAccessService = publicServiceAccessService;
    }

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

    /**
     * @param fileFolderService file folder service
     */
    public void setFileFolderService(FileFolderService fileFolderService) {
        this.fileFolderService = fileFolderService;
    }

    /**
     * @param lockService   lock service
     */
    public void setLockService(LockService lockService) {
        this.lockService = lockService;
    }

    /**
     * @param permissionService permission service
     */
    public void setPermissionService(PermissionService permissionService) {
        this.permissionService = permissionService;
    }

    /**
     * @param userPermissions   user permissions
     */
    public void setUserPermissions(String[] userPermissions) {
        this.userPermissions = userPermissions;
    }

    /**
     * @param contentService    content service
     */
    public void setContentService(ContentService contentService) {
        this.contentService = contentService;
    }

    /**
     * @param mimetypeService    mimetype service
     */
    public void setMimetypeService(MimetypeService mimetypeService) {
        this.mimetypeService = mimetypeService;
    }

    /**
     * Register a property decorator;
     * 
     * @param propertyDecorator PropertyDecorator
     */
    public void registerPropertyDecorator(PropertyDecorator propertyDecorator) {
        for (QName propertyName : propertyDecorator.getPropertyNames()) {
            propertyDecorators.put(propertyName, propertyDecorator);
        }
    }

    /**
     * Convert a node reference to a JSON string.  Selects the correct converter based on selection
     * implementation.
     */
    @SuppressWarnings("unchecked")
    public String toJSON(final NodeRef nodeRef, final boolean useShortQNames) {
        return toJSONObject(nodeRef, useShortQNames).toJSONString();
    }

    /**
     * Convert a node reference to a JSON object.  Selects the correct converter based on selection
     * implementation.
     */
    @SuppressWarnings("unchecked")
    public JSONObject toJSONObject(final NodeRef nodeRef, final boolean useShortQNames) {
        final JSONObject json = new JSONObject();

        if (this.nodeService.exists(nodeRef)) {
            if (publicServiceAccessService.hasAccess(ServiceRegistry.NODE_SERVICE.getLocalName(), "getProperties",
                    nodeRef) == AccessStatus.ALLOWED) {
                // init namespace prefix cache
                namespacePrefixCache.get().clear();

                // Get node info
                FileInfo nodeInfo = this.fileFolderService.getFileInfo(nodeRef);

                // Set root values
                setRootValues(nodeInfo, json, useShortQNames);

                // add permissions
                json.put("permissions", permissionsToJSON(nodeRef));

                // add properties
                json.put("properties", propertiesToJSON(nodeRef, nodeInfo.getProperties(), useShortQNames));

                // add aspects
                json.put("aspects", apsectsToJSON(nodeRef, useShortQNames));
            }
        }

        return json;
    }

    /**
     * 
     * @param nodeInfo FileInfo
     * @param rootJSONObject JSONObject
     * @param useShortQNames boolean
     */
    @SuppressWarnings("unchecked")
    protected void setRootValues(final FileInfo nodeInfo, final JSONObject rootJSONObject,
            final boolean useShortQNames) {
        final NodeRef nodeRef = nodeInfo.getNodeRef();

        rootJSONObject.put("nodeRef", nodeInfo.getNodeRef().toString());
        rootJSONObject.put("type", nameToString(nodeInfo.getType(), useShortQNames));
        rootJSONObject.put("isContainer", nodeInfo.isFolder()); //node.getIsContainer() || node.getIsLinkToContainer());
        rootJSONObject.put("isLocked", isLocked(nodeInfo.getNodeRef()));

        rootJSONObject.put("isLink", nodeInfo.isLink());
        if (nodeInfo.isLink()) {
            NodeRef targetNodeRef = nodeInfo.getLinkNodeRef();
            if (targetNodeRef != null) {
                rootJSONObject.put("linkedNode", toJSONObject(targetNodeRef, useShortQNames));
            }
        }

        // TODO should this be moved to the property output since we may have more than one content property
        //      or a non-standard content property 

        if (nodeInfo.isFolder() == false) {
            final ContentData cdata = nodeInfo.getContentData();
            if (cdata != null) {
                String contentURL = MessageFormat.format(CONTENT_DOWNLOAD_API_URL,
                        new Object[] { nodeRef.getStoreRef().getProtocol(), nodeRef.getStoreRef().getIdentifier(),
                                nodeRef.getId(), URLEncoder.encode(nodeInfo.getName()) });

                rootJSONObject.put("contentURL", contentURL);
                rootJSONObject.put("mimetype", cdata.getMimetype());
                Map<String, String> mimetypeDescriptions;
                mimetypeDescriptions = mimetypeService.getDisplaysByMimetype();

                if (mimetypeDescriptions.containsKey(cdata.getMimetype())) {
                    rootJSONObject.put("mimetypeDisplayName", mimetypeDescriptions.get(cdata.getMimetype()));
                }
                rootJSONObject.put("encoding", cdata.getEncoding());
                rootJSONObject.put("size", cdata.getSize());
            }
        }
    }

    /**
     * Handles the work of converting node permissions to JSON.
     *  
     * @param nodeRef NodeRef
     * @return JSONObject
     */
    @SuppressWarnings("unchecked")
    protected JSONObject permissionsToJSON(final NodeRef nodeRef) {
        final JSONObject permissionsJSON = new JSONObject();
        if (AccessStatus.ALLOWED
                .equals(permissionService.hasPermission(nodeRef, PermissionService.READ_PERMISSIONS)) == true) {
            permissionsJSON.put("inherited", permissionService.getInheritParentPermissions(nodeRef));
            permissionsJSON.put("roles", allSetPermissionsToJSON(nodeRef));
            permissionsJSON.put("user", userPermissionsToJSON(nodeRef));
        }
        return permissionsJSON;
    }

    /**
     * Handles the work of converting user permissions to JSON.
     * 
     * @param nodeRef NodeRef
     * @return JSONObject
     */
    @SuppressWarnings("unchecked")
    protected JSONObject userPermissionsToJSON(final NodeRef nodeRef) {
        final JSONObject userPermissionJSON = new JSONObject();
        for (String userPermission : this.userPermissions) {
            boolean hasPermission = AccessStatus.ALLOWED
                    .equals(permissionService.hasPermission(nodeRef, userPermission));
            userPermissionJSON.put(userPermission, hasPermission);
        }
        return userPermissionJSON;
    }

    /**
     * Handles the work of converting values to JSON.
     * 
     * @param nodeRef NodeRef
     * @param propertyName QName
     * @param key String
     * @param value Serializable
     * @return the JSON value
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    protected Object propertyToJSON(final NodeRef nodeRef, final QName propertyName, final String key,
            final Serializable value) {
        if (value != null) {
            // Has a decorator has been registered for this property?
            if (propertyDecorators.containsKey(propertyName)) {
                JSONAware jsonAware = propertyDecorators.get(propertyName).decorate(propertyName, nodeRef, value);
                if (jsonAware != null) {
                    return jsonAware;
                }
            } else {
                // Built-in data type processing
                if (value instanceof Date) {
                    JSONObject dateObj = new JSONObject();
                    dateObj.put("value", JSONObject.escape(value.toString()));
                    dateObj.put("iso8601", JSONObject.escape(ISO8601DateFormat.format((Date) value)));
                    return dateObj;
                } else if (value instanceof List) {
                    // Convert the List to a JSON list by recursively calling propertyToJSON
                    List<Object> jsonList = new ArrayList<Object>(((List<Serializable>) value).size());
                    for (Serializable listItem : (List<Serializable>) value) {
                        jsonList.add(propertyToJSON(nodeRef, propertyName, key, listItem));
                    }
                    return jsonList;
                } else if (value instanceof Double) {
                    return (Double.isInfinite((Double) value) || Double.isNaN((Double) value) ? null
                            : value.toString());
                } else if (value instanceof Float) {
                    return (Float.isInfinite((Float) value) || Float.isNaN((Float) value) ? null
                            : value.toString());
                } else {
                    return value.toString();
                }
            }
        }
        return null;
    }

    /**
     * 
     * @param nodeRef NodeRef
     * @param useShortQNames boolean
     * @return JSONObject
     */
    @SuppressWarnings("unchecked")
    protected JSONObject propertiesToJSON(NodeRef nodeRef, Map<QName, Serializable> properties,
            boolean useShortQNames) {
        JSONObject propertiesJSON = new JSONObject();

        for (QName propertyName : properties.keySet()) {
            try {
                String key = nameToString(propertyName, useShortQNames);
                Serializable value = properties.get(propertyName);

                propertiesJSON.put(key, propertyToJSON(nodeRef, propertyName, key, value));
            } catch (NamespaceException ne) {
                // ignore properties that do not have a registered namespace
                if (logger.isDebugEnabled())
                    logger.debug("Ignoring property '" + propertyName + "' as its namespace is not registered");
            }
        }

        return propertiesJSON;
    }

    /**
     * Handles the work of converting aspects to JSON.
     * 
     * @param nodeRef NodeRef
     * @param useShortQNames boolean
     * @return JSONArray
     */
    @SuppressWarnings("unchecked")
    protected JSONArray apsectsToJSON(NodeRef nodeRef, boolean useShortQNames) {
        JSONArray aspectsJSON = new JSONArray();

        Set<QName> aspects = this.nodeService.getAspects(nodeRef);
        for (QName aspect : aspects) {
            aspectsJSON.add(nameToString(aspect, useShortQNames));
        }

        return aspectsJSON;
    }

    /**
     * Handles the work of converting all set permissions to JSON.
     * 
     * @param nodeRef NodeRef
     * @return JSONArray
     */
    @SuppressWarnings("unchecked")
    protected JSONArray allSetPermissionsToJSON(NodeRef nodeRef) {
        Set<AccessPermission> acls = permissionService.getAllSetPermissions(nodeRef);
        JSONArray permissions = new JSONArray();

        List<AccessPermission> ordered = ScriptNode.getSortedACLs(acls);

        for (AccessPermission permission : ordered) {
            StringBuilder buf = new StringBuilder(64);
            buf.append(permission.getAccessStatus()).append(';').append(permission.getAuthority()).append(';')
                    .append(permission.getPermission()).append(';')
                    .append(permission.isSetDirectly() ? "DIRECT" : "INHERITED");
            permissions.add(buf.toString());
        }
        return permissions;
    }

    /**
     * Convert a qname to a string - either full or short prefixed named.
     * 
     * @param qname QName
     * @param isShortName boolean
     * @return qname string.
     */
    private String nameToString(final QName qname, final boolean isShortName) {
        String result;
        if (isShortName) {
            final Map<String, String> cache = namespacePrefixCache.get();
            String prefix = cache.get(qname.getNamespaceURI());
            if (prefix == null) {
                // first request for this namespace prefix, get and cache result
                Collection<String> prefixes = this.namespaceService.getPrefixes(qname.getNamespaceURI());
                prefix = prefixes.size() != 0 ? prefixes.iterator().next() : "";
                cache.put(qname.getNamespaceURI(), prefix);
            }
            result = prefix + QName.NAMESPACE_PREFIX + qname.getLocalName();
        } else {
            result = qname.toString();
        }
        return result;
    }

    /**
     * Return true if the node is locked.
     * 
     * @param nodeRef NodeRef
     * @return boolean
     */
    private boolean isLocked(final NodeRef nodeRef) {
        boolean locked = false;

        if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE) == true) {
            locked = lockService.isLocked(nodeRef);
        }

        return locked;
    }
}