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 */
016package org.ametys.cms.support;
017
018import javax.jcr.Node;
019import javax.jcr.Property;
020import javax.jcr.PropertyType;
021import javax.jcr.RepositoryException;
022import javax.jcr.version.OnParentVersionAction;
023
024import org.apache.commons.collections.Predicate;
025import org.apache.commons.collections.PredicateUtils;
026import org.apache.commons.lang.ArrayUtils;
027
028import org.ametys.cms.workflow.EditContentFunction;
029import org.ametys.plugins.repository.RepositoryConstants;
030import org.ametys.plugins.repository.jcr.NodeTypeHelper;
031
032/**
033 * Provides the following predicates:
034 * <ul>
035 * <li>propertyNamePredicate - accept a property depending on his name.
036 * <li>nodeNamePredicate - accept a node depending on his name.
037 * <li>nodeTypePredicate - accept a node depending on his node type.
038 * </ul>
039 */
040public class AmetysPredicateUtils extends PredicateUtils
041{
042    private static AllowedForLivePredicate _allowedLivePredicate = new  AllowedForLivePredicate();
043    
044    /** 
045     * Tests if the property has the following name.
046     * @param propertyName the property name.
047     * @return <code>true</code> if the property has the given name,
048     *         <code>false</code> otherwise.
049     */
050    public static Predicate propertyNamePredicate(final String propertyName)
051    {
052        return new Predicate()
053        {
054            @Override
055            public boolean evaluate(Object object)
056            {
057                if (object instanceof Property)
058                {
059                    try
060                    {
061                        return ((Property) object).getName().equals(propertyName);
062                    }
063                    catch (RepositoryException e)
064                    {
065                        throw new RuntimeException("Unable to get property name", e);
066                    }
067                }
068                
069                return false;
070            }
071        };
072    }
073    
074    /** 
075     * Tests if the property has a given name in a list.
076     * @param propertyNames one or several property names.
077     * @return <code>true</code> if the property has on of the given names,
078     *         <code>false</code> otherwise.
079     */
080    public static Predicate propertyNamesPredicate(final String ... propertyNames)
081    {
082        return new Predicate()
083        {
084            @Override
085            public boolean evaluate(Object object)
086            {
087                if (object instanceof Property)
088                {
089                    try
090                    {
091                        String propName = ((Property) object).getName();
092                        return ArrayUtils.contains(propertyNames, propName);
093                    }
094                    catch (RepositoryException e)
095                    {
096                        throw new RuntimeException("Unable to get property name", e);
097                    }
098                }
099                return false;
100            }
101        };
102    }
103    
104    /** 
105     * Tests if the property is not a protected one (e.g. jcr:uuid).
106     * @param predicate the additional predicate to evaluate.
107     * @return <code>true</code> if the property has the given name,
108     *         <code>false</code> otherwise.
109     */
110    public static Predicate ignoreProtectedProperties(final Predicate predicate)
111    {
112        return new IgnoreProtectedPredicate(predicate);
113    }
114    
115    /** 
116     * Tests if the node has the following name.
117     * @param nodeName the node name.
118     * @return <code>true</code> if the node has the given name,
119     *         <code>false</code> otherwise.
120     */
121    public static Predicate nodeNamePredicate(final String nodeName)
122    {
123        return new Predicate()
124        {
125            @Override
126            public boolean evaluate(Object object)
127            {
128                if (object instanceof Node)
129                {
130                    try
131                    {
132                        return ((Node) object).getName().equals(nodeName);
133                    }
134                    catch (RepositoryException e)
135                    {
136                        throw new RuntimeException("Unable to get node name", e);
137                    }
138                }
139                
140                return false;
141            }
142        };
143    }
144    
145    /** 
146     * Tests if the node is of the given node type.
147     * @param nodeTypeName the node type name.
148     * @return <code>true</code> if the node has the given node type,
149     *         <code>false</code> otherwise.
150     */
151    public static Predicate nodeTypePredicate(final String nodeTypeName)
152    {
153        return new Predicate()
154        {
155            @Override
156            public boolean evaluate(Object object)
157            {
158                if (object instanceof Node)
159                {
160                    try
161                    {
162                        return ((Node) object).isNodeType(nodeTypeName);
163                    }
164                    catch (RepositoryException e)
165                    {
166                        throw new RuntimeException("Unable to test node type", e);
167                    }
168                }
169                
170                return false;
171            }
172        };
173    }
174    
175    private static final class IgnoreProtectedPredicate implements Predicate
176    {
177        private Predicate _predicate;
178        
179        IgnoreProtectedPredicate(Predicate predicate)
180        {
181            _predicate = predicate;
182        }
183        
184        @Override
185        public boolean evaluate(Object object)
186        {
187            if (object instanceof Property)
188            {
189                Property property = (Property) object;
190                
191                try
192                {
193                    Node parentNode = property.getParent();
194                    String propertyName = property.getName();
195                    
196                    if (parentNode.isNodeType("nt:frozenNode"))
197                    {
198                        if (propertyName.startsWith("jcr:"))
199                        {
200                            if (!propertyName.equals("jcr:encoding") && !propertyName.equals("jcr:mimeType")
201                                    && !propertyName.equals("jcr:data") && !propertyName.equals("jcr:lastModified"))
202                            {
203                                // Refuse potential protected property
204                                return false;
205                            }
206                        }
207                        
208                        return _predicate.evaluate(object);
209                    }
210                    else
211                    {
212                        // If property is not protected, do process it
213                        return !property.getDefinition().isProtected() && _predicate.evaluate(object);
214                    }
215                }
216                catch (RepositoryException e)
217                {
218                    throw new RuntimeException("Unable to process property", e);
219                }
220            }
221            
222            return false;
223        }
224    }
225    
226    /** 
227     * Tests if the node or property is allowed in the live workspace
228     * @return <code>true</code> if the property or node is allowed,
229     *         <code>false</code> otherwise.
230     */
231    public static Predicate isAllowedForLiveContent()
232    {
233        return _allowedLivePredicate;
234    }
235    
236    private static final class AllowedForLivePredicate implements Predicate
237    {
238        AllowedForLivePredicate()
239        {
240        }
241        
242        @Override
243        public boolean evaluate(Object object)
244        {
245            if (object instanceof Property)
246            {
247               
248                Property property = (Property) object;
249                try
250                {
251                    // oswf:* item are not allowed
252                    return !property.getName().startsWith("oswf:") 
253                            && !property.getName().equals(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":initial-content") 
254                            && !property.getName().equals(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":workflowRef")
255                            && !(property.getName().startsWith(EditContentFunction.JCR_REFERENCE_PREFIX) && property.getType() ==  PropertyType.REFERENCE);
256                }
257                catch (RepositoryException e)
258                {
259                    throw new RuntimeException("Unable to process property", e);
260                }
261            }
262            else if (object instanceof Node)
263            {
264                Node node = (Node) object;
265                try
266                {
267                    String nodeType = NodeTypeHelper.getNodeTypeName(node);
268                    
269                    // oswf:* item are not allowed
270                    return !nodeType.startsWith("oswf:");
271                }
272                catch (RepositoryException e)
273                {
274                    throw new RuntimeException("Unable to process node", e);
275                }
276            }
277            
278            return false;
279        }
280    }
281    
282    
283    /** 
284     * Tests if the node or property is allowed in the live workspace
285     * @param node The parent node.
286     * @return <code>true</code> if the property or node is allowed,
287     *         <code>false</code> otherwise.
288     */
289    public static Predicate isNonVersionned(Node node)
290    {
291        return new NonVersionnedPredicate(node);
292    }
293    
294    private static final class NonVersionnedPredicate implements Predicate
295    {
296        private Node _node;
297
298        NonVersionnedPredicate(Node node)
299        {
300            _node = node;
301        }
302        
303        @Override
304        public boolean evaluate(Object object)
305        {
306            if (object instanceof Property)
307            {
308                Property property = (Property) object;
309                
310                try
311                {
312                    if (!property.getParent().getIdentifier().equals(_node.getIdentifier()))
313                    {
314                        // The property must be one of a child node (of a non versionned node)
315                        return true;
316                    }
317                    return property.getDefinition().getOnParentVersion() == OnParentVersionAction.IGNORE;
318                }
319                catch (RepositoryException e)
320                {
321                    throw new RuntimeException("Unable to process property", e);
322                }
323            }
324            else if (object instanceof Node)
325            {
326                Node node = (Node) object;
327                try
328                {
329                    if (!node.getParent().getIdentifier().equals(_node.getIdentifier()))
330                    {
331                        // The node must be one of a child node (of a non versionned node)
332                        return true;
333                    }
334                    return node.getDefinition().getOnParentVersion() == OnParentVersionAction.IGNORE;
335                }
336                catch (RepositoryException e)
337                {
338                    throw new RuntimeException("Unable to process node", e);
339                }
340            }
341            
342            return false;
343        }
344    }
345}