Package nexj.core.meta.xml

Source Code of nexj.core.meta.xml.XMLMetadataLoader$ClassFixup

// Copyright 2010-2011 NexJ Systems Inc. This software is licensed under the terms of the Eclipse Public License 1.0
package nexj.core.meta.xml;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
import java.net.URLStreamHandler;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import nexj.core.admin.etl.DataLoader;
import nexj.core.integration.MessageParser;
import nexj.core.meta.Action;
import nexj.core.meta.Argument;
import nexj.core.meta.Attribute;
import nexj.core.meta.ClassAspect;
import nexj.core.meta.Component;
import nexj.core.meta.ComponentCollectionPropertyInitializer;
import nexj.core.meta.ComponentPropertyInitializer;
import nexj.core.meta.Documented;
import nexj.core.meta.Event;
import nexj.core.meta.ExternalLibrary;
import nexj.core.meta.Metaclass;
import nexj.core.meta.Metadata;
import nexj.core.meta.MetadataCompatibilityException;
import nexj.core.meta.MetadataCompoundValidationException;
import nexj.core.meta.MetadataException;
import nexj.core.meta.MetadataLoader;
import nexj.core.meta.MetadataLoaderDispatcher;
import nexj.core.meta.MetadataLookupException;
import nexj.core.meta.MetadataMarker;
import nexj.core.meta.MetadataURLHandler;
import nexj.core.meta.MetadataValidationException;
import nexj.core.meta.Primitive;
import nexj.core.meta.PrimitiveCollectionPropertyInitializer;
import nexj.core.meta.PrimitivePrivilege;
import nexj.core.meta.PrimitivePropertyInitializer;
import nexj.core.meta.Privilege;
import nexj.core.meta.PrivilegeGroup;
import nexj.core.meta.PropertyHolder;
import nexj.core.meta.PropertyInitializer;
import nexj.core.meta.Type;
import nexj.core.meta.TypeConversionException;
import nexj.core.meta.integration.Channel;
import nexj.core.meta.integration.ChannelType;
import nexj.core.meta.integration.CompositeMessagePart;
import nexj.core.meta.integration.CompositeMessagePartInstance;
import nexj.core.meta.integration.CompositeMessagePartRef;
import nexj.core.meta.integration.Format;
import nexj.core.meta.integration.Message;
import nexj.core.meta.integration.MessagePart;
import nexj.core.meta.integration.MessagePartMapping;
import nexj.core.meta.integration.MessageTable;
import nexj.core.meta.integration.PrimitiveMessagePart;
import nexj.core.meta.integration.Transformation;
import nexj.core.meta.integration.TransformationArgument;
import nexj.core.meta.integration.TransformationEndpoint;
import nexj.core.meta.integration.TransformationMapping;
import nexj.core.meta.integration.TransformationSource;
import nexj.core.meta.integration.XMLIntegrationMetadataLoader;
import nexj.core.meta.integration.XMLMessageMappingLoader;
import nexj.core.meta.integration.channel.queueing.ObjectDispatcherQueue;
import nexj.core.meta.integration.format.xml.XSDMessageImporter;
import nexj.core.meta.integration.service.Binding;
import nexj.core.meta.integration.service.Case;
import nexj.core.meta.integration.service.Dispatch;
import nexj.core.meta.integration.service.Interface;
import nexj.core.meta.integration.service.Jump;
import nexj.core.meta.integration.service.Persist;
import nexj.core.meta.integration.service.Send;
import nexj.core.meta.integration.service.SendReceive;
import nexj.core.meta.integration.service.Service;
import nexj.core.meta.integration.service.Sync;
import nexj.core.meta.integration.service.Transform;
import nexj.core.meta.persistence.AttributeMapping;
import nexj.core.meta.persistence.ClassMapping;
import nexj.core.meta.persistence.DataSource;
import nexj.core.meta.persistence.DataSourceAdapter;
import nexj.core.meta.persistence.DataSourceType;
import nexj.core.meta.persistence.PersistenceMapping;
import nexj.core.meta.persistence.XMLPersistenceMetadataLoader;
import nexj.core.meta.workflow.Activity;
import nexj.core.meta.workflow.Assignment;
import nexj.core.meta.workflow.AutoCompletion;
import nexj.core.meta.workflow.Block;
import nexj.core.meta.workflow.Branch;
import nexj.core.meta.workflow.Catch;
import nexj.core.meta.workflow.Completion;
import nexj.core.meta.workflow.Concurrent;
import nexj.core.meta.workflow.Decision;
import nexj.core.meta.workflow.Flow;
import nexj.core.meta.workflow.FlowMacro;
import nexj.core.meta.workflow.FlowMacroScript;
import nexj.core.meta.workflow.Fork;
import nexj.core.meta.workflow.Goto;
import nexj.core.meta.workflow.Handler;
import nexj.core.meta.workflow.Join;
import nexj.core.meta.workflow.ManualCompletion;
import nexj.core.meta.workflow.Script;
import nexj.core.meta.workflow.Scripted;
import nexj.core.meta.workflow.Semaphore;
import nexj.core.meta.workflow.Step;
import nexj.core.meta.workflow.Timeout;
import nexj.core.meta.workflow.Trigger;
import nexj.core.meta.workflow.TryCatch;
import nexj.core.meta.workflow.Variable;
import nexj.core.meta.workflow.Wait;
import nexj.core.meta.workflow.Workflow;
import nexj.core.meta.xml.XMLMetadataHelper.CharacterStreamHandler;
import nexj.core.meta.xml.XMLMetadataHelper.ContextFixup;
import nexj.core.meta.xml.XMLMetadataHelper.Fixup;
import nexj.core.meta.xml.XMLMetadataHelper.ResourceHandler;
import nexj.core.meta.xml.XMLMetadataHelper.ResourceNameHandler;
import nexj.core.runtime.Context;
import nexj.core.runtime.InvocationContext;
import nexj.core.runtime.ThreadContextHolder;
import nexj.core.scripting.GlobalEnvironment;
import nexj.core.scripting.Intrinsic;
import nexj.core.scripting.Machine;
import nexj.core.scripting.Macro;
import nexj.core.scripting.PCodeFunction;
import nexj.core.scripting.Pair;
import nexj.core.scripting.SchemeParser;
import nexj.core.scripting.Symbol;
import nexj.core.util.CertificateUtil;
import nexj.core.util.GenericException;
import nexj.core.util.HTTP;
import nexj.core.util.HashHolder;
import nexj.core.util.HashTab;
import nexj.core.util.IOUtil;
import nexj.core.util.IdentityHashTab;
import nexj.core.util.J2EEUtil;
import nexj.core.util.Logger;
import nexj.core.util.Lookup;
import nexj.core.util.ObjUtil;
import nexj.core.util.ProgressListener;
import nexj.core.util.ProgressProxy;
import nexj.core.util.PropertyUtil;
import nexj.core.util.StringTable;
import nexj.core.util.StringUtil;
import nexj.core.util.SysUtil;
import nexj.core.util.TZ;
import nexj.core.util.TextPositionReader;
import nexj.core.util.UncheckedException;
import nexj.core.util.Undefined;
import nexj.core.util.XMLUtil;
import nexj.core.util.XMLUtil.ElementHandler;
import nexj.core.util.auth.SimplePrincipal;
import nexj.core.util.cipher.CharacterStreamCipherDispatcher;
import nexj.core.version.Version;


/**
* Metadata loader using an XML serialization format.
*/
public class XMLMetadataLoader implements MetadataLoader
{
   // constants

   /**
    * The base URL property name.
    */
   public final static String BASE_URL_PROPERTY = "meta.base.url";

   /**
    * The compatible URL property name.
    */
   public final static String COMPATIBLE_URL_PROPERTY = "meta.compatible.url";

   /**
    * Connections URL property.
    */
   public final static String CONNECTIONS_URL_PROPERTY = "meta.connections.url";

   /**
    * Array with length of 1, containing the String class object.
    */
   protected final static Class[] STRING_CLASS_ARRAY = new Class[]{String.class};

   /**
    * "next" Symbol for use with iterators.
    */
   protected final static Symbol NEXT = Symbol.define("next");

   /**
    * "hasNext" Symbol for use with iterators.
    */
   protected final static Symbol HAS_NEXT = Symbol.define("hasNext");

   /**
    * Loading stage: no metadata loaded.
    */
   public final static byte STAGE_START = 0;

   /**
    * Loading stage: environment metadata loaded.
    */
   public final static byte STAGE_LOADED_ENVIRONMENT = 1;

   /**
    * Loading stage: system components loaded.
    */
   public final static byte STAGE_LOADED_COMPONENTS = 2;

   /**
    * Loading stage: executing environment fixups.
    */
   public final static byte STAGE_LOAD_ENVIRONMENT_FIXUPS = 3;

   /**
    * Loading stage: finished.
    */
   public final static byte STAGE_FINISHED = 4;

   // attributes

   /**
    * The environment or server metadata URL.
    */
   protected String m_sConfigURL;

   /**
    * The environment container, one of the J2EEUtil constants.
    */
   protected int m_nContainer;

   /**
    * The metadata loading stage, one of the STAGE_* constants;
    */
   protected byte m_nStage;

   /**
    * The environment-only flag.
    */
   protected boolean m_bEnvironmentOnly;

   /**
    * The runtime exclusion flag.
    */
   protected boolean m_bRuntimeExcluded;

   /**
    * The integration exclusion flag.
    */
   protected boolean m_bIntegrationExcluded;

   /**
    * The validation-only flag.
    */
   protected boolean m_bValidatedOnly;

   /**
    * The documentation loading flag.
    */
   protected boolean m_bDocumented;

   /**
    * The flag for leaving the metadata writable.
    */
   protected boolean m_bWritable;

   // associations

   /**
    * The password decryptor.
    */
   protected CharacterStreamCipherDispatcher m_decryptionDispatcher;

   /**
    * The metadata helper.
    */
   protected XMLMetadataHelper m_helper;

   /**
    * The metadata that is being loaded.
    */
   protected XMLMetadata m_metadata;

   /**
    * The VM for script loading.
    */
   protected Machine m_machine;

   /**
    * The environment properties.
    */
   protected Properties m_properties;

   /**
    * The enumeration name to value set map: Set[String].
    */
   protected Lookup m_enumerationValueMap; // of type Set[String].

   /**
    * The topologically sorted class aspect array.
    */
   protected ClassAspect[] m_classAspectArray;

   /**
    * The class inheritance check collection.
    */
   protected List m_inheritanceCheckList; // of type Fixup

   /**
    * The class inheritance fixup collection.
    */
   protected List m_inheritanceFixupList; // of type Fixup

   /**
    * The class inheritance additional first pass fixup collection.
    */
   protected List m_inheritance1stPassFixupList; // of type Fixup

   /**
    * The 2nd pass class inheritance fixup collection.
    */
   protected List m_inheritance2ndPassFixupList; // of type Fixup

   /**
    * The class inheritance fourth pass fixup collection.
    */
   protected List m_inheritance4thPassFixupList; // of type Fixup

   /**
    * The attribute generation fixup collection.
    */
   protected List m_attributeGenerationList; // of type Fixup

   /**
    * The attribute fixup collection.
    */
   protected List m_attributeFixupList; // of type Fixup

   /**
    * The attribute expression resolution collection.
    */
   protected List m_attributeResolutionList; // of type Fixup

   /**
    * The attribute dependency resolution collection.
    */
   protected List m_attributeDependencyList; // of type Fixup

   /**
    * The attribute dependency set.
    */
   protected Set m_attributeDependencySet; // of type Attribute

   /**
    * The action fixup collection.
    */
   protected List m_actionFixupList; // of type Fixup

   /**
    * The class second pass collection.
    */
   protected List m_class2ndPassList; // of type Fixup

   /**
    * The flow element fixup collection.
    */
   protected List m_flowFixupList; // of type Fixup.

   /**
    * The pre-inheritance message fixup collection. To be executed before
    * resolution of message inheritance.
    */
   protected List m_preInheritanceMessageFixupList; // of type Fixup

   /**
    * The post-inheritance message fixup collection. To be executed after
    * resolution of message inheritance.
    */
   protected List m_postInheritanceMessageFixupList; // of type Fixup

   /**
    * The transformation fixup list.
    */
   protected List m_transformationFixupList; // of type Fixup

   /**
    * The post-inheritance transformation fixup list. To be executed after
    * resolution of transformation inheritance.
    */
   protected List m_postInheritanceTransformationFixupList; // of type Fixup

   /**
    * The persistence mapping loading collection.
    */
   protected List m_persistenceMappingLoadList; // of type Fixup

   /**
    * The Document used for reparenting fixup Elements to conserve memory.
    */
   protected Document m_tmpDocument;

   /**
    * The persistence mapping fixup collection.
    */
   protected List m_persistenceMappingFixupList; // of type Fixup

   /**
    * The persistence mapping generation collection.
    */
   protected List m_persistenceMappingGenerationList; // of type Fixup

   /**
    * The IO fixup collection.
    */
   protected List m_ioFixupList; // of type Fixup

   /**
    * The environment fixup collection.
    */
   protected List m_environmentFixupList; // of type Fixup

   /**
    * The privilege fixup collection.
    */
   protected List m_privilegeFixupList; // of type Fixup

   /**
    * The component fixup collection.
    */
   protected List m_componentFixupList; // of type Fixup

   /**
    * The singleton component fixup collection.
    */
   protected List m_singletonFixupList; // of type Fixup

   /**
    * The system dispatcher channel.
    */
   protected ObjectDispatcherQueue m_dispatcherChannel;

   /**
    * The Services Oriented Architecture metadata loader.
    */
   protected XMLSOAMetadataLoader m_soaLoader;

