com.flexive.cmis.spi.FlexiveDocument.java Source code

Java tutorial

Introduction

Here is the source code for com.flexive.cmis.spi.FlexiveDocument.java

Source

/***************************************************************
 *  This file is part of the [fleXive](R) framework.
 *
 *  Copyright (c) 1999-2014
 *  UCS - unique computing solutions gmbh (http://www.ucs.at)
 *  All rights reserved
 *
 *  The [fleXive](R) project is free software; you can redistribute
 *  it and/or modify it under the terms of the GNU Lesser General Public
 *  License version 2.1 or higher as published by the Free Software Foundation.
 *
 *  The GNU Lesser General Public License can be found at
 *  http://www.gnu.org/licenses/lgpl.html.
 *  A copy is found in the textfile LGPL.txt and important notices to the
 *  license from the author are found in LICENSE.txt distributed with
 *  these libraries.
 *
 *  This library 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 General Public License for more details.
 *
 *  For further information about UCS - unique computing solutions gmbh,
 *  please see the company website: http://www.ucs.at
 *
 *  For further information about [fleXive](R), please see the
 *  project website: http://www.flexive.org
 *
 *
 *  This copyright notice MUST APPEAR in all copies of the file!
 ***************************************************************/
package com.flexive.cmis.spi;

import com.flexive.shared.EJBLookup;
import com.flexive.shared.FxSharedUtils;
import com.flexive.shared.content.FxContent;
import com.flexive.shared.content.FxContentVersionInfo;
import com.flexive.shared.content.FxPK;
import com.flexive.shared.exceptions.FxApplicationException;
import com.flexive.shared.exceptions.FxStreamException;
import com.flexive.shared.security.LifeCycleInfo;
import com.flexive.shared.structure.FxDataType;
import com.flexive.shared.structure.FxPropertyAssignment;
import com.flexive.shared.tree.FxTreeMode;
import com.flexive.shared.tree.FxTreeNode;
import com.flexive.shared.tree.FxTreeNodeEdit;
import com.flexive.shared.tree.FxTreeRemoveOp;
import com.flexive.shared.value.BinaryDescriptor;
import com.flexive.shared.value.FxBinary;
import com.flexive.shared.value.FxString;
import com.flexive.shared.value.FxValue;
import com.google.common.collect.Lists;
import org.apache.chemistry.*;
import org.apache.chemistry.impl.simple.SimpleContentStream;
import org.apache.commons.lang.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import static com.flexive.shared.EJBLookup.getContentEngine;
import static com.flexive.shared.EJBLookup.getTreeEngine;
import static com.google.common.collect.Lists.newArrayList;

/**
 * A non-folder FxContent instance.
 *
 * @author Daniel Lichtenberger (daniel.lichtenberger@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
 * @version $Rev$
 */
public class FlexiveDocument extends FlexiveObjectEntry implements Document {
    private final FxPK pk; // only setting PK leads to lazy loading of the content
    private final String treePathName;
    private final long folderParentId;

    private String newTreeName;

    private FxContent _cachedContent;
    private FxString _cachedCaption;
    private long _cachedTypeId = -1;
    private FxTreeNode _cachedFolderParent; // the parent if the document was loaded from a folder
    private LifeCycleInfo _cachedLifeCycleInfo;
    private FxTreeNode _cachedFolderNode;

    FlexiveDocument(FlexiveConnection.Context context, FxContent content) {
        super(context);
        this._cachedContent = content;
        this.pk = content.getPk();
        this.folderParentId = -1;
        this.treePathName = null;
    }

    FlexiveDocument(FlexiveConnection.Context context, FxPK pk) {
        this(context, pk, -1, null, null);
    }

    FlexiveDocument(FlexiveConnection.Context context, FxTreeNode node, FxTreeNode folderParent) {
        this(context, node.getReference(), folderParent.getId(), folderParent, node.getLabel().toString());
        applyNodeInfo(node);
    }

    FlexiveDocument(FlexiveConnection.Context context, FxTreeNode node, long folderParentId) {
        this(context, node.getReference(), folderParentId, null, node.getLabel().toString());
        applyNodeInfo(node);
    }

    FlexiveDocument(FlexiveConnection.Context context, FxPK pk, long folderParentId, FxTreeNode folderParent,
            String treePathName) {
        super(context);
        this.pk = pk;
        this._cachedFolderParent = folderParent;
        this.folderParentId = folderParent == null ? folderParentId : folderParent.getId();
        this.treePathName = treePathName;
    }

