alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
 * Microsystems, Inc. All Rights Reserved.
 */
package org.netbeans.lib.jmi.xmi;

import java.util.*;

import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.AttributesImpl;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;

import org.netbeans.api.xmi.*;
import org.netbeans.lib.jmi.util.DebugException;

import javax.jmi.reflect.*;
import javax.jmi.model.*;
import org.netbeans.lib.jmi.util.Logger;

public abstract class XmiElement {
            
    // context in which current elements are being processed
    protected XmiContext context;
    // parent element of this element
    protected XmiElement parent;

    public XmiElement (XmiElement parent, XmiContext context) {
        this.parent = parent;
        this.context = context;        
    }

    public XmiElement (XmiContext context) {
        this.context = context;
    }

    /**
     * Handles startElement event generated by SAX parser. Usually creates sub-element and 
     * returns it or returns itself.
     */
    public XmiElement startSubElement (String qName, Attributes attrs) {
        return this;
    }

    /**
     * Handles endElement event generated by SAX parser.
     */
    public XmiElement endElement (String qName) {
        return parent;
    }

    /**
     * Handles characters event generated by SAX parser.
     */
    public void characters (char buf[], int offset, int len) {
        // default behaviour - ignore characters
        return;
    }

    /**
     * Method called by sub-elements to pass values resolved by them.
     */
    public void receiveValue (Object value) {
        // should be called on instances that override it only
        throw new DebugException ("Unexpected call of receiveValue () method.");
    }

// ------------------------------------------------------------------------------
// inner classes
// ------------------------------------------------------------------------------

    // **************************************************************************
    // Document
    // **************************************************************************
    public static class Document extends XmiElement {

        private String rootElementName;
        private Content content = null;
        private XmiElement xmiElement = null;
        
        public Document (XmiElement parent, XmiContext context, String rootName, Attributes attrs) {
            super (parent, context);
            rootElementName = rootName;            
            context.setVersion (attrs);
            if (context.isXmi20) {
                content = new Content (this, context);
                if (!rootElementName.equals (context.xmiNsPrefix + XmiConstants.XMI_ROOT)) {
                    AttributesImpl attrs2 = new AttributesImpl ();
                    for (int x = 0; x < attrs.getLength (); x++) {
                        String name = attrs.getQName (x);
                        if (!(name.equals ("xmlns") || name.startsWith ("xmlns:") || 
                            name.equals (context.xmiNsPrefix + XmiConstants.XMI20_VERSION))) {
                            attrs2.addAttribute (null, null, name, null, attrs.getValue (x));
                        } // if
                    } // for
                    xmiElement = content.startSubElement (rootElementName, attrs2);
                } // if
            } // if
        }

        public XmiElement startSubElement (String qName, Attributes attrs) {
            if (context.isXmi20) {
                // delegate startElement to content or xmiElement
                if (xmiElement != null)
                    return xmiElement.startSubElement (qName, attrs);
                else
                    return content.startSubElement (qName, attrs);
            } else {
                if (qName.equals (XmiConstants.XMI_CONTENT)) {
                    return new XmiElement.Content (this, context);
                } else if (qName.equals (XmiConstants.XMI_DIFFERENCE)) {
                    return new XmiElement.Difference (this, context, attrs);
                } else if (qName.equals (XmiConstants.XMI_HEADER)) {
                    return new XmiElement.Header (this, context);
                } else {
                    if (context.ignoreUnknownElements()) {
                        return new XmiElement.Dummy(this, context, qName);
                    } else {
                        throw new DebugException("Invalid element name: " + qName);
                    }
                }
            }
        }

        public XmiElement endElement (String qName) {            
            if (qName.equals (rootElementName)) {
                if (context.isXmi20) {
                    if (xmiElement != null)
                        xmiElement.endElement (qName);
                    content.endElement (qName);
                }
                context.finish ();
                return parent;
            }
            return this;
        }

    } // Document

    
    // **************************************************************************
    // Header
    // **************************************************************************
    
    public static class Header extends XmiElement {
    
        private int level = 0;
        private StringBuffer buffer = new StringBuffer ();
        
        public Header (XmiElement parent, XmiContext context) {
            super (parent, context);
        }
        
        public XmiElement startSubElement (String qName, Attributes attrs) {
            level++;
            buffer.append ('<' + qName);
            int attrsLength = attrs.getLength ();
            for (int index = 0; index < attrsLength; index++) {
                String attrName = attrs.getQName (index);
                String attrValue = attrs.getValue (index);
                buffer.append (" " + attrName + " = '" + attrValue + "'");
            } // for
            buffer.append ('>');
            return this;
        }

        public XmiElement endElement (String qName) {            
            if (level == 0) {
                context.receiveHeader (buffer.toString ());
                return parent;
            }
            buffer.append ("');
            level--;
            return this;
        }
        
        public void characters (char buf[], int offset, int len) {
            buffer.append (buf, offset, len);
        }
        
    } // Header
    
    // **************************************************************************
    // Difference
    // **************************************************************************
    public static class Difference extends XmiElement {

        //[PENDING] resolving of differences is not implemented yet
        
        // link defining a targed the differences apply on
        private String href;
        // true if one of Add, Delete or Replace sub-elements is being currently read
        private boolean diffReading;
        // read sub-element
        private Diff currentDiff;
        // storage of all Add, Delete, Replace sub-elements
        private HashMap diffs = new HashMap ();
        private int timeStamp = 1;
        
        public Difference (XmiElement parent, XmiContext context, Attributes attrs) {
            super (parent, context);
            href = attrs.getValue (context.XMI_HREF);
            if (href == null)
                throw new DebugException (
                    "Differences referring to the document they are placed in are not supported."
                );
            diffReading = false;
        }

        public XmiElement startSubElement (String qName, Attributes attrs) {
            if (!diffReading) {
                if (qName.equals (XmiConstants.XMI_DIFFERENCE))
                    throw new DebugException ("Nested differences are not supported.");
                int kind = Diff.ADD;
                if (qName.equals (XmiConstants.XMI_DELETE))
                    kind = Diff.DELETE;
                else if (qName.equals (XmiConstants.XMI_REPLACE))
                    kind = Diff.REPLACE;
                String localHref = attrs.getValue (context.XMI_HREF);
                int index = localHref.lastIndexOf ("|");
                if (index == -1)
                    index = localHref.lastIndexOf ("#");
                String id = localHref.substring (index + 1, localHref.length ());
                String posString = attrs.getValue (XmiConstants.XMI_POSITION);
                int position = 1; // default value
                if (posString != null) {
                    try {
                        position = Integer.parseInt (posString);
                    } catch (NumberFormatException e) {
                        throw new DebugException ("Differences - bad number format: " + posString);
                    }
                } // if
                if (position < 0)
                    throw new DebugException ("Negative values of xmi.position parameters are not supported: " + posString);
                currentDiff = new Diff (kind, id, position, timeStamp);
                timeStamp++;
                diffReading = true;
            } else { // diffReading == true
                currentDiff.items.add (new Item (qName, attrs));
            }
            return this;
        }
        