   /**
    * The date parse format.
    */
   protected final static SimpleDateFormat s_dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);

   /**
    * The time parse format.
    */
   protected final static SimpleDateFormat s_timeFormat = new SimpleDateFormat("HH:mm:ss", Locale.ENGLISH);

   /**
    * The property parsers.
    */
   protected final static Lookup s_propertyParserMap = new HashTab(32); // of type PropertyParser[Class]

   /**
    * Names of intrinsic types that may be mapped to 'any'.
    */
   protected final static Set s_unsupportedIntrinsicSet = new HashHolder();

   static
   {
      s_dateFormat.setTimeZone(TZ.UTC);
      s_timeFormat.setTimeZone(TZ.UTC);

      s_propertyParserMap.put(Byte.class, new PrimitivePropertyParser(Byte.class, true));
      s_propertyParserMap.put(Byte.TYPE, new PrimitivePropertyParser(Byte.class, false));
      s_propertyParserMap.put(Short.class, new PrimitivePropertyParser(Short.class, true));
      s_propertyParserMap.put(Short.TYPE, new PrimitivePropertyParser(Short.class, false));
      s_propertyParserMap.put(Integer.class, new PrimitivePropertyParser(Integer.class, true));
      s_propertyParserMap.put(Integer.TYPE, new PrimitivePropertyParser(Integer.class, false));
      s_propertyParserMap.put(Long.class, new PrimitivePropertyParser(Long.class, true));
      s_propertyParserMap.put(Long.TYPE, new PrimitivePropertyParser(Long.class, false));
      s_propertyParserMap.put(Float.class, new PrimitivePropertyParser(Float.class, true));
      s_propertyParserMap.put(Float.TYPE, new PrimitivePropertyParser(Float.class, false));
      s_propertyParserMap.put(Double.class, new PrimitivePropertyParser(Double.class, true));
      s_propertyParserMap.put(Double.TYPE, new PrimitivePropertyParser(Double.class, false));
      s_propertyParserMap.put(BigDecimal.class, new PrimitivePropertyParser(BigDecimal.class, true));
      s_propertyParserMap.put(BigInteger.class, new PrimitivePropertyParser(BigInteger.class, true));

      s_propertyParserMap.put(Character.class, new PropertyParser()
      {
         public void parse(String sValue, PropertyInitializer initializer, XMLMetadataLoader loader)
         {
            if (StringUtil.isEmpty(sValue))
            {
               initializer.initializeValue(null);
            }
            else if (sValue.length() == 1)
            {
               initializer.initializeValue(Primitive.createCharacter(sValue.charAt(0)));
            }
            else
            {
               throw new MetadataException("err.meta.charValue", new Object[]{sValue});
            }
         }
      });

      s_propertyParserMap.put(Character.TYPE, new PropertyParser()
      {
         public void parse(String sValue, PropertyInitializer initializer, XMLMetadataLoader loader)
         {
            if (sValue != null && sValue.length() == 1)
            {
               initializer.initializeValue(Primitive.createCharacter(sValue.charAt(0)));
            }
            else
            {
               throw new MetadataException("err.meta.charValue", new Object[]{sValue});
            }
         }
      });

      s_propertyParserMap.put(String.class, new PropertyParser()
      {
         public void parse(String sValue, PropertyInitializer initializer, XMLMetadataLoader loader)
         {
            initializer.initializeValue(sValue);
         }
      });

      s_propertyParserMap.put(Boolean.class, new PropertyParser()
      {
         public void parse(String sValue, PropertyInitializer initializer, XMLMetadataLoader loader)
         {
            if (StringUtil.isEmpty(sValue))
            {
               initializer.initializeValue(null);
            }
            else if (sValue.equals("1") || sValue.equalsIgnoreCase("true") || sValue.equalsIgnoreCase("yes"))
            {
               initializer.initializeValue(Boolean.TRUE);
            }
            else if (sValue.equals("0") || sValue.equalsIgnoreCase("false") || sValue.equalsIgnoreCase("no"))
            {
               initializer.initializeValue(Boolean.FALSE);
            }
            else
            {
               throw new MetadataException("err.meta.booleanValue", new Object[]{sValue});
            }
         }
      });

      s_propertyParserMap.put(Boolean.TYPE, new PropertyParser()
      {
         public void parse(String sValue, PropertyInitializer initializer, XMLMetadataLoader loader)
         {
            if (StringUtil.isEmpty(sValue))
            {
               initializer.initializeValue(Boolean.FALSE);
            }
            else if (sValue.equals("1") || sValue.equalsIgnoreCase("true") || sValue.equalsIgnoreCase("yes"))
            {
               initializer.initializeValue(Boolean.TRUE);
            }
            else if (sValue.equals("0") || sValue.equalsIgnoreCase("false") || sValue.equalsIgnoreCase("no"))
            {
               initializer.initializeValue(Boolean.FALSE);
            }
            else
            {
               throw new MetadataException("err.meta.booleanValue", new Object[]{sValue});
            }
         }
      });

      s_propertyParserMap.put(java.util.Date.class, new PropertyParser()
      {
         public void parse(String sValue, PropertyInitializer initializer, XMLMetadataLoader loader)
         {
            if (StringUtil.isEmpty(sValue))
            {
               initializer.initializeValue(null);
            }
            else
            {
               ParsePosition pos = new ParsePosition(0);
               java.util.Date dt;

               synchronized (s_dateFormat)
               {
                  dt = s_dateFormat.parse(sValue, pos);
               }

               if (pos.getErrorIndex() >= 0 || pos.getIndex() < sValue.length())
               {
                  throw new MetadataException("err.meta.dateFormat", new Object[]{sValue});
               }

               initializer.initializeValue(dt);
            }
         }
      });

      s_propertyParserMap.put(Date.class, new PropertyParser()
      {
         public void parse(String sValue, PropertyInitializer initializer, XMLMetadataLoader loader)
         {
            if (StringUtil.isEmpty(sValue))
            {
               initializer.initializeValue(null);
            }
            else
            {
               ParsePosition pos = new ParsePosition(0);
               java.util.Date dt;

               synchronized (s_dateFormat)
               {
                  dt = s_dateFormat.parse(sValue, pos);
               }

               if (pos.getErrorIndex() >= 0 || pos.getIndex() < sValue.length())
               {
                  throw new MetadataException("err.meta.dateFormat", new Object[]{sValue});
               }

               initializer.initializeValue(new Date(dt.getTime()));
            }
         }
      });

      s_propertyParserMap.put(Time.class, new PropertyParser()
      {
         public void parse(String sValue, PropertyInitializer initializer, XMLMetadataLoader loader)
         {
            if (StringUtil.isEmpty(sValue))
            {
               initializer.initializeValue(null);
            }
            else
            {
               ParsePosition pos = new ParsePosition(0);
               java.util.Date dt;

               synchronized (s_timeFormat)
               {
                  dt = s_timeFormat.parse(sValue, pos);
               }

               if (pos.getErrorIndex() >= 0 || pos.getIndex() < sValue.length())
               {
                  throw new MetadataException("err.meta.timeFormat", new Object[]{sValue});
               }

               initializer.initializeValue(new Time(dt.getTime()));
            }
         }
      });

      s_propertyParserMap.put(Timestamp.class, new PropertyParser()
      {
         public void parse(String sValue, PropertyInitializer initializer, XMLMetadataLoader loader)
         {
            if (StringUtil.isEmpty(sValue))
            {
               initializer.initializeValue(null);
            }
            else
            {
               try
               {
                  initializer.initializeValue(Primitive.toTimestamp(sValue));
               }
               catch (TypeConversionException e)
               {
                  throw new MetadataException("err.meta.timestampFormat", new Object[]{sValue});
               }
            }
         }
      });

      s_propertyParserMap.put(Primitive.class, new PropertyParser()
      {
         public void parse(String sValue, PropertyInitializer initializer, XMLMetadataLoader loader)
         {
            if (StringUtil.isEmpty(sValue))
            {
               initializer.initializeValue(null);
            }
            else
            {
               initializer.initializeValue(Primitive.parse(sValue));
            }
         }
      });

      s_propertyParserMap.put(Component.class, new PropertyParser()
      {
         public void parse(final String sValue, final PropertyInitializer initializer, final XMLMetadataLoader loader)
         {
            if (StringUtil.isEmpty(sValue))
            {
               initializer.initializeValue(null);
            }
            else
            {
               loader.m_componentFixupList.add(new ContextFixup(loader.getHelper())
               {
                  public void fixup()
                  {
                     initializer.initializeValue(loader.getMetadata().getComponent(sValue));
                  }
               });
            }
         }
      });

      s_propertyParserMap.put(Metaclass.class, new PropertyParser()
      {
         public void parse(final String sValue, final PropertyInitializer initializer, final XMLMetadataLoader loader)
         {
            if (StringUtil.isEmpty(sValue))
            {
               initializer.initializeValue(null);
            }
            else
            {
               loader.m_componentFixupList.add(new ContextFixup(loader.getHelper())
               {
                  public void fixup()
                  {
                     initializer.initializeValue(loader.getMetadata().getMetaclass(sValue));
                  }
               });
            }
         }
      });

      s_propertyParserMap.put(Type.class, new PropertyParser()
      {
         public void parse(final String sValue, final PropertyInitializer initializer, final XMLMetadataLoader loader)
         {
            if (StringUtil.isEmpty(sValue))
            {
               initializer.initializeValue(null);
            }
            else
            {
               Type type = Primitive.find(sValue);

               if (type != null)
               {
                  initializer.initializeValue(type);
               }
               else
               {
                  loader.m_componentFixupList.add(new ContextFixup(loader.getHelper())
                  {
                     public void fixup()
                     {
                        initializer.initializeValue(loader.getMetadata().getMetaclass(sValue));
                     }
                  });
               }
            }
         }
      });

      s_propertyParserMap.put(Metadata.class, new PropertyParser()
      {
         public void parse(final String sValue, final PropertyInitializer initializer, final XMLMetadataLoader loader)
         {
            if (sValue != null)
            {
               throw new MetadataException("err.meta.metadataProperty");
            }

            initializer.initializeValue(loader.getMetadata());
         }
      });

      s_propertyParserMap.put(Properties.class, new PropertyParser()
      {
         public void parse(String sValue, PropertyInitializer initializer, XMLMetadataLoader loader)
         {
            try
            {
               initializer.initializeValue(PropertyUtil.fromString((sValue != null) ? sValue : ""));
            }
            catch (Exception e)
            {
               throw new MetadataException("err.meta.propertiesFormat", new Object[]{sValue});
            }
         }
      });

      s_unsupportedIntrinsicSet.add("lambda");
      s_unsupportedIntrinsicSet.add("list");
      s_unsupportedIntrinsicSet.add("pair");
      s_unsupportedIntrinsicSet.add("symbol");
      s_unsupportedIntrinsicSet.add("port");
      s_unsupportedIntrinsicSet.add("vector");
      s_unsupportedIntrinsicSet.add("bytevector");
      s_unsupportedIntrinsicSet.add("lookup");
      s_unsupportedIntrinsicSet.add("map");
      s_unsupportedIntrinsicSet.add("holder");
      s_unsupportedIntrinsicSet.add("set");
      s_unsupportedIntrinsicSet.add("environment");
      s_unsupportedIntrinsicSet.add("invocation-context");
      s_unsupportedIntrinsicSet.add("class");
      s_unsupportedIntrinsicSet.add("oid");
      s_unsupportedIntrinsicSet.add("iterator");
      s_unsupportedIntrinsicSet.add("message");
   }

   /**
    * The logger.
    */
   protected final static Logger s_logger = Logger.getLogger(XMLMetadataLoader.class);

   // operations

   /**
    * Decrypts the password.
    * @param sEncrypted The password to decrypt.
    * @return The decrypted password.
    */
   public String decryptPassword(String sEncrypted)
   {
      if (sEncrypted == null)
      {
         return null;
      }

      if (!m_metadata.isEncrypted())
      {
         // Extract the encryption scheme name
         int nColon = sEncrypted.indexOf(':');

         if (nColon >= 0)
         {
            m_helper.addEncryptionScheme(sEncrypted.substring(0, nColon));
         }

         return m_decryptionDispatcher.decrypt(sEncrypted);
      }

      return sEncrypted;
   }

   /**
    * @see nexj.core.meta.MetadataLoader#load(java.lang.String, java.util.Properties, int, nexj.core.util.ProgressListener, nexj.core.meta.MetadataLoader.MetadataLoaderCallback)
    */
   public Metadata load(String sURI, Properties properties, int nFlags, ProgressListener progress)
   {
      m_nStage = STAGE_START;

      if (properties == null)
      {
         properties = new Properties();
      }

      URLStreamHandler handler = null;
      String sURLHandlerName = properties.getProperty(MetadataLoader.METADATA_URL_HANDLER_PROPERTY);

      if (!StringUtil.isEmpty(sURLHandlerName))
      {
         try
         {
            Class handlerClass = Class.forName(sURLHandlerName);

            handler = (URLStreamHandler)handlerClass.getConstructor(null).newInstance(null);

            if (handler instanceof MetadataURLHandler)
            {
               MetadataURLHandler urlHandler = (MetadataURLHandler)handler;
               Metadata metadata = urlHandler.initialize(sURI, properties);

               if (metadata != null)
               {
                  return metadata;
               }
            }
         }
         catch (Throwable t)
         {
            throw new MetadataException("err.meta.handlerCreate", new Object[]{sURLHandlerName}, t);
         }
      }

      URL rootURL = XMLMetadataHelper.getURL(sURI, true, handler);

      if (s_logger.isInfoEnabled())
      {
         s_logger.info("Metadata URL: \"" + rootURL + '"');
      }

      URL baseURL = null;
      String sBaseURI = properties.getProperty(BASE_URL_PROPERTY);

      if (!StringUtil.isEmpty(sBaseURI) && handler == null)
      {
         baseURL = XMLMetadataHelper.getURL(sBaseURI, true, handler);

         if (s_logger.isInfoEnabled())
         {
            s_logger.info("Base metadata URL: \"" + baseURL + '"');
         }
      }

      String sCompatibleURI = properties.getProperty(COMPATIBLE_URL_PROPERTY);

      URL configURL = null;

      try
      {
         configURL = XMLMetadataHelper.getURL(properties.getProperty(
            SysUtil.CONFIG_PROPERTY, SysUtil.DEFAULT_CONFIG_URL), false, handler);
         m_sConfigURL = configURL.toString();
      }
      catch (MetadataException e)
      {
         throw new MetadataException("err.meta.configURL",
            new Object[]{properties.getProperty(SysUtil.CONFIG_PROPERTY)}, e);
      }

      if (s_logger.isInfoEnabled())
      {
         s_logger.info("Configuration URL: \"" + m_sConfigURL + '"');
      }

      if (!StringUtil.parseBoolean(properties.getProperty(METADATA_INTEGRATION_PROPERTY, "true")))
      {
         nFlags |= INTEGRATION_EXCLUDED;
      }

      if (StringUtil.parseBoolean(properties.getProperty(METADATA_DOCUMENTATION_PROPERTY, "false")))
      {
         nFlags |= DOCUMENTATION_INCLUDED;
      }

      if (StringUtil.parseBoolean(properties.getProperty(METADATA_WARNING_PROPERTY, "false")))
      {
         nFlags |= WARNING;
      }

      boolean bDataSourceOnly = (nFlags & DATASOURCE_ONLY) != 0;

      m_properties = new Properties(properties);

      // Initialize the member variables for loading

      m_bEnvironmentOnly = ((nFlags & ENVIRONMENT_ONLY) != 0);
      m_bRuntimeExcluded = ((nFlags & RUNTIME_EXCLUDED) != 0);
      m_bIntegrationExcluded = ((nFlags & INTEGRATION_EXCLUDED) != 0);
      m_bValidatedOnly = ((nFlags & VALIDATED_ONLY) != 0);
      m_bDocumented = ((nFlags & DOCUMENTATION_INCLUDED) != 0);
      m_bWritable = ((nFlags & WRITABLE) != 0);
      m_helper = new XMLMetadataHelper(rootURL, baseURL, m_properties, null, handler);
      m_metadata = createXMLMetadata(sURI, rootURL, baseURL, (m_bValidatedOnly) ? m_helper : null);
      m_metadata.setConfigurationURL(configURL);
      m_machine = new Machine(m_metadata.getGlobalEnvironment(),
         new InvocationContext(m_metadata, m_metadata.getGlobalEnvironment()));
      m_enumerationValueMap = new HashTab(16);
      m_inheritanceCheckList = new ArrayList();
      m_inheritanceFixupList = new ArrayList();
      m_inheritance1stPassFixupList = new ArrayList();
      m_inheritance2ndPassFixupList = new ArrayList(1);
      m_inheritance4thPassFixupList = new ArrayList(1);
      m_attributeGenerationList = new ArrayList();
      m_attributeFixupList = new ArrayList();
      m_attributeResolutionList = new ArrayList(1);
      m_actionFixupList = new ArrayList();
      m_persistenceMappingLoadList = new ArrayList();
      m_persistenceMappingFixupList = new ArrayList();
      m_persistenceMappingGenerationList = new ArrayList();
      m_attributeDependencyList = new ArrayList(1);
      m_attributeDependencySet = new HashHolder();
      m_class2ndPassList = new ArrayList(1);
      m_ioFixupList = new ArrayList();
      m_environmentFixupList = new ArrayList();
      m_privilegeFixupList = new ArrayList();
      m_preInheritanceMessageFixupList = new ArrayList();
      m_postInheritanceMessageFixupList = new ArrayList();
      m_transformationFixupList = new ArrayList();
      m_postInheritanceTransformationFixupList = new ArrayList();
      m_componentFixupList = new ArrayList();
      m_singletonFixupList = new ArrayList();
      m_tmpDocument = XMLUtil.parse(new StringReader("<TmpDocument/>"));

      m_metadata.setEncrypted((nFlags & ENCRYPTED) != 0);
      m_metadata.setEnvironmentOnly(m_bEnvironmentOnly);

      if ((nFlags & WARNING) != 0)
      {
         m_helper.setWarnings(new GenericException(null)
         {
            private final static long serialVersionUID = -2984934222963813773L;

            public void addException(Throwable e)
            {
               e.setStackTrace(ObjUtil.EMPTY_STACK_TRACE);
               super.addException(e);
            }
         });
      }

      // Prepare the progress listener
      ProgressProxy progressProxy = new ProgressProxy(progress);

      if (progress != null)
      {
         progress = progressProxy;
      }

      // Load the repository descriptors and process the elements in them

      if (progress != null)
      {
         progress.progress("info.meta.loadingDescriptors", null, 0);
      }

      Element baseDescElement = m_helper.getDescriptorElement(false);
      Element rootDescElement = m_helper.getDescriptorElement(true);

      progressProxy.setRange(0.01, 0.02);

      m_metadata.setListing(m_helper.getListing(new XMLMetadataHelper.MixinHandler()
         {
            /**
             * Metadata repository information, indexed by namespace.
             */
            private Lookup m_metadataMap = new HashTab();

            private String getMetadataName(String sNamespace)
            {
               MetadataInfo info = ((MetadataInfo)m_metadataMap.get(sNamespace));

               return (info == null || info.metadata == null) ? sNamespace : info.metadata.getName();
            }

            public void handleRepository(XMLMixin mixin)
            {
               String sNamespace = mixin.getNamespace();
               XMLMetadataHelper helper = mixin.getHelper();
               MetadataInfo info = (MetadataInfo)m_metadataMap.get(sNamespace);

               if (info == null)
               {
                  info = new MetadataInfo();
               }

               // get the metadata instance
               if (m_metadataMap.size() == 0)
               {
                  info.metadata = m_metadata;
               }
               else
               {
                  URL rootURL = helper.getRootURL();

                  info.metadata = createXMLMetadata(rootURL.getPath(), rootURL, helper.getBaseURL(), helper);
               }

               // load the version
               loadVersion(info.metadata, helper);
               m_metadataMap.put(sNamespace, info);

               // validate loaded mixin
               if (m_metadata != info.metadata)
               {
                  // Adopt the highest coreVersion available from any of the repositories.
                  if (m_metadata.getCoreVersion() == null || (info.metadata.getCoreVersion() != null &&
                     StringUtil.compareVersionRanges(m_metadata.getCoreVersion(), info.metadata.getCoreVersion()) < 0))
                  {
                     m_metadata.setCoreVersion(info.metadata.getCoreVersion());
                  }

                  if (info.ref == null)
                  {
                     throw new MetadataValidationException("err.meta.mixinNamespace", new Object[]{info.metadata.getName(), sNamespace});
                  }

                  String sVersion = info.ref.getVersion();
                  String sChecksum = info.ref.getChecksum();

                  // validate that repository matches mixin declaration
                  if ((sVersion != null && !sVersion.equals(info.metadata.getVersion()))
                     || (sChecksum != null && !sChecksum.equals(info.metadata.getChecksum())))
                  {
                     throw new MetadataValidationException("err.meta.mixinLinkVersion", new Object[]{
                        info.metadata.getName(),
                        info.metadata.getVersion(),
                        info.metadata.getChecksum(),
                        sVersion,
                        sChecksum,
                        getMetadataName(info.parentNamespace)
                     });
                  }
               }
            }

            public void handleMixinReference(XMLMixin ref, XMLMixin parent)
            {
               String sParentNamespace = parent.getNamespace();
               String sNamespace = ref.getNamespace();
               String sVersion = ref.getVersion();
               String sChecksum = ref.getChecksum();
               MetadataInfo info = (MetadataInfo)m_metadataMap.get(sNamespace);

               if (info == null)
               {
                  info = new MetadataInfo();
                  info.parentNamespace = sParentNamespace;
                  info.ref = (XMLMixin)ref.clone();
                  m_metadataMap.put(sNamespace, info);

                  // mixin declared at top-level is also base
                  if (sParentNamespace.equals(m_metadata.getNamespace()) && sNamespace.equals(m_metadata.getBaseNamespace()))
                  {
                     m_helper.addException(new MetadataValidationException("err.meta.mixinDup",
                        new Object[] {sNamespace, getMetadataName(sParentNamespace)}));
                  }

                  // mixin matches base, but has different version/checksum
                  if (sNamespace.equals(m_metadata.getBaseNamespace()) &&
                     (!sVersion.equals(m_metadata.getBaseVersion()) || !sChecksum.equals(m_metadata.getBaseChecksum())))
                  {
                     m_helper.addException(new MetadataValidationException("err.meta.mixinBaseVersion",
                        new Object[]{sNamespace, sVersion, sChecksum, m_metadata.getBaseVersion(), m_metadata.getBaseChecksum()}));
                  }
               }
               else if (info.ref != null)
               {
                  if (sParentNamespace.equals(info.parentNamespace))
                  {
                     // mixin declared twice in same repository
                     m_helper.addException(new MetadataValidationException("err.meta.mixinDup",
                        new Object[] {sNamespace, getMetadataName(sParentNamespace)}));
                  }

                  // mixin declared more than once with different version/checksum
                  if ((info.ref.getVersion() != null && !info.ref.getVersion().equals(sVersion)) ||
                     (info.ref.getChecksum() != null && !info.ref.getChecksum().equals(sChecksum)))
                  {
                     m_helper.addException(new MetadataValidationException("err.meta.mixinVersion", new Object[] {
                        sNamespace,
                        sVersion,
                        sChecksum,
                        getMetadataName(sParentNamespace),
                        info.ref.getVersion(),
                        info.ref.getChecksum(),
                        getMetadataName(info.parentNamespace)
                     }));
                  }
               }
            }

            public void handleMixinOverrideConflict(XMLMixin ref, XMLMixin mixin)
            {
               throw new MetadataValidationException("err.meta.mixinOverride", new Object[]{ref.getName(), mixin.getName()});
            }

            public void handleCircularReference(XMLMixin mixin)
            {
               throw new MetadataValidationException("err.meta.mixinCycle", new Object[]{mixin.getName()});
            }

            public void handleResourceConflict(XMLResource first, XMLResource second)
            {
               m_helper.addException(new MetadataValidationException("err.meta.mixinConflict",
                  new Object[]{
                     first.getName(),
                     getMetadataName(first.getNamespace()),
                     getMetadataName(second.getNamespace())
                  }));
            }

            public void handleLinkFailure(XMLMixin ref, Exception e)
            {
               if (e instanceof MetadataValidationException)
               {
                  m_helper.addException((MetadataValidationException)e);
               }
               else
               {
                  throw new MetadataException("err.meta.mixin", new Object[]{ref}, e);
               }
            }

            public void handleAlternateResource(XMLResource first, XMLResource second)
            {
            }

            public void handleResourceOverride(XMLResource source, XMLResource overridden)
            {
            }

            final class MetadataInfo
            {
               /**
                * The metadata instance of the repository.
                */
               private XMLMetadata metadata;

               /**
                * The namespace of the repository that linked this repository.
                */
               private String parentNamespace;

               /**
                * The reference that caused this repository to be linked.
                */
               private XMLMixin ref;
            }
         }));

      m_helper.setMetadataCoreVersion(
         m_metadata.getCoreVersion(), m_metadata.getUpgradeResources());

      progressProxy.shiftRange(0.05);

      if (!m_bEnvironmentOnly && !bDataSourceOnly)
      {
         Lookup localeMap = new HashTab();
         m_helper.addResources(localeMap, ".locale", "Locale", "locale");
         loadStrings(localeMap.iterator());

         Lookup stringResMap = new HashTab();
         m_helper.addResources(stringResMap, ".strings", "Strings", "strings");
         loadStrings(stringResMap.iterator());

         if (StringTable.getInstance() == null)
         {
            StringTable.setInstance(m_metadata.getStringTable(Metadata.DEFAULT_LOCALE));
            StringTable.setTimeZone(TZ.UTC);
         }
      }

      progressProxy.shiftRange(0.10);

      m_helper.loadEncryptedResourceURL(configURL, "Server", "server", new ResourceHandler()
      {
         public void handleResource(final Element element, final String sName)
         {
            if (element.getNodeName().equals("Environment"))
            {
               String sConnectionsURL = m_properties.getProperty(CONNECTIONS_URL_PROPERTY);

               if (StringUtil.isEmpty(sConnectionsURL))
               {
                  m_properties.setProperty(CONNECTIONS_URL_PROPERTY, m_sConfigURL);
               }
            }

            NamedNodeMap attrMap = element.getAttributes();

            for (int i = 0; i != attrMap.getLength(); ++i)
            {
               Node node = attrMap.item(i);
               String sOldValue = m_properties.getProperty(node.getNodeName());

               if (StringUtil.isEmpty(sOldValue))
               {
                  m_properties.setProperty(node.getNodeName(), node.getNodeValue());
               }
            }
         }
      }, m_properties);

      m_decryptionDispatcher = new CharacterStreamCipherDispatcher();
      m_decryptionDispatcher.init(m_properties);
      m_helper.setEncryptionSchemeSet(new HashHolder(4));

      loadServer(m_properties);

      String sConnectionsURL = m_properties.getProperty(CONNECTIONS_URL_PROPERTY);
      URL connectionsURL = null;

      if (!StringUtil.isEmpty(sConnectionsURL))
      {
         connectionsURL = XMLMetadataHelper.getURL(sConnectionsURL, false, handler);
         sConnectionsURL = connectionsURL.toString();

         if (s_logger.isInfoEnabled())
         {
            s_logger.info("Connections URL: \"" + sConnectionsURL + "\"");
         }
      }

      m_helper.loadSystemResource("system.dstypes",
         "DataSourceTypes", "dataSourceTypes", new ResourceHandler()
      {
         public void handleResource(Element element, String sName)
         {
            loadDataSourceTypes(element);
         }
      });

      m_helper.loadResources(".dstype", "DataSourceType", "dataSourceType", new ResourceHandler()
      {
         public void handleResource(Element element, String sName)
         {
            loadDataSourceType(element, sName);
         }
      }, progress);

      m_helper.loadResources(".datasource", "DataSource", "dataSource", new ResourceHandler()
      {
         public void handleResource(Element element, String sName)
         {
            loadDataSource(element, sName);
         }
      }, progress);

      if (!m_bIntegrationExcluded)
      {
         m_helper.loadSystemResource("system.chtypes",
            "ChannelTypes", "channelTypes", new ResourceHandler()
         {
            public void handleResource(Element element, String sName)
            {
               loadChannelTypes(element);
            }
         });

         m_helper.loadResources(".chtype", "ChannelType", "channelType", new ResourceHandler()
         {
            public void handleResource(Element element, String sName)
            {
               loadChannelType(element, sName);
            }
         }, progress);

         // Load the SOA definition XML metadata (which may include definitions of other XML metadata)
         m_soaLoader = new XMLSOAMetadataLoader(m_helper);
         m_helper.loadResources(".soadef", "SOADefinition", "soadef", new ResourceHandler()
         {
            public void handleResource(Element element, String sName)
            {
               m_soaLoader.loadDefinition(element, sName);
            }
         }, progress);
         m_soaLoader.resolveReferences();

         // dispatcher channel properties loaded from environment.
         ChannelType type = m_metadata.getChannelType("ObjectQueue");

         m_dispatcherChannel.setType(type);
         m_metadata.addChannel(m_dispatcherChannel);

         m_helper.loadResources(".channel", "Channel", "channel", new ResourceHandler()
         {
            public void handleResource(Element element, String sName)
            {
               loadChannel(element, sName);
            }
         }, progress);
      }

      final Set componentNameSet = new HashHolder();
     
      m_helper.loadResources(".comp", "Component", "component", new ResourceHandler()
      {
         public void handleResource(Element element, String sName)
         {
            if (m_bEnvironmentOnly)
            {
               XMLMetadataHelper.verifyRootElement(element, "Component");

               m_metadata.addComponent(new Component(sName));
            }
           
            componentNameSet.add(sName);
         }
      }, progress);
     
      if (componentNameSet.contains("System.ObjectQueueDispatcher") &&
         Boolean.parseBoolean(properties.getProperty("queueing.enabled", "true")))
      {
      // only enable the dispatcher if the metadata supports it.
         m_dispatcherChannel.setSendable(true);
         m_dispatcherChannel.setReceivable(true);
      }

      if (connectionsURL != null)
      {
         boolean bEnvironment = (sConnectionsURL.equals(m_sConfigURL) ||
            sConnectionsURL.toLowerCase(Locale.ENGLISH).endsWith(".environment"));

         m_helper.loadEncryptedResourceURL(connectionsURL,
            (bEnvironment) ? "Environment" : "Connections",
            (bEnvironment) ? "environment" : "connections",
            new ResourceHandler()
         {
            public void handleResource(final Element element, final String sName)
            {
               loadConnections(element, sName);
            }
         }, m_properties);
      }
      else
      {
         loadConnections(null, null);
      }

      // .server file overrides highest scheme used in the connections file
      m_metadata.setEncryptionScheme(m_properties.getProperty("cipher.scheme",
            CharacterStreamCipherDispatcher.computeMostSecure(m_helper.getEncryptionSchemeSet())));

      if (!bDataSourceOnly)
      {
         loadUIMetadata(baseDescElement, rootDescElement);
      }

      progressProxy.shiftRange(0.12);

      m_helper.fixup(m_ioFixupList.iterator());
      m_ioFixupList = null;

      m_helper.loadResources(".extlib", "ExternalLibrary", "externalLibrary", new ResourceHandler()
      {
         public void handleResource(Element element, String sName)
         {
            loadExternalLibrary(element, sName);
         }
      }, progress);

      m_nStage = STAGE_LOADED_ENVIRONMENT;
      progressProxy.progress(null, null, 1.0);

      if (m_bEnvironmentOnly || bDataSourceOnly)
      {
         m_nStage = STAGE_LOAD_ENVIRONMENT_FIXUPS;
         m_helper.fixup(m_environmentFixupList.iterator()); // safe to run since no J2EE container
         m_nStage = STAGE_FINISHED;

         return m_metadata;
      }

      progressProxy.shiftRange(0.13);

      m_helper.loadResources(".security", "SecurityDescriptor", "securityDescriptor", new ResourceHandler()
      {
         public void handleResource(Element element, String sName)
         {
            loadSecurityDescriptor(element);
         }
      }, progress);

      m_helper.fixup(m_privilegeFixupList.iterator());
      m_privilegeFixupList = null;
      PrivilegeGroup.resolve(m_metadata);

      if (progress != null)
      {
         progressProxy.shiftRange(0.15);
         progress.progress("info.meta.loadingSystemResources", null, 0);
      }

      Context contextSaved = ThreadContextHolder.getContext();

      try
      {
         ThreadContextHolder.setContext(m_machine.getContext());

         m_helper.loadSystemResource("scheme.scm",
            "Library", "library", new CharacterStreamHandler()
         {
            public void handleCharacterStream(Reader reader, String sName) throws IOException
            {
               loadLibrary(reader, sName, true);
            }
         });

         // Load the Dynamic Object System
         m_helper.loadSystemResource("object.scm",
            "Library", "library", new CharacterStreamHandler()
         {
            public void handleCharacterStream(Reader reader, String sName) throws IOException
            {
               loadLibrary(reader, sName, true);
            }
         });

         // Load the Services Oriented Architecture functionality
         m_helper.loadSystemResource("soa.scm",
            "Library", "library", new CharacterStreamHandler()
         {
            public void handleCharacterStream(Reader reader, String sName) throws IOException
            {
               loadLibrary(reader, sName, true);
            }
         });

         m_helper.loadSystemResource("server.scm",
            "Library", "library", new CharacterStreamHandler()
         {
            public void handleCharacterStream(Reader reader, String sName) throws IOException
            {
               loadLibrary(reader, sName, true);
            }
         });

         m_helper.loadSystemResource(Metadata.ROOT_CLASS_NAME + ".meta",
            "Class", "class", new ResourceHandler()
         {
            public void handleResource(Element rootElement, String sName)
            {
               loadClass(rootElement, sName);
            }
         });

         loadExtendedSystemMetadata();

         progressProxy.shiftRange(0.16);

         m_helper.loadResources(".scm", "Library", "library", new CharacterStreamHandler()
         {
            public void handleCharacterStream(Reader reader, String sName) throws IOException
            {
               loadLibrary(reader, sName, false);
            }
         }, progress);
      }
      finally
      {
         ThreadContextHolder.setContext(contextSaved);
      }

      progressProxy.shiftRange(0.23);

      m_helper.loadResources(".meta", "Class", "class", new ResourceHandler()
      {
         public void handleResource(Element element, String sName)
         {
            String sElement = element.getNodeName();

            if (sElement.equals("Class"))
            {
               loadClass(element, sName);
            }
            else if (sElement.equals("Aspect"))
            {
               loadClassAspect(element, sName);
            }
            else
            {
               throw new MetadataException("err.meta.metaDocRoot", new Object[]{sElement});
            }
         }
      }, progress);

      progressProxy.shiftRange(0.24);

      m_helper.loadResources(".enum", "Enumeration", "enum", new ResourceHandler()
      {
         public void handleResource(Element element, String sName)
         {
            loadEnumeration(element, sName);
         }
      }, progress);

      progressProxy.shiftRange(0.25);

      loadUIEventMetadata(baseDescElement, rootDescElement, progress);

      if (progress != null)
      {
         progressProxy.shiftRange(0.26);
         progress.progress("info.meta.resolvingResourceReferences", null, 0);
      }

      m_helper.fixup(m_inheritanceCheckList.iterator());
      m_inheritanceCheckList = null;
      m_helper.fixup(m_attributeGenerationList.iterator());
      m_attributeGenerationList = null;

      m_helper.fixup(Collections.singleton(new ClassFixup()
      {
         public void fixup()
         {
            m_classAspectArray = ClassAspect.resolveAspects(m_metadata);
         }
      }).iterator());

      m_helper.fixup(m_inheritanceFixupList.iterator());
      m_inheritanceFixupList = null;
      m_enumerationValueMap = null;
      m_helper.fixup(m_attributeFixupList.iterator());
      m_attributeFixupList = null;
      m_helper.fixup(m_actionFixupList.iterator());
      m_actionFixupList = null;

      if (m_classAspectArray != null)
      {
         ClassAspect.resolveMembers(m_classAspectArray);
      }

      m_helper.fixup(m_persistenceMappingLoadList.iterator());
      m_persistenceMappingLoadList = null;
      m_helper.fixup(m_attributeResolutionList.iterator());
      m_attributeResolutionList = null;
      m_helper.fixup(m_inheritance1stPassFixupList.iterator());
      m_inheritance1stPassFixupList = null;

      progressProxy.shiftRange(0.27);

      m_helper.loadResources(".action", "Action", "action", new ResourceHandler()
      {
         public void handleResource(Element element, String sName)
         {
            loadFlowMacro(element, sName);
         }
      }, progress);

      progressProxy.shiftRange(0.32);

      m_helper.loadResources(".workflow", "Workflow", "workflow", new ResourceHandler()
      {
         public void handleResource(Element element, String sName)
         {
            loadWorkflow(element, sName);
         }
      }, progress);

      progressProxy.shiftRange(0.33);

      if (!m_bIntegrationExcluded)
      {
         m_helper.loadSystemResource("system.formats",
            "Formats", "formats", new ResourceHandler()
         {
            public void handleResource(Element element, String sName)
            {
               loadFormats(element);
            }
         });

         m_helper.loadResources(".format", "Format", "format", new ResourceHandler()
         {
            public void handleResource(Element element, String sName)
            {
               loadFormat(element, sName);
            }
         }, progress);

         progressProxy.shiftRange(0.34);

         final Lookup importedMessageMap = new HashTab();

         m_helper.loadResources(".xsd", "XSD", "xsd", new ResourceNameHandler()
         {
            public void handleResource(String sName, String sRelativePath)
            {
               loadXSD(m_helper.getResource(sRelativePath).getURL(), sName, importedMessageMap);
            }
         }, progress);

         m_helper.loadResources(".wsdl", "WSDL", "wsdl", new ResourceNameHandler()
         {
            public void handleResource(String sName, String sRelativePath)
            {
               loadXSD(m_helper.getResource(sRelativePath).getURL(), sName, importedMessageMap);
            }
         }, progress);

         m_helper.loadResources(".soaimpl", "SOAImplementation", "soaimpl", new ResourceHandler()
         {
            public void handleResource(Element element, String sName)
            {
               m_soaLoader.loadImplementation(element, sName, m_metadata.getGlobalEnvironment());
            }
         }, progress);

         contextSaved = ThreadContextHolder.getContext();

         try
         {
            ThreadContextHolder.setContext(m_machine.getContext());
            m_soaLoader.initDefinitions(m_machine);
         }
         finally
         {
            ThreadContextHolder.setContext(contextSaved);
         }

         m_helper.loadResources(".message", "Message", "message", new ResourceHandler()
         {
            public void handleResource(Element element, String sName)
            {
               loadMessage(element, sName);
            }
         }, progress);

         // Explicitly-defined Message metadata shall override Message metadata from imported XSDs and WSDLs
         for (Lookup.Iterator itr = importedMessageMap.iterator(); itr.hasNext(); )
         {
            String sName = (String)itr.next();

            if (m_metadata.findMessage(sName) == null)
            {
               if (s_logger.isDebugEnabled())
               {
                  s_logger.debug("Adding imported message \"" + sName + "\"");
               }

               m_metadata.addMessage((Message)itr.getValue());
            }
            else
            {
               if (s_logger.isDebugEnabled())
               {
                  s_logger.debug("Skipping the overridden imported message \"" + sName + "\"");
               }
            }
         }

         // Resolve Inheritance
         m_helper.fixup(m_preInheritanceMessageFixupList.iterator());
         m_preInheritanceMessageFixupList = null;

         List sortedMessageList = new ArrayList(m_metadata.getMessageCount());

         Message.resolveInheritance(m_metadata.getMessageIterator(), sortedMessageList);

         m_helper.fixup(m_postInheritanceMessageFixupList.iterator());
         m_postInheritanceMessageFixupList = null;

         Message.resolveReferences(m_metadata.getMessageIterator());

         progressProxy.shiftRange(0.36);

         m_helper.loadResources(".transformation", "Transformation", "transformation", new ResourceHandler()
         {
            public void handleResource(Element element, String sName)
            {
               loadTransformation(element, sName);
            }
         }, progress);

         m_helper.fixup(m_transformationFixupList.iterator());
         m_transformationFixupList = null;

         Transformation.resolveInheritance(m_metadata.getTransformationIterator());

         m_helper.fixup(m_postInheritanceTransformationFixupList.iterator());
         m_postInheritanceTransformationFixupList = null;

         for (Iterator itr = m_metadata.getTransformationIterator(); itr.hasNext(); )
         {
            Transformation transformation = (Transformation)itr.next();

            try
            {
               transformation.finish(m_machine);
            }
            catch (UncheckedException ex)
            {
               m_helper.addException(ex);
            }
         }

         m_helper.checkForError();

         progressProxy.shiftRange(0.37);

         m_helper.loadResources(".interface", "Interface", "interface", new ResourceHandler()
         {
            public void handleResource(Element element, String sName)
            {
               loadInterface(element, sName);
            }
         }, progress);

         progressProxy.shiftRange(0.38);

         m_helper.loadResources(".service", "Service", "service", new ResourceHandler()
         {
            public void handleResource(Element element, String sName)
            {
               loadService(element, sName);
            }
         }, progress);

         finishMessagePartMappings(sortedMessageList.iterator());
      }

      if (progress != null)
      {
         progressProxy.shiftRange(0.39);
         progress.progress("info.meta.resolvingResourceReferences", null, 0);
      }

      m_helper.fixup(m_persistenceMappingFixupList.iterator());
      m_persistenceMappingFixupList = null;
      m_helper.fixup(m_persistenceMappingGenerationList.iterator());
      m_persistenceMappingGenerationList = null;
      m_helper.fixup(m_attributeDependencyList.iterator());
      m_attributeDependencyList = null;
      Attribute.resolveInverseDependency(m_attributeDependencySet.iterator());
      m_attributeDependencySet = null;

      if (m_classAspectArray != null)
      {
         m_helper.fixup(Collections.singleton(new ClassFixup()
         {
            public void fixup()
            {
               ClassAspect.resolvePersistence(m_classAspectArray);
            }
         }).iterator());
      }

      m_helper.fixup(m_inheritance2ndPassFixupList.iterator());
      m_inheritance2ndPassFixupList = null;
      m_helper.fixup(m_class2ndPassList.iterator());
      m_class2ndPassList = null;

      loadLocales();
      progressProxy.shiftRange(0.40);

      m_helper.loadResources(".comp", "Component", "component", new ResourceHandler()
      {
         public void handleResource(Element element, String sName)
         {
            loadComponent(element, sName);
         }
      }, progress);

      m_nStage = STAGE_LOADED_COMPONENTS;
      progressProxy.progress(null, null, 0);

      m_nStage = STAGE_LOAD_ENVIRONMENT_FIXUPS;
      progressProxy.shiftRange(0.405);

      m_helper.fixup(m_environmentFixupList.iterator());

      if (progress != null)
      {
         progressProxy.shiftRange(0.41);
         progress.progress("info.meta.instantiatingComponents", null, 0);
      }

      m_helper.fixup(m_componentFixupList.iterator());
      m_componentFixupList = null;
      m_helper.fixup(m_inheritance4thPassFixupList.iterator());
      m_inheritance4thPassFixupList = null;
      m_helper.fixup(m_singletonFixupList.iterator());
      m_singletonFixupList = null;

      m_helper.addResources(m_metadata.getDumpResourceMap(), ".dump", "Dump", "dump");
      m_helper.addResources(m_metadata.getUnitTestResourceMap(), ".utest", "UnitTest", "unittest");
      m_helper.addResources(m_metadata.getUpgradeResourceMap(), ".upgrade", "Upgrade", "upgrade");

      loadExtendedMetadata(baseDescElement, rootDescElement);

      if (m_bValidatedOnly)
      {
         progressProxy.setRange(0.42, 0.96);

         m_helper.validateResources(m_metadata.getUnitTestResourceMap(), "UnitTest", "unittest", new ResourceNameHandler()
         {
            public void handleResource(String sName, String sFullName)
            {
               m_metadata.getUnitTest(sName);
            }
         }, null);

         progressProxy.shiftRange(0.98);

         m_helper.validateResources(m_metadata.getUpgradeResourceMap(), "Upgrade", "upgrade", new ResourceNameHandler()
         {
            public void handleResource(String sName, String sFullName)
            {
               if (m_helper.findResource(sFullName, XMLMetadataHelper.SEARCH_ROOT_THEN_BASE) != null)
               {
                  m_metadata.getUpgrade(sName).validate(m_metadata, m_helper.getWarnings());
               }
            }
         }, null);

         validateExtendedMetadata(progressProxy);
      }

      try
      {
         m_metadata.validate(m_metadata, m_helper.getWarnings());

         if (!StringUtil.isEmpty(sCompatibleURI))
         {
            progressProxy.shiftRange(0.988);
            progressProxy.progress("info.meta.loadCompatibleModel", null, 0);

            Properties compatibleProperties = new Properties(properties);

            compatibleProperties.setProperty(BASE_URL_PROPERTY, "");
            compatibleProperties.setProperty(COMPATIBLE_URL_PROPERTY, "");

            Metadata compatible;

            try
            {
               compatible = new MetadataLoaderDispatcher().load(sCompatibleURI, compatibleProperties, nFlags, null);
            }
            catch (UncheckedException e)
            {
               throw new MetadataCompatibilityException("err.meta.compatibleMetadataLoadFailure", null, e);
            }

            progressProxy.progress("info.meta.verifyModelCompatibility", null, 1);
            m_metadata.checkCompatibility(compatible);
         }
      }
      catch (UncheckedException e)
      {
         m_helper.addException(e);
      }

      if (m_bValidatedOnly)
      {
         progressProxy.shiftRange(.989);

         m_helper.validateResources(m_metadata.getDumpResourceMap(), "Dump", "dump", new ResourceNameHandler()
         {
            public void handleResource(String sName, String sFullName)
            {
               if (m_helper.findResource(sFullName, XMLMetadataHelper.SEARCH_ROOT_THEN_BASE) == null)
               {
                  return;
               }

               InputStream is = null;
               Pair dumpVersion = null;

               try
               {
                  dumpVersion = new DataLoader(null).getDumpVersion(is = m_helper.getResourceAsStream(sFullName));
               }
               catch (Exception e)
               {
                  throw new MetadataException("err.meta.dumpLoad", new Object[]
                  {
                     sName
                  }, e);
               }
               finally
               {
                  IOUtil.close(is);
               }

               if (dumpVersion != null
                  && (!m_metadata.getNamespace().equals(dumpVersion.getHead()) || !m_metadata.getVersion().equals(
                     dumpVersion.getTail())))
               {
                  throw new MetadataException("err.meta.dumpVersion", new Object[]
                  {
                     dumpVersion.getHead(),
                     dumpVersion.getTail(),
                     m_metadata.getNamespace(),
                     m_metadata.getVersion()
                  });
               }
            }
         }, null);
      }

      if (!m_bWritable)
      {
         m_metadata.makeReadOnly();
      }

      m_helper.checkForError();

      if (progress != null)
      {
         progressProxy.shiftRange(0.99);
         progress.progress("info.meta.validatingResourceReferentialIntegrity", null, 0);
      }

      if (m_helper.getWarnings() != null)
      {
         if (s_logger.isWarnEnabled())
         {
            for (Iterator itr = m_helper.getWarnings().getExceptionIterator(); itr.hasNext();)
            {
               s_logger.warn(ObjUtil.getMessage((Throwable)itr.next()));
            }
         }
      }

      m_nStage = STAGE_FINISHED;

      return m_metadata;
   }

   /**
    * Load environment descriptors.
    * @param baseDescElement Base repository descriptor element.
    * @param rootDescElement Repository descriptor element.
    * @param progress A progress listener.
    */
   protected void loadUIMetadata(Element baseDescElement, Element rootDescElement)
   {
   }

   /**
    * Create the Metadata instance.
    * @param sURI The repository name.
    * @param rootURL The repository URL.
    * @param baseURL The base repository URL.
    * @param helper The XMLMetadataHelper. May be null.
    * @return an XMLMetadata instance.
    */
   public XMLMetadata createXMLMetadata(String sURI, URL rootURL, URL baseURL, XMLMetadataHelper helper)
   {
       return new XMLMetadata(sURI, rootURL, baseURL, m_properties, helper);
   }

   /**
    * Gets the current metadata loading stage.
    * @return The loading stage, one of the STAGE_* constants.
    */
   public byte getStage()
   {
      return m_nStage;
   }

   /**
    * @return The environment-only flag.
    */
   public boolean isEnvironmentOnly()
   {
      return m_bEnvironmentOnly;
   }

   /**
    * @return The runtime exclusion flag.
    */
   public boolean isRuntimeExcluded()
   {
      return m_bRuntimeExcluded;
   }

   /**
    * @return The validation-only flag.
    */
   public boolean isValidatedOnly()
   {
      return m_bValidatedOnly;
   }

   /**
    * @return The flag for leaving the metadata writable.
    */
   public boolean isWritable()
   {
      return m_bWritable;
   }

   /**
    * @return The environment container, one of the J2EEUtil constants.
    */
   public int getContainer()
   {
      return m_nContainer;
   }

   /**
    * Gets an environment property.
    * @param sName The property name.
    * @return The property value, or null if not specified.
    */
   public String getProperty(String sName)
   {
      if (m_properties == null)
      {
         return null;
      }

      String sValue = m_properties.getProperty(sName);

      if (sValue != null && sValue.length() == 0)
      {
         sValue = null;
      }

      return sValue;
   }

   /**
    * Gets an environment property.
    * @param sName The property name.
    * @param lDefaultValue The default value, if not specified.
    * @return The property value.
    */
   public long getProperty(String sName, long lDefaultValue)
   {
      Long value = Primitive.toLong(getProperty(sName));

      return (value == null) ? lDefaultValue : value.longValue();
   }

   /**
    * Loads a descriptor version.
    * @param metadata The metadata for which to load the version.
    * @param helper The XMLMetadataHelper for metadata.
    */
   protected void loadVersion(XMLMetadata metadata, XMLMetadataHelper helper)
   {
      Element descriptorElement = helper.getDescriptorElement(true);
      Element baseDescriptorElement = helper.getDescriptorElement(false);
      int nCookie = getHelper().pushMarker(MetadataValidationException.TYPE_NAME, "Metadata");

      try
      {
         metadata.setName(helper.getName(true));
         metadata.setRevision(helper.getRevision(true));
         metadata.setNamespace(helper.getNamespace(true));
         metadata.setVersion(helper.getVersion(true));
         metadata.setCoreVersion(XMLUtil.getStringAttr(descriptorElement, "coreVersion"));
         metadata.setChecksum(helper.getChecksum(true));

         String sUpgradeNamespace = XMLUtil.getStringAttr(descriptorElement, "upgradeNamespace");

         if (sUpgradeNamespace != null)
         {
            metadata.setBaseNamespace(sUpgradeNamespace);
            metadata.setBaseVersion(XMLUtil.getStringAttr(descriptorElement, "upgradeVersion"));
            metadata.setBaseChecksum(XMLUtil.getStringAttr(descriptorElement, "upgradeChecksum"));
         }
         else
         {
            metadata.setBaseNamespace(XMLUtil.getStringAttr(descriptorElement, "baseNamespace"));
            metadata.setBaseVersion(XMLUtil.getStringAttr(descriptorElement, "baseVersion"));
            metadata.setBaseChecksum(XMLUtil.getStringAttr(descriptorElement, "baseChecksum"));
         }

         if (s_logger.isInfoEnabled())
         {
            s_logger.info("Metadata URI: \"" + metadata.getNamespace() + '#' +
               metadata.getVersion() + '-' + metadata.getChecksum() + '"');

            if (metadata.getBaseNamespace() != null)
            {
               s_logger.info("Base metadata URI: \"" + metadata.getBaseNamespace() + '#' +
                  metadata.getBaseVersion() + '-' + metadata.getBaseChecksum() + '"');
            }
         }

         if (baseDescriptorElement != null && metadata.getBaseNamespace() != null)
         {
            String sBaseChecksum = "";

            if (!metadata.getBaseChecksum().equals(""))
            {
               sBaseChecksum = helper.getChecksum(false);
            }

            if (!metadata.getBaseNamespace().equals(helper.getNamespace(false)) ||
               !metadata.getBaseVersion().equals(helper.getVersion(false)) ||
               !metadata.getBaseChecksum().equals(sBaseChecksum))
            {
               throw new MetadataException("err.meta.baseVersion",
                  new Object[]
                  {
                     metadata.getBaseNamespace(),
                     metadata.getBaseVersion(),
                     metadata.getBaseChecksum(),
                     helper.getNamespace(false),
                     helper.getVersion(false),
                     sBaseChecksum
                  });
            }

            metadata.setBaseChecksum(sBaseChecksum);

            String sBaseCoreVersion = XMLUtil.getStringAttr(baseDescriptorElement, "coreVersion");

            if (metadata.getCoreVersion() != null)
            {
               if (sBaseCoreVersion != null &&
                  StringUtil.compareVersionRanges(metadata.getCoreVersion(), sBaseCoreVersion) < 0)
               {
                  throw new MetadataException("err.meta.baseCoreVersion",
                     new Object[] {XMLUtil.getStringAttr(baseDescriptorElement, "coreVersion"), metadata.getCoreVersion()}
                  );
               }
            }
            else
            {
               metadata.setCoreVersion(sBaseCoreVersion);
            }

            String sModule = XMLMetadataHelper.normalizeScope(XMLUtil.getStringAttr(descriptorElement, "module"));
            String sBaseModule = XMLMetadataHelper.normalizeScope(XMLUtil.getStringAttr(baseDescriptorElement, "module"));

            if (!ObjUtil.equal(sModule, sBaseModule))
            {
               throw new MetadataException("err.meta.baseModule",
                  new Object[]{(sModule == null) ? "" : sModule, (sBaseModule == null) ? "" : sBaseModule});
            }
         }

         String sCoreVersion = metadata.getCoreVersion();

         if (sCoreVersion != null)
         {
            boolean bPlus = sCoreVersion.length() > 0 &&
                            sCoreVersion.charAt(sCoreVersion.length() - 1) == '+';
            int nCompare = StringUtil.compareVersionRanges(Version.RELEASE, sCoreVersion);

            // if metadata coreVersion is older than Version.RELEASE then coreVersion must have '+'
            // if metadata coreVersion is newer than Version.RELEASE then metadata not compatible
            if (nCompare != 0 && (!bPlus || nCompare < 0))
            {
               throw new MetadataException(
                  "err.meta.coreVersion",
                  new Object[]
                  {
                     Version.RELEASE,
                     sCoreVersion
                  });
            }
         }
      }
      catch (UncheckedException e)
      {
         MetadataValidationException x = new MetadataValidationException(e);
         getHelper().setMarker(x);
         throw x;
      }
      finally
      {
         getHelper().restoreMarker(nCookie);
      }
   }


   /**
    * Loads a library.
    * @param reader The character input stream.
    * @param sName The library name.
    * @param bSystem If true the scripting URL will prefix will be "syslibrary:"
    *           rather than "library:".
    */
   protected void loadLibrary(Reader reader, String sName, boolean bSystem) throws IOException
   {
      GlobalEnvironment.Listener listener = new GlobalEnvironment.Listener()
      {
         protected boolean m_bClient;

         public void setScope(GlobalEnvironment env, Symbol scope)
         {
            String sName = scope.getName();

            if (sName != null)
            {
               if (sName.equals("all") || sName.equals("client"))
               {
                  m_bClient = true;
               }
               else if (sName.equals("server"))
               {
                  m_bClient = false;
               }
               else
               {
                  throw new MetadataException("err.meta.library.scope",
                     new Object[]{sName});
               }
            }
         }

         public void setVariable(GlobalEnvironment env, Symbol name, Object value)
         {
            if (m_bClient && value instanceof PCodeFunction && !(value instanceof Macro))
            {
               m_metadata.addClientSymbol(name);
            }
            else
            {
               m_metadata.removeClientSymbol(name);
            }
         }
      };

      try
      {
         m_metadata.getGlobalEnvironment().addListener(listener);
         Intrinsic.load(reader, ((bSystem) ? "syslibrary:" : "library:") + sName, m_machine);
      }
      finally
      {
         m_metadata.getGlobalEnvironment().removeListener(listener);
      }
   }

   /**
    * Loads data source types from a DOM element.
    * @param dstElement The DOM element containing the data source types.
    */
   protected void loadDataSourceTypes(Element dstElement)
   {
      XMLMetadataHelper.verifyRootElement(dstElement, "DataSourceTypes");

      XMLUtil.forEachChildElement(dstElement, "DataSourceType",
         m_helper.new ElementHandler("dataSourceType")
      {
         public void handleElement(Element element, String sName)
         {
            loadDataSourceType(element, sName);
         }
      });
   }

   /**
    * Loads a data source type from a DOM element.
    * @param dstElement The DOM element containing the data source type.
    * @param sName The data source type name.
    */
   protected void loadDataSourceType(Element dstElement, String sName)
   {
      XMLMetadataHelper.verifyRootElement(dstElement, "DataSourceType");

      final DataSourceType type = new DataSourceType(sName);

      type.setMetadata(m_metadata);
      type.setLoader(m_helper.getClassObject(XMLUtil.getReqStringAttr(dstElement, "loader")));

      String sExporter = XMLUtil.getStringAttr(dstElement, "exporter");

      if (sExporter != null)
      {
         type.setExporter(m_helper.getClassObject(sExporter));
      }

      String sElement = XMLUtil.getReqStringAttr(dstElement, "element");

      XMLMetadataHelper.validateName(sElement);
      m_metadata.addDataSourceTypeElement(sElement, type);

      XMLUtil.withFirstChildElement(dstElement, "Adapters", false, new ElementHandler()
      {
         public void handleElement(Element adaptersElement)
         {
            XMLUtil.forEachChildElement(adaptersElement, "Adapter",
               m_helper.new ElementHandler("adapter")
            {
               public void handleElement(Element adapterElement, String sAdapterName)
               {
                  DataSourceAdapter adapter = new DataSourceAdapter(sAdapterName);

                  try
                  {
                     adapter.setType(type);
                     adapter.setClassObject(m_helper.getClassObject(XMLUtil.getReqStringAttr(adapterElement, "class")));
                     adapter.setVersion(XMLUtil.getStringAttr(adapterElement, "version"));
                     type.addAdapter(adapter);
                  }
                  catch (MetadataException e)
                  {
                     if (!(e.getCause() instanceof ClassNotFoundException) &&
                        !(e.getCause() instanceof NoClassDefFoundError))
                     {
                        throw e;
                     }

                     // if data source type can't be loaded, don't add it to the metadata, but don't fail.
                     s_logger.debug("Cannot load DataSourceAdapter: " + adapter.getName() + " " + XMLUtil.getReqStringAttr(adapterElement, "class"));
                  }
               }
            });
         }
      });

      m_metadata.addDataSourceType(type);
   }

   /**
    * Loads a data source from a DOM element.
    * @param dataSourceElement The DOM element containing the data source.
    * @param sName The data source name.
    */
   protected void loadDataSource(Element dataSourceElement, String sName)
   {
      DataSourceType type = m_metadata.getDataSourceTypeByElement(dataSourceElement);

      m_metadata.addDataSource(((XMLPersistenceMetadataLoader)m_helper.getClassInstance(type.getLoader()))
         .loadDataSource(dataSourceElement, sName, type, this));
   }

   /**
    * Loads data source common attributes from a DOM element.
    * @param element The DOM element containing the data source.
    * @param dataSource The target data source object.
    */
   public void loadDataSource(Element element, final DataSource dataSource)
   {
      dataSource.setReadable(XMLUtil.getBooleanAttr(element, "read", true));
      dataSource.setCreatable(XMLUtil.getBooleanAttr(element, "create", true));
      dataSource.setUpdatable(XMLUtil.getBooleanAttr(element, "update", true));
      dataSource.setDeletable(XMLUtil.getBooleanAttr(element, "delete", true));
      dataSource.setExecutable(XMLUtil.getBooleanAttr(element, "execute", true));
      dataSource.setJoinable(XMLUtil.getBooleanAttr(element, "join", true));
      dataSource.setReadLimit(XMLUtil.getIntAttr(element, "readLimit", dataSource.getReadLimit()));

      XMLUtil.withFirstChildElement(element, "Properties", false, new ElementHandler()
      {
         public void handleElement(final Element propertiesElement)
         {
            addIOFixup(new ContextFixup(m_helper)
            {
               public void fixup()
               {
                  loadComponentProperties(propertiesElement, dataSource.getComponent());
               }
            });
         }
      });
   }

   /**
    * Loads channel types from a DOM element.
    * @param chtElement The DOM element containing the channel types.
    */
   protected void loadChannelTypes(Element chtElement)
   {
      XMLMetadataHelper.verifyRootElement(chtElement, "ChannelTypes");

      XMLUtil.forEachChildElement(chtElement, "ChannelType",
         m_helper.new ElementHandler("channelType")
      {
         public void handleElement(Element element, String sName)
         {
            loadChannelType(element, sName);
         }
      });
   }

   /**
    * Loads a channel type from a DOM element.
    * @param chtElement The DOM element containing the channel type.
    * @param sName The channel type name.
    */
   protected void loadChannelType(Element chtElement, String sName)
   {
      XMLMetadataHelper.verifyRootElement(chtElement, "ChannelType");

      final ChannelType type = new ChannelType(sName);

      type.setMetadata(m_metadata);
      type.setLoader(m_helper.getClassObject(XMLUtil.getReqStringAttr(chtElement, "loader")));

      String sExporter = XMLUtil.getStringAttr(chtElement, "exporter");

      if (sExporter != null)
      {
         type.setExporter(m_helper.getClassObject(sExporter));
      }

      type.setSender(m_helper.getClassObject(XMLUtil.getReqStringAttr(chtElement, "sender")));

      String sReceiver = XMLUtil.getStringAttr(chtElement, "receiver");

      if (sReceiver != null)
      {
         type.setReceiver(m_helper.getClassObject(sReceiver));
      }

      String sElement = XMLUtil.getReqStringAttr(chtElement, "element");

      XMLMetadataHelper.validateName(sElement);
      m_metadata.addChannelTypeElement(sElement, type);
      m_metadata.addChannelType(type);
   }

   /**
    * Loads a channel from a DOM element.
    * @param channelElement The DOM element containing the channel.
    * @param sName The data source name.
    */
   protected void loadChannel(Element channelElement, String sName)
   {
      ChannelType type = m_metadata.getChannelTypeByElement(channelElement);

      m_metadata.addChannel(((XMLIntegrationMetadataLoader)m_helper.getClassInstance(type.getLoader()))
         .loadChannel(channelElement, sName, type, this));
   }

   /**
    * Loads channel common attributes from a DOM element.
    * @param element The DOM element containing the channel.
    * @param channel The target channel object.
    */
   public void loadChannel(Element element, final Channel channel)
   {
      channel.setSendable(XMLUtil.getBooleanAttr(element, "send", true));
      channel.setReceivable(XMLUtil.getBooleanAttr(element, "receive", true));

      String sCombine = XMLUtil.getStringAttr(element, "combine", "none");

      if (sCombine.equals("none"))
      {
         channel.setCombinationMode(Channel.COMBINE_NONE);
      }
      else if (sCombine.equals("first"))
      {
         channel.setCombinationMode(Channel.COMBINE_FIRST);
      }
      else if (sCombine.equals("all"))
      {
         channel.setCombinationMode(Channel.COMBINE_ALL);
      }
      else
      {
         throw new MetadataException("err.meta.integration.combine",
            new Object[]{sCombine, channel.getName()});
      }

      XMLUtil.withFirstChildElement(element, "SenderProperties", false, new ElementHandler()
      {
         public void handleElement(final Element propertiesElement)
         {
            addIOFixup(new ContextFixup(m_helper)
            {
               public void fixup()
               {
                  if (channel.getSender() != null)
                  {
                     loadComponentProperties(propertiesElement, channel.getSender());
                  }
               }
            });
         }
      });

      XMLUtil.withFirstChildElement(element, "ReceiverProperties", false, new ElementHandler()
      {
         public void handleElement(final Element propertiesElement)
         {
            addIOFixup(new ContextFixup(m_helper)
            {
               public void fixup()
               {
                  if (channel.getReceiver() != null)
                  {
                     loadComponentProperties(propertiesElement, channel.getReceiver());
                  }
               }
            });
         }
      });

      if (!m_bIntegrationExcluded)
      {
         XMLUtil.withFirstChildElement(element, "ServiceBindings", false, new ElementHandler()
         {
            public void handleElement(final Element bindingsElement)
            {
               addPersistenceMappingFixup(new ContextFixup(m_helper)
               {
                  public void fixup()
                  {
                     XMLUtil.forEachChildElement(bindingsElement, "ServiceBinding", m_helper.new ElementHandler("service")
                     {
                        protected String getName(Element element)
                        {
                           return XMLUtil.getReqStringAttr(element, "service");
                        }

                        public void handleElement(Element bindingElement, String sService)
                        {
                           final Binding binding = new Binding(m_metadata.getService(sService), channel);
                           String sOutput = XMLUtil.getStringAttr(bindingElement, "output");

                           if (sOutput != null)
                           {
                              binding.setOutput(m_metadata.getChannel(sOutput));
                           }

                           XMLUtil.withFirstChildElement(bindingElement, "Arguments", false, new ElementHandler()
                           {
                              public void handleElement(Element argumentsElement)
                              {
                                 XMLUtil.forEachChildElement(argumentsElement, "Argument", m_helper.new ElementHandler("argument")
                                 {
                                    public void handleElement(Element argumentElement, String sArgument)
                                    {
                                       String sChannel = XMLUtil.getStringAttr(argumentElement, "channel");

                                       if (sChannel != null)
                                       {
                                          binding.setArgumentChannel(sArgument, m_metadata.getChannel(sChannel));
                                       }

                                       String sValue = XMLUtil.getStringAttr(argumentElement, "value");

                                       if (sValue != null)
                                       {
                                          binding.setArgumentValue(sArgument,
                                             m_helper.parse(sValue, false, null, null, m_metadata.getGlobalEnvironment()));
                                       }
                                    }
                                 });
                              }
                           });

                           binding.compile(m_machine);
                           channel.addBinding(binding);
                        }
                     });
                  }
               });

               addSingletonFixup(new ContextFixup(m_helper)
               {
                  public void fixup()
                  {
                     MessageTable table = channel.getMessageTable();

                     if (table != null && table.getFormat() != null)
                     {
                        ((MessageParser)table.getFormat().getParser().getInstance(null)).initializeMessageTable(table);
                     }
                  }
               });
            }
         });
      }
   }

   /**
    * Loads the server properties from a property map.
    * @param envProps The environment properties.
    */
   protected void loadServer(Properties envProps)
   {
      m_metadata.setPortOffset(
         Integer.parseInt(SysUtil.getConfigProperties().getProperty("port.offset", "0")));

      String sType = envProps.getProperty("type", "Generic");

      if (sType.equals("JBoss"))
      {
         m_nContainer = J2EEUtil.JBOSS;
      }
      else if (sType.equals("WebSphere"))
      {
         m_nContainer = J2EEUtil.WEBSPHERE;
      }
      else
      {
         m_nContainer = J2EEUtil.NONE;
      }

      String sAuthProtocol = envProps.getProperty("authProtocol", "basic");

      if (sAuthProtocol.equals("basic"))
      {
         m_metadata.setAuthenticationProtocol(Metadata.AUTH_PROTOCOL_BASIC);
      }
      else if (sAuthProtocol.equals("spnego"))
      {
         m_metadata.setAuthenticationProtocol(Metadata.AUTH_PROTOCOL_SPNEGO);
      }
      else if (sAuthProtocol.equals("certificate"))
      {
         m_metadata.setAuthenticationProtocol(Metadata.AUTH_PROTOCOL_CERTIFICATE);
      }
      else
      {
         throw new MetadataException("err.meta.authProtocol", new Object[]{sAuthProtocol});
      }

      m_metadata.setAuthenticationService(envProps.getProperty("authService"));
      m_metadata.setAuthenticationDomain(envProps.getProperty("authDomain"));
      m_metadata.setSessionTimeout(Integer.parseInt(envProps.getProperty(
         "sessionTimeout", Integer.toString(m_metadata.getSessionTimeout()))));
      m_metadata.setReplicatedSession(StringUtil.parseBoolean(envProps.getProperty(
         "replicatedSession", Boolean.toString(m_metadata.isReplicatedSession()))));
      m_metadata.setPersistentSession(StringUtil.parseBoolean(envProps.getProperty(
         "persistentSession", Boolean.toString(m_metadata.isPersistentSession()))));
      m_metadata.setSecureTransport(StringUtil.parseBoolean(envProps.getProperty(
         "secureTransport", Boolean.toString(m_metadata.isSecureTransport()))));
      m_metadata.setTestInterceptor(StringUtil.parseBoolean(envProps.getProperty(
         "test.record", Boolean.toString(m_metadata.isTestInterceptor()))));
      m_metadata.setTestEnvironment(StringUtil.parseBoolean(envProps.getProperty(
         "test", Boolean.toString(m_metadata.isTestEnvironment()))));

      m_metadata.setKeyStorePassword(decryptPassword(envProps.getProperty("keystorePassword")));
      String sHTTPURL = envProps.getProperty("httpURL");

      m_metadata.setHTTPRoot(sHTTPURL);
      m_metadata.setHTTPContextRoot(HTTP.getContextPath(sHTTPURL));
      m_metadata.setHTTPAnonymousContextRoot(HTTP.getContextPath(envProps.getProperty("httpAnonymousURL",
         m_metadata.getHTTPAnonymousContextRoot())));
      m_metadata.setHTTPFormContextRoot(HTTP.getContextPath(envProps.getProperty("httpFormURL",
         m_metadata.getHTTPFormContextRoot())));
      m_metadata.setHTTPPushRedirectorContextRoot(HTTP.getContextPath(envProps.getProperty("pushRedirectorURL",
         m_metadata.getHTTPPushRedirectorContextRoot())));

      if (m_metadata.getHTTPContextRoot().equals(m_metadata.getHTTPAnonymousContextRoot()) ||
         m_metadata.getHTTPContextRoot().equals(m_metadata.getHTTPFormContextRoot()) ||
         m_metadata.getHTTPAnonymousContextRoot().equals(m_metadata.getHTTPFormContextRoot()))
      {
         throw new MetadataException("err.meta.contextPathDup");
      }

      m_metadata.setHTTPFormLoginPage(envProps.getProperty("httpFormLoginPage",
         m_metadata.getHTTPFormLoginPage()));
      m_metadata.setHTTPFormErrorPage(envProps.getProperty("httpFormErrorPage",
         m_metadata.getHTTPFormErrorPage()));

      m_metadata.setTrustedCertificate(CertificateUtil.parseCertificate(envProps.getProperty("trust")));
      m_metadata.setGenericRPCAllowed(StringUtil.parseBoolean(envProps.getProperty("authRPC", "true")));

      String sAnonymousUser = envProps.getProperty("anonUser", "anonymous");

      m_metadata.setAnonymousUser(new SimplePrincipal(sAnonymousUser));
      m_metadata.setAnonymousRPCEnabled(StringUtil.parseBoolean(envProps.getProperty("anonRPC", "false")));
      m_metadata.setAnonymousFlatPageEnabled(StringUtil.parseBoolean(envProps.getProperty("anonWeb", "false")));

      final String sRPCPrivilege = envProps.getProperty("rpcPrivilege");

      if (sRPCPrivilege != null)
      {
         m_privilegeFixupList.add(new Fixup()
         {
            public void fixup()
            {
               m_metadata.setGenericRPCPrivilege(m_metadata.getPrimitivePrivilege(sRPCPrivilege));
            }

            public void setMarker(MetadataMarker e)
            {
               e.setResourceName(m_sConfigURL);
            }
         });
      }

      m_dispatcherChannel = new ObjectDispatcherQueue("SysObjectQueueDispatcher");
      m_dispatcherChannel.setMaxReceivers(
         Integer.parseInt(
            envProps.getProperty("maxMessageReceivers", Integer.toString(m_dispatcherChannel.getMaxReceivers()))));
      m_dispatcherChannel.setMaxSenders(
         Integer.parseInt(
            envProps.getProperty("maxMessageSenders", Integer.toString(m_dispatcherChannel.getMaxSenders()))));
   }

   /**
    * Loads connections from a DOM element.
    * @param connectionsElement The DOM element containing the environment. Can be null to load default connections.
    * @param sConnectionsName The name of the environment.
    */
   protected void loadConnections(Element connectionsElement, final String sConnectionsName)
   {
      final Set connectionSet = new HashHolder();

      if (connectionsElement != null)
      {
         XMLUtil.withFirstChildElement(connectionsElement, "DataSourceConnections", false, new ElementHandler()
         {
            public void handleElement(Element connectionsElement)
            {
               XMLUtil.forEachChildElement(connectionsElement, null,
                  m_helper.new ElementHandler("dataSource")
               {
                  protected String getName(Element element)
                  {
                     return XMLUtil.getReqStringAttr(element, "dataSource");
                  }

                  public void handleElement(Element connectionElement, String sDataSource)
                  {
                     DataSource source = m_metadata.getDataSource(sDataSource);
                     String sExpectedElement = m_metadata.getDataSourceTypeElement(source.getType()) + "Connection";

                     if (!sExpectedElement.equals(connectionElement.getNodeName()))
                     {
                        throw new MetadataException("err.meta.dataSourceConnectionElement",
                           new Object[]{connectionElement.getNodeName(), source.getName(),
                           sConnectionsName, sExpectedElement});
                     }

                     if (!connectionSet.add(source))
                     {
                        throw new MetadataException("err.meta.dupDataSourceConnection", new Object[]{sDataSource});
                     }

                     String sAdapter = XMLUtil.getStringAttr(connectionElement, "adapter");

                     if (sAdapter != null)
                     {
                        source.setAdapter(source.getType().getAdapter(sAdapter));
                     }

                     source.setReadLimit(XMLUtil.getIntAttr(connectionElement, "readLimit", source.getReadLimit()));

                     //Load DataSource-specific attributes.
                     ((XMLPersistenceMetadataLoader)m_helper.getClassInstance(source.getType().getLoader()))
                        .loadConnection(connectionElement, source, XMLMetadataLoader.this);
                  }
               });
            }
         });
      }

      //Call loadConnection to initialize DataSources with no entry in the .connections file.
      for (Iterator itr = m_metadata.getDataSourceIterator(); itr.hasNext();)
      {
         DataSource source = (DataSource)itr.next();

         if (!connectionSet.contains(source))
         {
            int nCookie = getHelper().pushMarker(MetadataValidationException.TYPE_NAME, "DataSourceConnection");
            getHelper().pushMarker("dataSourceConnection", source.getName());

            try
            {
               ((XMLPersistenceMetadataLoader)m_helper.getClassInstance(source.getType().getLoader()))
                  .loadConnection(null, source, XMLMetadataLoader.this);
            }
            catch (MetadataException e)
            {
               m_helper.addException(e);
            }
            finally
            {
               m_helper.restoreMarker(nCookie);
            }
         }
      }

      if (connectionsElement != null && !m_bIntegrationExcluded)
      {
         XMLUtil.withFirstChildElement(connectionsElement, "ChannelConnections", false, new ElementHandler()
         {
            public void handleElement(Element connectionsElement)
            {
               XMLUtil.forEachChildElement(connectionsElement, null,
                  m_helper.new ElementHandler("channel")
               {
                  protected String getName(Element element)
                  {
                     return XMLUtil.getReqStringAttr(element, "channel");
                  }

                  public void handleElement(Element connectionElement, String sChannel)
                  {
                     Channel channel = m_metadata.getChannel(sChannel);
                     String sExpectedElement = m_metadata.getChannelTypeElement(channel.getType()) + "Connection";

                     if (!sExpectedElement.equals(connectionElement.getNodeName()))
                     {
                        throw new MetadataException("err.meta.channelConnectionElement",
                           new Object[]{connectionElement.getNodeName(), channel.getName(),
                           sConnectionsName, sExpectedElement});
                     }

                     if (!connectionSet.add(channel))
                     {
                        throw new MetadataException("err.meta.dupChannelConnection", new Object[]{sChannel});
                     }

                     //Load Channel-specific attributes
                     ((XMLIntegrationMetadataLoader)m_helper.getClassInstance(channel.getType().getLoader()))
                        .loadConnection(connectionElement, channel, XMLMetadataLoader.this);
                  }
               });
            }
         });
      }

      //Call loadConnection to initialize Channels with no entry in the .connections file.
      for (Iterator itr = m_metadata.getChannelIterator(); itr.hasNext();)
      {
         Channel channel = (Channel)itr.next();

         if (!connectionSet.contains(channel))
         {
            int nCookie = getHelper().pushMarker(MetadataValidationException.TYPE_NAME, "ChannelConnection");
            getHelper().pushMarker("channelConnection", channel.getName());

            try
            {
               ((XMLIntegrationMetadataLoader)m_helper.getClassInstance(channel.getType().getLoader()))
                  .loadConnection(null, channel, XMLMetadataLoader.this);
            }
            catch (MetadataException e)
            {
               m_helper.addException(e);
            }
            finally
            {
               m_helper.restoreMarker(nCookie);
            }
         }
      }
   }

   /**
    * Loads a security descriptor.
    * @param descriptorElement The DOM element containing the descriptor.
    */
   protected void loadSecurityDescriptor(final Element descriptorElement)
   {
      XMLMetadataHelper.verifyRootElement(descriptorElement, "SecurityDescriptor");

      XMLUtil.withFirstChildElement(descriptorElement, "Privileges", false, new ElementHandler()
      {
         public void handleElement(Element privilegesElement)
         {
            XMLUtil.forEachChildElement(privilegesElement, "Privilege",
               m_helper.new ElementHandler("privilege")
            {
               public void handleElement(Element privilegeElement, String sPrivilegeName)
               {
                  Privilege privilege = new PrimitivePrivilege(sPrivilegeName);

                  m_metadata.addPrivilege(privilege);
                  privilege.setCaption(XMLUtil.getStringAttr(privilegeElement,
                     "caption", privilege.getCaption()));
               }
            });
         }
      });

      XMLUtil.withFirstChildElement(descriptorElement, "PrivilegeGroups", false, new ElementHandler()
      {
         public void handleElement(Element privilegeGroupsElement)
         {
            XMLUtil.forEachChildElement(privilegeGroupsElement, "PrivilegeGroup",
               m_helper.new ElementHandler("privilegeGroup")
            {
               public void handleElement(Element privilegeGroupElement, String sPrivilegeSetName)
               {
                  final PrivilegeGroup privilegeGroup = new PrivilegeGroup(sPrivilegeSetName);

                  m_metadata.addPrivilege(privilegeGroup);
                  privilegeGroup.setCaption(XMLUtil.getStringAttr(privilegeGroupElement,
                     "caption", privilegeGroup.getCaption()));

                  XMLUtil.withFirstChildElement(privilegeGroupElement, "Privileges", false, new ElementHandler()
                  {
                     public void handleElement(Element privilegesElement)
                     {
                        XMLUtil.forEachChildElement(privilegesElement, "Privilege",
                           m_helper.new ElementHandler("privilege")
                        {
                           public void handleElement(Element privilegeElement, final String sPrivilegeName)
                           {
                              m_privilegeFixupList.add(new ContextFixup(m_helper)
                              {
                                 public void fixup()
                                 {
                                    privilegeGroup.addPrivilege(m_metadata.getPrivilege(sPrivilegeName));
                                 }
                              });
                           }
                        });
                     }
                  });
               }
            });
         }
      });
   }

   /**
    * Loads class or aspect metadata from a DOM element.
    * @param classElement The DOM element containing the class.
    * @param metaclass The metaclass object.
    */
   protected void loadMetaclass(final Element classElement, final Metaclass metaclass)
   {
      Lookup posMap = new IdentityHashTab(32);

      XMLMetadataHelper.validateName(metaclass.getName());
      metaclass.setResourceName(m_helper.getCurResourceName());
      metaclass.setForward(false);
      loadDocumentation(classElement, metaclass);

      metaclass.setWhere(m_helper.parse(XMLUtil.getStringAttr(classElement, "where"),
         false, null, null, m_metadata.getGlobalEnvironment()));

      metaclass.setValidation(m_helper.parse(XMLUtil.getStringAttr(classElement, "validation"),
         false, posMap, Undefined.VALUE, m_metadata.getGlobalEnvironment()));

      metaclass.setTextPositionMap(posMap);

      XMLMetadataLoader.parsePatterns(XMLUtil.getStringAttr(classElement, "aspects"),
         new XMLMetadataLoader.PatternHandler()
         {
            public void handlePattern(final String sName, final boolean bInclusive)
            {
               metaclass.addAspectOverride(m_metadata.defineClassAspect(sName, metaclass), bInclusive);
            }
         });

      XMLUtil.withFirstChildElement(classElement, "Attributes", false, new ElementHandler()
      {
         public void handleElement(Element attributesElement)
         {
            XMLUtil.forEachChildElement(attributesElement, "Attribute",
               m_helper.new ElementHandler("attribute")
            {
               public void handleElement(Element attributeElement, String sAttributeName)
               {
                  XMLMetadataHelper.validateName(sAttributeName);

                  Attribute attribute = new Attribute(sAttributeName);

                  attribute.setMetaclass(metaclass);
                  attribute.setDeclarator(metaclass);
                  loadAttribute(attributeElement, attribute);
                  metaclass.addAttribute(attribute);
               }
            });
         }
      });

      XMLUtil.withFirstChildElement(classElement, "Events", false, new ElementHandler()
      {
         public void handleElement(Element eventsElement)
         {
            XMLUtil.forEachChildElement(eventsElement, "Event",
               m_helper.new ElementHandler("event")
            {
               public void handleElement(Element eventElement, String sEventName)
               {
                  XMLMetadataHelper.validateName(sEventName);

                  Event event = new Event(sEventName);

                  event.setMetaclass(metaclass);
                  event.setDeclarator(metaclass);
                  loadEvent(eventElement, event);
                  metaclass.addEvent(event);
               }
            });
         }
      });
   }

   /**
    * Loads a class from a DOM element.
    * @param classElement The DOM element containing the class.
    * @param sName The class name.
    */
   protected void loadClass(Element classElement, final String sClassName)
   {
      XMLMetadataHelper.verifyRootElement(classElement, "Class");

      final Metaclass metaclass = m_metadata.defineMetaclass(sClassName, null);

      if (!metaclass.isForward())
      {
         m_metadata.addMetaclass(metaclass);
      }

      loadMetaclass(classElement, metaclass);

      metaclass.setCaption(XMLUtil.getStringAttr(classElement, "caption", metaclass.getCaption()));
      metaclass.setVisibility(parseVisibility(XMLUtil.getStringAttr(classElement, "visibility"), Metaclass.PUBLIC));

      final String sNameAttribute = XMLUtil.getStringAttr(classElement, "nameAttribute");

      if (sNameAttribute != null)
      {
         m_attributeFixupList.add(new ContextFixup(m_helper)
         {
            public void fixup()
            {
               metaclass.setNameAttribute(sNameAttribute);
            }
         });
      }

      m_inheritanceCheckList.add(new ContextFixup(m_helper)
      {
         public void fixup()
         {
            metaclass.checkInheritance();
         }
      });

      final String sBaseName = XMLUtil.getStringAttr(classElement, "base",
         (sClassName.equals(Metadata.ROOT_CLASS_NAME)) ? null : Metadata.ROOT_CLASS_NAME);

      if (sBaseName != null)
      {
         metaclass.setPointcut(true);
         m_metadata.defineMetaclass(sBaseName, metaclass).addDerived(metaclass);
      }
      else
      {
         m_attributeResolutionList.add(new ContextFixup(m_helper)
         {
            public void fixup()
            {
               metaclass.resolveAttributes(m_machine);
            }
         });

         m_attributeDependencyList.add(new ContextFixup(m_helper)
         {
            public void fixup()
            {
               metaclass.computeInverseDependency(m_attributeDependencySet);
            }
         });

         m_inheritanceFixupList.add(new ClassFixup()
         {
            public void fixup()
            {
               metaclass.resolveInheritance();
            }
         });

         m_inheritance1stPassFixupList.add(new ClassFixup()
         {
            public void fixup()
            {
               metaclass.resolveInheritance1();
            }
         });

         m_inheritance2ndPassFixupList.add(new ClassFixup()
         {
            public void fixup()
            {
               metaclass.compile(m_machine);
            }
         });

         m_inheritance4thPassFixupList.add(new ClassFixup()
         {
            public void fixup()
            {
               metaclass.resolveInheritance4();
            }
         });

         m_class2ndPassList.add(new ContextFixup(m_helper)
         {
            public void fixup()
            {
               metaclass.resolveInitializers(m_machine);
            }
         });
      }

      // Queue for second pass only if a persistence mapping element is present

      XMLUtil.withFirstChildElement(classElement, "PersistenceMapping", false, new ElementHandler()
      {
         public void handleElement(Element element)
         {
            final String sResourceName = m_helper.getCurResourceName();
            final Element clone = (Element)m_tmpDocument.importNode(element, true);

            m_persistenceMappingLoadList.add(new ContextFixup(m_helper)
            {
               public void fixup()
               {
                  int nCookie = m_helper.pushMarker(MetadataValidationException.RESOURCE_NAME, sResourceName);

                  try
                  {
                     metaclass.setPersistenceMapping(loadPersistenceMapping(clone, metaclass));
                  }
                  finally
                  {
                     m_helper.restoreMarker(nCookie);
                  }
               }
            });
         }
      });
   }

   /**
    * Loads a class aspect from a DOM element.
    * @param aspectElement The DOM element containing the aspect.
    */
   protected void loadClassAspect(final Element aspectElement, final String sAspectName)
   {
      XMLMetadataHelper.verifyRootElement(aspectElement, "Aspect");

      final ClassAspect aspect = m_metadata.defineClassAspect(sAspectName, null);

      if (!aspect.isForward())
      {
         m_metadata.addClassAspect(aspect);
      }

      loadMetaclass(aspectElement, aspect);

      XMLMetadataLoader.parsePatterns(XMLUtil.getStringAttr(aspectElement, "pointcuts"),
         new XMLMetadataLoader.PatternHandler()
         {
            public void handlePattern(final String sPattern, final boolean bInclusive)
            {
               aspect.addPointcutPattern(sPattern, bInclusive);
            }
         });

      XMLUtil.withFirstChildElement(aspectElement, "PersistenceMappings", false, new ElementHandler()
      {
         public void handleElement(Element persistenceMappingsElement)
         {
            XMLUtil.forEachChildElement(persistenceMappingsElement, "PersistenceMapping", new ElementHandler()
            {
               public void handleElement(Element persistenceMappingElement)
               {
                  final String sResourceName = m_helper.getCurResourceName();
                  final Element clone = (Element)m_tmpDocument.importNode(persistenceMappingElement, true);

                  m_persistenceMappingLoadList.add(new ContextFixup(m_helper)
                  {
                     public void fixup()
                     {
                        int nCookie = m_helper.pushMarker(MetadataValidationException.RESOURCE_NAME, sResourceName);

                        try
                        {
                           aspect.addPersistenceMapping(loadPersistenceMapping(clone, aspect));
                        }
                        finally
                        {
                           m_helper.restoreMarker(nCookie);
                        }
                     }
                  });
               }
            });
         }
      });
   }

   /**
    * Loads an attribute from a DOM element.
    * @param attributeElement The DOM element containing the attribute.
    * @param attribute The attribute object.
    */
   protected void loadAttribute(Element attributeElement, final Attribute attribute)
   {
      loadDocumentation(attributeElement, attribute);
      attribute.setCaption(XMLUtil.getStringAttr(attributeElement, "caption"));
      attribute.setVisibility(parseVisibility(XMLUtil.getStringAttr(attributeElement, "visibility"), Metaclass.PUBLIC));
      attribute.setReadPrivilege(getPrivilege(attributeElement, "readPrivilege"));
      attribute.setUpdatePrivilege(getPrivilege(attributeElement, "updatePrivilege"));

      final String sURLPrefix = attribute.getURL();
      final String sTypeName = XMLUtil.getReqStringAttr(attributeElement, "type");
      Type type = Primitive.find(sTypeName);

      if (type == null)
      {
         type = m_metadata.defineMetaclass(sTypeName, attribute);
      }

      attribute.setType(type);
      attribute.setRequired(XMLUtil.getBooleanAttr(attributeElement, "required", false));
      attribute.setCollection(XMLUtil.getBooleanAttr(attributeElement, "collection", false));
      attribute.setCompatible(XMLUtil.getBooleanAttr(attributeElement, "compatible", false));
      attribute.setStatic(XMLUtil.getBooleanAttr(attributeElement, "static", false));
      attribute.setReadOnly(XMLUtil.getBooleanAttr(attributeElement, "readOnly", false));
      attribute.setCached(XMLUtil.getBooleanObjAttr(attributeElement, "cached"));

      Lookup posMap = new IdentityHashTab();

      attribute.setValue(m_helper.parse(XMLUtil.getStringAttr(attributeElement, "value"),
         false, sURLPrefix + "$value", posMap, Undefined.VALUE, m_metadata.getGlobalEnvironment()));
      attribute.setInitializer(m_helper.parse(XMLUtil.getStringAttr(attributeElement, "initializer"),
         false, sURLPrefix + "$initializer", posMap, Undefined.VALUE, m_metadata.getGlobalEnvironment()));
      attribute.setValidation(m_helper.parse(XMLUtil.getStringAttr(attributeElement, "validation"),
         false, sURLPrefix + "$validation", posMap, Undefined.VALUE, m_metadata.getGlobalEnvironment()));
      attribute.setWhere(m_helper.parse(XMLUtil.getStringAttr(attributeElement, "where"),
         false, null, null, m_metadata.getGlobalEnvironment()));
      attribute.setCascadeMode(parseCascadeMode(XMLUtil.getStringAttr(attributeElement, "cascade"), Attribute.CASCADE_DEFAULT));

      final String sReverseName = XMLUtil.getStringAttr(attributeElement, "reverse");

      if (sReverseName != null)
      {
         m_attributeFixupList.add(new ContextFixup(m_helper)
         {
            public void fixup()
            {
               if (attribute.getType().isPrimitive())
               {
                  throw new MetadataException("err.meta.primitiveReverseAttrib",
                     new Object[]{sReverseName, attribute.getName(),
                        attribute.getMetaclass().getName()});
               }

               attribute.setReverse(((Metaclass)attribute.getType()).getAttribute(sReverseName));
            }
         });
      }

      final String sEnumeration = XMLUtil.getStringAttr(attributeElement, "enumeration");

      if (sEnumeration != null)
      {
         attribute.setEnumeration(m_metadata.defineMetaclass(sEnumeration, attribute));
      }

      attribute.setConstrained(XMLUtil.getBooleanAttr(attributeElement, "constrained", false));

      if (sEnumeration != null || attribute.isConstrained())
      {
         m_attributeFixupList.add(new ContextFixup(m_helper)
         {
            public void fixup()
            {
               attribute.validateEnumeration(m_metadata);
            }
         });
      }

      final String sAccessAttributeName = XMLUtil.getStringAttr(attributeElement, "access");

      if (sAccessAttributeName != null)
      {
         m_attributeFixupList.add(new ContextFixup(m_helper)
         {
            public void fixup()
            {
               attribute.setAccessAttribute(attribute.getMetaclass().getAttribute(sAccessAttributeName));
            }
         });
      }

      attribute.setDependency((Pair)m_helper.parse(
         XMLUtil.getStringAttr(attributeElement, "dependency"),
         true, null, null, m_metadata.getGlobalEnvironment()));

      attribute.setOrderBy((Pair)m_helper.parse(
         XMLUtil.getStringAttr(attributeElement, "orderBy"),
         true, null, null, m_metadata.getGlobalEnvironment()));

      attribute.setTextPositionMap(posMap);
   }

   /**
    * Load an argument from an XML element, optionally register it with m_classFixupList if
    * required. Fixups must be resolved before class inheritance.
    * @param element The element that defines this argument (not null).
    * @param sName The argument name.
    * @return The defined Argument.
    */
   protected Argument loadArgument(Element element, String sName)
   {
      final Argument arg = new Argument(sName);
      final String sType = XMLUtil.getReqStringAttr(element, "type");
      Type type = Primitive.find(sType);

      if (type == null && s_unsupportedIntrinsicSet.contains(sType))
      {
         type = Primitive.ANY;
      }

      arg.setRequired(XMLUtil.getBooleanAttr(element, "required"));
      arg.setCollection(XMLUtil.getBooleanAttr(element, "collection"));
      arg.setDescription(XMLUtil.getStringAttr(element, "description"));

      if (type == null)
      {
         type = m_metadata.defineMetaclass(sType, arg);
      }

      arg.setType(type);

      return arg;
   }

   /**
    * Loads an event from a DOM element.
    * @param eventElement The DOM element containing the event.
    * @param event The event object.
    */
   protected void loadEvent(Element eventElement, final Event event)
   {
      loadDocumentation(eventElement, event);
      event.setCompatible(XMLUtil.getBooleanAttr(eventElement, "compatible", false));
      event.setStatic(XMLUtil.getBooleanAttr(eventElement, "static", false));
      event.setVisibility(parseVisibility(XMLUtil.getStringAttr(eventElement, "visibility"), Metaclass.PROTECTED));
      event.setPrivilege(getPrivilege(eventElement, "privilege"));
      event.setTransactionMode(parseTransactionMode(XMLUtil.getStringAttr(eventElement, "transaction"), Event.TX_DEFAULT));
      event.setAudited(XMLUtil.getBooleanObjAttr(eventElement, "audit"));
      event.setVarArg(XMLUtil.getBooleanAttr(eventElement, "vararg", false));

      XMLUtil.withFirstChildElement(eventElement, "Result", false, new ElementHandler()
      {
         public void handleElement(Element resultElement)
         {
            event.setResult(loadArgument(resultElement, null));
         }
      });

      String sArgList = XMLUtil.getStringAttr(eventElement, "args");
      final Lookup/*<String, Argument>*/ argMap = new HashTab/*<String, Argument>*/();

      XMLUtil.withFirstChildElement(eventElement, "Arguments", false, new ElementHandler()
      {
         public void handleElement(Element argsElement)
         {
            XMLUtil.forEachChildElement(argsElement, "Argument",
               m_helper.new ElementHandler("argument")
            {
               protected void handleElement(Element argElement, String sArgName)
               {
                  argMap.put(sArgName, loadArgument(argElement, sArgName));
               }
            });
         }
      });

      if (sArgList != null)
      {
         for (StringTokenizer tokenizer = new StringTokenizer(sArgList); tokenizer.hasMoreTokens();)
         {
            String sArgName = tokenizer.nextToken();
            Argument arg = (Argument)argMap.remove(sArgName);

            XMLMetadataHelper.validateName(sArgName);
            event.addArgument((arg == null) ? new Argument(sArgName) : arg);
         }
      }

      MetadataCompoundValidationException e = null;

      for (Iterator/*<Argument>*/ itr = argMap.valueIterator(); itr.hasNext();)
      {
         if (e == null)
         {
            e = new MetadataCompoundValidationException();
         }

         e.addException(
            new MetadataException("err.meta.argLookup", new Object[]{itr.next(), event}));
      }

      if (e != null)
      {
         throw e;
      }

      event.addVariables((Pair)m_helper.parse(XMLUtil.getStringAttr(eventElement, "variables"), true, null, null, m_metadata.getGlobalEnvironment()));

      final String sAccessAttributeName = XMLUtil.getStringAttr(eventElement, "access");

      if (sAccessAttributeName != null)
      {
         m_attributeFixupList.add(new ContextFixup(m_helper)
         {
            public void fixup()
            {
               event.setAccessAttribute(event.getMetaclass().getAttribute(sAccessAttributeName));
            }
         });
      }

      XMLUtil.withFirstChildElement(eventElement, "Actions", false, new ElementHandler()
      {
         public void handleElement(Element actionsElement)
         {
            XMLUtil.forEachChildElement(actionsElement, "Action",
               m_helper.new ElementHandler("action")
            {
               public void handleElement(Element actionElement, String sActionName)
               {
                  String sGroupName = null;
                  int i = sActionName.indexOf(':');

                  if (i > 0 && i < sActionName.length() - 1)
                  {
                     sGroupName = sActionName.substring(0, i);
                     sActionName = sActionName.substring(i + 1);
                  }

                  XMLMetadataHelper.validateName(sActionName);

                  Action action = new Action(sActionName, sGroupName);

                  action.setEvent(event);
                  loadAction(actionElement, action);
                  event.addAction(action);
               }
            });
         }
      });
   }

   /**
    * Loads an action from a DOM element.
    * @param actionElement The DOM element containing the action.
    * @param action The action object.
    */
   protected void loadAction(Element actionElement, final Action action)
   {
      String sType = XMLUtil.getReqStringAttr(actionElement, "type");

      if (sType.equals("main"))
      {
         action.setType(Action.MAIN);
      }
      else if (sType.equals("before"))
      {
         action.setType(Action.BEFORE);
      }
      else if (sType.equals("after"))
      {
         action.setType(Action.AFTER);
      }
      else if (sType.equals("around"))
      {
         action.setType(Action.AROUND);
      }
      else
      {
         throw new MetadataException("err.meta.actionType", new Object[]{sType});
      }

      action.validate(m_metadata, m_helper.getWarnings());

      final String sRelativeName = XMLUtil.getStringAttr(actionElement, "relative");

      if (sRelativeName != null)
      {
         if (action.getType() == Action.MAIN)
         {
            throw new MetadataException("err.meta.relativeMainAction");
         }

         m_actionFixupList.add(new ContextFixup(m_helper)
         {
            public void fixup()
            {
               action.setNextAction(sRelativeName);
            }
         });
      }

      Lookup posMap = new IdentityHashTab();

      action.setCondition(m_helper.parse(XMLUtil.getStringAttr(actionElement, "condition"),
         false, posMap, Boolean.TRUE, m_metadata.getGlobalEnvironment()));

      String sMethodName = XMLUtil.getStringAttr(actionElement, "method");

      if (sMethodName != null)
      {
         int i = sMethodName.lastIndexOf('.');

         if (i < 0)
         {
            throw new MetadataException("err.meta.methodName", new Object[]{sMethodName});
         }

         if (!isRuntimeExcluded())
         {
            String sClassName = sMethodName.substring(0, i);
            sMethodName = sMethodName.substring(i + 1);
            Method[] methodArray;

            try
            {
               Class clazz = Class.forName(sClassName);
               methodArray = clazz.getMethods();
            }
            catch (Exception e)
            {
               throw new MetadataException("err.meta.classLoad", new Object[]{sClassName}, e);
            }

            Method method = null;

            for (i = 0; i < methodArray.length; ++i)
            {
               Method meth = methodArray[i];

               if (Modifier.isPublic(meth.getModifiers()) &&
                  meth.getName().equals(sMethodName))
               {
                  Class[] paramArray = meth.getParameterTypes();

                  if (paramArray.length == action.getEvent().getArgumentCount() + 2)
                  {
                     if (method != null)
                     {
                        throw new MetadataException("err.meta.javaMethodDup", new Object[]{sMethodName, sClassName});
                     }

                     method = meth;
                  }
               }
            }

            if (method == null)
            {
               throw new MetadataException("err.meta.javaMethodLookup",
                  new Object[]{sMethodName, sClassName});
            }

            action.setMethod(method);
         }
      }

      Object body = m_helper.parse(m_helper.getElementValue(actionElement),
         true, posMap, null, m_metadata.getGlobalEnvironment());

      if (body != null)
      {
         if (action.getMethod() != null)
         {
            throw new MetadataException("err.meta.scriptAndMethod", new Object[]{action.getName()});
         }

         action.setBody(body);
      }

      action.setTextPositionMap(posMap);
   }

   /**
    * Loads the values of an enumeration from a DOM element and adds them to the given collection.
    * @param enumerationElement The DOM element containing the enumeration definition.
    * @param valueCollection The collection to which values should be added.
    */
   public static void loadEnumerationValues(Element enumerationElement, final Collection valueCollection)
   {
      XMLMetadataHelper.verifyRootElement(enumerationElement, "Enumeration");

      XMLUtil.withFirstChildElement(enumerationElement, "Values", false, new ElementHandler()
      {
         public void handleElement(Element valuesElement)
         {
            XMLUtil.forEachChildElement(valuesElement, "Value",
               new XMLUtil.ElementHandler()
               {
                  public void handleElement(Element element)
                  {
                     valueCollection.add(XMLUtil.getReqStringAttr(element, "value"));
                  }
               }
            );
         }
      });
   }

   /**
    * Loads an enumeration from a DOM element.
    * @param enumerationElement The DOM element containing the enumeration definition.
    * @param sEnumerationName The enumeration name.
    */
   protected void loadEnumeration(Element enumerationElement, final String sEnumerationName)
   {
      XMLMetadataHelper.verifyRootElement(enumerationElement, "Enumeration");
      XMLMetadataHelper.validateName(sEnumerationName);

      final String sBaseName = XMLUtil.getStringAttr(enumerationElement,
         "base", Metadata.ENUMERATION_CLASS_NAME);

      final Metaclass metaclass = (sEnumerationName.equals(sBaseName)) ? null :
         m_metadata.defineMetaclass(sEnumerationName, null);
      final String sParentEnumeration = XMLUtil.getStringAttr(enumerationElement, "parent");

      if (metaclass != null)
      {
         if (metaclass.isForward())
         {
            metaclass.setResourceName(m_helper.getCurResourceName());
            metaclass.setForward(false);
         }
         else
         {
            m_metadata.addMetaclass(metaclass);
         }

         loadDocumentation(enumerationElement, metaclass);
         m_metadata.defineMetaclass(sBaseName, metaclass).addDerived(metaclass);

         m_inheritanceCheckList.add(new ContextFixup(m_helper)
         {
            public void fixup()
            {
               metaclass.checkInheritance();
            }
         });

         if (sParentEnumeration != null)
         {
            final String sAssociation = XMLUtil.getStringAttr(enumerationElement, "association");

            if (sAssociation == null)
            {
               throw new MetadataException("err.meta.missingEnumAssoc", new Object[]{sEnumerationName});
            }

            final String sReverse = XMLUtil.getStringAttr(enumerationElement, "reverse");

            if (sReverse == null)
            {
               throw new MetadataException("err.meta.missingEnumReverse", new Object[]{sEnumerationName});
            }

            XMLMetadataHelper.validateName(sAssociation);

            m_attributeGenerationList.add(new ContextFixup(m_helper)
            {
               public void fixup()
               {
                  final Metaclass parentMetaclass = m_metadata.getMetaclass(sParentEnumeration);

                  final Attribute parentAttribute = new Attribute(sReverse);

                  parentAttribute.setMetaclass(metaclass);
                  parentAttribute.setDeclarator(metaclass);
                  parentAttribute.setType(parentMetaclass);
                  parentAttribute.setRequired(true);

                  metaclass.addAttribute(parentAttribute);

                  final Attribute childAttribute = new Attribute(sAssociation);

                  childAttribute.setMetaclass(parentMetaclass);
                  childAttribute.setDeclarator(parentMetaclass);
                  childAttribute.setType(metaclass);
                  childAttribute.setCollection(true);
                  childAttribute.setReverse(parentAttribute);

                  parentMetaclass.addAttribute(childAttribute);

                  m_persistenceMappingGenerationList.add(new ContextFixup(m_helper)
                  {
                     public void fixup()
                     {
                        copyAttributeMapping(parentAttribute, metaclass.getAttribute("parent"));
                        copyAttributeMapping(childAttribute, parentMetaclass.getAttribute("children"));
                     }

                     protected void copyAttributeMapping(Attribute dst, Attribute src)
                     {
                        PersistenceMapping mapping = src.getMetaclass().getPersistenceMapping();

                        if (mapping.getAttributeMapping(dst) != null)
                        {
                           return;
                        }

                        for (PersistenceMapping baseMapping = mapping;
                           baseMapping != null && baseMapping.isCompatible(mapping);
                           baseMapping = baseMapping.getBaseMapping())
                        {
                           Attribute attribute = baseMapping.getMetaclass().findAttribute(src.getName());

                           if (attribute != null)
                           {
                              AttributeMapping attributeMapping = baseMapping.getAttributeMapping(attribute);

                              if (attributeMapping != null)
                              {
                                 attributeMapping = (AttributeMapping)attributeMapping.clone();

                                 attributeMapping.setPersistenceMapping(mapping);
                                 attributeMapping.setAttribute(dst);

                                 ((ClassMapping)attributeMapping).setMapping(((Metaclass)dst.getType()).getPersistenceMapping());

                                 mapping.addAttributeMapping(attributeMapping);

                                 return;
                              }
                           }
                        }

                        throw new MetadataException("err.meta.enumAssociation",
                           new Object[]{src.getName(), src.getMetaclass().getName()});
                     }
                  });
               }
            });
         }

         final String sTypeCode = XMLUtil.getReqStringAttr(enumerationElement, "typeCode");

         m_inheritance1stPassFixupList.add(new ContextFixup(m_helper)
         {
            public void fixup()
            {
               Attribute attribute = null;

               for (PersistenceMapping mapping = metaclass.getPersistenceMapping();
                  mapping != null; mapping = mapping.getBaseMapping())
               {
                  attribute = mapping.getTypeCodeAttribute();

                  if (attribute != null)
                  {
                     attribute = metaclass.getDerivedAttribute(attribute);

                     break;
                  }
               }

               if (attribute == null)
               {
                  throw new MetadataException("err.meta.noTypeCodeAttribute",
                     new Object[]{metaclass.getName()});
               }

               attribute.setValue(attribute.getType().convert(sTypeCode));
            }
         });
      }

      if (m_bValidatedOnly)
      {
         XMLUtil.withFirstChildElement(enumerationElement, "Locales", false, new ElementHandler()
         {
            public void handleElement(Element localesElement)
            {
               final Set localeSet = new HashHolder();

               XMLUtil.forEachChildElement(localesElement, "Locale",
                  m_helper.new ElementHandler("locale")
               {
                  public void handleElement(Element localeElement, String sLocale)
                  {
                     if (!localeSet.add(sLocale))
                     {
                        throw new MetadataException("err.meta.enumLocaleDup",
                           new Object[]{sLocale, sEnumerationName});
                     }

                     XMLUtil.getReqStringAttr(localeElement, "caption");
                  }
               });
            }
         });
      }

      final Set valueSet = new HashHolder();
      m_enumerationValueMap.put(sEnumerationName, valueSet);

      XMLUtil.withFirstChildElement(enumerationElement, "Values", false, new ElementHandler()
      {
         public void handleElement(Element valuesElement)
         {
            final Set nameSet = new HashHolder();
            final Set behaveAsSet = new HashHolder();
            final Set externSet = new HashHolder();
            final List fixupList = new ArrayList();

            XMLUtil.forEachChildElement(valuesElement, "Value",
               m_helper.new ElementHandler("value")
            {
               public void handleElement(Element valueElement, String sName)
               {
                  XMLMetadataHelper.validateName(sName);

                  fixupList.add(sName);

                  if (!nameSet.add(sName))
                  {
                     throw new MetadataException("err.meta.enumValueNameDup",
                        new Object[]{sName, sEnumerationName});
                  }

                  final String sValue = XMLUtil.getReqStringAttr(valueElement, "value");

                  if (!valueSet.add(sValue))
                  {
                     throw new MetadataException("err.meta.enumValueDup",
                        new Object[]{sValue, sEnumerationName});
                  }

                  fixupList.add(sValue);

                  boolean bHasBehavior = XMLUtil.getBooleanAttr(valueElement, "hasBehavior", false);

                  fixupList.add(Boolean.valueOf(bHasBehavior));

                  String sBehaveAsValue = XMLUtil.getStringAttr(valueElement, "behaveAsValue");

                  if (sBehaveAsValue != null)
                  {
                     if (bHasBehavior)
                     {
                        if (!sBehaveAsValue.equals(sValue))
                        {
                           throw new MetadataException("err.meta.enumBehaveAsValueMismatch",
                              new Object[]{sBehaveAsValue, sValue, sEnumerationName});
                        }
                     }
                     else
                     {
                        if (sBehaveAsValue.equals(sValue))
                        {
                           throw new MetadataException("err.meta.enumInvalidSelfReference",
                              new Object[]{sValue, sEnumerationName});
                        }

                        behaveAsSet.add(sBehaveAsValue);
                     }
                  }

                  String sExternValue = XMLUtil.getStringAttr(valueElement, "externalValue");

                  if (sExternValue != null)
                  {
                     if (!externSet.add(sExternValue))
                     {
                        throw new MetadataException("err.meta.enumExternValueDup",
                           new Object[]{sExternValue, sEnumerationName});
                     }
                  }

                  final String sParentValue = XMLUtil.getStringAttr(valueElement, "parentValue");

                  if (sParentValue != null)
                  {
                     if (sParentEnumeration == null)
                     {
                        throw new MetadataException("err.meta.enumParentValue", new Object[]{sEnumerationName});
                     }

                     m_inheritanceFixupList.add(new ContextFixup(m_helper)
                     {
                        public void fixup()
                        {
                           Metaclass parent = m_metadata.getMetaclass(sParentEnumeration);

                           if (!((Set)m_enumerationValueMap.get(sParentEnumeration)).contains(parent.getAttribute("value").getType().convert(sParentValue)))
                           {
                              throw new MetadataLookupException("err.meta.enumParentValueLookup", sParentValue, sParentEnumeration);
                           }
                        }
                     });
                  }
                  else
                  {
                     if (sParentEnumeration != null)
                     {
                        throw new MetadataException("err.meta.enumNoParentValue", new Object[]{sValue, sEnumerationName});
                     }
                  }

                  if (m_bValidatedOnly)
                  {
                     XMLUtil.withFirstChildElement(valueElement, "Locales", false, new ElementHandler()
                     {
                        public void handleElement(Element localesElement)
                        {
                           final Set localeSet = new HashHolder();

                           XMLUtil.forEachChildElement(localesElement, "Locale",
                              m_helper.new ElementHandler("locale")
                           {
                              public void handleElement(Element localeElement, String sLocale)
                              {
                                 if (!localeSet.add(sLocale))
                                 {
                                    throw new MetadataException("err.meta.enumValueLocaleDup",
                                       new Object[]{sLocale, sValue, sEnumerationName});
                                 }

                                 XMLUtil.getReqStringAttr(localeElement, "caption");
                              }
                           });
                        }
                     });
                  }
               }
            });

            if (behaveAsSet.size() != 0)
            {
               // Remove the values that have no behavior
               for (int i = 0, n = fixupList.size(); i != n; i += 3)
               {
                  if (!((Boolean)fixupList.get(i + 2)).booleanValue())
                  {
                     valueSet.remove(fixupList.get(i + 1));
                  }
               }

               for (Iterator itr = behaveAsSet.iterator(); itr.hasNext();)
               {
                  String sValue = (String)itr.next();

                  if (!valueSet.contains(sValue))
                  {
                     throw new MetadataLookupException("err.meta.enumBehaveAsValueLookup",
                        sValue, sEnumerationName);
                  }
               }
            }

            valueSet.clear();

            m_attributeGenerationList.add(new ContextFixup(m_helper)
            {
               public void fixup()
               {
                  Primitive type = null;

                  for (Metaclass base = ((metaclass == null) ?
                     m_metadata.getMetaclass(sEnumerationName) : metaclass).getBase();
                     base != null; base = base.getBase())
                  {
                     Attribute attribute = base.findAttribute("value");

                     if (attribute != null)
                     {
                        if (!attribute.getType().isPrimitive())
                        {
                           throw new MetadataException("err.meta.enumBaseValueType",
                              new Object[]{base.getName(), sEnumerationName});
                        }

                        if (attribute.isStatic())
                        {
                           throw new MetadataException("err.meta.enumBaseValueScope",
                              new Object[]{base.getName(), sEnumerationName});
                        }

                        type = (Primitive)attribute.getType();

                        break;
                     }
                  }

                  if (type == null)
                  {
                     throw new MetadataException("err.meta.enumBaseValue", new Object[]{sEnumerationName});
                  }

                  for (int i = 0, n = fixupList.size(); i != n; i += 3)
                  {
                     Object value = fixupList.get(i + 1);

                     if (type != Primitive.STRING)
                     {
                        value = type.convert(value);
                     }

                     if (!valueSet.add(value))
                     {
                        throw new MetadataException("err.meta.enumValueDup",
                           new Object[]{value, sEnumerationName});
                     }

                     if (metaclass != null && ((Boolean)fixupList.get(i + 2)).booleanValue())
                     {
                        Attribute attribute = new Attribute((String)fixupList.get(i));

                        attribute.setMetaclass(metaclass);
                        attribute.setDeclarator(metaclass);
                        attribute.setStatic(true);
                        attribute.setReadOnly(true);
                        attribute.setType(type);
                        attribute.setValue(value);

                        metaclass.addAttribute(attribute);
                     }
                  }
               }
            });
         }
      });
   }

   /**
    * Loads a flow macro from a DOM element.
    * @param macroElement The DOM element containing the macro definition.
    * @param sMacroName The macro name.
    */
   protected void loadFlowMacro(Element macroElement, String sMacroName)
   {
      XMLMetadataHelper.verifyRootElement(macroElement, "Action");

      final FlowMacro macro = new FlowMacro(sMacroName);

      macro.setWorkflowCompatible(XMLUtil.getBooleanAttr(macroElement, "workflow", macro.isWorkflowCompatible()));
      macro.setServiceCompatible(XMLUtil.getBooleanAttr(macroElement, "service", macro.isServiceCompatible()));

      XMLUtil.withFirstChildElement(macroElement, "Arguments", false, new XMLUtil.ElementHandler()
      {
         public void handleElement(Element argumentsElement)
         {
            XMLUtil.forEachChildElement(argumentsElement, "Argument", m_helper.new ElementHandler("argument")
            {
               protected void handleElement(Element argumentElement, String sName)
               {
                  FlowMacro.Argument arg = new FlowMacro.Argument(sName);
                  String sTypeName = XMLUtil.getReqStringAttr(argumentElement, "type");

                  arg.setType((sTypeName.equals("list")) ? null : Primitive.parse(sTypeName));
                  arg.setDefault(m_helper.parse(XMLUtil.getStringAttr(argumentElement, "default"),
                     false, macro.getTextPositionMap(), arg.getDefault(), m_metadata.getGlobalEnvironment()));

                  macro.addArgument(arg);
               }
            });
         }
      });

      XMLUtil.withFirstChildElement(macroElement, "Script", false, new XMLUtil.ElementHandler()
      {
         public void handleElement(Element script)
         {
            macro.setBody((Pair)m_helper.parse(m_helper.getElementValue(script),
               true, macro.getTextPositionMap(), null, m_metadata.getGlobalEnvironment()));
         }
      });

      macro.compile(m_machine);

      m_metadata.addFlowMacro(macro);
   }

   /**
    * Loads a flow macro step.
    * @param stepElement The macro step element.
    * @param sStepName The macro step name.
    * @param activity The activity where to append the step.
    * @param helper The XML metadata helper.
    * @param machine The VM for compilation.
    * @param bFull true to set function arguments.
    * @return The macro script step, or null if no macro is found.
    */
   public static FlowMacroScript loadFlowMacroScript(Element stepElement, String sStepName, Activity activity,
      XMLMetadataHelper helper, Machine machine, boolean bFull)
   {
      Flow flow = activity.getFlow();
      FlowMacro macro = flow.getMetadata().findFlowMacro(stepElement.getNodeName());

      if (macro == null)
      {
         return null;
      }

      if (flow instanceof Workflow && !macro.isWorkflowCompatible() ||
         flow instanceof Service && !macro.isServiceCompatible())
      {
         throw new MetadataException("err.meta." + flow.getPropName() + ".incompatibleMacro",
            new Object[]{macro.getName(), flow.getFullName()});
      }

      FlowMacroScript script = new FlowMacroScript(sStepName);

      script.setActivity(activity);
      script.setMacro(macro);

      NamedNodeMap attributeMap = stepElement.getAttributes();
      Set attributeSet = new HashHolder(attributeMap.getLength());

      for (int i = 0; i < attributeMap.getLength(); ++i)
      {
         attributeSet.add(attributeMap.item(i).getNodeName());
      }

      Object[] args = new Object[macro.getArgumentCount()];

      for (int i = 0, n = macro.getArgumentCount(); i < n; ++i)
      {
         FlowMacro.Argument arg = macro.getArgument(i);
         String sValue = stepElement.getAttribute(arg.getName());

         if (arg.getType() == null || arg.getType() == Primitive.ANY)
         {
            Object value = helper.parse(sValue, arg.getType() == null,
               flow.getPosMap(), Undefined.VALUE,
               machine.getGlobalEnvironment());

            if (bFull)
            {
               script.setArgumentValue(i, value);
            }

            args[i] = (value == Undefined.VALUE) ? arg.getDefault() : value;
         }
         else
         {
            if (sValue == null)
            {
               args[i] = arg.getDefault();

               if (bFull)
               {
                  script.setArgumentValue(i, Undefined.VALUE);
               }
            }
            else
            {
               args[i] = arg.getType().convert(sValue);

               if (bFull)
               {
                  script.setArgumentValue(i, args[i]);
               }
            }
         }

         if (args[i] == Undefined.VALUE)
         {
            throw new MetadataException("err.meta." + flow.getPropName() + ".missingMacroArg",
               new Object[]{arg.getName(), macro.getName(), flow.getFullName()});
         }

         attributeSet.remove(arg.getName());
      }

      attributeSet.remove("name");
      attributeSet.remove("caption");
      attributeSet.remove("description");
      attributeSet.remove("layout");

      if (attributeSet.size() != 0)
      {
         throw new MetadataException("err.meta." + flow.getPropName() + ".macroArgLookup",
            new Object[]{attributeSet.iterator().next(), macro.getName(), flow.getFullName()});
      }

      script.setBody(new Pair(machine.invoke(macro.getFunction(), args)));

      Lookup flowPosMap = flow.getPosMap();
      Lookup macroPosMap = macro.getTextPositionMap();

      if (flowPosMap != null && macroPosMap != null)
      {
         for (Lookup.Iterator itr = macroPosMap.iterator(); itr.hasNext();)
         {
            itr.next();
            flowPosMap.put(itr.getKey(), itr.getValue());
         }
      }

      return script;
   }

   /**
    * Loads a flow macro step.
    * @param stepElement The macro step element.
    * @param sStepName The macro step name.
    * @param activity The activity where to append the step.
    * @return The macro script step, or null if not macro is found.
    */
   protected FlowMacroScript loadFlowMacroScript(Element stepElement, String sStepName, Activity activity)
   {
      return loadFlowMacroScript(stepElement, sStepName, activity, m_helper, m_machine, m_bDocumented);
   }

   /**
    * Loads a workflow from a DOM element.
    * @param workflowElement The DOM element containing the flow definition.
    * @param sWorkflowName The workflow name.
    */
   protected void loadWorkflow(Element workflowElement, String sWorkflowName)
   {
      XMLMetadataHelper.verifyRootElement(workflowElement, "Workflow");

      int nVersion = 0;
      int i = sWorkflowName.lastIndexOf('.');

      if (i > 0)
      {
         try
         {
            nVersion = Integer.parseInt(sWorkflowName.substring(i + 1));
         }
         catch (NumberFormatException e)
         {
            throw new MetadataException("err.meta.workflow.version", new Object[]{sWorkflowName.substring(i + 1)}, e);
         }

         sWorkflowName = sWorkflowName.substring(0, i);
      }

      XMLMetadataHelper.validateName(sWorkflowName);

      final Workflow workflow = new Workflow(sWorkflowName);

      workflow.setResourceName(m_helper.getCurResourceName());
      workflow.setVersion(nVersion);
      workflow.setMetaclass(m_metadata.getMetaclass(XMLUtil.getReqStringAttr(workflowElement, "class")));

      workflow.setAttributes((Pair)m_helper.parse(
         XMLUtil.getStringAttr(workflowElement, "attributes"),
         true, null, null, m_metadata.getGlobalEnvironment()));

      workflow.setPrivileged(XMLUtil.getBooleanAttr(workflowElement, "privileged", workflow.isPrivileged()));
      workflow.setCaption(XMLUtil.getStringAttr(workflowElement, "caption"));

      String sVarList = XMLUtil.getStringAttr(workflowElement, "variables");

      if (sVarList != null)
      {
         for (StringTokenizer tokenizer = new StringTokenizer(sVarList); tokenizer.hasMoreTokens();)
         {
            String sVarName = tokenizer.nextToken();

            XMLMetadataHelper.validateName(sVarName);
            workflow.addVariable(new Variable(sVarName));
         }
      }

      if (isWorkflowHandler(workflowElement))
      {
         loadWorkflowHandler(workflowElement, workflow, workflow);
      }

      m_flowFixupList = new ArrayList();
      loadActivity(workflowElement.getFirstChild(), workflow);
      m_metadata.addWorkflow(workflow);
      m_helper.fixup(m_flowFixupList.iterator());
      m_flowFixupList = null;

      m_persistenceMappingFixupList.add(new ContextFixup(m_helper)
      {
         public void fixup()
         {
            workflow.resolve(m_machine);
         }
      });
   }

   /**
    * Checks if a DOM element contains the workflow event handler attributes.
    */
   protected static boolean isWorkflowHandler(Element element)
   {
      if (XMLUtil.getStringAttr(element, "event") != null)
      {
         return true;
      }

      if (XMLUtil.getStringAttr(element, "args") != null ||
         XMLUtil.getStringAttr(element, "association") != null ||
         XMLUtil.getStringAttr(element, "condition") != null)
      {
         throw new MetadataException("err.meta.workflow.actionEventAttributes");
      }

      return false;
   }

   /**
    * Loads a workflow event handler attributes.
    * @param element The DOM element from which to load the handler.
    * @param handler The handler to load.
    * @param workflow The containing workflow.
    */
   protected void loadWorkflowHandler(Element element, Handler handler, Flow workflow)
   {
      String sAssociation = XMLUtil.getStringAttr(element, "association");

      if (sAssociation != null)
      {
         for (StringTokenizer tokenizer = new StringTokenizer(sAssociation); tokenizer.hasMoreTokens();)
         {
            handler.addAssociation(handler.getTarget().getAttribute(tokenizer.nextToken()));
         }
      }

      String sEventName = XMLUtil.getReqStringAttr(element, "event");
      int nArgCount = XMLUtil.getIntAttr(element, "args", 0);

      handler.setEvent(sEventName, nArgCount);
      handler.setCondition(m_helper.parse(XMLUtil.getStringAttr(element, "condition"),
         false, workflow.getPosMap(), Boolean.TRUE, m_metadata.getGlobalEnvironment()));
   }

   /**
    * Loads a workflow queue step.
    * @param queueElement The DOM element from which to load the queue step.
    * @param assignment The assignment element, which starts the queue processing.
    * @return The last loaded step after the queue element.
    */
   protected Step loadQueue(Element queueElement, final Assignment assignment)
   {
      final Lookup eventMap = new HashTab();
      final Fork fork = new Fork();
      final Decision decision = new Decision();

      fork.setActivity(assignment.getActivity());
      decision.setActivity(assignment.getActivity());

      XMLUtil.forEachChildElement(queueElement, null,
         m_helper.new ElementHandler("queueEvent")
      {
         private boolean m_bTimer;

         protected String getName(Element eventElement)
         {
            return XMLUtil.getStringAttr(eventElement, "name");
         }

         public void handleElement(Element eventElement, String sEventName)
         {
            Node child = eventElement.getFirstChild();
            String sElement = eventElement.getNodeName();
            Concurrent activity = new Concurrent();
            Branch branch = null;

            activity.setFork(fork);

            if (sElement.equals("TimerEvent"))
            {
               if (m_bTimer)
               {
                  throw new MetadataException("err.meta.workflow.multipleQueueTimers",
                     new Object[]{assignment.getName()});
               }

               Timeout timeout = new Timeout();

               timeout.setActivity(activity);
               timeout.setValue(m_helper.parse(XMLUtil.getReqStringAttr(eventElement, "value"),
                  false, activity.getFlow().getPosMap(), null, m_metadata.getGlobalEnvironment()));
               activity.addStep(timeout);

               Wait wait = new Wait(timeout);

               timeout.setNext(wait);
               activity.addStep(wait);

               m_bTimer = true;
            }
            else if (sElement.equals("ClassEvent"))
            {
               AutoCompletion completion = new AutoCompletion(assignment);

               loadWorkflowHandler(eventElement, completion, activity.getFlow());
               activity.addStep(completion);
            }
            else if (sElement.equals("ManualEvent"))
            {
               Object condition = m_helper.parse(XMLUtil.getStringAttr(eventElement, "condition"),
                  false, assignment.getActivity().getFlow().getPosMap(),
                  Boolean.TRUE, m_metadata.getGlobalEnvironment());
               boolean bTarget = loadTarget(eventElement, assignment, condition, sEventName, false);
               Element first = XMLUtil.findFirstElement(child);

               if (first != null && first.getNodeName().equals("UIAction"))
               {
                  loadTarget(first, assignment, condition, sEventName, bTarget);
                  child = first.getNextSibling();
               }

               if (assignment.getManualCompletion() == null)
               {
                  ManualCompletion completion = new ManualCompletion(assignment);

                  activity.addStep(completion);
               }
               else
               {
                  activity = null;
               }
            }
            else if (sElement.equals("ProcessEvent"))
            {
               branch = new Branch();
               decision.addBranch(branch);

               assignment.setSemaphore(true);

               Semaphore semaphore = new Semaphore(assignment.getName() + ":Semaphore", assignment);

               semaphore.setActivity(activity);
               activity.addStep(semaphore);

               Block block = new Block();

               block.setActivity(branch);
               branch.addStep(block);
               loadActivity(child, block.getContainedActivity());
               block.setCleanupCode(semaphore.getExitCode());
            }
            else
            {
               throw new MetadataException("err.meta.workflow.invalidQueueElement",
                  new Object[]{sElement, assignment.getName()});
            }

            if (eventMap.put(sEventName, Boolean.TRUE) != null)
            {
               throw new MetadataException("err.meta.workflow.queueEventDup",
                  new Object[]{sEventName, assignment.getName()});
            }

            Variable var = assignment.getActivity().getFlow().findVariable(assignment.getName());

            if (var == null)
            {
               var = new Variable(assignment.getName());
               assignment.getActivity().getFlow().addVariable(var);
            }

            if (activity != null)
            {
               Object code;

               if (activity.getStep(0) instanceof ManualCompletion)
               {
                  // ((:state'bind ('<assignment>'targetFunction)) '() (:flow'getToken <step>))
                  code =
                     Pair.list(
                        Pair.list(Symbol._STATE, Pair.quote(Symbol.BIND),
                           Pair.list(Pair.quote(assignment), Pair.quote(Symbol.TARGETFUNCTION))),
                        null,
                        Pair.list(Symbol._FLOW, Pair.quote(Symbol.GETTOKEN), Pair.quote(activity.getStep(0))));
               }
               else
               {
                  code = sEventName;
               }

               Script script = new Script();

               script.setBody(Pair.list(Pair.list(Symbol.SET, var.getSymbol(), code)));
               activity.addStep(script);
               fork.addConcurrent(activity);
            }

            if (branch == null)
            {
               branch = new Branch();
               decision.addBranch(branch);
               loadActivity(child, branch);
            }

            branch.setCondition(Pair.list(Symbol.EQUAL_P, var.getSymbol(), sEventName));
         }
      });

      if (fork.getConcurrentCount() == 0)
      {
         throw new MetadataException("err.meta.workflow.eventlessQueue",
            new Object[]{assignment.getName()});
      }

      if (fork.getConcurrentCount() == 1)
      {
         Activity activity = fork.getConcurrent(0);

         if (activity.getStep(0) instanceof Timeout)
         {
            activity.addStep(new Completion(assignment));
         }

         if (decision.getBranchCount() > 1)
         {
            for (int i = 0; i != activity.getStepCount(); ++i)
            {
               assignment.getActivity().addStep(activity.getStep(i));
            }

            return decision;
         }

         if (decision.getBranch(0).getStepCount() != 0)
         {
            for (int i = 0; i != activity.getStepCount(); ++i)
            {
               assignment.getActivity().addStep(activity.getStep(i));
            }

            activity = decision.getBranch(0);

            for (int i = 0; i != activity.getStepCount() - 1; ++i)
            {
               assignment.getActivity().addStep(activity.getStep(i));
            }
         }
         else
         {
            for (int i = 0; i != activity.getStepCount() - 1; ++i)
            {
               assignment.getActivity().addStep(activity.getStep(i));
            }
         }

         return activity.getStep(activity.getStepCount() - 1);
      }

      assignment.getActivity().addStep(fork);

      Join join = new Join(fork);

      join.setAny(true);
      assignment.getActivity().addStep(join);

      return decision;
   }

   /**
    * Loads a workflow assignment target and form from a DOM element.
    * @param element The DOM element.
    * @param assignment The assignment step.
    * @param condition The target selection condition.
    * @param sBranch The branch name.
    * @param bCheckDup True to check for duplicate target/form attributes in the DOM element.
    * @return True if the target or form attributes have been found.
    */
   protected static boolean loadTarget(Element element, Assignment assignment, Object condition, String sBranch, boolean bCheckDup)
   {
      String sTarget = XMLUtil.getStringAttr(element, "target");
      String sForm = XMLUtil.getStringAttr(element, "form");

      if (sTarget == null && sForm == null)
      {
         if (sBranch != null)
         {
            assignment.addTarget(condition, null, null, Assignment.SDI, sBranch);
         }

         return false;
      }

      if (bCheckDup)
      {
         throw new MetadataException("err.meta.workflow.superfluousUIAction",
            new Object[]{assignment.getName()});
      }

      String sMode = XMLUtil.getStringAttr(element, "uimode", "modeless");
      int nMode;

      if (sMode.equals("modal"))
      {
         nMode = Assignment.MODAL;
      }
      else if (sMode.equals("modeless"))
      {
         nMode = Assignment.MODELESS;
      }
      else if (sMode.equals("sdi"))
      {
         nMode = Assignment.SDI;
      }
      else
      {
         throw new MetadataException("err.meta.uimode", new Object[]{sMode});
      }

      assignment.addTarget(condition, sTarget, sForm, nMode, sBranch);

      return true;
   }

   /**
    * Load extended system metadata.
    */
   protected void loadExtendedSystemMetadata()
   {
   }

   /**
    * Load UIEvents from repository.
    * @param baseDescElement Base repository descriptor element.
    * @param rootDescElement Repository descriptor element.
    * @param progress A progress listener.
    */
   protected void loadUIEventMetadata(Element baseDescElement, Element rootDescElement, ProgressListener progress)
   {
   }

   /**
    * Load extended metadata.
    * @param baseDescElement Base repository descriptor element.
    * @param rootDescElement Repository descriptor element.
    */
   public void loadExtendedMetadata(Element baseDescElement, Element rootDescElement)
   {
   }

   /**
    * Validate extended metadata.
    * @param baseDescElement Base repository descriptor element.
    * @param rootDescElement Repository descriptor element.
    * @param progress A progress listener.
    */
   public void validateExtendedMetadata(ProgressListener progress)
   {
   }

   /**
    * Loads a flow decision step.
    * @param element The DOM element, from which to load the decision.
    * @param decision The decision step.
    * @param helper The XML metadata helper.
    * @param metadata The root metadata object.
    * @param loader The branch loader.
    */
   public static void loadDecision(Element element, final Decision decision,
      final XMLMetadataHelper helper, final Metadata metadata, final BranchLoader loader)
   {
      XMLUtil.forEachChildElement(element, loader.getElementName(), new ElementHandler()
      {
         public void handleElement(Element branchElement)
         {
            Branch branch = loader.createBranch();

            branch.setDecision(decision);
            branch.setCondition(helper.parse(XMLUtil.getStringAttr(branchElement, "condition"),
               false, decision.getActivity().getFlow().getPosMap(), Boolean.TRUE,
               metadata.getGlobalEnvironment()));
            branch.setCaption(XMLUtil.getStringAttr(branchElement, "caption"));

            if (branch.getCaption() == null)
            {
               branch.setCaption(XMLUtil.getStringAttr(branchElement, "name"));
            }

            loader.loadActivity(branchElement, branch);
            decision.addBranch(branch);
         }
      });
   }

   /**
    * Parses the given queue name expression. If it is a symbol and not a flow variable,
    * then treat it as a String. Otherwise, it is an expression.
    *
    * @param sQueueName The queue name expression to parse.
    * @param activity The activity.
    * @param machine The machine for parsing.
    * @return The expression.
    */
   public static Object parseQueueName(String sQueueName, Activity activity, Machine machine)
   {
      if (sQueueName == null)
      {
         return null;
      }

      SchemeParser parser = new SchemeParser(machine.getGlobalEnvironment());
      Reader reader = new TextPositionReader(new StringReader(sQueueName));
      Object expr = parser.parse(reader, activity.getFlow().getPosMap());

      if (expr instanceof Symbol && activity.getFlow().findVariable(((Symbol)expr).getName()) == null)
      {
         return sQueueName;
      }

      return expr;
   }

   /**
    * Loads a flow Semaphore step.
    *
    * @param element The DOM element from which to load the steps to execute within the Semaphore.
    * @param sStepName The Semaphore step name.
    * @param outerActivity The activity that will contain the Semaphore.
    * @param machine The VM for script loading.
    * @param loader The activity loader.
    * @return The outer step.
    */
   public Step loadSemaphore(Element element, String sStepName,
      Activity outerActivity, Machine machine, ActivityLoader loader)
   {
      Assignment assignment = new Assignment(sStepName + ":Assignment");

      assignment.setActivity(outerActivity);
      assignment.setQueue(parseQueueName(XMLUtil.getStringAttr(element, "queue"), outerActivity, machine));
      assignment.setCaption(m_helper.parse(XMLUtil.getStringAttr(element, "title"),
         false, outerActivity.getFlow().getPosMap(), XMLUtil.getStringAttr(element, "caption"),
         m_metadata.getGlobalEnvironment()));
      assignment.setAssignee(m_helper.parse(XMLUtil.getStringAttr(element, "assignee"),
         false, outerActivity.getFlow().getPosMap(), assignment.getAssignee(), m_metadata.getGlobalEnvironment()));
      assignment.setOwner(m_helper.parse(XMLUtil.getStringAttr(element, "owner"),
         false, outerActivity.getFlow().getPosMap(), assignment.getOwner(), m_metadata.getGlobalEnvironment()));
      assignment.setFactory((Pair)m_helper.parse(XMLUtil.getStringAttr(element, "factory"),
         true, outerActivity.getFlow().getPosMap(), assignment.getFactory(),
         m_metadata.getGlobalEnvironment()));
      assignment.setSemaphore(true);
      outerActivity.addStep(assignment);

      Block block = new Block();

      block.setActivity(outerActivity);
      assignment.setNext(block);

      Activity blockActivity = block.getContainedActivity();

      Semaphore semaphore = new Semaphore(sStepName, assignment);

      semaphore.setActivity(blockActivity);
      blockActivity.addStep(semaphore);
      loader.loadActivity(element, blockActivity);
      block.setCleanupCode(semaphore.getExitCode());

      return block;
   }

   /**
    * Loads a flow try-catch step.
    * @param element The DOM element, from which to load the try-catch.
    * @param tryCatch The TryCatch step.
    * @param outerActivity The activity that will contain the TryCatch.
    * @param helper The XML metadata helper.
    * @param metadata The root metadata object.
    * @param loader The activity loader.
    * @return The outer step.
    */
   public static Step loadTryCatch(Element element, final TryCatch tryCatch,
      final Activity outerActivity, final XMLMetadataHelper helper, final Metadata metadata,
      final ActivityLoader loader)
   {
      final Pair codeHolder = new Pair(null);
      Step outerStep;

      XMLUtil.withFirstChildElement(element, "Finally", false, new ElementHandler()
      {
         public void handleElement(Element finallyElement)
         {
            codeHolder.setHead(
                  helper.parse(helper.getElementValue(finallyElement),
                  true, outerActivity.getFlow().getPosMap(),
                  null, metadata.getGlobalEnvironment()
               )
            );
         }
      });

      if (codeHolder.getHead() != null)
      {
         Block block = new Block(tryCatch.getName() + ".finalizer");

         block.setActivity(outerActivity);
         block.setCleanupCode((Pair)codeHolder.getHead());
         tryCatch.setActivity(block.getContainedActivity());
         block.getContainedActivity().addStep(tryCatch);
         outerStep = block;
      }
      else
      {
         tryCatch.setActivity(outerActivity);
         outerStep = tryCatch;
      }

      String sVariable = XMLUtil.getStringAttr(element, "variable");

      if (sVariable != null)
      {
         tryCatch.setExceptionVariable(outerActivity.getFlow().getVariable(sVariable));
      }

      XMLUtil.withFirstChildElement(element, "Try", false, new ElementHandler()
      {
         public void handleElement(Element tryElement)
         {
            loader.loadActivity(tryElement, tryCatch.getTry());
         }
      });

      loadDecision(element, tryCatch, helper, metadata, new BranchLoader()
      {
         public String getElementName()
         {
            return "Catch";
         }

         public Branch createBranch()
         {
            return new Catch();
         }

         public void loadActivity(Element element, Activity activity)
         {
            String sException = XMLUtil.getStringAttr(element, "exception");

            if (sException != null)
            {
               ((Catch)activity).setException(helper.getClassObject(sException));
            }

            loader.loadActivity(element, activity);
         }
      });

      return outerStep;
   }

   /**
    * Loads a workflow activity from a DOM element.
    * @param node The first child node of the activity element.
    * @param activity The activity metadata object.
    */
   protected void loadActivity(Node node, final Activity activity)
   {
      for (; node != null; node = node.getNextSibling())
      {
         if (node.getNodeType() != Node.ELEMENT_NODE)
         {
            continue;
         }

         Element element = (Element)node;
         Element nextElement = null;
         String sElement = element.getNodeName();
         String sStepName = XMLUtil.getStringAttr(element, "name");
         int nCookie = m_helper.pushMarker(MetadataValidationException.TYPE_NAME, sElement);
         m_helper.pushMarker("step", sStepName);

         try
         {
            Step step;

            if (sElement.equals("Action"))
            {
               if (isWorkflowHandler(element))
               {
                  step = new Trigger(sStepName);
                  step.setActivity(activity);
                  loadWorkflowHandler(element, (Handler)step, activity.getFlow());
               }
               else
               {
                  step = new Script(sStepName);
                  step.setActivity(activity);
               }

               String sCleanup = XMLUtil.getStringAttr(element, "cleanup");

               if (sCleanup != null)
               {
                  for (StringTokenizer tokenizer = new StringTokenizer(sCleanup); tokenizer.hasMoreTokens();)
                  {
                     step.addCleanupAssoc(Symbol.define(tokenizer.nextToken()));
                  }
               }

               ((Scripted)step).setBody((Pair)m_helper.parse(m_helper.getElementValue(element),
                  true, activity.getFlow().getPosMap(), null, m_metadata.getGlobalEnvironment()));
            }
            else if (sElement.equals("Timeout"))
            {
               Timeout timeout = new Timeout(sStepName);

               timeout.setActivity(activity);
               timeout.setValue(m_helper.parse(XMLUtil.getReqStringAttr(element, "value"),
                  false, activity.getFlow().getPosMap(), null, m_metadata.getGlobalEnvironment()));
               activity.addStep(timeout);
               step = new Wait(timeout);
               timeout.setNext(step);
            }
            else if (sElement.equals("Decision"))
            {
               final Decision decision = new Decision(sStepName);

               decision.setActivity(activity);
               decision.setManual(XMLUtil.getBooleanAttr(element, "manual", false));

               loadDecision(element, decision, m_helper, m_metadata, new BranchLoader()
               {
                  public String getElementName()
                  {
                     return "Branch";
                  }

                  public Branch createBranch()
                  {
                     return new Branch();
                  }

                  public void loadActivity(Element element, Activity activity)
                  {
                     XMLMetadataLoader.this.loadActivity(element.getFirstChild(), activity);
                  }
               });

               step = decision;
            }
            else if (sElement.equals("Loop"))
            {
               loadLoop(element, activity, false, m_helper, m_metadata, new ActivityLoader()
               {
                  public void loadActivity(Element element, Activity activity)
                  {
                     XMLMetadataLoader.this.loadActivity(element.getFirstChild(), activity);
                  }
               });
               step = null;
            }
            else if (sElement.equals("TryCatch"))
            {
               TryCatch tryCatch = new TryCatch(sStepName);

               step = loadTryCatch(element, tryCatch, activity, m_helper, m_metadata, new ActivityLoader()
               {
                  public void loadActivity(Element element, Activity activity)
                  {
                     XMLMetadataLoader.this.loadActivity(element.getFirstChild(), activity);
                  }
               });
            }
            else if (sElement.equals("Join"))
            {
               final Fork fork = new Fork(sStepName);

               fork.setActivity(activity);

               Join join = new Join(fork);
               String sType = XMLUtil.getStringAttr(element, "type", "all");

               if (sType.equals("all"))
               {
                  join.setAny(false);
               }
               else if (sType.equals("any"))
               {
                  join.setAny(true);
               }
               else
               {
                  throw new MetadataException("err.meta.workflow.joinType",
                     new Object[]{sType, activity.getFlow().getFullName()});
               }

               XMLUtil.forEachChildElement(element, "Activity", new ElementHandler()
               {
                  public void handleElement(Element activityElement)
                  {
                     Concurrent concurrent = new Concurrent();

                     concurrent.setFork(fork);
                     loadActivity(activityElement.getFirstChild(), concurrent);
                     fork.addConcurrent(concurrent);
                  }
               });

               activity.addStep(fork);
               step = join;
            }
            else if (sElement.equals("Assignment"))
            {
               Flow workflow = activity.getFlow();
               Assignment assignment = new Assignment(sStepName);

               assignment.setActivity(activity);
               assignment.setQueue(parseQueueName(XMLUtil.getStringAttr(element, "queue"), activity, m_machine));
               assignment.setPriority(m_helper.parse(XMLUtil.getStringAttr(element, "priority"),
                  false, workflow.getPosMap(), assignment.getPriority(), m_metadata.getGlobalEnvironment()));
               assignment.setAssignee(m_helper.parse(XMLUtil.getStringAttr(element, "assignee"),
                  false, workflow.getPosMap(), assignment.getAssignee(), m_metadata.getGlobalEnvironment()));
               assignment.setOwner(m_helper.parse(XMLUtil.getStringAttr(element, "owner"),
                  false, workflow.getPosMap(), assignment.getOwner(), m_metadata.getGlobalEnvironment()));
               assignment.setCaption(m_helper.parse(XMLUtil.getStringAttr(element, "title"),
                  false, workflow.getPosMap(), XMLUtil.getStringAttr(element, "caption"),
                  m_metadata.getGlobalEnvironment()));
               assignment.setFactory((Pair)m_helper.parse(XMLUtil.getStringAttr(element, "factory"),
                  true, workflow.getPosMap(), assignment.getFactory(),
                  m_metadata.getGlobalEnvironment()));

               boolean bTarget = loadTarget(element, assignment, Boolean.TRUE, null, false);

               activity.addStep(assignment);

               if (isWorkflowHandler(element))
               {
                  AutoCompletion unassignment = new AutoCompletion(assignment);

                  loadWorkflowHandler(element, unassignment, workflow);
                  step = unassignment;
               }
               else
               {
                  step = new ManualCompletion(assignment);
               }

               nextElement = XMLUtil.findFirstElement(element.getNextSibling());

               if (nextElement != null)
               {
                  if (nextElement.getNodeName().equals("UIAction"))
                  {
                     loadTarget(element, assignment, Boolean.TRUE, null, bTarget);
                  }
                  else
                  {
                     nextElement = null;
                  }
               }

               assignment.setNext(step);
            }
            else if (sElement.equals("Semaphore"))
            {
               step = loadSemaphore(element, sStepName, activity, m_machine, new ActivityLoader()
               {
                  public void loadActivity(Element element, Activity activity)
                  {
                     XMLMetadataLoader.this.loadActivity(element.getFirstChild(), activity);
                  }
               });
            }
            else if (sElement.equals("Goto"))
            {
               step = new Goto();
               step.setActivity(activity);
            }
            else if (sElement.equals("Queue"))
            {
               final Flow workflow = activity.getFlow();
               Assignment assignment = new Assignment(sStepName);

               assignment.setActivity(activity);
               assignment.setQueue(parseQueueName(XMLUtil.getStringAttr(element, "queue"), activity, m_machine));
               assignment.setPriority(m_helper.parse(XMLUtil.getStringAttr(element, "priority"),
                  false, workflow.getPosMap(), assignment.getPriority(), m_metadata.getGlobalEnvironment()));
               assignment.setAssignee(m_helper.parse(XMLUtil.getStringAttr(element, "assignee"),
                  false, workflow.getPosMap(), assignment.getAssignee(), m_metadata.getGlobalEnvironment()));
               assignment.setOwner(m_helper.parse(XMLUtil.getStringAttr(element, "owner"),
                  false, workflow.getPosMap(), assignment.getOwner(), m_metadata.getGlobalEnvironment()));
               assignment.setCaption(m_helper.parse(XMLUtil.getStringAttr(element, "title"),
                  false, workflow.getPosMap(), XMLUtil.getStringAttr(element, "caption"),
                  m_metadata.getGlobalEnvironment()));
               assignment.setFactory((Pair)m_helper.parse(XMLUtil.getStringAttr(element, "factory"),
                  true, workflow.getPosMap(), assignment.getFactory(),
                  m_metadata.getGlobalEnvironment()));

               activity.addStep(assignment);
               step = loadQueue(element, assignment);
            }
            else if (sElement.equals("UIAction"))
            {
               m_helper.restoreMarker(nCookie);
               throw new MetadataException("err.meta.workflow.misplacedElement",
                  new Object[]{sElement, activity.getFlow().getFullName()});
            }
            else
            {
               step = loadFlowMacroScript(element, sStepName, activity);

               if (step == null)
               {
                  m_helper.restoreMarker(nCookie);
                  throw new MetadataException("err.meta.workflow.invalidElement",
                     new Object[]{sElement, activity.getFlow().getFullName()});
               }
            }

            final String sNextStepName = XMLUtil.getStringAttr(element, "next");

            if (sNextStepName != null)
            {
               if (step == null)
               {
                  step = new Goto();
               }

               final Step fstep = step;

               m_flowFixupList.add(new ContextFixup(getHelper())
               {
                  public void fixup()
                  {
                     fstep.setNext(activity.getFlow().getFlowStep(sNextStepName));
                  }
               });
            }

            if (step != null)
            {
               activity.addStep(step);
            }
         }
         catch (MetadataException e)
         {
            m_helper.addException(e);
         }
         finally
         {
            m_helper.restoreMarker(nCookie);
         }

         if (nextElement != null)
         {
            element = nextElement;
         }
      }
   }

   /**
    * Loads formats from a DOM element.
    * @param fmtElement The DOM element containing the formats.
    */
   protected void loadFormats(Element fmtElement)
   {
      XMLMetadataHelper.verifyRootElement(fmtElement, "Formats");

      XMLUtil.forEachChildElement(fmtElement, "Format",
         m_helper.new ElementHandler("format")
      {
         public void handleElement(Element element, String sName)
         {
            loadFormat(element, sName);
         }
      });
   }

   /**
    * Loads a message format from a DOM element.
    * @param formatElement The DOM element containing the schema.
    * @param sName The persistence engine name.
    */
   protected void loadFormat(Element formatElement, final String sName)
   {
      XMLMetadataHelper.verifyRootElement(formatElement, "Format");

      final Format format = new Format(sName);

      format.setMetadata(m_metadata);
      format.setLoader(m_helper.getClassObject(XMLUtil.getReqStringAttr(formatElement, "loader")));

      String sExporter = XMLUtil.getStringAttr(formatElement, "exporter");

      if (sExporter != null)
      {
         format.setExporter(m_helper.getClassObject(sExporter));
      }

      XMLUtil.withFirstChildElement(formatElement, "Parser", true, new ElementHandler()
      {
         public void handleElement(Element componentElement)
         {
            Component parser = new Component("<MessageParser:" + sName + ">");

            loadComponent(componentElement, parser);
            format.setParser(parser);
         }
      });

      XMLUtil.withFirstChildElement(formatElement, "Formatter", true, new ElementHandler()
      {
         public void handleElement(Element componentElement)
         {
            Component formatter = new Component("<MessageFormatter:" + sName + ">");

            loadComponent(componentElement, formatter);
            format.setFormatter(formatter);
         }
      });

      m_metadata.addFormat(format);
   }

   /**
    * Loads messages from an XSD or WSDL.
    * @param xsdURL The URL to the XSD or WSDL file.
    * @param sName The name of the file being imported.
    * @param messageMap The map into which the imported messages will be placed.
    */
   protected void loadXSD(URL xsdURL, String sName, Lookup messageMap)
   {
      try
      {
         final String sPrefix = sName;
         Message[] xsdMsgArray = XSDMessageImporter.createMessageParts(xsdURL, m_metadata, new XSDMessageImporter.MessageNameResolver()
         {
            public String resolveName(String sSuggestedName)
            {
               StringBuilder buf = new StringBuilder(sPrefix.length() + sSuggestedName.length() + 1);

               if (sPrefix.length() >= 1)
               {
                  buf.append(sPrefix.substring(0, 1).toUpperCase(Locale.ENGLISH));
                  buf.append(sPrefix, 1, sPrefix.length());
               }

               buf.append('_');
               buf.append(sSuggestedName);

               return buf.toString();
            }
         }, false);

         for (int i = 0; i < xsdMsgArray.length; i++)
         {
            if (messageMap.put(xsdMsgArray[i].getName(), xsdMsgArray[i]) != null)
            {
               throw new MetadataException("err.meta.messageDup",
                  new Object[]
                  {
                     xsdMsgArray[i].getName(),
                     m_metadata.getName()
                  }
               );
            }
         }
      }
      catch (IOException ex)
      {
         throw new MetadataException("err.meta.resourceOpen", new Object[] {sName}, ex);
      }
   }

   /**
    * Loads a message from a DOM element.
    * @param messageElement The DOM element containing the message.
    * @param sName The message name.
    */
   protected void loadMessage(Element messageElement, String sName)
   {
      XMLMetadataHelper.verifyRootElement(messageElement, "Message");

      final Message message = new Message(sName);

      message.setMetadata(m_metadata);
      message.setResourceName(m_helper.getCurResourceName());

      String sFormat = XMLUtil.getStringAttr(messageElement, "format");

      if (sFormat != null)
      {
         message.setFormat(m_metadata.getFormat(sFormat));
      }

      final String sResponse = XMLUtil.getStringAttr(messageElement, "response");

      if (sResponse != null)
      {
         addPreInheritanceMessageFixup(new ContextFixup(getHelper())
         {
            public void fixup()
            {
               message.setResponse(m_metadata.getMessage(sResponse));
            }
         });
      }

      String sDerivation = XMLUtil.getStringAttr(messageElement, "derivation", "virtual");

      if (sDerivation.equals("virtual"))
      {
         message.setDerivation(Message.DERIVATION_VIRTUAL);
      }
      else if (sDerivation.equals("final"))
      {
         message.setDerivation(Message.DERIVATION_FINAL);
      }
      else if (sDerivation.equals("abstract"))
      {
         message.setDerivation(Message.DERIVATION_ABSTRACT);
      }
      else
      {
         throw new MetadataException("err.meta.integration.messageDerivation",
            new Object[] {sDerivation, message.getName()});
      }

      final String sBaseMessage = XMLUtil.getStringAttr(messageElement, "base");

      if (!loadMessageRef(null, message, messageElement, sName))
      {
         CompositeMessagePart compMsgPart = new CompositeMessagePartInstance(sName);

         message.setRoot(compMsgPart);
         loadCompositeMessagePart(messageElement, compMsgPart, message);
      }

      m_metadata.addMessage(message);

      if (sBaseMessage != null)
      {
         addPreInheritanceMessageFixup(new ContextFixup(m_helper)
         {
            public void fixup()
            {
               Message baseMessage = m_metadata.getMessage(sBaseMessage);

               message.setBaseMessage(baseMessage);
               baseMessage.addDerivedMessage(message);
            }
         });
      }
   }

   /**
    * Loads a message part from a DOM element.
    * @param partElement The DOM element.
    * @param part The object to load.
    * @param message The root message object.
    */
   protected void loadMessagePart(Element partElement, MessagePart part, Message message)
   {
      part.setMinCount(XMLUtil.getIntAttr(partElement, "minCount", 0));
      part.setMaxCount(XMLUtil.getIntAttr(partElement, "maxCount", 1));
      part.setDeclarator(message);
      loadDocumentation(partElement, part);

      if (message.getFormat() != null)
      {
         MessagePartMapping mapping = ((XMLMessageMappingLoader)m_helper.getClassInstance(message.getFormat().getLoader()))
            .loadMapping(partElement, message, part, message.getFormat(), this);

         part.setMapping(mapping);

         if (mapping != null)
         {
            mapping.init(part);
         }
      }
   }

   /**
    * Initializes base attributes of a CompositeMessagePart instance
    * @param partElement The DOM element.
    * @param part The object to initialize.
    * @param message The root message object.
    */
   protected void initCompositeMessagePart(Element partElement, final CompositeMessagePart composite, final Message message)
   {
      // Aggregation does not apply to referenced message parts
      if (composite instanceof CompositeMessagePartInstance)
      {
         CompositeMessagePartInstance part = (CompositeMessagePartInstance)composite;
         String sAggregation = XMLUtil.getStringAttr(partElement, "aggregation", "sequential");

         if (sAggregation != null)
         {
            if (sAggregation.equals("sequential"))
            {
               part.setAggregation(CompositeMessagePart.SEQUENTIAL);
            }
            else if (sAggregation.equals("random"))
            {
               part.setAggregation(CompositeMessagePart.RANDOM);
            }
            else if (sAggregation.equals("single"))
            {
               part.setAggregation(CompositeMessagePart.SINGLE);
            }
            else
            {
               throw new MetadataException("err.meta.integration.aggregation",
                  new Object[]{sAggregation});
            }
         }

         part.setLax(XMLUtil.getBooleanAttr(partElement, "lax", part.isLax()));
      }

      loadMessagePart(partElement, composite, message);
   }

   /**
    * Loads a message ref from a DOM element if there exists a "ref" attribute
    * @param composite The parent composite message if any.
    * @param message The root message.
    * @param msgElement The DOM element to check.
    * @param sName The name of the message part to create.
    */
   protected boolean loadMessageRef(final CompositeMessagePart composite, final Message message, Element msgElement, String sName)
   {
      final String sRef = XMLUtil.getStringAttr(msgElement, "ref");

      if (sRef == null)
      {
         return false;
      }

      final CompositeMessagePartRef partRef = new CompositeMessagePartRef(sName);

      if (composite == null)
      {
         message.setRoot(partRef);
      }
      else
      {
         composite.addPart(partRef);
      }

      assert (composite == null) || (composite.getRoot() == message.getRoot());

      initCompositeMessagePart(msgElement, partRef, message);

      int nCookie = m_helper.pushMarker("fullName", partRef.getFullPath());

      try
      {
         addPreInheritanceMessageFixup(new ContextFixup(m_helper)
         {
            public void fixup()
            {
               CompositeMessagePart root = m_metadata.getMessage(sRef).getRoot();

               partRef.setRefPart(root);

               MessagePartMapping rootMapping = root.getMapping();

               if (rootMapping != null)
               {
                  if (partRef.getMapping() == null)
                  {
                     partRef.setMapping(rootMapping);
                  }

                  message.addRef(partRef);
               }
            }
         });
      }
      finally
      {
         m_helper.restoreMarker(nCookie);
      }

      return true;
   }

   /**
    * Loads a composite message part from a DOM element.
    * @param partElement The DOM element.
    * @param part The object to load.
    * @param message The root message object.
    */
   protected void loadCompositeMessagePart(Element partElement, final CompositeMessagePart composite, final Message message)
   {
      initCompositeMessagePart(partElement, composite, message);

      XMLUtil.withFirstChildElement(partElement, "Parts", false, new ElementHandler()
      {
         public void handleElement(Element partsElement)
         {
            XMLUtil.forEachChildElement(partsElement, null,
               m_helper.new ElementHandler("messagePart")
            {
               protected void handleElement(Element element, String sName)
               {
                  if (element.getNodeName().equals("Message"))
                  {
                     if (!loadMessageRef(composite, message, element, sName))
                     {
                        CompositeMessagePart part = new CompositeMessagePartInstance(sName);

                        composite.addPart(part);
                        loadCompositeMessagePart(element, part, message);
                     }
                  }
                  else if (element.getNodeName().equals("Value"))
                  {
                     final PrimitiveMessagePart part = new PrimitiveMessagePart(sName);

                     composite.addPart(part);
                     part.setType(Primitive.parse(XMLUtil.getReqStringAttr(element, "type")));
                     loadMessagePart(element, part, message);

                     XMLUtil.withFirstChildElement(element, "Enumerations", false, new ElementHandler()
                     {
                        public void handleElement(Element enumerationsElement)
                        {
                           XMLUtil.forEachChildElement(enumerationsElement, "Enumeration", new ElementHandler()
                           {
                              public void handleElement(Element enumerationElement)
                              {
                                 part.addEnumeration(XMLUtil.getReqStringAttr(enumerationElement, "value"));
                              }
                           });
                        }
                     });
                  }
                  else
                  {
                     throw new MetadataException("err.meta.msgPartElement", new Object[]{element.getNodeName()});
                  }
               }
            });
         }
      });
   }

   /**
    * Calls MessagePartMapping.finish() on the root part of all messages.
    * @param iterator An iterator over the set of messages on which to
    * call finish().
    */
   public static void finishMessagePartMappings(Iterator iterator)
   {
      MetadataCompoundValidationException comp = null;

      while (iterator.hasNext())
      {
         Message message = (Message)iterator.next();
         CompositeMessagePart root = message.getRoot();
         MessagePartMapping mapping = root.getMapping();

         if (mapping != null)
         {
            try
            {
               mapping.finish(root);
            }
            catch (UncheckedException ex)
            {
               if (comp == null)
               {
                  comp = new MetadataCompoundValidationException();
               }

               message.addException(comp, ex);
            }
         }
      }

      if (comp != null)
      {
         throw comp;
      }
   }

   /**
    * Gets a transformation source or destination.
    * @param sName The end-point name.
    * @param sTransformationName The name of the transformation.
    * @return The end-point. The endpoint.
    */
   protected TransformationEndpoint getTransformationEndpoint(String sName, String sTransformationName)
   {
      TransformationEndpoint meta;

      if (sName.startsWith("class:"))
      {
         meta = m_metadata.getMetaclass(sName.substring("class:".length()));
      }
      else
      {
         meta = m_metadata.findMessage(sName);
      }

      if (meta == null)
      {
         meta = m_metadata.findMetaclass(sName);
      }

      if (meta == null)
      {
         throw new MetadataLookupException("err.meta.transformationEndpointLookup", sName, sTransformationName);
      }

      return meta;
   }

   /**
    * Loads a message transformation from a DOM element.
    * @param transformationElement The DOM element containing the message.
    * @param sName The transformation name.
    */
   protected void loadTransformation(Element transformationElement, String sName)
   {
      XMLMetadataHelper.verifyRootElement(transformationElement, "Transformation");

      final Transformation transformation = new Transformation(sName);
      final String sURLPrefix = "transformation:" + sName;

      transformation.setCategory(XMLUtil.getStringAttr(transformationElement, "category"));
      transformation.setResourceName(m_helper.getCurResourceName());
      transformation.setSource(getTransformationEndpoint(XMLUtil.getReqStringAttr(transformationElement, "source"), sName));
      transformation.setDestination(getTransformationEndpoint(XMLUtil.getReqStringAttr(transformationElement, "destination"), sName));

      String sArguments = XMLUtil.getStringAttr(transformationElement, "args");

      if (sArguments != null)
      {
         StringTokenizer tokenizer = new StringTokenizer(sArguments);

         while (tokenizer.hasMoreTokens())
         {
            transformation.addArgument(tokenizer.nextToken());
         }
      }

      final String sBase = XMLUtil.getStringAttr(transformationElement, "base");

      if (sBase != null)
      {
         addTransformationFixup(new ContextFixup(m_helper)
         {
            public void fixup()
            {
               Transformation baseTransformation = m_metadata.getTransformation(sBase);

               transformation.setBaseTransformation(baseTransformation);
               baseTransformation.addDerivedTransformation(transformation);
            }
         });
      }

      String sDerivation = XMLUtil.getStringAttr(transformationElement, "derivation", "virtual");

      if (sDerivation.equals("virtual"))
      {
         transformation.setDerivation(Transformation.DERIVATION_VIRTUAL);
      }
      else if (sDerivation.equals("final"))
      {
         transformation.setDerivation(Transformation.DERIVATION_FINAL);
      }
      else if (sDerivation.equals("abstract"))
      {
         transformation.setDerivation(Transformation.DERIVATION_ABSTRACT);
      }
      else
      {
         throw new MetadataException("err.meta.integration.transformationDerivation",
            new Object[] {sDerivation, transformation.getName()});
      }

      transformation.setPrimary(XMLUtil.getBooleanAttr(transformationElement, "primary", true));

      XMLUtil.withFirstChildElement(transformationElement, "Initializer", false, new ElementHandler()
      {
         public void handleElement(Element initializerElement)
         {
            transformation.setInitializer((Pair)m_helper.parse(m_helper.getElementValue(initializerElement),
               true, sURLPrefix + ".initializer", transformation.getPosMap(), null, m_metadata.getGlobalEnvironment()));
         }
      });

      XMLUtil.withFirstChildElement(transformationElement, "Mappings", false, new ElementHandler()
      {
         public void handleElement(Element mappingsElement)
         {
            XMLUtil.forEachChildElement(mappingsElement, "Mapping",
               m_helper.new ElementHandler("mapping")
            {
               private int m_nOrdinal;

               public void handleElement(Element mappingElement, String sMappingName)
               {
                  TransformationMapping mapping = new TransformationMapping(sMappingName);

                  if (sMappingName == null)
                  {
                     sMappingName = String.valueOf(m_nOrdinal);
                  }

                  loadTransformationMapping(mappingElement, sURLPrefix + ".mapping." + sMappingName, mapping, transformation);
                  transformation.addMapping(mapping);

                  m_nOrdinal += 1;
               }

               protected String getName(Element element)
               {
                  return XMLUtil.getStringAttr(element, "name");
               }
            });
         }
      });

      XMLUtil.withFirstChildElement(transformationElement, "Finalizer", false,new ElementHandler()
      {
         public void handleElement(Element finalizerElement)
         {
            transformation.setFinalizer((Pair)m_helper.parse(m_helper.getElementValue(finalizerElement),
               true, sURLPrefix + ".finalizer", transformation.getPosMap(), null, m_metadata.getGlobalEnvironment()));
         }
      });

      m_metadata.addTransformation(transformation);
   }

   /**
    * Loads a transformation mapping from a DOM element.
    * @param mappingElement The DOM element containing the mapping.
    * @param sCodeURL The code URL for this element
    * @param mapping The mapping to load.
    * @param transformation The containing transformation.
    */
   protected void loadTransformationMapping(Element mappingElement,
      final String sCodeURL, final TransformationMapping mapping, final Transformation transformation)
   {
      mapping.setRootPart(transformation.getDestination());
      mapping.setCondition(m_helper.parse(XMLUtil.getStringAttr(mappingElement, "condition"),
         false, sCodeURL + "$condition", transformation.getPosMap(), mapping.getCondition(), m_metadata.getGlobalEnvironment()));

      String sDestination = XMLUtil.getStringAttr(mappingElement, "destination");

      if (sDestination != null)
      {
         StringTokenizer tokenizer = new StringTokenizer(sDestination);

         while (tokenizer.hasMoreTokens())
         {
            String sPartName = tokenizer.nextToken();
            boolean bFixed = sPartName.endsWith("$");

            if (bFixed)
            {
               sPartName = sPartName.substring(0, sPartName.length() - 1);
            }

            mapping.addDestination(sPartName, bFixed);
         }
      }

      XMLUtil.withFirstChildElement(mappingElement, "Sources", false, new ElementHandler()
      {
         public void handleElement(Element sourcesElement)
         {
            XMLUtil.forEachChildElement(sourcesElement, "Source",
               m_helper.new ElementHandler("source")
            {
               public void handleElement(Element sourceElement, final String sSourceName)
               {
                  String sSource = XMLUtil.getStringAttr(sourceElement, "source");
                  final String sMapping = XMLUtil.getStringAttr(sourceElement, "mapping");

                  if (sSource != null && sMapping != null)
                  {
                     throw new MetadataException("err.meta.transformation.bothSourceMapping",
                        new Object[]{mapping.getName(), transformation.getName()});
                  }

                  final TransformationArgument argument = new TransformationArgument(sSourceName);

                  if (sMapping == null)
                  {
                     if (sSource == null)
                     {
                        sSource = "";
                     }

                     StringTokenizer tokenizer = new StringTokenizer(sSource);
                     TransformationSource source = transformation.getRoot();

                     while (tokenizer.hasMoreTokens())
                     {
                        String sPartName = tokenizer.nextToken();
                        boolean bFixed = sPartName.endsWith("$");

                        if (bFixed)
                        {
                           sPartName = sPartName.substring(0, sPartName.length() - 1);
                        }

                        source = source.addChild(sPartName);

                        if (bFixed)
                        {
                           mapping.setFixedSource(source, true);
                        }
                     }

                     argument.setSource(source);
                  }
                  else
                  {
                     m_postInheritanceTransformationFixupList.add(new ContextFixup(getHelper())
                     {
                        public void fixup()
                        {
                           argument.setMapping(transformation, sMapping);
                        }
                     });
                  }

                  argument.setNull(XMLUtil.getBooleanAttr(sourceElement, "null", argument.isNull()));
                  argument.setDefaultValue(m_helper.parse(XMLUtil.getStringAttr(sourceElement, "default"), false,
                     sCodeURL + '.' + argument.getName(), transformation.getPosMap(), argument.getDefaultValue(),
                     m_metadata.getGlobalEnvironment()));
                  mapping.addArgument(argument);
               }

               protected String getName(Element element)
               {
                  return XMLUtil.getStringAttr(element, "name");
               }
            });
         }
      });

      Element scriptElement = XMLUtil.findChildElement(mappingElement, "Script");
      Element transformElement = XMLUtil.findChildElement(mappingElement, "Transformation");

      if (scriptElement != null && transformElement != null)
      {
         throw new MetadataException("err.meta.transformation.bothScriptTransformation",
            new Object[]{mapping.getName(), transformation.getName()});
      }

      if (scriptElement != null)
      {
         mapping.setScript((Pair)m_helper.parse(m_helper.getElementValue(scriptElement),
            true, sCodeURL, transformation.getPosMap(), null, m_metadata.getGlobalEnvironment()));
      }

      if (transformElement != null)
      {
         mapping.setTransformation(m_helper.parse(XMLUtil.getReqStringAttr(transformElement, "ref"),
            false, transformation.getPosMap(), null, m_metadata.getGlobalEnvironment()));
         mapping.setTransformationArguments(m_helper.parse(XMLUtil.getStringAttr(transformElement, "args"),
            true, transformation.getPosMap(), null, m_metadata.getGlobalEnvironment()));
         mapping.setTransformationInput(m_helper.parse(XMLUtil.getStringAttr(transformElement, "source"),
            false, transformation.getPosMap(), null, m_metadata.getGlobalEnvironment()));
      }
   }

   /**
    * Loads a service interface from a DOM element.
    * @param interfaceElement The DOM element containing the interface definition.
    * @param sInterfaceName The interface name.
    */
   protected void loadInterface(Element interfaceElement, String sInterfaceName)
   {
      XMLMetadataHelper.verifyRootElement(interfaceElement, "Interface");
      XMLMetadataHelper.validateName(sInterfaceName);

      final Interface iface = m_metadata.defineInterface(sInterfaceName, null);

      if (!iface.isForward())
      {
         m_metadata.addInterface(iface);
      }

      iface.setForward(false);
      iface.setResourceName(m_helper.getCurResourceName());

      String sFormatName = XMLUtil.getStringAttr(interfaceElement, "format");

      if (sFormatName != null)
      {
         iface.setFormat(m_metadata.getFormat(sFormatName));
      }

      XMLUtil.withFirstChildElement(interfaceElement, "Requests", false, new ElementHandler()
      {
         public void handleElement(Element requestsElement)
         {
            XMLUtil.forEachChildElement(requestsElement, "Request", m_helper.new ElementHandler("request")
            {
               public void handleElement(Element requestElement, String sMessageName)
               {
                  iface.addRequest(m_metadata.getMessage(sMessageName));
               }

               protected String getName(Element element)
               {
                  return XMLUtil.getReqStringAttr(element, "message");
               }
            });
         }
      });

      XMLUtil.withFirstChildElement(interfaceElement, "Responses", false, new ElementHandler()
      {
         public void handleElement(Element requestsElement)
         {
            XMLUtil.forEachChildElement(requestsElement, "Response", m_helper.new ElementHandler("response")
            {
               public void handleElement(Element requestElement, String sMessageName)
               {
                  iface.addResponse(m_metadata.getMessage(sMessageName));
               }

               protected String getName(Element element)
               {
                  return XMLUtil.getReqStringAttr(element, "message");
               }
            });
         }
      });

      addSingletonFixup(new ContextFixup(m_helper)
      {
         public void fixup()
         {
            MessageTable table = iface.getRequestTable();

            if (table.getFormat() != null)
            {
               ((MessageParser)table.getFormat().getParser().getInstance(null)).initializeMessageTable(table);
            }

            table = iface.getResponseTable();

            if (table.getFormat() != null)
            {
               ((MessageParser)table.getFormat().getParser().getInstance(null)).initializeMessageTable(table);
            }
         }
      });
   }

   /**
    * Loads a service from a DOM element.
    * @param serviceElement The DOM element containing the flow definition.
    * @param sServiceName The service name.
    */
   protected void loadService(Element serviceElement, String sServiceName)
   {
      XMLMetadataHelper.verifyRootElement(serviceElement, "Service");

      int nVersion = 0;
      int i = sServiceName.lastIndexOf('.');

      if (i > 0)
      {
         try
         {
            nVersion = Integer.parseInt(sServiceName.substring(i + 1));
         }
         catch (NumberFormatException e)
         {
            throw new MetadataException("err.meta.service.version", new Object[]{sServiceName.substring(i + 1)}, e);
         }

         sServiceName = sServiceName.substring(0, i);
      }

      XMLMetadataHelper.validateName(sServiceName);

      final Service service = new Service(sServiceName);

      service.setResourceName(m_helper.getCurResourceName());
      service.setMetadata(m_metadata);
      service.setVersion(nVersion);
      service.setPrivileged(XMLUtil.getBooleanAttr(serviceElement, "privileged", service.isPrivileged()));
      service.setCaption(XMLUtil.getStringAttr(serviceElement, "caption"));

      String sInterface = XMLUtil.getStringAttr(serviceElement, "interface");

      if (sInterface != null)
      {
         service.setInterface(m_metadata.defineInterface(sInterface, service));
      }

      String sArgList = XMLUtil.getStringAttr(serviceElement, "args");

      if (sArgList != null)
      {
         for (StringTokenizer tokenizer = new StringTokenizer(sArgList); tokenizer.hasMoreTokens();)
         {
            String sArgName = tokenizer.nextToken();

            XMLMetadataHelper.validateName(sArgName);
            service.addArgument(new Variable(sArgName));
         }
      }

      String sVarList = XMLUtil.getStringAttr(serviceElement, "variables");

      if (sVarList != null)
      {
         for (StringTokenizer tokenizer = new StringTokenizer(sVarList); tokenizer.hasMoreTokens();)
         {
            String sVarName = tokenizer.nextToken();

            XMLMetadataHelper.validateName(sVarName);
            service.addVariable(new Variable(sVarName));
         }
      }

      service.setPrivilege(getPrivilege(serviceElement, "privilege"));
      m_flowFixupList = new ArrayList();
      loadSequence(serviceElement, service);
      m_metadata.addService(service);
      m_helper.fixup(m_flowFixupList.iterator());
      m_flowFixupList = null;

      m_persistenceMappingFixupList.add(new ContextFixup(m_helper)
      {
         public void fixup()
         {
            service.resolve(m_machine);
         }
      });
   }

   /**
    * Loads a message flow sequence from a DOM element.
    * @param sequenceElement The DOM element containing the sequence.
    * @param activity The activity metadata object.
    */
   protected void loadSequence(Element sequenceElement, final Activity activity)
   {
      XMLUtil.forEachChildElement(sequenceElement, null, m_helper.new ElementHandler("step")
      {
         public void handleElement(Element element, String sStepName)
         {
            String sElement = element.getNodeName();
            Step step;

            if (sElement.equals("Script"))
            {
               Script script = new Script(sStepName);

               script.setActivity(activity);
               script.setBody((Pair)m_helper.parse(m_helper.getElementValue(element),
                  true, activity.getFlow().getPosMap(), Jump.BODY, m_metadata.getGlobalEnvironment()));
               step = script;
            }
            else if (sElement.equals("Transform"))
            {
               Transform transform = new Transform(sStepName);

               transform.setActivity(activity);
               transform.setTransformationExpression(m_helper.parse(XMLUtil.getReqStringAttr(element, "transformation"),
                  false, activity.getFlow().getPosMap(), null, m_metadata.getGlobalEnvironment()));
               transform.setTransformationArguments(m_helper.parse(XMLUtil.getStringAttr(element, "args", null),
                  true, activity.getFlow().getPosMap(), null, m_metadata.getGlobalEnvironment()));
               step = transform;
            }
            else if (sElement.equals("Dispatch"))
            {
               final Dispatch dispatch = new Dispatch(sStepName);

               dispatch.setActivity(activity);
               loadDecision(element, dispatch, m_helper, m_metadata, new BranchLoader()
               {
                  public String getElementName()
                  {
                     return "Case";
                  }

                  public Branch createBranch()
                  {
                     return new Case();
                  }

                  public void loadActivity(Element element, Activity activity)
                  {
                     String sMessage = XMLUtil.getStringAttr(element, "message");

                     if (sMessage != null)
                     {
                        ((Case)activity).setMessage(m_metadata.getMessage(sMessage));
                     }

                     loadSequence(element, activity);
                  }
               });

               step = dispatch;
            }
            else if (sElement.equals("Loop"))
            {
               loadLoop(element, activity, true, m_helper, m_metadata, new ActivityLoader()
               {
                  public void loadActivity(Element element, Activity activity)
                  {
                     loadSequence(element, activity);
                  }
               });
               step = null;
            }
            else if (sElement.equals("TryCatch"))
            {
               TryCatch tryCatch = new TryCatch(sStepName);

               step = loadTryCatch(element, tryCatch, activity, m_helper, m_metadata, new ActivityLoader()
               {
                  public void loadActivity(Element element, Activity activity)
                  {
                     loadSequence(element, activity);
                  }
               });
            }
            else if (sElement.equals("Persist"))
            {
               final Persist persist = new Persist(sStepName);
               persist.setActivity(activity);
               persist.setRespond(XMLUtil.getBooleanAttr(element, "respond", persist.isRespond()));
               persist.setOnError(getOnError(element));
               step = persist;
            }
            else if (sElement.equals("Sync"))
            {
               Sync sync = new Sync(sStepName);
               sync.setSyncLink(m_helper.parse(XMLUtil.getReqStringAttr(element, "link"), false, activity.getFlow().getPosMap(), null, m_metadata.getGlobalEnvironment()));
               sync.setSyncScript((Pair)m_helper.parse(m_helper.getElementValue(element), true, activity.getFlow().getPosMap(), null, m_metadata.getGlobalEnvironment()));
               sync.setOnError(getOnError(element));
               step = sync;
            }
            else if (sElement.equals("Send"))
            {
               Send send = new Send(sStepName);
               send.setActivity(activity);
               String sOutput = XMLUtil.getStringAttr(element, "output");
               send.setOutputExpression(m_helper.parse(sOutput, false, activity.getFlow().getPosMap(), null, m_metadata.getGlobalEnvironment()));
               String sInterface = XMLUtil.getStringAttr(element, "interface");

               if (sInterface != null)
               {
                  send.setInterface(m_metadata.defineInterface(sInterface, send));
               }

               step = send;
            }
            else if (sElement.equals("SendReceive"))
            {
               SendReceive send = new SendReceive(sStepName);

               send.setActivity(activity);

               String sOutput = XMLUtil.getStringAttr(element, "output");

               if (sOutput != null)
               {
                  Object outputExpression = m_helper.parse(sOutput, false, activity.getFlow().getPosMap(), Boolean.TRUE, m_metadata.getGlobalEnvironment());
                  send.setOutputExpression(outputExpression);
               }

               String sInterface = XMLUtil.getStringAttr(element, "interface");

               if (sInterface != null)
               {
                  send.setInterface(m_metadata.defineInterface(sInterface, send));
               }

               step = send;
            }
            else if (sElement.equals("Semaphore"))
            {
               step = loadSemaphore(element, sStepName, activity, m_machine, new ActivityLoader()
               {
                  public void loadActivity(Element element, Activity activity)
                  {
                     loadSequence(element, activity);
                  }
               });
            }
            else if (sElement.equals("Goto"))
            {
               step = new Jump();
               step.setActivity(activity);
            }
            else
            {
               step = loadFlowMacroScript(element, sStepName, activity);

               if (step == null)
               {
                  throw new MetadataException("err.meta.service.invalidElement",
                     new Object[]{sElement, activity.getFlow().getFullName()});
               }
            }

            final String sNextStepName = XMLUtil.getStringAttr(element, "next");

            if (sNextStepName != null)
            {
               if (step == null)
               {
                  step = new Jump();
               }

               final Step fstep = step;

               m_flowFixupList.add(new ContextFixup(getHelper())
               {
                  public void fixup()
                  {
                     fstep.setNext(activity.getFlow().getFlowStep(sNextStepName));
                  }
               });
            }

            if (step != null)
            {
               activity.addStep(step);
            }
         }

         /**
          * Load onError value for the Persist step
          * @param persist step to be configured
          * @param element element to load configuration from
          */
         private byte getOnError(Element element)
         {
            String sOnError = XMLUtil.getStringAttr(element, "onError", "fail");
            byte nOnError = Persist.FAIL_ON_ERROR;

            if (sOnError.equals("collect"))
            {
               nOnError = Persist.COLLECT_ON_ERROR;
            }
            else if (sOnError.equals("commit"))
            {
               nOnError = Persist.COMMIT_ON_ERROR;
            }

            return nOnError;
         }

         protected String getName(Element element)
         {
            return XMLUtil.getStringAttr(element, "name");
         }
      });
   }

   /**
    * Loads a flow loop step:
    *
    *                                        +--------------------------------------------------------------+
    *                                        |                                                              |
    *                                        v                                                              |
    *       +---------------+       +-----------------+      +---------------+                              |
    * ----->| LoopName:init |------>| LoopName:branch |----->| LoopName:next |-----> Inside <Loop> Tag -----+
    *       +---------------+       +-----------------+      +---------------+
    *                                        |
    *                                        +------> Step after <Loop> tag
    *
    * @param element The loop step element.
    * @param activity The activity that will contain the loop.
    * @param bDefaultToThis True if "collection" and "variable" attributes should default to "this".
    * @param helper The XML metadata helper.
    * @param metadata The root metadata object.
    * @param loader The activity loader.
    */
   public static void loadLoop(Element element, Activity activity, boolean bDefaultToThis,
      XMLMetadataHelper helper, Metadata metadata, ActivityLoader loader)
   {
      String sCollectionExpression;
      String sItemVariableName;

      if (bDefaultToThis)
      {
         sCollectionExpression = XMLUtil.getStringAttr(element, "collection", "this");
         sItemVariableName = XMLUtil.getStringAttr(element, "variable", "this");
      }
      else
      {
         sCollectionExpression = XMLUtil.getReqStringAttr(element, "collection");
         sItemVariableName = XMLUtil.getReqStringAttr(element, "variable");
      }

      Variable itemVar = activity.getFlow().findVariable(sItemVariableName);

      if (itemVar == null)
      {
         itemVar = new Variable(sItemVariableName);
         activity.getFlow().addVariable(itemVar);
      }

      String sLoopName = XMLUtil.getReqStringAttr(element, "name");
      Object collectionExpression = helper.parse(sCollectionExpression,
         false, activity.getFlow().getPosMap(), null, metadata.getGlobalEnvironment()
      );
      Variable collectionVar;
      boolean bUseOldCollectionVar = (collectionExpression instanceof Symbol) && collectionExpression != Symbol.THIS;

      // If collection is already stored in a variable, do not create a new one.
      if (bUseOldCollectionVar)
      {
         collectionVar = activity.getFlow().getVariable(((Symbol)collectionExpression).getName());
      }
      else
      {
         collectionVar = new Variable(sLoopName + ":collection");
         activity.getFlow().addVariable(collectionVar);
      }

      Variable indexVar = new Variable(sLoopName + ":var");

      activity.getFlow().addVariable(indexVar);

      Symbol collectionSym = collectionVar.getSymbol();
      Symbol indexSym = indexVar.getSymbol();
      Symbol itemSym = itemVar.getSymbol();

      // Loop initializer step
      Script initializer = new Script(sLoopName + ":init");

      initializer.setActivity(activity);
      initializer.setBody(new Pair(
         Pair.list(Symbol.SET, indexSym,
            Pair.list(Symbol.COND,
               Pair.list(Pair.list(Symbol.PAIR_P, collectionSym), collectionSym),
               Pair.list(Pair.list(Symbol.VECTOR_P, collectionSym), Primitive.ZERO_INTEGER)
            )
         ),
         activity.getFlow().getDefaultReturnCode()
      ));

      if (!bUseOldCollectionVar)
      {
         initializer.setBody(new Pair(
            Pair.list(Symbol.SET, collectionSym, collectionExpression),
            initializer.getBody()
         ));
      }

      activity.addStep(initializer);

      // Loop test step
      Decision decision = new Decision(sLoopName + ":branch");
      Branch loopBody = new Branch();

      decision.setActivity(activity);
      loopBody.setDecision(decision);
      loopBody.setCondition(Pair.list(Symbol.COND,
         Pair.list(Pair.list(Symbol.PAIR_P, collectionSym),
            Pair.list(Symbol.NOT, Pair.list(Symbol.NULL_P, indexSym))),
         Pair.list(Pair.list(Symbol.VECTOR_P, collectionSym),
            Pair.list(Symbol.LT, indexSym, Pair.list(Symbol.VECTOR_LENGTH, collectionSym))),
         Pair.list(Symbol.ELSE,
            Pair.list(collectionSym, Pair.quote(XMLMetadataLoader.HAS_NEXT)))
      ));

      // Get current item and advance to next step
      Script getItem = new Script(sLoopName + ":next");

      getItem.setActivity(loopBody);
      getItem.setBody(new Pair(
         Pair.list(Symbol.COND,
            Pair.list(Pair.list(Symbol.PAIR_P, collectionSym),
               Pair.list(Symbol.SET, itemSym, Pair.list(Symbol.CAR, indexSym)),
               Pair.list(Symbol.SET, indexSym, Pair.list(Symbol.CDR, indexSym))
            ),
            Pair.list(Pair.list(Symbol.VECTOR_P, collectionSym),
               Pair.list(Symbol.SET, itemSym, Pair.list(collectionSym, indexSym)),
               Pair.list(Symbol.SET, indexSym, Pair.list(Symbol.PLUS, indexSym, Primitive.ONE_INTEGER))
            ),
            Pair.list(Symbol.ELSE,
               Pair.list(Symbol.SET, itemSym, Pair.list(collectionSym, Pair.quote(XMLMetadataLoader.NEXT)))
            )
         ),
         activity.getFlow().getDefaultReturnCode()
      ));
      loopBody.addStep(getItem);

      // Load loop body
      loader.loadActivity(element, loopBody);

      loopBody.getStep(loopBody.getStepCount() - 1).setNext(decision);
      decision.addBranch(loopBody);

      // Branch to follow when done
      Branch doneBranch = new Branch();

      doneBranch.setDecision(decision);
      doneBranch.setCondition(Boolean.TRUE);
      decision.addBranch(doneBranch);

      // Finish the loop expansion
      activity.addStep(decision);
   }

   /**
    * Loads the data-source-specific persistence mapping details from a DOM element.
    * @param element The DOM element containing the mapping.
    * @param metaclass The class for which the mapping applies.
    * @return The loaded persistence mapping object.
    */
   public PersistenceMapping loadPersistenceMappingDetails(Element element, Metaclass metaclass)
   {
      String sDataSource = XMLUtil.getReqStringAttr(element, "dataSource");
      DataSource dataSource = m_metadata.getDataSource(sDataSource);
      return ((XMLPersistenceMetadataLoader)m_helper.getClassInstance(dataSource.getType().getLoader()))
         .loadMapping(element, metaclass, dataSource, XMLMetadataLoader.this);
   }

   /**
    * Loads a persistence mapping from a DOM element.
    * @param element The DOM element containing the mapping.
    * @param metaclass The class for which the mapping applies.
    * @return The loaded persistence mapping object.
    */
   public PersistenceMapping loadPersistenceMapping(Element element, Metaclass metaclass)
   {
      PersistenceMapping mapping = loadPersistenceMappingDetails(element, metaclass);

      String sLockingAttribute = XMLUtil.getStringAttr(element, "lockingAttribute");

      if (sLockingAttribute != null)
      {
         mapping.setLockingAttribute(metaclass.getAttribute(sLockingAttribute));
      }

      String sTypeCodeAttribute = XMLUtil.getStringAttr(element, "classCodeAttribute");

      if (sTypeCodeAttribute != null)
      {
         mapping.setTypeCodeAttribute(metaclass.getAttribute(sTypeCodeAttribute));
      }

      mapping.setTypeCodeForced(XMLUtil.getBooleanAttr(element, "classCodeForced", false));

      String sFragmentAttribute = XMLUtil.getStringAttr(element, "fragmentAttribute");

      if (sFragmentAttribute != null)
      {
         mapping.setFragmentAttribute(metaclass.getAttribute(sFragmentAttribute));
      }

      String sFragmentReplication = XMLUtil.getStringAttr(element, "fragmentReplication");

      if (sFragmentReplication != null)
      {
         if (sFragmentReplication.equals("unicast"))
         {
            mapping.setFragmentReplication(PersistenceMapping.REPLICATION_UNICAST);
         }
         else if (sFragmentReplication.equals("broadcast"))
         {
            mapping.setFragmentReplication(PersistenceMapping.REPLICATION_BROADCAST);
         }
         else
         {
            throw new MetadataException("err.meta.fragmentReplication",
               new Object[]{sFragmentReplication, metaclass.getName()});
         }
      }

      String sCaching = XMLUtil.getStringAttr(element, "caching");

      if (sCaching != null)
      {
         if (sCaching.equals("instance"))
         {
            mapping.setCaching(PersistenceMapping.CACHING_INSTANCE);
         }
         else if (sCaching.equals("class"))
         {
            mapping.setCaching(PersistenceMapping.CACHING_CLASS);
         }
         else
         {
            throw new MetadataException("err.meta.caching",
               new Object[]{sCaching, metaclass.getName()});
         }
      }

      return mapping;
   }

   /**
    * Loads a component from a DOM element.
    * @param componentElement The DOM element containing the component.
    * @param sName The component name.
    */
   protected void loadComponent(Element componentElement, String sName)
   {
      XMLMetadataHelper.verifyRootElement(componentElement, "Component");

      Component component = new Component(sName);

      component.setMetadata(m_metadata);
      loadComponent(componentElement, component);
      m_metadata.addComponent(component);
   }

   /**
    * Loads a component from a DOM element.
    * @param componentElement The DOM element containing the component.
    * @param component The component instance.
    */
   public void loadComponent(Element componentElement, final Component component)
   {
      String sActivation = XMLUtil.getReqStringAttr(componentElement, "activation");

      if (sActivation.equals("singleton"))
      {
         component.setActivation(Component.SINGLETON);

         addSingletonFixup(new ContextFixup(m_helper)
         {
            public void fixup()
            {
               component.getInstance(null);
            }
         });
      }
      else if (sActivation.equals("context"))
      {
         component.setActivation(Component.CONTEXT);
      }
      else if (sActivation.equals("new"))
      {
         component.setActivation(Component.NEW);
      }
      else
      {
         throw new MetadataException("err.meta.componentActivation", new Object[]{sActivation});
      }

      if (!isRuntimeExcluded())
      {
         component.setType(m_helper.getClassObject(XMLUtil.getReqStringAttr(componentElement, "type")));
      }

      XMLUtil.withFirstChildElement(componentElement, "Factory", false, new ElementHandler()
      {
         public void handleElement(Element factoryElement)
         {
            int nCookie = m_helper.pushMarker("factory", "true");

            try
            {
               Component factory = new Component(component.getName() + ".<factory>");

               factory.setMetadata(m_metadata);
               loadComponent(factoryElement, factory);

               if (!m_bRuntimeExcluded)
               {
                  component.setFactory(factory, XMLUtil.getStringAttr(factoryElement, "method", "create"));
               }
            }
            finally
            {
               m_helper.restoreMarker(nCookie);
            }
         }
      });

      XMLUtil.withFirstChildElement(componentElement, "Properties", false, new ElementHandler()
      {
         public void handleElement(Element propertiesElement)
         {
            loadComponentProperties(propertiesElement, component);
         }
      });

      component.validate(m_metadata, m_helper.getWarnings());
   }

   /**
    * Loads a component properties from a DOM element.
    * @param propertiesElement The DOM element containing the properties.
    * @param component The component into which to load the properties.
    */
   public void loadComponentProperties(Element propertiesElement, final Component component)
   {
      XMLUtil.forEachChildElement(propertiesElement, null,
         m_helper.new ElementHandler("property")
      {
         public void handleElement(Element propertyElement, String sName)
         {
            boolean bCollection;

            if (propertyElement.getNodeName().equals("Property"))
            {
               bCollection = false;
            }
            else if (propertyElement.getNodeName().equals("Collection"))
            {
               bCollection = true;
            }
            else
            {
               return;
            }

            XMLMetadataHelper.validateName(sName);

            if (!m_helper.isElementEnabled(propertyElement) || component.getType() == null)
            {
               return;
            }

            Method method = component.getPropertyMethod(sName, bCollection);
            final Class type = method.getParameterTypes()[0];
            final PropertyParser parser = findPropertyParser(type);
            final PropertyInitializer initializer;

            if (parser == null)
            {
               // Assuming a component

               if (bCollection)
               {
                  initializer = new ComponentCollectionPropertyInitializer(sName, type, method, component);

                  XMLUtil.forEachChildElement(propertyElement, "Item", new ElementHandler()
                  {
                     public void handleElement(Element element)
                     {
                        if (m_helper.isElementEnabled(element))
                        {
                           loadComponentProperty(element, initializer, type);
                        }
                     }
                  });
               }
               else
               {
                  initializer = new ComponentPropertyInitializer(sName, type, method, component);
                  loadComponentProperty(propertyElement, initializer, type);
               }
            }
            else
            {
               if (bCollection)
               {
                  initializer = new PrimitiveCollectionPropertyInitializer(sName, type, method, component);

                  XMLUtil.forEachChildElement(propertyElement, "Item", new ElementHandler()
                  {
                     public void handleElement(Element element)
                     {
                        if (m_helper.isElementEnabled(element))
                        {
                           loadPrimitiveProperty(element, initializer, parser, type);
                        }
                     }
                  });
               }
               else
               {
                  initializer = new PrimitivePropertyInitializer(sName, type, method, component);
                  loadPrimitiveProperty(propertyElement, initializer, parser, type);
               }
            }

            component.addPropertyInitializer(initializer);
         }
      });
   }

   /**
    * Loads a primitive property value from a DOM element.
    * @param propertyElement The DOM element containing the value.
    * @param initializer The property initializer where to set the configured value.
    * @param parser The property value parser.
    * @param type The property value type.
    */
   protected void loadPrimitiveProperty(Element propertyElement,
      final PropertyInitializer initializer,
      PropertyParser parser, Class type)
   {
      Element element = findComponentElement(propertyElement);

      if (element == null)
      {
         parser.parse(m_helper.getElementValue(propertyElement), initializer, this);
      }
      else
      {
         if (!type.equals(Component.class))
         {
            throw new MetadataException("err.meta.componentElement",
               new Object[]{initializer.getName(), initializer.getComponent().getName()});
         }

         final Component component = new Component(initializer.getComponent().getName() +
            "/" + initializer.getName());

         component.setMetadata(m_metadata);
         loadComponent(element, component);

         m_componentFixupList.add(new ContextFixup(m_helper)
         {
            public void fixup()
            {
               initializer.initializeValue(component);
            }
         });
      }
   }

   /**
    * Loads a component property (one that is initialized with a
    * component instance) from a DOM element.
    * @param propertyElement The DOM element containing the value.
    * @param initializer The property initializer where to set the configured value.
    * @param type The property value type.
    */
   protected void loadComponentProperty(Element propertyElement,
      final PropertyInitializer initializer, final Class type)
   {
      Element element = findComponentElement(propertyElement);

      if (element == null)
      {
         final String sValue = m_helper.getElementValue(propertyElement);

         m_componentFixupList.add(new ContextFixup(m_helper)
         {
            public void fixup()
            {
               if (StringUtil.isEmpty(sValue))
               {
                  initializer.initializeValue(null);
               }
               else
               {
                  Component component = initializer.getComponent().getMetadata().getComponent(sValue);

                  if (!m_bRuntimeExcluded && !type.isAssignableFrom(component.getType()))
                  {
                     throw new MetadataException("err.meta.propertyTypeMismatch",
                        new Object[]{initializer.getName(), initializer.getComponent().getName()});
                  }

                  initializer.initializeValue(component);
               }
            }
         });
      }
      else
      {
         final Component component = new Component(initializer.getComponent().getName() +
            "/" + initializer.getName());

         component.setMetadata(m_metadata);
         loadComponent(element, component);

         if (!m_bRuntimeExcluded && !type.isAssignableFrom(component.getType()))
         {
            throw new MetadataException("err.meta.propertyTypeMismatch",
               new Object[]{initializer.getName(), initializer.getComponent().getName()});
         }

         m_componentFixupList.add(new ContextFixup(m_helper)
         {
            public void fixup()
            {
               initializer.initializeValue(component);
            }
         });
      }
   }

   /**
    * Loads a extended library from a DOM element.
    * @param externalLibraryElement The DOM element containing the extended library.
    * @param sName The extended library name.
    */
   protected void loadExternalLibrary(Element externalLibraryElement, String sName)
   {
      XMLMetadataHelper.verifyRootElement(externalLibraryElement, "ExternalLibrary");

      final ExternalLibrary externalLibrary = new ExternalLibrary(sName);

      externalLibrary.setMetadata(m_metadata);

      XMLUtil.withFirstChildElement(externalLibraryElement, "Files", false, new ElementHandler()
      {
         public void handleElement(Element filesElement)
         {
            XMLUtil.forEachChildElement(filesElement, "File", new ElementHandler()
            {
               public void handleElement(Element fileElement)
               {
                  externalLibrary.addFile(XMLUtil.getReqStringAttr(fileElement, "name"));
               }
            });
         }
      });

      m_metadata.addExternalLibrary(externalLibrary);
   }

   /**
    * Loads the supported locales.
    */
   protected void loadLocales()
   {
      Metaclass metaclass = m_metadata.findMetaclass(Metadata.LOCALE_CLASS_NAME);

      if (metaclass != null)
      {
         for (int i = 0, n = metaclass.getStaticAttributeCount(); i != n; ++i)
         {
            Attribute attribute = metaclass.getStaticAttribute(i);

            if (attribute.isStatic() && attribute.isReadOnly() &&
               attribute.getValue() instanceof String)
            {
               m_metadata.addLocale((String)attribute.getValue());
            }
         }
      }

      if (!m_metadata.isLocaleSupported(Metadata.DEFAULT_LOCALE))
      {
         m_metadata.addLocale(Metadata.DEFAULT_LOCALE);
      }
   }

   /**
    * Loads the string resources from a map iterator over ResourcePath[ResourceName].
    * @param itr The map iterator.
    */
   protected void loadStrings(Lookup.Iterator itr)
   {
      while (itr.hasNext())
      {
         String sLocale = (String)itr.next();
         int i = sLocale.lastIndexOf('.');

         if (i >= 0)
         {
            sLocale = sLocale.substring(i + 1);
         }

         m_metadata.addString(sLocale, (String)itr.getValue());
      }
   }

   /**
    * Loads the documentation from a given element.
    * @param element The element from which to load the documentation.
    * @param documented The destination object.
    */
   public void loadDocumentation(Element element, Documented documented)
   {
      if (m_bDocumented)
      {
         documented.setDescription(XMLUtil.getStringAttr(element, "description"));
      }
   }

   /**
    * Loads custom properties from a Properties element into a property holder.
    * @param element The element containing the container element element.
    * @param sContainer The container element name.
    * @param holder The property holder.
    */
   public void loadProperties(Element element, String sContainer, final PropertyHolder holder)
   {
      XMLUtil.withFirstChildElement(element, sContainer, false, new ElementHandler()
      {
         public void handleElement(Element propertiesElement)
         {
            XMLUtil.forEachChildElement(propertiesElement, "Property",
               getHelper().new ElementHandler("property")
            {
               public void handleElement(Element propertyElement, String sPropertyName)
               {
                  holder.addProperty(sPropertyName, XMLUtil.getStringAttr(propertyElement, "value", ""));
               }
            });
         }
      });
   }

   /**
    * Gets the privilege from a given attribute.
    * @param element The element containing the attribute.
    * @param sName The name of the attribute.
    * @return The privilege.
    */
   public PrimitivePrivilege getPrivilege(Element element, String sName)
   {
      String sPrivilege = XMLUtil.getStringAttr(element, sName);

      if (sPrivilege == null)
      {
         return null;
      }

      return m_metadata.getPrimitivePrivilege(sPrivilege);
   }

   /**
    * Adds a persistence mapping fixup handler.
    * @param fixup The fixup handler.
    */
   public void addPersistenceMappingFixup(Fixup fixup)
   {
      m_persistenceMappingFixupList.add(fixup);
   }

   /**
    * Adds an I/O fixup handler.
    * @param fixup The fixup handler.
    */
   public void addIOFixup(Fixup fixup)
   {
      m_ioFixupList.add(fixup);
   }

   /**
    * Adds an environment fixup handler.
    * @param fixup The fixup handler.
    */
   public void addEnvironmentFixup(Fixup fixup)
   {
      m_environmentFixupList.add(fixup);
   }

   /**
    * Adds a privilege fixup handler.
    * @param fixup The fixup handler.
    */
   public void addPrivilegeFixup(Fixup fixup)
   {
      m_privilegeFixupList.add(fixup);
   }

   /**
    * Adds a pre-inheritance message fixup handler. Executed before resolution of message inheritance.
    * @param fixup The fixup handler.
    */
   public void addPreInheritanceMessageFixup(Fixup fixup)
   {
      m_preInheritanceMessageFixupList.add(fixup);
   }

   /**
    * Adds a post-inheritance message fixup handler. Executed immediately after resolution of
    * message inheritance.
    * @param fixup The fixup handler.
    */
   public void addPostInheritanceMessageFixup(Fixup fixup)
   {
      m_postInheritanceMessageFixupList.add(fixup);
   }

   /**
    * Adds a transformation fixup handler.
    * @param fixup The fixup handler.
    */
   public void addTransformationFixup(Fixup fixup)
   {
      m_transformationFixupList.add(fixup);
   }

   /**
    * Adds a component fixup handler.
    * @param fixup The fixup handler.
    */
   public void addComponentFixup(Fixup fixup)
   {
      m_componentFixupList.add(fixup);
   }

   /**
    * Adds a singleton fixup handler.
    * @param fixup The fixup handler.
    */
   public void addSingletonFixup(Fixup fixup)
   {
      if (!m_bRuntimeExcluded)
      {
         m_singletonFixupList.add(fixup);
      }
   }

   /**
    * Adds a singleton fixup handler.
    * @param component The component to fixup.
    */
   public void addSingletonFixup(final Component component)
   {
      addSingletonFixup(new ContextFixup(m_helper)
      {
         public void fixup()
         {
            component.getInstance(null);
         }
      });
   }

   /**
    * @return The metadata object currently being loaded.
    */
   public XMLMetadata getMetadata()
   {
      return m_metadata;
   }

   /**
    * @return  The VM for metadata loading.
    */
   public Machine getMachine()
   {
      return m_machine;
   }

   /**
    * @return The current helper object.
    */
   public XMLMetadataHelper getHelper()
   {
      return m_helper;
   }

   /**
    * Parses a visibility string.
    * @param sVisibility The visibility string to parse.
    * @param nDefault The value to return if the string is empty.
    * @return Metaclass.PUBLIC or Metaclass.PROTECTED.
    * @throws MetadataException if the visibility string is invalid.
    */
   public static byte parseVisibility(String sVisibility, byte nDefault) throws MetadataException
   {
      if (sVisibility == null || sVisibility.length() == 0)
      {
         return nDefault;
      }

      if (sVisibility.equals("public"))
      {
         return Metaclass.PUBLIC;
      }

      if (sVisibility.equals("protected"))
      {
         return Metaclass.PROTECTED;
      }

      throw new MetadataException("err.meta.visibility", new Object[]{sVisibility});
   }

   /**
    * Parses a transaction mode string.
    * @param sTransactionMode The transaction mode string to parse.
    * @param nDefault The value to return if the string is empty.
    * @return One of the Metaclass.TX_* constants.
    * @throws MetadataException if the transaction string is invalid.
    */
   public static byte parseTransactionMode(String sTransactionMode, byte nDefault) throws MetadataException
   {
      if (sTransactionMode == null || sTransactionMode.length() == 0)
      {
         return nDefault;
      }

      if (sTransactionMode.equals("supported"))
      {
         return Event.TX_SUPPORTED;
      }

      if (sTransactionMode.equals("required"))
      {
         return Event.TX_REQUIRED;
      }

      if (sTransactionMode.equals("new"))
      {
         return Event.TX_NEW;
      }

      if (sTransactionMode.equals("none"))
      {
         return Event.TX_NONE;
      }

      if (sTransactionMode.equals("mandatory"))
      {
         return Event.TX_MANDATORY;
      }

      if (sTransactionMode.equals("unsupported"))
      {
         return Event.TX_UNSUPPORTED;
      }

      throw new MetadataException("err.meta.transactionMode", new Object[]{sTransactionMode});
   }

   /**
    * Parses a cascade mode string.
    * @param sCascadeMode The cascade mode string to parse.
    * @param nDefault The value to return if the string is empty.
    * @return One of the Attribute.CASCADE_* constants.
    * @throws MetadataException if the cascade string is invalid.
    */
   public static byte parseCascadeMode(String sCascadeMode, byte nDefault) throws MetadataException
   {
      if (sCascadeMode == null || sCascadeMode.length() == 0)
      {
         return nDefault;
      }

      if (sCascadeMode.equals("none"))
      {
         return Attribute.CASCADE_NONE;
      }

      if (sCascadeMode.equals("delete"))
      {
         return Attribute.CASCADE_DELETE;
      }

      if (sCascadeMode.equals("clear"))
      {
         return Attribute.CASCADE_CLEAR;
      }

      if (sCascadeMode.equals("cancel"))
      {
         return Attribute.CASCADE_CANCEL;
      }

      throw new MetadataException("err.meta.cascadeMode", new Object[]{sCascadeMode});
   }

   /**
    * Parses a pattern string: [!]pattern1 ... [!]patternN.
    * @param sPatterns The pattern string. Can be null.
    * @param handler The aspect handler.
    * @throws MetadataException if the aspect string is invalid.
    */
   public static void parsePatterns(String sPatterns, PatternHandler handler) throws MetadataException
   {
      if (sPatterns != null)
      {
         for (StringTokenizer tokenizer = new StringTokenizer(sPatterns); tokenizer.hasMoreTokens();)
         {
            String sToken = tokenizer.nextToken();

            if (sToken.length() != 0)
            {
               if (sToken.charAt(0) == '!')
               {
                  if (sToken.length() == 1 || sToken.charAt(1) == '!')
                  {
                     throw new MetadataException("err.meta.emptyNegation");
                  }

                  handler.handlePattern(sToken.substring(1), false);
               }
               else
               {
                  handler.handlePattern(sToken, true);
               }
            }
         }
      }
   }

   /**
    * Finds a component property parser for a given property type.
    * @param type The property type.
    * @return The property parser, or null if not found.
    */
   public static PropertyParser findPropertyParser(Class type)
   {
      return (PropertyParser)s_propertyParserMap.get(type);
   }

   /**
    * Finds the component tag in a given element.
    * @param element The element containing the component tag.
    * @return The component tag, or null if not found.
    */
   public static Element findComponentElement(Element element)
   {
      return XMLUtil.findChildElement(element, "Component");
   }

   // inner classes

   /**
    * Interface implemented by property parser classes.
    */
   public interface PropertyParser
   {
      /**
       * Parses the property value and sets it on the specified initializer.
       * @param sValue The value to parse.
       * @param initializer The initializer on which to set the value.
       * @param loader The metadata loader.
       */
      void parse(String sValue, PropertyInitializer initializer, XMLMetadataLoader loader);
   }

   /**
    * Parser class for primitive property types.
    */
   private static class PrimitivePropertyParser implements PropertyParser
   {
      private Constructor m_constructor;
      private boolean m_bNullAllowed;

      public PrimitivePropertyParser(Class type, boolean bNullAllowed)
      {
         try
         {
            m_constructor = type.getConstructor(new Class[]{String.class});
         }
         catch (Exception e)
         {
            throw new IllegalArgumentException("Unknown primitive type constructor " + type);
         }

         m_bNullAllowed = bNullAllowed;
      }

      /**
       * @see nexj.core.meta.xml.XMLMetadataLoader.PropertyParser#parse(java.lang.String, nexj.core.meta.PropertyInitializer, nexj.core.meta.xml.XMLMetadataLoader)
       */
      public void parse(String sValue, PropertyInitializer initializer, XMLMetadataLoader loader)
      {
         Object value;

         if (m_bNullAllowed && (sValue == null || sValue.length() == 0))
         {
            value = null;
         }
         else
         {
            try
            {
               value = m_constructor.newInstance(new Object[]{sValue});
            }
            catch (Exception e)
            {
               throw new MetadataException("err.meta.propertyValue",
                  new Object[]{sValue, m_constructor.getDeclaringClass().getName()}, e);
            }
         }

         initializer.initializeValue(value);
      }
   }

   /**
    * Interface implemented by activity loader strategies.
    */
   public interface ActivityLoader
   {
      /**
       * Loads an activity from a DOM element.
       * @param element The DOM element.
       * @param activity The activity to load.
       */
      void loadActivity(Element element, Activity activity);
   }

   /**
    * Interface implemented by flow decision loader strategies.
    */
   public interface BranchLoader extends ActivityLoader
   {
      /**
       * @return The branch element name.
       */
      String getElementName();

      /**
       * @return A new branch.
       */
      Branch createBranch();
   }

   /**
    * Interface for processing a pattern.
    */
   public interface PatternHandler
   {
      /**
       * Processes the pattern.
       * @param sPattern The pattern.
       * @param bInclusive True if the pattern is inclusive.
       */
      void handlePattern(String sPattern, boolean bApply);
   }

   /**
    * Fixup that sets the resource name based on an exception class.
    */
   protected abstract class ClassFixup implements Fixup
   {
      public void setMarker(MetadataMarker marker)
      {
      }
   }
}
TOP

Related Classes of nexj.core.meta.xml.XMLMetadataLoader$ClassFixup

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.