Package nexj.core.meta.persistence.sql

Source Code of nexj.core.meta.persistence.sql.XMLRelationalMetadataLoader

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

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import org.w3c.dom.Element;

import nexj.core.meta.Attribute;
import nexj.core.meta.Component;
import nexj.core.meta.Metaclass;
import nexj.core.meta.MetadataException;
import nexj.core.meta.MetadataValidationException;
import nexj.core.meta.Primitive;
import nexj.core.meta.persistence.AttributeMapping;
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.SchemaUpgrade;
import nexj.core.meta.persistence.XMLPersistenceMetadataLoader;
import nexj.core.meta.persistence.sql.upgrade.AlterColumnStep;
import nexj.core.meta.persistence.sql.upgrade.AlterTableStep;
import nexj.core.meta.persistence.sql.upgrade.ApplyIndexAspectStep;
import nexj.core.meta.persistence.sql.upgrade.ApplyTableAspectStep;
import nexj.core.meta.persistence.sql.upgrade.ColumnOutline;
import nexj.core.meta.persistence.sql.upgrade.CreateColumnStep;
import nexj.core.meta.persistence.sql.upgrade.CreateIndexStep;
import nexj.core.meta.persistence.sql.upgrade.CreateObjectStep;
import nexj.core.meta.persistence.sql.upgrade.CreateTableStep;
import nexj.core.meta.persistence.sql.upgrade.DropColumnStep;
import nexj.core.meta.persistence.sql.upgrade.DropIndexStep;
import nexj.core.meta.persistence.sql.upgrade.DropObjectStep;
import nexj.core.meta.persistence.sql.upgrade.DropTableStep;
import nexj.core.meta.persistence.sql.upgrade.ExecStep;
import nexj.core.meta.persistence.sql.upgrade.IndexOutline;
import nexj.core.meta.persistence.sql.upgrade.RelationalSchemaUpgrade;
import nexj.core.meta.persistence.sql.upgrade.RemoveIndexAspectStep;
import nexj.core.meta.persistence.sql.upgrade.RemoveTableAspectStep;
import nexj.core.meta.persistence.sql.upgrade.RenameColumnStep;
import nexj.core.meta.persistence.sql.upgrade.RenameIndexStep;
import nexj.core.meta.persistence.sql.upgrade.RenameTableStep;
import nexj.core.meta.persistence.sql.upgrade.SupportAdapterStep;
import nexj.core.meta.xml.XMLMetadata;
import nexj.core.meta.xml.XMLMetadataHelper;
import nexj.core.meta.xml.XMLMetadataLoader;
import nexj.core.meta.xml.XMLMetadataHelper.ContextFixup;
import nexj.core.persistence.sql.SQLAdapter;
import nexj.core.util.HashTab;
import nexj.core.util.J2EEUtil;
import nexj.core.util.Lookup;
import nexj.core.util.SysUtil;
import nexj.core.util.XMLUtil;
import nexj.core.util.XMLUtil.ElementHandler;

/**
* Persistence metadata loader for RDBMS schemas.
*/
public class XMLRelationalMetadataLoader implements XMLPersistenceMetadataLoader
{
   /**
    * The metadata loader.
    */
   private XMLMetadataLoader m_loader;

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

   /**
    * The schema being currently processed.
    */
   private RelationalSchema m_schema;

   /**
    * The table fixup collection.
    */
   private List m_tableFixupList; // of type Fixup

   /**
    * The index fixup collection.
    */
   private List m_indexFixupList; // of type Fixup

   /**
    * The aspect fixup collection.
    */
   private List m_aspectFixupList; // of type Fixup

   /**
    * The key fixup collection.
    */
   private List m_keyFixupList; // of type Fixup

   /**
    * Temporary persistence mapping element used during the parsing.
    */
   private Element m_persistenceMappingElement;
  
   /**
    * Work persistence mapping element handler.
    */
   private ElementHandler m_persistenceMappingElementHandler = new ElementHandler()
   {
      public void handleElement(Element element)
      {
         m_persistenceMappingElement = element;
      }
   };

   // operations

   /**
    * @return The current metadata object.
    */
   protected XMLMetadata getMetadata()
   {
      return m_loader.getMetadata();
   }

   /**
    * @return The metadata helper object.
    */
   protected XMLMetadataHelper getHelper()
   {
      return m_helper;
   }

