001/*
002 *  Copyright 2010 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016
017package org.ametys.plugins.repository.collection;
018
019import java.util.Iterator;
020import java.util.Map;
021import java.util.Set;
022
023import javax.jcr.Node;
024import javax.jcr.PathNotFoundException;
025import javax.jcr.RepositoryException;
026
027import org.ametys.core.group.GroupIdentity;
028import org.ametys.core.right.ProfileAssignmentStorage.AnonymousOrAnyConnectedKeys;
029import org.ametys.core.right.ProfileAssignmentStorage.UserOrGroup;
030import org.ametys.core.user.UserIdentity;
031import org.ametys.plugins.repository.AmetysObject;
032import org.ametys.plugins.repository.AmetysObjectIterable;
033import org.ametys.plugins.repository.AmetysRepositoryException;
034import org.ametys.plugins.repository.ModifiableACLAmetysObject;
035import org.ametys.plugins.repository.ModifiableTraversableAmetysObject;
036import org.ametys.plugins.repository.RepositoryIntegrityViolationException;
037import org.ametys.plugins.repository.UnknownAmetysObjectException;
038import org.ametys.plugins.repository.jcr.ACLJCRAmetysObjectHelper;
039import org.ametys.plugins.repository.jcr.SimpleAmetysObject;
040
041/**
042 * An {@link AmetysObject} representing a collection of other {@link AmetysObject}s.<br>
043 * The collection stored its contents in the JCR Repository in a hash tree for
044 * optimizing performances.<br>
045 * Please note that this implementation does not keep the insertion order of elements.
046 * @param <F> the type of the actual factory.
047 * @param <A> the type of the composite {@link AmetysObject}.
048 */
049public class AmetysObjectCollection<F extends AmetysObjectCollectionFactory, A extends AmetysObject> extends SimpleAmetysObject<F> implements ModifiableTraversableAmetysObject, Iterable<A>, ModifiableACLAmetysObject
050{
051    AmetysObjectCollectionFactory _factory;
052    
053    /**
054     * Creates an {@link AmetysObjectCollection}.
055     * @param node the node backing this AmetysObject.
056     * @param parentPath the parentPath in the Ametys hierarchy.
057     * @param factory the {@link AmetysObjectCollectionFactory} which created the AmetysObject.
058     */
059    public AmetysObjectCollection(Node node, String parentPath, F factory)
060    {
061        super(node, parentPath, factory);
062        _factory = factory;
063    }
064
065    @SuppressWarnings("unchecked")
066    public AmetysObjectIterable<A> getChildren() throws AmetysRepositoryException
067    {
068        return _factory.getChildren(getPath(), getNode());
069    }
070    
071    public Iterator<A> iterator()
072    {
073        return _factory.getChildren(getPath(), getNode()).iterator();
074    }
075    
076    public boolean hasChild(String name) throws AmetysRepositoryException
077    {
078        if (name == null || "".equals(name) || name.charAt(0) == '/')
079        {
080            throw new AmetysRepositoryException("Child name cannot be null, empty or absolute");
081        }
082        
083        if (".".equals(name) || "..".equals(name))
084        {
085            throw new AmetysRepositoryException("Child name cannot be . or ..");
086        }
087        
088        try
089        {
090            return getNode().hasNode(_getPath(name));
091        }
092        catch (RepositoryException e)
093        {
094            throw new AmetysRepositoryException("An error occured getting child object " + name + " for object " + getName(), e);
095        }
096    }
097    
098    @SuppressWarnings("unchecked")
099    public A getChild(String path) throws AmetysRepositoryException, UnknownAmetysObjectException
100    {
101        if (path == null || "".equals(path) || path.charAt(0) == '/')
102        {
103            throw new AmetysRepositoryException("Child path cannot be null, empty or absolute");
104        }
105        
106        String subPath = null;
107        String name;
108        
109        int i = path.indexOf('/');
110        if (i == -1)
111        {
112            name = path;
113        }
114        else
115        {
116            name = path.substring(0, i);
117            subPath = path.substring(i + 1);
118        }
119        
120        if (".".equals(name) || "..".equals(name))
121        {
122            throw new AmetysRepositoryException("Path cannot contain segment with . or ..");
123        }
124        
125        try
126        {
127            Node childNode = getNode().getNode(_getPath(name));
128            
129            return (A) _factory.getObject(null, childNode, subPath);
130        }
131        catch (PathNotFoundException e)
132        {
133            throw new UnknownAmetysObjectException("Unable to retrieve child node for path: " + path, e);
134        }
135        catch (RepositoryException e)
136        {
137            throw new AmetysRepositoryException(e);
138        }
139    }
140    
141    @SuppressWarnings("unchecked")
142    public A createChild(String name, String type) throws AmetysRepositoryException, RepositoryIntegrityViolationException
143    {
144        String[] hash = _factory.getHashedPath(name);
145        
146        Node contextNode = getNode();
147        
148        try
149        {
150            for (String hashPart : hash)
151            {
152                if (!contextNode.hasNode(hashPart))
153                {
154                    contextNode = contextNode.addNode(hashPart, AmetysObjectCollectionFactory.COLLECTION_ELEMENT_NODETYPE);
155                }
156                else
157                {
158                    contextNode = contextNode.getNode(hashPart); 
159                }
160            }
161            
162            return (A) _factory.createChild(getPath(), contextNode, name, type);
163        }
164        catch (RepositoryException e)
165        {
166            throw new AmetysRepositoryException("Unable to create child: " + name + " and type: " + type, e);
167        }
168    }
169    
170    @Override
171    @SuppressWarnings("unchecked")
172    public AmetysObject getParent() throws AmetysRepositoryException
173    {
174        return _factory.getParent(this);
175    }
176    
177    private String _getPath(String childName)
178    {
179        String[] hash = _factory.getHashedPath(childName);
180        
181        StringBuilder path = new StringBuilder();
182        
183        for (String hashPart : hash)
184        {
185            path.append(hashPart);
186            path.append('/');
187        }
188        
189        path.append(childName);
190        
191        return path.toString();
192    }
193
194    public Map<AnonymousOrAnyConnectedKeys, Set<String>> getProfilesForAnonymousAndAnyConnectedUser()
195    {
196        return ACLJCRAmetysObjectHelper.getProfilesForAnonymousAndAnyConnectedUser(getNode());
197    }
198    
199    public Map<GroupIdentity, Map<UserOrGroup, Set<String>>> getProfilesForGroups(Set<GroupIdentity> groups)
200    {
201        return ACLJCRAmetysObjectHelper.getProfilesForGroups(getNode(), groups);
202    }
203    
204    public Map<UserIdentity, Map<UserOrGroup, Set<String>>> getProfilesForUsers(UserIdentity user)
205    {
206        return ACLJCRAmetysObjectHelper.getProfilesForUsers(getNode(), user);
207    }
208    
209    public void addAllowedProfilesForAnyConnectedUser(Set<String> profileIds)
210    {
211        ACLJCRAmetysObjectHelper.addAllowedProfilesForAnyConnectedUser(getNode(), profileIds);
212    }
213    
214    public void removeAllowedProfilesForAnyConnectedUser(Set<String> profileIds)
215    {
216        ACLJCRAmetysObjectHelper.removeAllowedProfilesForAnyConnectedUser(getNode(), profileIds);
217    }
218    
219    public void addDeniedProfilesForAnyConnectedUser(Set<String> profileIds)
220    {
221        ACLJCRAmetysObjectHelper.addDeniedProfilesForAnyConnectedUser(getNode(), profileIds);
222    }
223    
224    public void removeDeniedProfilesForAnyConnectedUser(Set<String> profileIds)
225    {
226        ACLJCRAmetysObjectHelper.removeDeniedProfilesForAnyConnectedUser(getNode(), profileIds);
227    }
228    
229    public void addAllowedProfilesForAnonymous(Set<String> profileIds)
230    {
231        ACLJCRAmetysObjectHelper.addAllowedProfilesForAnonymous(getNode(), profileIds);
232    }
233    
234    public void removeAllowedProfilesForAnonymous(Set<String> profileIds)
235    {
236        ACLJCRAmetysObjectHelper.removeAllowedProfilesForAnonymous(getNode(), profileIds);
237    }
238    
239    public void addDeniedProfilesForAnonymous(Set<String> profileIds)
240    {
241        ACLJCRAmetysObjectHelper.addDeniedProfilesForAnonymous(getNode(), profileIds);
242    }
243    
244    public void removeDeniedProfilesForAnonymous(Set<String> profileIds)
245    {
246        ACLJCRAmetysObjectHelper.removeDeniedProfilesForAnonymous(getNode(), profileIds);
247    }
248    
249    public void addAllowedUsers(Set<UserIdentity> users, String profileId)
250    {
251        ACLJCRAmetysObjectHelper.addAllowedUsers(users, getNode(), profileId);
252    }
253    
254    public void removeAllowedUsers(Set<UserIdentity> users, String profileId)
255    {
256        ACLJCRAmetysObjectHelper.removeAllowedUsers(users, getNode(), profileId);
257    }
258    
259    public void removeAllowedUsers(Set<UserIdentity> users)
260    {
261        ACLJCRAmetysObjectHelper.removeAllowedUsers(users, getNode());
262    }
263    
264    public void addAllowedGroups(Set<GroupIdentity> groups, String profileId)
265    {
266        ACLJCRAmetysObjectHelper.addAllowedGroups(groups, getNode(), profileId);
267    }
268    
269    public void removeAllowedGroups(Set<GroupIdentity> groups, String profileId)
270    {
271        ACLJCRAmetysObjectHelper.removeAllowedGroups(groups, getNode(), profileId);
272    }
273    
274    public void removeAllowedGroups(Set<GroupIdentity> groups)
275    {
276        ACLJCRAmetysObjectHelper.removeAllowedGroups(groups, getNode());
277    }
278    
279    public void addDeniedUsers(Set<UserIdentity> users, String profileId)
280    {
281        ACLJCRAmetysObjectHelper.addDeniedUsers(users, getNode(), profileId);
282    }
283    
284    public void removeDeniedUsers(Set<UserIdentity> users, String profileId)
285    {
286        ACLJCRAmetysObjectHelper.removeDeniedUsers(users, getNode(), profileId);
287    }
288    
289    public void removeDeniedUsers(Set<UserIdentity> users)
290    {
291        ACLJCRAmetysObjectHelper.removeDeniedUsers(users, getNode());
292    }
293    
294    public void addDeniedGroups(Set<GroupIdentity> groups, String profileId)
295    {
296        ACLJCRAmetysObjectHelper.addDeniedGroups(groups, getNode(), profileId);
297    }
298    
299    public void removeDeniedGroups(Set<GroupIdentity> groups, String profileId)
300    {
301        ACLJCRAmetysObjectHelper.removeDeniedGroups(groups, getNode(), profileId);
302    }
303    
304    public void removeDeniedGroups(Set<GroupIdentity> groups)
305    {
306        ACLJCRAmetysObjectHelper.removeDeniedGroups(groups, getNode());
307    }
308    
309    public boolean isInheritanceDisallowed()
310    {
311        return ACLJCRAmetysObjectHelper.isInheritanceDisallowed(getNode());
312    }
313    
314    public void disallowInheritance(boolean disallow)
315    {
316        ACLJCRAmetysObjectHelper.disallowInheritance(getNode(), disallow);
317    }
318}