View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors. 
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   *
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.jcr;
25  
26  import java.util.Iterator;
27  import javax.jcr.InvalidItemStateException;
28  import javax.jcr.Item;
29  import javax.jcr.ItemNotFoundException;
30  import javax.jcr.ItemVisitor;
31  import javax.jcr.Node;
32  import javax.jcr.PathNotFoundException;
33  import javax.jcr.Property;
34  import javax.jcr.RepositoryException;
35  import javax.jcr.lock.Lock;
36  import javax.jcr.lock.LockException;
37  import javax.jcr.nodetype.ConstraintViolationException;
38  import javax.jcr.nodetype.PropertyDefinition;
39  import javax.jcr.version.VersionException;
40  import net.jcip.annotations.NotThreadSafe;
41  import org.modeshape.common.util.CheckArg;
42  import org.modeshape.graph.property.Name;
43  import org.modeshape.graph.property.Path;
44  import org.modeshape.graph.property.ValueFactory;
45  import org.modeshape.graph.session.GraphSession.PropertyInfo;
46  import org.modeshape.jcr.SessionCache.JcrPropertyPayload;
47  import org.modeshape.jcr.SessionCache.NodeEditor;
48  
49  /**
50   * An abstract {@link Property JCR Property} implementation.
51   */
52  @NotThreadSafe
53  abstract class AbstractJcrProperty extends AbstractJcrItem implements Property, Comparable<Property> {
54  
55      protected final AbstractJcrNode node;
56      protected final Name name;
57  
58      AbstractJcrProperty( SessionCache cache,
59                           AbstractJcrNode node,
60                           Name name ) {
61          super(cache);
62          assert node != null;
63          assert name != null;
64          this.node = node;
65          this.name = name;
66      }
67  
68      final NodeEditor editor() throws ItemNotFoundException, InvalidItemStateException, RepositoryException {
69          return node.editor();
70      }
71  
72      public abstract boolean isMultiple();
73  
74      /**
75       * Checks that this property's parent node is not already locked by another session. If the parent node is not locked or the
76       * parent node is locked but the lock is owned by this {@code Session}, this method completes silently. If the parent node is
77       * locked (either directly or as part of a deep lock from an ancestor), this method throws a {@code LockException}.
78       * 
79       * @throws LockException if the parent node of this property is locked (that is, if {@code getParent().isLocked() == true &&
80       *         getParent().getLock().getLockToken() == null}.
81       * @throws RepositoryException if any other error occurs
82       * @see Node#isLocked()
83       * @see Lock#getLockToken()
84       */
85      protected final void checkForLock() throws LockException, RepositoryException {
86  
87          if (this.getParent().isLocked() && !getParent().getLock().isLockOwningSession()) {
88              Lock parentLock = this.getParent().getLock();
89              if (parentLock != null && parentLock.getLockToken() == null) {
90                  throw new LockException(JcrI18n.lockTokenNotHeld.text(this.getParent().location));
91              }
92          }
93      }
94  
95      /**
96       * {@inheritDoc}
97       * 
98       * @throws IllegalArgumentException if <code>visitor</code> is <code>null</code>.
99       * @see javax.jcr.Item#accept(javax.jcr.ItemVisitor)
100      */
101     public final void accept( ItemVisitor visitor ) throws RepositoryException {
102         CheckArg.isNotNull(visitor, "visitor");
103         checkSession();
104         visitor.visit(this);
105     }
106 
107     final PropertyInfo<JcrPropertyPayload> propertyInfo() throws PathNotFoundException, RepositoryException {
108         return node.nodeInfo().getProperty(name);
109     }
110 
111     final Name name() {
112         return name;
113     }
114 
115     final JcrPropertyPayload payload() throws RepositoryException {
116         return propertyInfo().getPayload();
117     }
118 
119     final org.modeshape.graph.property.Property property() throws RepositoryException {
120         return propertyInfo().getProperty();
121     }
122 
123     JcrValue createValue( Object value ) throws RepositoryException {
124         return new JcrValue(context().getValueFactories(), this.cache, payload().getPropertyType(), value);
125     }
126 
127     final JcrValue createValue( Object value,
128                                 int propertyType ) {
129         return new JcrValue(context().getValueFactories(), this.cache, propertyType, value);
130     }
131 
132     @Override
133     Path path() throws RepositoryException {
134         return context().getValueFactories().getPathFactory().create(node.path(), name);
135     }
136 
137     /**
138      * {@inheritDoc}
139      * 
140      * @see javax.jcr.Property#getType()
141      */
142     public int getType() throws RepositoryException {
143         checkSession();
144         return payload().getPropertyType();
145     }
146 
147     /**
148      * {@inheritDoc}
149      * 
150      * @see javax.jcr.Property#getDefinition()
151      */
152     public final PropertyDefinition getDefinition() throws RepositoryException {
153         checkSession();
154         return cache.session().nodeTypeManager().getPropertyDefinition(payload().getPropertyDefinitionId());
155     }
156 
157     /**
158      * {@inheritDoc}
159      * <p>
160      * This method returns the string form of the {@link org.modeshape.graph.property.Property#getName()}, computed dynamically
161      * each time this method is called to ensure that the property namespace prefix is used.
162      * </p>
163      * 
164      * @see javax.jcr.Item#getName()
165      */
166     public final String getName() {
167         return name.getString(namespaces());
168     }
169 
170     /**
171      * {@inheritDoc}
172      * 
173      * @see javax.jcr.Item#getParent()
174      */
175     public final AbstractJcrNode getParent() {
176         return node;
177     }
178 
179     /**
180      * {@inheritDoc}
181      * 
182      * @see javax.jcr.Item#getPath()
183      */
184     public final String getPath() throws RepositoryException {
185         return path().getString(namespaces());
186     }
187 
188     /**
189      * {@inheritDoc}
190      * 
191      * @see javax.jcr.Item#isModified()
192      */
193     public final boolean isModified() {
194         try {
195             checkSession();
196             return propertyInfo().isModified();
197         } catch (RepositoryException re) {
198             throw new IllegalStateException(re);
199         }
200     }
201 
202     /**
203      * {@inheritDoc}
204      * 
205      * @see javax.jcr.Item#isNew()
206      */
207     public final boolean isNew() {
208         try {
209             checkSession();
210             return propertyInfo().isNew();
211         } catch (RepositoryException re) {
212             throw new IllegalStateException(re);
213         }
214     }
215 
216     /**
217      * {@inheritDoc}
218      * 
219      * @return false
220      * @see javax.jcr.Item#isNode()
221      */
222     public final boolean isNode() {
223         return false;
224     }
225 
226     /**
227      * {@inheritDoc}
228      * 
229      * @see javax.jcr.Item#isSame(javax.jcr.Item)
230      */
231     @Override
232     public final boolean isSame( Item otherItem ) throws RepositoryException {
233         checkSession();
234         if (otherItem instanceof Property) {
235             Property otherProperty = (Property)otherItem;
236             // The nodes that own the properties must be the same ...
237             if (!getParent().isSame(otherProperty.getParent())) return false;
238             // The properties must have the same name ...
239             return getName().equals(otherProperty.getName());
240         }
241         return false;
242     }
243 
244     /**
245      * {@inheritDoc}
246      * 
247      * @throws UnsupportedOperationException always
248      * @see javax.jcr.Item#refresh(boolean)
249      */
250     public void refresh( boolean keepChanges ) {
251         throw new UnsupportedOperationException();
252     }
253 
254     /**
255      * {@inheritDoc}
256      * 
257      * @see javax.jcr.Item#remove()
258      */
259     public void remove() throws VersionException, LockException, ConstraintViolationException, RepositoryException {
260         checkSession();
261         AbstractJcrNode parentNode = getParent();
262         if (parentNode.isLocked()) {
263             Lock parentLock = parentNode.lockManager().getLock(parentNode);
264             if (parentLock != null && !parentLock.isLockOwningSession()) {
265                 throw new LockException(JcrI18n.lockTokenNotHeld.text(getPath()));
266             }
267         }
268 
269         if (!parentNode.isCheckedOut()) {
270             throw new VersionException(JcrI18n.nodeIsCheckedIn.text(getPath()));
271         }
272 
273         editor().removeProperty(name);
274     }
275 
276     /**
277      * {@inheritDoc}
278      * 
279      * @see javax.jcr.Item#save()
280      */
281     public void save() throws RepositoryException {
282         checkSession();
283         // This is not a correct implementation, but it's good enough to work around some TCK requirements for version tests
284         // Plus, Item.save() has been removed from the JCR 2.0 spec (and deprecated in JCR 2.0's Java API).
285         getParent().save();
286     }
287 
288     /**
289      * {@inheritDoc}
290      * 
291      * @see java.lang.Comparable#compareTo(java.lang.Object)
292      */
293     public int compareTo( Property that ) {
294         if (that == this) return 0;
295         try {
296             return this.getName().compareTo(that.getName());
297         } catch (RepositoryException e) {
298             throw new RuntimeException(e);
299         }
300     }
301 
302     /**
303      * {@inheritDoc}
304      * 
305      * @see java.lang.Object#toString()
306      */
307     @Override
308     public String toString() {
309         try {
310             ValueFactory<String> stringFactory = session().getExecutionContext().getValueFactories().getStringFactory();
311             StringBuilder sb = new StringBuilder();
312             sb.append(getName()).append('=');
313             org.modeshape.graph.property.Property property = propertyInfo().getProperty();
314             if (isMultiple()) {
315                 sb.append('[');
316                 Iterator<?> iter = property.iterator();
317                 if (iter.hasNext()) {
318                     sb.append(stringFactory.create(iter.next()));
319                     if (iter.hasNext()) sb.append(',');
320                 }
321                 sb.append(']');
322             } else {
323                 sb.append(stringFactory.create(property.getFirstValue()));
324             }
325             return sb.toString();
326         } catch (RepositoryException e) {
327             return super.toString();
328         }
329     }
330 }