   /**
    * @see nexj.core.meta.persistence.XMLPersistenceMetadataLoader#loadDataSource(org.w3c.dom.Element, java.lang.String, nexj.core.meta.persistence.DataSourceType, nexj.core.meta.xml.XMLMetadataLoader)
    */
   public DataSource loadDataSource(Element element, String sName, DataSourceType type, XMLMetadataLoader loader)
   {
      final RelationalDatabase db = new RelationalDatabase(sName);

      db.setType(type);
      loader.loadDataSource(element, db);

      db.setAlias(XMLUtil.getStringAttr(element, "alias"));

      if (db.getAlias() == null)
      {
         db.setAlias(XMLMetadataHelper.makeAlias(db.getName()));
      }

      m_loader = loader;
      m_helper = loader.getHelper();
      m_schema = new RelationalSchema();

      db.setSchema(m_schema);
      m_schema.setPortable(XMLUtil.getBooleanAttr(element, "portable", m_schema.isPortable()));
      m_schema.setPrefix(XMLUtil.getStringAttr(element, "prefix", m_schema.getPrefix()));
      m_schema.setTablespaceName(XMLUtil.getStringAttr(element, "tablespace", m_schema.getTablespaceName()));
      m_schema.setIndexspaceName(XMLUtil.getStringAttr(element, "indexspace", m_schema.getIndexspaceName()));
      m_schema.setLongspaceName(XMLUtil.getStringAttr(element, "longspace", m_schema.getLongspaceName()));
      m_schema.setRoleName(XMLUtil.getStringAttr(element, "role", m_schema.getRoleName()));
      m_schema.setIndexFill(XMLUtil.getIntAttr(element, "indexfill", m_schema.getIndexFill()));

      if (!loader.isEnvironmentOnly())
      {
         m_tableFixupList = new ArrayList(256);
         m_indexFixupList = new ArrayList(256);
         m_aspectFixupList = new ArrayList(256);
         m_keyFixupList = new ArrayList(256);

         XMLUtil.withFirstChildElement(element, "Tables", false, new ElementHandler()
         {
            public void handleElement(Element tablesElement)
            {
               XMLUtil.forEachChildElement(tablesElement, "Table",
                  getHelper().new ElementHandler("table")
               {
                  public void handleElement(Element tableElement, String sTableName)
                  {
                     XMLMetadataHelper.validateName(sTableName, XMLMetadataHelper.NAME_DOT);

                     Table table = new Table(m_schema);

                     loadTable(tableElement, sTableName, table);
                     m_schema.addTable(table);
                  }
               });
            }
         });

         getHelper().fixup(m_tableFixupList.iterator());
         m_tableFixupList = null;

         RelationalSchemaAspectManager aspectManager = new RelationalSchemaAspectManager(m_schema)
         {
            protected Iterator getTableAspectIterator()
            {
               return m_schema.getTableIterator();
            }

            protected Iterator getTablePointcutIterator()
            {
               return m_schema.getTableIterator();
            }

            protected Iterator getIndexAspectIterator()
            {
               return m_schema.getIndexIterator();
            }

            protected Iterator getIndexPointcutIterator()
            {
               return m_schema.getIndexIterator();
            }
         };

         aspectManager.applyAspects(0);

         getHelper().fixup(m_indexFixupList.iterator());
         m_indexFixupList = null;

         aspectManager.applyAspects(1);

         getHelper().fixup(m_aspectFixupList.iterator());
         m_aspectFixupList = null;

         aspectManager.applyAspects(2);

         getHelper().fixup(m_keyFixupList.iterator());
         m_keyFixupList = null;

         String sVersionTable = XMLUtil.getStringAttr(element, "versionTable");

         if (sVersionTable != null)
         {
            m_schema.setVersionTable(m_schema.getTable(sVersionTable));
         }

         XMLUtil.withFirstChildElement(element, "Objects", false, new ElementHandler()
         {
            public void handleElement(Element tablesElement)
            {
               XMLUtil.forEachChildElement(tablesElement, "Object",
                  getHelper().new ElementHandler("object")
               {
                  public void handleElement(Element objectElement, String sObjectName)
                  {
                     XMLMetadataHelper.validateName(sObjectName, XMLMetadataHelper.NAME_DOT);

                     final SQLObject sqlObject = new SQLObject();

                     loadObject(objectElement, sObjectName, sqlObject);
                     m_schema.addObject(sqlObject);
                  }
               });
            }
         });
      }

      boolean bFirstRelationalDatabase = true;

      for (Iterator itr = m_loader.getMetadata().getDataSourceIterator(); itr.hasNext() && bFirstRelationalDatabase; )
      {
         DataSource ds = (DataSource)itr.next();

         if (ds instanceof RelationalDatabase)
         {
            bFirstRelationalDatabase = false;
         }
      }

      if (bFirstRelationalDatabase)
      {
         if (!loader.isEnvironmentOnly())
         {
            // Find unmapped columns from mapped tables and set their required flag
            m_loader.addSingletonFixup(new ContextFixup(getHelper())
            {
               public void fixup()
               {
                  RelationalMapping.resolve(m_loader.getMetadata());
               }
            });
         }

         m_loader.addIOFixup(new ContextFixup(getHelper())
         {
            public void fixup()
            {
               Lookup aliasFragmentMap = new HashTab();

               for (Iterator itr = m_loader.getMetadata().getDataSourceIterator(); itr.hasNext(); )
               {
                  DataSource ds = (DataSource)itr.next();

                  if (ds instanceof RelationalDatabase)
                  {
                     RelationalDatabase rel = (RelationalDatabase)ds;

                     for (Iterator fragItr = rel.getFragmentIterator(); fragItr.hasNext(); )
                     {
                        RelationalDatabaseFragment frag = (RelationalDatabaseFragment)fragItr.next();
                        RelationalDatabaseFragment oldFrag = (RelationalDatabaseFragment)aliasFragmentMap.put(frag.getAlias(), frag);

                        if (oldFrag == null)
                        {
                           frag.setFirst(true);
                        }
                        else
                        {
                           frag.setFirst(false);
                           aliasFragmentMap.put(frag.getAlias(), oldFrag);

                           if (!frag.isCompatible(oldFrag))
                           {
                              throw new MetadataValidationException("err.meta.persistence.fragmentPropertyMismatch",
                                 new Object[]{frag.getDataSource().getName() + frag.getSuffix(), oldFrag.getDataSource().getName() + oldFrag.getSuffix()});
                           }
                        }
                     }
                  }
               }
            }
         });
      }

      return db;
   }

   /**
    * Loads an SQL Object from a DOM element.
    * @param objectElement The DOM element containing the SQL Object definition.
    * @param sObjectName The name of the SQL Object.
    * @param sqlObject The SQL Object to fill with definition.
    */
   protected void loadObject(Element objectElement, String sObjectName, final SQLObject sqlObject)
   {
      sqlObject.setName(sObjectName);

      XMLUtil.withFirstChildElement(objectElement, "Create", false, new ElementHandler()
      {
         public void handleElement(Element scriptElement)
         {
            SQLScript script = new SQLScript();

            loadSQLScript(scriptElement, script);
            sqlObject.setCreateScript(script);
         }
      });

      XMLUtil.withFirstChildElement(objectElement, "Drop", false, new ElementHandler()
      {
         public void handleElement(Element scriptElement)
         {
            SQLScript script = new SQLScript();

            loadSQLScript(scriptElement, script);
            sqlObject.setDropScript(script);
         }
      });
   }

