Package org.jahia.services.content

Source Code of org.jahia.services.content.JCRNodeWrapperImpl

/**
* This file is part of Jahia, next-generation open source CMS:
* Jahia's next-generation, open source CMS stems from a widely acknowledged vision
* of enterprise application convergence - web, search, document, social and portal -
* unified by the simplicity of web content management.
*
* For more information, please visit http://www.jahia.com.
*
* Copyright (C) 2002-2011 Jahia Solutions Group SA. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* As a special exception to the terms and conditions of version 2.0 of
* the GPL (or any later version), you may redistribute this Program in connection
* with Free/Libre and Open Source Software ("FLOSS") applications as described
* in Jahia's FLOSS exception. You should have received a copy of the text
* describing the FLOSS exception, and it is also available here:
* http://www.jahia.com/license
*
* Commercial and Supported Versions of the program (dual licensing):
* alternatively, commercial and supported versions of the program may be used
* in accordance with the terms and conditions contained in a separate
* written agreement between you and Jahia Solutions Group SA.
*
* If you are unsure which license is appropriate for your use,
* please contact the sales department at sales@jahia.com.
*/

package org.jahia.services.content;

import net.htmlparser.jericho.Source;
import net.htmlparser.jericho.TextExtractor;
import org.apache.commons.lang.StringUtils;
import org.apache.jackrabbit.commons.iterator.PropertyIteratorAdapter;
import org.apache.jackrabbit.core.JahiaSessionImpl;
import org.apache.jackrabbit.core.NodeImpl;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
import org.apache.jackrabbit.util.Text;
import org.slf4j.Logger;
import org.jahia.api.Constants;
import org.jahia.bin.Jahia;
import org.jahia.registries.ServicesRegistry;
import org.jahia.services.content.decorator.JCRFileContent;
import org.jahia.services.content.decorator.JCRPlaceholderNode;
import org.jahia.services.content.decorator.JCRSiteNode;
import org.jahia.services.content.decorator.JCRVersion;
import org.jahia.services.content.nodetypes.ExtendedNodeDefinition;
import org.jahia.services.content.nodetypes.ExtendedNodeType;
import org.jahia.services.content.nodetypes.ExtendedPropertyDefinition;
import org.jahia.services.content.nodetypes.NodeTypeRegistry;
import org.jahia.services.importexport.ReferencesHelper;
import org.jahia.services.usermanager.JahiaUser;
import org.jahia.utils.LanguageCodeConverters;

import javax.jcr.*;
import javax.jcr.lock.Lock;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.nodetype.NodeType;
import javax.jcr.query.*;
import javax.jcr.security.AccessControlException;
import javax.jcr.security.AccessControlManager;
import javax.jcr.security.Privilege;
import javax.jcr.version.*;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.*;

/**
* Wrappers around <code>javax.jcr.Node</code> to be able to inject
* Jahia specific actions.
*
* @author toto
*/
public class JCRNodeWrapperImpl extends JCRItemWrapperImpl implements JCRNodeWrapper {
    protected static final Logger logger = org.slf4j.LoggerFactory.getLogger(JCRNodeWrapper.class);

    protected Node objectNode = null;
    protected JCRFileContent fileContent = null;
    protected JCRSiteNode site = null;
    protected boolean parentAlreadyResolved = false;
    protected JCRNodeWrapper resolvedParentNode = null;

    protected Map<Locale, Node> i18NobjectNodes = null;

    private static final String REFERENCE_NODE_IDENTIFIERS_PROPERTYNAME = "j:referenceNodeIdentifiers";
    private static final String REFERENCE_PROPERTY_NAMES_PROPERTYNAME = "j:referencePropertyNames";
    public static final String EXTERNAL_IDENTIFIER_PROP_NAME_SEPARATOR = "___";

    private Map<String, ExtendedPropertyDefinition> applicablePropertyDefinition = new HashMap<String, ExtendedPropertyDefinition>();
    private Map<String, Boolean> hasPropertyCache = new HashMap<String, Boolean>();

    protected JCRNodeWrapperImpl(Node objectNode, String path, JCRNodeWrapper parent, JCRSessionWrapper session, JCRStoreProvider provider) {
        super(session, provider);
        this.objectNode = objectNode;
        setItem(objectNode);
        if (path != null) {
            this.localPath = path;
        } else {
            try {
                this.localPath = objectNode.getPath();
            } catch (RepositoryException e) {
                logger.error(e.getMessage(), e);
            }
        }
        if (parent != null) {
            parentAlreadyResolved = true;
            resolvedParentNode = parent;
        }
    }

    /**
     * {@inheritDoc}
     */
    public Node getRealNode() {
        return objectNode;
    }

