001: // THIS SOFTWARE IS PROVIDED BY SOFTARIS PTY.LTD. AND OTHER METABOSS
002: // CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
003: // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
004: // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTARIS PTY.LTD.
005: // OR OTHER METABOSS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
006: // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
007: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
008: // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
009: // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
010: // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
011: // EVEN IF SOFTARIS PTY.LTD. OR OTHER METABOSS CONTRIBUTORS ARE ADVISED OF THE
012: // POSSIBILITY OF SUCH DAMAGE.
013: //
014: // Copyright 2000-2005 © Softaris Pty.Ltd. All Rights Reserved.
015: package com.metaboss.sdlctools.models.xpathsearch;
016:
017: import java.util.ArrayList;
018: import java.util.Arrays;
019: import java.util.Collection;
020: import java.util.Collections;
021: import java.util.HashMap;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.Map;
025: import java.util.NoSuchElementException;
026:
027: import javax.jmi.model.AggregationKindEnum;
028: import javax.jmi.model.AssociationEnd;
029: import javax.jmi.model.Attribute;
030: import javax.jmi.model.ModelElement;
031: import javax.jmi.model.MofClass;
032: import javax.jmi.model.Reference;
033: import javax.jmi.reflect.InvalidNameException;
034: import javax.jmi.reflect.RefException;
035: import javax.jmi.reflect.RefObject;
036: import javax.jmi.reflect.RefPackage;
037: import javax.naming.Context;
038: import javax.naming.InitialContext;
039: import javax.naming.NamingException;
040:
041: import org.apache.commons.logging.Log;
042: import org.apache.commons.logging.LogFactory;
043: import org.jaxen.DefaultNavigator;
044: import org.jaxen.Function;
045: import org.jaxen.FunctionCallException;
046: import org.jaxen.FunctionContext;
047: import org.jaxen.JaxenException;
048: import org.jaxen.UnsupportedAxisException;
049: import org.jaxen.XPath;
050: import org.jaxen.XPathFunctionContext;
051: import org.jaxen.util.SingleObjectIterator;
052: import org.saxpath.SAXPathException;
053:
054: import com.metaboss.sdlctools.models.ModelRepository;
055: import com.metaboss.sdlctools.models.ModelRepositoryException;
056: import com.metaboss.util.ObjectUtils;
057:
058: /** This class implements the org.jaxen.DefaultNavigator interface and allows
059: * to navigate object tree against MOF model */
060: public class MOFXPathDocumentNavigator extends DefaultNavigator {
061: private static final Log sLogger = LogFactory
062: .getLog(MOFXPathDocumentNavigator.class);
063: // Empty iterator
064: private final static Iterator sEmptyIterator = new ArrayList()
065: .iterator();
066: private static Object sInternalCreationSemaphore = new Object();
067: private static MOFXPathDocumentNavigator sNavigator = null;
068: private static XPathFunctionContext sFunctionContext = null;
069: private static ModelRepository sModelRepository = null;
070:
071: // Special wrapper class. Acts as the holder for the attribute
072: private static class AttributeHolder {
073: private RefObject mOwnerElement;
074: private Attribute mAttributeMetaObject;
075: private String mAttribuiteName;
076:
077: public AttributeHolder(RefObject pOwnerElement,
078: Attribute pAttributeMetaObject) {
079: mOwnerElement = pOwnerElement;
080: mAttributeMetaObject = pAttributeMetaObject;
081: mAttribuiteName = (String) mAttributeMetaObject
082: .refGetValue("name");
083: }
084:
085: /** Overidden to do proper compare */
086: public boolean equals(Object pObject) {
087: if (pObject == null)
088: return false;
089: if ((pObject instanceof AttributeHolder) == false)
090: return false;
091: AttributeHolder lOther = (AttributeHolder) pObject;
092: return ObjectUtils.equals(mOwnerElement,
093: lOther.mOwnerElement)
094: && ObjectUtils.equals(mAttributeMetaObject,
095: lOther.mAttributeMetaObject);
096: }
097:
098: /** Returns the name of the attribute */
099: public String getAttributeName() {
100: return mAttribuiteName;
101: }
102:
103: /** Returns the qualified name of the attribute */
104: public String getAttributeQName() {
105: return getAttributeName();
106: }
107:
108: /** Returns the owner of the attribute */
109: public RefObject getOwnerElement() {
110: return mOwnerElement;
111: }
112:
113: /** Returns the value of the attribute */
114: public Object getAttributeValue() {
115: return mOwnerElement.refGetValue(mAttributeMetaObject);
116: }
117:
118: /** Returns the String value of the attribute */
119: public String getAttributeStringValue() {
120: Object lAttributeValue = mOwnerElement
121: .refGetValue(mAttributeMetaObject);
122: if (lAttributeValue == null)
123: return "";
124: if (lAttributeValue instanceof String)
125: return (String) lAttributeValue;
126: return lAttributeValue.toString();
127: }
128: }
129:
130: // Special Function class. Returns node set of references
131: private static class ReferenceFunction implements Function {
132: public Object call(org.jaxen.Context context, List args)
133: throws FunctionCallException {
134: if (args.size() != 1)
135: throw new FunctionCallException(
136: "reference() requires one argument - the name of the reference");
137: String lReferenceName = args.get(0).toString();
138:
139: List contextNodes = context.getNodeSet();
140: if (contextNodes.isEmpty())
141: return Collections.EMPTY_LIST;
142: List lReferences = new ArrayList();
143: for (Iterator lContextNodesIterator = contextNodes
144: .iterator(); lContextNodesIterator.hasNext();) {
145: Object lObject = lContextNodesIterator.next();
146: if (lObject instanceof RefObject) {
147: RefObject lElementObject = (RefObject) lObject;
148: TypeInformation lTypeInformation = getElementTypeInformation(lElementObject);
149: Reference lReference = (Reference) lTypeInformation.mAllReferences
150: .get(lReferenceName);
151: if (lReference != null) {
152: AssociationEnd lReferencedEnd = lReference
153: .getReferencedEnd();
154: if (lReferencedEnd.getMultiplicity().getUpper() != 1) {
155: // This is the collection
156: Collection lReferencedElementsCollection = (Collection) lElementObject
157: .refGetValue(lReference);
158: lReferences
159: .addAll(lReferencedElementsCollection);
160: } else {
161: RefObject lReferencedObject = (RefObject) lElementObject
162: .refGetValue(lReference);
163: if (lReferencedObject != null)
164: lReferences.add(lReferencedObject);
165: }
166: }
167: }
168:
169: }
170: return lReferences;
171: }
172: }
173:
174: // Special Function class. Invokes specified method on all objects in the current axis
175: private static class InvokeFunction implements Function {
176: public Object call(org.jaxen.Context context, List args)
177: throws FunctionCallException {
178: if (args.size() < 1)
179: throw new FunctionCallException(
180: "invoke() requires at least one argument - the name of the operation to invoke");
181: String lOperationName = args.get(0).toString();
182: // Prepare invocation args
183: List lInvocationArgs = new ArrayList(args);
184: lInvocationArgs.remove(0);
185:
186: List contextNodes = context.getNodeSet();
187: if (contextNodes.isEmpty())
188: return Collections.EMPTY_LIST;
189: List lResults = new ArrayList();
190: for (Iterator lContextNodesIterator = contextNodes
191: .iterator(); lContextNodesIterator.hasNext();) {
192: Object lObject = lContextNodesIterator.next();
193: if (lObject instanceof RefObject) {
194: try {
195: RefObject lElementObject = (RefObject) lObject;
196: Object lResultObject = lElementObject
197: .refInvokeOperation(lOperationName,
198: lInvocationArgs);
199: if (lResultObject != null) {
200: if (lResultObject instanceof Collection)
201: lResults
202: .addAll((Collection) lResultObject);
203: else
204: lResults.add(lResultObject);
205: }
206: } catch (InvalidNameException e) {
207: throw new FunctionCallException(
208: "Unable to execute operation '"
209: + lOperationName
210: + "' on the model element.", e);
211: } catch (RefException e) {
212: throw new FunctionCallException(
213: "Unable to execute operation '"
214: + lOperationName
215: + "' on the model element.", e);
216: }
217: }
218:
219: }
220: return lResults;
221: }
222: }
223:
224: // Special Function class. Returns axis with objects which are present in the
225: // current axis as well as in the given set
226: private static class IntersectionFunction implements Function {
227: public Object call(org.jaxen.Context context, List args)
228: throws FunctionCallException {
229: if (args.size() != 1)
230: throw new FunctionCallException(
231: "intersection() requires one argument - the set to intersect the current set with");
232: // First collection is the current node set in the context
233: Collection lFirstCollection = context.getNodeSet();
234: if (lFirstCollection.isEmpty())
235: return Collections.EMPTY_LIST;
236: // Second collection is passed as an argument
237: Object lArgument = args.get(0);
238: Collection lSecondCollection = (lArgument instanceof Collection) ? new ArrayList(
239: (Collection) lArgument)
240: : Arrays.asList(new Object[] { lArgument });
241: if (lSecondCollection.isEmpty())
242: return Collections.EMPTY_LIST;
243: // Go match one against the other and return only objects present in two collections
244: List lResults = new ArrayList();
245: for (Iterator lFirstCollectionIterator = lFirstCollection
246: .iterator(); lFirstCollectionIterator.hasNext();) {
247: Object lFirstObject = lFirstCollectionIterator.next();
248: for (Iterator lSecondCollectionIterator = lSecondCollection
249: .iterator(); lSecondCollectionIterator
250: .hasNext();) {
251: Object lSecondObject = lSecondCollectionIterator
252: .next();
253: if (lFirstObject.equals(lSecondObject)) {
254: // Foun the match. Put the object in the result and continue wth the next object
255: lResults.add(lFirstObject);
256: break;
257: }
258: }
259: }
260: return lResults;
261: }
262: }
263:
264: /** This method will preprocess MOF object and return the wrapper ready for XPath */
265: public Object preprocessObject(Object pObject)
266: throws ModelRepositoryException {
267: if (isDocument(pObject))
268: return pObject;
269: if (isElement(pObject))
270: return pObject;
271: throw new ModelRepositoryException(
272: "Illegal XPath context object. Objects other than Model extent or Model elements are not supported at the moment as an input to the XPath search. Object: "
273: + pObject.toString());
274: }
275:
276: /** This method will postprocess returned object and strip possible wrappers */
277: public Object postprocessObject(Object pObject)
278: throws ModelRepositoryException {
279: if (pObject instanceof AttributeHolder)
280: return ((AttributeHolder) pObject).getAttributeValue();
281: // The rest are just passed as is
282: return pObject;
283: }
284:
285: /** The factory like creator method */
286: public static MOFXPathDocumentNavigator getInstance()
287: throws JaxenException {
288: if (sNavigator == null) {
289: synchronized (sInternalCreationSemaphore) {
290: if (sNavigator == null) {
291: try {
292: // Create model repository to use
293: Context lContext = new InitialContext();
294: sModelRepository = (ModelRepository) lContext
295: .lookup(ModelRepository.COMPONENT_URL);
296: // Create navigator
297: sNavigator = new MOFXPathDocumentNavigator();
298: } catch (NamingException e) {
299: throw new JaxenException(e);
300: }
301: }
302: }
303: }
304: return sNavigator;
305: }
306:
307: /** The factory like creator method */
308: public static FunctionContext getFunctionContextInstance() {
309: if (sFunctionContext == null) {
310: synchronized (sInternalCreationSemaphore) {
311: if (sFunctionContext == null) {
312: sFunctionContext = (XPathFunctionContext) XPathFunctionContext
313: .getInstance();
314: // Register special MOF functions
315: sFunctionContext.registerFunction(null, // namespace URI
316: "reference", new ReferenceFunction());
317: // Register special MOF functions
318: sFunctionContext.registerFunction(null, // namespace URI
319: "invoke", new InvokeFunction());
320: // Register special MOF functions
321: sFunctionContext.registerFunction(null, // namespace URI
322: "intersection", new IntersectionFunction());
323: }
324: }
325: }
326: return sFunctionContext;
327: }
328:
329: /** Default constructor. */
330: private MOFXPathDocumentNavigator() {
331: }
332:
333: /** Test if object is a document. */
334: public boolean isDocument(Object pObject) {
335: try {
336: if ((pObject instanceof RefPackage) == false)
337: return false;
338: String lModelName = sModelRepository
339: .getOwnerModelName((RefPackage) pObject);
340: if (lModelName == null)
341: return false;
342: RefPackage lModelExtent = sModelRepository
343: .getModelExtent(lModelName);
344: return pObject.equals(lModelExtent);
345: } catch (ModelRepositoryException e) {
346: sLogger
347: .error(
348: "Caught exception while trying to use ModelRepository",
349: e);
350: return false;
351: }
352: }
353:
354: /** Returns document node for the given object */
355: public Object getDocumentNode(Object pObject) {
356: try {
357: String lModelName = null;
358: if (isElement(pObject))
359: lModelName = sModelRepository
360: .getOwnerModelName((RefObject) pObject);
361: else if (isDocument(pObject))
362: lModelName = sModelRepository
363: .getOwnerModelName((RefPackage) pObject);
364: else if (pObject instanceof AttributeHolder)
365: lModelName = sModelRepository
366: .getOwnerModelName(((AttributeHolder) pObject)
367: .getOwnerElement());
368: if (lModelName == null)
369: return null;
370: return sModelRepository.getModelExtent(lModelName);
371: } catch (ModelRepositoryException e) {
372: sLogger
373: .error(
374: "Caught exception while trying to use ModelRepository",
375: e);
376: return null;
377: }
378: }
379:
380: /** Test if object is an element. */
381: public boolean isElement(Object pObject) {
382: return (pObject instanceof RefObject);
383: }
384:
385: /** Test if object is an attribute. */
386: public boolean isAttribute(Object pObject) {
387: return (pObject instanceof AttributeHolder);
388: }
389:
390: /** Test if object is a comment. */
391: public boolean isComment(Object pObject) {
392: return false;
393: }
394:
395: /** Test if object is a namespace. */
396: public boolean isNamespace(Object pObject) {
397: return false;
398: }
399:
400: /** Test if object is a processing instruction. */
401: public boolean isProcessingInstruction(Object pObject) {
402: return false;
403: }
404:
405: /** Test if object is a processing instruction. */
406: public boolean isText(Object pObject) {
407: return false;
408: }
409:
410: /** Retrieve the name of the given element node */
411: public String getElementName(Object pObject) {
412: if (pObject instanceof RefObject)
413: return (String) ((MofClass) ((RefObject) pObject)
414: .refMetaObject()).refGetValue("name");
415: return null;
416: }
417:
418: /** Retrieve the name of the given element node */
419: public String getElementQName(Object pObject) {
420: return getElementName(pObject);
421: }
422:
423: /** Get the local name of an attribute. */
424: public String getAttributeName(Object pObject) {
425: if (pObject instanceof AttributeHolder)
426: return (String) ((AttributeHolder) pObject)
427: .getAttributeName();
428: return null;
429: }
430:
431: /** Get the qualified name of an attribute. */
432: public String getAttributeQName(Object pObject) {
433: if (pObject instanceof AttributeHolder)
434: return ((AttributeHolder) pObject).getAttributeQName();
435: return null;
436: }
437:
438: /** Get the string value for the attribute. */
439: public String getAttributeStringValue(Object pObject) {
440: if (pObject instanceof AttributeHolder)
441: return ((AttributeHolder) pObject)
442: .getAttributeStringValue();
443: return "";
444: }
445:
446: /** Get the string value for the element. */
447: public String getElementStringValue(Object pObject) {
448: if (pObject instanceof RefObject)
449: return pObject.toString();
450: return null;
451: }
452:
453: /** Get the Namespace URI of an attribute. */
454: public String getAttributeNamespaceUri(Object object) {
455: return "";
456: }
457:
458: /** Get the Namespace URI of an element. */
459: public String getElementNamespaceUri(Object pObject) {
460: return "";
461: }
462:
463: /** Returns a parsed form of the given xpath string, which will be suitable
464: * for queries on MOF documents. */
465: public XPath parseXPath(String pXPathExpression)
466: throws SAXPathException {
467: return new MOFXPath(this , pXPathExpression);
468: }
469:
470: /** Get an iterator over all elements which are children to the given node.
471: * @return An iterator. May be empty but never null. */
472: public Iterator getChildAxisIterator(Object pObject)
473: throws UnsupportedAxisException {
474: if (pObject instanceof RefObject) {
475: return new AllChildElementsIterator((RefObject) pObject);
476: } else if (isDocument(pObject)) {
477: return getAllChildElements((RefPackage) pObject).iterator();
478: }
479: // This axis is empty
480: return sEmptyIterator;
481: }
482:
483: /** Get an iterator over parent element of the given node.
484: * @return An iterator. May be empty but never null. */
485: public Iterator getParentAxisIterator(Object pObject)
486: throws UnsupportedAxisException {
487: if (isElement(pObject)) {
488: return new SingleObjectIterator(((RefObject) pObject)
489: .refImmediateComposite());
490: } else if (isAttribute(pObject)) {
491: return new SingleObjectIterator(((AttributeHolder) pObject)
492: .getOwnerElement());
493: }
494: // This axis is empty
495: return sEmptyIterator;
496: }
497:
498: /** Get an iterator over all attributes in the given node.
499: * @return An iterator. May be empty but never null. */
500: public Iterator getAttributeAxisIterator(Object pObject) {
501: if (pObject instanceof RefObject) {
502: RefObject lModelElement = (RefObject) pObject;
503: TypeInformation lTypeInformation = getElementTypeInformation(lModelElement);
504: // Create list of attribute holders for this object and return an iterator over it
505: List lAttributeHolders = new ArrayList();
506: for (Iterator lTypeAttributesIterator = lTypeInformation.mAttributes
507: .iterator(); lTypeAttributesIterator.hasNext();) {
508: Attribute lAttribute = (Attribute) lTypeAttributesIterator
509: .next();
510: lAttributeHolders.add(new AttributeHolder(
511: lModelElement, lAttribute));
512: }
513: return lAttributeHolders.iterator();
514: }
515: // This axis is empty
516: return sEmptyIterator;
517: }
518:
519: /** Get the string value for the comment. */
520: public String getCommentStringValue(Object pObject) {
521: return null;
522: }
523:
524: /** Get the string value for the text. */
525: public String getTextStringValue(Object pObject) {
526: return null;
527: }
528:
529: /** Get the string value for the namespace. */
530: public String getNamespaceStringValue(Object pObject) {
531: return null;
532: }
533:
534: /** Get the string value for the namespace prefix. */
535: public String getNamespacePrefix(Object pObject) {
536: return null;
537: }
538:
539: // This class holds cached Model Element type information
540: private static class TypeInformation {
541: private MofClass mType = null;
542: private List mContentsReferences = new ArrayList(); // References to all contained objects
543: private Map mAllReferences = new HashMap(); // All references mapped by thir name
544: private List mAttributes = new ArrayList(); // All attributes
545: }
546:
547: // This map holds the type information for every type in the model
548: private static Map sTypeInformationMap = new HashMap();
549:
550: // This helper retrieves the cached metainformation about the type
551: private static TypeInformation getElementTypeInformation(
552: RefObject pElement) {
553: MofClass lMetaObject = (MofClass) pElement.refMetaObject();
554: TypeInformation lTypeInformation = (TypeInformation) sTypeInformationMap
555: .get(lMetaObject);
556: if (lTypeInformation == null) {
557: lTypeInformation = new TypeInformation();
558: lTypeInformation.mType = lMetaObject;
559: // Collect contents for the type and all supertypes
560: List lAllContents = new ArrayList();
561: lAllContents.addAll(lMetaObject.getContents());
562: for (Iterator lSupertypesIterator = lMetaObject
563: .allSupertypes().iterator(); lSupertypesIterator
564: .hasNext();)
565: lAllContents.addAll(((MofClass) lSupertypesIterator
566: .next()).getContents());
567: for (Iterator lAllContentsIterator = lAllContents
568: .iterator(); lAllContentsIterator.hasNext();) {
569: ModelElement lModelElement = (ModelElement) lAllContentsIterator
570: .next();
571: if (lModelElement instanceof Attribute) {
572: Attribute lAttribute = (Attribute) lModelElement;
573: lTypeInformation.mAttributes.add(lAttribute);
574: } else if (lModelElement instanceof Reference) {
575: Reference lReference = (Reference) lModelElement;
576: lTypeInformation.mAllReferences.put(lReference
577: .getName(), lReference);
578: AssociationEnd lExposedEnd = lReference
579: .getExposedEnd();
580: if (lExposedEnd.getAggregation().equals(
581: AggregationKindEnum.COMPOSITE)) {
582: lTypeInformation.mContentsReferences
583: .add(lReference);
584: }
585: }
586:
587: }
588: sTypeInformationMap.put(lMetaObject, lTypeInformation);
589: }
590: return lTypeInformation;
591: }
592:
593: // Helper. Get all child elements
594: private Collection getAllChildElements(RefPackage pDocument) {
595: List lContents = new ArrayList();
596: try {
597: // Create list of contained top level elements for this object and return an iterator over it
598: Collection lTopLevelModelElements = sModelRepository
599: .getTopLevelModelObjects(sModelRepository
600: .getOwnerModelName(pDocument));
601: for (Iterator lTopLevelModelElementIterator = lTopLevelModelElements
602: .iterator(); lTopLevelModelElementIterator
603: .hasNext();) {
604: RefObject lTopLevelModelElement = (RefObject) lTopLevelModelElementIterator
605: .next();
606: lContents.add(lTopLevelModelElement);
607: }
608: } catch (ModelRepositoryException e) {
609: sLogger
610: .error(
611: "Caught exception while trying to use ModelRepository",
612: e);
613: }
614: return lContents;
615: }
616:
617: // // Helper. Get all child elements
618: // private Collection getAllChildElements(RefObject pElement)
619: // {
620: // TypeInformation lTypeInformation = getElementTypeInformation(pElement);
621: // // Create list of contained elements for this object and return an iterator over it
622: // List lContents = new ArrayList();
623: // for (Iterator lContentsReferencesIter = lTypeInformation.mContentsReferences.iterator(); lContentsReferencesIter.hasNext();)
624: // {
625: // Reference lReference = (Reference)lContentsReferencesIter.next();
626: // AssociationEnd lReferencedEnd = lReference.getReferencedEnd();
627: // if (lReferencedEnd.getMultiplicity().getUpper() != 1)
628: // {
629: // // This is the collection
630: // Collection lContainedElementsCollection = (Collection)pElement.refGetValue(lReference);
631: // if (!lContainedElementsCollection.isEmpty())
632: // {
633: // for (Iterator lContentsIter = lContainedElementsCollection.iterator();lContentsIter.hasNext();)
634: // lContents.add(lContentsIter.next());
635: // }
636: // }
637: // else
638: // {
639: // RefObject lContainedObject = (RefObject)pElement.refGetValue(lReference);
640: // if (lContainedObject != null)
641: // lContents.add(lContainedObject);
642: // }
643: // }
644: // return lContents;
645: // }
646:
647: // This class implements an iterator over all children
648: private static class AllChildElementsIterator implements Iterator {
649: private RefObject mParentElement;
650: private TypeInformation mParentElementTypeInformation;
651: private Iterator mContentsIterator;
652: private Iterator mCurrentMultipleReferenceIterator = null;
653: private Object mNextObject = null;
654: private boolean mFetchedLast = false;
655:
656: public AllChildElementsIterator(RefObject pParentElement) {
657: mParentElement = pParentElement;
658: TypeInformation mParentElementTypeInformation = getElementTypeInformation(pParentElement);
659: mContentsIterator = mParentElementTypeInformation.mContentsReferences
660: .iterator();
661: mCurrentMultipleReferenceIterator = null;
662: fetchNextObject();
663: }
664:
665: /**
666: * Returns <tt>true</tt> if the iteration has more elements. (In other
667: * words, returns <tt>true</tt> if <tt>next</tt> would return an element
668: * rather than throwing an exception.)
669: *
670: * @return <tt>true</tt> if the iterator has more elements.
671: */
672: public boolean hasNext() {
673: return mNextObject != null;
674: }
675:
676: /**
677: * Returns the next element in the iteration.
678: *
679: * @return the next element in the iteration.
680: * @exception NoSuchElementException iteration has no more elements.
681: */
682: public Object next() {
683: Object lObjectToReturn = mNextObject;
684: fetchNextObject();
685: return lObjectToReturn;
686: }
687:
688: /**
689: *
690: * Removes from the underlying collection the last element returned by the
691: * iterator (optional operation). This method can be called only once per
692: * call to <tt>next</tt>. The behavior of an iterator is unspecified if
693: * the underlying collection is modified while the iteration is in
694: * progress in any way other than by calling this method.
695: *
696: * @exception UnsupportedOperationException if the <tt>remove</tt>
697: * operation is not supported by this Iterator.
698:
699: * @exception IllegalStateException if the <tt>next</tt> method has not
700: * yet been called, or the <tt>remove</tt> method has already
701: * been called after the last call to the <tt>next</tt>
702: * method.
703: */
704: public void remove() {
705: throw new UnsupportedOperationException(
706: "remove() operation is not supported in this read-only iterator.");
707: }
708:
709: private void fetchNextObject() {
710: if (mFetchedLast)
711: return; // Nothing more to do
712: if (mCurrentMultipleReferenceIterator != null) {
713: if (mCurrentMultipleReferenceIterator.hasNext()) {
714: mNextObject = mCurrentMultipleReferenceIterator
715: .next();
716: return;
717: }
718: mCurrentMultipleReferenceIterator = null;
719: }
720:
721: while (mContentsIterator.hasNext()) {
722: Reference lReference = (Reference) mContentsIterator
723: .next();
724: AssociationEnd lReferencedEnd = lReference
725: .getReferencedEnd();
726: if (lReferencedEnd.getMultiplicity().getUpper() != 1) {
727: Collection lCurrentMultipleReferenceCollection = (Collection) mParentElement
728: .refGetValue(lReference);
729: if (!lCurrentMultipleReferenceCollection.isEmpty()) {
730: mCurrentMultipleReferenceIterator = lCurrentMultipleReferenceCollection
731: .iterator();
732: mNextObject = mCurrentMultipleReferenceIterator
733: .next();
734: return;
735: }
736: } else {
737: RefObject lContainedObject = (RefObject) mParentElement
738: .refGetValue(lReference);
739: if (lContainedObject != null) {
740: mNextObject = lContainedObject;
741: return;
742: }
743: }
744: }
745: // If we are here - it means that nothing left in the parent's list
746: mNextObject = null;
747: mFetchedLast = true;
748: }
749: }
750: }
|