You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2017/07/25 12:25:22 UTC

[07/16] cayenne git commit: CAY-2335: New XML loading/saving mechanics with support of plugable handlers - new XML loader for DataMap - new project version - updated test projects

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/map/Attribute.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/Attribute.java b/cayenne-server/src/main/java/org/apache/cayenne/map/Attribute.java
index 34b4596..8c8a48e 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/Attribute.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/Attribute.java
@@ -21,6 +21,7 @@ package org.apache.cayenne.map;
 
 import java.io.Serializable;
 
+import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
 import org.apache.cayenne.util.CayenneMapEntry;
 import org.apache.cayenne.util.ToStringBuilder;
 import org.apache.cayenne.util.XMLEncoder;
@@ -54,7 +55,8 @@ public abstract class Attribute implements CayenneMapEntry, XMLSerializable, Ser
         return new ToStringBuilder(this).append("name", getName()).toString();
     }
 
-    public abstract void encodeAsXML(XMLEncoder encoder);
+    @Override
+    public abstract void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate);
 
     /**
      * Returns parent entity that holds this attribute.

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/map/CallbackMap.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/CallbackMap.java b/cayenne-server/src/main/java/org/apache/cayenne/map/CallbackMap.java
index 9cc4b18..b355f43 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/CallbackMap.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/CallbackMap.java
@@ -106,11 +106,7 @@ public class CallbackMap implements Serializable {
             XMLEncoder encoder) {
 
         for (String methodName : descriptor.getCallbackMethods()) {
-            encoder.print("<");
-            encoder.print(stringCallbackName);
-            encoder.print(" method-name=\"");
-            encoder.print(methodName);
-            encoder.println("\"/>");
+            encoder.start(stringCallbackName).attribute("method-name", methodName).end();
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/map/DataMap.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/DataMap.java b/cayenne-server/src/main/java/org/apache/cayenne/map/DataMap.java
index b3002df..1cffde2 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/DataMap.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/DataMap.java
@@ -19,7 +19,6 @@
 
 package org.apache.cayenne.map;
 
-import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.ObjectId;
 import org.apache.cayenne.Persistent;
 import org.apache.cayenne.configuration.ConfigurationNode;
@@ -120,7 +119,7 @@ public class DataMap implements Serializable, ConfigurationNode, XMLSerializable
 	 * The namespace in which the data map XML file will be created. This is
 	 * also the URI to locate a copy of the schema document.
 	 */
-	public static final String SCHEMA_XSD = "http://cayenne.apache.org/schema/9/modelMap";
+	public static final String SCHEMA_XSD = "http://cayenne.apache.org/schema/10/modelMap";
 
 	protected String name;
 	protected String location;
@@ -147,8 +146,7 @@ public class DataMap implements Serializable, ConfigurationNode, XMLSerializable
 	private SortedMap<String, SQLResult> results;
 
 	/**
-	 * @deprecated since 4.0 unused as listeners are no longer tied to a
-	 *             DataMap.
+	 * @deprecated since 4.0 unused as listeners are no longer tied to a DataMap.
 	 */
 	private List<EntityListener> defaultEntityListeners;
 
@@ -177,13 +175,13 @@ public class DataMap implements Serializable, ConfigurationNode, XMLSerializable
 	}
 
 	public DataMap(String mapName, Map<String, Object> properties) {
-		embeddablesMap = new TreeMap<String, Embeddable>();
-		objEntityMap = new TreeMap<String, ObjEntity>();
-		dbEntityMap = new TreeMap<String, DbEntity>();
-		procedureMap = new TreeMap<String, Procedure>();
+		embeddablesMap = new TreeMap<>();
+		objEntityMap = new TreeMap<>();
+		dbEntityMap = new TreeMap<>();
+		procedureMap = new TreeMap<>();
 		queryDescriptorMap = new TreeMap<>();
 		defaultEntityListeners = new ArrayList<>(3);
-		results = new TreeMap<String, SQLResult>();
+		results = new TreeMap<>();
 		setName(mapName);
 		initWithProperties(properties);
 	}
@@ -265,8 +263,7 @@ public class DataMap implements Serializable, ConfigurationNode, XMLSerializable
 				: ObjEntity.LOCK_TYPE_NONE;
 
 		this.defaultPackage = (packageName != null) ? packageName.toString() : null;
-		this.quotingSQLIdentifiers = (quoteSqlIdentifier != null) ? "true".equalsIgnoreCase(quoteSqlIdentifier
-				.toString()) : false;
+		this.quotingSQLIdentifiers = (quoteSqlIdentifier != null) ? "true".equalsIgnoreCase(quoteSqlIdentifier.toString()) : false;
 		this.defaultSchema = (schema != null) ? schema.toString() : null;
 		this.defaultCatalog = (catalog != null) ? catalog.toString() : null;
 		this.defaultSuperclass = (superclass != null) ? superclass.toString() : null;
@@ -310,96 +307,58 @@ public class DataMap implements Serializable, ConfigurationNode, XMLSerializable
 	 * 
 	 * @since 1.1
 	 */
