<commit_before>package com.thinkbiganalytics.metadata.modeshape.support;

/*-
 * #%L
 * thinkbig-metadata-modeshape
 * %%
 * Copyright (C) 2017 ThinkBig Analytics
 * %%
 * 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.
 * #L%
 */

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.thinkbiganalytics.metadata.modeshape.JcrMetadataAccess;
import com.thinkbiganalytics.metadata.modeshape.MetadataRepositoryException;
import com.thinkbiganalytics.metadata.modeshape.common.JcrObject;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import javax.jcr.AccessDeniedException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.lock.LockManager;
import javax.jcr.nodetype.NodeType;
import javax.jcr.version.Version;
import javax.jcr.version.VersionHistory;
import javax.jcr.version.VersionIterator;
import javax.jcr.version.VersionManager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 */
public class JcrVersionUtil {
    
    private static final Logger log = LoggerFactory.getLogger(JcrVersionUtil.class);
    
    public static Node createAutoCheckoutProxy(Node node) {
        InvocationHandler handler = new VersionableNodeInvocationHandler(node);
        return (Node) Proxy.newProxyInstance(Node.class.getClassLoader(), new Class<?>[] { Node.class }, handler);
    }

    /**
     * Returns the LockManager object from the given session.
     *
     * @param session {@link Session}
     * @return a {@link LockManager} object
     */
    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
     */
    public static VersionManager getVersionManager(Session session) throws RepositoryException {
        VersionManager versionMgr = session.getWorkspace().getVersionManager();
        return versionMgr;
    }
    
    /**
     * Ensures that the given node, or a versionable ancestor, is checked out.
     * Passing a node that is not in a versionable hierarch has no effect.
     * @param n the node to whose hierarchy is to be checked out
     */
    public static void ensureCheckoutNode(Node node) {
        getVersionableAncestor(node).ifPresent(a -> JcrMetadataAccess.ensureCheckoutNode(a));
    }
    
    /**
     * Finds the first node in the argument node's parentage that is versionable, if any.  If the argument 
     * node itself is versionable then an Optional containing it is returned. If there is no versionable
     * node in this node's hierarchy then an empty Optional is returned.
     * @param node the node who's parentage is to be searched
     * @return An Optional containing the closest versionable parent of the node if it exists, otherwise an empty Optional
     */
    public static Optional<Node> getVersionableAncestor(Node node) {
        try {
            if (node.getSession().getRootNode().getIdentifier().equals(node.getParent().getIdentifier())) {
                return Optional.empty();
            } else if (isVersionable(node)) {
                return Optional.of(node);
            } else {
                return getVersionableAncestor(node.getParent());
            }
        } catch (RepositoryException e) {
            throw new MetadataRepositoryException("Unable to check if node is in versionable hierarchy", e);
        }
    }
    
    /**
     * Checks if the given node is within a versionable hierarchy.
     * @param node he node who's parentage is to be searched
     * @return true if this node or one of its parents is versionable, otherwise false
     */
    public static boolean inVersionableHierarchy(Node node) {
        return getVersionableAncestor(node).map(n -> true).orElse(false);
    }

    /**
     * @param node the node to test
     * @return true if this node is versionable
     */
    public static boolean isVersionable(Node node) {
        String name = "";
        boolean versionable = false;
        try {
            name = node.getName();
            versionable = JcrUtil.hasMixinType(node, "mix:versionable");
            return versionable;
        } catch (AccessDeniedException e) {
            log.debug("Access denied", e);
            throw new AccessControlException(e.getMessage());
        } catch (RepositoryException e) {
            throw new MetadataRepositoryException("Unable to check if versionable for Node " + name, e);
        }
    }