   /**
    * Loads a table from a DOM element.
    * @param tableElement The DOM element containing the table definition.
    * @param table The table object.
    */
   protected void loadTable(Element tableElement, String sTableName, final Table table)
   {
      table.setType(parseTableType(XMLUtil.getStringAttr(tableElement, "type"), table.getType()));
      table.setName(sTableName);
      table.setQuotedName(XMLUtil.getStringAttr(tableElement, "alias"));
      table.setTablespaceName(XMLUtil.getStringAttr(tableElement, "tablespace", table.getTablespaceName()));
      table.setIndexspaceName(XMLUtil.getStringAttr(tableElement, "indexspace", table.getIndexspaceName()));
      table.setLongspaceName(XMLUtil.getStringAttr(tableElement, "longspace", table.getLongspaceName()));
      m_loader.loadDocumentation(tableElement, table);

      XMLMetadataLoader.parsePatterns(XMLUtil.getStringAttr(tableElement, "aspects"),
         new XMLMetadataLoader.PatternHandler()
         {
            public void handlePattern(final String sName, final boolean bInclusive)
            {
               m_tableFixupList.add(new ContextFixup(getHelper())
               {
                  public void fixup()
                  {
                     table.addAspectOverride(table.getSchema().getTable(sName), bInclusive);
                  }
               });
            }
         });

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

      XMLUtil.withFirstChildElement(tableElement, "Columns", false, new ElementHandler()
      {
         public void handleElement(Element columnsElement)
         {
            XMLUtil.forEachChildElement(columnsElement, "Column",
               getHelper().new ElementHandler("column")
            {
               public void handleElement(Element columnElement, String sColumnName)
               {
                  XMLMetadataHelper.validateName(sColumnName);
                 
                  Column column = new Column(sColumnName, table);

                  loadColumn(columnElement, column);
                  table.addColumn(column);
               }
            });
         }
      });

      XMLUtil.withFirstChildElement(tableElement, "Indexes", false, new ElementHandler()
      {
         public void handleElement(Element indexesElement)
         {
            XMLUtil.forEachChildElement(indexesElement, "Index",
               getHelper().new ElementHandler("index")
            {
               public void handleElement(Element indexElement, String sIndexName)
               {
                  XMLMetadataHelper.validateName(sIndexName, XMLMetadataHelper.NAME_DOT);

                  final Index index = new Index(table);

                  loadIndex(indexElement, sIndexName, index);

                  m_indexFixupList.add(new ContextFixup(getHelper())
                  {
                     public void fixup()
                     {
                        table.addIndex(index);
                     }
                  });
               }
            });
         }
      });

      XMLUtil.withFirstChildElement(tableElement, "View", false, new ElementHandler()
      {
         public void handleElement(Element viewElement)
         {
            final SQLScript script = new SQLScript();

            loadSQLScript(viewElement, script);
            table.setViewScript(script);
            table.setViewAutoUpdated(
               XMLUtil.getBooleanAttr(viewElement, "autoupdated", table.isViewAutoUpdated()));
         }
      });

      String sHints = XMLUtil.getStringAttr(tableElement, "hints");

      if (sHints != null)
      {
         for (StringTokenizer tokenizer = new StringTokenizer(sHints); tokenizer.hasMoreTokens();)
         {
            table.addHint(tokenizer.nextToken());
         }
      }

      final String sPrimaryKey = (table.isAspect()) ? XMLUtil.getStringAttr(tableElement, "primaryKey") :
         XMLUtil.getReqStringAttr(tableElement, "primaryKey");

      m_keyFixupList.add(new ContextFixup(getHelper())
      {
         public void fixup()
         {
            if (sPrimaryKey != null)
            {
               table.setPrimaryKey(table.getIndex(sPrimaryKey));
            }

            table.computePrimaryKeyParts();
         }
      });
   }

   /**
    * Loads a column from a DOM element.
    * @param columnElement The DOM element containing the column definition.
    * @param column The column object.
    */
   protected void loadColumn(Element columnElement, final Column column)
   {
      m_loader.loadDocumentation(columnElement, column);

      // Must precede type setting
      column.setCaseInsensitive(XMLUtil.getBooleanAttr(columnElement, "caseInsensitive",
         column.isCaseInsensitive()));

      column.setType(parseColumnType(XMLUtil.getStringAttr(columnElement, "type")),
         XMLUtil.getIntegerObjAttr(columnElement, "precision"),
         XMLUtil.getIntegerObjAttr(columnElement, "scale"),
         parseColumnAllocation(XMLUtil.getStringAttr(columnElement, "allocation")));

      column.setNullable(XMLUtil.getBooleanAttr(columnElement, "nullable", column.isNullable()));
      column.setLiteral(XMLUtil.getBooleanAttr(columnElement, "literal", column.isLiteral()));

      final String sConverterName = XMLUtil.getStringAttr(columnElement, "converter");

      if (sConverterName != null)
      {
         m_loader.addComponentFixup(new ContextFixup(getHelper())
         {
            public void fixup()
            {
               column.setConverter(column.getTable().getSchema().getMetadata().getComponent(sConverterName));
            }
         });
      }

      if (column.getType() == null)
      {
         m_loader.addComponentFixup(new ContextFixup(getHelper())
         {
            public void fixup()
            {
               column.computeType();
            }
         });
      }

      if (column.getTable().isAspect() && (sConverterName != null || column.getType() == null))
      {
         m_loader.addComponentFixup(new ContextFixup(getHelper())
         {
            public void fixup()
            {
               for (Iterator itr = column.getTable().getSchema().getTableIterator(); itr.hasNext();)
               {
                  Table table = (Table)itr.next();

                  if (table.hasAspect(column.getTable()))
                  {
                     Column col = table.getColumn(column.getName());

                     col.setConverter(column.getConverter());
                     col.computeType();
                  }
               }
            }
         });
      }
   }

   /**
    * Loads an index from a DOM element.
    * @param indexElement The DOM element containing the index definition.
    * @param sName The index name.
    * @param index The index object.
    */
   protected void loadIndex(Element indexElement, String sName, final Index index)
   {
      index.setType(parseIndexType(XMLUtil.getStringAttr(indexElement, "type"), index.getType()));
      index.setName(sName);
      index.setUnique(XMLUtil.getBooleanAttr(indexElement, "unique", index.isUnique()));
      index.setFill(XMLUtil.getIntAttr(indexElement, "fill", index.getFill()));

      XMLMetadataLoader.parsePatterns(XMLUtil.getStringAttr(indexElement, "aspects"),
         new XMLMetadataLoader.PatternHandler()
         {
            public void handlePattern(final String sName, final boolean bInclusive)
            {
               m_aspectFixupList.add(new ContextFixup(getHelper())
               {
                  public void fixup()
                  {
                     index.addAspectOverride(index.getTable().getSchema().getIndex(sName), bInclusive);
                  }
               });
            }
         });

      final String sRelatedTableName = XMLUtil.getStringAttr(indexElement, "relatedTable");

      if (sRelatedTableName != null)
      {
         m_indexFixupList.add(new ContextFixup(getHelper())
         {
            public void fixup()
            {
               index.getTable().getSchema().getTable(sRelatedTableName).addRelatedKey(index);
            }
         });
      }

      XMLUtil.forEachChildElement(indexElement, "IndexColumn",
         getHelper().new ElementHandler("column")
      {
         public void handleElement(Element element, final String sName)
         {
            final boolean bAscending = XMLUtil.getBooleanAttr(element, "ascending", true);

            m_indexFixupList.add(new ContextFixup(getHelper())
            {
               public void fixup()
               {
                  index.addIndexColumn(new IndexColumn(index.getTable().getColumn(sName), bAscending));
               }
            });
         }
      });
   }