        public void characters (char buf[], int offset, int len) {
            if (diffReading) {
                currentDiff.items.add (new String (buf, offset, len));
            }
        }

        public XmiElement endElement (String qName) {
            if (diffReading) {
                if ((qName.equals (XmiConstants.XMI_ADD)) ||
                    (qName.equals (XmiConstants.XMI_DELETE)) ||
                    (qName.equals (XmiConstants.XMI_REPLACE))) {
                    diffReading = false;
                    LinkedList list = (LinkedList) diffs.get (currentDiff.xmiId);
                    if (list == null)
                        diffs.put (currentDiff.xmiId, list = new LinkedList ());
                    if (currentDiff.kind == Diff.DELETE)
                        list.addFirst (currentDiff);
                    else
                        list.addLast (currentDiff);
                } else
                    currentDiff.items.add (new Item (qName));
                return this;
            }
            //[TODO] take diffs and read referenced document
            context.resolveDifferences (href, diffs);            
            return parent;
        }
        
        // stores data related to one difference (add, delete or replace)
        public static class Diff {
            // constants for kind of difference
            public static final int ADD = 0;
            public static final int DELETE = 1;
            public static final int REPLACE = 2;
            
            // kind of this difference
            public int kind;
            // optional position parameter, not relevant in case of Delete difference
            public int position;
            // xmi id of an elemenet the difference applays on
            public String xmiId;
            // content stored as a sequence of events represented by items or a String in case
            // of a characters event
            public LinkedList items = new LinkedList ();
            public int timeStamp;
            
            public Diff (int kind, String xmiId, int position, int timeStamp) {
                this.kind = kind;
                this.xmiId = xmiId;
                this.position = position;
                this.timeStamp = timeStamp;
            }
            
        }

        // stores one start element or end element event
        public static class Item {
            // if true, represents start of an element
            public boolean isStart;
            // element name
            public String qName;
            // related attributes (in case of start of element)
            public Attributes attrs;
            
            // end element event
            public Item (String qName) {
                this.qName = qName;
                isStart = false;
            }
            
            // start element event
            public Item (String qName, Attributes attrs) {
                this.qName = qName;
                // attributes cannot be stored directly, they have to be copied
                this.attrs = new AttributesImpl (attrs);
                isStart = true;
            }
        }
        
    } // Difference
    
    // **************************************************************************
    // Content
    // **************************************************************************
    public static class Content extends XmiElement {
        // Since Content element encloses class-scoped attribute value elements,
        // it handles collecting and setting them
        
        // a class-scoped attribute whose (multi-)value is being currently read
        private Attribute attr = null;
        // class proxy related to the currently read class-scoped attribute
        private RefClass refClass = null;
        // stores currently read (multi-)value(s) of a class-scoped attribute
        private List values;
        
        public Content (XmiElement parent, XmiContext context) {
            super (parent, context);
        }

        public XmiElement startSubElement (String qName, Attributes attrs) {
            Object ref = context.resolveElementName (qName);
            if ((ref == null) && context.ignoreUnknownElements()) {
                return new XmiElement.Dummy(this, context, qName);
            }
            // [TODO] resolved 'ref' value can be passed to constructors of sub-elements
            if (ref instanceof RefClass)
                return context.resolveInstanceOrReference (this, qName, attrs);
            // class-scoped attribute ...........................................
            if (ref instanceof Attribute) {
                RefClass readRefClass = (RefClass) context.resolveElementName (
                    qName.substring (0, qName.lastIndexOf (XmiConstants.DOT_SEPARATOR))
                );
                if ((ref != attr) || (refClass != readRefClass)) {
                    // the first value element related to the first or the next class-scoped attribute
                    if (attr != null) {
                        // so, it is the next attribute, not the first one; set the previously obtained value ...
                        setAttributeValue ();
                    }
                    attr = (Attribute) ref;
                    refClass = readRefClass;
                    values = new LinkedList ();
                }
                // [PENDING] ClassLevelAttribute instance can be shared, similarly as, e.g., context.PRIMITIVE_VALUE
                return new XmiElement.ClassLevelAttribute (this, context, attr, attrs);
            }
            // ..................................................................
            if (ref instanceof Association)
                return new XmiElement.AssociationElement (this, context, (Association) ref);
            // an unexpected element
            throw new DebugException ("Unexpected element: " + qName);
        }

        public XmiElement endElement (String qName) {
            if (attr != null) {
                // end of Content has been encountered and there is still previously 
                // collected (multi-)value of a class-scoped attribute that should be set
                setAttributeValue ();
            }
            
            // register all requests for objects in external documents
            context.resolveExternalReferences ();
            
            if (context.isMain && !context.allReferencesResolved ()) {
                // (verification performed in case of main document only)
                // some unresolved reference (i.e. related instance does not exist) or
                // violation of Mof constraints, there is a cycle in the relation 
                // "an instance is a value of an attribute of another instance"
                String badRef = context.getUnresolvedRefId ();
                Logger.getDefault ().log ("Unknown reference or circularity in instance dependences detected, bad reference: " + badRef);
                // throw new DebugException ("Unknown reference or circularity in instance dependences detected, bad reference: " + badRef);
            }
            return parent;
        }
        
        public void receiveValue (Object value) {
            // collects parts of (multi-)value of a class-scoped attribute
            if (attr == null)
                throw new DebugException ("Unexpected call of Content.receiveValue ()");
            if (value instanceof List)
                values.addAll ((List) value);
            else
                values.add (value);
        }

        private void setAttributeValue () {
            LinkedList list = new LinkedList ();
            Iterator iter = values.iterator ();
            Object value;
            while (iter.hasNext ()) {
                value = iter.next ();
                if (value instanceof UnresolvedReference) {
                    value = ((UnresolvedReference) value).getValue ();
                    if (value == null) {
                        throw new DebugException ("Class-scoped attribute value not resolved: " + attr.getName ());
                    }
                }
                list.add (value);
            }
            if (XmiContext.isMultivalued (attr))
                value = list;
            else {
                if (!(list.size () == 1))
                    throw new DebugException ("Cannot set a multi-value to a non-multivalued attribute:" + attr.getName ());
                value = list.get (0);
            }
            refClass.refSetValue (attr, value);
            attr = null;
            refClass = null;
            values = null;
        }
        
    } // Content                 
    
    // **************************************************************************
    // Instance
    // **************************************************************************
    public static class Instance extends XmiElement implements ReferencesCounter {
        // fully qualified name (possibly composed using a namespace prefix)
        private String name;
        // xmi.id value
        private String xmiId = null;
        // id of related document
        private String docId;
        // number of currently unresolved references preventing creation of this instance
        private int unresolvedRefsCounter = 0;
        // flag indicating if all sub-elements are already read, i.e. endElement () method has been called
        private boolean endReached = false;
        // holds an unresolved reference to this instance that should be set after the instance creation
        private UnresolvedReference unresRef = null;
        // currently processed attribute or reference
        private StructuralFeature currentFeature;
        // type of currently processed attribute
        private Classifier currentType;