-	public void encodeAsXML(XMLEncoder encoder) {
-		encoder.println("<data-map xmlns=\"http://cayenne.apache.org/schema/9/modelMap\"");
-
-		encoder.indent(1);
-		encoder.println(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
-		encoder.println(" xsi:schemaLocation=\"" + SCHEMA_XSD + " " + SCHEMA_XSD + ".xsd\"");
-
-		encoder.printProjectVersion();
-		encoder.println(">");
-
-		// properties
-		if (defaultLockType == ObjEntity.LOCK_TYPE_OPTIMISTIC) {
-			encoder.printProperty(DEFAULT_LOCK_TYPE_PROPERTY, "optimistic");
-		}
-
-		if (!Util.isEmptyString(defaultPackage)) {
-			encoder.printProperty(DEFAULT_PACKAGE_PROPERTY, defaultPackage);
-		}
-
-		if (!Util.isEmptyString(defaultCatalog)) {
-			encoder.printProperty(DEFAULT_CATALOG_PROPERTY, defaultCatalog);
-		}
-
-		if (!Util.isEmptyString(defaultSchema)) {
-			encoder.printProperty(DEFAULT_SCHEMA_PROPERTY, defaultSchema);
-		}
-
-		if (!Util.isEmptyString(defaultSuperclass)) {
-			encoder.printProperty(DEFAULT_SUPERCLASS_PROPERTY, defaultSuperclass);
-		}
-
-		if (quotingSQLIdentifiers) {
-			encoder.printProperty(DEFAULT_QUOTE_SQL_IDENTIFIERS_PROPERTY, quotingSQLIdentifiers);
-		}
-
-		if (clientSupported) {
-			encoder.printProperty(CLIENT_SUPPORTED_PROPERTY, "true");
-		}
-
-		if (!Util.isEmptyString(defaultClientPackage)) {
-			encoder.printProperty(DEFAULT_CLIENT_PACKAGE_PROPERTY, defaultClientPackage);
-		}
-
-		if (!Util.isEmptyString(defaultClientSuperclass)) {
-			encoder.printProperty(DEFAULT_CLIENT_SUPERCLASS_PROPERTY, defaultClientSuperclass);
-		}
-
-		// embeddables
-		encoder.print(getEmbeddableMap());
-
-		// procedures
-		encoder.print(getProcedureMap());
-
-		// DbEntities
-		for (DbEntity dbe : getDbEntityMap().values()) {
-			dbe.encodeAsXML(encoder);
-		}
-
-		// others...
-		encoder.print(getObjEntityMap());
-		encodeDBRelationshipsAsXML(getDbEntityMap(), encoder);
-		encodeOBJRelationshipsAsXML(getObjEntityMap(), encoder);
-
-		for (QueryDescriptor query : getQueryDescriptors()) {
-			query.encodeAsXML(encoder);
-		}
-
-		encoder.indent(-1);
-		encoder.println("</data-map>");
+	public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
+		encoder.start("data-map")
+				.attribute("xmlns", SCHEMA_XSD)
+				.attribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance", true)
+				.attribute("xsi:schemaLocation", SCHEMA_XSD + " " + SCHEMA_XSD + ".xsd", true)
+				.projectVersion()
+				// properties
+				.property(DEFAULT_LOCK_TYPE_PROPERTY, defaultLockType)
+				.property(DEFAULT_PACKAGE_PROPERTY, defaultPackage)
+				.property(DEFAULT_CATALOG_PROPERTY, defaultCatalog)
+				.property(DEFAULT_SCHEMA_PROPERTY, defaultSchema)
+				.property(DEFAULT_SUPERCLASS_PROPERTY, defaultSuperclass)
+				.property(DEFAULT_QUOTE_SQL_IDENTIFIERS_PROPERTY, quotingSQLIdentifiers)
+				.property(CLIENT_SUPPORTED_PROPERTY, clientSupported)
+				.property(DEFAULT_CLIENT_PACKAGE_PROPERTY, defaultClientPackage)
+				.property(DEFAULT_CLIENT_SUPERCLASS_PROPERTY, defaultClientSuperclass)
+				// elements
+				.nested(getEmbeddableMap(), delegate)
+				.nested(getProcedureMap(), delegate)
+				.nested(getDbEntityMap(), delegate)
+				.nested(getObjEntityMap(), delegate);
+
+		// and finally relationships
+		encodeDbRelationshipsAsXML(encoder, delegate);
+		encodeObjRelationshipsAsXML(encoder, delegate);
+
+		// descriptors at the end just to keep logic from older versions
+		encoder.nested(getQueryDescriptors(), delegate);
+
+		delegate.visitDataMap(this);
+		encoder.end();
 	}
 
 	// stores relationships for the map of entities
-	private final void encodeDBRelationshipsAsXML(Map<String, DbEntity> entityMap, XMLEncoder encoder) {
-		for (Entity entity : entityMap.values()) {
+	private void encodeDbRelationshipsAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
+		for (Entity entity : getDbEntityMap().values()) {
 			for (Relationship relationship : entity.getRelationships()) {
 				// filter out synthetic
 				if (!relationship.isRuntime()) {
-					relationship.encodeAsXML(encoder);
+					relationship.encodeAsXML(encoder, delegate);
 				}
 			}
 		}
 	}
 
 	// stores relationships for the map of entities
-	private final void encodeOBJRelationshipsAsXML(Map<String, ObjEntity> entityMap, XMLEncoder encoder) {
-		for (ObjEntity entity : entityMap.values()) {
+	private void encodeObjRelationshipsAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
+		for (ObjEntity entity : getObjEntityMap().values()) {
 			for (Relationship relationship : entity.getDeclaredRelationships()) {
 				// filter out synthetic
 				if (!relationship.isRuntime()) {
-					relationship.encodeAsXML(encoder);
+					relationship.encodeAsXML(encoder, delegate);
 				}
 			}
 		}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/map/DbAttribute.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/DbAttribute.java b/cayenne-server/src/main/java/org/apache/cayenne/map/DbAttribute.java
index 796d69c..37bbc60 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/DbAttribute.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/DbAttribute.java
@@ -24,7 +24,6 @@ import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
 import org.apache.cayenne.dba.TypesMapping;
 import org.apache.cayenne.map.event.AttributeEvent;
 import org.apache.cayenne.map.event.DbAttributeListener;
-import org.apache.cayenne.util.Util;
 import org.apache.cayenne.util.XMLEncoder;
 
 /**
@@ -99,49 +98,40 @@ public class DbAttribute extends Attribute implements ConfigurationNode {
      * @since 1.1
      */
     @Override
-    public void encodeAsXML(XMLEncoder encoder) {
+    public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
 
-        encoder.print("<db-attribute name=\"");
-        encoder.print(Util.encodeXmlAttribute(getName()));
-        encoder.print('\"');
+        encoder.start("db-attribute").attribute("name", getName());
 
         String type = TypesMapping.getSqlNameByType(getType());
-        if (type != null) {
-            encoder.print(" type=\"" + type + '\"');
-        }
+        encoder.attribute("type", type);
 
         if (isPrimaryKey()) {
-            encoder.print(" isPrimaryKey=\"true\"");
+            encoder.attribute("isPrimaryKey", true);
 
             // only allow generated if an attribute is a PK.
             if (isGenerated()) {
-                encoder.print(" isGenerated=\"true\"");
+                encoder.attribute("isGenerated", true);
             }
         }
 
         if (isMandatory()) {
-            encoder.print(" isMandatory=\"true\"");
+            encoder.attribute("isMandatory", true);
         }
 
         if (getMaxLength() > 0) {
-            encoder.print(" length=\"");
-            encoder.print(getMaxLength());
-            encoder.print('\"');
+            encoder.attribute("length", getMaxLength());
         }
 
         if (getScale() > 0) {
-            encoder.print(" scale=\"");
-            encoder.print(getScale());
-            encoder.print('\"');
+            encoder.attribute("scale", getScale());
         }
 
         if (getAttributePrecision() > 0) {
-            encoder.print(" attributePrecision=\"");
-            encoder.print(getAttributePrecision());
-            encoder.print('\"');
+            encoder.attribute("attributePrecision", getAttributePrecision());
         }
 
-        encoder.println("/>");
+        delegate.visitDbAttribute(this);
+        encoder.end();
     }
 
     public String getAliasedName(String alias) {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/map/DbEntity.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/DbEntity.java b/cayenne-server/src/main/java/org/apache/cayenne/map/DbEntity.java
index 3c3af26..d3dda7e 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/DbEntity.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/DbEntity.java
@@ -34,7 +34,6 @@ import org.apache.cayenne.map.event.EntityEvent;
 import org.apache.cayenne.map.event.MapEvent;
 import org.apache.cayenne.map.event.RelationshipEvent;
 import org.apache.cayenne.util.CayenneMapEntry;
-import org.apache.cayenne.util.Util;
 import org.apache.cayenne.util.XMLEncoder;
 import org.apache.commons.collections.Transformer;
 
@@ -114,40 +113,30 @@ public class DbEntity extends Entity implements ConfigurationNode, DbEntityListe
      * @since 1.1
      */
     @Override
-    public void encodeAsXML(XMLEncoder encoder) {
-        encoder.print("<db-entity name=\"");
-        encoder.print(Util.encodeXmlAttribute(getName()));
-        encoder.print('\"');
+    public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
+        encoder.start("db-entity").attribute("name", getName());
 
         if (getSchema() != null && getSchema().trim().length() > 0) {
-            encoder.print(" schema=\"");
-            encoder.print(Util.encodeXmlAttribute(getSchema().trim()));
-            encoder.print('\"');
+            encoder.attribute("schema", getSchema().trim());
         }
-
         if (getCatalog() != null && getCatalog().trim().length() > 0) {
-            encoder.print(" catalog=\"");
-            encoder.print(Util.encodeXmlAttribute(getCatalog().trim()));
-            encoder.print('\"');
+            encoder.attribute("catalog", getCatalog().trim());
         }
 
-        encoder.println('>');
-
-        encoder.indent(1);
-        encoder.print(getAttributeMap());
+        encoder.nested(getAttributeMap(), delegate);
 
         if (getPrimaryKeyGenerator() != null) {
-            getPrimaryKeyGenerator().encodeAsXML(encoder);
+            getPrimaryKeyGenerator().encodeAsXML(encoder, delegate);
         }
 
         if (getQualifier() != null) {
-            encoder.print("<qualifier>");
-            getQualifier().encodeAsXML(encoder);
-            encoder.println("</qualifier>");
+            encoder.start("qualifier");
+            getQualifier().encodeAsXML(encoder, delegate);
+            encoder.end();
         }
 
-        encoder.indent(-1);
-        encoder.println("</db-entity>");
+        delegate.visitDbEntity(this);
+        encoder.end();
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/map/DbJoin.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/DbJoin.java b/cayenne-server/src/main/java/org/apache/cayenne/map/DbJoin.java
index ecb9ab8..d0de023 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/DbJoin.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/DbJoin.java
@@ -22,6 +22,7 @@ package org.apache.cayenne.map;
 import java.io.Serializable;
 
 import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
 import org.apache.cayenne.util.ToStringBuilder;
 import org.apache.cayenne.util.XMLEncoder;
 import org.apache.cayenne.util.XMLSerializable;
@@ -95,23 +96,12 @@ public class DbJoin implements XMLSerializable, Serializable {
     /**
      * Prints itself as XML to the provided XMLEncoder.
      */
-    public void encodeAsXML(XMLEncoder encoder) {
-        encoder.print("<db-attribute-pair");
-
-        // sanity check
-        if (getSourceName() != null) {
-            encoder.print(" source=\"");
-            encoder.print(getSourceName());
-            encoder.print("\"");
-        }
-
-        if (getTargetName() != null) {
-            encoder.print(" target=\"");
-            encoder.print(getTargetName());
-            encoder.print("\"");
-        }
-
-        encoder.println("/>");
+    @Override
+    public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
+        encoder.start("db-attribute-pair")
+                .attribute("source", getSourceName())
+                .attribute("target", getTargetName())
+                .end();
     }
 
     public DbRelationship getRelationship() {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/map/DbKeyGenerator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/DbKeyGenerator.java b/cayenne-server/src/main/java/org/apache/cayenne/map/DbKeyGenerator.java
index f6cd94e..d91e787 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/DbKeyGenerator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/DbKeyGenerator.java
@@ -22,6 +22,7 @@ package org.apache.cayenne.map;
 
 import java.io.Serializable;
 
+import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
 import org.apache.cayenne.util.CayenneMapEntry;
 import org.apache.cayenne.util.XMLEncoder;
 import org.apache.cayenne.util.XMLSerializable;
@@ -77,32 +78,22 @@ public class DbKeyGenerator implements CayenneMapEntry, XMLSerializable, Seriali
      * 
      * @since 1.1
      */
-    public void encodeAsXML(XMLEncoder encoder) {
+    @Override
+    public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
         if (getGeneratorType() == null) {
             return;
         }
 
-        encoder.println("<db-key-generator>");
-        encoder.indent(1);
-
-        encoder.print("<db-generator-type>");
-        encoder.print(getGeneratorType());
-        encoder.println("</db-generator-type>");
+        encoder.start("db-key-generator")
+                .start("db-generator-type").cdata(getGeneratorType()).end();
 
         if (getGeneratorName() != null) {
-            encoder.print("<db-generator-name>");
-            encoder.print(getGeneratorName());
-            encoder.println("</db-generator-name>");
+            encoder.start("db-generator-name").cdata(getGeneratorName()).end();
         }
-
         if (getKeyCacheSize() != null) {
-            encoder.print("<db-key-cache-size>");
-            encoder.print(String.valueOf(getKeyCacheSize()));
-            encoder.println("</db-key-cache-size>");
+            encoder.start("db-key-cache-size").cdata(String.valueOf(getKeyCacheSize())).end();
         }
-
-        encoder.indent(-1);
-        encoder.println("</db-key-generator>");
+        encoder.end();
     }
 
     public DbEntity getDbEntity() {
@@ -117,8 +108,8 @@ public class DbKeyGenerator implements CayenneMapEntry, XMLSerializable, Seriali
         this.generatorType = generatorType;
         if (this.generatorType != null) {
             this.generatorType = this.generatorType.trim().toUpperCase();
-            if (!(ORACLE_TYPE.equals(this.generatorType) || NAMED_SEQUENCE_TABLE_TYPE
-                    .equals(this.generatorType)))
+            if (!(ORACLE_TYPE.equals(this.generatorType)
+                    || NAMED_SEQUENCE_TABLE_TYPE.equals(this.generatorType)))
                 this.generatorType = null;
         }
     }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/map/DbRelationship.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/DbRelationship.java b/cayenne-server/src/main/java/org/apache/cayenne/map/DbRelationship.java
index 0d58cd0..53d4c73 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/DbRelationship.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/DbRelationship.java
@@ -74,30 +74,22 @@ public class DbRelationship extends Relationship implements ConfigurationNode {
      * 
      * @since 1.1
      */
-    public void encodeAsXML(XMLEncoder encoder) {
-        encoder.print("<db-relationship name=\"");
-        encoder.print(Util.encodeXmlAttribute(getName()));
-        encoder.print("\" source=\"");
-        encoder.print(Util.encodeXmlAttribute(getSourceEntity().getName()));
+    public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
+        encoder.start("db-relationship")
+                .attribute("name", getName())
+                .attribute("source", getSourceEntity().getName());
 
         if (getTargetEntityName() != null && getTargetEntity() != null) {
-            encoder.print("\" target=\"");
-            encoder.print(Util.encodeXmlAttribute(getTargetEntityName()));
+            encoder.attribute("target", getTargetEntityName());
         }
 
-        if (isToDependentPK() && isValidForDepPk()) {
-            encoder.print("\" toDependentPK=\"true");
-        }
-
-        encoder.print("\" toMany=\"");
-        encoder.print(isToMany());
-        encoder.println("\">");
+        encoder.attribute("toDependentPK", isToDependentPK() && isValidForDepPk());
+        encoder.attribute("toMany", isToMany());
 
-        encoder.indent(1);
-        encoder.print(getJoins());
-        encoder.indent(-1);
+        encoder.nested(getJoins(), delegate);
 
-        encoder.println("</db-relationship>");
+        delegate.visitDbRelationship(this);
+        encoder.end();
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/map/EJBQLQueryDescriptor.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/EJBQLQueryDescriptor.java b/cayenne-server/src/main/java/org/apache/cayenne/map/EJBQLQueryDescriptor.java
index d3adde5..ed7f2e0 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/EJBQLQueryDescriptor.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/EJBQLQueryDescriptor.java
@@ -18,6 +18,7 @@
  ****************************************************************/
 package org.apache.cayenne.map;
 
+import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
 import org.apache.cayenne.query.EJBQLQuery;
 import org.apache.cayenne.util.XMLEncoder;
 
@@ -59,25 +60,17 @@ public class EJBQLQueryDescriptor extends QueryDescriptor {
     }
 
     @Override
-    public void encodeAsXML(XMLEncoder encoder) {
-        encoder.print("<query name=\"");
-        encoder.print(getName());
-        encoder.print("\" type=\"");
-        encoder.print(type);
-        encoder.println("\">");
-
-        encoder.indent(1);
+    public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
+        encoder.start("query").attribute("name", getName()).attribute("type", type);
 
         // print properties
         encodeProperties(encoder);
 
         if (ejbql != null) {
-            encoder.print("<ejbql><![CDATA[");
-            encoder.print(ejbql);
-            encoder.println("]]></ejbql>");
+            encoder.start("ejbql").cdata(ejbql, true).end();
         }
 
-        encoder.indent(-1);
-        encoder.println("</query>");
+        delegate.visitQuery(this);
+        encoder.end();
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/map/Embeddable.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/Embeddable.java b/cayenne-server/src/main/java/org/apache/cayenne/map/Embeddable.java
index 744a5cc..d6a354e 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/Embeddable.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/Embeddable.java
@@ -168,18 +168,12 @@ public class Embeddable implements ConfigurationNode, XMLSerializable, Serializa
 	/**
 	 * {@link XMLSerializable} implementation that generates XML for embeddable.
 	 */
-	public void encodeAsXML(XMLEncoder encoder) {
-		encoder.print("<embeddable");
-		if (getClassName() != null) {
-			encoder.print(" className=\"");
-			encoder.print(getClassName());
-			encoder.print("\"");
-		}
-		encoder.println(">");
-
-		encoder.indent(1);
-		encoder.print(attributes);
-		encoder.indent(-1);
-		encoder.println("</embeddable>");
+	@Override
+	public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
+		encoder.start("embeddable")
+				.attribute("className", getClassName())
+				.nested(attributes, delegate);
+		delegate.visitEmbeddable(this);
+		encoder.end();
 	}
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/map/EmbeddableAttribute.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/EmbeddableAttribute.java b/cayenne-server/src/main/java/org/apache/cayenne/map/EmbeddableAttribute.java
index 27a8da7..197b7dc 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/EmbeddableAttribute.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/EmbeddableAttribute.java
@@ -22,7 +22,6 @@ import java.io.Serializable;
 
 import org.apache.cayenne.configuration.ConfigurationNode;
 import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
-import org.apache.cayenne.util.Util;
 import org.apache.cayenne.util.XMLEncoder;
 import org.apache.cayenne.util.XMLSerializable;
 
@@ -55,23 +54,14 @@ public class EmbeddableAttribute implements ConfigurationNode, XMLSerializable,
         return visitor.visitEmbeddableAttribute(this);
     }
 
-    public void encodeAsXML(XMLEncoder encoder) {
-        encoder.print("<embeddable-attribute name=\"" + getName() + '\"');
-
-        if (getType() != null) {
-            encoder.print(" type=\"");
-            encoder.print(getType());
-            encoder.print('\"');
-        }
-
-        // If this obj attribute is mapped to db attribute
-        if (dbAttributeName != null) {
-            encoder.print(" db-attribute-name=\"");
-            encoder.print(Util.encodeXmlAttribute(dbAttributeName));
-            encoder.print('\"');
-        }
-
-        encoder.println("/>");
+    @Override
+    public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
+        encoder.start("embeddable-attribute")
+                .attribute("name", getName())
+                .attribute("type", getType())
+                .attribute("db-attribute-name", dbAttributeName);
+        delegate.visitEmbeddableAttribute(this);
+        encoder.end();
     }
 
     public String getDbAttributeName() {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/map/EmbeddedAttribute.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/EmbeddedAttribute.java b/cayenne-server/src/main/java/org/apache/cayenne/map/EmbeddedAttribute.java
index 1eeaa2e..3be89e5 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/EmbeddedAttribute.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/EmbeddedAttribute.java
@@ -19,6 +19,7 @@
 package org.apache.cayenne.map;
 
 import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
 import org.apache.cayenne.util.Util;
 import org.apache.cayenne.util.XMLEncoder;
 
@@ -55,33 +56,19 @@ public class EmbeddedAttribute extends ObjAttribute {
     }
 
     @Override
-    public void encodeAsXML(XMLEncoder encoder) {
-        encoder.print("<embedded-attribute name=\"" + getName() + '\"');
-        if (getType() != null) {
-            encoder.print(" type=\"");
-            encoder.print(getType());
-            encoder.print('\"');
-        }
-
-        if (attributeOverrides.isEmpty()) {
-            encoder.println("/>");
-            return;
-        }
-
-        encoder.println('>');
-
-        encoder.indent(1);
+    public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
+        encoder.start("embedded-attribute")
+                .attribute("name", getName())
+                .attribute("type", getType());
 
         for (Map.Entry<String, String> e : attributeOverrides.entrySet()) {
-            encoder.print("<embeddable-attribute-override name=\"");
-            encoder.print(e.getKey());
-            encoder.print("\" db-attribute-path=\"");
-            encoder.print(e.getValue());
-            encoder.println("\"/>");
+            encoder.start("embeddable-attribute-override")
+                    .attribute("name", e.getKey())
+                    .attribute("db-attribute-path", e.getValue())
+                    .end();
         }
 
-        encoder.indent(-1);
-        encoder.println("</embedded-attribute>");
+        encoder.end();
     }
 
     public Map<String, String> getAttributeOverrides() {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/map/EntityListener.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/EntityListener.java b/cayenne-server/src/main/java/org/apache/cayenne/map/EntityListener.java
index 84e172d..f9486bc 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/EntityListener.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/EntityListener.java
@@ -20,6 +20,7 @@ package org.apache.cayenne.map;
 
 import java.io.Serializable;
 
+import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
 import org.apache.cayenne.util.XMLEncoder;
 import org.apache.cayenne.util.XMLSerializable;
 
@@ -63,7 +64,8 @@ public class EntityListener implements Serializable, XMLSerializable {
         return callbacks;
     }
 
-    public void encodeAsXML(XMLEncoder encoder) {
+    @Override
+    public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
         encoder.print("<entity-listener class=\"");
         encoder.print(className);
         encoder.println("\">");

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/map/MapLoader.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/MapLoader.java b/cayenne-server/src/main/java/org/apache/cayenne/map/MapLoader.java
deleted file mode 100644
index 2ea7a6f..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/MapLoader.java
+++ /dev/null
@@ -1,1261 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.map;
-
-import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.dba.TypesMapping;
-import org.apache.cayenne.exp.ExpressionFactory;
-import org.apache.cayenne.util.Util;
-import org.xml.sax.Attributes;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.DefaultHandler;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.TreeMap;
-
-/**
- * Default MapLoader. Its responsibilities include reading DataMaps from XML
- * files and saving DataMap objects back to XML.
- */
-public class MapLoader extends DefaultHandler {
-
-	// TODO: andrus, 7/17/2006 - move upgrade logic out of here
-	final static String _1_2_PACKAGE_PREFIX = "org.objectstyle.cayenne.";
-	final static String _2_0_PACKAGE_PREFIX = "org.apache.cayenne.";
-
-	public static final String DATA_MAP_TAG = "data-map";
-
-	public static final String PROPERTY_TAG = "property";
-
-	/**
-	 * @since 3.0
-	 */
-	public static final String EMBEDDABLE_TAG = "embeddable";
-
-	/**
-	 * @since 3.0
-	 */
-	public static final String EMBEDDABLE_ATTRIBUTE_TAG = "embeddable-attribute";
-
-	/**
-	 * @since 3.0
-	 */
-	public static final String EMBEDDED_ATTRIBUTE_TAG = "embedded-attribute";
-
-	/**
-	 * @since 3.0
-	 */
-	public static final String EMBEDDABLE_ATTRIBUTE_OVERRIDE_TAG = "embeddable-attribute-override";
-
-	public static final String DB_ENTITY_TAG = "db-entity";
-	public static final String OBJ_ENTITY_TAG = "obj-entity";
-	public static final String DB_ATTRIBUTE_TAG = "db-attribute";
-	public static final String OBJ_ATTRIBUTE_TAG = "obj-attribute";
-	public static final String OBJ_ATTRIBUTE_OVERRIDE_TAG = "attribute-override";
-	public static final String OBJ_RELATIONSHIP_TAG = "obj-relationship";
-	public static final String DB_RELATIONSHIP_TAG = "db-relationship";
-	public static final String DB_RELATIONSHIP_REF_TAG = "db-relationship-ref";
-	public static final String DB_ATTRIBUTE_PAIR_TAG = "db-attribute-pair";
-	public static final String PROCEDURE_TAG = "procedure";
-	public static final String PROCEDURE_PARAMETER_TAG = "procedure-parameter";
-
-	// lifecycle listeners and callbacks related
-	public static final String POST_ADD_TAG = "post-add";
-	public static final String PRE_PERSIST_TAG = "pre-persist";
-	public static final String POST_PERSIST_TAG = "post-persist";
-	public static final String PRE_UPDATE_TAG = "pre-update";
-	public static final String POST_UPDATE_TAG = "post-update";
-	public static final String PRE_REMOVE_TAG = "pre-remove";
-	public static final String POST_REMOVE_TAG = "post-remove";
-	public static final String POST_LOAD_TAG = "post-load";
-
-	// Query-related
-	public static final String QUERY_TAG = "query";
-
-	public static final String QUERY_SQL_TAG = "sql";
-	public static final String QUERY_EJBQL_TAG = "ejbql";
-	public static final String QUERY_QUALIFIER_TAG = "qualifier";
-	public static final String QUERY_ORDERING_TAG = "ordering";
-	public static final String QUERY_PREFETCH_TAG = "prefetch";
-
-	public static final String TRUE = "true";
-	public static final String FALSE = "false";
-
-	public static final String DB_KEY_GENERATOR_TAG = "db-key-generator";
-	public static final String DB_GENERATOR_TYPE_TAG = "db-generator-type";
-	public static final String DB_GENERATOR_NAME_TAG = "db-generator-name";
-	public static final String DB_KEY_CACHE_SIZE_TAG = "db-key-cache-size";
-
-	/**
-	 * @since 3.0
-	 */
-	public static final String OBJ_ENTITY_ROOT = "obj-entity";
-
-	/**
-	 * @since 3.0
-	 */
-	public static final String DB_ENTITY_ROOT = "db-entity";
-
-	/**
-	 * @since 3.0
-	 */
-	public static final String PROCEDURE_ROOT = "procedure";
-
-	/**
-	 * @since 3.0
-	 */
-	public static final String DATA_MAP_ROOT = "data-map";
-
-	/**
-	 * @since 3.0
-	 */
-	public static final String JAVA_CLASS_ROOT = "java-class";
-
-	private static final String DATA_MAP_LOCATION_SUFFIX = ".map.xml";
-
-	// Reading from XML
-	private String mapVersion;
-	private DataMap dataMap;
-	private DbEntity dbEntity;
-	private ObjEntity objEntity;
-	private Embeddable embeddable;
-	private EmbeddedAttribute embeddedAttribute;
-	private DbRelationship dbRelationship;
-	private ObjRelationship objRelationship;
-	private DbAttribute attrib;
-	private Procedure procedure;
-	private QueryDescriptorLoader queryBuilder;
-	private String sqlKey;
-
-	private String descending;
-	private String ignoreCase;
-
-	private Map<String, StartClosure> startTagOpMap;
-	private Map<String, EndClosure> endTagOpMap;
-	private String currentTag;
-	private Attributes currentAttributes;
-	private StringBuilder charactersBuffer;
-	private Map<String, Object> mapProperties;
-
-	public MapLoader() {
-		// compile tag processors.
-		startTagOpMap = new HashMap<>(40);
-		endTagOpMap = new HashMap<>(40);
-
-		startTagOpMap.put(DATA_MAP_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartDataMap(attributes);
-			}
-		});
-		
-		startTagOpMap.put(DB_ENTITY_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartDbEntity(attributes);
-			}
-		});
-
-		startTagOpMap.put(DB_ATTRIBUTE_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartDbAttribute(attributes);
-			}
-		});
-
-		startTagOpMap.put(OBJ_ENTITY_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartObjEntity(attributes);
-			}
-		});
-
-		startTagOpMap.put(OBJ_ATTRIBUTE_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartObjAttribute(attributes);
-			}
-		});
-
-		startTagOpMap.put(OBJ_ATTRIBUTE_OVERRIDE_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartAttributeOverride(attributes);
-			}
-		});
-
-		startTagOpMap.put(EMBEDDABLE_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartEmbeddable(attributes);
-			}
-		});
-
-		startTagOpMap.put(EMBEDDABLE_ATTRIBUTE_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartEmbeddableAttribute(attributes);
-			}
-		});
-
-		startTagOpMap.put(EMBEDDABLE_ATTRIBUTE_OVERRIDE_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartEmbeddableAttributeOverride(attributes);
-			}
-		});
-
-		startTagOpMap.put(EMBEDDED_ATTRIBUTE_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartEmbeddedAttribute(attributes);
-			}
-		});
-
-		startTagOpMap.put(DB_RELATIONSHIP_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartDbRelationship(attributes);
-			}
-		});
-
-		startTagOpMap.put(DB_ATTRIBUTE_PAIR_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartDbAttributePair(attributes);
-			}
-		});
-
-		startTagOpMap.put(OBJ_RELATIONSHIP_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartObjRelationship(attributes);
-			}
-		});
-
-		startTagOpMap.put(DB_RELATIONSHIP_REF_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartDbRelationshipRef(attributes);
-			}
-		});
-
-		startTagOpMap.put(PROCEDURE_PARAMETER_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartProcedureParameter(attributes);
-			}
-		});
-
-		startTagOpMap.put(PROCEDURE_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartProcedure(attributes);
-			}
-		});
-
-		startTagOpMap.put(QUERY_EJBQL_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				charactersBuffer = new StringBuilder();
-			}
-		});
-
-		startTagOpMap.put(QUERY_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartQuery(attributes);
-			}
-		});
-
-		startTagOpMap.put(QUERY_SQL_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				charactersBuffer = new StringBuilder();
-				processStartQuerySQL(attributes);
-			}
-		});
-
-		startTagOpMap.put(QUERY_ORDERING_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				charactersBuffer = new StringBuilder();
-				processStartQueryOrdering(attributes);
-			}
-		});
-
-		startTagOpMap.put(DB_KEY_GENERATOR_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartDbKeyGenerator(attributes);
-			}
-		});
-
-		startTagOpMap.put(PROPERTY_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				// properties can belong to query or DataMap
-				if (queryBuilder != null) {
-					processStartQueryProperty(attributes);
-				} else {
-					processStartDataMapProperty(attributes);
-				}
-			}
-		});
-
-		startTagOpMap.put(POST_ADD_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartPostAdd(attributes);
-			}
-		});
-
-		startTagOpMap.put(PRE_PERSIST_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartPrePersist(attributes);
-			}
-		});
-
-		startTagOpMap.put(POST_PERSIST_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartPostPersist(attributes);
-			}
-		});
-
-		startTagOpMap.put(PRE_UPDATE_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartPreUpdate(attributes);
-			}
-		});
-
-		startTagOpMap.put(POST_UPDATE_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartPostUpdate(attributes);
-			}
-		});
-
-		startTagOpMap.put(PRE_REMOVE_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartPreRemove(attributes);
-			}
-		});
-
-		startTagOpMap.put(POST_REMOVE_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartPostRemove(attributes);
-			}
-		});
-
-		startTagOpMap.put(POST_LOAD_TAG, new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				processStartPostLoad(attributes);
-			}
-		});
-
-		StartClosure resetBuffer = new StartClosure() {
-
-			@Override
-			void execute(Attributes attributes) throws SAXException {
-				charactersBuffer = new StringBuilder();
-			}
-		};
-
-		startTagOpMap.put(QUERY_PREFETCH_TAG, resetBuffer);
-		startTagOpMap.put(QUERY_QUALIFIER_TAG, resetBuffer);
-		startTagOpMap.put(DB_GENERATOR_TYPE_TAG, resetBuffer);
-		startTagOpMap.put(DB_GENERATOR_NAME_TAG, resetBuffer);
-		startTagOpMap.put(DB_KEY_CACHE_SIZE_TAG, resetBuffer);
-
-		endTagOpMap.put(DATA_MAP_TAG, new EndClosure() {
-
-			@Override
-			void execute() throws SAXException {
-				processEndDataMap();
-			}
-		});
-		endTagOpMap.put(DB_ENTITY_TAG, new EndClosure() {
-
-			@Override
-			void execute() throws SAXException {
-				processEndDbEntity();
-			}
-		});
-		endTagOpMap.put(OBJ_ENTITY_TAG, new EndClosure() {
-
-			@Override
-			void execute() throws SAXException {
-				processEndObjEntity();
-			}
-		});
-		endTagOpMap.put(EMBEDDABLE_TAG, new EndClosure() {
-
-			@Override
-			void execute() throws SAXException {
-				processEndEmbeddable();
-			}
-		});
-		endTagOpMap.put(EMBEDDABLE_ATTRIBUTE_TAG, new EndClosure() {
-
-			@Override
-			void execute() throws SAXException {
-				processEndEmbeddedAttribute();
-			}
-		});
-
-		endTagOpMap.put(DB_ATTRIBUTE_TAG, new EndClosure() {
-
-			@Override
-			void execute() throws SAXException {
-				processEndDbAttribute();
-			}
-		});
-
-		endTagOpMap.put(DB_RELATIONSHIP_TAG, new EndClosure() {
-
-			@Override
-			void execute() throws SAXException {
-				processEndDbRelationship();
-			}
-		});
-		endTagOpMap.put(OBJ_RELATIONSHIP_TAG, new EndClosure() {
-
-			@Override
-			void execute() throws SAXException {
-				processEndObjRelationship();
-			}
-		});
-		endTagOpMap.put(DB_GENERATOR_TYPE_TAG, new EndClosure() {
-
-			@Override
-			void execute() throws SAXException {
-				processEndDbGeneratorType();
-			}
-		});
-		endTagOpMap.put(DB_GENERATOR_NAME_TAG, new EndClosure() {
-
-			@Override
-			void execute() throws SAXException {
-				processEndDbGeneratorName();
-			}
-		});
-		endTagOpMap.put(DB_KEY_CACHE_SIZE_TAG, new EndClosure() {
-
-			@Override
-			void execute() throws SAXException {
-				processEndDbKeyCacheSize();
-			}
-		});
-		endTagOpMap.put(PROCEDURE_PARAMETER_TAG, new EndClosure() {
-
-			@Override
-			void execute() throws SAXException {
-				processEndProcedureParameter();
-			}
-		});
-		endTagOpMap.put(PROCEDURE_TAG, new EndClosure() {
-
-			@Override
-			void execute() throws SAXException {
-				processEndProcedure();
-			}
-		});
-		endTagOpMap.put(QUERY_TAG, new EndClosure() {
-
-			@Override
-			void execute() throws SAXException {
-				processEndQuery();
-			}
-		});
-		endTagOpMap.put(QUERY_SQL_TAG, new EndClosure() {
-
-			@Override
-			void execute() throws SAXException {
-				processEndQuerySQL();
-			}
-		});
-
-		endTagOpMap.put(QUERY_EJBQL_TAG, new EndClosure() {
-
-			@Override
-			void execute() throws SAXException {
-				processEndEjbqlQuery();
-			}
-		});
-
-		endTagOpMap.put(QUERY_QUALIFIER_TAG, new EndClosure() {
-
-			@Override
-			void execute() throws SAXException {
-				processEndQualifier();
-			}
-		});
-		endTagOpMap.put(QUERY_ORDERING_TAG, new EndClosure() {
-
-			@Override
-			void execute() throws SAXException {
-				processEndQueryOrdering();
-			}
-		});
-		endTagOpMap.put(QUERY_PREFETCH_TAG, new EndClosure() {
-
-			@Override
-			void execute() throws SAXException {
-				processEndQueryPrefetch();
-			}
-		});
-	}
-	
-	private void processStartDataMap(Attributes attributes) {
-		this.mapVersion = attributes.getValue("", "project-version");
-	}
-
-	private void processStartPostAdd(Attributes attributes) {
-		String methodName = attributes.getValue("", "method-name");
-		if (objEntity != null) {
-			// new callback tags - children of "obj-entity"
-			objEntity.getCallbackMap().getPostAdd().addCallbackMethod(methodName);
-		}
-	}
-
-	private void processStartPrePersist(Attributes attributes) {
-
-		// 3.0 -> 3.0.0.1 upgrade hack... treat pre-persist as post-add
-		// only 3.0 used "pre-persist" in a "post-add" sense
-		if ("3.0".equals(mapVersion)) {
-			processStartPostAdd(attributes);
-		} else {
-
-			String methodName = attributes.getValue("", "method-name");
-
-			if (objEntity != null) {
-				// new callback tags - children of "obj-entity"
-				objEntity.getCallbackMap().getPrePersist().addCallbackMethod(methodName);
-			}
-		}
-	}
-
-	private void processStartPostPersist(Attributes attributes) {
-		String methodName = attributes.getValue("", "method-name");
-		if (objEntity != null) {
-			objEntity.getCallbackMap().getPostPersist().addCallbackMethod(methodName);
-		}
-	}
-
-	private void processStartPreUpdate(Attributes attributes) {
-		String methodName = attributes.getValue("", "method-name");
-		if (objEntity != null) {
-			objEntity.getCallbackMap().getPreUpdate().addCallbackMethod(methodName);
-		}
-	}
-
-	private void processStartPostUpdate(Attributes attributes) {
-		String methodName = attributes.getValue("", "method-name");
-		if (objEntity != null) {
-			objEntity.getCallbackMap().getPostUpdate().addCallbackMethod(methodName);
-		}
-	}
-
-	private void processStartPreRemove(Attributes attributes) {
-		String methodName = attributes.getValue("", "method-name");
-		if (objEntity != null) {
-			objEntity.getCallbackMap().getPreRemove().addCallbackMethod(methodName);
-		}
-	}
-
-	private void processStartPostRemove(Attributes attributes) {
-		String methodName = attributes.getValue("", "method-name");
-		if (objEntity != null) {
-			objEntity.getCallbackMap().getPostRemove().addCallbackMethod(methodName);
-		}
-	}
-
-	private void processStartPostLoad(Attributes attributes) {
-		String methodName = attributes.getValue("", "method-name");
-		if (objEntity != null) {
-			objEntity.getCallbackMap().getPostLoad().addCallbackMethod(methodName);
-		}
-	}
-
-	/**
-	 * Loads a DataMap from XML input source.
-	 */
-	public synchronized DataMap loadDataMap(InputSource src) throws CayenneRuntimeException {
-		if (src == null) {
-			throw new NullPointerException("Null InputSource.");
-		}
-
-		try {
-			String mapName = mapNameFromLocation(src.getSystemId());
-			dataMap = new DataMap(mapName);
-			XMLReader parser = Util.createXmlReader();
-
-			parser.setContentHandler(this);
-			parser.setErrorHandler(this);
-			parser.parse(src);
-		} catch (SAXException e) {
-			dataMap = null;
-			throw new CayenneRuntimeException("Wrong DataMap format, last processed tag: "
-					+ constructCurrentStateString(), Util.unwindException(e));
-		} catch (Exception e) {
-			dataMap = null;
-			throw new CayenneRuntimeException("Error loading DataMap, last processed tag: "
-					+ constructCurrentStateString(), Util.unwindException(e));
-		}
-		return dataMap;
-	}
-
-	/**
-	 * Constructs error message for displaying as exception message
-	 */
-	private Appendable constructCurrentStateString() {
-		StringBuilder sb = new StringBuilder();
-		sb.append("<").append(currentTag);
-
-		if (currentAttributes != null) {
-			for (int i = 0; i < currentAttributes.getLength(); i++) {
-				sb.append(" ").append(currentAttributes.getLocalName(i)).append("=").append("\"")
-						.append(currentAttributes.getValue(i)).append("\"");
-			}
-		}
-		sb.append(">");
-
-		return sb;
-	}
-
-	/**
-	 * Helper method to guess the map name from its location.
-	 */
-	protected String mapNameFromLocation(String location) {
-		if (location == null) {
-			return "Untitled";
-		}
-
-		int lastSlash = location.lastIndexOf('/');
-		if (lastSlash < 0) {
-			lastSlash = location.lastIndexOf('\\');
-		}
-
-		if (lastSlash >= 0 && lastSlash + 1 < location.length()) {
-			location = location.substring(lastSlash + 1);
-		}
-
-		if (location.endsWith(DATA_MAP_LOCATION_SUFFIX)) {
-			location = location.substring(0, location.length() - DATA_MAP_LOCATION_SUFFIX.length());
-		}
-
-		return location;
-	}
-
-	@Override
-	public void startElement(String namespaceUri, String localName, String qName, Attributes attributes)
-			throws SAXException {
-
-		rememberCurrentState(localName, attributes);
-
-		StartClosure op = startTagOpMap.get(localName);
-		if (op != null) {
-			op.execute(attributes);
-		}
-	}
-
-	@Override
-	public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
-
-		EndClosure op = endTagOpMap.get(localName);
-		if (op != null) {
-			op.execute();
-		}
-
-		resetCurrentState();
-		charactersBuffer = null;
-	}
-
-	private void processStartEmbeddable(Attributes atts) {
-		embeddable = new Embeddable(atts.getValue("", "className"));
-		dataMap.addEmbeddable(embeddable);
-	}
-
-	private void processStartEmbeddableAttribute(Attributes atts) {
-		String name = atts.getValue("", "name");
-		String type = atts.getValue("", "type");
-		String dbName = atts.getValue("", "db-attribute-name");
-
-		EmbeddableAttribute ea = new EmbeddableAttribute(name);
-		ea.setType(type);
-		ea.setDbAttributeName(dbName);
-		embeddable.addAttribute(ea);
-	}
-
-	private void processStartEmbeddedAttribute(Attributes atts) {
-
-		String name = atts.getValue("", "name");
-		String type = atts.getValue("", "type");
-
-		embeddedAttribute = new EmbeddedAttribute(name);
-		embeddedAttribute.setType(type);
-		objEntity.addAttribute(embeddedAttribute);
-	}
-
-	private void processStartEmbeddableAttributeOverride(Attributes atts) {
-		String name = atts.getValue("", "name");
-		String dbName = atts.getValue("", "db-attribute-path");
-		embeddedAttribute.addAttributeOverride(name, dbName);
-	}
-
-	private void processStartDbEntity(Attributes atts) {
-		String name = atts.getValue("", "name");
-
-		dbEntity = new DbEntity(name);
-		dbEntity.setSchema(atts.getValue("", "schema"));
-		dbEntity.setCatalog(atts.getValue("", "catalog"));
-
-		dataMap.addDbEntity(dbEntity);
-	}
-
-	private void processStartDbAttribute(Attributes atts) {
-		String name = atts.getValue("", "name");
-		String type = atts.getValue("", "type");
-
-		attrib = new DbAttribute(name);
-		attrib.setType(TypesMapping.getSqlTypeByName(type));
-		dbEntity.addAttribute(attrib);
-
-		String length = atts.getValue("", "length");
-		if (length != null) {
-			attrib.setMaxLength(Integer.parseInt(length));
-		}
-
-		// this is an obsolete 1.2 'precision' attribute that really meant
-		// 'scale'
-		String pseudoPrecision = atts.getValue("", "precision");
-		if (pseudoPrecision != null) {
-			attrib.setScale(Integer.parseInt(pseudoPrecision));
-		}
-
-		String precision = atts.getValue("", "attributePrecision");
-		if (precision != null) {
-			attrib.setAttributePrecision(Integer.parseInt(precision));
-		}
-
-		String scale = atts.getValue("", "scale");
-		if (scale != null) {
-			attrib.setScale(Integer.parseInt(scale));
-		}
-
-		attrib.setPrimaryKey(TRUE.equalsIgnoreCase(atts.getValue("", "isPrimaryKey")));
-		attrib.setMandatory(TRUE.equalsIgnoreCase(atts.getValue("", "isMandatory")));
-		attrib.setGenerated(TRUE.equalsIgnoreCase(atts.getValue("", "isGenerated")));
-	}
-
-	private void processStartDbKeyGenerator(Attributes atts) {
-		DbKeyGenerator pkGenerator = new DbKeyGenerator();
-		dbEntity.setPrimaryKeyGenerator(pkGenerator);
-	}
-
-	private void processStartQuerySQL(Attributes atts) {
-		this.sqlKey = convertClassNameFromV1_2(atts.getValue("", "adapter-class"));
-	}
-
-	private void processStartObjEntity(Attributes atts) {
-		objEntity = new ObjEntity(atts.getValue("", "name"));
-		objEntity.setClassName(atts.getValue("", "className"));
-		objEntity.setClientClassName(atts.getValue("", "clientClassName"));
-
-		String isAbstract = atts.getValue("", "abstract");
-		objEntity.setAbstract(TRUE.equalsIgnoreCase(isAbstract));
-
-		String readOnly = atts.getValue("", "readOnly");
-		objEntity.setReadOnly(TRUE.equalsIgnoreCase(readOnly));
-
-		String serverOnly = atts.getValue("", "serverOnly");
-		objEntity.setServerOnly(TRUE.equalsIgnoreCase(serverOnly));
-
-		String excludeSuperclassListeners = atts.getValue("", "exclude-superclass-listeners");
-		objEntity.setExcludingSuperclassListeners(TRUE.equalsIgnoreCase(excludeSuperclassListeners));
-
-		String excludeDefaultListeners = atts.getValue("", "exclude-default-listeners");
-		objEntity.setExcludingDefaultListeners(TRUE.equalsIgnoreCase(excludeDefaultListeners));
-
-		String lockType = atts.getValue("", "lock-type");
-		if ("optimistic".equals(lockType)) {
-			objEntity.setDeclaredLockType(ObjEntity.LOCK_TYPE_OPTIMISTIC);
-		}
-
-		String superEntityName = atts.getValue("", "superEntityName");
-		if (superEntityName != null) {
-			objEntity.setSuperEntityName(superEntityName);
-		} else {
-			objEntity.setSuperClassName(atts.getValue("", "superClassName"));
-			objEntity.setClientSuperClassName(atts.getValue("", "clientSuperClassName"));
-		}
-
-		objEntity.setDbEntityName(atts.getValue("", "dbEntityName"));
-
-		dataMap.addObjEntity(objEntity);
-	}
-
-	private void processStartObjAttribute(Attributes atts) {
-		String name = atts.getValue("", "name");
-		String type = atts.getValue("", "type");
-
-		String lock = atts.getValue("", "lock");
-
-		ObjAttribute oa = new ObjAttribute(name);
-		oa.setType(type);
-		oa.setUsedForLocking(TRUE.equalsIgnoreCase(lock));
-		objEntity.addAttribute(oa);
-		String dbPath = atts.getValue("", "db-attribute-path");
-		if (dbPath == null) {
-			dbPath = atts.getValue("", "db-attribute-name");
-		}
-		oa.setDbAttributePath(dbPath);
-	}
-
-	private void processStartAttributeOverride(Attributes atts) {
-		String name = atts.getValue("", "name");
-		String dbPath = atts.getValue("", "db-attribute-path");
-
-		objEntity.addAttributeOverride(name, dbPath);
-	}
-
-	private void processStartDbRelationship(Attributes atts) throws SAXException {
-		String name = atts.getValue("", "name");
-		if (name == null) {
-			throw new SAXException("MapLoader::processStartDbRelationship()," + " Unable to parse name. Attributes:\n"
-					+ printAttributes(atts));
-		}
-
-		String sourceName = atts.getValue("", "source");
-		if (sourceName == null) {
-			throw new SAXException("MapLoader::processStartDbRelationship() - null source entity");
-		}
-
-		DbEntity source = dataMap.getDbEntity(sourceName);
-		if (source == null) {
-			return;
-		}
-
-		String toManyString = atts.getValue("", "toMany");
-		boolean toMany = toManyString != null && toManyString.equalsIgnoreCase(TRUE);
-
-		String toDependPkString = atts.getValue("", "toDependentPK");
-		boolean toDependentPK = toDependPkString != null && toDependPkString.equalsIgnoreCase(TRUE);
-
-		dbRelationship = new DbRelationship(name);
-		dbRelationship.setSourceEntity(source);
-		dbRelationship.setTargetEntityName(atts.getValue("", "target"));
-		dbRelationship.setToMany(toMany);
-		dbRelationship.setToDependentPK(toDependentPK);
-
-		source.addRelationship(dbRelationship);
-	}
-
-	private void processStartDbRelationshipRef(Attributes atts) throws SAXException {
-		// db-relationship-ref element is deprecated and is supported for
-		// backwards
-		// compatibility only
-
-		String name = atts.getValue("", "name");
-		if (name == null) {
-			throw new SAXException("MapLoader::processStartDbRelationshipRef()" + ", Null DbRelationship name for "
-					+ objRelationship.getName());
-		}
-
-		String path = objRelationship.getDbRelationshipPath();
-		path = (path != null) ? path + "." + name : name;
-		objRelationship.setDbRelationshipPath(path);
-	}
-
-	private void processStartDbAttributePair(Attributes atts) {
-		DbJoin join = new DbJoin(dbRelationship);
-		join.setSourceName(atts.getValue("", "source"));
-		join.setTargetName(atts.getValue("", "target"));
-		dbRelationship.addJoin(join);
-	}
-
-	private void processStartObjRelationship(Attributes atts) throws SAXException {
-		String name = atts.getValue("", "name");
-		if (null == name) {
-			throw new SAXException("MapLoader::processStartObjRelationship(),"
-					+ " Unable to parse target. Attributes:\n" + printAttributes(atts));
-		}
-
-		String collectionType = atts.getValue("", "collection-type");
-		String mapKey = atts.getValue("", "map-key");
-
-		String sourceName = atts.getValue("", "source");
-		if (sourceName == null) {
-			throw new SAXException("MapLoader::processStartObjRelationship(),"
-					+ " Unable to parse source. Attributes:\n" + printAttributes(atts));
-		}
-
-		ObjEntity source = dataMap.getObjEntity(sourceName);
-		if (source == null) {
-			throw new SAXException("MapLoader::processStartObjRelationship()," + " Unable to find source " + sourceName);
-		}
-
-		String deleteRuleName = atts.getValue("", "deleteRule");
-		int deleteRule = (deleteRuleName != null) ? DeleteRule.deleteRuleForName(deleteRuleName) : DeleteRule.NO_ACTION;
-
-		objRelationship = new ObjRelationship(name);
-		objRelationship.setSourceEntity(source);
-		objRelationship.setTargetEntityName(atts.getValue("", "target"));
-		objRelationship.setDeleteRule(deleteRule);
-		objRelationship.setUsedForLocking(TRUE.equalsIgnoreCase(atts.getValue("", "lock")));
-		objRelationship.setDeferredDbRelationshipPath((atts.getValue("", "db-relationship-path")));
-		objRelationship.setCollectionType(collectionType);
-		objRelationship.setMapKey(mapKey);
-		source.addRelationship(objRelationship);
-	}
-
-	private void processStartProcedure(Attributes attributes) throws SAXException {
-
-		String name = attributes.getValue("", "name");
-		if (null == name) {
-			throw new SAXException("MapLoader::processStartProcedure()," + " no procedure name.");
-		}
-
-		String schema = attributes.getValue("", "schema");
-		String catalog = attributes.getValue("", "catalog");
-		String returningValue = attributes.getValue("", "returningValue");
-
-		procedure = new Procedure(name);
-		procedure.setReturningValue(returningValue != null && returningValue.equalsIgnoreCase(TRUE));
-		procedure.setSchema(schema);
-		procedure.setCatalog(catalog);
-		dataMap.addProcedure(procedure);
-	}
-
-	private void processStartProcedureParameter(Attributes attributes) throws SAXException {
-
-		String name = attributes.getValue("", "name");
-		if (name == null) {
-			throw new SAXException("MapLoader::processStartProcedureParameter()," + " no procedure parameter name.");
-		}
-
-		ProcedureParameter parameter = new ProcedureParameter(name);
-
-		String type = attributes.getValue("", "type");
-		if (type != null) {
-			parameter.setType(TypesMapping.getSqlTypeByName(type));
-		}
-
-		String length = attributes.getValue("", "length");
-		if (length != null) {
-			parameter.setMaxLength(Integer.parseInt(length));
-		}
-
-		String precision = attributes.getValue("", "precision");
-		if (precision != null) {
-			parameter.setPrecision(Integer.parseInt(precision));
-		}
-
-		String direction = attributes.getValue("", "direction");
-		if ("in".equals(direction)) {
-			parameter.setDirection(ProcedureParameter.IN_PARAMETER);
-		} else if ("out".equals(direction)) {
-			parameter.setDirection(ProcedureParameter.OUT_PARAMETER);
-		} else if ("in_out".equals(direction)) {
-			parameter.setDirection(ProcedureParameter.IN_OUT_PARAMETER);
-		}
-
-		procedure.addCallParameter(parameter);
-	}
-
-	private void processStartQuery(Attributes attributes) throws SAXException {
-		String name = attributes.getValue("", "name");
-		if (null == name) {
-			throw new SAXException("MapLoader::processStartQuery(), no query name.");
-		}
-
-		this.queryBuilder = new QueryDescriptorLoader();
-
-		String type = attributes.getValue("", "type");
-		// Legacy format support (v7 and older)
-		if(type == null) {
-			queryBuilder.setLegacyFactory(attributes.getValue("", "factory"));
-		} else {
-			queryBuilder.setQueryType(type);
-		}
-
-		String rootType = attributes.getValue("", "root");
-		String rootName = attributes.getValue("", "root-name");
-		String resultEntity = attributes.getValue("", "result-entity");
-
-		queryBuilder.setName(name);
-		queryBuilder.setRoot(dataMap, rootType, rootName);
-
-		// TODO: Andrus, 2/13/2006 'result-type' is only used in ProcedureQuery
-		// and is
-		// deprecated in 1.2
-		if (!Util.isEmptyString(resultEntity)) {
-			queryBuilder.setResultEntity(resultEntity);
-		}
-	}
-
-	private void processStartQueryProperty(Attributes attributes) throws SAXException {
-		String name = attributes.getValue("", "name");
-		if (null == name) {
-			throw new SAXException("MapLoader::processStartQueryProperty(), no property name.");
-		}
-
-		String value = attributes.getValue("", "value");
-		if (null == value) {
-			throw new SAXException("MapLoader::processStartQueryProperty(), no property value.");
-		}
-
-		queryBuilder.addProperty(name, value);
-	}
-
-	private void processStartDataMapProperty(Attributes attributes) throws SAXException {
-		String name = attributes.getValue("", "name");
-		if (null == name) {
-			throw new SAXException("MapLoader::processStartDataMapProperty(), no property name.");
-		}
-
-		String value = attributes.getValue("", "value");
-		if (null == value) {
-			throw new SAXException("MapLoader::processStartDataMapProperty(), no property value.");
-		}
-
-		if (mapProperties == null) {
-			mapProperties = new TreeMap<String, Object>();
-		}
-
-		mapProperties.put(name, value);
-	}
-
-	private void processEndQueryPrefetch() {
-		queryBuilder.addPrefetch(charactersBuffer.toString());
-	}
-
-	private void processStartQueryOrdering(Attributes attributes) {
-		descending = attributes.getValue("", "descending");
-		ignoreCase = attributes.getValue("", "ignore-case");
-	}
-
-	private void processEndQuery() {
-		dataMap.addQueryDescriptor(queryBuilder.buildQueryDescriptor());
-		queryBuilder = null;
-	}
-
-	private void processEndEjbqlQuery() throws SAXException {
-		queryBuilder.setEjbql(charactersBuffer.toString());
-	}
-
-	private void processEndQuerySQL() {
-		queryBuilder.addSql(charactersBuffer.toString(), sqlKey);
-		sqlKey = null;
-	}
-
-	private void processEndQualifier() {
-		String qualifier = charactersBuffer.toString();
-		if (qualifier.trim().length() == 0) {
-			return;
-		}
-
-		// qualifier can belong to ObjEntity, DbEntity or a query
-		if (objEntity != null) {
-			objEntity.setDeclaredQualifier(ExpressionFactory.exp(qualifier));
-		} else if (dbEntity != null) {
-			dbEntity.setQualifier(ExpressionFactory.exp(qualifier));
-		} else {
-			queryBuilder.setQualifier(qualifier);
-		}
-	}
-
-	private void processEndQueryOrdering() {
-		String path = charactersBuffer.toString();
-		queryBuilder.addOrdering(path, descending, ignoreCase);
-	}
-
-	private void processEndDbAttribute() {
-		attrib = null;
-	}
-
-	private void processEndDbEntity() {
-		dbEntity = null;
-	}
-
-	private void processEndProcedure() {
-		procedure = null;
-	}
-
-	private void processEndProcedureParameter() {
-	}
-
-	private void processEndDbGeneratorType() {
-		if (dbEntity == null)
-			return;
-		DbKeyGenerator pkGenerator = dbEntity.getPrimaryKeyGenerator();
-		if (pkGenerator == null)
-			return;
-		pkGenerator.setGeneratorType(charactersBuffer.toString());
-		if (pkGenerator.getGeneratorType() == null) {
-			dbEntity.setPrimaryKeyGenerator(null);
-		}
-	}
-
-	private void processEndDbGeneratorName() {
-		if (dbEntity == null)
-			return;
-		DbKeyGenerator pkGenerator = dbEntity.getPrimaryKeyGenerator();
-		if (pkGenerator == null)
-			return;
-		pkGenerator.setGeneratorName(charactersBuffer.toString());
-	}
-
-	private void processEndDbKeyCacheSize() {
-		if (dbEntity == null)
-			return;
-		DbKeyGenerator pkGenerator = dbEntity.getPrimaryKeyGenerator();
-		if (pkGenerator == null)
-			return;
-		try {
-			pkGenerator.setKeyCacheSize(new Integer(charactersBuffer.toString().trim()));
-		} catch (Exception ex) {
-			pkGenerator.setKeyCacheSize(null);
-		}
-	}
-
-	private void processEndDataMap() {
-		if (mapProperties != null) {
-			dataMap.initWithProperties(mapProperties);
-		}
-
-		mapProperties = null;
-		mapVersion = null;
-	}
-
-	private void processEndObjEntity() {
-		objEntity = null;
-	}
-
-	private void processEndEmbeddable() {
-		embeddable = null;
-	}
-
-	private void processEndEmbeddedAttribute() {
-		embeddedAttribute = null;
-	}
-
-	private void processEndDbRelationship() {
-		dbRelationship = null;
-	}
-
-	private void processEndObjRelationship() {
-		objRelationship = null;
-	}
-
-	/** Prints the attributes. Used for error reporting purposes. */
-	private StringBuffer printAttributes(Attributes atts) {
-		StringBuffer sb = new StringBuffer();
-		String name, value;
-		for (int i = 0; i < atts.getLength(); i++) {
-			value = atts.getQName(i);
-			name = atts.getValue(i);
-			sb.append("Name: ").append(name).append("\tValue: ").append(value).append("\n");
-		}
-		return sb;
-	}
-
-	@Override
-	public void characters(char[] text, int start, int length) throws org.xml.sax.SAXException {
-		if (charactersBuffer != null) {
-			charactersBuffer.append(text, start, length);
-		}
-	}
-
-	private void rememberCurrentState(String tag, Attributes attrs) {
-		currentTag = tag;
-		currentAttributes = attrs;
-	}
-
-	private void resetCurrentState() {
-		currentTag = null;
-		currentAttributes = null;
-	}
-
-	/**
-	 * @since 2.0
-	 */
-	String convertClassNameFromV1_2(String name) {
-		if (name == null) {
-			return null;
-		}
-
-		// upgrade from v. <= 1.2
-		if (name.startsWith(_1_2_PACKAGE_PREFIX)) {
-			return _2_0_PACKAGE_PREFIX + name.substring(_1_2_PACKAGE_PREFIX.length());
-		}
-
-		return name;
-	}
-
-	abstract class StartClosure {
-
-		abstract void execute(Attributes attributes) throws SAXException;
-	}
-
-	abstract class EndClosure {
-
-		abstract void execute() throws SAXException;
-	}
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/map/ObjAttribute.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/ObjAttribute.java b/cayenne-server/src/main/java/org/apache/cayenne/map/ObjAttribute.java
index 57bfca3..ae03d13 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/ObjAttribute.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/ObjAttribute.java
@@ -24,7 +24,6 @@ import java.util.Iterator;
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.configuration.ConfigurationNode;
 import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