   /**
    * @see nexj.core.meta.persistence.XMLPersistenceMetadataLoader#loadMapping(org.w3c.dom.Element, nexj.core.meta.Metaclass, nexj.core.meta.persistence.DataSource, nexj.core.meta.xml.XMLMetadataLoader)
    */
   public PersistenceMapping loadMapping(
      final Element persistenceMappingElement,
      final Metaclass metaclass,
      final DataSource dataSource,
      final XMLMetadataLoader loader)
   {
      m_loader = loader;
      m_helper = loader.getHelper();
      m_schema = (RelationalSchema)dataSource.getSchema();

      final RelationalMapping relationalMapping = new RelationalMapping();
      int nCookie = getHelper().pushMarker(MetadataValidationException.TYPE_NAME, "RelationalMapping");
      getHelper().pushMarker("class", metaclass.getName());

      try
      {
         relationalMapping.setMetaclass(metaclass);
         relationalMapping.setDataSource(dataSource);

         XMLUtil.withFirstChildElement(persistenceMappingElement, "RelationalMapping", true, new ElementHandler()
         {
            public void handleElement(Element relationalMappingElement)
            {
               String sPrimaryTable = XMLUtil.getStringAttr(relationalMappingElement, "primaryTable");
              
               relationalMapping.setPrimaryTable((sPrimaryTable == null) ? null : m_schema.getTable(sPrimaryTable));

               final String sKeyGeneratorName = XMLUtil.getStringAttr(relationalMappingElement, "keyGenerator");

               if (sKeyGeneratorName != null)
               {
                  if (sKeyGeneratorName.equals("identity"))
                  {
                     relationalMapping.setKeyGenerator(RelationalMapping.KEY_GEN_IDENTITY);
                  }
                  else
                  {
                     m_loader.addComponentFixup(new ContextFixup(getHelper())
                     {
                        public void fixup()
                        {
                           relationalMapping.setKeyGenerator(
                              relationalMapping.getMetaclass().getMetadata()
                              .getComponent(sKeyGeneratorName));
                        }
                     });
                  }
               }

               XMLUtil.withFirstChildElement(relationalMappingElement, "AttributeMappings", false, new ElementHandler()
               {
                  public void handleElement(Element attributeMappingsElement)
                  {
                     XMLUtil.forEachChildElement(attributeMappingsElement, "AttributeMapping",
                        getHelper().new ElementHandler("attribute")
                     {
                        public void handleElement(Element attributeMappingElement, String sAttributeName)
                        {
                           final Attribute attribute = metaclass.getAttribute(sAttributeName);
                           final String sTableName = XMLUtil.getStringAttr(attributeMappingElement, "table");
                           final String sColumnName = XMLUtil.getStringAttr(attributeMappingElement, "column");
                           final String sSourceKeyName = XMLUtil.getStringAttr(attributeMappingElement, "sourceKey");
                           final String sDestinationKeyName = XMLUtil.getStringAttr(attributeMappingElement, "destinationKey");
                           AttributeMapping mapping;
                          
                           m_persistenceMappingElement = null;
                           XMLUtil.withFirstChildElement(attributeMappingElement,
                              "PersistenceMapping", false, m_persistenceMappingElementHandler);

                           if (attribute.getType().isPrimitive())
                           {
                              if (sColumnName == null || sSourceKeyName != null ||
                                 sDestinationKeyName != null || m_persistenceMappingElement != null)
                              {
                                 throw new MetadataException("err.meta.primitiveRelationalMapping",
                                    new Object[]{attribute.getName(), metaclass.getName()});
                              }
                             
                              if (attribute.isCollection())
                              {
                                 throw new MetadataException("err.meta.unsupportedRelationalMapping",
                                    new Object[]{attribute.getName(), metaclass.getName()});
                              }

                              RelationalPrimitiveMapping primitiveMapping = new RelationalPrimitiveMapping();
                              Table table = (sTableName == null) ? relationalMapping.getPrimaryTable() : m_schema.getTable(sTableName);

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

                              primitiveMapping.setColumn(table.getColumn(sColumnName));
                              primitiveMapping.setAttribute(attribute);
                              mapping = primitiveMapping;
                           }
                           else
                           {
                              if (sTableName != null || sColumnName != null ||
                                 m_persistenceMappingElement != null &&
                                 (sSourceKeyName != null || sDestinationKeyName != null))
                              {
                                 throw new MetadataException("err.meta.classRelationalMapping",
                                    new Object[]{attribute.getName(), metaclass.getName()});
                              }
                             
                              final RelationalClassMapping classMapping = new RelationalClassMapping();
                             
                              classMapping.setAttribute(attribute);

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

                                 classMapping.setSourceKey(relationalMapping.getPrimaryTable().getPrimaryKey());
                                 classMapping.setMapping(m_loader.loadPersistenceMapping(m_persistenceMappingElement, (Metaclass)attribute.getType()));
                              }
                              else
                              {
                                 if (sSourceKeyName != null)
                                 {
                                    classMapping.setSourceKey(m_schema.getIndex(sSourceKeyName));
                                 }
                                 else
                                 {
                                    Table primaryTable = relationalMapping.getPrimaryTable();

                                    if (primaryTable != null)
                                    {
                                       classMapping.setSourceKey(primaryTable.getPrimaryKey());
                                    }
                                 }

                                 m_loader.addPersistenceMappingFixup(new ContextFixup(getHelper())
                                 {
                                    public void fixup()
                                    {
                                       Metaclass type = (Metaclass)attribute.getType();
                                       PersistenceMapping mapping = type.getPersistenceMapping();

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

                                       classMapping.setDestinationKey(mapping.addForeignKey(sDestinationKeyName, classMapping));
                                    }
                                 });
                              }

                              mapping = classMapping;
                           }

                           relationalMapping.addAttributeMapping(mapping);
                        }
                     });
                  }
               });

               XMLUtil.withFirstChildElement(relationalMappingElement, "Hook", false, new ElementHandler()
               {
                  public void handleElement(Element componentElement)
                  {
                     final Component hook = new Component("<PersistenceHook:" + metaclass.getName() + ">");

                     m_loader.loadComponent(componentElement, hook);

                     if (!loader.isRuntimeExcluded())
                     {
                        m_loader.addComponentFixup(new ContextFixup(getHelper())
                        {
                           public void fixup()
                           {
                              relationalMapping.setHook(hook);
                           }
                        });
                     }
                  }
               });
            }
         });
      }
      finally
      {
         getHelper().restoreMarker(nCookie);
      }

