/*
* (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others.
*
* 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.
*
* Contributors:
* Bogdan Stefanescu
* Florent Guillaume
*/
package org.nuxeo.ecm.core.api;
import java.io.Serializable;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import org.nuxeo.common.collections.ScopeType;
import org.nuxeo.common.collections.ScopedMap;
import org.nuxeo.common.utils.Path;
import org.nuxeo.ecm.core.api.model.DocumentPart;
import org.nuxeo.ecm.core.api.model.Property;
import org.nuxeo.ecm.core.api.model.PropertyVisitor;
import org.nuxeo.ecm.core.api.model.resolver.PropertyObjectResolver;
import org.nuxeo.ecm.core.api.security.ACP;
import org.nuxeo.ecm.core.schema.DocumentType;
import org.nuxeo.ecm.core.schema.Prefetch;
/**
* The document model is a serializable representation of a core document.
* <p>
* The document model is made from several data models, each data model is bound to a schema. All the information about
* a document (like security) is expressed using schemas (and implicitly data models).
* <p>
* Data models are lazily loaded as they are needed. At document model creation only data models corresponding to the
* default schemas are loaded. The default schemas are configured in the type manager through extension points.
*
* @see CoreSession
*/
public interface DocumentModel extends Serializable {
int REFRESH_STATE = 1; // "small" state (life cycle, lock, versioning)
int REFRESH_PREFETCH = 4;
int REFRESH_ACP_IF_LOADED = 8; // refresh now only if already loaded
int REFRESH_ACP_LAZY = 16; // refresh later in lazy mode
int REFRESH_ACP = 32; // refresh now
int REFRESH_CONTENT_IF_LOADED = 64; // refresh now only if already loaded
int REFRESH_CONTENT_LAZY = 128; // refresh later in lazy mode
int REFRESH_CONTENT = 256; // refresh now
int REFRESH_IF_LOADED = REFRESH_STATE | REFRESH_PREFETCH | REFRESH_ACP_IF_LOADED | REFRESH_CONTENT_IF_LOADED;
int REFRESH_LAZY = REFRESH_STATE | REFRESH_PREFETCH | REFRESH_ACP_LAZY | REFRESH_CONTENT_LAZY;
int REFRESH_ALL = REFRESH_STATE | REFRESH_PREFETCH | REFRESH_ACP | REFRESH_CONTENT;
int REFRESH_DEFAULT = REFRESH_STATE | REFRESH_PREFETCH | REFRESH_ACP_IF_LOADED | REFRESH_CONTENT_LAZY;
/**
* Gets the document type object.
*
* @return the document type object
*/
DocumentType getDocumentType();
/**
* Retrieves the session id corresponding to this object.
* <p>
* This method should rarely be used, use {@link #getCoreSession} directly instead.
* <p>
* Using the session id you can retrieve the core session that created the object.
* <p>
* Document models created by the user on the client side are not bound to any session. They are simple DTO used to
* transport data.
*
* @return the session id the session ID for server side created doc models or null for client side models (used for
* data transportation)
*/
String getSessionId();
/**
* Gets the core session to which this document is tied.
* <p>
* This may be null if the document has been detached from a session.
*
* @return the core session
* @since 5.2.GA
*/
CoreSession getCoreSession();
/**
* Detaches the documentImpl from its existing session, so that it can survive beyond the session's closing.
*
* @param loadAll if {@code true}, load all data and ACP from the session before detaching
* @since 5.6
*/
void detach(boolean loadAll);
/**
* Reattaches a document impl to an existing session.
*
* @param sid the session id
* @since 5.6
*/
void attach(String sid);
/**
* Gets a reference to the core document that can be used either remotely or locally (opens the core JVM).
*
* @return the document reference
*/
DocumentRef getRef();
/**
* Retrieves the parent reference of the current document.
*
* @return the parent reference or null if no parent
*/
DocumentRef getParentRef();
/**
* Gets the document UUID.
*
* @return the document UUID
*/
String getId();
/**
* Gets the document name.
*
* @return the document name
*/
String getName();
/**
* Gets the document's position in its containing folder (if ordered).
*
* @return the position, or {@code null} if the containing folder is not ordered
* @since 6.0
*/
Long getPos();
/**
* Get a text suitable to be shown in a UI for this document.
*
* @return the title or the internal name if no title could be found
*/
String getTitle();
/**
* Gets the document path as a string.
*
* @return the document path as string
*/
String getPathAsString();
/**
* Gets the document path.
*
* @return the document path as string
*/
Path getPath();
/**
* Gets the document type name.
*
* @return the document type name
*/
String getType();
/**
* Gets the schemas available on this document (from the type and the facets).
*
* @return the schemas
* @since 5.4.2
*/
String[] getSchemas();
/**
* Gets the schemas available on this document (from the type and the facets).
*
* @deprecated use {@link #getSchemas} instead, or call {@link #getDocumentType} and look up the type schemas
* @return the schemas
*/
@Deprecated
String[] getDeclaredSchemas();
/**
* Checks if the document has the given schema, either from its type or added on the instance through a facet.
*
* @param schema the schema name
* @return {@code true} if the document has the schema
*/
boolean hasSchema(String schema);
/**
* Gets the facets available on this document (from the type and the instance facets).
*
* @return the facets
* @since 5.4.2
*/
Set<String> getFacets();
/**
* Gets the facets available on this document (from the type and the instance facets).
*
* @deprecated use {@link #getFacets} instead, or call {@link #getDocumentType} and look up the type facets
* @return the facets
*/
@Deprecated
Set<String> getDeclaredFacets();
/**
* Checks if the document has a facet, either from its type or added on the instance.
*
* @param facet the facet name
* @return {@code true} if the document has the facet
*/
boolean hasFacet(String facet);
/**
* Adds a facet to the document instance.
* <p>
* Does nothing if the facet was already present on the document.
*
* @param facet the facet name
* @return {@code true} if the facet was added, or {@code false} if it is already present
* @throws IllegalArgumentException if the facet does not exist
* @since 5.4.2
*/
boolean addFacet(String facet);
/**
* Removes a facet from the document instance.
* <p>
* It's not possible to remove a facet coming from the document type.
*
* @param facet the facet name
* @return {@code true} if the facet was removed, or {@code false} if it isn't present or is present on the type or
* does not exit
* @since 5.4.2
*/
boolean removeFacet(String facet);
/**
* INTERNAL, not for public use.
* <p>
* Gets a list with the currently fetched data models.
*
* @return the data models that are already fetched as a collection
* @deprecated since 8.4, internal method
* @see #getSchemas
* @see #getProperties
* @see #getPropertyObject
* @see #getPropertyObjects
*/
@Deprecated
Collection<DataModel> getDataModelsCollection();
/**
* Gets the data models.
*
* @return the data models that are already fetched.
* @deprecated since 8.4, use direct {@link Property} getters instead
* @see #getSchemas
* @see #getProperties
* @see #getPropertyObject
* @see #getPropertyObjects
*/
@Deprecated
Map<String, DataModel> getDataModels();
/**
* Gets the data model corresponding to the given schema.
* <p>
* Null is returned if the document type has no such schema.
*
* @param schema the schema name
* @return the data model or null if no such schema is supported
* @deprecated since 8.4, use direct {@link Property} getters instead
* @see #getSchemas
* @see #getProperties
* @see #getPropertyObject
* @see #getPropertyObjects
*/
@Deprecated
DataModel getDataModel(String schema);
/**
* Sets path info.
* <p>
* path and ref attributes will be set according to info
*/
void setPathInfo(String parentPath, String name);
/**
* Tests if the document is locked.
* <p>
* Lock info is cached on the document for performance. Use {@link CoreSession#getLockInfo} to get the non-cached
* status.
*
* @return the lock key if the document is locked or null otherwise
*/
boolean isLocked();
/**
* Sets a lock on the document.
*
* @return the lock info that was set
* @throws LockException if the document is already locked
* @since 5.4.2
*/
Lock setLock() throws LockException;
/**
* Gets the lock info on the document.
* <p>
* Lock info is cached on the document for performance. Use {@link CoreSession#getLockInfo} to get the non-cached
* status.
*
* @return the lock info if the document is locked, or {@code null} otherwise
* @since 5.4.2
*/
Lock getLockInfo();
/**
* Removes the lock on the document.
* <p>
* The caller principal should be the same as the one who set the lock or to belongs to the administrator group,
* otherwise an exception will be throw.
* <p>
* If the document was not locked, does nothing.
* <p>
* Returns the previous lock info.
*
* @return the removed lock info, or {@code null} if there was no lock
* @throws LockException if the document is locked by someone else
* @since 5.4.2
*/
Lock removeLock() throws LockException;
/**
* Tests if the document is checked out.
* <p>
* A checked out document can be modified normally. A checked in document is identical to the last version that it
* created, and not modifiable.
* <p>
* Only applicable to documents that are live (not versions and not proxies).
*
* @return {@code true} if the document is checked out, {@code false} if it is checked in
* @since 5.4
*/
boolean isCheckedOut();
/**
* Checks out a document.
* <p>
* A checked out document can be modified normally.
* <p>
* Only applicable to documents that are live (not versions and not proxies).
*
* @since 5.4
*/
void checkOut();
/**
* Checks in a document and returns the created version.
* <p>
* A checked in document is identical to the last version that it created, and not modifiable.
* <p>
* Only applicable to documents that are live (not versions and not proxies).
*
* @param option whether to do create a new {@link VersioningOption#MINOR} or {@link VersioningOption#MAJOR} version
* during check in
* @param checkinComment the checkin comment
* @return the version just created
* @since 5.4
*/
DocumentRef checkIn(VersioningOption option, String checkinComment);
/**
* Returns the version label.
* <p>
* The label returned is computed by the VersioningService.
*
* @return the version label, or {@code null}
*/
String getVersionLabel();
/**
* Returns the checkin comment if the document model is a version.
*
* @return the checkin comment, or {@code null}
* @since 5.4
*/
String getCheckinComment();
/**
* Gets the version series id for this document.
* <p>
* All documents and versions derived by a check in or checkout from the same original document share the same
* version series id.
*
* @return the version series id
* @since 5.4
*/
String getVersionSeriesId();
/**
* Checks if a document is the latest version in the version series.
*
* @since 5.4
*/
boolean isLatestVersion();
/**
* Checks if a document is a major version.
*
* @since 5.4
*/
boolean isMajorVersion();
/**
* Checks if a document is the latest major version in the version series.
*
* @since 5.4
*/
boolean isLatestMajorVersion();
/**
* Checks if there is a checked out working copy for the version series of this document.
*
* @since 5.4
*/
boolean isVersionSeriesCheckedOut();
/**
* Gets the access control policy (ACP) for this document.
* <p>
* Returns null if no security was defined on this document.
* <p>
* The ACP can be used to introspect or to evaluate user privileges on this document.
* <p>
* This is a wrapper for {@link CoreSession#getACP(DocumentRef)} but it is recommended since it caches the ACP for
* later usage.
*
* @return the security data model or null if none
*/
ACP getACP();
/**
* Sets the ACP for this document model.
* <p>
* This is a wrapper for {@link CoreSession#setACP(DocumentRef, ACP, boolean)}
*
* @see {@link CoreSession#setACP(DocumentRef, ACP, boolean)}
* @param acp the ACP to set
* @param overwrite whether to overwrite the old ACP or not
*/
void setACP(ACP acp, boolean overwrite);
/**
* Gets a property from the given schema.
* <p>
* The data model owning the property will be fetched from the server if not already fetched.
*
* @param schemaName the schema name
* @param name the property name
* @return the property value or null if no such property exists
*/
Object getProperty(String schemaName, String name);
/**
* Gets a property object from the given schema.
*
* @param schema the schema name
* @param name the property name
* @return the property, or {@code null} if no such property exists
* @since 8.4
*/
Property getPropertyObject(String schema, String name);
/**
* Sets the property value from the given schema.
* <p>
* This operation will not fetch the data model if not already fetched
*
* @param schemaName the schema name
* @param name the property name
* @param value the property value
*/
void setProperty(String schemaName, String name, Object value);
/**
* Gets the values from the given data model as a map.
* <p>
* The operation will fetch the data model from the server if not already fetched.
*
* @param schemaName the data model schema name
* @return the values map
*/
Map<String, Object> getProperties(String schemaName);
/**
* Sets values for the given data model.
* <p>
* This will not fetch the data model if not already fetched.
*
* @param schemaName the schema name
* @param data the values to set
*/
void setProperties(String schemaName, Map<String, Object> data);
/**
* Checks if this document is a folder.
*
* @return true if the document is a folder, false otherwise
*/
boolean isFolder();
/**
* Checks if this document can have versions.
*
* @return true if the document can have versions, false otherwise
*/
boolean isVersionable();
/**
* Checks if this document can be downloaded.
*
* @return true if the document has downloadable content, false otherwise
*/
boolean isDownloadable();
/**
* Checks if this document is a version.
*
* @return true if the document is an older version of another document, false otherwise
*/
boolean isVersion();
/**
* Checks if this document is a proxy.
*
* @return true if the document is a proxy false otherwise
*/
boolean isProxy();
/**
* Checks if this document is immutable.
*
* @return {@code true} if the document is a version or a proxy to a version, {@code false} otherwise
* @since 1.6.1 (5.3.1)
*/
boolean isImmutable();
/**
* Checks if the document has actual data to write (dirty parts).
*
* @since 5.5
*/
boolean isDirty();
/**
* Method that implement the visitor pattern.
* <p>
* The visitor must return null to stop visiting children otherwise a context object that will be passed as the arg
* argument to children
*
* @param visitor the visitor to accept
* @param arg an argument passed to the visitor. This should be used by the visitor to carry on the visiting
* context.
* @since 5.5
*/
void accept(PropertyVisitor visitor, Object arg);
/**
* Adapts the document to the given interface.
* <p>
* Attention, the first computation will cache the adaptation result for later calls.
* </p>
*
* @param <T> the interface type to adapt to
* @param itf the interface class
* @return the adapted document
*/
<T> T getAdapter(Class<T> itf);
/**
* Adapts the document to the given interface.
*
* @param <T> the interface type to adapt to
* @param itf the interface class
* @param refreshCache : readapt and stores in cache if already exists.
* @return the adapted document
*/
<T> T getAdapter(Class<T> itf, boolean refreshCache);
/**
* Returns the life cycle of the document.
*
* @see org.nuxeo.ecm.core.lifecycle
* @return the life cycle as a string
*/
String getCurrentLifeCycleState();
/**
* Returns the life cycle policy of the document.
*
* @see org.nuxeo.ecm.core.lifecycle
* @return the life cycle policy
*/
String getLifeCyclePolicy();
/**
* Follows a given life cycle transition.
* <p>
* This will update the current life cycle of the document.
*
* @param transition the name of the transition to follow
* @return a boolean representing the status if the operation
*/
boolean followTransition(String transition);
/**
* Gets the allowed state transitions for this document.
*
* @return a collection of state transitions as string
*/
Collection<String> getAllowedStateTransitions();
/**
* Gets the context data associated to this document.
* <p>
* NOTE since 9.1 the {@link ScopedMap} return type is deprecated, use {@code java.util.Map<String, Serializable>}
* instead
*
* @return a map of context data
*/
ScopedMap getContextData();
/**
* Gets the context data associated to this document for given scope and given key.
*
* @deprecated since 9.1, scope is unused, use {@link #getContextData(String)} instead
*/
@Deprecated
Serializable getContextData(ScopeType scope, String key);
/**
* Adds mapping to the context data for given scope.
* <p>
* Context data is like a request map set on the document model to pass additional information to components
* interacting with the document model (events processing for instance).
*
* @deprecated since 9.1, scope is unused, use {@link #putContextData(String, Serializable)} instead
*/
@Deprecated
void putContextData(ScopeType scope, String key, Serializable value);
/**
* Gets the context data using the default scope.
*
* @param key the context data key
* @return the value
*/
Serializable getContextData(String key);
/**
* Sets a context data in the default scope.
*
* @param key the context data key
* @param value the value
*/
void putContextData(String key, Serializable value);
/**
* Copies the context data from given document to this document.
*/
void copyContextData(DocumentModel otherDocument);
/**
* Copies all the data from a source document.
*/
void copyContent(DocumentModel sourceDoc);
/**
* Returns the name of the repository in which the document is stored.
*
* @return the repository name as a string.
*/
String getRepositoryName();
/**
* Returns a cache key.
* <p>
* Cache key will be computed like this : <code>
* docUUID + "-" + sessionId + "-" + timestamp
* </code>
* <p>
* We will use the last modification time if present for the timestamp.
* <p>
* Since 5.6, the timestamp does not hold milliseconds anymore as some databases do not store them, which could
* interfere with cache key comparisons.
*
* @return the cache key as a string
*/
String getCacheKey();
/**
* Returns the source document identifier.
* <p>
* This is useful when not interested about the repository UUID itself. Technically, this is the current version
* UUID.
*
* @return the source id as a string.
*/
String getSourceId();
/**
* Checks if a property is prefetched.
*
* @param xpath the property xpath
* @return {@code true} if it is prefetched
* @since 5.5
*/
boolean isPrefetched(String xpath);
/**
* Checks if a property is prefetched.
*
* @param schemaName the schema name
* @param name the property name
* @return {@code true} if it is prefetched
* @since 5.5
*/
boolean isPrefetched(String schemaName, String name);
/**
* Used to set lifecycle state along with prefetching other properties.
*/
void prefetchCurrentLifecycleState(String lifecycle);
/**
* Used to set lifecycle policy along with prefetching other properties.
*/
void prefetchLifeCyclePolicy(String lifeCyclePolicy);
boolean isLifeCycleLoaded();
/**
* Gets system property of the specified type. This is not a lazy loaded property, thus the request is made directly
* to the server. This is needed as some critical system properties might be changed directly in the core.
*/
<T extends Serializable> T getSystemProp(String systemProperty, Class<T> type);
/**
* Get a document part given its schema name
*
* @param schema the schema
* @return the document aprt or null if none exists for that schema
* @deprecated since 8.4, use direct {@link Property} getters instead
* @see #getPropertyObject
* @see #getPropertyObjects
*/
@Deprecated
DocumentPart getPart(String schema);
/**
* Gets this document's parts.
*
* @deprecated since 8.4, use direct {@link Property} getters instead
* @see #getSchemas
* @see #getPropertyObject
* @see #getPropertyObjects
*/
@Deprecated
DocumentPart[] getParts();
/**
* Gets the {@link Property} objects for the given schema.
* <p>
* An empty list is returned if the document doesn't have the schema.
*
* @param schema the schema
* @return the properties
* @since 8.4
*/
Collection<Property> getPropertyObjects(String schema);
/**
* Gets a property given a xpath.
* <p>
* Note that what's called xpath in this context is not an actual XPath as specified by the w3c. Main differences
* are that in our xpath:
* <ul>
* <li>Indexes start at 0 instead of 1</li>
* <li>You can express {@code foo/bar[i]/baz} as {@code foo/i/baz}</li>
* </ul>
* The latter is possible because in Nuxeo lists of complex elements are homogenous, so the name of the second-level
* element is implied.
*/
Property getProperty(String xpath) throws PropertyException;
/**
* Gets a property value given a xpath.
* <p>
* Note that what's called xpath in this context is not an actual XPath as specified by the w3c. Main differences
* are that in our xpath:
* <ul>
* <li>Indexes start at 0 instead of 1</li>
* <li>You can express {@code foo/bar[i]/baz} as {@code foo/i/baz}</li>
* </ul>
* The latter is possible because in Nuxeo lists of complex elements are homogenous, so the name of the second-level
* element is implied.
*/
Serializable getPropertyValue(String xpath) throws PropertyException;
/**
* Sets a property value given a xpath.
*/
void setPropertyValue(String xpath, Serializable value) throws PropertyException;
/**
* Clears any prefetched or cached document data.
* <p>
* This will force the document to lazily update its data when required.
*/
void reset();
/**
* Refresh document data from server.
* <p>
* The data models will be removed and all prefetch and system data will be refreshed from the server
* <p>
* The refreshed data contains:
* <ul>
* <li>document life cycle
* <li>document lock state, acp if required
* <li>document prefetch map
* <li>acp if required - otherwise acp info will be cleared so that it will be refetched in lazy way
* <li>document parts if required - otherwise parts data will be removed to be refreshed lazy
* </ul>
* The refresh flags are:
* <ul>
* <li>{@link DocumentModel#REFRESH_STATE}
* <li>{@link DocumentModel#REFRESH_PREFETCH}
* <li>{@link DocumentModel#REFRESH_ACP_IF_LOADED}
* <li>{@link DocumentModel#REFRESH_ACP_LAZY}
* <li>{@link DocumentModel#REFRESH_ACP}
* <li>{@link DocumentModel#REFRESH_CONTENT_IF_LOADED}
* <li>{@link DocumentModel#REFRESH_CONTENT_LAZY}
* <li>{@link DocumentModel#REFRESH_CONTENT}
* <li>{@link DocumentModel#REFRESH_DEFAULT} same as REFRESH_STATE | REFRESH_DEFAULT | REFRESH_ACP_IF_LOADED |
* REFRESH_CONTENT_IF_LOADED
* <li>{@link DocumentModel#REFRESH_ALL} same as REFRESH_STATE | REFRESH_PREFTECH | REFRESH_ACP | REFRESH_CONTENT
* </ul>
* If XX_IF_LOADED is used then XX will be refreshed only if already loaded in the document - otherwise a lazy
* refresh will be done
*
* @param refreshFlags the refresh flags
* @param schemas the document parts (schemas) that should be refreshed now
*/
void refresh(int refreshFlags, String[] schemas);
/** Info fetched internally during a refresh. */
class DocumentModelRefresh {
public String lifeCycleState;
public String lifeCyclePolicy;
public boolean isCheckedOut;
public boolean isLatestVersion;
public boolean isMajorVersion;
public boolean isLatestMajorVersion;
public boolean isVersionSeriesCheckedOut;
public String versionSeriesId;
public String checkinComment;
public ACP acp;
public Prefetch prefetch;
public Set<String> instanceFacets;
public DocumentPart[] documentParts;
}
/**
* Same as {@code DocumentModel.refresh(REFRESH_DEFAULT)}.
*/
void refresh();
/**
* Clone operation. Must be made public instead of just protected as in Object.
*/
DocumentModel clone() throws CloneNotSupportedException;
/**
* Gets the current change token for this document.
* <p>
* The change token is an opaque string which is modified every time the document is changed.
* <p>
* Before saving a document through {@link CoreSession#saveDocument} it's possible to pass an expected change token
* in the document context data through {@code doc.putContextData(CoreSession.CHANGE_TOKEN, expectedChangeToken)}.
* If the change token does not match the stored one, it means that a concurrent update happened, and a
* {@link org.nuxeo.ecm.core.api.ConcurrentUpdateException ConcurrentUpdateException} will be thrown.
*
* @return the change token
* @since 5.5
* @see #putContextData
* @see CoreSession#CHANGE_TOKEN
* @see CoreSession#getChangeToken
*/
String getChangeToken();
/**
* Gets the fulltext extracted from the binary fields.
*
* @since 5.9.3
*/
Map<String, String> getBinaryFulltext();
/**
* @param xpath the property xpath
* @return A {@link PropertyObjectResolver} to manage the property reference to external entities, null if this
* property's type has no resolver.
* @since 7.1
*/
PropertyObjectResolver getObjectResolver(String xpath);
}