/** * This file is part of the JCROM project. * Copyright (C) 2008-2014 - All rights reserved. * Authors: Olafur Gauti Gudmundsson, Nicolas Dos Santos * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jcrom.util; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.sql.Timestamp; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.StringTokenizer; import javax.jcr.Binary; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.Value; import javax.jcr.ValueFactory; import javax.jcr.lock.LockManager; import javax.jcr.nodetype.NodeType; import javax.jcr.version.Version; import javax.jcr.version.VersionManager; import javafx.beans.property.*; import org.jcrom.JcrMappingException; import org.jcrom.util.io.IOUtils; /** * Contains utilities used for JCR nodes * * @author Nicolas Dos Santos */ public final class JcrUtils { private JcrUtils() { } /** * Checks whether the given mixin node type is in effect for the given node. * * @param node the node * @param mixinType the mixin node type * @return <code>true</code> when the misin node type is present, <code>false</code> instead. * @throws RepositoryException */ public static boolean hasMixinType(Node node, String mixinType) throws RepositoryException { for (NodeType nodeType : node.getMixinNodeTypes()) { if (nodeType.getName().equals(mixinType)) { return true; } } return false; } /** * Returns the LockManager object from the given session. * @param session {@link Session} * @return a {@link LockManager} object * @throws RepositoryException */ public static LockManager getLockManager(Session session) throws RepositoryException { LockManager lockMgr = session.getWorkspace().getLockManager(); return lockMgr; } /** * Returns the VersionManager object from the given session. * * @param session {@link Session} * @return a {@link VersionManager} object * @throws RepositoryException */ public static VersionManager getVersionManager(Session session) throws RepositoryException { VersionManager versionMgr = session.getWorkspace().getVersionManager(); return versionMgr; } /** * Sets the given node to checked-out status. * @param node node to check-out * @throws RepositoryException */ public static void checkout(Node node) throws RepositoryException { getVersionManager(node.getSession()).checkout(node.getPath()); } /** * Creates for the given node a new version and returns that version. * Put the node into the checked-in state. * * @param node node to checkin * @return the created version * @throws RepositoryException */ public static Version checkin(Node node) throws RepositoryException { return getVersionManager(node.getSession()).checkin(node.getPath()); } public static void checkinRecursively(Node node) { try { NodeIterator it = node.getNodes(); while (it.hasNext()) { checkinRecursively(it.nextNode()); } if (node.isCheckedOut() && node.isNodeType(NodeType.MIX_VERSIONABLE)) { //node.checkin(); checkin(node); } } catch (RepositoryException e) { throw new JcrMappingException("Could not perform check-in", e); } } public static void checkoutRecursively(Node node) { try { NodeIterator it = node.getNodes(); while (it.hasNext()) { checkoutRecursively(it.nextNode()); } if (!node.isCheckedOut() && node.isNodeType(NodeType.MIX_VERSIONABLE)) { //node.checkout(); checkout(node); } } catch (RepositoryException e) { throw new JcrMappingException("Could not perform check-out", e); } } /** * Places a lock on the given node. * * @param node the node to be locked * @param isDeep if <code>true</code> this lock will apply to this node and all its descendants; if <code>false</code>, it applies only to this node. * @param isSessionScoped if <code>true</code>, this lock expires with the current session; if <code>false</code> it expires when explicitly or automatically unlocked for some other reason. * @param timeoutHint desired lock timeout in seconds (servers are free to ignore this value); specify Long.MAX_VALUE for no timeout. * @param ownerInfo a string containing owner information supplied by the client; servers are free to ignore this value. * @see javax.jcr.lock.LockManager#lock(String, boolean, boolean, long, String) */ public static void lock(Node node, boolean isDeep, boolean isSessionScoped, long timeoutHint, String ownerInfo) { try { getLockManager(node.getSession()).lock(node.getPath(), isDeep, isSessionScoped, timeoutHint, ownerInfo); } catch (RepositoryException e) { throw new JcrMappingException("Could not perform lock", e); } } /** * Removes the lock on the given node. * * @param node the node unlock * @see javax.jcr.lock.LockManager#unlock(String) */ public static void unlock(Node node) { try { getLockManager(node.getSession()).unlock(node.getPath()); } catch (RepositoryException e) { throw new JcrMappingException("Could not perform unlock", e); } } /** * Returns a {@link Value} object from the given class and the specified value. * * @param c {@link Class} used to create the {@link Value} object with the correct {@link PropertyType} * @param value the value * @param valueFactory {@link ValueFactory} used to invoke the methods to create {@link Value} * @return a {@link Value} object * @throws RepositoryException */ public static Value createValue(Class<?> c, Object value, ValueFactory valueFactory) throws RepositoryException { if (Property.class.isAssignableFrom(c)) { Object wrappedValue = ((Property) value).getValue(); if (wrappedValue == null) { return null; } else { return createValue(wrappedValue.getClass(), wrappedValue, valueFactory); } } else if (c == String.class) { return valueFactory.createValue((String) value); } else if (c == Date.class) { Calendar cal = Calendar.getInstance(); cal.setTime((Date) value); return valueFactory.createValue(cal); } else if (c == Timestamp.class) { Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(((Timestamp) value).getTime()); return valueFactory.createValue(cal); } else if (c == Calendar.class) { return valueFactory.createValue((Calendar) value); } else if (c == InputStream.class) { //return valueFactory.createValue((InputStream) fieldValue); Binary binary = valueFactory.createBinary((InputStream) value); return valueFactory.createValue(binary); } else if (c.isArray() && c.getComponentType() == byte.class) { //return valueFactory.createValue(new ByteArrayInputStream((byte[]) fieldValue)); Binary binary = valueFactory.createBinary(new ByteArrayInputStream((byte[]) value)); return valueFactory.createValue(binary); } else if (c == Integer.class || c == int.class) { return valueFactory.createValue((Integer) value); } else if (c == Long.class || c == long.class) { return valueFactory.createValue((Long) value); } else if (c == Double.class || c == double.class) { return valueFactory.createValue((Double) value); } else if (c == Boolean.class || c == boolean.class) { return valueFactory.createValue((Boolean) value); } else if (c == Locale.class) { return valueFactory.createValue(((Locale) value).toString()); } else if (c.isEnum()) { return valueFactory.createValue(((Enum<?>) value).name()); } return null; } /** * Returns a representation of the given {@link Value} by using the given {@link Class}. * * @param c {@link Class} used to return the representation with correct type * @param value {@link Value} object * @return a representation of {@link Value}. * @throws RepositoryException * @throws IOException */ public static Object getValue(Class<?> c, Value value) throws RepositoryException, IOException { if (c == String.class || StringProperty.class.isAssignableFrom(c)) { return value.getString(); } else if (c == Date.class) { return value.getDate().getTime(); } else if (c == Timestamp.class) { return new Timestamp(value.getDate().getTimeInMillis()); } else if (c == Calendar.class) { return value.getDate(); } else if (c == InputStream.class) { //return value.getStream(); return value.getBinary().getStream(); } else if (c.isArray() && c.getComponentType() == byte.class) { // byte array...we need to read from the stream //return Mapper.readBytes(value.getStream()); return IOUtils.toByteArray(value.getBinary().getStream()); } else if (c == Integer.class || c == int.class || IntegerProperty.class.isAssignableFrom(c)) { return (int) value.getDouble(); } else if (c == Long.class || c == long.class || LongProperty.class.isAssignableFrom(c)) { return value.getLong(); } else if (c == Double.class || c == double.class || DoubleProperty.class.isAssignableFrom(c)) { return value.getDouble(); } else if (c == Boolean.class || c == boolean.class || BooleanProperty.class.isAssignableFrom(c)) { return value.getBoolean(); } else if (c == Locale.class) { return parseLocale(value.getString()); } else if (c.isEnum()) { return Enum.valueOf((Class<? extends Enum>) c, value.getString()); } return null; } /** * Parses given locale string to Locale object. If the string is empty or null then the we return null. * * @param localeString * a string containing locale in <code>language_country_variant</code> format. * @return Locale */ public static Locale parseLocale(String localeString) { if (localeString != null && localeString.length() > 0) { StringTokenizer st = new StringTokenizer(localeString, "_"); String language = st.hasMoreElements() ? st.nextToken() : Locale.getDefault().getLanguage(); String country = st.hasMoreElements() ? st.nextToken() : ""; String variant = st.hasMoreElements() ? st.nextToken() : ""; return new Locale(language, country, variant); } return null; } }