      return relationalMapping;
   }

   /**
    * @see nexj.core.meta.persistence.XMLPersistenceMetadataLoader#loadConnection(org.w3c.dom.Element, nexj.core.meta.persistence.DataSource, nexj.core.meta.xml.XMLMetadataLoader)
    */
   public void loadConnection(Element element, final DataSource source, final XMLMetadataLoader loader)
   {
      final RelationalDatabase db = (RelationalDatabase)source;
      final RelationalDatabaseFragment defaultFragment = (RelationalDatabaseFragment)db.getDefaultFragment();

      m_loader = loader;
      defaultFragment.setAlias(db.getAlias() + defaultFragment.getSuffix());

      if (element != null)
      {
         db.setUnicode(XMLUtil.getBooleanAttr(element, "unicode", db.isUnicode()));
         db.setPath(XMLUtil.getStringAttr(element, "path"));
         db.setDriver(XMLUtil.getStringAttr(element, "driver"));
         db.setLiteral(XMLUtil.getBooleanAttr(element, "literal", db.isLiteral()));
         db.setPageSize(XMLUtil.getIntAttr(element, "pageSize", db.getPageSize()));
         db.setIndexspaceName(XMLUtil.getStringAttr(element, "indexspace",db.getIndexspaceName()));
         db.setLongspaceName(XMLUtil.getStringAttr(element, "longspace", db.getLongspaceName()));
         db.setTablespaceName(XMLUtil.getStringAttr(element, "tablespace",db.getTablespaceName()));

         loadFragment(element, defaultFragment, false);

         XMLUtil.withFirstChildElement(element, "Fragments", false, new ElementHandler()
         {
            public void handleElement(Element fragmentsElement)
            {
               XMLUtil.forEachChildElement(fragmentsElement, "Fragment", getHelper().new ElementHandler("fragment")
               {
                  protected void handleElement(Element fragmentElement, String sName)
                  {
                     RelationalDatabaseFragment fragment = (RelationalDatabaseFragment)defaultFragment.clone(false);

                     fragment.setName(sName);
                     db.addFragment(fragment);
                     loadFragment(fragmentElement, fragment, true);
                  }
               });
            }
         });
      }

      db.setDefaultProperties(loader.getContainer()); // done after loading config from XML Element

      DataSourceAdapter adapter = source.getAdapter();

      if (adapter == null)
      {
         return; // no RDBMS mapping defined for "optional" DataSource, hence no component required
      }

      final Component component =
         new Component(source.getName(), adapter.getClassObject(), Component.CONTEXT);

      component.setMetadata(loader.getMetadata());

      if (element != null)
      {
         XMLUtil.withFirstChildElement(element, "SQLHook", false, new ElementHandler()
         {
            public void handleElement(Element componentElement)
            {
               Component hook = new Component("<SQLHook:" + source.getName() + ">");

               m_loader.loadComponent(componentElement, hook);
               component.addComponentPropertyInitializer("SQLHook", hook);
            }
         });
      }

      db.setComponent(component);

      if (adapter.getVersion() != null)
      {
         component.addPrimitivePropertyInitializer("version", adapter.getVersion());
      }

      if (!db.isUnicode())
      {
         component.addPrimitivePropertyInitializer("unicode", Boolean.FALSE);
      }

      if (db.isLiteral())
      {
         component.addPrimitivePropertyInitializer("literal", Boolean.TRUE);
      }

      if (!loader.isEnvironmentOnly() && !loader.isValidatedOnly())
      {
         loader.addEnvironmentFixup(new ContextFixup(loader.getHelper())
         {
            public void fixup()
            {
               Component cf = null;

               for (Iterator fragmentItr = db.getFragmentIterator(); fragmentItr.hasNext();)
               {
                  RelationalDatabaseFragment fragment = (RelationalDatabaseFragment)fragmentItr.next();

                  if (J2EEUtil.isContained() && db.isEnabled())
                  {
                     cf = new Component(source.getName() + ".ConnectionFactory" + fragment.getSuffix(),
                        loader.getHelper().getClassObject(SysUtil.PACKAGE + ".core.persistence.sql.SQLJNDIConnectionFactory"),
                        Component.SINGLETON);

                     cf.addPrimitivePropertyInitializer("dataSource", db.getName() + fragment.getSuffix());
                  }
                  else
                  {
                     cf = new Component(source.getName() + ".ConnectionFactory" + fragment.getSuffix(),
                        loader.getHelper().getClassObject(SysUtil.PACKAGE + ".core.persistence.sql.SQLRAConnectionFactory"),
                        Component.SINGLETON);

                     SQLAdapter adapterInstance = (SQLAdapter)component.getInstance(null);

                     cf.addPrimitivePropertyInitializer("fragment", fragment);
                     cf.addPrimitivePropertyInitializer("initialSQL", adapterInstance.getInitialSQL());
                     cf.addPrimitivePropertyInitializer("testSQL", adapterInstance.getTestSQL());
                  }

                  cf.setMetadata(loader.getMetadata());
                  fragment.setConnectionFactory(cf);
                  loader.addSingletonFixup(cf);
               }

               if (db.getFragmentCount() > 1)
               {
                  cf = new Component(source.getName() + ".FragmentConnectionFactory",
                           loader.getHelper().getClassObject(SysUtil.PACKAGE + ".core.persistence.sql.SQLFragmentConnectionFactory"),
                     Component.SINGLETON);

                  cf.setMetadata(loader.getMetadata());
                  cf.addPrimitivePropertyInitializer("database", db);
                  component.addPrimitivePropertyInitializer("fragmented", Boolean.TRUE);
               }

               component.addComponentPropertyInitializer("connectionFactory", cf);
            }
         });
      }
   }

   /**
    * Loads a fragment from a DOM element.
    * @param element The DOM element.
    * @param fragment The fragment to load.
    * @param bDefault True to default the required attributes.
    */
   protected void loadFragment(Element element, RelationalDatabaseFragment fragment, boolean bDefault)
   {
      fragment.setAlias(XMLUtil.getStringAttr(element, "alias", ((RelationalDatabase)fragment.getDataSource()).getAlias() + fragment.getSuffix()));

      fragment.setHost((bDefault) ?
         XMLUtil.getStringAttr(element, "host", fragment.getHost()) :
         XMLUtil.getReqStringAttr(element, "host"));

      fragment.setPort(XMLUtil.getIntAttr(element, "port", fragment.getPort()));
      fragment.setInstance(XMLUtil.getStringAttr(element, "instance", fragment.getInstance()));

      fragment.setDatabase((bDefault) ?
         XMLUtil.getStringAttr(element, "database", fragment.getDatabase()) :
         XMLUtil.getReqStringAttr(element, "database"));

      fragment.setUser(XMLUtil.getStringAttr(element, "user", fragment.getUser()));
      fragment.setPassword(m_loader.decryptPassword(XMLUtil.getStringAttr(element, "password", fragment.getPassword())));
      fragment.setMinPoolSize(XMLUtil.getIntAttr(element, "minPoolSize", fragment.getMinPoolSize()));
      fragment.setMaxPoolSize(XMLUtil.getIntAttr(element, "maxPoolSize", fragment.getMaxPoolSize()));
      fragment.setStatementCacheSize(XMLUtil.getIntAttr(element, "statementCacheSize", fragment.getStatementCacheSize()));
      fragment.setQueryTimeout(XMLUtil.getIntAttr(element, "queryTimeout", fragment.getQueryTimeout()));
      fragment.setWarningTimeout(m_loader.getProperty("dataSourceConnection." + fragment.getDataSource().getName() +
         ".warningTimeout", XMLUtil.getLongAttr(element, "warningTimeout", fragment.getWarningTimeout())));

      m_loader.loadProperties(element, "Properties", fragment.getPropertyHolder());
   }

   /**
    * @see nexj.core.meta.persistence.XMLPersistenceMetadataLoader#loadUpgrade(org.w3c.dom.Element, java.lang.String, nexj.core.meta.persistence.DataSource, nexj.core.meta.xml.XMLMetadataHelper)
    */
   public SchemaUpgrade loadUpgrade(Element upgradeElement, final String sName,
      final DataSource dataSource, final XMLMetadataHelper helper)
   {
      final RelationalSchemaUpgrade upgrade = new RelationalSchemaUpgrade(sName);

      m_helper = helper;
      m_schema = (RelationalSchema)dataSource.getSchema();
      upgrade.setDataSource(dataSource);

      XMLUtil.forEachChildElement(upgradeElement, null, helper.new ElementHandler("step")
      {
         private int m_nOrdinal;

         protected void handleElement(Element stepElement, final String sExecOrdinal)
         {
            String sElement = stepElement.getNodeName();

            if (sElement.equals("CreateTable"))
            {
               final CreateTableStep create = new CreateTableStep();

               create.setType(parseTableType(XMLUtil.getStringAttr(stepElement, "type"), create.getType()));
               create.setName(XMLMetadataHelper.getNameAttr(stepElement, "name", XMLMetadataHelper.NAME_DOT));
               create.setAlias(XMLUtil.getStringAttr(stepElement, "alias"));
               create.setTablespaceName(XMLUtil.getStringAttr(stepElement, "tablespace"));
               create.setIndexspaceName(XMLUtil.getStringAttr(stepElement, "indexspace"));
               create.setLongspaceName(XMLUtil.getStringAttr(stepElement, "longspace"));

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

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

               XMLUtil.withFirstChildElement(stepElement, "Columns", false, new ElementHandler()
               {
                  public void handleElement(Element columnsElement)
                  {
                     XMLUtil.forEachChildElement(columnsElement, "Column",
                        getHelper().new ElementHandler("column")
                     {
                        public void handleElement(Element columnElement, String sColumnName)
                        {
                           ColumnOutline outline = new ColumnOutline();

                           loadColumnOutline(columnElement, outline, true);
                           create.addColumnOutline(outline);
                        }
                     });
                  }
               });
              
               XMLUtil.withFirstChildElement(stepElement, "Indexes", false, new ElementHandler()
               {
                  public void handleElement(Element indexesElement)
                  {
                     XMLUtil.forEachChildElement(indexesElement, "Index",
                        getHelper().new ElementHandler("index")
                     {
                        public void handleElement(Element indexElement, String sIndexName)
                        {
                           IndexOutline outline = new IndexOutline();

                           loadIndexOutline(indexElement, outline);
                           create.addIndexOutline(outline);
                        }
                     });
                  }
               });

               XMLUtil.withFirstChildElement(stepElement, "View", false, new ElementHandler()
               {
                  public void handleElement(Element viewElement)
                  {
                     final SQLScript script = new SQLScript();

                     loadSQLScript(viewElement, script);
                     create.setViewScript(script);
                     create.setViewAutoUpdated(
                        XMLUtil.getBooleanAttr(
                           viewElement, "autoupdated", create.isViewAutoUpdated()));
                  }
               });

               String sHints = XMLUtil.getStringAttr(stepElement, "hints");

               if (sHints != null)
               {
                  for (StringTokenizer tokenizer = new StringTokenizer(sHints);
                       tokenizer.hasMoreTokens();)
                  {
                     create.addHint(tokenizer.nextToken());
                  }
               }

               create.setPrimaryKeyName((create.getType() == Table.ASPECT) ?
                  XMLUtil.getStringAttr(stepElement, "primaryKey") :
                  XMLUtil.getReqStringAttr(stepElement, "primaryKey"));

               upgrade.addStep(create);
            }
            else if (sElement.equals("AlterTable"))
            {
               final AlterTableStep alter = new AlterTableStep();

               alter.setType(parseTableType(XMLUtil.getStringAttr(stepElement, "type"), alter.getType()));
               alter.setName(XMLMetadataHelper.getNameAttr(stepElement, "name", XMLMetadataHelper.NAME_DOT));
               alter.setAlias(XMLUtil.getStringAttr(stepElement, "alias"));
               alter.setPrimaryKeyName(XMLUtil.getStringAttr(stepElement, "primaryKey"));

               XMLMetadataLoader.parsePatterns(
                  XMLUtil.getStringAttr(stepElement, "hints"),
                  new XMLMetadataLoader.PatternHandler()
               {
                  public void handlePattern(String sName, boolean bEnabled)
                  {
                     alter.setHint(sName, bEnabled);
                  }
               });

               upgrade.addStep(alter);
            }
            else if (sElement.equals("RenameTable"))
            {
               RenameTableStep ren = new RenameTableStep();

               ren.setOldName(XMLMetadataHelper.getNameAttr(stepElement, "name", XMLMetadataHelper.NAME_DOT));
               ren.setNewName(XMLMetadataHelper.getNameAttr(stepElement, "to", XMLMetadataHelper.NAME_DOT));

               upgrade.addStep(ren);
            }
            else if (sElement.equals("DropTable"))
            {
               DropTableStep drop = new DropTableStep();

               drop.setName(XMLMetadataHelper.getNameAttr(stepElement, "name", XMLMetadataHelper.NAME_DOT));

               upgrade.addStep(drop);
            }
            else if (sElement.equals("ApplyTableAspect"))
            {
               final ApplyTableAspectStep apply = new ApplyTableAspectStep();

               apply.setAspectName(XMLMetadataHelper.getNameAttr(stepElement, "name", XMLMetadataHelper.NAME_DOT));
               apply.setOverride(XMLUtil.getBooleanAttr(stepElement, "override", apply.isOverride()));

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

               loadSQLScripts(stepElement, apply.getScriptHolder());

               upgrade.addStep(apply);
            }
            else if (sElement.equals("RemoveTableAspect"))
            {
               final RemoveTableAspectStep remove = new RemoveTableAspectStep();

               remove.setAspectName(XMLMetadataHelper.getNameAttr(stepElement, "name", XMLMetadataHelper.NAME_DOT));
               remove.setOverride(XMLUtil.getBooleanAttr(stepElement, "override", remove.isOverride()));

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

               upgrade.addStep(remove);
            }
            else if (sElement.equals("CreateColumn"))
            {
               CreateColumnStep create = new CreateColumnStep();

               create.setTableName(XMLMetadataHelper.getNameAttr(stepElement, "table", XMLMetadataHelper.NAME_DOT));

               ColumnOutline outline = new ColumnOutline();

               loadColumnOutline(stepElement, outline, true);
               create.setOutline(outline);

               loadSQLScripts(stepElement, create.getScriptHolder());

               upgrade.addStep(create);
            }
            else if (sElement.equals("AlterColumn"))
            {
               AlterColumnStep alter = new AlterColumnStep();

               alter.setTableName(XMLMetadataHelper.getNameAttr(stepElement, "table", XMLMetadataHelper.NAME_DOT));

               ColumnOutline outline = new ColumnOutline();

               loadColumnOutline(stepElement, outline, false);
               alter.setOutline(outline);

               upgrade.addStep(alter);
            }
            else if (sElement.equals("RenameColumn"))
            {
               RenameColumnStep ren = new RenameColumnStep();

               ren.setTableName(XMLMetadataHelper.getNameAttr(stepElement, "table", XMLMetadataHelper.NAME_DOT));
               ren.setOldName(XMLMetadataHelper.getNameAttr(stepElement, "name", XMLMetadataHelper.NAME_ID));
               ren.setNewName(XMLMetadataHelper.getNameAttr(stepElement, "to", XMLMetadataHelper.NAME_ID));

               upgrade.addStep(ren);
            }
            else if (sElement.equals("DropColumn"))
            {
               DropColumnStep drop = new DropColumnStep();

               drop.setTableName(XMLMetadataHelper.getNameAttr(stepElement, "table", XMLMetadataHelper.NAME_DOT));
               drop.setName(XMLMetadataHelper.getNameAttr(stepElement, "name", XMLMetadataHelper.NAME_ID));

               upgrade.addStep(drop);
            }
            else if (sElement.equals("CreateIndex"))
            {
               CreateIndexStep create = new CreateIndexStep();

               create.setTableName(XMLMetadataHelper.getNameAttr(stepElement, "table", XMLMetadataHelper.NAME_DOT));

               IndexOutline outline = new IndexOutline();

               loadIndexOutline(stepElement, outline);

               create.setOutline(outline);

               upgrade.addStep(create);
            }
            else if (sElement.equals("RenameIndex"))
            {
               RenameIndexStep ren = new RenameIndexStep();

               ren.setOldName(XMLMetadataHelper.getNameAttr(stepElement, "name", XMLMetadataHelper.NAME_DOT));
               ren.setNewName(XMLMetadataHelper.getNameAttr(stepElement, "to", XMLMetadataHelper.NAME_DOT));

               upgrade.addStep(ren);
            }
            else if (sElement.equals("DropIndex"))
            {
               DropIndexStep drop = new DropIndexStep();

               drop.setName(XMLMetadataHelper.getNameAttr(stepElement, "name", XMLMetadataHelper.NAME_DOT));

               upgrade.addStep(drop);
            }
            else if (sElement.equals("ApplyIndexAspect"))
            {
               final ApplyIndexAspectStep apply = new ApplyIndexAspectStep();

               apply.setAspectName(XMLMetadataHelper.getNameAttr(stepElement, "name", XMLMetadataHelper.NAME_DOT));
               apply.setOverride(XMLUtil.getBooleanAttr(stepElement, "override", apply.isOverride()));

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

               upgrade.addStep(apply);
            }
            else if (sElement.equals("RemoveIndexAspect"))
            {
               final RemoveIndexAspectStep remove = new RemoveIndexAspectStep();

               remove.setAspectName(XMLMetadataHelper.getNameAttr(stepElement, "name", XMLMetadataHelper.NAME_DOT));
               remove.setOverride(XMLUtil.getBooleanAttr(stepElement, "override", remove.isOverride()));

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

               upgrade.addStep(remove);
            }
            else if (sElement.equals("CreateObject"))
            {
               CreateObjectStep create = new CreateObjectStep();
               SQLScript script = new SQLScript();

               create.setName(
                  XMLMetadataHelper.getNameAttr(stepElement, "name", XMLMetadataHelper.NAME_DOT));
               loadSQLScript(stepElement, script);
               create.setScript(script);
               upgrade.addStep(create);
            }
            else if (sElement.equals("DropObject"))
            {
               DropObjectStep drop = new DropObjectStep();
               SQLScript script = new SQLScript();

               drop.setName(
                  XMLMetadataHelper.getNameAttr(stepElement, "name", XMLMetadataHelper.NAME_DOT));
               loadSQLScript(stepElement, script);
               drop.setScript(script);
               upgrade.addStep(drop);
            }
            else if (sElement.equals("Exec"))
            {
               final ExecStep exec = new ExecStep();

               exec.setUpgrade(upgrade);
               loadSQLScripts(stepElement, exec.getScriptHolder());

               upgrade.addStep(exec);
            }
            else if (sElement.equals("SupportAdapter"))
            {
               SupportAdapterStep adapter = new SupportAdapterStep();

               adapter.setAdapter(
                  dataSource.getType().getAdapter(XMLUtil.getStringAttr(stepElement, "name")));
               upgrade.addStep(adapter);
            }
            else
            {
               throw new MetadataException("err.meta.persistence.sql.upgrade.invalidStepElement",
                  new Object[]{sElement, sName});
            }
         }

         protected String getName(Element element)
         {
            return String.valueOf(m_nOrdinal++);
         }
      });

      return upgrade;
   }

   /**
    * Parses a table type form a string.
    * @param sType The string to parse.
    * @param nDefault The default type value.
    */
   protected byte parseTableType(String sType, byte nDefault)
   {
      if (sType == null)
      {
         return nDefault;
      }

      if (sType.equals("managed"))
      {
         return Table.MANAGED;
      }

      if (sType.equals("external"))
      {
         return Table.EXTERNAL;
      }

      if (sType.equals("query"))
      {
         return Table.QUERY;
      }

      if (sType.equals("view"))
      {
         return Table.VIEW;
      }

      if (sType.equals("aspect"))
      {
         return Table.ASPECT;
      }

      throw new MetadataException("err.meta.tableType", new Object[]{sType});
   }

   /**
    * Parses a column type from a string.
    * @param sType The column type string. Can be null.
    * @return The primitive type, or null if sType is null.
    */
   protected Primitive parseColumnType(String sType) throws MetadataException
   {
      if (sType == null)
      {
         return null;
      }

      return Primitive.parse(sType);
   }

   /**
    * Parses a column allocation from a string.
    * @param sAllocation The column allocation string. Can be null.
    * @return One of the Column.* constants, or -1 if sAllocation is null.
    */
   protected byte parseColumnAllocation(String sAllocation) throws MetadataException
   {
      if (sAllocation == null)
      {
         return -1;
      }

      if (sAllocation.equals("fixed"))
      {
         return Column.FIXED;
      }

      if (sAllocation.equals("varying"))
      {
         return Column.VARYING;
      }

      if (sAllocation.equals("locator"))
      {
         return Column.LOCATOR;
      }

      throw new MetadataException("err.meta.allocation", new Object[]{sAllocation});
   }

   /**
    * Loads a column outline from a DOM element.
    * @param element The column DOM element.
    * @param outline The column outline.
    * @param bRequired True if the full definition for a new column is required.
    */
   protected void loadColumnOutline(Element element, ColumnOutline outline, boolean bRequired)
   {
      outline.setName(XMLMetadataHelper.getNameAttr(element, "name", XMLMetadataHelper.NAME_ID));
      outline.setCaseInsensitive(XMLUtil.getBooleanObjAttr(element, "caseInsensitive"));

      outline.setType(parseColumnType((bRequired) ? XMLUtil.getReqStringAttr(element, "type") :
            XMLUtil.getStringAttr(element, "type")),
         XMLUtil.getIntegerObjAttr(element, "precision"),
         XMLUtil.getIntegerObjAttr(element, "scale"),
         parseColumnAllocation(XMLUtil.getStringAttr(element, "allocation")));

      outline.setNullable(XMLUtil.getBooleanObjAttr(element, "nullable"));
      outline.setLiteral(XMLUtil.getBooleanObjAttr(element, "literal"));
      outline.setConverterName(XMLUtil.getStringAttr(element, "converter"));
   }

   /**
    * Parses an index type from a string.
    * @param sType The index type string.
    * @param nDefault The default index type, one of the Index.* constants.
    * @return The index type, one of the Index.* constants.
    */
   protected byte parseIndexType(String sType, byte nDefault) throws MetadataException
   {
      if (sType == null)
      {
         return nDefault;
      }

      if (sType.equals("btree"))
      {
         return Index.BTREE;
      }

      if (sType.equals("text"))
      {
         return Index.TEXT;
      }

      if (sType.equals("cluster"))
      {
         return Index.CLUSTER;
      }

      if (sType.equals("virtual"))
      {
         return Index.VIRTUAL;
      }

      if (sType.equals("query"))
      {
         return Index.QUERY;
      }

      if (sType.equals("aspect"))
      {
         return Index.ASPECT;
      }

      throw new MetadataException("err.meta.indexType", new Object[]{sType});
   }

   /**
    * Loads an index outline from a DOM element.
    * @param element The DOM element.
    * @param outline The index outline.
    */
   protected void loadIndexOutline(Element element, final IndexOutline outline)
   {
      outline.setName(XMLMetadataHelper.getNameAttr(element, "name", XMLMetadataHelper.NAME_DOT));
      outline.setRelatedTableName(XMLUtil.getStringAttr(element, "relatedTable"));
      outline.setType(parseIndexType(XMLUtil.getStringAttr(element, "type"), outline.getType()));
      outline.setUnique(XMLUtil.getBooleanAttr(element, "unique", outline.isUnique()));
      outline.setFill(XMLUtil.getIntAttr(element, "fill", outline.getFill()));

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

      XMLUtil.forEachChildElement(element, "IndexColumn",
         getHelper().new ElementHandler("column")
      {
         public void handleElement(Element element, String sName)
         {
            outline.addColumn(sName, XMLUtil.getBooleanAttr(element, "ascending", true));
         }
      });
   }

   /**
    * Loads SQL scripts from a DOM element.
    * @param element The DOM element containing the script elements.
    * @param holder The destination script holder.
    */
   protected void loadSQLScripts(Element element, final SQLScriptHolder holder)
   {
      XMLUtil.forEachChildElement(element, null, m_helper.new ElementHandler("script")
      {
         private int m_nOrdinal;

         protected void handleElement(Element scriptElement, String sSQLOrdinal)
         {
            final SQLScript script = new SQLScript();
            String sElement = scriptElement.getNodeName();

            if (sElement.equals("SQL"))
            {
               SQLStatement stmt = new SQLStatement();

               stmt.setSQL(XMLUtil.getElementValue(scriptElement));
               script.addStatement(stmt);
            }
            else if (sElement.equals("Switch"))
            {
               loadSQLScript(scriptElement, script);
            }
            else
            {
               throw new MetadataException(
                  "err.meta.persistence.sql.upgrade.invalidSQLScriptElement",
                  new Object[]{sElement});
            }

            holder.addScript(script);
         }

         protected String getName(Element element)
         {
            return String.valueOf(m_nOrdinal++);
         }
      });
   }

   /**
    * Loads an SQL script from a DOM element.
    * @param scriptElement The DOM element.
    * @param script The script metadata object.
    */
   protected void loadSQLScript(Element scriptElement, final SQLScript script)
   {
      XMLUtil.forEachChildElement(scriptElement, "SQL", m_helper.new ElementHandler("sql")
      {
         private int m_nOrdinal;

         protected void handleElement(Element sqlElement, String sOrdinal)
         {
            SQLStatement stmt = new SQLStatement();

            stmt.setScript(script);

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

            if (sAdapter != null)
            {
               for (StringTokenizer tokenizer = new StringTokenizer(sAdapter);
                  tokenizer.hasMoreTokens();)
               {
                  stmt.addAdapter(tokenizer.nextToken(), m_schema.getDataSource().getType());
               }
            }

            stmt.setSQL(XMLUtil.getElementValue(sqlElement));
            script.addStatement(stmt);
         }

         protected String getName(Element element)
         {
            return String.valueOf(m_nOrdinal++);
         }
      });
   }
}
TOP

Related Classes of nexj.core.meta.persistence.sql.XMLRelationalMetadataLoader

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.