it = path.iterator();
        while(it.hasNext())
        {
            Path.ChildAssocElement elem = (Path.ChildAssocElement)it.next();
            QName qname = elem.getRef().getQName();
            if(qname != null)
            {
                ret = isHidden(qname.getLocalName());
                if(ret != null)
                {
                    break;
                }
            }
        }
        return ret;
    }
    /**
     * Hides the node by applying the hidden and not indexed aspects. The node will be hidden from all clients.
     * 
     * @param nodeRef nodeRef
     * @param cascadeHiddenAspect boolean
     * @param cascadeIndexControlAspect boolean
     * @param clientControlled boolean
     */
    public void hideNode(NodeRef nodeRef, boolean cascadeHiddenAspect, boolean cascadeIndexControlAspect, boolean clientControlled)
    {
        addHiddenAspect(nodeRef, 0, cascadeHiddenAspect, cascadeIndexControlAspect, clientControlled);
        addIndexControlAspect(nodeRef);
    }
    
    /**
     * Removes the hidden and index contol aspect.   Reverses the effect of calling hideNode(NodeRef nodeRef)
     * 
     * @param nodeRef the node to show
     * @param cascade true to cascade to all descendents of this node
     */
    public void showNode(NodeRef nodeRef, boolean cascade)
    {
        removeHiddenAspect(nodeRef);
        removeIndexControlAspect(nodeRef);
        
        if(cascade)
        {
	        for(ChildAssociationRef childRef : nodeService.getChildAssocs(nodeRef))
	        {
	        	showNode(childRef.getChildRef(), cascade);
	        }
        }
    }
    
    /**
     * Hides the node by applying the hidden and not indexed aspects. The node will be hidden from clients
     * according to the visibility mask.
     * 
     * @param nodeRef the node to hide
     * @param clientVisibilityMask int
     * @param cascadeHiddenAspect boolean
     * @param cascadeIndexControlAspect boolean
     * @param clientControlled boolean
     */
    public void hideNode(NodeRef nodeRef, int clientVisibilityMask, boolean cascadeHiddenAspect, boolean cascadeIndexControlAspect, boolean clientControlled)
    {
        addHiddenAspect(nodeRef, clientVisibilityMask, cascadeHiddenAspect, cascadeIndexControlAspect, clientControlled);
        addIndexControlAspect(nodeRef);
    }
    
    /**
     * Searches for nodes in the given store that should be hidden (i.e. match the hidden pattern)
     * and hides them if they are not already hidden.
     * 
     * @param storeRef StoreRef
     */
    public void checkHidden(StoreRef storeRef)
    {
        for(HiddenFileInfo filter : filters)
        {
            String pattern = filter.getFilter();
            ResultSet rs = searchForName(storeRef, pattern);
            for(NodeRef nodeRef : rs.getNodeRefs())
            {
                if(!hasHiddenAspect(nodeRef))
                {
                    hideNode(nodeRef, filter.getVisibilityMask(), true, true, false);
                }
            }
        }
    }
    
    /**
     * Checks whether the file should be hidden and applies the hidden and not indexed aspects if so.
     * 
     * @param fileInfo FileInfo
     * @param both     if true, will check if the node should not be hidden and remove hidden and index control
     *                 aspects if they are present
     * @param  checkChildren boolean
     * @return boolean
     */
    public boolean checkHidden(FileInfo fileInfo, boolean both, boolean checkChildren)
    {
        NodeRef nodeRef = fileInfo.getNodeRef();
        boolean ret = checkHidden(nodeRef, both, checkChildren);
        return ret;
    }
    /**
     * Hides the node by applying the hidden and not indexed aspects. The node will be hidden from clients
     * according to the visibility mask.
     * 
     * @param fileInfo file to make hidden
     * @param visibilityMask int
     * @param cascadeHiddenAspect boolean
     * @param cascadeIndexControlAspect boolean
     * @param clientControlled boolean
     */
    public void hideNode(FileInfoImpl fileInfo, int visibilityMask, boolean cascadeHiddenAspect, boolean cascadeIndexControlAspect, boolean clientControlled)
    {
        hideNode(fileInfo.getNodeRef(), visibilityMask, cascadeHiddenAspect, cascadeIndexControlAspect, clientControlled);
        fileInfo.setHidden(true);
    }
    
    private HiddenFileInfo isParentHidden(NodeRef nodeRef)
    {
        HiddenFileInfo info = null;
        ChildAssociationRef childAssocRef = nodeService.getPrimaryParent(nodeRef);
        if(childAssocRef != null)
        {
            NodeRef primaryParentNodeRef = childAssocRef.getParentRef();
            if(primaryParentNodeRef != null)
            {
                boolean isParentHidden = hasHiddenAspect(primaryParentNodeRef);
                if(isParentHidden)
                {
                    final Integer visibilityMask = (Integer)nodeService.getProperty(primaryParentNodeRef, ContentModel.PROP_VISIBILITY_MASK);
                    final Boolean cascadeHidden = (Boolean)nodeService.getProperty(primaryParentNodeRef, ContentModel.PROP_CASCADE_HIDDEN);
                    final Boolean cascadeIndexControl = (Boolean)nodeService.getProperty(primaryParentNodeRef, ContentModel.PROP_CASCADE_HIDDEN);
	    			final Boolean clientControlled = (Boolean)nodeService.getProperty(primaryParentNodeRef, ContentModel.PROP_CLIENT_CONTROLLED);
                    info = new HiddenFileInfo()
                    {
                        @Override
                        public boolean isHidden(String path)
                        {
                            // not checking by path but by hidden aspect, not used in this use case anyway
                            return false;
                        }
                        
                        @Override
                        public int getVisibilityMask()
                        {
                            // default is hidden to all clients if not specified
                            return visibilityMask != null ? visibilityMask.intValue() : 0;
                        }
                        @Override
                        public boolean isClientControlled()
                        {
                            return clientControlled != null ? clientControlled.booleanValue() : false;
                        }
                        
                        @Override
                        public String getFilter()
                        {
                            return null;
                        }
                        @Override
                        public boolean cascadeIndexControlAspect()
                        {
                            return cascadeIndexControl != null ? cascadeIndexControl.booleanValue() : false;
                        }
                        @Override
                        public boolean cascadeHiddenAspect()
                        {
                            return cascadeHidden != null ? cascadeHidden.booleanValue() : false;
                        }
                    };
                }
            }
        }
        return info;
    }
    
    public boolean isClientControlled(NodeRef nodeRef)
    {
    	Boolean clientControlled = (Boolean)nodeService.getProperty(nodeRef, ContentModel.PROP_CLIENT_CONTROLLED);
    	return clientControlled != null && clientControlled.booleanValue();
    }
    /**
     * Checks whether the file should be hidden and applies the hidden and not indexed aspects to it
     * and its children (if cascadeHidden == true). The visibility mask property will determine visibility for specific
     * clients.
     * 
     * Can optionally remove the hidden and index control aspects if the name of a node no longer matches the filter.
     * 
     * @param nodeRef NodeRef
     * @param both     if true, will check both if the node should not be hidden and remove hidden and index control
     * 				   aspects if they are present, and if the node should be hidden and add hidden and index control
     * 				   aspects if they are not present.
     * @param checkChildren boolean
     * @return true if the node is hidden, irrespective of the clientVisibility property value.
     */
    public boolean checkHidden(NodeRef nodeRef, boolean both, boolean checkChildren)
    {
        if(nodeService.hasAspect(nodeRef, ContentModel.ASPECT_HIDDEN))
        {
            Boolean isHiddenFlag = (Boolean)nodeService.getProperty(nodeRef, ContentModel.PROP_HIDDEN_FLAG);
            if(isHiddenFlag != null && isHiddenFlag)
            {
                logger.debug("node has hidden flag set");
                // node has hidden flag - we are not going to change anything.
                return true;
            }
        }
        
        boolean isHidden = false;
    	if(hasHiddenAspect(nodeRef) && isClientControlled(nodeRef))
    	{
    		// node is already hidden and client controlled -> hidden
    		isHidden = true;
    	}
    	else
    	{
            HiddenFileInfo info = isParentHidden(nodeRef);
            if(info != null && info.cascadeHiddenAspect())
            {
                // Parent has hidden aspect and cascade == true, so apply hidden aspect to children
                isHidden = true;
                if(!hasHiddenAspect(nodeRef))
                {
                    addHiddenAspect(nodeRef, info);
                }
                if(!hasIndexControlAspect(nodeRef))
                {
                    addIndexControlAspect(nodeRef);
                }
                applyHidden(nodeRef, info, checkChildren);
            }
	    	else
	        {
	    		// apply the "old" behaviour: try to match the node path against one of the registered hidden file patterns.
	    		info = findMatch(nodeRef);
	            if(info != null)
	            {
	                isHidden = true;
	                if(!hasHiddenAspect(nodeRef))
	                {
	                    addHiddenAspect(nodeRef, info);
	                }
	                else
	                {
	                    nodeService.setProperty(nodeRef, ContentModel.PROP_VISIBILITY_MASK, info.getVisibilityMask());
	                    nodeService.setProperty(nodeRef, ContentModel.PROP_CASCADE_HIDDEN, info.cascadeHiddenAspect());
	                    nodeService.setProperty(nodeRef, ContentModel.PROP_CASCADE_INDEX_CONTROL, info.cascadeIndexControlAspect());
	                }
	                if(!hasIndexControlAspect(nodeRef))
	                {
	                    addIndexControlAspect(nodeRef);
	                }
	                applyHidden(nodeRef, info, checkChildren);
	            }
	            else if(both)
	            {
	                // the file does not match the pattern, ensure that the hidden and index control aspects are not present
	                if(hasHiddenAspect(nodeRef))
	                {
	                    removeHiddenAspect(nodeRef);
	                }
	
	                if(hasIndexControlAspect(nodeRef))
	                {
	                    removeIndexControlAspect(nodeRef);
	                }
	                removeHidden(nodeRef);
	            }
	        }
        }
        return isHidden;
    }
    /**
     * Gets the visibility constraint for the given client on the given node.
     * 
     * @param client Client
     * @param nodeRef NodeRef
     * 
     * @return the visibility constraint for the given client and node
     */
    public Visibility getVisibility(Client client, NodeRef nodeRef)
    {
        Visibility ret = Visibility.Visible;
        if (! AuthenticationUtil.isRunAsUserTheSystemUser())
        {
            if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_HIDDEN))
            {
                Integer visibilityMask = (Integer)nodeService.getProperty(nodeRef, ContentModel.PROP_VISIBILITY_MASK);
                if (visibilityMask != null)
                {
                	if(client != null && client.equals(Client.admin))
                	{
                        ret = Visibility.Visible;
                	}
                	else if(visibilityMask.intValue() == 0)
                    {
                        ret = Visibility.NotVisible;
                    }
                    else if(client == null)
                    {
                        ret = Visibility.NotVisible;
                    }
                    else
                    {
                        ret = getVisibility(visibilityMask.intValue(), client);
                    }
                }
                else
                {
                    // no visibility mask property, so retain backwards compatibility with 3.4 hidden aspect behaviour
                    if(client == Client.cifs)
                    {
                        ret = Visibility.HiddenAttribute;
                    }
                    else if(client == Client.webdav || client == Client.nfs || client == Client.imap)
                    {
                        ret = Visibility.Visible;
                    }
                    else
                    {
                        ret = Visibility.NotVisible;
                    }
                }
            }
        }
        return ret;
    }
    private class HiddenFileInfoImpl implements ConfigurableHiddenFileInfo
    {
        private Pattern filter;
        private Set clientVisibility = new HashSet(10);
        private Set hiddenAttribute = new HashSet(10);
        private int visibilityMask;
        private boolean cascadeHiddenAspect;
        private boolean cascadeIndexControlAspect;
        private boolean cmisDisableHideConfig;
        public HiddenFileInfoImpl(String regexp, String visibility, String hiddenAttribute, boolean cascadeHiddenAspect, boolean cascadeIndexControlAspect)
        {
            this.filter = Pattern.compile(regexp);
            this.cascadeHiddenAspect = cascadeHiddenAspect;
            this.cascadeIndexControlAspect = cascadeIndexControlAspect;
            setVisibility(visibility);
            setHiddenAttribute(hiddenAttribute);
            calculateVisibilityMask();
        }
        
        public HiddenFileInfoImpl(String regexp, String visibility, String hiddenAttribute, boolean cascadeHiddenAspect, boolean cascadeIndexControlAspect, boolean cmisDisableHideConfig)
        {
            this(regexp,visibility,hiddenAttribute, cascadeHiddenAspect, cascadeIndexControlAspect);
            this.cmisDisableHideConfig = cmisDisableHideConfig;
        }
        private void setVisibility(String visibility)
        {
            if(visibility != null && !visibility.equals(""))
            {
                for(String clientStr : visibility.split(","))
                {
                    Client client = Client.getClient(clientStr);
                    this.clientVisibility.add(client);
                }
            }
        }
        
        private void setHiddenAttribute(String hiddenAttribute)
        {
            if(hiddenAttribute != null && !hiddenAttribute.equals(""))
            {
                for(String clientStr : hiddenAttribute.split(","))
                {
                    Client client = Client.getClient(clientStr);
                    this.hiddenAttribute.add(client);
                }
            }
        }
        
        private void calculateVisibilityMask()
        {
            visibilityMask = 0;
            for(Client client : getClients())
            {
                if(clientVisibility.contains(client))
                {
                    visibilityMask |= getClientVisibilityMask(client, Visibility.Visible);
                }
                else if(hiddenAttribute.contains(client))
                {
                    visibilityMask |= getClientVisibilityMask(client, Visibility.HiddenAttribute);
                }
                else
                {
                    visibilityMask |= getClientVisibilityMask(client, Visibility.NotVisible);
                }
            }
        }
        public String getFilter()
        {
            return filter.pattern();
        }
        public int getVisibilityMask()
        {
            return visibilityMask;
        }
        public boolean isHidden(String path)
        {
            return filter.matcher(path).matches();
        }
        
        public boolean cascadeHiddenAspect()
        {
        	return cascadeHiddenAspect;
        }
        
        public boolean cascadeIndexControlAspect()
        {
        	return cascadeIndexControlAspect;
        }
		@Override
		public boolean isClientControlled()
		{
			return false;
		}
		
        public boolean isCmisDisableHideConfig()
        {
            return cmisDisableHideConfig;
        }
        public void setCmisDisableHideConfig(boolean cmisDisableHideConfig)
        {
            this.cmisDisableHideConfig = cmisDisableHideConfig;
        }
    }
}