        // RefClass resolved according to the name
        private RefClass refClass;
        // meta class corresponding to refClass
        private MofClass metaClass;
        // temporal storage for values of instance level attributes
        private HashMap attributesValues = new HashMap ();
        // temporal storage for values of instance level references
        private HashMap referencesValues = new HashMap ();

        public Instance (XmiElement parent, XmiContext context, String qName, 
            RefClass refClass, Attributes attrs) {
                        
            super (parent, context);
            this.name = qName;
            this.docId = context.getCurrentDocId ();
            this.refClass = refClass;
            metaClass = (MofClass) refClass.refMetaObject ();

            int index; // index of currently processed attribute
            String attrName, attrValue;
            int attrsLength = attrs.getLength ();
            // process all passed instance attributes and xmi id's
            for (index = 0; index < attrsLength; index++) {
                attrName = attrs.getQName (index);
                attrValue = attrs.getValue (index);
                if (attrName.equals (context.XMI_ID)) {
                    xmiId = attrValue;
                } else {
                    resolveAttributeValue (attrName, attrValue);
                }
            } // for            
        }

        public XmiElement startSubElement (String qName, Attributes attrs) {
            Object ref = context.resolveElementName (qName);
            if (ref == null && context.ignoreUnknownElements()) {
                return new XmiElement.Dummy(this, context, qName);
            }
            
            if (!(ref instanceof StructuralFeature)) {
                throw new DebugException ("Invalid sub-element: " + qName);
            }            
            currentFeature = (StructuralFeature) ref;
            if (currentFeature.getScope ().equals (ScopeKindEnum.CLASSIFIER_LEVEL) && !context.isXmi20) {
                throw new DebugException ("An instance serialization contains value of static attribute: " + currentFeature.getName ());
            }
            
            if (currentFeature instanceof Attribute) {                
                Classifier type = currentFeature.getType ();
                return context.resolveValue (this, type, attrs);
            } else if (currentFeature instanceof Reference) {
                return new XmiElement.ObjectValues (this, context, null, false);
            }
            throw new DebugException ("Invalid sub-element: " + qName);
        }

        /**
         * Creates an instance in repository. Called when all needed references 
         * related to attribute values have been resolved.
         */
        private RefObject createInstance () {
            
            List attributes = context.instanceAttributes (refClass);
            List references = context.instanceReferences (refClass);

            // obtain list of attributes values
            List args = new LinkedList ();
            Iterator iter = attributes.iterator ();
            while (iter.hasNext ()) {
                Attribute attr = (Attribute) iter.next ();
                Object param = attributesValues.get (attr);
                if (param instanceof UnresolvedReference) {
                    param = ((UnresolvedReference) param).getValue ();
                } else if (param instanceof List) {
                    Iterator iter2 = ((List) param).iterator ();
                    List temp = new LinkedList ();
                    while (iter2.hasNext ()) {
                        Object value = iter2.next ();
                        if (value instanceof UnresolvedReference) {
                            value = ((UnresolvedReference) value).getValue ();
                            if (value instanceof CollectionWrapper)
                                value = ((CollectionWrapper) value).getCollection ();
                        }
                        temp.add (value);
                    }
                    param = temp;
                }
                if (param instanceof CollectionWrapper)
                    param = ((CollectionWrapper) param).getCollection ();
                if (param == null)
                    param = XmiContext.defaultValue (attr);
                args.add (param);
            } // while
            RefObject instance;
            try {
                instance = refClass.refCreateInstance (args);
            } catch (Exception e) {
                StringBuffer params = new StringBuffer(50);
                for (Iterator it = args.iterator(); it.hasNext();) {
                    Object arg = it.next();
                    params.append("    ");
                    if (arg == null) {
                        params.append("");
                    } else {
                        params.append("(" + arg.getClass() + ") " + arg.toString());
                    }
                    params.append("\n");
                }
                /*
                DebugException ne = new DebugException("Instance of " + name + " cannot be created, bad parameters:\n" + params.toString() + "\n    reason: " + e.toString());
                Logger.getDefault().annotate(ne, e);
                throw (DebugException) Logger.getDefault().annotate(ne, "Instance or " + name + " cannot be created, bad parameters:\n" + params.toString());
                 */
                String msg = "Instance of " + name + " cannot be created, parameters:\n" + params.toString() + "\n    reason: " + e.toString();                
                Logger.getDefault ().log (msg);
                return null;
            }
            if (parent instanceof XmiElement.Content) {
                context.addOutermostObject (instance);
            }

            // if xmiId != null, store resolved reference
            if (xmiId != null)
                context.putReference (docId, xmiId, instance);

            // set resolved references, register unresolved
            iter = references.iterator ();
            while (iter.hasNext ()) {
                Reference ref = (Reference) iter.next ();
                List values = (List) referencesValues.get (ref);
                if (values != null)
                    new ReferenceHandler (ref, instance, values, context);
            } // while
            
            context.countInstance ();
            
            if (context.isXmi20) {
                // check if there were some static attributes serialized, if so set these values
                iter = context.staticAttributes (refClass).iterator ();
                while (iter.hasNext ()) {
                    Attribute attr = (Attribute) iter.next ();
                    Object value = attributesValues.get (attr);
                    if (value != null) {
                        refClass.refSetValue (attr, value);
                    } // if
                } // while
            } // if
            
            return instance;
        }

        public XmiElement endElement (String qName) {
            // reading of instance object has been finished
            // corresponding instance (initialized with read attributes values)
            // can be created in repository if all needed references have been
            // already resolved
            endReached = true;
            if (unresolvedRefsCounter == 0) {
                RefObject instance = createInstance ();
                if (!(parent instanceof XmiElement.Content)) {
                    parent.receiveValue (instance);
                }
            } else {
                if (!(parent instanceof XmiElement.Content)) {
                    unresRef = new UnresolvedReference ();
                    parent.receiveValue (unresRef);
                }
            }
            return parent;
        }

        /*
         * Stores attribute value. It can be a single-value or a part of multi-value
         * as well. Thus, in case of multi-values, the method can be called several
         * times with the same attribute parameter.
         */
        // [PENDING] review setAttributeValue methods and storing of values
        // (single-values vs. multi-values)
        private void setAttributeValue (StructuralFeature attr, Object value) {
            boolean isMultivalued = XmiContext.isMultivalued (attr);
            Object tempValue = attributesValues.get (attr);
            if (!isMultivalued) {
                if (tempValue != null)
                    throw new DebugException ("Cannot set a multi-value to a non-multivalued attribute:" + attr.getName ());
                attributesValues.put (attr, value);

                /*
                if (value instanceof CollectionWrapper) {
                    System.out.println("#: " + ((CollectionWrapper) value).getCollection ().size ());
                }
                */
                
            } else {
                if (tempValue == null)
                    attributesValues.put (attr, tempValue = new LinkedList ());
                ((List) tempValue).add (value);
            } // else
        }

