qnames = dictionaryService.getSubTypes(ContentModel.TYPE_CONTENT, true);
        fileTypeQNames.addAll(qnames);
        fileTypeQNames.add(ContentModel.TYPE_CONTENT);
        qnames = dictionaryService.getSubTypes(ContentModel.TYPE_LINK, true);
        fileTypeQNames.addAll(qnames);
        fileTypeQNames.add(ContentModel.TYPE_LINK);
        
        return fileTypeQNames;
    }
    
    /**
     * A deep version of listSimple.   Which recursively walks down the tree from a given starting point, returning 
     * the node refs of files or folders found along the way.
     * 
     * The folder filter is called for each sub-folder to determine whether to search in that sub-folder, should a subfolder be excluded 
     * then all its chidren are excluded as well.
     * 
     * @param contextNodeRef the starting point.
     * @param files return nodes of type files.
     * @param folders return nodes of type folders.
     * @param folderFilter filter controls which folders to search.  If null then all subfolders are searched.
     * @return list of node references
     */
   /* 
    * MER: I've added this rather than changing listSimple to minimise the risk of breaking 
    * the existing code.   This is a quick performance improvement between using 
    * XPath which is awful or adding new methods to the NodeService/DB   This is also a dangerous method in that it can return a 
    * lot of data and take a long time.
    */  
    private List listSimpleDeep(NodeRef contextNodeRef, boolean files, boolean folders, SubFolderFilter folderFilter)
    {
        if(logger.isDebugEnabled())
        {
            logger.debug("searchSimpleDeep contextNodeRef:" + contextNodeRef);
        }
        // To hold the results.
        List result = new ArrayList();
        
        // Build a list of folder types
        Set folderTypeQNames = buildFolderTypes();
        Set fileTypeQNames = (files ? buildFileTypes() : new HashSet(0));
        
        if(!folders && !files)
        {
            return Collections.emptyList();
            
        }
        
        // Shortcut
        if (folderTypeQNames.size() == 0)
        {
            return Collections.emptyList();
        }
        
        Stack toSearch = new Stack();
        toSearch.push(contextNodeRef);
        
        // Now we need to walk down the folders.
        while(!toSearch.empty())
        {
            NodeRef currentDir = toSearch.pop();
            
            List folderAssocRefs = nodeService.getChildAssocs(currentDir, folderTypeQNames);
            
            for (ChildAssociationRef folderRef : folderAssocRefs)
            {
                // We have some child folders
                boolean include = true;
                if(folderFilter != null)
                {
                    include = folderFilter.isEnterSubfolder(folderRef);
                    if(include)
                    {
                        // yes search in these subfolders
                        toSearch.push(folderRef.getChildRef());
                    }
                }
                else
                {
                    // No filter - Add the folders in the currentDir
                    toSearch.push(folderRef.getChildRef());
                }
                
                if(folders && include)
                {
                    result.add(folderRef.getChildRef());
                }
            }
                
            if(files)
            {
                // Add the files in the current dir
                List fileAssocRefs = nodeService.getChildAssocs(currentDir, fileTypeQNames);
                for (ChildAssociationRef fileRef : fileAssocRefs)
                {
                    result.add(fileRef.getChildRef());
                }
            }
        }
        
        if(logger.isDebugEnabled())
        {
            logger.debug("searchSimpleDeep finished size:" + result.size());
        }
 
        // Done
        return result;
    }
    
    /**
     * @see #move(NodeRef, NodeRef, String)
     */
    @Override
    @Extend(traitAPI=FileFolderServiceTrait.class,extensionAPI=FileFolderServiceExtension.class)
    public FileInfo rename(NodeRef sourceNodeRef, String newName) throws FileExistsException, FileNotFoundException
    {
        return moveOrCopy(sourceNodeRef, null, null, newName, true);
    }
    /**
     * @see #moveOrCopy(NodeRef, NodeRef, NodeRef, String, boolean)
     */
    @Override
    public FileInfo move(NodeRef sourceNodeRef, NodeRef targetParentRef, String newName) throws FileExistsException, FileNotFoundException
    {
        return moveOrCopy(sourceNodeRef, null, targetParentRef, newName, true);
    }
    
    /**
     * @see #moveOrCopy(NodeRef, NodeRef, NodeRef, String, boolean)
     */
    @Override
    public FileInfo moveFrom(NodeRef sourceNodeRef, NodeRef sourceParentRef, NodeRef targetParentRef, String newName) throws FileExistsException, FileNotFoundException
    {
        return moveOrCopy(sourceNodeRef, sourceParentRef, targetParentRef, newName, true);
    }
    
    /**
     * @deprecated
     */
    @Override
    public FileInfo move(NodeRef sourceNodeRef, NodeRef sourceParentRef, NodeRef targetParentRef, String newName) throws FileExistsException, FileNotFoundException
    {
        return moveOrCopy(sourceNodeRef, sourceParentRef, targetParentRef, newName, true);
    }
    /**
     * @see #moveOrCopy(NodeRef, NodeRef, NodeRef, String, boolean)
     */
    @Override
    public FileInfo copy(NodeRef sourceNodeRef, NodeRef targetParentRef, String newName) throws FileExistsException, FileNotFoundException
    {
        return moveOrCopy(sourceNodeRef, null, targetParentRef, newName, false);
    }
    /**
     * Implements both move and copy behaviour
     * 
     * @param move true to move, otherwise false to copy
     */
    private FileInfo moveOrCopy(NodeRef sourceNodeRef, NodeRef sourceParentRef, NodeRef targetParentRef, String newName, boolean move) throws FileExistsException, FileNotFoundException
    {
        // get file/folder in its current state
        FileInfo beforeFileInfo = toFileInfo(sourceNodeRef, true);
        // check the name - null means keep the existing name
        if (newName == null)
        {
            newName = beforeFileInfo.getName();
        }
        boolean nameChanged = (newName.equals(beforeFileInfo.getName()) == false);
        AssociationCopyInfo targetInfo = getAssociationCopyInfo(nodeService, sourceNodeRef, sourceParentRef, newName, nameChanged);
        QName qname = targetInfo.getTargetAssocQName();
        boolean isPrimaryParent = targetInfo.getSourceParentAssoc().isPrimary();
        ChildAssociationRef assocRef = targetInfo.getSourceParentAssoc();
        if (targetParentRef == null)
        {
            targetParentRef = assocRef.getParentRef();
        }
        
        boolean changedParent = !targetParentRef.equals(assocRef.getParentRef());
        // there is nothing to do if both the name and parent folder haven't changed
        if (!nameChanged && !changedParent)
        {
            if (logger.isDebugEnabled())
            {
                logger.debug("Doing nothing - neither filename or parent has changed: \n" +
                        "   parent: " + targetParentRef + "\n" +
                        "   before: " + beforeFileInfo + "\n" +
                        "   new name: " + newName);
            }
            return beforeFileInfo;
        }
        
        QName targetParentType = nodeService.getType(targetParentRef);
        
        // Fix AWC-1517 & ALF-5569
        QName assocTypeQname = null;
        if (nameChanged && move)
        {
            // if it's a rename use the existing assoc type
            assocTypeQname = assocRef.getTypeQName();
        }
        else
        {
            if (dictionaryService.isSubClass(targetParentType, ContentModel.TYPE_FOLDER))
            {
                assocTypeQname = ContentModel.ASSOC_CONTAINS; // cm:folder -> cm:contains
            }
            else if (dictionaryService.isSubClass(targetParentType, ContentModel.TYPE_CONTAINER))
            {
                assocTypeQname = ContentModel.ASSOC_CHILDREN; // sys:container -> sys:children
            }
            else
            {
                throw new InvalidTypeException("Unexpected type (" + targetParentType + ") for target parent: " + targetParentRef);
            }
        }
        
        // move or copy
        NodeRef targetNodeRef = null;
        if (move)
        {
            // TODO: Replace this with a more formal means of identifying "system" folders (i.e. aspect or UUID)
            if (!isSystemPath(sourceNodeRef))
            {
                // The cm:name might clash with another node in the target location.
                if (nameChanged)
                {
                    // The name will be changing, so we really need to set the node's name to the new
                    // name.  This can't be done at the same time as the move - to avoid incorrect violations
                    // of the name constraints, the cm:name is set to something random and will be reset
                    // to the correct name later.
                    nodeService.setProperty(sourceNodeRef, ContentModel.PROP_NAME, GUID.generate());
                }
                try
                {
                    ChildAssociationRef newAssocRef = null;
                    if (isPrimaryParent)
                    {
                        // move the node so that the association moves as well
                        boolean auditableBehaviorWasDisabled = preserveAuditableData && behaviourFilter.isEnabled(ContentModel.ASPECT_AUDITABLE);
                        if (auditableBehaviorWasDisabled)
                        {
                            behaviourFilter.disableBehaviour(ContentModel.ASPECT_AUDITABLE);
                        }
                        try
                        {
                            newAssocRef = nodeService.moveNode(sourceNodeRef, targetParentRef, assocTypeQname, qname);
                        }
                        finally
                        {
                            if (auditableBehaviorWasDisabled)
                            {
                                behaviourFilter.enableBehaviour(ContentModel.ASPECT_AUDITABLE);
                            }
                        }
                    }
                    else
                    {
                        nodeService.removeChild(sourceParentRef, sourceNodeRef);
                        newAssocRef = nodeService.addChild(targetParentRef, sourceNodeRef, assocRef.getTypeQName(), assocRef.getQName());
                    }
                    targetNodeRef = newAssocRef.getChildRef();
                }
                catch (DuplicateChildNodeNameException e)
                {
                    throw new FileExistsException(targetParentRef, newName);
                }
            }
            else
            {
                // system path folders do not need to be moved
                targetNodeRef = sourceNodeRef;
            }
        }
        else
        {
            // Check if during copy top level name will be changed to some new
            String newNameAfterCopy = copyService.getTopLevelNodeNewName(sourceNodeRef, targetParentRef, assocTypeQname, qname);
            if (newNameAfterCopy != null && !newNameAfterCopy.equals(newName))
            {
                newName = newNameAfterCopy;
                qname = QName.createQName(
                        assocRef.getQName().getNamespaceURI(),
                        QName.createValidLocalName(newNameAfterCopy));
            }
            try
            {
                // Copy the node.  The cm:name will be dropped and reset later.
                targetNodeRef = copyService.copy(
                        sourceNodeRef,
                        targetParentRef,
                        assocTypeQname,
                        qname,
                        true);
            }
            catch (DuplicateChildNodeNameException e)
            {
                throw new FileExistsException(targetParentRef, newName);
            }
        }
       
        // Only update the name if it has changed
        String currentName = (String)nodeService.getProperty(targetNodeRef, ContentModel.PROP_NAME);
        
        // ALF-13949: WorkingCopyAspect intentionally generates new names for all copies of working copies (which no
        // longer have the working copy aspect) so leave these alone after copy
        if (!currentName.equals(newName) && (move || !nodeService.hasAspect(sourceNodeRef, ContentModel.ASPECT_WORKING_COPY)))
        {
            try
            {
                // changed the name property
                nodeService.setProperty(targetNodeRef, ContentModel.PROP_NAME, newName);
                
                // May need to update the mimetype, to support apps using .tmp files when saving
                ContentData contentData = (ContentData)nodeService.getProperty(targetNodeRef, ContentModel.PROP_CONTENT);
                // Check the newName and oldName extensions.
                // Keep previous mimetype if
                //      1. new extension is empty
                //      2. new extension is '.tmp'
                //      3. extension was not changed,
                // 
                // It fixes the ETWOTWO-16 issue.
                String oldExt = getExtension(beforeFileInfo.getName(), true).getSecond();
                String newExt = getExtension(newName, true).getSecond();
                if (contentData != null &&
                        newExt.length() != 0 &&
                        !"tmp".equalsIgnoreCase(newExt) &&
                        !newExt.equalsIgnoreCase(oldExt))
                {
                    String targetMimetype = contentData.getMimetype();
                    final ContentReader reader = contentService.getReader(targetNodeRef, ContentModel.PROP_CONTENT);
                    String newMimetype = mimetypeService.guessMimetype(newName, reader);
                    if (!targetMimetype.equalsIgnoreCase(newMimetype))
                    {
                        contentData = ContentData.setMimetype(contentData, newMimetype);
                        nodeService.setProperty(targetNodeRef, ContentModel.PROP_CONTENT, contentData);
                    }
                }
            }
            catch (DuplicateChildNodeNameException e)
            {
                throw new FileExistsException(targetParentRef, newName);
            }
        }
        
        // get the details after the operation
        FileInfo afterFileInfo = toFileInfo(targetNodeRef, true);
        // done
        if (logger.isDebugEnabled())
        {
            logger.debug("" + (move ? "Moved" : "Copied") + " node: \n" +
                    "   parent: " + targetParentRef + "\n" +
                    "   before: " + beforeFileInfo + "\n" +
                    "   after: " + afterFileInfo);
        }
        return afterFileInfo;
    }
    
    /**
     * Determine if the specified node is a special "system" folder path based node
     * 
     * TODO: Replace this with a more formal means of identifying "system" folders (i.e. aspect or UUID)
     * 
     * @param nodeRef  node to check
     * @return  true => system folder path based node
     */
    private boolean isSystemPath(NodeRef nodeRef)
    {
        Path path = nodeService.getPath(nodeRef);
        String prefixedPath = path.toPrefixString(namespaceService);
        return systemPaths.contains(prefixedPath);
    }
    
    @Override
    public FileInfo create(NodeRef parentNodeRef, String name, QName typeQName) throws FileExistsException
    {
        return createImpl(parentNodeRef, name, typeQName, null);
    } 
    
    @Override
    public FileInfo create(NodeRef parentNodeRef, String name, QName typeQName, QName assocQName) throws FileExistsException
    {
        return createImpl(parentNodeRef, name, typeQName, assocQName);
    }
    private FileInfo createImpl(NodeRef parentNodeRef, String name, QName typeQName, QName assocQName) throws FileExistsException
    {
        // set up initial properties
        Map properties = new HashMap(11);
        properties.put(ContentModel.PROP_NAME, (Serializable) name);
        
        // Check the type is valid for file/folder service
        FileFolderServiceType type = getType(typeQName);
        
        switch (type)
        {
            case SYSTEM_FOLDER:
                throw new InvalidTypeException("System Folders are not handled by this service :" + typeQName);
            case INVALID:
                throw new InvalidTypeException("Type is not handled by this service: " + typeQName);
            case FILE:
            case FOLDER:
            default:
        }
        
        // create the node
        if (assocQName == null)
        {
            assocQName = QName.createQName(
                    NamespaceService.CONTENT_MODEL_1_0_URI,
                    QName.createValidLocalName(name));
        }
        ChildAssociationRef assocRef = null;
        try
        {
            assocRef = nodeService.createNode(
                    parentNodeRef,
                    ContentModel.ASSOC_CONTAINS,
                    assocQName,
                    typeQName,
                    properties);
        }
        catch (DuplicateChildNodeNameException e)
        {
            throw new FileExistsException(parentNodeRef, name);
        }
        
        NodeRef nodeRef = assocRef.getChildRef();
        FileInfo fileInfo = toFileInfo(nodeRef, true);
        // done
        if (logger.isDebugEnabled())
        {
            logger.debug("Created: \n" +
                    "   parent: " + parentNodeRef + "\n" +
                    "   created: " + fileInfo);
        }
        return fileInfo;
    }
    
    @Override
    public void delete(NodeRef nodeRef)
    {
        nodeService.deleteNode(nodeRef);
        // Done
        if (logger.isDebugEnabled())
        {
            logger.debug("Deleted: \n" +
                    "   node: " + nodeRef);
        }
    }
    /**
     * Checks for the presence of, and creates as necessary, the folder structure in the provided path.
     * 
     * An empty path list is not allowed as it would be impossible to necessarily return file info
     * for the parent node - it might not be a folder node.
     * @param parentNodeRef the node under which the path will be created
     * @param pathElements the folder name path to create - may not be empty
     * @param folderTypeQName the types of nodes to create.  This must be a valid subtype of
     *      {@link org.alfresco.model.ContentModel#TYPE_FOLDER they folder type}.
     * @return Returns the info of the last folder in the path.
     * @deprecated Use FileFolderUtil.makeFolders rather than directly accessing this implementation class.
     */
    public FileInfo makeFolders(NodeRef parentNodeRef, List pathElements, QName folderTypeQName)
    {
        return FileFolderUtil.makeFolders(this, parentNodeRef, pathElements, folderTypeQName);
    }
    
    /**
     * Checks for the presence of, and creates as necessary, the folder structure in the provided path.
     * 
     * An empty path list is not allowed as it would be impossible to necessarily return file info
     * for the parent node - it might not be a folder node.
     * @param parentNodeRef the node under which the path will be created
     * @param pathElements the folder name path to create - may not be empty
     * @param folderTypeQName the types of nodes to create.  This must be a valid subtype of
     *      {@link org.alfresco.model.ContentModel#TYPE_FOLDER they folder type}.
     * @return Returns the info of the last folder in the path.
     * @deprecated Use FileFolderUtil.makeFolders rather than directly accessing this implementation class.
     */
    public static FileInfo makeFolders(FileFolderService service, NodeRef parentNodeRef, List pathElements, QName folderTypeQName)
    {
        return FileFolderUtil.makeFolders(service, parentNodeRef, pathElements, folderTypeQName);
    }
    /**
     * Get the file or folder information from the root down to and including the node provided.
     * 
     *   - The root node can be of any type and is not included in the path list.*
- Only the primary path is considered.  If the target node is not a descendant of the
     *       root along purely primary associations, then an exception is generated.*
- If an invalid type is encountered along the path, then an exception is generated.*
* 
     * @param rootNodeRef the start of the returned path, or null if the store root
     *        node must be assumed.
     * @param nodeRef a reference to the file or folder
     * @return Returns a list of file/folder infos from the root (excluded) down to and
     *         including the destination file or folder
     * @throws FileNotFoundException if the node could not be found
     */
    @Override
    public List getNamePath(NodeRef rootNodeRef, NodeRef nodeRef) throws FileNotFoundException
    {
        // check the root
        if (rootNodeRef == null)
        {
            rootNodeRef = nodeService.getRootNode(nodeRef.getStoreRef());
        }
        try
        {
            ArrayList results = new ArrayList(10);
            // get the primary path
            Path path = nodeService.getPath(nodeRef);
            // iterate and turn the results into file info objects
            boolean foundRoot = false;
            for (Path.Element element : path)
            {
                // ignore everything down to the root
                Path.ChildAssocElement assocElement = (Path.ChildAssocElement) element;
                final NodeRef childNodeRef = assocElement.getRef().getChildRef();
                if (childNodeRef.equals(rootNodeRef))
                {
                    // just found the root - but we don't put in an entry for it
                    foundRoot = true;
                    continue;
                }
                else if (!foundRoot)
                {
                    // keep looking for the root
                    continue;
                }
                // we found the root and expect to be building the path up
                // Run as system as the user could not have access to all folders in the path, see ALF-13816
                FileInfo pathInfo = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork()
                {
                    public FileInfo doWork() throws Exception
                    {
                        return toFileInfo(childNodeRef, true);
                    }
                }, AuthenticationUtil.getSystemUserName());
                
                // we can't append a path element to the results if there is already a (non-folder) file at the tail
                // since this would result in a path anomoly - file's cannot contain other files.
                if (!results.isEmpty() && !results.get(results.size()-1).isFolder())
                {
                    throw new InvalidTypeException(
                                "File is not the last element in path: files cannot contain other files.");
                }
                results.add(pathInfo);
            }
            // check that we found the root
            if (!foundRoot)
            {
                throw new FileNotFoundException(nodeRef);
            }
            // done
            if (logger.isDebugEnabled())
            {
                logger.debug("Built name path for node: \n" +
                        "   root: " + rootNodeRef + "\n" +
                        "   node: " + nodeRef + "\n" +
                        "   path: " + results);
            }
            return results;
        }
        catch (InvalidNodeRefException e)
        {
            throw new FileNotFoundException(nodeRef);
        }
    }
    
    /**
     * Get the file or folder names from the root down to and including the node provided.
     *
     *   - The root node can be of any type and is not included in the path list.*
- Only the primary path is considered.  If the target node is not a descendant of the
     *       root along purely primary associations, then an exception is generated.*
- If an invalid type is encountered along the path, then an exception is generated.*
* 
     * @param rootNodeRef the start of the returned path, or null if the store root
     *        node must be assumed.
     * @param nodeRef a reference to the file or folder
     * @return Returns a list of file/folder names from the root (excluded) down to and
     *         including the destination file or folder
     * @throws FileNotFoundException if the node could not be found
     */
    @Override
    public List getNameOnlyPath(NodeRef rootNodeRef, final NodeRef nodeRef) throws FileNotFoundException
    {
        // check the root
        if (rootNodeRef == null)
        {
            rootNodeRef = nodeService.getRootNode(nodeRef.getStoreRef());
        }
        try
        {
            final NodeRef rNodeRef = rootNodeRef;
            final ArrayList results = new ArrayList(10);
            // Run as system as the user could not have access to all folders in the path, see ALF-13816
            AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork()
            {
                public Void doWork() throws Exception
                {
                    // get the primary path
                    Path path = nodeService.getPath(nodeRef);
                    // iterate and turn the results into file info objects
                    boolean foundRoot = false;
                    for (Path.Element element : path)
                    {
                        // ignore everything down to the root
                        Path.ChildAssocElement assocElement = (Path.ChildAssocElement) element;
                        final NodeRef childNodeRef = assocElement.getRef().getChildRef();
                        if (childNodeRef.equals(rNodeRef))
                        {
                            // just found the root - but we don't put in an entry for it
                            foundRoot = true;
                            continue;
                        }
                        else if (!foundRoot)
                        {
                            // keep looking for the root
                            continue;
                        }
                        results.add(nodeService.getProperty(childNodeRef, ContentModel.PROP_NAME).toString());
                    }
                    // check that we found the root
                    if (!foundRoot)
                    {
                        throw new FileNotFoundException(nodeRef);
                    }
                    // done
                    if (logger.isDebugEnabled())
                    {
                        logger.debug("Built name path for node: \n" +
                                "   root: " + rNodeRef + "\n" +
                                "   node: " + nodeRef + "\n" +
                                "   path: " + results);
                    }
                    return null;
                }
            }, AuthenticationUtil.getSystemUserName());
            
            return results;
        }
        catch (InvalidNodeRefException e)
        {
            throw new FileNotFoundException(nodeRef);
        }
        catch (RuntimeException e)
        {
            // the runAs() is too keen on wrapping everything in an outer RuntimeException - which we don't want.
            if (e.getCause() instanceof FileNotFoundException)
            {
                throw (FileNotFoundException)e.getCause();
            }
            else throw e;
        }
    }
    @Override
    public FileInfo resolveNamePath(NodeRef rootNodeRef, List pathElements) throws FileNotFoundException
    {
        return resolveNamePath(rootNodeRef, pathElements, true);        
    }
    @Override
    public FileInfo resolveNamePath(NodeRef rootNodeRef, List pathElements, boolean mustExist) throws FileNotFoundException
    {
        if (pathElements.size() == 0)
        {
            throw new IllegalArgumentException("Path elements list is empty");
        }
        // walk the folder tree first
        NodeRef parentNodeRef = rootNodeRef;
        StringBuilder currentPath = new StringBuilder(pathElements.size() << 4);
        int folderCount = pathElements.size() - 1;
        for (int i = 0; i < folderCount; i++)
        {
            String pathElement = pathElements.get(i);
            currentPath.append("/").append(pathElement);
            NodeRef folderNodeRef = searchSimple(parentNodeRef, pathElement);
            if (folderNodeRef == null)
            {
                if (mustExist)
                {
                    throw new FileNotFoundException("Folder not found: " + currentPath + " (in " + rootNodeRef + ")");
                }
                else
                {
                    return null;
                }
            }
            parentNodeRef = folderNodeRef;
        }
        // we have resolved the folder path - resolve the last component
        String pathElement = pathElements.get(pathElements.size() - 1);
        currentPath.append("/").append(pathElement);
        NodeRef fileNodeRef = searchSimple(parentNodeRef, pathElement);
        if (fileNodeRef == null)
        {
            if (mustExist)
            {
                throw new FileNotFoundException("File not found: " + currentPath + " (in " + rootNodeRef + ")");
            }
            else
            {
                return null;
            }
        }
        FileInfo result = getFileInfo(fileNodeRef);
        // found it
        if (logger.isDebugEnabled())
        {
            logger.debug("Resoved path element: \n" +
                    "   root: " + rootNodeRef + "\n" +
                    "   path: " + currentPath + "\n" +
                    "   node: " + result);
        }
        return result;
    }
    @Override
    public FileInfo getFileInfo(NodeRef nodeRef)
    {
        try
        {
            return toFileInfo(nodeRef, true);
        }
        catch (InvalidTypeException e)
        {
            return null;
        }
    }
    @Override
    public ContentReader getReader(NodeRef nodeRef)
    {
        FileInfo fileInfo = toFileInfo(nodeRef, false);
        if (fileInfo.isFolder())
        {
            throw new InvalidTypeException("Unable to get a content reader for a folder: " + fileInfo);
        }
        return contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
    }
    @Override
    public ContentWriter getWriter(NodeRef nodeRef)
    {
        FileInfo fileInfo = toFileInfo(nodeRef, false);
        if (fileInfo.isFolder())
        {
            throw new InvalidTypeException("Unable to get a content writer for a folder: " + fileInfo);
        }
        final ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
        // Ensure that a mimetype is set based on the filename (ALF-6560)
        // This has been removed from the create code in 3.4 to prevent insert-update behaviour
        // of the ContentData.
        if (writer.getMimetype() == null)
        {
            final String name = fileInfo.getName();
            writer.guessMimetype(name);
        }
        // Done
        return writer;
    }
    
    /**
     * Split a filename into the base (part before the '.') and the extension (part after the '.')
     */
    private Pair getExtension(String name, boolean useLastDot)
    {
        String ext = "";
        String base = name;
        if (name != null)
        {
            name = name.trim();
            int index = useLastDot ? name.lastIndexOf('.') : name.indexOf('.');
            if (index > -1 && (index < name.length() - 1))
            {
                base = name.substring(0, index);
                ext = name.substring(index + 1);
            }
        }
        return new Pair(base, ext);
    }
    @Override
    public void setHidden(NodeRef nodeRef, boolean isHidden)
    {
        int mask = 0;
        boolean allVisible = true;
        Visibility webDavVisibility = isHidden ? Visibility.NotVisible : Visibility.Visible;
        for (Client client : hiddenAspect.getClients())
        {
            Visibility clientVisibility = client == FileFilterMode.getClient() ? webDavVisibility : hiddenAspect
                    .getVisibility(client, nodeRef);
            if (clientVisibility != Visibility.Visible)
            {
                allVisible = false;
            }
            mask |= hiddenAspect.getClientVisibilityMask(client, clientVisibility);
        }
        if (allVisible)
        {
            hiddenAspect.showNode(nodeRef, true);
        }
        else
        {
            hiddenAspect.hideNode(nodeRef, mask, true, true, false);
        }
    }
    @Override
    public boolean isHidden(NodeRef nodeRef)
    {
        return hiddenAspect.getVisibility(FileFilterMode.getClient(), nodeRef) != Visibility.Visible;
    }
    
    @Override
    public  ExtendedTrait  getTrait(Class extends M> traitAPI)
    {
        return (ExtendedTrait) fileFolderTrait;
    }
    
    private FileFolderServiceTrait createFileFolderTrait()
    {
        return new FileFolderServiceTraitImpl(this);
    }
    public static class FileFolderServiceTraitImpl implements FileFolderServiceTrait
    {
        private FileFolderServiceImpl thisService;
        public FileFolderServiceTraitImpl(FileFolderServiceImpl fileFolderServiceImpl)
        {
            thisService = fileFolderServiceImpl;
        }
        @Override
        public Pair, Set> buildSearchTypesAndIgnoreAspects(boolean files, boolean folders,
                    Set ignoreQNameTypes)
        {
            return thisService.buildSearchTypesAndIgnoreAspects(files,
                                                                folders,
                                                                ignoreQNameTypes);
        }
        @Override
        public FileInfo createFileInfo(NodeRef nodeRef, QName typeQName, boolean isFolder, boolean isHidden,
                    Map properties)
        {
            return new FileInfoImpl(nodeRef,
                                    typeQName,
                                    isFolder,
                                    isHidden,
                                    properties);
        }
        @Override
        public FileFolderServiceType getType(QName typeQName)
        {
            return thisService.getType(typeQName);
        }
        @Override
        public List list(final NodeRef contextNodeRef)
        {
            return thisService.list(contextNodeRef);
        }
        @Override
        public PagingResults list(final NodeRef contextNodeRef, final boolean files, final boolean folders,
                    final String pattern, final Set ignoreQNames, final List> sortProps,
                    final PagingRequest pagingRequest)
        {
            return thisService.list(contextNodeRef,
                                    files,
                                    folders,
                                    pattern,
                                    ignoreQNames,
                                    sortProps,
                                    pagingRequest);
        }
        @Override
        public PagingResults list(final NodeRef rootNodeRef, final Set searchTypeQNames,
                    final Set ignoreAspectQNames, final List> sortProps,
                    final PagingRequest pagingRequest)
        {
            return thisService.list(rootNodeRef,
                                    searchTypeQNames,
                                    ignoreAspectQNames,
                                    sortProps,
                                    pagingRequest);
        }
        @Override
        public List search(final NodeRef contextNodeRef, final String namePattern, final boolean fileSearch,
                    final boolean folderSearch, final boolean includeSubFolders)
        {
            return thisService.search(contextNodeRef,
                                      namePattern,
                                      fileSearch,
                                      folderSearch,
                                      includeSubFolders);
        }
       
        @Override
        public FileInfo rename(final NodeRef sourceNodeRef, final String newName) throws FileExistsException, FileNotFoundException
        {
            
            return thisService.rename(sourceNodeRef, newName);
                                       
        }
        
        public PagingResults list(NodeRef contextNodeRef,
                    boolean files,
                    boolean folders,
                    Set ignoreQNames,
                    List> sortProps,
                    PagingRequest pagingRequest)
        {
            return thisService.list(contextNodeRef,
                                    files,
                                    folders,
                                    ignoreQNames,
                                    sortProps,
                                    pagingRequest);
        }
    };
}