001: /*
002: * Copyright 2004 Outerthought bvba and Schaubroeck nv
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.outerj.daisy.repository.commonimpl;
017:
018: import java.util.Date;
019: import java.util.GregorianCalendar;
020: import java.util.Map;
021:
022: import org.outerj.daisy.repository.AvailableVariants;
023: import org.outerj.daisy.repository.ChangeType;
024: import org.outerj.daisy.repository.Document;
025: import org.outerj.daisy.repository.DocumentCollection;
026: import org.outerj.daisy.repository.DocumentCollections;
027: import org.outerj.daisy.repository.DocumentTypeInconsistencyException;
028: import org.outerj.daisy.repository.Field;
029: import org.outerj.daisy.repository.FieldNotFoundException;
030: import org.outerj.daisy.repository.Fields;
031: import org.outerj.daisy.repository.Links;
032: import org.outerj.daisy.repository.LockInfo;
033: import org.outerj.daisy.repository.LockType;
034: import org.outerj.daisy.repository.Part;
035: import org.outerj.daisy.repository.PartDataSource;
036: import org.outerj.daisy.repository.PartNotFoundException;
037: import org.outerj.daisy.repository.Parts;
038: import org.outerj.daisy.repository.RepositoryException;
039: import org.outerj.daisy.repository.RepositoryRuntimeException;
040: import org.outerj.daisy.repository.VariantKey;
041: import org.outerj.daisy.repository.Version;
042: import org.outerj.daisy.repository.VersionKey;
043: import org.outerj.daisy.repository.VersionState;
044: import org.outerj.daisy.repository.Versions;
045: import org.outerj.daisy.repository.acl.AccessDetails;
046: import org.outerj.daisy.repository.variant.Language;
047: import org.outerx.daisy.x10.DocumentDocument;
048:
049: /**
050: * Implementation of the Document interface.
051: *
052: * <p>This document implementation depends on a {@link DocumentStrategy} which allows
053: * the persistence logic for the document to be pluggable.
054: *
055: * <p>Please note that all methods in this class that are not present in the Document interface,
056: * including public methods, are considered to be <b>*for internal use only*</b> and hence should
057: * never be called by "end users".
058: */
059: public class DocumentImpl implements Document, DocumentWrapper {
060: private DocumentStrategy documentStrategy;
061: private CommonRepository repository;
062: private AuthenticatedUser currentUser;
063: private DocId docId;
064: /** For new documents (not yet saved), this can contain the document ID under which to save the document. */
065: private DocId requestedDocId;
066: private Date lastModified;
067: private long lastModifier = -1;
068: private Date created;
069: private long owner;
070: private boolean _private = false;
071: private long referenceLanguageId = -1;
072: private boolean readOnly = false;
073: /** Tracks if any properties of the document have been changed (not of the document variant) */
074: private boolean changes = false;
075: private long updateCount = 0;
076: private DocumentVariantImpl variant;
077: private IntimateAccess intimateAccess = new IntimateAccess();
078:
079: public static final String ERROR_ACCESSING_REPOSITORY_SCHEMA = "Error accessing repository schema information.";
080: private static final String READ_ONLY_MESSAGE = "This document object is read-only.";
081:
082: public DocumentImpl(DocumentStrategy documentStrategy,
083: CommonRepository repository, AuthenticatedUser currentUser,
084: long documentTypeId, long branchId, long languageId) {
085: this .documentStrategy = documentStrategy;
086: this .repository = repository;
087: this .currentUser = currentUser;
088: this .variant = new DocumentVariantImpl(this , documentStrategy,
089: repository, currentUser, documentTypeId, branchId,
090: languageId);
091: this .owner = currentUser.getId(); // initialiase in case of a new document, otherwise will be overwritten later
092: }
093:
094: public IntimateAccess getIntimateAccess(
095: DocumentStrategy documentStrategy) {
096: if (this .documentStrategy == documentStrategy)
097: return intimateAccess;
098: else
099: return null;
100: }
101:
102: public DocumentImpl getWrappedDocument(DocumentStrategy strategy) {
103: return this ;
104: }
105:
106: public boolean canReadLiveOnly() {
107: return false;
108: }
109:
110: public String getId() {
111: if (docId != null)
112: return docId.toString();
113: else
114: return null;
115: }
116:
117: public long getSeqId() {
118: if (docId != null)
119: return docId.getSeqId();
120: else
121: return -1;
122: }
123:
124: public String getNamespace() {
125: if (docId != null)
126: return docId.getNamespace();
127: else
128: return null;
129: }
130:
131: public boolean isNew() {
132: return docId == null;
133: }
134:
135: public void setRequestedId(String documentId) {
136: if (!isNew())
137: throw new RepositoryRuntimeException(
138: "A document ID can only be requested for non-new documents.");
139:
140: if (documentId == null) {
141: this .requestedDocId = null;
142: return;
143: }
144:
145: DocId requestedDocId = DocId.parseDocId(documentId, repository);
146: if (requestedDocId.getNamespace().equals(
147: repository.getNamespaceManager()
148: .getRepositoryNamespace())) {
149: throw new RepositoryRuntimeException(
150: "A document ID can only be specified for foreign namespaces, which "
151: + requestedDocId.getNamespace()
152: + " is not.");
153: }
154:
155: this .requestedDocId = requestedDocId;
156: }
157:
158: public String getRequestedId() {
159: if (requestedDocId == null)
160: return null;
161: else
162: return requestedDocId.toString();
163: }
164:
165: public long getBranchId() {
166: return variant.getBranchId();
167: }
168:
169: public long getLanguageId() {
170: return variant.getLanguageId();
171: }
172:
173: public VariantKey getVariantKey() {
174: if (this .docId == null)
175: return null;
176: return new VariantKey(getId(), getBranchId(), getLanguageId());
177: }
178:
179: public boolean isVariantNew() {
180: return variant.isNew();
181: }
182:
183: public AvailableVariants getAvailableVariants()
184: throws RepositoryException {
185: if (this .docId != null) {
186: return repository.getAvailableVariants(docId, currentUser);
187: }
188: return new AvailableVariantsImpl(new AvailableVariantImpl[0]);
189: }
190:
191: public long getDocumentTypeId() {
192: return variant.getDocumentTypeId();
193: }
194:
195: public void changeDocumentType(long documentTypeId)
196: throws RepositoryException {
197: variant.changeDocumentType(documentTypeId);
198: }
199:
200: public void changeDocumentType(String documentTypeName)
201: throws RepositoryException {
202: variant.changeDocumentType(documentTypeName);
203: }
204:
205: public long getOwner() {
206: return owner;
207: }
208:
209: public void setOwner(long userId) {
210: if (readOnly)
211: throw new RuntimeException(READ_ONLY_MESSAGE);
212:
213: if (userId == owner)
214: return;
215:
216: if (!currentUser.isInAdministratorRole()
217: && currentUser.getId() != owner)
218: throw new RepositoryRuntimeException(
219: "The owner of a document can only be changed by the current owner or users acting in the Administrator role.");
220:
221: this .owner = userId;
222: this .changes = true;
223: }
224:
225: public boolean isPrivate() {
226: return _private;
227: }
228:
229: public void setPrivate(boolean _private) {
230: if (readOnly)
231: throw new RuntimeException(READ_ONLY_MESSAGE);
232:
233: this ._private = _private;
234: this .changes = true;
235: }
236:
237: public Field getField(String name) throws FieldNotFoundException {
238: return variant.getField(name);
239: }
240:
241: public Field getField(long fieldTypeId)
242: throws FieldNotFoundException {
243: return variant.getField(fieldTypeId);
244: }
245:
246: public boolean hasField(long fieldTypeId) {
247: return variant.hasField(fieldTypeId);
248: }
249:
250: public boolean hasField(String fieldTypeName) {
251: return variant.hasField(fieldTypeName);
252: }
253:
254: public Fields getFields() {
255: return variant.getFields();
256: }
257:
258: public Fields getFieldsInOrder() {
259: return variant.getFieldsInOrder();
260: }
261:
262: public void setField(String name, Object value)
263: throws DocumentTypeInconsistencyException {
264: variant.setField(name, value);
265: }
266:
267: public void setField(long fieldTypeId, Object value)
268: throws DocumentTypeInconsistencyException {
269: variant.setField(fieldTypeId, value);
270: }
271:
272: public void deleteField(String name) {
273: variant.deleteField(name);
274: }
275:
276: public void deleteField(long fieldTypeId) {
277: variant.deleteField(fieldTypeId);
278: }
279:
280: public LockInfo getLockInfo(boolean fresh)
281: throws RepositoryException {
282: return variant.getLockInfo(fresh);
283: }
284:
285: // This method is by purpose not in the Document interface. It is used to clear lock
286: // info in cached document objects. If it would be in the interface, then the method
287: // getLockInfo should cover the case where lockInfo is null and id is -1.
288: public void clearLockInfo() {
289: variant.clearLockInfo();
290: }
291:
292: public boolean lock(long duration, LockType lockType)
293: throws RepositoryException {
294: return variant.lock(duration, lockType);
295: }
296:
297: public boolean releaseLock() throws RepositoryException {
298: return variant.releaseLock();
299: }
300:
301: public DocumentDocument getXml() throws RepositoryException {
302: return getXml(null);
303: }
304:
305: public DocumentDocument getXml(AccessDetails accessDetails)
306: throws RepositoryException {
307: DocumentDocument documentDocument = getXmlWithoutVariant();
308: DocumentDocument.Document documentXml = documentDocument
309: .getDocument();
310: variant.addXml(documentXml, accessDetails);
311: return documentDocument;
312: }
313:
314: public DocumentDocument getXml(long versionId)
315: throws RepositoryException {
316: return getXml(versionId, null);
317: }
318:
319: public DocumentDocument getXml(long versionId,
320: AccessDetails accessDetails) throws RepositoryException {
321: DocumentDocument documentDocument = getXmlWithoutVariant();
322: DocumentDocument.Document documentXml = documentDocument
323: .getDocument();
324: variant.addXml(documentXml, versionId, accessDetails);
325: return documentDocument;
326: }
327:
328: public DocumentDocument getXmlWithoutVersionedData(
329: AccessDetails accessDetails) throws RepositoryException {
330: DocumentDocument documentDocument = getXmlWithoutVariant();
331: DocumentDocument.Document documentXml = documentDocument
332: .getDocument();
333: variant.addNonVersionedDataToXml(documentXml, accessDetails);
334: return documentDocument;
335: }
336:
337: public DocumentDocument getXmlWithoutVersionedData()
338: throws RepositoryException {
339: return getXmlWithoutVersionedData(null);
340: }
341:
342: public DocumentDocument getXmlWithoutVariant() {
343: DocumentDocument documentDocument = DocumentDocument.Factory
344: .newInstance();
345: DocumentDocument.Document documentXml = documentDocument
346: .addNewDocument();
347:
348: if (docId != null) {
349: documentXml.setId(docId.toString());
350: GregorianCalendar lastModified = new GregorianCalendar();
351: lastModified.setTime(this .lastModified);
352: documentXml.setLastModified(lastModified);
353: documentXml.setLastModifier(lastModifier);
354: GregorianCalendar created = new GregorianCalendar();
355: created.setTime(this .created);
356: documentXml.setCreated(created);
357: } else if (requestedDocId != null) {
358: documentXml.setRequestedId(requestedDocId.toString());
359: }
360:
361: documentXml.setOwner(owner);
362: documentXml.setPrivate(_private);
363: documentXml.setUpdateCount(updateCount);
364: documentXml.setReferenceLanguageId(getReferenceLanguageId());
365:
366: return documentDocument;
367: }
368:
369: public void setName(String name) {
370: variant.setName(name);
371: }
372:
373: public String getName() {
374: return variant.getName();
375: }
376:
377: public String getDocumentName() {
378: return getName();
379: }
380:
381: public void setPart(String partTypeName, String mimeType,
382: byte[] data) throws DocumentTypeInconsistencyException {
383: variant.setPart(partTypeName, mimeType, data);
384: }
385:
386: public void setPart(long partTypeId, String mimeType, byte[] data)
387: throws DocumentTypeInconsistencyException {
388: variant.setPart(partTypeId, mimeType, data);
389: }
390:
391: public void setPart(String partTypeName, String mimeType,
392: PartDataSource partDataSource)
393: throws DocumentTypeInconsistencyException {
394: variant.setPart(partTypeName, mimeType, partDataSource);
395: }
396:
397: public void setPart(long partTypeId, String mimeType,
398: PartDataSource partDataSource)
399: throws DocumentTypeInconsistencyException {
400: variant.setPart(partTypeId, mimeType, partDataSource);
401: }
402:
403: public void setPartFileName(String partTypeName, String fileName) {
404: variant.setPartFileName(partTypeName, fileName);
405: }
406:
407: public void setPartFileName(long partTypeId, String fileName) {
408: variant.setPartFileName(partTypeId, fileName);
409: }
410:
411: public void setPartMimeType(String partTypeName, String mimeType) {
412: variant.setPartMimeType(partTypeName, mimeType);
413: }
414:
415: public void setPartMimeType(long partTypeId, String mimeType) {
416: variant.setPartMimeType(partTypeId, mimeType);
417: }
418:
419: public Parts getParts() {
420: return variant.getParts();
421: }
422:
423: public Parts getPartsInOrder() {
424: return variant.getPartsInOrder();
425: }
426:
427: public void deletePart(long partTypeId) {
428: variant.deletePart(partTypeId);
429: }
430:
431: public void deletePart(String name) {
432: variant.deletePart(name);
433: }
434:
435: public Part getPart(long partTypeId) throws PartNotFoundException {
436: return variant.getPart(partTypeId);
437: }
438:
439: public Part getPart(String name) throws PartNotFoundException {
440: return variant.getPart(name);
441: }
442:
443: public boolean hasPart(long partTypeId) {
444: return variant.hasPart(partTypeId);
445: }
446:
447: public boolean hasPart(String name) {
448: return variant.hasPart(name);
449: }
450:
451: public void setCustomField(String name, String value) {
452: variant.setCustomField(name, value);
453: }
454:
455: public void deleteCustomField(String name) {
456: variant.deleteCustomField(name);
457: }
458:
459: public void clearCustomFields() {
460: variant.clearCustomFields();
461: }
462:
463: public void clearCollections() {
464: variant.clearCollections();
465: }
466:
467: public Map<String, String> getCustomFields() {
468: return variant.getCustomFields();
469: }
470:
471: public String getCustomField(String name) {
472: return variant.getCustomField(name);
473: }
474:
475: public boolean hasCustomField(String name) {
476: return variant.hasCustomField(name);
477: }
478:
479: public Links getLinks() {
480: return variant.getLinks();
481: }
482:
483: public void addLink(String title, String target) {
484: variant.addLink(title, target);
485: }
486:
487: public void deleteLink(int index) {
488: variant.deleteLink(index);
489: }
490:
491: public void clearLinks() {
492: variant.clearLinks();
493: }
494:
495: public void save() throws RepositoryException {
496: save(true);
497: }
498:
499: public void save(boolean validate) throws RepositoryException {
500: if (readOnly)
501: throw new RuntimeException(READ_ONLY_MESSAGE);
502:
503: // first check if the document needs saving at all
504: if (!isNew() && !needsSaving() && !variant.isNew()
505: && !variant.needsSaving())
506: return;
507:
508: if (validate)
509: validate();
510:
511: variant.setValidateOnSave(validate);
512: documentStrategy.store(this );
513: }
514:
515: public void validate() throws DocumentTypeInconsistencyException {
516: variant.validate();
517: }
518:
519: public void setNewVersionState(VersionState versionState) {
520: variant.setNewVersionState(versionState);
521: }
522:
523: public VersionState getNewVersionState() {
524: return variant.getNewVersionState();
525: }
526:
527: public Version getVersion(long versionId)
528: throws RepositoryException {
529: return variant.getVersion(versionId);
530: }
531:
532: public Version getLastVersion() throws RepositoryException {
533: return variant.getLastVersion();
534: }
535:
536: public Version getLiveVersion() throws RepositoryException {
537: return variant.getLiveVersion();
538: }
539:
540: public long getLiveVersionId() {
541: return variant.getLiveVersionId();
542: }
543:
544: public Versions getVersions() throws RepositoryException {
545: return variant.getVersions();
546: }
547:
548: public long getLastVersionId() {
549: return variant.getLastVersionId();
550: }
551:
552: public Date getLastModified() {
553: if (lastModified != null)
554: return (Date) lastModified.clone();
555: return lastModified;
556: }
557:
558: public long getLastModifier() {
559: return lastModifier;
560: }
561:
562: public Date getVariantLastModified() {
563: return variant.getLastModified();
564: }
565:
566: public long getVariantLastModifier() {
567: return variant.getLastModifier();
568: }
569:
570: public Date getCreated() {
571: return created;
572: }
573:
574: public boolean isRetired() {
575: return variant.isRetired();
576: }
577:
578: public void setRetired(boolean retired) {
579: variant.setRetired(retired);
580: }
581:
582: public DocumentCollections getCollections() {
583: return variant.getCollections();
584: }
585:
586: public boolean inCollection(DocumentCollection collection) {
587: return variant.inCollection(collection);
588: }
589:
590: public boolean inCollection(long collectionId) {
591: return variant.inCollection(collectionId);
592: }
593:
594: public void addToCollection(DocumentCollection collection) {
595: variant.addToCollection(collection);
596: }
597:
598: public void removeFromCollection(DocumentCollection collection) {
599: variant.removeFromCollection(collection);
600: }
601:
602: public String getSummary() {
603: return variant.getSummary();
604: }
605:
606: public long getVariantCreatedFromBranchId() {
607: return variant.getCreatedFromBranchId();
608: }
609:
610: public long getVariantCreatedFromLanguageId() {
611: return variant.getCreatedFromLanguageId();
612: }
613:
614: public long getVariantCreatedFromVersionId() {
615: return variant.getCreatedFromVersionId();
616: }
617:
618: public void setDocumentTypeChecksEnabled(
619: boolean documentTypeChecksEnabled) {
620: variant.setDocumentTypeChecksEnabled(documentTypeChecksEnabled);
621: }
622:
623: public long getUpdateCount() {
624: return updateCount;
625: }
626:
627: public long getVariantUpdateCount() {
628: return variant.getUpdateCount();
629: }
630:
631: public long getReferenceLanguageId() {
632: return referenceLanguageId;
633: }
634:
635: public void setReferenceLanguage(String referenceLanguageName)
636: throws RepositoryException {
637: if (readOnly)
638: throw new RuntimeException(READ_ONLY_MESSAGE);
639:
640: long referenceLanguageId = repository.getVariantManager()
641: .getLanguage(referenceLanguageName, false, currentUser)
642: .getId();
643: if (this .referenceLanguageId != referenceLanguageId) {
644: this .changes = true;
645: }
646: this .referenceLanguageId = referenceLanguageId;
647: }
648:
649: public void setReferenceLanguageId(long referenceLanguageId) {
650: if (readOnly)
651: throw new RuntimeException(READ_ONLY_MESSAGE);
652:
653: if (this .referenceLanguageId != referenceLanguageId) {
654: this .changes = true;
655: }
656: this .referenceLanguageId = referenceLanguageId;
657: }
658:
659: public long getLastMajorChangeVersionId() {
660: return variant.getLastMajorChangeVersionId();
661: }
662:
663: public long getLiveMajorChangeVersionId() {
664: return variant.getLiveMajorChangeVersionId();
665: }
666:
667: public VersionKey getNewSyncedWith() {
668: return variant.getNewSyncedWith();
669: }
670:
671: public void setNewSyncedWith(long languageId, long versionId)
672: throws RepositoryException {
673: variant.setNewSyncedWithVersion(languageId, versionId);
674: }
675:
676: public void setNewSyncedWith(String languageName, long versionId)
677: throws RepositoryException {
678: if ((languageName == null && versionId != -1)
679: || (languageName != null && versionId == -1))
680: throw new IllegalArgumentException(
681: "If languageName is null or versionId is -1, both should be set this way.");
682:
683: if (languageName == null) {
684: variant.setNewSyncedWithVersion(-1, -1);
685: } else {
686: Language lang = repository
687: .getVariantManager()
688: .getLanguageByName(languageName, false, currentUser);
689: variant.setNewSyncedWithVersion(lang.getId(), versionId);
690: }
691: }
692:
693: public void setNewSyncedWith(VersionKey syncedWith)
694: throws RepositoryException {
695: if (syncedWith == null)
696: variant.setNewSyncedWithVersion(-1, -1);
697: else
698: variant.setNewSyncedWithVersion(syncedWith.getLanguageId(),
699: syncedWith.getVersionId());
700: }
701:
702: public void setNewChangeType(ChangeType changeType) {
703: variant.setNewChangeType(changeType);
704: }
705:
706: public ChangeType getNewChangeType() {
707: return variant.getNewChangeType();
708: }
709:
710: public void setNewChangeComment(String changeComment) {
711: variant.setNewChangeComment(changeComment);
712: }
713:
714: public String getNewChangeComment() {
715: return variant.getNewChangeComment();
716: }
717:
718: public void makeReadOnly() {
719: this .readOnly = true;
720: }
721:
722: public boolean isReadOnly() {
723: return this .readOnly;
724: }
725:
726: public boolean needsSaving() {
727: return changes;
728: }
729:
730: public String toString() {
731: return getVariantKey().toString();
732: }
733:
734: public class IntimateAccess {
735:
736: private IntimateAccess() {
737: }
738:
739: public DocId getDocId() {
740: return DocumentImpl.this .docId;
741: }
742:
743: public DocId getRequestedDocId() {
744: return DocumentImpl.this .requestedDocId;
745: }
746:
747: /**
748: * Updates the state of this Document object after saving it, also resets
749: * all 'dirty' flags.
750: *
751: * @param created can be null if not modified (only required after first save)
752: */
753: public void saved(DocId docId, Date lastModified, Date created,
754: long updateCount) {
755: DocumentImpl.this .docId = docId;
756: DocumentImpl.this .requestedDocId = null;
757: DocumentImpl.this .lastModified = lastModified;
758: DocumentImpl.this .lastModifier = currentUser.getId();
759: DocumentImpl.this .updateCount = updateCount;
760: DocumentImpl.this .created = created;
761: DocumentImpl.this .changes = false;
762: }
763:
764: public void setCreated(Date created) {
765: DocumentImpl.this .created = created;
766: }
767:
768: public AuthenticatedUser getCurrentUser() {
769: return currentUser;
770: }
771:
772: public DocumentImpl getDocument() {
773: return DocumentImpl.this ;
774: }
775:
776: public DocumentVariantImpl getVariant() {
777: return variant;
778: }
779:
780: /**
781: * Intialises the document object as when loading an existing document.
782: */
783: public void load(DocId docId, Date lastModified,
784: long lastModifier, Date created, long owner,
785: boolean _private, long updateCount,
786: long referenceLanguageId) {
787: DocumentImpl.this.docId = docId;
788: DocumentImpl.this.lastModified = lastModified;
789: DocumentImpl.this.lastModifier = lastModifier;
790: DocumentImpl.this.created = created;
791: DocumentImpl.this.owner = owner;
792: DocumentImpl.this._private = _private;
793: DocumentImpl.this.updateCount = updateCount;
794: DocumentImpl.this.referenceLanguageId = referenceLanguageId;
795: }
796: }
797:
798: }
|