        /**
         * Similar as @link #setAttributeValue, except that a list of values is passed
         * rather than one value.
         */
        private void setAttributeValues (StructuralFeature attr, List values) {
            boolean isMultivalued = XmiContext.isMultivalued (attr);
            if (!isMultivalued && (values.size () == 1)) {
                setAttributeValue (attr, values.get (0));
                return;
            }

            /*
            System.out.println("passed");
            */
            
            if (!isMultivalued) {
                throw new DebugException ("Cannot set a multi-value to a non-multivalued attribute:" + attr.getName ());
            }
            Object tempValue = attributesValues.get (attr);
            if (tempValue == null)
                attributesValues.put (attr, tempValue = new LinkedList ());
            ((List) tempValue).addAll (values);
        }

        /**
         * Stores values of a reference.
         */
        private void setReferenceValues (Reference ref, List values) {
            boolean isMultivalued = XmiContext.isMultivalued (ref);
            Object tempValue = referencesValues.get (ref);
            if (!isMultivalued) {
                if ((tempValue != null) || (values.size () > 1)) {
                    throw new DebugException
                        ("Cannot set a multi-value to a non-multivalued reference:" + ref.getName());
                }
                referencesValues.put (ref, values);
                return;
            }
            if (tempValue == null)
                referencesValues.put (ref, tempValue = new LinkedList ());
            ((List) tempValue).addAll (values);
        }

        public void receiveValue (Object value) {
            if (currentFeature instanceof Attribute) {
                if (value instanceof List)
                    setAttributeValues (currentFeature, (List) value);
                else
                    setAttributeValue (currentFeature, value);
            } else { // Reference
                // [PENDING] performance - change the following code !!
                if (!(value instanceof List)) {
                    List temp = new LinkedList ();
                    temp.add (value);
                    value = temp;
                }
                setReferenceValues ((Reference) currentFeature, (List) value);
            }
         }

        /**
         * Resolves attribute value stored as XMI element attribute
         * (example:  ).
         */
        private void resolveAttributeValue (String attrName, String attrValue) {
            StructuralFeature attr;
            
            attr = context.instanceElementByName (refClass, attrName);
            if ((attr == null) && context.isXmi20) {
                attr = context.staticAttributeByName (refClass, attrName);
            }
            if (attr == null)
                return;

            Classifier type = attr.getType ();
            while (type instanceof AliasType)
                type = ((AliasType) type).getType ();
            Object value = null;

            if (type instanceof PrimitiveType) {
                value = XmiContext.resolvePrimitiveValue ((PrimitiveType) type, attrValue);
                setAttributeValue (attr, value);
            } else if (type instanceof EnumerationType) {
                value = context.resolveEnumerationValue ((EnumerationType) type, attrValue);
                setAttributeValue (attr, value);
            } else if (type instanceof MofClass) {
                boolean isReference = attr instanceof Reference;
                StringTokenizer tokenizer = new StringTokenizer (attrValue, " ");
                List list = new LinkedList ();
                while (tokenizer.hasMoreTokens ()) {
                    // [PENDING] what about additional spaces in a string ???
                    // Is an empty token generated for them or not ?
                    String xmiId = tokenizer.nextToken ();
                    Object obj = context.getReference (xmiId);
                    if (obj == null) {
                        // reference not known yet
                        if (isReference)
                            obj = new UnresolvedReference ();
                        else
                            obj = new UnresolvedReference (this);
                        context.registerUnresolvedRef (xmiId, (UnresolvedReference) obj);
                    }
                    list.add (obj);
                } // while
                if (isReference)
                    setReferenceValues ((Reference) attr, list);
                else
                    setAttributeValues (attr, list);
            } else {
                throw new DebugException ("type cannot be resolved: " + attr.getType ().getName ());
            }
        }

        // ReferencesCounter interface .........................................

        public void increaseUnresolvedRefs () {
            unresolvedRefsCounter++;
        }

        public void decreaseUnresolvedRefs () {
            unresolvedRefsCounter--;
            if (endReached && (unresolvedRefsCounter == 0)) {
                RefObject instance = createInstance ();
                if (unresRef != null)
                    unresRef.referenceResolved (instance);
            }
        }

    } // Instance

    // **************************************************************************
    // DataTypeElement
    // **************************************************************************
    public static class DataTypeElement extends XmiElement {

        // ==========================================
        // MOF 1.3 compatibility
        // ==========================================        
        /* 
         * Since DataType is an abstract class in MOF 1.4, its instances cannot be created,
         * thus reading of DataType instances is handled using this special class that
         * substitutes DataType by an equivalent legal type.
         *
         * How does it work: A complete tree composed of @link #DataTypeElement.Node
         * instances having  element as its root is created and
         * @link #XmiContext.resolveCorbaType is called on the tree.
         */
        
        // idicates if typeCode attribute is currently processed
        private boolean typeCodeReading = false;
        // stores current node when tree structure of XMI elements is being created
        private Node node = null;
        // possibly stores xmi.id of this DataType value
        private String xmiId;
        // id of related document
        private String docId;
        
        public DataTypeElement (XmiElement parent, XmiContext context, 
            String name, Attributes attrs) {
            super (parent, context);
            xmiId = attrs.getValue (context.XMI_ID);
            docId = context.getCurrentDocId ();
        }
        
        public XmiElement startSubElement (String qName, Attributes attrs) {
            if (!typeCodeReading) {
                if (qName.endsWith ("typeCode"))
                    typeCodeReading = true;
                return this;
            }
            if ((node == null) && (!qName.equals (XmiConstants.XMI_CORBA_TYPE_CODE)))
                throw new DebugException ("XMI.CorbaTypeCode element expected");
            node = new Node (qName, attrs, node);
            return this;
        }

        public XmiElement endElement (String qName) {
            if (typeCodeReading) {
                if (node.parent != null)
                    node = node.parent;
                else
                    typeCodeReading = false;
                return this;
            }
            if (!qName.endsWith ("DataType"))
                return this;
            // the end of DataTypeElement has been reached
            RefObject typeEquivalent = context.resolveCorbaType (node, false);
            if (xmiId != null)                
                context.putReference (docId, xmiId, typeEquivalent);
            if (!(parent instanceof XmiElement.Content))
                parent.receiveValue (typeEquivalent);
            return parent;
        }

        // stores one XMI element
        public static class Node {
            // parent node of this node, null in case of a root
            public Node parent;
            // list of all direct sub-nodes
            public List subnodes = new LinkedList ();
            // name of this XMI element
            public String name;
            // value of xmi.tcName attribute (if present, otherwise null)
            public String tcName;
            
