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.user.UserIdentity;
029import org.ametys.plugins.repository.AmetysObject;
030import org.ametys.plugins.repository.AmetysObjectIterable;
031import org.ametys.plugins.repository.AmetysRepositoryException;
032import org.ametys.plugins.repository.ModifiableACLAmetysObject;
033import org.ametys.plugins.repository.ModifiableTraversableAmetysObject;
034import org.ametys.plugins.repository.RepositoryIntegrityViolationException;
035import org.ametys.plugins.repository.UnknownAmetysObjectException;
036import org.ametys.plugins.repository.jcr.ACLJCRAmetysObjectHelper;
037import org.ametys.plugins.repository.jcr.SimpleAmetysObject;
038
039/**
040 * An {@link AmetysObject} representing a collection of other {@link AmetysObject}s.<br>
041 * The collection stored its contents in the JCR Repository in a hash tree for
042 * optimizing performances.<br>
043 * Please note that this implementation does not keep the insertion order of elements.
044 * @param <F> the type of the actual factory.
045 * @param <A> the type of the composite {@link AmetysObject}.
046 */
047public class AmetysObjectCollection<F extends AmetysObjectCollectionFactory, A extends AmetysObject> extends SimpleAmetysObject<F> implements ModifiableTraversableAmetysObject, Iterable<A>, ModifiableACLAmetysObject
048{
049    AmetysObjectCollectionFactory _factory;
050    
051    /**
052     * Creates an {@link AmetysObjectCollection}.
053     * @param node the node backing this AmetysObject.
054     * @param parentPath the parentPath in the Ametys hierarchy.
055     * @param factory the {@link AmetysObjectCollectionFactory} which created the AmetysObject.
056     */
057    public AmetysObjectCollection(Node node, String parentPath, F factory)
058    {
059        super(node, parentPath, factory);
060        _factory = factory;
061    }
062
063    @SuppressWarnings("unchecked")
064    public AmetysObjectIterable<A> getChildren() throws AmetysRepositoryException
065    {
066        return _factory.getChildren(getPath(), getNode());
067    }
068    
069    public Iterator<A> iterator()
070    {
071        return _factory.getChildren(getPath(), getNode()).iterator();
072    }
073    
074    public boolean hasChild(String name) throws AmetysRepositoryException
075    {
076        if (name == null || "".equals(name) || name.charAt(0) == '/')
077        {
078            throw new AmetysRepositoryException("Child name cannot be null, empty or absolute");
079        }
080        
081        if (".".equals(name) || "..".equals(name))
082        {
083            throw new AmetysRepositoryException("Child name cannot be . or ..");
084        }
085        
086        try
087        {
088            return getNode().hasNode(_getPath(name));
089        }
090        catch (RepositoryException e)
091        {
092            throw new AmetysRepositoryException("An error occured getting child object " + name + " for object " + getName(), e);
093        }
094    }
095    
096    @SuppressWarnings("unchecked")
097    public A getChild(String path) throws AmetysRepositoryException, UnknownAmetysObjectException
098    {
099        if (path == null || "".equals(path) || path.charAt(0) == '/')
100        {
101            throw new AmetysRepositoryException("Child path cannot be null, empty or absolute");
102        }
103        
104        String subPath = null;
105        String name;
106        
107        int i = path.indexOf('/');
108        if (i == -1)
109        {
110            name = path;
111        }
112        else
113        {
114            name = path.substring(0, i);
115            subPath = path.substring(i + 1);
116        }
117        
118        if (".".equals(name) || "..".equals(name))
119        {
120            throw new AmetysRepositoryException("Path cannot contain segment with . or ..");
121        }
122        
123        try
124        {
125            Node childNode = getNode().getNode(_getPath(name));
126            
127            return (A) _factory.getObject(null, childNode, subPath);
128        }
129        catch (PathNotFoundException e)
130        {
131            throw new UnknownAmetysObjectException("Unable to retrieve child node for path: " + path, e);
132        }
133        catch (RepositoryException e)
134        {
135            throw new AmetysRepositoryException(e);
136        }
137    }
138    
139    @SuppressWarnings("unchecked")
140    public A createChild(String name, String type) throws AmetysRepositoryException, RepositoryIntegrityViolationException
141    {
142        String[] hash = _factory.getHashedPath(name);
143        
144        Node contextNode = getNode();
145        
146        try
147        {
148            for (String hashPart : hash)
149            {
150                if (!contextNode.hasNode(hashPart))
151                {
152                    contextNode = contextNode.addNode(hashPart, AmetysObjectCollectionFactory.COLLECTION_ELEMENT_NODETYPE);
153                }
154                else
155                {
156                    contextNode = contextNode.getNode(hashPart); 
157                }
158            }
159            
160            return (A) _factory.createChild(getPath(), contextNode, name, type);
161        }
162        catch (RepositoryException e)
163        {
164            throw new AmetysRepositoryException("Unable to create child: " + name + " and type: " + type, e);
165        }
166    }
167    
168    @Override
169    @SuppressWarnings("unchecked")
170    public AmetysObject getParent() throws AmetysRepositoryException
171    {
172        return _factory.getParent(this);
173    }
174    
175    private String _getPath(String childName)
176    {
177        String[] hash = _factory.getHashedPath(childName);
178        
179        StringBuilder path = new StringBuilder();
180        
181        for (String hashPart : hash)
182        {
183            path.append(hashPart);
184            path.append('/');
185        }
186        
187        path.append(childName);
188        
189        return path.toString();
190    }
191    
192    @Override
193    public Set<String> getAllowedProfilesForAnyConnectedUser()
194    {
195        return ACLJCRAmetysObjectHelper.getAllowedProfilesForAnyConnectedUser(getNode());
196    }
197
198    @Override
199    public void addAllowedProfilesForAnyConnectedUser(Set<String> profileIds)
200    {
201        ACLJCRAmetysObjectHelper.addAllowedProfilesForAnyConnectedUser(getNode(), profileIds);
202    }
203    
204    @Override
205    public void removeAllowedProfilesForAnyConnectedUser(Set<String> profileIds)
206    {
207        ACLJCRAmetysObjectHelper.removeAllowedProfilesForAnyConnectedUser(getNode(), profileIds);
208    }
209    
210    @Override
211    public Set<String> getDeniedProfilesForAnyConnectedUser()
212    {
213        return ACLJCRAmetysObjectHelper.getDeniedProfilesForAnyConnectedUser(getNode());
214    }
215    
216    @Override
217    public void addDeniedProfilesForAnyConnectedUser(Set<String> profileIds)
218    {
219        ACLJCRAmetysObjectHelper.addDeniedProfilesForAnyConnectedUser(getNode(), profileIds);
220    }
221    
222    @Override
223    public void removeDeniedProfilesForAnyConnectedUser(Set<String> profileIds)
224    {
225        ACLJCRAmetysObjectHelper.removeDeniedProfilesForAnyConnectedUser(getNode(), profileIds);
226    }
227    
228    @Override
229    public Set<String> getAllowedProfilesForAnonymous()
230    {
231        return ACLJCRAmetysObjectHelper.getAllowedProfilesForAnonymous(getNode());
232    }
233    
234    @Override
235    public void addAllowedProfilesForAnonymous(Set<String> profileIds)
236    {
237        ACLJCRAmetysObjectHelper.addAllowedProfilesForAnonymous(getNode(), profileIds);
238    }
239    
240    @Override
241    public void removeAllowedProfilesForAnonymous(Set<String> profileIds)
242    {
243        ACLJCRAmetysObjectHelper.removeAllowedProfilesForAnonymous(getNode(), profileIds);
244    }
245    
246    @Override
247    public Set<String> getDeniedProfilesForAnonymous()
248    {
249        return ACLJCRAmetysObjectHelper.getDeniedProfilesForAnonymous(getNode());
250    }
251    
252    @Override
253    public void addDeniedProfilesForAnonymous(Set<String> profileIds)
254    {
255        ACLJCRAmetysObjectHelper.addDeniedProfilesForAnonymous(getNode(), profileIds);
256    }
257    
258    @Override
259    public void removeDeniedProfilesForAnonymous(Set<String> profileIds)
260    {
261        ACLJCRAmetysObjectHelper.removeDeniedProfilesForAnonymous(getNode(), profileIds);
262    }
263    
264    @Override
265    public Set<String> getAllowedProfilesForUser(UserIdentity user)
266    {
267        return ACLJCRAmetysObjectHelper.getAllowedProfilesForUser(getNode(), user);
268    }
269    
270    @Override
271    public Map<UserIdentity, Set<String>> getAllowedProfilesForUsers()
272    {
273        return ACLJCRAmetysObjectHelper.getAllowedProfilesForUsers(getNode());
274    }
275
276    @Override
277    public Set<UserIdentity> getAllowedUsers(String profileId)
278    {
279        return ACLJCRAmetysObjectHelper.getAllowedUsers(getNode(), profileId);
280    }
281
282    @Override
283    public void addAllowedUsers(Set<UserIdentity> users, String profileId)
284    {
285        ACLJCRAmetysObjectHelper.addAllowedUsers(users, getNode(), profileId);
286    }
287    
288    @Override
289    public void removeAllowedUsers(Set<UserIdentity> users, String profileId)
290    {
291        ACLJCRAmetysObjectHelper.removeAllowedUsers(users, getNode(), profileId);
292    }
293    
294    @Override
295    public void removeAllowedUsers(Set<UserIdentity> users)
296    {
297        ACLJCRAmetysObjectHelper.removeAllowedUsers(users, getNode());
298    }
299    
300    @Override
301    public Map<GroupIdentity, Set<String>> getAllowedProfilesForGroups()
302    {
303        return ACLJCRAmetysObjectHelper.getAllowedProfilesForGroups(getNode());
304    }
305
306    @Override
307    public Set<GroupIdentity> getAllowedGroups(String profileId)
308    {
309        return ACLJCRAmetysObjectHelper.getAllowedGroups(getNode(), profileId);
310    }
311
312    @Override
313    public void addAllowedGroups(Set<GroupIdentity> groups, String profileId)
314    {
315        ACLJCRAmetysObjectHelper.addAllowedGroups(groups, getNode(), profileId);
316    }
317    
318    @Override
319    public void removeAllowedGroups(Set<GroupIdentity> groups, String profileId)
320    {
321        ACLJCRAmetysObjectHelper.removeAllowedGroups(groups, getNode(), profileId);
322    }
323    
324    @Override
325    public void removeAllowedGroups(Set<GroupIdentity> groups)
326    {
327        ACLJCRAmetysObjectHelper.removeAllowedGroups(groups, getNode());
328    }
329    
330    @Override
331    public Set<String> getDeniedProfilesForUser(UserIdentity user)
332    {
333        return ACLJCRAmetysObjectHelper.getDeniedProfilesForUser(getNode(), user);
334    }
335    
336    @Override
337    public Map<UserIdentity, Set<String>> getDeniedProfilesForUsers()
338    {
339        return ACLJCRAmetysObjectHelper.getDeniedProfilesForUsers(getNode());
340    }
341
342    @Override
343    public Set<UserIdentity> getDeniedUsers(String profileId)
344    {
345        return ACLJCRAmetysObjectHelper.getDeniedUsers(getNode(), profileId);
346    }
347    
348    @Override
349    public void addDeniedUsers(Set<UserIdentity> users, String profileId)
350    {
351        ACLJCRAmetysObjectHelper.addDeniedUsers(users, getNode(), profileId);
352    }
353    
354    @Override
355    public void removeDeniedUsers(Set<UserIdentity> users, String profileId)
356    {
357        ACLJCRAmetysObjectHelper.removeDeniedUsers(users, getNode(), profileId);
358    }
359    
360    @Override
361    public void removeDeniedUsers(Set<UserIdentity> users)
362    {
363        ACLJCRAmetysObjectHelper.removeDeniedUsers(users, getNode());
364    }
365    
366    @Override
367    public Map<GroupIdentity, Set<String>> getDeniedProfilesForGroups()
368    {
369        return ACLJCRAmetysObjectHelper.getDeniedProfilesForGroups(getNode());
370    }
371
372    @Override
373    public Set<GroupIdentity> getDeniedGroups(String profileId)
374    {
375        return ACLJCRAmetysObjectHelper.getDeniedGroups(getNode(), profileId);
376    }
377
378    @Override
379    public void addDeniedGroups(Set<GroupIdentity> groups, String profileId)
380    {
381        ACLJCRAmetysObjectHelper.addDeniedGroups(groups, getNode(), profileId);
382    }
383    
384    @Override
385    public void removeDeniedGroups(Set<GroupIdentity> groups, String profileId)
386    {
387        ACLJCRAmetysObjectHelper.removeDeniedGroups(groups, getNode(), profileId);
388    }
389    
390    @Override
391    public void removeDeniedGroups(Set<GroupIdentity> groups)
392    {
393        ACLJCRAmetysObjectHelper.removeDeniedGroups(groups, getNode());
394    }
395    
396    @Override
397    public boolean isInheritanceDisallowed()
398    {
399        return ACLJCRAmetysObjectHelper.isInheritanceDisallowed(getNode());
400    }
401    
402    @Override
403    public void disallowInheritance(boolean disallow)
404    {
405        ACLJCRAmetysObjectHelper.disallowInheritance(getNode(), disallow);
406    }
407}