    protected final void applyNodeInfo(FxTreeNode node) {
        this._cachedCaption = node.getLabel();
        this._cachedTypeId = node.getReferenceTypeId();
        this._cachedLifeCycleInfo = node.getReferenceLifeCycleInfo();
        this._cachedFolderNode = node;
    }

    @Override
    protected FxContent getContent() {
        if (_cachedContent == null) {
            try {
                _cachedContent = EJBLookup.getContentEngine().load(pk);
            } catch (FxApplicationException e) {
                throw e.asRuntimeException();
            }
        }
        return _cachedContent;
    }

    @Override
    public String getId() {
        return getPK().toString();
    }

    @Override
    protected long getFxTypeId() {
        if (_cachedTypeId == -1) {
            _cachedTypeId = getContent().getTypeId();
        }
        return _cachedTypeId;
    }

    @Override
    protected LifeCycleInfo getLifeCycleInfo() {
        if (_cachedLifeCycleInfo != null) {
            return _cachedLifeCycleInfo;
        } else {
            return super.getLifeCycleInfo();
        }
    }

    protected FxPK getPK() {
        return pk;
    }

    protected FxTreeNode getFolderParentNode() {
        if (_cachedFolderParent == null && folderParentId != -1) {
            try {
                _cachedFolderParent = getTreeEngine().getNode(FxTreeMode.Edit, folderParentId);
            } catch (FxApplicationException e) {
                throw e.asRuntimeException();
            }
        }
        return _cachedFolderParent;
    }

    @Override
    protected void addCustomProperties(Map<String, Property> properties) {
        // add PK
        addVirtualProperty(properties, VirtualProperties.OBJECT_ID, getPK().toString());
        // add direct parent folder if the content was loaded from a folder
        if (folderParentId != -1) {
            addVirtualProperty(properties, VirtualProperties.PARENT_ID, String.valueOf(folderParentId));
        }
        // set content name (= caption)
        addVirtualProperty(properties, VirtualProperties.NAME, getName());
        // add binary stream, if available
        final FxPropertyAssignment binaryAssignment = SPIUtils.getContentStreamAssignment(getFxType());
        if (binaryAssignment != null && getContent().containsValue(binaryAssignment.getXPath())) {
            if (binaryAssignment.getProperty().getDataType() == FxDataType.Binary) {
                final BinaryDescriptor binary = ((FxBinary) getContent().getValue(binaryAssignment.getXPath()))
                        .getBestTranslation();
                //addVirtualProperty(properties, VirtualProperties.CONTENT_STREAM_FILENAME, binary.getName());
                // TODO: the AtomPub provider currently assumes it's an Integer, but should be a Long really
                addVirtualProperty(properties, VirtualProperties.CONTENT_STREAM_LENGTH, (int) binary.getSize());
                addVirtualProperty(properties, VirtualProperties.CONTENT_STREAM_MIME_TYPE, binary.getMimeType());
                // TODO: currently, the URI does not seem to be necessary, at least for the AtomPub binding, since that
                // will be handled by Abdera
                //addVirtualProperty(properties, VirtualProperties.CONTENT_STREAM_URI, context.getConfig().getContentStreamURI() + binary.getId());
            } else {
                final String value = getContent().getValue(binaryAssignment.getXPath()).getBestTranslation()
                        .toString();
                addVirtualProperty(properties, VirtualProperties.CONTENT_STREAM_LENGTH, value.getBytes().length);
                // TODO: mime type?
                addVirtualProperty(properties, VirtualProperties.CONTENT_STREAM_MIME_TYPE, "unknown/unknown");
            }
        }
    }

    public Document checkOut() {
        return new FlexiveDocument(context, context.getPwcManager().checkout(getContent()));
    }

    public void cancelCheckOut() {
        context.getPwcManager().cancelCheckout(getPK());
    }

    public Document checkIn(boolean major, String comment) {
        return new FlexiveDocument(context, context.getPwcManager().checkin(getContent()));
    }

