KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > exoplatform > services > jcr > impl > core > NodeImpl


1 /***************************************************************************
2  * Copyright 2001-2003 The eXo Platform SARL All rights reserved. *
3  * Please look at license.txt in info directory for more license detail. *
4  **************************************************************************/

5
6 package org.exoplatform.services.jcr.impl.core;
7
8
9 import java.io.InputStream JavaDoc;
10 import java.util.ArrayList JavaDoc;
11 import java.util.Calendar JavaDoc;
12 import java.util.Collection JavaDoc;
13 import java.util.Iterator JavaDoc;
14 import java.util.List JavaDoc;
15
16 import javax.jcr.ActionVetoedException;
17 import javax.jcr.BinaryValue;
18 import javax.jcr.BooleanValue;
19 import javax.jcr.DateValue;
20 import javax.jcr.DoubleValue;
21 import javax.jcr.Item;
22 import javax.jcr.ItemExistsException;
23 import javax.jcr.ItemNotFoundException;
24 import javax.jcr.ItemVisitor;
25 import javax.jcr.LongValue;
26 import javax.jcr.MergeException;
27 import javax.jcr.NoSuchWorkspaceException;
28 import javax.jcr.Node;
29 import javax.jcr.NodeIterator;
30 import javax.jcr.PathNotFoundException;
31 import javax.jcr.Property;
32 import javax.jcr.PropertyIterator;
33 import javax.jcr.PropertyType;
34 import javax.jcr.RepositoryException;
35 import javax.jcr.StringIterator;
36 import javax.jcr.StringValue;
37 import javax.jcr.UnsupportedRepositoryOperationException;
38 import javax.jcr.Value;
39 import javax.jcr.ValueFormatException;
40 import javax.jcr.access.AccessDeniedException;
41 import javax.jcr.nodetype.ConstraintViolationException;
42 import javax.jcr.nodetype.NoSuchNodeTypeException;
43 import javax.jcr.nodetype.NodeDef;
44 import javax.jcr.nodetype.NodeType;
45 import javax.jcr.nodetype.PropertyDef;
46 import javax.jcr.version.Version;
47 import javax.jcr.version.VersionHistory;
48
49 import org.exoplatform.services.jcr.core.ItemLocation;
50 import org.exoplatform.services.jcr.core.NodeData;
51 import org.exoplatform.services.jcr.impl.core.itemfilters.AllAcceptedFilter;
52 import org.exoplatform.services.jcr.impl.core.itemfilters.ItemFilter;
53 import org.exoplatform.services.jcr.impl.core.itemfilters.NamePatternFilter;
54 import org.exoplatform.services.jcr.impl.core.nodetype.NodeDefImpl;
55 import org.exoplatform.services.jcr.impl.core.nodetype.NodeTypeImpl;
56 import org.exoplatform.services.jcr.impl.core.nodetype.NodeTypeManagerImpl;
57 import org.exoplatform.services.jcr.impl.util.EntityCollection;
58 import org.exoplatform.services.jcr.impl.util.PropertyConvertor;
59
60 /**
61  * Created by The eXo Platform SARL.
62  *
63  * @author <a HREF="mailto:geaz@users.sourceforge.net">Gennady Azarenkov</a>
64  * @version $Id: NodeImpl.java,v 1.59 2004/11/02 18:36:32 geaz Exp $
65  */

