[272378] Element returns null instead of empty string for undefined attributes
diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/DocumentImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/DocumentImpl.java
index d62030d..691970f 100644
--- a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/DocumentImpl.java
+++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/DocumentImpl.java
@@ -21,13 +21,16 @@
 // for org.apache.xerces 3.2.1
 // import org.apache.xerces.utils.XMLCharacterProperties;
 // DMW modified for XML4J 4.0.1
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.Map;
 
 import org.apache.xerces.dom.TreeWalkerImpl;
 import org.eclipse.wst.sse.core.internal.ltk.modelhandler.IModelHandler;
 import org.eclipse.wst.xml.core.internal.commentelement.impl.CommentElementRegistry;
 import org.eclipse.wst.xml.core.internal.contentmodel.CMDocument;
+import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration;
 import org.eclipse.wst.xml.core.internal.contentmodel.CMEntityDeclaration;
 import org.eclipse.wst.xml.core.internal.contentmodel.CMNamedNodeMap;
 import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.ModelQuery;
@@ -118,6 +121,18 @@
 
 	}
 
+	private class LimitedCache extends LinkedHashMap {
+		private static final long serialVersionUID = 1L;
+		private static final int MAX_SIZE = 10;
+		public LimitedCache() {
+			super(0, 0.75f, true);
+		}
+
+		protected boolean removeEldestEntry(java.util.Map.Entry entry) {
+			return size() > MAX_SIZE;
+		}
+	}
+
 	// this is a constant just to give compile-time control over
 	// whether or not to use the cache. If, in future, its found that
 	// there are no (or few) "duplicate requests" ... then this cache
@@ -129,6 +144,7 @@
 	private DOMModelImpl model = null;
 	private TagNameCache tagNameCache;
 
+	private Map fCMCache;
 	/**
 	 * DocumentImpl constructor
 	 */
@@ -137,6 +153,7 @@
 		if (usetagnamecache) {
 			tagNameCache = new TagNameCache();
 		}
+		fCMCache = Collections.synchronizedMap(new LimitedCache());
 	}
 
 	/**
@@ -150,6 +167,7 @@
 		if (usetagnamecache) {
 			tagNameCache = new TagNameCache();
 		}
+		fCMCache = Collections.synchronizedMap(new LimitedCache());
 	}
 
 	/**
@@ -1045,6 +1063,23 @@
 	}
 
 	/**
+	 * Provides an element's attribute declarations
+	 * @param element the element to retrieve the attribute map of
+	 * @return a <code>CMNamedNodeMap</code> of attributes if the declaration exists; null otherwise.
+	 */
+	CMNamedNodeMap getCMAttributes(Element element) {
+		CMNamedNodeMap map = (CMNamedNodeMap) fCMCache.get(element);
+		if (map == null) {
+			CMElementDeclaration decl = ModelQueryUtil.getModelQuery(this).getCMElementDeclaration(element);
+			if (decl != null) {
+				map = decl.getAttributes();
+				fCMCache.put(element, map);
+			}
+		}
+		return map;
+	}
+
+	/**
 	 * <p>
 	 * EXPERIMENTAL! Based on the <a
 	 * href='http://www.w3.org/TR/2001/WD-DOM-Level-3-Core-20010605'>Document
diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/ElementImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/ElementImpl.java
index 2de1968..bf2d0bc 100644
--- a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/ElementImpl.java
+++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/ElementImpl.java
@@ -28,7 +28,10 @@
 import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
 import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
 import org.eclipse.wst.xml.core.internal.commentelement.CommentElementAdapter;
+import org.eclipse.wst.xml.core.internal.contentmodel.CMAttributeDeclaration;
 import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration;
+import org.eclipse.wst.xml.core.internal.contentmodel.CMNamedNodeMap;
+import org.eclipse.wst.xml.core.internal.contentmodel.CMNode;
 import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.ModelQuery;
 import org.eclipse.wst.xml.core.internal.modelquery.ModelQueryUtil;
 import org.eclipse.wst.xml.core.internal.parser.XMLSourceParser;
@@ -235,9 +238,23 @@
 	 */
 	public String getAttribute(String name) {
 		Attr attr = getAttributeNode(name);
-		if (attr == null)
-			return null;
-		return attr.getValue();
+		// In the absence of the attribute, get the default value
+		return (attr != null) ? attr.getValue() : getDefaultValue(name);
+	}
+
+	/**
+	 * get the default value for attribute <code>name</code>. Returns an empty string
+	 * @param name
+	 * @return
+	 */
+	private String getDefaultValue(String name) {
+		CMNamedNodeMap map = ((DocumentImpl) getOwnerDocument()).getCMAttributes(this);
+		if (map != null) {
+			CMNode attribute = map.getNamedItem(name);
+			if (attribute instanceof CMAttributeDeclaration)
+				return ((CMAttributeDeclaration) attribute).getAttrType().getImpliedValue();
+		}
+		return new String();
 	}
 
 	/**
@@ -303,9 +320,8 @@
 	 */
 	public String getAttributeNS(String uri, String name) {
 		Attr attr = getAttributeNodeNS(uri, name);
-		if (attr == null)
-			return null;
-		return attr.getValue();
+		// In the absence of the attribute, get the default value
+		return (attr != null) ? attr.getValue() : getDefaultValue(name);
 	}
 
 	/**