    /**
     * Sets the given node to checked-out status.
     *
     * @param node node to check-out
     */
    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
     */
    public static Version checkin(Node node) throws RepositoryException {
        if (node.getSession().isLive()) {
            return getVersionManager(node.getSession()).checkin(node.getPath());
        } else {
            return null;
        }
    }

    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)) {
                checkin(node);
            }

        } catch (RepositoryException e) {
            throw new MetadataRepositoryException("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)) {<commit_msg>KYLO-914: Workaround for kylo checkin bug.<commit_after>package com.thinkbiganalytics.metadata.modeshape.support;

/*-
 * #%L
 * thinkbig-metadata-modeshape
 * %%
 * Copyright (C) 2017 ThinkBig Analytics
 * %%
 * 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.
 * #L%
 */

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.thinkbiganalytics.metadata.modeshape.JcrMetadataAccess;
import com.thinkbiganalytics.metadata.modeshape.MetadataRepositoryException;
import com.thinkbiganalytics.metadata.modeshape.common.JcrObject;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import javax.jcr.AccessDeniedException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.lock.LockManager;
import javax.jcr.nodetype.NodeType;
import javax.jcr.version.Version;
import javax.jcr.version.VersionHistory;
import javax.jcr.version.VersionIterator;
import javax.jcr.version.VersionManager;

import org.modeshape.jcr.JcrLexicon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 */
public class JcrVersionUtil {
    
    private static final Logger log = LoggerFactory.getLogger(JcrVersionUtil.class);
    
    public static Node createAutoCheckoutProxy(Node node) {
        InvocationHandler handler = new VersionableNodeInvocationHandler(node);
        return (Node) Proxy.newProxyInstance(Node.class.getClassLoader(), new Class<?>[] { Node.class }, handler);
    }

    /**
     * Returns the LockManager object from the given session.
     *
     * @param session {@link Session}
     * @return a {@link LockManager} object
     */
    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
     */
    public static VersionManager getVersionManager(Session session) throws RepositoryException {
        VersionManager versionMgr = session.getWorkspace().getVersionManager();
        return versionMgr;
    }
    
    /**
     * Ensures that the given node, or a versionable ancestor, is checked out.
     * Passing a node that is not in a versionable hierarch has no effect.
     * @param n the node to whose hierarchy is to be checked out
     */
    public static void ensureCheckoutNode(Node node) {
        getVersionableAncestor(node).ifPresent(a -> JcrMetadataAccess.ensureCheckoutNode(a));
    }
    
    /**
     * Finds the first node in the argument node's parentage that is versionable, if any.  If the argument 
     * node itself is versionable then an Optional containing it is returned. If there is no versionable
     * node in this node's hierarchy then an empty Optional is returned.
     * @param node the node who's parentage is to be searched
     * @return An Optional containing the closest versionable parent of the node if it exists, otherwise an empty Optional
     */
    public static Optional<Node> getVersionableAncestor(Node node) {
        try {
            if (node.getSession().getRootNode().getIdentifier().equals(node.getParent().getIdentifier())) {
                return Optional.empty();
            } else if (isVersionable(node)) {
                return Optional.of(node);
            } else {
                return getVersionableAncestor(node.getParent());
            }
        } catch (RepositoryException e) {
            throw new MetadataRepositoryException("Unable to check if node is in versionable hierarchy", e);
        }
    }
    
    /**
     * Checks if the given node is within a versionable hierarchy.
     * @param node he node who's parentage is to be searched
     * @return true if this node or one of its parents is versionable, otherwise false
     */
    public static boolean inVersionableHierarchy(Node node) {
        return getVersionableAncestor(node).map(n -> true).orElse(false);
    }

    /**
     * @param node the node to test
     * @return true if this node is versionable
     */
    public static boolean isVersionable(Node node) {
        String name = "";
        boolean versionable = false;
        try {
            name = node.getName();
            versionable = JcrUtil.hasMixinType(node, "mix:versionable");
            return versionable;
        } catch (AccessDeniedException e) {
            log.debug("Access denied", e);
            throw new AccessControlException(e.getMessage());
        } catch (RepositoryException e) {
            throw new MetadataRepositoryException("Unable to check if versionable for Node " + name, e);
        }
    }

    /**
     * Sets the given node to checked-out status.
     *
     * @param node node to check-out
     */
    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
     */
    public static Version checkin(Node node) throws RepositoryException {
        // Checking if the checked-out property exists fixes a ModeShape bug where this property is not checked
        // if it is null on check-in in as the node.isCheckedOut() method does.  This can be null
        // when the node being checked-in has not been saved after being just made versionable; which 
        // can occur during upgrades.
        if (node.getSession().isLive() && node.isCheckedOut() && JcrPropertyUtil.hasProperty(node, JcrLexicon.IS_CHECKED_OUT.getString())) {
            return getVersionManager(node.getSession()).checkin(node.getPath());
        } else {
            return null;
        }
    }

    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)) {
                checkin(node);
            }

        } catch (RepositoryException e) {
            throw new MetadataRepositoryException("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)) {
{'fraction_non_alphanumeric': 0.08929822103936126, 'fraction_numerical': 0.001610869869729654, 'mean_word_length': 2.8026631158455393, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4050615', 'n_tokens_mistral': 3819, 'n_tokens_neox': 3751, 'n_words': 1195}