001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.uml.core.metamodel.infrastructure.coreinfrastructure;
043:
044: import org.netbeans.modules.uml.core.metamodel.core.foundation.FactoryRetriever;
045: import org.netbeans.modules.uml.core.metamodel.core.foundation.ElementConnector;
046: import org.netbeans.modules.uml.core.metamodel.core.foundation.ElementCollector;
047: import org.netbeans.modules.uml.core.metamodel.core.foundation.EventContextManager;
048: import org.netbeans.modules.uml.core.metamodel.core.foundation.IBackPointer;
049: import org.netbeans.modules.uml.core.metamodel.core.foundation.RelationshipEventsHelper;
050: import org.netbeans.modules.uml.core.metamodel.core.foundation.PreventElementReEntrance;
051: import org.netbeans.modules.uml.core.metamodel.core.foundation.UMLXMLManip;
052: import org.netbeans.modules.uml.core.metamodel.core.foundation.IVersionableElement;
053: import org.netbeans.modules.uml.core.metamodel.core.foundation.INamedElement;
054: import org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.ILanguageManager;
055: import org.netbeans.modules.uml.core.reverseengineering.reframework.parsingframework.ILanguageDataType;
056: import org.netbeans.modules.uml.core.coreapplication.ICoreProduct;
057: import org.dom4j.Node;
058: import org.dom4j.Document;
059:
060: import org.netbeans.modules.uml.core.eventframework.EventDispatchRetriever;
061: import org.netbeans.modules.uml.core.eventframework.EventDispatchNameKeeper;
062: import org.netbeans.modules.uml.core.eventframework.IEventPayload;
063: import org.netbeans.modules.uml.core.support.umlsupport.ProductRetriever;
064: import org.netbeans.modules.uml.core.support.umlutils.ETArrayList;
065: import org.netbeans.modules.uml.core.support.umlutils.ETList;
066: import org.netbeans.modules.uml.core.metamodel.core.foundation.IMultiplicity;
067: import org.netbeans.modules.uml.core.metamodel.core.foundation.IMultiplicityRange;
068:
069: public class AssociationEnd extends StructuralFeature implements
070: IAssociationEnd {
071: private IMultiplicity multiplicity;
072:
073: /**
074: * getAssociation. Gets the association
075: */
076: public IAssociation getAssociation() {
077: if (getNode() != null) {
078: Node parentNode = getNode().getParent();
079:
080: if (parentNode != null) {
081: // The node we want is always to up from the actual
082: // Parameter node. UML:BehavioralFeature/UML:BehavioralFeature.parameter..
083: Node owningNode = parentNode.getParent();
084: if (owningNode != null) {
085: FactoryRetriever fact = FactoryRetriever.instance();
086: if (fact != null) {
087: Object obj = fact.createTypeAndFill(
088: retrieveSimpleName(owningNode),
089: owningNode);
090:
091: //return IAssociation after casting the obj to IAssociation.
092: if (obj instanceof IAssociation)
093: return (IAssociation) obj;
094: }
095: }
096: }
097: }
098: return null;
099: }
100:
101: public void setAssociation(IAssociation assoc) {
102: final IAssociation association = assoc;
103: new ElementConnector<IAssociationEnd>().addChildAndConnect(
104: this , true, "association", "association", association,
105: new IBackPointer<IAssociationEnd>() {
106: public void execute(IAssociationEnd obj) {
107: if (association != null)
108: association.addEnd(obj);
109: }
110: });
111: }
112:
113: public void addQualifier(IAttribute attr) {
114: EventDispatchRetriever ret = EventDispatchRetriever.instance();
115: // Pop the context that has been plugging events
116: // for feature.
117: EventContextManager man = new EventContextManager();
118: man.revokeEventContext(attr, ret.getController());
119:
120: IClassifierEventDispatcher disp = (IClassifierEventDispatcher) ret
121: .getDispatcher(EventDispatchNameKeeper.classifier());
122:
123: boolean proceed = true;
124: IEventPayload payload = null;
125: if (disp != null) {
126: payload = disp.createPayload("PreQualifierAttributeAdded");
127: proceed = disp.firePreQualifierAttributeAdded(this , attr,
128: payload);
129: }
130: if (proceed) {
131: final IAttribute attribute = attr;
132: new ElementConnector<IAssociationEnd>().addChildAndConnect(
133: this , false, "UML:AssociationEnd.qualifier",
134: "UML:AssociationEnd.qualifier", attribute,
135: new IBackPointer<IAssociationEnd>() {
136: public void execute(IAssociationEnd obj) {
137: attribute.setAssociationEnd(obj);
138: }
139: });
140: if (disp != null) {
141: payload = disp.createPayload("QualifierAttributeAdded");
142: disp.fireQualifierAttributeAdded(this , attr, payload);
143: }
144: }
145: }
146:
147: public void removeQualifier(IAttribute attr) {
148: EventDispatchRetriever ret = EventDispatchRetriever.instance();
149: // Pop the context that has been plugging events
150: // for feature.
151: EventContextManager man = new EventContextManager();
152: man.revokeEventContext(attr, ret.getController());
153:
154: IClassifierEventDispatcher disp = (IClassifierEventDispatcher) ret
155: .getDispatcher(EventDispatchNameKeeper.classifier());
156:
157: boolean proceed = true;
158: IEventPayload payload = null;
159: if (disp != null) {
160: payload = disp.createPayload("PreQualifierAttributeRemove");
161: proceed = disp.firePreQualifierAttributeRemoved(this , attr,
162: payload);
163: }
164: if (proceed) {
165: final IAttribute attribute = attr;
166: new ElementConnector<IAssociationEnd>().removeElement(this ,
167: attribute, "UML:AssociationEnd.qualifier/*",
168: new IBackPointer<IAssociationEnd>() {
169: public void execute(IAssociationEnd obj) {
170: attribute.setAssociationEnd(obj);
171: }
172: });
173: if (disp != null) {
174: payload = disp
175: .createPayload("QualifierAttributeRemoved");
176: disp.fireQualifierAttributeRemoved(this , attr, payload);
177: }
178: }
179: }
180:
181: public ETList<IAttribute> getQualifiers() {
182: ElementCollector<IAttribute> collector = new ElementCollector<IAttribute>();
183: return collector.retrieveElementCollection(m_Node,
184: "UML:AssociationEnd.qualifier/*", IAttribute.class);
185: }
186:
187: public void setType(IClassifier classifier) {
188: setParticipant(classifier);
189: }
190:
191: public void setFeaturingClassifier(IClassifier classifier) {
192: setParticipant(classifier);
193: }
194:
195: public IClassifier getFeaturingClassifier() {
196: return getParticipant();
197: }
198:
199: /**
200: * Designates the Classifier participating in the Association
201: * at the given end.
202: *
203: * @return IClassifier
204: */
205: public IClassifier getParticipant() {
206: return getType();
207: }
208:
209: /**
210: * Designates the Classifier participating in the Association
211: * at the given end.
212: *
213: * @param IClassifier
214: */
215: public void setParticipant(IClassifier newParti) {
216: PreventElementReEntrance reEnt = new PreventElementReEntrance(
217: this );
218: try {
219: if (!reEnt.isBlocking()) {
220: IAssociation asso = getAssociation();
221: IClassifier currentParticipant = getParticipant();
222: if (currentParticipant != null) {
223: IAssociationEnd curObj = (IAssociationEnd) this ;
224: // Remove the current participant before putting the new one
225: currentParticipant.removeAssociationEnd(curObj);
226: }
227:
228: RelationshipEventsHelper helper = new RelationshipEventsHelper(
229: this );
230: boolean isModified = helper.firePreEndModified("type",
231: newParti, null);
232: if (isModified) {
233: super .setType(newParti);
234: if (newParti != null) {
235: newParti.addAssociationEnd(this );
236: }
237: helper.fireEndModified();
238: } else {
239: //throw exception.
240: }
241: }
242: } finally {
243: reEnt.releaseBlock();
244: }
245: }
246:
247: /**
248: * Retrieves the other ends of the Association this end is a part of.
249: */
250: public ETList<IAssociationEnd> getOtherEnd() {
251: IAssociation assoc = getAssociation();
252: ETList<IAssociationEnd> otherEnds = new ETArrayList<IAssociationEnd>();
253: if (assoc != null) {
254: ETList<IAssociationEnd> assocEnds = assoc.getEnds();
255: if (assocEnds != null) {
256: int noOfEnds = assocEnds.size();
257: boolean found = false;
258: for (int i = 0; i < noOfEnds; i++) {
259: IAssociationEnd end = assocEnds.get(i);
260: if (end != null) {
261: boolean same = isSame(end);
262: if (!same) {
263: otherEnds.add(end);
264: found = true;
265: }
266: }
267: }
268: //Let's make sure we don't have a reflexive link
269: if (!found && (noOfEnds == 2)) {
270:
271: }
272: }
273: }
274: return otherEnds;
275: }
276:
277: /**
278: *
279: * Turns this end into a navigable end.
280: *
281: * @return INavigableEnd
282: * @warning Once MakeNavigable() has been called, the AssociationEnd that the call
283: * was made on should be discarded immediately. Use the INavigableEnd
284: * returned from this method instead.
285: *
286: */
287: public INavigableEnd makeNavigable() {
288: String newForm = "NavigableEnd";
289:
290: EventDispatchRetriever ret = EventDispatchRetriever.instance();
291: IClassifierEventDispatcher disp = (IClassifierEventDispatcher) ret
292: .getDispatcher(EventDispatchNameKeeper.classifier());
293: boolean proceed = true;
294: if (disp != null) {
295: IEventPayload payload = disp
296: .createPayload("PreAssociationEndTransform");
297: proceed = disp.firePreAssociationEndTransform(this ,
298: newForm, payload);
299: }
300:
301: if (proceed) {
302: Object navEnd = UMLXMLManip.transformElement(this , newForm);
303: INavigableEnd nEnd = navEnd instanceof INavigableEnd ? (INavigableEnd) navEnd
304: : null;
305:
306: if (disp != null) {
307: IEventPayload payload = disp
308: .createPayload("AssociationEndTransform");
309: disp.fireAssociationEndTransformed(nEnd, payload);
310: }
311:
312: if (nEnd != null)
313: return nEnd;
314: } else {
315: //cancel the event
316: }
317: return null;
318: }
319:
320: public boolean getIsNavigable() {
321: boolean isNavigable = false;
322: if (this instanceof INavigableEnd)
323: isNavigable = true;
324:
325: return isNavigable;
326: }
327:
328: /**
329: * Establishes the appropriate XML elements for this UML type.
330: *
331: * [in] The document where this element will reside
332: * [in] The element's parent node
333: */
334: public void establishNodePresence(Document doc, Node parent) {
335: buildNodePresence("UML:AssociationEnd", doc, parent);
336: }
337:
338: public void establishDefaultName() {
339: //For now, AssociationEnds won't be named by default. Need a preference here
340: }
341:
342: /**
343: * Retrieves the first end found in the OtherEnd collection. This is usually sufficient in
344: * every association other than a ternary.
345: */
346: public IAssociationEnd getOtherEnd2() {
347: ETList<IAssociationEnd> assocEnds = getOtherEnd();
348: IAssociationEnd end = null;
349: if (assocEnds != null) {
350: int noOfEnds = assocEnds.size();
351: if (noOfEnds > 0) {
352: end = assocEnds.get(0);
353: }
354: }
355: return end;
356: }
357:
358: /**
359: *
360: * Determines whether or not the participant encapsulates the same data as the passed in element.
361: *
362: * @param element The element to check
363: */
364: public boolean isSameParticipant(IVersionableElement elem) {
365: boolean isSame = false;
366: IClassifier participant = getParticipant();
367: if (participant != null) {
368: isSame = participant.isSame(elem);
369: }
370: return isSame;
371: }
372:
373: /**
374: * Creates a new attribute with the passed-in information. The new attribute
375: * is returned. NOTE: the attribute is NOT added to this Classifier.
376: *
377: * @param newType[in] The type of this attribute. If the type is not found
378: * in the model, a dummy DataType will be created with
379: * that type as the name. If 0 or "" is passed, a default type is
380: * used.
381: * @param newName[in] The name of the attribute. If 0 or "" is passed, a default
382: * name is used.
383: */
384: public IAttribute createQualifier(String newType, String newName) {
385: IAttribute retAttr = null;
386: if (newType == null || newType.length() == 0) {
387: ICoreProduct prod = ProductRetriever.retrieveProduct();
388: if (prod != null) {
389: ILanguageManager langMan = prod.getLanguageManager();
390: if (langMan != null) {
391: ILanguageDataType dataType = langMan
392: .getAttributeDefaultType(this );
393: if (dataType != null) {
394: newType = dataType.getName();
395: }
396: }
397: }
398: }
399: if (newName == null || newName.length() == 0) {
400: newName = retrieveDefaultName();
401: }
402:
403: IClassifier classifier = resolveSingleClassifierFromString(newType);
404: if (classifier != null) {
405: retAttr = createQualifier2(classifier, newName);
406: }
407: return retAttr;
408: }
409:
410: /**
411: * Creates a new attribute with the passed-in information. The new attribute
412: * is returned. NOTE: the attribute is NOT added to this Classifier.
413: *
414: * @param type[in] The type of this attribute
415: * @param name[in] The name of the attribute
416: */
417: public IAttribute createQualifier2(IClassifier type, String name) {
418: // Now create the new attribute and the type of the attribute
419: FactoryRetriever fact = FactoryRetriever.instance();
420: IAttribute newAttr = null;
421: if (fact != null) {
422: Object obj = new Object();
423: Object ret = fact.createType("Attribute", obj);
424: if (ret != null) {
425: newAttr = (IAttribute) ret;
426: if (newAttr != null)
427: establishEventContext(newAttr);
428: ITypedElement element = (ITypedElement) newAttr;
429: if (element != null) {
430: element.setType(type);
431: }
432: newAttr.setName(name);
433: }
434: }
435: return newAttr;
436: }
437:
438: /**
439: * Creates a new Qualifier, giving it a default type and name.
440: */
441: public IAttribute createQualifier3() {
442: return createQualifier(null, null);
443: }
444:
445: /**
446: *
447: * Attempts to find a single classifier in this classifier's namespace or
448: * above namespaces. If more than one classifier is found with the same name,
449: * only the first one is used.
450: *
451: * @param typeName[in] The name to match against
452: */
453: public IClassifier resolveSingleClassifierFromString(String typeName) {
454: INamedElement element = null;
455: if (typeName != null && typeName.length() > 0) {
456: element = resolveSingleTypeFromString(typeName);
457: }
458: if (element != null)
459: return (IClassifier) element;
460: return null;
461: }
462:
463: /**
464: *
465: * Creates a new EventContext that will be propogated to all
466: * EventDispatchers on the Product's EventDispatchController.
467: * This Context will prevent events from firing when initiated
468: * from the passed-in element.
469: *
470: * @param feature[in] The feature to create the context and
471: * EventFilter with
472: */
473: public void establishEventContext(IFeature feature) {
474: EventContextManager manager = new EventContextManager();
475: manager.establishVersionableElementContext(this , feature, null);
476: }
477:
478: public IMultiplicity getMultiplicity() {
479: if (multiplicity == null) {
480: multiplicity = super .getMultiplicity();
481: }
482: return multiplicity;
483: }
484:
485: public ETList<IMultiplicityRange> getRanges() {
486: return this .getMultiplicity().getRanges();
487: }
488:
489: public void setRanges() {
490: super .setMultiplicity(this .getMultiplicity());
491: }
492:
493: public void removeRange(IMultiplicityRange range) {
494: this .getMultiplicity().removeRange(range);
495: //super.setMultiplicity(multiplicity);
496: }
497:
498: public IMultiplicityRange createRange() {
499: return this .getMultiplicity().createRange();
500: }
501:
502: public void addRange(IMultiplicityRange range) {
503: this .getMultiplicity().addRange(range);
504: //super.setMultiplicity(multiplicity);
505: }
506:
507: }
|