1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.modeshape.jcr;
25
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.LinkedList;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Set;
36 import javax.jcr.PropertyType;
37 import javax.jcr.Value;
38 import javax.jcr.nodetype.NodeType;
39 import javax.jcr.nodetype.NodeTypeIterator;
40 import javax.jcr.nodetype.PropertyDefinition;
41 import net.jcip.annotations.ThreadSafe;
42 import org.modeshape.common.util.CheckArg;
43 import org.modeshape.graph.ExecutionContext;
44 import org.modeshape.graph.property.Name;
45 import org.modeshape.graph.property.basic.BasicName;
46
47
48
49
50 @ThreadSafe
51 class JcrNodeType implements NodeType {
52
53 public static final String RESIDUAL_ITEM_NAME = "*";
54 public static final Name RESIDUAL_NAME = new BasicName("", RESIDUAL_ITEM_NAME);
55
56
57 private final Name name;
58
59 private final Name primaryItemName;
60
61
62 private final List<JcrNodeType> declaredSupertypes;
63
64
65
66
67
68 private final List<JcrNodeType> allSupertypes;
69
70
71
72
73
74 private final List<JcrNodeType> thisAndAllSupertypes;
75 private final Set<Name> thisAndAllSupertypesNames;
76
77
78 private final boolean mixin;
79
80 private final boolean orderableChildNodes;
81
82
83 private final boolean isAbstract;
84
85
86 private final boolean queryable;
87
88
89
90
91 private final List<JcrNodeDefinition> childNodeDefinitions;
92
93
94
95
96 private final List<JcrPropertyDefinition> propertyDefinitions;
97
98
99
100
101
102
103 private final DefinitionCache allDefinitions;
104
105
106
107
108
109 private ExecutionContext context;
110
111
112 private RepositoryNodeTypeManager nodeTypeManager;
113
114 JcrNodeType( ExecutionContext context,
115 RepositoryNodeTypeManager nodeTypeManager,
116 Name name,
117 List<JcrNodeType> declaredSupertypes,
118 Name primaryItemName,
119 Collection<JcrNodeDefinition> childNodeDefinitions,
120 Collection<JcrPropertyDefinition> propertyDefinitions,
121 boolean mixin,
122 boolean isAbstract,
123 boolean queryable,
124 boolean orderableChildNodes ) {
125 assert context != null;
126
127 this.context = context;
128 this.nodeTypeManager = nodeTypeManager;
129 this.name = name;
130 this.primaryItemName = primaryItemName;
131 this.declaredSupertypes = declaredSupertypes != null ? declaredSupertypes : Collections.<JcrNodeType>emptyList();
132 this.mixin = mixin;
133 this.queryable = queryable;
134 this.isAbstract = isAbstract;
135 this.orderableChildNodes = orderableChildNodes;
136 this.propertyDefinitions = new ArrayList<JcrPropertyDefinition>(propertyDefinitions.size());
137 for (JcrPropertyDefinition property : propertyDefinitions) {
138 this.propertyDefinitions.add(property.with(this));
139 }
140
141 this.childNodeDefinitions = new ArrayList<JcrNodeDefinition>(childNodeDefinitions.size());
142 for (JcrNodeDefinition childNode : childNodeDefinitions) {
143 this.childNodeDefinitions.add(childNode.with(this));
144 }
145
146
147 List<JcrNodeType> thisAndAllSupertypes = new LinkedList<JcrNodeType>();
148 Set<Name> typeNames = new HashSet<Name>();
149 thisAndAllSupertypes.add(this);
150 typeNames.add(this.name);
151 for (int i = 0; i != thisAndAllSupertypes.size(); ++i) {
152 JcrNodeType superType = thisAndAllSupertypes.get(i);
153 for (NodeType superSuperType : superType.getDeclaredSupertypes()) {
154 JcrNodeType jcrSuperSuperType = (JcrNodeType)superSuperType;
155
156 if (jcrSuperSuperType == null) {
157 assert JcrNtLexicon.BASE.equals(name);
158 continue;
159 }
160
161 if (typeNames.add(jcrSuperSuperType.getInternalName())) {
162 thisAndAllSupertypes.add(jcrSuperSuperType);
163 }
164 }
165 }
166 this.thisAndAllSupertypes = Collections.unmodifiableList(thisAndAllSupertypes);
167
168 this.allSupertypes = thisAndAllSupertypes.size() > 1 ? thisAndAllSupertypes.subList(1, thisAndAllSupertypes.size()) : Collections.<JcrNodeType>emptyList();
169
170
171 this.thisAndAllSupertypesNames = Collections.unmodifiableSet(typeNames);
172
173 this.allDefinitions = new DefinitionCache(this);
174 }
175
176 List<JcrNodeType> getTypeAndSupertypes() {
177 return thisAndAllSupertypes;
178 }
179
180 List<JcrNodeType> supertypes() {
181 return allSupertypes;
182 }
183
184
185
186
187
188
189 List<JcrNodeDefinition> childNodeDefinitions() {
190 return childNodeDefinitions;
191 }
192
193
194
195
196
197
198 List<JcrPropertyDefinition> propertyDefinitions() {
199 return propertyDefinitions;
200 }
201
202
203
204
205
206
207 Collection<JcrPropertyDefinition> allPropertyDefinitions() {
208 return allDefinitions.allPropertyDefinitions();
209 }
210
211 Collection<JcrPropertyDefinition> allSingleValuePropertyDefinitions( Name propertyName ) {
212 return allDefinitions.allSingleValuePropertyDefinitions(propertyName);
213 }
214
215 Collection<JcrPropertyDefinition> allMultiValuePropertyDefinitions( Name propertyName ) {
216 return allDefinitions.allMultiValuePropertyDefinitions(propertyName);
217 }
218
219 Collection<JcrPropertyDefinition> allPropertyDefinitions( Name propertyName ) {
220 return allDefinitions.allPropertyDefinitions(propertyName);
221 }
222
223
224
225
226
227
228 Collection<JcrNodeDefinition> allChildNodeDefinitions() {
229 return allDefinitions.allChildNodeDefinitions();
230 }
231
232 Collection<JcrNodeDefinition> allChildNodeDefinitions( Name childName,
233 boolean requireSns ) {
234 return allDefinitions.allChildNodeDefinitions(childName, requireSns);
235 }
236
237 Collection<JcrNodeDefinition> allChildNodeDefinitions( Name childName ) {
238 return allDefinitions.allChildNodeDefinitions(childName);
239 }
240
241 JcrNodeDefinition childNodeDefinition( NodeDefinitionId nodeDefnId ) {
242 List<Name> requiredPrimaryTypeNames = Arrays.asList(nodeDefnId.getRequiredPrimaryTypes());
243 for (JcrNodeDefinition nodeDefn : allChildNodeDefinitions(nodeDefnId.getChildDefinitionName())) {
244 if (nodeDefn.requiredPrimaryTypeNameSet().size() == requiredPrimaryTypeNames.size()
245 && nodeDefn.requiredPrimaryTypeNameSet().containsAll(requiredPrimaryTypeNames)) {
246 return nodeDefn;
247 }
248 }
249 return null;
250 }
251
252
253
254
255
256
257 public boolean canAddChildNode( String childNodeName ) {
258 CheckArg.isNotNull(childNodeName, "childNodeName");
259 Name childName = context.getValueFactories().getNameFactory().create(childNodeName);
260 return nodeTypeManager().findChildNodeDefinition(this.name, null, childName, null, 0, true) != null;
261 }
262
263
264
265
266
267
268 public boolean canAddChildNode( String childNodeName,
269 String primaryNodeTypeName ) {
270
271 CheckArg.isNotNull(childNodeName, "childNodeName");
272 CheckArg.isNotNull(primaryNodeTypeName, "primaryNodeTypeName");
273 Name childName = context.getValueFactories().getNameFactory().create(childNodeName);
274 Name childPrimaryTypeName = context.getValueFactories().getNameFactory().create(primaryNodeTypeName);
275
276 if (primaryNodeTypeName != null) {
277 JcrNodeType childType = this.nodeTypeManager().getNodeType(childPrimaryTypeName);
278 if (childType.isAbstract() || childType.isMixin()) return false;
279 }
280
281 return nodeTypeManager().findChildNodeDefinition(this.name, null, childName, childPrimaryTypeName, 0, true) != null;
282 }
283
284 public boolean canRemoveNode( String itemName ) {
285 CheckArg.isNotNull(itemName, "itemName");
286 Name childName = context.getValueFactories().getNameFactory().create(itemName);
287 return nodeTypeManager().canRemoveAllChildren(this.name, null, childName, true);
288 }
289
290
291
292
293
294
295
296
297
298
299 public boolean canRemoveItem( String itemName ) {
300 CheckArg.isNotNull(itemName, "itemName");
301 Name childName = context.getValueFactories().getNameFactory().create(itemName);
302 return nodeTypeManager().canRemoveItem(this.name, null, childName, true);
303 }
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319 boolean canCastToTypeAndMatchesConstraints( JcrPropertyDefinition propertyDefinition,
320 Value value ) {
321 try {
322 assert value instanceof JcrValue : "Illegal implementation of Value interface";
323 ((JcrValue)value).asType(propertyDefinition.getRequiredType());
324 return propertyDefinition.satisfiesConstraints(value);
325 } catch (javax.jcr.ValueFormatException vfe) {
326
327 return false;
328 }
329 }
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345 boolean canCastToTypeAndMatchesConstraints( JcrPropertyDefinition propertyDefinition,
346 Value[] values ) {
347 for (Value value : values) {
348 if (!canCastToTypeAndMatchesConstraints(propertyDefinition, value)) return false;
349 }
350 return true;
351 }
352
353
354
355
356
357
358 public boolean canSetProperty( String propertyName,
359 Value value ) {
360 CheckArg.isNotNull(propertyName, "propertyName");
361 Name name = context.getValueFactories().getNameFactory().create(propertyName);
362
363
364 return nodeTypeManager().findPropertyDefinition(this.name, null, name, value, false, true) != null;
365 }
366
367
368
369
370
371
372 public boolean canSetProperty( String propertyName,
373 Value[] values ) {
374 CheckArg.isNotNull(propertyName, "propertyName");
375 if (values == null || values.length == 0) {
376 return canRemoveProperty(propertyName);
377 }
378
379 Name name = context.getValueFactories().getNameFactory().create(propertyName);
380
381 return nodeTypeManager().findPropertyDefinition(this.name, null, name, values, true) != null;
382 }
383
384 public boolean canRemoveProperty( String propertyName ) {
385 CheckArg.isNotNull(propertyName, "propertyName");
386 Name name = context.getValueFactories().getNameFactory().create(propertyName);
387
388
389 return nodeTypeManager().canRemoveProperty(this.name, null, name, true);
390 }
391
392
393
394
395
396
397 public JcrNodeDefinition[] getDeclaredChildNodeDefinitions() {
398
399 return childNodeDefinitions.toArray(new JcrNodeDefinition[childNodeDefinitions.size()]);
400 }
401
402
403
404
405
406
407 public JcrNodeDefinition[] getChildNodeDefinitions() {
408
409 Collection<JcrNodeDefinition> definitions = this.allDefinitions.allChildNodeDefinitions();
410 return definitions.toArray(new JcrNodeDefinition[definitions.size()]);
411 }
412
413
414
415
416
417
418 public JcrPropertyDefinition[] getPropertyDefinitions() {
419
420 Collection<JcrPropertyDefinition> definitions = this.allDefinitions.allPropertyDefinitions();
421 return definitions.toArray(new JcrPropertyDefinition[definitions.size()]);
422 }
423
424
425
426
427
428
429 public JcrNodeType[] getDeclaredSupertypes() {
430
431 return declaredSupertypes.toArray(new JcrNodeType[declaredSupertypes.size()]);
432 }
433
434
435
436
437 public String[] getDeclaredSupertypeNames() {
438 List<String> supertypeNames = new ArrayList<String>(declaredSupertypes.size());
439
440 for (JcrNodeType declaredSupertype : declaredSupertypes) {
441 supertypeNames.add(declaredSupertype.getName());
442 }
443
444
445 return supertypeNames.toArray(new String[supertypeNames.size()]);
446 }
447
448 public NodeTypeIterator getSubtypes() {
449 return new JcrNodeTypeIterator(nodeTypeManager.subtypesFor(this));
450 }
451
452 public NodeTypeIterator getDeclaredSubtypes() {
453 return new JcrNodeTypeIterator(nodeTypeManager.declaredSubtypesFor(this));
454 }
455
456
457
458
459
460
461 public String getName() {
462
463 return name.getString(context.getNamespaceRegistry());
464 }
465
466
467
468
469
470
471
472 Name getInternalName() {
473 return name;
474 }
475
476
477
478
479
480
481
482 Name getInternalPrimaryItemName() {
483 return primaryItemName;
484 }
485
486
487
488
489
490
491 public String getPrimaryItemName() {
492 if (primaryItemName == null) {
493 return null;
494 }
495
496
497 return primaryItemName.getString(context.getNamespaceRegistry());
498 }
499
500
501
502
503
504
505 public JcrPropertyDefinition[] getDeclaredPropertyDefinitions() {
506 return propertyDefinitions.toArray(new JcrPropertyDefinition[propertyDefinitions.size()]);
507 }
508
509
510
511
512
513
514 public NodeType[] getSupertypes() {
515 return allSupertypes.toArray(new NodeType[allSupertypes.size()]);
516 }
517
518
519
520
521
522
523 public boolean hasOrderableChildNodes() {
524 return orderableChildNodes;
525 }
526
527
528
529
530
531
532 public boolean isMixin() {
533 return mixin;
534 }
535
536 public boolean isAbstract() {
537 return isAbstract;
538 }
539
540 public boolean isQueryable() {
541 return queryable;
542 }
543
544
545
546
547
548
549 public boolean isNodeType( String nodeTypeName ) {
550 if (nodeTypeName == null) return false;
551 Name name = context.getValueFactories().getNameFactory().create(nodeTypeName);
552 return this.thisAndAllSupertypesNames.contains(name);
553 }
554
555 boolean isNodeType( Name nodeTypeName ) {
556 if (nodeTypeName == null) return false;
557 return this.thisAndAllSupertypesNames.contains(nodeTypeName);
558 }
559
560 boolean isNodeTypeOneOf( Name... nodeTypeNames ) {
561 if (nodeTypeNames == null || nodeTypeNames.length == 0) return false;
562 for (Name nodeTypeName : nodeTypeNames) {
563 if (this.thisAndAllSupertypesNames.contains(nodeTypeName)) return true;
564 }
565 return false;
566 }
567
568
569
570
571
572
573 @Override
574 public int hashCode() {
575 return this.name.hashCode();
576 }
577
578
579
580
581
582
583 @Override
584 public boolean equals( Object obj ) {
585 if (obj == this) return true;
586 if (obj instanceof JcrNodeType) {
587 JcrNodeType that = (JcrNodeType)obj;
588 return this.name.equals(that.name);
589 }
590 if (obj instanceof NodeType) {
591 NodeType that = (NodeType)obj;
592 return this.getName().equals(that.getName());
593 }
594 return false;
595 }
596
597 @Override
598 public String toString() {
599 return getName();
600 }
601
602
603
604
605
606
607
608
609
610 final JcrNodeType with( RepositoryNodeTypeManager nodeTypeManager ) {
611 return new JcrNodeType(this.context, nodeTypeManager, this.name, this.declaredSupertypes, this.primaryItemName,
612 this.childNodeDefinitions, this.propertyDefinitions, this.mixin, this.isAbstract, this.queryable,
613 this.orderableChildNodes);
614 }
615
616
617
618
619
620
621
622
623 final JcrNodeType with( ExecutionContext context ) {
624 return new JcrNodeType(context, this.nodeTypeManager, this.name, this.declaredSupertypes, this.primaryItemName,
625 this.childNodeDefinitions, this.propertyDefinitions, this.mixin, this.isAbstract, this.queryable,
626 this.orderableChildNodes);
627 }
628
629 final RepositoryNodeTypeManager nodeTypeManager() {
630 return nodeTypeManager;
631 }
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652 final boolean conflictsWith( NodeType primaryNodeType,
653 NodeType[] mixinNodeTypes ) {
654 Map<PropertyDefinitionId, JcrPropertyDefinition> props = new HashMap<PropertyDefinitionId, JcrPropertyDefinition>();
655
656
657
658 final Name DEFAULT_NAME = this.name;
659
660 for (JcrPropertyDefinition property : propertyDefinitions()) {
661
662
663
664
665
666 PropertyDefinitionId pid = new PropertyDefinitionId(DEFAULT_NAME, property.name, PropertyType.UNDEFINED,
667 property.isMultiple());
668 props.put(pid, property);
669 }
670
671
672
673
674
675 if (primaryNodeType.getName().equals(getName())) {
676
677 return true;
678 }
679
680 for (JcrPropertyDefinition property : ((JcrNodeType)primaryNodeType).propertyDefinitions()) {
681 PropertyDefinitionId pid = new PropertyDefinitionId(DEFAULT_NAME, property.name, PropertyType.UNDEFINED,
682 property.isMultiple());
683 JcrPropertyDefinition oldProp = props.put(pid, property);
684 if (oldProp != null) {
685 String oldPropTypeName = oldProp.getDeclaringNodeType().getName();
686 String propTypeName = property.getDeclaringNodeType().getName();
687 if (!oldPropTypeName.equals(propTypeName)) {
688
689 return true;
690 }
691 }
692 }
693
694 for (NodeType mixinNodeType : mixinNodeTypes) {
695
696
697
698
699 if (mixinNodeType.getName().equals(getName())) {
700
701 return true;
702 }
703
704 for (JcrPropertyDefinition property : ((JcrNodeType)mixinNodeType).propertyDefinitions()) {
705 PropertyDefinitionId pid = new PropertyDefinitionId(DEFAULT_NAME, property.name, PropertyType.UNDEFINED,
706 property.isMultiple());
707 JcrPropertyDefinition oldProp = props.put(pid, property);
708 if (oldProp != null) {
709 String oldPropTypeName = oldProp.getDeclaringNodeType().getName();
710 String propTypeName = property.getDeclaringNodeType().getName();
711 if (!oldPropTypeName.equals(propTypeName)) {
712
713 return true;
714 }
715 }
716 }
717 }
718
719 Map<NodeDefinitionId, JcrNodeDefinition> childNodes = new HashMap<NodeDefinitionId, JcrNodeDefinition>();
720
721 for (JcrNodeDefinition childNode : childNodeDefinitions()) {
722 NodeDefinitionId nid = new NodeDefinitionId(DEFAULT_NAME, childNode.name, new Name[0]);
723 childNodes.put(nid, childNode);
724 }
725
726 for (JcrNodeDefinition childNode : ((JcrNodeType)primaryNodeType).childNodeDefinitions()) {
727 NodeDefinitionId nid = new NodeDefinitionId(DEFAULT_NAME, childNode.name, new Name[0]);
728 JcrNodeDefinition oldNode = childNodes.put(nid, childNode);
729 if (oldNode != null) {
730 String oldNodeTypeName = oldNode.getDeclaringNodeType().getName();
731 String childNodeTypeName = childNode.getDeclaringNodeType().getName();
732 if (!oldNodeTypeName.equals(childNodeTypeName)) {
733
734 return true;
735 }
736 }
737 }
738
739 for (NodeType mixinNodeType : mixinNodeTypes) {
740 for (JcrNodeDefinition childNode : ((JcrNodeType)mixinNodeType).childNodeDefinitions()) {
741 NodeDefinitionId nid = new NodeDefinitionId(DEFAULT_NAME, childNode.name, new Name[0]);
742 JcrNodeDefinition oldNode = childNodes.put(nid, childNode);
743 if (oldNode != null) {
744 String oldNodeTypeName = oldNode.getDeclaringNodeType().getName();
745 String childNodeTypeName = childNode.getDeclaringNodeType().getName();
746 if (!oldNodeTypeName.equals(childNodeTypeName)) {
747
748 return true;
749 }
750 }
751 }
752 }
753
754 return false;
755 }
756
757 }