View Javadoc

1   /**
2    * This file Copyright (c) 2003-2010 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.cms.core.version;
35  
36  import info.magnolia.cms.core.AbstractContent;
37  import info.magnolia.cms.core.Content;
38  import info.magnolia.cms.core.HierarchyManager;
39  import info.magnolia.cms.core.ItemType;
40  import info.magnolia.cms.core.NodeData;
41  import info.magnolia.cms.core.DefaultContent;
42  import info.magnolia.cms.security.AccessDeniedException;
43  import info.magnolia.cms.security.AccessManager;
44  import info.magnolia.cms.security.AccessManagerImpl;
45  import info.magnolia.cms.security.Permission;
46  import info.magnolia.cms.security.PermissionImpl;
47  import info.magnolia.cms.util.ContentWrapper;
48  import info.magnolia.cms.util.HierarchyManagerWrapper;
49  import info.magnolia.cms.util.NodeDataWrapper;
50  import info.magnolia.cms.util.Rule;
51  import info.magnolia.cms.util.SimpleUrlPattern;
52  
53  import java.util.ArrayList;
54  import java.util.Calendar;
55  import java.util.Collection;
56  import java.util.Comparator;
57  import java.util.List;
58  
59  import javax.jcr.PathNotFoundException;
60  import javax.jcr.PropertyType;
61  import javax.jcr.RepositoryException;
62  import javax.jcr.Value;
63  import javax.jcr.Workspace;
64  import javax.jcr.lock.Lock;
65  import javax.jcr.lock.LockException;
66  import javax.jcr.nodetype.NodeType;
67  import javax.jcr.nodetype.NodeTypeManager;
68  import javax.jcr.version.Version;
69  import javax.jcr.version.VersionHistory;
70  import javax.jcr.version.VersionIterator;
71  
72  import org.apache.commons.lang.StringUtils;
73  import org.slf4j.Logger;
74  import org.slf4j.LoggerFactory;
75  
76  
77  /**
78   * Wraps a versioned node (frozen node) and allows traversing the hierarchy as if the node where in the original place.
79   * @author Sameer Charles
80   * $Id: ContentVersion.java 38823 2010-11-05 08:54:44Z had $
81   */
82  public class ContentVersion extends DefaultContent {
83  
84      /**
85       * Makes sure that the handle hides the fact that the nodes live in the version store.
86       * @version $Id: ContentVersion.java 38823 2010-11-05 08:54:44Z had $
87       *
88       */
89      private final class ContentVersionChildWrapper extends ContentWrapper {
90          private final Content parent;
91  
92          private ContentVersionChildWrapper(Content wrappedContent, Content parent) {
93              super(wrappedContent);
94              this.parent = parent;
95          }
96  
97          @Override
98          public Content getParent() throws RepositoryException {
99              return parent;
100         }
101 
102         /**
103          * Show the original path not the one from the version store.
104          */
105         @Override
106         public String getHandle() {
107             try {
108                 return getParent().getHandle() + "/" + getName();
109             }
110             catch (RepositoryException e) {
111                 throw new RuntimeException("Can't create handle for versioned node.", e);
112             }
113         }
114 
115         /**
116          * We have to wrap the node data to make sure that the handle is correct.
117          */
118         @Override
119         public NodeData newNodeDataInstance(String name, int type, boolean createIfNotExisting) throws AccessDeniedException, RepositoryException {
120             return new NodeDataWrapper(super.newNodeDataInstance(name, type, createIfNotExisting)) {
121                 @Override
122                 public String getHandle() {
123                     return ContentVersionChildWrapper.this.getHandle() + "/" + getName();
124                 }
125             };
126         }
127 
128         @Override
129         protected Content wrap(Content node) {
130             return new ContentVersionChildWrapper(node, this);
131         }
132     }
133 
134     private static Logger log = LoggerFactory.getLogger(ContentVersion.class);
135 
136     /**
137      * User who created this version.
138      */
139     public static final String VERSION_USER = "versionUser"; //$NON-NLS-1$
140 
141     /**
142      * Name of the base node.
143      */
144     public static final String NAME = "name";
145 
146     /**
147      * Version node (nt:version).
148      */
149     private final Version state;
150 
151     /**
152      * The node as existing in the workspace. Not the version node.
153      */
154     private final AbstractContent base;
155 
156     /**
157      * Rule used to create this version.
158      */
159     private Rule rule;
160 
161     public ContentVersion(Version thisVersion, AbstractContent base) throws RepositoryException {
162         if (thisVersion == null) {
163             throw new RepositoryException("Failed to get ContentVersion, version does not exist");
164         }
165         this.state = thisVersion;
166         this.base = base;
167 
168         this.hierarchyManager = new HierarchyManagerWrapper(base.getHierarchyManager()) {
169             private AccessManagerImpl accessManager;
170 
171             {
172                 // child nodes (and metaData if nothing else) depends on this to have access when root access is restricted for given user
173                 List<Permission> permissions = new ArrayList<Permission>(getWrappedHierarchyManager().getAccessManager().getPermissionList());
174                 PermissionImpl p = new PermissionImpl();
175                 p.setPattern(new SimpleUrlPattern("/jcr:system/jcr:versionStorage/*"));
176                 // read only
177                 p.setPermissions(8);
178                 permissions.add(p);
179                 // use dedicated AM and not the one base share with its parent
180                 accessManager = new AccessManagerImpl();
181                 accessManager.setPermissionList(permissions);
182             }
183 
184             @Override
185             public AccessManager getAccessManager() {
186                 return accessManager;
187             }
188         };
189         this.init();
190     }
191 
192     /**
193      * Set frozen node of this version as working node.
194      * @throws RepositoryException
195      */
196     private void init() throws RepositoryException {
197         this.setNode(this.state.getNode(ItemType.JCR_FROZENNODE));
198         try {
199             if (!StringUtils.equalsIgnoreCase(this.state.getName(), VersionManager.ROOT_VERSION)) {
200                 this.rule = VersionManager.getInstance().getUsedFilter(this);
201             }
202         }
203         catch (Exception e) {
204             log.error(e.getMessage(), e);
205         }
206         if (this.rule == null) {
207             log.info("failed to get filter used for creating this version, use open filter");
208             this.rule = new Rule();
209         }
210     }
211 
212     /**
213      * Get creation date of this version.
214      * @throws RepositoryException
215      * @return creation date as calendar
216      */
217     public Calendar getCreated() throws RepositoryException {
218         return this.state.getCreated();
219     }
220 
221     /**
222      * Return the name of the version represented by this object.
223      * @return the versions name
224      * @throws RepositoryException
225      */
226     public String getVersionLabel() throws RepositoryException {
227         return this.state.getName();
228     }
229 
230     /**
231      * Get containing version history.
232      * @throws RepositoryException
233      * @return version history associated to this version
234      */
235     public VersionHistory getContainingHistory() throws RepositoryException {
236         return this.state.getContainingHistory();
237     }
238 
239     /**
240      * The original name of the node.
241      */
242     @Override
243     public String getName() {
244         try {
245             return VersionManager.getInstance().getSystemNode(this).getNodeData(NAME).getString();
246         }
247         catch (RepositoryException re) {
248             log.error("Failed to retrieve name from version system node", re);
249             return "";
250         }
251     }
252 
253     /**
254      * The name of the user who created this version.
255      */
256     public String getUserName() {
257         try {
258             return VersionManager.getInstance().getSystemNode(this).getNodeData(VERSION_USER).getString();
259         }
260         catch (RepositoryException re) {
261             log.error("Failed to retrieve user from version system node", re);
262             return "";
263         }
264     }
265 
266     /**
267      * Get original path of this versioned content.
268      */
269     @Override
270     public String getHandle() {
271         return this.base.getHandle();
272     }
273 
274     /**
275      * Returns a direct child if it was included in the version. Otherwise it tries to get the child from the original place.
276      * The versioning rule is respected.
277      */
278     @Override
279     public Content getContent(String name) throws PathNotFoundException, RepositoryException, AccessDeniedException {
280         //first we have to check if this is a direct child
281         if(super.hasContent(name)){
282             return new ContentVersionChildWrapper(super.getContent(name), this);
283         }
284         else{
285             Content content = base.getContent(name);
286             // only return the node if it was excluded from the versioning, otherwise the node is new
287             if(!rule.isAllowed(content.getNodeTypeName())){
288                 return content;
289             }
290             else{
291                 throw new PathNotFoundException(base.getHandle() + "/" + name);
292             }
293         }
294     }
295 
296     /**
297      * Uses the same approach as {@link #getContent(String)}.
298      */
299     @Override
300     public boolean hasContent(String name) throws RepositoryException {
301         if(super.hasContent(name)){
302             return true;
303         }
304         else if(base.hasContent(name)){
305             Content content = base.getContent(name);
306             // only return the node if it was excluded from the versioning, otherwise the node is new
307             if(!rule.isAllowed(content.getNodeTypeName())){
308                 return true;
309             }
310         }
311         return false;
312     }
313 
314     /**
315      * Throws an {@link AccessDeniedException} as versions are read only.
316      */
317     @Override
318     public Content createContent(String name) throws AccessDeniedException {
319         throw new AccessDeniedException("Not allowed to write on version preview");
320     }
321 
322     /**
323      * Throws an {@link AccessDeniedException} as versions are read only.
324      */
325     @Override
326     public Content createContent(String name, String contentType) throws AccessDeniedException {
327         throw new AccessDeniedException("Not allowed to write on version preview");
328     }
329 
330     /**
331      * Throws an {@link AccessDeniedException} as versions are read only.
332      */
333     @Override
334     public Content createContent(String name, ItemType contentType) throws AccessDeniedException {
335         throw new AccessDeniedException("Not allowed to write on version preview");
336     }
337 
338     /**
339      * Throws an {@link AccessDeniedException} as versions are read only.
340      */
341     @Override
342     public NodeData createNodeData(String name) throws AccessDeniedException {
343         throw new AccessDeniedException("Not allowed to write on version preview");
344     }
345 
346     /**
347      * Throws an {@link AccessDeniedException} as versions are read only.
348      */
349     public NodeData createNodeData(String name, Value value, int type) throws AccessDeniedException {
350         throw new AccessDeniedException("Not allowed to write on version preview");
351     }
352 
353     /**
354      * Throws an {@link AccessDeniedException} as versions are read only.
355      */
356     @Override
357     public NodeData createNodeData(String name, Value value) throws AccessDeniedException {
358         throw new AccessDeniedException("Not allowed to write on version preview");
359     }
360 
361     /**
362      *  Throws an {@link AccessDeniedException} as versions are read only.
363      */
364     @Override
365     public NodeData createNodeData(String name, int type) throws AccessDeniedException {
366         throw new AccessDeniedException("Not allowed to write on version preview");
367     }
368 
369     /**
370      * Throws an {@link AccessDeniedException} as versions are read only.
371      */
372     @Override
373     public void deleteNodeData(String name) throws RepositoryException {
374         throw new AccessDeniedException("Not allowed to write on version preview");
375     }
376 
377     /**
378      * Throws an {@link AccessDeniedException} as versions are read only.
379      */
380     @Override
381     public void updateMetaData() throws AccessDeniedException {
382         throw new AccessDeniedException("Not allowed to write on version preview");
383     }
384 
385     /**
386      * All {@link #getChildren()} methods delegate to this method. We combine the direct children and children
387      * from the current node which were not included by the version rule.
388      */
389     @Override
390     public Collection<Content> getChildren(ContentFilter filter, String namePattern, Comparator<Content> orderCriteria) {
391         ArrayList<Content> result = new ArrayList<Content>();
392         result.addAll(wrap(super.getChildren(filter, namePattern, orderCriteria)));
393 
394         Collection<Content> transientChildren = this.base.getChildren(filter, namePattern, orderCriteria);
395         for (Content transientChild : transientChildren) {
396             try {
397                 if(!rule.isAllowed(transientChild.getNodeTypeName())){
398                     result.add(transientChild);
399                 }
400             }
401             catch (RepositoryException e) {
402                 throw new RuntimeException("Can't determine node type of " + transientChild, e);
403             }
404         }
405 
406         return result;
407     }
408 
409     private Collection<Content> wrap(Collection<Content> children) {
410         List<Content> transformed = new ArrayList<Content>();
411         for (Content child : children) {
412             transformed.add(new ContentVersionChildWrapper(child, this));
413         }
414         return transformed;
415     }
416 
417     /**
418      * @return Boolean, if sub node(s) exists
419      */
420     @Override
421     public boolean hasChildren() {
422         return (this.getChildren().size() > 0);
423     }
424 
425     /**
426      * @param contentType JCR node type as configured
427      * @return Boolean, if sub <code>collectionType</code> exists
428      */
429     @Override
430     public boolean hasChildren(String contentType) {
431         return (this.getChildren(contentType).size() > 0);
432     }
433 
434     /**
435      * Returns the parent of the base node.
436      */
437     @Override
438     public Content getParent() throws PathNotFoundException, RepositoryException, AccessDeniedException {
439         return this.base.getParent();
440     }
441 
442     @Override
443     public Content getAncestor(int level) throws PathNotFoundException, RepositoryException, AccessDeniedException {
444         return this.base.getAncestor(level);
445     }
446 
447     /**
448      * Convenience method for taglib.
449      * @return Content representing node on level 0
450      * @throws javax.jcr.RepositoryException if an error occurs
451      */
452     @Override
453     public Collection<Content> getAncestors() throws PathNotFoundException, RepositoryException {
454         return this.base.getAncestors();
455     }
456 
457     /**
458      * Get node level from the ROOT node : FIXME implement getDepth in javax.jcr.
459      * @return level at which current node exist, relative to the ROOT node
460      * @throws javax.jcr.PathNotFoundException
461      * @throws javax.jcr.RepositoryException if an error occurs
462      */
463     @Override
464     public int getLevel() throws PathNotFoundException, RepositoryException {
465         return this.base.getLevel();
466     }
467 
468     /**
469      * Throws an {@link AccessDeniedException} as versions are read only.
470      */
471     @Override
472     public void orderBefore(String srcName, String beforeName) throws RepositoryException {
473         throw new AccessDeniedException("Not allowed to write on version preview");
474     }
475 
476     /**
477      * This method returns the index of this node within the ordered set of its same-name sibling nodes. This index is
478      * the one used to address same-name siblings using the square-bracket notation, e.g., /a[3]/b[4]. Note that the
479      * index always starts at 1 (not 0), for compatibility with XPath. As a result, for nodes that do not have
480      * same-name-siblings, this method will always return 1.
481      * @return The index of this node within the ordered set of its same-name sibling nodes.
482      * @throws javax.jcr.RepositoryException if an error occurs
483      */
484     @Override
485     public int getIndex() throws RepositoryException {
486         return this.base.getIndex();
487     }
488 
489     /**
490      * Returns primary node type definition of the associated Node of this object.
491      * @throws RepositoryException if an error occurs
492      */
493     @Override
494     public NodeType getNodeType() throws RepositoryException {
495         log.warn("This is a Version node, it will always return NT_FROZEN as node type.");
496         log.warn("Use getNodeTypeName to retrieve base node primary type");
497         return super.getNodeType();
498     }
499 
500     /**
501      * Throws an {@link AccessDeniedException} as versions are read only.
502      */
503     @Override
504     public void restore(String versionName, boolean removeExisting) throws RepositoryException {
505         throw new AccessDeniedException("Not allowed to write on version preview");
506     }
507 
508     /**
509      * Throws an {@link AccessDeniedException} as versions are read only.
510      */
511     @Override
512     public void restore(Version version, boolean removeExisting) throws RepositoryException {
513         throw new AccessDeniedException("Not allowed to write on version preview");
514     }
515 
516     /**
517      * Throws an {@link AccessDeniedException} as versions are read only.
518      */
519     @Override
520     public void restore(Version version, String relPath, boolean removeExisting) throws RepositoryException {
521         throw new AccessDeniedException("Not allowed to write on version preview");
522     }
523 
524     /**
525      * Throws an {@link AccessDeniedException} as versions are read only.
526      */
527     @Override
528     public void restoreByLabel(String versionLabel, boolean removeExisting) throws RepositoryException {
529         throw new AccessDeniedException("Not allowed to write on version preview");
530     }
531 
532     /**
533      * Throws an {@link AccessDeniedException} as versions are read only.
534      */
535     @Override
536     public Version addVersion() throws RepositoryException {
537         throw new AccessDeniedException("Not allowed to add version on version preview");
538     }
539 
540     /**
541      * Throws an {@link AccessDeniedException} as versions are read only.
542      */
543     @Override
544     public Version addVersion(Rule rule) throws RepositoryException {
545         throw new AccessDeniedException("Not allowed to add version on version preview");
546     }
547 
548     /**
549      * Returns always false as verions are read only.
550      */
551     @Override
552     public boolean isModified() {
553         log.error("Not valid for version");
554         return false;
555     }
556 
557     /**
558      * Throws an {@link AccessDeniedException} as versions are read only.
559      */
560     @Override
561     public VersionHistory getVersionHistory() throws RepositoryException {
562         throw new AccessDeniedException("Not allowed to read VersionHistory of Version");
563     }
564 
565     /**
566      * Throws an {@link AccessDeniedException} as versions are read only.
567      */
568     @Override
569     public VersionIterator getAllVersions() throws RepositoryException {
570         throw new AccessDeniedException("Not allowed to get VersionIterator of Version");
571     }
572 
573     /**
574      * Throws an {@link AccessDeniedException} as versions are read only.
575      */
576     @Override
577     public ContentVersion getBaseVersion() throws RepositoryException {
578         throw new AccessDeniedException("Not allowed to get base version of Version");
579     }
580 
581     /**
582      * Throws an {@link AccessDeniedException} as versions are read only.
583      */
584     @Override
585     public ContentVersion getVersionedContent(Version version) throws RepositoryException {
586         throw new AccessDeniedException("Not allowed to get preview of Version itself");
587     }
588 
589     /**
590      * Throws an {@link AccessDeniedException} as versions are read only.
591      */
592     @Override
593     public ContentVersion getVersionedContent(String versionName) throws RepositoryException {
594         throw new AccessDeniedException("Not allowed to get preview of Version itself");
595     }
596 
597     /**
598      * Throws an {@link AccessDeniedException} as versions are read only.
599      */
600     @Override
601     public void save() throws RepositoryException {
602         throw new AccessDeniedException("Not allowed to write on version preview");
603     }
604 
605     /**
606      * Checks for the allowed access rights.
607      * @param permissions as defined in javax.jcr.Permission
608      * @return true is the current user has specified access on this node.
609      */
610     @Override
611     public boolean isGranted(long permissions) {
612         return (permissions & Permission.READ) == permissions;
613     }
614 
615     /**
616      * Throws an {@link AccessDeniedException} as versions are read only.
617      */
618     @Override
619     public void delete() throws RepositoryException {
620         throw new AccessDeniedException("Not allowed to write on version preview");
621     }
622 
623     /**
624      * Throws an {@link AccessDeniedException} as versions are read only.
625      */
626     @Override
627     public void delete(String path) throws RepositoryException {
628         throw new AccessDeniedException("Not allowed to write on version preview");
629     }
630 
631     /**
632      * UUID of the node refrenced by this object.
633      * @return uuid
634      */
635     @Override
636     public String getUUID() {
637         return this.base.getUUID();
638     }
639 
640     /**
641      * Throws an {@link AccessDeniedException} as versions are read only.
642      */
643     @Override
644     public void addMixin(String type) throws RepositoryException {
645         throw new AccessDeniedException("Not allowed to write on version preview");
646     }
647 
648     /**
649      * Throws an {@link AccessDeniedException} as versions are read only.
650      */
651     @Override
652     public void removeMixin(String type) throws RepositoryException {
653         throw new AccessDeniedException("Not allowed to write on version preview");
654     }
655 
656     /**
657      * Throws an {@link AccessDeniedException} as versions are read only.
658      */
659     @Override
660     public Lock lock(boolean isDeep, boolean isSessionScoped) throws LockException, RepositoryException {
661         throw new AccessDeniedException("Lock not supported on version preview");
662     }
663 
664     /**
665      * Throws an {@link AccessDeniedException} as versions are read only.
666      */
667     @Override
668     public Lock lock(boolean isDeep, boolean isSessionScoped, long yieldFor) throws LockException, RepositoryException {
669         throw new AccessDeniedException("Lock not supported on version preview");
670     }
671 
672     /**
673      * Throws an {@link AccessDeniedException} as versions are read only.
674      */
675     @Override
676     public Lock getLock() throws LockException, RepositoryException {
677         throw new AccessDeniedException("Lock not supported on version preview");
678     }
679 
680     /**
681      * Throws an {@link AccessDeniedException} as versions are read only.
682      */
683     @Override
684     public void unlock() throws LockException, RepositoryException {
685         throw new AccessDeniedException("Lock not supported on version preview");
686     }
687 
688     /**
689      * Throws an {@link AccessDeniedException} as versions are read only.
690      */
691     @Override
692     public boolean holdsLock() throws RepositoryException {
693         throw new AccessDeniedException("Lock not supported on version preview");
694     }
695 
696     /**
697      * Throws an {@link AccessDeniedException} as versions are read only.
698      */
699     @Override
700     public boolean isLocked() throws RepositoryException {
701         throw new AccessDeniedException("Lock not supported on version preview");
702     }
703 
704     /**
705      * Get hierarchy manager if previously set for this object.
706      * @return HierarchyManager
707      */
708     @Override
709     public HierarchyManager getHierarchyManager() {
710         return this.base.getHierarchyManager();
711     }
712 
713     /**
714      * Get access manager if previously set for this object.
715      * @return AccessManager
716      * @deprecated use getHierarchyManager instead
717      */
718     @Deprecated
719     @Override
720     public AccessManager getAccessManager() {
721         return this.base.getAccessManager();
722     }
723 
724     @Override
725     public Workspace getWorkspace() throws RepositoryException {
726         return this.base.getWorkspace();
727     }
728 
729     @Override
730     public boolean hasNodeData(String name) throws RepositoryException {
731         if (this.node.hasProperty(name)) {
732             return true;
733         }
734         else { // check for mgnl:resource node
735             if (this.node.hasNode(name) && this.node.getNode(name).getProperty("jcr:frozenPrimaryType").getValue().getString().equals(ItemType.NT_RESOURCE)) {
736                 return true;
737             }
738         }
739         return false;
740     }
741 
742     @Override
743     protected int determineNodeDataType(String name) {
744         // FIXME: maybe delegate to NodeDataImplementations?
745         try {
746             if (this.node.hasProperty(name)) {
747                 return this.node.getProperty(name).getType();
748             }
749             else { // check for mgnl:resource node
750                 if (this.node.hasNode(name) && this.node.getNode(name).getProperty("jcr:frozenPrimaryType").getValue().getString().equals(ItemType.NT_RESOURCE)) {
751                     return PropertyType.BINARY;
752                 }
753             }
754         }
755         catch (RepositoryException e) {
756             throw new IllegalStateException("Can't determine property type of [" + getHandle() + "/" + name + "]", e);
757         }
758         return PropertyType.UNDEFINED;
759     }
760 
761     @Override
762     public NodeType[] getMixinNodeTypes() throws RepositoryException {
763         Value[] vals = this.node.getProperty("jcr:frozenMixinTypes").getValues();
764         NodeTypeManager typeMan = this.hierarchyManager.getWorkspace().getNodeTypeManager();
765         NodeType[] types = new NodeType[vals.length];
766         int i = 0;
767         for (Value val : vals) {
768             types[i++] = typeMan.getNodeType(val.getString());
769         }
770         return types;
771     }
772 
773     //    public List<ContentVersion> getPredecessors() throws RepositoryException {
774     //        List<ContentVersion> list = new ArrayList<ContentVersion>();
775     //        for (Version v : this.state.getPredecessors()) {
776     //            list.add(new ContentVersion(v, this.base));
777     //        }
778     //        return list;
779     //    }
780     public Version[] getPredecessors() throws RepositoryException {
781         // would prefer to return the above version (List of ContentVersions), but since our other APIs return jcr Version as well ...
782         return this.state.getPredecessors();
783     }
784 }