3 import java
.io
.ByteArrayOutputStream
;
4 import java
.io
.IOException
;
5 import java
.io
.InputStream
;
6 import java
.math
.BigDecimal
;
7 import java
.text
.MessageFormat
;
8 import java
.time
.Instant
;
9 import java
.util
.ArrayList
;
10 import java
.util
.Arrays
;
11 import java
.util
.Calendar
;
12 import java
.util
.Collections
;
13 import java
.util
.Date
;
14 import java
.util
.GregorianCalendar
;
15 import java
.util
.Iterator
;
16 import java
.util
.List
;
17 import java
.util
.Objects
;
19 import javax
.jcr
.Binary
;
20 import javax
.jcr
.ItemNotFoundException
;
21 import javax
.jcr
.Node
;
22 import javax
.jcr
.NodeIterator
;
23 import javax
.jcr
.Property
;
24 import javax
.jcr
.PropertyType
;
25 import javax
.jcr
.Repository
;
26 import javax
.jcr
.RepositoryException
;
27 import javax
.jcr
.Session
;
28 import javax
.jcr
.Value
;
29 import javax
.jcr
.Workspace
;
30 import javax
.jcr
.nodetype
.NodeType
;
31 import javax
.jcr
.query
.Query
;
32 import javax
.jcr
.query
.QueryManager
;
33 import javax
.jcr
.query
.Row
;
34 import javax
.jcr
.security
.Privilege
;
35 import javax
.jcr
.version
.Version
;
36 import javax
.jcr
.version
.VersionHistory
;
37 import javax
.jcr
.version
.VersionIterator
;
38 import javax
.jcr
.version
.VersionManager
;
40 import org
.apache
.commons
.io
.IOUtils
;
43 * Utility class whose purpose is to make using JCR less verbose by
44 * systematically using unchecked exceptions and returning <code>null</code>
45 * when something is not found. This is especially useful when writing user
46 * interfaces (such as with SWT) where listeners and callbacks expect unchecked
47 * exceptions. Loosely inspired by Java's <code>Files</code> singleton.
51 * The name of a node which will be serialized as XML text, as per section 7.3.1
52 * of the JCR 2.0 specifications.
54 * @see Workspace#NAME_JCR_XMLTEXT
56 public final static String JCR_XMLTEXT
= "jcr:xmltext";
58 * The name of a property which will be serialized as XML text, as per section
59 * 7.3.1 of the JCR 2.0 specifications.
61 * @see Workspace#NAME_JCR_XMLCHARACTERS
63 public final static String JCR_XMLCHARACTERS
= "jcr:xmlcharacters";
65 * <code>jcr:name</code>, when used in another context than
66 * {@link Property#JCR_NAME}, typically to name a node rather than a property.
68 public final static String JCR_NAME
= "jcr:name";
70 * <code>jcr:path</code>, when used in another context than
71 * {@link Property#JCR_PATH}, typically to name a node rather than a property.
73 public final static String JCR_PATH
= "jcr:path";
75 * <code>jcr:primaryType</code> with prefix instead of namespace (as in
76 * {@link Property#JCR_PRIMARY_TYPE}.
78 public final static String JCR_PRIMARY_TYPE
= "jcr:primaryType";
80 * <code>jcr:mixinTypes</code> with prefix instead of namespace (as in
81 * {@link Property#JCR_MIXIN_TYPES}.
83 public final static String JCR_MIXIN_TYPES
= "jcr:mixinTypes";
85 * <code>jcr:uuid</code> with prefix instead of namespace (as in
86 * {@link Property#JCR_UUID}.
88 public final static String JCR_UUID
= "jcr:uuid";
90 * <code>jcr:created</code> with prefix instead of namespace (as in
91 * {@link Property#JCR_CREATED}.
93 public final static String JCR_CREATED
= "jcr:created";
95 * <code>jcr:createdBy</code> with prefix instead of namespace (as in
96 * {@link Property#JCR_CREATED_BY}.
98 public final static String JCR_CREATED_BY
= "jcr:createdBy";
100 * <code>jcr:lastModified</code> with prefix instead of namespace (as in
101 * {@link Property#JCR_LAST_MODIFIED}.
103 public final static String JCR_LAST_MODIFIED
= "jcr:lastModified";
105 * <code>jcr:lastModifiedBy</code> with prefix instead of namespace (as in
106 * {@link Property#JCR_LAST_MODIFIED_BY}.
108 public final static String JCR_LAST_MODIFIED_BY
= "jcr:lastModifiedBy";
111 * @see Node#isNodeType(String)
112 * @throws JcrException caused by {@link RepositoryException}
114 public static boolean isNodeType(Node node
, String nodeTypeName
) {
116 return node
.isNodeType(nodeTypeName
);
117 } catch (RepositoryException e
) {
118 throw new JcrException("Cannot get whether " + node
+ " is of type " + nodeTypeName
, e
);
123 * @see Node#hasNodes()
124 * @throws JcrException caused by {@link RepositoryException}
126 public static boolean hasNodes(Node node
) {
128 return node
.hasNodes();
129 } catch (RepositoryException e
) {
130 throw new JcrException("Cannot get whether " + node
+ " has children.", e
);
135 * @see Node#getParent()
136 * @throws JcrException caused by {@link RepositoryException}
138 public static Node
getParent(Node node
) {
140 return isRoot(node
) ?
null : node
.getParent();
141 } catch (RepositoryException e
) {
142 throw new JcrException("Cannot get parent of " + node
, e
);
147 * @see Node#getParent()
148 * @throws JcrException caused by {@link RepositoryException}
150 public static String
getParentPath(Node node
) {
151 return getPath(getParent(node
));
155 * Whether this node is the root node.
157 * @throws JcrException caused by {@link RepositoryException}
159 public static boolean isRoot(Node node
) {
161 return node
.getDepth() == 0;
162 } catch (RepositoryException e
) {
163 throw new JcrException("Cannot get depth of " + node
, e
);
168 * @see Node#getPath()
169 * @throws JcrException caused by {@link RepositoryException}
171 public static String
getPath(Node node
) {
173 return node
.getPath();
174 } catch (RepositoryException e
) {
175 throw new JcrException("Cannot get path of " + node
, e
);
180 * @see Node#getSession()
181 * @see Session#getWorkspace()
182 * @see Workspace#getName()
184 public static String
getWorkspaceName(Node node
) {
185 return session(node
).getWorkspace().getName();
189 * @see Node#getIdentifier()
190 * @throws JcrException caused by {@link RepositoryException}
192 public static String
getIdentifier(Node node
) {
194 return node
.getIdentifier();
195 } catch (RepositoryException e
) {
196 throw new JcrException("Cannot get identifier of " + node
, e
);
201 * @see Node#getName()
202 * @throws JcrException caused by {@link RepositoryException}
204 public static String
getName(Node node
) {
206 return node
.getName();
207 } catch (RepositoryException e
) {
208 throw new JcrException("Cannot get name of " + node
, e
);
213 * Returns the node name with its current index (useful for re-ordering).
215 * @see Node#getName()
216 * @see Node#getIndex()
217 * @throws JcrException caused by {@link RepositoryException}
219 public static String
getIndexedName(Node node
) {
221 return node
.getName() + "[" + node
.getIndex() + "]";
222 } catch (RepositoryException e
) {
223 throw new JcrException("Cannot get name of " + node
, e
);
228 * @see Node#getProperty(String)
229 * @throws JcrException caused by {@link RepositoryException}
231 public static Property
getProperty(Node node
, String property
) {
233 if (node
.hasProperty(property
))
234 return node
.getProperty(property
);
237 } catch (RepositoryException e
) {
238 throw new JcrException("Cannot get property " + property
+ " of " + node
, e
);
243 * @see Node#getIndex()
244 * @throws JcrException caused by {@link RepositoryException}
246 public static int getIndex(Node node
) {
248 return node
.getIndex();
249 } catch (RepositoryException e
) {
250 throw new JcrException("Cannot get index of " + node
, e
);
255 * If node has mixin {@link NodeType#MIX_TITLE}, return
256 * {@link Property#JCR_TITLE}, otherwise return {@link #getName(Node)}.
258 public static String
getTitle(Node node
) {
259 if (Jcr
.isNodeType(node
, NodeType
.MIX_TITLE
))
260 return get(node
, Property
.JCR_TITLE
);
262 return Jcr
.getName(node
);
265 /** Accesses a {@link NodeIterator} as an {@link Iterable}. */
266 @SuppressWarnings("unchecked")
267 public static Iterable
<Node
> iterate(NodeIterator nodeIterator
) {
268 return new Iterable
<Node
>() {
271 public Iterator
<Node
> iterator() {
278 * @return the children as an {@link Iterable} for use in for-each llops.
279 * @see Node#getNodes()
280 * @throws JcrException caused by {@link RepositoryException}
282 public static Iterable
<Node
> nodes(Node node
) {
284 return iterate(node
.getNodes());
285 } catch (RepositoryException e
) {
286 throw new JcrException("Cannot get children of " + node
, e
);
291 * @return the children as a (possibly empty) {@link List}.
292 * @see Node#getNodes()
293 * @throws JcrException caused by {@link RepositoryException}
295 public static List
<Node
> getNodes(Node node
) {
296 List
<Node
> nodes
= new ArrayList
<>();
298 if (node
.hasNodes()) {
299 NodeIterator nit
= node
.getNodes();
300 while (nit
.hasNext())
301 nodes
.add(nit
.nextNode());
305 } catch (RepositoryException e
) {
306 throw new JcrException("Cannot get children of " + node
, e
);
311 * @return the child or <code>null</node> if not found
312 * @see Node#getNode(String)
313 * @throws JcrException caused by {@link RepositoryException}
315 public static Node
getNode(Node node
, String child
) {
317 if (node
.hasNode(child
))
318 return node
.getNode(child
);
321 } catch (RepositoryException e
) {
322 throw new JcrException("Cannot get child of " + node
, e
);
327 * @return the node at this path or <code>null</node> if not found
328 * @see Session#getNode(String)
329 * @throws JcrException caused by {@link RepositoryException}
331 public static Node
getNode(Session session
, String path
) {
333 if (session
.nodeExists(path
))
334 return session
.getNode(path
);
337 } catch (RepositoryException e
) {
338 throw new JcrException("Cannot get node " + path
, e
);
343 * Add a node to this parent, setting its primary type and its mixins.
345 * @param parent the parent node
346 * @param name the name of the node, if <code>null</code>, the primary
347 * type will be used (typically for XML structures)
348 * @param primaryType the primary type, if <code>null</code>
349 * {@link NodeType#NT_UNSTRUCTURED} will be used.
350 * @param mixins the mixins
351 * @return the created node
352 * @see Node#addNode(String, String)
353 * @see Node#addMixin(String)
355 public static Node
addNode(Node parent
, String name
, String primaryType
, String
... mixins
) {
356 if (name
== null && primaryType
== null)
357 throw new IllegalArgumentException("Both node name and primary type cannot be null");
359 Node newNode
= parent
.addNode(name
== null ? primaryType
: name
,
360 primaryType
== null ? NodeType
.NT_UNSTRUCTURED
: primaryType
);
361 for (String mixin
: mixins
) {
362 newNode
.addMixin(mixin
);
365 } catch (RepositoryException e
) {
366 throw new JcrException("Cannot add node " + name
+ " to " + parent
, e
);
371 * Add an {@link NodeType#NT_BASE} node to this parent.
373 * @param parent the parent node
374 * @param name the name of the node, cannot be <code>null</code>
375 * @return the created node
377 * @see Node#addNode(String)
379 public static Node
addNode(Node parent
, String name
) {
381 throw new IllegalArgumentException("Node name cannot be null");
383 Node newNode
= parent
.addNode(name
);
385 } catch (RepositoryException e
) {
386 throw new JcrException("Cannot add node " + name
+ " to " + parent
, e
);
391 * Add mixins to a node.
393 * @param node the node
394 * @param mixins the mixins
395 * @see Node#addMixin(String)
397 public static void addMixin(Node node
, String
... mixins
) {
399 for (String mixin
: mixins
) {
400 node
.addMixin(mixin
);
402 } catch (RepositoryException e
) {
403 throw new JcrException("Cannot add mixins " + Arrays
.asList(mixins
) + " to " + node
, e
);
412 public static void remove(Node node
) {
415 } catch (RepositoryException e
) {
416 throw new JcrException("Cannot remove node " + node
, e
);
421 * @return the node with htis id or <code>null</node> if not found
422 * @see Session#getNodeByIdentifier(String)
423 * @throws JcrException caused by {@link RepositoryException}
425 public static Node
getNodeById(Session session
, String id
) {
427 return session
.getNodeByIdentifier(id
);
428 } catch (ItemNotFoundException e
) {
430 } catch (RepositoryException e
) {
431 throw new JcrException("Cannot get node with id " + id
, e
);
436 * Whether this node has this property.
438 * @see Node#hasProperty(String)
439 * @throws JcrException caused by {@link RepositoryException}
441 public static boolean hasProperty(Node node
, String property
) {
443 return node
.hasProperty(property
);
444 } catch (RepositoryException e
) {
445 throw new JcrException("Cannot check whether " + node
+ " has property " + property
, e
);
450 * Set a property to the given value, or remove it if the value is
453 * @throws JcrException caused by {@link RepositoryException}
455 public static void set(Node node
, String property
, Object value
) {
457 if (!node
.hasProperty(property
)) {
459 if (value
instanceof List
) {// multiple
460 List
<?
> lst
= (List
<?
>) value
;
461 String
[] values
= new String
[lst
.size()];
462 for (int i
= 0; i
< lst
.size(); i
++) {
463 values
[i
] = lst
.get(i
).toString();
465 node
.setProperty(property
, values
);
467 node
.setProperty(property
, value
.toString());
472 Property prop
= node
.getProperty(property
);
479 if (value
instanceof List
) {
480 List
<?
> lst
= (List
<?
>) value
;
481 String
[] values
= new String
[lst
.size()];
483 for (int i
= 0; i
< lst
.size(); i
++) {
484 values
[i
] = lst
.get(i
).toString();
486 if (!prop
.isMultiple())
488 node
.setProperty(property
, values
);
493 if (prop
.isMultiple()) {
495 node
.setProperty(property
, value
.toString());
499 if (value
instanceof String
)
500 prop
.setValue((String
) value
);
501 else if (value
instanceof Long
)
502 prop
.setValue((Long
) value
);
503 else if (value
instanceof Integer
)
504 prop
.setValue(((Integer
) value
).longValue());
505 else if (value
instanceof Double
)
506 prop
.setValue((Double
) value
);
507 else if (value
instanceof Float
)
508 prop
.setValue(((Float
) value
).doubleValue());
509 else if (value
instanceof Calendar
)
510 prop
.setValue((Calendar
) value
);
511 else if (value
instanceof BigDecimal
)
512 prop
.setValue((BigDecimal
) value
);
513 else if (value
instanceof Boolean
)
514 prop
.setValue((Boolean
) value
);
515 else if (value
instanceof byte[])
516 JcrUtils
.setBinaryAsBytes(prop
, (byte[]) value
);
517 else if (value
instanceof Instant
) {
518 Instant instant
= (Instant
) value
;
519 GregorianCalendar calendar
= new GregorianCalendar();
520 calendar
.setTime(Date
.from(instant
));
521 prop
.setValue(calendar
);
522 } else // try with toString()
523 prop
.setValue(value
.toString());
524 } catch (RepositoryException e
) {
525 throw new JcrException("Cannot set property " + property
+ " of " + node
+ " to " + value
, e
);
530 * Get property as {@link String}.
532 * @return the value of
533 * {@link Node#getProperty(String)}.{@link Property#getString()} or
534 * <code>null</code> if the property does not exist.
535 * @throws JcrException caused by {@link RepositoryException}
537 public static String
get(Node node
, String property
) {
538 return get(node
, property
, null);
542 * Get property as a {@link String}. If the property is multiple it returns the
545 * @return the value of
546 * {@link Node#getProperty(String)}.{@link Property#getString()} or
547 * <code>defaultValue</code> if the property does not exist.
548 * @throws JcrException caused by {@link RepositoryException}
550 public static String
get(Node node
, String property
, String defaultValue
) {
552 if (node
.hasProperty(property
)) {
553 Property p
= node
.getProperty(property
);
555 return p
.getString();
557 Value
[] values
= p
.getValues();
558 if (values
.length
== 0)
561 return values
[0].getString();
565 } catch (RepositoryException e
) {
566 throw new JcrException("Cannot retrieve property " + property
+ " from " + node
, e
);
571 * Get property as a {@link Value}.
573 * @return {@link Node#getProperty(String)} or <code>null</code> if the property
575 * @throws JcrException caused by {@link RepositoryException}
577 public static Value
getValue(Node node
, String property
) {
579 if (node
.hasProperty(property
))
580 return node
.getProperty(property
).getValue();
583 } catch (RepositoryException e
) {
584 throw new JcrException("Cannot retrieve property " + property
+ " from " + node
, e
);
589 * Get property doing a best effort to cast it as the target object.
591 * @return the value of {@link Node#getProperty(String)} or
592 * <code>defaultValue</code> if the property does not exist.
593 * @throws IllegalArgumentException if the value could not be cast
594 * @throws JcrException in case of unexpected
595 * {@link RepositoryException}
597 @SuppressWarnings("unchecked")
598 public static <T
> T
getAs(Node node
, String property
, T defaultValue
) {
599 Objects
.requireNonNull(defaultValue
);
601 // TODO deal with multiple
602 if (node
.hasProperty(property
)) {
603 Property p
= node
.getProperty(property
);
605 if (p
.isMultiple()) {
606 throw new UnsupportedOperationException("Multiple values properties are not supported");
608 Value value
= p
.getValue();
609 return (T
) get(value
);
610 } catch (ClassCastException e
) {
611 throw new IllegalArgumentException(
612 "Cannot cast property of type " + PropertyType
.nameFromValue(p
.getType()), e
);
617 } catch (RepositoryException e
) {
618 throw new JcrException("Cannot retrieve property " + property
+ " from " + node
, e
);
622 @SuppressWarnings("unchecked")
623 public static <T
> T
getAs(Node node
, String property
, Class
<T
> clss
) {
625 Property p
= node
.getProperty(property
);
627 if (p
.isMultiple()) {
628 throw new UnsupportedOperationException("Multiple values properties are not supported");
630 Value value
= p
.getValue();
631 return (T
) get(value
);
632 } catch (ClassCastException e
) {
633 throw new IllegalArgumentException(
634 "Cannot cast property of type " + PropertyType
.nameFromValue(p
.getType()), e
);
636 } catch (RepositoryException e
) {
637 throw new JcrException("Cannot retrieve property " + property
+ " from " + node
, e
);
639 // if (String.class.isAssignableFrom(clss)) {
640 // return (T) get(node, property);
641 // } else if (Long.class.isAssignableFrom(clss)) {
642 // return (T) get(node, property);
643 // } else if (Boolean.class.isAssignableFrom(clss)) {
644 // return (T) get(node, property);
646 // throw new IllegalArgumentException("Unsupported format " + clss);
651 * Retrieve a {@link PropertyType#DATE} property as an {@link Instant}.
653 * @return the property value, or <code>null</code> if not found.
655 public static Instant
getAsInstant(Node node
, String property
) {
657 if (!node
.hasProperty(property
))
659 Calendar calendar
= node
.getProperty(property
).getDate();
660 return calendar
.getTime().toInstant();
661 } catch (RepositoryException e
) {
662 throw new JcrException("Cannot get property " + property
+ " of " + node
+ " as an instant.", e
);
668 * Get a multiple property as a list, doing a best effort to cast it as the
671 * @return the value of {@link Node#getProperty(String)}.
672 * @throws IllegalArgumentException if the value could not be cast
673 * @throws JcrException in case of unexpected
674 * {@link RepositoryException}
676 public static <T
> List
<T
> getMultiple(Node node
, String property
) {
678 if (node
.hasProperty(property
)) {
679 Property p
= node
.getProperty(property
);
680 return getMultiple(p
);
684 } catch (RepositoryException e
) {
685 throw new JcrException("Cannot retrieve multiple values property " + property
+ " from " + node
, e
);
690 * Get a multiple property as a list, doing a best effort to cast it as the
693 @SuppressWarnings("unchecked")
694 public static <T
> List
<T
> getMultiple(Property p
) {
696 List
<T
> res
= new ArrayList
<>();
697 if (!p
.isMultiple()) {
698 res
.add((T
) get(p
.getValue()));
701 Value
[] values
= p
.getValues();
702 for (Value value
: values
) {
703 res
.add((T
) get(value
));
706 } catch (ClassCastException
| RepositoryException e
) {
707 throw new IllegalArgumentException("Cannot get property " + p
, e
);
711 /** Cast a {@link Value} to a standard Java object. */
712 public static Object
get(Value value
) {
713 Binary binary
= null;
715 switch (value
.getType()) {
716 case PropertyType
.STRING
:
717 return value
.getString();
718 case PropertyType
.DOUBLE
:
719 return (Double
) value
.getDouble();
720 case PropertyType
.LONG
:
721 return (Long
) value
.getLong();
722 case PropertyType
.BOOLEAN
:
723 return (Boolean
) value
.getBoolean();
724 case PropertyType
.DATE
:
725 return value
.getDate();
726 case PropertyType
.BINARY
:
727 binary
= value
.getBinary();
729 try (InputStream in
= binary
.getStream(); ByteArrayOutputStream out
= new ByteArrayOutputStream();) {
730 IOUtils
.copy(in
, out
);
731 arr
= out
.toByteArray();
732 } catch (IOException e
) {
733 throw new RuntimeException("Cannot read binary from " + value
, e
);
737 return value
.getString();
739 } catch (RepositoryException e
) {
740 throw new JcrException("Cannot cast value from " + value
, e
);
748 * Retrieves the {@link Session} related to this node.
750 * @deprecated Use {@link #getSession(Node)} instead.
753 public static Session
session(Node node
) {
754 return getSession(node
);
757 /** Retrieves the {@link Session} related to this node. */
758 public static Session
getSession(Node node
) {
760 return node
.getSession();
761 } catch (RepositoryException e
) {
762 throw new JcrException("Cannot retrieve session related to " + node
, e
);
766 /** Retrieves the root node related to this session. */
767 public static Node
getRootNode(Session session
) {
769 return session
.getRootNode();
770 } catch (RepositoryException e
) {
771 throw new JcrException("Cannot get root node for " + session
, e
);
775 /** Whether this item exists. */
776 public static boolean itemExists(Session session
, String path
) {
778 return session
.itemExists(path
);
779 } catch (RepositoryException e
) {
780 throw new JcrException("Cannot check whether " + path
+ " exists", e
);
785 * Saves the {@link Session} related to this node. Note that all other unrelated
786 * modifications in this session will also be saved.
788 public static void save(Node node
) {
790 Session session
= node
.getSession();
791 // if (node.isNodeType(NodeType.MIX_LAST_MODIFIED)) {
792 // set(node, Property.JCR_LAST_MODIFIED, Instant.now());
793 // set(node, Property.JCR_LAST_MODIFIED_BY, session.getUserID());
795 if (session
.hasPendingChanges())
797 } catch (RepositoryException e
) {
798 throw new JcrException("Cannot save session related to " + node
+ " in workspace "
799 + session(node
).getWorkspace().getName(), e
);
803 /** Login to a JCR repository. */
804 public static Session
login(Repository repository
, String workspace
) {
806 return repository
.login(workspace
);
807 } catch (RepositoryException e
) {
808 throw new IllegalArgumentException("Cannot login to repository", e
);
812 /** Safely and silently logs out a session. */
813 public static void logout(Session session
) {
816 if (session
.isLive())
818 } catch (Exception e
) {
823 /** Safely and silently logs out the underlying session. */
824 public static void logout(Node node
) {
825 Jcr
.logout(session(node
));
832 * Add a single privilege to a node.
836 public static void addPrivilege(Node node
, String principal
, String privilege
) {
838 Session session
= node
.getSession();
839 JcrUtils
.addPrivilege(session
, node
.getPath(), principal
, privilege
);
840 } catch (RepositoryException e
) {
841 throw new JcrException("Cannot add privilege " + privilege
+ " to " + node
, e
);
848 /** Get checked out status. */
849 public static boolean isCheckedOut(Node node
) {
851 return node
.isCheckedOut();
852 } catch (RepositoryException e
) {
853 throw new JcrException("Cannot retrieve checked out status of " + node
, e
);
857 /** @see VersionManager#checkpoint(String) */
858 public static void checkpoint(Node node
) {
860 versionManager(node
).checkpoint(node
.getPath());
861 } catch (RepositoryException e
) {
862 throw new JcrException("Cannot check in " + node
, e
);
866 /** @see VersionManager#checkin(String) */
867 public static void checkin(Node node
) {
869 versionManager(node
).checkin(node
.getPath());
870 } catch (RepositoryException e
) {
871 throw new JcrException("Cannot check in " + node
, e
);
875 /** @see VersionManager#checkout(String) */
876 public static void checkout(Node node
) {
878 versionManager(node
).checkout(node
.getPath());
879 } catch (RepositoryException e
) {
880 throw new JcrException("Cannot check out " + node
, e
);
884 /** Get the {@link VersionManager} related to this node. */
885 public static VersionManager
versionManager(Node node
) {
887 return node
.getSession().getWorkspace().getVersionManager();
888 } catch (RepositoryException e
) {
889 throw new JcrException("Cannot get version manager from " + node
, e
);
893 /** Get the {@link VersionHistory} related to this node. */
894 public static VersionHistory
getVersionHistory(Node node
) {
896 return versionManager(node
).getVersionHistory(node
.getPath());
897 } catch (RepositoryException e
) {
898 throw new JcrException("Cannot get version history from " + node
, e
);
903 * The linear versions of this version history in reverse order and without the
906 public static List
<Version
> getLinearVersions(VersionHistory versionHistory
) {
908 List
<Version
> lst
= new ArrayList
<>();
909 VersionIterator vit
= versionHistory
.getAllLinearVersions();
910 while (vit
.hasNext())
911 lst
.add(vit
.nextVersion());
913 Collections
.reverse(lst
);
915 } catch (RepositoryException e
) {
916 throw new JcrException("Cannot get linear versions from " + versionHistory
, e
);
920 /** The frozen node related to this {@link Version}. */
921 public static Node
getFrozenNode(Version version
) {
923 return version
.getFrozenNode();
924 } catch (RepositoryException e
) {
925 throw new JcrException("Cannot get frozen node from " + version
, e
);
929 /** Get the base {@link Version} related to this node. */
930 public static Version
getBaseVersion(Node node
) {
932 return versionManager(node
).getBaseVersion(node
.getPath());
933 } catch (RepositoryException e
) {
934 throw new JcrException("Cannot get base version from " + node
, e
);
942 * Returns the size of this file.
944 * @see NodeType#NT_FILE
946 public static long getFileSize(Node fileNode
) {
948 if (!fileNode
.isNodeType(NodeType
.NT_FILE
))
949 throw new IllegalArgumentException(fileNode
+ " must be a file.");
950 return getBinarySize(fileNode
.getNode(Node
.JCR_CONTENT
).getProperty(Property
.JCR_DATA
).getBinary());
951 } catch (RepositoryException e
) {
952 throw new JcrException("Cannot get file size of " + fileNode
, e
);
956 /** Returns the size of this {@link Binary}. */
957 public static long getBinarySize(Binary binaryArg
) {
959 try (Bin binary
= new Bin(binaryArg
)) {
960 return binary
.getSize();
962 } catch (RepositoryException e
) {
963 throw new JcrException("Cannot get file size of binary " + binaryArg
, e
);
968 /** Creates a JCR-SQL2 query using {@link MessageFormat}. */
969 public static Query
createQuery(QueryManager qm
, String sql
, Object
... args
) {
971 sql
= sql
.replaceAll("'", "''");
972 String query
= MessageFormat
.format(sql
, args
);
974 return qm
.createQuery(query
, Query
.JCR_SQL2
);
975 } catch (RepositoryException e
) {
976 throw new JcrException("Cannot create JCR-SQL2 query from " + query
, e
);
980 /** Executes a JCR-SQL2 query using {@link MessageFormat}. */
981 public static NodeIterator
executeQuery(QueryManager qm
, String sql
, Object
... args
) {
982 Query query
= createQuery(qm
, sql
, args
);
984 return query
.execute().getNodes();
985 } catch (RepositoryException e
) {
986 throw new JcrException("Cannot execute query " + sql
+ " with arguments " + Arrays
.asList(args
), e
);
990 /** Executes a JCR-SQL2 query using {@link MessageFormat}. */
991 public static NodeIterator
executeQuery(Session session
, String sql
, Object
... args
) {
992 QueryManager queryManager
;
994 queryManager
= session
.getWorkspace().getQueryManager();
995 } catch (RepositoryException e
) {
996 throw new JcrException("Cannot get query manager from session " + session
, e
);
998 return executeQuery(queryManager
, sql
, args
);
1002 * Executes a JCR-SQL2 query using {@link MessageFormat}, which must return a
1003 * single node at most.
1005 * @return the node or <code>null</code> if not found.
1007 public static Node
getNode(QueryManager qm
, String sql
, Object
... args
) {
1008 NodeIterator nit
= executeQuery(qm
, sql
, args
);
1009 if (nit
.hasNext()) {
1010 Node node
= nit
.nextNode();
1012 throw new IllegalStateException(
1013 "Query " + sql
+ " with arguments " + Arrays
.asList(args
) + " returned more than one node.");
1021 * Executes a JCR-SQL2 query using {@link MessageFormat}, which must return a
1022 * single node at most.
1024 * @return the node or <code>null</code> if not found.
1026 public static Node
getNode(Session session
, String sql
, Object
... args
) {
1027 QueryManager queryManager
;
1029 queryManager
= session
.getWorkspace().getQueryManager();
1030 } catch (RepositoryException e
) {
1031 throw new JcrException("Cannot get query manager from session " + session
, e
);
1033 return getNode(queryManager
, sql
, args
);
1036 public static Node
getRowNode(Row row
, String selectorName
) {
1038 return row
.getNode(selectorName
);
1039 } catch (RepositoryException e
) {
1040 throw new JcrException("Cannot get node " + selectorName
+ " from row", e
);