    /**
     * {@inheritDoc}
     */
    public JCRNodeWrapper getParent() throws ItemNotFoundException, AccessDeniedException, RepositoryException {
        if (parentAlreadyResolved) {
            return resolvedParentNode;
        }

        try {
            if (localPath.equals("/") || localPath.equals(provider.getRelativeRoot())) {
                if (provider.getMountPoint().equals("/")) {
                    throw new ItemNotFoundException();
                }
                return (JCRNodeWrapper) session.getItem(StringUtils.substringBeforeLast(provider.getMountPoint(), "/"));
            } else {
                return (JCRNodeWrapper) session.getItem(StringUtils.substringBeforeLast(getPath(), "/"));
            }
        } catch (PathNotFoundException e) {
            throw new ItemNotFoundException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    public JahiaUser getUser() {
        return session.getUser();
    }

    /**
     * {@inheritDoc}
     */
    public Map<String, List<String[]>> getAclEntries() {
        try {
            Map<String, List<String[]>> results = new HashMap<String, List<String[]>>();

            Node n = objectNode;
            try {
                while (true) {
                    if (n.hasNode("j:acl")) {
                        Node acl = n.getNode("j:acl");
                        NodeIterator aces = acl.getNodes();
                        while (aces.hasNext()) {
                            Node ace = aces.nextNode();
                            if (ace.isNodeType("jnt:ace")) {
                                String principal = ace.getProperty("j:principal").getString();
                                String type = ace.getProperty("j:aceType").getString();
                                if (!ace.hasProperty(Constants.J_ROLES)) {
                                    continue;
                                }
                                Value[] roles = ace.getProperty(Constants.J_ROLES).getValues();

                                if (!results.containsKey(principal)) {
                                    results.put(principal, new ArrayList<String[]>());
                                }

                                for (Value role : roles) {
                                    results.get(principal).add(new String[]{n.getPath(), type, role.getString()});
                                }
                            }
                        }
                        if (acl.hasProperty("j:inherit") && !acl.getProperty("j:inherit").getBoolean()) {
                            return results;
                        }
                    }
                    n = n.getParent();
                }
            } catch (ItemNotFoundException e) {
                logger.debug(e.getMessage(), e);
            }


            return results;
        } catch (RepositoryException e) {
            logger.error(e.getMessage(), e);
        }
        return null;
    }


    /**
     * {@inheritDoc}
     */
    public Map<String, Map<String, String>> getActualAclEntries() {
        Map<String, Map<String, String>> actualACLs = new HashMap<String, Map<String, String>>();
        Map<String, List<String[]>> allACLs = getAclEntries();
        if (allACLs != null) {
            for (Map.Entry<String, List<String[]>> entry : allACLs.entrySet()) {
                Map<String, String> permissionsForUser = new HashMap<String, String>();
                // filtering stuff (path, GRANT/DENY, jcr:perm)
                for (String[] perms : entry.getValue()) {
                    if (permissionsForUser.containsKey(perms[2])) {
                        if (perms[0].equals(getPath())) {
                            permissionsForUser.put(perms[2], perms[1]);
                        }
                    } else {
                        permissionsForUser.put(perms[2], perms[1]);
                    }
                }
                actualACLs.put(entry.getKey(), permissionsForUser);
            }
        }
        return actualACLs;
    }

    /**
     * {@inheritDoc}
     */
    public Map<String, List<JCRNodeWrapper>> getAvailableRoles() throws RepositoryException {
        JCRNodeWrapper roles = session.getNode("/roles");
        Map<String,List<JCRNodeWrapper>> res = new HashMap<String,List<JCRNodeWrapper>>();
        NodeIterator ni = roles.getNodes();
        while (ni.hasNext()) {
            JCRNodeWrapper role = (JCRNodeWrapper) ni.nextNode();
            if (role.isNodeType("jnt:role")) {
                boolean add = false;
                if (role.hasProperty("j:hidden") && role.getProperty("j:hidden").getBoolean()) {
                    // skip
                } else if (role.hasProperty("j:nodeTypes")) {
                    Value[] values = role.getProperty("j:nodeTypes").getValues();
                    for (Value value : values) {
                        if (isNodeType(value.getString())) {
                            add = true;
                            break;
                        }
                    }
                } else {
                    add = true;
                }
                if (add) {
                    String roleGroup;
                    if (role.hasProperty("j:roleGroup")) {
                        roleGroup = role.getProperty("j:roleGroup").getString();
                    } else {
                        roleGroup = "default";
                    }
                    if (!res.containsKey(roleGroup)) {
                        res.put(roleGroup, new ArrayList<JCRNodeWrapper>());
                    }
                    res.get(roleGroup).add(role);
                }
            }
        }
        return res;
    }

    /**
     * {@inheritDoc}
     */
    public boolean hasPermission(String perm) {
        try {
            if (session.isSystem()) {
                return true;
            }

            AccessControlManager accessControlManager = getAccessControlManager();
            if (accessControlManager != null) {
//                        List<Privilege> privileges = convertPermToPrivileges(perm, accessControlManager);

                return accessControlManager.hasPrivileges(localPath, new Privilege[]{accessControlManager.privilegeFromName(perm)});
            }
            return true;
        } catch (AccessControlException e) {
            return false;
        } catch (RepositoryException re) {
            logger.error("Cannot check permission " + perm, re);
            return false;
        }
    }

    public AccessControlManager getAccessControlManager() throws RepositoryException {
        Session providerSession = session.getProviderSession(provider);
        // this is not a Jackrabbit implementation, we will use the new JCR 2.0 API instead.
        return providerSession.getAccessControlManager();
    }

    public Set<String> getPermissions() {
        Set<String> result = new HashSet<String>();
        try {
            AccessControlManager accessControlManager = getAccessControlManager();
            if (accessControlManager != null) {
                Privilege[] p = accessControlManager.getPrivileges(localPath);
                for (Privilege privilege : p) {
                    result.add(privilege.getName());
                    for (Privilege privilege1 : privilege.getAggregatePrivileges()) {
                        result.add(privilege1.getName());
                    }
                }
            }
        } catch (RepositoryException re) {
            logger.error("Cannot check perm ", re);
        }
        return result;
    }

    public BitSet getPermissionsAsBitSet() {
        BitSet b = null;
        try {
            AccessControlManager accessControlManager = getAccessControlManager();
            List<Privilege> app = Arrays.asList(accessControlManager.getPrivileges(localPath));
            List<Privilege> pr = Arrays.asList(accessControlManager.getSupportedPrivileges(localPath));               
            b = new BitSet(pr.size());
            for (Privilege privilege : app) {
                b.set(pr.indexOf(privilege));
                for (Privilege privilege1 : privilege.getAggregatePrivileges()) {
                    b.set(pr.indexOf(privilege1));
                }
            }
        } catch (RepositoryException e) {
            logger.error("Cannot check perm ", e);
        }
        return b;
    }


    /**
     * {@inheritDoc}
     */
    public boolean grantRoles(String principalKey, Set<String> roles) throws RepositoryException {
        Map m = new HashMap<String, String>();
        for (String role : roles) {
            m.put(role, "GRANT");
        }
        return changeRoles(principalKey, m);
    }

    /**
     * {@inheritDoc}
     */
    public boolean denyRoles(String principalKey, Set<String> roles) throws RepositoryException {
        Map m = new HashMap<String, String>();
        for (String role : roles) {
            m.put(role, "DENY");
        }
        return changeRoles(principalKey, m);
    }

    /**
     * {@inheritDoc}
     */
    public boolean changeRoles(String principalKey, Map<String, String> roles) throws RepositoryException {
        if (!objectNode.isCheckedOut() && objectNode.isNodeType(Constants.MIX_VERSIONABLE)) {
            objectNode.getSession().getWorkspace().getVersionManager().checkout(objectNode.getPath());
        }

        List<String> gr = new ArrayList<String>();
        List<String> den = new ArrayList<String>();

        for (Map.Entry<String, String> entry : roles.entrySet()) {
            if ("GRANT".equals(entry.getValue())) {
                gr.add(entry.getKey());
            } else if ("DENY".equals(entry.getValue())) {
                den.add(entry.getKey());
            }
        }

        Node acl = getOrCreateAcl();
        NodeIterator ni = acl.getNodes();
        Node aceg = null;
        Node aced = null;
        while (ni.hasNext()) {
            Node ace = ni.nextNode();
            if (ace.getProperty("j:principal").getString().equals(principalKey)) {
                if (ace.getProperty("j:aceType").getString().equals("GRANT")) {
                    aceg = ace;
                } else {
                    aced = ace;
                }
            }
        }
        if (aceg == null) {
            aceg = acl.addNode("GRANT_" + principalKey.replace(':', '_'), "jnt:ace");
            aceg.setProperty("j:principal", principalKey);
            aceg.setProperty("j:protected", false);
            aceg.setProperty("j:aceType", "GRANT");
        }
        if (aced == null) {
            aced = acl.addNode("DENY_" + principalKey.replace(':', '_'), "jnt:ace");
            aced.setProperty("j:principal", principalKey);
            aced.setProperty("j:protected", false);
            aced.setProperty("j:aceType", "DENY");
        }

        List<String> grClone = new ArrayList<String>(gr);
        List<String> denClone = new ArrayList<String>(den);
        if (aceg.hasProperty(Constants.J_ROLES)) {
            final Value[] values = aceg.getProperty(Constants.J_ROLES).getValues();
            for (Value value : values) {
                final String s = value.getString();
                if (!gr.contains(s) && !den.contains(s)) {
                    grClone.add(s);
                }
            }
        }
        if (aced.hasProperty(Constants.J_ROLES)) {
            final Value[] values = aced.getProperty(Constants.J_ROLES).getValues();
            for (Value value : values) {
                final String s = value.getString();
                if (!gr.contains(s) && !den.contains(s)) {
                    denClone.add(s);
                }
            }
        }
        String[] grs = new String[grClone.size()];
        grClone.toArray(grs);
        if (grs.length == 0) {
            aceg.remove();
        } else {
            aceg.setProperty(Constants.J_ROLES, grs);
        }
        String[] dens = new String[denClone.size()];
        denClone.toArray(dens);
        if (dens.length == 0) {
            aced.remove();
        } else {
            aced.setProperty(Constants.J_ROLES, dens);
        }

        return true;
    }

    /**
     * {@inheritDoc}
     */
    public boolean revokeRolesForPrincipal(String principalKey) throws RepositoryException {
        Node acl = getOrCreateAcl();

        NodeIterator ni = acl.getNodes();
        while (ni.hasNext()) {
            Node ace = ni.nextNode();
            if (ace.getProperty("j:principal").getString().equals(principalKey)) {
                ace.remove();
            }
        }
        return true;
    }

    /**
     * {@inheritDoc}
     */
    public boolean revokeAllRoles() throws RepositoryException {
        if (objectNode.hasNode("j:acl")) {
            objectNode.getNode("j:acl").remove();
            if(objectNode.isNodeType("jmix:accessControlled"))
                objectNode.removeMixin("jmix:accessControlled");
            return true;
        }
        return false;
    }

    /**
     * {@inheritDoc}
     */
    public boolean setAclInheritanceBreak(boolean inheritance) throws RepositoryException {
        try {
            getOrCreateAcl().setProperty("j:inherit", !inheritance);
        } catch (RepositoryException e) {
            logger.error("Cannot change acl", e);
            return false;
        }

        return true;
    }

    /**
     * Check if acl inheritance is broken on the given node or not
     *
     * @return true if ACL inheritance is broken
     * @throws RepositoryException
     */
    public boolean getAclInheritanceBreak() throws RepositoryException {
        if (objectNode.hasNode("j:acl")) {
            Node acl = objectNode.getNode("j:acl");
            return acl.hasProperty("j:inherit") && !acl.getProperty("j:inherit").getBoolean();
        }
        return false;
    }

    /**
     * Returns the ACL node of the given node or creates one
     *
     * @return the ACL <code>Node</code> for the given node
     * @throws RepositoryException
     */
    public Node getOrCreateAcl() throws RepositoryException {
        if (objectNode.hasNode("j:acl")) {
            return objectNode.getNode("j:acl");
        } else {
            if (!objectNode.isCheckedOut()) {
                objectNode.checkout();
            }
            objectNode.addMixin("jmix:accessControlled");
            return objectNode.addNode("j:acl", "jnt:acl");
        }
    }

    /**
     * {@inheritDoc}
     */
    public JCRNodeWrapper createCollection(String name) throws RepositoryException {
        return addNode(name, Constants.JAHIANT_FOLDER);
    }

    /**
     * {@inheritDoc}
     */
    public JCRNodeWrapper uploadFile(String name, final InputStream is, final String contentType) throws RepositoryException {
        checkLock();
       
        name = JCRContentUtils.escapeLocalNodeName(name);

        JCRNodeWrapper file = null;
        try {
            file = getNode(name);
            if (!file.isCheckedOut()) {
                file.getSession().checkout(file);
            }
        } catch (PathNotFoundException e) {
            logger.debug("file " + name + " does not exist, creating...");
            if (!isCheckedOut()) {
                getSession().checkout(this);
            }
            file = addNode(name, Constants.JAHIANT_FILE);
        }
        if (file != null) {
            file.getFileContent().uploadFile(is, contentType);
        } else {
            logger.error("can't write to file " + name + " because it doesn't exist");
        }
        return file;
    }

    /**
     * {@inheritDoc}
     */
    public JCRNodeWrapper addNode(String name) throws RepositoryException {
        checkLock();

        Node n = objectNode.addNode(name);
        return provider.getNodeWrapper(n, buildSubnodePath(name), this, session);
    }

    private String buildSubnodePath(String name) {
        if (localPath.equals("/")) {
            return localPath + name;
        } else {
            return localPath + "/" + name;
        }
    }

    /**
     * {@inheritDoc}
     */
    public JCRNodeWrapper addNode(String name, String type) throws RepositoryException {
        checkLock();

        Node n = objectNode.addNode(name, type);
        return provider.getNodeWrapper(n, buildSubnodePath(name), this, session);
    }

    public JCRNodeWrapper addNode(String name, String type, String identifier, Calendar created, String createdBy, Calendar lastModified, String lastModifiedBy) throws ItemExistsException, PathNotFoundException, NoSuchNodeTypeException, LockException, VersionException, ConstraintViolationException, RepositoryException {
        checkLock();

        if (objectNode instanceof NodeImpl) {
            JahiaSessionImpl jrSession = (JahiaSessionImpl) objectNode.getSession();

            jrSession.getNodeTypeInstanceHandler().setCreated(created);
            jrSession.getNodeTypeInstanceHandler().setCreatedBy(createdBy);
            jrSession.getNodeTypeInstanceHandler().setLastModified(lastModified);
            jrSession.getNodeTypeInstanceHandler().setLastModifiedBy(lastModifiedBy);
            try {
                if (identifier != null) {

                    org.jahia.services.content.nodetypes.Name jahiaName = new org.jahia.services.content.nodetypes.Name(name, NodeTypeRegistry.getInstance().getNamespaces());
                    Name qname = NameFactoryImpl.getInstance().create(jahiaName.getUri() == null ? "" : jahiaName.getUri(), jahiaName.getLocalName());
                    Name typeName = null;
                    if (type != null) {
                        org.jahia.services.content.nodetypes.Name jahiaTypeName = NodeTypeRegistry.getInstance()
                                .getNodeType(type).getNameObject();
                        typeName = NameFactoryImpl.getInstance().create(jahiaTypeName.getUri(),
                                jahiaTypeName.getLocalName());
                    }
                    Node child = ((NodeImpl) objectNode).addNode(qname, typeName, org.apache.jackrabbit.core.id.NodeId.valueOf(identifier));
                    return provider.getNodeWrapper(child, buildSubnodePath(name), this, session);
                } else {
                    return addNode(name, type);
                }
            } finally {
                jrSession.getNodeTypeInstanceHandler().setCreated(null);
                jrSession.getNodeTypeInstanceHandler().setCreatedBy(null);
                jrSession.getNodeTypeInstanceHandler().setLastModified(null);
                jrSession.getNodeTypeInstanceHandler().setLastModifiedBy(null);
            }
        } else {
            return addNode(name, type);
        }
    }

    /**
     * {@inheritDoc}
     */
    public JCRPlaceholderNode getPlaceholder() throws RepositoryException {
        return new JCRPlaceholderNode(this);
    }

    /**
     * {@inheritDoc}
     *
     * @deprecated As of JCR 2.0, {@link #getIdentifier()} should be used instead.
     */
    public String getUUID() throws UnsupportedRepositoryOperationException, RepositoryException {
        return objectNode.getUUID();
    }

    /**
     * {@inheritDoc}
     */
    public String getStorageName() {
        String uuid;
        try {
            uuid = objectNode.getIdentifier();
        } catch (RepositoryException e) {
            uuid = localPath;
        }
        return provider.getKey() + ":" + uuid;
    }

    /**
     * {@inheritDoc}
     */
    public String getAbsoluteUrl(ServletRequest request) {
        return provider.getAbsoluteContextPath(request) + getUrl();
    }

    /**
     * {@inheritDoc}
     */
    public String getUrl() {
        try {
        if (isNodeType(Constants.JAHIANT_FILE)) {
            return provider.getHttpPath() + "/" + getSession().getWorkspace().getName() + Text.escapePath(getPath());
        } else {
            String path = JCRSessionFactory.getInstance().getCurrentServletPath();
            if (path == null) {
                path = "/cms/render";
            }
            return Jahia.getContextPath() + path + "/" + getSession().getWorkspace().getName() + "/" + getSession().getLocale() + Text.escapePath(getPath()) + ".html";
        }
        } catch (RepositoryException e) {
            logger.error("Cannot get type",e);
            return null;
        }
    }

    /**
     * {@inheritDoc}
     */
    public String getAbsoluteWebdavUrl(final HttpServletRequest request) {
        return provider.getAbsoluteContextPath(request) + getWebdavUrl();
    }

    /**
     * {@inheritDoc}
     */
    public String getWebdavUrl() {
        try {
            return Jahia.getContextPath() + provider.getWebdavPath() + "/" + session.getWorkspace().getName() + objectNode.getPath();
        } catch (RepositoryException e) {
            logger.error("Cannot get file path", e);
        }
        return "";
    }

    /**
     * {@inheritDoc}
     */
    public List<String> getThumbnails() {
        List<String> names = new ArrayList<String>();
        try {
            NodeIterator ni = objectNode.getNodes();
            while (ni.hasNext()) {
                Node node = ni.nextNode();
                if (node.isNodeType("jnt:resource")) {
                    if (!node.getName().equals("jcr:content")) {
                        names.add(node.getName());
                    }
                }
            }
        } catch (RepositoryException e) {
            logger.error(e.getMessage(), e);
        }
        return names;
    }

    /**
     * {@inheritDoc}
     */
    public String getThumbnailUrl(String name) {
        return getUrl() + "?t=" +name;
    }

    /**
     * {@inheritDoc}
     */
    public Map<String, String> getThumbnailUrls() {
        List<String> list = getThumbnails();
        Map<String, String> map = new HashMap<String, String>(list.size());
        for (String thumbnailName : list) {
            map.put(thumbnailName, getThumbnailUrl(thumbnailName));
        }
        return map;
    }

    /**
     * {@inheritDoc}
     *
     * @deprecated
     */
    public List<JCRNodeWrapper> getChildren() {
        List<JCRNodeWrapper> list = new ArrayList<JCRNodeWrapper>();
        try {
            NodeIterator nodes = getNodes();
            while (nodes.hasNext()) {
                JCRNodeWrapper node = (JCRNodeWrapper) nodes.next();
                list.add(node);
            }
        } catch (RepositoryException e) {
            logger.error("Repository error", e);
        }
        return list;
    }

    /**
     * {@inheritDoc}
     */
    public List<JCRNodeWrapper> getEditableChildren() {
        List<JCRNodeWrapper> list = getChildren();
        list.add(new JCRPlaceholderNode(this));
        return list;
    }

    /**
     * @see #getNodes(String)
     * @deprecated
     */
    public List<JCRNodeWrapper> getChildren(String name) {
        List<JCRNodeWrapper> list = new ArrayList<JCRNodeWrapper>();
        try {
            NodeIterator nodes = getNodes(name);
            while (nodes.hasNext()) {
                JCRNodeWrapper node = (JCRNodeWrapper) nodes.next();
                list.add(node);
            }
        } catch (RepositoryException e) {
            logger.error("Repository error", e);
        }
        return list;
    }

    /**
     * {@inheritDoc}
     */
    public NodeIterator getNodes() throws RepositoryException {
        List<JCRNodeWrapper> list = new ArrayList<JCRNodeWrapper>();
        if (provider.getService() != null) {
            Map<String, JCRStoreProvider> mountPoints = provider.getSessionFactory().getMountPoints();
            for (Map.Entry<String, JCRStoreProvider> entry : mountPoints.entrySet()) {
                if (!entry.getKey().equals("/")) {
                    String mpp = entry.getKey().substring(0, entry.getKey().lastIndexOf('/'));
                    if (mpp.equals("")) mpp = "/";
                    if (mpp.equals(getPath())) {
                        JCRStoreProvider storeProvider = entry.getValue();
                        String root = storeProvider.getRelativeRoot();
                        final Node node = session.getProviderSession(storeProvider).getNode(root.length() == 0 ? "/" : root);
                        list.add(storeProvider.getNodeWrapper(node, "/", this, session));
                    }
                }
            }
        }

        NodeIterator ni = objectNode.getNodes();

        while (ni.hasNext()) {
            Node node = ni.nextNode();
            if (session.getLocale() == null || !node.getName().startsWith("j:translation_")) {
                try {
                    JCRNodeWrapper child = provider.getNodeWrapper(node, buildSubnodePath(node.getName()), this, session);
                    list.add(child);
                } catch (ItemNotFoundException e) {
                    if (logger.isDebugEnabled())
                        logger.debug(e.getMessage(), e);
                } catch (PathNotFoundException e) {
                    if (logger.isDebugEnabled())
                        logger.debug(e.getMessage(), e);
                }
            }
        }
        return new NodeIteratorImpl(list.iterator(), list.size());
    }

    /**
     * {@inheritDoc}
     */
    public NodeIterator getNodes(String name) throws RepositoryException {
        List<JCRNodeWrapper> list = new ArrayList<JCRNodeWrapper>();
        if (provider.getService() != null) {
            Map<String, JCRStoreProvider> mountPoints = provider.getSessionFactory().getMountPoints();

            if (mountPoints.containsKey(getPath() + "/" + name)) {
                JCRStoreProvider storeProvider = mountPoints.get(getPath() + "/" + name);
                final Node node = session.getProviderSession(storeProvider).getNode(storeProvider.getRelativeRoot());
                list.add(storeProvider.getNodeWrapper(node, "/", this, session));
            }
        }

        NodeIterator ni = objectNode.getNodes(name);

        while (ni.hasNext()) {
            Node node = ni.nextNode();
            JCRNodeWrapper child = provider.getNodeWrapper(node, buildSubnodePath(node.getName()), this, session);
            list.add(child);
        }

        return new NodeIteratorImpl(list.iterator(), list.size());
    }

    /**
     * {@inheritDoc}
     */
    public JCRNodeWrapper getNode(String s) throws PathNotFoundException, RepositoryException {
        if (objectNode.hasNode(s)) {
            if (!s.contains("/")) {
                return provider.getNodeWrapper(objectNode.getNode(s), buildSubnodePath(s), this, session);
            } else {
                return provider.getNodeWrapper(objectNode.getNode(s), session);
            }
        }
        List<JCRNodeWrapper> c = getChildren();
        for (JCRNodeWrapper jcrNodeWrapper : c) {
            if (jcrNodeWrapper.getName().equals(s)) {
                return jcrNodeWrapper;
            }
        }
        throw new PathNotFoundException(s);
    }

    public Map<String, String> getPropertiesAsString() throws RepositoryException {
            Map<String, String> res = new HashMap<String, String>();
            PropertyIterator pi = getProperties();
            if (pi != null) {
                while (pi.hasNext()) {
                    Property p = pi.nextProperty();
                    if (p.getType() == PropertyType.BINARY) {
                        continue;
                    }
                    if (!p.isMultiple()) {
                        res.put(p.getName(), p.getString());
                    } else {
                        Value[] vs = p.getValues();
                        StringBuffer b = new StringBuffer();
                        for (int i = 0; i < vs.length; i++) {
                            Value v = vs[i];
                            b.append(v.getString());
                            if (i + 1 < vs.length) {
                                b.append(" ");
                            }
                        }
                        res.put(p.getName(), b.toString());
                    }
                }
            }
        return res;
        }

    /**
     * {@inheritDoc}
     */
    public String getName() {
        try {
            if ((objectNode.getPath().equals("/") || objectNode.getPath().equals(provider.getRelativeRoot())) && provider.getMountPoint().length() > 1) {
                String mp = provider.getMountPoint();
                return mp.substring(mp.lastIndexOf('/') + 1);
            } else {
                return objectNode.getName(); //JCRContentUtils.decodeInternalName(name);
            }
        } catch (RepositoryException e) {
            logger.error("Repository error", e);
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public ExtendedNodeType getPrimaryNodeType() throws RepositoryException {
        try {
            return NodeTypeRegistry.getInstance().getNodeType(objectNode.getPrimaryNodeType().getName());
        } catch (NoSuchNodeTypeException e) {
            return NodeTypeRegistry.getInstance().getNodeType("nt:base");
        }
    }

    /**
     * {@inheritDoc}
     */
    public String getPrimaryNodeTypeName() throws RepositoryException {
        return objectNode.getPrimaryNodeType().getName();
    }

    /**
     * {@inheritDoc}
     */
    public ExtendedNodeType[] getMixinNodeTypes() throws RepositoryException {
        List<NodeType> l = new ArrayList<NodeType>();
        for (NodeType nodeType : objectNode.getMixinNodeTypes()) {
            l.add(NodeTypeRegistry.getInstance().getNodeType(nodeType.getName()));
        }
        return l.toArray(new ExtendedNodeType[l.size()]);
    }

    /**
     * {@inheritDoc}
     */
    public void addMixin(String s) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException {
        checkLock();
        applicablePropertyDefinition.clear();
        objectNode.addMixin(s);
    }

    /**
     * {@inheritDoc}
     */
    public void removeMixin(String s) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException {
        checkLock();
        applicablePropertyDefinition.clear();
        objectNode.removeMixin(s);
    }

    /**
     * {@inheritDoc}
     */
    public boolean canAddMixin(String s) throws NoSuchNodeTypeException, RepositoryException {
        try {
            checkLock();
        } catch (LockException e) {
            return false;
        }

        return objectNode.canAddMixin(s);
    }

    /**
     * {@inheritDoc}
     */
    public ExtendedNodeDefinition getDefinition() throws RepositoryException {
        NodeDefinition definition = objectNode.getDefinition();
        if (definition == null) {
            return null;
        }
        ExtendedNodeType nt = NodeTypeRegistry.getInstance().getNodeType(definition.getDeclaringNodeType().getName());
        if (definition.getName().equals("*")) {
            for (ExtendedNodeDefinition d : nt.getUnstructuredChildNodeDefinitions().values()) {
                ExtendedNodeType[] requiredPrimaryTypes = d.getRequiredPrimaryTypes();
                NodeType[] a2 = definition.getRequiredPrimaryTypes();
                boolean valid = true;
                for (ExtendedNodeType extendedNodeType : requiredPrimaryTypes) {
                    boolean found = false;
                    for (NodeType nodeType : a2) {
                        if (nodeType.getName().equals(extendedNodeType.getName())) {
                            found = true;
                            break;
                        }
                    }
                    if (!found) {
                        valid = false;
                        break;
                    }
                }
                if (valid)
                    return d;
            }

        } else {
            return nt.getNodeDefinition(definition.getName());
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public List<String> getNodeTypes() throws RepositoryException {
        List<String> results = new ArrayList<String>();
        results.add(objectNode.getPrimaryNodeType().getName());
        NodeType[] mixin = objectNode.getMixinNodeTypes();
        for (int i = 0; i < mixin.length; i++) {
            NodeType mixinType = mixin[i];
            results.add(mixinType.getName());
        }
        return results;
    }

    /**
     * {@inheritDoc}
     */
    public boolean isNodeType(String type) throws RepositoryException {
            return objectNode.isNodeType(type);
    }

    /**
     * {@inheritDoc}
     */
    public boolean isCollection() {
        return true;
//        try {
//            return objectNode.isNodeType("jmix:collection") || objectNode.isNodeType("nt:folder") || objectNode.getPath().equals("/");
//        } catch (RepositoryException e) {
//            return false;
//        }
    }

    /**
     * {@inheritDoc}
     */
    public boolean isFile() {
        try {
        return isNodeType(Constants.NT_FILE);
        } catch (RepositoryException e) {
            logger.error("Cannot get type",e);
            return false;
    }
    }

    /**
     * {@inheritDoc}
     */
    public boolean isPortlet() {
        try {
        return isNodeType(Constants.JAHIANT_PORTLET);
        } catch (RepositoryException e) {
            logger.error("Cannot get type",e);
            return false;
    }
    }

    /**
     * {@inheritDoc}
     */
    public Date getLastModifiedAsDate() {
        try {
            return objectNode.getProperty(Constants.JCR_LASTMODIFIED).getDate().getTime();
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public Date getContentLastModifiedAsDate() {
        try {
            Node content = objectNode.getNode(Constants.JCR_CONTENT);
            return content.getProperty(Constants.JCR_LASTMODIFIED).getDate().getTime();
        } catch (PathNotFoundException pnfe) {
        } catch (RepositoryException e) {
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public Date getLastPublishedAsDate() {
        try {
            return objectNode.getProperty(Constants.LASTPUBLISHED).getDate().getTime();
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public Date getContentLastPublishedAsDate() {
        try {
            Node content = objectNode.getNode(Constants.JCR_CONTENT);
            return content.getProperty(Constants.LASTPUBLISHED).getDate().getTime();
        } catch (PathNotFoundException pnfe) {
        } catch (RepositoryException e) {
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public Date getCreationDateAsDate() {
        try {
            return objectNode.getProperty(Constants.JCR_CREATED).getDate().getTime();
        } catch (RepositoryException e) {
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public String getCreationUser() {
        try {
            return objectNode.getProperty(Constants.JCR_CREATEDBY).getString();
        } catch (RepositoryException e) {
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public String getModificationUser() {
        try {
            return objectNode.getProperty(Constants.JCR_LASTMODIFIEDBY).getString();
        } catch (RepositoryException e) {
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public String getPublicationUser() {
        try {
            return objectNode.getProperty(Constants.LASTPUBLISHEDBY).getString();
        } catch (RepositoryException e) {
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public String getLanguage() {
        String language = null;
        try {
            if (Constants.JAHIANT_TRANSLATION.equals(getPrimaryNodeTypeName())) {
                language = getProperty("jcr:language").getString();
            }
        } catch (RepositoryException e1) {
        }
        if (language == null && getSession().getLocale() != null) {
            try {
                language = getI18N(getSession().getLocale()).getProperty("jcr:language")
                        .getString();
            } catch (Exception e) {
                language = getSession().getLocale().toString();
            }
        }
        return language;
    }

    public List<Locale> getExistingLocales() throws RepositoryException {
        List<Locale> r = new ArrayList<Locale>();
        NodeIterator ni = objectNode.getNodes("j:translation*");
        while (ni.hasNext()) {
            Node n = ni.nextNode();
            r.add(new Locale(n.getProperty("jcr:language").getString()));
        }
        return r;
    }

    /**
     * Return the internationalization node, containing localized properties
     *
     * @param locale
     * @return
     */
    public Node getI18N(Locale locale) throws RepositoryException {
        return getI18N(locale, true);
    }

    public boolean hasI18N(Locale locale) throws RepositoryException {
        return hasI18N(locale, true);
    }
   
    private boolean hasI18N(Locale locale, boolean fallback) throws RepositoryException {
        boolean b = (i18NobjectNodes != null && i18NobjectNodes.containsKey(locale)) || objectNode.hasNode(
                "j:translation_" + locale);
        if(!b && fallback) {
            final Locale fallbackLocale = getSession().getFallbackLocale();
            if (fallbackLocale != null && fallbackLocale != locale) {
                b = (i18NobjectNodes != null && i18NobjectNodes.containsKey(fallbackLocale)) || objectNode.hasNode(
                "j:translation_" + fallbackLocale);
            }
        }
        return b;
    }

    protected Node getI18N(Locale locale, boolean fallback) throws RepositoryException {
        //getSession().getLocale()
        if (i18NobjectNodes == null) {
            i18NobjectNodes = new HashMap<Locale, Node>();
        }
        Node node;
        if (i18NobjectNodes.containsKey(locale)) {
            node = i18NobjectNodes.get(locale);
            if (node != null) {
                return node;
            }
        } else if (objectNode.hasNode("j:translation_" + locale)) {
            node = objectNode.getNode("j:translation_" + locale);
            i18NobjectNodes.put(locale, node);
            return node;
        }

        if (fallback) {
            final Locale fallbackLocale = getSession().getFallbackLocale();
            if (fallbackLocale != null && fallbackLocale != locale) {
                return getI18N(fallbackLocale);
            }
        }
        throw new ItemNotFoundException(locale.toString());
    }

    public Node getOrCreateI18N(Locale locale) throws RepositoryException {
        try {
            return getI18N(locale, false);
        } catch (RepositoryException e) {
            Node t = objectNode.addNode("j:translation_" + locale, Constants.JAHIANT_TRANSLATION);
            t.setProperty("jcr:language", locale.toString());

            i18NobjectNodes.put(locale, t);
            return t;
        }
    }

    /**
     * {@inheritDoc}
     */
    public JCRPropertyWrapper getProperty(String name) throws javax.jcr.PathNotFoundException, javax.jcr.RepositoryException {
        ExtendedPropertyDefinition epd = getApplicablePropertyDefinition(name);
        if (epd != null) {
            if ((epd.getRequiredType() == PropertyType.WEAKREFERENCE) ||
                    (epd.getRequiredType() == PropertyType.REFERENCE)) {
                if (isNodeType(Constants.JAHIAMIX_EXTERNALREFERENCE)) {
                    return retrieveExternalReferenceProperty(name, epd);
                }
            }
            return internalGetProperty(name, epd);
        } else {
            throw new PathNotFoundException(name);
        }
    }

    private JCRPropertyWrapper internalGetProperty(String name, ExtendedPropertyDefinition epd) throws RepositoryException {
        final Locale locale = getSession().getLocale();
        if (epd == null) {
            epd = getApplicablePropertyDefinition(name);
        }
        if (locale != null) {
            if (epd != null && epd.isInternationalized()) {
                try {
                    final Node localizedNode = getI18N(locale);
                    return new JCRPropertyWrapperImpl(this, localizedNode.getProperty(name),
                            session, provider, epd,
                            name);
                } catch (ItemNotFoundException e) {
                    return new JCRPropertyWrapperImpl(this, objectNode.getProperty(name), session, provider, epd);
                }
            }
        }
        return new JCRPropertyWrapperImpl(this, objectNode.getProperty(name), session, provider, epd);
    }

    // TODO does not yet support i18n reference nodes

    private JCRPropertyWrapper retrieveExternalReferenceProperty(String name, ExtendedPropertyDefinition epd) throws RepositoryException {
        Property referenceProperty = getProperty(REFERENCE_PROPERTY_NAMES_PROPERTYNAME);
        Value[] propertyReferences = referenceProperty.getValues();
        String foundNodeIdentifier = null;
        for (Value propertyReference : propertyReferences) {
            String curPropertyReference = propertyReference.getString();
            String[] refParts = curPropertyReference.split(EXTERNAL_IDENTIFIER_PROP_NAME_SEPARATOR);
            String curNodeIdentifier = refParts[0];
            String curPropertyName = refParts[1];
            if (curPropertyName.equals(name)) {
                foundNodeIdentifier = curNodeIdentifier;
                break;
            }
        }
        if (foundNodeIdentifier != null) {
            Node referencedNode = getSession().getNodeByIdentifier(foundNodeIdentifier);
            Property nodeProperty = new ExternalReferencePropertyImpl(name, this, session, foundNodeIdentifier, referencedNode);
            return new JCRPropertyWrapperImpl(this, nodeProperty, session, provider, epd);
        } else {
            throw new PathNotFoundException("Couldn't find matching external property reference for name " + name);
        }
    }

    /**
     * {@inheritDoc}
     */
    public PropertyIterator getProperties() throws RepositoryException {
        final Locale locale = getSession().getLocale();
        if (locale != null) {
            return new LazyPropertyIterator(this, locale);
        }
        return new LazyPropertyIterator(this);
    }

    /**
     * {@inheritDoc}
     */
    public PropertyIterator getProperties(String s) throws RepositoryException {
        final Locale locale = getSession().getLocale();
        if (locale != null) {
            return new LazyPropertyIterator(this, locale, s);
        }
        return new LazyPropertyIterator(this, null, s);
    }


    /**
     * {@inheritDoc}
     */
    public String getPropertyAsString(String name) {
        try {
        return getPropertiesAsString().get(name);
        } catch (RepositoryException e) {
            return null;
        }
    }

    /**
     * {@inheritDoc}
     */
    public JCRPropertyWrapper setProperty(String name, String value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
        Value v = null;
        if (value != null) {
            v = getSession().getValueFactory().createValue(value);
        }
        return setProperty(name, v);
    }

    private String ensurePrefixedName(String name) {
        if (!name.startsWith("{")) {
            return name;
        }
        org.jahia.services.content.nodetypes.Name nameObj = new org.jahia.services.content.nodetypes.Name(
                name, NodeTypeRegistry.getInstance().getNamespaces());
        return StringUtils.isEmpty(nameObj.getPrefix()) ? nameObj.getLocalName() : nameObj
                .getPrefix() + ":" + nameObj.getLocalName();
    }

    /**
     * {@inheritDoc}
     */
    public JCRPropertyWrapper setProperty(String name, Value value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
        checkLock();
        hasPropertyCache.remove(name);

        name = ensurePrefixedName(name);
        final Locale locale = getSession().getLocale();
        ExtendedPropertyDefinition epd = getApplicablePropertyDefinition(name);
        if (epd == null) {
            throw new ConstraintViolationException("Couldn't find definition for property " + name);
        }

        if (value != null && PropertyType.UNDEFINED != epd.getRequiredType() && value.getType() != epd.getRequiredType()) {
            // if the type doesn't match the required type, we attempt a conversion.
            value = getSession().getValueFactory().createValue(value.getString(), epd.getRequiredType());
        }
        value = JCRStoreService.getInstance().getInterceptorChain().beforeSetValue(this, name, epd, value);

        if (value instanceof ExternalReferenceValue) {
            String nodeIdentifier = value.getString();
            JCRNodeWrapper node = getSession().getNodeByIdentifier(nodeIdentifier);
            return setExternalReferenceProperty(name, node, epd);
        }

        if (locale != null) {
            if (epd != null && epd.isInternationalized()) {
                return new JCRPropertyWrapperImpl(this, getOrCreateI18N(locale).setProperty(name, value), session, provider, epd, name);
            }
        }

        if (value == null) {
            objectNode.setProperty(name, value);
            return new JCRPropertyWrapperImpl(this, null, session, provider, epd);
        }
        return new JCRPropertyWrapperImpl(this, objectNode.setProperty(name, value), session, provider, epd);
    }

    /**
     * {@inheritDoc}
     */
    public JCRPropertyWrapper setProperty(String name, Value value, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
        checkLock();
        hasPropertyCache.remove(name);

        final Locale locale = getSession().getLocale();
        name = ensurePrefixedName(name);
        ExtendedPropertyDefinition epd = getApplicablePropertyDefinition(name);
        if (epd == null) {
            throw new ConstraintViolationException("Couldn't find definition for property " + name);
        }
        value = JCRStoreService.getInstance().getInterceptorChain().beforeSetValue(this, name, epd, value);

        if (value instanceof ExternalReferenceValue) {
            String nodeIdentifier = value.getString();
            JCRNodeWrapper node = getSession().getNodeByIdentifier(nodeIdentifier);
            return setExternalReferenceProperty(name, node, epd);
        }

        if (locale != null) {
            if (epd != null && epd.isInternationalized()) {
                return new JCRPropertyWrapperImpl(this, getOrCreateI18N(locale).setProperty(name, value, type), session, provider, epd, name);
            }
        }

        if (value == null) {
            objectNode.setProperty(name, value);
            return new JCRPropertyWrapperImpl(this, null, session, provider, epd);
        }
        return new JCRPropertyWrapperImpl(this, objectNode.setProperty(name, value, type), session, provider, epd);
    }

    /**
     * {@inheritDoc}
     */
    public JCRPropertyWrapper setProperty(String name, Value[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
        checkLock();
        hasPropertyCache.remove(name);
        final Locale locale = getSession().getLocale();
        name = ensurePrefixedName(name);
        ExtendedPropertyDefinition epd = getApplicablePropertyDefinition(name);
        if (epd == null) {
            throw new ConstraintViolationException("Couldn't find definition for property " + name);
        }

        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                if (values[i] != null && PropertyType.UNDEFINED != epd.getRequiredType() && values[i].getType() != epd.getRequiredType()) {
                    values[i] = getSession().getValueFactory()
                            .createValue(values[i].getString(), epd.getRequiredType());
                }
            }
        }

        values = JCRStoreService.getInstance().getInterceptorChain().beforeSetValues(this, name, epd, values);

        if (locale != null) {
            if (epd != null && epd.isInternationalized()) {
                return new JCRPropertyWrapperImpl(this, getOrCreateI18N(locale).setProperty(name, values), session, provider, epd, name);
            }
        }

        return new JCRPropertyWrapperImpl(this, objectNode.setProperty(name, values), session, provider, epd);
    }

    /**
     * {@inheritDoc}
     */
    public JCRPropertyWrapper setProperty(String name, Value[] values, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
        final Locale locale = getSession().getLocale();
        hasPropertyCache.remove(name);
        name = ensurePrefixedName(name);
        ExtendedPropertyDefinition epd = getApplicablePropertyDefinition(name);
        if (epd == null) {
            throw new ConstraintViolationException("Couldn't find definition for property " + name);
        }

        values = JCRStoreService.getInstance().getInterceptorChain().beforeSetValues(this, name, epd, values);

        if (locale != null) {
            if (epd != null && epd.isInternationalized()) {
                return new JCRPropertyWrapperImpl(this, getOrCreateI18N(locale).setProperty(name, values, type), session, provider, epd, name);
            }
        }

        return new JCRPropertyWrapperImpl(this, objectNode.setProperty(name, values, type), session, provider, epd);
    }

    /**
     * {@inheritDoc}
     */
    public JCRPropertyWrapper setProperty(String name, String[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
        Value[] v = null;
        if (values != null) {
            v = new Value[values.length];
            for (int i = 0; i < values.length; i++) {
                if (values[i] != null) {
                    v[i] = getSession().getValueFactory().createValue(values[i]);
                } else {
                    v[i] = null;
                }
            }
        }
        return setProperty(name, v);
    }

    /**
     * {@inheritDoc}
     */
    public JCRPropertyWrapper setProperty(String name, String[] values, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
        Value[] v = null;
        if (values != null) {
            v = new Value[values.length];
            for (int i = 0; i < values.length; i++) {
                if (values[i] != null) {
                    v[i] = getSession().getValueFactory().createValue(values[i], type);
                } else {
                    v[i] = null;
                }
            }
        }
        return setProperty(name, v);
    }

    /**
     * {@inheritDoc}
     */
    public JCRPropertyWrapper setProperty(String name, String value, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
        Value v = null;
        if (value != null) {
            v = getSession().getValueFactory().createValue(value, type);
        }
        return setProperty(name, v);
    }

    /**
     * {@inheritDoc}
     */
    public JCRPropertyWrapper setProperty(String name, InputStream value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
        Value v = null;
        if (value != null) {
            v = getSession().getValueFactory().createValue(value);
        }
        return setProperty(name, v);
    }

    /**
     * {@inheritDoc}
     */
    public JCRPropertyWrapper setProperty(String name, boolean value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
        Value v = getSession().getValueFactory().createValue(value);
        return setProperty(name, v);
    }

    /**
     * {@inheritDoc}
     */
    public JCRPropertyWrapper setProperty(String name, double value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
        Value v = getSession().getValueFactory().createValue(value);
        return setProperty(name, v);
    }

    /**
     * {@inheritDoc}
     */
    public JCRPropertyWrapper setProperty(String name, long value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
        Value v = getSession().getValueFactory().createValue(value);
        return setProperty(name, v);
    }

    /**
     * {@inheritDoc}
     */
    public JCRPropertyWrapper setProperty(String name, Calendar value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
        Value v = null;
        if (value != null) {
            v = getSession().getValueFactory().createValue(value);
        }
        return setProperty(name, v);
    }

    /**
     * {@inheritDoc}
     */
    public JCRPropertyWrapper setProperty(String name, Node value) throws ValueFormatException, VersionException, LockException, RepositoryException {
        Value v = null;
        if (value != null) {
            if (value instanceof JCRNodeWrapper) {
                value = ((JCRNodeWrapper) value).getRealNode();
            }
            ExtendedPropertyDefinition epd = getApplicablePropertyDefinition(name);
            if (epd != null) {
                if (value.getClass().getName().equals(getRealNode().getClass().getName())) {
                    v = getSession().getValueFactory().createValue(value, epd.getRequiredType() == PropertyType.WEAKREFERENCE);
                } else {
                    return setExternalReferenceProperty(name, value, epd);

                }
            }
        }
        return setProperty(name, v);
    }

    private JCRPropertyWrapper setExternalReferenceProperty(String name, Node node, ExtendedPropertyDefinition epd) throws RepositoryException {
        internalSetExternalReferenceProperty(name, node.getIdentifier());

        Property nodeProperty = new ExternalReferencePropertyImpl(name, this, session, node.getIdentifier(), node);
        return new JCRPropertyWrapperImpl(this, nodeProperty, session, provider, epd);
    }

    private void internalSetExternalReferenceProperty(String name, String nodeIdentifier) throws RepositoryException {
        // we are creating a reference to a node that is in another repository, we will use a special mixin for that.
        List<Value> nodeIdentifiers = null;
        List<Value> referenceProperties = null;
        if (!isNodeType(Constants.JAHIAMIX_EXTERNALREFERENCE)) {
            addMixin(Constants.JAHIAMIX_EXTERNALREFERENCE);
            nodeIdentifiers = new ArrayList<Value>();
            referenceProperties = new ArrayList<Value>();
        } else {
            Property nodeIdentifierProperty = getProperty(REFERENCE_NODE_IDENTIFIERS_PROPERTYNAME);
            if (nodeIdentifierProperty != null) {
                // We create an ArrayList because Arrays.asList returns a non-mutable list
                nodeIdentifiers = new ArrayList<Value>(Arrays.asList(nodeIdentifierProperty.getValues()));
            } else {
                nodeIdentifiers = new ArrayList<Value>();
            }
            Property referenceProperty = getProperty(REFERENCE_PROPERTY_NAMES_PROPERTYNAME);
            if (referenceProperty != null) {
                // We create an ArrayList because Arrays.asList returns a non-mutable list
                referenceProperties = new ArrayList<Value>(Arrays.asList(referenceProperty.getValues()));
            } else {
                referenceProperties = new ArrayList<Value>();
            }

        }
        Value newNodeIdentifierValue = getSession().getValueFactory().createValue(nodeIdentifier);
        if (!nodeIdentifiers.contains(newNodeIdentifierValue)) {
            nodeIdentifiers.add(newNodeIdentifierValue);
        }
        setProperty(REFERENCE_NODE_IDENTIFIERS_PROPERTYNAME, nodeIdentifiers.toArray(new Value[nodeIdentifiers.size()]));

        Value newPropertyReferenceValue = getSession().getValueFactory().createValue(nodeIdentifier + EXTERNAL_IDENTIFIER_PROP_NAME_SEPARATOR + name);
        if (!referenceProperties.contains(newPropertyReferenceValue)) {
            referenceProperties.add(newPropertyReferenceValue);
        }
        setProperty(REFERENCE_PROPERTY_NAMES_PROPERTYNAME, referenceProperties.toArray(new Value[referenceProperties.size()]));
    }

    protected void removeExternalReferenceProperty(String name) throws ItemNotFoundException, RepositoryException {
        if (!isNodeType(Constants.JAHIAMIX_EXTERNALREFERENCE)) {
            // quick sanity check, but usually we will do this in this method's caller
            return;
        }
        boolean mustRemoveMixin = false;
        String externalReferenceId = findExternalReferenceIdFromPropertyName(name);
        if (externalReferenceId == null) {
            return;
        }
        Property nodeIdentifierProperty = getProperty(REFERENCE_NODE_IDENTIFIERS_PROPERTYNAME);
        List<Value> identifiers = new ArrayList<Value>(Arrays.asList(nodeIdentifierProperty.getValues()));
        if (identifiers.size() == 0) {
            nodeIdentifierProperty.remove();
            return;
        }
        Value externalReferenceIdValue = getSession().getValueFactory().createValue(externalReferenceId);
        if (identifiers.contains(externalReferenceIdValue)) {
            identifiers.remove(externalReferenceIdValue);
        }
        if (identifiers.size() > 0) {
            setProperty(REFERENCE_NODE_IDENTIFIERS_PROPERTYNAME, identifiers.toArray(new Value[identifiers.size()]));
        } else {
            nodeIdentifierProperty.remove();
            mustRemoveMixin = true;
        }

        Property referencePropertyNames = getProperty(REFERENCE_PROPERTY_NAMES_PROPERTYNAME);
        List<Value> propertyNames = new ArrayList<Value>(Arrays.asList(referencePropertyNames.getValues()));
        if (propertyNames.size() == 0) {
            referencePropertyNames.remove();
        }
        Value externalReferencePropertyValue = getSession().getValueFactory().createValue(externalReferenceId + EXTERNAL_IDENTIFIER_PROP_NAME_SEPARATOR + name);
        if (propertyNames.contains(externalReferencePropertyValue)) {
            propertyNames.remove(externalReferencePropertyValue);
        }
        if (propertyNames.size() > 0) {
            setProperty(REFERENCE_NODE_IDENTIFIERS_PROPERTYNAME, propertyNames.toArray(new Value[propertyNames.size()]));
        } else {
            referencePropertyNames.remove();
            mustRemoveMixin = true;
        }

        if (internalHasProperty(name)) {
            Property property = internalGetProperty(name, null);
            property.remove();
        }

        if (mustRemoveMixin) {
            // as the node can have multiple reference properties, we only remove the mixin if all properties have
            // been removed.
            removeMixin(Constants.JAHIAMIX_EXTERNALREFERENCE);
        }

    }

    private String findExternalReferenceIdFromPropertyName(String name) throws ItemNotFoundException, RepositoryException {
        Property referenceProperty = getProperty(REFERENCE_PROPERTY_NAMES_PROPERTYNAME);
        if (referenceProperty == null) {
            throw new ItemNotFoundException("Couldn't find " + REFERENCE_PROPERTY_NAMES_PROPERTYNAME + " property on node " + getPath());
        }
        Value[] referencePropNames = referenceProperty.getValues();
        for (Value referencePropName : referencePropNames) {
            String[] referencePropNameParts = referencePropName.getString().split(EXTERNAL_IDENTIFIER_PROP_NAME_SEPARATOR);
            if (referencePropNameParts[1].equals(name)) {
                return referencePropNameParts[0];
            }
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public JCRPropertyWrapper setProperty(String name, Binary value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
        Value v = null;
        if (value != null) {
            v = getSession().getValueFactory().createValue(value);
        }
        return setProperty(name, v);
    }

    /**
     * {@inheritDoc}
     */
    public JCRPropertyWrapper setProperty(String name, BigDecimal value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
        Value v = null;
        if (value != null) {
            v = getSession().getValueFactory().createValue(value);
        }
        return setProperty(name, v);
    }


    /**
     * {@inheritDoc}
     */
    public boolean hasProperty(String propertyName) throws RepositoryException {
        if (hasPropertyCache.containsKey(propertyName)) {
            return hasPropertyCache.get(propertyName);
        }
        boolean result = internalHasProperty(propertyName);
        hasPropertyCache.put(propertyName, result);
        return result;
    }

    private boolean internalHasProperty(String propertyName) throws RepositoryException {
        final Locale locale = getSession().getLocale();
        if (locale != null && !propertyName.equals("jcr:language")) {
            try {
                ExtendedPropertyDefinition epd = getApplicablePropertyDefinition(propertyName);
                if (epd != null && epd.isInternationalized()) {
                    if (hasI18N(locale, true)) {
                        final Node localizedNode = getI18N(locale);
                        return localizedNode.hasProperty(propertyName);
                    }
                }
            } catch (ConstraintViolationException e) {
                return false;
            }
        }
        return objectNode.hasProperty(propertyName);
    }

    /**
     * {@inheritDoc}
     */
    public boolean hasProperties() throws RepositoryException {
        boolean result = objectNode.hasProperties();
        if (result) return true;
        final Locale locale = getSession().getLocale();
        if (locale != null) {
            if (hasI18N(locale, true)) {
                return getI18N(locale).hasProperties();
            }
        }
        return false;
    }

    /**
     * {@inheritDoc}
     */
    public JCRPropertyWrapper setProperty(String namespace, String name, String value) throws RepositoryException {
        String pref = objectNode.getSession().getNamespacePrefix(namespace);
        String key = pref + ":" + name;
        return setProperty(key, value);
    }

    /**
     * {@inheritDoc}
     */
    public List<JCRItemWrapper> getAncestors() throws RepositoryException {
        List<JCRItemWrapper> ancestors = new ArrayList<JCRItemWrapper>();
        for (int i = 0; i < getDepth(); i++) {
            try {
                ancestors.add(getAncestor(i));
            } catch (PathNotFoundException nfe) {
            } catch (AccessDeniedException ade) {
                return ancestors;
            }
        }
        return ancestors;
    }

    /**
     * {@inheritDoc}
     */
    public boolean rename(String newName) throws RepositoryException {
        checkLock();

        getSession().checkout(this);
        JCRNodeWrapper parent = getParent();
        getSession().checkout(parent);


        // the following code is use to conserve the ordering when renaming a node, we do this only if the parent
        // node is orderable.
        String nextNodeName = null;
        boolean nodePositionFound = false;
        if (parent.getPrimaryNodeType().hasOrderableChildNodes()) {
            NodeIterator nodeIterator = parent.getNodes();
            while (nodeIterator.hasNext()) {
                Node currentNode = nodeIterator.nextNode();
                if (currentNode.getIdentifier().equals(getIdentifier())) {
                    nodePositionFound = true;
                    if (nodeIterator.hasNext()) {
                        nextNodeName = nodeIterator.nextNode().getName();
                    } else {
                        // do nothing, we will keep null as the nextNode value
                    }
                    break;
                }
            }
        }

        getSession().move(getPath(), parent.getPath() + "/" + newName);
        this.localPath = parent.getPath() + "/" + newName;
        String mountPoint = getProvider().getMountPoint();
        if (mountPoint.length() > 1 && localPath.startsWith(mountPoint)) {
            localPath = StringUtils.substringAfter(localPath, mountPoint);
        }
        this.objectNode = getSession().getProviderSession(getProvider()).getNode(localPath);
        if ((nodePositionFound) && (parent.getPrimaryNodeType().hasOrderableChildNodes())) {
            parent.orderBefore(newName, nextNodeName);
        }
        return true;
    }

    /**
     * {@inheritDoc}
     */
    public boolean copy(String dest) throws RepositoryException {
        return copy(dest, getName());
    }

    /**
     * {@inheritDoc}
     */
    public boolean copy(String dest, String name) throws RepositoryException {
        JCRNodeWrapper node = (JCRNodeWrapper) session.getItem(dest);
        boolean sameProvider = (provider.getKey().equals(node.getProvider().getKey()));
        if (!sameProvider) {
            copy(node, name, true);
            node.save();
        } else {
            copy(node, name, true);
        }
        return true;
    }

    /**
     * {@inheritDoc}
     */
    public boolean copy(JCRNodeWrapper dest, String name, boolean allowsExternalSharedNodes) throws RepositoryException {
        Map<String, List<String>> references = new HashMap<String, List<String>>();
        boolean copy = copy(dest, name, allowsExternalSharedNodes, references);
        ReferencesHelper.resolveCrossReferences(getSession(), references);
        return copy;
    }

    /**
     * {@inheritDoc}
     */
    public boolean copy(JCRNodeWrapper dest, String name, boolean allowsExternalSharedNodes, Map<String, List<String>> references) throws RepositoryException {
        JCRNodeWrapper copy = null;
        try {
            copy = (JCRNodeWrapper) session
                    .getItem(dest.getPath() + "/" + name);
            getSession().checkout(copy);
        } catch (PathNotFoundException ex) {
            // node does not exist
        }

        final Map<String, String> uuidMapping = getSession().getUuidMapping();

        if (copy == null || copy.getDefinition().allowsSameNameSiblings()) {
            if (dest.isVersioned()) {
                session.checkout(dest);
            }
            String typeName = getPrimaryNodeTypeName();
            copy = dest.addNode(name, typeName);
        }

        try {
            NodeType[] mixin = objectNode.getMixinNodeTypes();
            for (NodeType aMixin : mixin) {
                copy.addMixin(aMixin.getName());
            }
        } catch (RepositoryException e) {
            logger.error("Error adding mixin types to copy", e);
        }

        if (copy != null) {
            uuidMapping.put(getIdentifier(), copy.getIdentifier());
            if (hasProperty("jcr:language")) {
                copy.setProperty("jcr:language", getProperty("jcr:language").getString());
            }
            copyProperties(copy, references);
        }

        NodeIterator ni = getNodes();
        while (ni.hasNext()) {
            JCRNodeWrapper source = (JCRNodeWrapper) ni.next();
            if (source.isNodeType("mix:shareable")) {
                if (uuidMapping.containsKey(source.getIdentifier())) {
                    // ugly save because to make node really shareable
                    session.save();
                    copy.clone(session.getNodeByUUID(uuidMapping.get(source.getIdentifier())), source.getName());
                } else if (allowsExternalSharedNodes) {
                    copy.clone(source, source.getName());
                } else {
                    source.copy(copy, source.getName(), allowsExternalSharedNodes, references);
                }
            } else {
                source.copy(copy, source.getName(), allowsExternalSharedNodes, references);
            }
        }

        return true;
    }

    public void copyProperties(JCRNodeWrapper destinationNode, Map<String, List<String>> references) throws RepositoryException {
        PropertyIterator props = getProperties();

        while (props.hasNext()) {
            Property property = props.nextProperty();
            try {
                if (!Constants.forbiddenPropertiesToCopy.contains(property.getName())) {
                    if (property.getType() == PropertyType.REFERENCE || property.getType() == PropertyType.WEAKREFERENCE) {
                        if (property.getDefinition().isMultiple() && (property.isMultiple())) {
                            Value[] values = property.getValues();
                            for (Value value : values) {
                                keepReference(destinationNode, references, property, value.getString());
                            }
                        } else {
                            keepReference(destinationNode, references, property, property.getValue().getString());
                        }
                    }
                    if (property.getDefinition().isMultiple() && (property.isMultiple())) {
                        destinationNode.setProperty(property.getName(), property.getValues());
                    } else {
                        destinationNode.setProperty(property.getName(), property.getValue());
                    }
                }
            } catch (Exception e) {
                logger.warn("Unable to copy property '" + property.getName() + "'. Skipping.", e);
            }
        }
    }

    private void keepReference(JCRNodeWrapper destinationNode, Map<String, List<String>> references, Property property, String value) throws RepositoryException {
        if (!references.containsKey(value)) {
            references.put(value, new ArrayList<String>());
        }
        references.get(value).add(destinationNode.getIdentifier() + "/" + property.getName());
    }


    /**
     * {@inheritDoc}
     */
    public void remove() throws VersionException, LockException, ConstraintViolationException, RepositoryException {
        if (!getSession().getWorkspace().getName().equals(Constants.LIVE_WORKSPACE) && provider.getMountPoint().equals("/")) {
            try {
                JCRNodeWrapper parent = getParent();
                getCorrespondingNodePath(Constants.LIVE_WORKSPACE);
                if (!parent.isNodeType("jmix:deletedChildren")) {
                    parent.addMixin("jmix:deletedChildren");
                    parent.setProperty("j:deletedChildren", new String[] {getIdentifier()});
                } else {
                    parent.getProperty("j:deletedChildren").addValue(getIdentifier());
                }
            } catch (ItemNotFoundException e) {
                // no live
            }
        }
        item.remove();
    }

    /**
     * {@inheritDoc}
     */
    public Lock lock(boolean isDeep, boolean isSessionScoped) throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException {
        return objectNode.lock(isDeep, isSessionScoped);
    }

    /**
     * {@inheritDoc}
     *
     * @param type
     */
    public boolean lockAndStoreToken(String type) throws RepositoryException {
        String l = getSession().isSystem() ? " system " : getSession().getUserID();

        return lockAndStoreToken(type, l);
    }

    /**
     * {@inheritDoc}
     *
     * @param type
     */
    public boolean lockAndStoreToken(String type, String userID) throws RepositoryException {
        if (!isNodeType("jmix:lockable")) {
            return false;
        }
        if (!objectNode.isLocked()) {
            lockNode(objectNode);
        } else {
            Property property = objectNode.getProperty("j:locktoken");
            String token = property.getString();

            objectNode.getSession().addLockToken(token);
        }

        addLockTypeValue(objectNode, userID + ":" + type);

        if (session.getLocale() != null && !isNodeType(Constants.JAHIANT_TRANSLATION)) {
            Node trans = null;
            try {
                trans = getI18N(session.getLocale());
                if (!trans.isLocked()) {
                    lockNode(trans);
                }
                addLockTypeValue(trans, userID + ":" + type);
            } catch (ItemNotFoundException e) {
            }
        }
        objectNode.getSession().save();
        return true;
    }

    private void lockNode(final Node objectNode) throws RepositoryException {
        getSession().checkout(objectNode);
        Lock lock = objectNode.lock(false, false);
        if (lock.getLockToken() != null) {
            try {
                objectNode.setProperty("j:locktoken", lock.getLockToken());
//                objectNode.getSession().removeLockToken(lock.getLockToken());
            } catch (RepositoryException e) {
                logger.error("Cannot store token for " + getPath(), e);
                objectNode.unlock();
            }
        } else {
            logger.error("Lost lock ! " + objectNode.getPath());
        }
    }

    private void addLockTypeValue(final Node objectNode, String l) throws RepositoryException {
        getSession().checkout(objectNode);

        if (objectNode.hasProperty(Constants.JAHIA_LOCKTYPES)) {
            Property property = objectNode.getProperty(Constants.JAHIA_LOCKTYPES);
            Value[] oldValues = property.getValues();
            for (Value value : oldValues) {
                if (value.getString().equals(l)) {
                    return;
                }
            }
            Value[] newValues = new Value[oldValues.length + 1];
            System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
            newValues[oldValues.length] = getSession().getValueFactory().createValue(l);
            property.setValue(newValues);
        } else {
            objectNode.setProperty(Constants.JAHIA_LOCKTYPES, new String[]{l});
        }
    }


    /**
     * {@inheritDoc}
     */
    public boolean isLocked() {
        try {
            return objectNode.isLocked();
        } catch (RepositoryException e) {
            return false;
        }
    }

    /**
     * {@inheritDoc}
     */
    public boolean isLockable() {
        try {
            return objectNode.isNodeType(Constants.MIX_LOCKABLE);
        } catch (RepositoryException e) {
            return false;
        }
    }

    public List<Locale> getLockedLocales() throws RepositoryException {
        List<Locale> r = new ArrayList<Locale>();
        NodeIterator ni = objectNode.getNodes("j:translation*");
        while (ni.hasNext()) {
            Node n = ni.nextNode();
            if (n.isLocked()) {
                r.add(new Locale(n.getProperty("jcr:language").getString()));
            }
        }
        return r;
    }

    public List<Locale> getLockedLocalesForUserAndType(String type) throws RepositoryException {
        List<Locale> r = new ArrayList<Locale>();
        NodeIterator ni = objectNode.getNodes("j:translation*");
        while (ni.hasNext()) {
            Node n = ni.nextNode();
            if (n.isLocked() && n.hasProperty(Constants.JAHIA_LOCKTYPES)) {
                String l = (getSession().isSystem() ? " system " : getSession().getUserID()) + ":" + type;
                Value[] v = n.getProperty(Constants.JAHIA_LOCKTYPES).getValues();
                for (Value value : v) {
                    if (value.getString().equals(l)) {
                        r.add(LanguageCodeConverters.getLocaleFromCode(n.getProperty("jcr:language").getString()));
                        break;
                    }
                }

            }
        }
        return r;
    }

    /**
     * {@inheritDoc}
     */
    public javax.jcr.lock.Lock getLock() {
        try {
            final javax.jcr.lock.Lock lock = objectNode.getLock();
            return new javax.jcr.lock.Lock() {
                public String getLockOwner() {
                    return lock.getLockOwner();
                }

                public boolean isDeep() {
                    return lock.isDeep();
                }

                public long getSecondsRemaining() throws RepositoryException {
                    return lock.getSecondsRemaining();
                }

                public boolean isLockOwningSession() {
                    return lock.isLockOwningSession();
                }

                public Node getNode() {
                    try {
                        return getProvider().getNodeWrapper(lock.getNode(), getSession());
                    } catch (RepositoryException e) {
                        logger.warn("Can't get wrapper for node holding lock", e);
                        return JCRNodeWrapperImpl.this;
                    }
                }

                public String getLockToken() {
                    return lock.getLockToken();
                }

                public boolean isLive() throws RepositoryException {
                    return lock.isLive();
                }

                public boolean isSessionScoped() {
                    return lock.isSessionScoped();
                }

                public void refresh() throws LockException, RepositoryException {
                    lock.isSessionScoped();
                }
            };
        } catch (RepositoryException e) {
            return null;
        }
    }

    /**
     * {@inheritDoc}
     */
    public void unlock() throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException {
        unlock("user");
    }

    public void unlock(String type)
            throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException,
            InvalidItemStateException, RepositoryException {
        unlock(type,getSession().getUserID());
    }

    public void unlock(String type, String userID)
            throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException,
            InvalidItemStateException, RepositoryException {
        if (!isLocked()) {
            throw new LockException("Node not locked");
        }

        if (session.getLocale() != null && !isNodeType(Constants.JAHIANT_TRANSLATION) && hasI18N(session.getLocale(),
                false)) {
            Node trans = getI18N(session.getLocale(), false);
            if (trans.isLocked()) {
                unlock(trans, type, userID);
            }
        }

        if (isNodeType(Constants.JAHIANT_TRANSLATION) && !getLockedLocalesForUserAndType(type).isEmpty()) {
            return;
        }

        unlock(objectNode, type, userID);
    }

    private void unlock(final Node objectNode, String type, String userID) throws RepositoryException {
        if (objectNode.hasProperty("j:locktoken")) {
            Property property = objectNode.getProperty("j:locktoken");
            String token = property.getString();
            Value[] types = objectNode.getProperty(Constants.JAHIA_LOCKTYPES).getValues();
            for (Value value : types) {
                String owner = StringUtils.substringBefore(value.getString(), ":");
                String currentType = StringUtils.substringAfter(value.getString(), ":");
                if (currentType.equals(type)) {
                    if (userID.equals(owner)) {
                        objectNode.getSession().addLockToken(token);
                        final Map<String, Value> valueList = new HashMap<String, Value>();
                        for (Value v : types) {
                            valueList.put(v.getString(), v);
                        }
                        valueList.remove(value.getString());
                        getSession().checkout(objectNode);

                        if (valueList.isEmpty()) {
                            objectNode.unlock();
                            property.remove();
                            objectNode.getProperty(Constants.JAHIA_LOCKTYPES).remove();
                        } else {
                            objectNode.setProperty(Constants.JAHIA_LOCKTYPES, valueList.values().toArray(new Value[valueList.size()]));
                        }
                        getSession().save();
                    }
                }
            }
        } else {
            objectNode.unlock();
        }
    }

    public void clearAllLocks() throws RepositoryException {
        if (session.getLocale() != null && !isNodeType(Constants.JAHIANT_TRANSLATION) && hasI18N(session.getLocale(),
                false)) {
            Node trans = getI18N(session.getLocale(), false);
            if (trans.isLocked()) {
                clearAllLocks(trans);
            }
        }

        if (isNodeType(Constants.JAHIANT_TRANSLATION) && !getLockedLocales().isEmpty()) {
            return;
        }

        clearAllLocks(objectNode);
    }

    private void clearAllLocks(final Node objectNode) throws RepositoryException {
        if (objectNode.hasProperty("j:locktoken")) {
            Property property = objectNode.getProperty("j:locktoken");
            String token = property.getString();

            objectNode.getSession().addLockToken(token);

            getSession().checkout(objectNode);
            objectNode.unlock();
            property.remove();
            objectNode.getProperty(Constants.JAHIA_LOCKTYPES).remove();

            getSession().save();
        }
    }

    protected void checkLock() throws RepositoryException {
        if (isLocked() && !session.isSystem()) {
            List<String> owners = getLockOwners(objectNode);
            if (owners.size() == 1 && owners.contains(session.getUserID())) {
                session.addLockToken(objectNode.getProperty("j:locktoken").getString());
            } else {
                throw new LockException("Node locked.");
            }
            if (session.getLocale() != null) {
                Node i18n = getI18N(session.getLocale());
                if (i18n.isLocked()) {
                    owners = getLockOwners(i18n);
                    if (owners.size() == 1 && owners.contains(session.getUserID())) {
                        session.addLockToken(i18n.getProperty("j:locktoken").getString());
                    } else {
                        throw new LockException("Node locked.");
                    }
                }
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public boolean holdsLock() throws RepositoryException {
        return objectNode.holdsLock();
    }

    /**
     * {@inheritDoc}
     */
    public String getLockOwner() throws RepositoryException {
        if (getLock() == null) {
            return null;
        }
        if (!"shared".equals(provider.getAuthenticationType())) {
            StringBuffer owners = new StringBuffer();
            List<String> lockOwners = getLockOwners(objectNode);
            if (lockOwners.isEmpty()) {
                return null;
            }
            for (String s : lockOwners) {
                owners.append(s).append(" ");
            }
            return owners.toString().trim();
        } else {
            return getSession().getUserID();
        }
    }

    public Map<String, List<String>> getLockInfos() throws RepositoryException {
        Map<String,List<String>> locks = new HashMap<String, List<String>>();
        List<String> lockInfos = getLockInfos(objectNode);
        if (!lockInfos.isEmpty()) {
            locks.put(null, lockInfos);
        }
        NodeIterator ni = objectNode.getNodes("j:translation*");
        while (ni.hasNext()) {
            Node n = ni.nextNode();
            lockInfos = getLockInfos(n);
            if (!lockInfos.isEmpty()) {
                locks.put(n.getProperty("jcr:language").getString(), lockInfos);
            }
        }
        return locks;
    }

    private List<String> getLockOwners(Node node) throws RepositoryException {
        List<String> types= getLockInfos(node);

        List<String> r = new ArrayList<String>();
        for (String type : types) {
            String owner = StringUtils.substringBefore(type, ":");
            if (!r.contains(owner)) {
                r.add(owner);
            }
        }
        return r;
    }

    private List<String> getLockInfos(Node node) throws RepositoryException {
        List<String> r = new ArrayList<String>();
        if (node.hasProperty(Constants.JAHIA_LOCKTYPES)) {
            Value[] values = node.getProperty(Constants.JAHIA_LOCKTYPES).getValues();
            for (Value value : values) {
                if (!r.contains(value.getString())) {
                    r.add(value.getString());
                }
            }
        }
        return r;
    }

    /**
     * {@inheritDoc}
     */
    public void versionFile() {
        try {
            objectNode.addMixin(Constants.MIX_VERSIONABLE);
        } catch (RepositoryException e) {
            logger.error("Error while adding versionable mixin type", e);
        }
    }

    /**
     * {@inheritDoc}
     */
    public boolean isVersioned() {
        try {
            return objectNode.isNodeType(Constants.MIX_VERSIONABLE);
        } catch (RepositoryException e) {
            logger.error("Error while checking if object node is versioned", e);
        }
        return false;
    }

    /**
     * {@inheritDoc}
     */
    public void checkpoint() {
        try {
            JCRObservationManager.doWorkspaceWriteCall(getSession(), JCRObservationManager.NODE_CHECKPOINT, new JCRCallback<Object>() {
                public Object doInJCR(JCRSessionWrapper session) throws RepositoryException {
                    session.getWorkspace().getVersionManager().checkpoint(localPath);
                    return null;
                }
            });
        } catch (RepositoryException e) {
            logger.error("Error setting checkpoint", e);
        }
    }

    /**
     * {@inheritDoc}
     */
    public List<String> getVersions() {
        List<String> results = new ArrayList<String>();
        try {
            VersionHistory vh = objectNode.getVersionHistory();
            VersionIterator vi = vh.getAllVersions();

            // forget root version
            vi.nextVersion();

            while (vi.hasNext()) {
                Version version = vi.nextVersion();
                results.add(version.getName());
            }
        } catch (RepositoryException e) {
            logger.error("Error while retrieving versions", e);
        }
        return results;
    }

    /**
     * {@inheritDoc}
     */
    public List<Version> getVersionsAsVersion() {
        List<Version> results = new ArrayList<Version>();
        try {
            VersionHistory vh = objectNode.getVersionHistory();
            VersionIterator vi = vh.getAllVersions();

            // forget root version
            vi.nextVersion();

            while (vi.hasNext()) {
                Version version = vi.nextVersion();
                results.add(version);
            }
            Collections.sort(results, new Comparator<Version>() {
                public int compare(Version o1, Version o2) {
                    try {
                        return o1.getCreated().compareTo(o2.getCreated());
                    } catch (RepositoryException e) {
                        return -1;
                    }
                }
            });
        } catch (RepositoryException e) {
            logger.error("Error while retrieving versions", e);
        }
        return results;
    }

    /**
     * {@inheritDoc}
     */
    public List<VersionInfo> getVersionInfos() throws RepositoryException {
        return ServicesRegistry.getInstance().getJCRVersionService().getVersionInfos(session, this);
    }

    /**
     * {@inheritDoc}
     */
    public JCRStoreProvider getJCRProvider() {
        return provider;
    }

    /**
     * {@inheritDoc}
     */
    public JCRStoreProvider getProvider() {
        return provider;
    }

    /**
     * {@inheritDoc}
     */
    public void orderBefore(String s, String s1) throws UnsupportedRepositoryOperationException, VersionException, ConstraintViolationException, ItemNotFoundException, LockException, RepositoryException {
        checkLock();
        objectNode.orderBefore(s, s1);
    }

    /**
     * {@inheritDoc}
     */
    public JCRItemWrapper getPrimaryItem() throws ItemNotFoundException, RepositoryException {
        return provider.getItemWrapper(objectNode.getPrimaryItem(), session);
    }

    /**
     * {@inheritDoc}
     */
    public int getIndex() throws RepositoryException {
        return objectNode.getIndex();
    }

    /**
     * {@inheritDoc}
     */
    public PropertyIterator getReferences() throws RepositoryException {
        return new PropertyIteratorImpl(objectNode.getReferences(), this);
    }

    /**
     * {@inheritDoc}
     */
    public boolean hasNode(String s) throws RepositoryException {
        // add mountpoints here
        final boolean b = objectNode.hasNode(s);
        if (b && Constants.LIVE_WORKSPACE.equals(getSession().getWorkspace().getName()) && !s.startsWith("j:translation")) {
            final JCRNodeWrapper wrapper;
            try {
                wrapper = (JCRNodeWrapper) getNode(s);
            } catch (RepositoryException e) {
                return false;
            }
            return wrapper.checkValidity();
        }
        return b;
    }

    /**
     * {@inheritDoc}
     */
    public boolean hasNodes() throws RepositoryException {
        return objectNode.hasNodes();
    }

    /**
     * {@inheritDoc}
     */
    public JCRVersion checkin() throws VersionException, UnsupportedRepositoryOperationException, InvalidItemStateException, LockException, RepositoryException {
        return (JCRVersion) session.getWorkspace().getVersionManager().checkin(getPath());
    }

    /**
     * {@inheritDoc}
     */
    public void checkout() throws UnsupportedRepositoryOperationException, LockException, RepositoryException {
        session.checkout(this);
    }

    /**
     * {@inheritDoc}
     */
    public void doneMerge(Version version) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException {
        VersionManager versionManager = session.getWorkspace().getVersionManager();
        versionManager.doneMerge(objectNode.getPath(), ((JCRVersion) version).getRealNode());
    }

    /**
     * {@inheritDoc}
     */
    public void cancelMerge(Version version) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException {
        VersionManager versionManager = session.getWorkspace().getVersionManager();
        versionManager.cancelMerge(objectNode.getPath(), ((JCRVersion) version).getRealNode());
    }

    /**
     * {@inheritDoc}
     */
    public void update(final String srcWorkspace) throws NoSuchWorkspaceException, AccessDeniedException, LockException, InvalidItemStateException, RepositoryException {
        JCRObservationManager.doWorkspaceWriteCall(getSession(), JCRObservationManager.NODE_UPDATE, new JCRCallback<Object>() {
            public Object doInJCR(JCRSessionWrapper session) throws RepositoryException {
                objectNode.update(srcWorkspace);
                return null;
            }
        });
    }

    /**
     * {@inheritDoc}
     */
    public NodeIterator merge(String s, boolean b) throws NoSuchWorkspaceException, AccessDeniedException, MergeException, LockException, InvalidItemStateException, RepositoryException {
        throw new UnsupportedOperationException();
    }

    /**
     * {@inheritDoc}
     */
    public String getCorrespondingNodePath(String s) throws ItemNotFoundException, NoSuchWorkspaceException, AccessDeniedException, RepositoryException {
        if (provider.getMountPoint().equals("/")) {
            return objectNode.getCorrespondingNodePath(s);
        } else {
            return provider.getMountPoint() + objectNode.getCorrespondingNodePath(s);
        }
    }

    /**
     * {@inheritDoc}
     */
    public boolean isCheckedOut() throws RepositoryException {
        VersionManager versionManager = session.getProviderSession(provider).getWorkspace().getVersionManager();
        boolean co = versionManager.isCheckedOut(objectNode.getPath());
        if (co && session.getLocale() != null) {
            try {
                co &= versionManager.isCheckedOut(getI18N(session.getLocale()).getPath());
            } catch (ItemNotFoundException e) {
                // no i18n node
            }
        }

        return co;
    }

    /**
     * {@inheritDoc}
     */
    public void restore(String s, boolean b) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException {
        getRealNode().restore(s, b);
    }

    /**
     * {@inheritDoc}
     */
    public void restore(Version version, boolean b) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, RepositoryException {
        getRealNode().restore(version instanceof JCRVersion ? ((JCRVersion) version).getRealNode() : version, b);
    }

    /**
     * {@inheritDoc}
     */
    public void restore(Version version, String s, boolean b) throws PathNotFoundException, ItemExistsException, VersionException, ConstraintViolationException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException {
        getRealNode().restore(version instanceof JCRVersion ? ((JCRVersion) version).getRealNode() : version, s, b);
    }

    /**
     * {@inheritDoc}
     */
    public void restoreByLabel(String s, boolean b) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException {
        getRealNode().restoreByLabel(s, b);
    }

    /**
     * {@inheritDoc}
     */
    public VersionHistory getVersionHistory() throws UnsupportedRepositoryOperationException, RepositoryException {
        return (VersionHistory) getProvider().getNodeWrapper((Node) getRealNode().getVersionHistory(), (JCRSessionWrapper) getSession());
    }

    /**
     * {@inheritDoc}
     */
    public Version getBaseVersion() throws UnsupportedRepositoryOperationException, RepositoryException {
        return (Version) getProvider().getNodeWrapper((Node) getRealNode().getBaseVersion(), (JCRSessionWrapper) getSession());
    }

    /**
     * {@inheritDoc}
     */
    public JCRFileContent getFileContent() {
        if (fileContent == null) {
            fileContent = new JCRFileContent(this, objectNode);
        }
        return fileContent;
    }


    /**
     * {@inheritDoc}
     */
    public ExtendedPropertyDefinition getApplicablePropertyDefinition(String propertyName)
            throws RepositoryException {
        ExtendedPropertyDefinition result = null;
        if (applicablePropertyDefinition.containsKey(propertyName)) {
            result = applicablePropertyDefinition.get(propertyName);
            return result;
        }

        List<ExtendedNodeType> types = new ArrayList<ExtendedNodeType>();
        Iterator<ExtendedNodeType> iterator = getNodeTypesIterator();
        while (iterator.hasNext()) {
            ExtendedNodeType type = iterator.next();
            final Map<String, ExtendedPropertyDefinition> definitionMap = type.getPropertyDefinitionsAsMap();
            applicablePropertyDefinition.putAll(definitionMap);
            if (definitionMap.containsKey(propertyName)) {
                result = definitionMap.get(propertyName);
                return result;
            }
            types.add(type);
        }

        if (isNodeType(Constants.JAHIANT_TRANSLATION) && !propertyName.equals("jcr:language")) {
            result = getParent().getApplicablePropertyDefinition(propertyName);
            if (result!=null) {
                applicablePropertyDefinition.put(propertyName, result);
                return result;
            }
        }

        for (ExtendedNodeType type : types) {
            for (ExtendedPropertyDefinition epd : type.getUnstructuredPropertyDefinitions().values()) {
                // check type .. ?
                result = epd;
                applicablePropertyDefinition.put(propertyName, result);
                return result;
            }
        }
        applicablePropertyDefinition.put(propertyName, result);
        return result;
    }

    public List<ExtendedPropertyDefinition> getReferenceProperties() throws RepositoryException {

        List<ExtendedPropertyDefinition> defs = new ArrayList<ExtendedPropertyDefinition>();
        List<ExtendedNodeType> types = new ArrayList<ExtendedNodeType>();
        Iterator<ExtendedNodeType> iterator = getNodeTypesIterator();

        if (isNodeType(Constants.JAHIANT_TRANSLATION)) {
            return getParent().getReferenceProperties();
        }

        while (iterator.hasNext()) {
            ExtendedNodeType type = iterator.next();
            final Map<String, ExtendedPropertyDefinition> definitionMap = type.getPropertyDefinitionsAsMap();
            for (ExtendedPropertyDefinition definition : definitionMap.values()) {
                if (definition.getRequiredType() == PropertyType.REFERENCE || definition.getRequiredType() == PropertyType.WEAKREFERENCE) {
                    defs.add(definition);
                }
            }

            types.add(type);
        }
        for (ExtendedNodeType type : types) {
            for (ExtendedPropertyDefinition definition : type.getUnstructuredPropertyDefinitions().values()) {
                if (definition.getRequiredType() == PropertyType.REFERENCE || definition.getRequiredType() == PropertyType.WEAKREFERENCE) {
                    defs.add(definition);
                }
            }
        }

        return defs;
    }

    public ExtendedNodeDefinition getApplicableChildNodeDefinition(String childName, String nodeType)
            throws ConstraintViolationException, RepositoryException {
        ExtendedNodeType requiredType = NodeTypeRegistry.getInstance().getNodeType(nodeType);

        List<ExtendedNodeType> types = new ArrayList<ExtendedNodeType>();
        Iterator<ExtendedNodeType> iterator = getNodeTypesIterator();
        while (iterator.hasNext()) {
            ExtendedNodeType type = iterator.next();
            final Map<String, ExtendedNodeDefinition> definitionMap = type.getChildNodeDefinitionsAsMap();
            if (definitionMap.containsKey(childName)) {
                ExtendedNodeDefinition epd = definitionMap.get(childName);
                for (String req : epd.getRequiredPrimaryTypeNames()) {
                    if (requiredType.isNodeType(req)) {
                        return epd;
                    }
                }
                throw new ConstraintViolationException("Definition type for " + childName + " on node " + getName() + " (" + getPrimaryNodeTypeName() + ") does not match " + nodeType);
            }
            types.add(type);
        }
        for (ExtendedNodeType type : types) {
            for (ExtendedNodeDefinition epd : type.getUnstructuredChildNodeDefinitions().values()) {
                for (String req : epd.getRequiredPrimaryTypeNames()) {
                    if (requiredType.isNodeType(req)) {
                        return epd;
                    }
                }
            }
        }
        throw new ConstraintViolationException("Cannot find definition for " + childName + " on node " + getName() + " (" + getPrimaryNodeTypeName() + ")");
    }

    private Iterator<ExtendedNodeType> getNodeTypesIterator() {
        return new Iterator<ExtendedNodeType>() {
            int i = 0;
            ExtendedNodeType next;
            boolean fetched = false;
            Iterator<ExtendedNodeType> mix = null;

            public boolean hasNext() {
                if (!fetched) {
                    try {
                        if (i == 0) {
                            next = getPrimaryNodeType();
                        } else if (i == 1 && isNodeType("nt:frozenNode")) {
                            next = NodeTypeRegistry.getInstance().getNodeType(objectNode.getProperty("jcr:frozenPrimaryType").getString());
                        } else {
                            if (mix == null) {
                                mix = Arrays.asList(getMixinNodeTypes()).iterator();
                            }
                            if (mix.hasNext()) {
                                next = mix.next();
                            } else {
                                next = null;
                            }
                        }
                    } catch (RepositoryException e) {
                        logger.warn(e.getMessage(), e);
                    } finally {
                        i++;
                    }
                    fetched = true;
                }
                return (next != null);
            }

            private boolean isNodeType(String nodeType) {
                boolean isNodeType = false;
                try {
                    isNodeType = objectNode.isNodeType(nodeType);
                } catch (RepositoryException e) {
                    logger.warn(e.getMessage(), e);
                }
                return isNodeType;
            }

            public ExtendedNodeType next() {
                if (!fetched) {
                    hasNext();
                }
                if (next != null) {
                    fetched = false;
                    return next;
                }
                throw new NoSuchElementException();
            }

            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }

    /**
     * {@inheritDoc}
     */
    public boolean equals(final Object o) {
        if (this == o) return true;
        if (o == null || this.getClass() != o.getClass()) return false;

        final JCRNodeWrapper fileNodeWrapper = (JCRNodeWrapper) o;

        return !(getPath() != null ? !getPath().equals(fileNodeWrapper.getPath()) : fileNodeWrapper.getPath() != null);
    }

    /**
     * {@inheritDoc}
     */
    public int hashCode() {
        return (getPath() != null ? getPath().hashCode() : 0);
    }

    /**
     * {@inheritDoc}
     *
     * @throws UnsupportedRepositoryOperationException
     *          as long as Jahia doesn't support it
     */
    public NodeIterator getNodes(String[] nameGlobs) throws RepositoryException {
        throw new UnsupportedRepositoryOperationException();
    }

    /**
     * {@inheritDoc}
     *
     * @throws UnsupportedRepositoryOperationException
     *          as long as Jahia doesn't support it
     */
    public PropertyIterator getProperties(String[] strings) throws RepositoryException {
        throw new UnsupportedRepositoryOperationException();
    }

    /**
     * {@inheritDoc}
     */
    public String getIdentifier() throws RepositoryException {
        return objectNode.getIdentifier();
    }

    /**
     * {@inheritDoc}
     */
    public PropertyIterator getReferences(String name) throws RepositoryException {
        return new PropertyIteratorImpl(objectNode.getReferences(name), this);
    }

    /**
     * {@inheritDoc}
     */
    public PropertyIterator getWeakReferences() throws RepositoryException {
        if (provider.getKey().equals("default")) {
            return new PropertyIteratorImpl(objectNode.getWeakReferences(), this);
        } else {
            return getExternalWeakReferences();
        }
    }

    /**
     * {@inheritDoc}
     *
     * @throws UnsupportedRepositoryOperationException
     *          as long as Jahia doesn't support it
     */
    public PropertyIterator getWeakReferences(String name) throws RepositoryException {
        return new PropertyIteratorImpl(objectNode.getWeakReferences(name), this);
    }

    /**
     * {@inheritDoc}
     *
     * @throws UnsupportedRepositoryOperationException
     *          as long as Jahia doesn't support it
     */
    public void setPrimaryType(String nodeTypeName) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException {
        throw new UnsupportedRepositoryOperationException();
    }

    /**
     * {@inheritDoc}
     */
    public NodeIterator getSharedSet() throws RepositoryException {
        List<JCRNodeWrapper> list = new ArrayList<JCRNodeWrapper>();

        NodeIterator ni = objectNode.getSharedSet();
        while (ni.hasNext()) {
            Node node = ni.nextNode();
            JCRNodeWrapper child = provider.getNodeWrapper(node, session);
            list.add(child);
        }

        return new NodeIteratorImpl(list.iterator(), list.size());
    }

    /**
     * {@inheritDoc}
     */
    public void removeSharedSet() throws VersionException, LockException, ConstraintViolationException, RepositoryException {
        objectNode.removeSharedSet();
    }

    /**
     * {@inheritDoc}
     */
    public void removeShare() throws VersionException, LockException, ConstraintViolationException, RepositoryException {
        objectNode.removeShare();
    }

    /**
     * {@inheritDoc}
     *
     * @throws UnsupportedRepositoryOperationException
     *          as long as Jahia doesn't support it
     */
    public void followLifecycleTransition(String transition) throws UnsupportedRepositoryOperationException, InvalidLifecycleTransitionException, RepositoryException {
        throw new UnsupportedRepositoryOperationException();
    }

    /**
     * {@inheritDoc}
     */
    public String[] getAllowedLifecycleTransistions() throws UnsupportedRepositoryOperationException, RepositoryException {
        return new String[0];
    }

    public JCRNodeWrapper clone(JCRNodeWrapper sharedNode, String name) throws ItemExistsException, VersionException,
            ConstraintViolationException, LockException,
            RepositoryException {
        if (!sharedNode.isNodeType("jmix:shareable")) {
            getSession().checkout(sharedNode);
            sharedNode.addMixin("jmix:shareable");
            sharedNode.getRealNode().getSession().save();

            try {
                final String path = sharedNode.getCorrespondingNodePath(Constants.LIVE_WORKSPACE);
                JCRTemplate.getInstance().doExecuteWithSystemSession(null, Constants.LIVE_WORKSPACE, new JCRCallback<Object>() {
                    public Object doInJCR(JCRSessionWrapper session) throws RepositoryException {
                        JCRNodeWrapper n = session.getNode(path);
                        getSession().checkout(n);
                        n.addMixin("jmix:shareable");
                        n.getRealNode().getSession().save();
                        return null;
                    }
                });
            } catch (ItemNotFoundException e) {
            } catch (RepositoryException e) {
                logger.warn(e.getMessage(), e);
            }
        }

        if (getRealNode() instanceof NodeImpl && sharedNode.getRealNode() instanceof NodeImpl) {
            String uri = "";
            if (name.contains(":")) {
                uri = session.getNamespaceURI(StringUtils.substringBefore(name, ":"));
                name = StringUtils.substringAfter(name, ":");
            }
            org.apache.jackrabbit.spi.Name jrname = NameFactoryImpl.getInstance().create(uri, name);

            NodeImpl node = (NodeImpl) getRealNode();

            try {
                return provider.getNodeWrapper(node.clone((NodeImpl) sharedNode.getRealNode(), jrname), buildSubnodePath(name),
                        this, session);
            } catch (RepositoryException e) {
                logger.error(e.getMessage(), e);
            }
        }
        throw new UnsupportedRepositoryOperationException();
    }

    public boolean checkValidity() {
        final JCRSessionWrapper jcrSessionWrapper = getSession();
        try {
            if (Constants.LIVE_WORKSPACE.equals(jcrSessionWrapper.getWorkspace().getName())) {
                if (jcrSessionWrapper.getLocale() != null) {
                    if (objectNode.hasProperty("j:published") && !objectNode.getProperty("j:published").getBoolean()) {
                        return false;
                    }
                }
                return checkLanguageValidity(null);
            }
        } catch (RepositoryException e) {
            return false;
        }
        return true;
    }

    public boolean checkLanguageValidity(Set<String> languages) {
        final JCRSessionWrapper jcrSessionWrapper = getSession();
        try {
            Locale locale = jcrSessionWrapper.getLocale();
            if (locale != null) {
                JCRSiteNode siteNode = getResolveSite();
                if (siteNode != null) {
                    Set<String> mandatoryLanguages = siteNode.getMandatoryLanguages();
                    List<Locale> locales = jcrSessionWrapper.isLive() ? siteNode.getActiveLanguagesAsLocales() : siteNode.getLanguagesAsLocales();
                    if (locales.size() == 0) {
                        return true;
                    }
                    if (!locales.contains(locale)) {
                        return false;
                    }
                    for (String mandatoryLanguage : mandatoryLanguages) {
                        if (!checkI18nAndMandatoryPropertiesForLocale(LanguageCodeConverters.getLocaleFromCode(mandatoryLanguage))) {
                            return false;
                        }
                    }
                }
                boolean b = checkI18nAndMandatoryPropertiesForLocale(locale);
                if(!b && siteNode != null && siteNode.isMixLanguagesActive()) {
                    b = checkI18nAndMandatoryPropertiesForLocale(LanguageCodeConverters.getLocaleFromCode(siteNode.getDefaultLanguage()));
                }
                return b;
            } else if (languages != null) {
                for (String language : languages) {
                    if (checkI18nAndMandatoryPropertiesForLocale(LanguageCodeConverters.getLocaleFromCode(language))) {
                        JCRSiteNode siteNode = getResolveSite();
                        if (siteNode != null) {
                            Set<String> mandatoryLanguages = siteNode.getMandatoryLanguages();
                            if (mandatoryLanguages == null || mandatoryLanguages.isEmpty()) {
                                return true;
                            }
                            for (String mandatoryLanguage : mandatoryLanguages) {
                                if (!checkI18nAndMandatoryPropertiesForLocale(LanguageCodeConverters.getLocaleFromCode(mandatoryLanguage))) {
                                    return false;
                                }
                            }
                        }
                    } else {
                        return false;
                    }
                }
            }
        } catch (RepositoryException e) {
            return false;
        }
        return true;
    }

    public boolean hasTranslations() throws RepositoryException {
        NodeIterator ni = objectNode.getNodes();
        boolean translated = false;
        while (ni.hasNext()) {
            Node n = ni.nextNode();
            if (n.getName().startsWith("j:translation_")) {
                translated = true;
                break;
            }
        }
        return translated;
    }

    public boolean checkI18nAndMandatoryPropertiesForLocale(Locale locale)
            throws RepositoryException {
        Node i18n = null;
        if (hasI18N(locale,false)) {
            i18n = getI18N(locale, false);
        }
        for (ExtendedPropertyDefinition def : getPrimaryNodeType().getPropertyDefinitionsAsMap().values()) {
            if (def.isInternationalized() && def.isMandatory()) {
                if (i18n == null || !i18n.hasProperty(def.getName())) {
                    return false;
                }
            }
        }
        return true;
    }


    public JCRSiteNode getResolveSite() throws RepositoryException {
        if (site != null) {
            return site;
        }

        try {
            String path = getPath();

            if (path.startsWith("/sites/") || path.startsWith("/templateSets/")) {
                int index = path.indexOf('/', path.indexOf('/',1)+1);
                if (index == -1) {
                    JCRNodeWrapper node = provider.getService().decorate(this);
                    if (node instanceof JCRSiteNode)  {
                        return (site = (JCRSiteNode) node);
                    }
                }
                try {
                    return (site = (JCRSiteNode) (getSession().getNode(index == -1 ? path : path.substring(0, index))));
                } catch (ClassCastException e) {
                    // if node is not a site ( eg ACL / workflow )
                }
            }

            return (site = (JCRSiteNode) (getSession().getNode(JCRContentUtils.getSystemSitePath())));
        } catch (PathNotFoundException e) {
        } catch (ItemNotFoundException e) {
        }
        return null;
//        return ServicesRegistry.getInstance().getJahiaSitesService().getDefaultSite();
    }

    public PropertyIterator getExternalWeakReferences() throws RepositoryException {
        QueryManager queryManager = session.getWorkspace().getQueryManager();
        Query query = queryManager.createQuery("SELECT * FROM [jmix:externalReference] as n WHERE n.[" + REFERENCE_NODE_IDENTIFIERS_PROPERTYNAME + "]='" + getIdentifier() + "'", Query.JCR_SQL2);
        QueryResult queryResult = query.execute();
        RowIterator rowIterator = queryResult.getRows();
        List<Property> matchingProperties = new ArrayList<Property>();
        while (rowIterator.hasNext()) {
            Row row = rowIterator.nextRow();
            JCRNodeWrapper node = (JCRNodeWrapper) row.getNode();
            Property referenceProperty = node.getProperty(REFERENCE_PROPERTY_NAMES_PROPERTYNAME);
            Value[] propertyReferences = referenceProperty.getValues();
            String foundPropertyName = null;
            for (Value propertyReference : propertyReferences) {
                String curPropertyReference = propertyReference.getString();
                String[] refParts = curPropertyReference.split(EXTERNAL_IDENTIFIER_PROP_NAME_SEPARATOR);
                String curNodeIdentifier = refParts[0];
                String curPropertyName = refParts[1];
                if (curNodeIdentifier.equals(getIdentifier())) {
                    foundPropertyName = curPropertyName;
                    break;
                }
            }
            if (foundPropertyName != null) {
                ExtendedPropertyDefinition epd = node.getApplicablePropertyDefinition(foundPropertyName);
                if (epd != null)  {
                    Property nodeProperty = new ExternalReferencePropertyImpl(foundPropertyName, node, session, getIdentifier(), this);
                    matchingProperties.add(new JCRPropertyWrapperImpl(this, nodeProperty, session, provider, epd));
                }
            } else {
                throw new PathNotFoundException("Couldn't find matching external property reference for name " + foundPropertyName);
            }

        }
        return new PropertyIteratorAdapter(matchingProperties.iterator());
    }


    public String getDisplayableName() {
        String title = null;
        try {
            title = getProperty(Constants.JCR_TITLE).getValue().getString();
        } catch (RepositoryException e) {
            //Search for primary field if present
            try {
                String itemName = getPrimaryNodeType().getPrimaryItemName();
                if (itemName != null) {
                    String s = getProperty(itemName).getValue().getString();
                    title = new TextExtractor(new Source(s != null ? s : getName())).toString();
                }
            } catch (RepositoryException e1) {
                title = null;
            }
        }
        return title != null ? title : getName();
    }

    public void flushLocalCaches() {
        hasPropertyCache.clear();
    }
}
TOP

Related Classes of org.jahia.services.content.JCRNodeWrapperImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.