            public Node (String name, Attributes attrs, Node parent) {
                this.parent = parent;
                this.name = name;
                tcName = attrs.getValue (XmiConstants.XMI_TCNAME);
                if (parent != null)
                    parent.addSubNode (this);                                
            }
            
            public void addSubNode (Node subNode) {
                subnodes.add (subNode);
            }
            
            public Node firstSubNode () {
                return (Node) subnodes.get (0);
            }
            
        } // Node
        
    } // DataTypeElement
        
    // **************************************************************************
    // ClassLevelAttribute
    // **************************************************************************
    public static class ClassLevelAttribute extends XmiElement {
        
        private Classifier type;
        
        public ClassLevelAttribute (XmiElement parent, XmiContext context, Attribute attr, 
            Attributes attrs) {
            super (parent, context);
            type = attr.getType ();
        }

        public XmiElement startSubElement (String qName, Attributes attrs) {
            return context.resolveValue (this, type, attrs);
        }
        
        public void receiveValue (Object value) {
            // post value to Content element
            parent.receiveValue (value);
        }

    } // ClassLevelAttribute

    // **************************************************************************
    // AssociationElement
    // **************************************************************************
    public static class AssociationElement extends XmiElement implements ReferencesCounter {
        
        // related association
        private Association assoc;
        // flag indicating if the first object of a link has been read
        private boolean oddNumberOfElementsRead = false;        
        // unresolved references counter
        private int counter = 0;
        // flag indicating if the whole association element (including sub-elements) has been read
        private boolean endReached = false;
        // list of read elements forming ends of links
        private List elements = new LinkedList ();
		
        public AssociationElement (XmiElement parent, XmiContext context, Association assoc) {
            super (parent, context);
            this.assoc = assoc;
	}

	public XmiElement startSubElement (String qName, Attributes attrs) {            
            return context.resolveInstanceOrReference (this, qName, attrs);            
	}

        public void receiveValue (Object obj) {
            if (obj instanceof UnresolvedReference) {
                ((UnresolvedReference) obj).setOwner (this);
            }
            elements.add (obj);
            oddNumberOfElementsRead = !oddNumberOfElementsRead;
        }
        
	public XmiElement endElement (String qName) {
            if (oddNumberOfElementsRead) {
                throw new DebugException ("Odd number of association ends serialized: " + assoc.getName ());
            }
            endReached = true;
            if (counter == 0)
                createLinks ();
            return parent;
	}

        private void createLinks () {
            RefAssociation refAssoc = ((RefPackage) context.findProxy (assoc)).refAssociation (assoc);
            Iterator iter = elements.iterator ();
            while (iter.hasNext ()) {
                Object firstObject = iter.next ();
                Object secondObject = iter.next ();
                if (firstObject instanceof UnresolvedReference)
                    firstObject = ((UnresolvedReference) firstObject).getValue ();
                if (secondObject instanceof UnresolvedReference)
                    secondObject = ((UnresolvedReference) secondObject).getValue ();
                if (!refAssoc.refLinkExists ((RefObject) firstObject, (RefObject) secondObject)) {
                    refAssoc.refAddLink ((RefObject) firstObject, (RefObject) secondObject);
                }
            } // while
        }
        
        // ReferencesCounter implementation .....................................
        
        public void increaseUnresolvedRefs() {
            counter++;
        }
        
        public void decreaseUnresolvedRefs() {
            counter--;
            if (endReached && (counter == 0))
                createLinks ();
        }
        
    } // AssociationElement

    // **************************************************************************
    // PrimitiveValue
    // **************************************************************************
    public static class PrimitiveValue extends XmiElement {

        // value of xmi.value attribute (if present - XMI 1.1 compatibility)
        private String xmiValue;
        // buffer for value of characters event
        private String valueAsText = "";
        // type of this primitive value
        private PrimitiveType type;
        // flag indicating if  value is read
        private boolean xmiAnyValueEndExpected = false;
        // flag that is set as true when  is encountered
        private boolean stopCharsReading = false;

        public PrimitiveValue (XmiElement parent, XmiContext context,
            PrimitiveType type, Attributes attrs) {
            super (parent, context);
            this.type = type;
            xmiValue = attrs.getValue (XmiConstants.XMI_VALUE);
        }

        /**
         * Constructor used to create shared instance of PrimitiveValue.
         * (shared instance is stored in context, see @link #XmiContext.PRIMITIVE_VALUE,
         * every time a new instance of PrimitiveValue is needed shared instance is taken
         * and inited by @link #init )
         */
        public PrimitiveValue (XmiContext context) {
            super (context);
        }

        public void init (XmiElement parent, PrimitiveType type, Attributes attrs) {
            this.parent = parent;
            this.type = type;
            xmiValue = attrs.getValue (XmiConstants.XMI_VALUE);
            valueAsText = "";
            xmiAnyValueEndExpected = false;
            stopCharsReading = false;
        }

        public void characters (char buf[], int offset, int len) {
            if (!stopCharsReading)
                valueAsText = valueAsText + new String (buf, offset, len);
        }

	public XmiElement startSubElement (String qName, Attributes attrs) {
            // =======================================
            // XMI 1.1 compatibility
            // =======================================
            if (!qName.equals (XmiConstants.XMI_ANY_TYPE))
                throw new DebugException ("Unexpected element: " + qName);
            xmiAnyValueEndExpected = true;
            valueAsText = "";
            return this;
	}
		
        public XmiElement endElement (String qName) {
            if (xmiAnyValueEndExpected) {
                stopCharsReading = true;
                xmiAnyValueEndExpected = false;
                return this;
            }
            // =======================================
            // XMI 1.1 compatibility
            // =======================================
            if (xmiValue != null)
                valueAsText = xmiValue;
            // =======================================
            parent.receiveValue
                (XmiContext.resolvePrimitiveValue ((PrimitiveType) type, valueAsText));
            return parent;
        }

    } // PrimitiveValue

    // **************************************************************************
    // EnumerationValue
    // **************************************************************************
    public static class EnumerationValue extends XmiElement {

        public EnumerationValue (XmiElement parent, XmiContext context,
            EnumerationType type, Attributes attributes) {
            super (parent, context);
            init (parent, type, attributes);
        }

        /**
         * Constructor used to create shared instance of EnumerationValue.
         * (shared instance is stored in context, see @link #XmiContext.ENUMERATION_VALUE,
         * every time a new instance of EnumerationValue is needed shared instance is taken
         * and inited by @link #init )
         */
        public EnumerationValue (XmiContext context) {
            super (context);
        }

        public void init (XmiElement parent, EnumerationType type,
            Attributes attrs) {
            this.parent = parent;
            String enumValue = attrs.getValue (XmiConstants.XMI_VALUE);
            if (enumValue == null)
                throw new DebugException ("xmi.value attribute expected in Enum element");
            parent.receiveValue
                (context.resolveEnumerationValue (type, enumValue));
        }

    } // EnumerationValue