    public Document getLatestVersion(boolean major) {
        // TODO: support major version flag
        try {
            final FxContentVersionInfo info = getContentEngine().getContentVersionInfo(getPK());
            assert info.getVersions().length > 0 : "Content must have at least one version";
            final Integer versionId = info.getVersions()[info.getVersions().length - 1];
            return (versionId == getPK().getVersion()) ? this
                    : new FlexiveDocument(context, new FxPK(getPK().getId(), versionId));
        } catch (FxApplicationException e) {
            throw e.asRuntimeException();
        }
    }

    public Collection<Document> getAllVersions() {
        final List<Document> result = newArrayList();
        try {
            final FxContentVersionInfo info = getContentEngine().getContentVersionInfo(getPK());
            for (int version : info.getVersions()) {
                if (version == getPK().getVersion()) {
                    result.add(this);
                } else {
                    result.add(new FlexiveDocument(context,
                            getContentEngine().load(new FxPK(getPK().getId(), version))));
                }
            }
            return result;
        } catch (FxApplicationException e) {
            throw e.asRuntimeException();
        }
    }

    public void deleteAllVersions() {
        try {
            getContentEngine().remove(getPK());
        } catch (FxApplicationException e) {
            throw e.asRuntimeException();
        }
    }

    public InputStream getStream() throws IOException {
        final ContentStream stream = getContentStream();
        return stream != null ? stream.getStream() : null;
    }

    public ContentStream getContentStream() throws IOException {
        final FxPropertyAssignment assignment = SPIUtils.getContentStreamAssignment(getFxType());
        if (assignment == null || !getContent().containsValue(assignment.getXPath())) {
            return null;
        }
        if (assignment.getProperty().getDataType() == FxDataType.Binary) {
            return new FlexiveContentStream((FxBinary) getContent().getValue(assignment.getXPath()));
        } else {
            final String streamOption = assignment.getOption(SPIUtils.OPTION_CONTENTSTREAM).getValue();
            return new SimpleContentStream(
                    getContent().getValue(assignment.getXPath()).getBestTranslation().toString().getBytes("UTF-8"),
                    // use MIME type set in stream value if available, otherwise default to text/plain
                    StringUtils.countMatches(streamOption, "/") == 1 ? streamOption : "text/plain", "content");
        }
    }

    public void setContentStream(ContentStream contentStream) throws IOException {
        final FxPropertyAssignment assignment = SPIUtils.getContentStreamAssignment(getFxType());
        if (assignment == null) {
            throw new IllegalArgumentException(
                    "Content of type " + getFxType().getName() + " does not have a suitable binary property.");
        }
        try {
            if (contentStream == null) {
                getContent().remove(assignment.getXPath());
            } else {
                if (assignment.getProperty().getDataType() == FxDataType.Binary) {
                    getContent().setValue(assignment.getXPath(), new BinaryDescriptor(contentStream.getFileName(),
                            contentStream.getLength(), contentStream.getMimeType(), contentStream.getStream()));
                } else {
                    final String content = FxSharedUtils.loadFromInputStream(contentStream.getStream());
                    final FxValue value = assignment.getEmptyValue();
                    value.setValue(value.fromString(content));
                    getContent().setValue(assignment.getXPath(), value);
                }
            }
        } catch (FxStreamException e) {
            throw e.asRuntimeException();
        }
    }

    public void move(Folder targetFolder, Folder sourceFolder) {
        if (sourceFolder == null && getParents().size() > 1) {
            throw new IllegalArgumentException(
                    "Document " + getId() + " is multi-filed, but no source folder was specified.");
        }
        final Folder source = sourceFolder != null ? sourceFolder : getParent();
        try {
            // get our node ID
            final FxTreeNode node = getTreeEngine().findChild(FxTreeMode.Edit, SPIUtils.getNodeId(source.getId()),
                    getPK());

            // move to new location
            getTreeEngine().move(FxTreeMode.Edit, node.getId(), SPIUtils.getNodeId(targetFolder.getId()),
                    Integer.MAX_VALUE);
        } catch (FxApplicationException e) {
            throw e.asRuntimeException();
        }
    }

    public void delete() {
        try {
            getContentEngine().removeVersion(getPK());
        } catch (FxApplicationException e) {
            throw e.asRuntimeException();
        }
    }

