001: /*
002: * The contents of this file are subject to the terms of the Common Development
003: * and Distribution License (the License). You may not use this file except in
004: * compliance with the License.
005: *
006: * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
007: * or http://www.netbeans.org/cddl.txt.
008: *
009: * When distributing Covered Code, include this CDDL Header Notice in each file
010: * and include the License file at http://www.netbeans.org/cddl.txt.
011: * If applicable, add the following below the CDDL Header, with the fields
012: * enclosed by brackets [] replaced by your own identifying information:
013: * "Portions Copyrighted [year] [name of copyright owner]"
014: *
015: * The Original Software is NetBeans. The Initial Developer of the Original
016: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
017: * Microsystems, Inc. All Rights Reserved.
018: */
019:
020: package org.netbeans.modules.bpel.model.impl;
021:
022: import java.util.Collection;
023: import java.util.Collections;
024: import java.util.List;
025:
026: import org.netbeans.modules.bpel.model.api.BpelContainer;
027: import org.netbeans.modules.bpel.model.api.BpelEntity;
028: import org.netbeans.modules.bpel.model.api.events.ArrayUpdateEvent;
029: import org.netbeans.modules.bpel.model.api.events.ChangeEvent;
030: import org.netbeans.modules.bpel.model.api.events.EntityInsertEvent;
031: import org.netbeans.modules.bpel.model.api.events.EntityRemoveEvent;
032: import org.netbeans.modules.bpel.model.api.events.EntityUpdateEvent;
033: import org.netbeans.modules.bpel.model.api.events.VetoException;
034: import org.netbeans.modules.bpel.model.xam.BpelElements;
035: import org.netbeans.modules.bpel.model.xam.BpelTypes;
036: import org.netbeans.modules.xml.xam.Component;
037: import org.w3c.dom.Element;
038: import org.w3c.dom.Node;
039: import org.w3c.dom.NodeList;
040:
041: /**
042: * @author ads
043: */
044: public abstract class BpelContainerImpl extends BpelEntityImpl
045: implements BpelContainer {
046:
047: protected BpelContainerImpl(BpelModelImpl model, Element e) {
048: super (model, e);
049: }
050:
051: protected BpelContainerImpl(BpelBuilderImpl builder, String tagName) {
052: super (builder, tagName);
053: }
054:
055: protected BpelContainerImpl(BpelBuilderImpl builder,
056: BpelElements elem) {
057: super (builder, elem);
058: }
059:
060: @Override
061: public boolean canPaste(Component child) {
062: if (!Utils.checkPasteCompensate(this , child)) {
063: return false;
064: }
065: return true;
066: }
067:
068: /**
069: * This method should be implemented by each container . Container should
070: * recognize in <code>element</code> its child and create Bpel element
071: * respectively. Warning! Here exist some possibility for error. Each
072: * container should recognize only those children that could be inside it as
073: * specification said. One cannot create one static mehtod for recognition
074: * all BPEL elements because f.e. container VariableContainer could have
075: * ONLY Variable inside it. It cannot have "flow" tag inside it ( it can
076: * have "flow" but this is just extention element that will not be treated
077: * as BPEL element ).
078: *
079: * @param element
080: * @return
081: */
082: protected abstract BpelEntity create(Element element);
083:
084: /* (non-Javadoc)
085: * @see org.netbeans.modules.bpel.model.api.BpelContainer#indexOf(java.lang.Class, T)
086: */
087: public <T extends BpelEntity> int indexOf(Class<T> type, T entity) {
088: readLock();
089: try {
090: List<T> list = getChildren(type);
091: return list.indexOf(entity);
092: } finally {
093: readUnlock();
094: }
095: }
096:
097: /*
098: * (non-Javadoc)
099: *
100: * @see org.netbeans.modules.soa.model.bpel20.api.BpelContainer#remove(org.netbeans.modules.soa.model.bpel20.api.BpelEntity)
101: */
102: public <T extends BpelEntity> void remove(T entity) {
103: writeLock();
104: try {
105: assert entity != null;
106: checkDeleted();
107:
108: Class<? extends BpelEntity> clazz = getChildType(entity);
109:
110: List<? extends BpelEntity> list = getChildren(clazz);
111: int i = list.indexOf(entity);
112:
113: if (i > -1) {
114: BpelEntity nonRegular = null;
115: if (getMultiplicity(entity) == Multiplicity.SINGLE
116: && i == 0) {
117: i = -1;
118: if (list.size() > 1) { // there non-regular elements exist
119: nonRegular = list.get(1);
120: }
121: }
122: ChangeEvent event = null;
123: if (nonRegular == null) {
124: event = preEntityRemove(entity, i);
125: } else {
126: event = preEntityUpdate(entity, nonRegular, i);
127: }
128:
129: boolean isRegular = isRegular(entity);
130: removeChild(entity);
131: postGlobalEvent(event, isRegular);
132: } else {
133: throw new IllegalArgumentException(
134: "Specified entity is not"// NOI18N
135: + " found in this container"); // NOI18N
136: }
137: } finally {
138: writeUnlock();
139: }
140: }
141:
142: @Override
143: public <T extends BpelEntity> List<T> getChildren(Class<T> type) {
144: readLock();
145: try {
146: checkDeleted();
147: return super .getChildren(type);
148: } finally {
149: readUnlock();
150: }
151:
152: }
153:
154: /**
155: * This method should be overriden in concrete implementation
156: * if <code>entity</code> can present in this container
157: * with not unbounded multiplicity.
158: */
159: protected Multiplicity getMultiplicity(BpelEntity entity) {
160: return Multiplicity.UNBOUNDED;
161: }
162:
163: /**
164: *
165: * This method possibly needs to be overriden by some containers...
166: * F.e. it SHOULD to be overriden ActivityHolder and CompositeActivity
167: * because in these cases those containers contains elements as a whole
168: * they don't distinguish them by its own types ( Empty.class , etc. ).
169: * They need to count index for such children in common array ,not
170: * personal array.
171: */
172: protected <T extends BpelEntity> Class<? extends BpelEntity> getChildType(
173: T entity) {
174: return entity.getElementType();
175: }
176:
177: @Override
178: protected <T extends BpelEntity> T getChild(Class<T> type) {
179: readLock();
180: try {
181: checkDeleted();
182: return super .getChild(type);
183: } finally {
184: readUnlock();
185: }
186: }
187:
188: protected <T extends BpelEntity> T getChild(Class<T> type, int i) {
189: readLock();
190: try {
191: checkDeleted();
192: return getChildren(type).get(i);
193: } finally {
194: readUnlock();
195: }
196: }
197:
198: /**
199: * This method removes all old elements in array and set new list of
200: * elements with specified <code>clazz</code> type. This method add
201: * <code>entities</code> array right AFTER elements with type in
202: * <code>types</code> array.
203: */
204: @SuppressWarnings("unchecked")
205: protected <T extends BpelEntity> void setArrayAfter(T[] entities,
206: Class<T> clazz, BpelTypes... types) {
207: writeLock();
208: try {
209: assert entities != null;
210: List<? extends BpelEntity> list = getChildren(clazz);
211: ArrayUpdateEvent<BpelEntity> event = preArrayUpdate(clazz,
212: list, entities);
213: if (entities.length == 0) {
214: for (BpelEntity component : list) {
215: BpelEntityImpl impl = (BpelEntityImpl) component;
216: removeChild(impl.getEntityName(), component);
217: }
218: } else {
219: // we remove all previous elements from children list and
220: // set first child
221: setChildAfter(clazz, ((BpelEntityImpl) entities[0])
222: .getEntityName(), entities[0], types);
223: // then we consequently add all other elements from array.
224: if (entities.length > 1) {
225: for (int i = 1; i < entities.length; i++) {
226: BpelEntityImpl entity = (BpelEntityImpl) entities[i];
227: addAfter(entity.getEntityName(), entity, Utils
228: .of(types));
229: }
230: }
231: }
232: postGlobalEvent(event);
233: } finally {
234: writeUnlock();
235: }
236: }
237:
238: /**
239: * This method removes all old elements in array and set new list of
240: * elements with specified <code>clazz</code> type. This method add
241: * <code>entities</code> array right BEFORE elements with type in
242: * <code>types</code> array.
243: */
244: @SuppressWarnings("unchecked")
245: protected <T extends BpelEntity> void setArrayBefore(T[] entities,
246: Class<T> clazz, BpelTypes... types) {
247: writeLock();
248: try {
249: assert entities != null;
250: List<T> list = getChildren(clazz);
251: ArrayUpdateEvent<BpelEntity> event = preArrayUpdate(clazz,
252: list, entities);
253: if (entities.length == 0) {
254: for (BpelEntity component : getChildren(clazz)) {
255: BpelEntityImpl impl = (BpelEntityImpl) component;
256: removeChild(impl.getEntityName(), component);
257: }
258: } else {
259: // we remove all previous elements from children list and
260: // set first child
261: setChildBefore(clazz, ((BpelEntityImpl) entities[0])
262: .getEntityName(), entities[0], types);
263: // then we consequently add all other elements from array.
264: if (entities.length > 1) {
265: for (int i = 1; i < entities.length; i++) {
266: BpelEntityImpl entity = (BpelEntityImpl) entities[i];
267: addBefore(entity.getEntityName(), entity, Utils
268: .of(types));
269: }
270: }
271: }
272: postGlobalEvent(event);
273: } finally {
274: writeUnlock();
275: }
276: }
277:
278: protected <T extends BpelEntity> void addChildBefore(T entity,
279: Class<T> clazz, BpelTypes... types) {
280: writeLock();
281: try {
282: checkDeleted();
283: List<T> list = getChildren(clazz);
284: int i = list.size();
285:
286: EntityInsertEvent<? extends BpelEntity> event = preEntityInsert(
287: entity, i);
288: addBefore(((BpelEntityImpl) entity).getEntityName(),
289: entity, Utils.of(types));
290: postGlobalEvent(event, isRegular(entity));
291: } finally {
292: writeUnlock();
293: }
294: }
295:
296: protected <T extends BpelEntity> void addChildAfter(T entity,
297: Class<T> clazz, BpelTypes... types) {
298: writeLock();
299: try {
300: List<T> list = getChildren(clazz);
301: int i = list.size();
302: addEntityAfter(entity, i, types);
303: } finally {
304: writeUnlock();
305: }
306: }
307:
308: /**
309: * This method adds <code>entity</code> element to the end of children
310: * list in this container. This is useful method when we have container with
311: * only one element types. Then we just need to append element.
312: *
313: * @param entity
314: * @param clazz
315: */
316: protected <T extends BpelEntity> void addChild(T entity,
317: Class<T> clazz) {
318: writeLock();
319: try {
320: checkDeleted();
321: List<T> list = getChildren(clazz);
322: int i = list.size();
323: EntityInsertEvent<T> event = preEntityInsert(entity, i);
324: appendChild(((BpelEntityImpl) entity).getEntityName(),
325: entity);
326: postGlobalEvent(event);
327: } finally {
328: writeUnlock();
329: }
330: }
331:
332: protected <T extends BpelEntity> void insertAtIndex(T entity,
333: Class<T> clazz, int index, BpelTypes... types) {
334: writeLock();
335: try {
336: checkDeleted();
337: if ((index < 0) || (getChildren(clazz).size() < index)) {
338: throw new ArrayIndexOutOfBoundsException(
339: "Index is out of bound, "
340: + "index is :"
341: + index// NOI18N
342: + ", size of children with needed type : "// NOI18N
343: + getChildren(clazz).size());// NOI18N
344: }
345: if (index == getChildren(clazz).size()) {
346: addChildBefore(entity, clazz, types);
347: return;
348: }
349: BpelEntity previous = null;
350: if (getMultiplicity(entity) == Multiplicity.SINGLE) {
351: previous = getChild(clazz);
352: }
353:
354: ChangeEvent event;
355: if (previous == null) {
356: event = preEntityInsert(entity, index);
357: } else {
358: event = preEntityUpdate(previous, entity, -1);
359: }
360: insertAtIndex(((BpelEntityImpl) entity).getEntityName(),
361: entity, index, clazz);
362: postGlobalEvent(event);
363: } finally {
364: writeUnlock();
365: }
366: }
367:
368: /**
369: * This method is used by SyncUpdateVisitor for inserting
370: * element with given index.
371: * Index is absolute index in common children list for this container.
372: * We need to recalculate this index respectively
373: * <code>clazz</code> type children list.
374: * @return false if inserting was not performed. So
375: * There should be "add" performed instead of insert.
376: * We don't know how to perform "add" because of ordering issue
377: * ( we possibly need to know class type for object that follow
378: * clazz type ).
379: */
380: protected <T extends BpelEntity> boolean insertAtAbsoluteIndex(
381: T entity, Class<T> clazz, int index) {
382: writeLock();
383: try {
384: checkDeleted();
385: if (index < 0) {
386: // in this case we need to add .
387: return false;
388: }
389: List<T> list = getChildren(clazz);
390: if (list.size() == 0) {
391: // there was not found any children with "clazz" type. So we need to add this entity.
392: return false;
393: }
394: int indexForInsert = 0;
395: for (BpelEntity child : list) {
396: BpelEntityImpl impl = (BpelEntityImpl) child;
397: int ind = getModel().getAccess().getElementIndexOf(
398: getPeer(), impl.getPeer());
399: if (ind >= index) {
400: break;
401: }
402: indexForInsert++;
403: }
404: if (indexForInsert >= list.size()) {
405: // index is more then quantity of elements in the children list of specified type
406: return false;
407: }
408: insertAtIndex(entity, clazz, indexForInsert);
409: return true;
410: } finally {
411: writeUnlock();
412: }
413: }
414:
415: protected <T extends BpelEntity> void insertAtIndexAfter(T entity,
416: Class<T> clazz, int index, BpelTypes... types) {
417: writeLock();
418: try {
419: checkDeleted();
420: if ((index < 0) || (getChildren(clazz).size() < index)) {
421: throw new ArrayIndexOutOfBoundsException(
422: "Index is out of bound, "
423: + "index is :"
424: + index// NOI18N
425: + ", size of children with needed type : "// NOI18N
426: + getChildren(clazz).size());// NOI18N
427: }
428: if (index == getChildren(clazz).size()) {
429: addChildAfter(entity, clazz, types);
430: return;
431: }
432: EntityInsertEvent<T> event = preEntityInsert(entity, index);
433: insertAtIndex(((BpelEntityImpl) entity).getEntityName(),
434: entity, index, clazz);
435: postGlobalEvent(event);
436: } finally {
437: writeUnlock();
438: }
439: }
440:
441: protected <T extends BpelEntity> void setChildAtIndex(T entity,
442: Class<T> clazz, int index) {
443: writeLock();
444: try {
445: List<T> list = getChildren(clazz);
446: BpelEntity component = list.get(index);
447: String name = ((BpelEntityImpl) component).getEntityName();
448:
449: EntityUpdateEvent<T> event = preEntityUpdate(clazz
450: .cast(component), entity, index);
451: removeChild(name, component);
452: insertAtIndex(entity, clazz, index);
453:
454: postGlobalEvent(event);
455: } finally {
456: writeUnlock();
457: }
458: }
459:
460: /**
461: * This method will be used consistently when one need to insert element as
462: * child in some container. We need to keep some order for children in many
463: * containers and will always use <code>types</code> as "next" elements
464: * even if container doesn't have any children in <code>types</code>.
465: * Element will be added BEFORE elements in types array.
466: */
467: protected <T extends BpelEntity> void setChild(T newEl,
468: Class<T> classType, BpelTypes... types) {
469: writeLock();
470: try {
471: T old = getChild(classType);
472: EntityUpdateEvent<T> event = preEntityUpdate(old, newEl, -1);
473:
474: setChildBefore(classType, ((BpelEntityImpl) newEl)
475: .getEntityName(), newEl, types);
476:
477: postGlobalEvent(event);
478: } finally {
479: writeUnlock();
480: }
481:
482: }
483:
484: protected void removeChild(Class<? extends BpelEntity> classType) {
485: writeLock();
486: try {
487: checkDeleted();
488: BpelEntity component = getChild(classType);
489: BpelEntity nonRegular = null;
490:
491: if (component == null) {
492: return;
493: }
494:
495: if (getMultiplicity(component) == Multiplicity.SINGLE) {
496: // actully this should be always for this method
497: List<? extends BpelEntity> list = getChildren(classType);
498: if (list.size() > 1) { // there non-regular elements exist
499: nonRegular = list.get(1);
500: }
501: }
502:
503: ChangeEvent event;
504: if (nonRegular == null) {
505: event = preEntityRemove(component, -1);
506: } else {
507: event = preEntityUpdate(component, nonRegular, -1);
508: }
509:
510: removeChild(component);
511:
512: postGlobalEvent(event);
513: } finally {
514: writeUnlock();
515: }
516: }
517:
518: protected void removeChild(Class<? extends BpelEntity> classType,
519: int i) {
520: writeLock();
521: try {
522: checkDeleted();
523: BpelEntity component = getChildren(classType).get(i);
524: EntityRemoveEvent<? extends BpelEntity> event = preEntityRemove(
525: component, -1);
526: removeChild(component);
527:
528: postGlobalEvent(event);
529: } finally {
530: writeUnlock();
531: }
532: }
533:
534: @Override
535: protected void populateChildren(List<BpelEntity> children) {
536: NodeList nl = getPeer().getChildNodes();
537: if (nl != null) {
538: for (int i = 0; i < nl.getLength(); i++) {
539: Node n = nl.item(i);
540: if (n instanceof Element) {
541: BpelEntity component = getModel().createComponent(
542: this , (Element) n);
543: if (component != null) {
544: children.add(component);
545: }
546: }
547: }
548: }
549:
550: }
551:
552: /**
553: * This method is used for determening regularity of element.
554: * Regular element means "correct" element from API point of view.
555: * There is possibility for element to be OM entity but is not
556: * valid element from API OM point of view.
557: * F.e. many activities in Process are not allowed. So only
558: * first activity is correct. All others are valid OM entity but
559: * not correctly located. User can always work with
560: * such non-regular entities from source editor.
561: * But design view based on OM will not allow to user
562: * work with such elements. This method helps determine
563: * such elements and it will be used for distinguishing
564: * elements that could be subject of events in OM and
565: * non-regular elements that will be not subjects of events.
566: *
567: * @return Is <code>entity</code> regular.
568: */
569: final boolean isRegular(BpelEntity entity) {
570: assert entity != null;
571: Multiplicity mult = getMultiplicity(entity);
572: if (mult == Multiplicity.UNBOUNDED) {
573: return true;
574: }
575: if (mult == Multiplicity.SINGLE) {
576: BpelEntity child = getChild(getChildType(entity));
577: return entity.equals(child);
578: }
579: assert false;
580: return true;
581: }
582:
583: class CopyKey {
584: };
585:
586: enum Multiplicity {
587: SINGLE, UNBOUNDED
588: }
589:
590: @SuppressWarnings("unchecked")
591: private <T extends BpelEntity> void setChildAfter(
592: Class<T> classType, String propertyName, T newEl,
593: BpelTypes... types) {
594: writeLock();
595: try {
596: Collection<Class<? extends BpelEntity>> classes = Collections.EMPTY_LIST;
597: if (types.length != 0) {
598: classes = Utils.of(types);
599: }
600: super
601: .setChildAfter(classType, propertyName, newEl,
602: classes);
603: } finally {
604: writeUnlock();
605: }
606: }
607:
608: private <T extends BpelEntity> void setChildBefore(
609: Class<T> classType, String propertyName, T newEl,
610: BpelTypes... types) {
611: assert newEl != null;
612: writeLock();
613: try {
614: Collection<Class<? extends BpelEntity>> classes = Utils
615: .of(types);
616: super .setChildBefore(classType, propertyName, newEl,
617: classes);
618: } finally {
619: writeUnlock();
620: }
621: }
622:
623: private void removeChild(BpelEntity component) {
624: if (component == null) {
625: return;
626: }
627: String name = ((BpelEntityImpl) component).getEntityName();
628: removeChild(name, component);
629: }
630:
631: @SuppressWarnings("unchecked")
632: private <T extends BpelEntity> void addEntityAfter(T entity, int i,
633: BpelTypes... types) {
634: checkDeleted();
635: EntityInsertEvent<T> event = preEntityInsert(entity, i);
636: super .addAfter(((BpelEntityImpl) entity).getEntityName(),
637: entity, Utils.of(types));
638: postGlobalEvent(event, isRegular(entity));
639: }
640:
641: private <T extends BpelEntity> EntityRemoveEvent<T> preEntityRemove(
642: T entity, int indx) {
643: EntityRemoveEvent<T> event = new EntityRemoveEvent<T>(
644: getModel().getSource(), this , ((BpelEntityImpl) entity)
645: .getEntityName(), entity, indx);
646: try {
647: getModel().preInnerEventNotify(event);
648: } catch (VetoException e) {
649: assert false;
650: }
651: return event;
652: }
653:
654: private <T extends BpelEntity> EntityInsertEvent<T> preEntityInsert(
655: T entity, int i) {
656: EntityInsertEvent<T> event = new EntityInsertEvent<T>(
657: getModel().getSource(), this , ((BpelEntityImpl) entity)
658: .getEntityName(), entity, i);
659: try {
660: getModel().preInnerEventNotify(event);
661: } catch (VetoException e) {
662: assert false;
663: }
664: if (isInTree()) {
665: ((BpelEntityImpl) entity).setInTreeRecursively();
666: }
667: return event;
668: }
669:
670: private <T extends BpelEntity> EntityUpdateEvent<T> preEntityUpdate(
671: T old, T entity, int i) {
672: EntityUpdateEvent<T> event = new EntityUpdateEvent<T>(
673: getModel().getSource(), this , ((BpelEntityImpl) entity)
674: .getEntityName(), old, entity, i);
675: try {
676: getModel().preInnerEventNotify(event);
677: } catch (VetoException e) {
678: assert false;
679: }
680: if (isInTree()) {
681: ((BpelEntityImpl) entity).setInTreeRecursively();
682: }
683: return event;
684: }
685:
686: private <T extends BpelEntity> ArrayUpdateEvent<BpelEntity> preArrayUpdate(
687: Class<T> clazz, List<? extends BpelEntity> oldChildren,
688: T[] newChildren) {
689: checkDeleted();
690: /*
691: * We allow to set as child in new newChildren array any children that
692: * already was in this container ( and ONLY this ). So we need to
693: * perform copy for any such child ( because we cannot insert the same
694: * entity one more time event after deletion ). And set instead of
695: * original entity in newChildren array it copy. The enity is identified
696: * by its UID. So we will assign to copied entity the same uid. Inner
697: * visitor will need to set corresponding id's for children of that
698: * entity if any. This method can be called for reordering elements in
699: * array f.e.
700: */
701: BpelEntity[] old = new BpelEntity[oldChildren.size()];
702: int j = 0;
703: for (BpelEntity component : oldChildren) {
704: old[j] = component;
705: for (int i = 0; i < newChildren.length; i++) {
706: if (component == newChildren[i]) {
707: ((BpelEntityImpl) newChildren[i]).checkDeleted();
708: BpelEntity child = ((BpelEntityImpl) newChildren[i])
709: .copy(this );
710: component.setCookie(CopyKey.class, child);
711: newChildren[i] = clazz.cast(child);
712: break;
713: }
714: }
715: j++;
716: }
717:
718: // now newChildren array don't have children that already in tree
719: // and we just set up new array.
720:
721: ArrayUpdateEvent<BpelEntity> event = new ArrayUpdateEvent<BpelEntity>(
722: getModel().getSource(), this , null, old, newChildren);
723:
724: try {
725: getModel().preInnerEventNotify(event);
726: } catch (VetoException e) {
727: assert false;
728: }
729: if (isInTree()) {
730: for (BpelEntity child : newChildren) {
731: ((BpelEntityImpl) child).setInTreeRecursively();
732: }
733: }
734: return event;
735: }
736: }
|