    // **************************************************************************
    // StructureValue
    // **************************************************************************
    public static class StructureValue extends XmiElement
        implements ReferencesCounter {

        // type of this structure value
        private StructureType type;
        // structure's fields, i.e. a list of StructureFields
        private List fields;
        /* this iterator is used if oldFormat == true, it iterates trough all elements in
         * @link #fields
         * current field in iterator corresponds to currently read field value */
        private Iterator fieldsIterator;
        // field related to the currently read value
        private StructureField currentField;
        // storage for already read fields' values
        private HashMap fieldsValues = new HashMap ();
        // if true, the structure is serialized using  elements
        private boolean oldFormat;

        // number of currently unresolved references preventing creation of this structure
        private int counter = 0;
        // flag indicating if all sub-elements are already read, i.e. endElement () method has been called
        private boolean endReached = false;
        // holds an unresolved reference to this structure that should be set after the instance creation
        private UnresolvedReference unresRef = null;

        public StructureValue (XmiElement parent, XmiContext context,
            StructureType type, Attributes attrs, boolean oldFormat) {
            super (parent, context);
            this.type = type;
            this.oldFormat = oldFormat;

            int attrsLength = attrs.getLength ();
            fields = context.structureFields (type);
                        
            if (oldFormat)
                fieldsIterator = fields.iterator ();
            else {
                boolean isMultiplicityType = 
                    XmiContext.getQualifiedName (type).equals ("Model.MultiplicityType");
                String fieldName, fieldValue;
                for (int index = 0; index < attrsLength; index++) {
                    fieldName = attrs.getQName (index);
                    // MOF 1.3 COMPATIBILITY PATCH ===========
                    if (isMultiplicityType) {
                        if (fieldName.equals ("is_ordered"))
                            fieldName = "isOrdered";
                        else if (fieldName.equals ("is_unique"))
                            fieldName = "isUnique";
                    }
                    // =======================================
                    fieldValue = attrs.getValue (index);
                    resolveFieldValue (fieldName, fieldValue);
                } // for
            } // else
        }

        public XmiElement startSubElement (String qName, Attributes attrs) {
            if (oldFormat) { 
                // the structure value is serialized using  elements
                if (!fieldsIterator.hasNext ()) {                    
                    finishElement ();
                    return parent.startSubElement (qName, attrs);
                }
                currentField = (StructureField) fieldsIterator.next ();
            } else {
                currentField = (StructureField) context.resolveElementName (qName);
            }

            Classifier type = currentField.getType ();
            return context.resolveValue (this, type, attrs);
        }

        public void receiveValue (Object value) { 
            if (value instanceof List) {
                if (((List) value).size () != 1)
                    throw new DebugException ("Multi-valued structure field: " + currentField.getName ());
                value = ((List) value).get (0);
            }
            if (value instanceof UnresolvedReference)
                ((UnresolvedReference) value).setOwner (this);
            setFieldValue (currentField, value);
        }

        private void setFieldValue (TypedElement field, Object value) {
            if (fieldsValues.get (field) != null)
                throw new DebugException ("Multi-valued structure field: " + field.getName ());
            fieldsValues.put (field, value);
        }

        private void resolveFieldValue (String fieldName, String fieldValue) {
            StructureField field;
            try {
                field = (StructureField) type.lookupElement (fieldName);
            } catch (NameNotFoundException e) {
                throw new DebugException ("Field name cannot be resolved: " + type.getName () + "." + fieldName);
            }

            Classifier type = field.getType ();
            while (type instanceof AliasType)
                type = ((AliasType) type).getType ();
            Object value = null;

            if (type instanceof PrimitiveType) {
                value = XmiContext.resolvePrimitiveValue ((PrimitiveType) type, fieldValue);
            } else if (type instanceof EnumerationType) {
                value = context.resolveEnumerationValue ((EnumerationType) type, fieldValue);
            } else if (type instanceof MofClass) {
                String xmiId = fieldValue.trim ();
                Object obj = context.getReference (xmiId);
                if (obj == null) {
                    // reference not known yet
                    obj = new UnresolvedReference (this);
                    context.registerUnresolvedRef (xmiId, (UnresolvedReference) obj);
                    value = obj;
                }
            } else {
                throw new DebugException ("Field cannot be resolved, invalid type: " + type.getName ());
            }
            setFieldValue (field, value);
        }

        private RefStruct createInstance () {
            // obtain list of fields values
            List args = new LinkedList ();
            Iterator iter = fields.iterator ();
            while (iter.hasNext ()) {
                StructureField field = (StructureField) iter.next ();
                Object param = fieldsValues.get (field);
                if (param instanceof UnresolvedReference) {
                    param = ((UnresolvedReference) param).getValue ();
                }
                if (param instanceof CollectionWrapper) {
                    param = ((CollectionWrapper) param).getCollection ();
                }
                if (param == null)
                    param = XmiContext.defaultValue (field.getType ());
                args.add (param);
            } // while
            
            RefStruct struct = null;
            RefBaseObject proxy = context.findProxy (type);
            if (proxy == null)
                throw new DebugException ("Proxy not found: " + type.getName ());
            if (proxy instanceof RefClass)
                struct = ((RefClass) proxy).refCreateStruct (type, args);
            else
                struct = ((RefPackage) proxy).refCreateStruct (type, args);
            return struct;
        }

        private void finishElement () {
            endReached = true;
            if (counter == 0) {
                RefStruct instance = createInstance ();
                if (!(parent instanceof XmiElement.Content)) {
                    parent.receiveValue (instance);
                }
            } else {
                if (!(parent instanceof XmiElement.Content)) {
                    unresRef = new UnresolvedReference ();
                    parent.receiveValue (unresRef);
                }
            }
        }
        
        public XmiElement endElement (String qName) {
            if (oldFormat) {
                if (fieldsIterator.hasNext ())
                    throw new DebugException ("Structure value serialization not complete: " + type.getName ());
                finishElement ();
                // A PATCH - see comment in StructureValues.startSubElement () method.
                return parent.endElement (qName);
            } else {
                finishElement ();            
                return parent;
            }
        }

        // ReferencesCounter interface ..........................................

        public void increaseUnresolvedRefs () {
            counter++;
        }

        public void decreaseUnresolvedRefs () {
            counter--;
            if (endReached && (counter == 0)) {
                RefStruct instance = createInstance ();
                if (unresRef != null)
                    unresRef.referenceResolved (instance);
            }
        }

    } // StructureValue

    // **************************************************************************
    // CollectionValues
    // **************************************************************************
    public static class CollectionValues extends XmiElement {

        // storage of (multi-)value(s)
        private List values = new LinkedList ();
        // type of serialized structure value(s)
        private CollectionType type;        

        public CollectionValues (XmiElement parent, XmiContext context, CollectionType type) {
            super (parent, context);
            this.type = type;
        }

        public XmiElement startSubElement (String qName, Attributes attrs) {
            return new CollectionValue (this, context, type);
        }

        public void receiveValue (Object value) {
            values.add (value);
        }