    public void unfile() {
        try {
            final List<FxTreeNode> nodes = getTreeEngine().getNodesWithReference(FxTreeMode.Edit, getPK().getId());
            for (FxTreeNode node : nodes) {
                // remove node with our document reference. Since there could be children that CMIS cannot see,
                // unfile the children as well.
                getTreeEngine().remove(node, FxTreeRemoveOp.Unfile, true);
            }
        } catch (FxApplicationException e) {
            throw e.asRuntimeException();
        }
    }

    public Folder getParent() {
        final List<FxTreeNode> nodes = getTreeNodes();
        if (nodes.size() > 1) {
            throw new IllegalArgumentException("Document " + getPK() + " is multi-filed.");
        }
        return nodes.isEmpty() ? null : new FlexiveFolder(context, nodes.get(0).getParentNodeId());
    }

    public Collection<Folder> getParents() {
        final List<FxTreeNode> nodes = getTreeNodes();
        final List<Folder> result = Lists.newArrayListWithCapacity(nodes.size());
        for (FxTreeNode node : nodes) {
            if (node.getParentNodeId() > 0) {
                result.add(new FlexiveFolder(context, node.getParentNodeId()));
            }
        }
        return result;
    }

    public List<Relationship> getRelationships(RelationshipDirection direction, String typeId,
            boolean includeSubRelationshipTypes) {
        throw new UnsupportedOperationException();
    }

    public void applyPolicy(Policy policy) {
        throw new UnsupportedOperationException();
    }

    public void removePolicy(Policy policy) {
        throw new UnsupportedOperationException();
    }

    public Collection<Policy> getPolicies() {
        throw new UnsupportedOperationException();
    }

    public Type getType() {
        return new FlexiveType(getFxType());
    }

    @Override
    public String getBaseTypeId() {
        return BaseType.DOCUMENT.getId();
    }

    public Property getProperty(String name) {
        return getProperties().get(name);
    }

    public void save() {
        try {
            // TODO: sync folders
            getContent().save();

            // sync folder name
            if (newTreeName != null && getFolderNode() != null) {
                final FxTreeNodeEdit editNode = getFolderNode().asEditable();
                editNode.getLabel().setValue(newTreeName);
                editNode.setName(newTreeName);
                getTreeEngine().save(editNode);
            }
        } catch (FxApplicationException e) {
            throw e.asRuntimeException();
        }
    }

    @Override
    public String getName() {
        if (treePathName != null) {
            // loaded from a tree node, use the tree node path
            return treePathName;
        }
        if (_cachedCaption != null) {
            // avoid content load if caption was provided from the caller
            return _cachedCaption.getBestTranslation();
        } else if (getContent().hasCaption()) {
            return getContent().getCaption().getBestTranslation();
        } else {
            return getPK().toString();
        }
    }

    @Override
    public void setName(String name) {
        if (getFolderParentNode() != null) {
            checkUniqueName(getNodeWithChildren(getFolderParentNode()), name);
        }
        final String captionXPath = getCaptionXPath();
        if (captionXPath != null) {
            getContent().setValue(captionXPath, name);
            _cachedCaption = null;
        } else {
            throw new IllegalArgumentException("No caption property available for type: " + getTypeId());
        }
        this.newTreeName = name;
    }

    public ContentStream getContentStream(String contentStreamId) throws IOException {
        // TODO: support contentStreamId
        return getContentStream();
    }

    /**
     * Return an independent copy of this document.
     *
     * @return  an independent copy of this document.
     *
     * // TODO remove
     */
    public FlexiveObjectEntry copy() {
        return new FlexiveNewDocument(context, getContent().copyAsNewInstance(), null);
    }

    public Document copy(Folder folder) throws NameConstraintViolationException {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public String getPathSegment() {
        return getName();
    }

    private List<FxTreeNode> getTreeNodes() {
        final List<FxTreeNode> nodes;
        try {
            nodes = getTreeEngine().getNodesWithReference(FxTreeMode.Edit, getContent().getId());
        } catch (FxApplicationException e) {
            throw e.asRuntimeException();
        }
        return nodes;
    }

    /**
     * @return  the tree node if this content was loaded from a folder, or null otherwise
     */
    private FxTreeNode getFolderNode() {
        if (_cachedFolderNode == null && folderParentId != -1) {
            try {
                _cachedFolderNode = getTreeEngine().findChild(FxTreeMode.Edit, folderParentId, getPK());
            } catch (FxApplicationException e) {
                throw e.asRuntimeException();
            }
        }
        return _cachedFolderNode;
    }

}