-import org.apache.cayenne.dba.TypesMapping;
 import org.apache.cayenne.util.CayenneMapEntry;
 import org.apache.cayenne.util.Util;
 import org.apache.cayenne.util.XMLEncoder;
@@ -100,28 +99,15 @@ public class ObjAttribute extends Attribute implements ConfigurationNode {
      * @since 1.1
      */
     @Override
-    public void encodeAsXML(XMLEncoder encoder) {
-        encoder.print("<obj-attribute name=\"" + getName() + '\"');
-
-        if (getType() != null) {
-            encoder.print(" type=\"");
-            encoder.print(Util.encodeXmlAttribute(getType()));
-            encoder.print('\"');
-        }
-
-        if (isUsedForLocking()) {
-            encoder.print(" lock=\"true\"");
-        }
-
-        // If this obj attribute is mapped to db attribute
-        if (/*getDbAttribute() != null
-                || (((ObjEntity) getEntity()).isAbstract() && */!Util.isEmptyString(getDbAttributePath())) {
-            encoder.print(" db-attribute-path=\"");
-            encoder.print(Util.encodeXmlAttribute(getDbAttributePath()));
-            encoder.print('\"');
-        }
-
-        encoder.println("/>");
+    public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
+        encoder.start("obj-attribute")
+                .attribute("name", getName())
+                .attribute("type", getType())
+                .attribute("lock", isUsedForLocking())
+                .attribute("db-attribute-path", getDbAttributePath());
+
+        delegate.visitObjAttribute(this);
+        encoder.end();
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/map/ObjEntity.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/ObjEntity.java b/cayenne-server/src/main/java/org/apache/cayenne/map/ObjEntity.java
index fee79a3..311f5ed 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/ObjEntity.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/ObjEntity.java
@@ -118,103 +118,59 @@ public class ObjEntity extends Entity implements ObjEntityListener, Configuratio
      * @since 1.1
      */
     @Override
-    public void encodeAsXML(XMLEncoder encoder) {
-        encoder.print("<obj-entity name=\"");
-        encoder.print(getName());
+    public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
+        encoder.start("obj-entity").attribute("name", getName());
 
-        // additionally validate that superentity exists
+        // additionally validate that super entity exists
         if (getSuperEntityName() != null && getSuperEntity() != null) {
-            encoder.print("\" superEntityName=\"");
-            encoder.print(getSuperEntityName());
+            encoder.attribute("superEntityName", getSuperEntityName());
         }
 
-        if (isAbstract()) {
-            encoder.print("\" abstract=\"true");
-        }
-
-        if (isServerOnly()) {
-            encoder.print("\" serverOnly=\"true");
-        }
-
-        if (getClassName() != null) {
-            encoder.print("\" className=\"");
-            encoder.print(getClassName());
-        }
-
-        if (getClientClassName() != null) {
-            encoder.print("\" clientClassName=\"");
-            encoder.print(getClientClassName());
-        }
-
-        if (isReadOnly()) {
-            encoder.print("\" readOnly=\"true");
-        }
+        encoder.attribute("abstract", isAbstract())
+                .attribute("serverOnly", isServerOnly())
+                .attribute("className", getClassName())
+                .attribute("clientClassName", getClientClassName())
+                .attribute("readOnly", isReadOnly());
 
         if (getDeclaredLockType() == LOCK_TYPE_OPTIMISTIC) {
-            encoder.print("\" lock-type=\"optimistic");
+            encoder.attribute("lock-type", "optimistic");
         }
 
         if (getDbEntityName() != null && getDbEntity() != null) {
-
             // not writing DbEntity name if sub entity has same DbEntity
             // as super entity, see CAY-1477
             if (!(getSuperEntity() != null && getSuperEntity().getDbEntity() == getDbEntity())) {
-                encoder.print("\" dbEntityName=\"");
-                encoder.print(Util.encodeXmlAttribute(getDbEntityName()));
+                encoder.attribute("dbEntityName", getDbEntityName());
             }
         }
 
         if (getSuperEntityName() == null && getSuperClassName() != null) {
-            encoder.print("\" superClassName=\"");
-            encoder.print(getSuperClassName());
+            encoder.attribute("superClassName", getSuperClassName());
         }
 
         if (getSuperEntityName() == null && getClientSuperClassName() != null) {
-            encoder.print("\" clientSuperClassName=\"");
-            encoder.print(getClientSuperClassName());
-        }
-
-        // deprecated
-        if (isExcludingSuperclassListeners()) {
-            encoder.print("\" exclude-superclass-listeners=\"true");
-        }
-
-        // deprecated
-        if (isExcludingDefaultListeners()) {
-            encoder.print("\" exclude-default-listeners=\"true");
+            encoder.attribute("clientSuperClassName", getClientSuperClassName());
         }
 
-        encoder.println("\">");
-        encoder.indent(1);
-
         if (qualifier != null) {
-            encoder.print("<qualifier>");
-            qualifier.encodeAsXML(encoder);
-            encoder.println("</qualifier>");
+            encoder.start("qualifier").nested(qualifier, delegate).end();
         }
 
         // store attributes
-        encoder.print(getDeclaredAttributes());
+        encoder.nested(getDeclaredAttributes(), delegate);
 
         for (Map.Entry<String, String> override : attributeOverrides.entrySet()) {
-            encoder.print("<attribute-override name=\"" + override.getKey() + '\"');
-            encoder.print(" db-attribute-path=\"");
-            encoder.print(Util.encodeXmlAttribute(override.getValue()));
-            encoder.print('\"');
-            encoder.println("/>");
-        }
-
-        // deprecated
-        // write entity listeners
-        for (EntityListener entityListener : entityListeners) {
-            entityListener.encodeAsXML(encoder);
+            encoder.start("attribute-override")
+                    .attribute("name", override.getKey())
+                    .attribute("db-attribute-path", override.getValue())
+                    .end();
         }
 
         // write entity-level callbacks
         getCallbackMap().encodeCallbacksAsXML(encoder);
 
-        encoder.indent(-1);
-        encoder.println("</obj-entity>");
+        delegate.visitObjEntity(this);
+        encoder.end();
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/map/ObjRelationship.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/ObjRelationship.java b/cayenne-server/src/main/java/org/apache/cayenne/map/ObjRelationship.java
index 6163684..0fa0812 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/ObjRelationship.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/ObjRelationship.java
@@ -104,51 +104,43 @@ public class ObjRelationship extends Relationship implements ConfigurationNode {
      * 
      * @since 1.1
      */
-    public void encodeAsXML(XMLEncoder encoder) {
-        ObjEntity source = (ObjEntity) getSourceEntity();
+    @Override
+    public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
+        ObjEntity source = getSourceEntity();
         if (source == null) {
             return;
         }
 
-        encoder.print("<obj-relationship name=\"" + getName());
-        encoder.print("\" source=\"" + source.getName());
+        encoder.start("obj-relationship")
+                .attribute("name", getName())
+                .attribute("source", source.getName());
 
         // looking up a target entity ensures that bogus names are not saved...
-        // whether
-        // this is good or bad is debatable, as users may want to point to
-        // non-existent
-        // entities on purpose.
-        ObjEntity target = (ObjEntity) getTargetEntity();
+        // whether this is good or bad is debatable, as users may want to point to
+        // non-existent entities on purpose.
+        ObjEntity target = getTargetEntity();
         if (target != null) {
-            encoder.print("\" target=\"" + target.getName());
+            encoder.attribute("target", target.getName());
         }
 
         if (getCollectionType() != null && !DEFAULT_COLLECTION_TYPE.equals(getCollectionType())) {
-            encoder.print("\" collection-type=\"" + getCollectionType());
+            encoder.attribute("collection-type", getCollectionType());
         }
 
-        if (getMapKey() != null) {
-            encoder.print("\" map-key=\"" + getMapKey());
-        }
-
-        if (isUsedForLocking()) {
-            encoder.print("\" lock=\"true");
-        }
+        encoder.attribute("lock", isUsedForLocking())
+                .attribute("map-key", getMapKey());
 
         String deleteRule = DeleteRule.deleteRuleName(getDeleteRule());
-        if (getDeleteRule() != DeleteRule.NO_ACTION && deleteRule != null) {
-            encoder.print("\" deleteRule=\"" + deleteRule);
+        if (deleteRule != null && getDeleteRule() != DeleteRule.NO_ACTION) {
+            encoder.attribute("deleteRule", deleteRule);
         }
 
         // quietly get rid of invalid path... this is not the best way of doing
-        // things,
-        // but it is consistent across map package
-        String path = getValidRelationshipPath();
-        if (path != null) {
-            encoder.print("\" db-relationship-path=\"" + path);
-        }
+        // things, but it is consistent across map package
+        encoder.attribute("db-relationship-path", getValidRelationshipPath());
 
-        encoder.println("\"/>");
+        delegate.visitObjRelationship(this);
+        encoder.end();
     }
 
     /**
@@ -563,8 +555,9 @@ public class ObjRelationship extends Relationship implements ConfigurationNode {
     /**
      * Sets relationship path, but does not trigger its conversion to
      * List<DbRelationship> For internal purposes, primarily datamap loading
+     * @since 4.1 this method is public as it is used by new XML loaders
      */
-    void setDeferredDbRelationshipPath(String relationshipPath) {
+    public void setDeferredDbRelationshipPath(String relationshipPath) {
         if (!Util.nullSafeEquals(getDbRelationshipPath(), relationshipPath)) {
             deferredPath = relationshipPath;
         }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/map/Procedure.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/Procedure.java b/cayenne-server/src/main/java/org/apache/cayenne/map/Procedure.java
index 8ec25cb..5358178 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/Procedure.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/Procedure.java
@@ -22,7 +22,6 @@ package org.apache.cayenne.map;
 import org.apache.cayenne.configuration.ConfigurationNode;
 import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
 import org.apache.cayenne.util.CayenneMapEntry;
-import org.apache.cayenne.util.Util;
 import org.apache.cayenne.util.XMLEncoder;
 import org.apache.cayenne.util.XMLSerializable;
 
@@ -90,34 +89,16 @@ public class Procedure implements ConfigurationNode, CayenneMapEntry, XMLSeriali
      * 
      * @since 1.1
      */
-    public void encodeAsXML(XMLEncoder encoder) {
-        encoder.print("<procedure name=\"");
-        encoder.print(Util.encodeXmlAttribute(getName()));
-        encoder.print('\"');
-
-        if (getSchema() != null && getSchema().trim().length() > 0) {
-            encoder.print(" schema=\"");
-            encoder.print(getSchema().trim());
-            encoder.print('\"');
-        }
-
-        if (getCatalog() != null && getCatalog().trim().length() > 0) {
-            encoder.print(" catalog=\"");
-            encoder.print(getCatalog().trim());
-            encoder.print('\"');
-        }
-
-        if (isReturningValue()) {
-            encoder.print(" returningValue=\"true\"");
-        }
-
-        encoder.println('>');
-
-        encoder.indent(1);
-        encoder.print(getCallParameters());
-        encoder.indent(-1);
-
-        encoder.println("</procedure>");
+    @Override
+    public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
+        encoder.start("procedure")
+                .attribute("name", getName())
+                .attribute("schema", getSchema())
+                .attribute("catalog", getCatalog())
+                .attribute("returningValue", isReturningValue())
+                .nested(getCallParameters(), delegate);
+        delegate.visitProcedure(this);
+        encoder.end();
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/map/ProcedureParameter.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/ProcedureParameter.java b/cayenne-server/src/main/java/org/apache/cayenne/map/ProcedureParameter.java
index 1180f30..b9fb55a 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/ProcedureParameter.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/ProcedureParameter.java
@@ -25,7 +25,6 @@ import org.apache.cayenne.configuration.ConfigurationNode;
 import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
 import org.apache.cayenne.dba.TypesMapping;
 import org.apache.cayenne.util.CayenneMapEntry;
-import org.apache.cayenne.util.Util;
 import org.apache.cayenne.util.XMLEncoder;
 import org.apache.cayenne.util.XMLSerializable;
 
@@ -85,8 +84,7 @@ public class ProcedureParameter implements ConfigurationNode, CayenneMapEntry,
 
     public void setParent(Object parent) {
         if (parent != null && !(parent instanceof Procedure)) {
-            throw new IllegalArgumentException("Expected null or Procedure, got: "
-                    + parent);
+            throw new IllegalArgumentException("Expected null or Procedure, got: " + parent);
         }
 
         setProcedure((Procedure) parent);
@@ -97,40 +95,25 @@ public class ProcedureParameter implements ConfigurationNode, CayenneMapEntry,
      * 
      * @since 1.1
      */
-    public void encodeAsXML(XMLEncoder encoder) {
-        encoder.print("<procedure-parameter name=\""
-                + Util.encodeXmlAttribute(getName())
-                + '\"');
-
-        String type = TypesMapping.getSqlNameByType(getType());
-        if (type != null) {
-            encoder.print(" type=\"" + type + '\"');
-        }
-
-        if (getMaxLength() > 0) {
-            encoder.print(" length=\"");
-            encoder.print(getMaxLength());
-            encoder.print('\"');
-        }
-
-        if (getPrecision() > 0) {
-            encoder.print(" precision=\"");
-            encoder.print(getPrecision());
-            encoder.print('\"');
-        }
+    @Override
+    public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
+        encoder.start("procedure-parameter")
+                .attribute("name", getName())
+                .attribute("type", TypesMapping.getSqlNameByType(getType()))
+                .attribute("length", getMaxLength() > 0 ? getMaxLength() : 0)
+                .attribute("precision", getPrecision() > 0 ? getPrecision() : 0);
 
         int direction = getDirection();
         if (direction == ProcedureParameter.IN_PARAMETER) {
-            encoder.print(" direction=\"in\"");
-        }
-        else if (direction == ProcedureParameter.IN_OUT_PARAMETER) {
-            encoder.print(" direction=\"in_out\"");
-        }
-        else if (direction == ProcedureParameter.OUT_PARAMETER) {
-            encoder.print(" direction=\"out\"");
+            encoder.attribute("direction", "in");
+        } else if (direction == ProcedureParameter.IN_OUT_PARAMETER) {
+            encoder.attribute("direction", "in_out");
+        } else if (direction == ProcedureParameter.OUT_PARAMETER) {
+            encoder.attribute("direction", "out");
         }
 
-        encoder.println("/>");
+        delegate.visitProcedureParameter(this);
+        encoder.end();
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/map/ProcedureQueryDescriptor.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/ProcedureQueryDescriptor.java b/cayenne-server/src/main/java/org/apache/cayenne/map/ProcedureQueryDescriptor.java
index fa1a087..a3ad9cf 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/ProcedureQueryDescriptor.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/ProcedureQueryDescriptor.java
@@ -18,6 +18,7 @@
  ****************************************************************/
 package org.apache.cayenne.map;
 
+import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
 import org.apache.cayenne.query.ProcedureQuery;
 import org.apache.cayenne.util.XMLEncoder;
 
@@ -65,41 +66,26 @@ public class ProcedureQueryDescriptor extends QueryDescriptor {
     }
 
     @Override
-    public void encodeAsXML(XMLEncoder encoder) {
-        encoder.print("<query name=\"");
-        encoder.print(getName());
-        encoder.print("\" type=\"");
-        encoder.print(type);
-
-        encoder.print("\" root=\"");
-        encoder.print(MapLoader.PROCEDURE_ROOT);
+    public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
+        encoder.start("query")
+                .attribute("name", getName())
+                .attribute("type", getType())
+                .attribute("root", QueryDescriptor.PROCEDURE_ROOT);
 
         String rootString = null;
-
         if (root instanceof String) {
             rootString = root.toString();
-        }
-        else if (root instanceof Procedure) {
+        } else if (root instanceof Procedure) {
             rootString = ((Procedure) root).getName();
         }
 
-        if (rootString != null) {
-            encoder.print("\" root-name=\"");
-            encoder.print(rootString);
-        }
-
-        if (resultEntityName != null) {
-            encoder.print("\" result-entity=\"");
-            encoder.print(resultEntityName);
-        }
-
-        encoder.println("\">");
-        encoder.indent(1);
+        encoder.attribute("root-name", rootString)
+                .attribute("result-entity", resultEntityName);
 
         // print properties
         encodeProperties(encoder);
 
-        encoder.indent(-1);
-        encoder.println("</query>");
+        delegate.visitQuery(this);
+        encoder.end();
     }
 }