        public XmiElement endElement (String qName) {
            parent.receiveValue (values);
            return parent;
        }                

    } // CollectionValues
    
    // **************************************************************************
    // CollectionValue
    // **************************************************************************
    public static class CollectionValue extends XmiElement implements ReferencesCounter {

        private Classifier type;        
        private List values = new LinkedList ();
        private String collName;
                
        private int counter = 0;
        private boolean endReached = false;
        private UnresolvedReference unresRef = null;
        
        public CollectionValue (XmiElement parent, XmiContext context, CollectionType collType) {
            super (parent, context);            
            type = collType.getType ();
            collName = collType.getName ();
        }
        
        public XmiElement startSubElement (String qName, Attributes attrs) {
            if (type instanceof MofClass) {
                return context.resolveInstanceOrReference (this, qName, attrs);
            }
            if (type instanceof CollectionType) {
                return new XmiElement.CollectionValue (this, context, (CollectionType) type);
            }
            return context.resolveValue (this, type, attrs);
        }

        public void receiveValue (Object value) {
            if (value instanceof List) {
                Iterator iter = ((List) value).iterator ();
                while (iter.hasNext ()) {
                    Object obj = iter.next ();
                    if (obj instanceof UnresolvedReference)
                        ((UnresolvedReference) obj).setOwner (this);
                    values.add (obj);
                }                
            } else {
                if (value instanceof UnresolvedReference)
                    ((UnresolvedReference) value).setOwner (this);
                values.add (value);
            }
        }

        public XmiElement endElement (String qName) {
            finishElement ();
            return parent;
        }

        private void finishElement () {
            endReached = true;
            if (counter == 0) {
                CollectionWrapper val = createValue ();
                if (!(parent instanceof XmiElement.Content)) {
                    parent.receiveValue (val);
                }
            } else {
                if (!(parent instanceof XmiElement.Content)) {
                    unresRef = new UnresolvedReference ();
                    parent.receiveValue (unresRef);
                }
            }
        }
        
        public CollectionWrapper createValue () {
            List list = new LinkedList ();
            Iterator iter = values.iterator ();
            while (iter.hasNext ()) {
                Object obj = iter.next ();
                if (obj instanceof UnresolvedReference) {
                    obj = ((UnresolvedReference) obj).getValue ();
                }
                if (obj instanceof CollectionWrapper) {
                    obj = ((CollectionWrapper) obj).getCollection ();
                }
                list.add (obj);
            }
            return new CollectionWrapper (list);
        }
        
        // ReferencesCounter interface ..........................................

        public void increaseUnresolvedRefs () {
            counter++;
        }

        public void decreaseUnresolvedRefs () {
            counter--;
            if (endReached && (counter == 0)) {
                CollectionWrapper value = createValue ();
                if (unresRef != null)
                    unresRef.referenceResolved (value);
            }
        }
        
    } // CollectionValue

    // **************************************************************************
    // ObjectValues
    // **************************************************************************
    public static class ObjectValues extends XmiElement {
        /* ObjectValues represents an element that consists of a sequence of 
         * ObjectValue elements.
         * If an attribute of Class type or a reference is encountered, 
         * this element is created to handle resolving its (multi-)value. 
         */

        // storage of (multi-)value(s)
        private List values = new LinkedList ();
        // stores correspondent value passed in the constructor, see the comment there
        private ReferencesCounter target;
        // XMI 2.0, if true then "nil=true" attribute is present
        private boolean isNull;

        /**
         * @param target an auxiliar parameter;
         * If target is not of null value and some of sub-values is
         * resolved as UnresolvedReference, target is set as an owner of this
         * @link #UnresolvedReference, 
         * see @link #UnresolvedReference.setOwner
         * (target is null iff value of reference is read - in this
         * case an owner cannot be set till it is created, see creation of 
         * @link #ReferenceHandler in #Instance.createInstance )
         */
        public ObjectValues (XmiElement parent, XmiContext context, ReferencesCounter target, boolean isNull) {
            super (parent, context);
            this.target = target;
            this.isNull = isNull;
        }

        public XmiElement startSubElement (String qName, Attributes attrs) {
            return context.resolveInstanceOrReference (this, qName, attrs);
        }

        public void receiveValue (Object value) {
            values.add (value);
            if ((value instanceof UnresolvedReference) && (target != null))
                ((UnresolvedReference) value).setOwner (target);
        }

        public XmiElement endElement (String qName) {
            parent.receiveValue (isNull ? null : values);
            return parent;
        }

    } // ObjectValues

    // **************************************************************************
    // StructureValues
    // **************************************************************************
    public static class StructureValues extends XmiElement {
        /* The purpose of this element is similar to the purpose of 
         * @link #ObjectValues
         * A sequence of structures is expected. */

        // storage of (multi-)value(s)
        private List values = new LinkedList ();
        // type of serialized structure value(s)
        private StructureType type;
        // if true, structure values are serialized using  elements
        private boolean oldFormat = false;

        public StructureValues (XmiElement parent, XmiContext context, StructureType type) {
            super (parent, context);
            this.type = type;
        }

        public XmiElement startSubElement (String qName, Attributes attrs) {
            if (qName.equals (XmiConstants.XMI_FIELD))
                oldFormat = true;
            StructureValue struct = new StructureValue (this, context, type, attrs, oldFormat);
            if (oldFormat) {
                /* A PATCH - we need to call startSubElement method, because items of the structure
                 * (that will be read) are encapsulated directly by StructureValues element
                 * (compare to "class style" serialization, where items are encapsulated by
                 * StructureValue element rather than StructureValues.
                 * There is pairwise call of this.endElement () in StructureValue.endElemnet ()
                 * method. */
                return struct.startSubElement (qName, attrs);
            }
            return struct;
        }

        public void receiveValue (Object value) {
            values.add (value);
        }

        public XmiElement endElement (String qName) {
            parent.receiveValue (values);
            return parent;
        }

    } // StructureValues

    // **************************************************************************
    // ReferenceValue
    // **************************************************************************
    public static class ReferenceValue extends XmiElement {
        /* This element reads an object value stored as reference,
         * example: 
         */

        public ReferenceValue (XmiElement parent, XmiContext context, String xmiId) {
            super (parent, context);
            init (parent, xmiId);
        }

        /**
         * Constructor used to create shared instance of ReferenceValue.
         * (shared instance is stored in context, see @link #XmiContext.REFERENCE_VALUE,
         * every time a new instance of ReferenceValue is needed shared instance is taken
         * and inited by @link #init )
         */
        public ReferenceValue (XmiContext context) {
            super (context); 
        }

        public void init (XmiElement parent, String xmiId) {
            this.parent = parent;
            Object obj = context.getReference (xmiId);
            if (obj == null) {
                obj = new UnresolvedReference ();
                context.registerUnresolvedRef (xmiId, (UnresolvedReference) obj);
            }
            parent.receiveValue (obj);
        }
        
