0001: /*
0002: * Copyright 2004 Outerthought bvba and Schaubroeck nv
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: package org.outerj.daisy.repository.commonimpl;
0017:
0018: import java.util.ArrayList;
0019: import java.util.Arrays;
0020: import java.util.Date;
0021: import java.util.GregorianCalendar;
0022: import java.util.HashMap;
0023: import java.util.Iterator;
0024: import java.util.List;
0025: import java.util.Map;
0026:
0027: import org.outerj.daisy.repository.ByteArrayPartDataSource;
0028: import org.outerj.daisy.repository.ChangeType;
0029: import org.outerj.daisy.repository.DocumentCollection;
0030: import org.outerj.daisy.repository.DocumentCollections;
0031: import org.outerj.daisy.repository.DocumentTypeInconsistencyException;
0032: import org.outerj.daisy.repository.Field;
0033: import org.outerj.daisy.repository.FieldNotFoundException;
0034: import org.outerj.daisy.repository.Fields;
0035: import org.outerj.daisy.repository.HierarchyPath;
0036: import org.outerj.daisy.repository.Link;
0037: import org.outerj.daisy.repository.Links;
0038: import org.outerj.daisy.repository.LockInfo;
0039: import org.outerj.daisy.repository.LockType;
0040: import org.outerj.daisy.repository.Part;
0041: import org.outerj.daisy.repository.PartDataSource;
0042: import org.outerj.daisy.repository.PartNotFoundException;
0043: import org.outerj.daisy.repository.Parts;
0044: import org.outerj.daisy.repository.RepositoryException;
0045: import org.outerj.daisy.repository.RepositoryRuntimeException;
0046: import org.outerj.daisy.repository.ValueType;
0047: import org.outerj.daisy.repository.VariantKey;
0048: import org.outerj.daisy.repository.Version;
0049: import org.outerj.daisy.repository.VersionKey;
0050: import org.outerj.daisy.repository.VersionNotFoundException;
0051: import org.outerj.daisy.repository.VersionState;
0052: import org.outerj.daisy.repository.Versions;
0053: import org.outerj.daisy.repository.acl.AccessDetails;
0054: import org.outerj.daisy.repository.acl.AclDetailPermission;
0055: import org.outerj.daisy.repository.commonimpl.schema.CommonRepositorySchema;
0056: import org.outerj.daisy.repository.commonimpl.variant.CommonVariantManager;
0057: import org.outerj.daisy.repository.schema.DocumentType;
0058: import org.outerj.daisy.repository.schema.FieldType;
0059: import org.outerj.daisy.repository.schema.FieldTypeUse;
0060: import org.outerj.daisy.repository.schema.PartType;
0061: import org.outerj.daisy.repository.schema.PartTypeUse;
0062: import org.outerx.daisy.x10.DocumentDocument;
0063:
0064: /**
0065: * Encapsulates all variant-specific data of a document.
0066: * An instance of this class is contained by {@link DocumentImpl}.
0067: */
0068: public class DocumentVariantImpl {
0069: private DocumentImpl ownerDocument;
0070: private DocumentStrategy documentStrategy;
0071: private CommonRepository repository;
0072: private AuthenticatedUser currentUser;
0073: /** The parts mapped by PartType ID. */
0074: private Map<Long, Part> parts = new HashMap<Long, Part>();
0075: private boolean partChanges = false;
0076: private long documentTypeId;
0077: private boolean documentTypeChanged = false;
0078: private String name;
0079: private boolean nameUpdated = false;
0080: private long branchId;
0081: private long languageId;
0082: private boolean isNew;
0083: private Date lastModified;
0084: private long lastModifier = -1;
0085: private boolean retired = false;
0086: private boolean retiredChanged = false;
0087: private VersionImpl[] versions;
0088: /** Cached reference to the live version. */
0089: private Version liveVersion;
0090: /** Indicates if the variable liveVersion has been initialiased. */
0091: private boolean liveVersionLoaded = false;
0092: /** Cached reference to the last version. */
0093: private Version lastVersion;
0094: /** Indicates if the variable lastVersion has been initialiased. */
0095: private boolean lastVersionLoaded = false;
0096: private long lastVersionId = -1;
0097: private long liveVersionId = -1;
0098: /** The fields mapped by FieldType ID. */
0099: private Map<Long, Field> fields = new HashMap<Long, Field>();
0100: private boolean fieldChanges = false;
0101: private List<Link> links = new ArrayList<Link>(3);
0102: private boolean linkChanges = false;
0103: private Map<String, String> customFields = new HashMap<String, String>();
0104: private boolean customFieldChanges = false;
0105: private Map<Long, DocumentCollection> documentCollections = new HashMap<Long, DocumentCollection>();
0106: private boolean documentCollectionChanges = false;
0107: private LockInfoImpl lockInfo = new LockInfoImpl();
0108: private VersionState versionState = VersionState.PUBLISH;
0109: private String summary;
0110: private long updateCount = 0;
0111: private IntimateAccess intimateAccess = new IntimateAccess();
0112: private long createdFromBranchId = -1;
0113: private long createdFromLanguageId = -1;
0114: private long createdFromVersionId = -1;
0115: // the variables below are values we need to store to be able to pass them on
0116: // from the remote implementation
0117: private boolean validateOnSave = true;
0118: private boolean documentTypeChecksEnabled = true;
0119: private long startBranchId = -1;
0120: private long startLanguageId = -1;
0121: private VersionKey syncedWith;
0122: private ChangeType changeType = ChangeType.MAJOR;
0123: private String changeComment;
0124: private long lastMajorChangeVersionId = -1;
0125: private long liveMajorChangeVersionId = -1;
0126:
0127: public static final String ERROR_ACCESSING_REPOSITORY_SCHEMA = "Error accessing repository schema information.";
0128: private static final String READ_ONLY_MESSAGE = "This document object is read-only.";
0129:
0130: public DocumentVariantImpl(DocumentImpl ownerDocument,
0131: DocumentStrategy documentStrategy,
0132: CommonRepository repository, AuthenticatedUser currentUser,
0133: long documentTypeId, long branchId, long languageId) {
0134: this .ownerDocument = ownerDocument;
0135: this .documentStrategy = documentStrategy;
0136: this .repository = repository;
0137: this .currentUser = currentUser;
0138: this .documentTypeId = documentTypeId;
0139: this .branchId = branchId;
0140: this .languageId = languageId;
0141: this .isNew = true;
0142: }
0143:
0144: public DocumentVariantImpl.IntimateAccess getIntimateAccess(
0145: DocumentStrategy strategy) {
0146: if (this .documentStrategy == strategy)
0147: return intimateAccess;
0148: return null;
0149: }
0150:
0151: public long getBranchId() {
0152: return branchId;
0153: }
0154:
0155: public long getLanguageId() {
0156: return languageId;
0157: }
0158:
0159: public boolean isNew() {
0160: return isNew;
0161: }
0162:
0163: public long getDocumentTypeId() {
0164: return documentTypeId;
0165: }
0166:
0167: public String getDocumentId() {
0168: return ownerDocument.getId();
0169: }
0170:
0171: public long getDocSeqId() {
0172: return ownerDocument.getSeqId();
0173: }
0174:
0175: public String getDocNamespace() {
0176: return ownerDocument.getNamespace();
0177: }
0178:
0179: public VariantKey getKey() {
0180: if (ownerDocument.isNew())
0181: throw new IllegalStateException(
0182: "Cannot get variant key: document is new and thus has no ID assigned just yet.");
0183: return new VariantKey(ownerDocument.getId(), getBranchId(),
0184: getLanguageId());
0185: }
0186:
0187: public void setValidateOnSave(boolean validateOnSave) {
0188: this .validateOnSave = validateOnSave;
0189: }
0190:
0191: public void changeDocumentType(long documentTypeId)
0192: throws RepositoryException {
0193: if (ownerDocument.isReadOnly())
0194: throw new RuntimeException(READ_ONLY_MESSAGE);
0195:
0196: if (documentTypeId != this .documentTypeId) {
0197: // fetch the document type to be sure it exists
0198: // the method below will throw an exception if it doesn't exist
0199: repository.getRepositorySchema().getDocumentTypeById(
0200: documentTypeId, false, currentUser);
0201:
0202: this .documentTypeId = documentTypeId;
0203: this .documentTypeChanged = true;
0204: }
0205: }
0206:
0207: public void changeDocumentType(String documentTypeName)
0208: throws RepositoryException {
0209: if (ownerDocument.isReadOnly())
0210: throw new RuntimeException(READ_ONLY_MESSAGE);
0211:
0212: DocumentType documentType = repository.getRepositorySchema()
0213: .getDocumentTypeByName(documentTypeName, false,
0214: currentUser);
0215: changeDocumentType(documentType.getId());
0216: }
0217:
0218: public Field getField(String name) throws FieldNotFoundException {
0219: FieldType fieldType;
0220: try {
0221: fieldType = repository.getRepositorySchema()
0222: .getFieldTypeByName(name, false, currentUser);
0223: } catch (RepositoryException e) {
0224: throw new RuntimeException(
0225: ERROR_ACCESSING_REPOSITORY_SCHEMA, e);
0226: }
0227: return getField(fieldType.getId());
0228: }
0229:
0230: public Field getField(long fieldTypeId)
0231: throws FieldNotFoundException {
0232: Field field = fields.get(fieldTypeId);
0233: if (field == null)
0234: throw new FieldNotFoundException(fieldTypeId);
0235: else
0236: return field;
0237: }
0238:
0239: public boolean hasField(long fieldTypeId) {
0240: Field field = fields.get(fieldTypeId);
0241: return field != null;
0242: }
0243:
0244: public boolean hasField(String fieldTypeName) {
0245: FieldType fieldType;
0246: try {
0247: fieldType = repository.getRepositorySchema()
0248: .getFieldTypeByName(fieldTypeName, false,
0249: currentUser);
0250: } catch (RepositoryException e) {
0251: throw new RuntimeException(
0252: ERROR_ACCESSING_REPOSITORY_SCHEMA, e);
0253: }
0254: return hasField(fieldType.getId());
0255: }
0256:
0257: public Fields getFields() {
0258: return new FieldsImpl(fields.values().toArray(new Field[0]));
0259: }
0260:
0261: public Fields getFieldsInOrder() {
0262: return new FieldsImpl(orderFields(fields.values().toArray(
0263: new Field[0])));
0264: }
0265:
0266: public void setField(String name, Object value)
0267: throws DocumentTypeInconsistencyException {
0268: if (ownerDocument.isReadOnly())
0269: throw new RuntimeException(READ_ONLY_MESSAGE);
0270:
0271: FieldType fieldType;
0272: try {
0273: fieldType = repository.getRepositorySchema()
0274: .getFieldTypeByName(name, false, currentUser);
0275: } catch (RepositoryException e) {
0276: throw new RuntimeException(
0277: ERROR_ACCESSING_REPOSITORY_SCHEMA, e);
0278: }
0279: setField(fieldType.getId(), value);
0280: }
0281:
0282: public void setField(long fieldTypeId, Object value)
0283: throws DocumentTypeInconsistencyException {
0284: if (ownerDocument.isReadOnly())
0285: throw new RuntimeException(READ_ONLY_MESSAGE);
0286:
0287: if (value == null)
0288: throw new NullPointerException(
0289: "Field value cannot be null.");
0290:
0291: DocumentType documentType;
0292: try {
0293: documentType = repository.getRepositorySchema()
0294: .getDocumentTypeById(documentTypeId, false,
0295: currentUser);
0296: } catch (RepositoryException e) {
0297: throw new RuntimeException(
0298: ERROR_ACCESSING_REPOSITORY_SCHEMA, e);
0299: }
0300:
0301: FieldType fieldType;
0302: try {
0303: fieldType = repository.getRepositorySchema()
0304: .getFieldTypeById(fieldTypeId, false, currentUser);
0305: } catch (RepositoryException e) {
0306: throw new RuntimeException(
0307: ERROR_ACCESSING_REPOSITORY_SCHEMA, e);
0308: }
0309:
0310: if (documentTypeChecksEnabled
0311: && !documentType.hasFieldType(fieldTypeId))
0312: throw new DocumentTypeInconsistencyException(
0313: "The FieldType \"" + fieldType.getName()
0314: + "\" (ID: " + fieldTypeId
0315: + ") is not allowed on this document.");
0316:
0317: // For multivalue fields, make sure the array is a clone so that it's unmodifiable from the outside
0318: if (value instanceof Object[])
0319: value = ((Object[]) value).clone();
0320:
0321: value = checkAndNormalizeFieldValue(fieldType, value);
0322:
0323: FieldImpl field = (FieldImpl) fields.get(new Long(fieldTypeId));
0324:
0325: // if the value didn't change, don't update it
0326: if (field != null
0327: && (fieldType.isMultiValue() ? Arrays.equals(
0328: (Object[]) field.getValue(), (Object[]) value)
0329: : field.getValue().equals(value)))
0330: return;
0331:
0332: field = new FieldImpl(intimateAccess, fieldTypeId, value);
0333: fields.put(new Long(fieldTypeId), field);
0334: fieldChanges = true;
0335: }
0336:
0337: private Object checkAndNormalizeFieldValue(FieldType fieldType,
0338: Object value) throws DocumentTypeInconsistencyException {
0339: if (fieldType.isMultiValue()) {
0340: if (!value.getClass().isArray())
0341: throw new DocumentTypeInconsistencyException(
0342: "The value for the multivalue-field \""
0343: + fieldType.getName()
0344: + "\" should be an array.");
0345: Object[] values = (Object[]) value;
0346: if (values.length == 0)
0347: throw new DocumentTypeInconsistencyException(
0348: "The value supplied for the multivalue field \""
0349: + fieldType.getName()
0350: + "\" is a zero-length array, it should be an array of at least one element.");
0351: for (int i = 0; i < values.length; i++) {
0352: values[i] = checkAndNormalizeHierarchyValue(fieldType,
0353: values[i], i);
0354: }
0355: return values;
0356: } else {
0357: return checkAndNormalizeHierarchyValue(fieldType, value, -1);
0358: }
0359: }
0360:
0361: private Object checkAndNormalizeHierarchyValue(FieldType fieldType,
0362: Object value, int multiValueIndex)
0363: throws DocumentTypeInconsistencyException {
0364: if (fieldType.isHierarchical()) {
0365: if (!(value instanceof HierarchyPath))
0366: throw new DocumentTypeInconsistencyException(
0367: "The supplied value for hierarchical field \""
0368: + fieldType.getName()
0369: + "\" should be a HierarchyPath object.");
0370: HierarchyPath hierarchyPath = (HierarchyPath) value;
0371: Object[] values = hierarchyPath.getElements();
0372: if (values.length == 0)
0373: throw new DocumentTypeInconsistencyException(
0374: "The supplied HierarchyPath object for hierarchical field \""
0375: + fieldType.getName()
0376: + "\" should contain at least one element.");
0377: for (int i = 0; i < values.length; i++) {
0378: values[i] = checkAndNormalizePrimitiveValue(fieldType,
0379: values[i], multiValueIndex, i);
0380: }
0381: return new HierarchyPath(values);
0382: } else {
0383: return checkAndNormalizePrimitiveValue(fieldType, value,
0384: multiValueIndex, -1);
0385: }
0386: }
0387:
0388: private Object checkAndNormalizePrimitiveValue(FieldType fieldType,
0389: Object value, int multiValueIndex, int hierarchyPathIndex)
0390: throws DocumentTypeInconsistencyException {
0391: ValueType valueType = fieldType.getValueType();
0392: if (!valueType.getTypeClass()
0393: .isAssignableFrom(value.getClass()))
0394: throw new DocumentTypeInconsistencyException(
0395: "The supplied value for the field \""
0396: + fieldType.getName()
0397: + "\" is not of the correct type. Expected was a "
0398: + valueType.toString()
0399: + " ("
0400: + valueType.getTypeClass().getName()
0401: + ") but got a "
0402: + value.getClass().getName()
0403: + getFieldValuePositionDetails(
0404: multiValueIndex, hierarchyPathIndex));
0405:
0406: // perform normalization of values for certain value types
0407: if (valueType == ValueType.DATE
0408: || valueType == ValueType.DATETIME) {
0409: boolean keepTime = fieldType.getValueType() == ValueType.DATETIME;
0410: value = DateUtil.getNormalizedDate((Date) value, keepTime);
0411: } else if (valueType == ValueType.LINK) {
0412: VariantKey variantKey = (VariantKey) value;
0413: value = new VariantKey(repository
0414: .normalizeDocumentId(variantKey.getDocumentId()),
0415: variantKey.getBranchId(), variantKey
0416: .getLanguageId());
0417: }
0418:
0419: return value;
0420: }
0421:
0422: private String getFieldValuePositionDetails(int multiValueIndex,
0423: int hierarchyPathIndex) {
0424: StringBuilder builder = new StringBuilder();
0425: if (multiValueIndex != -1 || hierarchyPathIndex != -1)
0426: builder.append(" (");
0427: if (multiValueIndex != -1)
0428: builder.append("multi value index: ").append(
0429: multiValueIndex);
0430: if (hierarchyPathIndex != -1) {
0431: if (multiValueIndex != -1)
0432: builder.append(", ");
0433: builder.append("hierarchy path index: ").append(
0434: hierarchyPathIndex);
0435: }
0436: if (builder.length() > 0)
0437: builder.append(")");
0438: return builder.toString();
0439: }
0440:
0441: public void deleteField(String name) {
0442: if (ownerDocument.isReadOnly())
0443: throw new RuntimeException(READ_ONLY_MESSAGE);
0444:
0445: FieldType fieldType;
0446: try {
0447: fieldType = repository.getRepositorySchema()
0448: .getFieldTypeByName(name, false, currentUser);
0449: } catch (RepositoryException e) {
0450: throw new RuntimeException(
0451: ERROR_ACCESSING_REPOSITORY_SCHEMA, e);
0452: }
0453: deleteField(fieldType.getId());
0454: }
0455:
0456: public void deleteField(long fieldTypeId) {
0457: if (ownerDocument.isReadOnly())
0458: throw new RuntimeException(READ_ONLY_MESSAGE);
0459:
0460: Long key = new Long(fieldTypeId);
0461: if (fields.containsKey(key)) {
0462: fields.remove(new Long(fieldTypeId));
0463: fieldChanges = true;
0464: }
0465: }
0466:
0467: public LockInfo getLockInfo(boolean fresh)
0468: throws RepositoryException {
0469: LockInfoImpl lockInfo = this .lockInfo;
0470: if ((fresh || lockInfo == null) && !isNew) {
0471: synchronized (this ) {
0472: lockInfo = documentStrategy.getLockInfo(this );
0473: this .lockInfo = lockInfo;
0474: }
0475: }
0476: return lockInfo;
0477: }
0478:
0479: // This method is by purpose not in the Document interface. It is used to clear lock
0480: // info in cached document objects. If it would be in the interface, then the method
0481: // getLockInfo should cover the case where lockInfo is null and id is -1.
0482: public void clearLockInfo() {
0483: this .lockInfo = null;
0484: }
0485:
0486: public boolean lock(long duration, LockType lockType)
0487: throws RepositoryException {
0488: if (ownerDocument.isReadOnly())
0489: throw new RuntimeException(READ_ONLY_MESSAGE);
0490:
0491: if (isNew)
0492: throw new RepositoryException(
0493: "Can't take a lock on a new, non-saved document variant.");
0494:
0495: lockInfo = documentStrategy.lock(this , duration, lockType);
0496:
0497: if (!lockInfo.hasLock())
0498: return false;
0499:
0500: return lockInfo.getUserId() == currentUser.getId();
0501: }
0502:
0503: public boolean releaseLock() throws RepositoryException {
0504: if (ownerDocument.isReadOnly())
0505: throw new RuntimeException(READ_ONLY_MESSAGE);
0506:
0507: if (isNew)
0508: return true;
0509:
0510: lockInfo = documentStrategy.releaseLock(this );
0511: return !lockInfo.hasLock();
0512: }
0513:
0514: public void addXml(DocumentDocument.Document documentXml,
0515: AccessDetails accessDetails) throws RepositoryException {
0516: addNonVersionedDataToXml(documentXml, accessDetails);
0517:
0518: documentXml.setDataVersionId(-1); // -1 indicates "current data in object, whether it's saved or not"
0519: documentXml.setName(name);
0520: documentXml
0521: .setFields(VersionedDataAccessWrapper.filterFields(
0522: getFieldsInOrder(), accessDetails).getXml()
0523: .getFields());
0524: documentXml.setParts(VersionedDataAccessWrapper.filterParts(
0525: getPartsInOrder(), accessDetails).getXml().getParts());
0526: documentXml.setLinks(getLinks().getXml().getLinks());
0527: documentXml.setValidateOnSave(validateOnSave);
0528: }
0529:
0530: public void addXml(DocumentDocument.Document documentXml,
0531: long versionId, AccessDetails accessDetails)
0532: throws RepositoryException {
0533: Version version = getVersion(versionId);
0534:
0535: addNonVersionedDataToXml(documentXml, accessDetails);
0536: documentXml.setDataVersionId(versionId);
0537: documentXml.setName(version.getDocumentName());
0538: documentXml.setFields(VersionedDataAccessWrapper.filterFields(
0539: version.getFieldsInOrder(), accessDetails).getXml()
0540: .getFields());
0541: documentXml.setParts(VersionedDataAccessWrapper.filterParts(
0542: version.getPartsInOrder(), accessDetails).getXml()
0543: .getParts());
0544: documentXml.setLinks(version.getLinks().getXml().getLinks());
0545: }
0546:
0547: public void addNonVersionedDataToXml(
0548: DocumentDocument.Document documentXml,
0549: AccessDetails accessDetails) {
0550: if (!isNew) {
0551: GregorianCalendar lastModified = new GregorianCalendar();
0552: lastModified.setTime(this .lastModified);
0553: documentXml.setVariantLastModified(lastModified);
0554: documentXml.setVariantLastModifier(lastModifier);
0555: if (liveVersionId != -1)
0556: documentXml.setLiveVersionId(liveVersionId);
0557: }
0558:
0559: documentXml.setBranchId(branchId);
0560: documentXml.setLanguageId(languageId);
0561: documentXml.setTypeId(documentTypeId);
0562: documentXml.setLastVersionId(lastVersionId);
0563: documentXml.setRetired(retired);
0564: documentXml
0565: .setNewVersionState(DocumentDocument.Document.NewVersionState.Enum
0566: .forString(versionState.toString()));
0567: if (syncedWith != null) {
0568: documentXml.setNewSyncedWithLanguageId(syncedWith
0569: .getLanguageId());
0570: documentXml.setNewSyncedWithVersionId(syncedWith
0571: .getVersionId());
0572: }
0573: documentXml
0574: .setNewChangeType(DocumentDocument.Document.NewChangeType.Enum
0575: .forString(changeType.toString()));
0576: documentXml.setNewChangeComment(changeComment);
0577: documentXml.setVariantUpdateCount(updateCount);
0578: if ((accessDetails == null || accessDetails
0579: .isGranted(AclDetailPermission.SUMMARY))
0580: && summary != null)
0581: documentXml.setSummary(summary);
0582: documentXml.setCreatedFromBranchId(createdFromBranchId);
0583: documentXml.setCreatedFromLanguageId(createdFromLanguageId);
0584: documentXml.setCreatedFromVersionId(createdFromVersionId);
0585: documentXml
0586: .setDocumentTypeChecksEnabled(documentTypeChecksEnabled);
0587: documentXml
0588: .setLastMajorChangeVersionId(lastMajorChangeVersionId);
0589: documentXml
0590: .setLiveMajorChangeVersionId(liveMajorChangeVersionId);
0591:
0592: DocumentDocument.Document.CustomFields customFieldsXml = documentXml
0593: .addNewCustomFields();
0594: for (Map.Entry<String, String> customField : customFields
0595: .entrySet()) {
0596: DocumentDocument.Document.CustomFields.CustomField customFieldXml = customFieldsXml
0597: .addNewCustomField();
0598: customFieldXml.setName(customField.getKey());
0599: customFieldXml.setValue(customField.getValue());
0600: }
0601:
0602: LockInfo lockInfo;
0603: try {
0604: lockInfo = getLockInfo(false);
0605: } catch (RepositoryException e) {
0606: throw new RuntimeException(e);
0607: }
0608: documentXml.setLockInfo(lockInfo.getXml().getLockInfo());
0609:
0610: long[] collectionIds = new long[documentCollections.size()];
0611: Iterator collectionsIt = documentCollections.values()
0612: .iterator();
0613: int i = 0;
0614: while (collectionsIt.hasNext()) {
0615: DocumentCollection collection = (DocumentCollection) collectionsIt
0616: .next();
0617: collectionIds[i] = collection.getId();
0618: i++;
0619: }
0620: documentXml.addNewCollectionIds().setCollectionIdArray(
0621: collectionIds);
0622: }
0623:
0624: public void setName(String name) {
0625: if (ownerDocument.isReadOnly())
0626: throw new RuntimeException(READ_ONLY_MESSAGE);
0627:
0628: if (name == null)
0629: throw new NullPointerException("name may not be null.");
0630:
0631: if (!name.equals(this .name)) {
0632: this .name = name;
0633: nameUpdated = true;
0634: }
0635: }
0636:
0637: public String getName() {
0638: return name;
0639: }
0640:
0641: public void setPart(String partTypeName, String mimeType,
0642: byte[] data) throws DocumentTypeInconsistencyException {
0643: if (ownerDocument.isReadOnly())
0644: throw new RuntimeException(READ_ONLY_MESSAGE);
0645:
0646: if (data == null)
0647: throw new NullPointerException(
0648: "data argument cannot be null.");
0649:
0650: setPart(partTypeName, mimeType, new ByteArrayPartDataSource(
0651: data));
0652:
0653: }
0654:
0655: public void setPart(long partTypeId, String mimeType, byte[] data)
0656: throws DocumentTypeInconsistencyException {
0657: if (ownerDocument.isReadOnly())
0658: throw new RuntimeException(READ_ONLY_MESSAGE);
0659:
0660: if (data == null)
0661: throw new NullPointerException(
0662: "data argument cannot be null.");
0663:
0664: setPart(partTypeId, mimeType, new ByteArrayPartDataSource(data));
0665: }
0666:
0667: public void setPart(String partTypeName, String mimeType,
0668: PartDataSource partDataSource)
0669: throws DocumentTypeInconsistencyException {
0670: if (ownerDocument.isReadOnly())
0671: throw new RuntimeException(READ_ONLY_MESSAGE);
0672:
0673: PartType partType;
0674: try {
0675: partType = repository
0676: .getRepositorySchema()
0677: .getPartTypeByName(partTypeName, false, currentUser);
0678: } catch (RepositoryException e) {
0679: throw new RuntimeException(
0680: ERROR_ACCESSING_REPOSITORY_SCHEMA, e);
0681: }
0682:
0683: setPart(partType.getId(), mimeType, partDataSource);
0684: }
0685:
0686: public void setPart(long partTypeId, String mimeType,
0687: PartDataSource partDataSource)
0688: throws DocumentTypeInconsistencyException {
0689: if (ownerDocument.isReadOnly())
0690: throw new RuntimeException(READ_ONLY_MESSAGE);
0691:
0692: // first check with the DocumentType if this part is allowed
0693: DocumentType documentType;
0694: try {
0695: documentType = repository.getRepositorySchema()
0696: .getDocumentTypeById(documentTypeId, false,
0697: currentUser);
0698: } catch (RepositoryException e) {
0699: throw new RuntimeException(
0700: ERROR_ACCESSING_REPOSITORY_SCHEMA, e);
0701: }
0702:
0703: PartType partType;
0704: try {
0705: partType = repository.getRepositorySchema()
0706: .getPartTypeById(partTypeId, false, currentUser);
0707: } catch (RepositoryException e) {
0708: throw new RuntimeException(
0709: ERROR_ACCESSING_REPOSITORY_SCHEMA, e);
0710: }
0711:
0712: if (mimeType == null || mimeType.length() == 0)
0713: throw new NullPointerException(
0714: "mimeType argument cannot be null or an empty string");
0715:
0716: if (partDataSource == null)
0717: throw new NullPointerException(
0718: "partDataSource argument cannot be null.");
0719:
0720: if (documentTypeChecksEnabled
0721: && !documentType.hasPartType(partTypeId))
0722: throw new DocumentTypeInconsistencyException(
0723: "The PartType \"" + partType.getName() + "\" (ID: "
0724: + partTypeId
0725: + ") is not allowed on this document.");
0726:
0727: if (documentTypeChecksEnabled
0728: && !partType.mimeTypeAllowed(mimeType))
0729: throw new DocumentTypeInconsistencyException(
0730: "The mime-type \""
0731: + mimeType
0732: + "\" isn't part of the allowed mime types ("
0733: + partType.getMimeTypes()
0734: + ") required by the PartType \""
0735: + partType.getName() + "\" (ID: "
0736: + partTypeId + ").");
0737:
0738: PartImpl part = new PartImpl(intimateAccess, partTypeId,
0739: lastVersionId, -1);
0740: PartImpl.IntimateAccess partInt = part
0741: .getIntimateAccess(documentStrategy);
0742: partInt.setMimeType(mimeType);
0743: partInt.setData(partDataSource);
0744: partInt.setNewOrUpdated(true, true);
0745:
0746: parts.put(new Long(partTypeId), part);
0747: partChanges = true;
0748: }
0749:
0750: public void setPartFileName(String partTypeName, String fileName) {
0751: if (ownerDocument.isReadOnly())
0752: throw new RuntimeException(READ_ONLY_MESSAGE);
0753:
0754: PartType partType;
0755: try {
0756: partType = repository
0757: .getRepositorySchema()
0758: .getPartTypeByName(partTypeName, false, currentUser);
0759: } catch (RepositoryException e) {
0760: throw new RuntimeException(
0761: ERROR_ACCESSING_REPOSITORY_SCHEMA, e);
0762: }
0763:
0764: setPartFileName(partType.getId(), fileName);
0765: }
0766:
0767: public void setPartFileName(long partTypeId, String fileName) {
0768: if (ownerDocument.isReadOnly())
0769: throw new RuntimeException(READ_ONLY_MESSAGE);
0770:
0771: PartImpl part = (PartImpl) parts.get(new Long(partTypeId));
0772: if (part == null)
0773: throw new RepositoryRuntimeException(
0774: "The document does not have a part for part type ID "
0775: + partTypeId);
0776:
0777: if ((part.getFileName() == null && fileName == null)
0778: || (part.getFileName() != null && part.getFileName()
0779: .equals(fileName)))
0780: return;
0781:
0782: PartImpl.IntimateAccess partInt = part
0783: .getIntimateAccess(documentStrategy);
0784: partInt.setFileName(fileName);
0785: partInt.setNewOrUpdated(true, partInt.isDataUpdated());
0786: partChanges = true;
0787: }
0788:
0789: public void setPartMimeType(String partTypeName, String mimeType) {
0790: if (ownerDocument.isReadOnly())
0791: throw new RuntimeException(READ_ONLY_MESSAGE);
0792:
0793: PartType partType;
0794: try {
0795: partType = repository
0796: .getRepositorySchema()
0797: .getPartTypeByName(partTypeName, false, currentUser);
0798: } catch (RepositoryException e) {
0799: throw new RuntimeException(
0800: ERROR_ACCESSING_REPOSITORY_SCHEMA, e);
0801: }
0802:
0803: setPartMimeType(partType.getId(), mimeType);
0804: }
0805:
0806: public void setPartMimeType(long partTypeId, String mimeType) {
0807: if (ownerDocument.isReadOnly())
0808: throw new RuntimeException(READ_ONLY_MESSAGE);
0809:
0810: if (mimeType == null)
0811: throw new IllegalArgumentException(
0812: "Part mime-type cannot be set to null value.");
0813:
0814: PartImpl part = (PartImpl) parts.get(new Long(partTypeId));
0815: if (part == null)
0816: throw new RepositoryRuntimeException(
0817: "The document does not have a part for part type ID "
0818: + partTypeId);
0819:
0820: if (part.getMimeType().equals(mimeType))
0821: return;
0822:
0823: PartImpl.IntimateAccess partInt = part
0824: .getIntimateAccess(documentStrategy);
0825: partInt.setMimeType(mimeType);
0826: partInt.setNewOrUpdated(true, partInt.isDataUpdated());
0827: partChanges = true;
0828: }
0829:
0830: public Parts getParts() {
0831: return new PartsImpl(parts.values().toArray(new Part[0]));
0832: }
0833:
0834: public Parts getPartsInOrder() {
0835: return new PartsImpl(orderParts(parts.values().toArray(
0836: new Part[0])));
0837: }
0838:
0839: private Part[] orderParts(Part[] parts) {
0840: Part[] resultList = new Part[parts.length];
0841: boolean[] handled = new boolean[parts.length];
0842:
0843: DocumentType documentType;
0844: try {
0845: documentType = repository.getRepositorySchema()
0846: .getDocumentTypeById(documentTypeId, false,
0847: currentUser);
0848: } catch (RepositoryException e) {
0849: throw new RuntimeException(
0850: ERROR_ACCESSING_REPOSITORY_SCHEMA, e);
0851: }
0852: PartTypeUse[] partTypeUses = documentType.getPartTypeUses();
0853:
0854: int resultListPos = -1;
0855: for (PartTypeUse partTypeUse : partTypeUses) {
0856: long id = partTypeUse.getPartType().getId();
0857: for (int k = 0; k < parts.length; k++) {
0858: if (parts[k].getTypeId() == id) {
0859: handled[k] = true;
0860: resultListPos++;
0861: resultList[resultListPos] = parts[k];
0862: break;
0863: }
0864: }
0865: }
0866:
0867: for (int i = 0; i < handled.length; i++) {
0868: if (!handled[i]) {
0869: resultListPos++;
0870: resultList[resultListPos] = parts[i];
0871: }
0872: }
0873:
0874: return resultList;
0875: }
0876:
0877: private Field[] orderFields(Field[] fields) {
0878: Field[] resultList = new Field[fields.length];
0879: boolean[] handled = new boolean[fields.length];
0880:
0881: DocumentType documentType;
0882: try {
0883: documentType = repository.getRepositorySchema()
0884: .getDocumentTypeById(documentTypeId, false,
0885: currentUser);
0886: } catch (RepositoryException e) {
0887: throw new RuntimeException(
0888: ERROR_ACCESSING_REPOSITORY_SCHEMA, e);
0889: }
0890: FieldTypeUse[] fieldTypeUses = documentType.getFieldTypeUses();
0891:
0892: int resultListPos = -1;
0893: for (FieldTypeUse fieldTypeUse : fieldTypeUses) {
0894: long id = fieldTypeUse.getFieldType().getId();
0895: for (int k = 0; k < fields.length; k++) {
0896: if (fields[k].getTypeId() == id) {
0897: handled[k] = true;
0898: resultListPos++;
0899: resultList[resultListPos] = fields[k];
0900: break;
0901: }
0902: }
0903: }
0904:
0905: for (int i = 0; i < handled.length; i++) {
0906: if (!handled[i]) {
0907: resultListPos++;
0908: resultList[resultListPos] = fields[i];
0909: }
0910: }
0911:
0912: return resultList;
0913: }
0914:
0915: public void deletePart(long partTypeId) {
0916: if (ownerDocument.isReadOnly())
0917: throw new RuntimeException(READ_ONLY_MESSAGE);
0918:
0919: Long key = new Long(partTypeId);
0920: if (parts.containsKey(key)) {
0921: parts.remove(key);
0922: partChanges = true;
0923: }
0924: }
0925:
0926: public void deletePart(String name) {
0927: if (ownerDocument.isReadOnly())
0928: throw new RuntimeException(READ_ONLY_MESSAGE);
0929:
0930: PartType partType;
0931: try {
0932: partType = repository.getRepositorySchema()
0933: .getPartTypeByName(name, false, currentUser);
0934: } catch (RepositoryException e) {
0935: throw new RuntimeException(
0936: ERROR_ACCESSING_REPOSITORY_SCHEMA, e);
0937: }
0938: deletePart(partType.getId());
0939: }
0940:
0941: public Part getPart(long partTypeId) throws PartNotFoundException {
0942: Part part = parts.get(new Long(partTypeId));
0943: if (part == null)
0944: throw new PartNotFoundException(partTypeId);
0945: else
0946: return part;
0947: }
0948:
0949: public Part getPart(String name) throws PartNotFoundException {
0950: PartType partType;
0951: try {
0952: partType = repository.getRepositorySchema()
0953: .getPartTypeByName(name, false, currentUser);
0954: } catch (RepositoryException e) {
0955: throw new RuntimeException(
0956: ERROR_ACCESSING_REPOSITORY_SCHEMA, e);
0957: }
0958: return getPart(partType.getId());
0959: }
0960:
0961: public boolean hasPart(long partTypeId) {
0962: Part part = parts.get(new Long(partTypeId));
0963: return part != null;
0964: }
0965:
0966: public boolean hasPart(String name) {
0967: PartType partType;
0968: try {
0969: partType = repository.getRepositorySchema()
0970: .getPartTypeByName(name, false, currentUser);
0971: } catch (RepositoryException e) {
0972: throw new RuntimeException(
0973: ERROR_ACCESSING_REPOSITORY_SCHEMA, e);
0974: }
0975: return hasPart(partType.getId());
0976: }
0977:
0978: public void setCustomField(String name, String value) {
0979: if (ownerDocument.isReadOnly())
0980: throw new RuntimeException(READ_ONLY_MESSAGE);
0981:
0982: if (name == null)
0983: throw new RuntimeException("name argument cannot be null.");
0984: if (value == null)
0985: throw new RuntimeException("value argument cannot be null.");
0986:
0987: customFields.put(name, value);
0988: customFieldChanges = true;
0989: }
0990:
0991: public void deleteCustomField(String name) {
0992: if (ownerDocument.isReadOnly())
0993: throw new RuntimeException(READ_ONLY_MESSAGE);
0994:
0995: if (name == null)
0996: throw new RuntimeException("name argument cannot be null.");
0997:
0998: customFields.remove(name);
0999: customFieldChanges = true;
1000: }
1001:
1002: public void clearCustomFields() {
1003: if (ownerDocument.isReadOnly())
1004: throw new RuntimeException(READ_ONLY_MESSAGE);
1005:
1006: customFields.clear();
1007: customFieldChanges = true;
1008: }
1009:
1010: public void clearCollections() {
1011: if (ownerDocument.isReadOnly())
1012: throw new RuntimeException(READ_ONLY_MESSAGE);
1013:
1014: documentCollections.clear();
1015: documentCollectionChanges = true;
1016: }
1017:
1018: public Map<String, String> getCustomFields() {
1019: return new HashMap<String, String>(customFields);
1020: }
1021:
1022: public String getCustomField(String name) {
1023: if (name == null)
1024: throw new NullPointerException(
1025: "name argument cannot be null.");
1026:
1027: return customFields.get(name);
1028: }
1029:
1030: public boolean hasCustomField(String name) {
1031: if (name == null)
1032: throw new NullPointerException(
1033: "name argument cannot be null.");
1034:
1035: return customFields.containsKey(name);
1036: }
1037:
1038: public Links getLinks() {
1039: return new LinksImpl(links.toArray(new Link[links.size()]));
1040: }
1041:
1042: public void addLink(String title, String target) {
1043: if (ownerDocument.isReadOnly())
1044: throw new RuntimeException(READ_ONLY_MESSAGE);
1045:
1046: links.add(new LinkImpl(title, target));
1047: linkChanges = true;
1048: }
1049:
1050: public void deleteLink(int index) {
1051: if (ownerDocument.isReadOnly())
1052: throw new RuntimeException(READ_ONLY_MESSAGE);
1053:
1054: links.remove(index);
1055: linkChanges = true;
1056: }
1057:
1058: public void clearLinks() {
1059: if (ownerDocument.isReadOnly())
1060: throw new RuntimeException(READ_ONLY_MESSAGE);
1061:
1062: links.clear();
1063: linkChanges = true;
1064: }
1065:
1066: public void validate() throws DocumentTypeInconsistencyException {
1067: fullConsistencyCheck();
1068: }
1069:
1070: public void setNewVersionState(VersionState versionState) {
1071: if (ownerDocument.isReadOnly())
1072: throw new RuntimeException(READ_ONLY_MESSAGE);
1073:
1074: if (versionState == null)
1075: throw new NullPointerException(
1076: "versionState argument cannot be null.");
1077:
1078: this .versionState = versionState;
1079: }
1080:
1081: public VersionState getNewVersionState() {
1082: return versionState;
1083: }
1084:
1085: public synchronized Version getVersion(long versionId)
1086: throws RepositoryException {
1087: if (isNew)
1088: throw new RepositoryException(
1089: "A new document variant has no versions.");
1090: if (versionId < 1 || versionId > lastVersionId)
1091: throw new VersionNotFoundException(String
1092: .valueOf(versionId), ownerDocument.getId(), String
1093: .valueOf(branchId), String.valueOf(languageId));
1094:
1095: checkVersionsArray();
1096:
1097: int arrayPos = (int) versionId - 1;
1098: if (versions[arrayPos] == null) {
1099: versions[arrayPos] = documentStrategy.loadVersion(this ,
1100: versionId);
1101: }
1102:
1103: return versions[arrayPos];
1104: }
1105:
1106: public Version getLastVersion() throws RepositoryException {
1107: if (isNew)
1108: return null;
1109:
1110: if (lastVersionLoaded)
1111: return lastVersion;
1112:
1113: Version[] versions = getVersions().getArray();
1114: lastVersion = versions[versions.length - 1];
1115: lastVersionLoaded = true;
1116: return lastVersion;
1117: }
1118:
1119: public Version getLiveVersion() throws RepositoryException {
1120: if (isNew || liveVersionId == -1)
1121: return null;
1122:
1123: if (liveVersionLoaded)
1124: return liveVersion;
1125:
1126: checkVersionsArray();
1127: int liveVersionPos = (int) liveVersionId - 1; // position in the versions array
1128: if (versions[(int) liveVersionId - 1] != null) {
1129: liveVersion = versions[liveVersionPos];
1130: } else {
1131: VersionImpl newLiveVersion = documentStrategy.loadVersion(
1132: this , liveVersionId);
1133: versions[liveVersionPos] = newLiveVersion;
1134: liveVersion = newLiveVersion;
1135: }
1136: liveVersionLoaded = true;
1137: return liveVersion;
1138: }
1139:
1140: public long getLiveVersionId() {
1141: return liveVersionId;
1142: }
1143:
1144: public synchronized Versions getVersions()
1145: throws RepositoryException {
1146: if (isNew)
1147: return new VersionsImpl(new Version[0]);
1148:
1149: loadVersions();
1150:
1151: return new VersionsImpl(versions.clone());
1152: }
1153:
1154: private void loadVersions() throws RepositoryException {
1155: checkVersionsArray();
1156:
1157: VersionImpl[] loadedVersions = null;
1158:
1159: // check if all versions are loaded, if not load them
1160: // note that we don't simply replace the whole version array because some
1161: // versions may already be loaded fully, and otherwise that work would be lost.
1162: for (int i = 0; i < versions.length; i++) {
1163: if (versions[i] == null) {
1164: if (loadedVersions == null)
1165: loadedVersions = documentStrategy
1166: .loadShallowVersions(this );
1167: versions[i] = loadedVersions[i];
1168: }
1169: }
1170: }
1171:
1172: public long getLastVersionId() {
1173: return lastVersionId;
1174: }
1175:
1176: private void checkVersionsArray() {
1177: if (versions == null) {
1178: versions = new VersionImpl[(int) lastVersionId];
1179: } else if (versions.length != lastVersionId) {
1180: VersionImpl[] oldVersions = versions;
1181: versions = new VersionImpl[(int) lastVersionId];
1182: System.arraycopy(oldVersions, 0, versions, 0,
1183: oldVersions.length);
1184: }
1185: }
1186:
1187: private void fullConsistencyCheck()
1188: throws DocumentTypeInconsistencyException {
1189: DocumentType documentType;
1190: try {
1191: documentType = repository.getRepositorySchema()
1192: .getDocumentTypeById(documentTypeId, false,
1193: currentUser);
1194: } catch (RepositoryException e) {
1195: throw new RuntimeException(
1196: ERROR_ACCESSING_REPOSITORY_SCHEMA);
1197: }
1198:
1199: // check the parts
1200: PartTypeUse[] partTypeUses = documentType.getPartTypeUses();
1201: boolean[] hasPartType = new boolean[partTypeUses.length];
1202:
1203: Part[] parts = getParts().getArray();
1204: for (Part part : parts) {
1205: long partTypeId = part.getTypeId();
1206: int partTypeIndex = -1;
1207: for (int j = 0; j < partTypeUses.length; j++) {
1208: if (partTypeUses[j].getPartType().getId() == partTypeId) {
1209: partTypeIndex = j;
1210: }
1211: }
1212: if (partTypeIndex == -1)
1213: throw new DocumentTypeInconsistencyException(
1214: "The document contains a not-allowed part (PartType ID: "
1215: + partTypeId + ").");
1216: hasPartType[partTypeIndex] = true;
1217: if (!partTypeUses[partTypeIndex].getPartType()
1218: .mimeTypeAllowed(part.getMimeType()))
1219: throw new DocumentTypeInconsistencyException(
1220: "The mime-type for the part \""
1221: + partTypeUses[partTypeIndex]
1222: .getPartType().getName()
1223: + "\" isn't part of the allowed mime types (PartType ID: "
1224: + partTypeId + ").");
1225: }
1226:
1227: for (int i = 0; i < partTypeUses.length; i++) {
1228: if (partTypeUses[i].isRequired() && !hasPartType[i])
1229: throw new DocumentTypeInconsistencyException(
1230: "The document doesn't have the required part \""
1231: + partTypeUses[i].getPartType()
1232: .getName() + "\" (ID: "
1233: + partTypeUses[i].getPartType().getId()
1234: + ").");
1235: }
1236:
1237: // check the fields
1238: FieldTypeUse[] fieldTypeUses = documentType.getFieldTypeUses();
1239: boolean[] hasFieldType = new boolean[fieldTypeUses.length];
1240:
1241: Field[] fields = getFields().getArray();
1242: for (Field field : fields) {
1243: long fieldTypeId = field.getTypeId();
1244: int fieldTypeIndex = -1;
1245: for (int j = 0; j < fieldTypeUses.length; j++) {
1246: if (fieldTypeUses[j].getFieldType().getId() == fieldTypeId) {
1247: fieldTypeIndex = j;
1248: }
1249: }
1250: if (fieldTypeIndex == -1)
1251: throw new DocumentTypeInconsistencyException(
1252: "The document contains a not-allowed field (FieldType ID: "
1253: + fieldTypeId + ").");
1254: hasFieldType[fieldTypeIndex] = true;
1255: }
1256:
1257: for (int i = 0; i < fieldTypeUses.length; i++) {
1258: if (fieldTypeUses[i].isRequired() && !hasFieldType[i])
1259: throw new DocumentTypeInconsistencyException(
1260: "The document doesn't have the required field \""
1261: + fieldTypeUses[i].getFieldType()
1262: .getName()
1263: + "\" (ID: "
1264: + fieldTypeUses[i].getFieldType()
1265: .getId() + ").");
1266: }
1267: }
1268:
1269: public Date getLastModified() {
1270: if (lastModified != null)
1271: return (Date) lastModified.clone();
1272: else
1273: return lastModified;
1274: }
1275:
1276: public long getLastModifier() {
1277: return lastModifier;
1278: }
1279:
1280: public boolean isRetired() {
1281: return retired;
1282: }
1283:
1284: public void setRetired(boolean retired) {
1285: if (ownerDocument.isReadOnly())
1286: throw new RuntimeException(READ_ONLY_MESSAGE);
1287:
1288: if (retired != this .retired) {
1289: this .retired = retired;
1290: this .retiredChanged = true;
1291: }
1292: }
1293:
1294: public DocumentCollections getCollections() {
1295: return new DocumentCollectionsImpl(documentCollections.values()
1296: .toArray(new DocumentCollection[0]));
1297: }
1298:
1299: public boolean inCollection(DocumentCollection collection) {
1300: return documentCollections.containsKey(new Long(collection
1301: .getId()));
1302: }
1303:
1304: public boolean inCollection(long collectionId) {
1305: return documentCollections.containsKey(new Long(collectionId));
1306: }
1307:
1308: public void addToCollection(DocumentCollection c) {
1309: if (ownerDocument.isReadOnly())
1310: throw new RuntimeException(READ_ONLY_MESSAGE);
1311:
1312: if (c.getId() == -1)
1313: throw new IllegalArgumentException(
1314: "The specified collection has not yet been saved.");
1315:
1316: this .documentCollectionChanges = true;
1317: this .documentCollections.put(new Long(c.getId()), c);
1318: }
1319:
1320: public void removeFromCollection(DocumentCollection c) {
1321: if (ownerDocument.isReadOnly())
1322: throw new RuntimeException(READ_ONLY_MESSAGE);
1323:
1324: this .documentCollectionChanges = true;
1325: documentCollections.remove(new Long(c.getId()));
1326: }
1327:
1328: public String getSummary() {
1329: return summary == null ? "" : summary;
1330: }
1331:
1332: public long getLastMajorChangeVersionId() {
1333: return lastMajorChangeVersionId;
1334: }
1335:
1336: public long getLiveMajorChangeVersionId() {
1337: return liveMajorChangeVersionId;
1338: }
1339:
1340: public long getUpdateCount() {
1341: return updateCount;
1342: }
1343:
1344: public long getCreatedFromBranchId() {
1345: return createdFromBranchId;
1346: }
1347:
1348: public long getCreatedFromLanguageId() {
1349: return createdFromLanguageId;
1350: }
1351:
1352: public long getCreatedFromVersionId() {
1353: return createdFromVersionId;
1354: }
1355:
1356: public void setDocumentTypeChecksEnabled(
1357: boolean documentTypeChecksEnabled) {
1358: if (ownerDocument.isReadOnly())
1359: throw new RuntimeException(READ_ONLY_MESSAGE);
1360:
1361: this .documentTypeChecksEnabled = documentTypeChecksEnabled;
1362: }
1363:
1364: public void setNewSyncedWithVersion(long syncedWithLanguageId,
1365: long syncedWithVersionId) throws RepositoryException {
1366: if (ownerDocument.isReadOnly())
1367: throw new RuntimeException(READ_ONLY_MESSAGE);
1368:
1369: // NOTE: similar code to VersionImpl.setSyncedWithVersion
1370:
1371: if (syncedWithLanguageId == -1 || syncedWithVersionId == -1) {
1372: if (syncedWithLanguageId != -1 || syncedWithVersionId != -1)
1373: throw new IllegalArgumentException(
1374: "The languageId and versionId arguments should both be -1 or not be -1 at all.");
1375:
1376: syncedWith = null;
1377: return;
1378: }
1379:
1380: if (syncedWithLanguageId == this .languageId) {
1381: throw new IllegalArgumentException(
1382: "You can not make a document synced with a version in the same language");
1383: }
1384:
1385: // Check the language exists
1386: repository.getVariantManager().getLanguage(languageId, false,
1387: currentUser);
1388:
1389: syncedWith = new VersionKey(this .getDocumentId(), branchId,
1390: syncedWithLanguageId, syncedWithVersionId);
1391: }
1392:
1393: public VersionKey getNewSyncedWith() {
1394: return syncedWith;
1395: }
1396:
1397: public void setNewChangeType(ChangeType changeType) {
1398: if (ownerDocument.isReadOnly())
1399: throw new RuntimeException(READ_ONLY_MESSAGE);
1400:
1401: if (changeType == null)
1402: throw new IllegalArgumentException(
1403: "Null argument: changeType");
1404:
1405: this .changeType = changeType;
1406: }
1407:
1408: public ChangeType getNewChangeType() {
1409: return changeType;
1410: }
1411:
1412: public void setNewChangeComment(String changeComment) {
1413: if (ownerDocument.isReadOnly())
1414: throw new RuntimeException(READ_ONLY_MESSAGE);
1415:
1416: if (changeComment != null) {
1417: changeComment = changeComment.trim();
1418: if (changeComment.length() == 0)
1419: changeComment = null;
1420: }
1421:
1422: this .changeComment = changeComment;
1423: }
1424:
1425: public String getNewChangeComment() {
1426: return changeComment;
1427: }
1428:
1429: /**
1430: * Checks whether this document needs a new version. This is the case when:
1431: */
1432: public boolean needsNewVersion() {
1433: // a new document always needs an initial version
1434: if (isNew)
1435: return true;
1436:
1437: return nameUpdated || fieldChanges || linkChanges
1438: || partChanges;
1439: }
1440:
1441: public boolean needsSaving() {
1442: boolean nonVersionedChanges = documentCollectionChanges
1443: || customFieldChanges || documentTypeChanged
1444: || retiredChanged;
1445: if (nonVersionedChanges)
1446: return true;
1447: else
1448: return needsNewVersion();
1449: }
1450:
1451: public class IntimateAccess {
1452:
1453: private IntimateAccess() {
1454: }
1455:
1456: public void setLockInfo(LockInfoImpl lockInfo) {
1457: DocumentVariantImpl.this .lockInfo = lockInfo;
1458: }
1459:
1460: public long getLastVersionId() {
1461: return lastVersionId;
1462: }
1463:
1464: public boolean hasCustomFieldChanges() {
1465: return customFieldChanges;
1466: }
1467:
1468: public void setIsNew(boolean isNew) {
1469: DocumentVariantImpl.this .isNew = isNew;
1470: }
1471:
1472: public boolean isNameUpdated() {
1473: return nameUpdated;
1474: }
1475:
1476: public boolean hasFieldChanges() {
1477: return fieldChanges;
1478: }
1479:
1480: public boolean hasLinkChanges() {
1481: return linkChanges;
1482: }
1483:
1484: public boolean hasPartChanges() {
1485: return partChanges;
1486: }
1487:
1488: public boolean hasCollectionChanges() {
1489: return documentCollectionChanges;
1490: }
1491:
1492: /**
1493: * Intialises required fields when loading an existing document variant.
1494: */
1495: public void load(long documentTypeId, boolean retired,
1496: long lastVersionId, long liveVersionId,
1497: Date lastModified, long lastModifier,
1498: long createdFromBranchId, long createdFromLanguageId,
1499: long createdFromVersionId,
1500: long lastMajorChangeVersionId,
1501: long liveMajorChangeVersionId, long updateCount) {
1502: DocumentVariantImpl.this .documentTypeId = documentTypeId;
1503: DocumentVariantImpl.this .retired = retired;
1504: DocumentVariantImpl.this .lastVersionId = lastVersionId;
1505: DocumentVariantImpl.this .liveVersionId = liveVersionId;
1506: DocumentVariantImpl.this .lastModified = lastModified;
1507: DocumentVariantImpl.this .lastModifier = lastModifier;
1508: DocumentVariantImpl.this .createdFromBranchId = createdFromBranchId;
1509: DocumentVariantImpl.this .createdFromLanguageId = createdFromLanguageId;
1510: DocumentVariantImpl.this .createdFromVersionId = createdFromVersionId;
1511: DocumentVariantImpl.this .lastMajorChangeVersionId = lastMajorChangeVersionId;
1512: DocumentVariantImpl.this .liveMajorChangeVersionId = liveMajorChangeVersionId;
1513: DocumentVariantImpl.this .updateCount = updateCount;
1514: isNew = false;
1515: }
1516:
1517: /**
1518: * Updates the state of this Document object after saving it, also resets
1519: * all 'dirty' flags.
1520: */
1521: public void saved(long lastVersionId, long liveVersionId,
1522: Date lastModified, String summary, long updateCount) {
1523: DocumentVariantImpl.this .lastVersionId = lastVersionId;
1524: DocumentVariantImpl.this .liveVersionId = liveVersionId;
1525: DocumentVariantImpl.this .lastModified = lastModified;
1526: DocumentVariantImpl.this .lastModifier = currentUser.getId();
1527: DocumentVariantImpl.this .updateCount = updateCount;
1528: DocumentVariantImpl.this .summary = summary;
1529:
1530: nameUpdated = false;
1531: partChanges = false;
1532: linkChanges = false;
1533: documentCollectionChanges = false;
1534:
1535: PartImpl[] parts = getPartImpls();
1536: for (PartImpl part : parts) {
1537: PartImpl.IntimateAccess partInt = part
1538: .getIntimateAccess(documentStrategy);
1539: partInt.setVersionId(lastVersionId);
1540: if (partInt.isDataUpdated())
1541: partInt.setDataChangedInVersion(lastVersionId);
1542: partInt.setNewOrUpdated(false, false);
1543: partInt.setData(null);
1544: }
1545: fieldChanges = false;
1546: customFieldChanges = false;
1547:
1548: // last/live version might have changed
1549: liveVersion = null;
1550: liveVersionLoaded = false;
1551: lastVersion = null;
1552: lastVersionLoaded = false;
1553: isNew = false;
1554: retiredChanged = false;
1555: documentTypeChanged = false;
1556: }
1557:
1558: public DocumentStrategy getDocumentStrategy() {
1559: return documentStrategy;
1560: }
1561:
1562: public AuthenticatedUser getCurrentUser() {
1563: return currentUser;
1564: }
1565:
1566: /**
1567: * Sets a user field without marking the user fields as modified.
1568: */
1569: public void setCustomField(String name, String value) {
1570: customFields.put(name, value);
1571: }
1572:
1573: public PartImpl[] getPartImpls() {
1574: return parts.values().toArray(new PartImpl[0]);
1575: }
1576:
1577: public DocumentCollectionImpl[] getDocumentCollectionImpls() {
1578: return documentCollections.values().toArray(
1579: new DocumentCollectionImpl[0]);
1580: }
1581:
1582: public void addPart(PartImpl part) {
1583: parts.put(new Long(part.getTypeId()), part);
1584: }
1585:
1586: /**
1587: * Adds a link without marking the links as being modified.
1588: */
1589: public void addLink(LinkImpl link) {
1590: links.add(link);
1591: }
1592:
1593: /**
1594: * Sets the name of the document without altering the "name dirty" flag.
1595: */
1596: public void setName(String name) {
1597: if (name == null)
1598: throw new NullPointerException("name may not be null.");
1599: DocumentVariantImpl.this .name = name;
1600: }
1601:
1602: /**
1603: * Adds the given field. This method will not change the flag indicating
1604: * whether there were field changes.
1605: */
1606: public void addField(FieldImpl field) {
1607: fields.put(new Long(field.getTypeId()), field);
1608: }
1609:
1610: public DocumentImpl getDocument() {
1611: return ownerDocument;
1612: }
1613:
1614: public DocId getDocId() {
1615: return ownerDocument.getIntimateAccess(documentStrategy)
1616: .getDocId();
1617: }
1618:
1619: public CommonRepositorySchema getRepositorySchema() {
1620: return repository.getRepositorySchema();
1621: }
1622:
1623: public CommonVariantManager getVariantManager() {
1624: return repository.getVariantManager();
1625: }
1626:
1627: /**
1628: * Adds the specified collection. This method will not change the flag indicating
1629: * whether there were collection changes.
1630: */
1631: public void addCollection(DocumentCollectionImpl collection) {
1632: documentCollections.put(new Long(collection.getId()),
1633: collection);
1634: }
1635:
1636: public Part[] orderParts(Part[] parts) {
1637: return DocumentVariantImpl.this .orderParts(parts);
1638: }
1639:
1640: public Field[] orderFields(Field[] fields) {
1641: return DocumentVariantImpl.this .orderFields(fields);
1642: }
1643:
1644: public void setSummary(String summary) {
1645: DocumentVariantImpl.this .summary = summary;
1646: }
1647:
1648: public DocumentVariantImpl getVariant() {
1649: return DocumentVariantImpl.this ;
1650: }
1651:
1652: public void setCreatedFrom(long branchId, long languageId,
1653: long versionId) {
1654: createdFromBranchId = branchId;
1655: createdFromLanguageId = languageId;
1656: createdFromVersionId = versionId;
1657: }
1658:
1659: public void setStartFrom(long branchId, long languageId) {
1660: startBranchId = branchId;
1661: startLanguageId = languageId;
1662: }
1663:
1664: public long getStartBranchId() {
1665: return startBranchId;
1666: }
1667:
1668: public long getStartLanguageId() {
1669: return startLanguageId;
1670: }
1671: }
1672:
1673: }
|