66
67 public class NodeImpl extends ItemImpl implements NodeData {
68
69   private List JavaDoc properties;
70
71
72   /**
73    * Level1 constructor
74    */

75   public NodeImpl(String JavaDoc absPath) throws PathNotFoundException {
76     this(absPath, "nt:default");
77   }
78
79   public NodeImpl(String JavaDoc absPath, String JavaDoc type) throws PathNotFoundException {
80     super(absPath);
81     properties = new ArrayList JavaDoc();
82     try {
83       properties.add(new PropertyImpl(location.getPath() + "/jcr:primaryType",
84           new StringValue(type), PropertyType.STRING));
85     } catch (Exception JavaDoc e) {
86       throw new PathNotFoundException("NodeImpl() failed. Reason: " + e.getMessage());
87     }
88
89   }
90
91   public NodeImpl(String JavaDoc absPath, List JavaDoc props) throws PathNotFoundException {
92     super(absPath);
93     this.properties = props;
94   }
95
96   // Deep copy
97
public NodeImpl(NodeImpl node) throws PathNotFoundException, RepositoryException {
98     super(node.getPath());
99     refresh(node);
100
101   }
102
103
104   /**
105    * Returns the node at <code>relPath</code> relative to <code>this</code> node.
106    * The properties and child nodes of the returned node can then be read
107    * (and if permissions allow) changed and written. However, any changes
108    * made to this node, its properties or its child nodes
109    * (and their properties and child nodes, etc.) will only be persisted to
110    * the repository upon calling save. Within the scope of a single
111    * <code>Ticket</code> object, if a node has been acquired with
112    * <code>getNode</code>, any subsequent call of <code>getNode</code>
113    * re-acquiring the same node will return a reference to same object,
114    * not a new copy.
115    *
116    * @param relPath The relative path of the node to retrieve.
117    * @return The node at <code>relPath</code>.
118    * @throws PathNotFoundException If no node exists at the
119    * specified path.
120    * @throws RepositoryException If another error occurs.
121    */

122   public Node getNode(String JavaDoc path) throws PathNotFoundException, RepositoryException {
123     ItemLocation loc = new ItemLocation(this.location.getPath(), path);
124     if (loc.equals(this.location))
125       return this;
126     else
127       return ticket.getNodeByAbsPath(loc.getPath());
128   }
129
130   /**
131    * 6.2.1
132    * Gets all child nodes of this node. Does not include
133    * properties of this node. The same save and re-acquisition
134    * semantics apply as with getNode.
135    */

136   public NodeIterator getNodes() throws RepositoryException {
137     log.debug("get nodes");
138     return retrieveChildNodes(new AllAcceptedFilter());
139   }
140
141   /**
142    * 6.2.1
143    * Gets all child nodes of this node that match
144    * namePattern. The pattern may be a full name or a
145    * partial name with one or more wildcard characters
146    * ("*"), or a disjunction (using the �|� character to
147    * represent logical OR) of these. For example,
148    * N.getNodes("jcr:* | myapp:report")
149    * would return a NodeIterator holding all child nodes of
150    * N that are either called myapp:report or begin with the
151    * prefix jcr:. The pattern does not represent paths, i.e.,
152    * it may not contain / characters.
153    * The same save and re-acquisition semantics apply as with getNode.
154    */

155   public NodeIterator getNodes(String JavaDoc namePattern) throws RepositoryException {
156     return retrieveChildNodes(new NamePatternFilter(namePattern));
157   }
158
159   /**
160    * Returns the property at <code>relPath</code> relative to <code>this</code>
161    * node. The same <code>save</code> and re-acquisition
162    * semantics apply as with <code>{@link #getNode(String)}</code>.
163    *
164    * @param relPath The relative path of the property to retrieve.
165    * @return The property at <code>relPath</code>.
166    * @throws PathNotFoundException If no property exists at the
167    * specified path.
168    * @throws RepositoryException If another error occurs.
169    */

170   public Property getProperty(String JavaDoc path) throws PathNotFoundException, RepositoryException {
171     ItemLocation loc = new ItemLocation(this.location.getPath(), path);
172     if (!this.location.getPath().equals(loc.getParentPath())) {
173       Node node = ticket.getNodeByAbsPath(loc.getParentPath());
174       return node.getProperty(loc.getName());
175     }
176
177     EntityCollection res = retrieveProperties(new NamePatternFilter(loc.getName()));
178     if (res.hasNext()) {
179       PropertyImpl property = (PropertyImpl) res.nextProperty();
180       property.setTicket(ticket);
181       return property;
182     } else
183       throw new PathNotFoundException("No property at the path '" + loc.getPath() + "'");
184   }
185
186
187   /**
188    * 6.2.1
189    * Gets all properties of this node. Does not include child
190    * nodes of this node. The same save and re-acquisition
191    * semantics apply as with getNode.
192    */

193   public PropertyIterator getProperties() {
194     return retrieveProperties(new AllAcceptedFilter());
195   }
196
197   /**
198    * 6.2.1
199    * Gets all properties of this node that match
200    * namePattern. The pattern may be a full name or a
201    * partial name with one or more wildcard characters
202    * ("*"), or a disjunction (using the �|� character to
203    * represent logical OR) of these. For example,
204    * N.getProperties("jcr:* | myapp:name")
205    * would return a PropertyIterator holding all
206    * properties of N that are either called myapp:name or
207    * begin with the prefix jcr. The pattern does not
208    * represent paths, i.e., it may not contain / characters.
209    * The same save and re-acquisition semantics apply as with getNode.
210    */

211   public PropertyIterator getProperties(String JavaDoc namePattern) {
212     return retrieveProperties(new NamePatternFilter(namePattern));
213   }
214
215   /**
216    * 6.2.1
217    * Returns the first property of this node found with the
218    * specified value. What makes a particular property
219    * "first" (that is, the search order) is implementation
220    * dependent. If the specified value and the value of a
221    * property are of different types then a conversion is
222    * attempted before the equality test is made. Returns
223    * null if no such property is found. The same save and
224    * re-acquisition semantics apply as with getNode.
225    */

226   public Property findProperty(Value value) throws RepositoryException {
227     PropertyIterator iterator = findProperties(value);
228     if (iterator.hasNext())
229       return iterator.nextProperty();
230     return null;
231   }
232
233   /**
234    * 6.2.1
235    * Returns all properties of this node with the specified
236    * value. If the specified value and the value of a
237    * property are of different types then a conversion is
238    * attempted before the equality test is made. Returns an
239    * empty iterator if no such property could be found. The
240    * same save and re-acquisition semantics apply as with getNode.
241    */

242   public PropertyIterator findProperties(Value value) throws RepositoryException {
243     log.debug("find properties");
244     if (value == null)
245       throw new RepositoryException("Value must not be null");
246
247     ArrayList JavaDoc props = new ArrayList JavaDoc();
248     PropertyIterator propsI = retrieveProperties(new AllAcceptedFilter());
249     while (propsI.hasNext()) {
250       PropertyImpl prop = (PropertyImpl) propsI.nextProperty();
251       Value convertedValue = null;
252       try {
253         convertedValue = PropertyConvertor.convert(prop.getValue(), value.getType());
254         log.debug("find properties - test if "+value+" of "+prop+" equals "+convertedValue+" result="+value.equals(convertedValue));
255
256         if (value.equals(convertedValue))
257           props.add(prop);
258
259       } catch (IllegalStateException JavaDoc e) {
260       } catch (RepositoryException e) {
261       }
262     }
263     return new EntityCollection(props);
264   }
265
266   public ItemIterator getItems(ItemFilter filter) {
267     List JavaDoc items = retrieveChildNodes(filter).getList();
268     items.addAll(retrieveProperties(filter).getList());
269     return new EntityCollection(items);
270   }
271
272   /**
273    * 6.2.1
274    * A node's type can specify a
275    * maximum of one of its child items (child node or
276    * property) as its primary child item. This method
277    * traverses the chain of primary child items of this node
278    * until it either encounters a property or encounters a
279    * node that does not have a primary child item. It then
280    * returns that property or node. If this node itself (the
281    * one that this method is being called on) has no
282    * primary child item then this method throws an
283    * ItemNotFoundException. The same save and re-acquisition
284    * semantics apply as with getNode.
285    */

286   public Item getPrimaryItem() throws ItemNotFoundException, RepositoryException {
287     NodeImpl node = this;
288
289     // Traverse children...
290
ItemImpl item = findPrimaryItem(node);
291
292 // System.out.println("ITEM returned " + item.getPath());
293

294     if (item.equals(this))
295       throw new ItemNotFoundException("NodeImpl.getPrimaryItem() Node does not have the primary items!");
296     else
297       return item;
298   }
299
300   /**
301    * Returns the UUID of this node as recorded in the node's jcr:UUID
302    * property. This method only works on nodes of mixin node type
303    * <code>mix:referenceable</code>. On nonreferenceable nodes, this method
304    * throws an <code>UnsupportedRepositoryOperationException</code>.
305    *
306    * @return the UUID of this node
307    * @throws UnsupportedRepositoryOperationException
308    * If this node nonreferenceable.
309    * @throws RepositoryException If another error occurs.
310    */

311   public String JavaDoc getUUID() throws UnsupportedRepositoryOperationException, RepositoryException {
312     try {
313       return getProperty("jcr:uuid").getString();
314     } catch (PathNotFoundException e) {
315       throw new UnsupportedRepositoryOperationException("that node should have mix:referenceable mixin type", e);
316     }
317   }
318
319
320   /**
321    * 6.2.1
322    * Returns true if a node exists at path and false otherwise.
323    */

324   public boolean hasNode(String JavaDoc relPath) throws RepositoryException {
325     try {
326       getNode(relPath);
327     } catch (PathNotFoundException e) {
328 // System.out.println("PATHHHHHHHHHHHHHHHHHHH NOTTTTTTTTTTTTTT F "+relPath);
329
return false;
330     }
331     return true;
332   }
333
334   /**
335    * 6.2.1
336    * Returns true if this node has one or more child nodes.
337    * Returns false otherwise.
338    */

339   public boolean hasNodes() throws RepositoryException {
340     return getNodes().hasNext();
341   }
342
343   /**
344    * 6.2.1
345    * Returns true if a property exists at path and false otherwise.
346    */

347   public boolean hasProperty(String JavaDoc relPath) throws RepositoryException {
348     try {
349       if (getProperty(relPath) != null)
350         return true;
351     } catch (RepositoryException e) {
352     }
353     return false;
354   }
355
356   /**
357    * 6.2.1
358    * Returns true if this node has one or more properties.Returns false otherwise.
359    */

360   public boolean hasProperties() throws RepositoryException {
361     return getProperties().hasNext();
362   }
363
364   /**
365    * 6.3.1
366    * Creates a new node at path. The new node will only be
367    * persisted to the workspace when save is called on the new
368    * node's parent node or higher-order ancestor node
369    * (parent's parent, etc.) and if the structure of the new node
370    * (its child nodes and properties) meets the constraint
371    * criteria of the parent node's node type.
372    * If path implies intermediary nodes that do not exist, then
373    * a PathNotFoundException is thrown immediately (i.e., not at save).
374    * If an item already exists at path then an
375    * ItemExistsException is thrown on save.
376    * If an attempt is made to add a node as a child of a
377    * property then a ConstraintViolationException is
378    * thrown immediately (not on save).
379    * Node type validation is done at save. At that time, the
380    * new node's node type will be determined by the node type
381    * of its parent. If that node type does not unambiguously
382    * determine the child node's type then the system checks if
383    * the built-in node type nt:default is acceptable. If so,
384    * then it is assigned. If not, then a
385    * ConstraintViolationException will be thrown on save.
386    */

387   public Node addNode(String JavaDoc path) throws ItemExistsException, PathNotFoundException,
388       ConstraintViolationException, RepositoryException {
389
390     ItemLocation loc = new ItemLocation(location.getPath(), path);
391     String JavaDoc name = loc.getName();
392     NodeImpl parent = getParentToAddNodeTo(loc.getParentPath());
393
394     if (!parent.getPrimaryNodeType().canAddChildNode(name)) {
395       ticket.getNodesManager().addValidationError(loc.getPath(),
396           new ConstraintViolationException("Can't add node " + name) );
397     }
398
399
400     // Find node Type
401
String JavaDoc nodeTypeName = findNodeType(parent, name);
402
403     if (nodeTypeName == null)
404       throw new ConstraintViolationException("Can not define node type : for <" + name + "> !");
405
406     return addNode(path, nodeTypeName);
407   }
408
409   private String JavaDoc findNodeType(NodeImpl parent, String JavaDoc name) {
410     String JavaDoc nodeTypeName = null;
411     NodeDef[] nodeDefs = parent.getPrimaryNodeType().getChildNodeDefs();
412     boolean residualFlag = false;
413     if (nodeDefs != null) {
414       for (int i = 0; i < nodeDefs.length; i++) {
415         NodeDef nodeDef = nodeDefs[i];
416         if (nodeDef.getName() != null) {
417           if (nodeDef.getName().equals(name)) {
418             return nodeDef.getDefaultPrimaryType().getName();
419           }
420         } else {
421           residualFlag = true;
422         }
423       }
424       //manage residual definitions
425
if (residualFlag) {
426         for (int i = 0; i < nodeDefs.length; i++) {
427           NodeDef nodeDef = nodeDefs[i];
428           if (nodeDef.getName() == null) {
429             return nodeDef.getDefaultPrimaryType().getName();
430           }
431         }
432       }
433     }
434     return nodeTypeName;
435   }
436
437   /**
438    * 6.3.1
439    * Creates a new node at path of the specified node type.
440    * The same as addNode(String path) except that the type
441    * of the new node is explicitly specified.
442    * If the node type of the new node's parent is incompatible
443    * with the specified node type then a
444    * ConstraintViolationException is thrown on save().
445    * If nodeTypeName is not recognized, then a
446    * NoSuchNodeTypeException is thrown on save.
447    */

448   public Node addNode(String JavaDoc path, String JavaDoc nodeTypeName)
449       throws ItemExistsException, PathNotFoundException, NoSuchNodeTypeException,
450       ConstraintViolationException, RepositoryException {
451     // Primary Node type Name!!!!
452
NodeType type = null;
453
454     ItemLocation loc = new ItemLocation(location.getPath(), path);
455     String JavaDoc name = loc.getName();
456     NodeImpl parent = getParentToAddNodeTo(loc.getParentPath());
457
458     if( ticket.getNodesManager().getNodeByPath(loc.getPath()) != null )
459       ticket.getNodesManager().addValidationError(loc.getPath(), new ItemExistsException("Can't add node " + path + ". The node "
460           + loc.getPath() + " already exists") );
461
462     NodeType parentNodeType = parent.getPrimaryNodeType();
463     if (!parentNodeType.canAddChildNode(name, nodeTypeName)) {
464       log.debug("Can't add node " + name + " type " + nodeTypeName+ " to parent type "+parentNodeType.getName());
465       ticket.getNodesManager().addValidationError(loc.getPath(), new ConstraintViolationException("Can't add node " + name
466           + " type " + nodeTypeName+ " to parent type "+parentNodeType.getName()));
467     }
468
469     // NodeType
470
if (nodeTypeName != null) {
471       type = NodeTypeManagerImpl.getInstance().getNodeType(nodeTypeName);
472     } else {
473       throw new ConstraintViolationException("Add Node failed: Node Type <" + nodeTypeName + "> not found!");
474     }
475
476     if (type.isMixin())
477       throw new ConstraintViolationException("Add Node failed: Node Type <" + nodeTypeName + "> is MIXIN type!");
478
479     log.debug("Add node. Parent: " + parent + " path: " + path + " type " + type);
480     NodeImpl node = createNode(parent, name, type);
481
482     return node;
483   }
484
485   private NodeImpl getParentToAddNodeTo(String JavaDoc parentPath) throws PathNotFoundException, ConstraintViolationException {
486     try {
487       return (NodeImpl) getNode(parentPath);
488     } catch (RepositoryException e) {
489       try {
490         getProperty(parentPath);
491       } catch (RepositoryException e1) {
492         throw new PathNotFoundException("Add Node failed: parent node at <" + parentPath + "> not found!");
493       }
494       throw new ConstraintViolationException("Can not add a node to a property item");
495     }
496   }
497
498   /**
499    * Adds the existing node at <code>absPath</code> as child of this node, thus adding
500    * <code>this</code> node as an addional parent of the node at <code>absPath</code>.
501    * <p/>
502    * This change will be persisted (if valid) on <code>save</code>.
503    * <p/>
504    * If the node at <code>absPath</code> is not of mixin type
505    * <code>mix:referenceable</code>, a <code>ConstraintViolationException</code>
506    * will be thrown on <code>save</code>.
507    * <p/>
508    * The name of the new child node as accessed from <code>this</code> node
509    * will be the same as its current name in <code>absPath</code> (that is, the last path
510    * segment in that <code>absPath</code>).
511    *
512    * @param absPath The absolute path of the new child node.
513    * @return The new child node.
514    * @throws PathNotFoundException If no node exists at <code>absPath</code>.
515    * @throws RepositoryException In level 2: If another error occurs.
516    */

517   public Node addExistingNode(String JavaDoc absPath) throws PathNotFoundException, RepositoryException {
518     ItemLocation location = new ItemLocation(absPath);
519     return addExistingNode(absPath, location.getName());
520 // throw new RepositoryException("Add existing node is not supported yet!");
521
}
522
523   /**
524    * The same as <code>addExistingNode(String absPath)</code> except that the
525    * node at <code>absPath</code> adopts <code>newName</code> in the path
526    * where this node is its parent.
527    *
528    * @param absPath The absolute path of the new child node.
529    * @param newName The new name for this node when referenced as a child of this node.
530    * @return The new child node.
531    * @throws PathNotFoundException If no node exists at <code>absPath</code>.
532    * @throws RepositoryException If another error occurs.
533    */

534   public Node addExistingNode(String JavaDoc absPath, String JavaDoc newName) throws PathNotFoundException, RepositoryException {
535
536     NodeImpl realNode = (NodeImpl)ticket.getNodesManager().getNodeByPath(absPath);
537     if(realNode == null)
538        throw new PathNotFoundException("There are no node at "+realNode);
539
540     Property uuid = realNode.getPermanentProperty("jcr:uuid");
541     if (uuid == null) {
542         ticket.getNodesManager().addValidationError(absPath, new ConstraintViolationException("Can't add reference as " + realNode + " is not referenceable."));
543         log.debug("Can't add reference as " + absPath + " is not referenceable.");
544      }
545
546     String JavaDoc refPath = new ItemLocation(getPath(), newName).getPath();
547 // NodeImpl node = new NodeImpl(refPath, realNode.getPermanentProperties());
548

549     ticket.getNodesManager().addReference(uuid.getString(), refPath);
550
551     return realNode;
552
553 // throw new RepositoryException("Add existing node is not supported yet!");
554
}
555
556
557   /**
558    * 6.3.2
559    * Sets the property called name to the specified value.
560    * The property type of the property will be that specified
561    * by the node type of this node.
562    * If the property type of the supplied Value object is
563    * different from that required then a best-effort
564    * conversion is attempted. If the conversion fails, a
565    * ConstraintViolationException is thrown on the next save.
566    * If the node type of this node does not indicate a
567    * specific property type, then the property type of the
568    * supplied Value object is used.
569    * If the property already exists (has previously been set)
570    * it assumes both the new value and new property type.
571    * To erase a property, use Node.remove.
572    * To persist the addition, removal or change of a
573    * property to the workspace, save must be called on
574    * this node or a higher-order ancestor of the property.
575    */

576   public Property setProperty(String JavaDoc name, Value value) throws ValueFormatException, RepositoryException {
577     if (value == null)
578       throw new ValueFormatException("Set Property failed: value type could not be recognized!");
579
580     return setProperty(name, value, value.getType());
581   }
582
583   /**
584    * 6.3.2
585    */

586   public Property setProperty(String JavaDoc name, Value value, int type)
587       throws ValueFormatException, RepositoryException {
588     Value[] array = {value};
589     return setProperty(name, array, type);
590   }
591
592
593   /**
594    * Sets the specified property to the specified array of values (assuming it
595    * is a multi-value property) and the specified type. Same as
596    * <code>{@link #setProperty(String name, Value value, int type)}<code>
597    * except that an array of <code>Value</code> objects is assigned instead
598    * of a single <code>Value</code>. If an array of more than one element
599    * is passed and the property is not multi-valued then a
600    * <code>ValueFormatException</code> is thrown.
601    *
602    * @param name the name of the property to be set.
603    * @param values an array of <code>Value</code> objects.
604    * @param type a <code>PropertyType</code> constant.
605    * @return the updated <code>Property</code> object.
606    * @throws ValueFormatException if the type or format of <code>values</code>
607    * is incompatible with the type of the specified property or if
608    * <code>values</code> is incompatible with (i.e. can not be converted to)
609    * <code>type</code> or if an array of more than one value is being passed
610    * and the property is not multi-valued.
611    * @throws RepositoryException if another error occurs.
612    */

613   public Property setProperty(String JavaDoc name, Value[] values, int type)
614       throws ValueFormatException, RepositoryException {
615     if (name == null)
616       throw new IllegalArgumentException JavaDoc("Property name can not be null");
617
618     if(values.length > 1){
619       PropertyDef propertyDef = ((NodeTypeImpl)getPrimaryNodeType()).getPropertyDef(name);
620       if(propertyDef != null && !propertyDef.isMultiple())
621         throw new ValueFormatException("Can not add multiple value to this property");
622     }
623
624     values = PropertyConvertor.convert(values, type);
625
626     ItemLocation loc = new ItemLocation(location.getPath(), name);
627
628     if (values.length == 1) {
629       if (!getPrimaryNodeType().canSetProperty(name, values[0]))
630         ticket.getNodesManager().addValidationError(loc.getParentPath(),
631             new ConstraintViolationException("Can't set prop " + name));
632     }
633
634     PropertyDef[] propertyDef = getPrimaryNodeType().getPropertyDefs();
635     int residualNb = 0;
636     Collection JavaDoc residuals = new ArrayList JavaDoc();
637     for (int i = 0; i < propertyDef.length; i++) {
638       PropertyDef def = propertyDef[i];
639       if (def != null) {
640         if (def.getName() == null) {
641           //residual property definition
642
residuals.add(def);
643           residualNb++;
644         } else if (name.equals(def.getName())) {
645           int requiredType = def.getRequiredType();
646           if (requiredType != type && requiredType != PropertyType.UNDEFINED) {
647             return updateProperty(name, requiredType, PropertyConvertor.convert(values, requiredType));
648           } else {
649             return updateProperty(name, type, values);
650           }
651         }
652       }
653     }
654     if (residualNb == 1) {
655       int requiredType = ((PropertyDef) residuals.iterator().next()).getRequiredType();
656       if (requiredType != type && requiredType != PropertyType.UNDEFINED) {
657         return updateProperty(name, requiredType, PropertyConvertor.convert(values, requiredType));
658       } else {
659         return updateProperty(name, type, values);
660       }
661     } else if (residualNb > 1) {
662       for (Iterator JavaDoc iterator = residuals.iterator(); iterator.hasNext();) {
663         PropertyDef def = (PropertyDef) iterator.next();
664         int requiredType = def.getRequiredType();
665         if (requiredType == type) {
666           return updateProperty(name, type, values);
667         }
668       }
669       throw new RepositoryException("Can not resolve property type due to multi residuals");
670     }
671     PropertyImpl prop = updateProperty(name, type, values);
672     return prop;
673   }
674
675   /**
676    * 6.3.2
677    */

678   public Property setProperty(String JavaDoc name, String JavaDoc value, int type)
679       throws ValueFormatException, RepositoryException {
680     return setProperty(name, new StringValue(value), type);
681   }
682
683   /**
684    * Sets the specified property to the specified array of values (assuming it
685    * is a multi-value property) and the specified type. Same as
686    * <code>{@link #setProperty(String name, String[] value, int type)}<code>
687    * except that an array of <code>String</code> objects is assigned instead
688    * of a single <code>String</code>. If an array of more than one element
689    * is passed and the property is not multi-valued then a
690    * <code>ValueFormatException</code> is thrown.
691    *
692    * @param name the name of the property to be set.
693    * @param values an array of <code>String</code> objects.
694    * @param type a <code>PropertyType</code> constant.
695    * @return the updated <code>Property</code> object.
696    * @throws ValueFormatException if the type or format of <code>values</code>
697    * is incompatible with the type of the specified property or if
698    * <code>values</code> is incompatible with (i.e. can not be converted to)
699    * <code>type</code> or if an array of more than one value is being passed
700    * and the property is not multi-valued.
701    * @throws RepositoryException if another error occurs.
702    */

703   public Property setProperty(String JavaDoc name, String JavaDoc[] values, int type)
704       throws ValueFormatException, RepositoryException {
705     Value[] convertedValues = new Value[values.length];
706     for (int i = 0; i < values.length; i++) {
707       String JavaDoc value = values[i];
708       convertedValues[i] = new StringValue(value);
709     }
710     return setProperty(name, convertedValues, type);
711   }
712
713   /**
714    * 6.3.2
715    */

716   public Property setProperty(String JavaDoc name, String JavaDoc value) throws ValueFormatException, RepositoryException {
717     return setProperty(name, new StringValue(value), PropertyType.STRING);
718   }
719
720   /**
721    * 6.3.2
722    */

723   public Property setProperty(String JavaDoc name, InputStream JavaDoc value) throws ValueFormatException, RepositoryException {
724     return setProperty(name, new BinaryValue(value), PropertyType.BINARY);
725   }
726
727   /**
728    * 6.3.2
729    */

730   public Property setProperty(String JavaDoc name, boolean value) throws ValueFormatException, RepositoryException {
731     return setProperty(name, new BooleanValue(value), PropertyType.BOOLEAN);
732   }
733
734   /**
735    * 6.3.2
736    */

737   public Property setProperty(String JavaDoc name, Calendar JavaDoc value) throws ValueFormatException, RepositoryException {
738     return setProperty(name, new DateValue(value), PropertyType.DATE);
739   }
740
741   /**
742    * 6.3.2
743    */

744   public Property setProperty(String JavaDoc name, double value) throws ValueFormatException, RepositoryException {
745     return setProperty(name, new DoubleValue(value), PropertyType.DOUBLE);
746   }
747
748   /**
749    * 6.3.2
750    */

751   public Property setProperty(String JavaDoc name, long value) throws ValueFormatException, RepositoryException {
752     return setProperty(name, new LongValue(value), PropertyType.LONG);
753   }
754
755   /**
756    * 6.3.3
757    * Removes (that is, actually erases) the item at path and its
758    * subtree. As with addNode, setProperty and so forth, the change
759    * (in this case the removal of the item) is only persisted to the
760    * workspace when save is called on the parent (or higher order
761    * ancestor) of the removed item.
762    * In level 2: Removes the reference along path to the item. If this
763    * item is a node that has other references to it (due to hard links),
764    * then all of these other references to the node are preserved. If
765    * the item at path has no other references to it then it is actually
766    * erased, along with any child items that also have no other
767    * references and so on down the subtree. As in level 1, save is
768    * required to persist and change to the workspace
769    * then all of these other references to the node are preserved. If
770    * the item at path has no other references to it then it is actually
771    * erased, along with any child items that also have no other
772    * references and so on down the subtree. As in level 1, save is
773    * required to persist and change to the workspace
774    */

775   public void remove(String JavaDoc path) throws PathNotFoundException, RepositoryException {
776     // TODO REL PATH only!
777
log.debug("Node.remove(" + path + ")") ;
778     ItemLocation loc = new ItemLocation(location.getPath(), path);
779     String JavaDoc name = loc.getName();
780     NodeImpl parent = (NodeImpl) getNode(loc.getParentPath());
781
782     if (parent == null)
783       throw new PathNotFoundException("Remove failed: parent node for <" + path + "> not found!");
784
785     if (!parent.getPrimaryNodeType().checkRemoveItem(name))
786       ticket.getNodesManager().addValidationError(loc.getPath(),
787           new ConstraintViolationException("Can't remove item " + name));
788
789     ItemIterator items = parent.getItems(new NamePatternFilter(name));
790     if (!items.hasNext()) {
791       log.debug("Can not find item : " + name + " with parent : " + parent.getPath());
792       return;
793     }
794
795     ItemImpl item = (ItemImpl)items.nextItem();
796     item.setTicket(ticket);
797     if(item.isNode())
798       ticket.getNodesManager().delete((NodeImpl)item);
799     else {
800       parent.removePermanentProperty(name);
801       ticket.getNodesManager().update(parent);
802     }
803
804 // ticket.getNodesManager().delete(item);
805

806   }
807
808
809   /**
810    * 6.3.4
811    * Persists all changes to this node and its descendants made
812    * since the last save or get of this node. The scope of a save
813    * is the subtree rooted at this node.
814    * Constraints mandated by node types are validated on
815    * save. If validation fails all changes made since the last
816    * save or get are rolled-back and a
817    * ConstraintViolationException is thrown.
818    * The save method allows a node to be in a temporarily
819    * invalid state while it is being "built", that is, while it is
820    * having properties and child nodes added to it. Once it is
821    * "finished", save is called and the structure is validated.
822    * An AccessDeniedException will be thrown if an attempt is
823    * made to save changes for which the current user does not
824    * have sufficient access rights.
825    * A StaleItemException will be thrown if an attempt is
826    * made to save a change to a node or property that has
827    * been changed (and saved) by another user, in the
828    * intervening period since this user acquired it.
829    * intervening period since this user acquired it.
830    */

831   public void save(boolean shallow) throws AccessDeniedException, ConstraintViolationException,
832       ActionVetoedException, RepositoryException {
833     if(!ticket.getNodesManager().hasPersistedParent(this))
834       throw new ConstraintViolationException("parent node does not exist or is not persisted yet");
835     ticket.getNodesManager().validate(this, shallow);
836     ticket.getNodesManager().commit(this, shallow);
837   }
838
839
840   /**
841    * Returns the primary node type of this node.
842    *
843    * @return a <code>NodeType</code> object.
844    */

845   public NodeType getPrimaryNodeType() {
846 // return getNodeType();
847
PropertyImpl nodeTypeProp = (PropertyImpl) getPermanentProperty ("jcr:primaryType");
848     NodeType type;
849     if (nodeTypeProp != null) {
850       try {
851         type = NodeTypeManagerImpl.getInstance().getNodeType(nodeTypeProp.getString());
852       } catch (NoSuchNodeTypeException e) {
853         throw new RuntimeException JavaDoc("NodeImpl.getNodeType() error: NodeType <" + nodeTypeProp.toString() +
854             "> not found in NodeTypeManager!", e);
855       }
856       return type;
857     } else {
858       log.warn("getNodeType() jcr:primaryType not found for " + getPath() + " nt:default is applied.");
859       return new org.exoplatform.services.jcr.impl.core.nodetype.nt.Default();
860     }
861   }
862
863   /**
864    * Returns an array of NodeType objects representing the mixin node types
865    * assigned to this node.
866    *
867    * @return a <code>NodeType</code> object.
868    */

869   public NodeType[] getMixinNodeTypes() {
870
871     if(getPermanentProperty("jcr:mixinType") == null)
872         return new NodeType[0];
873
874     try {
875        Value[] mixinNodeTypeNames = getPermanentProperty("jcr:mixinType").getValues();
876        NodeType[] mixinTypes = new NodeType[mixinNodeTypeNames.length];
877        for(int i=0; i<mixinNodeTypeNames.length; i++) {
878            mixinTypes[i] = NodeTypeManagerImpl.getInstance().getNodeType(mixinNodeTypeNames[i].getString());
879
880        }
881        return mixinTypes;
882     } catch (Exception JavaDoc e) {
883        e.printStackTrace();
884        log.error("NoSuchNodeTypeException (getMixinNodeTypes) "+e);
885        return null;
886     }
887
888 // return getDefinition().getDefaultMixinTypes();
889
}
890
891   /**
892    * 6.10.3
893    * Returns true if this node is of the specified node type or a
894    * subtype thereof. Returns false otherwise.
895    */

896   public boolean isNodeType(String JavaDoc nodeTypeName) throws RepositoryException {
897     NodeType testNodeType;
898     try {
899         testNodeType = NodeTypeManagerImpl.getInstance().getNodeType(nodeTypeName);
900      } catch (NoSuchNodeTypeException e) {
901         log.error("Node.isNodeType() No such node: "+nodeTypeName);
902         return false;
903 // throw new RepositoryException("No such node: "+nodeTypeName);
904
}
905
906     if(NodeTypeImpl.isSameOrSubType(testNodeType, getPrimaryNodeType()))
907         return true;
908     else {
909         for(int i=0; i<getMixinNodeTypes().length; i++) {
910            if(NodeTypeImpl.isSameOrSubType(testNodeType, getMixinNodeTypes()[i]))
911              return true;
912         }
913     }
914     return false;
915     // TODO MIXIN type
916
// return getPrimaryNodeType().getName().equals(nodeTypeName);
917
}
918
919   /**
920    * Adds the specified mixin node type to this node. If a conflict with
921    * another assigned mixin or the main node type results, then an exception
922    * is thrown on save. Adding a mixin node type to a node immediately adds
923    * the name of that type to the list held in that node�s
924    * <code>jcr:mixinTypes</code> property.
925    *
926    * @param mixinName
927    */

928   public void addMixin(String JavaDoc mixinName) {
929
930     NodeType type = null;
931     try {
932       type = NodeTypeManagerImpl.getInstance().getNodeType(mixinName);
933       log.debug("Node.addMixin "+type);
934     } catch (NoSuchNodeTypeException e) {
935 // e.printStackTrace();
936
log.error("Node.addMixin failed: "+e);
937     }
938
939     if(type == null || !type.isMixin()) {
940       ticket.getNodesManager().addValidationError(getPath(), new ConstraintViolationException("Node Type " + mixinName +
941           " not found or not mixin type."));
942       return;
943     }
944
945      Value[] values;
946      Property mixinProperty = getPermanentProperty("jcr:mixinType");
947      try {
948         if(mixinProperty == null) {
949           values = new Value[1];
950           values[0] = new StringValue(mixinName);
951         } else {
952           // if(validate mixin for conflict
953
if(hasSameOrSubtypeMixin((NodeTypeImpl)type)) {
954             log.warn("The node has already exist mixin type "+ mixinName + " or its subtype. Adding of mixin ignored.");
955             return;
956           }
957           values = new Value[mixinProperty.getValues().length+1];
958           for(int i=0; i<values.length - 1; i++)
959              values[i] = mixinProperty.getValues()[i];
960           values[values.length - 1] = new StringValue(mixinName);
961        }
962        addPermanentProperty(new PropertyImpl(getPath()+"/"+"jcr:mixinType", values, PropertyType.STRING));
963        log.debug("Node.addMixin "+mixinName+ " added");
964        addAutoCreatedItems(type);
965     } catch (Exception JavaDoc e) {
966        ticket.getNodesManager().addValidationError(getPath(), new ConstraintViolationException("Unexpected exception while adding mixin type " + mixinName +
967           " Reason: "+e));
968     }
969 // throw new RuntimeException("Mixin node type is not supported yet!");
970
}
971   
972   private boolean hasSameOrSubtypeMixin(NodeTypeImpl type) throws RepositoryException, ValueFormatException {
973
974     Value[] mixinValues = getPermanentProperty("jcr:mixinType").getValues();
975     for(int i=0; i<mixinValues.length; i++) {
976         NodeType testNodeType;
977         try {
978            testNodeType = NodeTypeManagerImpl.getInstance().getNodeType(mixinValues[i].getString());
979         } catch (NoSuchNodeTypeException e) {
980           throw new RepositoryException("No such node: "+mixinValues[i].getString());
981         }
982
983         if(NodeTypeImpl.isSameOrSubType(type, testNodeType))
984           return true;
985     }
986     return false;
987
988   }
989
990   /**
991    * 6.10.14
992    * Returns the definition of this Node. This method is
993    * actually a shortcut to searching through this node's
994    * parent's node type (and its supertypes) for the child node
995    * definition applicable to this node.
996    */

997   public NodeDef getDefinition() {
998     NodeTypeImpl parentNodeType;
999     try {
1000      Node parent = getParent();
1001      if (parent != null)
1002        parentNodeType = (NodeTypeImpl) parent.getPrimaryNodeType();
1003      else
1004        throw new RuntimeException JavaDoc("Parent is NULL!");
1005    } catch (Exception JavaDoc e) {
1006      throw new RuntimeException JavaDoc(e.getMessage());
1007    }
1008    NodeDef def = parentNodeType.getChildNodeDef(getName());
1009
1010    if (def == null)
1011      def = new NodeDefImpl();
1012    return def;
1013  }
1014
1015  /**
1016   * 6.3.4
1017   * Analogous to save except that while save acts by
1018   * persisting changes from the transient layer to the
1019   * workspace, checkin acts by persisting changes from the
1020   * workspace layer to the repository layer.
1021   * The scope of the checkin is the subtree rooted at this
1022   * node. If this node or its subtree has changed without
1023   * those changes yet being saved to the workspace, then the
1024   * checkin automatically does the save, persisting the
1025   * changes first to the workspace and then on to the
1026   * repository.
1027   * In level 2: checkin causes a new version of this node to
1028   * be created in the repository layer.
1029   */

1030  public Version checkin() throws UnsupportedRepositoryOperationException, RepositoryException {
1031// log.debug("Checkin called for "+this);
1032
// ((WorkspaceImpl)getWorkspace()).updateNode(this);
1033
// ((WorkspaceImpl)getWorkspace()).doUpdateNode(this);
1034

1035    throw new UnsupportedRepositoryOperationException("Node.checkin() is not supported yet!");
1036
1037// workspace.doUpdateNode(this);
1038
}
1039
1040  public void checkout() throws RepositoryException, UnsupportedRepositoryOperationException {
1041    throw new UnsupportedRepositoryOperationException("Node.checkout() is not supported by Level 1 of JCR.");
1042  }
1043
1044  /**
1045   * Updates this node to reflect the state (i.e., have the same properties
1046   * and child nodes) of this node's corresponding node in srcWorkspace (that
1047   * is, the node in srcWorkspace with the same UUID as this node). If
1048   * shallow is set to false, then only this node is updated. If shallow is
1049   * set to true then every node with a UUID in the subtree rooted at this
1050   * node is updated. If the current ticket does not have sufficient rights
1051   * to perform the update or the specified workspace does not exist, a
1052   * <code>NoSuchWorkspaceException</code> is thrown. If another error occurs
1053   * then a RepositoryException is thrown. If the update succeeds the changes
1054   * made to this node are persisted immediately, there is no need to call
1055   * save.
1056   *
1057   * @param srcWorkspaceName the name of the source workspace.
1058   * @param shallow a boolean
1059   * @throws RepositoryException If another error occurs.
1060   */

1061  public void update(String JavaDoc srcWorkspaceName, boolean shallow) throws NoSuchWorkspaceException, RepositoryException {
1062  }
1063
1064  /**
1065   * Performs the same function as update (above) with one restriction:
1066   * merge only succeeds if the base version of the corresponding node in
1067   * <code>srcWorkspace</code> is a successor (or a successor of a successor, etc., to
1068   * any degree of separation) of the base version of this node. Otherwise,
1069   * the operation throws a <code>MergeException</code>. In repositories that
1070   * do not support versioning, <code>merge</code> throws an
1071   * <code>UnsupportedRepositoryOperationException</code>. If the current
1072   * ticket does not have sufficient rights to perform the <code>merge</code> or the
1073   * specified workspace does not exist, a <code>NoSuchWorkspaceException</code> is thrown.
1074   * If the <code>merge</code> succeeds, the changes made to this node are
1075   * persisted immediately, there is no need to call <code>save</code>.
1076   *
1077   * @param srcWorkspace the name of the source workspace.
1078   * @param shallow a boolean
1079   * @throws UnsupportedRepositoryOperationException
1080   * if versioning is not supported.
1081   * @throws MergeException succeeds if the base version of the corresponding
1082   * node in srcWorkspace is not a successor of the base version of this node.
1083   * @throws NoSuchWorkspaceException If the current
1084   * ticket does not have sufficient rights to perform the <code>merge</code> or the
1085   * specified workspace does not exist.
1086   * @throws RepositoryException If another error occurs.
1087   */

1088  public void merge(String JavaDoc srcWorkspace, boolean shallow) throws UnsupportedRepositoryOperationException, NoSuchWorkspaceException, MergeException, RepositoryException {
1089    throw new UnsupportedRepositoryOperationException("Node.merge() is not supported by Level 1 of JCR.");
1090  }
1091
1092  /**
1093   * Returns <code>true</code> if this node is currently checked-out and <code>false</code> otherwise.
1094   *
1095   * @return a boolean
1096   * @throws UnsupportedRepositoryOperationException
1097   * if versioning is not supported.
1098   * @throws RepositoryException If another error occurs.
1099   */

1100  public boolean isCheckedOut() throws UnsupportedRepositoryOperationException, RepositoryException {
1101    throw new UnsupportedRepositoryOperationException("Node.isCheckedOut() is not supported by Level 1 of JCR.");
1102  }
1103
1104  /**
1105   * Restores this node to the state recorded in the specified version.
1106   *
1107   * @param versionName
1108   * @throws UnsupportedRepositoryOperationException
1109   * if versioning is not supported.
1110   * @throws RepositoryException If another error occurs.
1111   */

1112  public void restore(String JavaDoc versionName) throws UnsupportedRepositoryOperationException, RepositoryException {
1113    throw new UnsupportedRepositoryOperationException("Node.restore() is not supported by Level 1 of JCR.");
1114  }
1115
1116  /**
1117   * Restores this node to the state recorded in the specified version.
1118   *
1119   * @param version a <code>Version</code> object
1120   * @throws UnsupportedRepositoryOperationException
1121   * if versioning is not supported.
1122   * @throws RepositoryException If another error occurs.
1123   */

1124  public void restore(Version version) throws UnsupportedRepositoryOperationException, RepositoryException {
1125    throw new UnsupportedRepositoryOperationException("Node.restore() is not supported by Level 1 of JCR.");
1126  }
1127
1128  /**
1129   * Restores this node to the state recorded in the version corresponding to the specified date.
1130   *
1131   * @param date a <codeCalendar</code> object
1132   * @throws UnsupportedRepositoryOperationException
1133   * if versioning is not supported.
1134   * @throws RepositoryException If another error occurs.
1135   */

1136  public void restore(Calendar JavaDoc date) throws UnsupportedRepositoryOperationException, RepositoryException {
1137    throw new UnsupportedRepositoryOperationException("Node.restore() is not supported by Level 1 of JCR.");
1138  }
1139
1140  /**
1141   * Restores this node to the state recorded in the version specified by
1142   * <code>versionLabel</code>.
1143   *
1144   * @param versionLabel a String
1145   * @throws UnsupportedRepositoryOperationException
1146   * if versioning is not supported.
1147   * @throws RepositoryException If another error occurs.
1148   */

1149  public void restoreByLabel(String JavaDoc versionLabel) throws UnsupportedRepositoryOperationException, RepositoryException {
1150    throw new UnsupportedRepositoryOperationException("Node.restore() is not supported by Level 1 of JCR.");
1151  }
1152
1153  /**
1154   * Returns the <code>VersionHistory</code> object of this node. This object
1155   * is simply a wrapper for the <code>nt:versionHistory</code> node holding
1156   * this node's versions.
1157   *
1158   * @return a <code>VersionHistory</code> object
1159   * @throws UnsupportedRepositoryOperationException
1160   * if versioning is not supported.
1161   * @throws RepositoryException If another error occurs.
1162   */

1163  public VersionHistory getVersionHistory() throws UnsupportedRepositoryOperationException, RepositoryException {
1164    throw new UnsupportedRepositoryOperationException("Node.getVersionHistory() is not supported by Level 1 of JCR.");
1165  }
1166
1167  /**
1168   * Returns the current base version of this versionable node.
1169   *
1170   * @return a <code>Version</code> object.
1171   * @throws UnsupportedRepositoryOperationException
1172   * if versioning is not supported.
1173   * @throws RepositoryException If another error occurs.
1174   */

1175  public Version getBaseVersion() throws UnsupportedRepositoryOperationException, RepositoryException {
1176    throw new UnsupportedRepositoryOperationException("Node.getBaseVersion() is not supported by Level 1 of JCR.");
1177  }
1178
1179 //////////////////// Item implementation ////////////////////
1180
public void accept(ItemVisitor visitor) throws RepositoryException {
1181    visitor.visit(this);
1182  }
1183
1184  public boolean isNode() {
1185    return true;
1186  }
1187
1188  protected boolean isItemIdentical(Item otherItem) {
1189    try {
1190      NodeIterator nodes = getNodes();
1191      Node otherNode = (Node) otherItem;
1192      while (nodes.hasNext()) {
1193        if (otherNode.hasNode(nodes.nextNode().getName()))
1194          return false;
1195        }
1196      PropertyIterator props = this.getProperties();
1197        while (props.hasNext()) {
1198          Property prop1 = props.nextProperty();
1199          Property prop2 = null;
1200          try {
1201            prop2 = otherNode.getProperty(prop1.getName());
1202          } catch (PathNotFoundException e) {
1203            return false;
1204          }
1205          if (!prop1.isIdentical(prop2))
1206            return false;
1207        }
1208        return true;
1209    } catch (Exception JavaDoc e) {
1210        log.error("Node.isIdentical() failed. Reason: "+e);
1211        return false;
1212// throw new RuntimeException(e.getMessage(), e);
1213
}
1214
1215  }
1216
1217  public StringIterator getPaths() {
1218    ArrayList JavaDoc list = new ArrayList JavaDoc();
1219    Property uuid = getPermanentProperty("jcr:uuid");
1220    if( uuid != null)
1221       list.addAll(ticket.getNodesManager().getPaths(uuid.getString()));
1222    else
1223        list.add(getPath());
1224    EntityCollection paths = new EntityCollection(list);
1225    return paths;
1226  }
1227
1228
1229  // NodeData implementation //////////////////////////
1230
public void addPermanentProperty(Property property) {
1231    removePermanentProperty(property.getName());
1232    properties.add(property);
1233    log.debug("Add permanent property "+property+" hash="+property.hashCode() );
1234  }
1235
1236  public void removePermanentProperty(String JavaDoc name) {
1237    for (int i = 0; i < properties.size(); i++) {
1238      PropertyImpl prop = (PropertyImpl) properties.get(i);
1239      if (prop.getName().equals(name)) {
1240        properties.remove(i);
1241        log.debug("Remove permanent property "+prop+" hash="+prop.hashCode() );
1242      }
1243    }
1244  }
1245
1246  public Property getPermanentProperty(String JavaDoc name) {
1247    for (int i = 0; i < properties.size(); i++) {
1248      PropertyImpl prop = (PropertyImpl) properties.get(i);
1249      if (prop.getName().equals(name))
1250        return prop;
1251    }
1252    return null;
1253  }
1254
1255  public List JavaDoc getPermanentProperties() {
1256    return properties;
1257  }
1258
1259  public void refresh(Node withNode) throws PathNotFoundException, RepositoryException {
1260    properties = new ArrayList JavaDoc();
1261    List JavaDoc props = ((NodeImpl)withNode).getPermanentProperties();
1262    for(int i=0; i<props.size(); i++)
1263       this.properties.add( new PropertyImpl((PropertyImpl)props.get(i)) );
1264  }
1265
1266
1267  ///////////////////////////////////////////////
1268

1269  public String JavaDoc toString() {
1270    int psize = (properties == null) ? 0 : properties.size();
1271    NodeType ntype = getPrimaryNodeType();
1272    return "Node:( path=" + getPath() +
1273        " type=" + ntype +
1274        " properties=" + psize
1275        + ")";
1276  }
1277
1278  PropertyImpl updateProperty(String JavaDoc name, int type, Value[] values) throws ConstraintViolationException {
1279
1280    PropertyImpl property = null;
1281    try {
1282      ItemLocation loc = new ItemLocation(getPath(), name);
1283      property = new PropertyImpl(loc.getPath(), values, type);
1284      log.debug("NodeImpl.updateProperty-- "+this.getPermanentProperty(name)+" with "+property+" type = "+type);
1285      property.setTicket(ticket);
1286
1287// removePermanentProperty(property.getName());
1288
addPermanentProperty(property);
1289      ticket.getNodesManager().update(this);
1290
1291// ticket.getNodesManager().updateProperty(this, property);
1292
} catch (Exception JavaDoc e) {
1293      e.printStackTrace();
1294      throw new ConstraintViolationException("Node.updateProperty() failed for '" + name, e);
1295    }
1296    log.debug("createProperty (" + property + ") " + getPath());
1297    return property;
1298  }
1299
1300  protected NodeImpl createNode(NodeImpl parent, String JavaDoc name, NodeType type) throws ConstraintViolationException {
1301    NodeImpl node = null;
1302    try {
1303      String JavaDoc nodePath = new ItemLocation(parent.getPath(), name).getPath();
1304      node = new NodeImpl(nodePath, type.getName());
1305    } catch (PathNotFoundException e) {
1306      throw new ConstraintViolationException("Node.createNode() failed Reason: " + e);
1307    }
1308    node.setTicket(ticket);
1309    log.debug("Create node " + node.getPath() + " type =" + type + " manager " + ticket);
1310    ticket.getNodesManager().add(node);
1311    node.addAutoCreatedItems(type);
1312    return node;
1313  }
1314
1315  private EntityCollection retrieveChildNodes(ItemFilter filter) {
1316    ArrayList JavaDoc list = new ArrayList JavaDoc();
1317    Iterator JavaDoc children = ticket.getNodesManager().getChildren(getPath()).iterator();
1318    while(children.hasNext()) {
1319      String JavaDoc path = (String JavaDoc) children.next();
1320      NodeImpl item = (NodeImpl) ticket.getNodesManager().getNodeByPath(path);
1321      item.setTicket(ticket);
1322      log.debug("Retrieve item : " + item.getPath());
1323      if (filter.accept(item))
1324        list.add(item);
1325    }
1326    return new EntityCollection(list);
1327  }
1328
1329  private EntityCollection retrieveProperties(ItemFilter filter) {
1330
1331    log.debug("retrieve properties of node : " + this.getPath());
1332    ArrayList JavaDoc list = new ArrayList JavaDoc();
1333// List properties = ticket.getNodesManager().getProperties(getPath());
1334

1335    for (int i = 0; i < properties.size(); i++) {
1336      Property prop = (Property) properties.get(i);
1337      if (prop != null && filter.accept(prop)) {
1338        log.debug("retrieve filtered property " + prop.getName());
1339        list.add(prop);
1340      }
1341    }
1342    return new EntityCollection(list);
1343  }
1344
1345  void addAutoCreatedItems(NodeType type) throws ConstraintViolationException {
1346    NodeDef[] nodeDefs = type.getChildNodeDefs();
1347    PropertyDef[] propDefs = type.getPropertyDefs();
1348
1349    for (int i = 0; i < propDefs.length; i++) {
1350      if (propDefs[i].isAutoCreate()) {
1351        // Real Property value OR propDefs[i].getDefaultValue() is not found
1352
Value propVal = propDefs[i].getDefaultValue();
1353        String JavaDoc name = propDefs[i].getName();
1354
1355        // SYSTEM PROP jcr:primaryType
1356
if (name.equals("jcr:primaryType"))
1357          propVal = new StringValue(type.getName());
1358
1359
1360        Value[] array = { propVal };
1361        log.debug("AutoCreate Property-> for " + this.getName() + " property name " + propDefs[i].getName() + " " +
1362            propDefs[i].getRequiredType()+ " "+ getPermanentProperty ("jcr:primaryType"));
1363        PropertyImpl prop = updateProperty(name, propDefs[i].getRequiredType(), array);
1364      }
1365    }
1366
1367    if (nodeDefs == null)
1368      return;
1369    for (int i = 0; i < nodeDefs.length; i++) {
1370      log.debug("AutoCreate nodedef ->" + nodeDefs[i].getName());
1371      if (nodeDefs[i].isAutoCreate()) {
1372        createNode(this, nodeDefs[i].getName(), nodeDefs[i].getDefaultPrimaryType());
1373        log.debug("AutoCreate Node->" + this + " " + nodeDefs[i].getName());
1374      }
1375    }
1376  }
1377
1378  private ItemImpl findPrimaryItem(NodeImpl parent)
1379      throws ItemNotFoundException, AccessDeniedException, RepositoryException {
1380    PropertyIterator properties = parent.getProperties();
1381    // HAS Primary property
1382
while (properties.hasNext()) {
1383      PropertyImpl prop = (PropertyImpl) properties.nextProperty();
1384      prop.setTicket(ticket);
1385      if (prop.getDefinition() != null && prop.getDefinition().isPrimaryItem()) {
1386        return (PropertyImpl) prop;
1387      }
1388    }
1389
1390    //... OR HAS Primary Node
1391
NodeIterator nodes = parent.getNodes();
1392
1393    NodeImpl primaryItem = null;
1394    while (nodes.hasNext()) {
1395      NodeImpl node = (NodeImpl) nodes.nextNode();
1396      node.setTicket(ticket);
1397      if (node.getDefinition() != null && node.getDefinition().isPrimaryItem()) {
1398        // Traverse if primary
1399
return findPrimaryItem((NodeImpl) node);
1400      }
1401    }
1402    return parent;
1403  }
1404
1405}
1406
Popular Tags