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.jcr;
018
019import javax.jcr.Node;
020import javax.jcr.RepositoryException;
021import javax.jcr.nodetype.ConstraintViolationException;
022
023import org.apache.jackrabbit.util.Text;
024
025import org.ametys.plugins.repository.AbstractAmetysObject;
026import org.ametys.plugins.repository.AmetysObject;
027import org.ametys.plugins.repository.AmetysRepositoryException;
028import org.ametys.plugins.repository.RepositoryIntegrityViolationException;
029import org.ametys.plugins.repository.metadata.CompositeMetadata;
030import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata;
031import org.ametys.plugins.repository.metadata.jcr.JCRCompositeMetadata;
032
033/**
034 * Default implementation of an {@link AmetysObject}, backed by a JCR node.<br>
035 * This implementation heavily relies on its {@link SimpleAmetysObjectFactory} counterpart.
036 * @param <F> the actual type of factory.
037 */
038public class SimpleAmetysObject<F extends SimpleAmetysObjectFactory> extends AbstractAmetysObject implements JCRAmetysObject 
039{
040    /** The corresponding {@link SimpleAmetysObjectFactory} */
041    private F _factory;
042    
043    private Node _node;
044    
045    private String _name;
046    private String _parentPath;
047
048    /**
049     * Creates an {@link SimpleAmetysObject}.
050     * @param node the node backing this {@link AmetysObject}
051     * @param parentPath the parentPath in the Ametys hierarchy
052     * @param factory the DefaultTraversableAmetysObjectFactory which created the AmetysObject
053     */
054    public SimpleAmetysObject(Node node, String parentPath, F factory)
055    {
056        _node = node;
057        _parentPath = parentPath;
058        _factory = factory;
059        
060        try
061        {
062            _name = Text.unescapeIllegalJcrChars(_node.getName());
063        }
064        catch (RepositoryException e)
065        {
066            throw new AmetysRepositoryException("Unable to get node name", e);
067        }
068    }
069    
070    /**
071     * Retrieves the factory.
072     * @return the factory.
073     */
074    protected F _getFactory()
075    {
076        return _factory;
077    }
078    
079    public String getName() throws AmetysRepositoryException
080    {
081        return _name;
082    }
083    
084    /**
085     * Returns the root {@link CompositeMetadata} of this {@link AmetysObject}.
086     * @return the root {@link CompositeMetadata} of this {@link AmetysObject}
087     * @deprecated use getRepositoryData instead
088     */
089    @Deprecated
090    public ModifiableCompositeMetadata getMetadataHolder()
091    {
092        return new JCRCompositeMetadata(getNode(), _getFactory()._getResolver());
093    }
094    
095    public String getParentPath() throws AmetysRepositoryException
096    {
097        if (_parentPath == null)
098        {
099            _parentPath = getParent().getPath();
100        }
101        
102        return _parentPath;
103    }
104    
105    /**
106     * Invalidates cached parent path.
107     */
108    protected void _invalidateParentPath()
109    {
110        _parentPath = null;
111    }
112    
113    /**
114     * Recompute the name from the Node.<br>
115     * To be used when the node name changesd internally.
116     */
117    protected void _invalidateName()
118    {
119        try
120        {
121            _name = Text.unescapeIllegalJcrChars(getNode().getName());
122        }
123        catch (RepositoryException e)
124        {
125            throw new AmetysRepositoryException(e);
126        }
127    }
128    
129    public String getPath() throws AmetysRepositoryException
130    {
131        return getParentPath() + "/" + getName();
132    }
133    
134    public Node getNode()
135    {
136        return _node;
137    }
138    
139    public String getId()
140    {
141        try
142        {
143            return _factory.getScheme() + "://" + _node.getIdentifier();
144        }
145        catch (RepositoryException e)
146        {
147            throw new AmetysRepositoryException("Unable to get node UUID", e);
148        }
149    }
150    
151    @Override
152    public void rename(String newName) throws AmetysRepositoryException
153    {
154        try
155        {
156            NodeHelper.rename(getNode(), newName);
157            _name = Text.unescapeIllegalJcrChars(getNode().getName());
158        }
159        catch (RepositoryException e)
160        {
161            throw new AmetysRepositoryException(e);
162        }
163    }
164    
165    public void remove() throws AmetysRepositoryException, RepositoryIntegrityViolationException
166    {
167        try
168        {
169            getNode().remove();
170        }
171        catch (ConstraintViolationException e)
172        {
173            throw new RepositoryIntegrityViolationException(e);
174        }
175        catch (RepositoryException e)
176        {
177            throw new AmetysRepositoryException(e);
178        }
179    }
180
181    @SuppressWarnings("unchecked")
182    public <A extends AmetysObject> A getParent() throws AmetysRepositoryException
183    {
184        return (A) _factory.getParent(this);
185    }
186    
187    @Override
188    public boolean needsSave() throws AmetysRepositoryException
189    {
190        try
191        {
192            return _node.getSession().hasPendingChanges();
193        }
194        catch (RepositoryException e)
195        {
196            throw new AmetysRepositoryException(e);
197        }
198    }
199
200    public void saveChanges() throws AmetysRepositoryException
201    {
202        try
203        {
204            getNode().getSession().save();
205        }
206        catch (javax.jcr.RepositoryException e)
207        {
208            throw new AmetysRepositoryException("Unable to save changes", e);
209        }
210    }
211    
212    @Override
213    public void revertChanges() throws AmetysRepositoryException
214    {
215        try
216        {
217            getNode().refresh(false);
218        }
219        catch (javax.jcr.RepositoryException e)
220        {
221            throw new AmetysRepositoryException("Unable to revert changes.", e);
222        }
223    }
224    
225}