/* * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Alfresco 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ package org.alfresco.repo.model.filefolder; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import org.alfresco.api.AlfrescoPublicApi; import org.alfresco.model.ContentModel; import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingResults; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileFolderServiceType; import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.Path; import org.alfresco.service.cmr.repository.Path.Element; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.namespace.QName; import org.alfresco.util.FileFilterMode; import org.alfresco.util.FileFilterMode.Client; import org.alfresco.util.SearchLanguageConversion; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Functionality relating to hidden files and folders. * * Support for nodes marked as hidden but with visibility constraints for specific clients. A node * can have the hidden aspect applied, which means that the node is hidden. However, * for specific clients it can be defined whether the node is visible or will have its hidden attribute * set in FileInfo. * */ @AlfrescoPublicApi public class HiddenAspect { private static Log logger = LogFactory.getLog(HiddenAspect.class); public static Set HIDDEN_PROPERTIES = new HashSet(); static { HIDDEN_PROPERTIES.add(ContentModel.PROP_CLIENT_CONTROLLED); HIDDEN_PROPERTIES.add(ContentModel.PROP_CASCADE_HIDDEN); HIDDEN_PROPERTIES.add(ContentModel.PROP_CASCADE_INDEX_CONTROL); HIDDEN_PROPERTIES.add(ContentModel.PROP_VISIBILITY_MASK); } public static enum Visibility { NotVisible, Visible, HiddenAttribute; public int getMask() { if(this == Visible) { return 2; } else if(this == HiddenAttribute) { return 1; } else if(this == NotVisible) { return 0; } else { throw new IllegalArgumentException(); } } public static Visibility getVisibility(int mask) { if(mask == 2) { return Visible; } else if(mask == 1) { return HiddenAttribute; } else if(mask == 0) { return NotVisible; } else { throw new IllegalArgumentException(); } } }; private List filters = new ArrayList(10); private NodeService nodeService; private FileFolderService fileFolderService; private SearchService searchService; private PolicyComponent policyComponent; public HiddenAspect() { } public void setPolicyComponent(PolicyComponent policyComponent) { this.policyComponent = policyComponent; } public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } public void setFileFolderService(FileFolderService fileFolderService) { this.fileFolderService = fileFolderService; } public void setSearchService(SearchService searchService) { this.searchService = searchService; } public void setPatterns(List filters) { for(HiddenFileFilter filter : filters) { this.filters.add(new HiddenFileInfoImpl(filter.getFilter(), filter.getVisibility(), filter.getHiddenAttribute(), filter.cascadeHiddenAspect(), filter.cascadeIndexControlAspect())); } } public void init() { } public List getPatterns() { return filters; } public Client[] getClients() { return Client.values(); } private ResultSet searchForName(StoreRef storeRef, String name) { SearchParameters sp = new SearchParameters(); sp.addStore(storeRef); sp.setLanguage("lucene"); sp.setQuery("@" + SearchLanguageConversion.escapeLuceneQuery(ContentModel.PROP_NAME.toString()) + ":\"" + name + "\""); sp.addLocale(new Locale("en")); return searchService.query(sp); } private Integer getClientIndex(Client client) { return client.ordinal(); } private void addIndexControlAspect(NodeRef nodeRef) { Map props = new HashMap(2); props.put(ContentModel.PROP_IS_INDEXED, Boolean.FALSE); props.put(ContentModel.PROP_IS_CONTENT_INDEXED, Boolean.FALSE); nodeService.addAspect(nodeRef, ContentModel.ASPECT_INDEX_CONTROL, props); if (logger.isDebugEnabled()) { logger.debug("Applied index control marker: " + nodeRef); } } private void removeIndexControlAspect(NodeRef nodeRef) { nodeService.removeAspect(nodeRef, ContentModel.ASPECT_INDEX_CONTROL); if (logger.isDebugEnabled()) { logger.debug("Removed index control marker: " + nodeRef); } } /** * Mark this node as hidden regardless of any name/pattern/matching rules. Following this call the node * will be hidden. * * If the node is already hidden will do nothing. * * @param nodeRef */ public void hideNodeExplicit(NodeRef nodeRef) { int mask = 0; mask |= getClientVisibilityMask(Client.cifs, Visibility.HiddenAttribute); mask |= getClientVisibilityMask(Client.webdav, Visibility.Visible); mask |= getClientVisibilityMask(Client.nfs, Visibility.Visible); mask |= getClientVisibilityMask(Client.ftp, Visibility.Visible); addHiddenAspect(nodeRef, mask, true); } /** * Remove the explicit hiding of a node. Following this call the node may or may not remain hidden based upon the other * properties of the node. * * @param nodeRef */ public void unhideExplicit(NodeRef nodeRef) { nodeService.setProperty(nodeRef, ContentModel.PROP_HIDDEN_FLAG, false); checkHidden(nodeRef, true, false); } private void addHiddenAspect(NodeRef nodeRef, int visibilityMask, boolean explicit) { Map props = new HashMap(1); props.put(ContentModel.PROP_VISIBILITY_MASK, visibilityMask); props.put(ContentModel.PROP_HIDDEN_FLAG, explicit); nodeService.addAspect(nodeRef, ContentModel.ASPECT_HIDDEN, props); if (logger.isDebugEnabled()) { logger.debug("Applied hidden marker: " + nodeRef); } } private void addHiddenAspect(NodeRef nodeRef, int visibilityMask, boolean cascadeHiddenAspect, boolean cascadeIndexControlAspect, boolean clientControlled) { Map props = new HashMap(1); props.put(ContentModel.PROP_VISIBILITY_MASK, visibilityMask); props.put(ContentModel.PROP_CASCADE_HIDDEN, cascadeHiddenAspect); props.put(ContentModel.PROP_CASCADE_INDEX_CONTROL, cascadeIndexControlAspect); props.put(ContentModel.PROP_CLIENT_CONTROLLED, clientControlled); nodeService.addAspect(nodeRef, ContentModel.ASPECT_HIDDEN, props); if (logger.isDebugEnabled()) { logger.debug("Applied hidden marker: " + nodeRef); } } private void addHiddenAspect(NodeRef nodeRef, HiddenFileInfo filter) { Map props = new HashMap(1); props.put(ContentModel.PROP_VISIBILITY_MASK, filter.getVisibilityMask()); props.put(ContentModel.PROP_CASCADE_HIDDEN, filter.cascadeHiddenAspect()); props.put(ContentModel.PROP_CASCADE_INDEX_CONTROL, filter.cascadeIndexControlAspect()); props.put(ContentModel.PROP_CLIENT_CONTROLLED, filter.isClientControlled()); nodeService.addAspect(nodeRef, ContentModel.ASPECT_HIDDEN, props); if (logger.isDebugEnabled()) { logger.debug("Applied hidden marker: " + nodeRef); } } public void removeHiddenAspect(NodeRef nodeRef) { // Remove the aspect nodeService.removeAspect(nodeRef, ContentModel.ASPECT_HIDDEN); if (logger.isDebugEnabled()) { logger.debug("Removed hidden marker: " + nodeRef); } } private Visibility getVisibility(Integer mask, Client client) { if(mask == null || mask.intValue() == 0) { return Visibility.NotVisible; } mask = (mask.intValue() >> (getClientIndex(client))*2) & 3; return Visibility.getVisibility(mask); } /* * Determines whether the path matches any one of the hidden file patterns and, if so, * returns the matching pattern. * * @param path * @return */ private HiddenFileInfo isHidden(String name) { // check against all the filters HiddenFileInfo matched = null; for(HiddenFileInfo filter : filters) { if(filter.isHidden(name)) { matched = filter; break; } } return matched; } public boolean hasHiddenAspect(NodeRef nodeRef) { return nodeService.hasAspect(nodeRef, ContentModel.ASPECT_HIDDEN); } private boolean hasIndexControlAspect(NodeRef nodeRef) { return nodeService.hasAspect(nodeRef, ContentModel.ASPECT_INDEX_CONTROL); } // private void applyHidden(NodeRef nodeRef, HiddenFileInfo filter, int visibilityMask) // { // if(!filter.cascadeHiddenAspect() && !filter.cascadeIndexControlAspect()) // { // return; // } // // PagingRequest pagingRequest = new PagingRequest(0, Integer.MAX_VALUE, null); // PagingResults results = fileFolderService.list(nodeRef, true, true, null, null, pagingRequest); // List files = results.getPage(); // // // apply the hidden aspect to all folders and folders and then recursively to all sub-folders, unless the sub-folder // // already has the hidden aspect applied (it may have been applied for a different pattern). // for(FileInfo file : files) // { // NodeRef childNodeRef = file.getNodeRef(); // if(filter.cascadeHiddenAspect() && !hasHiddenAspect(childNodeRef)) // { // addHiddenAspect(childNodeRef, visibilityMask, false); // } // // if(filter.cascadeIndexControlAspect() && !hasIndexControlAspect(childNodeRef)) // { // addIndexControlAspect(childNodeRef); // } // // if(file.isFolder()) // { // applyHidden(file.getNodeRef(), filter, visibilityMask); // } // } // } private void applyHidden(FileInfo fileInfo, HiddenFileInfo filter) { NodeRef nodeRef = fileInfo.getNodeRef(); if(!hasHiddenAspect(nodeRef)) { // the file matches a pattern, apply the hidden and aspect control aspects addHiddenAspect(nodeRef, filter); } else { nodeService.setProperty(nodeRef, ContentModel.PROP_VISIBILITY_MASK, filter.getVisibilityMask()); nodeService.setProperty(nodeRef, ContentModel.PROP_CASCADE_HIDDEN, filter.cascadeHiddenAspect()); nodeService.setProperty(nodeRef, ContentModel.PROP_CASCADE_INDEX_CONTROL, filter.cascadeIndexControlAspect()); } if(!hasIndexControlAspect(nodeRef)) { addIndexControlAspect(nodeRef); } if(fileInfo.isFolder() && (filter.cascadeHiddenAspect() || filter.cascadeIndexControlAspect())) { PagingRequest pagingRequest = new PagingRequest(0, Integer.MAX_VALUE, null); PagingResults results = fileFolderService.list(nodeRef, true, true, null, null, pagingRequest); List files = results.getPage(); // apply the hidden aspect to all folders and folders and then recursively to all sub-folders, unless the sub-folder // already has the hidden aspect applied (it may have been applied for a different pattern). for(FileInfo file : files) { applyHidden(file, filter); } } } private void applyHidden(NodeRef nodeRef, HiddenFileInfo filter, boolean checkChildren) { if(!hasHiddenAspect(nodeRef)) { addHiddenAspect(nodeRef, filter); } else { nodeService.setProperty(nodeRef, ContentModel.PROP_VISIBILITY_MASK, filter.getVisibilityMask()); nodeService.setProperty(nodeRef, ContentModel.PROP_CASCADE_HIDDEN, filter.cascadeHiddenAspect()); nodeService.setProperty(nodeRef, ContentModel.PROP_CASCADE_INDEX_CONTROL, filter.cascadeIndexControlAspect()); } if(!hasIndexControlAspect(nodeRef)) { addIndexControlAspect(nodeRef); } QName typeQName = nodeService.getType(nodeRef); FileFolderServiceType type = fileFolderService.getType(typeQName); boolean isFolder = type.equals(FileFolderServiceType.FOLDER) || type.equals(FileFolderServiceType.SYSTEM_FOLDER); if(isFolder && checkChildren && (filter.cascadeHiddenAspect() || filter.cascadeIndexControlAspect())) { PagingRequest pagingRequest = new PagingRequest(0, Integer.MAX_VALUE, null); PagingResults results = fileFolderService.list(nodeRef, true, true, null, null, pagingRequest); List files = results.getPage(); // apply the hidden aspect to all folders and folders and then recursively to all sub-folders, unless the sub-folder // already has the hidden aspect applied (it may have been applied for a different pattern). for(FileInfo file : files) { applyHidden(file, filter); } } } public void removeHidden(NodeRef nodeRef) { Client saveClient = FileFilterMode.getClient(); FileFilterMode.setClient(Client.admin); try { PagingRequest pagingRequest = new PagingRequest(0, Integer.MAX_VALUE, null); PagingResults results = fileFolderService.list(nodeRef, true, true, null, null, pagingRequest); List files = results.getPage(); for(FileInfo file : files) { String name = (String)nodeService.getProperty(file.getNodeRef(), ContentModel.PROP_NAME); // remove hidden aspect only if it doesn't match a hidden pattern if(isHidden(name) == null) { removeHiddenAspect(file.getNodeRef()); removeIndexControlAspect(file.getNodeRef()); if(file.isFolder()) { removeHidden(file.getNodeRef()); } } } } finally { FileFilterMode.setClient(saveClient); } } private HiddenFileInfo findMatch(NodeRef nodeRef) { HiddenFileInfo ret = null; Path path = null; String name = null; OUTER: for(HiddenFileInfo filter : filters) { if(filter.cascadeHiddenAspect() || filter.cascadeIndexControlAspect()) { if(path == null) { path = nodeService.getPath(nodeRef); } // TODO would be nice to check each part of the path in turn, bailing out if a match is found Iterator it = path.iterator(); while(it.hasNext()) { Path.ChildAssocElement elem = (Path.ChildAssocElement)it.next(); QName qname = elem.getRef().getQName(); if(qname != null) { if(filter.isHidden(qname.getLocalName())) { ret = filter; break OUTER; } } } } else { if(name == null) { name = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); } if(filter.isHidden(name)) { ret = filter; break; } } } return ret; } /** * getClientVisibilityMap * * @param client * @param visibility * @return the client visibilityMask */ public int getClientVisibilityMask(Client client, Visibility visibility) { return visibility.getMask() << getClientIndex(client)*2; } /** * Checks whether the node is on a hidden path * * @param nodeRef * @return the matching filter, or null if no match */ public HiddenFileInfo onHiddenPath(NodeRef nodeRef) { HiddenFileInfo ret = null; // TODO would be nice to check each part of the path in turn, bailing out if a match is found Path path = nodeService.getPath(nodeRef); nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); Iterator 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 client * @param fileInfo * @return */ 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 */ 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 */ 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 * @param both if true, will check if the node should not be hidden and remove hidden and index control * aspects if they are present * @return */ 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. * * @see getClientVisibilityMask() * @param fileInfo, file to make hidden * @param visibilityMask */ 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 * @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. * @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 * @param 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 HiddenFileInfo { private Pattern filter; private Set clientVisibility = new HashSet(10); private Set hiddenAttribute = new HashSet(10); private int visibilityMask; private boolean cascadeHiddenAspect; private boolean cascadeIndexControlAspect; 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(); } 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; } } }