        public void initExternal (XmiElement parent, String hRef) {
            this.parent = parent;
            Object obj;                    
            
            XMIReferenceProvider.XMIReference ref = context.toXMIReference (hRef);
            
            String docId = ref.getSystemId ();
            String xmiId = ref.getXmiId ();
            obj = context.getReference (docId, xmiId);
            if (obj == null) {
                obj = new UnresolvedReference ();
                context.registerUnresolvedExternalRef (docId, xmiId, (UnresolvedReference) obj);
            }
            parent.receiveValue (obj);
        }

    } // ReferenceValue

    // **************************************************************************
    // Dummy
    // **************************************************************************
    public static class Dummy extends XmiElement {

        private int level = 0;
        
        public Dummy (XmiElement parent, XmiContext context, String qName) {
            super (parent, null);
            context.unknownElementFound(qName);
        }
    
        public XmiElement startSubElement (String qName, Attributes attrs) {
            level++;
            return this;
        }

        public XmiElement endElement (String qName) {            
            if (level == 0) {
                return parent;
            } else {
                level--;
                return this;
            }
        }
        
    }
        
    // **************************************************************************
    // ReferencesCounter
    // **************************************************************************
    public static interface ReferencesCounter {
        /** 
         * Increments counter counting the number of unresolved references preventing
         * given object to be created.
         */
        public void increaseUnresolvedRefs ();

        /** Decrements counter. */
        public void decreaseUnresolvedRefs ();

    }
    
    // **************************************************************************
    // UnresolvedReference
    // **************************************************************************
    public static class UnresolvedReference {
        /**
         * Unresolved reference represents *link* to a not yet created instance.
         * It can be registered in context, see @link #XmiContext.registerUnresolvedRef,
         * then, if the desired instance is created and put into context by
         * @link #XmiContext.putRefrence, @link #referenceResolved is called -
         * it decreases owner's counter of unresolved references preventing instance
         * creation.
         */
        
        // when resolved, stores the resolved value
        private Object value = null;
        // owner of this unresolved reference
        private ReferencesCounter owner;

        /** 
         * Creates UnresolvedReference with no owner (owner can be set later by 
         * @link #setOwner ).
         */
        public UnresolvedReference () {
        }

        public UnresolvedReference (ReferencesCounter owner) {
            this.owner = owner;
            owner.increaseUnresolvedRefs ();
        }

        public void referenceResolved (Object value) {
            this.value = value;
            if (owner != null)
                owner.decreaseUnresolvedRefs ();
        }

        public Object getValue () {
            return value;
        }

        public void setOwner (ReferencesCounter owner) {
            this.owner = owner;
            owner.increaseUnresolvedRefs ();
        }

    } // UnresolvedReference

    // **************************************************************************
    // ReferenceHandler
    // **************************************************************************
    public static class ReferenceHandler implements ReferencesCounter {
        /**
         * ReferenceHandler is used to set value of a reference later, when all needed
         * references are resolved (it implements @link #ReferenceCounter, so it is
         * notified when the situation occurs).
         */
        
        // object that contains this reference
        private RefObject obj;
        // related reference
        private Reference ref;
        // reference values
        private List values = new LinkedList ();
        // counter of unresolved references
        private int unresolvedRefsCounter = 0;
        // context
        private XmiContext context;

        public ReferenceHandler (Reference ref, RefObject obj, List args, XmiContext context) {
            this.obj = obj;
            this.ref = ref;
            this.context = context;
            Iterator iter = args.iterator ();
            while (iter.hasNext ()) {
                Object value = iter.next ();
                if (value instanceof UnresolvedReference) {
                    Object val = ((UnresolvedReference) value).getValue ();
                    if (val != null)
                        value = val;
                    else
                        ((UnresolvedReference) value).setOwner (this);
                } // if
                values.add (value);
            } // while
            if (unresolvedRefsCounter == 0) {
                setReference ();
            }
        }

        public void increaseUnresolvedRefs () {
            unresolvedRefsCounter++;
        }

        public void decreaseUnresolvedRefs () {
            unresolvedRefsCounter--;
            if (unresolvedRefsCounter == 0)
                setReference ();
        }

        private void setReference () {
            Iterator iter;
            AssociationEnd end = ref.getReferencedEnd ();
            boolean isOrdered = end.getMultiplicity ().isOrdered ();
            Association association = (Association) end.getContainer ();

            /*
            boolean debug = "Assoc".equals (association.getName ());
            if (debug) {
                System.out.println("setReference: " + ref.getName () + " " + end.getName ());
            }
            */
            
            boolean isFirst = false;
            for (iter = association.getContents ().iterator (); iter.hasNext ();) {
                ModelElement me = (ModelElement) iter.next ();
                if (me instanceof AssociationEnd) {
                    isFirst = me.equals (end);
                    break;
                }
            }
            
            RefPackage refPackage = (RefPackage) context.findProxy (association);
            if (refPackage == null)
                throw new DebugException ("Proxy not found: " + association.getName ());
            RefAssociation refAssoc = refPackage.refAssociation (association);
            if (refAssoc == null)
                throw new DebugException ("Proxy not found: " + association.getName ());
            iter = values.iterator ();
            while (iter.hasNext ()) {
                Object value = iter.next ();
                RefObject endValue;
                if (value instanceof UnresolvedReference)
                    endValue = (RefObject) ((UnresolvedReference) value).getValue ();
                else
                    endValue = (RefObject) value;
                RefObject firstObj = isFirst ? endValue : obj;
                RefObject secondObj = isFirst ? obj : endValue;
                if ((firstObj != null) && (secondObj != null)) {
                    if (!refAssoc.refLinkExists (firstObj, secondObj)) {
                        refAssoc.refAddLink (firstObj, secondObj);

                        /*
                        if (debug)
                            System.out.println("add");
                        */
                        
                    } else if (isOrdered) {
                        refAssoc.refRemoveLink (firstObj, secondObj);
                        refAssoc.refAddLink (firstObj, secondObj);

                        /*
                        if (debug)
                            System.out.println("remove, add");
                        */
                        
                    } else {
                        /*
                        if (debug)
                            System.out.println("skip");
                        */
                        
                    }
                        
                }
            } // while

            /*
            if (debug) {
                iter = refAssoc.refAllLinks ().iterator ();
                while (iter.hasNext ()) {
                    RefAssociationLink link = (RefAssociationLink) iter.next ();
                    String n_1 = (String) link.refFirstEnd ().refGetValue ("name");
                    String n_2 = (String) link.refSecondEnd ().refGetValue ("name");
                    System.out.println("link: " + n_1 + " " + n_2);
                }
            }
            */
            
        }
        
    } // ReferenceHandler

    // **************************************************************************
    // CollectionWrapper
    // **************************************************************************
    public static class CollectionWrapper {
        
        private Collection coll;
        
        public CollectionWrapper (Collection coll) {
            this.coll = coll;
        }
        
        public Collection getCollection () {
            return coll;
        }
                
    } // CollectionWrapper
    
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.